From 69e3afc7707d467b758858b52d3784947f7a502b Mon Sep 17 00:00:00 2001
From: 疯狂的狮子Li <15040126243@163.com>
Date: 星期一, 20 五月 2024 10:25:23 +0800
Subject: [PATCH] !538 ♥️发布 5.2.0-BETA 公测版本 Merge pull request !538 from 疯狂的狮子Li/dev

---
 ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java                                |   56 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java                                   |  152 
 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java              |   54 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java                                        |   51 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfDefinitionConfigService.java                                |   83 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java                                       |   13 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java                                   |   22 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfFormManageVo.java                                          |   63 
 ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java                                           |   16 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowProcessEventHandler.java                         |   20 
 ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm                                                               |   29 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java                                                   |   28 
 script/bin/ry.bat                                                                                                                      |    2 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java                       |   82 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java                                          |    5 
 ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml                                                                 |   92 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java                                   |  105 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java                                                   |   18 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java                                     |    3 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowTaskEventHandler.java                            |   19 
 ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java                                                             |   31 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java                                          |   65 
 ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java                                      |   37 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml                                               |    7 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfNodeConfigMapper.xml                                                 |    7 
 .run/ruoyi-snailjob-server.run.xml                                                                                                     |    6 
 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java                       |   17 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java                                      |  295 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfDefinitionConfigMapper.xml                                           |    7 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java                                      |   51 
 ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java                                                          |    9 
 ruoyi-admin/Dockerfile                                                                                                                 |    4 
 script/docker/nginx/conf/nginx.conf                                                                                                    |   16 
 ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports    |    1 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java                                          |   49 
 script/sql/update/oracle/update_5.1.0-5.1.1.sql                                                                                        |    4 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java                                     |  147 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java                        |  108 
 ruoyi-admin/src/main/resources/application-prod.yml                                                                                    |   92 
 script/sql/update/sqlserver/update_5.1.2-5.2.0.sql                                                                                     |   29 
 ruoyi-extend/ruoyi-snailjob-server/src/main/java/org/dromara/snailjob/SnailJobServerApplication.java                                   |   19 
 ruoyi-common/ruoyi-common-redis/pom.xml                                                                                                |   10 
 ruoyi-admin/src/main/resources/application-dev.yml                                                                                     |   93 
 ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java                                 |    4 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java                                              |   38 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java                                        |   16 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java                                                   |    2 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java                       |   39 
 ruoyi-common/ruoyi-common-oss/pom.xml                                                                                                  |   40 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java                           |  160 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java                                               |   17 
 pom.xml                                                                                                                                |   95 
 ruoyi-modules/pom.xml                                                                                                                  |    1 
 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java                                          |    8 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java                                             |    6 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomProcessHandler.java                                 |   31 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java                                         |   54 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java                                        |   89 
 ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml                                                          |  104 
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java                                 |  100 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java                                                   |    5 
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java                                        |    5 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInvalidBo.java                                        |   31 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfNodeConfigServiceImpl.java                              |   75 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java                                     |   10 
 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java                      |   14 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfFormManageController.java                                 |  114 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveTaskListener.java                                    |   21 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java                                                  |   29 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java                                       |    6 
 script/sql/update/sqlserver/update_5.0-5.1.sql                                                                                         |    2 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java                                                  |    1 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java                                           |   44 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java                                       |   30 
 ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java                                                              |    6 
 ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml                                                              |   47 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java                                               |   16 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java                        |  117 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInstanceBo.java                                       |   43 
 ruoyi-common/ruoyi-common-mybatis/pom.xml                                                                                              |   13 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java                                              |   40 
 ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java                                          |   37 
 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java                                    |    4 
 ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java                                         |   34 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java                                     |   34 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java                                         |   13 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java                                             |   70 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfNodeConfig.java                                               |   61 
 ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application.yml                                                                  |   39 
 .run/ruoyi-server.run.xml                                                                                                              |    2 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java                                      |   54 
 ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/RedisExceptionHandler.java                              |   30 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java                                                   |    4 
 ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java                                                            |   14 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java                                     |   11 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfDefinitionConfigController.java                           |   79 
 ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java                                               |   31 
 ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json                                                           |    7 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java                                       |   94 
 ruoyi-common/ruoyi-common-satoken/pom.xml                                                                                              |    5 
 script/sql/oracle/oracle_ry_vue_5.X.sql                                                                                                |  344 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java                          |   61 
 ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java                                            |   72 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/TestLeaveMapper.java                                            |   15 
 script/sql/postgres/flowable.sql                                                                                                       |  341 
 ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java                                                               |   10 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java                                           |   73 
 ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java                                           |   12 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java                                               |  343 
 script/sql/sqlserver/snail_job_sqlserver.sql                                                                                           | 2697 ++++++
 ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml                                                             |   47 
 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java                               |   35 
 ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java                                                                   |   14 
 script/sql/snail_job.sql                                                                                                               |  509 +
 ruoyi-common/ruoyi-common-encrypt/pom.xml                                                                                              |   17 
 script/sql/update/update_5.1.1-5.1.2.sql                                                                                               |    1 
 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java                  |    4 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java                     | 1120 ++
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomTaskHandler.java                                    |   23 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfFormManageMapper.java                                         |   15 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java                              |  111 
 ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml                                                          |   23 
 ruoyi-common/ruoyi-common-bom/pom.xml                                                                                                  |    2 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java                                |  129 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java                                 |   23 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java                            |   40 
 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java                      |   83 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java                                       |   69 
 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/package-info.java                                                                |    5 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java                            |  141 
 ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java                        |   52 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java                                 |    2 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java                                           |   15 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java                                              |   37 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java                                          |   45 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/TaskTimeoutJobHandler.java                            |   38 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java                                    |  106 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml                                                      |   78 
 script/sql/ry_vue_5.X.sql                                                                                                              |  331 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java                                      |  137 
 ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports      |    1 
 ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java                                             |   29 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java                                            |    2 
 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestClassJobExecutor.java                                               |   19 
 ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java                                         |   45 
 ruoyi-modules/ruoyi-workflow/pom.xml                                                                                                   |  116 
 script/sql/oracle/flowable.sql                                                                                                         |  259 
 script/bin/ry.sh                                                                                                                       |    2 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java                                |    5 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java                                            |   46 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java                      |  438 +
 ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java                                                |   23 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java                                                        |   10 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java                                                 |   48 
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java                                  |   51 
 ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java                             |    4 
 ruoyi-extend/ruoyi-snailjob-server/Dockerfile                                                                                          |   19 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfNodeConfigService.java                                      |   56 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java                                                 |   52 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java                              |   37 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java                                     |   15 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java                                    |   65 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfFormManage.java                                               |   51 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfFormManageBo.java                                          |   53 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml                                                |    7 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java                                                  |    2 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java                                   |   18 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java                                            |   58 
 ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml                                                   |   38 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java                                           |    4 
 ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java                            |    7 
 ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java              |    4 
 script/sql/update/postgres/update_5.0-5.1.sql                                                                                          |    2 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java                                     |   31 
 ruoyi-common/ruoyi-common-web/pom.xml                                                                                                  |    5 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java                                                   |    6 
 ruoyi-admin/src/main/resources/application.yml                                                                                         |   32 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java                               |    6 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java                                 |   27 
 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java                  |    4 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfFormManageMapper.xml                                                 |    7 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java                              |   91 
 ruoyi-extend/ruoyi-monitor-admin/Dockerfile                                                                                            |    5 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java                                                 |   66 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java                                   |   13 
 ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java                                                            |    5 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfDefinitionConfigBo.java                                    |   59 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java                                              |  152 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java                                                  |  173 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java                                                        |    7 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java                                                  |   47 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java                                                   |    6 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java                                           |    8 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java                                                  |   15 
 ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm                                                            |    4 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java                              |   52 
 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java                     |   28 
 ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java                                     |   82 
 script/sql/update/postgres/update_5.1.1-5.1.2.sql                                                                                      |    1 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java                                            |   54 
 ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java                                                         |   25 
 ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm                                                           |   30 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java                                              |  193 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java                                       |   29 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java                                                  |   33 
 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java                             |   14 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfNodeConfigBo.java                                          |   63 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java                                               |    8 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfDefinitionConfigMapper.java                                   |   15 
 script/sql/update/oracle/update_5.0-5.1.sql                                                                                            |    2 
 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java                      |    1 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java                                          |   39 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowEventStrategy.java                               |   73 
 ruoyi-common/ruoyi-common-tenant/pom.xml                                                                                               |    6 
 ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm                                                              |   77 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java                                |  118 
 ruoyi-common/ruoyi-common-sms/pom.xml                                                                                                  |   13 
 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java                                    |   54 
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java                       |    5 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java                                  |  424 +
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java                                                  |   22 
 ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java                                       |    5 
 ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java                             |   90 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java                         |  147 
 ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java                                              |   61 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java                                        |   64 
 script/sql/update/oracle/update_5.1.1-5.1.2.sql                                                                                        |    1 
 script/docker/docker-compose.yml                                                                                                       |   20 
 ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java                      |   76 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java                                       |  227 
 ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties                                                                    |    8 
 script/sql/sqlserver/sqlserver_ry_vue_5.X.sql                                                                                          |  709 +
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java                                                  |   58 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java                                   |  872 ++
 script/sql/flowable.sql                                                                                                                |  175 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java                                          |   83 
 ruoyi-extend/ruoyi-snailjob-server/pom.xml                                                                                             |   46 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md                                                                 |    3 
 script/sql/postgres/postgres_ry_vue_5.X.sql                                                                                            |  363 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfDefinitionConfig.java                                         |   56 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java                                               |    3 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java                                            |    6 
 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java                                           |    2 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java                                   |   36 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java                                        |   16 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java                                              |    4 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java                                           |   43 
 ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java                                                 |  649 +
 ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |    1 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java                                   |    6 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml                                                   |    7 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java                                                 |    6 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java                                       |  100 
 script/sql/sqlserver/flowable.sql                                                                                                      |  447 +
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java                                              |   47 
 ruoyi-extend/ruoyi-snailjob-server/src/main/resources/banner.txt                                                                       |   11 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java                                           |   52 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java                                              |    2 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java                                           |    9 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfFormManageService.java                                      |   81 
 ruoyi-admin/src/main/resources/ip2region.xdb                                                                                           |    0 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskUrgingBo.java                                            |   34 
 ruoyi-common/ruoyi-common-log/pom.xml                                                                                                  |    5 
 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java                                         |    9 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java                                           |   31 
 .run/ruoyi-monitor-admin.run.xml                                                                                                       |    2 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveExecutionListener.java                               |   28 
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java            |    7 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java                                          |    7 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java                                        |   96 
 ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml                                                          |   16 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java                                  |   51 
 ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java                                                                 |   42 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/QueryUtils.java                                                  |  154 
 README.md                                                                                                                              |   10 
 ruoyi-extend/pom.xml                                                                                                                   |    2 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java                                             |    3 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml                                                |    7 
 ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java                                   |    9 
 ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java                                                  |   30 
 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java                                  |   96 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfNodeConfigMapper.java                                         |   15 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java                             |   18 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfNodeConfigVo.java                                          |   75 
 ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java                                    |   18 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java                            |  139 
 ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml                                                          |   13 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java                        |  683 +
 ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java                                |   35 
 ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java                                      |    8 
 ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java                       |   16 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java                                                  |  289 
 script/sql/update/update_5.1.2-5.2.0.sql                                                                                               |    5 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java                                   |   26 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java                                               |   24 
 ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm                                                                   |   91 
 script/sql/update/oracle/update_5.1.2-5.2.0.sql                                                                                        |    9 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java                                 |  121 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java                                             |   75 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java                                             |   61 
 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java                                 |   48 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java                                                |   44 
 script/bpmn/模型.zip                                                                                                                     |    0 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java                                   |   36 
 ruoyi-admin/pom.xml                                                                                                                    |   21 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java                                             |    4 
 ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java                                      |   96 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java                                         |   33 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfDefinitionConfigVo.java                                    |   70 
 ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java                                            |    6 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java                                   |   18 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/VariableVo.java                                              |   28 
 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml                                                    |    7 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java                                           |   47 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java                                           |  161 
 ruoyi-common/ruoyi-common-job/pom.xml                                                                                                  |   16 
 /dev/null                                                                                                                              |  175 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java                                         |   49 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java                                   |  106 
 script/sql/update/postgres/update_5.1.2-5.2.0.sql                                                                                      |    9 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java                             |   51 
 ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java                                                                  |    9 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java                                     |    1 
 script/sql/postgres/snail_job_postgre.sql                                                                                              |  825 +
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java                                           |   11 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java                                     |   70 
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java                                    |   32 
 ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java                                                       |    9 
 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java                        |    3 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java                                                   |    5 
 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java                                       |   28 
 ruoyi-common/ruoyi-common-core/pom.xml                                                                                                 |    5 
 script/sql/oracle/snail_job_oracle.sql                                                                                                 |  894 ++
 ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java                                                            |    9 
 ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java                                     |    2 
 ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java                                         |   18 
 336 files changed, 22,890 insertions(+), 2,157 deletions(-)

diff --git a/.run/ruoyi-monitor-admin.run.xml b/.run/ruoyi-monitor-admin.run.xml
index 4837d6a..095b3d7 100644
--- a/.run/ruoyi-monitor-admin.run.xml
+++ b/.run/ruoyi-monitor-admin.run.xml
@@ -2,7 +2,7 @@
   <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.1.2" />
+        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.2.0" />
         <option name="buildOnly" value="true" />
         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
       </settings>
diff --git a/.run/ruoyi-server.run.xml b/.run/ruoyi-server.run.xml
index a29ff2a..0463c34 100644
--- a/.run/ruoyi-server.run.xml
+++ b/.run/ruoyi-server.run.xml
@@ -2,7 +2,7 @@
   <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-server:5.1.2" />
+        <option name="imageTag" value="ruoyi/ruoyi-server:5.2.0" />
         <option name="buildOnly" value="true" />
         <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
       </settings>
diff --git a/.run/ruoyi-powerjob-server.run.xml b/.run/ruoyi-snailjob-server.run.xml
similarity index 68%
rename from .run/ruoyi-powerjob-server.run.xml
rename to .run/ruoyi-snailjob-server.run.xml
index 77a4af0..761915e 100644
--- a/.run/ruoyi-powerjob-server.run.xml
+++ b/.run/ruoyi-snailjob-server.run.xml
@@ -1,10 +1,10 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="ruoyi-powerjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
+  <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
     <deployment type="dockerfile">
       <settings>
-        <option name="imageTag" value="ruoyi/ruoyi-powerjob-server:5.1.2" />
+        <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.2.0" />
         <option name="buildOnly" value="true" />
-        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-powerjob-server/Dockerfile" />
+        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
       </settings>
     </deployment>
     <method v="2" />
diff --git a/README.md b/README.md
index a542b47..05dd679 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
 [![浣跨敤IntelliJ IDEA寮�鍙戠淮鎶(https://img.shields.io/badge/IntelliJ%20IDEA-鎻愪緵鏀寔-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
 <br>
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.1.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
+[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.2.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
 [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.1-blue.svg)]()
 [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
 [![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()
@@ -27,8 +27,10 @@
 
 ## 璧炲姪鍟�
 
-MaxKey - https://gitee.com/dromara/MaxKey <br>
-CCFlow - https://gitee.com/opencc/RuoYi-JFlow <br>
+MaxKey 涓氱晫棰嗗厛鍗曠偣鐧诲綍浜у搧 - https://gitee.com/dromara/MaxKey <br>
+CCFlow 椹拌仒浣庝唬鐮�-娴佺▼-琛ㄥ崟 - https://gitee.com/opencc/RuoYi-JFlow <br>
+鏁拌埖绉戞妧 杞欢瀹氬埗寮�鍙慉PP灏忕▼搴忕瓑 - http://www.shuduokeji.com/ <br>
+寮曡繄淇℃伅 杞欢寮�鍙戝钩鍙� - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
 [濡備綍鎴愪负璧炲姪鍟� 鍔犵兢鑱旂郴浣滆�呰璋圿(https://plus-doc.dromara.org/#/common/add_group)
 
 # 鏈鏋朵笌RuoYi鐨勫姛鑳藉樊寮�
@@ -62,7 +64,7 @@
 | 搴忓垪鍖�         | 閲囩敤 Jackson Spring瀹樻柟鍐呯疆搴忓垪鍖� 闈犺氨!!!                                                                                    | 閲囩敤 fastjson bugjson 杩滆繎闂诲悕                                                           | 
 | 鍒嗗竷寮忓箓绛�       | 鍙傝�冪編鍥TIS闃查噸绯荤粺绠�鍖栧疄鐜�(缁嗚妭鍙湅鏂囨。)                                                                                          | 鎵嬪姩缂栧啓娉ㄨВ鍩轰簬aop瀹炵幇                                                                      |
 | 鍒嗗竷寮忛攣        | 閲囩敤 Lock4j 搴曞眰鍩轰簬 Redisson                                                                                           | 鏃�                                                                                  |
-| 鍒嗗竷寮忎换鍔¤皟搴�     | 閲囩敤 PowerJob 澶╃敓鏀寔鍒嗗竷寮� 缁熶竴鐨勭鐞嗕腑蹇�                                                                                       | 閲囩敤 Quartz 鍩轰簬鏁版嵁搴撻攣鎬ц兘宸� 闆嗙兢闇�瑕佸仛寰堝閰嶇疆涓庢敼閫�                                                   | 
+| 鍒嗗竷寮忎换鍔¤皟搴�     | 閲囩敤 SnailJob 澶╃敓鏀寔鍒嗗竷寮� 缁熶竴鐨勭鐞嗕腑蹇� 鏀寔澶氱鏁版嵁搴� 鏀寔鍒嗙墖閲嶈瘯DAG浠诲姟娴佺瓑                                                                 | 閲囩敤 Quartz 鍩轰簬鏁版嵁搴撻攣鎬ц兘宸� 闆嗙兢闇�瑕佸仛寰堝閰嶇疆涓庢敼閫�                                                   | 
 | 鏂囦欢瀛樺偍        | 閲囩敤 Minio 鍒嗗竷寮忔枃浠跺瓨鍌� 澶╃敓鏀寔澶氭満銆佸纭洏銆佸鍒嗙墖銆佸鍓湰瀛樺偍<br/>鏀寔鏉冮檺绠$悊 瀹夊叏鍙潬 鏂囦欢鍙姞瀵嗗瓨鍌�                                                     | 閲囩敤 鏈満鏂囦欢瀛樺偍 鏂囦欢瑁告紡 鏄撲涪澶辨硠婕� 涓嶆敮鎸侀泦缇ゆ湁鍗曠偣鏁堝簲                                                    |
 | 浜戝瓨鍌�         | 閲囩敤 AWS S3 鍗忚瀹㈡埛绔� 鏀寔 涓冪墰銆侀樋閲屻�佽吘璁� 绛変竴鍒囨敮鎸丼3鍗忚鐨勫巶瀹�                                                                          | 涓嶆敮鎸�                                                                                |
 | 鐭俊          | 閲囩敤 sms4j 鐭俊铻嶅悎鍖� 鏀寔鏁板崄绉嶇煭淇″巶瀹� 鍙渶鍦▂ml閰嶇疆濂藉巶瀹跺瘑閽ュ嵆鍙娇鐢� 鍙鍘傚鍏辩敤                                                                 | 涓嶆敮鎸�                                                                                |
diff --git a/pom.xml b/pom.xml
index 3aafe14..4d04d39 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,40 +13,41 @@
     <description>RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</description>
 
     <properties>
-        <revision>5.1.2</revision>
-        <spring-boot.version>3.1.7</spring-boot.version>
+        <revision>5.2.0-BETA</revision>
+        <spring-boot.version>3.2.5</spring-boot.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
         <java.version>17</java.version>
-        <spring-boot.mybatis>3.0.3</spring-boot.mybatis>
-        <springdoc.version>2.2.0</springdoc.version>
+        <mybatis.version>3.5.16</mybatis.version>
+        <springdoc.version>2.5.0</springdoc.version>
         <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
         <poi.version>5.2.3</poi.version>
-        <easyexcel.version>3.3.3</easyexcel.version>
+        <easyexcel.version>3.3.4</easyexcel.version>
         <velocity.version>2.3</velocity.version>
-        <satoken.version>1.37.0</satoken.version>
-        <mybatis-plus.version>3.5.4</mybatis-plus.version>
+        <satoken.version>1.38.0</satoken.version>
+        <mybatis-plus.version>3.5.6</mybatis-plus.version>
         <p6spy.version>3.9.1</p6spy.version>
-        <hutool.version>5.8.22</hutool.version>
+        <hutool.version>5.8.27</hutool.version>
         <okhttp.version>4.10.0</okhttp.version>
-        <spring-boot-admin.version>3.1.8</spring-boot-admin.version>
-        <redisson.version>3.24.3</redisson.version>
-        <lock4j.version>2.2.5</lock4j.version>
-        <dynamic-ds.version>4.2.0</dynamic-ds.version>
+        <spring-boot-admin.version>3.2.3</spring-boot-admin.version>
+        <redisson.version>3.29.0</redisson.version>
+        <lock4j.version>2.2.7</lock4j.version>
+        <dynamic-ds.version>4.3.0</dynamic-ds.version>
         <alibaba-ttl.version>2.14.4</alibaba-ttl.version>
-        <powerjob.version>4.3.6</powerjob.version>
-        <mapstruct-plus.version>1.3.5</mapstruct-plus.version>
+        <snailjob.version>1.0.0-beta1</snailjob.version>
+        <mapstruct-plus.version>1.3.6</mapstruct-plus.version>
         <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
-        <lombok.version>1.18.30</lombok.version>
+        <lombok.version>1.18.32</lombok.version>
         <bouncycastle.version>1.76</bouncycastle.version>
         <justauth.version>1.16.6</justauth.version>
         <!-- 绂荤嚎IP鍦板潃瀹氫綅搴� -->
         <ip2region.version>2.7.0</ip2region.version>
 
         <!-- OSS 閰嶇疆 -->
-        <aws-java-sdk-s3.version>1.12.600</aws-java-sdk-s3.version>
+        <aws.sdk.version>2.25.15</aws.sdk.version>
+        <aws.crt.version>0.29.13</aws.crt.version>
         <!-- SMS 閰嶇疆 -->
-        <sms4j.version>2.2.0</sms4j.version>
+        <sms4j.version>3.2.1</sms4j.version>
         <!-- 闄愬埗妗嗘灦涓殑fastjson鐗堟湰 -->
         <fastjson.version>1.2.83</fastjson.version>
 
@@ -56,6 +57,9 @@
         <maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
         <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
         <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
+
+        <!--宸ヤ綔娴侀厤缃�-->
+        <flowable.version>7.0.0</flowable.version>
     </properties>
 
     <profiles>
@@ -106,6 +110,14 @@
                 <groupId>cn.hutool</groupId>
                 <artifactId>hutool-bom</artifactId>
                 <version>${hutool.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <dependency>
+                <groupId>org.flowable</groupId>
+                <artifactId>flowable-bom</artifactId>
+                <version>${flowable.version}</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
@@ -205,14 +217,14 @@
             </dependency>
 
             <dependency>
-                <groupId>org.mybatis.spring.boot</groupId>
-                <artifactId>mybatis-spring-boot-starter</artifactId>
-                <version>${spring-boot.mybatis}</version>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis</artifactId>
+                <version>${mybatis.version}</version>
             </dependency>
 
             <dependency>
                 <groupId>com.baomidou</groupId>
-                <artifactId>mybatis-plus-boot-starter</artifactId>
+                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
                 <version>${mybatis-plus.version}</version>
             </dependency>
 
@@ -235,10 +247,23 @@
                 <version>${okhttp.version}</version>
             </dependency>
 
+            <!--  AWS SDK for Java 2.x  -->
             <dependency>
-                <groupId>com.amazonaws</groupId>
-                <artifactId>aws-java-sdk-s3</artifactId>
-                <version>${aws-java-sdk-s3.version}</version>
+                <groupId>software.amazon.awssdk</groupId>
+                <artifactId>s3</artifactId>
+                <version>${aws.sdk.version}</version>
+            </dependency>
+            <!-- 浣跨敤AWS鍩轰簬 CRT 鐨� S3 瀹㈡埛绔� -->
+            <dependency>
+                <groupId>software.amazon.awssdk.crt</groupId>
+                <artifactId>aws-crt</artifactId>
+                <version>${aws.crt.version}</version>
+            </dependency>
+            <!-- 鍩轰簬 AWS CRT 鐨� S3 瀹㈡埛绔殑鎬ц兘澧炲己鐨� S3 浼犺緭绠$悊鍣� -->
+            <dependency>
+                <groupId>software.amazon.awssdk</groupId>
+                <artifactId>s3-transfer-manager</artifactId>
+                <version>${aws.sdk.version}</version>
             </dependency>
             <!--鐭俊sms4j-->
             <dependency>
@@ -271,16 +296,16 @@
                 <version>${lock4j.version}</version>
             </dependency>
 
-            <!-- PowerJob -->
+            <!-- SnailJob Client -->
             <dependency>
-                <groupId>tech.powerjob</groupId>
-                <artifactId>powerjob-worker-spring-boot-starter</artifactId>
-                <version>${powerjob.version}</version>
+                <groupId>com.aizuda</groupId>
+                <artifactId>snail-job-client-starter</artifactId>
+                <version>${snailjob.version}</version>
             </dependency>
             <dependency>
-                <groupId>tech.powerjob</groupId>
-                <artifactId>powerjob-official-processors</artifactId>
-                <version>${powerjob.version}</version>
+                <groupId>com.aizuda</groupId>
+                <artifactId>snail-job-client-job-core</artifactId>
+                <version>${snailjob.version}</version>
             </dependency>
 
             <dependency>
@@ -336,6 +361,13 @@
             <dependency>
                 <groupId>org.dromara</groupId>
                 <artifactId>ruoyi-demo</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!--  宸ヤ綔娴佹ā鍧�  -->
+            <dependency>
+                <groupId>org.dromara</groupId>
+                <artifactId>ruoyi-workflow</artifactId>
                 <version>${revision}</version>
             </dependency>
 
@@ -398,6 +430,7 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>${maven-surefire-plugin.version}</version>
                 <configuration>
+                    <argLine>-Dfile.encoding=UTF-8</argLine>
                     <!-- 鏍规嵁鎵撳寘鐜鎵ц瀵瑰簲鐨凘Tag娴嬭瘯鏂规硶 -->
                     <groups>${profiles.active}</groups>
                     <!-- 鎺掗櫎鏍囩 -->
diff --git a/ruoyi-admin/Dockerfile b/ruoyi-admin/Dockerfile
index 4237ef9..609b04a 100644
--- a/ruoyi-admin/Dockerfile
+++ b/ruoyi-admin/Dockerfile
@@ -19,6 +19,6 @@
            # 搴旂敤鍚嶇О 濡傛灉鎯冲尯鍒嗛泦缇よ妭鐐圭洃鎺� 鏀规垚涓嶅悓鐨勫悕绉板嵆鍙�
            #-Dskywalking.agent.service_name=ruoyi-server \
            #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
-           -jar app.jar \
-           -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS}
+           -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
+           -jar app.jar
 
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 26cd023..0ae1850 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -55,6 +55,11 @@
 
         <dependency>
             <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-mail</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
             <artifactId>ruoyi-system</artifactId>
         </dependency>
 
@@ -75,6 +80,12 @@
             <artifactId>ruoyi-demo</artifactId>
         </dependency>
 
+        <!--  宸ヤ綔娴佹ā鍧�  -->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-workflow</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>de.codecentric</groupId>
             <artifactId>spring-boot-admin-starter-client</artifactId>
@@ -91,6 +102,16 @@
             <artifactId>JustAuth</artifactId>
         </dependency>
 
+        <!-- SnailJob client -->
+        <dependency>
+            <groupId>com.aizuda</groupId>
+            <artifactId>snail-job-client-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.aizuda</groupId>
+            <artifactId>snail-job-client-job-core</artifactId>
+        </dependency>
+
         <!-- skywalking 鏁村悎 logback -->
 <!--        <dependency>-->
 <!--            <groupId>org.apache.skywalking</groupId>-->
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java
index d0d7931..43e689b 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java
@@ -23,9 +23,10 @@
 import org.dromara.common.social.config.properties.SocialProperties;
 import org.dromara.common.social.utils.SocialUtils;
 import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.common.websocket.dto.WebSocketMessageDto;
 import org.dromara.common.websocket.utils.WebSocketUtils;
-import org.dromara.system.domain.SysClient;
 import org.dromara.system.domain.bo.SysTenantBo;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysTenantVo;
 import org.dromara.system.service.ISysClientService;
 import org.dromara.system.service.ISysConfigService;
@@ -81,7 +82,7 @@
         // 鎺堟潈绫诲瀷鍜屽鎴风id
         String clientId = loginBody.getClientId();
         String grantType = loginBody.getGrantType();
-        SysClient client = clientService.queryByClientId(clientId);
+        SysClientVo client = clientService.queryByClientId(clientId);
         // 鏌ヨ涓嶅埌 client 鎴� client 鍐呬笉鍖呭惈 grantType
         if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
             log.info("瀹㈡埛绔痠d: {} 璁よ瘉绫诲瀷锛歿} 寮傚父!.", clientId, grantType);
@@ -96,7 +97,10 @@
 
         Long userId = LoginHelper.getUserId();
         scheduledExecutorService.schedule(() -> {
-            WebSocketUtils.sendMessage(userId, "娆㈣繋鐧诲綍RuoYi-Vue-Plus鍚庡彴绠$悊绯荤粺");
+            WebSocketMessageDto dto = new WebSocketMessageDto();
+            dto.setMessage("娆㈣繋鐧诲綍RuoYi-Vue-Plus鍚庡彴绠$悊绯荤粺");
+            dto.setSessionKeys(List.of(userId));
+            WebSocketUtils.publishMessage(dto);
         }, 3, TimeUnit.SECONDS);
         return R.ok(loginVo);
     }
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
index ef33f5b..1a476a9 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
@@ -5,6 +5,9 @@
 import cn.hutool.captcha.generator.CodeGenerator;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.RandomUtil;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.constant.GlobalConstants;
 import org.dromara.common.core.domain.R;
@@ -21,11 +24,7 @@
 import org.dromara.sms4j.api.SmsBlend;
 import org.dromara.sms4j.api.entity.SmsResponse;
 import org.dromara.sms4j.core.factory.SmsFactory;
-import org.dromara.sms4j.provider.enumerate.SupplierType;
 import org.dromara.web.domain.vo.CaptchaVo;
-import jakarta.validation.constraints.NotBlank;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -66,11 +65,11 @@
         String templateId = "";
         LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
         map.put("code", code);
-        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
         SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
-        if (!"OK".equals(smsResponse.getCode())) {
+        if (!smsResponse.isSuccess()) {
             log.error("楠岃瘉鐮佺煭淇″彂閫佸紓甯� => {}", smsResponse);
-            return R.fail(smsResponse.getMessage());
+            return R.fail(smsResponse.getData().toString());
         }
         return R.ok();
     }
@@ -121,6 +120,7 @@
         AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
         captcha.setGenerator(codeGenerator);
         captcha.createCode();
+        // 濡傛灉鏄暟瀛﹂獙璇佺爜锛屼娇鐢⊿pEL琛ㄨ揪寮忓鐞嗛獙璇佺爜缁撴灉
         String code = captcha.getCode();
         if (isMath) {
             ExpressionParser parser = new SpelExpressionParser();
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java b/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java
index 4d4bc89..db9c271 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/domain/vo/TenantListVo.java
@@ -13,10 +13,19 @@
 @AutoMapper(target = SysTenantVo.class)
 public class TenantListVo {
 
+    /**
+     * 绉熸埛缂栧彿
+     */
     private String tenantId;
 
+    /**
+     * 浼佷笟鍚嶇О
+     */
     private String companyName;
 
+    /**
+     * 鍩熷悕
+     */
     private String domain;
 
 }
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java
index 6d67fb1..a472404 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java
@@ -10,7 +10,6 @@
 import org.dromara.common.core.constant.CacheConstants;
 import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.domain.dto.UserOnlineDTO;
-import org.dromara.common.core.domain.model.LoginUser;
 import org.dromara.common.core.utils.MessageUtils;
 import org.dromara.common.core.utils.ServletUtils;
 import org.dromara.common.core.utils.SpringUtils;
@@ -18,6 +17,7 @@
 import org.dromara.common.log.event.LogininforEvent;
 import org.dromara.common.redis.utils.RedisUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
 import org.dromara.web.service.SysLoginService;
 import org.springframework.stereotype.Component;
 
@@ -43,7 +43,6 @@
     public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
         UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
         String ip = ServletUtils.getClientIP();
-        LoginUser user = LoginHelper.getLoginUser();
         UserOnlineDTO dto = new UserOnlineDTO();
         dto.setIpaddr(ip);
         dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
@@ -51,25 +50,29 @@
         dto.setOs(userAgent.getOs().getName());
         dto.setLoginTime(System.currentTimeMillis());
         dto.setTokenId(tokenValue);
-        dto.setUserName(user.getUsername());
-        dto.setClientKey(user.getClientKey());
-        dto.setDeviceType(user.getDeviceType());
-        dto.setDeptName(user.getDeptName());
-        if(tokenConfig.getTimeout() == -1) {
-            RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
-        } else {
-            RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
-        }
+        String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
+        String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
+        dto.setUserName(username);
+        dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
+        dto.setDeviceType(loginModel.getDevice());
+        dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
+        TenantHelper.dynamic(tenantId, () -> {
+            if(tokenConfig.getTimeout() == -1) {
+                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
+            } else {
+                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
+            }
+        });
         // 璁板綍鐧诲綍鏃ュ織
         LogininforEvent logininforEvent = new LogininforEvent();
-        logininforEvent.setTenantId(user.getTenantId());
-        logininforEvent.setUsername(user.getUsername());
+        logininforEvent.setTenantId(tenantId);
+        logininforEvent.setUsername(username);
         logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
         logininforEvent.setMessage(MessageUtils.message("user.login.success"));
         logininforEvent.setRequest(ServletUtils.getRequest());
         SpringUtils.context().publishEvent(logininforEvent);
         // 鏇存柊鐧诲綍淇℃伅
-        loginService.recordLoginInfo(user.getUserId(), ip);
+        loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
         log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
     }
 
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java
index d4f9c73..a75b913 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/service/IAuthStrategy.java
@@ -4,6 +4,7 @@
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.system.domain.SysClient;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.web.domain.vo.LoginVo;
 
 /**
@@ -17,8 +18,13 @@
 
     /**
      * 鐧诲綍
+     *
+     * @param body      鐧诲綍瀵硅薄
+     * @param client    鎺堟潈绠$悊瑙嗗浘瀵硅薄
+     * @param grantType 鎺堟潈绫诲瀷
+     * @return 鐧诲綍楠岃瘉淇℃伅
      */
-    static LoginVo login(String body, SysClient client, String grantType) {
+    static LoginVo login(String body, SysClientVo client, String grantType) {
         // 鎺堟潈绫诲瀷鍜屽鎴风id
         String beanName = grantType + BASE_NAME;
         if (!SpringUtils.containsBean(beanName)) {
@@ -30,7 +36,11 @@
 
     /**
      * 鐧诲綍
+     *
+     * @param body   鐧诲綍瀵硅薄
+     * @param client 鎺堟潈绠$悊瑙嗗浘瀵硅薄
+     * @return 鐧诲綍楠岃瘉淇℃伅
      */
-    LoginVo login(String body, SysClient client);
+    LoginVo login(String body, SysClientVo client);
 
 }
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
index 37802b7..af6e7f5 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java
@@ -5,6 +5,7 @@
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.lock.annotation.Lock4j;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.zhyd.oauth.model.AuthUser;
@@ -15,6 +16,7 @@
 import org.dromara.common.core.domain.model.LoginUser;
 import org.dromara.common.core.enums.LoginType;
 import org.dromara.common.core.enums.TenantStatus;
+import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.exception.user.UserException;
 import org.dromara.common.core.utils.*;
 import org.dromara.common.log.event.LogininforEvent;
@@ -25,13 +27,9 @@
 import org.dromara.common.tenant.helper.TenantHelper;
 import org.dromara.system.domain.SysUser;
 import org.dromara.system.domain.bo.SysSocialBo;
-import org.dromara.system.domain.vo.SysSocialVo;
-import org.dromara.system.domain.vo.SysTenantVo;
-import org.dromara.system.domain.vo.SysUserVo;
+import org.dromara.system.domain.vo.*;
 import org.dromara.system.mapper.SysUserMapper;
-import org.dromara.system.service.ISysPermissionService;
-import org.dromara.system.service.ISysSocialService;
-import org.dromara.system.service.ISysTenantService;
+import org.dromara.system.service.*;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
@@ -59,6 +57,8 @@
     private final ISysTenantService tenantService;
     private final ISysPermissionService permissionService;
     private final ISysSocialService sysSocialService;
+    private final ISysRoleService roleService;
+    private final ISysDeptService deptService;
     private final SysUserMapper userMapper;
 
 
@@ -66,20 +66,28 @@
      * 缁戝畾绗笁鏂圭敤鎴�
      *
      * @param authUserData 鎺堟潈鍝嶅簲瀹炰綋
-     * @return 缁熶竴鍝嶅簲瀹炰綋
      */
+    @Lock4j
     public void socialRegister(AuthUser authUserData) {
         String authId = authUserData.getSource() + authUserData.getUuid();
         // 绗笁鏂圭敤鎴蜂俊鎭�
         SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class);
         BeanUtil.copyProperties(authUserData.getToken(), bo);
-        bo.setUserId(LoginHelper.getUserId());
+        Long userId = LoginHelper.getUserId();
+        bo.setUserId(userId);
         bo.setAuthId(authId);
         bo.setOpenId(authUserData.getUuid());
         bo.setUserName(authUserData.getUsername());
         bo.setNickName(authUserData.getNickname());
+        List<SysSocialVo> checkList = sysSocialService.selectByAuthId(authId);
+        if (CollUtil.isNotEmpty(checkList)) {
+            throw new ServiceException("姝や笁鏂硅处鍙峰凡缁忚缁戝畾!");
+        }
         // 鏌ヨ鏄惁宸茬粡缁戝畾鐢ㄦ埛
-        List<SysSocialVo> list = sysSocialService.selectByAuthId(authId);
+        SysSocialBo params = new SysSocialBo();
+        params.setUserId(userId);
+        params.setSource(bo.getSource());
+        List<SysSocialVo> list = sysSocialService.queryList(params);
         if (CollUtil.isEmpty(list)) {
             // 娌℃湁缁戝畾鐢ㄦ埛, 鏂板鐢ㄦ埛淇℃伅
             sysSocialService.insertByBo(bo);
@@ -87,6 +95,8 @@
             // 鏇存柊鐢ㄦ埛淇℃伅
             bo.setId(list.get(0).getId());
             sysSocialService.updateByBo(bo);
+            // 濡傛灉瑕佺粦瀹氱殑骞冲彴璐﹀彿宸茬粡琚粦瀹氳繃浜� 鏄惁鎶涘紓甯歌嚜琛屽喅鏂�
+            // throw new ServiceException("姝ゅ钩鍙拌处鍙峰凡缁忚缁戝畾!");
         }
     }
 
@@ -132,7 +142,6 @@
         SpringUtils.context().publishEvent(logininforEvent);
     }
 
-
     /**
      * 鏋勫缓鐧诲綍鐢ㄦ埛
      */
@@ -146,9 +155,16 @@
         loginUser.setUserType(user.getUserType());
         loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
         loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
-        loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName());
-        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
-        loginUser.setRoles(roles);
+        TenantHelper.dynamic(user.getTenantId(), () -> {
+            SysDeptVo dept = null;
+            if (ObjectUtil.isNotNull(user.getDeptId())) {
+                dept = deptService.selectDeptById(user.getDeptId());
+            }
+            loginUser.setDeptName(ObjectUtil.isNull(dept) ? "" : dept.getDeptName());
+            loginUser.setDeptCategory(ObjectUtil.isNull(dept) ? "" : dept.getDeptCategory());
+            List<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId());
+            loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
+        });
         return loginUser;
     }
 
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java
index f38d074..ddab279 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java
@@ -1,7 +1,6 @@
 package org.dromara.web.service;
 
 import cn.dev33.satoken.secure.BCrypt;
-import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.constant.Constants;
@@ -61,8 +60,7 @@
 
         boolean exist = TenantHelper.dynamic(tenantId, () -> {
             return userMapper.exists(new LambdaQueryWrapper<SysUser>()
-                .eq(SysUser::getUserName, sysUser.getUserName())
-                .ne(ObjectUtil.isNotNull(sysUser.getUserId()), SysUser::getUserId, sysUser.getUserId()));
+                .eq(SysUser::getUserName, sysUser.getUserName()));
         });
         if (exist) {
             throw new UserException("user.register.save.error", username);
@@ -82,7 +80,7 @@
      * @param uuid     鍞竴鏍囪瘑
      */
     public void validateCaptcha(String tenantId, String username, String code, String uuid) {
-        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
+        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
         String captcha = RedisUtils.getCacheObject(verifyKey);
         RedisUtils.deleteObject(verifyKey);
         if (captcha == null) {
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java
index a2636bc..38fdc44 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java
@@ -23,6 +23,7 @@
 import org.dromara.common.tenant.helper.TenantHelper;
 import org.dromara.system.domain.SysClient;
 import org.dromara.system.domain.SysUser;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.system.mapper.SysUserMapper;
 import org.dromara.web.domain.vo.LoginVo;
@@ -44,7 +45,7 @@
     private final SysUserMapper userMapper;
 
     @Override
-    public LoginVo login(String body, SysClient client) {
+    public LoginVo login(String body, SysClientVo client) {
         EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
         ValidatorUtils.validate(loginBody);
         String tenantId = loginBody.getTenantId();
@@ -90,9 +91,7 @@
 
     private SysUserVo loadUserByEmail(String tenantId, String email) {
         return TenantHelper.dynamic(tenantId, () -> {
-            SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
-                .select(SysUser::getEmail, SysUser::getStatus)
-                .eq(SysUser::getEmail, email));
+            SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
             if (ObjectUtil.isNull(user)) {
                 log.info("鐧诲綍鐢ㄦ埛锛歿} 涓嶅瓨鍦�.", email);
                 throw new UserException("user.not.exists", email);
@@ -100,7 +99,7 @@
                 log.info("鐧诲綍鐢ㄦ埛锛歿} 宸茶鍋滅敤.", email);
                 throw new UserException("user.blocked", email);
             }
-            return userMapper.selectUserByEmail(email);
+            return user;
         });
     }
 
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java
index bcb5916..cd33ea4 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java
@@ -26,6 +26,7 @@
 import org.dromara.common.web.config.properties.CaptchaProperties;
 import org.dromara.system.domain.SysClient;
 import org.dromara.system.domain.SysUser;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.system.mapper.SysUserMapper;
 import org.dromara.web.domain.vo.LoginVo;
@@ -48,7 +49,7 @@
     private final SysUserMapper userMapper;
 
     @Override
-    public LoginVo login(String body, SysClient client) {
+    public LoginVo login(String body, SysClientVo client) {
         PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
         ValidatorUtils.validate(loginBody);
         String tenantId = loginBody.getTenantId();
@@ -109,9 +110,7 @@
 
     private SysUserVo loadUserByUsername(String tenantId, String username) {
         return TenantHelper.dynamic(tenantId, () -> {
-            SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
-                .select(SysUser::getUserName, SysUser::getStatus)
-                .eq(SysUser::getUserName, username));
+            SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
             if (ObjectUtil.isNull(user)) {
                 log.info("鐧诲綍鐢ㄦ埛锛歿} 涓嶅瓨鍦�.", username);
                 throw new UserException("user.not.exists", username);
@@ -119,7 +118,7 @@
                 log.info("鐧诲綍鐢ㄦ埛锛歿} 宸茶鍋滅敤.", username);
                 throw new UserException("user.blocked", username);
             }
-            return userMapper.selectUserByUserName(username);
+            return user;
         });
     }
 
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java
index a4fa11c..f883632 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java
@@ -23,6 +23,7 @@
 import org.dromara.common.tenant.helper.TenantHelper;
 import org.dromara.system.domain.SysClient;
 import org.dromara.system.domain.SysUser;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.system.mapper.SysUserMapper;
 import org.dromara.web.domain.vo.LoginVo;
@@ -44,7 +45,7 @@
     private final SysUserMapper userMapper;
 
     @Override
-    public LoginVo login(String body, SysClient client) {
+    public LoginVo login(String body, SysClientVo client) {
         SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
         ValidatorUtils.validate(loginBody);
         String tenantId = loginBody.getTenantId();
@@ -90,9 +91,7 @@
 
     private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) {
         return TenantHelper.dynamic(tenantId, () -> {
-            SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
-                .select(SysUser::getPhonenumber, SysUser::getStatus)
-                .eq(SysUser::getPhonenumber, phonenumber));
+            SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
             if (ObjectUtil.isNull(user)) {
                 log.info("鐧诲綍鐢ㄦ埛锛歿} 涓嶅瓨鍦�.", phonenumber);
                 throw new UserException("user.not.exists", phonenumber);
@@ -100,7 +99,7 @@
                 log.info("鐧诲綍鐢ㄦ埛锛歿} 宸茶鍋滅敤.", phonenumber);
                 throw new UserException("user.blocked", phonenumber);
             }
-            return userMapper.selectUserByPhonenumber(phonenumber);
+            return user;
         });
     }
 
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
index a12386e..6694165 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SocialAuthStrategy.java
@@ -7,7 +7,6 @@
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.http.Method;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import me.zhyd.oauth.model.AuthResponse;
@@ -23,8 +22,7 @@
 import org.dromara.common.social.config.properties.SocialProperties;
 import org.dromara.common.social.utils.SocialUtils;
 import org.dromara.common.tenant.helper.TenantHelper;
-import org.dromara.system.domain.SysClient;
-import org.dromara.system.domain.SysUser;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysSocialVo;
 import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.system.mapper.SysUserMapper;
@@ -59,7 +57,7 @@
      * @param client   瀹㈡埛绔俊鎭�
      */
     @Override
-    public LoginVo login(String body, SysClient client) {
+    public LoginVo login(String body, SysClientVo client) {
         SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
         ValidatorUtils.validate(loginBody);
         AuthResponse<AuthUser> response = SocialUtils.loginAuth(
@@ -83,11 +81,16 @@
         if (CollUtil.isEmpty(list)) {
             throw new ServiceException("浣犺繕娌℃湁缁戝畾绗笁鏂硅处鍙凤紝缁戝畾鍚庢墠鍙互鐧诲綍锛�");
         }
-        Optional<SysSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny();
-        if (opt.isEmpty()) {
-            throw new ServiceException("瀵逛笉璧凤紝浣犳病鏈夋潈闄愮櫥褰曞綋鍓嶇鎴凤紒");
+        SysSocialVo social;
+        if (TenantHelper.isEnable()) {
+            Optional<SysSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny();
+            if (opt.isEmpty()) {
+                throw new ServiceException("瀵逛笉璧凤紝浣犳病鏈夋潈闄愮櫥褰曞綋鍓嶇鎴凤紒");
+            }
+            social = opt.get();
+        } else {
+            social = list.get(0);
         }
-        SysSocialVo social = opt.get();
         // 鏌ユ壘鐢ㄦ埛
         SysUserVo user = loadUser(social.getTenantId(), social.getUserId());
 
@@ -114,9 +117,7 @@
 
     private SysUserVo loadUser(String tenantId, Long userId) {
         return TenantHelper.dynamic(tenantId, () -> {
-            SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
-                .select(SysUser::getUserName, SysUser::getStatus)
-                .eq(SysUser::getUserId, userId));
+            SysUserVo user = userMapper.selectVoById(userId);
             if (ObjectUtil.isNull(user)) {
                 log.info("鐧诲綍鐢ㄦ埛锛歿} 涓嶅瓨鍦�.", "");
                 throw new UserException("user.not.exists", "");
@@ -124,7 +125,7 @@
                 log.info("鐧诲綍鐢ㄦ埛锛歿} 宸茶鍋滅敤.", "");
                 throw new UserException("user.blocked", "");
             }
-            return userMapper.selectUserByUserName(user.getUserName());
+            return user;
         });
     }
 
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java
index e5aef1f..aa8be73 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/XcxAuthStrategy.java
@@ -12,6 +12,7 @@
 import org.dromara.common.json.utils.JsonUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.system.domain.SysClient;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.web.domain.vo.LoginVo;
 import org.dromara.web.service.IAuthStrategy;
@@ -19,7 +20,7 @@
 import org.springframework.stereotype.Service;
 
 /**
- * 閭欢璁よ瘉绛栫暐
+ * 灏忕▼搴忚璇佺瓥鐣�
  *
  * @author Michelle.Chung
  */
@@ -31,7 +32,7 @@
     private final SysLoginService loginService;
 
     @Override
-    public LoginVo login(String body, SysClient client) {
+    public LoginVo login(String body, SysClientVo client) {
         XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
         ValidatorUtils.validate(loginBody);
         // xcxCode 涓� 灏忕▼搴忚皟鐢� wx.login 鎺堟潈鍚庤幏鍙�
diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml
index 4aaea57..529a106 100644
--- a/ruoyi-admin/src/main/resources/application-dev.yml
+++ b/ruoyi-admin/src/main/resources/application-dev.yml
@@ -8,21 +8,19 @@
   username: ruoyi
   password: 123456
 
---- # powerjob 閰嶇疆
-powerjob:
-  worker:
-    # 濡備綍寮�鍚皟搴︿腑蹇冭鏌ョ湅鏂囨。鏁欑▼
-    enabled: false
-    # 闇�瑕佸厛鍦� powerjob 鐧诲綍椤垫墽琛屽簲鐢ㄦ敞鍐屽悗鎵嶈兘浣跨敤
-    app-name: ruoyi-worker
-    allow-lazy-connect-server: false
-    max-appended-wf-context-length: 4096
-    max-result-length: 4096
-    # 28080 绔彛 闅忕潃涓诲簲鐢ㄧ鍙i閫� 閬垮厤闆嗙兢鍐茬獊
-    port: 2${server.port}
-    protocol: http
-    server-address: 127.0.0.1:7700
-    store-strategy: disk
+--- # snail-job 閰嶇疆
+snail-job:
+  enabled: true
+  # 闇�瑕佸湪 SnailJob 鍚庡彴缁勭鐞嗗垱寤哄搴斿悕绉扮殑缁�,鐒跺悗鍒涘缓浠诲姟鐨勬椂鍊欓�夋嫨瀵瑰簲鐨勭粍,鎵嶈兘姝g‘鍒嗘淳浠诲姟
+  group-name: "ruoyi_group"
+  # SnailJob 鎺ュ叆楠岃瘉浠ょ墝 璇﹁ script/sql/snail_job.sql `sj_group_config` 琛�
+  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+  server:
+    host: 127.0.0.1
+    port: 1788
+  # 璇﹁ script/sql/snail_job.sql `sj_namespace` 琛�
+  namespace: ${spring.profiles.active}
+
 
 --- # 鏁版嵁婧愰厤缃�
 spring:
@@ -43,7 +41,7 @@
           driverClassName: com.mysql.cj.jdbc.Driver
           # jdbc 鎵�鏈夊弬鏁伴厤缃弬鑰� https://lionli.blog.csdn.net/article/details/122018562
           # rewriteBatchedStatements=true 鎵瑰鐞嗕紭鍖� 澶у箙鎻愬崌鎵归噺鎻掑叆鏇存柊鍒犻櫎鎬ц兘(瀵规暟鎹簱鏈夋�ц兘鎹熻�� 浣跨敤鎵归噺鎿嶄綔搴旇�冭檻鎬ц兘闂)
-          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username: root
           password: root
         # 浠庡簱鏁版嵁婧�
@@ -51,7 +49,7 @@
           lazy: true
           type: ${spring.datasource.type}
           driverClassName: com.mysql.cj.jdbc.Driver
-          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username:
           password:
 #        oracle:
@@ -149,36 +147,40 @@
   connectionTimeout: 0
 
 --- # sms 鐭俊 鏀寔 闃块噷浜� 鑵捐浜� 浜戠墖 绛夌瓑鍚勫紡鍚勬牱鐨勭煭淇℃湇鍔″晢
-# https://wind.kim/doc/start 鏂囨。鍦板潃 鍚勪釜鍘傚晢鍙悓鏃朵娇鐢�
+# https://sms4j.com/doc3/ 宸紓閰嶇疆鏂囨。鍦板潃 鏀寔鍗曞巶鍟嗗閰嶇疆锛屽彲浠ラ厤缃涓悓鏃朵娇鐢�
 sms:
-  # 闃块噷浜� dysmsapi.aliyuncs.com
-  alibaba:
-    #璇锋眰鍦板潃 榛樿涓� dysmsapi.aliyuncs.com 濡傛棤鐗规畩鏀瑰彉鍙互涓嶇敤璁剧疆
-    requestUrl: dysmsapi.aliyuncs.com
-    #闃块噷浜戠殑accessKey
-    accessKeyId: xxxxxxx
-    #闃块噷浜戠殑accessKeySecret
-    accessKeySecret: xxxxxxx
-    #鐭俊绛惧悕
-    signature: 娴嬭瘯
-  tencent:
-    #璇锋眰鍦板潃榛樿涓� sms.tencentcloudapi.com 濡傛棤鐗规畩鏀瑰彉鍙笉鐢ㄨ缃�
-    requestUrl: sms.tencentcloudapi.com
-    #鑵捐浜戠殑accessKey
-    accessKeyId: xxxxxxx
-    #鑵捐浜戠殑accessKeySecret
-    accessKeySecret: xxxxxxx
-    #鐭俊绛惧悕
-    signature: 娴嬭瘯
-    #鐭俊sdkAppId
-    sdkAppId: appid
-    #鍦板煙淇℃伅榛樿涓� ap-guangzhou 濡傛棤鐗规畩鏀瑰彉鍙笉鐢ㄨ缃�
-    territory: ap-guangzhou
+  # 閰嶇疆婧愮被鍨嬬敤浜庢爣瀹氶厤缃潵婧�(interface,yaml)
+  config-type: yaml
+  # 鐢ㄤ簬鏍囧畾yml涓殑閰嶇疆鏄惁寮�鍚煭淇℃嫤鎴紝鎺ュ彛閰嶇疆涓嶅彈姝ら檺鍒�
+  restricted: true
+  # 鐭俊鎷︽埅闄愬埗鍗曟墜鏈哄彿姣忓垎閽熸渶澶у彂閫侊紝鍙寮�鍚簡鎷︽埅鐨勯厤缃湁鏁�
+  minute-max: 1
+  # 鐭俊鎷︽埅闄愬埗鍗曟墜鏈哄彿姣忔棩鏈�澶у彂閫侀噺锛屽彧瀵瑰紑鍚簡鎷︽埅鐨勯厤缃湁鏁�
+  account-max: 30
+  # 浠ヤ笅閰嶇疆鏉ヨ嚜浜� org.dromara.sms4j.provider.config.BaseConfig绫讳腑
+  blends:
+    # 鍞竴ID 鐢ㄤ簬鍙戦�佺煭淇″鎵惧叿浣撻厤缃� 闅忎究瀹氫箟鍒敤涓枃鍗冲彲
+    # 鍙互鍚屾椂瀛樺湪涓や釜鐩稿悓鍘傚晢 渚嬪: ali1 ali2 涓や釜涓嶅悓鐨勯樋閲岀煭淇¤处鍙� 涔熷彲鐢ㄤ簬鍖哄垎绉熸埛
+    config1:
+      # 妗嗘灦瀹氫箟鐨勫巶鍟嗗悕绉版爣璇嗭紝鏍囧畾姝ら厤缃槸鍝釜鍘傚晢锛岃缁嗚鐪嬪巶鍟嗘爣璇嗕粙缁嶉儴鍒�
+      supplier: alibaba
+      # 鏈変簺绉颁负accessKey鏈変簺绉颁箣涓篴piKey锛屼篃鏈夌О涓簊dkKey鎴栬�卆ppId銆�
+      access-key-id: 鎮ㄧ殑accessKey
+      # 绉颁负accessSecret鏈変簺绉颁箣涓篴piSecret
+      access-key-secret: 鎮ㄧ殑accessKeySecret
+      signature: 鎮ㄧ殑鐭俊绛惧悕
+      sdk-app-id: 鎮ㄧ殑sdkAppId
+    config2:
+      # 鍘傚晢鏍囪瘑锛屾爣瀹氭閰嶇疆鏄摢涓巶鍟嗭紝璇︾粏璇风湅鍘傚晢鏍囪瘑浠嬬粛閮ㄥ垎
+      supplier: tencent
+      access-key-id: 鎮ㄧ殑accessKey
+      access-key-secret: 鎮ㄧ殑accessKeySecret
+      signature: 鎮ㄧ殑鐭俊绛惧悕
+      sdk-app-id: 鎮ㄧ殑sdkAppId
 
 
 --- # 涓夋柟鎺堟潈
 justauth:
-  enabled: true
   # 鍓嶇澶栫綉璁块棶鍦板潃
   address: http://localhost:80
   type:
@@ -189,6 +191,13 @@
       client-id: 876892492581044224
       client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
       redirect-uri: ${justauth.address}/social-callback?source=maxkey
+    topiam:
+      # topiam 鏈嶅姟鍣ㄥ湴鍧�
+      server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
+      client-id: 449c4*********937************759
+      client-secret: ac7***********1e0************28d
+      redirect-uri: ${justauth.address}/social-callback?source=topiam
+      scopes: [openid, email, phone, profile]
     qq:
       client-id: 10**********6
       client-secret: 1f7d08**********5b7**********29e
diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml
index 782a680..0613bd7 100644
--- a/ruoyi-admin/src/main/resources/application-prod.yml
+++ b/ruoyi-admin/src/main/resources/application-prod.yml
@@ -11,21 +11,18 @@
   username: ruoyi
   password: 123456
 
---- # powerjob 閰嶇疆
-powerjob:
-  worker:
-    # 濡備綍寮�鍚皟搴︿腑蹇冭鏌ョ湅鏂囨。鏁欑▼
-    enabled: false
-    # 闇�瑕佸厛鍦� powerjob 鐧诲綍椤垫墽琛屽簲鐢ㄦ敞鍐屽悗鎵嶈兘浣跨敤
-    app-name: ruoyi-worker
-    allow-lazy-connect-server: false
-    max-appended-wf-context-length: 4096
-    max-result-length: 4096
-    # 28080 绔彛 闅忕潃涓诲簲鐢ㄧ鍙i閫� 閬垮厤闆嗙兢鍐茬獊
-    port: 2${server.port}
-    protocol: http
-    server-address: 127.0.0.1:7700
-    store-strategy: disk
+--- # snail-job 閰嶇疆
+snail-job:
+  enabled: false
+  # 闇�瑕佸湪 SnailJob 鍚庡彴缁勭鐞嗗垱寤哄搴斿悕绉扮殑缁�,鐒跺悗鍒涘缓浠诲姟鐨勬椂鍊欓�夋嫨瀵瑰簲鐨勭粍,鎵嶈兘姝g‘鍒嗘淳浠诲姟
+  group-name: "ruoyi_group"
+  # SnailJob 鎺ュ叆楠岃瘉浠ょ墝 璇﹁ script/sql/snail_job.sql `sj_group_config` 琛�
+  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
+  server:
+    host: 127.0.0.1
+    port: 1788
+  # 璇﹁ script/sql/snail_job.sql `sj_namespace` 琛�
+  namespace: ${spring.profiles.active}
 
 --- # 鏁版嵁婧愰厤缃�
 spring:
@@ -46,7 +43,7 @@
           driverClassName: com.mysql.cj.jdbc.Driver
           # jdbc 鎵�鏈夊弬鏁伴厤缃弬鑰� https://lionli.blog.csdn.net/article/details/122018562
           # rewriteBatchedStatements=true 鎵瑰鐞嗕紭鍖� 澶у箙鎻愬崌鎵归噺鎻掑叆鏇存柊鍒犻櫎鎬ц兘(瀵规暟鎹簱鏈夋�ц兘鎹熻�� 浣跨敤鎵归噺鎿嶄綔搴旇�冭檻鎬ц兘闂)
-          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username: root
           password: root
         # 浠庡簱鏁版嵁婧�
@@ -54,7 +51,7 @@
           lazy: true
           type: ${spring.datasource.type}
           driverClassName: com.mysql.cj.jdbc.Driver
-          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username:
           password:
 #        oracle:
@@ -152,35 +149,39 @@
   connectionTimeout: 0
 
 --- # sms 鐭俊 鏀寔 闃块噷浜� 鑵捐浜� 浜戠墖 绛夌瓑鍚勫紡鍚勬牱鐨勭煭淇℃湇鍔″晢
-# https://wind.kim/doc/start 鏂囨。鍦板潃 鍚勪釜鍘傚晢鍙悓鏃朵娇鐢�
+# https://sms4j.com/doc3/ 宸紓閰嶇疆鏂囨。鍦板潃 鏀寔鍗曞巶鍟嗗閰嶇疆锛屽彲浠ラ厤缃涓悓鏃朵娇鐢�
 sms:
-  # 闃块噷浜� dysmsapi.aliyuncs.com
-  alibaba:
-    #璇锋眰鍦板潃 榛樿涓� dysmsapi.aliyuncs.com 濡傛棤鐗规畩鏀瑰彉鍙互涓嶇敤璁剧疆
-    requestUrl: dysmsapi.aliyuncs.com
-    #闃块噷浜戠殑accessKey
-    accessKeyId: xxxxxxx
-    #闃块噷浜戠殑accessKeySecret
-    accessKeySecret: xxxxxxx
-    #鐭俊绛惧悕
-    signature: 娴嬭瘯
-  tencent:
-    #璇锋眰鍦板潃榛樿涓� sms.tencentcloudapi.com 濡傛棤鐗规畩鏀瑰彉鍙笉鐢ㄨ缃�
-    requestUrl: sms.tencentcloudapi.com
-    #鑵捐浜戠殑accessKey
-    accessKeyId: xxxxxxx
-    #鑵捐浜戠殑accessKeySecret
-    accessKeySecret: xxxxxxx
-    #鐭俊绛惧悕
-    signature: 娴嬭瘯
-    #鐭俊sdkAppId
-    sdkAppId: appid
-    #鍦板煙淇℃伅榛樿涓� ap-guangzhou 濡傛棤鐗规畩鏀瑰彉鍙笉鐢ㄨ缃�
-    territory: ap-guangzhou
+  # 閰嶇疆婧愮被鍨嬬敤浜庢爣瀹氶厤缃潵婧�(interface,yaml)
+  config-type: yaml
+  # 鐢ㄤ簬鏍囧畾yml涓殑閰嶇疆鏄惁寮�鍚煭淇℃嫤鎴紝鎺ュ彛閰嶇疆涓嶅彈姝ら檺鍒�
+  restricted: true
+  # 鐭俊鎷︽埅闄愬埗鍗曟墜鏈哄彿姣忓垎閽熸渶澶у彂閫侊紝鍙寮�鍚簡鎷︽埅鐨勯厤缃湁鏁�
+  minute-max: 1
+  # 鐭俊鎷︽埅闄愬埗鍗曟墜鏈哄彿姣忔棩鏈�澶у彂閫侀噺锛屽彧瀵瑰紑鍚簡鎷︽埅鐨勯厤缃湁鏁�
+  account-max: 30
+  # 浠ヤ笅閰嶇疆鏉ヨ嚜浜� org.dromara.sms4j.provider.config.BaseConfig绫讳腑
+  blends:
+    # 鍞竴ID 鐢ㄤ簬鍙戦�佺煭淇″鎵惧叿浣撻厤缃� 闅忎究瀹氫箟鍒敤涓枃鍗冲彲
+    # 鍙互鍚屾椂瀛樺湪涓や釜鐩稿悓鍘傚晢 渚嬪: ali1 ali2 涓や釜涓嶅悓鐨勯樋閲岀煭淇¤处鍙� 涔熷彲鐢ㄤ簬鍖哄垎绉熸埛
+    config1:
+      # 妗嗘灦瀹氫箟鐨勫巶鍟嗗悕绉版爣璇嗭紝鏍囧畾姝ら厤缃槸鍝釜鍘傚晢锛岃缁嗚鐪嬪巶鍟嗘爣璇嗕粙缁嶉儴鍒�
+      supplier: alibaba
+      # 鏈変簺绉颁负accessKey鏈変簺绉颁箣涓篴piKey锛屼篃鏈夌О涓簊dkKey鎴栬�卆ppId銆�
+      access-key-id: 鎮ㄧ殑accessKey
+      # 绉颁负accessSecret鏈変簺绉颁箣涓篴piSecret
+      access-key-secret: 鎮ㄧ殑accessKeySecret
+      signature: 鎮ㄧ殑鐭俊绛惧悕
+      sdk-app-id: 鎮ㄧ殑sdkAppId
+    config2:
+      # 鍘傚晢鏍囪瘑锛屾爣瀹氭閰嶇疆鏄摢涓巶鍟嗭紝璇︾粏璇风湅鍘傚晢鏍囪瘑浠嬬粛閮ㄥ垎
+      supplier: tencent
+      access-key-id: 鎮ㄧ殑accessKey
+      access-key-secret: 鎮ㄧ殑accessKeySecret
+      signature: 鎮ㄧ殑鐭俊绛惧悕
+      sdk-app-id: 鎮ㄧ殑sdkAppId
 
 --- # 涓夋柟鎺堟潈
 justauth:
-  enabled: true
   # 鍓嶇澶栫綉璁块棶鍦板潃
   address: http://localhost:80
   type:
@@ -191,6 +192,13 @@
       client-id: 876892492581044224
       client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
       redirect-uri: ${justauth.address}/social-callback?source=maxkey
+    topiam:
+      # topiam 鏈嶅姟鍣ㄥ湴鍧�
+      server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
+      client-id: 449c4*********937************759
+      client-secret: ac7***********1e0************28d
+      redirect-uri: ${justauth.address}/social-callback?source=topiam
+      scopes: [ openid, email, phone, profile ]
     qq:
       client-id: 10**********6
       client-secret: 1f7d08**********5b7**********29e
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 3f54f64..27de286 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -5,7 +5,7 @@
   # 鐗堟湰
   version: ${revision}
   # 鐗堟潈骞翠唤
-  copyrightYear: 2023
+  copyrightYear: 2024
 
 captcha:
   enable: true
@@ -46,7 +46,7 @@
   level:
     org.dromara: @logging.level@
     org.springframework: warn
-    tech.powerjob.worker.background: warn
+    org.mybatis.spring.mapper: error
   config: classpath:logback-plus.xml
 
 # 鐢ㄦ埛閰嶇疆
@@ -61,6 +61,10 @@
 spring:
   application:
     name: ${ruoyi.name}
+  threads:
+    # 寮�鍚櫄鎷熺嚎绋� 浠卝dk21鍙敤
+    virtual:
+      enabled: false
   # 璧勬簮淇℃伅
   messages:
     # 鍥介檯鍖栬祫婧愭枃浠惰矾寰�
@@ -75,6 +79,8 @@
       # 璁剧疆鎬讳笂浼犵殑鏂囦欢澶у皬
       max-request-size: 20MB
   mvc:
+    # 璁剧疆闈欐�佽祫婧愯矾寰� 闃叉鎵�鏈夎姹傞兘鍘绘煡闈欐�佽祫婧�
+    static-path-pattern: /static/**
     format:
       date-time: yyyy-MM-dd HH:mm:ss
   jackson:
@@ -138,8 +144,7 @@
 # MyBatisPlus閰嶇疆
 # https://baomidou.com/config/
 mybatis-plus:
-  # 涓嶆敮鎸佸鍖�, 濡傛湁闇�瑕佸彲鍦ㄦ敞瑙i厤缃� 鎴� 鎻愬崌鎵寘绛夌骇
-  # 渚嬪 com.**.**.mapper
+  # 澶氬寘鍚嶄娇鐢� 渚嬪 org.dromara.**.mapper,org.xxx.**.mapper
   mapperPackage: org.dromara.**.mapper
   # 瀵瑰簲鐨� XML 鏂囦欢浣嶇疆
   mapperLocations: classpath*:mapper/**/*Mapper.xml
@@ -226,6 +231,7 @@
   urlPatterns: /system/*,/monitor/*,/tool/*
 
 # 鍏ㄥ眬绾跨▼姹犵浉鍏抽厤缃�
+# 濡備娇鐢↗DK21璇风洿鎺ヤ娇鐢ㄨ櫄鎷熺嚎绋� 涓嶈寮�鍚閰嶇疆
 thread-pool:
   # 鏄惁寮�鍚嚎绋嬫睜
   enabled: false
@@ -261,3 +267,21 @@
   path: /resource/websocket
   # 璁剧疆璁块棶婧愬湴鍧�
   allowedOrigins: '*'
+
+--- #flowable閰嶇疆
+flowable:
+  async-executor-activate: false #鍏抽棴瀹氭椂浠诲姟JOB
+  #  灏哾atabaseSchemaUpdate璁剧疆涓簍rue銆傚綋Flowable鍙戠幇搴撲笌鏁版嵁搴撹〃缁撴瀯涓嶄竴鑷存椂锛屼細鑷姩灏嗘暟鎹簱琛ㄧ粨鏋勫崌绾ц嚦鏂扮増鏈��
+  database-schema-update: true
+  activity-font-name: 瀹嬩綋
+  label-font-name: 瀹嬩綋
+  annotation-font-name: 瀹嬩綋
+  # 鍏抽棴鍚勪釜妯″潡鐢熸垚琛紝鐩墠鍙娇鐢ㄥ伐浣滄祦鍩虹琛�
+  idm:
+    enabled: false
+  cmmn:
+    enabled: false
+  dmn:
+    enabled: false
+  app:
+    enabled: false
diff --git a/ruoyi-admin/src/main/resources/ip2region.xdb b/ruoyi-admin/src/main/resources/ip2region.xdb
index 31f96a1..7052c05 100644
--- a/ruoyi-admin/src/main/resources/ip2region.xdb
+++ b/ruoyi-admin/src/main/resources/ip2region.xdb
Binary files differ
diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml
index 004b035..f9af9de 100644
--- a/ruoyi-common/ruoyi-common-bom/pom.xml
+++ b/ruoyi-common/ruoyi-common-bom/pom.xml
@@ -14,7 +14,7 @@
     </description>
 
     <properties>
-        <revision>5.1.2</revision>
+        <revision>5.2.0-BETA</revision>
     </properties>
 
     <dependencyManagement>
diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml
index ad37e90..5925c9b 100644
--- a/ruoyi-common/ruoyi-common-core/pom.xml
+++ b/ruoyi-common/ruoyi-common-core/pom.xml
@@ -94,6 +94,11 @@
             <artifactId>ip2region</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>transmittable-thread-local</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java
index 07500ba..d120087 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java
@@ -2,6 +2,7 @@
 
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableAsync;
 
 /**
  * 绋嬪簭娉ㄨВ閰嶇疆
@@ -11,6 +12,7 @@
 @AutoConfiguration
 // 琛ㄧず閫氳繃aop妗嗘灦鏆撮湶璇ヤ唬鐞嗗璞�,AopContext鑳藉璁块棶
 @EnableAspectJAutoProxy(exposeProxy = true)
+@EnableAsync(proxyTargetClass = true)
 public class ApplicationConfig {
 
 }
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java
index 9a32afe..cd01e33 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java
@@ -5,18 +5,19 @@
 import org.dromara.common.core.utils.SpringUtils;
 import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.core.task.VirtualThreadTaskExecutor;
 import org.springframework.scheduling.annotation.AsyncConfigurer;
-import org.springframework.scheduling.annotation.EnableAsync;
 
 import java.util.Arrays;
 import java.util.concurrent.Executor;
 
 /**
  * 寮傛閰嶇疆
+ * <p>
+ * 濡傛灉鏈娇鐢ㄨ櫄鎷熺嚎绋嬪垯鐢熸晥
  *
  * @author Lion Li
  */
-@EnableAsync(proxyTargetClass = true)
 @AutoConfiguration
 public class AsyncConfig implements AsyncConfigurer {
 
@@ -25,6 +26,9 @@
      */
     @Override
     public Executor getAsyncExecutor() {
+        if(SpringUtils.isVirtual()) {
+            return new VirtualThreadTaskExecutor("async-");
+        }
         return SpringUtils.getBean("scheduledExecutorService");
     }
 
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
index e59277a..28ba177 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java
@@ -36,6 +36,11 @@
     String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
 
     /**
+     * 瀹㈡埛绔�
+     */
+    String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
+
+    /**
      * 鐢ㄦ埛璐︽埛
      */
     String SYS_USER_NAME = "sys_user_name#30d";
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java
new file mode 100644
index 0000000..77eed8c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java
@@ -0,0 +1,54 @@
+package org.dromara.common.core.constant;
+
+import cn.hutool.core.lang.RegexPool;
+
+/**
+ * 甯哥敤姝e垯琛ㄨ揪寮忓瓧绗︿覆
+ * <p>
+ * 甯哥敤姝e垯琛ㄨ揪寮忛泦鍚堬紝鏇村姝e垯瑙�: https://any86.github.io/any-rule/
+ *
+ * @author Feng
+ */
+public interface RegexConstants extends RegexPool {
+
+    /**
+     * 瀛楀吀绫诲瀷蹇呴』浠ュ瓧姣嶅紑澶达紝涓斿彧鑳戒负锛堝皬鍐欏瓧姣嶏紝鏁板瓧锛屼笅婊戠嚎锛�
+     */
+    String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
+
+    /**
+     * 鏉冮檺鏍囪瘑蹇呴』绗﹀悎 tool:build:list 鏍煎紡锛屾垨鑰呯┖瀛楃涓�
+     */
+    String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$";
+
+    /**
+     * 韬唤璇佸彿鐮侊紙鍚�6浣嶏級
+     */
+    String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
+
+    /**
+     * QQ鍙风爜
+     */
+    String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$";
+
+    /**
+     * 閭斂缂栫爜
+     */
+    String POSTAL_CODE = "^[1-9]\\d{5}$";
+
+    /**
+     * 娉ㄥ唽璐﹀彿
+     */
+    String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
+
+    /**
+     * 瀵嗙爜锛氬寘鍚嚦灏�8涓瓧绗︼紝鍖呮嫭澶у啓瀛楁瘝銆佸皬鍐欏瓧姣嶃�佹暟瀛楀拰鐗规畩瀛楃
+     */
+    String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
+
+    /**
+     * 閫氱敤鐘舵�侊紙0琛ㄧず姝e父锛�1琛ㄧず鍋滅敤锛�
+     */
+    String STATUS = "^[01]$";
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java
new file mode 100644
index 0000000..463821c
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/OssDTO.java
@@ -0,0 +1,46 @@
+package org.dromara.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * OSS瀵硅薄
+ *
+ * @author Lion Li
+ */
+@Data
+@NoArgsConstructor
+public class OssDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 瀵硅薄瀛樺偍涓婚敭
+     */
+    private Long ossId;
+
+    /**
+     * 鏂囦欢鍚�
+     */
+    private String fileName;
+
+    /**
+     * 鍘熷悕
+     */
+    private String originalName;
+
+    /**
+     * 鏂囦欢鍚庣紑鍚�
+     */
+    private String fileSuffix;
+
+    /**
+     * URL鍦板潃
+     */
+    private String url;
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java
index 03d6166..aea8e7a 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/RoleDTO.java
@@ -3,6 +3,7 @@
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.io.Serial;
 import java.io.Serializable;
 
 /**
@@ -15,6 +16,9 @@
 @NoArgsConstructor
 public class RoleDTO implements Serializable {
 
+    @Serial
+    private static final long serialVersionUID = 1L;
+
     /**
      * 瑙掕壊ID
      */
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java
new file mode 100644
index 0000000..cb5def9
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/UserDTO.java
@@ -0,0 +1,73 @@
+package org.dromara.common.core.domain.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 鐢ㄦ埛
+ *
+ * @author Michelle.Chung
+ */
+@Data
+@NoArgsConstructor
+public class UserDTO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 鐢ㄦ埛ID
+     */
+    private Long userId;
+
+    /**
+     * 閮ㄩ棬ID
+     */
+    private Long deptId;
+
+    /**
+     * 鐢ㄦ埛璐﹀彿
+     */
+    private String userName;
+
+    /**
+     * 鐢ㄦ埛鏄电О
+     */
+    private String nickName;
+
+    /**
+     * 鐢ㄦ埛绫诲瀷锛坰ys_user绯荤粺鐢ㄦ埛锛�
+     */
+    private String userType;
+
+    /**
+     * 鐢ㄦ埛閭
+     */
+    private String email;
+
+    /**
+     * 鎵嬫満鍙风爜
+     */
+    private String phonenumber;
+
+    /**
+     * 鐢ㄦ埛鎬у埆锛�0鐢� 1濂� 2鏈煡锛�
+     */
+    private String sex;
+
+    /**
+     * 甯愬彿鐘舵�侊紙0姝e父 1鍋滅敤锛�
+     */
+    private String status;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    private Date createTime;
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java
index e267124..c723e76 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/model/LoginUser.java
@@ -14,7 +14,6 @@
  *
  * @author Lion Li
  */
-
 @Data
 @NoArgsConstructor
 public class LoginUser implements Serializable {
@@ -38,6 +37,11 @@
     private Long deptId;
 
     /**
+     * 閮ㄩ棬绫诲埆缂栫爜
+     */
+    private String deptCategory;
+
+    /**
      * 閮ㄩ棬鍚�
      */
     private String deptName;
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java
index 4fb097a..e9dc6ec 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java
@@ -1,9 +1,6 @@
 package org.dromara.common.core.exception;
 
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.NoArgsConstructor;
+import lombok.*;
 
 import java.io.Serial;
 
@@ -45,17 +42,9 @@
         this.code = code;
     }
 
-    public String getDetailMessage() {
-        return detailMessage;
-    }
-
     @Override
     public String getMessage() {
         return message;
-    }
-
-    public Integer getCode() {
-        return code;
     }
 
     public ServiceException setMessage(String message) {
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java
new file mode 100644
index 0000000..fd907d2
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java
@@ -0,0 +1,52 @@
+package org.dromara.common.core.factory;
+
+import cn.hutool.core.lang.PatternPool;
+import org.dromara.common.core.constant.RegexConstants;
+
+import java.util.regex.Pattern;
+
+/**
+ * 姝e垯琛ㄨ揪寮忔ā寮忔睜宸ュ巶
+ * <p>鍒濆鍖栫殑鏃跺�欏皢姝e垯琛ㄨ揪寮忓姞鍏ョ紦瀛樻睜褰撲腑</p>
+ * <p>鎻愰珮姝e垯琛ㄨ揪寮忕殑鎬ц兘锛岄伩鍏嶉噸澶嶇紪璇戠浉鍚岀殑姝e垯琛ㄨ揪寮�</p>
+ *
+ * @author 21001
+ */
+public class RegexPatternPoolFactory extends PatternPool {
+
+    /**
+     * 瀛楀吀绫诲瀷蹇呴』浠ュ瓧姣嶅紑澶达紝涓斿彧鑳戒负锛堝皬鍐欏瓧姣嶏紝鏁板瓧锛屼笅婊戠嚎锛�
+     */
+    public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE);
+
+    /**
+     * 韬唤璇佸彿鐮侊紙鍚�6浣嶏級
+     */
+    public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6);
+
+    /**
+     * QQ鍙风爜
+     */
+    public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER);
+
+    /**
+     * 閭斂缂栫爜
+     */
+    public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE);
+
+    /**
+     * 娉ㄥ唽璐﹀彿
+     */
+    public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT);
+
+    /**
+     * 瀵嗙爜锛氬寘鍚嚦灏�8涓瓧绗︼紝鍖呮嫭澶у啓瀛楁瘝銆佸皬鍐欏瓧姣嶃�佹暟瀛楀拰鐗规畩瀛楃
+     */
+    public static final Pattern PASSWORD = get(RegexConstants.PASSWORD);
+
+    /**
+     * 閫氱敤鐘舵�侊紙0琛ㄧず姝e父锛�1琛ㄧず鍋滅敤锛�
+     */
+    public static final Pattern STATUS = get(RegexConstants.STATUS);
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java
index 43742b3..1a52de0 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/OssService.java
@@ -1,5 +1,9 @@
 package org.dromara.common.core.service;
 
+import org.dromara.common.core.domain.dto.OssDTO;
+
+import java.util.List;
+
 /**
  * 閫氱敤 OSS鏈嶅姟
  *
@@ -15,4 +19,11 @@
      */
     String selectUrlByIds(String ossIds);
 
+    /**
+     * 閫氳繃ossId鏌ヨ鍒楄〃
+     *
+     * @param ossIds ossId涓查�楀彿鍒嗛殧
+     * @return 鍒楄〃
+     */
+    List<OssDTO> selectByIds(String ossIds);
 }
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
index d6b312a..0f2878d 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/UserService.java
@@ -1,5 +1,9 @@
 package org.dromara.common.core.service;
 
+import org.dromara.common.core.domain.dto.UserDTO;
+
+import java.util.List;
+
 /**
  * 閫氱敤 鐢ㄦ埛鏈嶅姟
  *
@@ -19,8 +23,47 @@
      * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛璐︽埛
      *
      * @param userId 鐢ㄦ埛ID
-     * @return 鐢ㄦ埛璐︽埛
+     * @return 鐢ㄦ埛鍚嶇О
      */
     String selectNicknameById(Long userId);
 
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛璐︽埛
+     *
+     * @param userIds 鐢ㄦ埛ID 澶氫釜鐢ㄩ�楀彿闅斿紑
+     * @return 鐢ㄦ埛鍚嶇О
+     */
+    String selectNicknameByIds(String userIds);
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛鎵嬫満鍙�
+     *
+     * @param userId 鐢ㄦ埛id
+     * @return 鐢ㄦ埛鎵嬫満鍙�
+     */
+    String selectPhonenumberById(Long userId);
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛閭
+     *
+     * @param userId 鐢ㄦ埛id
+     * @return 鐢ㄦ埛閭
+     */
+    String selectEmailById(Long userId);
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛鍒楄〃
+     *
+     * @param userIds 鐢ㄦ埛ids
+     * @return 鐢ㄦ埛鍒楄〃
+     */
+    List<UserDTO> selectListByIds(List<Long> userIds);
+
+    /**
+     * 閫氳繃瑙掕壊ID鏌ヨ鐢ㄦ埛ID
+     *
+     * @param roleIds 瑙掕壊ids
+     * @return 鐢ㄦ埛ids
+     */
+    List<Long> selectUserIdsByRoleIds(List<Long> roleIds);
 }
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java
index ab50539..e58c394 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java
@@ -3,7 +3,9 @@
 import cn.hutool.extra.spring.SpringUtil;
 import org.springframework.aop.framework.AopContext;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.boot.autoconfigure.thread.Threading;
 import org.springframework.context.ApplicationContext;
+import org.springframework.core.env.Environment;
 import org.springframework.stereotype.Component;
 
 /**
@@ -59,4 +61,8 @@
         return getApplicationContext();
     }
 
+    public static boolean isVirtual() {
+        return Threading.VIRTUAL.isActive(getBean(Environment.class));
+    }
+
 }
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java
index 5e4db50..dd6ebb1 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java
@@ -22,6 +22,8 @@
 
     public static final String SEPARATOR = ",";
 
+    public static final String SLASH = "/";
+
     /**
      * 鑾峰彇鍙傛暟涓嶄负绌哄��
      *
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java
index f94f916..06b8fd6 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ValidatorUtils.java
@@ -1,11 +1,11 @@
 package org.dromara.common.core.utils;
 
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-
 import jakarta.validation.ConstraintViolation;
 import jakarta.validation.ConstraintViolationException;
 import jakarta.validation.Validator;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
 import java.util.Set;
 
 /**
@@ -18,6 +18,13 @@
 
     private static final Validator VALID = SpringUtils.getBean(Validator.class);
 
+    /**
+     * 瀵圭粰瀹氬璞¤繘琛屽弬鏁版牎楠岋紝骞舵牴鎹寚瀹氱殑鏍¢獙缁勮繘琛屾牎楠�
+     *
+     * @param object 瑕佽繘琛屾牎楠岀殑瀵硅薄
+     * @param groups 鏍¢獙缁�
+     * @throws ConstraintViolationException 濡傛灉鏍¢獙涓嶉�氳繃锛屽垯鎶涘嚭鍙傛暟鏍¢獙寮傚父
+     */
     public static <T> void validate(T object, Class<?>... groups) {
         Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
         if (!validate.isEmpty()) {
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java
new file mode 100644
index 0000000..b8b12d4
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java
@@ -0,0 +1,30 @@
+package org.dromara.common.core.utils.regex;
+
+
+import cn.hutool.core.util.ReUtil;
+import org.dromara.common.core.constant.RegexConstants;
+
+/**
+ * 姝e垯鐩稿叧宸ュ叿绫�
+ *
+ * @author Feng
+ */
+public final class RegexUtils extends ReUtil {
+
+    /**
+     * 浠庤緭鍏ュ瓧绗︿覆涓彁鍙栧尮閰嶇殑閮ㄥ垎锛屽鏋滄病鏈夊尮閰嶅垯杩斿洖榛樿鍊�
+     *
+     * @param input        瑕佹彁鍙栫殑杈撳叆瀛楃涓�
+     * @param regex        鐢ㄤ簬鍖归厤鐨勬鍒欒〃杈惧紡锛屽彲浠ヤ娇鐢� {@link RegexConstants} 涓畾涔夌殑甯搁噺
+     * @param defaultInput 濡傛灉娌℃湁鍖归厤鏃惰繑鍥炵殑榛樿鍊�
+     * @return 濡傛灉鎵惧埌鍖归厤鐨勯儴鍒嗭紝鍒欒繑鍥炲尮閰嶇殑閮ㄥ垎锛屽惁鍒欒繑鍥為粯璁ゅ��
+     */
+    public static String extractFromString(String input, String regex, String defaultInput) {
+        try {
+            return ReUtil.get(regex, input, 1);
+        } catch (Exception e) {
+            return defaultInput;
+        }
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java
new file mode 100644
index 0000000..c0dda20
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java
@@ -0,0 +1,105 @@
+package org.dromara.common.core.utils.regex;
+
+import cn.hutool.core.exceptions.ValidateException;
+import cn.hutool.core.lang.Validator;
+import org.dromara.common.core.factory.RegexPatternPoolFactory;
+
+import java.util.regex.Pattern;
+
+/**
+ * 姝e垯瀛楁鏍¢獙鍣�
+ * 涓昏楠岃瘉瀛楁闈炵┖銆佹槸鍚︿负婊¤冻鎸囧畾鏍煎紡绛�
+ *
+ * @author Feng
+ */
+public class RegexValidator extends Validator {
+
+    /**
+     * 瀛楀吀绫诲瀷蹇呴』浠ュ瓧姣嶅紑澶达紝涓斿彧鑳戒负锛堝皬鍐欏瓧姣嶏紝鏁板瓧锛屼笅婊戠嚎锛�
+     */
+    public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE;
+
+    /**
+     * 韬唤璇佸彿鐮侊紙鍚�6浣嶏級
+     */
+    public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6;
+
+    /**
+     * QQ鍙风爜
+     */
+    public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER;
+
+    /**
+     * 閭斂缂栫爜
+     */
+    public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE;
+
+    /**
+     * 娉ㄥ唽璐﹀彿
+     */
+    public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT;
+
+    /**
+     * 瀵嗙爜锛氬寘鍚嚦灏�8涓瓧绗︼紝鍖呮嫭澶у啓瀛楁瘝銆佸皬鍐欏瓧姣嶃�佹暟瀛楀拰鐗规畩瀛楃
+     */
+    public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD;
+
+    /**
+     * 閫氱敤鐘舵�侊紙0琛ㄧず姝e父锛�1琛ㄧず鍋滅敤锛�
+     */
+    public static final Pattern STATUS = RegexPatternPoolFactory.STATUS;
+
+
+    /**
+     * 妫�鏌ヨ緭鍏ョ殑璐﹀彿鏄惁鍖归厤棰勫畾涔夌殑瑙勫垯
+     *
+     * @param value 瑕侀獙璇佺殑璐﹀彿
+     * @return 濡傛灉璐﹀彿绗﹀悎瑙勫垯锛岃繑鍥� true锛涘惁鍒欙紝杩斿洖 false銆�
+     */
+    public static boolean isAccount(CharSequence value) {
+        return isMatchRegex(ACCOUNT, value);
+    }
+
+    /**
+     * 楠岃瘉杈撳叆鐨勮处鍙锋槸鍚︾鍚堣鍒欙紝濡傛灉涓嶇鍚堬紝鍒欐姏鍑� ValidateException 寮傚父
+     *
+     * @param value    瑕侀獙璇佺殑璐﹀彿
+     * @param errorMsg 楠岃瘉澶辫触鏃舵姏鍑虹殑寮傚父娑堟伅
+     * @param <T>      CharSequence 鐨勫瓙绫诲瀷
+     * @return 濡傛灉楠岃瘉閫氳繃锛岃繑鍥炶緭鍏ョ殑璐﹀彿
+     * @throws ValidateException 濡傛灉楠岃瘉澶辫触
+     */
+    public static <T extends CharSequence> T validateAccount(T value, String errorMsg) throws ValidateException {
+        if (!isAccount(value)) {
+            throw new ValidateException(errorMsg);
+        }
+        return value;
+    }
+
+    /**
+     * 妫�鏌ヨ緭鍏ョ殑鐘舵�佹槸鍚﹀尮閰嶉瀹氫箟鐨勮鍒�
+     *
+     * @param value 瑕侀獙璇佺殑鐘舵��
+     * @return 濡傛灉鐘舵�佺鍚堣鍒欙紝杩斿洖 true锛涘惁鍒欙紝杩斿洖 false銆�
+     */
+    public static boolean isStatus(CharSequence value) {
+        return isMatchRegex(STATUS, value);
+    }
+
+    /**
+     * 楠岃瘉杈撳叆鐨勭姸鎬佹槸鍚︾鍚堣鍒欙紝濡傛灉涓嶇鍚堬紝鍒欐姏鍑� ValidateException 寮傚父
+     *
+     * @param value    瑕侀獙璇佺殑鐘舵��
+     * @param errorMsg 楠岃瘉澶辫触鏃舵姏鍑虹殑寮傚父娑堟伅
+     * @param <T>      CharSequence 鐨勫瓙绫诲瀷
+     * @return 濡傛灉楠岃瘉閫氳繃锛岃繑鍥炶緭鍏ョ殑鐘舵��
+     * @throws ValidateException 濡傛灉楠岃瘉澶辫触
+     */
+    public static <T extends CharSequence> T validateStatus(T value, String errorMsg) throws ValidateException {
+        if (!isStatus(value)) {
+            throw new ValidateException(errorMsg);
+        }
+        return value;
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-encrypt/pom.xml b/ruoyi-common/ruoyi-common-encrypt/pom.xml
index df3222b..ed4910e 100644
--- a/ruoyi-common/ruoyi-common-encrypt/pom.xml
+++ b/ruoyi-common/ruoyi-common-encrypt/pom.xml
@@ -23,11 +23,6 @@
         </dependency>
 
         <dependency>
-            <groupId>org.mybatis.spring.boot</groupId>
-            <artifactId>mybatis-spring-boot-starter</artifactId>
-        </dependency>
-
-        <dependency>
             <groupId>org.bouncycastle</groupId>
             <artifactId>bcprov-jdk15to18</artifactId>
         </dependency>
@@ -42,6 +37,18 @@
             <artifactId>spring-webmvc</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+            <optional>true</optional>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.mybatis</groupId>
+                    <artifactId>mybatis-spring</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
     </dependencies>
 
 </project>
diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java
index e988a3a..fbc4e52 100644
--- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java
+++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java
@@ -1,5 +1,8 @@
 package org.dromara.common.encrypt.config;
 
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
+import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.encrypt.core.EncryptorManager;
 import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor;
 import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor;
@@ -16,17 +19,18 @@
  * @author 鑰侀┈
  * @version 4.6.0
  */
-@AutoConfiguration
+@AutoConfiguration(after = MybatisPlusAutoConfiguration.class)
 @EnableConfigurationProperties(EncryptorProperties.class)
 @ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
+@Slf4j
 public class EncryptorAutoConfiguration {
 
     @Autowired
     private EncryptorProperties properties;
 
     @Bean
-    public EncryptorManager encryptorManager() {
-        return new EncryptorManager();
+    public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) {
+        return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage());
     }
 
     @Bean
@@ -38,4 +42,8 @@
     public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
         return new MybatisDecryptInterceptor(encryptorManager, properties);
     }
+
 }
+
+
+
diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java
index 498b4b8..a6d3cf9 100644
--- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java
+++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java
@@ -1,14 +1,23 @@
 package org.dromara.common.encrypt.core;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ReflectUtil;
+import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.io.Resources;
+import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.encrypt.annotation.EncryptField;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.type.ClassMetadata;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.util.ClassUtils;
 
 import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
@@ -19,6 +28,7 @@
  * @version 4.6.0
  */
 @Slf4j
+@NoArgsConstructor
 public class EncryptorManager {
 
     /**
@@ -32,24 +42,23 @@
     Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
 
     /**
+     * 鏋勯�犳柟娉曚紶鍏ョ被鍔犲瘑瀛楁缂撳瓨
+     *
+     * @param typeAliasesPackage 瀹炰綋绫诲寘
+     */
+    public EncryptorManager(String typeAliasesPackage) {
+        scanEncryptClasses(typeAliasesPackage);
+    }
+
+
+    /**
      * 鑾峰彇绫诲姞瀵嗗瓧娈电紦瀛�
      */
     public Set<Field> getFieldCache(Class<?> sourceClazz) {
-        return fieldCache.computeIfAbsent(sourceClazz, clazz -> {
-            Set<Field> fieldSet = new HashSet<>();
-            while (clazz != null) {
-                Field[] fields = clazz.getDeclaredFields();
-                fieldSet.addAll(Arrays.asList(fields));
-                clazz = clazz.getSuperclass();
-            }
-            fieldSet = fieldSet.stream().filter(field ->
-                    field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
-                .collect(Collectors.toSet());
-            for (Field field : fieldSet) {
-                field.setAccessible(true);
-            }
-            return fieldSet;
-        });
+        if (ObjectUtil.isNotNull(fieldCache)) {
+            return fieldCache.get(sourceClazz);
+        }
+        return null;
     }
 
     /**
@@ -97,4 +106,53 @@
         return encryptor.decrypt(value);
     }
 
+    /**
+     * 閫氳繃 typeAliasesPackage 璁剧疆鐨勬壂鎻忓寘 鎵弿缂撳瓨瀹炰綋
+     */
+    private void scanEncryptClasses(String typeAliasesPackage) {
+        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
+        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
+        String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
+        String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
+        try {
+            for (String packagePattern : packagePatternArray) {
+                String path = ClassUtils.convertClassNameToResourcePath(packagePattern);
+                Resource[] resources = resolver.getResources(classpath + path + "/*.class");
+                for (Resource resource : resources) {
+                    ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata();
+                    Class<?> clazz = Resources.classForName(classMetadata.getClassName());
+                    Set<Field> encryptFieldSet = getEncryptFieldSetFromClazz(clazz);
+                    if (CollUtil.isNotEmpty(encryptFieldSet)) {
+                        fieldCache.put(clazz, encryptFieldSet);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("鍒濆鍖栨暟鎹畨鍏ㄧ紦瀛樻椂鍑洪敊:{}", e.getMessage());
+        }
+    }
+
+    /**
+     * 鑾峰緱涓�涓被鐨勫姞瀵嗗瓧娈甸泦鍚�
+     */
+    private Set<Field> getEncryptFieldSetFromClazz(Class<?> clazz) {
+        Set<Field> fieldSet = new HashSet<>();
+        // 鍒ゆ柇clazz濡傛灉鏄帴鍙�,鍐呴儴绫�,鍖垮悕绫诲氨鐩存帴杩斿洖
+        if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
+            return fieldSet;
+        }
+        while (clazz != null) {
+            Field[] fields = clazz.getDeclaredFields();
+            fieldSet.addAll(Arrays.asList(fields));
+            clazz = clazz.getSuperclass();
+        }
+        fieldSet = fieldSet.stream().filter(field ->
+                field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
+            .collect(Collectors.toSet());
+        for (Field field : fieldSet) {
+            field.setAccessible(true);
+        }
+        return fieldSet;
+    }
+
 }
diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java
index b43881a..9835132 100644
--- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java
+++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java
@@ -11,14 +11,12 @@
 import org.dromara.common.encrypt.annotation.ApiEncrypt;
 import org.dromara.common.encrypt.properties.ApiDecryptProperties;
 import org.springframework.http.HttpMethod;
-import org.springframework.http.MediaType;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerExceptionResolver;
 import org.springframework.web.servlet.HandlerExecutionChain;
 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
 
 import java.io.IOException;
-import java.io.PrintWriter;
 
 
 /**
@@ -37,42 +35,38 @@
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
         HttpServletRequest servletRequest = (HttpServletRequest) request;
         HttpServletResponse servletResponse = (HttpServletResponse) response;
-
-        boolean responseFlag = false;
+        // 鑾峰彇鍔犲瘑娉ㄨВ
+        ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest);
+        boolean responseFlag = apiEncrypt != null && apiEncrypt.response();
         ServletRequest requestWrapper = null;
         ServletResponse responseWrapper = null;
         EncryptResponseBodyWrapper responseBodyWrapper = null;
 
-        // 鏄惁涓� json 璇锋眰
-        if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
-            // 鏄惁涓� put 鎴栬�� post 璇锋眰
-            if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {
-                // 鏄惁瀛樺湪鍔犲瘑鏍囧ご
-                String headerValue = servletRequest.getHeader(properties.getHeaderFlag());
-                // 鑾峰彇鍔犲瘑娉ㄨВ
-                ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest);
-                responseFlag = apiEncrypt != null && apiEncrypt.response();
-                if (StringUtils.isNotBlank(headerValue)) {
-                    // 璇锋眰瑙e瘑
-                    requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
-                } else {
-                    // 鏄惁鏈夋敞瑙o紝鏈夊氨鎶ラ敊锛屾病鏈夋斁琛�
-                    if (ObjectUtil.isNotNull(apiEncrypt)) {
-                        HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
-                        exceptionResolver.resolveException(
-                            servletRequest, servletResponse, null,
-                            new ServiceException("娌℃湁璁块棶鏉冮檺锛岃鑱旂郴绠$悊鍛樻巿鏉�", HttpStatus.FORBIDDEN));
-                        return;
-                    }
-                }
-                // 鍒ゆ柇鏄惁鍝嶅簲鍔犲瘑
-                if (responseFlag) {
-                    responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
-                    responseWrapper = responseBodyWrapper;
+        // 鏄惁涓� put 鎴栬�� post 璇锋眰
+        if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {
+            // 鏄惁瀛樺湪鍔犲瘑鏍囧ご
+            String headerValue = servletRequest.getHeader(properties.getHeaderFlag());
+            if (StringUtils.isNotBlank(headerValue)) {
+                // 璇锋眰瑙e瘑
+                requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
+            } else {
+                // 鏄惁鏈夋敞瑙o紝鏈夊氨鎶ラ敊锛屾病鏈夋斁琛�
+                if (ObjectUtil.isNotNull(apiEncrypt)) {
+                    HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
+                    exceptionResolver.resolveException(
+                        servletRequest, servletResponse, null,
+                        new ServiceException("娌℃湁璁块棶鏉冮檺锛岃鑱旂郴绠$悊鍛樻巿鏉�", HttpStatus.FORBIDDEN));
+                    return;
                 }
             }
         }
 
+        // 鍒ゆ柇鏄惁鍝嶅簲鍔犲瘑
+        if (responseFlag) {
+            responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
+            responseWrapper = responseBodyWrapper;
+        }
+
         chain.doFilter(
             ObjectUtil.defaultIfNull(requestWrapper, request),
             ObjectUtil.defaultIfNull(responseWrapper, response));
diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java
index 05cf444..c0af232 100644
--- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java
+++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java
@@ -76,6 +76,7 @@
         String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey);
 
         // 璁剧疆鍝嶅簲澶�
+        servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);
         servletResponse.setHeader(headerFlag, encryptPassword);
         servletResponse.setHeader("Access-Control-Allow-Origin", "*");
         servletResponse.setHeader("Access-Control-Allow-Methods", "*");
diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java
index 7c2508f..460aa36 100644
--- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java
+++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java
@@ -73,7 +73,11 @@
             list.forEach(this::decryptHandler);
             return;
         }
+        // 涓嶅湪缂撳瓨涓殑绫�,灏辨槸娌℃湁鍔犲瘑娉ㄨВ鐨勭被(褰撶劧涔熸湁鍙兘鏄痶ypeAliasesPackage鍐欓敊)
         Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
+        if(ObjectUtil.isNull(fields)){
+            return;
+        }
         try {
             for (Field field : fields) {
                 field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));
diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java
index 152f7db..bcc2f4c 100644
--- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java
+++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java
@@ -82,7 +82,11 @@
             list.forEach(this::encryptHandler);
             return;
         }
+        // 涓嶅湪缂撳瓨涓殑绫�,灏辨槸娌℃湁鍔犲瘑娉ㄨВ鐨勭被(褰撶劧涔熸湁鍙兘鏄痶ypeAliasesPackage鍐欓敊)
         Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
+        if(ObjectUtil.isNull(fields)){
+            return;
+        }
         try {
             for (Field field : fields) {
                 field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field));
diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java
index bbdaaa1..6b9211b 100644
--- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java
+++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java
@@ -21,4 +21,9 @@
 	 */
 	int index() default -1;
 
+    /**
+     * 鍚堝苟闇�瑕佷緷璧栫殑鍏朵粬瀛楁鍚嶇О
+     */
+    String[] mergeBy() default {};
+
 }
diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java
index bcb5be7..7c0a48b 100644
--- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java
+++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java
@@ -1,8 +1,12 @@
 package org.dromara.common.excel.core;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
 import com.alibaba.excel.annotation.ExcelProperty;
 import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.write.handler.WorkbookWriteHandler;
+import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext;
 import com.alibaba.excel.write.merge.AbstractMergeStrategy;
 import lombok.AllArgsConstructor;
 import lombok.Data;
@@ -15,10 +19,7 @@
 import org.dromara.common.excel.annotation.CellMerge;
 
 import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * 鍒楀�奸噸澶嶅悎骞剁瓥鐣�
@@ -26,7 +27,7 @@
  * @author Lion Li
  */
 @Slf4j
-public class CellMergeStrategy extends AbstractMergeStrategy {
+public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler {
 
     private final List<CellRangeAddress> cellList;
     private final boolean hasTitle;
@@ -41,13 +42,24 @@
 
     @Override
     protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
-        // judge the list is not null
-        if (CollUtil.isNotEmpty(cellList)) {
-            // the judge is necessary
-            if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == 0) {
-                for (CellRangeAddress item : cellList) {
-                    sheet.addMergedRegion(item);
+        //鍗曞厓鏍煎啓鍏ヤ簡,閬嶅巻鍚堝苟鍖哄煙,濡傛灉璇ell鍦ㄥ尯鍩熷唴,浣嗛潪棣栬,鍒欐竻绌�
+        final int rowIndex = cell.getRowIndex();
+        if (CollUtil.isNotEmpty(cellList)){
+            for (CellRangeAddress cellAddresses : cellList) {
+                final int firstRow = cellAddresses.getFirstRow();
+                if (cellAddresses.isInRange(cell) && rowIndex != firstRow){
+                    cell.setBlank();
                 }
+            }
+        }
+    }
+
+    @Override
+    public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) {
+        //褰撳墠琛ㄦ牸鍐欏畬鍚庯紝缁熶竴鍐欏叆
+        if (CollUtil.isNotEmpty(cellList)){
+            for (CellRangeAddress item : cellList) {
+                context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item);
             }
         }
     }
@@ -93,35 +105,15 @@
                         // 绌哄�艰烦杩囦笉鍚堝苟
                         continue;
                     }
+
                     if (!cellValue.equals(val)) {
-                        if (i - repeatCell.getCurrent() > 1) {
+                        if ((i - repeatCell.getCurrent() > 1) && isMerge(list, i, field)) {
                             cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
                         }
                         map.put(field, new RepeatCell(val, i));
-                    } else if (j == 0) {
-                        if (i == list.size() - 1) {
-                            if (i > repeatCell.getCurrent()) {
-                                cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
-                            }
-                        }
-                    } else {
-                        // 鍒ゆ柇鍓嶉潰鐨勬槸鍚﹀悎骞朵簡
-                        RepeatCell firstCell = map.get(mergeFields.get(0));
-                        if (repeatCell.getCurrent() != firstCell.getCurrent()) {
-                            if (i == list.size() - 1) {
-                                if (i > repeatCell.getCurrent()) {
-                                    cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
-                                }
-                            } else if (repeatCell.getCurrent() < firstCell.getCurrent()) {
-                                if (i - repeatCell.getCurrent() > 1) {
-                                    cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
-                                }
-                                map.put(field, new RepeatCell(val, i));
-                            }
-                        } else if (i == list.size() - 1) {
-                            if (i > repeatCell.getCurrent()) {
-                                cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
-                            }
+                    } else if (i == list.size() - 1) {
+                        if (i > repeatCell.getCurrent() && isMerge(list, i, field)) {
+                            cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum));
                         }
                     }
                 }
@@ -130,6 +122,24 @@
         return cellList;
     }
 
+    private boolean isMerge(List<?> list, int i, Field field) {
+        boolean isMerge = true;
+        CellMerge cm = field.getAnnotation(CellMerge.class);
+        final String[] mergeBy = cm.mergeBy();
+        if (StrUtil.isAllNotBlank(mergeBy)) {
+            //姣斿褰撳墠list(i)鍜宭ist(i - 1)鐨勫悇涓睘鎬у�间竴涓�姣斿 濡傛灉鍏ㄤ负鐪� 鍒欎负鐪�
+            for (String fieldName : mergeBy) {
+                final Object valCurrent = ReflectUtil.getFieldValue(list.get(i), fieldName);
+                final Object valPre = ReflectUtil.getFieldValue(list.get(i - 1), fieldName);
+                if (!Objects.equals(valPre, valCurrent)) {
+                    //渚濊禆瀛楁濡傛湁浠讳竴涓嶇瓑鍊�,鍒欐爣璁颁负涓嶅彲鍚堝苟
+                    isMerge = false;
+                }
+            }
+        }
+        return isMerge;
+    }
+
     @Data
     @AllArgsConstructor
     static class RepeatCell {
diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java
index 3b791ea..b3f68ed 100644
--- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java
+++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java
@@ -20,6 +20,7 @@
 import org.dromara.common.core.service.DictService;
 import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.excel.annotation.ExcelDictFormat;
 import org.dromara.common.excel.annotation.ExcelEnumFormat;
 
@@ -99,15 +100,16 @@
                 ExcelDictFormat format = field.getDeclaredAnnotation(ExcelDictFormat.class);
                 String dictType = format.dictType();
                 String converterExp = format.readConverterExp();
-                if (StrUtil.isNotBlank(dictType)) {
+                if (StringUtils.isNotBlank(dictType)) {
                     // 濡傛灉浼犻�掍簡瀛楀吀鍚嶏紝鍒欎緷鎹瓧鍏稿缓绔嬩笅鎷�
                     Collection<String> values = Optional.ofNullable(dictService.getAllDictByDictType(dictType))
                         .orElseThrow(() -> new ServiceException(String.format("瀛楀吀 %s 涓嶅瓨鍦�", dictType)))
                         .values();
                     options = new ArrayList<>(values);
-                } else if (StrUtil.isNotBlank(converterExp)) {
+                } else if (StringUtils.isNotBlank(converterExp)) {
                     // 濡傛灉鎸囧畾浜嗙‘鍒囩殑鍊硷紝鍒欑洿鎺ヨВ鏋愮‘鍒囩殑鍊�
-                    options = StrUtil.split(converterExp, format.separator(), true, true);
+                    List<String> strList = StringUtils.splitList(converterExp, format.separator());
+                    options = StreamUtils.toList(strList, s -> StringUtils.split(s, "=")[1]);
                 }
             } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) {
                 // 鍚﹀垯濡傛灉鎸囧畾浜咢ExcelEnumFormat锛屽垯浣跨敤鏋氫妇鐨勯�昏緫
diff --git a/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java
index 016fe0f..5a27e91 100644
--- a/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java
+++ b/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java
@@ -4,6 +4,13 @@
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.crypto.SecureUtil;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
 import org.dromara.common.core.constant.GlobalConstants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.exception.ServiceException;
@@ -13,13 +20,6 @@
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
 import org.dromara.common.json.utils.JsonUtils;
 import org.dromara.common.redis.utils.RedisUtils;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.annotation.AfterReturning;
-import org.aspectj.lang.annotation.AfterThrowing;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Before;
 import org.springframework.validation.BindingResult;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -127,7 +127,7 @@
     public boolean isFilterObject(final Object o) {
         Class<?> clazz = o.getClass();
         if (clazz.isArray()) {
-            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+            return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
         } else if (Collection.class.isAssignableFrom(clazz)) {
             Collection collection = (Collection) o;
             for (Object value : collection) {
diff --git a/ruoyi-common/ruoyi-common-job/pom.xml b/ruoyi-common/ruoyi-common-job/pom.xml
index 1a6e729..3a4a0cb 100644
--- a/ruoyi-common/ruoyi-common-job/pom.xml
+++ b/ruoyi-common/ruoyi-common-job/pom.xml
@@ -22,20 +22,14 @@
             <artifactId>spring-boot-autoconfigure</artifactId>
         </dependency>
 
-        <!--PowerJob-->
+        <!-- SnailJob client -->
         <dependency>
-            <groupId>tech.powerjob</groupId>
-            <artifactId>powerjob-worker-spring-boot-starter</artifactId>
-            <exclusions>
-                <exclusion>
-                    <artifactId>powerjob-remote-impl-akka</artifactId>
-                    <groupId>tech.powerjob</groupId>
-                </exclusion>
-            </exclusions>
+            <groupId>com.aizuda</groupId>
+            <artifactId>snail-job-client-starter</artifactId>
         </dependency>
         <dependency>
-            <groupId>tech.powerjob</groupId>
-            <artifactId>powerjob-official-processors</artifactId>
+            <groupId>com.aizuda</groupId>
+            <artifactId>snail-job-client-job-core</artifactId>
         </dependency>
 
         <dependency>
diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java
deleted file mode 100644
index 67208c0..0000000
--- a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.dromara.common.job.config;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.scheduling.annotation.EnableScheduling;
-import tech.powerjob.worker.PowerJobWorker;
-
-/**
- * 鍚姩瀹氭椂浠诲姟
- * @author yhan219
- * @since 2023/6/2
- */
-@Configuration
-@ConditionalOnBean(PowerJobWorker.class)
-@ConditionalOnProperty(prefix = "powerjob.worker", name = "enabled", havingValue = "true")
-@EnableScheduling
-public class PowerJobConfig {
-
-
-}
diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java
new file mode 100644
index 0000000..d671f0e
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java
@@ -0,0 +1,37 @@
+package org.dromara.common.job.config;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import com.aizuda.snailjob.client.common.appender.SnailLogbackAppender;
+import com.aizuda.snailjob.client.common.event.SnailClientStartingEvent;
+import com.aizuda.snailjob.client.starter.EnableSnailJob;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * 鍚姩瀹氭椂浠诲姟
+ *
+ * @author opensnail
+ * @date 2024-05-17
+ */
+@AutoConfiguration
+@ConditionalOnProperty(prefix = "snail-job", name = "enabled", havingValue = "true")
+@EnableScheduling
+@EnableSnailJob(group = "${snail-job.group-name}")
+public class SnailJobConfig {
+
+    @EventListener(SnailClientStartingEvent.class)
+    public void onStarting(SnailClientStartingEvent event) {
+        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+        SnailLogbackAppender<ILoggingEvent> ca = new SnailLogbackAppender<>();
+        ca.setName("snail_log_appender");
+        ca.start();
+        Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
+        rootLogger.addAppender(ca);
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..3aa1881
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+org.dromara.common.job.config.SnailJobConfig
diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java
index 42af8da..65c2faa 100644
--- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java
+++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java
@@ -7,10 +7,10 @@
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.exc.MismatchedInputException;
-import org.dromara.common.core.utils.SpringUtils;
-import org.dromara.common.core.utils.StringUtils;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StringUtils;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -30,6 +30,13 @@
         return OBJECT_MAPPER;
     }
 
+    /**
+     * 灏嗗璞¤浆鎹负JSON鏍煎紡鐨勫瓧绗︿覆
+     *
+     * @param object 瑕佽浆鎹㈢殑瀵硅薄
+     * @return JSON鏍煎紡鐨勫瓧绗︿覆锛屽鏋滃璞′负null锛屽垯杩斿洖null
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烰SON澶勭悊寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
     public static String toJsonString(Object object) {
         if (ObjectUtil.isNull(object)) {
             return null;
@@ -41,6 +48,15 @@
         }
     }
 
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓烘寚瀹氱被鍨嬬殑瀵硅薄
+     *
+     * @param text  JSON鏍煎紡鐨勫瓧绗︿覆
+     * @param clazz 瑕佽浆鎹㈢殑鐩爣瀵硅薄绫诲瀷
+     * @param <T>   鐩爣瀵硅薄鐨勬硾鍨嬬被鍨�
+     * @return 杞崲鍚庣殑瀵硅薄锛屽鏋滃瓧绗︿覆涓虹┖鍒欒繑鍥瀗ull
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
     public static <T> T parseObject(String text, Class<T> clazz) {
         if (StringUtils.isEmpty(text)) {
             return null;
@@ -52,6 +68,15 @@
         }
     }
 
+    /**
+     * 灏嗗瓧鑺傛暟缁勮浆鎹负鎸囧畾绫诲瀷鐨勫璞�
+     *
+     * @param bytes 瀛楄妭鏁扮粍
+     * @param clazz 瑕佽浆鎹㈢殑鐩爣瀵硅薄绫诲瀷
+     * @param <T>   鐩爣瀵硅薄鐨勬硾鍨嬬被鍨�
+     * @return 杞崲鍚庣殑瀵硅薄锛屽鏋滃瓧鑺傛暟缁勪负绌哄垯杩斿洖null
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
     public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
         if (ArrayUtil.isEmpty(bytes)) {
             return null;
@@ -63,6 +88,15 @@
         }
     }
 
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓烘寚瀹氱被鍨嬬殑瀵硅薄锛屾敮鎸佸鏉傜被鍨�
+     *
+     * @param text          JSON鏍煎紡鐨勫瓧绗︿覆
+     * @param typeReference 鎸囧畾绫诲瀷鐨凾ypeReference瀵硅薄
+     * @param <T>           鐩爣瀵硅薄鐨勬硾鍨嬬被鍨�
+     * @return 杞崲鍚庣殑瀵硅薄锛屽鏋滃瓧绗︿覆涓虹┖鍒欒繑鍥瀗ull
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
     public static <T> T parseObject(String text, TypeReference<T> typeReference) {
         if (StringUtils.isBlank(text)) {
             return null;
@@ -74,6 +108,13 @@
         }
     }
 
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓篋ict瀵硅薄
+     *
+     * @param text JSON鏍煎紡鐨勫瓧绗︿覆
+     * @return 杞崲鍚庣殑Dict瀵硅薄锛屽鏋滃瓧绗︿覆涓虹┖鎴栬�呬笉鏄疛SON鏍煎紡鍒欒繑鍥瀗ull
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
     public static Dict parseMap(String text) {
         if (StringUtils.isBlank(text)) {
             return null;
@@ -88,6 +129,13 @@
         }
     }
 
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓篋ict瀵硅薄鐨勫垪琛�
+     *
+     * @param text JSON鏍煎紡鐨勫瓧绗︿覆
+     * @return 杞崲鍚庣殑Dict瀵硅薄鐨勫垪琛紝濡傛灉瀛楃涓蹭负绌哄垯杩斿洖null
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
     public static List<Dict> parseArrayMap(String text) {
         if (StringUtils.isBlank(text)) {
             return null;
@@ -99,6 +147,15 @@
         }
     }
 
+    /**
+     * 灏咼SON鏍煎紡鐨勫瓧绗︿覆杞崲涓烘寚瀹氱被鍨嬪璞$殑鍒楄〃
+     *
+     * @param text  JSON鏍煎紡鐨勫瓧绗︿覆
+     * @param clazz 瑕佽浆鎹㈢殑鐩爣瀵硅薄绫诲瀷
+     * @param <T>   鐩爣瀵硅薄鐨勬硾鍨嬬被鍨�
+     * @return 杞崲鍚庣殑瀵硅薄鐨勫垪琛紝濡傛灉瀛楃涓蹭负绌哄垯杩斿洖绌哄垪琛�
+     * @throws RuntimeException 濡傛灉杞崲杩囩▼涓彂鐢烮O寮傚父锛屽垯鎶涘嚭杩愯鏃跺紓甯�
+     */
     public static <T> List<T> parseArray(String text, Class<T> clazz) {
         if (StringUtils.isEmpty(text)) {
             return new ArrayList<>();
diff --git a/ruoyi-common/ruoyi-common-log/pom.xml b/ruoyi-common/ruoyi-common-log/pom.xml
index 28bd530..1e2b33b 100644
--- a/ruoyi-common/ruoyi-common-log/pom.xml
+++ b/ruoyi-common/ruoyi-common-log/pom.xml
@@ -27,11 +27,6 @@
             <artifactId>ruoyi-common-json</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>com.alibaba</groupId>
-            <artifactId>transmittable-thread-local</artifactId>
-        </dependency>
-
     </dependencies>
 
 </project>
diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java
index e98f4f7..8724072 100644
--- a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java
+++ b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java
@@ -4,16 +4,6 @@
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.ObjectUtil;
-import com.alibaba.ttl.TransmittableThreadLocal;
-import org.dromara.common.core.domain.model.LoginUser;
-import org.dromara.common.core.utils.ServletUtils;
-import org.dromara.common.core.utils.SpringUtils;
-import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.json.utils.JsonUtils;
-import org.dromara.common.log.annotation.Log;
-import org.dromara.common.log.enums.BusinessStatus;
-import org.dromara.common.log.event.OperLogEvent;
-import org.dromara.common.satoken.utils.LoginHelper;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
@@ -23,6 +13,15 @@
 import org.aspectj.lang.annotation.AfterThrowing;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
+import org.dromara.common.core.domain.model.LoginUser;
+import org.dromara.common.core.utils.ServletUtils;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.json.utils.JsonUtils;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessStatus;
+import org.dromara.common.log.event.OperLogEvent;
+import org.dromara.common.satoken.utils.LoginHelper;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.http.HttpMethod;
 import org.springframework.validation.BindingResult;
@@ -49,9 +48,9 @@
 
 
     /**
-     * 璁$畻鎿嶄綔娑堣�楁椂闂�
+     * 璁℃椂 key
      */
-    private static final ThreadLocal<StopWatch> TIME_THREADLOCAL = new TransmittableThreadLocal<>();
+    private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
 
     /**
      * 澶勭悊璇锋眰鍓嶆墽琛�
@@ -59,7 +58,7 @@
     @Before(value = "@annotation(controllerLog)")
     public void boBefore(JoinPoint joinPoint, Log controllerLog) {
         StopWatch stopWatch = new StopWatch();
-        TIME_THREADLOCAL.set(stopWatch);
+        KEY_CACHE.set(stopWatch);
         stopWatch.start();
     }
 
@@ -112,7 +111,7 @@
             // 澶勭悊璁剧疆娉ㄨВ涓婄殑鍙傛暟
             getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
             // 璁剧疆娑堣�楁椂闂�
-            StopWatch stopWatch = TIME_THREADLOCAL.get();
+            StopWatch stopWatch = KEY_CACHE.get();
             stopWatch.stop();
             operLog.setCostTime(stopWatch.getTime());
             // 鍙戝竷浜嬩欢淇濆瓨鏁版嵁搴�
@@ -122,7 +121,7 @@
             log.error("寮傚父淇℃伅:{}", exp.getMessage());
             exp.printStackTrace();
         } finally {
-            TIME_THREADLOCAL.remove();
+            KEY_CACHE.remove();
         }
     }
 
@@ -204,7 +203,7 @@
     public boolean isFilterObject(final Object o) {
         Class<?> clazz = o.getClass();
         if (clazz.isArray()) {
-            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+            return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
         } else if (Collection.class.isAssignableFrom(clazz)) {
             Collection collection = (Collection) o;
             for (Object value : collection) {
diff --git a/ruoyi-common/ruoyi-common-mybatis/pom.xml b/ruoyi-common/ruoyi-common-mybatis/pom.xml
index 2088b54..a58064a 100644
--- a/ruoyi-common/ruoyi-common-mybatis/pom.xml
+++ b/ruoyi-common/ruoyi-common-mybatis/pom.xml
@@ -33,19 +33,8 @@
         </dependency>
 
         <dependency>
-            <groupId>org.mybatis.spring.boot</groupId>
-            <artifactId>mybatis-spring-boot-starter</artifactId>
-        </dependency>
-
-        <dependency>
             <groupId>com.baomidou</groupId>
-            <artifactId>mybatis-plus-boot-starter</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.mybatis</groupId>
-                    <artifactId>mybatis-spring</artifactId>
-                </exclusion>
-            </exclusions>
+            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
         </dependency>
 
         <!-- sql鎬ц兘鍒嗘瀽鎻掍欢 -->
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java
index 1689e3c..0bc5b66 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java
@@ -1,26 +1,23 @@
 package org.dromara.common.mybatis.config;
 
 import cn.hutool.core.net.NetUtil;
-import com.baomidou.mybatisplus.autoconfigure.DdlApplicationRunner;
-import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
 import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
 import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
-import com.baomidou.mybatisplus.extension.ddl.IDdl;
 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
 import org.dromara.common.core.factory.YmlPropertySourceFactory;
+import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
+import org.dromara.common.mybatis.handler.MybatisExceptionHandler;
 import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor;
 import org.mybatis.spring.annotation.MapperScan;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.beans.BeansException;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.PropertySource;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
-
-import java.util.List;
 
 /**
  * mybatis-plus閰嶇疆绫�(涓嬫柟娉ㄩ噴鏈夋彃浠朵粙缁�)
@@ -28,7 +25,6 @@
  * @author Lion Li
  */
 @EnableTransactionManagement(proxyTargetClass = true)
-@AutoConfiguration(before = MybatisPlusAutoConfiguration.class)
 @MapperScan("${mybatis-plus.mapperPackage}")
 @PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class)
 public class MybatisPlusConfig {
@@ -36,6 +32,12 @@
     @Bean
     public MybatisPlusInterceptor mybatisPlusInterceptor() {
         MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        // 澶氱鎴锋彃浠� 蹇呴』鏀惧埌绗竴浣�
+        try {
+            TenantLineInnerInterceptor tenant = SpringUtils.getBean(TenantLineInnerInterceptor.class);
+            interceptor.addInnerInterceptor(tenant);
+        } catch (BeansException ignore) {
+        }
         // 鏁版嵁鏉冮檺澶勭悊
         interceptor.addInnerInterceptor(dataPermissionInterceptor());
         // 鍒嗛〉鎻掍欢
@@ -49,7 +51,7 @@
      * 鏁版嵁鏉冮檺鎷︽埅鍣�
      */
     public PlusDataPermissionInterceptor dataPermissionInterceptor() {
-        return new PlusDataPermissionInterceptor();
+        return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage"));
     }
 
     /**
@@ -57,8 +59,6 @@
      */
     public PaginationInnerInterceptor paginationInnerInterceptor() {
         PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
-        // 璁剧疆鏈�澶у崟椤甸檺鍒舵暟閲忥紝榛樿 500 鏉★紝-1 涓嶅彈闄愬埗
-        paginationInnerInterceptor.setMaxLimit(-1L);
         // 鍒嗛〉鍚堢悊鍖�
         paginationInnerInterceptor.setOverflow(true);
         return paginationInnerInterceptor;
@@ -89,6 +89,14 @@
     }
 
     /**
+     * 寮傚父澶勭悊鍣�
+     */
+    @Bean
+    public MybatisExceptionHandler mybatisExceptionHandler() {
+        return new MybatisExceptionHandler();
+    }
+
+    /**
      * PaginationInnerInterceptor 鍒嗛〉鎻掍欢锛岃嚜鍔ㄨ瘑鍒暟鎹簱绫诲瀷
      * https://baomidou.com/pages/97710a/
      * OptimisticLockerInnerInterceptor 涔愯閿佹彃浠�
@@ -107,10 +115,5 @@
      * DynamicTableNameInnerInterceptor 鍔ㄦ�佽〃鍚嶆彃浠�
      * https://baomidou.com/pages/2a45ff/
      */
-
-    @Bean
-    public DdlApplicationRunner ddlApplicationRunner(@Autowired(required = false) List<IDdl> ddlList) {
-        return new DdlApplicationRunner(ddlList);
-    }
 
 }
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
index 9018a79..08723f6 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
@@ -35,7 +35,6 @@
     Log log = LogFactory.getLog(BaseMapperPlus.class);
 
     default Class<V> currentVoClass() {
-        GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class);
         return (Class<V>) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1];
     }
 
@@ -145,11 +144,22 @@
         return selectVoOne(wrapper, this.currentVoClass());
     }
 
+    default V selectVoOne(Wrapper<T> wrapper, boolean throwEx) {
+        return selectVoOne(wrapper, this.currentVoClass(), throwEx);
+    }
+
     /**
      * 鏍规嵁 entity 鏉′欢锛屾煡璇竴鏉¤褰�
      */
     default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {
-        T obj = this.selectOne(wrapper);
+        return selectVoOne(wrapper, voClass, true);
+    }
+
+    /**
+     * 鏍规嵁 entity 鏉′欢锛屾煡璇竴鏉¤褰�
+     */
+    default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass, boolean throwEx) {
+        T obj = this.selectOne(wrapper, throwEx);
         if (ObjectUtil.isNull(obj)) {
             return null;
         }
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
index 8ef4a57..40b7530 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java
@@ -111,4 +111,8 @@
         return list;
     }
 
+    public Integer getFirstNum() {
+        return (pageNum - 1) * pageSize;
+    }
+
 }
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java
index 63653de..a66908f 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java
@@ -29,16 +29,17 @@
                     ? baseEntity.getCreateTime() : new Date();
                 baseEntity.setCreateTime(current);
                 baseEntity.setUpdateTime(current);
-                LoginUser loginUser = getLoginUser();
-                if (ObjectUtil.isNotNull(loginUser)) {
-                    Long userId = ObjectUtil.isNotNull(baseEntity.getCreateBy())
-                        ? baseEntity.getCreateBy() : loginUser.getUserId();
-                    // 褰撳墠宸茬櫥褰� 涓� 鍒涘缓浜轰负绌� 鍒欏~鍏�
-                    baseEntity.setCreateBy(userId);
-                    // 褰撳墠宸茬櫥褰� 涓� 鏇存柊浜轰负绌� 鍒欏~鍏�
-                    baseEntity.setUpdateBy(userId);
-                    baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept())
-                        ? baseEntity.getCreateDept() : loginUser.getDeptId());
+                if (ObjectUtil.isNull(baseEntity.getCreateBy())) {
+                    LoginUser loginUser = getLoginUser();
+                    if (ObjectUtil.isNotNull(loginUser)) {
+                        Long userId = loginUser.getUserId();
+                        // 褰撳墠宸茬櫥褰� 涓� 鍒涘缓浜轰负绌� 鍒欏~鍏�
+                        baseEntity.setCreateBy(userId);
+                        // 褰撳墠宸茬櫥褰� 涓� 鏇存柊浜轰负绌� 鍒欏~鍏�
+                        baseEntity.setUpdateBy(userId);
+                        baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept())
+                            ? baseEntity.getCreateDept() : loginUser.getDeptId());
+                    }
                 }
             }
         } catch (Exception e) {
@@ -53,11 +54,12 @@
                 Date current = new Date();
                 // 鏇存柊鏃堕棿濉厖(涓嶇涓轰笉涓虹┖)
                 baseEntity.setUpdateTime(current);
-                LoginUser loginUser = getLoginUser();
                 // 褰撳墠宸茬櫥褰� 鏇存柊浜哄~鍏�(涓嶇涓轰笉涓虹┖)
-                if (ObjectUtil.isNotNull(loginUser)) {
-                    baseEntity.setUpdateBy(loginUser.getUserId());
+                Long userId = LoginHelper.getUserId();
+                if (ObjectUtil.isNotNull(userId)) {
+                    baseEntity.setUpdateBy(userId);
                 }
+
             }
         } catch (Exception e) {
             throw new ServiceException("鑷姩娉ㄥ叆寮傚父 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java
index c517fa7..ec3ee0d 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java
@@ -2,6 +2,7 @@
 
 import org.dromara.common.core.domain.R;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.utils.StringUtils;
 import org.mybatis.spring.MyBatisSystemException;
 import org.springframework.dao.DuplicateKeyException;
 import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -35,7 +36,7 @@
     public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
         String requestURI = request.getRequestURI();
         String message = e.getMessage();
-        if ("CannotFindDataSourceException".contains(message)) {
+        if (StringUtils.contains("CannotFindDataSourceException", message)) {
             log.error("璇锋眰鍦板潃'{}', 鏈壘鍒版暟鎹簮", requestURI);
             return R.fail("鏈壘鍒版暟鎹簮锛岃鑱旂郴绠$悊鍛樼‘璁�");
         }
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java
index 6ddaa24..7d7fd84 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java
@@ -2,7 +2,6 @@
 
 import cn.hutool.core.annotation.AnnotationUtil;
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ClassUtil;
 import cn.hutool.core.util.ObjectUtil;
 import lombok.extern.slf4j.Slf4j;
 import net.sf.jsqlparser.JSQLParserException;
@@ -10,6 +9,7 @@
 import net.sf.jsqlparser.expression.Parenthesis;
 import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
 import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import org.apache.ibatis.io.Resources;
 import org.dromara.common.core.domain.dto.RoleDTO;
 import org.dromara.common.core.domain.model.LoginUser;
 import org.dromara.common.core.exception.ServiceException;
@@ -21,16 +21,26 @@
 import org.dromara.common.mybatis.enums.DataScopeType;
 import org.dromara.common.mybatis.helper.DataPermissionHelper;
 import org.dromara.common.satoken.utils.LoginHelper;
+import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.type.ClassMetadata;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
 import org.springframework.expression.BeanResolver;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.common.TemplateParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.util.ClassUtils;
 
 import java.lang.reflect.Method;
-import java.util.*;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 
@@ -58,9 +68,13 @@
      */
     private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory());
 
+    public PlusDataPermissionHandler(String mapperPackage) {
+        scanMapperClasses(mapperPackage);
+    }
+
 
     public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
-        DataColumn[] dataColumns = findAnnotation(mappedStatementId);
+        DataPermission dataPermission = getDataPermission(mappedStatementId);
         LoginUser currentUser = DataPermissionHelper.getVariable("user");
         if (ObjectUtil.isNull(currentUser)) {
             currentUser = LoginHelper.getLoginUser();
@@ -70,7 +84,7 @@
         if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) {
             return where;
         }
-        String dataFilterSql = buildDataFilter(dataColumns, isSelect);
+        String dataFilterSql = buildDataFilter(dataPermission.value(), isSelect);
         if (StringUtils.isBlank(dataFilterSql)) {
             return where;
         }
@@ -144,43 +158,64 @@
         return "";
     }
 
-    public DataColumn[] findAnnotation(String mappedStatementId) {
-        StringBuilder sb = new StringBuilder(mappedStatementId);
-        int index = sb.lastIndexOf(".");
-        String clazzName = sb.substring(0, index);
-        String methodName = sb.substring(index + 1, sb.length());
-        Class<?> clazz;
+    /**
+     * 閫氳繃 mapperPackage 璁剧疆鐨勬壂鎻忓寘 鎵弿缂撳瓨鏈夋敞瑙g殑鏂规硶涓庣被
+     */
+    private void scanMapperClasses(String mapperPackage) {
+        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
+        CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
+        String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
+        String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
         try {
-            clazz = ClassUtil.loadClass(clazzName);
+            for (String packagePattern : packagePatternArray) {
+                String path = ClassUtils.convertClassNameToResourcePath(packagePattern);
+                Resource[] resources = resolver.getResources(classpath + path + "/*.class");
+                for (Resource resource : resources) {
+                    ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata();
+                    Class<?> clazz = Resources.classForName(classMetadata.getClassName());
+                    findAnnotation(clazz);
+                }
+            }
         } catch (Exception e) {
-            return null;
+            log.error("鍒濆鍖栨暟鎹畨鍏ㄧ紦瀛樻椂鍑洪敊:{}", e.getMessage());
         }
-        List<Method> methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz))
-            .filter(method -> method.getName().equals(methodName)).toList();
+    }
+
+    private void findAnnotation(Class<?> clazz) {
         DataPermission dataPermission;
         // 鑾峰彇鏂规硶娉ㄨВ
-        for (Method method : methods) {
-            dataPermission = dataPermissionCacheMap.get(mappedStatementId);
-            if (ObjectUtil.isNotNull(dataPermission)) {
-                return dataPermission.value();
+        for (Method method : clazz.getMethods()) {
+            if (method.isDefault() || method.isVarArgs()) {
+                continue;
             }
+            String mappedStatementId = clazz.getName() + "." + method.getName();
             if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) {
                 dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class);
                 dataPermissionCacheMap.put(mappedStatementId, dataPermission);
-                return dataPermission.value();
             }
-        }
-        dataPermission = dataPermissionCacheMap.get(clazz.getName());
-        if (ObjectUtil.isNotNull(dataPermission)) {
-            return dataPermission.value();
         }
         // 鑾峰彇绫绘敞瑙�
         if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {
             dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class);
             dataPermissionCacheMap.put(clazz.getName(), dataPermission);
-            return dataPermission.value();
+        }
+    }
+
+    public DataPermission getDataPermission(String mapperId) {
+        if (dataPermissionCacheMap.containsKey(mapperId)) {
+            return dataPermissionCacheMap.get(mapperId);
+        }
+        String clazzName = mapperId.substring(0, mapperId.lastIndexOf("."));
+        if (dataPermissionCacheMap.containsKey(clazzName)) {
+            return dataPermissionCacheMap.get(clazzName);
         }
         return null;
     }
 
+    /**
+     * 鏄惁鏃犳晥
+     */
+    public boolean invalid(String mapperId) {
+        return getDataPermission(mapperId) == null;
+    }
 }
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java
index 0ab0c11..6eed8f7 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java
@@ -1,18 +1,16 @@
 package org.dromara.common.mybatis.interceptor;
 
-import cn.hutool.core.collection.ConcurrentHashSet;
-import cn.hutool.core.util.ArrayUtil;
 import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
 import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
-import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
+import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler;
+import com.baomidou.mybatisplus.extension.plugins.inner.BaseMultiTableInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
-import org.dromara.common.mybatis.annotation.DataColumn;
-import org.dromara.common.mybatis.handler.PlusDataPermissionHandler;
+import lombok.extern.slf4j.Slf4j;
 import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.schema.Table;
 import net.sf.jsqlparser.statement.delete.Delete;
 import net.sf.jsqlparser.statement.select.PlainSelect;
 import net.sf.jsqlparser.statement.select.Select;
-import net.sf.jsqlparser.statement.select.SelectBody;
 import net.sf.jsqlparser.statement.select.SetOperationList;
 import net.sf.jsqlparser.statement.update.Update;
 import org.apache.ibatis.executor.Executor;
@@ -22,11 +20,11 @@
 import org.apache.ibatis.mapping.SqlCommandType;
 import org.apache.ibatis.session.ResultHandler;
 import org.apache.ibatis.session.RowBounds;
+import org.dromara.common.mybatis.handler.PlusDataPermissionHandler;
 
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.List;
-import java.util.Set;
 
 /**
  * 鏁版嵁鏉冮檺鎷︽埅鍣�
@@ -34,13 +32,14 @@
  * @author Lion Li
  * @version 3.5.0
  */
-public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
+@Slf4j
+public class PlusDataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {
 
-    private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();
-    /**
-     * 鏃犳晥娉ㄨВ鏂规硶缂撳瓨鐢ㄤ簬蹇�熻繑鍥�
-     */
-    private final Set<String> invalidCacheSet = new ConcurrentHashSet<>();
+    private final PlusDataPermissionHandler dataPermissionHandler;
+
+    public PlusDataPermissionInterceptor(String mapperPackage) {
+        this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage);
+    }
 
     @Override
     public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
@@ -49,12 +48,7 @@
             return;
         }
         // 妫�鏌ユ槸鍚︽棤鏁� 鏃犳暟鎹潈闄愭敞瑙�
-        if (invalidCacheSet.contains(ms.getId())) {
-            return;
-        }
-        DataColumn[] dataColumns = dataPermissionHandler.findAnnotation(ms.getId());
-        if (ArrayUtil.isEmpty(dataColumns)) {
-            invalidCacheSet.add(ms.getId());
+        if (dataPermissionHandler.invalid(ms.getId())) {
             return;
         }
         // 瑙f瀽 sql 鍒嗛厤瀵瑰簲鏂规硶
@@ -72,12 +66,7 @@
                 return;
             }
             // 妫�鏌ユ槸鍚︽棤鏁� 鏃犳暟鎹潈闄愭敞瑙�
-            if (invalidCacheSet.contains(ms.getId())) {
-                return;
-            }
-            DataColumn[] dataColumns = dataPermissionHandler.findAnnotation(ms.getId());
-            if (ArrayUtil.isEmpty(dataColumns)) {
-                invalidCacheSet.add(ms.getId());
+            if (dataPermissionHandler.invalid(ms.getId())) {
                 return;
             }
             PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
@@ -87,11 +76,10 @@
 
     @Override
     protected void processSelect(Select select, int index, String sql, Object obj) {
-        SelectBody selectBody = select.getSelectBody();
-        if (selectBody instanceof PlainSelect plainSelect) {
-            this.setWhere(plainSelect, (String) obj);
-        } else if (selectBody instanceof SetOperationList setOperationList) {
-            List<SelectBody> selectBodyList = setOperationList.getSelects();
+        if (select instanceof PlainSelect) {
+            this.setWhere((PlainSelect) select, (String) obj);
+        } else if (select instanceof SetOperationList setOperationList) {
+            List<Select> selectBodyList = setOperationList.getSelects();
             selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));
         }
     }
@@ -125,5 +113,11 @@
         }
     }
 
+    @Override
+    public Expression buildTableExpression(Table table, Expression where, String whereSegment) {
+        // 鍙湁鏂扮増鏁版嵁鏉冮檺澶勭悊鍣ㄦ墠浼氭墽琛屽埌杩欓噷
+        final MultiDataPermissionHandler handler = (MultiDataPermissionHandler) dataPermissionHandler;
+        return handler.getSqlSegment(table, where, whereSegment);
+    }
 }
 
diff --git a/ruoyi-admin/src/main/resources/spy.properties b/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties
similarity index 80%
rename from ruoyi-admin/src/main/resources/spy.properties
rename to ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties
index abbd893..e9d10f3 100644
--- a/ruoyi-admin/src/main/resources/spy.properties
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties
@@ -6,8 +6,6 @@
 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
 # 浣跨敤鏃ュ織绯荤粺璁板綍 sql
 #appender=com.p6spy.engine.spy.appender.Slf4JLogger
-# 璁剧疆 p6spy driver 浠g悊
-#deregisterdrivers=true
 # 鍙栨秷JDBC URL鍓嶇紑
 useprefix=true
 # 閰嶇疆璁板綍 Log 渚嬪,鍙幓鎺夌殑缁撴灉闆嗘湁error,info,batch,debug,statement,commit,rollback,result,resultset.
@@ -16,12 +14,6 @@
 dateformat=yyyy-MM-dd HH:mm:ss
 # SQL璇彞鎵撳嵃鏃堕棿鏍煎紡
 databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss
-# 瀹為檯椹卞姩鍙涓�
-#driverlist=org.h2.Driver
-# 鏄惁寮�鍚參SQL璁板綍
-outagedetection=true
-# 鎱QL璁板綍鏍囧噯 2 绉�
-outagedetectioninterval=2
 # 鏄惁杩囨护 Log
 filter=true
 # 杩囨护 Log 鏃舵墍鎺掗櫎鐨� sql 鍏抽敭瀛楋紝浠ラ�楀彿鍒嗛殧
diff --git a/ruoyi-common/ruoyi-common-oss/pom.xml b/ruoyi-common/ruoyi-common-oss/pom.xml
index 8e7afff..18d004f 100644
--- a/ruoyi-common/ruoyi-common-oss/pom.xml
+++ b/ruoyi-common/ruoyi-common-oss/pom.xml
@@ -26,10 +26,46 @@
             <artifactId>ruoyi-common-redis</artifactId>
         </dependency>
 
+        <!--  AWS SDK for Java 2.x  -->
         <dependency>
-            <groupId>com.amazonaws</groupId>
-            <artifactId>aws-java-sdk-s3</artifactId>
+            <groupId>software.amazon.awssdk</groupId>
+            <artifactId>s3</artifactId>
+            <exclusions>
+                <!-- 灏嗗熀浜� Netty 鐨� HTTP 瀹㈡埛绔粠绫昏矾寰勪腑绉婚櫎 -->
+                <exclusion>
+                    <groupId>software.amazon.awssdk</groupId>
+                    <artifactId>netty-nio-client</artifactId>
+                </exclusion>
+                <!-- 灏嗗熀浜� CRT 鐨� HTTP 瀹㈡埛绔粠绫昏矾寰勪腑绉婚櫎 -->
+                <exclusion>
+                    <groupId>software.amazon.awssdk</groupId>
+                    <artifactId>aws-crt-client</artifactId>
+                </exclusion>
+                <!-- 灏嗗熀浜� Apache 鐨� HTTP 瀹㈡埛绔粠绫昏矾寰勪腑绉婚櫎 -->
+                <exclusion>
+                    <groupId>software.amazon.awssdk</groupId>
+                    <artifactId>apache-client</artifactId>
+                </exclusion>
+                <!-- 灏嗛厤缃熀浜� URL 杩炴帴鐨� HTTP 瀹㈡埛绔粠绫昏矾寰勪腑绉婚櫎 -->
+                <exclusion>
+                    <groupId>software.amazon.awssdk</groupId>
+                    <artifactId>url-connection-client</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
+
+        <!-- 浣跨敤AWS鍩轰簬 CRT 鐨� S3 瀹㈡埛绔� -->
+        <dependency>
+            <groupId>software.amazon.awssdk.crt</groupId>
+            <artifactId>aws-crt</artifactId>
+        </dependency>
+
+        <!-- 鍩轰簬 AWS CRT 鐨� S3 瀹㈡埛绔殑鎬ц兘澧炲己鐨� S3 浼犺緭绠$悊鍣� -->
+        <dependency>
+            <groupId>software.amazon.awssdk</groupId>
+            <artifactId>s3-transfer-manager</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>
diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
index 53e05c9..fcad35b 100644
--- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
+++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java
@@ -2,73 +2,117 @@
 
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.IdUtil;
-import com.amazonaws.ClientConfiguration;
-import com.amazonaws.HttpMethod;
-import com.amazonaws.Protocol;
-import com.amazonaws.auth.AWSCredentials;
-import com.amazonaws.auth.AWSCredentialsProvider;
-import com.amazonaws.auth.AWSStaticCredentialsProvider;
-import com.amazonaws.auth.BasicAWSCredentials;
-import com.amazonaws.client.builder.AwsClientBuilder;
-import com.amazonaws.services.s3.AmazonS3;
-import com.amazonaws.services.s3.AmazonS3Client;
-import com.amazonaws.services.s3.AmazonS3ClientBuilder;
-import com.amazonaws.services.s3.model.*;
+import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.utils.DateUtils;
 import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.file.FileUtils;
 import org.dromara.common.oss.constant.OssConstant;
 import org.dromara.common.oss.entity.UploadResult;
 import org.dromara.common.oss.enumd.AccessPolicyType;
 import org.dromara.common.oss.enumd.PolicyType;
 import org.dromara.common.oss.exception.OssException;
 import org.dromara.common.oss.properties.OssProperties;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.core.ResponseInputStream;
+import software.amazon.awssdk.core.async.AsyncRequestBody;
+import software.amazon.awssdk.core.async.AsyncResponseTransformer;
+import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.s3.S3AsyncClient;
+import software.amazon.awssdk.services.s3.S3Configuration;
+import software.amazon.awssdk.services.s3.model.GetObjectResponse;
+import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
+import software.amazon.awssdk.services.s3.model.S3Exception;
+import software.amazon.awssdk.services.s3.presigner.S3Presigner;
+import software.amazon.awssdk.transfer.s3.S3TransferManager;
+import software.amazon.awssdk.transfer.s3.model.*;
+import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
 
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.InputStream;
+import java.io.*;
+import java.net.URI;
 import java.net.URL;
-import java.util.Date;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Duration;
 
 /**
  * S3 瀛樺偍鍗忚 鎵�鏈夊吋瀹筍3鍗忚鐨勪簯鍘傚晢鍧囨敮鎸�
  * 闃块噷浜� 鑵捐浜� 涓冪墰浜� minio
  *
- * @author Lion Li
+ * @author AprilWind
  */
 public class OssClient {
 
+    /**
+     * 鏈嶅姟鍟�
+     */
     private final String configKey;
 
+    /**
+     * 閰嶇疆灞炴��
+     */
     private final OssProperties properties;
 
-    private final AmazonS3 client;
+    /**
+     * Amazon S3 寮傛瀹㈡埛绔�
+     */
+    private final S3AsyncClient client;
 
+    /**
+     * 鐢ㄤ簬绠$悊 S3 鏁版嵁浼犺緭鐨勯珮绾у伐鍏�
+     */
+    private final S3TransferManager transferManager;
+
+    /**
+     * AWS S3 棰勭鍚� URL 鐨勭敓鎴愬櫒
+     */
+    private final S3Presigner presigner;
+
+    /**
+     * 鏋勯�犳柟娉�
+     *
+     * @param configKey     閰嶇疆閿�
+     * @param ossProperties Oss閰嶇疆灞炴��
+     */
     public OssClient(String configKey, OssProperties ossProperties) {
         this.configKey = configKey;
         this.properties = ossProperties;
         try {
-            AwsClientBuilder.EndpointConfiguration endpointConfig =
-                new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion());
+            // 鍒涘缓 AWS 璁よ瘉淇℃伅
+            StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
+                AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()));
 
-            AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey());
-            AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
-            ClientConfiguration clientConfig = new ClientConfiguration();
-            if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) {
-                clientConfig.setProtocol(Protocol.HTTPS);
-            } else {
-                clientConfig.setProtocol(Protocol.HTTP);
-            }
-            AmazonS3ClientBuilder build = AmazonS3Client.builder()
-                .withEndpointConfiguration(endpointConfig)
-                .withClientConfiguration(clientConfig)
-                .withCredentials(credentialsProvider)
-                .disableChunkedEncoding();
-            if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) {
-                // minio 浣跨敤https闄愬埗浣跨敤鍩熷悕璁块棶 闇�瑕佹閰嶇疆 绔欑偣濉煙鍚�
-                build.enablePathStyleAccess();
-            }
-            this.client = build.build();
+            //MinIO 浣跨敤 HTTPS 闄愬埗浣跨敤鍩熷悕璁块棶锛岀珯鐐瑰~鍩熷悕銆傞渶瑕佸惎鐢ㄨ矾寰勬牱寮忚闂�
+            boolean isStyle = !StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE);
 
+            //鍒涘缓AWS鍩轰簬 CRT 鐨� S3 瀹㈡埛绔�
+            this.client = S3AsyncClient.crtBuilder()
+                .credentialsProvider(credentialsProvider)
+                .endpointOverride(URI.create(getEndpoint()))
+                .region(of())
+                .targetThroughputInGbps(20.0)
+                .minimumPartSizeInBytes(10 * 1025 * 1024L)
+                .checksumValidationEnabled(false)
+                .forcePathStyle(isStyle)
+                .build();
+
+            //AWS鍩轰簬 CRT 鐨� S3 AsyncClient 瀹炰緥鐢ㄤ綔 S3 浼犺緭绠$悊鍣ㄧ殑搴曞眰瀹㈡埛绔�
+            this.transferManager = S3TransferManager.builder().s3Client(this.client).build();
+
+            // 鍒涘缓 S3 閰嶇疆瀵硅薄
+            S3Configuration config = S3Configuration.builder().chunkedEncodingEnabled(false)
+                .pathStyleAccessEnabled(isStyle).build();
+
+            // 鍒涘缓 棰勭鍚� URL 鐨勭敓鎴愬櫒 瀹炰緥锛岀敤浜庣敓鎴� S3 棰勭鍚� URL
+            this.presigner = S3Presigner.builder()
+                .region(of())
+                .credentialsProvider(credentialsProvider)
+                .endpointOverride(URI.create(getDomain()))
+                .serviceConfiguration(config)
+                .build();
+
+            // 鍒涘缓瀛樺偍妗�
             createBucket();
         } catch (Exception e) {
             if (e instanceof OssException) {
@@ -78,126 +122,189 @@
         }
     }
 
+    /**
+     * 鍚屾鍒涘缓瀛樺偍妗�
+     * 濡傛灉瀛樺偍妗朵笉瀛樺湪锛屼細杩涜鍒涘缓锛涘鏋滃瓨鍌ㄦ《瀛樺湪锛屼笉鎵ц浠讳綍鎿嶄綔
+     *
+     * @throws OssException 褰撳垱寤哄瓨鍌ㄦ《鏃跺彂鐢熷紓甯告椂鎶涘嚭
+     */
     public void createBucket() {
+        String bucketName = properties.getBucketName();
         try {
-            String bucketName = properties.getBucketName();
-            if (client.doesBucketExistV2(bucketName)) {
-                return;
+            // 灏濊瘯鑾峰彇瀛樺偍妗剁殑淇℃伅
+            client.headBucket(
+                    x -> x.bucket(bucketName)
+                        .build())
+                .join();
+        } catch (Exception ex) {
+            if (ex.getCause() instanceof NoSuchBucketException) {
+                try {
+                    // 瀛樺偍妗朵笉瀛樺湪锛屽皾璇曞垱寤哄瓨鍌ㄦ《
+                    client.createBucket(
+                            x -> x.bucket(bucketName))
+                        .join();
+
+                    // 璁剧疆瀛樺偍妗剁殑璁块棶绛栫暐锛圔ucket Policy锛�
+                    client.putBucketPolicy(
+                            x -> x.bucket(bucketName)
+                                .policy(getPolicy(bucketName, getAccessPolicy().getPolicyType())))
+                        .join();
+                } catch (S3Exception e) {
+                    // 瀛樺偍妗跺垱寤烘垨绛栫暐璁剧疆澶辫触
+                    throw new OssException("鍒涘缓Bucket澶辫触, 璇锋牳瀵归厤缃俊鎭�:[" + e.getMessage() + "]");
+                }
+            } else {
+                throw new OssException("鍒ゆ柇Bucket鏄惁瀛樺湪澶辫触锛岃鏍稿閰嶇疆淇℃伅:[" + ex.getMessage() + "]");
             }
-            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
-            AccessPolicyType accessPolicy = getAccessPolicy();
-            createBucketRequest.setCannedAcl(accessPolicy.getAcl());
-            client.createBucket(createBucketRequest);
-            client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));
-        } catch (Exception e) {
-            throw new OssException("鍒涘缓Bucket澶辫触, 璇锋牳瀵归厤缃俊鎭�:[" + e.getMessage() + "]");
         }
     }
 
-    public UploadResult upload(byte[] data, String path, String contentType) {
-        return upload(new ByteArrayInputStream(data), path, contentType);
+    /**
+     * 涓婁紶鏂囦欢鍒� Amazon S3锛屽苟杩斿洖涓婁紶缁撴灉
+     *
+     * @param filePath  鏈湴鏂囦欢璺緞
+     * @param key       鍦� Amazon S3 涓殑瀵硅薄閿�
+     * @param md5Digest 鏈湴鏂囦欢鐨� MD5 鍝堝笇鍊硷紙鍙�夛級
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult upload(Path filePath, String key, String md5Digest) {
+        try {
+            // 鏋勫缓涓婁紶璇锋眰瀵硅薄
+            FileUpload fileUpload = transferManager.uploadFile(
+                x -> x.putObjectRequest(
+                        y -> y.bucket(properties.getBucketName())
+                            .key(key)
+                            .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null)
+                            .build())
+                    .addTransferListener(LoggingTransferListener.create())
+                    .source(filePath).build());
+
+            // 绛夊緟涓婁紶瀹屾垚骞惰幏鍙栦笂浼犵粨鏋�
+            CompletedFileUpload uploadResult = fileUpload.completionFuture().join();
+            String eTag = uploadResult.response().eTag();
+
+            // 鎻愬彇涓婁紶缁撴灉涓殑 ETag锛屽苟鏋勫缓涓�涓嚜瀹氫箟鐨� UploadResult 瀵硅薄
+            return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build();
+        } catch (Exception e) {
+            // 鎹曡幏寮傚父骞舵姏鍑鸿嚜瀹氫箟寮傚父
+            throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
+        } finally {
+            // 鏃犺涓婁紶鏄惁鎴愬姛锛屾渶缁堥兘浼氬垹闄や复鏃舵枃浠�
+            FileUtils.del(filePath);
+        }
     }
 
-    public UploadResult upload(InputStream inputStream, String path, String contentType) {
+    /**
+     * 涓婁紶 InputStream 鍒� Amazon S3
+     *
+     * @param inputStream 瑕佷笂浼犵殑杈撳叆娴�
+     * @param key         鍦� Amazon S3 涓殑瀵硅薄閿�
+     * @param length      杈撳叆娴佺殑闀垮害
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult upload(InputStream inputStream, String key, Long length) {
+        // 濡傛灉杈撳叆娴佷笉鏄� ByteArrayInputStream锛屽垯灏嗗叾璇诲彇涓哄瓧鑺傛暟缁勫啀鍒涘缓 ByteArrayInputStream
         if (!(inputStream instanceof ByteArrayInputStream)) {
             inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
         }
         try {
-            ObjectMetadata metadata = new ObjectMetadata();
-            metadata.setContentType(contentType);
-            metadata.setContentLength(inputStream.available());
-            PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata);
-            // 璁剧疆涓婁紶瀵硅薄鐨� Acl 涓哄叕鍏辫
-            putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
-            client.putObject(putObjectRequest);
+            // 鍒涘缓寮傛璇锋眰浣擄紙length濡傛灉涓虹┖浼氭姤閿欙級
+            BlockingInputStreamAsyncRequestBody body = AsyncRequestBody.forBlockingInputStream(length);
+
+            // 浣跨敤 transferManager 杩涜涓婁紶
+            Upload upload = transferManager.upload(
+                x -> x.requestBody(body)
+                    .putObjectRequest(
+                        y -> y.bucket(properties.getBucketName())
+                            .key(key)
+                            .build())
+                    .build());
+
+            // 灏嗚緭鍏ユ祦鍐欏叆璇锋眰浣�
+            body.writeInputStream(inputStream);
+
+            // 绛夊緟鏂囦欢涓婁紶鎿嶄綔瀹屾垚
+            CompletedUpload uploadResult = upload.completionFuture().join();
+            String eTag = uploadResult.response().eTag();
+
+            // 鎻愬彇涓婁紶缁撴灉涓殑 ETag锛屽苟鏋勫缓涓�涓嚜瀹氫箟鐨� UploadResult 瀵硅薄
+            return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build();
         } catch (Exception e) {
             throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
         }
-        return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
-    }
-
-    public UploadResult upload(File file, String path) {
-        try {
-            PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, file);
-            // 璁剧疆涓婁紶瀵硅薄鐨� Acl 涓哄叕鍏辫
-            putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
-            client.putObject(putObjectRequest);
-        } catch (Exception e) {
-            throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
-        }
-        return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
-    }
-
-    public void delete(String path) {
-        path = path.replace(getUrl() + "/", "");
-        try {
-            client.deleteObject(properties.getBucketName(), path);
-        } catch (Exception e) {
-            throw new OssException("鍒犻櫎鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
-        }
-    }
-
-    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
-        return upload(data, getPath(properties.getPrefix(), suffix), contentType);
-    }
-
-    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
-        return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
-    }
-
-    public UploadResult uploadSuffix(File file, String suffix) {
-        return upload(file, getPath(properties.getPrefix(), suffix));
     }
 
     /**
-     * 鑾峰彇鏂囦欢鍏冩暟鎹�
+     * 涓嬭浇鏂囦欢浠� Amazon S3 鍒颁复鏃剁洰褰�
      *
-     * @param path 瀹屾暣鏂囦欢璺緞
+     * @param path 鏂囦欢鍦� Amazon S3 涓殑瀵硅薄閿�
+     * @return 涓嬭浇鍚庣殑鏂囦欢鍦ㄦ湰鍦扮殑涓存椂璺緞
+     * @throws OssException 濡傛灉涓嬭浇澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
      */
-    public ObjectMetadata getObjectMetadata(String path) {
-        path = path.replace(getUrl() + "/", "");
-        S3Object object = client.getObject(properties.getBucketName(), path);
-        return object.getObjectMetadata();
+    public Path fileDownload(String path) {
+        // 鏋勫缓涓存椂鏂囦欢
+        Path tempFilePath = FileUtils.createTempFile().toPath();
+        // 浣跨敤 S3TransferManager 涓嬭浇鏂囦欢
+        FileDownload downloadFile = transferManager.downloadFile(
+            x -> x.getObjectRequest(
+                    y -> y.bucket(properties.getBucketName())
+                        .key(removeBaseUrl(path))
+                        .build())
+                .addTransferListener(LoggingTransferListener.create())
+                .destination(tempFilePath)
+                .build());
+        // 绛夊緟鏂囦欢涓嬭浇鎿嶄綔瀹屾垚
+        downloadFile.completionFuture().join();
+        return tempFilePath;
     }
 
-    public InputStream getObjectContent(String path) {
-        path = path.replace(getUrl() + "/", "");
-        S3Object object = client.getObject(properties.getBucketName(), path);
-        return object.getObjectContent();
-    }
-
-    public String getUrl() {
-        String domain = properties.getDomain();
-        String endpoint = properties.getEndpoint();
-        String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
-        // 浜戞湇鍔″晢鐩存帴杩斿洖
-        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
-            if (StringUtils.isNotBlank(domain)) {
-                return header + domain;
+    /**
+     * 涓嬭浇鏂囦欢浠� Amazon S3 鍒� 杈撳嚭娴�
+     *
+     * @param key 鏂囦欢鍦� Amazon S3 涓殑瀵硅薄閿�
+     * @param out 杈撳嚭娴�
+     * @return 杈撳嚭娴佷腑鍐欏叆鐨勫瓧鑺傛暟锛堥暱搴︼級
+     * @throws OssException 濡傛灉涓嬭浇澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public long download(String key, OutputStream out) {
+        try {
+            // 鏋勫缓涓嬭浇璇锋眰
+            DownloadRequest<ResponseInputStream<GetObjectResponse>> downloadRequest = DownloadRequest.builder()
+                // 鏂囦欢瀵硅薄
+                .getObjectRequest(y -> y.bucket(properties.getBucketName())
+                    .key(key)
+                    .build())
+                .addTransferListener(LoggingTransferListener.create())
+                // 浣跨敤璁㈤槄杞崲鍣�
+                .responseTransformer(AsyncResponseTransformer.toBlockingInputStream())
+                .build();
+            // 浣跨敤 S3TransferManager 涓嬭浇鏂囦欢
+            Download<ResponseInputStream<GetObjectResponse>> responseFuture = transferManager.download(downloadRequest);
+            // 杈撳嚭鍒版祦涓�
+            try (ResponseInputStream<GetObjectResponse> responseStream = responseFuture.completionFuture().join().result()) { // auto-closeable stream
+                return responseStream.transferTo(out); // 闃诲璋冪敤绾跨▼ blocks the calling thread
             }
-            return header + properties.getBucketName() + "." + endpoint;
+        } catch (Exception e) {
+            throw new OssException("鏂囦欢涓嬭浇澶辫触锛岄敊璇俊鎭�:[" + e.getMessage() + "]");
         }
-        // minio 鍗曠嫭澶勭悊
-        if (StringUtils.isNotBlank(domain)) {
-            return header + domain + "/" + properties.getBucketName();
-        }
-        return header + endpoint + "/" + properties.getBucketName();
     }
 
-    public String getPath(String prefix, String suffix) {
-        // 鐢熸垚uuid
-        String uuid = IdUtil.fastSimpleUUID();
-        // 鏂囦欢璺緞
-        String path = DateUtils.datePath() + "/" + uuid;
-        if (StringUtils.isNotBlank(prefix)) {
-            path = prefix + "/" + path;
+    /**
+     * 鍒犻櫎浜戝瓨鍌ㄦ湇鍔′腑鎸囧畾璺緞涓嬫枃浠�
+     *
+     * @param path 鎸囧畾璺緞
+     */
+    public void delete(String path) {
+        try {
+            client.deleteObject(
+                x -> x.bucket(properties.getBucketName())
+                    .key(removeBaseUrl(path))
+                    .build());
+        } catch (Exception e) {
+            throw new OssException("鍒犻櫎鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]");
         }
-        return path + suffix;
-    }
-
-
-    public String getConfigKey() {
-        return configKey;
     }
 
     /**
@@ -207,12 +314,187 @@
      * @param second    鎺堟潈鏃堕棿
      */
     public String getPrivateUrl(String objectKey, Integer second) {
-        GeneratePresignedUrlRequest generatePresignedUrlRequest =
-            new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
-                .withMethod(HttpMethod.GET)
-                .withExpiration(new Date(System.currentTimeMillis() + 1000L * second));
-        URL url = client.generatePresignedUrl(generatePresignedUrlRequest);
+        // 浣跨敤 AWS S3 棰勭鍚� URL 鐨勭敓鎴愬櫒 鑾峰彇瀵硅薄鐨勯绛惧悕 URL
+        URL url = presigner.presignGetObject(
+                x -> x.signatureDuration(Duration.ofSeconds(second))
+                    .getObjectRequest(
+                        y -> y.bucket(properties.getBucketName())
+                            .key(objectKey)
+                            .build())
+                    .build())
+            .url();
         return url.toString();
+    }
+
+    /**
+     * 涓婁紶 byte[] 鏁版嵁鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭銆�
+     *
+     * @param data   瑕佷笂浼犵殑 byte[] 鏁版嵁
+     * @param suffix 瀵硅薄閿殑鍚庣紑
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult uploadSuffix(byte[] data, String suffix) {
+        return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Long.valueOf(data.length));
+    }
+
+    /**
+     * 涓婁紶 InputStream 鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭銆�
+     *
+     * @param inputStream 瑕佷笂浼犵殑杈撳叆娴�
+     * @param suffix      瀵硅薄閿殑鍚庣紑
+     * @param length      杈撳叆娴佺殑闀垮害
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult uploadSuffix(InputStream inputStream, String suffix, Long length) {
+        return upload(inputStream, getPath(properties.getPrefix(), suffix), length);
+    }
+
+    /**
+     * 涓婁紶鏂囦欢鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭
+     *
+     * @param file   瑕佷笂浼犵殑鏂囦欢
+     * @param suffix 瀵硅薄閿殑鍚庣紑
+     * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅
+     * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父
+     */
+    public UploadResult uploadSuffix(File file, String suffix) {
+        return upload(file.toPath(), getPath(properties.getPrefix(), suffix), null);
+    }
+
+    /**
+     * 鑾峰彇鏂囦欢杈撳叆娴�
+     *
+     * @param path 瀹屾暣鏂囦欢璺緞
+     * @return 杈撳叆娴�
+     */
+    public InputStream getObjectContent(String path) throws IOException {
+        // 涓嬭浇鏂囦欢鍒颁复鏃剁洰褰�
+        Path tempFilePath = fileDownload(path);
+        // 鍒涘缓杈撳叆娴�
+        InputStream inputStream = Files.newInputStream(tempFilePath);
+        // 鍒犻櫎涓存椂鏂囦欢
+        FileUtils.del(tempFilePath);
+        // 杩斿洖瀵硅薄鍐呭鐨勮緭鍏ユ祦
+        return inputStream;
+    }
+
+    /**
+     * 鑾峰彇 S3 瀹㈡埛绔殑缁堢鐐� URL
+     *
+     * @return 缁堢鐐� URL
+     */
+    public String getEndpoint() {
+        // 鏍规嵁閰嶇疆鏂囦欢涓殑鏄惁浣跨敤 HTTPS锛岃缃崗璁ご閮�
+        String header = getIsHttps();
+        // 鎷兼帴鍗忚澶撮儴鍜岀粓绔偣锛屽緱鍒板畬鏁寸殑缁堢鐐� URL
+        return header + properties.getEndpoint();
+    }
+
+    /**
+     * 鑾峰彇 S3 瀹㈡埛绔殑缁堢鐐� URL锛堣嚜瀹氫箟鍩熷悕锛�
+     *
+     * @return 缁堢鐐� URL
+     */
+    public String getDomain() {
+        // 浠庨厤缃腑鑾峰彇鍩熷悕銆佺粓绔偣銆佹槸鍚︿娇鐢� HTTPS 绛変俊鎭�
+        String domain = properties.getDomain();
+        String endpoint = properties.getEndpoint();
+        String header = getIsHttps();
+
+        // 濡傛灉鏄簯鏈嶅姟鍟嗭紝鐩存帴杩斿洖鍩熷悕鎴栫粓绔偣
+        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
+            return StringUtils.isNotEmpty(domain) ? header + domain : header + endpoint;
+        }
+
+        // 濡傛灉鏄� MinIO锛屽鐞嗗煙鍚嶅苟杩斿洖
+        if (StringUtils.isNotEmpty(domain)) {
+            return domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP) ? domain : header + domain;
+        }
+
+        // 杩斿洖缁堢鐐�
+        return header + endpoint;
+    }
+
+    /**
+     * 鏍规嵁浼犲叆鐨� region 鍙傛暟杩斿洖鐩稿簲鐨� AWS 鍖哄煙
+     * 濡傛灉 region 鍙傛暟闈炵┖锛屼娇鐢� Region.of 鏂规硶鍒涘缓骞惰繑鍥炲搴旂殑 AWS 鍖哄煙瀵硅薄
+     * 濡傛灉 region 鍙傛暟涓虹┖锛岃繑鍥炰竴涓粯璁ょ殑 AWS 鍖哄煙锛堜緥濡傦紝us-east-1锛夛紝浣滀负骞挎硾鏀寔鐨勫尯鍩�
+     *
+     * @return 瀵瑰簲鐨� AWS 鍖哄煙瀵硅薄锛屾垨鑰呴粯璁ょ殑骞挎硾鏀寔鐨勫尯鍩燂紙us-east-1锛�
+     */
+    public Region of() {
+        //AWS 鍖哄煙瀛楃涓�
+        String region = properties.getRegion();
+        // 濡傛灉 region 鍙傛暟闈炵┖锛屼娇鐢� Region.of 鏂规硶鍒涘缓瀵瑰簲鐨� AWS 鍖哄煙瀵硅薄锛屽惁鍒欒繑鍥為粯璁ゅ尯鍩�
+        return StringUtils.isNotEmpty(region) ? Region.of(region) : Region.US_EAST_1;
+    }
+
+    /**
+     * 鑾峰彇浜戝瓨鍌ㄦ湇鍔$殑URL
+     *
+     * @return 鏂囦欢璺緞
+     */
+    public String getUrl() {
+        String domain = properties.getDomain();
+        String endpoint = properties.getEndpoint();
+        String header = getIsHttps();
+        // 浜戞湇鍔″晢鐩存帴杩斿洖
+        if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
+            return header + (StringUtils.isNotEmpty(domain) ? domain : properties.getBucketName() + "." + endpoint);
+        }
+        // MinIO 鍗曠嫭澶勭悊
+        if (StringUtils.isNotEmpty(domain)) {
+            // 濡傛灉 domain 浠� "https://" 鎴� "http://" 寮�澶�
+            return (domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP)) ?
+                domain + StringUtils.SLASH + properties.getBucketName() : header + domain + StringUtils.SLASH + properties.getBucketName();
+        }
+        return header + endpoint + StringUtils.SLASH + properties.getBucketName();
+    }
+
+    /**
+     * 鐢熸垚涓�涓鍚堢壒瀹氳鍒欑殑銆佸敮涓�鐨勬枃浠惰矾寰勩�傞�氳繃浣跨敤鏃ユ湡銆乁UID銆佸墠缂�鍜屽悗缂�绛夊厓绱犵殑缁勫悎锛岀‘淇濅簡鏂囦欢璺緞鐨勭嫭涓�鏃犱簩鎬�
+     *
+     * @param prefix 鍓嶇紑
+     * @param suffix 鍚庣紑
+     * @return 鏂囦欢璺緞
+     */
+    public String getPath(String prefix, String suffix) {
+        // 鐢熸垚uuid
+        String uuid = IdUtil.fastSimpleUUID();
+        // 鐢熸垚鏃ユ湡璺緞
+        String datePath = DateUtils.datePath();
+        // 鎷兼帴璺緞
+        String path = StringUtils.isNotEmpty(prefix) ?
+            prefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid : datePath + StringUtils.SLASH + uuid;
+        return path + suffix;
+    }
+
+    /**
+     * 绉婚櫎璺緞涓殑鍩虹URL閮ㄥ垎锛屽緱鍒扮浉瀵硅矾寰�
+     *
+     * @param path 瀹屾暣鐨勮矾寰勶紝鍖呮嫭鍩虹URL鍜岀浉瀵硅矾寰�
+     * @return 鍘婚櫎鍩虹URL鍚庣殑鐩稿璺緞
+     */
+    public String removeBaseUrl(String path) {
+        return path.replace(getUrl() + StringUtils.SLASH, "");
+    }
+
+    /**
+     * 鏈嶅姟鍟�
+     */
+    public String getConfigKey() {
+        return configKey;
+    }
+
+    /**
+     * 鑾峰彇鏄惁浣跨敤 HTTPS 鐨勯厤缃紝骞惰繑鍥炵浉搴旂殑鍗忚澶撮儴銆�
+     *
+     * @return 鍗忚澶撮儴锛屾牴鎹槸鍚︿娇鐢� HTTPS 杩斿洖 "https://" 鎴� "http://"
+     */
+    public String getIsHttps() {
+        return OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? Constants.HTTPS : Constants.HTTP;
     }
 
     /**
@@ -231,32 +513,77 @@
         return AccessPolicyType.getByType(properties.getAccessPolicy());
     }
 
+    /**
+     * 鐢熸垚 AWS S3 瀛樺偍妗惰闂瓥鐣�
+     *
+     * @param bucketName 瀛樺偍妗�
+     * @param policyType 妗剁瓥鐣ョ被鍨�
+     * @return 绗﹀悎 AWS S3 瀛樺偍妗惰闂瓥鐣ユ牸寮忕殑瀛楃涓�
+     */
     private static String getPolicy(String bucketName, PolicyType policyType) {
-        StringBuilder builder = new StringBuilder();
-        builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");
-        builder.append(switch (policyType) {
-            case WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n";
-            case READ_WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n";
-            default -> "\"s3:GetBucketLocation\"\n";
-        });
-        builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
-        builder.append(bucketName);
-        builder.append("\"\n},\n");
-        if (policyType == PolicyType.READ) {
-            builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
-            builder.append(bucketName);
-            builder.append("\"\n},\n");
-        }
-        builder.append("{\n\"Action\": ");
-        builder.append(switch (policyType) {
-            case WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n";
-            case READ_WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n";
-            default -> "\"s3:GetObject\",\n";
-        });
-        builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
-        builder.append(bucketName);
-        builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n");
-        return builder.toString();
+        String policy = switch (policyType) {
+            case WRITE -> """
+                {
+                  "Version": "2012-10-17",
+                  "Statement": []
+                }
+                """;
+            case READ_WRITE -> """
+                {
+                  "Version": "2012-10-17",
+                  "Statement": [
+                    {
+                      "Effect": "Allow",
+                      "Principal": "*",
+                      "Action": [
+                        "s3:GetBucketLocation",
+                        "s3:ListBucket",
+                        "s3:ListBucketMultipartUploads"
+                      ],
+                      "Resource": "arn:aws:s3:::bucketName"
+                    },
+                    {
+                      "Effect": "Allow",
+                      "Principal": "*",
+                      "Action": [
+                        "s3:AbortMultipartUpload",
+                        "s3:DeleteObject",
+                        "s3:GetObject",
+                        "s3:ListMultipartUploadParts",
+                        "s3:PutObject"
+                      ],
+                      "Resource": "arn:aws:s3:::bucketName/*"
+                    }
+                  ]
+                }
+                """;
+            case READ -> """
+                {
+                  "Version": "2012-10-17",
+                  "Statement": [
+                    {
+                      "Effect": "Allow",
+                      "Principal": "*",
+                      "Action": ["s3:GetBucketLocation"],
+                      "Resource": "arn:aws:s3:::bucketName"
+                    },
+                    {
+                      "Effect": "Deny",
+                      "Principal": "*",
+                      "Action": ["s3:ListBucket"],
+                      "Resource": "arn:aws:s3:::bucketName"
+                    },
+                    {
+                      "Effect": "Allow",
+                      "Principal": "*",
+                      "Action": "s3:GetObject",
+                      "Resource": "arn:aws:s3:::bucketName/*"
+                    }
+                  ]
+                }
+                """;
+        };
+        return policy.replaceAll("bucketName", bucketName);
     }
 
 }
diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java
index a6f57e5..81a18e6 100644
--- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java
+++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/entity/UploadResult.java
@@ -21,4 +21,10 @@
      * 鏂囦欢鍚�
      */
     private String filename;
+
+    /**
+     * 宸蹭笂浼犲璞$殑瀹炰綋鏍囪锛堢敤鏉ユ牎楠屾枃浠讹級
+     */
+    private String eTag;
+
 }
diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java
index 9074d72..6d39133 100644
--- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java
+++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/enumd/AccessPolicyType.java
@@ -1,8 +1,9 @@
 package org.dromara.common.oss.enumd;
 
-import com.amazonaws.services.s3.model.CannedAccessControlList;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
+import software.amazon.awssdk.services.s3.model.BucketCannedACL;
+import software.amazon.awssdk.services.s3.model.ObjectCannedACL;
 
 /**
  * 妗惰闂瓥鐣ラ厤缃�
@@ -16,27 +17,32 @@
     /**
      * private
      */
-    PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE),
+    PRIVATE("0", BucketCannedACL.PRIVATE, ObjectCannedACL.PRIVATE, PolicyType.WRITE),
 
     /**
      * public
      */
-    PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ),
+    PUBLIC("1", BucketCannedACL.PUBLIC_READ_WRITE, ObjectCannedACL.PUBLIC_READ_WRITE, PolicyType.READ_WRITE),
 
     /**
      * custom
      */
-    CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ);
+    CUSTOM("2", BucketCannedACL.PUBLIC_READ, ObjectCannedACL.PUBLIC_READ, PolicyType.READ);
 
     /**
-     * 妗� 鏉冮檺绫诲瀷
+     * 妗� 鏉冮檺绫诲瀷锛堟暟鎹簱鍊硷級
      */
     private final String type;
 
     /**
+     * 妗� 鏉冮檺绫诲瀷
+     */
+    private final BucketCannedACL bucketCannedACL;
+
+    /**
      * 鏂囦欢瀵硅薄 鏉冮檺绫诲瀷
      */
-    private final CannedAccessControlList acl;
+    private final ObjectCannedACL objectCannedACL;
 
     /**
      * 妗剁瓥鐣ョ被鍨�
diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java
index 763b090..d70270a 100644
--- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java
+++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java
@@ -1,5 +1,6 @@
 package org.dromara.common.oss.factory;
 
+import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.CacheNames;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.json.utils.JsonUtils;
@@ -9,10 +10,10 @@
 import org.dromara.common.oss.properties.OssProperties;
 import org.dromara.common.redis.utils.CacheUtils;
 import org.dromara.common.redis.utils.RedisUtils;
-import lombok.extern.slf4j.Slf4j;
 
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * 鏂囦欢涓婁紶Factory
@@ -23,6 +24,7 @@
 public class OssFactory {
 
     private static final Map<String, OssClient> CLIENT_CACHE = new ConcurrentHashMap<>();
+    private static final ReentrantLock LOCK = new ReentrantLock();
 
     /**
      * 鑾峰彇榛樿瀹炰緥
@@ -39,7 +41,7 @@
     /**
      * 鏍规嵁绫诲瀷鑾峰彇瀹炰緥
      */
-    public static synchronized OssClient instance(String configKey) {
+    public static OssClient instance(String configKey) {
         String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
         if (json == null) {
             throw new OssException("绯荤粺寮傚父, '" + configKey + "'閰嶇疆淇℃伅涓嶅瓨鍦�!");
@@ -48,16 +50,19 @@
         // 浣跨敤绉熸埛鏍囪瘑閬垮厤澶氫釜绉熸埛鐩稿悓key瀹炰緥瑕嗙洊
         String key = properties.getTenantId() + ":" + configKey;
         OssClient client = CLIENT_CACHE.get(key);
-        if (client == null) {
-            CLIENT_CACHE.put(key, new OssClient(configKey, properties));
-            log.info("鍒涘缓OSS瀹炰緥 key => {}", configKey);
-            return CLIENT_CACHE.get(key);
-        }
-        // 閰嶇疆涓嶇浉鍚屽垯閲嶆柊鏋勫缓
-        if (!client.checkPropertiesSame(properties)) {
-            CLIENT_CACHE.put(key, new OssClient(configKey, properties));
-            log.info("閲嶈浇OSS瀹炰緥 key => {}", configKey);
-            return CLIENT_CACHE.get(key);
+        // 瀹㈡埛绔笉瀛樺湪鎴栭厤缃笉鐩稿悓鍒欓噸鏂版瀯寤�
+        if (client == null || !client.checkPropertiesSame(properties)) {
+            LOCK.lock();
+            try {
+                client = CLIENT_CACHE.get(key);
+                if (client == null || !client.checkPropertiesSame(properties)) {
+                    CLIENT_CACHE.put(key, new OssClient(configKey, properties));
+                    log.info("鍒涘缓OSS瀹炰緥 key => {}", configKey);
+                    return CLIENT_CACHE.get(key);
+                }
+            } finally {
+                LOCK.unlock();
+            }
         }
         return client;
     }
diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java
index 8f3a5ca..02735b0 100644
--- a/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java
+++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/java/org/dromara/common/ratelimiter/aspectj/RateLimiterAspect.java
@@ -1,29 +1,29 @@
 package org.dromara.common.ratelimiter.aspectj;
 
-import cn.hutool.core.util.ArrayUtil;
-import org.dromara.common.core.constant.GlobalConstants;
-import org.dromara.common.core.exception.ServiceException;
-import org.dromara.common.core.utils.MessageUtils;
-import org.dromara.common.core.utils.ServletUtils;
-import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.ratelimiter.annotation.RateLimiter;
-import org.dromara.common.ratelimiter.enums.LimitType;
-import org.dromara.common.redis.utils.RedisUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
 import org.aspectj.lang.reflect.MethodSignature;
+import org.dromara.common.core.constant.GlobalConstants;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.MessageUtils;
+import org.dromara.common.core.utils.ServletUtils;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.ratelimiter.annotation.RateLimiter;
+import org.dromara.common.ratelimiter.enums.LimitType;
+import org.dromara.common.redis.utils.RedisUtils;
 import org.redisson.api.RateType;
+import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.context.expression.MethodBasedEvaluationContext;
 import org.springframework.core.DefaultParameterNameDiscoverer;
 import org.springframework.core.ParameterNameDiscoverer;
-import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.ParserContext;
 import org.springframework.expression.common.TemplateParserContext;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
-import org.springframework.expression.spel.support.StandardEvaluationContext;
 
 import java.lang.reflect.Method;
 
@@ -45,20 +45,17 @@
      */
     private final ParserContext parserContext = new TemplateParserContext();
     /**
-     * 瀹氫箟spel涓婁笅鏂囧璞¤繘琛岃В鏋�
-     */
-    private final EvaluationContext context = new StandardEvaluationContext();
-    /**
      * 鏂规硶鍙傛暟瑙f瀽鍣�
      */
     private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
 
+
     @Before("@annotation(rateLimiter)")
-    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
+    public void doBefore(JoinPoint point, RateLimiter rateLimiter) {
         int time = rateLimiter.time();
         int count = rateLimiter.count();
-        String combineKey = getCombineKey(rateLimiter, point);
         try {
+            String combineKey = getCombineKey(rateLimiter, point);
             RateType rateType = RateType.OVERALL;
             if (rateLimiter.limitType() == LimitType.CLUSTER) {
                 rateType = RateType.PER_CLIENT;
@@ -76,42 +73,29 @@
             if (e instanceof ServiceException) {
                 throw e;
             } else {
-                throw new RuntimeException("鏈嶅姟鍣ㄩ檺娴佸紓甯革紝璇风◢鍊欏啀璇�");
+                throw new RuntimeException("鏈嶅姟鍣ㄩ檺娴佸紓甯革紝璇风◢鍊欏啀璇�", e);
             }
         }
     }
 
-    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
+    private String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
         String key = rateLimiter.key();
-        // 鑾峰彇鏂规硶(閫氳繃鏂规硶绛惧悕鏉ヨ幏鍙�)
-        MethodSignature signature = (MethodSignature) point.getSignature();
-        Method method = signature.getMethod();
-        Class<?> targetClass = method.getDeclaringClass();
-        // 鍒ゆ柇鏄惁鏄痵pel鏍煎紡
-        if (StringUtils.containsAny(key, "#")) {
-            // 鑾峰彇鍙傛暟鍊�
+        if (StringUtils.isNotBlank(key)) {
+            MethodSignature signature = (MethodSignature) point.getSignature();
+            Method targetMethod = signature.getMethod();
             Object[] args = point.getArgs();
-            // 鑾峰彇鏂规硶涓婂弬鏁扮殑鍚嶇О
-            String[] parameterNames = pnd.getParameterNames(method);
-            if (ArrayUtil.isEmpty(parameterNames)) {
-                throw new ServiceException("闄愭祦key瑙f瀽寮傚父!璇疯仈绯荤鐞嗗憳!");
+            //noinspection DataFlowIssue
+            MethodBasedEvaluationContext context =
+                new MethodBasedEvaluationContext(null, targetMethod, args, pnd);
+            context.setBeanResolver(new BeanFactoryResolver(SpringUtils.getBeanFactory()));
+            Expression expression;
+            if (StringUtils.startsWith(key, parserContext.getExpressionPrefix())
+                && StringUtils.endsWith(key, parserContext.getExpressionSuffix())) {
+                expression = parser.parseExpression(key, parserContext);
+            } else {
+                expression = parser.parseExpression(key);
             }
-            for (int i = 0; i < parameterNames.length; i++) {
-                context.setVariable(parameterNames[i], args[i]);
-            }
-            // 瑙f瀽杩斿洖缁檏ey
-            try {
-                Expression expression;
-                if (StringUtils.startsWith(key, parserContext.getExpressionPrefix())
-                    && StringUtils.endsWith(key, parserContext.getExpressionSuffix())) {
-                    expression = parser.parseExpression(key, parserContext);
-                } else {
-                    expression = parser.parseExpression(key);
-                }
-                key = expression.getValue(context, String.class) + ":";
-            } catch (Exception e) {
-                throw new ServiceException("闄愭祦key瑙f瀽寮傚父!璇疯仈绯荤鐞嗗憳!");
-            }
+            key = expression.getValue(context, String.class);
         }
         StringBuilder stringBuffer = new StringBuilder(GlobalConstants.RATE_LIMIT_KEY);
         stringBuffer.append(ServletUtils.getRequest().getRequestURI()).append(":");
diff --git a/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json b/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json
new file mode 100644
index 0000000..b16432b
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-ratelimiter/src/main/resources/spel-extension.json
@@ -0,0 +1,7 @@
+{
+  "org.dromara.common.ratelimiter.annotation.RateLimiter@key": {
+    "method": {
+      "parameters": true
+    }
+  }
+}
diff --git a/ruoyi-common/ruoyi-common-redis/pom.xml b/ruoyi-common/ruoyi-common-redis/pom.xml
index 5e5c277..f56f8aa 100644
--- a/ruoyi-common/ruoyi-common-redis/pom.xml
+++ b/ruoyi-common/ruoyi-common-redis/pom.xml
@@ -32,6 +32,16 @@
             <groupId>com.baomidou</groupId>
             <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java
new file mode 100644
index 0000000..d57ba4e
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java
@@ -0,0 +1,45 @@
+package org.dromara.common.redis.config;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.dromara.common.redis.manager.PlusSpringCacheManager;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 缂撳瓨閰嶇疆
+ *
+ * @author Lion Li
+ */
+@AutoConfiguration
+@EnableCaching
+public class CacheConfig {
+
+    /**
+     * caffeine 鏈湴缂撳瓨澶勭悊鍣�
+     */
+    @Bean
+    public Cache<Object, Object> caffeine() {
+        return Caffeine.newBuilder()
+            // 璁剧疆鏈�鍚庝竴娆″啓鍏ユ垨璁块棶鍚庣粡杩囧浐瀹氭椂闂磋繃鏈�
+            .expireAfterWrite(30, TimeUnit.SECONDS)
+            // 鍒濆鐨勭紦瀛樼┖闂村ぇ灏�
+            .initialCapacity(100)
+            // 缂撳瓨鐨勬渶澶ф潯鏁�
+            .maximumSize(1000)
+            .build();
+    }
+
+    /**
+     * 鑷畾涔夌紦瀛樼鐞嗗櫒 鏁村悎spring-cache
+     */
+    @Bean
+    public CacheManager cacheManager() {
+        return new PlusSpringCacheManager();
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java
index 7999f2b..f8fe79a 100644
--- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java
@@ -5,10 +5,14 @@
 import com.fasterxml.jackson.annotation.PropertyAccessor;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.redis.config.properties.RedissonProperties;
 import org.dromara.common.redis.handler.KeyPrefixHandler;
-import org.dromara.common.redis.manager.PlusSpringCacheManager;
+import org.dromara.common.redis.handler.RedisExceptionHandler;
 import org.redisson.client.codec.StringCodec;
 import org.redisson.codec.CompositeCodec;
 import org.redisson.codec.TypedJsonJacksonCodec;
@@ -16,9 +20,12 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.cache.CacheManager;
-import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.context.annotation.Bean;
+import org.springframework.core.task.VirtualThreadTaskExecutor;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.TimeZone;
 
 /**
  * redis閰嶇疆
@@ -27,20 +34,22 @@
  */
 @Slf4j
 @AutoConfiguration
-@EnableCaching
 @EnableConfigurationProperties(RedissonProperties.class)
 public class RedisConfig {
 
     @Autowired
     private RedissonProperties redissonProperties;
 
-    @Autowired
-    private ObjectMapper objectMapper;
-
     @Bean
     public RedissonAutoConfigurationCustomizer redissonCustomizer() {
         return config -> {
-            ObjectMapper om = objectMapper.copy();
+            JavaTimeModule javaTimeModule = new JavaTimeModule();
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
+            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
+            ObjectMapper om = new ObjectMapper();
+            om.registerModule(javaTimeModule);
+            om.setTimeZone(TimeZone.getDefault());
             om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
             // 鎸囧畾搴忓垪鍖栬緭鍏ョ殑绫诲瀷锛岀被蹇呴』鏄潪final淇グ鐨勩�傚簭鍒楀寲鏃跺皢瀵硅薄鍏ㄧ被鍚嶄竴璧蜂繚瀛樹笅鏉�
             om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
@@ -52,6 +61,9 @@
                 // 缂撳瓨 Lua 鑴氭湰 鍑忓皯缃戠粶浼犺緭(redisson 澶ч儴鍒嗙殑鍔熻兘閮芥槸鍩轰簬 Lua 鑴氭湰瀹炵幇)
                 .setUseScriptCache(true)
                 .setCodec(codec);
+            if (SpringUtils.isVirtual()) {
+                config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-"));
+            }
             RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
             if (ObjectUtil.isNotNull(singleServerConfig)) {
                 // 浣跨敤鍗曟満妯″紡
@@ -87,11 +99,11 @@
     }
 
     /**
-     * 鑷畾涔夌紦瀛樼鐞嗗櫒 鏁村悎spring-cache
+     * 寮傚父澶勭悊鍣�
      */
     @Bean
-    public CacheManager cacheManager() {
-        return new PlusSpringCacheManager();
+    public RedisExceptionHandler redisExceptionHandler() {
+        return new RedisExceptionHandler();
     }
 
     /**
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/RedisExceptionHandler.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/RedisExceptionHandler.java
new file mode 100644
index 0000000..5ed12a6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/handler/RedisExceptionHandler.java
@@ -0,0 +1,30 @@
+package org.dromara.common.redis.handler;
+
+import cn.hutool.http.HttpStatus;
+import com.baomidou.lock.exception.LockFailureException;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.R;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * Redis寮傚父澶勭悊鍣�
+ *
+ * @author AprilWind
+ */
+@Slf4j
+@RestControllerAdvice
+public class RedisExceptionHandler {
+
+    /**
+     * 鍒嗗竷寮忛攣Lock4j寮傚父
+     */
+    @ExceptionHandler(LockFailureException.class)
+    public R<Void> handleLockFailureException(LockFailureException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("鑾峰彇閿佸け璐ヤ簡'{}',鍙戠敓Lock4j寮傚父." + requestURI, e.getMessage());
+        return R.fail(HttpStatus.HTTP_UNAVAILABLE, "涓氬姟澶勭悊涓紝璇风◢鍚庡啀璇�...");
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java
new file mode 100644
index 0000000..ee1d405
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java
@@ -0,0 +1,90 @@
+package org.dromara.common.redis.manager;
+
+import org.dromara.common.core.utils.SpringUtils;
+import org.springframework.cache.Cache;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Cache 瑁呴グ鍣ㄦā寮�(鐢ㄤ簬鎵╁睍 Caffeine 涓�绾х紦瀛�)
+ *
+ * @author LionLi
+ */
+public class CaffeineCacheDecorator implements Cache {
+
+    private static final com.github.benmanes.caffeine.cache.Cache<Object, Object>
+        CAFFEINE = SpringUtils.getBean("caffeine");
+
+    private final Cache cache;
+
+    public CaffeineCacheDecorator(Cache cache) {
+        this.cache = cache;
+    }
+
+    @Override
+    public String getName() {
+        return cache.getName();
+    }
+
+    @Override
+    public Object getNativeCache() {
+        return cache.getNativeCache();
+    }
+
+    public String getUniqueKey(Object key) {
+        return cache.getName() + ":" + key;
+    }
+
+    @Override
+    public ValueWrapper get(Object key) {
+        Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key));
+        return (ValueWrapper) o;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T get(Object key, Class<T> type) {
+        Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type));
+        return (T) o;
+    }
+
+    @Override
+    public void put(Object key, Object value) {
+        CAFFEINE.invalidate(getUniqueKey(key));
+        cache.put(key, value);
+    }
+
+    public ValueWrapper putIfAbsent(Object key, Object value) {
+        CAFFEINE.invalidate(getUniqueKey(key));
+        return cache.putIfAbsent(key, value);
+    }
+
+    @Override
+    public void evict(Object key) {
+        evictIfPresent(key);
+    }
+
+    public boolean evictIfPresent(Object key) {
+        boolean b = cache.evictIfPresent(key);
+        if (b) {
+            CAFFEINE.invalidate(getUniqueKey(key));
+        }
+        return b;
+    }
+
+    @Override
+    public void clear() {
+        cache.clear();
+    }
+
+    public boolean invalidate() {
+        return cache.invalidate();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T get(Object key, Callable<T> valueLoader) {
+        Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, valueLoader));
+        return (T) o;
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java
index 08ebc78..a48cb14 100644
--- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java
@@ -156,7 +156,7 @@
     private Cache createMap(String name, CacheConfig config) {
         RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
 
-        Cache cache = new RedissonCache(map, allowNullValues);
+        Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, allowNullValues));
         if (transactionAware) {
             cache = new TransactionAwareCacheDecorator(cache);
         }
@@ -170,7 +170,7 @@
     private Cache createMapCache(String name, CacheConfig config) {
         RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
 
-        Cache cache = new RedissonCache(map, config, allowNullValues);
+        Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, config, allowNullValues));
         if (transactionAware) {
             cache = new TransactionAwareCacheDecorator(cache);
         }
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java
index ce32055..67be2fb 100644
--- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java
+++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java
@@ -65,6 +65,12 @@
         consumer.accept(msg);
     }
 
+    /**
+     * 鍙戝竷娑堟伅鍒版寚瀹氱殑棰戦亾
+     *
+     * @param channelKey 閫氶亾key
+     * @param msg        鍙戦�佹暟鎹�
+     */
     public static <T> void publish(String channelKey, T msg) {
         RTopic topic = CLIENT.getTopic(channelKey);
         topic.publish(msg);
@@ -107,7 +113,11 @@
                 bucket.setAndKeepTTL(value);
             } catch (Exception e) {
                 long timeToLive = bucket.remainTimeToLive();
-                setCacheObject(key, value, Duration.ofMillis(timeToLive));
+                if (timeToLive == -1) {
+                    setCacheObject(key, value);
+                } else {
+                    setCacheObject(key, value, Duration.ofMillis(timeToLive));
+                }
             }
         } else {
             bucket.set(value);
diff --git a/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 59192c5..0475b19 100644
--- a/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/ruoyi-common/ruoyi-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1,2 @@
 org.dromara.common.redis.config.RedisConfig
+org.dromara.common.redis.config.CacheConfig
diff --git a/ruoyi-common/ruoyi-common-satoken/pom.xml b/ruoyi-common/ruoyi-common-satoken/pom.xml
index 01c282b..4df7d4d 100644
--- a/ruoyi-common/ruoyi-common-satoken/pom.xml
+++ b/ruoyi-common/ruoyi-common-satoken/pom.xml
@@ -36,6 +36,11 @@
             <artifactId>sa-token-jwt</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java
index ce8d85c..61c6b9a 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/config/SaTokenConfig.java
@@ -7,6 +7,7 @@
 import org.dromara.common.core.factory.YmlPropertySourceFactory;
 import org.dromara.common.satoken.core.dao.PlusSaTokenDao;
 import org.dromara.common.satoken.core.service.SaPermissionImpl;
+import org.dromara.common.satoken.handler.SaTokenExceptionHandler;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.PropertySource;
@@ -42,4 +43,12 @@
         return new PlusSaTokenDao();
     }
 
+    /**
+     * 寮傚父澶勭悊鍣�
+     */
+    @Bean
+    public SaTokenExceptionHandler saTokenExceptionHandler() {
+        return new SaTokenExceptionHandler();
+    }
+
 }
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java
index d885962..0664755 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java
@@ -2,26 +2,42 @@
 
 import cn.dev33.satoken.dao.SaTokenDao;
 import cn.dev33.satoken.util.SaFoxUtil;
+import cn.hutool.core.lang.Console;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
 import org.dromara.common.redis.utils.RedisUtils;
 
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Sa-Token鎸佷箙灞傛帴鍙�(浣跨敤妗嗘灦鑷甫RedisUtils瀹炵幇 鍗忚缁熶竴)
+ * <p>
+ * 閲囩敤 caffeine + redis 澶氱骇缂撳瓨 浼樺寲骞跺彂鏌ヨ鏁堢巼
  *
  * @author Lion Li
  */
 public class PlusSaTokenDao implements SaTokenDao {
+
+    private static final Cache<String, Object> CAFFEINE = Caffeine.newBuilder()
+        // 璁剧疆鏈�鍚庝竴娆″啓鍏ユ垨璁块棶鍚庣粡杩囧浐瀹氭椂闂磋繃鏈�
+        .expireAfterWrite(5, TimeUnit.SECONDS)
+        // 鍒濆鐨勭紦瀛樼┖闂村ぇ灏�
+        .initialCapacity(100)
+        // 缂撳瓨鐨勬渶澶ф潯鏁�
+        .maximumSize(1000)
+        .build();
 
     /**
      * 鑾峰彇Value锛屽鏃犺繑绌�
      */
     @Override
     public String get(String key) {
-        return RedisUtils.getCacheObject(key);
+        Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
+        return (String) o;
     }
 
     /**
@@ -38,6 +54,7 @@
         } else {
             RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout));
         }
+        CAFFEINE.put(key, value);
     }
 
     /**
@@ -47,6 +64,7 @@
     public void update(String key, String value) {
         if (RedisUtils.hasKey(key)) {
             RedisUtils.setCacheObject(key, value, true);
+            CAFFEINE.put(key, value);
         }
     }
 
@@ -81,7 +99,8 @@
      */
     @Override
     public Object getObject(String key) {
-        return RedisUtils.getCacheObject(key);
+        Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key));
+        return o;
     }
 
     /**
@@ -98,6 +117,7 @@
         } else {
             RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout));
         }
+        CAFFEINE.put(key, object);
     }
 
     /**
@@ -107,6 +127,7 @@
     public void updateObject(String key, Object object) {
         if (RedisUtils.hasKey(key)) {
             RedisUtils.setCacheObject(key, object, true);
+            CAFFEINE.put(key, object);
         }
     }
 
@@ -139,10 +160,14 @@
     /**
      * 鎼滅储鏁版嵁
      */
+    @SuppressWarnings("unchecked")
     @Override
     public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
-        Collection<String> keys = RedisUtils.keys(prefix + "*" + keyword + "*");
-        List<String> list = new ArrayList<>(keys);
-        return SaFoxUtil.searchList(list, start, size, sortType);
+        String keyStr = prefix + "*" + keyword + "*";
+        return (List<String>) CAFFEINE.get(keyStr, k -> {
+            Collection<String> keys = RedisUtils.keys(keyStr);
+            List<String> list = new ArrayList<>(keys);
+            return SaFoxUtil.searchList(list, start, size, sortType);
+        });
     }
 }
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java
new file mode 100644
index 0000000..a45af89
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java
@@ -0,0 +1,52 @@
+package org.dromara.common.satoken.handler;
+
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.dev33.satoken.exception.NotPermissionException;
+import cn.dev33.satoken.exception.NotRoleException;
+import cn.hutool.http.HttpStatus;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.R;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * SaToken寮傚父澶勭悊鍣�
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@RestControllerAdvice
+public class SaTokenExceptionHandler {
+
+    /**
+     * 鏉冮檺鐮佸紓甯�
+     */
+    @ExceptionHandler(NotPermissionException.class)
+    public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("璇锋眰鍦板潃'{}',鏉冮檺鐮佹牎楠屽け璐�'{}'", requestURI, e.getMessage());
+        return R.fail(HttpStatus.HTTP_FORBIDDEN, "娌℃湁璁块棶鏉冮檺锛岃鑱旂郴绠$悊鍛樻巿鏉�");
+    }
+
+    /**
+     * 瑙掕壊鏉冮檺寮傚父
+     */
+    @ExceptionHandler(NotRoleException.class)
+    public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("璇锋眰鍦板潃'{}',瑙掕壊鏉冮檺鏍¢獙澶辫触'{}'", requestURI, e.getMessage());
+        return R.fail(HttpStatus.HTTP_FORBIDDEN, "娌℃湁璁块棶鏉冮檺锛岃鑱旂郴绠$悊鍛樻巿鏉�");
+    }
+
+    /**
+     * 璁よ瘉澶辫触
+     */
+    @ExceptionHandler(NotLoginException.class)
+    public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("璇锋眰鍦板潃'{}',璁よ瘉澶辫触'{}',鏃犳硶璁块棶绯荤粺璧勬簮", requestURI, e.getMessage());
+        return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "璁よ瘉澶辫触锛屾棤娉曡闂郴缁熻祫婧�");
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
index 8488ac1..1e0d8a7 100644
--- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
+++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java
@@ -1,7 +1,5 @@
 package org.dromara.common.satoken.utils;
 
-import cn.dev33.satoken.context.SaHolder;
-import cn.dev33.satoken.context.model.SaStorage;
 import cn.dev33.satoken.session.SaSession;
 import cn.dev33.satoken.stp.SaLoginModel;
 import cn.dev33.satoken.stp.StpUtil;
@@ -15,7 +13,6 @@
 import org.dromara.common.core.enums.UserType;
 
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * 鐧诲綍閴存潈鍔╂墜
@@ -35,9 +32,11 @@
     public static final String LOGIN_USER_KEY = "loginUser";
     public static final String TENANT_KEY = "tenantId";
     public static final String USER_KEY = "userId";
+    public static final String USER_NAME_KEY = "userName";
     public static final String DEPT_KEY = "deptId";
+    public static final String DEPT_NAME_KEY = "deptName";
+    public static final String DEPT_CATEGORY_KEY = "deptCategory";
     public static final String CLIENT_KEY = "clientid";
-    public static final String TENANT_ADMIN_KEY = "isTenantAdmin";
 
     /**
      * 鐧诲綍绯荤粺 鍩轰簬 璁惧绫诲瀷
@@ -47,32 +46,27 @@
      * @param model     閰嶇疆鍙傛暟
      */
     public static void login(LoginUser loginUser, SaLoginModel model) {
-        SaStorage storage = SaHolder.getStorage();
-        storage.set(LOGIN_USER_KEY, loginUser);
-        storage.set(TENANT_KEY, loginUser.getTenantId());
-        storage.set(USER_KEY, loginUser.getUserId());
-        storage.set(DEPT_KEY, loginUser.getDeptId());
         model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
         StpUtil.login(loginUser.getLoginId(),
             model.setExtra(TENANT_KEY, loginUser.getTenantId())
                 .setExtra(USER_KEY, loginUser.getUserId())
-                .setExtra(DEPT_KEY, loginUser.getDeptId()));
-        SaSession tokenSession = StpUtil.getTokenSession();
-        tokenSession.updateTimeout(model.getTimeout());
-        tokenSession.set(LOGIN_USER_KEY, loginUser);
+                .setExtra(USER_NAME_KEY, loginUser.getUsername())
+                .setExtra(DEPT_KEY, loginUser.getDeptId())
+                .setExtra(DEPT_NAME_KEY, loginUser.getDeptName())
+                .setExtra(DEPT_CATEGORY_KEY, loginUser.getDeptCategory())
+        );
+        StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
     }
 
     /**
      * 鑾峰彇鐢ㄦ埛(澶氱骇缂撳瓨)
      */
     public static LoginUser getLoginUser() {
-        return (LoginUser) getStorageIfAbsentSet(LOGIN_USER_KEY, () -> {
-            SaSession session = StpUtil.getTokenSession();
-            if (ObjectUtil.isNull(session)) {
-                return null;
-            }
-            return session.get(LOGIN_USER_KEY);
-        });
+        SaSession session = StpUtil.getTokenSession();
+        if (ObjectUtil.isNull(session)) {
+            return null;
+        }
+        return (LoginUser) session.get(LOGIN_USER_KEY);
     }
 
     /**
@@ -90,7 +84,7 @@
      * 鑾峰彇鐢ㄦ埛id
      */
     public static Long getUserId() {
-        return  Convert.toLong(getExtra(USER_KEY));
+        return Convert.toLong(getExtra(USER_KEY));
     }
 
     /**
@@ -107,8 +101,32 @@
         return Convert.toLong(getExtra(DEPT_KEY));
     }
 
+    /**
+     * 鑾峰彇閮ㄩ棬鍚�
+     */
+    public static String getDeptName() {
+        return Convert.toStr(getExtra(DEPT_NAME_KEY));
+    }
+
+    /**
+     * 鑾峰彇閮ㄩ棬绫诲埆缂栫爜
+     */
+    public static String getDeptCategory() {
+        return Convert.toStr(getExtra(DEPT_CATEGORY_KEY));
+    }
+
+    /**
+     * 鑾峰彇褰撳墠 Token 鐨勬墿灞曚俊鎭�
+     *
+     * @param key 閿��
+     * @return 瀵瑰簲鐨勬墿灞曟暟鎹�
+     */
     private static Object getExtra(String key) {
-        return getStorageIfAbsentSet(key, () -> StpUtil.getExtra(key));
+        try {
+            return StpUtil.getExtra(key);
+        } catch (Exception e) {
+            return null;
+        }
     }
 
     /**
@@ -136,12 +154,17 @@
         return UserConstants.SUPER_ADMIN_ID.equals(userId);
     }
 
+    /**
+     * 鏄惁涓鸿秴绾х鐞嗗憳
+     *
+     * @return 缁撴灉
+     */
     public static boolean isSuperAdmin() {
         return isSuperAdmin(getUserId());
     }
 
     /**
-     * 鏄惁涓鸿秴绾х鐞嗗憳
+     * 鏄惁涓虹鎴风鐞嗗憳
      *
      * @param rolePermission 瑙掕壊鏉冮檺鏍囪瘑缁�
      * @return 缁撴灉
@@ -150,27 +173,22 @@
         return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
     }
 
+    /**
+     * 鏄惁涓虹鎴风鐞嗗憳
+     *
+     * @return 缁撴灉
+     */
     public static boolean isTenantAdmin() {
-        Object value = getStorageIfAbsentSet(TENANT_ADMIN_KEY, () -> {
-            return isTenantAdmin(getLoginUser().getRolePermission());
-        });
-        return Convert.toBool(value);
+        return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission()));
     }
 
+    /**
+     * 妫�鏌ュ綋鍓嶇敤鎴锋槸鍚﹀凡鐧诲綍
+     *
+     * @return 缁撴灉
+     */
     public static boolean isLogin() {
         return getLoginUser() != null;
     }
 
-    public static Object getStorageIfAbsentSet(String key, Supplier<Object> handle) {
-        try {
-            Object obj = SaHolder.getStorage().get(key);
-            if (ObjectUtil.isNull(obj)) {
-                obj = handle.get();
-                SaHolder.getStorage().set(key, obj);
-            }
-            return obj;
-        } catch (Exception e) {
-            return null;
-        }
-    }
 }
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java
index 63d7eb1..b9283e0 100644
--- a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java
+++ b/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/config/SecurityConfig.java
@@ -4,14 +4,14 @@
 import cn.dev33.satoken.interceptor.SaInterceptor;
 import cn.dev33.satoken.router.SaRouter;
 import cn.dev33.satoken.stp.StpUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.utils.ServletUtils;
 import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.common.security.config.properties.SecurityProperties;
 import org.dromara.common.security.handler.AllUrlHandler;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@@ -38,35 +38,35 @@
     public void addInterceptors(InterceptorRegistry registry) {
         // 娉ㄥ唽璺敱鎷︽埅鍣紝鑷畾涔夐獙璇佽鍒�
         registry.addInterceptor(new SaInterceptor(handler -> {
-            AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
-            // 鐧诲綍楠岃瘉 -- 鎺掗櫎澶氫釜璺緞
-            SaRouter
-                // 鑾峰彇鎵�鏈夌殑
-                .match(allUrlHandler.getUrls())
-                // 瀵规湭鎺掗櫎鐨勮矾寰勮繘琛屾鏌�
-                .check(() -> {
-                    // 妫�鏌ユ槸鍚︾櫥褰� 鏄惁鏈塼oken
-                    StpUtil.checkLogin();
+                AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
+                // 鐧诲綍楠岃瘉 -- 鎺掗櫎澶氫釜璺緞
+                SaRouter
+                    // 鑾峰彇鎵�鏈夌殑
+                    .match(allUrlHandler.getUrls())
+                    // 瀵规湭鎺掗櫎鐨勮矾寰勮繘琛屾鏌�
+                    .check(() -> {
+                        // 妫�鏌ユ槸鍚︾櫥褰� 鏄惁鏈塼oken
+                        StpUtil.checkLogin();
 
-                    // 妫�鏌� header 涓� param 閲岀殑 clientid 涓� token 閲岀殑鏄惁涓�鑷�
-                    String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY);
-                    String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY);
-                    String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString();
-                    if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) {
-                        // token 鏃犳晥
-                        throw NotLoginException.newInstance(StpUtil.getLoginType(),
-                            "-100", "瀹㈡埛绔疘D涓嶵oken涓嶅尮閰�",
-                            StpUtil.getTokenValue());
-                    }
+                        // 妫�鏌� header 涓� param 閲岀殑 clientid 涓� token 閲岀殑鏄惁涓�鑷�
+                        String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY);
+                        String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY);
+                        String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString();
+                        if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) {
+                            // token 鏃犳晥
+                            throw NotLoginException.newInstance(StpUtil.getLoginType(),
+                                "-100", "瀹㈡埛绔疘D涓嶵oken涓嶅尮閰�",
+                                StpUtil.getTokenValue());
+                        }
 
-                    // 鏈夋晥鐜囧奖鍝� 鐢ㄤ簬涓存椂娴嬭瘯
-                    // if (log.isDebugEnabled()) {
-                    //     log.info("鍓╀綑鏈夋晥鏃堕棿: {}", StpUtil.getTokenTimeout());
-                    //     log.info("涓存椂鏈夋晥鏃堕棿: {}", StpUtil.getTokenActivityTimeout());
-                    // }
+                        // 鏈夋晥鐜囧奖鍝� 鐢ㄤ簬涓存椂娴嬭瘯
+                        // if (log.isDebugEnabled()) {
+                        //     log.info("鍓╀綑鏈夋晥鏃堕棿: {}", StpUtil.getTokenTimeout());
+                        //     log.info("涓存椂鏈夋晥鏃堕棿: {}", StpUtil.getTokenActivityTimeout());
+                        // }
 
-                });
-        })).addPathPatterns("/**")
+                    });
+            })).addPathPatterns("/**")
             // 鎺掗櫎涓嶉渶瑕佹嫤鎴殑璺緞
             .excludePathPatterns(securityProperties.getExcludes());
     }
diff --git a/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index f78c781..6def724 100644
--- a/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/ruoyi-common/ruoyi-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,3 +1,2 @@
-org.dromara.common.security.handler.GlobalExceptionHandler
 org.dromara.common.security.handler.AllUrlHandler
 org.dromara.common.security.config.SecurityConfig
diff --git a/ruoyi-common/ruoyi-common-sms/pom.xml b/ruoyi-common/ruoyi-common-sms/pom.xml
index c50f222..932cb9d 100644
--- a/ruoyi-common/ruoyi-common-sms/pom.xml
+++ b/ruoyi-common/ruoyi-common-sms/pom.xml
@@ -20,13 +20,12 @@
         <dependency>
             <groupId>org.dromara.sms4j</groupId>
             <artifactId>sms4j-spring-boot-starter</artifactId>
-            <exclusions>
-                <!-- 鎺掗櫎浜笢鐭俊鍐呭瓨鍦ㄧ殑fastjson绛夊緟浣滆�呭悗缁慨澶� -->
-                <exclusion>
-                    <groupId>com.alibaba</groupId>
-                    <artifactId>fastjson</artifactId>
-                </exclusion>
-            </exclusions>
+        </dependency>
+
+        <!-- RuoYi Common Redis-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-redis</artifactId>
         </dependency>
 
     </dependencies>
diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java
index 29f60fc..1e88407 100644
--- a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java
+++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java
@@ -1,14 +1,24 @@
 package org.dromara.common.sms.config;
 
+import org.dromara.common.sms.core.dao.PlusSmsDao;
+import org.dromara.sms4j.api.dao.SmsDao;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
 
 /**
- * 鐭俊閰嶇疆绫�(鏆傛椂娌$敤 棰勭暀鎵╁睍)
+ * 鐭俊閰嶇疆绫�
  *
- * @author Lion Li
- * @version 4.2.0
+ * @author Feng
  */
-@AutoConfiguration
+@AutoConfiguration(after = {RedisAutoConfiguration.class})
 public class SmsAutoConfiguration {
 
+    @Primary
+    @Bean
+    public SmsDao smsDao() {
+        return new PlusSmsDao();
+    }
+
 }
diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java
new file mode 100644
index 0000000..91d8d24
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java
@@ -0,0 +1,72 @@
+package org.dromara.common.sms.core.dao;
+
+import org.dromara.common.core.constant.GlobalConstants;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.sms4j.api.dao.SmsDao;
+
+import java.time.Duration;
+
+/**
+ * SmsDao缂撳瓨閰嶇疆 (浣跨敤妗嗘灦鑷甫RedisUtils瀹炵幇 鍗忚缁熶竴)
+ * <p>涓昏鐢ㄤ簬鐭俊閲嶈瘯鍜屾嫤鎴殑缂撳瓨
+ *
+ * @author Feng
+ */
+public class PlusSmsDao implements SmsDao {
+
+    /**
+     * 瀛樺偍
+     *
+     * @param key       閿�
+     * @param value     鍊�
+     * @param cacheTime 缂撳瓨鏃堕棿锛堝崟浣嶏細绉�)
+     */
+    @Override
+    public void set(String key, Object value, long cacheTime) {
+        RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, Duration.ofSeconds(cacheTime));
+    }
+
+    /**
+     * 瀛樺偍
+     *
+     * @param key   閿�
+     * @param value 鍊�
+     */
+    @Override
+    public void set(String key, Object value) {
+        RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, true);
+    }
+
+    /**
+     * 璇诲彇
+     *
+     * @param key 閿�
+     * @return 鍊�
+     */
+    @Override
+    public Object get(String key) {
+        return RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
+    }
+
+    /**
+     * remove
+     * <p> 鏍规嵁key绉婚櫎缂撳瓨
+     *
+     * @param key 缂撳瓨閿�
+     * @return 琚垹闄ょ殑value
+     * @author :Wind
+     */
+    @Override
+    public Object remove(String key) {
+        return RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
+    }
+
+    /**
+     * 娓呯┖
+     */
+    @Override
+    public void clean() {
+        RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + "sms:");
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
index 76be234..5f49d9c 100644
--- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
@@ -2,6 +2,8 @@
 
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * 绀句氦鐧诲綍閰嶇疆
  *
@@ -65,4 +67,9 @@
      */
     private String serverUrl;
 
+    /**
+     * 璇锋眰鑼冨洿
+     */
+    private List<String> scopes;
+
 }
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java
index 1beb7d0..1487a6a 100644
--- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialProperties.java
@@ -17,11 +17,6 @@
 public class SocialProperties {
 
     /**
-     * 鏄惁鍚敤
-     */
-    private Boolean enabled;
-
-    /**
      * 鎺堟潈绫诲瀷
      */
     private Map<String, SocialLoginConfigProperties> type;
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java
new file mode 100644
index 0000000..13649f9
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java
@@ -0,0 +1,100 @@
+package org.dromara.common.social.topiam;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.StrUtil;
+import com.xkcoding.http.support.HttpHeader;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.cache.AuthStateCache;
+import me.zhyd.oauth.config.AuthConfig;
+import me.zhyd.oauth.exception.AuthException;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+import me.zhyd.oauth.utils.HttpUtils;
+import me.zhyd.oauth.utils.UrlBuilder;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.json.utils.JsonUtils;
+
+import static org.dromara.common.social.topiam.AuthTopiamSource.TOPIAM;
+
+/**
+ * TopIAM 璁よ瘉璇锋眰
+ *
+ * @author xlsea
+ * @since 2024-01-06
+ */
+@Slf4j
+public class AuthTopIamRequest extends AuthDefaultRequest {
+
+    public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.topiam.server-url");
+
+    /**
+     * 璁惧畾褰掑睘鍩�
+     */
+    public AuthTopIamRequest(AuthConfig config) {
+        super(config, TOPIAM);
+    }
+
+    public AuthTopIamRequest(AuthConfig config, AuthStateCache authStateCache) {
+        super(config, TOPIAM, authStateCache);
+    }
+
+    @Override
+    protected AuthToken getAccessToken(AuthCallback authCallback) {
+        String body = doPostAuthorizationCode(authCallback.getCode());
+        Dict object = JsonUtils.parseMap(body);
+        checkResponse(object);
+        return AuthToken.builder()
+            .accessToken(object.getStr("access_token"))
+            .refreshToken(object.getStr("refresh_token"))
+            .idToken(object.getStr("id_token"))
+            .tokenType(object.getStr("token_type"))
+            .scope(object.getStr("scope"))
+            .build();
+    }
+
+    @Override
+    protected AuthUser getUserInfo(AuthToken authToken) {
+        String body = doGetUserInfo(authToken);
+        Dict object = JsonUtils.parseMap(body);
+        checkResponse(object);
+        return AuthUser.builder()
+            .uuid(object.getStr("sub"))
+            .username(object.getStr("preferred_username"))
+            .nickname(object.getStr("nickname"))
+            .avatar(object.getStr("picture"))
+            .email(object.getStr("email"))
+            .token(authToken)
+            .source(source.toString())
+            .build();
+    }
+
+
+    @Override
+    protected String doGetUserInfo(AuthToken authToken) {
+        return new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
+            .add("Content-Type", "application/json")
+            .add("Authorization", "Bearer " + authToken.getAccessToken()), false).getBody();
+    }
+
+
+    @Override
+    public String authorize(String state) {
+        return UrlBuilder.fromBaseUrl(super.authorize(state))
+            .queryParam("scope", StrUtil.join("%20", config.getScopes()))
+            .build();
+    }
+
+    public static void checkResponse(Dict object) {
+        // oauth/token 楠岃瘉寮傚父
+        if (object.containsKey("error")) {
+            throw new AuthException(object.getStr("error_description"));
+        }
+        // user 楠岃瘉寮傚父
+        if (object.containsKey("message")) {
+            throw new AuthException(object.getStr("message"));
+        }
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java
new file mode 100644
index 0000000..e47d6c6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java
@@ -0,0 +1,51 @@
+package org.dromara.common.social.topiam;
+
+import me.zhyd.oauth.config.AuthSource;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+
+/**
+ * Oauth2 榛樿鎺ュ彛璇存槑
+ *
+ * @author xlsea
+ * @since 2024-01-06
+ */
+public enum AuthTopiamSource implements AuthSource {
+
+    /**
+     * 娴嬭瘯
+     */
+    TOPIAM {
+        /**
+         * 鎺堟潈鐨刟pi
+         */
+        @Override
+        public String authorize() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/auth";
+        }
+
+        /**
+         * 鑾峰彇accessToken鐨刟pi
+         */
+        @Override
+        public String accessToken() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/token";
+        }
+
+        /**
+         * 鑾峰彇鐢ㄦ埛淇℃伅鐨刟pi
+         */
+        @Override
+        public String userInfo() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/userinfo";
+        }
+
+        /**
+         * 骞冲彴瀵瑰簲鐨� AuthRequest 瀹炵幇绫伙紝蹇呴』缁ф壙鑷� {@link AuthDefaultRequest}
+         */
+        @Override
+        public Class<? extends AuthDefaultRequest> getTargetClass() {
+            return AuthTopIamRequest.class;
+        }
+
+    }
+}
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
index 51b7e13..04f6214 100644
--- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
@@ -11,6 +11,7 @@
 import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
 import org.dromara.common.social.config.properties.SocialProperties;
 import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
+import org.dromara.common.social.topiam.AuthTopIamRequest;
 
 /**
  * 璁よ瘉鎺堟潈宸ュ叿绫�
@@ -38,7 +39,8 @@
         AuthConfig.AuthConfigBuilder builder = AuthConfig.builder()
             .clientId(obj.getClientId())
             .clientSecret(obj.getClientSecret())
-            .redirectUri(obj.getRedirectUri());
+            .redirectUri(obj.getRedirectUri())
+            .scopes(obj.getScopes());
         return switch (source.toLowerCase()) {
             case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE);
             case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE);
@@ -63,6 +65,7 @@
             case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE);
             case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
             case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE);
+            case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE);
             default -> throw new AuthException("鏈幏鍙栧埌鏈夋晥鐨凙uth閰嶇疆");
         };
     }
diff --git a/ruoyi-common/ruoyi-common-tenant/pom.xml b/ruoyi-common/ruoyi-common-tenant/pom.xml
index 2b87bf9..8e1a6ab 100644
--- a/ruoyi-common/ruoyi-common-tenant/pom.xml
+++ b/ruoyi-common/ruoyi-common-tenant/pom.xml
@@ -19,16 +19,12 @@
         <dependency>
             <groupId>org.dromara</groupId>
             <artifactId>ruoyi-common-mybatis</artifactId>
+            <optional>true</optional>
         </dependency>
 
         <dependency>
             <groupId>org.dromara</groupId>
             <artifactId>ruoyi-common-redis</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.alibaba</groupId>
-            <artifactId>transmittable-thread-local</artifactId>
         </dependency>
 
     </dependencies>
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java
index 8610641..3fb99c5 100644
--- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java
@@ -2,8 +2,6 @@
 
 import cn.dev33.satoken.dao.SaTokenDao;
 import cn.hutool.core.util.ObjectUtil;
-import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
-import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
 import org.dromara.common.core.utils.reflect.ReflectUtils;
 import org.dromara.common.mybatis.config.MybatisPlusConfig;
@@ -18,14 +16,12 @@
 import org.redisson.config.SingleServerConfig;
 import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.cache.CacheManager;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Primary;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * 绉熸埛閰嶇疆绫�
@@ -33,29 +29,22 @@
  * @author Lion Li
  */
 @EnableConfigurationProperties(TenantProperties.class)
-@AutoConfiguration(after = {RedisConfig.class, MybatisPlusConfig.class})
+@AutoConfiguration(after = {RedisConfig.class})
 @ConditionalOnProperty(value = "tenant.enable", havingValue = "true")
 public class TenantConfig {
 
-    /**
-     * 鍒濆鍖栫鎴烽厤缃�
-     */
-    @Bean
-    public boolean tenantInit(MybatisPlusInterceptor mybatisPlusInterceptor,
-                              TenantProperties tenantProperties) {
-        List<InnerInterceptor> interceptors = new ArrayList<>();
-        // 澶氱鎴锋彃浠� 蹇呴』鏀惧埌绗竴浣�
-        interceptors.add(tenantLineInnerInterceptor(tenantProperties));
-        interceptors.addAll(mybatisPlusInterceptor.getInterceptors());
-        mybatisPlusInterceptor.setInterceptors(interceptors);
-        return true;
-    }
+    @ConditionalOnBean(MybatisPlusConfig.class)
+    @AutoConfiguration(after = {MybatisPlusConfig.class})
+    static class MybatisPlusConfigation {
 
-    /**
-     * 澶氱鎴锋彃浠�
-     */
-    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) {
-        return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties));
+        /**
+         * 澶氱鎴锋彃浠�
+         */
+        @Bean
+        public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties) {
+            return new TenantLineInnerInterceptor(new PlusTenantLineHandler(tenantProperties));
+        }
+
     }
 
     @Bean
diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java
index 14dad1a..9475398 100644
--- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java
+++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/handle/TenantKeyPrefixHandler.java
@@ -1,5 +1,6 @@
 package org.dromara.common.tenant.handle;
 
+import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.GlobalConstants;
 import org.dromara.common.core.utils.StringUtils;
@@ -26,6 +27,9 @@
         if (StringUtils.isBlank(name)) {
             return null;
         }
+        if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) {
+            return super.map(name);
+        }
         if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
             return super.map(name);
         }
@@ -49,6 +53,9 @@
         if (StringUtils.isBlank(unmap)) {
             return null;
         }
+        if (InterceptorIgnoreHelper.willIgnoreTenantLine("")) {
+            return super.unmap(name);
+        }
         if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) {
             return super.unmap(name);
         }
diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java
index ac65f5a..b92c7f0 100644
--- a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java
+++ b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java
@@ -20,7 +20,9 @@
     @Override
     public String translation(Object key, String other) {
         if (key instanceof Long id) {
-            return userService.selectNicknameById(id);
+            return userService.selectNicknameByIds(id.toString());
+        } else if (key instanceof String ids) {
+            return userService.selectNicknameByIds(ids);
         }
         return null;
     }
diff --git a/ruoyi-common/ruoyi-common-web/pom.xml b/ruoyi-common/ruoyi-common-web/pom.xml
index 293c522..b250fa9 100644
--- a/ruoyi-common/ruoyi-common-web/pom.xml
+++ b/ruoyi-common/ruoyi-common-web/pom.xml
@@ -57,11 +57,6 @@
             <groupId>cn.hutool</groupId>
             <artifactId>hutool-crypto</artifactId>
         </dependency>
-
-        <dependency>
-            <groupId>com.alibaba</groupId>
-            <artifactId>transmittable-thread-local</artifactId>
-        </dependency>
     </dependencies>
 
 </project>
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
index 9225a51..650517e 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/CaptchaConfig.java
@@ -23,7 +23,7 @@
 
     private static final int WIDTH = 160;
     private static final int HEIGHT = 60;
-    private static final Color BACKGROUND = Color.PINK;
+    private static final Color BACKGROUND = Color.LIGHT_GRAY;
     private static final Font FONT = new Font("Arial", Font.BOLD, 48);
 
     /**
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java
index 1b78ebc..1371913 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java
@@ -1,5 +1,6 @@
 package org.dromara.common.web.config;
 
+import org.dromara.common.web.handler.GlobalExceptionHandler;
 import org.dromara.common.web.interceptor.PlusWebInvokeTimeInterceptor;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.Bean;
@@ -49,4 +50,12 @@
         // 杩斿洖鏂扮殑CorsFilter
         return new CorsFilter(source);
     }
+
+    /**
+     * 鍏ㄥ眬寮傚父澶勭悊鍣�
+     */
+    @Bean
+    public GlobalExceptionHandler globalExceptionHandler() {
+        return new GlobalExceptionHandler();
+    }
 }
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java
index 421ce6d..0f78928 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java
@@ -2,9 +2,11 @@
 
 import io.undertow.server.DefaultByteBufferPool;
 import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
+import org.dromara.common.core.utils.SpringUtils;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
 import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.core.task.VirtualThreadTaskExecutor;
 
 /**
  * Undertow 鑷畾涔夐厤缃�
@@ -24,6 +26,12 @@
             WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
             webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 512));
             deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
+            // 浣跨敤铏氭嫙绾跨▼
+            if (SpringUtils.isVirtual()) {
+                VirtualThreadTaskExecutor executor = new VirtualThreadTaskExecutor("undertow-");
+                deploymentInfo.setExecutor(executor);
+                deploymentInfo.setAsyncExecutor(executor);
+            }
         });
     }
 
diff --git a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java
similarity index 77%
rename from ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/GlobalExceptionHandler.java
rename to ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java
index a4dcfe3..bd47c18 100644
--- a/ruoyi-common/ruoyi-common-security/src/main/java/org/dromara/common/security/handler/GlobalExceptionHandler.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java
@@ -1,8 +1,5 @@
-package org.dromara.common.security.handler;
+package org.dromara.common.web.handler;
 
-import cn.dev33.satoken.exception.NotLoginException;
-import cn.dev33.satoken.exception.NotPermissionException;
-import cn.dev33.satoken.exception.NotRoleException;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.HttpStatus;
 import jakarta.servlet.http.HttpServletRequest;
@@ -21,6 +18,7 @@
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+import org.springframework.web.servlet.NoHandlerFoundException;
 
 /**
  * 鍏ㄥ眬寮傚父澶勭悊鍣�
@@ -32,36 +30,6 @@
 public class GlobalExceptionHandler {
 
     /**
-     * 鏉冮檺鐮佸紓甯�
-     */
-    @ExceptionHandler(NotPermissionException.class)
-    public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
-        String requestURI = request.getRequestURI();
-        log.error("璇锋眰鍦板潃'{}',鏉冮檺鐮佹牎楠屽け璐�'{}'", requestURI, e.getMessage());
-        return R.fail(HttpStatus.HTTP_FORBIDDEN, "娌℃湁璁块棶鏉冮檺锛岃鑱旂郴绠$悊鍛樻巿鏉�");
-    }
-
-    /**
-     * 瑙掕壊鏉冮檺寮傚父
-     */
-    @ExceptionHandler(NotRoleException.class)
-    public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
-        String requestURI = request.getRequestURI();
-        log.error("璇锋眰鍦板潃'{}',瑙掕壊鏉冮檺鏍¢獙澶辫触'{}'", requestURI, e.getMessage());
-        return R.fail(HttpStatus.HTTP_FORBIDDEN, "娌℃湁璁块棶鏉冮檺锛岃鑱旂郴绠$悊鍛樻巿鏉�");
-    }
-
-    /**
-     * 璁よ瘉澶辫触
-     */
-    @ExceptionHandler(NotLoginException.class)
-    public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {
-        String requestURI = request.getRequestURI();
-        log.error("璇锋眰鍦板潃'{}',璁よ瘉澶辫触'{}',鏃犳硶璁块棶绯荤粺璧勬簮", requestURI, e.getMessage());
-        return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "璁よ瘉澶辫触锛屾棤娉曡闂郴缁熻祫婧�");
-    }
-
-    /**
      * 璇锋眰鏂瑰紡涓嶆敮鎸�
      */
     @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@@ -69,7 +37,7 @@
                                                                 HttpServletRequest request) {
         String requestURI = request.getRequestURI();
         log.error("璇锋眰鍦板潃'{}',涓嶆敮鎸�'{}'璇锋眰", requestURI, e.getMethod());
-        return R.fail(e.getMessage());
+        return R.fail(HttpStatus.HTTP_BAD_METHOD, e.getMessage());
     }
 
     /**
@@ -112,6 +80,16 @@
     }
 
     /**
+     * 鎵句笉鍒拌矾鐢�
+     */
+    @ExceptionHandler(NoHandlerFoundException.class)
+    public R<Void> handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        log.error("璇锋眰鍦板潃'{}'涓嶅瓨鍦�.", requestURI);
+        return R.fail(HttpStatus.HTTP_NOT_FOUND, e.getMessage());
+    }
+
+    /**
      * 鎷︽埅鏈煡鐨勮繍琛屾椂寮傚父
      */
     @ExceptionHandler(RuntimeException.class)
diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java
index 1b4ce3d..12c8086 100644
--- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java
+++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java
@@ -2,15 +2,14 @@
 
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.map.MapUtil;
-import com.alibaba.ttl.TransmittableThreadLocal;
-import org.dromara.common.core.utils.SpringUtils;
-import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.json.utils.JsonUtils;
-import org.dromara.common.web.filter.RepeatedlyRequestWrapper;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.time.StopWatch;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.json.utils.JsonUtils;
+import org.dromara.common.web.filter.RepeatedlyRequestWrapper;
 import org.springframework.http.MediaType;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
@@ -30,7 +29,7 @@
 
     private final String prodProfile = "prod";
 
-    private final TransmittableThreadLocal<StopWatch> invokeTimeTL = new TransmittableThreadLocal<>();
+    private final static ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
 
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
@@ -56,7 +55,7 @@
             }
 
             StopWatch stopWatch = new StopWatch();
-            invokeTimeTL.set(stopWatch);
+            KEY_CACHE.set(stopWatch);
             stopWatch.start();
         }
         return true;
@@ -70,10 +69,10 @@
     @Override
     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
         if (!prodProfile.equals(SpringUtils.getActiveProfile())) {
-            StopWatch stopWatch = invokeTimeTL.get();
+            StopWatch stopWatch = KEY_CACHE.get();
             stopWatch.stop();
             log.info("[PLUS]缁撴潫璇锋眰 => URL[{}],鑰楁椂:[{}]姣", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
-            invokeTimeTL.remove();
+            KEY_CACHE.remove();
         }
     }
 
diff --git a/ruoyi-extend/pom.xml b/ruoyi-extend/pom.xml
index f5348cc..d7280ce 100644
--- a/ruoyi-extend/pom.xml
+++ b/ruoyi-extend/pom.xml
@@ -13,7 +13,7 @@
 
     <modules>
         <module>ruoyi-monitor-admin</module>
-        <module>ruoyi-powerjob-server</module>
+        <module>ruoyi-snailjob-server</module>
     </modules>
 
 </project>
diff --git a/ruoyi-extend/ruoyi-monitor-admin/Dockerfile b/ruoyi-extend/ruoyi-monitor-admin/Dockerfile
index 7ae0682..a54f816 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/Dockerfile
+++ b/ruoyi-extend/ruoyi-monitor-admin/Dockerfile
@@ -13,5 +13,6 @@
 
 ADD ./target/ruoyi-monitor-admin.jar ./app.jar
 
-ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -jar app.jar \
-           -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS}
+ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom \
+           -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
+           -jar app.jar
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java
index 1f70c75..53d248e 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/java/org/dromara/monitor/admin/config/AdminServerConfig.java
@@ -3,7 +3,7 @@
 import de.codecentric.boot.admin.server.config.EnableAdminServer;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
-import org.springframework.boot.task.TaskExecutorBuilder;
+import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Lazy;
@@ -23,7 +23,7 @@
     @Lazy
     @Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
     @ConditionalOnMissingBean(Executor.class)
-    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
+    public ThreadPoolTaskExecutor applicationTaskExecutor(ThreadPoolTaskExecutorBuilder builder) {
         return builder.build();
     }
 
diff --git a/ruoyi-extend/ruoyi-powerjob-server/Dockerfile b/ruoyi-extend/ruoyi-powerjob-server/Dockerfile
deleted file mode 100644
index 729bb22..0000000
--- a/ruoyi-extend/ruoyi-powerjob-server/Dockerfile
+++ /dev/null
@@ -1,17 +0,0 @@
-#FROM findepi/graalvm:java17-native
-FROM openjdk:17.0.2-oraclelinux8
-
-MAINTAINER Lion Li
-
-RUN mkdir -p /ruoyi/powerjob/logs
-
-WORKDIR /ruoyi/powerjob
-
-ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m"
-
-EXPOSE 7700
-
-ADD ./target/ruoyi-powerjob-server.jar ./app.jar
-
-ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -jar app.jar \
-           -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS}
diff --git a/ruoyi-extend/ruoyi-powerjob-server/pom.xml b/ruoyi-extend/ruoyi-powerjob-server/pom.xml
deleted file mode 100644
index bdb92a4..0000000
--- a/ruoyi-extend/ruoyi-powerjob-server/pom.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <parent>
-        <groupId>org.dromara</groupId>
-        <artifactId>ruoyi-extend</artifactId>
-        <version>${revision}</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-    <packaging>jar</packaging>
-    <artifactId>ruoyi-powerjob-server</artifactId>
-
-    <properties>
-        <spring-boot.version>2.7.18</spring-boot.version>
-        <spring-boot-admin.version>2.7.11</spring-boot-admin.version>
-    </properties>
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-parent</artifactId>
-                <version>${spring-boot.version}</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <dependencies>
-
-        <!-- PowerJob server-->
-        <dependency>
-            <groupId>tech.powerjob</groupId>
-            <artifactId>powerjob-server-starter</artifactId>
-            <version>${powerjob.version}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>de.codecentric</groupId>
-            <artifactId>spring-boot-admin-starter-client</artifactId>
-            <version>${spring-boot-admin.version}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.projectlombok</groupId>
-            <artifactId>lombok</artifactId>
-        </dependency>
-
-    </dependencies>
-
-    <build>
-        <finalName>${project.artifactId}</finalName>
-        <plugins>
-            <plugin>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-maven-plugin</artifactId>
-                <version>${spring-boot.version}</version>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>repackage</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-</project>
diff --git a/ruoyi-extend/ruoyi-powerjob-server/src/main/java/org/dromara/powerjob/PowerJobServerApplication.java b/ruoyi-extend/ruoyi-powerjob-server/src/main/java/org/dromara/powerjob/PowerJobServerApplication.java
deleted file mode 100644
index 3898639..0000000
--- a/ruoyi-extend/ruoyi-powerjob-server/src/main/java/org/dromara/powerjob/PowerJobServerApplication.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.dromara.powerjob;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.scheduling.annotation.EnableScheduling;
-import tech.powerjob.server.common.utils.PropertyUtils;
-
-/**
- * powerjob 鍚姩绋嬪簭
- *
- * @author yhan219
- */
-@Slf4j
-@EnableScheduling
-@SpringBootApplication(scanBasePackages = "tech.powerjob.server")
-public class PowerJobServerApplication {
-
-    public static void main(String[] args) {
-        PropertyUtils.init();
-        SpringApplication.run(tech.powerjob.server.PowerJobServerApplication.class, args);
-        log.info("鏂囨。鍦板潃: https://www.yuque.com/powerjob/guidence/problem");
-    }
-
-}
diff --git a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application-dev.properties b/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application-dev.properties
deleted file mode 100644
index ba024d3..0000000
--- a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application-dev.properties
+++ /dev/null
@@ -1,50 +0,0 @@
-oms.env=dev
-
-####### Database properties(Configure according to the the environment) #######
-spring.datasource.core.driver-class-name=com.mysql.cj.jdbc.Driver
-spring.datasource.core.jdbc-url=jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
-spring.datasource.core.username=root
-spring.datasource.core.password=root
-spring.datasource.core.maximum-pool-size=20
-spring.datasource.core.minimum-idle=5
-
-# 鐩戞帶閰嶇疆
-# 瀹㈡埛绔紑鍏�
-spring.boot.admin.client.enabled=true
-# 璁剧疆 Spring Boot Admin Server 鍦板潃
-spring.boot.admin.client.url: http://localhost:9090/admin
-spring.boot.admin.client.instance.service-host-type=IP
-spring.boot.admin.client.username=ruoyi
-spring.boot.admin.client.password=123456
-
-####### MongoDB properties(Non-core configuration properties)  #######
-####### delete mongodb config to disable mongodb #######
-oms.mongodb.enable=false
-#spring.data.mongodb.uri=mongodb+srv://zqq:No1Bug2Please3!@cluster0.wie54.gcp.mongodb.net/powerjob_daily?retryWrites=true&w=majority
-
-####### Email properties(Non-core configuration properties) #######
-####### Delete the following code to disable the mail #######
-#spring.mail.host=smtp.163.com
-#spring.mail.username=zqq@163.com
-#spring.mail.password=GOFZPNARMVKCGONV
-#spring.mail.properties.mail.smtp.auth=true
-#spring.mail.properties.mail.smtp.starttls.enable=true
-#spring.mail.properties.mail.smtp.starttls.required=true
-
-####### DingTalk properties(Non-core configuration properties) #######
-####### Delete the following code to disable the DingTalk #######
-#oms.alarm.ding.app-key=dingauqwkvxxnqskknfv
-#oms.alarm.ding.app-secret=XWrEPdAZMPgJeFtHuL0LH73LRj-74umF2_0BFcoXMfvnX0pCQvt0rpb1JOJU_HLl
-#oms.alarm.ding.agent-id=847044348
-
-####### Resource cleaning properties #######
-oms.instanceinfo.retention=1
-oms.container.retention.local=1
-oms.container.retention.remote=-1
-
-####### Cache properties #######
-oms.instance.metadata.cache.size=1024
-
-####### Threshold in precise fetching server(0~100). 100 means full detection of server, in which #######
-####### split-brain could be avoided while performance overhead would increase. #######
-oms.accurate.select.server.percentage = 50
diff --git a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application-prod.properties b/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application-prod.properties
deleted file mode 100644
index b13c688..0000000
--- a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application-prod.properties
+++ /dev/null
@@ -1,50 +0,0 @@
-oms.env=prod
-
-####### Database properties(Configure according to the the environment) #######
-spring.datasource.core.driver-class-name=com.mysql.cj.jdbc.Driver
-spring.datasource.core.jdbc-url=jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
-spring.datasource.core.username=root
-spring.datasource.core.password=root
-spring.datasource.core.maximum-pool-size=20
-spring.datasource.core.minimum-idle=5
-
-# 鐩戞帶閰嶇疆
-# 瀹㈡埛绔紑鍏�
-spring.boot.admin.client.enabled=true
-# 璁剧疆 Spring Boot Admin Server 鍦板潃
-spring.boot.admin.client.url: http://localhost:9090/admin
-spring.boot.admin.client.instance.service-host-type=IP
-spring.boot.admin.client.username=ruoyi
-spring.boot.admin.client.password=123456
-
-####### MongoDB properties(Non-core configuration properties)  #######
-####### delete mongodb config to disable mongodb #######
-oms.mongodb.enable=false
-#spring.data.mongodb.uri=mongodb+srv://zqq:No1Bug2Please3!@cluster0.wie54.gcp.mongodb.net/powerjob_daily?retryWrites=true&w=majority
-
-####### Email properties(Non-core configuration properties) #######
-####### Delete the following code to disable the mail #######
-#spring.mail.host=smtp.163.com
-#spring.mail.username=zqq@163.com
-#spring.mail.password=GOFZPNARMVKCGONV
-#spring.mail.properties.mail.smtp.auth=true
-#spring.mail.properties.mail.smtp.starttls.enable=true
-#spring.mail.properties.mail.smtp.starttls.required=true
-
-####### DingTalk properties(Non-core configuration properties) #######
-####### Delete the following code to disable the DingTalk #######
-#oms.alarm.ding.app-key=dingauqwkvxxnqskknfv
-#oms.alarm.ding.app-secret=XWrEPdAZMPgJeFtHuL0LH73LRj-74umF2_0BFcoXMfvnX0pCQvt0rpb1JOJU_HLl
-#oms.alarm.ding.agent-id=847044348
-
-####### Resource cleaning properties #######
-oms.instanceinfo.retention=7
-oms.container.retention.local=7
-oms.container.retention.remote=-1
-
-####### Cache properties #######
-oms.instance.metadata.cache.size=2048
-
-####### Threshold in precise fetching server(0~100). 100 means full detection of server, in which #######
-####### split-brain could be avoided while performance overhead would increase. #######
-oms.accurate.select.server.percentage = 50
diff --git a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application.properties b/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application.properties
deleted file mode 100644
index 5b6516c..0000000
--- a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/application.properties
+++ /dev/null
@@ -1,33 +0,0 @@
-# Http server port
-server.port=7700
-
-spring.profiles.active=@profiles.active@
-spring.main.banner-mode=log
-spring.jpa.open-in-view=false
-spring.data.mongodb.repositories.type=none
-logging.level.org.mongodb=warn
-logging.level.tech.powerjob.server=warn
-logging.level.MONITOR_LOGGER_DB_OPERATION=warn
-logging.level.MONITOR_LOGGER_WORKER_HEART_BEAT=warn
-logging.config: classpath:logback-plus.xml
-
-# Configuration for uploading files.
-spring.servlet.multipart.enabled=true
-spring.servlet.multipart.file-size-threshold=0
-spring.servlet.multipart.max-file-size=209715200
-spring.servlet.multipart.max-request-size=209715200
-
-###### PowerJob transporter configuration  ######
-oms.transporter.active.protocols=AKKA,HTTP
-oms.transporter.main.protocol=HTTP
-oms.akka.port=10086
-oms.http.port=10010
-# Prefix for all tables. Default empty string. Config if you have needs, i.e. pj_
-oms.table-prefix=pj_
-
-# Actuator 鐩戞帶绔偣鐨勯厤缃」
-spring.application.name: ruoyi-powerjob-server
-management.endpoints.web.exposure.include=*
-management.endpoint.health.show-details=ALWAYS
-management.endpoint.logfile.external-file=./logs/ruoyi-powerjob-server.log
-management.health.mongo.enabled=${oms.mongodb.enable}
diff --git a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/banner.txt b/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/banner.txt
deleted file mode 100644
index c0fac79..0000000
--- a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/banner.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Application Version: ${revision}
-Spring Boot Version: ${spring-boot.version}
-                              _       _
-                             (_)     | |
- _ __   _____      _____ _ __ _  ___ | |__ ______ ___  ___ _ ____   _____ _ __
-| '_ \ / _ \ \ /\ / / _ \ '__| |/ _ \| '_ \______/ __|/ _ \ '__\ \ / / _ \ '__|
-| |_) | (_) \ V  V /  __/ |  | | (_) | |_) |     \__ \  __/ |   \ V /  __/ |
-| .__/ \___/ \_/\_/ \___|_|  | |\___/|_.__/      |___/\___|_|    \_/ \___|_|
-| |                         _/ |
-|_|                        |__/
-
diff --git a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/logback-plus.xml b/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/logback-plus.xml
deleted file mode 100644
index ad3bfa9..0000000
--- a/ruoyi-extend/ruoyi-powerjob-server/src/main/resources/logback-plus.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<configuration debug="false" scan="true" scanPeriod="1 seconds">
-
-    <contextName>logback</contextName>
-    <property name="log.path" value="./logs/ruoyi-powerjob-server"/>
-    <property name="console.log.pattern"
-              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
-    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
-
-    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
-        <encoder>
-            <pattern>${console.log.pattern}</pattern>
-            <charset>utf-8</charset>
-        </encoder>
-    </appender>
-
-    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
-        <file>${log.path}.log</file>
-        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-            <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.log</fileNamePattern>
-            <!-- 鏃ュ織鏈�澶х殑鍘嗗彶 60澶� -->
-            <maxHistory>60</maxHistory>
-        </rollingPolicy>
-        <encoder>
-            <pattern>${log.pattern}</pattern>
-        </encoder>
-    </appender>
-
-    <root level="info">
-        <appender-ref ref="console"/>
-        <appender-ref ref="file"/>
-    </root>
-
-</configuration>
diff --git a/ruoyi-extend/ruoyi-snailjob-server/Dockerfile b/ruoyi-extend/ruoyi-snailjob-server/Dockerfile
new file mode 100644
index 0000000..1fda8db
--- /dev/null
+++ b/ruoyi-extend/ruoyi-snailjob-server/Dockerfile
@@ -0,0 +1,19 @@
+#FROM findepi/graalvm:java17-native
+FROM openjdk:17.0.2-oraclelinux8
+
+MAINTAINER Lion Li
+
+RUN mkdir -p /ruoyi/snailjob/logs
+
+WORKDIR /ruoyi/snailjob
+
+ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="-Xms512m -Xmx1024m"
+
+EXPOSE 8800
+EXPOSE 1788
+
+ADD ./target/ruoyi-snailjob-server.jar ./app.jar
+
+ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom \
+           -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
+           -jar app.jar
diff --git a/ruoyi-extend/ruoyi-snailjob-server/pom.xml b/ruoyi-extend/ruoyi-snailjob-server/pom.xml
new file mode 100644
index 0000000..7348b29
--- /dev/null
+++ b/ruoyi-extend/ruoyi-snailjob-server/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-extend</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>ruoyi-snailjob-server</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.aizuda</groupId>
+            <artifactId>snail-job-server-starter</artifactId>
+            <version>${snailjob.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+            <version>${spring-boot-admin.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/java/org/dromara/snailjob/SnailJobServerApplication.java b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/org/dromara/snailjob/SnailJobServerApplication.java
new file mode 100644
index 0000000..dfab068
--- /dev/null
+++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/java/org/dromara/snailjob/SnailJobServerApplication.java
@@ -0,0 +1,19 @@
+package org.dromara.snailjob;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * SnailJob Server 鍚姩绋嬪簭
+ *
+ * @author opensnail
+ * @date 2024-05-17
+ */
+@SpringBootApplication
+public class SnailJobServerApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(com.aizuda.snailjob.server.SnailJobServerApplication.class, args);
+    }
+
+}
diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..be0b9b4
--- /dev/null
+++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-dev.yml
@@ -0,0 +1,47 @@
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+    username: root
+    password: root
+    hikari:
+      connection-timeout: 30000
+      validation-timeout: 5000
+      minimum-idle: 10
+      maximum-pool-size: 20
+      idle-timeout: 600000
+      max-lifetime: 900000
+      keepaliveTime: 30000
+
+--- # snail-job 鏈嶅姟绔厤缃�
+snail-job:
+  # 鎷夊彇閲嶈瘯鏁版嵁鐨勬瘡鎵规鐨勫ぇ灏�
+  retry-pull-page-size: 1000
+  # 鎷夊彇閲嶈瘯鏁版嵁鐨勬瘡鎵规鐨勫ぇ灏�
+  job-pull-page-size: 1000
+  # 鏈嶅姟绔痭etty绔彛
+  netty-port: 1788
+  # 涓�涓鎴风姣忕鏈�澶氭帴鏀剁殑閲嶈瘯鏁伴噺鎸囦护
+  limiter: 1000
+  # 鍙锋妯″紡涓嬫闀块厤缃�
+  step: 100
+  # 鏃ュ織淇濆瓨鏃堕棿(鍗曚綅: day)
+  log-storage: 90
+  # 鍥炶皟閰嶇疆
+  callback:
+    #鍥炶皟鏈�澶ф墽琛屾鏁�
+    max-count: 288
+    #闂撮殧鏃堕棿
+    trigger-interval: 900
+  retry-max-pull-count: 10
+
+--- # 鐩戞帶涓績閰嶇疆
+spring.boot.admin.client:
+  # 澧炲姞瀹㈡埛绔紑鍏�
+  enabled: true
+  url: http://localhost:9090/admin
+  instance:
+    service-host-type: IP
+  username: ruoyi
+  password: 123456
diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml
new file mode 100644
index 0000000..4c69ee7
--- /dev/null
+++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application-prod.yml
@@ -0,0 +1,47 @@
+spring:
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
+    username: root
+    password: root
+    hikari:
+      connection-timeout: 30000
+      validation-timeout: 5000
+      minimum-idle: 10
+      maximum-pool-size: 20
+      idle-timeout: 600000
+      max-lifetime: 900000
+      keepaliveTime: 30000
+
+--- # snail-job 鏈嶅姟绔厤缃�
+snail-job:
+  # 鎷夊彇閲嶈瘯鏁版嵁鐨勬瘡鎵规鐨勫ぇ灏�
+  retry-pull-page-size: 1000
+  # 鎷夊彇閲嶈瘯鏁版嵁鐨勬瘡鎵规鐨勫ぇ灏�
+  job-pull-page-size: 1000
+  # 鏈嶅姟绔� netty 绔彛
+  netty-port: 1788
+  # 涓�涓鎴风姣忕鏈�澶氭帴鏀剁殑閲嶈瘯鏁伴噺鎸囦护
+  limiter: 1000
+  # 鍙锋妯″紡涓嬫闀块厤缃�
+  step: 100
+  # 鏃ュ織淇濆瓨鏃堕棿(鍗曚綅: day)
+  log-storage: 90
+  # 鍥炶皟閰嶇疆
+  callback:
+    #鍥炶皟鏈�澶ф墽琛屾鏁�
+    max-count: 288
+    #闂撮殧鏃堕棿
+    trigger-interval: 900
+  retry-max-pull-count: 10
+
+--- # 鐩戞帶涓績閰嶇疆
+spring.boot.admin.client:
+  # 澧炲姞瀹㈡埛绔紑鍏�
+  enabled: true
+  url: http://localhost:9090/admin
+  instance:
+    service-host-type: IP
+  username: ruoyi
+  password: 123456
diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application.yml b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application.yml
new file mode 100644
index 0000000..ba6df45
--- /dev/null
+++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/application.yml
@@ -0,0 +1,39 @@
+server:
+  port: 8800
+  servlet:
+    context-path: /snail-job
+
+spring:
+  application:
+    name: ruoyi-snailjob-server
+  profiles:
+    active: @profiles.active@
+  web:
+    resources:
+      static-locations: classpath:admin/
+
+mybatis-plus:
+  typeAliasesPackage: com.aizuda.snailjob.template.datasource.persistence.po
+  global-config:
+    db-config:
+      where-strategy: NOT_EMPTY
+      capital-mode: false
+      logic-delete-value: 1
+      logic-not-delete-value: 0
+  configuration:
+    map-underscore-to-camel-case: true
+    cache-enabled: true
+
+logging:
+  config: classpath:logback-plus.xml
+
+management:
+  endpoints:
+    web:
+      exposure:
+        include: '*'
+  endpoint:
+    health:
+      show-details: ALWAYS
+    logfile:
+      external-file: ./logs/ruoyi-snailjob-server/console.log
diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/banner.txt b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/banner.txt
new file mode 100644
index 0000000..adb96c5
--- /dev/null
+++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/banner.txt
@@ -0,0 +1,11 @@
+Application Version: ${revision}
+Spring Boot Version: ${spring-boot.version}
+                 _ _ _       _
+                (_) (_)     | |
+ ___ _ __   __ _ _| |_  ___ | |__ ______ ___  ___ _ ____   _____ _ __
+/ __| '_ \ / _` | | | |/ _ \| '_ \______/ __|/ _ \ '__\ \ / / _ \ '__|
+\__ \ | | | (_| | | | | (_) | |_) |     \__ \  __/ |   \ V /  __/ |
+|___/_| |_|\__,_|_|_| |\___/|_.__/      |___/\___|_|    \_/ \___|_|
+                   _/ |
+                  |__/
+
diff --git a/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml
new file mode 100644
index 0000000..a40262c
--- /dev/null
+++ b/ruoyi-extend/ruoyi-snailjob-server/src/main/resources/logback-plus.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <property name="log.path" value="./logs/ruoyi-snailjob-server" />
+    <property name="console.log.pattern"
+              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
+    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
+
+
+    <!-- 鎺у埗鍙拌緭鍑� -->
+    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${console.log.pattern}</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+    </appender>
+
+    <!-- 鎺у埗鍙拌緭鍑� -->
+    <appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/console.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 鏃ュ織鏂囦欢鍚嶆牸寮� -->
+            <fileNamePattern>${log.path}/console.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <!-- 鏃ュ織鏈�澶� 1澶� -->
+            <maxHistory>1</maxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+            <charset>utf-8</charset>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <!-- 杩囨护鐨勭骇鍒� -->
+            <level>INFO</level>
+        </filter>
+    </appender>
+
+    <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/info.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <FileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</FileNamePattern>
+            <MaxHistory>60</MaxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>INFO</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${log.path}/error.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <FileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log
+            </FileNamePattern>
+            <MaxHistory>60</MaxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${log.pattern}</pattern>
+        </encoder>
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name ="async_info" class= "ch.qos.logback.classic.AsyncAppender">
+        <discardingThreshold >100</discardingThreshold>
+        <queueSize>1024</queueSize>
+        <appender-ref ref ="file_info"/>
+    </appender>
+
+    <appender name ="async_error" class= "ch.qos.logback.classic.AsyncAppender">
+        <discardingThreshold >100</discardingThreshold>
+        <queueSize>1024</queueSize>
+        <appender-ref ref ="file_error"/>
+    </appender>
+
+    <!-- SnailJob appender -->
+    <appender name="snail_log_server_appender" class="com.aizuda.snailjob.server.common.appender.SnailJobServerLogbackAppender">
+    </appender>
+
+    <!-- 鎺у埗鍙拌緭鍑烘棩蹇楃骇鍒� -->
+    <root level="info">
+        <appender-ref ref="console" />
+        <appender-ref ref="async_info" />
+        <appender-ref ref="async_error" />
+        <appender-ref ref="snail_log_server_appender" />
+    </root>
+</configuration>
diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml
index 4044916..daff497 100644
--- a/ruoyi-modules/pom.xml
+++ b/ruoyi-modules/pom.xml
@@ -14,6 +14,7 @@
         <module>ruoyi-generator</module>
         <module>ruoyi-job</module>
         <module>ruoyi-system</module>
+        <module>ruoyi-workflow</module>
     </modules>
 
     <artifactId>ruoyi-modules</artifactId>
diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java
index fb19731..b993f60 100644
--- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java
+++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java
@@ -5,7 +5,6 @@
 import org.dromara.sms4j.api.SmsBlend;
 import org.dromara.sms4j.api.entity.SmsResponse;
 import org.dromara.sms4j.core.factory.SmsFactory;
-import org.dromara.sms4j.provider.enumerate.SupplierType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -25,7 +24,6 @@
 @RestController
 @RequestMapping("/demo/sms")
 public class SmsController {
-
     /**
      * 鍙戦�佺煭淇liyun
      *
@@ -36,7 +34,7 @@
     public R<Object> sendAliyun(String phones, String templateId) {
         LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
         map.put("code", "1234");
-        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
         SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
         return R.ok(smsResponse);
     }
@@ -52,9 +50,33 @@
         LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
 //        map.put("2", "娴嬭瘯娴嬭瘯");
         map.put("1", "1234");
-        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.TENCENT);
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("config2");
         SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
         return R.ok(smsResponse);
     }
 
+    /**
+     * 娣诲姞榛戝悕鍗�
+     *
+     * @param phone 鎵嬫満鍙�
+     */
+    @GetMapping("/addBlacklist")
+    public R<Object> addBlacklist(String phone){
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
+        smsBlend.joinInBlacklist(phone);
+        return R.ok();
+    }
+
+    /**
+     * 绉婚櫎榛戝悕鍗�
+     *
+     * @param phone 鎵嬫満鍙�
+     */
+    @GetMapping("/removeBlacklist")
+    public R<Object> removeBlacklist(String phone){
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
+        smsBlend.removeFromBlacklist(phone);
+        return R.ok();
+    }
+
 }
diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java
index 5ce7ad0..ebdb993 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java
+++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTableColumn.java
@@ -42,7 +42,7 @@
     /**
      * 鍒楁弿杩�
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
     private String columnComment;
 
     /**
@@ -64,43 +64,43 @@
     /**
      * 鏄惁涓婚敭锛�1鏄級
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
     private String isPk;
 
     /**
      * 鏄惁鑷锛�1鏄級
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
     private String isIncrement;
 
     /**
      * 鏄惁蹇呭~锛�1鏄級
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
     private String isRequired;
 
     /**
      * 鏄惁涓烘彃鍏ュ瓧娈碉紙1鏄級
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
     private String isInsert;
 
     /**
      * 鏄惁缂栬緫瀛楁锛�1鏄級
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
     private String isEdit;
 
     /**
      * 鏄惁鍒楄〃瀛楁锛�1鏄級
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
     private String isList;
 
     /**
      * 鏄惁鏌ヨ瀛楁锛�1鏄級
      */
-    @TableField(updateStrategy = FieldStrategy.IGNORED, jdbcType = JdbcType.VARCHAR)
+    @TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
     private String isQuery;
 
     /**
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml
index b174589..901bbf0 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml
@@ -19,7 +19,8 @@
             select table_name, table_comment, create_time, update_time
             from information_schema.tables
             where table_schema = (select database())
-            AND table_name NOT LIKE 'pj_%' AND table_name NOT LIKE 'gen_%'
+            AND table_name NOT LIKE 'sj_%' AND table_name NOT LIKE 'gen_%'
+            AND table_name NOT LIKE 'act_%' AND table_name NOT LIKE 'flw_%'
             <if test="genTable.params.genTableNames != null and genTable.params.genTableNames.size > 0">
                 AND table_name NOT IN
                 <foreach collection="genTable.params.genTableNames" open="(" close=")" separator="," item="item">
@@ -40,7 +41,8 @@
             where dt.table_name = dtc.table_name
             and dt.table_name = uo.object_name
             and uo.object_type = 'TABLE'
-            AND dt.table_name NOT LIKE 'pj_%' AND dt.table_name NOT LIKE 'GEN_%'
+            AND dt.table_name NOT LIKE 'SJ_%' AND dt.table_name NOT LIKE 'GEN_%'
+            AND dt.table_name NOT LIKE 'ACT_%' AND dt.table_name NOT LIKE 'FLW_%'
             <if test="genTable.params.genTableNames != null and genTable.params.genTableNames.size > 0">
                 AND lower(dt.table_name) NOT IN
                 <foreach collection="genTable.params.genTableNames" open="(" close=")" separator="," item="item">
@@ -69,7 +71,8 @@
                     AND n.nspname = 'public'::name
                     AND n.nspname <![CDATA[ <> ]]> ''::name
             ) list_table
-            where table_name NOT LIKE 'pj_%' AND table_name NOT LIKE 'gen_%'
+            where table_name NOT LIKE 'sj_%' AND table_name NOT LIKE 'gen_%'
+            AND table_name NOT LIKE 'act_%' AND table_name NOT LIKE 'flw_%'
             <if test="genTable.params.genTableNames != null and genTable.params.genTableNames.size > 0">
                 AND table_name NOT IN
                 <foreach collection="genTable.params.genTableNames" open="(" close=")" separator="," item="item">
@@ -92,7 +95,8 @@
             FROM SYSOBJECTS D
                 INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID
                     AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'
-                    AND D.NAME NOT LIKE 'pj_%' AND D.NAME NOT LIKE 'gen_%'
+                    AND D.NAME NOT LIKE 'sj_%' AND D.NAME NOT LIKE 'gen_%'
+                    AND D.NAME NOT LIKE 'act_%' AND D.NAME NOT LIKE 'flw_%'
             <if test="genTable.params.genTableNames != null and genTable.params.genTableNames.size > 0">
                 AND D.NAME NOT IN
                 <foreach collection="genTable.params.genTableNames" open="(" close=")" separator="," item="item">
@@ -112,7 +116,9 @@
     <select id="selectDbTableListByNames" resultMap="GenTableResult">
         <if test="@org.dromara.common.mybatis.helper.DataBaseHelper@isMySql()">
             select table_name, table_comment, create_time, update_time from information_schema.tables
-            where table_name NOT LIKE 'pj_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
+            where table_schema = (select database())
+            and table_name NOT LIKE 'sj_%' and table_name NOT LIKE 'gen_%'
+            and table_name NOT LIKE 'act_%' AND table_name NOT LIKE 'flw_%'
             and table_name in
             <foreach collection="array" item="name" open="(" separator="," close=")">
                  #{name}
@@ -124,7 +130,8 @@
             where dt.table_name = dtc.table_name
             and dt.table_name = uo.object_name
             and uo.object_type = 'TABLE'
-            AND dt.table_name NOT LIKE 'pj_%' AND dt.table_name NOT LIKE 'GEN_%'
+            and dt.table_name NOT LIKE 'SJ_%' AND dt.table_name NOT LIKE 'GEN_%'
+            and dt.table_name NOT LIKE 'ACT_%' AND dt.table_name NOT LIKE 'FLW_%'
             and lower(dt.table_name) in
             <foreach collection="array" item="name" open="(" separator="," close=")">
                 #{name}
@@ -144,7 +151,8 @@
                     AND n.nspname = 'public'::name
                     AND n.nspname <![CDATA[ <> ]]> ''::name
             ) list_table
-            where table_name NOT LIKE 'pj_%' and table_name NOT LIKE 'gen_%'
+            where table_name NOT LIKE 'sj_%' and table_name NOT LIKE 'gen_%'
+            and table_name NOT LIKE 'act_%' and table_name NOT LIKE 'flw_%'
             and table_name in
             <foreach collection="array" item="name" open="(" separator="," close=")">
                 #{name}
@@ -158,7 +166,8 @@
             FROM SYSOBJECTS D
                 INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID
                     AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'
-                    AND D.NAME NOT LIKE 'pj_%' AND D.NAME NOT LIKE 'gen_%'
+                    AND D.NAME NOT LIKE 'sj_%' AND D.NAME NOT LIKE 'gen_%'
+                    AND D.NAME NOT LIKE 'act_%' AND D.NAME NOT LIKE 'flw_%'
                     AND D.NAME in
                     <foreach collection="array" item="name" open="(" separator="," close=")">
                         #{name}
@@ -169,7 +178,9 @@
     <select id="selectTableByName" parameterType="String" resultMap="GenTableResult">
         <if test="@org.dromara.common.mybatis.helper.DataBaseHelper@isMySql()">
             select table_name, table_comment, create_time, update_time from information_schema.tables
-            where table_name NOT LIKE 'pj_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
+            where table_schema = (select database())
+            and table_name NOT LIKE 'sj_%' and table_name NOT LIKE 'gen_%'
+            and table_name NOT LIKE 'act_%' AND table_name NOT LIKE 'flw_%'
             and table_name = #{tableName}
         </if>
         <if test="@org.dromara.common.mybatis.helper.DataBaseHelper@isOracle()">
@@ -178,7 +189,8 @@
             where dt.table_name = dtc.table_name
             and dt.table_name = uo.object_name
             and uo.object_type = 'TABLE'
-            AND dt.table_name NOT LIKE 'pj_%' AND dt.table_name NOT LIKE 'GEN_%'
+            AND dt.table_name NOT LIKE 'SJ_%' AND dt.table_name NOT LIKE 'GEN_%'
+            AND dt.table_name NOT LIKE 'ACT_%' AND dt.table_name NOT LIKE 'FLW_%'
             AND dt.table_name NOT IN (select table_name from gen_table)
             and lower(dt.table_name) = #{tableName}
         </if>
@@ -196,7 +208,8 @@
                     AND n.nspname = 'public'::name
                     AND n.nspname <![CDATA[ <> ]]> ''::name
             ) list_table
-            where table_name NOT LIKE 'pj_%' and table_name NOT LIKE 'gen_%'
+            where table_name NOT LIKE 'sj_%' and table_name NOT LIKE 'gen_%'
+            and table_name NOT LIKE 'act_%' and table_name NOT LIKE 'flw_%'
             and table_name = #{tableName}
         </if>
         <if test="@org.dromara.common.mybatis.helper.DataBaseHelper@isSqlServer()">
@@ -207,7 +220,8 @@
             FROM SYSOBJECTS D
                 INNER JOIN SYS.EXTENDED_PROPERTIES F ON D.ID = F.MAJOR_ID
                     AND F.MINOR_ID = 0 AND D.XTYPE = 'U' AND D.NAME != 'DTPROPERTIES'
-                    AND D.NAME NOT LIKE 'pj_%' AND D.NAME NOT LIKE 'gen_%'
+                    AND D.NAME NOT LIKE 'sj_%' AND D.NAME NOT LIKE 'gen_%'
+                    AND D.NAME NOT LIKE 'act_%' AND D.NAME NOT LIKE 'flw_%'
                     AND D.NAME = #{tableName}
         </if>
     </select>
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm
index 14177b5..6438971 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/controller.java.vm
@@ -20,7 +20,7 @@
 import ${packageName}.domain.vo.${ClassName}Vo;
 import ${packageName}.domain.bo.${ClassName}Bo;
 import ${packageName}.service.I${ClassName}Service;
-#if($table.crud || $table.sub)
+#if($table.crud)
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 #elseif($table.tree)
 #end
@@ -44,7 +44,7 @@
      */
     @SaCheckPermission("${permissionPrefix}:list")
     @GetMapping("/list")
-#if($table.crud || $table.sub)
+#if($table.crud)
     public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) {
         return ${className}Service.queryPageList(bo, pageQuery);
     }
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm
index d596a0e..4db9030 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/service.java.vm
@@ -1,9 +1,8 @@
 package ${packageName}.service;
 
-import ${packageName}.domain.${ClassName};
 import ${packageName}.domain.vo.${ClassName}Vo;
 import ${packageName}.domain.bo.${ClassName}Bo;
-#if($table.crud || $table.sub)
+#if($table.crud)
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
 #end
@@ -21,33 +20,53 @@
 
     /**
      * 鏌ヨ${functionName}
+     *
+     * @param ${pkColumn.javaField} 涓婚敭
+     * @return ${functionName}
      */
     ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField});
 
-#if($table.crud || $table.sub)
+#if($table.crud)
     /**
-     * 鏌ヨ${functionName}鍒楄〃
+     * 鍒嗛〉鏌ヨ${functionName}鍒楄〃
+     *
+     * @param bo        鏌ヨ鏉′欢
+     * @param pageQuery 鍒嗛〉鍙傛暟
+     * @return ${functionName}鍒嗛〉鍒楄〃
      */
     TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery);
 #end
 
     /**
-     * 鏌ヨ${functionName}鍒楄〃
+     * 鏌ヨ绗﹀悎鏉′欢鐨�${functionName}鍒楄〃
+     *
+     * @param bo 鏌ヨ鏉′欢
+     * @return ${functionName}鍒楄〃
      */
     List<${ClassName}Vo> queryList(${ClassName}Bo bo);
 
     /**
      * 鏂板${functionName}
+     *
+     * @param bo ${functionName}
+     * @return 鏄惁鏂板鎴愬姛
      */
     Boolean insertByBo(${ClassName}Bo bo);
 
     /**
      * 淇敼${functionName}
+     *
+     * @param bo ${functionName}
+     * @return 鏄惁淇敼鎴愬姛
      */
     Boolean updateByBo(${ClassName}Bo bo);
 
     /**
      * 鏍¢獙骞舵壒閲忓垹闄�${functionName}淇℃伅
+     *
+     * @param ids     寰呭垹闄ょ殑涓婚敭闆嗗悎
+     * @param isValid 鏄惁杩涜鏈夋晥鎬ф牎楠�
+     * @return 鏄惁鍒犻櫎鎴愬姛
      */
     Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid);
 }
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
index 75a9b83..e7236fd 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
@@ -2,7 +2,7 @@
 
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
-#if($table.crud || $table.sub)
+#if($table.crud)
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -35,15 +35,22 @@
 
     /**
      * 鏌ヨ${functionName}
+     *
+     * @param ${pkColumn.javaField} 涓婚敭
+     * @return ${functionName}
      */
     @Override
     public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){
         return baseMapper.selectVoById(${pkColumn.javaField});
     }
 
-#if($table.crud || $table.sub)
+#if($table.crud)
     /**
-     * 鏌ヨ${functionName}鍒楄〃
+     * 鍒嗛〉鏌ヨ${functionName}鍒楄〃
+     *
+     * @param bo        鏌ヨ鏉′欢
+     * @param pageQuery 鍒嗛〉鍙傛暟
+     * @return ${functionName}鍒嗛〉鍒楄〃
      */
     @Override
     public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) {
@@ -54,7 +61,10 @@
 #end
 
     /**
-     * 鏌ヨ${functionName}鍒楄〃
+     * 鏌ヨ绗﹀悎鏉′欢鐨�${functionName}鍒楄〃
+     *
+     * @param bo 鏌ヨ鏉′欢
+     * @return ${functionName}鍒楄〃
      */
     @Override
     public List<${ClassName}Vo> queryList(${ClassName}Bo bo) {
@@ -91,6 +101,9 @@
 
     /**
      * 鏂板${functionName}
+     *
+     * @param bo ${functionName}
+     * @return 鏄惁鏂板鎴愬姛
      */
     @Override
     public Boolean insertByBo(${ClassName}Bo bo) {
@@ -106,6 +119,9 @@
 
     /**
      * 淇敼${functionName}
+     *
+     * @param bo ${functionName}
+     * @return 鏄惁淇敼鎴愬姛
      */
     @Override
     public Boolean updateByBo(${ClassName}Bo bo) {
@@ -122,7 +138,11 @@
     }
 
     /**
-     * 鎵归噺鍒犻櫎${functionName}
+     * 鏍¢獙骞舵壒閲忓垹闄�${functionName}淇℃伅
+     *
+     * @param ids     寰呭垹闄ょ殑涓婚敭闆嗗悎
+     * @param isValid 鏄惁杩涜鏈夋晥鎬ф牎楠�
+     * @return 鏄惁鍒犻櫎鎴愬姛
      */
     @Override
     public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) {
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
index af3275e..d13ef2f 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
@@ -1,8 +1,9 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="search" v-show="showSearch">
-        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
 #foreach($column in $columns)
 #if($column.query)
 #set($dictType=$column.dictType)
@@ -14,38 +15,33 @@
 #set($comment=$column.columnComment)
 #end
 #if($column.htmlType == "input" || $column.htmlType == "textarea")
-          <el-form-item label="${comment}" prop="${column.javaField}">
-            <el-input v-model="queryParams.${column.javaField}" placeholder="璇疯緭鍏�${comment}" clearable style="width: 240px" @keyup.enter="handleQuery" />
-          </el-form-item>
+            <el-form-item label="${comment}" prop="${column.javaField}">
+              <el-input v-model="queryParams.${column.javaField}" placeholder="璇疯緭鍏�${comment}" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
 #elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
-          <el-form-item label="${comment}" prop="${column.javaField}">
-            <el-select v-model="queryParams.${column.javaField}" placeholder="璇烽�夋嫨${comment}" clearable>
-              <el-option
-                  v-for="dict in ${dictType}"
-                  :key="dict.value"
-                  :label="dict.label"
-                  :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
+            <el-form-item label="${comment}" prop="${column.javaField}">
+              <el-select v-model="queryParams.${column.javaField}" placeholder="璇烽�夋嫨${comment}" clearable>
+                <el-option v-for="dict in ${dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
+              </el-select>
+            </el-form-item>
 #elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
-          <el-form-item label="${comment}" prop="${column.javaField}">
-            <el-select v-model="queryParams.${column.javaField}" placeholder="璇烽�夋嫨${comment}" clearable>
-              <el-option label="璇烽�夋嫨瀛楀吀鐢熸垚" value="" />
-            </el-select>
-          </el-form-item>
+            <el-form-item label="${comment}" prop="${column.javaField}">
+              <el-select v-model="queryParams.${column.javaField}" placeholder="璇烽�夋嫨${comment}" clearable>
+                <el-option label="璇烽�夋嫨瀛楀吀鐢熸垚" value="" />
+              </el-select>
+            </el-form-item>
 #elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
-          <el-form-item label="${comment}" prop="${column.javaField}">
-            <el-date-picker clearable
+            <el-form-item label="${comment}" prop="${column.javaField}">
+              <el-date-picker clearable
                 v-model="queryParams.${column.javaField}"
                 type="date"
                 value-format="YYYY-MM-DD"
                 placeholder="閫夋嫨${comment}"
-            />
-          </el-form-item>
+              />
+            </el-form-item>
 #elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
-          <el-form-item label="${comment}" style="width: 308px">
-            <el-date-picker
+            <el-form-item label="${comment}" style="width: 308px">
+              <el-date-picker
                 v-model="dateRange${AttrName}"
                 value-format="YYYY-MM-DD HH:mm:ss"
                 type="daterange"
@@ -53,16 +49,17 @@
                 start-placeholder="寮�濮嬫棩鏈�"
                 end-placeholder="缁撴潫鏃ユ湡"
                 :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-            />
-          </el-form-item>
+              />
+            </el-form-item>
 #end
 #end
 #end
-          <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-          </el-form-item>
-        </el-form>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
       </div>
     </transition>
 
@@ -79,12 +76,12 @@
         </el-row>
       </template>
       <el-table
+        ref="${businessName}TableRef"
         v-loading="loading"
         :data="${businessName}List"
         row-key="${treeCode}"
         :default-expand-all="isExpandAll"
-        :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
-        ref="${businessName}TableRef"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
       >
 #foreach($column in $columns)
 #set($javaField=$column.javaField)
@@ -225,9 +222,9 @@
               v-for="dict in ${dictType}"
               :key="dict.value"
 #if($column.javaType == "Integer" || $column.javaType == "Long")
-              :label="parseInt(dict.value)"
+              :value="parseInt(dict.value)"
 #else
-              :label="dict.value"
+              :value="dict.value"
 #end
             >{{dict.label}}</el-radio>
           </el-radio-group>
@@ -235,7 +232,7 @@
 #elseif($column.htmlType == "radio" && $dictType)
         <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
-            <el-radio label="1">璇烽�夋嫨瀛楀吀鐢熸垚</el-radio>
+            <el-radio value="1">璇烽�夋嫨瀛楀吀鐢熸垚</el-radio>
           </el-radio-group>
         </el-form-item>
 #elseif($column.htmlType == "datetime")
@@ -424,9 +421,9 @@
   reset();
   getTreeselect();
   if (row != null && row.${treeCode}) {
-      form.value.${treeParentCode} = row.${treeCode};
+    form.value.${treeParentCode} = row.${treeCode};
   } else {
-      form.value.${treeParentCode} = 0;
+    form.value.${treeParentCode} = 0;
   }
   dialog.visible = true;
   dialog.title = "娣诲姞${functionName}";
diff --git a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
index 8b132f4..886f4ab 100644
--- a/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
+++ b/ruoyi-modules/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
@@ -1,8 +1,9 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="search" v-show="showSearch">
-        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
 #foreach($column in $columns)
 #if($column.query)
 #set($dictType=$column.dictType)
@@ -14,38 +15,33 @@
 #set($comment=$column.columnComment)
 #end
 #if($column.htmlType == "input" || $column.htmlType == "textarea")
-          <el-form-item label="${comment}" prop="${column.javaField}">
-            <el-input v-model="queryParams.${column.javaField}" placeholder="璇疯緭鍏�${comment}" clearable style="width: 240px" @keyup.enter="handleQuery" />
-          </el-form-item>
+            <el-form-item label="${comment}" prop="${column.javaField}">
+              <el-input v-model="queryParams.${column.javaField}" placeholder="璇疯緭鍏�${comment}" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
 #elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
-          <el-form-item label="${comment}" prop="${column.javaField}">
-            <el-select v-model="queryParams.${column.javaField}" placeholder="璇烽�夋嫨${comment}" clearable>
-              <el-option
-                v-for="dict in ${dictType}"
-                :key="dict.value"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
+            <el-form-item label="${comment}" prop="${column.javaField}">
+              <el-select v-model="queryParams.${column.javaField}" placeholder="璇烽�夋嫨${comment}" clearable >
+                <el-option v-for="dict in ${dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
+              </el-select>
+            </el-form-item>
 #elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
-          <el-form-item label="${comment}" prop="${column.javaField}">
-            <el-select v-model="queryParams.${column.javaField}" placeholder="璇烽�夋嫨${comment}" clearable>
-              <el-option label="璇烽�夋嫨瀛楀吀鐢熸垚" value="" />
-            </el-select>
-          </el-form-item>
+            <el-form-item label="${comment}" prop="${column.javaField}">
+              <el-select v-model="queryParams.${column.javaField}" placeholder="璇烽�夋嫨${comment}" clearable >
+                <el-option label="璇烽�夋嫨瀛楀吀鐢熸垚" value="" />
+              </el-select>
+            </el-form-item>
 #elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
-          <el-form-item label="${comment}" prop="${column.javaField}">
-            <el-date-picker clearable
-              v-model="queryParams.${column.javaField}"
-              type="date"
-              value-format="YYYY-MM-DD"
-              placeholder="璇烽�夋嫨${comment}"
-            />
-          </el-form-item>
+            <el-form-item label="${comment}" prop="${column.javaField}">
+              <el-date-picker clearable
+                v-model="queryParams.${column.javaField}"
+                type="date"
+                value-format="YYYY-MM-DD"
+                placeholder="璇烽�夋嫨${comment}"
+              />
+            </el-form-item>
 #elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
-          <el-form-item label="${comment}" style="width: 308px">
-            <el-date-picker
+            <el-form-item label="${comment}" style="width: 308px">
+              <el-date-picker
                 v-model="dateRange${AttrName}"
                 value-format="YYYY-MM-DD HH:mm:ss"
                 type="daterange"
@@ -53,16 +49,17 @@
                 start-placeholder="寮�濮嬫棩鏈�"
                 end-placeholder="缁撴潫鏃ユ湡"
                 :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
-            />
-          </el-form-item>
+              />
+            </el-form-item>
 #end
 #end
 #end
-          <el-form-item>
-            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
-            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
-          </el-form-item>
-        </el-form>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
       </div>
     </transition>
 
@@ -135,13 +132,7 @@
         </el-table-column>
       </el-table>
 
-      <pagination
-          v-show="total>0"
-          :total="total"
-          v-model:page="queryParams.pageNum"
-          v-model:limit="queryParams.pageSize"
-          @pagination="getList"
-      />
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀�${functionName}瀵硅瘽妗� -->
     <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
@@ -214,12 +205,12 @@
         <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
             <el-radio
-                v-for="dict in ${dictType}"
-                :key="dict.value"
+              v-for="dict in ${dictType}"
+              :key="dict.value"
 #if($column.javaType == "Integer" || $column.javaType == "Long")
-                :label="parseInt(dict.value)"
+              :value="parseInt(dict.value)"
 #else
-                :label="dict.value"
+              :value="dict.value"
 #end
             >{{dict.label}}</el-radio>
           </el-radio-group>
@@ -227,7 +218,7 @@
 #elseif($column.htmlType == "radio" && $dictType)
         <el-form-item label="${comment}" prop="${field}">
           <el-radio-group v-model="form.${field}">
-                <el-radio label="1">璇烽�夋嫨瀛楀吀鐢熸垚</el-radio>
+            <el-radio value="1">璇烽�夋嫨瀛楀吀鐢熸垚</el-radio>
           </el-radio-group>
         </el-form-item>
 #elseif($column.htmlType == "datetime")
@@ -439,7 +430,7 @@
       } else {
         await add${BusinessName}(form.value).finally(() =>  buttonLoading.value = false);
       }
-      proxy?.#[[$modal]]#.msgSuccess("淇敼鎴愬姛");
+      proxy?.#[[$modal]]#.msgSuccess("鎿嶄綔鎴愬姛");
       dialog.visible = false;
       await getList();
     }
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/package-info.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/package-info.java
index 325abdd..2f118b0 100644
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/package-info.java
+++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/package-info.java
@@ -1,6 +1 @@
-/**
- * 婕旂ず鐢ㄤ緥
- * copy from https://github.com/PowerJob/PowerJob/tree/master/powerjob-worker-samples/src/main/java/tech/powerjob/samples
- */
-
 package org.dromara.job;
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/BroadcastProcessorDemo.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/BroadcastProcessorDemo.java
deleted file mode 100644
index 2b4c28a..0000000
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/BroadcastProcessorDemo.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.dromara.job.processors;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-import tech.powerjob.common.utils.NetUtils;
-import tech.powerjob.worker.core.processor.ProcessResult;
-import tech.powerjob.worker.core.processor.TaskContext;
-import tech.powerjob.worker.core.processor.TaskResult;
-import tech.powerjob.worker.core.processor.sdk.BroadcastProcessor;
-import tech.powerjob.worker.log.OmsLogger;
-
-import java.util.List;
-
-/**
- * 骞挎挱澶勭悊鍣� 绀轰緥
- *
- * @author tjq
- * @since 2020/4/17
- */
-@Slf4j
-@Component
-public class BroadcastProcessorDemo implements BroadcastProcessor {
-
-    @Override
-    public ProcessResult preProcess(TaskContext context) {
-        System.out.println("===== BroadcastProcessorDemo#preProcess ======");
-        context.getOmsLogger().info("BroadcastProcessorDemo#preProcess, current host: {}", NetUtils.getLocalHost());
-        if ("rootFailed".equals(context.getJobParams())) {
-            return new ProcessResult(false, "console need failed");
-        } else {
-            return new ProcessResult(true);
-        }
-    }
-
-    @Override
-    public ProcessResult process(TaskContext taskContext) throws Exception {
-        OmsLogger logger = taskContext.getOmsLogger();
-        System.out.println("===== BroadcastProcessorDemo#process ======");
-        logger.info("BroadcastProcessorDemo#process, current host: {}", NetUtils.getLocalHost());
-        long sleepTime = 1000;
-        try {
-            sleepTime = Long.parseLong(taskContext.getJobParams());
-        } catch (Exception e) {
-            logger.warn("[BroadcastProcessor] parse sleep time failed!", e);
-        }
-        Thread.sleep(Math.max(sleepTime, 1000));
-        return new ProcessResult(true);
-    }
-
-    @Override
-    public ProcessResult postProcess(TaskContext context, List<TaskResult> taskResults) {
-        System.out.println("===== BroadcastProcessorDemo#postProcess ======");
-        context.getOmsLogger().info("BroadcastProcessorDemo#postProcess, current host: {}, taskResult: {}", NetUtils.getLocalHost(), taskResults);
-        return new ProcessResult(true, "success");
-    }
-}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/LogTestProcessor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/LogTestProcessor.java
deleted file mode 100644
index 2a1000f..0000000
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/LogTestProcessor.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.dromara.job.processors;
-
-import com.alibaba.fastjson.JSONObject;
-import org.springframework.stereotype.Component;
-import tech.powerjob.official.processors.util.CommonUtils;
-import tech.powerjob.worker.core.processor.ProcessResult;
-import tech.powerjob.worker.core.processor.TaskContext;
-import tech.powerjob.worker.core.processor.sdk.BasicProcessor;
-import tech.powerjob.worker.log.OmsLogger;
-
-import java.util.Date;
-import java.util.Optional;
-
-/**
- * LogTestProcessor
- *
- * @author tjq
- * @since 2022/9/18
- */
-@Component
-public class LogTestProcessor implements BasicProcessor {
-
-    @Override
-    public ProcessResult process(TaskContext context) throws Exception {
-
-        final OmsLogger omsLogger = context.getOmsLogger();
-        final String parseParams = CommonUtils.parseParams(context);
-        final JSONObject config = Optional.ofNullable(JSONObject.parseObject(parseParams)).orElse(new JSONObject());
-
-        final long loopTimes = Optional.ofNullable(config.getLong("loopTimes")).orElse(1000L);
-
-        for (int i = 0; i < loopTimes; i++) {
-            omsLogger.debug("[DEBUG] one DEBUG log in {}", new Date());
-            omsLogger.info("[INFO] one INFO log in {}", new Date());
-            omsLogger.warn("[WARN] one WARN log in {}", new Date());
-            omsLogger.error("[ERROR] one ERROR log in {}", new Date());
-        }
-
-        return new ProcessResult(true);
-    }
-}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/MapProcessorDemo.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/MapProcessorDemo.java
deleted file mode 100644
index 720d333..0000000
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/MapProcessorDemo.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.dromara.job.processors;
-
-import com.google.common.collect.Lists;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import org.dromara.common.json.utils.JsonUtils;
-import org.springframework.stereotype.Component;
-import tech.powerjob.worker.core.processor.ProcessResult;
-import tech.powerjob.worker.core.processor.TaskContext;
-import tech.powerjob.worker.core.processor.sdk.MapProcessor;
-
-import java.util.List;
-import java.util.concurrent.ThreadLocalRandom;
-
-/**
- * Map澶勭悊鍣� 绀轰緥
- *
- * @author tjq
- * @since 2020/4/18
- */
-@Component
-public class MapProcessorDemo implements MapProcessor {
-
-
-    /**
-     * 姣忎竴鎵瑰彂閫佷换鍔″ぇ灏�
-     */
-    private static final int BATCH_SIZE = 100;
-    /**
-     * 鍙戦�佺殑鎵规
-     */
-    private static final int BATCH_NUM = 5;
-
-    @Override
-    public ProcessResult process(TaskContext context) throws Exception {
-
-        log.info("============== MapProcessorDemo#process ==============");
-        log.info("isRootTask:{}", isRootTask());
-        log.info("taskContext:{}", JsonUtils.toJsonString(context));
-
-        if (isRootTask()) {
-            log.info("==== MAP ====");
-            List<SubTask> subTasks = Lists.newLinkedList();
-            for (int j = 0; j < BATCH_NUM; j++) {
-                SubTask subTask = new SubTask();
-                subTask.siteId = j;
-                subTask.itemIds = Lists.newLinkedList();
-                subTasks.add(subTask);
-                for (int i = 0; i < BATCH_SIZE; i++) {
-                    subTask.itemIds.add(i + j * 100);
-                }
-            }
-            map(subTasks, "MAP_TEST_TASK");
-            return new ProcessResult(true, "map successfully");
-        } else {
-
-            log.info("==== PROCESS ====");
-            SubTask subTask = (SubTask) context.getSubTask();
-            for (Integer itemId : subTask.getItemIds()) {
-                if (Thread.interrupted()) {
-                    // 浠诲姟琚腑鏂�
-                    log.info("job has been stop! so stop to process subTask: {} => {}", subTask.getSiteId(), itemId);
-                    break;
-                }
-                log.info("processing subTask: {} => {}", subTask.getSiteId(), itemId);
-                int max = Integer.MAX_VALUE >> 7;
-                for (int i = 0; ; i++) {
-                    // 妯℃嫙鑰楁椂鎿嶄綔
-                    if (i > max) {
-                        break;
-                    }
-                }
-            }
-            // 娴嬭瘯鍦� Map 浠诲姟涓拷鍔犱笂涓嬫枃
-            context.getWorkflowContext().appendData2WfContext("Yasuo", "A sword's poor company for a long road.");
-            boolean b = ThreadLocalRandom.current().nextBoolean();
-            if (context.getCurrentRetryTimes() >= 1) {
-                // 閲嶈瘯鐨勮瘽涓�瀹氫細鎴愬姛
-                b = true;
-            }
-            return new ProcessResult(b, "RESULT:" + b);
-        }
-    }
-
-    @Getter
-    @NoArgsConstructor
-    @AllArgsConstructor
-    public static class SubTask {
-        private Integer siteId;
-        private List<Integer> itemIds;
-    }
-}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/MapReduceProcessorDemo.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/MapReduceProcessorDemo.java
deleted file mode 100644
index 1498854..0000000
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/MapReduceProcessorDemo.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.dromara.job.processors;
-
-import cn.hutool.core.lang.Dict;
-import com.google.common.collect.Lists;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.ToString;
-import lombok.extern.slf4j.Slf4j;
-import org.dromara.common.json.utils.JsonUtils;
-import org.springframework.stereotype.Component;
-import tech.powerjob.worker.core.processor.ProcessResult;
-import tech.powerjob.worker.core.processor.TaskContext;
-import tech.powerjob.worker.core.processor.TaskResult;
-import tech.powerjob.worker.core.processor.sdk.MapReduceProcessor;
-import tech.powerjob.worker.log.OmsLogger;
-
-import java.util.List;
-import java.util.concurrent.ThreadLocalRandom;
-
-/**
- * MapReduce 澶勭悊鍣ㄧず渚�
- * 鎺у埗鍙板弬鏁帮細{"batchSize": 100, "batchNum": 2}
- *
- * @author tjq
- * @since 2020/4/17
- */
-@Slf4j
-@Component
-public class MapReduceProcessorDemo implements MapReduceProcessor {
-
-    @Override
-    public ProcessResult process(TaskContext context) throws Exception {
-
-        OmsLogger omsLogger = context.getOmsLogger();
-
-        log.info("============== TestMapReduceProcessor#process ==============");
-        log.info("isRootTask:{}", isRootTask());
-        log.info("taskContext:{}", JsonUtils.toJsonString(context));
-
-        // 鏍规嵁鎺у埗鍙板弬鏁拌幏鍙朚R鎵规鍙婂瓙浠诲姟澶у皬
-        final Dict jobParams = JsonUtils.parseMap(context.getJobParams());
-
-        Integer batchSize = (Integer) jobParams.getOrDefault("batchSize", 100);
-        Integer batchNum = (Integer) jobParams.getOrDefault("batchNum", 10);
-
-        if (isRootTask()) {
-            log.info("==== MAP ====");
-            omsLogger.info("[DemoMRProcessor] start root task~");
-            List<TestSubTask> subTasks = Lists.newLinkedList();
-            for (int j = 0; j < batchNum; j++) {
-                for (int i = 0; i < batchSize; i++) {
-                    int x = j * batchSize + i;
-                    subTasks.add(new TestSubTask("name" + x, x));
-                }
-                map(subTasks, "MAP_TEST_TASK");
-                subTasks.clear();
-            }
-            omsLogger.info("[DemoMRProcessor] map success~");
-            return new ProcessResult(true, "MAP_SUCCESS");
-        } else {
-            log.info("==== NORMAL_PROCESS ====");
-            omsLogger.info("[DemoMRProcessor] process subTask: {}.", JsonUtils.toJsonString(context.getSubTask()));
-            log.info("subTask: {}", JsonUtils.toJsonString(context.getSubTask()));
-            Thread.sleep(1000);
-            if (context.getCurrentRetryTimes() == 0) {
-                return new ProcessResult(false, "FIRST_FAILED");
-            } else {
-                return new ProcessResult(true, "PROCESS_SUCCESS");
-            }
-        }
-    }
-
-    @Override
-    public ProcessResult reduce(TaskContext context, List<TaskResult> taskResults) {
-        log.info("================ MapReduceProcessorDemo#reduce ================");
-        log.info("TaskContext: {}", JsonUtils.toJsonString(context));
-        log.info("List<TaskResult>: {}", JsonUtils.toJsonString(taskResults));
-        context.getOmsLogger().info("MapReduce job finished, result is {}.", taskResults);
-
-        boolean success = ThreadLocalRandom.current().nextBoolean();
-        return new ProcessResult(success, context + ": " + success);
-    }
-
-    @Getter
-    @ToString
-    @NoArgsConstructor
-    @AllArgsConstructor
-    public static class TestSubTask {
-        private String name;
-        private int age;
-    }
-}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/SimpleProcessor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/SimpleProcessor.java
deleted file mode 100644
index 3342cfe..0000000
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/SimpleProcessor.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.dromara.job.processors;
-
-import org.springframework.stereotype.Component;
-import tech.powerjob.worker.core.processor.ProcessResult;
-import tech.powerjob.worker.core.processor.TaskContext;
-import tech.powerjob.worker.core.processor.sdk.BasicProcessor;
-import tech.powerjob.worker.log.OmsLogger;
-
-import java.util.Optional;
-
-/**
- * @author Echo009
- * @since 2022/4/27
- */
-@Component
-public class SimpleProcessor implements BasicProcessor {
-
-    @Override
-    public ProcessResult process(TaskContext context) throws Exception {
-
-        OmsLogger logger = context.getOmsLogger();
-
-        String jobParams = Optional.ofNullable(context.getJobParams()).orElse("S");
-        logger.info("Current context:{}", context.getWorkflowContext());
-        logger.info("Current job params:{}", jobParams);
-
-        // 娴嬭瘯涓枃闂 #581
-        if (jobParams.contains("CN")) {
-            return new ProcessResult(true, "浠诲姟鎴愬姛鍟︼紒锛侊紒");
-        }
-
-        return jobParams.contains("F") ? new ProcessResult(false) : new ProcessResult(true, "yeah!");
-
-    }
-}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/StandaloneProcessorDemo.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/StandaloneProcessorDemo.java
deleted file mode 100644
index ea8eff3..0000000
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/StandaloneProcessorDemo.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.dromara.job.processors;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.stereotype.Component;
-import tech.powerjob.worker.core.processor.ProcessResult;
-import tech.powerjob.worker.core.processor.TaskContext;
-import tech.powerjob.worker.core.processor.sdk.BasicProcessor;
-import tech.powerjob.worker.log.OmsLogger;
-
-import java.util.Collections;
-
-/**
- * 鍗曟満澶勭悊鍣� 绀轰緥
- *
- * @author tjq
- * @since 2020/4/17
- */
-@Slf4j
-@Component
-public class StandaloneProcessorDemo implements BasicProcessor {
-
-    @Override
-    public ProcessResult process(TaskContext context) throws Exception {
-        OmsLogger omsLogger = context.getOmsLogger();
-        omsLogger.info("StandaloneProcessorDemo start process,context is {}.", context);
-        omsLogger.info("Notice! If you want this job process failed, your jobParams need to be 'failed'");
-        omsLogger.info("Let's test the exception~");
-        // 娴嬭瘯寮傚父鏃ュ織
-        try {
-            Collections.emptyList().add("277");
-        } catch (Exception e) {
-            omsLogger.error("oh~it seems that we have an exception~", e);
-        }
-        log.info("================ StandaloneProcessorDemo#process ================");
-        log.info("jobParam:{}", context.getJobParams());
-        log.info("instanceParams:{}", context.getInstanceParams());
-        String param;
-        // 瑙f瀽鍙傛暟锛岄潪澶勪簬宸ヤ綔娴佷腑鏃讹紝浼樺厛鍙栧疄渚嬪弬鏁帮紙鍏佽鍔ㄦ�乕instanceParams]瑕嗙洊闈欐�佸弬鏁癧jobParams]锛�
-        if (context.getWorkflowContext() == null) {
-            param = StringUtils.isBlank(context.getInstanceParams()) ? context.getJobParams() : context.getInstanceParams();
-        } else {
-            param = context.getJobParams();
-        }
-        // 鏍规嵁鍙傛暟鍒ゆ柇鏄惁鎴愬姛
-        boolean success = !"failed".equals(param);
-        omsLogger.info("StandaloneProcessorDemo finished process,success: {}", success);
-        omsLogger.info("anyway, we finished the job successfully~Congratulations!");
-        return new ProcessResult(success, context + ": " + success);
-    }
-}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/TimeoutProcessor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/TimeoutProcessor.java
deleted file mode 100644
index 4b5899f..0000000
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/processors/TimeoutProcessor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.dromara.job.processors;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-import tech.powerjob.worker.core.processor.ProcessResult;
-import tech.powerjob.worker.core.processor.TaskContext;
-import tech.powerjob.worker.core.processor.sdk.BasicProcessor;
-
-/**
- * 娴嬭瘯瓒呮椂浠诲姟锛堝彲涓柇锛�
- *
- * @author tjq
- * @since 2020/4/20
- */
-@Component
-@Slf4j
-public class TimeoutProcessor implements BasicProcessor {
-    @Override
-    public ProcessResult process(TaskContext context) throws Exception {
-        long sleepTime = Long.parseLong(context.getJobParams());
-        log.info("TaskInstance({}) will sleep {} ms", context.getInstanceId(), sleepTime);
-        Thread.sleep(Long.parseLong(context.getJobParams()));
-        return new ProcessResult(true, "impossible~~~~QAQ~");
-    }
-}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java
new file mode 100644
index 0000000..5bea9da
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestAnnoJobExecutor.java
@@ -0,0 +1,23 @@
+package org.dromara.job.snailjob;
+
+import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import com.aizuda.snailjob.common.core.util.JsonUtil;
+import com.aizuda.snailjob.common.log.SnailJobLog;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author opensnail
+ * @date 2024-05-17
+ */
+@Component
+@JobExecutor(name = "testJobExecutor")
+public class TestAnnoJobExecutor {
+
+    public ExecuteResult jobExecute(JobArgs jobArgs) {
+        SnailJobLog.LOCAL.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs));
+        SnailJobLog.REMOTE.info("testJobExecutor. jobArgs:{}", JsonUtil.toJsonString(jobArgs));
+        return ExecuteResult.success("娴嬭瘯鎴愬姛");
+    }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestClassJobExecutor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestClassJobExecutor.java
new file mode 100644
index 0000000..6f7c21f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestClassJobExecutor.java
@@ -0,0 +1,19 @@
+package org.dromara.job.snailjob;
+
+import com.aizuda.snailjob.client.job.core.dto.JobArgs;
+import com.aizuda.snailjob.client.job.core.executor.AbstractJobExecutor;
+import com.aizuda.snailjob.client.model.ExecuteResult;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author opensnail
+ * @date 2024-05-17
+ */
+@Component
+public class TestClassJobExecutor extends AbstractJobExecutor {
+
+    @Override
+    protected ExecuteResult doJobExecute(JobArgs jobArgs) {
+        return ExecuteResult.success("TestJobExecutor娴嬭瘯鎴愬姛");
+    }
+}
diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/workflow/WorkflowStandaloneProcessor.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/workflow/WorkflowStandaloneProcessor.java
deleted file mode 100644
index 51187e3..0000000
--- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/workflow/WorkflowStandaloneProcessor.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.dromara.job.workflow;
-
-import com.alibaba.fastjson.JSON;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-import tech.powerjob.worker.core.processor.ProcessResult;
-import tech.powerjob.worker.core.processor.TaskContext;
-import tech.powerjob.worker.core.processor.sdk.BasicProcessor;
-import tech.powerjob.worker.log.OmsLogger;
-
-import java.util.Map;
-
-/**
- * 宸ヤ綔娴佹祴璇�
- *
- * @author tjq
- * @since 2020/6/2
- */
-@Component
-@Slf4j
-public class WorkflowStandaloneProcessor implements BasicProcessor {
-
-    @Override
-    public ProcessResult process(TaskContext context) throws Exception {
-        OmsLogger logger = context.getOmsLogger();
-        logger.info("current jobParams: {}", context.getJobParams());
-        logger.info("current context: {}", context.getWorkflowContext());
-        log.info("jobParams:{}", context.getJobParams());
-        log.info("currentContext:{}", JSON.toJSONString(context));
-
-        // 灏濊瘯鑾峰彇涓婃父浠诲姟
-        Map<String, String> workflowContext = context.getWorkflowContext().fetchWorkflowContext();
-        log.info("宸ヤ綔娴佷笂涓嬫枃鏁版嵁:{}", workflowContext);
-        return new ProcessResult(true, context.getJobId() + " process successfully.");
-    }
-}
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java
index def448d..9b08c2d 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java
@@ -21,6 +21,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 鍦ㄧ嚎鐢ㄦ埛鐩戞帶
@@ -87,4 +88,43 @@
         }
         return R.ok();
     }
+
+    /**
+     * 鑾峰彇褰撳墠鐢ㄦ埛鐧诲綍鍦ㄧ嚎璁惧
+     */
+    @GetMapping()
+    public TableDataInfo<SysUserOnline> getInfo() {
+        // 鑾峰彇鎸囧畾璐﹀彿 id 鐨� token 闆嗗悎
+        List<String> tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString());
+        List<UserOnlineDTO> userOnlineDTOList = tokenIds.stream()
+            .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1)
+            .map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token))
+            .collect(Collectors.toList());
+        //澶嶅埗鍜屽鐞� SysUserOnline 瀵硅薄鍒楄〃
+        Collections.reverse(userOnlineDTOList);
+        userOnlineDTOList.removeAll(Collections.singleton(null));
+        List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
+        return TableDataInfo.build(userOnlineList);
+    }
+
+    /**
+     * 寮洪��褰撳墠鍦ㄧ嚎璁惧
+     *
+     * @param tokenId token鍊�
+     */
+    @Log(title = "鍦ㄧ嚎璁惧", businessType = BusinessType.FORCE)
+    @PostMapping("/{tokenId}")
+    public R<Void> remove(@PathVariable("tokenId") String tokenId) {
+        try {
+            // 鑾峰彇鎸囧畾璐﹀彿 id 鐨� token 闆嗗悎
+            List<String> keys = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString());
+            keys.stream()
+                .filter(key -> key.equals(tokenId))
+                .findFirst()
+                .ifPresent(key -> StpUtil.kickoutByTokenValue(tokenId));
+        } catch (NotLoginException ignored) {
+        }
+        return R.ok();
+    }
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java
index 61d30c7..13be4a4 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java
@@ -97,7 +97,7 @@
     @Log(title = "瀹㈡埛绔鐞�", businessType = BusinessType.UPDATE)
     @PutMapping("/changeStatus")
     public R<Void> changeStatus(@RequestBody SysClientBo bo) {
-        return toAjax(sysClientService.updateUserStatus(bo.getId(), bo.getStatus()));
+        return toAjax(sysClientService.updateUserStatus(bo.getClientId(), bo.getStatus()));
     }
 
     /**
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java
index 130c0d1..98b76e4 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDeptController.java
@@ -2,6 +2,7 @@
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import cn.hutool.core.convert.Convert;
+import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.constant.UserConstants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.utils.StringUtils;
@@ -11,7 +12,6 @@
 import org.dromara.system.domain.bo.SysDeptBo;
 import org.dromara.system.domain.vo.SysDeptVo;
 import org.dromara.system.service.ISysDeptService;
-import lombok.RequiredArgsConstructor;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
@@ -75,6 +75,8 @@
     public R<Void> add(@Validated @RequestBody SysDeptBo dept) {
         if (!deptService.checkDeptNameUnique(dept)) {
             return R.fail("鏂板閮ㄩ棬'" + dept.getDeptName() + "'澶辫触锛岄儴闂ㄥ悕绉板凡瀛樺湪");
+        } else if (StringUtils.isNotBlank(dept.getDeptCategory()) && !deptService.checkDeptCategoryUnique(dept)) {
+            return R.fail("鏂板閮ㄩ棬'" + dept.getDeptName() + "'澶辫触锛岄儴闂ㄧ被鍒紪鐮佸凡瀛樺湪");
         }
         return toAjax(deptService.insertDept(dept));
     }
@@ -90,6 +92,8 @@
         deptService.checkDeptDataScope(deptId);
         if (!deptService.checkDeptNameUnique(dept)) {
             return R.fail("淇敼閮ㄩ棬'" + dept.getDeptName() + "'澶辫触锛岄儴闂ㄥ悕绉板凡瀛樺湪");
+        } else if (StringUtils.isNotBlank(dept.getDeptCategory()) && !deptService.checkDeptCategoryUnique(dept)) {
+            return R.fail("淇敼閮ㄩ棬'" + dept.getDeptName() + "'澶辫触锛岄儴闂ㄧ被鍒紪鐮佸凡瀛樺湪");
         } else if (dept.getParentId().equals(deptId)) {
             return R.fail("淇敼閮ㄩ棬'" + dept.getDeptName() + "'澶辫触锛屼笂绾ч儴闂ㄤ笉鑳芥槸鑷繁");
         } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())) {
@@ -120,4 +124,16 @@
         deptService.checkDeptDataScope(deptId);
         return toAjax(deptService.deleteDeptById(deptId));
     }
+
+    /**
+     * 鑾峰彇閮ㄩ棬閫夋嫨妗嗗垪琛�
+     *
+     * @param deptIds 閮ㄩ棬ID涓�
+     */
+    @SaCheckPermission("system:dept:query")
+    @GetMapping("/optionselect")
+    public R<List<SysDeptVo>> optionselect(@RequestParam(required = false) Long[] deptIds) {
+        return R.ok(deptService.selectDeptByIds(deptIds == null ? null : List.of(deptIds)));
+    }
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java
index cc8034e..5752751 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java
@@ -87,6 +87,9 @@
     @Log(title = "瀛楀吀鏁版嵁", businessType = BusinessType.INSERT)
     @PostMapping
     public R<Void> add(@Validated @RequestBody SysDictDataBo dict) {
+        if (!dictDataService.checkDictDataUnique(dict)) {
+            return R.fail("鏂板瀛楀吀鏁版嵁'" + dict.getDictValue() + "'澶辫触锛屽瓧鍏搁敭鍊煎凡瀛樺湪");
+        }
         dictDataService.insertDictData(dict);
         return R.ok();
     }
@@ -98,6 +101,9 @@
     @Log(title = "瀛楀吀鏁版嵁", businessType = BusinessType.UPDATE)
     @PutMapping
     public R<Void> edit(@Validated @RequestBody SysDictDataBo dict) {
+        if (!dictDataService.checkDictDataUnique(dict)) {
+            return R.fail("淇敼瀛楀吀鏁版嵁'" + dict.getDictValue() + "'澶辫触锛屽瓧鍏搁敭鍊煎凡瀛樺湪");
+        }
         dictDataService.updateDictData(dict);
         return R.ok();
     }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java
index fe62fdb..782bcfc 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java
@@ -1,6 +1,9 @@
 package org.dromara.system.controller.system;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.ObjectUtil;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.constant.UserConstants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.excel.utils.ExcelUtil;
@@ -12,11 +15,10 @@
 import org.dromara.system.domain.bo.SysPostBo;
 import org.dromara.system.domain.vo.SysPostVo;
 import org.dromara.system.service.ISysPostService;
-import jakarta.servlet.http.HttpServletResponse;
-import lombok.RequiredArgsConstructor;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -110,12 +112,22 @@
 
     /**
      * 鑾峰彇宀椾綅閫夋嫨妗嗗垪琛�
+     *
+     * @param postIds 宀椾綅ID涓�
+     * @param deptId  閮ㄩ棬id
      */
+    @SaCheckPermission("system:post:query")
     @GetMapping("/optionselect")
-    public R<List<SysPostVo>> optionselect() {
-        SysPostBo postBo = new SysPostBo();
-        postBo.setStatus(UserConstants.POST_NORMAL);
-        List<SysPostVo> posts = postService.selectPostList(postBo);
-        return R.ok(posts);
+    public R<List<SysPostVo>> optionselect(@RequestParam(required = false) Long[] postIds, @RequestParam(required = false) Long deptId) {
+        List<SysPostVo> list = new ArrayList<>();
+        if (ObjectUtil.isNotNull(deptId)) {
+            SysPostBo post = new SysPostBo();
+            post.setDeptId(deptId);
+            list = postService.selectPostList(post);
+        } else if (postIds != null) {
+            list = postService.selectPostByIds(List.of(postIds));
+        }
+        return R.ok(list);
     }
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java
index f9c4b3d..0cdb675 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysProfileController.java
@@ -19,6 +19,7 @@
 import org.dromara.system.domain.vo.SysOssVo;
 import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.system.service.ISysOssService;
+import org.dromara.system.service.ISysRoleService;
 import org.dromara.system.service.ISysUserService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.http.MediaType;
@@ -50,8 +51,8 @@
         SysUserVo user = userService.selectUserById(LoginHelper.getUserId());
         ProfileVo profileVo = new ProfileVo();
         profileVo.setUser(user);
-        profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserName()));
-        profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserName()));
+        profileVo.setRoleGroup(userService.selectUserRoleGroup(user.getUserId()));
+        profileVo.setPostGroup(userService.selectUserPostGroup(user.getUserId()));
         return R.ok(profileVo);
     }
 
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java
index 3a9418b..d4a9dc8 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java
@@ -149,11 +149,13 @@
 
     /**
      * 鑾峰彇瑙掕壊閫夋嫨妗嗗垪琛�
+     *
+     * @param roleIds 瑙掕壊ID涓�
      */
     @SaCheckPermission("system:role:query")
     @GetMapping("/optionselect")
-    public R<List<SysRoleVo>> optionselect() {
-        return R.ok(roleService.selectRoleAll());
+    public R<List<SysRoleVo>> optionselect(@RequestParam(required = false) Long[] roleIds) {
+        return R.ok(roleService.selectRoleByIds(roleIds == null ? null : List.of(roleIds)));
     }
 
     /**
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java
index beefe4a..36104d6 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java
@@ -11,7 +11,6 @@
 import org.dromara.common.core.constant.UserConstants;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.domain.model.LoginUser;
-import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StreamUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.encrypt.annotation.ApiEncrypt;
@@ -72,9 +71,8 @@
     @SaCheckPermission("system:user:export")
     @PostMapping("/export")
     public void export(SysUserBo user, HttpServletResponse response) {
-        List<SysUserVo> list = userService.selectUserList(user);
-        List<SysUserExportVo> listVo = MapstructUtils.convert(list, SysUserExportVo.class);
-        ExcelUtil.exportExcel(listVo, "鐢ㄦ埛鏁版嵁", SysUserExportVo.class, response);
+        List<SysUserExportVo> list = userService.selectUserExportList(user);
+        ExcelUtil.exportExcel(list, "鐢ㄦ埛鏁版嵁", SysUserExportVo.class, response);
     }
 
     /**
@@ -134,16 +132,19 @@
         SysUserInfoVo userInfoVo = new SysUserInfoVo();
         SysRoleBo roleBo = new SysRoleBo();
         roleBo.setStatus(UserConstants.ROLE_NORMAL);
-        SysPostBo postBo = new SysPostBo();
-        postBo.setStatus(UserConstants.POST_NORMAL);
         List<SysRoleVo> roles = roleService.selectRoleList(roleBo);
         userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin()));
-        userInfoVo.setPosts(postService.selectPostList(postBo));
         if (ObjectUtil.isNotNull(userId)) {
             SysUserVo sysUser = userService.selectUserById(userId);
             userInfoVo.setUser(sysUser);
-            userInfoVo.setRoleIds(StreamUtils.toList(sysUser.getRoles(), SysRoleVo::getRoleId));
-            userInfoVo.setPostIds(postService.selectPostListByUserId(userId));
+            userInfoVo.setRoleIds(roleService.selectRoleListByUserId(userId));
+            Long deptId = sysUser.getDeptId();
+            if (ObjectUtil.isNotNull(deptId)) {
+                SysPostBo postBo = new SysPostBo();
+                postBo.setDeptId(deptId);
+                userInfoVo.setPosts(postService.selectPostList(postBo));
+                userInfoVo.setPostIds(postService.selectPostListByUserId(userId));
+            }
         }
         return R.ok(userInfoVo);
     }
@@ -208,6 +209,19 @@
     }
 
     /**
+     * 鏍规嵁鐢ㄦ埛ID涓叉壒閲忚幏鍙栫敤鎴峰熀纭�淇℃伅
+     *
+     * @param userIds 鐢ㄦ埛ID涓�
+     * @param deptId  閮ㄩ棬ID
+     */
+    @SaCheckPermission("system:user:query")
+    @GetMapping("/optionselect")
+    public R<List<SysUserVo>> optionselect(@RequestParam(required = false) Long[] userIds,
+                                           @RequestParam(required = false) Long deptId) {
+        return R.ok(userService.selectUserByIds(userIds == null ? null : List.of(userIds), deptId));
+    }
+
+    /**
      * 閲嶇疆瀵嗙爜
      */
     @ApiEncrypt
@@ -241,8 +255,9 @@
     @SaCheckPermission("system:user:query")
     @GetMapping("/authRole/{userId}")
     public R<SysUserInfoVo> authRole(@PathVariable Long userId) {
+        userService.checkUserDataScope(userId);
         SysUserVo user = userService.selectUserById(userId);
-        List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
+        List<SysRoleVo> roles = roleService.selectRolesAuthByUserId(userId);
         SysUserInfoVo userInfoVo = new SysUserInfoVo();
         userInfoVo.setUser(user);
         userInfoVo.setRoles(LoginHelper.isSuperAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isSuperAdmin()));
@@ -281,4 +296,5 @@
     public R<List<SysUserVo>> listByDept(@PathVariable @NotNull Long deptId) {
         return R.ok(userService.selectUserListByDept(deptId));
     }
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java
index 70f1a9a..48ca682 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDept.java
@@ -3,9 +3,9 @@
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
-import org.dromara.common.tenant.core.TenantEntity;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
 
 import java.io.Serial;
 
@@ -40,6 +40,11 @@
     private String deptName;
 
     /**
+     * 閮ㄩ棬绫诲埆缂栫爜
+     */
+    private String deptCategory;
+
+    /**
      * 鏄剧ず椤哄簭
      */
     private Integer orderNum;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java
index 8a9e25e..4b67d63 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysOssConfig.java
@@ -17,7 +17,7 @@
 public class SysOssConfig extends BaseEntity {
 
     /**
-     * 涓诲缓
+     * 涓婚敭
      */
     @TableId(value = "oss_config_id")
     private Long ossConfigId;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java
index 7fe7039..2c985da 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysPost.java
@@ -24,6 +24,11 @@
     private Long postId;
 
     /**
+     * 閮ㄩ棬id
+     */
+    private Long deptId;
+
+    /**
      * 宀椾綅缂栫爜
      */
     private String postCode;
@@ -34,6 +39,11 @@
     private String postName;
 
     /**
+     * 宀椾綅绫诲埆缂栫爜
+     */
+    private String postCategory;
+
+    /**
      * 宀椾綅鎺掑簭
      */
     private Integer postSort;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java
index 6def906..ba30eb6 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUserOnline.java
@@ -7,7 +7,6 @@
  *
  * @author Lion Li
  */
-
 @Data
 public class SysUserOnline {
 
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java
index a5031d7..5f64d6f 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDeptBo.java
@@ -39,6 +39,12 @@
     private String deptName;
 
     /**
+     * 閮ㄩ棬绫诲埆缂栫爜
+     */
+    @Size(min = 0, max = 100, message = "閮ㄩ棬绫诲埆缂栫爜闀垮害涓嶈兘瓒呰繃{max}涓瓧绗�")
+    private String deptCategory;
+
+    /**
      * 鏄剧ず椤哄簭
      */
     @NotNull(message = "鏄剧ず椤哄簭涓嶈兘涓虹┖")
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java
index 39257f7..fcc1ac1 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysDictTypeBo.java
@@ -6,6 +6,7 @@
 import jakarta.validation.constraints.Size;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.dromara.common.core.constant.RegexConstants;
 import org.dromara.common.mybatis.core.domain.BaseEntity;
 import org.dromara.system.domain.SysDictType;
 
@@ -37,7 +38,7 @@
      */
     @NotBlank(message = "瀛楀吀绫诲瀷涓嶈兘涓虹┖")
     @Size(min = 0, max = 100, message = "瀛楀吀绫诲瀷绫诲瀷闀垮害涓嶈兘瓒呰繃{max}涓瓧绗�")
-    @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "瀛楀吀绫诲瀷蹇呴』浠ュ瓧姣嶅紑澶达紝涓斿彧鑳戒负锛堝皬鍐欏瓧姣嶏紝鏁板瓧锛屼笅婊戠嚎锛�")
+    @Pattern(regexp = RegexConstants.DICTIONARY_TYPE, message = "瀛楀吀绫诲瀷蹇呴』浠ュ瓧姣嶅紑澶达紝涓斿彧鑳戒负锛堝皬鍐欏瓧姣嶏紝鏁板瓧锛屼笅婊戠嚎锛�")
     private String dictType;
 
     /**
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java
index 498280a..fbaafaa 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysMenuBo.java
@@ -4,9 +4,11 @@
 import io.github.linpeilie.annotations.AutoMapper;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Pattern;
 import jakarta.validation.constraints.Size;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.dromara.common.core.constant.RegexConstants;
 import org.dromara.common.mybatis.core.domain.BaseEntity;
 import org.dromara.system.domain.SysMenu;
 
@@ -92,6 +94,7 @@
      */
     @JsonInclude(JsonInclude.Include.NON_NULL)
     @Size(min = 0, max = 100, message = "鏉冮檺鏍囪瘑闀垮害涓嶈兘瓒呰繃{max}涓瓧绗�")
+    @Pattern(regexp = RegexConstants.PERMISSION_STRING, message = "鏉冮檺鏍囪瘑蹇呴』绗﹀悎 tool:build:list 鏍煎紡")
     private String perms;
 
     /**
@@ -103,6 +106,5 @@
      * 澶囨敞
      */
     private String remark;
-
 
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java
index 5887ded..3dc4328 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysOssConfigBo.java
@@ -25,9 +25,9 @@
 public class SysOssConfigBo extends BaseEntity {
 
     /**
-     * 涓诲缓
+     * 涓婚敭
      */
-    @NotNull(message = "涓诲缓涓嶈兘涓虹┖", groups = {EditGroup.class})
+    @NotNull(message = "涓婚敭涓嶈兘涓虹┖", groups = {EditGroup.class})
     private Long ossConfigId;
 
     /**
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java
index b93c39c..09805cd 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysPostBo.java
@@ -26,6 +26,17 @@
     private Long postId;
 
     /**
+     * 閮ㄩ棬id锛堝崟閮ㄩ棬锛�
+     */
+    @NotNull(message = "閮ㄩ棬id涓嶈兘涓虹┖")
+    private Long deptId;
+
+    /**
+     * 褰掑睘閮ㄩ棬id锛堥儴闂ㄦ爲锛�
+     */
+    private Long belongDeptId;
+
+    /**
      * 宀椾綅缂栫爜
      */
     @NotBlank(message = "宀椾綅缂栫爜涓嶈兘涓虹┖")
@@ -38,6 +49,12 @@
     @NotBlank(message = "宀椾綅鍚嶇О涓嶈兘涓虹┖")
     @Size(min = 0, max = 50, message = "宀椾綅鍚嶇О闀垮害涓嶈兘瓒呰繃{max}涓瓧绗�")
     private String postName;
+
+    /**
+     * 宀椾綅绫诲埆缂栫爜
+     */
+    @Size(min = 0, max = 100, message = "绫诲埆缂栫爜闀垮害涓嶈兘瓒呰繃{max}涓瓧绗�")
+    private String postCategory;
 
     /**
      * 鏄剧ず椤哄簭
@@ -54,6 +71,5 @@
      * 澶囨敞
      */
     private String remark;
-
 
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java
index 7784271..cede1e9 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java
@@ -29,9 +29,9 @@
     private Long id;
 
     /**
-     * 鐨勫敮涓�ID
+     * 璁よ瘉鍞竴ID
      */
-    @NotBlank(message = "鐨勫敮涓�ID涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    @NotBlank(message = "璁よ瘉鍞竴ID涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
     private String authId;
 
     /**
@@ -64,7 +64,7 @@
     /**
      * 鐢ㄦ埛鐨� ID
      */
-    @NotBlank(message = "鐢ㄦ埛鐨� ID涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    @NotBlank(message = "鐢ㄦ埛鐨処D涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
     private Long userId;
 
     /**
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java
index 011254e..7ad2759 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java
@@ -103,6 +103,11 @@
      */
     private Long roleId;
 
+    /**
+     * 鎺掗櫎涓嶆煡璇㈢殑鐢ㄦ埛(宸ヤ綔娴佺敤)
+     */
+    private String excludeUserIds;
+
     public SysUserBo(Long userId) {
         this.userId = userId;
     }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java
index c4dd412..c56fb09 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptVo.java
@@ -53,6 +53,12 @@
     private String deptName;
 
     /**
+     * 閮ㄩ棬绫诲埆缂栫爜
+     */
+    @ExcelProperty(value = "閮ㄩ棬绫诲埆缂栫爜")
+    private String deptCategory;
+
+    /**
      * 鏄剧ず椤哄簭
      */
     private Integer orderNum;
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java
index b88ec40..e7cfde4 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysOssConfigVo.java
@@ -25,7 +25,7 @@
     private static final long serialVersionUID = 1L;
 
     /**
-     * 涓诲缓
+     * 涓婚敭
      */
     private Long ossConfigId;
 
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java
index 3e6c6a8..69be547 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysPostVo.java
@@ -2,17 +2,17 @@
 
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
-import org.dromara.common.excel.annotation.ExcelDictFormat;
-import org.dromara.common.excel.convert.ExcelDictConvert;
-import org.dromara.system.domain.SysPost;
 import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.system.domain.SysPost;
 
 import java.io.Serial;
 import java.io.Serializable;
 import java.util.Date;
-
-
 
 /**
  * 宀椾綅淇℃伅瑙嗗浘瀵硅薄 sys_post
@@ -34,6 +34,12 @@
     private Long postId;
 
     /**
+     * 閮ㄩ棬id
+     */
+    @ExcelProperty(value = "閮ㄩ棬id")
+    private Long deptId;
+
+    /**
      * 宀椾綅缂栫爜
      */
     @ExcelProperty(value = "宀椾綅缂栫爜")
@@ -44,6 +50,12 @@
      */
     @ExcelProperty(value = "宀椾綅鍚嶇О")
     private String postName;
+
+    /**
+     * 宀椾綅绫诲埆缂栫爜
+     */
+    @ExcelProperty(value = "绫诲埆缂栫爜")
+    private String postCategory;
 
     /**
      * 鏄剧ず椤哄簭
@@ -70,4 +82,10 @@
     @ExcelProperty(value = "鍒涘缓鏃堕棿")
     private Date createTime;
 
+    /**
+     * 閮ㄩ棬鍚�
+     */
+    @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId")
+    private String deptName;
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java
index 3cc5186..37ec6b7 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserExportVo.java
@@ -20,7 +20,6 @@
 
 @Data
 @NoArgsConstructor
-@AutoMapper(target = SysUserVo.class, convertGenerate = false)
 public class SysUserExportVo implements Serializable {
 
     @Serial
@@ -85,14 +84,12 @@
     /**
      * 閮ㄩ棬鍚嶇О
      */
-    @ReverseAutoMapping(target = "deptName", source = "dept.deptName")
     @ExcelProperty(value = "閮ㄩ棬鍚嶇О")
     private String deptName;
 
     /**
      * 璐熻矗浜�
      */
-    @ReverseAutoMapping(target = "leaderName", source = "dept.leaderName")
     @ExcelProperty(value = "閮ㄩ棬璐熻矗浜�")
     private String leaderName;
 
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java
index 7db91d1..d1f4059 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysUserVo.java
@@ -114,9 +114,10 @@
     private Date createTime;
 
     /**
-     * 閮ㄩ棬瀵硅薄
+     * 閮ㄩ棬鍚�
      */
-    private SysDeptVo dept;
+    @Translation(type = TransConstant.DEPT_ID_TO_NAME, mapper = "deptId")
+    private String deptName;
 
     /**
      * 瑙掕壊瀵硅薄
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java
index 45ad77e..08dda66 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java
@@ -32,7 +32,7 @@
     @DataPermission({
         @DataColumn(key = "deptName", value = "dept_id")
     })
-    SysDeptVo selectDeptById(Long deptId);
+    long countDeptById(Long deptId);
 
     /**
      * 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戜俊鎭�
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java
index 48e6a12..f9bf134 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java
@@ -1,5 +1,11 @@
 package org.dromara.system.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import org.dromara.common.mybatis.annotation.DataColumn;
+import org.dromara.common.mybatis.annotation.DataPermission;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 import org.dromara.system.domain.SysPost;
 import org.dromara.system.domain.vo.SysPostVo;
@@ -13,20 +19,18 @@
  */
 public interface SysPostMapper extends BaseMapperPlus<SysPost, SysPostVo> {
 
-    /**
-     * 鏍规嵁鐢ㄦ埛ID鑾峰彇宀椾綅閫夋嫨妗嗗垪琛�
-     *
-     * @param userId 鐢ㄦ埛ID
-     * @return 閫変腑宀椾綅ID鍒楄〃
-     */
-    List<Long> selectPostListByUserId(Long userId);
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "create_by")
+    })
+    Page<SysPostVo> selectPagePostList(@Param("page") Page<SysPostVo> page, @Param(Constants.WRAPPER) Wrapper<SysPost> queryWrapper);
 
     /**
      * 鏌ヨ鐢ㄦ埛鎵�灞炲矖浣嶇粍
      *
-     * @param userName 鐢ㄦ埛鍚�
+     * @param userId 鐢ㄦ埛ID
      * @return 缁撴灉
      */
-    List<SysPostVo> selectPostsByUserName(String userName);
+    List<SysPostVo> selectPostsByUserId(Long userId);
 
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java
index 55ca769..ac5a47e 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java
@@ -3,12 +3,12 @@
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
 import org.dromara.common.mybatis.annotation.DataColumn;
 import org.dromara.common.mybatis.annotation.DataPermission;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 import org.dromara.system.domain.SysRole;
 import org.dromara.system.domain.vo.SysRoleVo;
-import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -51,21 +51,12 @@
      */
     List<SysRoleVo> selectRolePermissionByUserId(Long userId);
 
-
-    /**
-     * 鏍规嵁鐢ㄦ埛ID鑾峰彇瑙掕壊閫夋嫨妗嗗垪琛�
-     *
-     * @param userId 鐢ㄦ埛ID
-     * @return 閫変腑瑙掕壊ID鍒楄〃
-     */
-    List<Long> selectRoleListByUserId(Long userId);
-
     /**
      * 鏍规嵁鐢ㄦ埛ID鏌ヨ瑙掕壊
      *
-     * @param userName 鐢ㄦ埛鍚�
+     * @param userId 鐢ㄦ埛ID
      * @return 瑙掕壊鍒楄〃
      */
-    List<SysRoleVo> selectRolesByUserName(String userName);
+    List<SysRoleVo> selectRolesByUserId(Long userId);
 
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java
index 4322225..fc7fc6e 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java
@@ -8,6 +8,7 @@
 import org.dromara.common.mybatis.annotation.DataPermission;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 import org.dromara.system.domain.SysUser;
+import org.dromara.system.domain.vo.SysUserExportVo;
 import org.dromara.system.domain.vo.SysUserVo;
 
 import java.util.List;
@@ -20,10 +21,16 @@
 public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo> {
 
     @DataPermission({
-        @DataColumn(key = "deptName", value = "d.dept_id"),
+        @DataColumn(key = "deptName", value = "u.dept_id"),
         @DataColumn(key = "userName", value = "u.user_id")
     })
     Page<SysUserVo> selectPageUserList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
+
+    @DataPermission({
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
+    })
+    List<SysUserVo> selectUserList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
 
     /**
      * 鏍规嵁鏉′欢鍒嗛〉鏌ヨ鐢ㄦ埛鍒楄〃
@@ -35,7 +42,7 @@
         @DataColumn(key = "deptName", value = "d.dept_id"),
         @DataColumn(key = "userName", value = "u.user_id")
     })
-    List<SysUserVo> selectUserList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
+    List<SysUserExportVo> selectUserExportList(@Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
 
     /**
      * 鏍规嵁鏉′欢鍒嗛〉鏌ヨ宸查厤鐢ㄦ埛瑙掕壊鍒楄〃
@@ -61,41 +68,11 @@
     })
     Page<SysUserVo> selectUnallocatedList(@Param("page") Page<SysUser> page, @Param(Constants.WRAPPER) Wrapper<SysUser> queryWrapper);
 
-    /**
-     * 閫氳繃鐢ㄦ埛鍚嶆煡璇㈢敤鎴�
-     *
-     * @param userName 鐢ㄦ埛鍚�
-     * @return 鐢ㄦ埛瀵硅薄淇℃伅
-     */
-    SysUserVo selectUserByUserName(String userName);
-
-    /**
-     * 閫氳繃鎵嬫満鍙锋煡璇㈢敤鎴�
-     *
-     * @param phonenumber 鎵嬫満鍙�
-     * @return 鐢ㄦ埛瀵硅薄淇℃伅
-     */
-    SysUserVo selectUserByPhonenumber(String phonenumber);
-
-    /**
-     * 閫氳繃閭鏌ヨ鐢ㄦ埛
-     *
-     * @param email 閭
-     * @return 鐢ㄦ埛瀵硅薄淇℃伅
-     */
-    SysUserVo selectUserByEmail(String email);
-
-    /**
-     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛
-     *
-     * @param userId 鐢ㄦ埛ID
-     * @return 鐢ㄦ埛瀵硅薄淇℃伅
-     */
     @DataPermission({
-        @DataColumn(key = "deptName", value = "d.dept_id"),
-        @DataColumn(key = "userName", value = "u.user_id")
+        @DataColumn(key = "deptName", value = "dept_id"),
+        @DataColumn(key = "userName", value = "user_id")
     })
-    SysUserVo selectUserById(Long userId);
+    long countUserById(Long userId);
 
     @Override
     @DataPermission({
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java
index db48d4b..d0f8a3c 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysClientService.java
@@ -25,7 +25,7 @@
     /**
      * 鏌ヨ瀹㈡埛绔俊鎭熀浜庡鎴风id
      */
-    SysClient queryByClientId(String clientId);
+    SysClientVo queryByClientId(String clientId);
 
     /**
      * 鏌ヨ瀹㈡埛绔鐞嗗垪琛�
@@ -50,7 +50,7 @@
     /**
      * 淇敼鐘舵��
      */
-    int updateUserStatus(Long id, String status);
+    int updateUserStatus(String clientId, String status);
 
     /**
      * 鏍¢獙骞舵壒閲忓垹闄ゅ鎴风绠$悊淇℃伅
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java
index b209381..cd984d8 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDeptService.java
@@ -53,6 +53,14 @@
     SysDeptVo selectDeptById(Long deptId);
 
     /**
+     * 閫氳繃閮ㄩ棬ID涓叉煡璇㈤儴闂�
+     *
+     * @param deptIds 閮ㄩ棬id涓�
+     * @return 閮ㄩ棬鍒楄〃淇℃伅
+     */
+    List<SysDeptVo> selectDeptByIds(List<Long> deptIds);
+
+    /**
      * 鏍规嵁ID鏌ヨ鎵�鏈夊瓙閮ㄩ棬鏁帮紙姝e父鐘舵�侊級
      *
      * @param deptId 閮ㄩ棬ID
@@ -85,6 +93,14 @@
     boolean checkDeptNameUnique(SysDeptBo dept);
 
     /**
+     * 鏍¢獙閮ㄩ棬绫诲埆缂栫爜鏄惁鍞竴
+     *
+     * @param dept 閮ㄩ棬淇℃伅
+     * @return 缁撴灉
+     */
+    boolean checkDeptCategoryUnique(SysDeptBo dept);
+
+    /**
      * 鏍¢獙閮ㄩ棬鏄惁鏈夋暟鎹潈闄�
      *
      * @param deptId 閮ㄩ棬id
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java
index 30442ce..0e697db 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysDictDataService.java
@@ -64,4 +64,13 @@
      * @return 缁撴灉
      */
     List<SysDictDataVo> updateDictData(SysDictDataBo bo);
+
+    /**
+     * 鏍¢獙瀛楀吀閿�兼槸鍚﹀敮涓�
+     *
+     * @param dict 瀛楀吀鏁版嵁
+     * @return 缁撴灉
+     */
+    boolean checkDictDataUnique(SysDictDataBo dict);
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java
index a8bc57b..2f6dfc9 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssConfigService.java
@@ -31,12 +31,11 @@
      */
     TableDataInfo<SysOssConfigVo> queryPageList(SysOssConfigBo bo, PageQuery pageQuery);
 
-
     /**
      * 鏍规嵁鏂板涓氬姟瀵硅薄鎻掑叆瀵硅薄瀛樺偍閰嶇疆
      *
      * @param bo 瀵硅薄瀛樺偍閰嶇疆鏂板涓氬姟瀵硅薄
-     * @return
+     * @return 缁撴灉
      */
     Boolean insertByBo(SysOssConfigBo bo);
 
@@ -44,7 +43,7 @@
      * 鏍规嵁缂栬緫涓氬姟瀵硅薄淇敼瀵硅薄瀛樺偍閰嶇疆
      *
      * @param bo 瀵硅薄瀛樺偍閰嶇疆缂栬緫涓氬姟瀵硅薄
-     * @return
+     * @return 缁撴灉
      */
     Boolean updateByBo(SysOssConfigBo bo);
 
@@ -53,7 +52,7 @@
      *
      * @param ids     涓婚敭闆嗗悎
      * @param isValid 鏄惁鏍¢獙,true-鍒犻櫎鍓嶆牎楠�,false-涓嶆牎楠�
-     * @return
+     * @return 缁撴灉
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
 
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java
index 2dfe01f..057c068 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysOssService.java
@@ -19,18 +19,62 @@
  */
 public interface ISysOssService {
 
+    /**
+     * 鏌ヨOSS瀵硅薄瀛樺偍鍒楄〃
+     *
+     * @param sysOss    OSS瀵硅薄瀛樺偍鍒嗛〉鏌ヨ瀵硅薄
+     * @param pageQuery 鍒嗛〉鏌ヨ瀹炰綋绫�
+     * @return 缁撴灉
+     */
     TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss, PageQuery pageQuery);
 
+    /**
+     * 鏍规嵁涓�缁� ossIds 鑾峰彇瀵瑰簲鐨� SysOssVo 鍒楄〃
+     *
+     * @param ossIds 涓�缁勬枃浠跺湪鏁版嵁搴撲腑鐨勫敮涓�鏍囪瘑闆嗗悎
+     * @return 鍖呭惈 SysOssVo 瀵硅薄鐨勫垪琛�
+     */
     List<SysOssVo> listByIds(Collection<Long> ossIds);
 
+    /**
+     * 鏍规嵁 ossId 浠庣紦瀛樻垨鏁版嵁搴撲腑鑾峰彇 SysOssVo 瀵硅薄
+     *
+     * @param ossId 鏂囦欢鍦ㄦ暟鎹簱涓殑鍞竴鏍囪瘑
+     * @return SysOssVo 瀵硅薄锛屽寘鍚枃浠朵俊鎭�
+     */
     SysOssVo getById(Long ossId);
 
+    /**
+     * 涓婁紶 MultipartFile 鍒板璞″瓨鍌ㄦ湇鍔★紝骞朵繚瀛樻枃浠朵俊鎭埌鏁版嵁搴�
+     *
+     * @param file 瑕佷笂浼犵殑 MultipartFile 瀵硅薄
+     * @return 涓婁紶鎴愬姛鍚庣殑 SysOssVo 瀵硅薄锛屽寘鍚枃浠朵俊鎭�
+     */
     SysOssVo upload(MultipartFile file);
 
+    /**
+     * 涓婁紶鏂囦欢鍒板璞″瓨鍌ㄦ湇鍔★紝骞朵繚瀛樻枃浠朵俊鎭埌鏁版嵁搴�
+     *
+     * @param file 瑕佷笂浼犵殑鏂囦欢瀵硅薄
+     * @return 涓婁紶鎴愬姛鍚庣殑 SysOssVo 瀵硅薄锛屽寘鍚枃浠朵俊鎭�
+     */
     SysOssVo upload(File file);
 
+    /**
+     * 鏂囦欢涓嬭浇鏂规硶锛屾敮鎸佷竴娆℃�т笅杞藉畬鏁存枃浠�
+     *
+     * @param ossId    OSS瀵硅薄ID
+     * @param response HttpServletResponse瀵硅薄锛岀敤浜庤缃搷搴斿ご鍜屽悜瀹㈡埛绔彂閫佹枃浠跺唴瀹�
+     */
     void download(Long ossId, HttpServletResponse response) throws IOException;
 
+    /**
+     * 鍒犻櫎OSS瀵硅薄瀛樺偍
+     *
+     * @param ids     OSS瀵硅薄ID涓�
+     * @param isValid 鍒ゆ柇鏄惁闇�瑕佹牎楠�
+     * @return 缁撴灉
+     */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
 
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java
index c6409c8..c43f039 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysPostService.java
@@ -49,6 +49,14 @@
     List<Long> selectPostListByUserId(Long userId);
 
     /**
+     * 閫氳繃宀椾綅ID涓叉煡璇㈠矖浣�
+     *
+     * @param postIds 宀椾綅id涓�
+     * @return 宀椾綅鍒楄〃淇℃伅
+     */
+    List<SysPostVo> selectPostByIds(List<Long> postIds);
+
+    /**
      * 鏍¢獙宀椾綅鍚嶇О
      *
      * @param post 宀椾綅淇℃伅
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java
index d2ee61f..64740ae 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysRoleService.java
@@ -36,6 +36,14 @@
     List<SysRoleVo> selectRolesByUserId(Long userId);
 
     /**
+     * 鏍规嵁鐢ㄦ埛ID鏌ヨ瑙掕壊鍒楄〃(鍖呭惈琚巿鏉冪姸鎬�)
+     *
+     * @param userId 鐢ㄦ埛ID
+     * @return 瑙掕壊鍒楄〃
+     */
+    List<SysRoleVo> selectRolesAuthByUserId(Long userId);
+
+    /**
      * 鏍规嵁鐢ㄦ埛ID鏌ヨ瑙掕壊鏉冮檺
      *
      * @param userId 鐢ㄦ埛ID
@@ -65,6 +73,14 @@
      * @return 瑙掕壊瀵硅薄淇℃伅
      */
     SysRoleVo selectRoleById(Long roleId);
+
+    /**
+     * 閫氳繃瑙掕壊ID涓叉煡璇㈣鑹�
+     *
+     * @param roleIds 瑙掕壊ID涓�
+     * @return 瑙掕壊鍒楄〃淇℃伅
+     */
+    List<SysRoleVo> selectRoleByIds(List<Long> roleIds);
 
     /**
      * 鏍¢獙瑙掕壊鍚嶇О鏄惁鍞竴
@@ -180,4 +196,5 @@
     int insertAuthUsers(Long roleId, Long[] userIds);
 
     void cleanOnlineUserByRole(Long roleId);
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java
index d5702c9..cc7016e 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysSocialService.java
@@ -21,7 +21,7 @@
     /**
      * 鏌ヨ绀句細鍖栧叧绯诲垪琛�
      */
-    List<SysSocialVo> queryList();
+    List<SysSocialVo> queryList(SysSocialBo bo);
 
     /**
      * 鏌ヨ绀句細鍖栧叧绯诲垪琛�
@@ -45,9 +45,7 @@
 
 
     /**
-     * 鏍规嵁 authId 鏌ヨ SysSocial 琛ㄥ拰 SysUser 琛紝杩斿洖 SysSocialAuthResult 鏄犲皠鐨勫璞�
-     * @param authId 璁よ瘉ID
-     * @return SysSocial
+     * 鏍规嵁 authId 鏌ヨ
      */
     List<SysSocialVo> selectByAuthId(String authId);
 
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java
index bbe78be..0325a25 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java
@@ -3,6 +3,7 @@
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.system.domain.bo.SysUserBo;
+import org.dromara.system.domain.vo.SysUserExportVo;
 import org.dromara.system.domain.vo.SysUserVo;
 
 import java.util.List;
@@ -23,7 +24,7 @@
      * @param user 鐢ㄦ埛淇℃伅
      * @return 鐢ㄦ埛淇℃伅闆嗗悎淇℃伅
      */
-    List<SysUserVo> selectUserList(SysUserBo user);
+    List<SysUserExportVo> selectUserExportList(SysUserBo user);
 
     /**
      * 鏍规嵁鏉′欢鍒嗛〉鏌ヨ宸插垎閰嶇敤鎴疯鑹插垪琛�
@@ -66,20 +67,29 @@
     SysUserVo selectUserById(Long userId);
 
     /**
+     * 閫氳繃鐢ㄦ埛ID涓叉煡璇㈢敤鎴�
+     *
+     * @param userIds 鐢ㄦ埛ID涓�
+     * @param deptId  閮ㄩ棬id
+     * @return 鐢ㄦ埛鍒楄〃淇℃伅
+     */
+    List<SysUserVo> selectUserByIds(List<Long> userIds, Long deptId);
+
+    /**
      * 鏍规嵁鐢ㄦ埛ID鏌ヨ鐢ㄦ埛鎵�灞炶鑹茬粍
      *
-     * @param userName 鐢ㄦ埛鍚�
+     * @param userId 鐢ㄦ埛ID
      * @return 缁撴灉
      */
-    String selectUserRoleGroup(String userName);
+    String selectUserRoleGroup(Long userId);
 
     /**
      * 鏍规嵁鐢ㄦ埛ID鏌ヨ鐢ㄦ埛鎵�灞炲矖浣嶇粍
      *
-     * @param userName 鐢ㄦ埛鍚�
+     * @param userId 鐢ㄦ埛ID
      * @return 缁撴灉
      */
-    String selectUserPostGroup(String userName);
+    String selectUserPostGroup(Long userId);
 
     /**
      * 鏍¢獙鐢ㄦ埛鍚嶇О鏄惁鍞竴
@@ -205,8 +215,8 @@
     /**
      * 閫氳繃閮ㄩ棬id鏌ヨ褰撳墠閮ㄩ棬鎵�鏈夌敤鎴�
      *
-     * @param deptId
-     * @return
+     * @param deptId 閮ㄩ棬id
+     * @return 缁撴灉
      */
     List<SysUserVo> selectUserListByDept(Long deptId);
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java
index aecaf66..26bc491 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java
@@ -7,6 +7,7 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.constant.CacheNames;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
@@ -16,6 +17,8 @@
 import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.mapper.SysClientMapper;
 import org.dromara.system.service.ISysClientService;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
 import java.util.Collection;
@@ -48,9 +51,10 @@
     /**
      * 鏌ヨ瀹㈡埛绔鐞�
      */
+    @Cacheable(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId")
     @Override
-    public SysClient queryByClientId(String clientId) {
-        return baseMapper.selectOne(new LambdaQueryWrapper<SysClient>().eq(SysClient::getClientId, clientId));
+    public SysClientVo queryByClientId(String clientId) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<SysClient>().eq(SysClient::getClientId, clientId));
     }
 
     /**
@@ -105,6 +109,7 @@
     /**
      * 淇敼瀹㈡埛绔鐞�
      */
+    @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#bo.clientId")
     @Override
     public Boolean updateByBo(SysClientBo bo) {
         SysClient update = MapstructUtils.convert(bo, SysClient.class);
@@ -116,12 +121,13 @@
     /**
      * 淇敼鐘舵��
      */
+    @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId")
     @Override
-    public int updateUserStatus(Long id, String status) {
+    public int updateUserStatus(String clientId, String status) {
         return baseMapper.update(null,
             new LambdaUpdateWrapper<SysClient>()
                 .set(SysClient::getStatus, status)
-                .eq(SysClient::getId, id));
+                .eq(SysClient::getClientId, clientId));
     }
 
     /**
@@ -134,6 +140,7 @@
     /**
      * 鎵归噺鍒犻櫎瀹㈡埛绔鐞�
      */
+    @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true)
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
         if (isValid) {
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java
index 7738d80..bebfdad 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java
@@ -154,6 +154,7 @@
             }
             row = baseMapper.updateById(config);
         } else {
+            CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey());
             row = baseMapper.update(config, new LambdaQueryWrapper<SysConfig>()
                 .eq(SysConfig::getConfigKey, config.getConfigKey()));
         }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java
index fe5cc0e..cb3f340 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java
@@ -7,6 +7,7 @@
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.constant.CacheNames;
 import org.dromara.common.core.constant.UserConstants;
 import org.dromara.common.core.exception.ServiceException;
@@ -27,7 +28,6 @@
 import org.dromara.system.mapper.SysRoleMapper;
 import org.dromara.system.mapper.SysUserMapper;
 import org.dromara.system.service.ISysDeptService;
-import lombok.RequiredArgsConstructor;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
@@ -82,7 +82,9 @@
         lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId());
         lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId());
         lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName());
+        lqw.like(StringUtils.isNotBlank(bo.getDeptCategory()), SysDept::getDeptCategory, bo.getDeptCategory());
         lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus());
+        lqw.orderByAsc(SysDept::getAncestors);
         lqw.orderByAsc(SysDept::getParentId);
         lqw.orderByAsc(SysDept::getOrderNum);
         lqw.orderByAsc(SysDept::getDeptId);
@@ -136,6 +138,14 @@
             .select(SysDept::getDeptName).eq(SysDept::getDeptId, dept.getParentId()));
         dept.setParentName(ObjectUtil.isNotNull(parentDept) ? parentDept.getDeptName() : null);
         return dept;
+    }
+
+    @Override
+    public List<SysDeptVo> selectDeptByIds(List<Long> deptIds) {
+        return baseMapper.selectDeptList(new LambdaQueryWrapper<SysDept>()
+            .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getLeader)
+            .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL)
+            .in(CollUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds));
     }
 
     /**
@@ -209,6 +219,20 @@
     }
 
     /**
+     * 鏍¢獙閮ㄩ棬绫诲埆缂栫爜鏄惁鍞竴
+     *
+     * @param dept 閮ㄩ棬淇℃伅
+     * @return 缁撴灉
+     */
+    @Override
+    public boolean checkDeptCategoryUnique(SysDeptBo dept) {
+        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysDept>()
+            .eq(SysDept::getDeptCategory, dept.getDeptCategory())
+            .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId()));
+        return !exist;
+    }
+
+    /**
      * 鏍¢獙閮ㄩ棬鏄惁鏈夋暟鎹潈闄�
      *
      * @param deptId 閮ㄩ棬id
@@ -221,8 +245,7 @@
         if (LoginHelper.isSuperAdmin()) {
             return;
         }
-        SysDeptVo dept = baseMapper.selectDeptById(deptId);
-        if (ObjectUtil.isNull(dept)) {
+        if (baseMapper.countDeptById(deptId) == 0) {
             throw new ServiceException("娌℃湁鏉冮檺璁块棶閮ㄩ棬鏁版嵁锛�");
         }
     }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java
index 74d4454..1ab0faf 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java
@@ -1,5 +1,6 @@
 package org.dromara.system.service.impl;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -135,4 +136,21 @@
         throw new ServiceException("鎿嶄綔澶辫触");
     }
 
+    /**
+     * 鏍¢獙瀛楀吀閿�兼槸鍚﹀敮涓�
+     *
+     * @param dict 瀛楀吀鏁版嵁
+     * @return 缁撴灉
+     */
+    @Override
+    public boolean checkDictDataUnique(SysDictDataBo dict) {
+        Long dictCode = ObjectUtil.isNull(dict.getDictCode()) ? -1L : dict.getDictCode();
+        SysDictData entity = baseMapper.selectOne(new LambdaQueryWrapper<SysDictData>()
+            .eq(SysDictData::getDictType, dict.getDictType()).eq(SysDictData::getDictValue, dict.getDictValue()));
+        if (ObjectUtil.isNotNull(entity) && !dictCode.equals(entity.getDictCode())) {
+            return false;
+        }
+        return true;
+    }
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java
index 346d6bc..bc6663f 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java
@@ -1,13 +1,12 @@
 package org.dromara.system.service.impl;
 
-import cn.dev33.satoken.context.SaHolder;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.dromara.common.core.constant.CacheConstants;
+import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.constant.CacheNames;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.service.DictService;
@@ -26,7 +25,6 @@
 import org.dromara.system.mapper.SysDictDataMapper;
 import org.dromara.system.mapper.SysDictTypeMapper;
 import org.dromara.system.service.ISysDictTypeService;
-import lombok.RequiredArgsConstructor;
 import org.springframework.cache.annotation.CachePut;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
@@ -217,16 +215,9 @@
      * @param separator 鍒嗛殧绗�
      * @return 瀛楀吀鏍囩
      */
-    @SuppressWarnings("unchecked cast")
     @Override
     public String getDictLabel(String dictType, String dictValue, String separator) {
-        // 浼樺厛浠庢湰鍦扮紦瀛樿幏鍙�
-        List<SysDictDataVo> datas = (List<SysDictDataVo>) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType);
-        if (ObjectUtil.isNull(datas)) {
-            datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
-            SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas);
-        }
-
+        List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
         Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictValue, SysDictDataVo::getDictLabel);
         if (StringUtils.containsAny(dictValue, separator)) {
             return Arrays.stream(dictValue.split(separator))
@@ -245,16 +236,9 @@
      * @param separator 鍒嗛殧绗�
      * @return 瀛楀吀鍊�
      */
-    @SuppressWarnings("unchecked cast")
     @Override
     public String getDictValue(String dictType, String dictLabel, String separator) {
-        // 浼樺厛浠庢湰鍦扮紦瀛樿幏鍙�
-        List<SysDictDataVo> datas = (List<SysDictDataVo>) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType);
-        if (ObjectUtil.isNull(datas)) {
-            datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
-            SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas);
-        }
-
+        List<SysDictDataVo> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
         Map<String, String> map = StreamUtils.toMap(datas, SysDictDataVo::getDictLabel, SysDictDataVo::getDictValue);
         if (StringUtils.containsAny(dictLabel, separator)) {
             return Arrays.stream(dictLabel.split(separator))
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java
index d215d59..9c930a0 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLogininforServiceImpl.java
@@ -5,30 +5,29 @@
 import cn.hutool.http.useragent.UserAgentUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.Constants;
 import org.dromara.common.core.utils.MapstructUtils;
-import org.dromara.common.mybatis.core.page.PageQuery;
-import org.dromara.common.log.event.LogininforEvent;
-import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.core.utils.ServletUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.core.utils.ip.AddressUtils;
+import org.dromara.common.log.event.LogininforEvent;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.satoken.utils.LoginHelper;
-import org.dromara.system.domain.SysClient;
 import org.dromara.system.domain.SysLogininfor;
 import org.dromara.system.domain.bo.SysLogininforBo;
+import org.dromara.system.domain.vo.SysClientVo;
 import org.dromara.system.domain.vo.SysLogininforVo;
-import org.dromara.system.mapper.SysClientMapper;
 import org.dromara.system.mapper.SysLogininforMapper;
 import org.dromara.system.service.ISysClientService;
 import org.dromara.system.service.ISysLogininforService;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
-import jakarta.servlet.http.HttpServletRequest;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
@@ -60,10 +59,10 @@
         final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
         final String ip = ServletUtils.getClientIP(request);
         // 瀹㈡埛绔俊鎭�
-        String clientid = request.getHeader(LoginHelper.CLIENT_KEY);
-        SysClient client = null;
-        if (StringUtils.isNotBlank(clientid)) {
-            client = clientService.queryByClientId(clientid);
+        String clientId = request.getHeader(LoginHelper.CLIENT_KEY);
+        SysClientVo client = null;
+        if (StringUtils.isNotBlank(clientId)) {
+            client = clientService.queryByClientId(clientId);
         }
 
         String address = AddressUtils.getRealAddressByIP(ip);
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java
index 8e2b5ac..18b7a08 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java
@@ -9,6 +9,7 @@
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.system.domain.SysNotice;
+import org.dromara.system.domain.SysUser;
 import org.dromara.system.domain.bo.SysNoticeBo;
 import org.dromara.system.domain.vo.SysNoticeVo;
 import org.dromara.system.domain.vo.SysUserVo;
@@ -68,7 +69,7 @@
         lqw.like(StringUtils.isNotBlank(bo.getNoticeTitle()), SysNotice::getNoticeTitle, bo.getNoticeTitle());
         lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), SysNotice::getNoticeType, bo.getNoticeType());
         if (StringUtils.isNotBlank(bo.getCreateByName())) {
-            SysUserVo sysUser = userMapper.selectUserByUserName(bo.getCreateByName());
+            SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, bo.getCreateByName()));
             lqw.eq(SysNotice::getCreateBy, ObjectUtil.isNotNull(sysUser) ? sysUser.getUserId() : null);
         }
         lqw.orderByAsc(SysNotice::getNoticeId);
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java
index c7d4719..aa094b2 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java
@@ -1,12 +1,15 @@
 package org.dromara.system.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.convert.Convert;
-import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.constant.CacheNames;
+import org.dromara.common.core.domain.dto.OssDTO;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.service.OssService;
 import org.dromara.common.core.utils.MapstructUtils;
@@ -25,8 +28,6 @@
 import org.dromara.system.domain.vo.SysOssVo;
 import org.dromara.system.mapper.SysOssMapper;
 import org.dromara.system.service.ISysOssService;
-import jakarta.servlet.http.HttpServletResponse;
-import lombok.RequiredArgsConstructor;
 import org.jetbrains.annotations.NotNull;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.http.MediaType;
@@ -35,8 +36,10 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 /**
  * 鏂囦欢涓婁紶 鏈嶅姟灞傚疄鐜�
@@ -49,6 +52,13 @@
 
     private final SysOssMapper baseMapper;
 
+    /**
+     * 鏌ヨOSS瀵硅薄瀛樺偍鍒楄〃
+     *
+     * @param bo        OSS瀵硅薄瀛樺偍鍒嗛〉鏌ヨ瀵硅薄
+     * @param pageQuery 鍒嗛〉鏌ヨ瀹炰綋绫�
+     * @return 缁撴灉
+     */
     @Override
     public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {
         LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);
@@ -58,6 +68,12 @@
         return TableDataInfo.build(result);
     }
 
+    /**
+     * 鏍规嵁涓�缁� ossIds 鑾峰彇瀵瑰簲鐨� SysOssVo 鍒楄〃
+     *
+     * @param ossIds 涓�缁勬枃浠跺湪鏁版嵁搴撲腑鐨勫敮涓�鏍囪瘑闆嗗悎
+     * @return 鍖呭惈 SysOssVo 瀵硅薄鐨勫垪琛�
+     */
     @Override
     public List<SysOssVo> listByIds(Collection<Long> ossIds) {
         List<SysOssVo> list = new ArrayList<>();
@@ -75,6 +91,12 @@
         return list;
     }
 
+    /**
+     * 鏍规嵁涓�缁� ossIds 鑾峰彇瀵瑰簲鏂囦欢鐨� URL 鍒楄〃
+     *
+     * @param ossIds 浠ラ�楀彿鍒嗛殧鐨� ossId 瀛楃涓�
+     * @return 浠ラ�楀彿鍒嗛殧鐨勬枃浠� URL 瀛楃涓�
+     */
     @Override
     public String selectUrlByIds(String ossIds) {
         List<String> list = new ArrayList<>();
@@ -92,6 +114,24 @@
         return String.join(StringUtils.SEPARATOR, list);
     }
 
+    @Override
+    public List<OssDTO> selectByIds(String ossIds) {
+        List<OssDTO> list = new ArrayList<>();
+        for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) {
+            SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
+            if (ObjectUtil.isNotNull(vo)) {
+                try {
+                    vo.setUrl(this.matchingUrl(vo).getUrl());
+                    list.add(BeanUtil.toBean(vo, OssDTO.class));
+                } catch (Exception ignored) {
+                    // 濡傛灉oss寮傚父鏃犳硶杩炴帴鍒欏皢鏁版嵁鐩存帴杩斿洖
+                    list.add(BeanUtil.toBean(vo, OssDTO.class));
+                }
+            }
+        }
+        return list;
+    }
+
     private LambdaQueryWrapper<SysOss> buildQueryWrapper(SysOssBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<SysOss> lqw = Wrappers.lambdaQuery();
@@ -107,12 +147,25 @@
         return lqw;
     }
 
+    /**
+     * 鏍规嵁 ossId 浠庣紦瀛樻垨鏁版嵁搴撲腑鑾峰彇 SysOssVo 瀵硅薄
+     *
+     * @param ossId 鏂囦欢鍦ㄦ暟鎹簱涓殑鍞竴鏍囪瘑
+     * @return SysOssVo 瀵硅薄锛屽寘鍚枃浠朵俊鎭�
+     */
     @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId")
     @Override
     public SysOssVo getById(Long ossId) {
         return baseMapper.selectVoById(ossId);
     }
 
+
+    /**
+     * 鏂囦欢涓嬭浇鏂规硶锛屾敮鎸佷竴娆℃�т笅杞藉畬鏁存枃浠�
+     *
+     * @param ossId    OSS瀵硅薄ID
+     * @param response HttpServletResponse瀵硅薄锛岀敤浜庤缃搷搴斿ご鍜屽悜瀹㈡埛绔彂閫佹枃浠跺唴瀹�
+     */
     @Override
     public void download(Long ossId, HttpServletResponse response) throws IOException {
         SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
@@ -122,15 +175,17 @@
         FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
         response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
         OssClient storage = OssFactory.instance(sysOss.getService());
-        try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
-            int available = inputStream.available();
-            IoUtil.copy(inputStream, response.getOutputStream(), available);
-            response.setContentLength(available);
-        } catch (Exception e) {
-            throw new ServiceException(e.getMessage());
-        }
+        long contentLength = storage.download(sysOss.getFileName(), response.getOutputStream());
+        response.setContentLengthLong(contentLength);
     }
 
+    /**
+     * 涓婁紶 MultipartFile 鍒板璞″瓨鍌ㄦ湇鍔★紝骞朵繚瀛樻枃浠朵俊鎭埌鏁版嵁搴�
+     *
+     * @param file 瑕佷笂浼犵殑 MultipartFile 瀵硅薄
+     * @return 涓婁紶鎴愬姛鍚庣殑 SysOssVo 瀵硅薄锛屽寘鍚枃浠朵俊鎭�
+     * @throws ServiceException 濡傛灉涓婁紶杩囩▼涓彂鐢熷紓甯革紝鍒欐姏鍑� ServiceException 寮傚父
+     */
     @Override
     public SysOssVo upload(MultipartFile file) {
         String originalfileName = file.getOriginalFilename();
@@ -138,7 +193,7 @@
         OssClient storage = OssFactory.instance();
         UploadResult uploadResult;
         try {
-            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
+            uploadResult = storage.uploadSuffix(file.getBytes(), suffix);
         } catch (IOException e) {
             throw new ServiceException(e.getMessage());
         }
@@ -146,6 +201,12 @@
         return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult);
     }
 
+    /**
+     * 涓婁紶鏂囦欢鍒板璞″瓨鍌ㄦ湇鍔★紝骞朵繚瀛樻枃浠朵俊鎭埌鏁版嵁搴�
+     *
+     * @param file 瑕佷笂浼犵殑鏂囦欢瀵硅薄
+     * @return 涓婁紶鎴愬姛鍚庣殑 SysOssVo 瀵硅薄锛屽寘鍚枃浠朵俊鎭�
+     */
     @Override
     public SysOssVo upload(File file) {
         String originalfileName = file.getName();
@@ -169,6 +230,13 @@
         return this.matchingUrl(sysOssVo);
     }
 
+    /**
+     * 鍒犻櫎OSS瀵硅薄瀛樺偍
+     *
+     * @param ids     OSS瀵硅薄ID涓�
+     * @param isValid 鍒ゆ柇鏄惁闇�瑕佹牎楠�
+     * @return 缁撴灉
+     */
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
         if (isValid) {
@@ -183,7 +251,7 @@
     }
 
     /**
-     * 鍖归厤Url
+     * 妗剁被鍨嬩负 private 鐨刄RL 淇敼涓轰复鏃禪RL鏃堕暱涓�120s
      *
      * @param oss OSS瀵硅薄
      * @return oss 鍖归厤Url鐨凮SS瀵硅薄
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java
index b69a06c..17cd5ca 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java
@@ -1,27 +1,33 @@
 package org.dromara.system.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.constant.UserConstants;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StreamUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.helper.DataBaseHelper;
+import org.dromara.system.domain.SysDept;
 import org.dromara.system.domain.SysPost;
 import org.dromara.system.domain.SysUserPost;
 import org.dromara.system.domain.bo.SysPostBo;
 import org.dromara.system.domain.vo.SysPostVo;
+import org.dromara.system.mapper.SysDeptMapper;
 import org.dromara.system.mapper.SysPostMapper;
 import org.dromara.system.mapper.SysUserPostMapper;
 import org.dromara.system.service.ISysPostService;
-import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 宀椾綅淇℃伅 鏈嶅姟灞傚鐞�
@@ -33,12 +39,12 @@
 public class SysPostServiceImpl implements ISysPostService {
 
     private final SysPostMapper baseMapper;
+    private final SysDeptMapper deptMapper;
     private final SysUserPostMapper userPostMapper;
 
     @Override
     public TableDataInfo<SysPostVo> selectPagePostList(SysPostBo post, PageQuery pageQuery) {
-        LambdaQueryWrapper<SysPost> lqw = buildQueryWrapper(post);
-        Page<SysPostVo> page = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        Page<SysPostVo> page = baseMapper.selectPagePostList(pageQuery.build(), buildQueryWrapper(post));
         return TableDataInfo.build(page);
     }
 
@@ -50,17 +56,39 @@
      */
     @Override
     public List<SysPostVo> selectPostList(SysPostBo post) {
-        LambdaQueryWrapper<SysPost> lqw = buildQueryWrapper(post);
-        return baseMapper.selectVoList(lqw);
+        return baseMapper.selectVoList(buildQueryWrapper(post));
     }
 
+    /**
+     * 鏍规嵁鏌ヨ鏉′欢鏋勫缓鏌ヨ鍖呰鍣�
+     *
+     * @param bo 鏌ヨ鏉′欢瀵硅薄
+     * @return 鏋勫缓濂界殑鏌ヨ鍖呰鍣�
+     */
     private LambdaQueryWrapper<SysPost> buildQueryWrapper(SysPostBo bo) {
-        LambdaQueryWrapper<SysPost> lqw = Wrappers.lambdaQuery();
-        lqw.like(StringUtils.isNotBlank(bo.getPostCode()), SysPost::getPostCode, bo.getPostCode());
-        lqw.like(StringUtils.isNotBlank(bo.getPostName()), SysPost::getPostName, bo.getPostName());
-        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysPost::getStatus, bo.getStatus());
-        lqw.orderByAsc(SysPost::getPostSort);
-        return lqw;
+        LambdaQueryWrapper<SysPost> wrapper = new LambdaQueryWrapper<>();
+        wrapper.like(StringUtils.isNotBlank(bo.getPostCode()), SysPost::getPostCode, bo.getPostCode())
+            .like(StringUtils.isNotBlank(bo.getPostCategory()), SysPost::getPostCategory, bo.getPostCategory())
+            .like(StringUtils.isNotBlank(bo.getPostName()), SysPost::getPostName, bo.getPostName())
+            .eq(StringUtils.isNotBlank(bo.getStatus()), SysPost::getStatus, bo.getStatus())
+            .orderByAsc(SysPost::getPostSort);
+        if (ObjectUtil.isNotNull(bo.getDeptId())) {
+            //浼樺厛鍗曢儴闂ㄦ悳绱�
+            wrapper.eq(SysPost::getDeptId, bo.getDeptId());
+        } else if (ObjectUtil.isNotNull(bo.getBelongDeptId())) {
+            //閮ㄩ棬鏍戞悳绱�
+            wrapper.and(x -> {
+                List<Long> deptIds = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
+                        .select(SysDept::getDeptId)
+                        .apply(DataBaseHelper.findInSet(bo.getBelongDeptId(), "ancestors")))
+                    .stream()
+                    .map(SysDept::getDeptId)
+                    .collect(Collectors.toList());
+                deptIds.add(bo.getBelongDeptId());
+                x.in(SysPost::getDeptId, deptIds);
+            });
+        }
+        return wrapper;
     }
 
     /**
@@ -92,7 +120,22 @@
      */
     @Override
     public List<Long> selectPostListByUserId(Long userId) {
-        return baseMapper.selectPostListByUserId(userId);
+        List<SysPostVo> list = baseMapper.selectPostsByUserId(userId);
+        return StreamUtils.toList(list, SysPostVo::getPostId);
+    }
+
+    /**
+     * 閫氳繃宀椾綅ID涓叉煡璇㈠矖浣�
+     *
+     * @param postIds 宀椾綅id涓�
+     * @return 宀椾綅鍒楄〃淇℃伅
+     */
+    @Override
+    public List<SysPostVo> selectPostByIds(List<Long> postIds) {
+        return baseMapper.selectVoList(new LambdaQueryWrapper<SysPost>()
+            .select(SysPost::getPostId, SysPost::getPostName, SysPost::getPostCode)
+            .eq(SysPost::getStatus, UserConstants.POST_NORMAL)
+            .in(CollUtil.isNotEmpty(postIds), SysPost::getPostId, postIds));
     }
 
     /**
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java
index 8a2b939..abd294f 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java
@@ -80,7 +80,6 @@
             .between(params.get("beginTime") != null && params.get("endTime") != null,
                 "r.create_time", params.get("beginTime"), params.get("endTime"))
             .orderByAsc("r.role_sort").orderByAsc("r.create_time");
-        ;
         return wrapper;
     }
 
@@ -92,6 +91,17 @@
      */
     @Override
     public List<SysRoleVo> selectRolesByUserId(Long userId) {
+        return baseMapper.selectRolesByUserId(userId);
+    }
+
+    /**
+     * 鏍规嵁鐢ㄦ埛ID鏌ヨ瑙掕壊鍒楄〃(鍖呭惈琚巿鏉冪姸鎬�)
+     *
+     * @param userId 鐢ㄦ埛ID
+     * @return 瑙掕壊鍒楄〃
+     */
+    @Override
+    public List<SysRoleVo> selectRolesAuthByUserId(Long userId) {
         List<SysRoleVo> userRoles = baseMapper.selectRolePermissionByUserId(userId);
         List<SysRoleVo> roles = selectRoleAll();
         for (SysRoleVo role : roles) {
@@ -141,7 +151,8 @@
      */
     @Override
     public List<Long> selectRoleListByUserId(Long userId) {
-        return baseMapper.selectRoleListByUserId(userId);
+        List<SysRoleVo> list = baseMapper.selectRolesByUserId(userId);
+        return StreamUtils.toList(list, SysRoleVo::getRoleId);
     }
 
     /**
@@ -156,6 +167,19 @@
     }
 
     /**
+     * 閫氳繃瑙掕壊ID涓叉煡璇㈣鑹�
+     *
+     * @param roleIds 瑙掕壊ID涓�
+     * @return 瑙掕壊鍒楄〃淇℃伅
+     */
+    @Override
+    public List<SysRoleVo> selectRoleByIds(List<Long> roleIds) {
+        return baseMapper.selectRoleList(new QueryWrapper<SysRole>()
+            .eq("r.status", UserConstants.ROLE_NORMAL)
+            .in(CollUtil.isNotEmpty(roleIds), "r.role_id", roleIds));
+    }
+
+    /**
      * 鏍¢獙瑙掕壊鍚嶇О鏄惁鍞竴
      *
      * @param role 瑙掕壊淇℃伅
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java
index aa86ae1..9c54cbc 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java
@@ -1,8 +1,10 @@
 package org.dromara.system.service.impl;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
 import org.dromara.system.domain.SysSocial;
 import org.dromara.system.domain.bo.SysSocialBo;
 import org.dromara.system.domain.vo.SysSocialVo;
@@ -37,8 +39,12 @@
      * 鎺堟潈鍒楄〃
      */
     @Override
-    public List<SysSocialVo> queryList() {
-        return baseMapper.selectVoList();
+    public List<SysSocialVo> queryList(SysSocialBo bo) {
+        LambdaQueryWrapper<SysSocial> lqw = new LambdaQueryWrapper<SysSocial>()
+            .eq(ObjectUtil.isNotNull(bo.getUserId()), SysSocial::getUserId, bo.getUserId())
+            .eq(StringUtils.isNotBlank(bo.getAuthId()), SysSocial::getAuthId, bo.getAuthId())
+            .eq(StringUtils.isNotBlank(bo.getSource()), SysSocial::getSource, bo.getSource());
+        return baseMapper.selectVoList(lqw);
     }
 
     @Override
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
index 7ee19e8..93f1cc7 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java
@@ -1,6 +1,8 @@
 package org.dromara.system.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
@@ -13,9 +15,11 @@
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.core.constant.CacheNames;
 import org.dromara.common.core.constant.UserConstants;
+import org.dromara.common.core.domain.dto.UserDTO;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.service.UserService;
 import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StreamUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
@@ -29,6 +33,7 @@
 import org.dromara.system.domain.bo.SysUserBo;
 import org.dromara.system.domain.vo.SysPostVo;
 import org.dromara.system.domain.vo.SysRoleVo;
+import org.dromara.system.domain.vo.SysUserExportVo;
 import org.dromara.system.domain.vo.SysUserVo;
 import org.dromara.system.mapper.*;
 import org.dromara.system.service.ISysUserService;
@@ -36,6 +41,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -69,28 +75,31 @@
      * @return 鐢ㄦ埛淇℃伅闆嗗悎淇℃伅
      */
     @Override
-    public List<SysUserVo> selectUserList(SysUserBo user) {
-        return baseMapper.selectUserList(this.buildQueryWrapper(user));
+    public List<SysUserExportVo> selectUserExportList(SysUserBo user) {
+        return baseMapper.selectUserExportList(this.buildQueryWrapper(user));
     }
 
     private Wrapper<SysUser> buildQueryWrapper(SysUserBo user) {
         Map<String, Object> params = user.getParams();
         QueryWrapper<SysUser> wrapper = Wrappers.query();
         wrapper.eq("u.del_flag", UserConstants.USER_NORMAL)
-                .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId())
-                .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
-                .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus())
-                .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber())
-                .between(params.get("beginTime") != null && params.get("endTime") != null,
-                        "u.create_time", params.get("beginTime"), params.get("endTime"))
-                .and(ObjectUtil.isNotNull(user.getDeptId()), w -> {
-                    List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
-                            .select(SysDept::getDeptId)
-                            .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors")));
-                    List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
-                    ids.add(user.getDeptId());
-                    w.in("u.dept_id", ids);
-                }).orderByAsc("u.user_id");
+            .eq(ObjectUtil.isNotNull(user.getUserId()), "u.user_id", user.getUserId())
+            .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
+            .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus())
+            .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber())
+            .between(params.get("beginTime") != null && params.get("endTime") != null,
+                "u.create_time", params.get("beginTime"), params.get("endTime"))
+            .and(ObjectUtil.isNotNull(user.getDeptId()), w -> {
+                List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
+                    .select(SysDept::getDeptId)
+                    .apply(DataBaseHelper.findInSet(user.getDeptId(), "ancestors")));
+                List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
+                ids.add(user.getDeptId());
+                w.in("u.dept_id", ids);
+            }).orderByAsc("u.user_id");
+        if (StringUtils.isNotBlank(user.getExcludeUserIds())) {
+            wrapper.notIn("u.user_id", StringUtils.splitList(user.getExcludeUserIds()));
+        }
         return wrapper;
     }
 
@@ -104,11 +113,11 @@
     public TableDataInfo<SysUserVo> selectAllocatedList(SysUserBo user, PageQuery pageQuery) {
         QueryWrapper<SysUser> wrapper = Wrappers.query();
         wrapper.eq("u.del_flag", UserConstants.USER_NORMAL)
-                .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId())
-                .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
-                .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus())
-                .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber())
-                .orderByAsc("u.user_id");
+            .eq(ObjectUtil.isNotNull(user.getRoleId()), "r.role_id", user.getRoleId())
+            .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
+            .eq(StringUtils.isNotBlank(user.getStatus()), "u.status", user.getStatus())
+            .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber())
+            .orderByAsc("u.user_id");
         Page<SysUserVo> page = baseMapper.selectAllocatedList(pageQuery.build(), wrapper);
         return TableDataInfo.build(page);
     }
@@ -124,11 +133,11 @@
         List<Long> userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId());
         QueryWrapper<SysUser> wrapper = Wrappers.query();
         wrapper.eq("u.del_flag", UserConstants.USER_NORMAL)
-                .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id"))
-                .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds)
-                .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
-                .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber())
-                .orderByAsc("u.user_id");
+            .and(w -> w.ne("r.role_id", user.getRoleId()).or().isNull("r.role_id"))
+            .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds)
+            .like(StringUtils.isNotBlank(user.getUserName()), "u.user_name", user.getUserName())
+            .like(StringUtils.isNotBlank(user.getPhonenumber()), "u.phonenumber", user.getPhonenumber())
+            .orderByAsc("u.user_id");
         Page<SysUserVo> page = baseMapper.selectUnallocatedList(pageQuery.build(), wrapper);
         return TableDataInfo.build(page);
     }
@@ -141,7 +150,7 @@
      */
     @Override
     public SysUserVo selectUserByUserName(String userName) {
-        return baseMapper.selectUserByUserName(userName);
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, userName));
     }
 
     /**
@@ -152,7 +161,7 @@
      */
     @Override
     public SysUserVo selectUserByPhonenumber(String phonenumber) {
-        return baseMapper.selectUserByPhonenumber(phonenumber);
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
     }
 
     /**
@@ -163,18 +172,39 @@
      */
     @Override
     public SysUserVo selectUserById(Long userId) {
-        return baseMapper.selectUserById(userId);
+        SysUserVo user = baseMapper.selectVoById(userId);
+        if (ObjectUtil.isNull(user)) {
+            return user;
+        }
+        user.setRoles(roleMapper.selectRolesByUserId(user.getUserId()));
+        return user;
+    }
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID涓叉煡璇㈢敤鎴�
+     *
+     * @param userIds 鐢ㄦ埛ID涓�
+     * @param deptId  閮ㄩ棬id
+     * @return 鐢ㄦ埛鍒楄〃淇℃伅
+     */
+    @Override
+    public List<SysUserVo> selectUserByIds(List<Long> userIds, Long deptId) {
+        return baseMapper.selectUserList(new LambdaQueryWrapper<SysUser>()
+            .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName)
+            .eq(SysUser::getStatus, UserConstants.USER_NORMAL)
+            .eq(ObjectUtil.isNotNull(deptId), SysUser::getDeptId, deptId)
+            .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds));
     }
 
     /**
      * 鏌ヨ鐢ㄦ埛鎵�灞炶鑹茬粍
      *
-     * @param userName 鐢ㄦ埛鍚�
+     * @param userId 鐢ㄦ埛ID
      * @return 缁撴灉
      */
     @Override
-    public String selectUserRoleGroup(String userName) {
-        List<SysRoleVo> list = roleMapper.selectRolesByUserName(userName);
+    public String selectUserRoleGroup(Long userId) {
+        List<SysRoleVo> list = roleMapper.selectRolesByUserId(userId);
         if (CollUtil.isEmpty(list)) {
             return StringUtils.EMPTY;
         }
@@ -184,12 +214,12 @@
     /**
      * 鏌ヨ鐢ㄦ埛鎵�灞炲矖浣嶇粍
      *
-     * @param userName 鐢ㄦ埛鍚�
+     * @param userId 鐢ㄦ埛ID
      * @return 缁撴灉
      */
     @Override
-    public String selectUserPostGroup(String userName) {
-        List<SysPostVo> list = postMapper.selectPostsByUserName(userName);
+    public String selectUserPostGroup(Long userId) {
+        List<SysPostVo> list = postMapper.selectPostsByUserId(userId);
         if (CollUtil.isEmpty(list)) {
             return StringUtils.EMPTY;
         }
@@ -205,8 +235,8 @@
     @Override
     public boolean checkUserNameUnique(SysUserBo user) {
         boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()
-                .eq(SysUser::getUserName, user.getUserName())
-                .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
+            .eq(SysUser::getUserName, user.getUserName())
+            .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
         return !exist;
     }
 
@@ -218,8 +248,8 @@
     @Override
     public boolean checkPhoneUnique(SysUserBo user) {
         boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()
-                .eq(SysUser::getPhonenumber, user.getPhonenumber())
-                .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
+            .eq(SysUser::getPhonenumber, user.getPhonenumber())
+            .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
         return !exist;
     }
 
@@ -231,8 +261,8 @@
     @Override
     public boolean checkEmailUnique(SysUserBo user) {
         boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysUser>()
-                .eq(SysUser::getEmail, user.getEmail())
-                .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
+            .eq(SysUser::getEmail, user.getEmail())
+            .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()));
         return !exist;
     }
 
@@ -261,7 +291,7 @@
         if (LoginHelper.isSuperAdmin()) {
             return;
         }
-        if (ObjectUtil.isNull(baseMapper.selectUserById(userId))) {
+        if (baseMapper.countUserById(userId) == 0) {
             throw new ServiceException("娌℃湁鏉冮檺璁块棶鐢ㄦ埛鏁版嵁锛�");
         }
     }
@@ -294,8 +324,8 @@
      */
     @Override
     public boolean registerUser(SysUserBo user, String tenantId) {
-        user.setCreateBy(user.getUserId());
-        user.setUpdateBy(user.getUserId());
+        user.setCreateBy(0L);
+        user.setUpdateBy(0L);
         SysUser sysUser = MapstructUtils.convert(user, SysUser.class);
         sysUser.setTenantId(tenantId);
         return baseMapper.insert(sysUser) > 0;
@@ -345,9 +375,9 @@
     @Override
     public int updateUserStatus(Long userId, String status) {
         return baseMapper.update(null,
-                new LambdaUpdateWrapper<SysUser>()
-                        .set(SysUser::getStatus, status)
-                        .eq(SysUser::getUserId, userId));
+            new LambdaUpdateWrapper<SysUser>()
+                .set(SysUser::getStatus, status)
+                .eq(SysUser::getUserId, userId));
     }
 
     /**
@@ -359,12 +389,12 @@
     @Override
     public int updateUserProfile(SysUserBo user) {
         return baseMapper.update(null,
-                new LambdaUpdateWrapper<SysUser>()
-                        .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName())
-                        .set(SysUser::getPhonenumber, user.getPhonenumber())
-                        .set(SysUser::getEmail, user.getEmail())
-                        .set(SysUser::getSex, user.getSex())
-                        .eq(SysUser::getUserId, user.getUserId()));
+            new LambdaUpdateWrapper<SysUser>()
+                .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName())
+                .set(SysUser::getPhonenumber, user.getPhonenumber())
+                .set(SysUser::getEmail, user.getEmail())
+                .set(SysUser::getSex, user.getSex())
+                .eq(SysUser::getUserId, user.getUserId()));
     }
 
     /**
@@ -377,9 +407,9 @@
     @Override
     public boolean updateUserAvatar(Long userId, Long avatar) {
         return baseMapper.update(null,
-                new LambdaUpdateWrapper<SysUser>()
-                        .set(SysUser::getAvatar, avatar)
-                        .eq(SysUser::getUserId, userId)) > 0;
+            new LambdaUpdateWrapper<SysUser>()
+                .set(SysUser::getAvatar, avatar)
+                .eq(SysUser::getUserId, userId)) > 0;
     }
 
     /**
@@ -392,9 +422,9 @@
     @Override
     public int resetUserPwd(Long userId, String password) {
         return baseMapper.update(null,
-                new LambdaUpdateWrapper<SysUser>()
-                        .set(SysUser::getPassword, password)
-                        .eq(SysUser::getUserId, userId));
+            new LambdaUpdateWrapper<SysUser>()
+                .set(SysUser::getPassword, password)
+                .eq(SysUser::getUserId, userId));
     }
 
     /**
@@ -518,8 +548,8 @@
     /**
      * 閫氳繃閮ㄩ棬id鏌ヨ褰撳墠閮ㄩ棬鎵�鏈夌敤鎴�
      *
-     * @param deptId
-     * @return
+     * @param deptId 閮ㄩ棬ID
+     * @return 鐢ㄦ埛淇℃伅闆嗗悎淇℃伅
      */
     @Override
     public List<SysUserVo> selectUserListByDept(Long deptId) {
@@ -529,14 +559,26 @@
         return baseMapper.selectVoList(lqw);
     }
 
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛璐︽埛
+     *
+     * @param userId 鐢ㄦ埛ID
+     * @return 鐢ㄦ埛璐︽埛
+     */
     @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId")
     @Override
     public String selectUserNameById(Long userId) {
         SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper<SysUser>()
-                .select(SysUser::getUserName).eq(SysUser::getUserId, userId));
+            .select(SysUser::getUserName).eq(SysUser::getUserId, userId));
         return ObjectUtil.isNull(sysUser) ? null : sysUser.getUserName();
     }
 
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛璐︽埛
+     *
+     * @param userId 鐢ㄦ埛ID
+     * @return 鐢ㄦ埛璐︽埛
+     */
     @Override
     @Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId")
     public String selectNicknameById(Long userId) {
@@ -544,4 +586,65 @@
             .select(SysUser::getNickName).eq(SysUser::getUserId, userId));
         return ObjectUtil.isNull(sysUser) ? null : sysUser.getNickName();
     }
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛璐︽埛
+     *
+     * @param userIds 鐢ㄦ埛ID 澶氫釜鐢ㄩ�楀彿闅斿紑
+     * @return 鐢ㄦ埛璐︽埛
+     */
+    @Override
+    public String selectNicknameByIds(String userIds) {
+        List<String> list = new ArrayList<>();
+        for (Long id : StringUtils.splitTo(userIds, Convert::toLong)) {
+            String nickname = SpringUtils.getAopProxy(this).selectNicknameById(id);
+            if (StringUtils.isNotBlank(nickname)) {
+                list.add(nickname);
+            }
+        }
+        return String.join(StringUtils.SEPARATOR, list);
+    }
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛鎵嬫満鍙�
+     *
+     * @param userId 鐢ㄦ埛id
+     * @return 鐢ㄦ埛鎵嬫満鍙�
+     */
+    @Override
+    public String selectPhonenumberById(Long userId) {
+        SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper<SysUser>()
+            .select(SysUser::getPhonenumber).eq(SysUser::getUserId, userId));
+        return ObjectUtil.isNull(sysUser) ? null : sysUser.getPhonenumber();
+    }
+
+    /**
+     * 閫氳繃鐢ㄦ埛ID鏌ヨ鐢ㄦ埛閭
+     *
+     * @param userId 鐢ㄦ埛id
+     * @return 鐢ㄦ埛閭
+     */
+    @Override
+    public String selectEmailById(Long userId) {
+        SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper<SysUser>()
+            .select(SysUser::getEmail).eq(SysUser::getUserId, userId));
+        return ObjectUtil.isNull(sysUser) ? null : sysUser.getEmail();
+    }
+
+    @Override
+    public List<UserDTO> selectListByIds(List<Long> userIds) {
+        if (CollUtil.isEmpty(userIds)) {
+            return List.of();
+        }
+        List<SysUserVo> list = this.selectUserByIds(userIds, null);
+        return BeanUtil.copyToList(list, UserDTO.class);
+    }
+
+    @Override
+    public List<Long> selectUserIdsByRoleIds(List<Long> roleIds) {
+        List<SysUserRole> userRoles = userRoleMapper.selectList(
+            new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getRoleId, roleIds));
+        return StreamUtils.toList(userRoles, SysUserRole::getUserId);
+    }
+
 }
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
index bba949d..6ad866f 100644
--- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
@@ -8,11 +8,18 @@
     </resultMap>
 
     <select id="selectDeptList" resultMap="SysDeptResult">
-        select * from sys_dept ${ew.getCustomSqlSegment}
+        select
+        <if test="ew.getSqlSelect != null">
+            ${ew.getSqlSelect}
+        </if>
+        <if test="ew.getSqlSelect == null">
+            *
+        </if>
+        from sys_dept ${ew.getCustomSqlSegment}
     </select>
 
-    <select id="selectDeptById" resultMap="SysDeptResult">
-        select * from sys_dept where del_flag = '0' and dept_id = #{deptId}
+    <select id="countDeptById" resultType="Long">
+        select count(*) from sys_dept where del_flag = '0' and dept_id = #{deptId}
     </select>
 
     <select id="selectDeptListByRoleId" resultType="Long">
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml
index d2108eb..322403f 100644
--- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml
@@ -7,20 +7,23 @@
     <resultMap type="org.dromara.system.domain.vo.SysPostVo" id="SysPostResult">
     </resultMap>
 
-    <select id="selectPostListByUserId" parameterType="Long" resultType="Long">
-        select p.post_id
+    <select id="selectPagePostList" resultMap="SysPostResult">
+        select
+        <if test="ew.getSqlSelect != null">
+            ${ew.getSqlSelect}
+        </if>
+        <if test="ew.getSqlSelect == null">
+            *
+        </if>
+        from sys_post ${ew.getCustomSqlSegment}
+    </select>
+
+    <select id="selectPostsByUserId" parameterType="Long" resultMap="SysPostResult">
+        select p.post_id, p.dept_id, p.post_name, p.post_code, p.post_category
         from sys_post p
                  left join sys_user_post up on up.post_id = p.post_id
                  left join sys_user u on u.user_id = up.user_id
         where u.user_id = #{userId}
-    </select>
-
-    <select id="selectPostsByUserName" parameterType="String" resultMap="SysPostResult">
-        select p.post_id, p.post_name, p.post_code
-        from sys_post p
-                 left join sys_user_post up on up.post_id = p.post_id
-                 left join sys_user u on u.user_id = up.user_id
-        where u.user_name = #{userName}
     </select>
 
 </mapper>
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
index b191698..7b8bba7 100644
--- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
@@ -40,23 +40,17 @@
         WHERE r.del_flag = '0' and sur.user_id = #{userId}
     </select>
 
-    <select id="selectRoleListByUserId" parameterType="Long" resultType="Long">
-        select r.role_id
-        from sys_role r
-                 left join sys_user_role sur on sur.role_id = r.role_id
-                 left join sys_user u on u.user_id = sur.user_id
-        where u.user_id = #{userId}
-    </select>
-
-    <select id="selectRolesByUserName" parameterType="String" resultMap="SysRoleResult">
+    <select id="selectRolesByUserId" parameterType="Long" resultMap="SysRoleResult">
         select r.role_id,
                r.role_name,
                r.role_key,
-               r.role_sort
+               r.role_sort,
+               r.data_scope,
+               r.status
         from sys_role r
                  left join sys_user_role sur on sur.role_id = r.role_id
                  left join sys_user u on u.user_id = sur.user_id
-        WHERE r.del_flag = '0' and u.user_name = #{userName}
+        WHERE r.del_flag = '0' and sur.user_id = #{userId}
     </select>
 
     <select id="selectRoleById" resultMap="SysRoleResult">
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
index 7af7dd0..ded6fa8 100644
--- a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -4,78 +4,40 @@
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="org.dromara.system.mapper.SysUserMapper">
 
-    <!-- 澶氱粨鏋勫祵濂楄嚜鍔ㄦ槧灏勯渶甯︿笂姣忎釜瀹炰綋鐨勪富閿甶d 鍚﹀垯鏄犲皠浼氬け璐� -->
     <resultMap type="org.dromara.system.domain.vo.SysUserVo" id="SysUserResult">
         <id property="userId" column="user_id"/>
-        <result property="deptId" column="dept_id"/>
-        <association property="dept" column="dept_id" resultMap="deptResult"/>
-        <collection property="roles" javaType="java.util.List" resultMap="RoleResult"/>
     </resultMap>
-
-    <resultMap id="deptResult" type="org.dromara.system.domain.vo.SysDeptVo">
-        <id property="deptId" column="dept_id"/>
-        <result property="email" column="dept_email"/>
-        <result property="status" column="dept_status"/>
-        <result property="createTime" column="dept_create_time"/>
+    <resultMap type="org.dromara.system.domain.vo.SysUserExportVo" id="SysUserExportResult">
+        <id property="userId" column="user_id"/>
     </resultMap>
-
-    <resultMap id="RoleResult" type="org.dromara.system.domain.vo.SysRoleVo">
-        <id property="roleId" column="role_id"/>
-        <result property="status" column="role_status"/>
-        <result property="createTime" column="role_create_time"/>
-    </resultMap>
-
-    <sql id="selectUserVo">
-        select u.user_id,
-               u.tenant_id,
-               u.dept_id,
-               u.user_name,
-               u.nick_name,
-               u.user_type,
-               u.email,
-               u.avatar,
-               u.phonenumber,
-               u.password,
-               u.sex,
-               u.status,
-               u.del_flag,
-               u.login_ip,
-               u.login_date,
-               u.create_by,
-               u.create_time,
-               u.remark,
-               d.dept_id,
-               d.parent_id,
-               d.ancestors,
-               d.dept_name,
-               d.order_num,
-               d.leader,
-               d.status as dept_status,
-               d.email as dept_email,
-               d.create_time as dept_cteate_time,
-               r.role_id,
-               r.role_name,
-               r.role_key,
-               r.role_sort,
-               r.data_scope,
-               r.status as role_status
-        from sys_user u
-            left join sys_dept d on u.dept_id = d.dept_id
-            left join sys_user_role sur on u.user_id = sur.user_id
-            left join sys_role r on r.role_id = sur.role_id
-    </sql>
 
     <select id="selectPageUserList" resultMap="SysUserResult">
-        select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
-            u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
-            d.dept_name, d.leader, u1.user_name as leaderName
+        select
+        <if test="ew.getSqlSelect != null">
+            ${ew.getSqlSelect}
+        </if>
+        <if test="ew.getSqlSelect == null">
+            u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
+            u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark
+        </if>
         from sys_user u
-            left join sys_dept d on u.dept_id = d.dept_id
-            left join sys_user u1 on u1.user_id = d.leader
         ${ew.getCustomSqlSegment}
     </select>
 
     <select id="selectUserList" resultMap="SysUserResult">
+        select
+        <if test="ew.getSqlSelect != null">
+            ${ew.getSqlSelect}
+        </if>
+        <if test="ew.getSqlSelect == null">
+            u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
+            u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark
+        </if>
+        from sys_user u
+        ${ew.getCustomSqlSegment}
+    </select>
+
+    <select id="selectUserExportList" resultMap="SysUserExportResult">
         select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
             u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
             d.dept_name, d.leader, u1.user_name as leaderName
@@ -103,24 +65,8 @@
         ${ew.getCustomSqlSegment}
     </select>
 
-    <select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult">
-        <include refid="selectUserVo"/>
-        where u.del_flag = '0' and u.user_name = #{userName}
-    </select>
-
-    <select id="selectUserByPhonenumber" parameterType="String" resultMap="SysUserResult">
-        <include refid="selectUserVo"/>
-        where u.del_flag = '0' and u.phonenumber = #{phonenumber}
-    </select>
-
-    <select id="selectUserByEmail" parameterType="String" resultMap="SysUserResult">
-        <include refid="selectUserVo"/>
-        where u.del_flag = '0' and u.email = #{email}
-    </select>
-
-    <select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
-        <include refid="selectUserVo"/>
-        where u.del_flag = '0' and u.user_id = #{userId}
+    <select id="countUserById" resultType="Long">
+        select count(*) from sys_user where del_flag = '0' and user_id = #{userId}
     </select>
 
 
diff --git a/ruoyi-modules/ruoyi-workflow/pom.xml b/ruoyi-modules/ruoyi-workflow/pom.xml
new file mode 100644
index 0000000..0217098
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/pom.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.dromara</groupId>
+        <artifactId>ruoyi-modules</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>ruoyi-workflow</artifactId>
+
+    <description>
+        宸ヤ綔娴佹ā鍧�
+    </description>
+
+    <dependencies>
+
+        <!--寮曞叆flowable渚濊禆-->
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-spring-boot-autoconfigure</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.flowable</groupId>
+                    <artifactId>flowable-spring-security</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-spring-configurator</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-spring-boot-starter-actuator</artifactId>
+        </dependency>
+
+        <!-- 缁樺埗flowable娴佺▼鍥� -->
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-image-generator</artifactId>
+        </dependency>
+
+        <!-- flowable json 杞崲 -->
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-json-converter</artifactId>
+            <version>6.8.0</version>
+        </dependency>
+
+        <!-- svg杞琾ng鍥剧墖宸ュ叿-->
+        <dependency>
+            <groupId>org.apache.xmlgraphics</groupId>
+            <artifactId>batik-all</artifactId>
+            <version>1.10</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>xalan</groupId>
+                    <artifactId>xalan</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-websocket</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-mail</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-sms</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-mybatis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-log</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-idempotent</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-excel</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-translation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-tenant</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>
+
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java
new file mode 100644
index 0000000..5ea262d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/annotation/FlowListenerAnnotation.java
@@ -0,0 +1,27 @@
+package org.dromara.workflow.annotation;
+
+
+import java.lang.annotation.*;
+
+/**
+ * 娴佺▼浠诲姟鐩戝惉娉ㄨВ
+ *
+ * @author may
+ * @date 2023-12-27
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface FlowListenerAnnotation {
+
+    /**
+     * 娴佺▼瀹氫箟key
+     */
+    String processDefinitionKey();
+
+    /**
+     * 鑺傜偣id
+     */
+    String taskDefId() default "";
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java
new file mode 100644
index 0000000..df27d3c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java
@@ -0,0 +1,137 @@
+package org.dromara.workflow.common.constant;
+
+
+/**
+ * 宸ヤ綔娴佸父閲�
+ *
+ * @author may
+ */
+public interface FlowConstant {
+
+    String MESSAGE_CURRENT_TASK_IS_NULL = "褰撳墠浠诲姟涓嶅瓨鍦ㄦ垨浣犱笉鏄换鍔″姙鐞嗕汉锛�";
+
+    String MESSAGE_SUSPENDED = "褰撳墠浠诲姟宸叉寕璧蜂笉鍙鎵癸紒";
+
+    /**
+     * 杩炵嚎
+     */
+    String SEQUENCE_FLOW = "sequenceFlow";
+
+    /**
+     * 骞惰缃戝叧
+     */
+    String PARALLEL_GATEWAY = "parallelGateway";
+
+    /**
+     * 鎺掑畠缃戝叧
+     */
+    String EXCLUSIVE_GATEWAY = "exclusiveGateway";
+
+    /**
+     * 鍖呭惈缃戝叧
+     */
+    String INCLUSIVE_GATEWAY = "inclusiveGateway";
+
+    /**
+     * 缁撴潫鑺傜偣
+     */
+    String END_EVENT = "endEvent";
+
+
+    /**
+     * 娴佺▼濮旀淳鏍囪瘑
+     */
+    String PENDING = "PENDING";
+
+    /**
+     * 鍊欓�変汉鏍囪瘑
+     */
+    String CANDIDATE = "candidate";
+
+    /**
+     * 浼氱浠诲姟鎬绘暟
+     */
+    String NUMBER_OF_INSTANCES = "nrOfInstances";
+
+    /**
+     * 姝e湪鎵ц鐨勪細绛炬�绘暟
+     */
+    String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances";
+
+    /**
+     * 宸插畬鎴愮殑浼氱浠诲姟鎬绘暟
+     */
+    String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances";
+
+    /**
+     * 寰幆鐨勭储寮曞�硷紝鍙互浣跨敤elementIndexVariable灞炴�т慨鏀筶oopCounter鐨勫彉閲忓悕
+     */
+    String LOOP_COUNTER = "loopCounter";
+
+    String ZIP = "ZIP";
+
+    /**
+     * 娴佺▼瀹炰緥瀵硅薄
+     */
+    String PROCESS_INSTANCE_VO = "processInstanceVo";
+
+    /**
+     * 娴佺▼瀹氫箟閰嶇疆
+     */
+    String WF_DEFINITION_CONFIG_VO = "wfDefinitionConfigVo";
+
+    /**
+     * 鑺傜偣閰嶇疆
+     */
+    String WF_NODE_CONFIG_VO = "wfNodeConfigVo";
+
+    /**
+     * 娴佺▼鍙戣捣浜�
+     */
+    String INITIATOR = "initiator";
+
+    /**
+     * 娴佺▼瀹炰緥id
+     */
+    String PROCESS_INSTANCE_ID = "processInstanceId";
+
+    /**
+     * 涓氬姟id
+     */
+    String BUSINESS_KEY = "businessKey";
+
+    /**
+     * 娴佺▼瀹氫箟id
+     */
+    String PROCESS_DEFINITION_ID = "processDefinitionId";
+
+    /**
+     * 寮�鍚烦杩囪〃杈惧紡鍙橀噺
+     */
+    String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED";
+
+    /**
+     * 妯″瀷鏍囪瘑key鍛藉悕瑙勮寖姝e垯琛ㄨ揪寮�
+     */
+    String MODEL_KEY_PATTERN = "^[a-zA-Z][a-zA-Z0-9_]{0,254}$";
+
+    /**
+     * 鐢ㄦ埛浠诲姟
+     */
+    String USER_TASK = "userTask";
+
+    /**
+     * 浼氱
+     */
+    String MULTI_INSTANCE = "multiInstance";
+
+    /**
+     * 鏄�
+     */
+    String TRUE = "0";
+
+    /**
+     * 鍚�
+     */
+    String FALSE = "1";
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java
new file mode 100644
index 0000000..6eb6ffe
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java
@@ -0,0 +1,152 @@
+package org.dromara.workflow.common.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.StringUtils;
+
+import java.util.Arrays;
+
+/**
+ * 涓氬姟鐘舵�佹灇涓�
+ *
+ * @author may
+ */
+@Getter
+@AllArgsConstructor
+public enum BusinessStatusEnum {
+    /**
+     * 宸叉挙閿�
+     */
+    CANCEL("cancel", "宸叉挙閿�"),
+    /**
+     * 鑽夌
+     */
+    DRAFT("draft", "鑽夌"),
+    /**
+     * 寰呭鏍�
+     */
+    WAITING("waiting", "寰呭鏍�"),
+    /**
+     * 宸插畬鎴�
+     */
+    FINISH("finish", "宸插畬鎴�"),
+    /**
+     * 宸蹭綔搴�
+     */
+    INVALID("invalid", "宸蹭綔搴�"),
+    /**
+     * 宸查��鍥�
+     */
+    BACK("back", "宸查��鍥�"),
+    /**
+     * 宸茬粓姝�
+     */
+    TERMINATION("termination", "宸茬粓姝�");
+
+    /**
+     * 鐘舵��
+     */
+    private final String status;
+
+    /**
+     * 鎻忚堪
+     */
+    private final String desc;
+
+    /**
+     * 鑾峰彇涓氬姟鐘舵��
+     *
+     * @param status 鐘舵��
+     */
+    public static String findByStatus(String status) {
+        if (StringUtils.isBlank(status)) {
+            return StrUtil.EMPTY;
+        }
+        return Arrays.stream(BusinessStatusEnum.values())
+            .filter(statusEnum -> statusEnum.getStatus().equals(status))
+            .findFirst()
+            .map(BusinessStatusEnum::getDesc)
+            .orElse(StrUtil.EMPTY);
+    }
+
+    /**
+     * 鍚姩娴佺▼鏍¢獙
+     *
+     * @param status 鐘舵��
+     */
+    public static void checkStartStatus(String status) {
+        if (WAITING.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡鎻愪氦杩囩敵璇�,姝e湪瀹℃壒涓紒");
+        } else if (FINISH.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡瀹屾垚鐢宠锛�");
+        } else if (INVALID.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡浣滃簾锛�");
+        } else if (TERMINATION.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡缁堟锛�");
+        } else if (StringUtils.isBlank(status)) {
+            throw new ServiceException("娴佺▼鐘舵�佷负绌猴紒");
+        }
+    }
+
+    /**
+     * 鎾ら攢娴佺▼鏍¢獙
+     *
+     * @param status 鐘舵��
+     */
+    public static void checkCancelStatus(String status) {
+        if (CANCEL.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡鎾ら攢锛�");
+        } else if (FINISH.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡瀹屾垚鐢宠锛�");
+        } else if (INVALID.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡浣滃簾锛�");
+        } else if (TERMINATION.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡缁堟锛�");
+        } else if (BACK.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡閫�鍥烇紒");
+        } else if (StringUtils.isBlank(status)) {
+            throw new ServiceException("娴佺▼鐘舵�佷负绌猴紒");
+        }
+    }
+
+    /**
+     * 椹冲洖娴佺▼鏍¢獙
+     *
+     * @param status 鐘舵��
+     */
+    public static void checkBackStatus(String status) {
+        if (BACK.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡閫�鍥烇紒");
+        } else if (FINISH.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡瀹屾垚鐢宠锛�");
+        } else if (INVALID.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡浣滃簾锛�");
+        } else if (TERMINATION.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡缁堟锛�");
+        } else if (CANCEL.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡鎾ら攢锛�");
+        } else if (StringUtils.isBlank(status)) {
+            throw new ServiceException("娴佺▼鐘舵�佷负绌猴紒");
+        }
+    }
+
+    /**
+     * 浣滃簾,缁堟娴佺▼鏍¢獙
+     *
+     * @param status 鐘舵��
+     */
+    public static void checkInvalidStatus(String status) {
+        if (FINISH.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡瀹屾垚鐢宠锛�");
+        } else if (INVALID.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡浣滃簾锛�");
+        } else if (TERMINATION.getStatus().equals(status)) {
+            throw new ServiceException("璇ュ崟鎹凡缁堟锛�");
+        } else if (StringUtils.isBlank(status)) {
+            throw new ServiceException("娴佺▼鐘舵�佷负绌猴紒");
+        }
+    }
+}
+
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java
new file mode 100644
index 0000000..083ab7b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java
@@ -0,0 +1,54 @@
+package org.dromara.workflow.common.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+
+/**
+ * 浠诲姟鐘舵�佹灇涓�
+ *
+ * @author may
+ */
+@Getter
+@AllArgsConstructor
+public enum FormTypeEnum {
+    /**
+     * 鑷畾涔夎〃鍗�
+     */
+    STATIC("static", "鑷畾涔夎〃鍗�"),
+    /**
+     * 鍔ㄦ�佽〃鍗�
+     */
+    DYNAMIC("dynamic", "鍔ㄦ�佽〃鍗�");
+
+    /**
+     * 绫诲瀷
+     */
+    private final String type;
+
+    /**
+     * 鎻忚堪
+     */
+    private final String desc;
+
+    /**
+     * 琛ㄥ崟绫诲瀷
+     *
+     * @param formType 琛ㄥ崟绫诲瀷
+     */
+    public static String findByType(String formType) {
+        if (StringUtils.isBlank(formType)) {
+            return StrUtil.EMPTY;
+        }
+
+        return Arrays.stream(FormTypeEnum.values())
+            .filter(statusEnum -> statusEnum.getType().equals(formType))
+            .findFirst()
+            .map(FormTypeEnum::getDesc)
+            .orElse(StrUtil.EMPTY);
+    }
+}
+
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java
new file mode 100644
index 0000000..a282958
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java
@@ -0,0 +1,51 @@
+package org.dromara.workflow.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 娑堟伅绫诲瀷鏋氫妇
+ *
+ * @author may
+ */
+@Getter
+@AllArgsConstructor
+public enum MessageTypeEnum {
+    /**
+     * 绔欏唴淇�
+     */
+    SYSTEM_MESSAGE("1", "绔欏唴淇�"),
+    /**
+     * 閭
+     */
+    EMAIL_MESSAGE("2", "閭"),
+    /**
+     * 鐭俊
+     */
+    SMS_MESSAGE("3", "鐭俊");
+
+    private final String code;
+
+    private final String desc;
+
+    private final static Map<String, MessageTypeEnum> MESSAGE_TYPE_ENUM_MAP = new ConcurrentHashMap<>(MessageTypeEnum.values().length);
+
+    static {
+        for (MessageTypeEnum messageType : MessageTypeEnum.values()) {
+            MESSAGE_TYPE_ENUM_MAP.put(messageType.code, messageType);
+        }
+    }
+
+    /**
+     * 鏍规嵁娑堟伅绫诲瀷 code 鑾峰彇 MessageTypeEnum
+     * @param code 娑堟伅绫诲瀷code
+     * @return MessageTypeEnum
+     */
+    public static MessageTypeEnum getByCode(String code) {
+        return MESSAGE_TYPE_ENUM_MAP.get(code);
+    }
+}
+
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java
new file mode 100644
index 0000000..7b2f55c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java
@@ -0,0 +1,94 @@
+package org.dromara.workflow.common.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+
+/**
+ * 浠诲姟鐘舵�佹灇涓�
+ *
+ * @author may
+ */
+@Getter
+@AllArgsConstructor
+public enum TaskStatusEnum {
+    /**
+     * 鎾ら攢
+     */
+    CANCEL("cancel", "鎾ら攢"),
+    /**
+     * 閫氳繃
+     */
+    PASS("pass", "閫氳繃"),
+    /**
+     * 寰呭鏍�
+     */
+    WAITING("waiting", "寰呭鏍�"),
+    /**
+     * 浣滃簾
+     */
+    INVALID("invalid", "浣滃簾"),
+    /**
+     * 閫�鍥�
+     */
+    BACK("back", "閫�鍥�"),
+    /**
+     * 缁堟
+     */
+    TERMINATION("termination", "缁堟"),
+    /**
+     * 杞姙
+     */
+    TRANSFER("transfer", "杞姙"),
+    /**
+     * 濮旀墭
+     */
+    PENDING("pending", "濮旀墭"),
+    /**
+     * 鎶勯��
+     */
+    COPY("copy", "鎶勯��"),
+    /**
+     * 鍔犵
+     */
+    SIGN("sign", "鍔犵"),
+    /**
+     * 鍑忕
+     */
+    SIGN_OFF("sign_off", "鍑忕"),
+    /**
+     * 瓒呮椂
+     */
+    TIMEOUT("timeout", "瓒呮椂");
+
+    /**
+     * 鐘舵��
+     */
+    private final String status;
+
+    /**
+     * 鎻忚堪
+     */
+    private final String desc;
+
+    /**
+     * 浠诲姟涓氬姟鐘舵��
+     *
+     * @param status 鐘舵��
+     */
+    public static String findByStatus(String status) {
+        if (StringUtils.isBlank(status)) {
+            return StrUtil.EMPTY;
+        }
+
+        return Arrays.stream(TaskStatusEnum.values())
+            .filter(statusEnum -> statusEnum.getStatus().equals(status))
+            .findFirst()
+            .map(TaskStatusEnum::getDesc)
+            .orElse(StrUtil.EMPTY);
+    }
+}
+
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java
new file mode 100644
index 0000000..3332f86
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java
@@ -0,0 +1,147 @@
+package org.dromara.workflow.controller;
+
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.workflow.domain.bo.ModelBo;
+import org.dromara.workflow.domain.vo.ModelVo;
+import org.dromara.workflow.service.IActModelService;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.repository.Model;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 妯″瀷绠$悊 鎺у埗灞�
+ *
+ * @author may
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/workflow/model")
+public class ActModelController extends BaseController {
+
+    private final RepositoryService repositoryService;
+
+    private final IActModelService actModelService;
+
+
+    /**
+     * 鍒嗛〉鏌ヨ妯″瀷
+     *
+     * @param modelBo 妯″瀷鍙傛暟
+     */
+    @GetMapping("/list")
+    public TableDataInfo<Model> page(ModelBo modelBo, PageQuery pageQuery) {
+        return actModelService.page(modelBo, pageQuery);
+    }
+
+    /**
+     * 鏂板妯″瀷
+     *
+     * @param modelBo 妯″瀷璇锋眰瀵硅薄
+     */
+    @Log(title = "妯″瀷绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/save")
+    public R<Void> saveNewModel(@Validated(AddGroup.class) @RequestBody ModelBo modelBo) {
+        return toAjax(actModelService.saveNewModel(modelBo));
+    }
+
+    /**
+     * 鏌ヨ妯″瀷
+     *
+     * @param id 妯″瀷id
+     */
+    @GetMapping("/getInfo/{id}")
+    public R<ModelVo> getInfo(@NotBlank(message = "妯″瀷id涓嶈兘涓虹┖") @PathVariable String id) {
+        return R.ok(actModelService.getInfo(id));
+    }
+
+    /**
+     * 淇敼妯″瀷淇℃伅
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     */
+    @Log(title = "妯″瀷绠$悊", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping(value = "/update")
+    public R<Void> update(@RequestBody ModelBo modelBo) {
+        return toAjax(actModelService.update(modelBo));
+    }
+
+    /**
+     * 缂栬緫XMl妯″瀷
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     */
+    @Log(title = "妯″瀷绠$悊", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping(value = "/editModelXml")
+    public R<Void> editModel(@Validated(EditGroup.class) @RequestBody ModelBo modelBo) {
+        return toAjax(actModelService.editModelXml(modelBo));
+    }
+
+    /**
+     * 鍒犻櫎娴佺▼妯″瀷
+     *
+     * @param ids 妯″瀷id
+     */
+    @Log(title = "妯″瀷绠$悊", businessType = BusinessType.DELETE)
+    @RepeatSubmit()
+    @DeleteMapping("/{ids}")
+    @Transactional(rollbackFor = Exception.class)
+    public R<Void> delete(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖") @PathVariable String[] ids) {
+        Arrays.stream(ids).parallel().forEachOrdered(repositoryService::deleteModel);
+        return R.ok();
+    }
+
+    /**
+     * 妯″瀷閮ㄧ讲
+     *
+     * @param id 妯″瀷id
+     */
+    @Log(title = "妯″瀷绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/modelDeploy/{id}")
+    public R<Void> deploy(@NotBlank(message = "妯″瀷id涓嶈兘涓虹┖") @PathVariable("id") String id) {
+        return toAjax(actModelService.modelDeploy(id));
+    }
+
+    /**
+     * 瀵煎嚭妯″瀷zip鍘嬬缉鍖�
+     *
+     * @param modelIds 妯″瀷id
+     * @param response 鐩稿簲
+     */
+    @GetMapping("/export/zip/{modelIds}")
+    public void exportZip(@NotEmpty(message = "妯″瀷id涓嶈兘涓虹┖") @PathVariable List<String> modelIds,
+                          HttpServletResponse response) {
+        actModelService.exportZip(modelIds, response);
+    }
+
+    /**
+     * 澶嶅埗妯″瀷
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     */
+    @PostMapping("/copyModel")
+    public R<Void> copyModel(@RequestBody ModelBo modelBo) {
+        return toAjax(actModelService.copyModel(modelBo));
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java
new file mode 100644
index 0000000..5198bd1
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java
@@ -0,0 +1,147 @@
+package org.dromara.workflow.controller;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.workflow.domain.bo.ProcessDefinitionBo;
+import org.dromara.workflow.domain.vo.ProcessDefinitionVo;
+import org.dromara.workflow.service.IActProcessDefinitionService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 娴佺▼瀹氫箟绠$悊 鎺у埗灞�
+ *
+ * @author may
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/workflow/processDefinition")
+public class ActProcessDefinitionController extends BaseController {
+
+    private final IActProcessDefinitionService actProcessDefinitionService;
+
+    /**
+     * 鍒嗛〉鏌ヨ
+     *
+     * @param bo 鍙傛暟
+     */
+    @GetMapping("/list")
+    public TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo bo, PageQuery pageQuery) {
+        return actProcessDefinitionService.page(bo, pageQuery);
+    }
+
+    /**
+     * 鏌ヨ鍘嗗彶娴佺▼瀹氫箟鍒楄〃
+     *
+     * @param key 娴佺▼瀹氫箟key
+     */
+    @GetMapping("/getListByKey/{key}")
+    public R<List<ProcessDefinitionVo>> getListByKey(@NotEmpty(message = "娴佺▼瀹氫箟key涓嶈兘涓虹┖") @PathVariable String key) {
+        return R.ok("鎿嶄綔鎴愬姛", actProcessDefinitionService.getListByKey(key));
+    }
+
+    /**
+     * 鏌ョ湅娴佺▼瀹氫箟鍥剧墖
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    @GetMapping("/definitionImage/{processDefinitionId}")
+    public R<String> definitionImage(@PathVariable String processDefinitionId) {
+        return R.ok("鎿嶄綔鎴愬姛", actProcessDefinitionService.definitionImage(processDefinitionId));
+    }
+
+    /**
+     * 鏌ョ湅娴佺▼瀹氫箟xml鏂囦欢
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    @GetMapping("/definitionXml/{processDefinitionId}")
+    public R<Map<String, Object>> definitionXml(@NotBlank(message = "娴佺▼瀹氫箟id涓嶈兘涓虹┖") @PathVariable String processDefinitionId) {
+        Map<String, Object> map = new HashMap<>();
+        String xmlStr = actProcessDefinitionService.definitionXml(processDefinitionId);
+        map.put("xml", Arrays.asList(xmlStr.split("\n")));
+        map.put("xmlStr", xmlStr);
+        return R.ok(map);
+    }
+
+    /**
+     * 鍒犻櫎娴佺▼瀹氫箟
+     *
+     * @param deploymentIds        閮ㄧ讲id
+     * @param processDefinitionIds 娴佺▼瀹氫箟id
+     */
+    @Log(title = "娴佺▼瀹氫箟绠$悊", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{deploymentIds}/{processDefinitionIds}")
+    public R<Void> deleteDeployment(@NotNull(message = "娴佺▼閮ㄧ讲id涓嶈兘涓虹┖") @PathVariable List<String> deploymentIds,
+                                    @NotNull(message = "娴佺▼瀹氫箟id涓嶈兘涓虹┖") @PathVariable List<String> processDefinitionIds) {
+        return toAjax(actProcessDefinitionService.deleteDeployment(deploymentIds, processDefinitionIds));
+    }
+
+    /**
+     * 婵�娲绘垨鑰呮寕璧锋祦绋嬪畾涔�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    @Log(title = "娴佺▼瀹氫箟绠$悊", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping("/updateDefinitionState/{processDefinitionId}")
+    public R<Void> updateDefinitionState(@NotBlank(message = "娴佺▼瀹氫箟id涓嶈兘涓虹┖") @PathVariable String processDefinitionId) {
+        return toAjax(actProcessDefinitionService.updateDefinitionState(processDefinitionId));
+    }
+
+    /**
+     * 杩佺Щ娴佺▼瀹氫箟
+     *
+     * @param currentProcessDefinitionId 褰撳墠娴佺▼瀹氫箟id
+     * @param fromProcessDefinitionId    闇�瑕佽縼绉诲埌鐨勬祦绋嬪畾涔塱d
+     */
+    @Log(title = "娴佺▼瀹氫箟绠$悊", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping("/migrationDefinition/{currentProcessDefinitionId}/{fromProcessDefinitionId}")
+    public R<Void> migrationDefinition(@NotBlank(message = "褰撳墠娴佺▼瀹氫箟id") @PathVariable String currentProcessDefinitionId,
+                                       @NotBlank(message = "闇�瑕佽縼绉诲埌鐨勬祦绋嬪畾涔塱d") @PathVariable String fromProcessDefinitionId) {
+        return toAjax(actProcessDefinitionService.migrationDefinition(currentProcessDefinitionId, fromProcessDefinitionId));
+    }
+
+    /**
+     * 娴佺▼瀹氫箟杞崲涓烘ā鍨�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    @Log(title = "娴佺▼瀹氫箟绠$悊", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping("/convertToModel/{processDefinitionId}")
+    public R<Void> convertToModel(@NotEmpty(message = "娴佺▼瀹氫箟id涓嶈兘涓虹┖") @PathVariable String processDefinitionId) {
+        return toAjax(actProcessDefinitionService.convertToModel(processDefinitionId));
+    }
+
+    /**
+     * 閫氳繃zip鎴杧ml閮ㄧ讲娴佺▼瀹氫箟
+     *
+     * @param file         鏂囦欢
+     * @param categoryCode 鍒嗙被
+     */
+    @Log(title = "娴佺▼瀹氫箟绠$悊", businessType = BusinessType.INSERT)
+    @PostMapping("/deployByFile")
+    public void deployByFile(@RequestParam("file") MultipartFile file, @RequestParam("categoryCode") String categoryCode) {
+        actProcessDefinitionService.deployByFile(file, categoryCode);
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java
new file mode 100644
index 0000000..142386b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java
@@ -0,0 +1,160 @@
+package org.dromara.workflow.controller;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.workflow.domain.bo.ProcessInstanceBo;
+import org.dromara.workflow.domain.bo.ProcessInvalidBo;
+import org.dromara.workflow.domain.bo.TaskUrgingBo;
+import org.dromara.workflow.domain.vo.ActHistoryInfoVo;
+import org.dromara.workflow.domain.vo.ProcessInstanceVo;
+import org.dromara.workflow.service.IActProcessInstanceService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 娴佺▼瀹炰緥绠$悊 鎺у埗灞�
+ *
+ * @author may
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/workflow/processInstance")
+public class ActProcessInstanceController extends BaseController {
+
+    private final IActProcessInstanceService actProcessInstanceService;
+
+    /**
+     * 鍒嗛〉鏌ヨ姝e湪杩愯鐨勬祦绋嬪疄渚�
+     *
+     * @param bo 鍙傛暟
+     */
+    @GetMapping("/getPageByRunning")
+    public TableDataInfo<ProcessInstanceVo> getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) {
+        return actProcessInstanceService.getPageByRunning(bo, pageQuery);
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ宸茬粨鏉熺殑娴佺▼瀹炰緥
+     *
+     * @param bo 鍙傛暟
+     */
+    @GetMapping("/getPageByFinish")
+    public TableDataInfo<ProcessInstanceVo> getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) {
+        return actProcessInstanceService.getPageByFinish(bo, pageQuery);
+    }
+
+    /**
+     * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥�
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @GetMapping("/getHistoryImage/{processInstanceId}")
+    public R<String> getHistoryImage(@NotBlank(message = "娴佺▼瀹炰緥id涓嶈兘涓虹┖") @PathVariable String processInstanceId) {
+        return R.ok("鎿嶄綔鎴愬姛", actProcessInstanceService.getHistoryImage(processInstanceId));
+    }
+
+    /**
+     * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥捐繍琛屼腑锛屽巻鍙茬瓑鑺傜偣
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @GetMapping("/getHistoryList/{processInstanceId}")
+    public R<Map<String, Object>> getHistoryList(@NotBlank(message = "娴佺▼瀹炰緥id涓嶈兘涓虹┖") @PathVariable String processInstanceId) {
+        return R.ok("鎿嶄綔鎴愬姛", actProcessInstanceService.getHistoryList(processInstanceId));
+    }
+
+    /**
+     * 鑾峰彇瀹℃壒璁板綍
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @GetMapping("/getHistoryRecord/{processInstanceId}")
+    public R<List<ActHistoryInfoVo>> getHistoryRecord(@NotBlank(message = "娴佺▼瀹炰緥id涓嶈兘涓虹┖") @PathVariable String processInstanceId) {
+        return R.ok(actProcessInstanceService.getHistoryRecord(processInstanceId));
+    }
+
+    /**
+     * 浣滃簾娴佺▼瀹炰緥锛屼笉浼氬垹闄ゅ巻鍙茶褰�(鍒犻櫎杩愯涓殑瀹炰緥)
+     *
+     * @param processInvalidBo 鍙傛暟
+     */
+    @Log(title = "娴佺▼瀹炰緥绠$悊", businessType = BusinessType.DELETE)
+    @RepeatSubmit()
+    @PostMapping("/deleteRunInstance")
+    public R<Void> deleteRunInstance(@Validated(AddGroup.class) @RequestBody ProcessInvalidBo processInvalidBo) {
+        return toAjax(actProcessInstanceService.deleteRunInstance(processInvalidBo));
+    }
+
+    /**
+     * 杩愯涓殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     */
+    @Log(title = "娴佺▼瀹炰緥绠$悊", businessType = BusinessType.DELETE)
+    @RepeatSubmit()
+    @DeleteMapping("/deleteRunAndHisInstance/{processInstanceIds}")
+    public R<Void> deleteRunAndHisInstance(@NotNull(message = "娴佺▼瀹炰緥id涓嶈兘涓虹┖") @PathVariable String[] processInstanceIds) {
+        return toAjax(actProcessInstanceService.deleteRunAndHisInstance(Arrays.asList(processInstanceIds)));
+    }
+
+    /**
+     * 宸插畬鎴愮殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     */
+    @Log(title = "娴佺▼瀹炰緥绠$悊", businessType = BusinessType.DELETE)
+    @RepeatSubmit()
+    @DeleteMapping("/deleteFinishAndHisInstance/{processInstanceIds}")
+    public R<Void> deleteFinishAndHisInstance(@NotNull(message = "娴佺▼瀹炰緥id涓嶈兘涓虹┖") @PathVariable String[] processInstanceIds) {
+        return toAjax(actProcessInstanceService.deleteFinishAndHisInstance(Arrays.asList(processInstanceIds)));
+    }
+
+    /**
+     * 鎾ら攢娴佺▼鐢宠
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @Log(title = "娴佺▼瀹炰緥绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/cancelProcessApply/{processInstanceId}")
+    public R<Void> cancelProcessApply(@NotBlank(message = "娴佺▼瀹炰緥id涓嶈兘涓虹┖") @PathVariable String processInstanceId) {
+        return toAjax(actProcessInstanceService.cancelProcessApply(processInstanceId));
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ褰撳墠鐧诲綍浜哄崟鎹�
+     *
+     * @param bo 鍙傛暟
+     */
+    @GetMapping("/getPageByCurrent")
+    public TableDataInfo<ProcessInstanceVo> getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) {
+        return actProcessInstanceService.getPageByCurrent(bo, pageQuery);
+    }
+
+    /**
+     * 浠诲姟鍌姙(缁欏綋鍓嶄换鍔″姙鐞嗕汉鍙戦�佺珯鍐呬俊锛岄偖浠讹紝鐭俊绛�)
+     *
+     * @param taskUrgingBo 浠诲姟鍌姙
+     */
+    @Log(title = "娴佺▼瀹炰緥绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/taskUrging")
+    public R<Void> taskUrging(@RequestBody TaskUrgingBo taskUrgingBo) {
+        return toAjax(actProcessInstanceService.taskUrging(taskUrgingBo));
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java
new file mode 100644
index 0000000..7fc9b95
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java
@@ -0,0 +1,295 @@
+package org.dromara.workflow.controller;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.workflow.domain.WfTaskBackNode;
+import org.dromara.workflow.domain.bo.*;
+import org.dromara.workflow.domain.vo.TaskVo;
+import org.dromara.workflow.domain.vo.VariableVo;
+import org.dromara.workflow.service.IActTaskService;
+import org.dromara.workflow.service.IWfTaskBackNodeService;
+import org.dromara.workflow.utils.QueryUtils;
+import org.flowable.engine.TaskService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 浠诲姟绠$悊 鎺у埗灞�
+ *
+ * @author may
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/workflow/task")
+public class ActTaskController extends BaseController {
+
+    private final IActTaskService actTaskService;
+
+    private final TaskService taskService;
+
+    private final IWfTaskBackNodeService wfTaskBackNodeService;
+
+
+    /**
+     * 鍚姩浠诲姟
+     *
+     * @param startProcessBo 鍚姩娴佺▼鍙傛暟
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/startWorkFlow")
+    public R<Map<String, Object>> startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) {
+        Map<String, Object> map = actTaskService.startWorkFlow(startProcessBo);
+        return R.ok("鎻愪氦鎴愬姛", map);
+    }
+
+    /**
+     * 鍔炵悊浠诲姟
+     *
+     * @param completeTaskBo 鍔炵悊浠诲姟鍙傛暟
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/completeTask")
+    public R<Void> completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) {
+        return toAjax(actTaskService.completeTask(completeTaskBo));
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勫緟鍔炰换鍔�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @GetMapping("/getPageByTaskWait")
+    public TableDataInfo<TaskVo> getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery) {
+        return actTaskService.getPageByTaskWait(taskBo, pageQuery);
+    }
+
+    /**
+     * 鏌ヨ褰撳墠绉熸埛鎵�鏈夊緟鍔炰换鍔�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @GetMapping("/getPageByAllTaskWait")
+    public TableDataInfo<TaskVo> getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery) {
+        return actTaskService.getPageByAllTaskWait(taskBo, pageQuery);
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勫凡鍔炰换鍔�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @GetMapping("/getPageByTaskFinish")
+    public TableDataInfo<TaskVo> getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery) {
+        return actTaskService.getPageByTaskFinish(taskBo, pageQuery);
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勬妱閫�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @GetMapping("/getPageByTaskCopy")
+    public TableDataInfo<TaskVo> getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery) {
+        return actTaskService.getPageByTaskCopy(taskBo, pageQuery);
+    }
+
+    /**
+     * 鏌ヨ褰撳墠绉熸埛鎵�鏈夊凡鍔炰换鍔�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @GetMapping("/getPageByAllTaskFinish")
+    public TableDataInfo<TaskVo> getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery) {
+        return actTaskService.getPageByAllTaskFinish(taskBo, pageQuery);
+    }
+
+    /**
+     * 绛炬敹锛堟嬀鍙栵級浠诲姟
+     *
+     * @param taskId 浠诲姟id
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/claim/{taskId}")
+    public R<Void> claimTask(@NotBlank(message = "浠诲姟id涓嶈兘涓虹┖") @PathVariable String taskId) {
+        try {
+            taskService.claim(taskId, Convert.toStr(LoginHelper.getUserId()));
+            return R.ok();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.fail("绛炬敹浠诲姟澶辫触锛�" + e.getMessage());
+        }
+    }
+
+    /**
+     * 褰掕繕锛堟嬀鍙栫殑锛変换鍔�
+     *
+     * @param taskId 浠诲姟id
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/returnTask/{taskId}")
+    public R<Void> returnTask(@NotBlank(message = "浠诲姟id涓嶈兘涓虹┖") @PathVariable String taskId) {
+        try {
+            taskService.setAssignee(taskId, null);
+            return R.ok();
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.fail("褰掕繕浠诲姟澶辫触锛�" + e.getMessage());
+        }
+    }
+
+    /**
+     * 濮旀淳浠诲姟
+     *
+     * @param delegateBo 鍙傛暟
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/delegateTask")
+    public R<Void> delegateTask(@Validated({AddGroup.class}) @RequestBody DelegateBo delegateBo) {
+        return toAjax(actTaskService.delegateTask(delegateBo));
+    }
+
+    /**
+     * 缁堟浠诲姟
+     *
+     * @param terminationBo 鍙傛暟
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.DELETE)
+    @RepeatSubmit()
+    @PostMapping("/terminationTask")
+    public R<Void> terminationTask(@RequestBody TerminationBo terminationBo) {
+        return toAjax(actTaskService.terminationTask(terminationBo));
+    }
+
+    /**
+     * 杞姙浠诲姟
+     *
+     * @param transmitBo 鍙傛暟
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/transferTask")
+    public R<Void> transferTask(@Validated({AddGroup.class}) @RequestBody TransmitBo transmitBo) {
+        return toAjax(actTaskService.transferTask(transmitBo));
+    }
+
+    /**
+     * 浼氱浠诲姟鍔犵
+     *
+     * @param addMultiBo 鍙傛暟
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/addMultiInstanceExecution")
+    public R<Void> addMultiInstanceExecution(@Validated({AddGroup.class}) @RequestBody AddMultiBo addMultiBo) {
+        return toAjax(actTaskService.addMultiInstanceExecution(addMultiBo));
+    }
+
+    /**
+     * 浼氱浠诲姟鍑忕
+     *
+     * @param deleteMultiBo 鍙傛暟
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/deleteMultiInstanceExecution")
+    public R<Void> deleteMultiInstanceExecution(@Validated({AddGroup.class}) @RequestBody DeleteMultiBo deleteMultiBo) {
+        return toAjax(actTaskService.deleteMultiInstanceExecution(deleteMultiBo));
+    }
+
+    /**
+     * 椹冲洖瀹℃壒
+     *
+     * @param backProcessBo 鍙傛暟
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/backProcess")
+    public R<String> backProcess(@Validated({AddGroup.class}) @RequestBody BackProcessBo backProcessBo) {
+        return R.ok(actTaskService.backProcess(backProcessBo));
+    }
+
+    /**
+     * 鑾峰彇褰撳墠浠诲姟
+     *
+     * @param taskId 浠诲姟id
+     */
+    @GetMapping("/getTaskById/{taskId}")
+    public R<TaskVo> getTaskById(@PathVariable String taskId) {
+        return R.ok(QueryUtils.getTask(taskId));
+    }
+
+
+    /**
+     * 淇敼浠诲姟鍔炵悊浜�
+     *
+     * @param taskIds 浠诲姟id
+     * @param userId  鍔炵悊浜篿d
+     */
+    @Log(title = "浠诲姟绠$悊", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping("/updateAssignee/{taskIds}/{userId}")
+    public R<Void> updateAssignee(@PathVariable String[] taskIds, @PathVariable String userId) {
+        return toAjax(actTaskService.updateAssignee(taskIds, userId));
+    }
+
+    /**
+     * 鏌ヨ娴佺▼鍙橀噺
+     *
+     * @param taskId 浠诲姟id
+     */
+    @GetMapping("/getInstanceVariable/{taskId}")
+    public R<List<VariableVo>> getProcessInstVariable(@PathVariable String taskId) {
+        return R.ok(actTaskService.getInstanceVariable(taskId));
+    }
+
+    /**
+     * 鑾峰彇鍙┏鍥炲緱浠诲姟鑺傜偣
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @GetMapping("/getTaskNodeList/{processInstanceId}")
+    public R<List<WfTaskBackNode>> getNodeList(@PathVariable String processInstanceId) {
+        return R.ok(CollUtil.reverse(wfTaskBackNodeService.getListByInstanceId(processInstanceId)));
+    }
+
+    /**
+     * 鏌ヨ宸ヤ綔娴佷换鍔$敤鎴烽�夋嫨鍔犵浜哄憳
+     *
+     * @param taskId 浠诲姟id
+     */
+    @GetMapping("/getTaskUserIdsByAddMultiInstance/{taskId}")
+    public R<String> getTaskUserIdsByAddMultiInstance(@PathVariable String taskId) {
+        return R.ok(actTaskService.getTaskUserIdsByAddMultiInstance(taskId));
+    }
+
+    /**
+     * 鏌ヨ宸ヤ綔娴侀�夋嫨鍑忕浜哄憳
+     *
+     * @param taskId 浠诲姟id
+     */
+    @GetMapping("/getListByDeleteMultiInstance/{taskId}")
+    public R<List<TaskVo>> getListByDeleteMultiInstance(@PathVariable String taskId) {
+        return R.ok(actTaskService.getListByDeleteMultiInstance(taskId));
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java
new file mode 100644
index 0000000..17b6755
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java
@@ -0,0 +1,106 @@
+package org.dromara.workflow.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.workflow.domain.bo.TestLeaveBo;
+import org.dromara.workflow.domain.vo.TestLeaveVo;
+import org.dromara.workflow.service.ITestLeaveService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 璇峰亣
+ *
+ * @author may
+ * @date 2023-07-21
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/demo/leave")
+public class TestLeaveController extends BaseController {
+
+    private final ITestLeaveService testLeaveService;
+
+    /**
+     * 鏌ヨ璇峰亣鍒楄〃
+     */
+    @SaCheckPermission("demo:leave:list")
+    @GetMapping("/list")
+    public TableDataInfo<TestLeaveVo> list(TestLeaveBo bo, PageQuery pageQuery) {
+        return testLeaveService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 瀵煎嚭璇峰亣鍒楄〃
+     */
+    @SaCheckPermission("demo:leave:export")
+    @Log(title = "璇峰亣", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(TestLeaveBo bo, HttpServletResponse response) {
+        List<TestLeaveVo> list = testLeaveService.queryList(bo);
+        ExcelUtil.exportExcel(list, "璇峰亣", TestLeaveVo.class, response);
+    }
+
+    /**
+     * 鑾峰彇璇峰亣璇︾粏淇℃伅
+     *
+     * @param id 涓婚敭
+     */
+    @SaCheckPermission("demo:leave:query")
+    @GetMapping("/{id}")
+    public R<TestLeaveVo> getInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+                                  @PathVariable Long id) {
+        return R.ok(testLeaveService.queryById(id));
+    }
+
+    /**
+     * 鏂板璇峰亣
+     */
+    @SaCheckPermission("demo:leave:add")
+    @Log(title = "璇峰亣", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<TestLeaveVo> add(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) {
+        return R.ok(testLeaveService.insertByBo(bo));
+    }
+
+    /**
+     * 淇敼璇峰亣
+     */
+    @SaCheckPermission("demo:leave:edit")
+    @Log(title = "璇峰亣", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<TestLeaveVo> edit(@Validated(EditGroup.class) @RequestBody TestLeaveBo bo) {
+        return R.ok(testLeaveService.updateByBo(bo));
+    }
+
+    /**
+     * 鍒犻櫎璇峰亣
+     *
+     * @param ids 涓婚敭涓�
+     */
+    @SaCheckPermission("demo:leave:remove")
+    @Log(title = "璇峰亣", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+                          @PathVariable Long[] ids) {
+        return toAjax(testLeaveService.deleteWithValidByIds(List.of(ids)));
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java
new file mode 100644
index 0000000..8dced89
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java
@@ -0,0 +1,106 @@
+package org.dromara.workflow.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.workflow.domain.bo.WfCategoryBo;
+import org.dromara.workflow.domain.vo.WfCategoryVo;
+import org.dromara.workflow.service.IWfCategoryService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 娴佺▼鍒嗙被
+ *
+ * @author may
+ * @date 2023-06-28
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/workflow/category")
+public class WfCategoryController extends BaseController {
+
+    private final IWfCategoryService wfCategoryService;
+
+    /**
+     * 鏌ヨ娴佺▼鍒嗙被鍒楄〃
+     */
+    @SaCheckPermission("workflow:category:list")
+    @GetMapping("/list")
+    public R<List<WfCategoryVo>> list(WfCategoryBo bo) {
+        List<WfCategoryVo> list = wfCategoryService.queryList(bo);
+        return R.ok(list);
+
+    }
+
+    /**
+     * 瀵煎嚭娴佺▼鍒嗙被鍒楄〃
+     */
+    @SaCheckPermission("workflow:category:export")
+    @Log(title = "娴佺▼鍒嗙被", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(WfCategoryBo bo, HttpServletResponse response) {
+        List<WfCategoryVo> list = wfCategoryService.queryList(bo);
+        ExcelUtil.exportExcel(list, "娴佺▼鍒嗙被", WfCategoryVo.class, response);
+    }
+
+    /**
+     * 鑾峰彇娴佺▼鍒嗙被璇︾粏淇℃伅
+     *
+     * @param id 涓婚敭
+     */
+    @SaCheckPermission("workflow:category:query")
+    @GetMapping("/{id}")
+    public R<WfCategoryVo> getInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+                                   @PathVariable Long id) {
+        return R.ok(wfCategoryService.queryById(id));
+    }
+
+    /**
+     * 鏂板娴佺▼鍒嗙被
+     */
+    @SaCheckPermission("workflow:category:add")
+    @Log(title = "娴佺▼鍒嗙被", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody WfCategoryBo bo) {
+        return toAjax(wfCategoryService.insertByBo(bo));
+    }
+
+    /**
+     * 淇敼娴佺▼鍒嗙被
+     */
+    @SaCheckPermission("workflow:category:edit")
+    @Log(title = "娴佺▼鍒嗙被", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody WfCategoryBo bo) {
+        return toAjax(wfCategoryService.updateByBo(bo));
+    }
+
+    /**
+     * 鍒犻櫎娴佺▼鍒嗙被
+     *
+     * @param ids 涓婚敭涓�
+     */
+    @SaCheckPermission("workflow:category:remove")
+    @Log(title = "娴佺▼鍒嗙被", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+                          @PathVariable Long[] ids) {
+        return toAjax(wfCategoryService.deleteWithValidByIds(List.of(ids), true));
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfDefinitionConfigController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfDefinitionConfigController.java
new file mode 100644
index 0000000..176aba2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfDefinitionConfigController.java
@@ -0,0 +1,79 @@
+package org.dromara.workflow.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.validation.constraints.*;
+import org.dromara.workflow.domain.bo.WfDefinitionConfigBo;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.workflow.domain.vo.WfDefinitionConfigVo;
+import org.dromara.workflow.service.IWfDefinitionConfigService;
+
+/**
+ * 娴佺▼瀹氫箟閰嶇疆
+ *
+ * @author may
+ * @date 2024-03-18
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/workflow/definitionConfig")
+public class WfDefinitionConfigController extends BaseController {
+
+    private final IWfDefinitionConfigService wfDefinitionConfigService;
+
+
+    /**
+     * 鑾峰彇娴佺▼瀹氫箟閰嶇疆璇︾粏淇℃伅
+     *
+     * @param definitionId 涓婚敭
+     */
+    @GetMapping("/getByDefId/{definitionId}")
+    public R<WfDefinitionConfigVo> getByDefId(@NotBlank(message = "娴佺▼瀹氫箟ID涓嶈兘涓虹┖")
+                                              @PathVariable String definitionId) {
+        return R.ok(wfDefinitionConfigService.getByDefId(definitionId));
+    }
+
+    /**
+     * 鏂板娴佺▼瀹氫箟閰嶇疆
+     */
+    @Log(title = "娴佺▼瀹氫箟閰嶇疆", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/saveOrUpdate")
+    public R<Void> saveOrUpdate(@Validated(AddGroup.class) @RequestBody WfDefinitionConfigBo bo) {
+        return toAjax(wfDefinitionConfigService.saveOrUpdate(bo));
+    }
+
+    /**
+     * 鍒犻櫎娴佺▼瀹氫箟閰嶇疆
+     *
+     * @param ids 涓婚敭涓�
+     */
+    @Log(title = "娴佺▼瀹氫箟閰嶇疆", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+                          @PathVariable Long[] ids) {
+        return toAjax(wfDefinitionConfigService.deleteByIds(List.of(ids)));
+    }
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆鎺掗櫎褰撳墠鏌ヨ鐨勬祦绋嬪畾涔�
+     *
+     * @param tableName    琛ㄥ悕
+     * @param definitionId 娴佺▼瀹氫箟id
+     */
+    @GetMapping("/getByTableNameNotDefId/{tableName}/{definitionId}")
+    public R<List<WfDefinitionConfigVo>> getByTableNameNotDefId(@NotBlank(message = "琛ㄥ悕涓嶈兘涓虹┖") @PathVariable String tableName,
+                                                                @NotBlank(message = "娴佺▼瀹氫箟ID涓嶈兘涓虹┖") @PathVariable String definitionId) {
+        return R.ok(wfDefinitionConfigService.getByTableNameNotDefId(tableName, definitionId));
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfFormManageController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfFormManageController.java
new file mode 100644
index 0000000..198e233
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfFormManageController.java
@@ -0,0 +1,114 @@
+package org.dromara.workflow.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.common.excel.utils.ExcelUtil;
+import org.dromara.workflow.domain.vo.WfFormManageVo;
+import org.dromara.workflow.domain.bo.WfFormManageBo;
+import org.dromara.workflow.service.IWfFormManageService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 琛ㄥ崟绠$悊
+ *
+ * @author may
+ * @date 2024-03-29
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/workflow/formManage")
+public class WfFormManageController extends BaseController {
+
+    private final IWfFormManageService wfFormManageService;
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+     */
+    @SaCheckPermission("workflow:formManage:list")
+    @GetMapping("/list")
+    public TableDataInfo<WfFormManageVo> list(WfFormManageBo bo, PageQuery pageQuery) {
+        return wfFormManageService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+     */
+    @SaCheckPermission("workflow:formManage:list")
+    @GetMapping("/list/selectList")
+    public R<List<WfFormManageVo>> selectList() {
+        return R.ok(wfFormManageService.selectList());
+    }
+
+    /**
+     * 瀵煎嚭琛ㄥ崟绠$悊鍒楄〃
+     */
+    @SaCheckPermission("workflow:formManage:export")
+    @Log(title = "琛ㄥ崟绠$悊", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(WfFormManageBo bo, HttpServletResponse response) {
+        List<WfFormManageVo> list = wfFormManageService.queryList(bo);
+        ExcelUtil.exportExcel(list, "琛ㄥ崟绠$悊", WfFormManageVo.class, response);
+    }
+
+    /**
+     * 鑾峰彇琛ㄥ崟绠$悊璇︾粏淇℃伅
+     *
+     * @param id 涓婚敭
+     */
+    @SaCheckPermission("workflow:formManage:query")
+    @GetMapping("/{id}")
+    public R<WfFormManageVo> getInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+                                     @PathVariable Long id) {
+        return R.ok(wfFormManageService.queryById(id));
+    }
+
+    /**
+     * 鏂板琛ㄥ崟绠$悊
+     */
+    @SaCheckPermission("workflow:formManage:add")
+    @Log(title = "琛ㄥ崟绠$悊", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody WfFormManageBo bo) {
+        return toAjax(wfFormManageService.insertByBo(bo));
+    }
+
+    /**
+     * 淇敼琛ㄥ崟绠$悊
+     */
+    @SaCheckPermission("workflow:formManage:edit")
+    @Log(title = "琛ㄥ崟绠$悊", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody WfFormManageBo bo) {
+        return toAjax(wfFormManageService.updateByBo(bo));
+    }
+
+    /**
+     * 鍒犻櫎琛ㄥ崟绠$悊
+     *
+     * @param ids 涓婚敭涓�
+     */
+    @SaCheckPermission("workflow:formManage:remove")
+    @Log(title = "琛ㄥ崟绠$悊", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+                          @PathVariable Long[] ids) {
+        return toAjax(wfFormManageService.deleteByIds(List.of(ids)));
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java
new file mode 100644
index 0000000..e87fb92
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java
@@ -0,0 +1,152 @@
+package org.dromara.workflow.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 娴佺▼瀹炰緥瀵硅薄 act_hi_procinst
+ *
+ * @author may
+ * @date 2023-07-22
+ */
+@Data
+@TableName("act_hi_procinst")
+public class ActHiProcinst implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     *
+     */
+    @TableId(value = "ID_")
+    private String id;
+
+    /**
+     *
+     */
+    @TableField(value = "REV_")
+    private Long rev;
+
+    /**
+     *
+     */
+    @TableField(value = "PROC_INST_ID_")
+    private String procInstId;
+
+    /**
+     *
+     */
+    @TableField(value = "BUSINESS_KEY_")
+    private String businessKey;
+
+    /**
+     *
+     */
+    @TableField(value = "PROC_DEF_ID_")
+    private String procDefId;
+
+    /**
+     *
+     */
+    @TableField(value = "START_TIME_")
+    private Date startTime;
+
+    /**
+     *
+     */
+    @TableField(value = "END_TIME_")
+    private Date endTime;
+
+    /**
+     *
+     */
+    @TableField(value = "DURATION_")
+    private Long duration;
+
+    /**
+     *
+     */
+    @TableField(value = "START_USER_ID_")
+    private String startUserId;
+
+    /**
+     *
+     */
+    @TableField(value = "START_ACT_ID_")
+    private String startActId;
+
+    /**
+     *
+     */
+    @TableField(value = "END_ACT_ID_")
+    private String endActId;
+
+    /**
+     *
+     */
+    @TableField(value = "SUPER_PROCESS_INSTANCE_ID_")
+    private String superProcessInstanceId;
+
+    /**
+     *
+     */
+    @TableField(value = "DELETE_REASON_")
+    private String deleteReason;
+
+    /**
+     *
+     */
+    @TableField(value = "TENANT_ID_")
+    private String tenantId;
+
+    /**
+     *
+     */
+    @TableField(value = "NAME_")
+    private String name;
+
+    /**
+     *
+     */
+    @TableField(value = "CALLBACK_ID_")
+    private String callbackId;
+
+    /**
+     *
+     */
+    @TableField(value = "CALLBACK_TYPE_")
+    private String callbackType;
+
+    /**
+     *
+     */
+    @TableField(value = "REFERENCE_ID_")
+    private String referenceId;
+
+    /**
+     *
+     */
+    @TableField(value = "REFERENCE_TYPE_")
+    private String referenceType;
+
+    /**
+     *
+     */
+    @TableField(value = "PROPAGATED_STAGE_INST_ID_")
+    private String propagatedStageInstId;
+
+    /**
+     *
+     */
+    @TableField(value = "BUSINESS_STATUS_")
+    private String businessStatus;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java
new file mode 100644
index 0000000..abc17b5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java
@@ -0,0 +1,193 @@
+package org.dromara.workflow.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import java.io.Serial;
+
+/**
+ * 娴佺▼鍘嗗彶浠诲姟瀵硅薄 act_hi_taskinst
+ *
+ * @author may
+ * @date 2024-03-02
+ */
+@Data
+@TableName("act_hi_taskinst")
+public class ActHiTaskinst implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     *
+     */
+    @TableId(value = "ID_")
+    private String id;
+
+    /**
+     * 鐗堟湰
+     */
+    @TableField(value = "REV_")
+    private Long rev;
+
+    /**
+     * 娴佺▼瀹氫箟id
+     */
+    @TableField(value = "PROC_DEF_ID_")
+    private String procDefId;
+
+    /**
+     *
+     */
+    @TableField(value = "TASK_DEF_ID_")
+    private String taskDefId;
+
+    /**
+     * 浠诲姟鑺傜偣id
+     */
+    @TableField(value = "TASK_DEF_KEY_")
+    private String taskDefKey;
+
+    /**
+     * 娴佺▼瀹炰緥id
+     */
+    @TableField(value = "PROC_INST_ID_")
+    private String procInstId;
+
+    /**
+     * 娴佺▼鎵цid
+     */
+    @TableField(value = "EXECUTION_ID")
+    private String executionId;
+
+    /**
+     *
+     */
+    @TableField(value = "SCOPE_ID_")
+    private String scopeId;
+
+    /**
+     *
+     */
+    @TableField(value = "SUB_SCOPE_ID_")
+    private String subScopeId;
+
+    /**
+     * 鍏堢敤褰撳墠瀛楁鏍囪瘑鎶勯�佺被鍨�
+     */
+    @TableField(value = "SCOPE_TYPE_")
+    private String scopeType;
+
+    /**
+     *
+     */
+    @TableField(value = "SCOPE_DEFINITION_ID_")
+    private String scopeDefinitionId;
+
+    /**
+     *
+     */
+    @TableField(value = "PROPAGATED_STAGE_INST_ID_")
+    private String propagatedStageInstId;
+
+    /**
+     * 浠诲姟鍚嶇О
+     */
+    @TableField(value = "NAME_")
+    private String name;
+
+    /**
+     * 鐖剁骇id
+     */
+    @TableField(value = "PARENT_TASK_ID_")
+    private String parentTaskId;
+
+    /**
+     * 鎻忚堪
+     */
+    @TableField(value = "DESCRIPTION_")
+    private String description;
+
+    /**
+     * 鍔炵悊浜�
+     */
+    @TableField(value = "OWNER_")
+    private String owner;
+
+    /**
+     * 鍔炵悊浜�
+     */
+    @TableField(value = "ASSIGNEE_")
+    private String assignee;
+
+    /**
+     * 寮�濮嬩簨浠�
+     */
+    @TableField(value = "START_TIME_")
+    private Date startTime;
+
+    /**
+     * 璁ら鏃堕棿
+     */
+    @TableField(value = "CLAIM_TIME_")
+    private Date claimTime;
+
+    /**
+     * 缁撴潫鏃堕棿
+     */
+    @TableField(value = "END_TIME_")
+    private Date endTime;
+
+    /**
+     * 鎸佺画鏃堕棿
+     */
+    @TableField(value = "DURATION_")
+    private Long duration;
+
+    /**
+     * 鍒犻櫎鍘熷洜
+     */
+    @TableField(value = "DELETE_REASON_")
+    private String deleteReason;
+
+    /**
+     * 浼樺厛绾�
+     */
+    @TableField(value = "PRIORITY_")
+    private Long priority;
+
+    /**
+     * 鍒版湡鏃堕棿
+     */
+    @TableField(value = "DUE_DATE_")
+    private Date dueDate;
+
+    /**
+     *
+     */
+    @TableField(value = "FORM_KEY_")
+    private String formKey;
+
+    /**
+     * 鍒嗙被
+     */
+    @TableField(value = "CATEGORY_")
+    private String category;
+
+    /**
+     * 鏈�鍚庝慨鏀规椂闂�
+     */
+    @TableField(value = "LAST_UPDATED_TIME_")
+    private Date lastUpdatedTime;
+
+    /**
+     * 绉熸埛id
+     */
+    @TableField(value = "TENANT_ID_")
+    private String tenantId;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java
new file mode 100644
index 0000000..0e26467
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/TestLeave.java
@@ -0,0 +1,58 @@
+package org.dromara.workflow.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * 璇峰亣瀵硅薄 test_leave
+ *
+ * @author may
+ * @date 2023-07-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("test_leave")
+public class TestLeave extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 璇峰亣绫诲瀷
+     */
+    private String leaveType;
+
+    /**
+     * 寮�濮嬫椂闂�
+     */
+    private Date startDate;
+
+    /**
+     * 缁撴潫鏃堕棿
+     */
+    private Date endDate;
+
+    /**
+     * 璇峰亣澶╂暟
+     */
+    private Integer leaveDays;
+
+    /**
+     * 璇峰亣鍘熷洜
+     */
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java
new file mode 100644
index 0000000..94a7cf5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java
@@ -0,0 +1,52 @@
+package org.dromara.workflow.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+
+/**
+ * 娴佺▼鍒嗙被瀵硅薄 wf_category
+ *
+ * @author may
+ * @date 2023-06-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("wf_category")
+public class WfCategory extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 鍒嗙被鍚嶇О
+     */
+    private String categoryName;
+
+    /**
+     * 鍒嗙被缂栫爜
+     */
+    private String categoryCode;
+
+    /**
+     * 鐖剁骇id
+     */
+    private Long parentId;
+
+    /**
+     * 鎺掑簭
+     */
+    private Long sortNum;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfDefinitionConfig.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfDefinitionConfig.java
new file mode 100644
index 0000000..11dcaa0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfDefinitionConfig.java
@@ -0,0 +1,56 @@
+package org.dromara.workflow.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 娴佺▼瀹氫箟閰嶇疆瀵硅薄 wf_definition_config
+ *
+ * @author may
+ * @date 2024-03-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("wf_definition_config")
+public class WfDefinitionConfig extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 琛ㄥ悕
+     */
+    private String tableName;
+
+    /**
+     * 娴佺▼瀹氫箟ID
+     */
+    private String definitionId;
+
+    /**
+     * 娴佺▼KEY
+     */
+    private String processKey;
+
+    /**
+     * 娴佺▼鐗堟湰
+     */
+    private Integer version;
+
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfFormManage.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfFormManage.java
new file mode 100644
index 0000000..47f0d7a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfFormManage.java
@@ -0,0 +1,51 @@
+package org.dromara.workflow.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 琛ㄥ崟绠$悊瀵硅薄 wf_form_manage
+ *
+ * @author may
+ * @date 2024-03-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("wf_form_manage")
+public class WfFormManage extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 琛ㄥ崟鍚嶇О
+     */
+    private String formName;
+
+    /**
+     * 琛ㄥ崟绫诲瀷
+     */
+    private String formType;
+
+    /**
+     * 璺敱鍦板潃/琛ㄥ崟ID
+     */
+    private String router;
+
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfNodeConfig.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfNodeConfig.java
new file mode 100644
index 0000000..999425f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfNodeConfig.java
@@ -0,0 +1,61 @@
+package org.dromara.workflow.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 鑺傜偣閰嶇疆瀵硅薄 wf_node_config
+ *
+ * @author may
+ * @date 2024-03-30
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("wf_node_config")
+public class WfNodeConfig extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 琛ㄥ崟id
+     */
+    private Long formId;
+
+    /**
+     * 琛ㄥ崟绫诲瀷
+     */
+    private String formType;
+
+    /**
+     * 鑺傜偣鍚嶇О
+     */
+    private String nodeName;
+
+    /**
+     * 鑺傜偣id
+     */
+    private String nodeId;
+
+    /**
+     * 娴佺▼瀹氫箟id
+     */
+    private String definitionId;
+
+    /**
+     * 鏄惁涓虹敵璇蜂汉鑺傜偣 锛�0鏄� 1鍚︼級
+     */
+    private String applyUserTask;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java
new file mode 100644
index 0000000..6f59727
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java
@@ -0,0 +1,61 @@
+package org.dromara.workflow.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.tenant.core.TenantEntity;
+
+import java.io.Serial;
+
+/**
+ * 鑺傜偣椹冲洖璁板綍 wf_task_back_node
+ *
+ * @author may
+ * @date 2024-03-13
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("wf_task_back_node")
+public class WfTaskBackNode extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 瀹炰緥id
+     */
+    private String instanceId;
+
+    /**
+     * 鑺傜偣id
+     */
+    private String nodeId;
+
+    /**
+     * 鑺傜偣鍚嶇О
+     */
+    private String nodeName;
+
+    /**
+     * 鎺掑簭
+     */
+    private Integer orderNo;
+
+    /**
+     * 鑺傜偣绫诲瀷
+     */
+    private String taskType;
+
+    /**
+     * 鍔炵悊浜�
+     */
+    private String assignee;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java
new file mode 100644
index 0000000..320ec64
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java
@@ -0,0 +1,40 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 鍔犵鍙傛暟璇锋眰
+ *
+ * @author may
+ */
+@Data
+public class AddMultiBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浠诲姟ID
+     */
+    @NotBlank(message = "浠诲姟ID涓嶈兘涓虹┖", groups = AddGroup.class)
+    private String taskId;
+
+    /**
+     * 鍔犵浜哄憳id
+     */
+    @NotEmpty(message = "鍔犵浜哄憳涓嶈兘涓虹┖", groups = AddGroup.class)
+    private List<Long> assignees;
+
+    /**
+     * 鍔犵浜哄憳鍚嶇О
+     */
+    @NotEmpty(message = "鍔犵浜哄憳涓嶈兘涓虹┖", groups = AddGroup.class)
+    private List<String> assigneeNames;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java
new file mode 100644
index 0000000..d0f4369
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java
@@ -0,0 +1,44 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 椹冲洖鍙傛暟璇锋眰
+ *
+ * @author may
+ */
+@Data
+public class BackProcessBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浠诲姟ID
+     */
+    @NotBlank(message = "浠诲姟ID涓嶈兘涓虹┖", groups = AddGroup.class)
+    private String taskId;
+
+    /**
+     * 娑堟伅绫诲瀷
+     */
+    private List<String> messageType;
+
+    /**
+     * 椹冲洖鐨勮妭鐐筰d(鐩墠鏈娇鐢紝鐩存帴椹冲洖鍒扮敵璇蜂汉)
+     */
+    @NotBlank(message = "椹冲洖鐨勮妭鐐逛笉鑳戒负绌�", groups = AddGroup.class)
+    private String targetActivityId;
+
+    /**
+     * 鍔炵悊鎰忚
+     */
+    private String message;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java
new file mode 100644
index 0000000..0623905
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java
@@ -0,0 +1,65 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.workflow.domain.vo.WfCopy;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 鍔炵悊浠诲姟璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class CompleteTaskBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浠诲姟id
+     */
+    @NotBlank(message = "浠诲姟id涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String taskId;
+
+    /**
+     * 闄勪欢id
+     */
+    private String fileId;
+
+    /**
+     * 鎶勯�佷汉鍛�
+     */
+    private List<WfCopy> wfCopyList;
+
+    /**
+     * 娑堟伅绫诲瀷
+     */
+    private List<String> messageType;
+
+    /**
+     * 鍔炵悊鎰忚
+     */
+    private String message;
+
+    /**
+     * 娴佺▼鍙橀噺
+     */
+    private Map<String, Object> variables;
+
+    public Map<String, Object> getVariables() {
+        if (variables == null) {
+            return new HashMap<>(16);
+        }
+        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
+        return variables;
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java
new file mode 100644
index 0000000..a6846a6
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java
@@ -0,0 +1,38 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 濮旀淳浠诲姟璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class DelegateBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 濮旀淳浜篿d
+     */
+    @NotBlank(message = "濮旀淳浜篿d涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String userId;
+
+    /**
+     * 濮旀淳浜哄悕绉�
+     */
+    @NotBlank(message = "濮旀淳浜哄悕绉颁笉鑳戒负绌�", groups = {AddGroup.class})
+    private String nickName;
+
+    /**
+     * 浠诲姟id
+     */
+    @NotBlank(message = "浠诲姟id涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String taskId;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java
new file mode 100644
index 0000000..e533167
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java
@@ -0,0 +1,52 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 鍑忕鍙傛暟璇锋眰
+ *
+ * @author may
+ */
+@Data
+public class DeleteMultiBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浠诲姟ID
+     */
+    @NotBlank(message = "浠诲姟ID涓嶈兘涓虹┖", groups = AddGroup.class)
+    private String taskId;
+
+    /**
+     * 鍑忕浜哄憳
+     */
+    @NotEmpty(message = "鍑忕浜哄憳涓嶈兘涓虹┖", groups = AddGroup.class)
+    private List<String> taskIds;
+
+    /**
+     * 鎵цid
+     */
+    @NotEmpty(message = "鎵цid涓嶈兘涓虹┖", groups = AddGroup.class)
+    private List<String> executionIds;
+
+    /**
+     * 浜哄憳id
+     */
+    @NotEmpty(message = "鍑忕浜哄憳id涓嶈兘涓虹┖", groups = AddGroup.class)
+    private List<Long> assigneeIds;
+
+    /**
+     * 浜哄憳鍚嶇О
+     */
+    @NotEmpty(message = "鍑忕浜哄憳涓嶈兘涓虹┖", groups = AddGroup.class)
+    private List<String> assigneeNames;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java
new file mode 100644
index 0000000..efe9acd
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java
@@ -0,0 +1,66 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.workflow.common.constant.FlowConstant;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 妯″瀷璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class ModelBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 妯″瀷id
+     */
+    @NotBlank(message = "妯″瀷ID涓嶈兘涓虹┖", groups = {EditGroup.class})
+    private String id;
+
+    /**
+     * 妯″瀷鍚嶇О
+     */
+    @NotBlank(message = "妯″瀷鍚嶇О涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String name;
+
+    /**
+     * 妯″瀷鏍囪瘑key
+     */
+    @NotBlank(message = "妯″瀷鏍囪瘑key涓嶈兘涓虹┖", groups = {AddGroup.class})
+    @Pattern(regexp = FlowConstant.MODEL_KEY_PATTERN, message = "妯″瀷鏍囪瘑key鍙兘瀛楃鎴栬�呬笅鍒掔嚎寮�澶�", groups = {AddGroup.class})
+    private String key;
+
+    /**
+     * 妯″瀷鍒嗙被
+     */
+    @NotBlank(message = "妯″瀷鍒嗙被涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String categoryCode;
+
+    /**
+     * 妯″瀷XML
+     */
+    @NotBlank(message = "妯″瀷XML涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String xml;
+
+    /**
+     * 妯″瀷SVG鍥剧墖
+     */
+    @NotBlank(message = "妯″瀷SVG涓嶈兘涓虹┖", groups = {EditGroup.class})
+    private String svg;
+
+    /**
+     * 澶囨敞
+     */
+    private String description;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java
new file mode 100644
index 0000000..2025932
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java
@@ -0,0 +1,34 @@
+package org.dromara.workflow.domain.bo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 娴佺▼瀹氫箟璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class ProcessDefinitionBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 娴佺▼瀹氫箟鍚嶇Оkey
+     */
+    private String key;
+
+    /**
+     * 娴佺▼瀹氫箟鍚嶇О
+     */
+    private String name;
+
+    /**
+     * 妯″瀷鍒嗙被
+     */
+    private String categoryCode;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInstanceBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInstanceBo.java
new file mode 100644
index 0000000..2833b3e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInstanceBo.java
@@ -0,0 +1,43 @@
+package org.dromara.workflow.domain.bo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 娴佺▼瀹炰緥璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class ProcessInstanceBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 娴佺▼鍚嶇О
+     */
+    private String name;
+
+    /**
+     * 娴佺▼key
+     */
+    private String key;
+
+    /**
+     * 浠诲姟鍙戣捣浜�
+     */
+    private String startUserId;
+
+    /**
+     * 涓氬姟id
+     */
+    private String businessKey;
+
+    /**
+     * 妯″瀷鍒嗙被
+     */
+    private String categoryCode;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInvalidBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInvalidBo.java
new file mode 100644
index 0000000..35d5652
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessInvalidBo.java
@@ -0,0 +1,31 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 娴佺▼瀹炰緥浣滃簾璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class ProcessInvalidBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 娴佺▼瀹炰緥id
+     */
+    @NotBlank(message = "娴佺▼瀹炰緥id涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String processInstanceId;
+
+    /**
+     * 浣滃簾鍘熷洜
+     */
+    private String deleteReason;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java
new file mode 100644
index 0000000..7af7935
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java
@@ -0,0 +1,49 @@
+package org.dromara.workflow.domain.bo;
+
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 鍚姩娴佺▼瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class StartProcessBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓氬姟鍞竴鍊糹d
+     */
+    @NotBlank(message = "涓氬姟ID涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String businessKey;
+
+    /**
+     * 琛ㄥ悕
+     */
+    @NotBlank(message = "琛ㄥ悕涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String tableName;
+
+    /**
+     * 娴佺▼鍙橀噺锛屽墠绔細鎻愪氦涓�涓厓绱爗'entity': {涓氬姟璇︽儏鏁版嵁瀵硅薄}}
+     */
+    private Map<String, Object> variables;
+
+    public Map<String, Object> getVariables() {
+        if (variables == null) {
+            return new HashMap<>(16);
+        }
+        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
+        return variables;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java
new file mode 100644
index 0000000..e4d99e4
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java
@@ -0,0 +1,39 @@
+package org.dromara.workflow.domain.bo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 鐢ㄦ埛鍔犵鏌ヨ
+ *
+ * @author may
+ */
+@Data
+public class SysUserMultiBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浜哄憳鍚嶇О
+     */
+    private String userName;
+
+    /**
+     * 浜哄憳鍚嶇О
+     */
+    private String nickName;
+
+    /**
+     * 閮ㄩ棬id
+     */
+    private String deptId;
+
+    /**
+     * 浠诲姟id
+     */
+    private String taskId;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java
new file mode 100644
index 0000000..3037479
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java
@@ -0,0 +1,33 @@
+package org.dromara.workflow.domain.bo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 浠诲姟璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class TaskBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浠诲姟鍚嶇О
+     */
+    private String name;
+
+    /**
+     * 娴佺▼瀹氫箟鍚嶇О
+     */
+    private String processDefinitionName;
+
+    /**
+     * 娴佺▼瀹氫箟key
+     */
+    private String processDefinitionKey;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskUrgingBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskUrgingBo.java
new file mode 100644
index 0000000..20856ef
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskUrgingBo.java
@@ -0,0 +1,34 @@
+package org.dromara.workflow.domain.bo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 浠诲姟鍌姙
+ *
+ * @author may
+ */
+@Data
+public class TaskUrgingBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 娴佺▼瀹炰緥id
+     */
+    private String processInstanceId;
+
+    /**
+     * 娑堟伅绫诲瀷
+     */
+    private List<String> messageType;
+
+    /**
+     * 鍌姙鍐呭锛堜负绌洪粯璁ょ郴缁熷唴缃俊鎭級
+     */
+    private String message;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java
new file mode 100644
index 0000000..8f2206e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java
@@ -0,0 +1,31 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 缁堟浠诲姟璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class TerminationBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浠诲姟id
+     */
+    @NotBlank(message = "浠诲姟id涓虹┖", groups = AddGroup.class)
+    private String taskId;
+
+    /**
+     * 瀹℃壒鎰忚
+     */
+    private String comment;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java
new file mode 100644
index 0000000..e71be59
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TestLeaveBo.java
@@ -0,0 +1,75 @@
+package org.dromara.workflow.domain.bo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.workflow.domain.TestLeave;
+
+import java.util.Date;
+
+/**
+ * 璇峰亣涓氬姟瀵硅薄 test_leave
+ *
+ * @author may
+ * @date 2023-07-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = TestLeave.class, reverseConvertGenerate = false)
+public class TestLeaveBo extends BaseEntity {
+
+    /**
+     * 涓婚敭
+     */
+    @NotNull(message = "涓婚敭涓嶈兘涓虹┖", groups = {EditGroup.class})
+    private Long id;
+
+    /**
+     * 璇峰亣绫诲瀷
+     */
+    @NotBlank(message = "璇峰亣绫诲瀷涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private String leaveType;
+
+    /**
+     * 寮�濮嬫椂闂�
+     */
+    @NotNull(message = "寮�濮嬫椂闂翠笉鑳戒负绌�", groups = {AddGroup.class, EditGroup.class})
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date startDate;
+
+    /**
+     * 缁撴潫鏃堕棿
+     */
+    @NotNull(message = "缁撴潫鏃堕棿涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date endDate;
+
+    /**
+     * 璇峰亣澶╂暟
+     */
+    @NotNull(message = "璇峰亣澶╂暟涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private Integer leaveDays;
+
+    /**
+     * 寮�濮嬫椂闂�
+     */
+    private Integer startLeaveDays;
+
+    /**
+     * 缁撴潫鏃堕棿
+     */
+    private Integer endLeaveDays;
+
+    /**
+     * 璇峰亣鍘熷洜
+     */
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java
new file mode 100644
index 0000000..3eb6609
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java
@@ -0,0 +1,37 @@
+package org.dromara.workflow.domain.bo;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 缁堣浆鍔炲姟璇锋眰瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class TransmitBo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浠诲姟id
+     */
+    @NotBlank(message = "浠诲姟id涓虹┖", groups = AddGroup.class)
+    private String taskId;
+
+    /**
+     * 杞姙浜篿d
+     */
+    @NotBlank(message = "杞姙浜轰笉鑳戒负绌�", groups = AddGroup.class)
+    private String userId;
+
+    /**
+     * 瀹℃壒鎰忚
+     */
+    private String comment;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java
new file mode 100644
index 0000000..69608fd
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java
@@ -0,0 +1,54 @@
+package org.dromara.workflow.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.workflow.domain.WfCategory;
+
+/**
+ * 娴佺▼鍒嗙被涓氬姟瀵硅薄 wf_category
+ *
+ * @author may
+ * @date 2023-06-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = WfCategory.class, reverseConvertGenerate = false)
+public class WfCategoryBo extends BaseEntity {
+
+    /**
+     * 涓婚敭
+     */
+    @NotNull(message = "涓婚敭涓嶈兘涓虹┖", groups = {EditGroup.class})
+    private Long id;
+
+    /**
+     * 鍒嗙被鍚嶇О
+     */
+    @NotBlank(message = "鍒嗙被鍚嶇О涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private String categoryName;
+
+    /**
+     * 鍒嗙被缂栫爜
+     */
+    @NotBlank(message = "鍒嗙被缂栫爜涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private String categoryCode;
+
+    /**
+     * 鐖剁骇id
+     */
+    @NotNull(message = "鐖剁骇id涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private Long parentId;
+
+    /**
+     * 鎺掑簭
+     */
+    private Long sortNum;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfDefinitionConfigBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfDefinitionConfigBo.java
new file mode 100644
index 0000000..fac1770
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfDefinitionConfigBo.java
@@ -0,0 +1,59 @@
+package org.dromara.workflow.domain.bo;
+
+import org.dromara.workflow.domain.WfDefinitionConfig;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 娴佺▼瀹氫箟閰嶇疆涓氬姟瀵硅薄 wf_form_definition
+ *
+ * @author may
+ * @date 2024-03-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = WfDefinitionConfig.class, reverseConvertGenerate = false)
+public class WfDefinitionConfigBo extends BaseEntity {
+
+    /**
+     * 涓婚敭
+     */
+    @NotNull(message = "涓婚敭涓嶈兘涓虹┖", groups = {EditGroup.class})
+    private Long id;
+
+    /**
+     * 琛ㄥ悕
+     */
+    @NotBlank(message = "琛ㄥ悕涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String tableName;
+
+    /**
+     * 娴佺▼瀹氫箟ID
+     */
+    @NotBlank(message = "娴佺▼瀹氫箟ID涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String definitionId;
+
+    /**
+     * 娴佺▼KEY
+     */
+    @NotBlank(message = "娴佺▼KEY涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private String processKey;
+
+    /**
+     * 娴佺▼鐗堟湰
+     */
+    @NotNull(message = "娴佺▼鐗堟湰涓嶈兘涓虹┖", groups = {AddGroup.class})
+    private Integer version;
+
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfFormManageBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfFormManageBo.java
new file mode 100644
index 0000000..8afc286
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfFormManageBo.java
@@ -0,0 +1,53 @@
+package org.dromara.workflow.domain.bo;
+
+import org.dromara.workflow.domain.WfFormManage;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 琛ㄥ崟绠$悊涓氬姟瀵硅薄 wf_form_manage
+ *
+ * @author may
+ * @date 2024-03-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = WfFormManage.class, reverseConvertGenerate = false)
+public class WfFormManageBo extends BaseEntity {
+
+    /**
+     * 涓婚敭
+     */
+    @NotNull(message = "涓婚敭涓嶈兘涓虹┖", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 琛ㄥ崟鍚嶇О
+     */
+    @NotBlank(message = "琛ㄥ崟鍚嶇О涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    private String formName;
+
+    /**
+     * 琛ㄥ崟绫诲瀷
+     */
+    @NotBlank(message = "琛ㄥ崟绫诲瀷涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    private String formType;
+    /**
+     * 璺敱鍦板潃/琛ㄥ崟ID
+     */
+    @NotBlank(message = "璺敱鍦板潃/琛ㄥ崟ID涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class })
+    private String router;
+
+
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfNodeConfigBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfNodeConfigBo.java
new file mode 100644
index 0000000..de518d3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfNodeConfigBo.java
@@ -0,0 +1,63 @@
+package org.dromara.workflow.domain.bo;
+
+import org.dromara.workflow.domain.WfNodeConfig;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 鑺傜偣閰嶇疆涓氬姟瀵硅薄 wf_node_config
+ *
+ * @author may
+ * @date 2024-03-30
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = WfNodeConfig.class, reverseConvertGenerate = false)
+public class WfNodeConfigBo extends BaseEntity {
+
+    /**
+     * 涓婚敭
+     */
+    @NotNull(message = "涓婚敭涓嶈兘涓虹┖", groups = {EditGroup.class})
+    private Long id;
+
+    /**
+     * 琛ㄥ崟id
+     */
+    private Long formId;
+
+    /**
+     * 琛ㄥ崟绫诲瀷
+     */
+    private String formType;
+
+    /**
+     * 鑺傜偣鍚嶇О
+     */
+    @NotBlank(message = "鑺傜偣鍚嶇О涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private String nodeName;
+
+    /**
+     * 鑺傜偣id
+     */
+    @NotBlank(message = "鑺傜偣id涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private String nodeId;
+
+    /**
+     * 娴佺▼瀹氫箟id
+     */
+    @NotBlank(message = "娴佺▼瀹氫箟id涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private String definitionId;
+
+    /**
+     * 鏄惁涓虹敵璇蜂汉鑺傜偣 锛�0鏄� 1鍚︼級
+     */
+    @NotBlank(message = "鏄惁涓虹敵璇蜂汉鑺傜偣涓嶈兘涓虹┖", groups = {AddGroup.class, EditGroup.class})
+    private String applyUserTask;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java
new file mode 100644
index 0000000..31b9722
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java
@@ -0,0 +1,89 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.flowable.engine.task.Attachment;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 娴佺▼瀹℃壒璁板綍瑙嗗浘
+ *
+ * @author may
+ */
+@Data
+public class ActHistoryInfoVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+    /**
+     * 浠诲姟id
+     */
+    private String id;
+    /**
+     * 鑺傜偣id
+     */
+    private String taskDefinitionKey;
+    /**
+     * 浠诲姟鍚嶇О
+     */
+    private String name;
+    /**
+     * 娴佺▼瀹炰緥id
+     */
+    private String processInstanceId;
+    /**
+     * 寮�濮嬫椂闂�
+     */
+    private Date startTime;
+    /**
+     * 缁撴潫鏃堕棿
+     */
+    private Date endTime;
+    /**
+     * 杩愯鏃堕暱
+     */
+    private String runDuration;
+    /**
+     * 鐘舵��
+     */
+    private String status;
+    /**
+     * 鐘舵��
+     */
+    private String statusName;
+    /**
+     * 鍔炵悊浜篿d
+     */
+    private String assignee;
+
+    /**
+     * 鍔炵悊浜哄悕绉�
+     */
+    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assignee")
+    private String nickName;
+
+    /**
+     * 鍔炵悊浜篿d
+     */
+    private String owner;
+
+    /**
+     * 瀹℃壒淇℃伅id
+     */
+    private String commentId;
+
+    /**
+     * 瀹℃壒淇℃伅
+     */
+    private String comment;
+
+    /**
+     * 瀹℃壒闄勪欢
+     */
+    private List<Attachment> attachmentList;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java
new file mode 100644
index 0000000..7636131
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java
@@ -0,0 +1,47 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 鑺傜偣鍥惧舰淇℃伅
+ *
+ * @author may
+ */
+@Data
+public class GraphicInfoVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+    /**
+     * x鍧愭爣
+     */
+    private double x;
+
+    /**
+     * y鍧愭爣
+     */
+    private double y;
+
+    /**
+     * 鑺傜偣楂樺害
+     */
+    private double height;
+
+    /**
+     * 鑺傜偣瀹藉害
+     */
+    private double width;
+
+    /**
+     * 鑺傜偣id
+     */
+    private String nodeId;
+
+    /**
+     * 鑺傜偣鍚嶇О
+     */
+    private String nodeName;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java
new file mode 100644
index 0000000..b2ce811
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java
@@ -0,0 +1,48 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 妯″瀷瑙嗗浘瀵硅薄
+ *
+ * @author may
+ */
+@Data
+public class ModelVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 妯″瀷id
+     */
+    private String id;
+
+    /**
+     * 妯″瀷鍚嶇О
+     */
+    private String name;
+
+    /**
+     * 妯″瀷鏍囪瘑key
+     */
+    private String key;
+
+    /**
+     * 妯″瀷鍒嗙被
+     */
+    private String categoryCode;
+
+    /**
+     * 妯″瀷XML
+     */
+    private String xml;
+
+    /**
+     * 澶囨敞
+     */
+    private String description;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java
new file mode 100644
index 0000000..b998396
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java
@@ -0,0 +1,33 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 澶氬疄渚嬩俊鎭�
+ *
+ * @author may
+ */
+@Data
+public class MultiInstanceVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浼氱绫诲瀷锛堜覆琛岋紝骞惰锛�
+     */
+    private Object type;
+
+    /**
+     * 浼氱浜哄憳KEY
+     */
+    private String assignee;
+
+    /**
+     * 浼氱浜哄憳闆嗗悎KEY
+     */
+    private String assigneeList;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java
new file mode 100644
index 0000000..c5876f6
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java
@@ -0,0 +1,43 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 鍙備笌鑰�
+ *
+ * @author may
+ */
+@Data
+public class ParticipantVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 缁刬d锛堣鑹瞚d锛�
+     */
+    private List<Long> groupIds;
+
+    /**
+     * 鍊欓�変汉id锛堢敤鎴穒d锛� 褰撶粍id涓嶄负绌烘椂锛屽皢缁勫唴浜哄憳鏌ュ嚭鏀惧叆candidate
+     */
+    private List<Long> candidate;
+
+    /**
+     * 鍊欓�変汉鍚嶇О锛堢敤鎴峰悕绉帮級 褰撶粍id涓嶄负绌烘椂锛屽皢缁勫唴浜哄憳鏌ュ嚭鏀惧叆candidateName
+     */
+    private List<String> candidateName;
+
+    /**
+     * 鏄惁璁ら鏍囪瘑
+     * 褰撲负绌烘椂榛樿褰撳墠浠诲姟涓嶉渶瑕佽棰�
+     * 褰撲负true鏃跺綋鍓嶄换鍔¤鏄庝负鍊欓�夋ā寮忓苟涓旀湁浜哄凡缁忚棰嗕簡浠诲姟鍙互褰掕繕锛�
+     * 褰撲负false鏃跺綋鍓嶄换鍔¤鏄庝负鍊欓�夋ā寮忚浠诲姟鏈棰嗭紝
+     */
+    private Boolean claim;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java
new file mode 100644
index 0000000..034adbb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java
@@ -0,0 +1,70 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 娴佺▼瀹氫箟瑙嗗浘
+ *
+ * @author may
+ */
+@Data
+public class ProcessDefinitionVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 娴佺▼瀹氫箟id
+     */
+    private String id;
+
+    /**
+     * 娴佺▼瀹氫箟鍚嶇О
+     */
+    private String name;
+
+    /**
+     * 娴佺▼瀹氫箟鏍囪瘑key
+     */
+    private String key;
+
+    /**
+     * 娴佺▼瀹氫箟鐗堟湰
+     */
+    private int version;
+
+    /**
+     * 娴佺▼瀹氫箟鎸傝捣鎴栨縺娲� 1婵�娲� 2鎸傝捣
+     */
+    private int suspensionState;
+
+    /**
+     * 娴佺▼xml鍚嶇О
+     */
+    private String resourceName;
+
+    /**
+     * 娴佺▼鍥剧墖鍚嶇О
+     */
+    private String diagramResourceName;
+
+    /**
+     * 娴佺▼閮ㄧ讲id
+     */
+    private String deploymentId;
+
+    /**
+     * 娴佺▼閮ㄧ讲鏃堕棿
+     */
+    private Date deploymentTime;
+
+    /**
+     * 娴佺▼瀹氫箟閰嶇疆
+     */
+    private WfDefinitionConfigVo wfDefinitionConfigVo;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java
new file mode 100644
index 0000000..ab3e7a1
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java
@@ -0,0 +1,100 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 娴佺▼瀹炰緥瑙嗗浘
+ *
+ * @author may
+ */
+@Data
+public class ProcessInstanceVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 娴佺▼瀹炰緥id
+     */
+    private String id;
+
+    /**
+     * 娴佺▼瀹氫箟id
+     */
+    private String processDefinitionId;
+
+    /**
+     * 娴佺▼瀹氫箟鍚嶇О
+     */
+    private String processDefinitionName;
+
+    /**
+     * 娴佺▼瀹氫箟key
+     */
+    private String processDefinitionKey;
+
+    /**
+     * 娴佺▼瀹氫箟鐗堟湰
+     */
+    private Integer processDefinitionVersion;
+
+    /**
+     * 閮ㄧ讲id
+     */
+    private String deploymentId;
+
+    /**
+     * 涓氬姟id
+     */
+    private String businessKey;
+
+    /**
+     * 鏄惁鎸傝捣
+     */
+    private Boolean isSuspended;
+
+    /**
+     * 绉熸埛id
+     */
+    private String tenantId;
+
+    /**
+     * 鍚姩鏃堕棿
+     */
+    private Date startTime;
+
+    /**
+     * 缁撴潫鏃堕棿
+     */
+    private Date endTime;
+
+    /**
+     * 鍚姩浜篿d
+     */
+    private String startUserId;
+
+    /**
+     * 娴佺▼鐘舵��
+     */
+    private String businessStatus;
+
+    /**
+     * 娴佺▼鐘舵��
+     */
+    private String businessStatusName;
+
+    /**
+     * 寰呭姙浠诲姟闆嗗悎
+     */
+    private List<TaskVo> taskVoList;
+
+    /**
+     * 鑺傜偣閰嶇疆
+     */
+    private WfNodeConfigVo wfNodeConfigVo;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java
new file mode 100644
index 0000000..466e776
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java
@@ -0,0 +1,173 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 浠诲姟瑙嗗浘
+ *
+ * @author may
+ */
+@Data
+public class TaskVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 浠诲姟id
+     */
+    private String id;
+
+    /**
+     * 浠诲姟鍚嶇О
+     */
+    private String name;
+
+    /**
+     * 鎻忚堪
+     */
+    private String description;
+
+    /**
+     * 浼樺厛绾�
+     */
+    private Integer priority;
+
+    /**
+     * 璐熻矗姝や换鍔$殑浜哄憳鐨勭敤鎴穒d
+     */
+    private String owner;
+
+    /**
+     * 鍔炵悊浜篿d
+     */
+    private Long assignee;
+
+    /**
+     * 鍔炵悊浜�
+     */
+    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assignee")
+    private String assigneeName;
+
+
+    /**
+     * 娴佺▼瀹炰緥id
+     */
+    private String processInstanceId;
+
+    /**
+     * 鎵цid
+     */
+    private String executionId;
+
+    /**
+     * 鏃犵敤
+     */
+    private String taskDefinitionId;
+
+    /**
+     * 娴佺▼瀹氫箟id
+     */
+    private String processDefinitionId;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    private Date createTime;
+
+    /**
+     * 宸插姙浠诲姟-鍒涘缓鏃堕棿
+     */
+    private Date startTime;
+
+    /**
+     * 缁撴潫鏃堕棿
+     */
+    private Date endTime;
+
+    /**
+     * 鑺傜偣id
+     */
+    private String taskDefinitionKey;
+
+    /**
+     * 浠诲姟鎴鏃ユ湡
+     */
+    private Date dueDate;
+
+    /**
+     * 娴佺▼绫诲埆
+     */
+    private String category;
+
+    /**
+     * 鐖剁骇浠诲姟id
+     */
+    private String parentTaskId;
+
+    /**
+     * 绉熸埛id
+     */
+    private String tenantId;
+
+    /**
+     * 璁ら鏃堕棿
+     */
+    private Date claimTime;
+
+    /**
+     * 娴佺▼鐘舵��
+     */
+    private String businessStatus;
+
+    /**
+     * 娴佺▼鐘舵��
+     */
+    private String businessStatusName;
+
+    /**
+     * 娴佺▼瀹氫箟鍚嶇О
+     */
+    private String processDefinitionName;
+
+    /**
+     * 娴佺▼瀹氫箟key
+     */
+    private String processDefinitionKey;
+
+    /**
+     * 娴佺▼瀹氫箟鐗堟湰
+     */
+    private Integer processDefinitionVersion;
+
+    /**
+     * 鍙備笌鑰�
+     */
+    private ParticipantVo participantVo;
+
+    /**
+     * 鏄惁浼氱
+     */
+    private Boolean multiInstance;
+
+    /**
+     * 涓氬姟id
+     */
+    private String businessKey;
+
+    /**
+     * 娴佺▼瀹氫箟閰嶇疆
+     */
+    private WfDefinitionConfigVo wfDefinitionConfigVo;
+
+    /**
+     * 鑺傜偣閰嶇疆
+     */
+    private WfNodeConfigVo wfNodeConfigVo;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java
new file mode 100644
index 0000000..c62a356
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TestLeaveVo.java
@@ -0,0 +1,70 @@
+package org.dromara.workflow.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.workflow.domain.TestLeave;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 璇峰亣瑙嗗浘瀵硅薄 test_leave
+ *
+ * @author may
+ * @date 2023-07-21
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = TestLeave.class)
+public class TestLeaveVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @ExcelProperty(value = "涓婚敭")
+    private Long id;
+
+    /**
+     * 璇峰亣绫诲瀷
+     */
+    @ExcelProperty(value = "璇峰亣绫诲瀷")
+    private String leaveType;
+
+    /**
+     * 寮�濮嬫椂闂�
+     */
+    @ExcelProperty(value = "寮�濮嬫椂闂�")
+    private Date startDate;
+
+    /**
+     * 缁撴潫鏃堕棿
+     */
+    @ExcelProperty(value = "缁撴潫鏃堕棿")
+    private Date endDate;
+
+    /**
+     * 璇峰亣澶╂暟
+     */
+    @ExcelProperty(value = "璇峰亣澶╂暟")
+    private Integer leaveDays;
+
+    /**
+     * 澶囨敞
+     */
+    @ExcelProperty(value = "璇峰亣鍘熷洜")
+    private String remark;
+
+    /**
+     * 娴佺▼瀹炰緥瀵硅薄
+     */
+    private ProcessInstanceVo processInstanceVo;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/VariableVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/VariableVo.java
new file mode 100644
index 0000000..6a26c82
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/VariableVo.java
@@ -0,0 +1,28 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 娴佺▼鍙橀噺
+ *
+ * @author may
+ */
+@Data
+public class VariableVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 鍙橀噺key
+     */
+    private String key;
+
+    /**
+     * 鍙橀噺鍊�
+     */
+    private String value;
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java
new file mode 100644
index 0000000..362f646
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java
@@ -0,0 +1,58 @@
+package org.dromara.workflow.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.workflow.domain.WfCategory;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 娴佺▼鍒嗙被瑙嗗浘瀵硅薄 wf_category
+ *
+ * @author may
+ * @date 2023-06-27
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = WfCategory.class)
+public class WfCategoryVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @ExcelProperty(value = "涓婚敭")
+    private Long id;
+
+    /**
+     * 鍒嗙被鍚嶇О
+     */
+    @ExcelProperty(value = "鍒嗙被鍚嶇О")
+    private String categoryName;
+
+    /**
+     * 鍒嗙被缂栫爜
+     */
+    @ExcelProperty(value = "鍒嗙被缂栫爜")
+    private String categoryCode;
+
+    /**
+     * 鐖剁骇id
+     */
+    @ExcelProperty(value = "鐖剁骇id")
+    private Long parentId;
+
+    /**
+     * 鎺掑簭
+     */
+    @ExcelProperty(value = "鎺掑簭")
+    private Long sortNum;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java
new file mode 100644
index 0000000..88a5a21
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java
@@ -0,0 +1,29 @@
+package org.dromara.workflow.domain.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 鎶勯��
+ *
+ * @author may
+ */
+@Data
+public class WfCopy implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 鐢ㄦ埛id
+     */
+    private Long userId;
+
+    /**
+     * 鐢ㄦ埛鍚嶇О
+     */
+    private String userName;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfDefinitionConfigVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfDefinitionConfigVo.java
new file mode 100644
index 0000000..9c7b0d7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfDefinitionConfigVo.java
@@ -0,0 +1,70 @@
+package org.dromara.workflow.domain.vo;
+
+import org.dromara.workflow.domain.WfDefinitionConfig;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 娴佺▼瀹氫箟閰嶇疆瑙嗗浘瀵硅薄 wf_definition_config
+ *
+ * @author may
+ * @date 2024-03-18
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = WfDefinitionConfig.class)
+public class WfDefinitionConfigVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @ExcelProperty(value = "涓婚敭")
+    private Long id;
+
+    /**
+     * 琛ㄥ悕
+     */
+    @ExcelProperty(value = "琛ㄥ悕")
+    private String tableName;
+
+    /**
+     * 娴佺▼瀹氫箟ID
+     */
+    @ExcelProperty(value = "娴佺▼瀹氫箟ID")
+    private String definitionId;
+
+    /**
+     * 娴佺▼KEY
+     */
+    @ExcelProperty(value = "娴佺▼KEY")
+    private String processKey;
+
+
+    /**
+     * 娴佺▼鐗堟湰
+     */
+    @ExcelProperty(value = "娴佺▼鐗堟湰")
+    private Integer version;
+
+    /**
+     * 澶囨敞
+     */
+    @ExcelProperty(value = "澶囨敞")
+    private String remark;
+
+    /**
+     * 琛ㄥ崟绠$悊
+     */
+    private WfFormManageVo wfFormManageVo;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfFormManageVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfFormManageVo.java
new file mode 100644
index 0000000..302df23
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfFormManageVo.java
@@ -0,0 +1,63 @@
+package org.dromara.workflow.domain.vo;
+
+import org.dromara.workflow.domain.WfFormManage;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 琛ㄥ崟绠$悊瑙嗗浘瀵硅薄 wf_form_manage
+ *
+ * @author may
+ * @date 2024-03-29
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = WfFormManage.class)
+public class WfFormManageVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @ExcelProperty(value = "涓婚敭")
+    private Long id;
+
+    /**
+     * 琛ㄥ崟鍚嶇О
+     */
+    @ExcelProperty(value = "琛ㄥ崟鍚嶇О")
+    private String formName;
+
+    /**
+     * 琛ㄥ崟绫诲瀷
+     */
+    @ExcelProperty(value = "琛ㄥ崟绫诲瀷")
+    private String formType;
+
+    /**
+     * 琛ㄥ崟绫诲瀷鍚嶇О
+     */
+    private String formTypeName;
+
+    /**
+     * 璺敱鍦板潃/琛ㄥ崟ID
+     */
+    @ExcelProperty(value = "璺敱鍦板潃/琛ㄥ崟ID")
+    private String router;
+
+    /**
+     * 澶囨敞
+     */
+    @ExcelProperty(value = "澶囨敞")
+    private String remark;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfNodeConfigVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfNodeConfigVo.java
new file mode 100644
index 0000000..89e9d9b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfNodeConfigVo.java
@@ -0,0 +1,75 @@
+package org.dromara.workflow.domain.vo;
+
+import org.dromara.workflow.domain.WfNodeConfig;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 鑺傜偣閰嶇疆瑙嗗浘瀵硅薄 wf_node_config
+ *
+ * @author may
+ * @date 2024-03-30
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = WfNodeConfig.class)
+public class WfNodeConfigVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @ExcelProperty(value = "涓婚敭")
+    private Long id;
+
+    /**
+     * 琛ㄥ崟id
+     */
+    @ExcelProperty(value = "琛ㄥ崟id")
+    private Long formId;
+
+    /**
+     * 琛ㄥ崟绫诲瀷
+     */
+    @ExcelProperty(value = "琛ㄥ崟绫诲瀷")
+    private String formType;
+
+    /**
+     * 鑺傜偣鍚嶇О
+     */
+    @ExcelProperty(value = "鑺傜偣鍚嶇О")
+    private String nodeName;
+
+    /**
+     * 鑺傜偣id
+     */
+    @ExcelProperty(value = "鑺傜偣id")
+    private String nodeId;
+
+    /**
+     * 娴佺▼瀹氫箟id
+     */
+    @ExcelProperty(value = "娴佺▼瀹氫箟id")
+    private String definitionId;
+
+    /**
+     * 鏄惁涓虹敵璇蜂汉鑺傜偣 锛�0鏄� 1鍚︼級
+     */
+    @ExcelProperty(value = "鏄惁涓虹敵璇蜂汉鑺傜偣 锛�0鏄� 1鍚︼級")
+    private String applyUserTask;
+
+    /**
+     * 琛ㄥ崟绠$悊
+     */
+    private WfFormManageVo wfFormManageVo;
+
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java
new file mode 100644
index 0000000..39fd9d3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java
@@ -0,0 +1,108 @@
+package org.dromara.workflow.flowable;
+
+import org.flowable.bpmn.model.AssociationDirection;
+import org.flowable.image.impl.DefaultProcessDiagramCanvas;
+
+import java.awt.*;
+import java.awt.geom.Line2D;
+import java.awt.geom.RoundRectangle2D;
+
+public class CustomDefaultProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
+    //璁剧疆楂樹寒绾跨殑棰滆壊  杩欓噷鎴戣缃垚缁胯壊
+    protected static Color HIGHLIGHT_SEQUENCEFLOW_COLOR = Color.GREEN;
+
+    public CustomDefaultProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
+        super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
+    }
+
+    /**
+     * 鐢荤嚎棰滆壊璁剧疆
+     */
+    public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType,
+                               AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {
+
+        Paint originalPaint = g.getPaint();
+        Stroke originalStroke = g.getStroke();
+
+        g.setPaint(CONNECTION_COLOR);
+        if (connectionType.equals("association")) {
+            g.setStroke(ASSOCIATION_STROKE);
+        } else if (highLighted) {
+            //璁剧疆绾跨殑棰滆壊
+            g.setPaint(HIGHLIGHT_SEQUENCEFLOW_COLOR);
+            g.setStroke(HIGHLIGHT_FLOW_STROKE);
+        }
+
+        for (int i = 1; i < xPoints.length; i++) {
+            Integer sourceX = xPoints[i - 1];
+            Integer sourceY = yPoints[i - 1];
+            Integer targetX = xPoints[i];
+            Integer targetY = yPoints[i];
+            Line2D.Double line = new Line2D.Double(sourceX, sourceY, targetX, targetY);
+            g.draw(line);
+        }
+
+        if (isDefault) {
+            Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
+            drawDefaultSequenceFlowIndicator(line, scaleFactor);
+        }
+
+        if (conditional) {
+            Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
+            drawConditionalSequenceFlowIndicator(line, scaleFactor);
+        }
+
+        if (associationDirection == AssociationDirection.ONE || associationDirection == AssociationDirection.BOTH) {
+            Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2], yPoints[xPoints.length - 2], xPoints[xPoints.length - 1], yPoints[xPoints.length - 1]);
+            drawArrowHead(line, scaleFactor);
+        }
+        if (associationDirection == AssociationDirection.BOTH) {
+            Line2D.Double line = new Line2D.Double(xPoints[1], yPoints[1], xPoints[0], yPoints[0]);
+            drawArrowHead(line, scaleFactor);
+        }
+        g.setPaint(originalPaint);
+        g.setStroke(originalStroke);
+    }
+
+    /**
+     * 楂樹寒鑺傜偣璁剧疆
+     */
+    public void drawHighLight(int x, int y, int width, int height) {
+        Paint originalPaint = g.getPaint();
+        Stroke originalStroke = g.getStroke();
+        //璁剧疆楂樹寒鑺傜偣鐨勯鑹�
+        g.setPaint(HIGHLIGHT_COLOR);
+        g.setStroke(THICK_TASK_BORDER_STROKE);
+
+        RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
+        g.draw(rect);
+
+        g.setPaint(originalPaint);
+        g.setStroke(originalStroke);
+    }
+
+    /**
+     * @description: 楂樹寒鑺傜偣绾㈣壊
+     * @param: x
+     * @param: y
+     * @param: width
+     * @param: height
+     * @return: void
+     * @author: gssong
+     * @date: 2022/4/12
+     */
+    public void drawHighLightRed(int x, int y, int width, int height) {
+        Paint originalPaint = g.getPaint();
+        Stroke originalStroke = g.getStroke();
+        //璁剧疆楂樹寒鑺傜偣鐨勯鑹�
+        g.setPaint(Color.green);
+        g.setStroke(THICK_TASK_BORDER_STROKE);
+
+        RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
+        g.draw(rect);
+
+        g.setPaint(originalPaint);
+        g.setStroke(originalStroke);
+    }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java
new file mode 100644
index 0000000..e4793a2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java
@@ -0,0 +1,1120 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.workflow.flowable;
+
+import org.flowable.bpmn.model.Event;
+import org.flowable.bpmn.model.Process;
+import org.flowable.bpmn.model.*;
+import org.flowable.image.ProcessDiagramGenerator;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+import java.util.List;
+import java.util.*;
+
+/**
+ * Class to generate an image based the diagram interchange information in a BPMN 2.0 process.
+ *
+ * @author Joram Barrez
+ * @author Tijs Rademakers
+ * @author Zheng Ji
+ */
+public class CustomDefaultProcessDiagramGenerator implements ProcessDiagramGenerator {
+
+    protected Map<Class<? extends BaseElement>, ActivityDrawInstruction> activityDrawInstructions = new HashMap<>();
+    protected Map<Class<? extends BaseElement>, ArtifactDrawInstruction> artifactDrawInstructions = new HashMap<>();
+
+    public CustomDefaultProcessDiagramGenerator() {
+        this(1.0);
+    }
+
+    // The instructions on how to draw a certain construct is
+    // created statically and stored in a map for performance.
+    public CustomDefaultProcessDiagramGenerator(final double scaleFactor) {
+        // start event
+        activityDrawInstructions.put(StartEvent.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                StartEvent startEvent = (StartEvent) flowNode;
+                if (startEvent.getEventDefinitions() != null && !startEvent.getEventDefinitions().isEmpty()) {
+                    EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);
+                    if (eventDefinition instanceof TimerEventDefinition) {
+                        processDiagramCanvas.drawTimerStartEvent(graphicInfo, scaleFactor);
+                    } else if (eventDefinition instanceof ErrorEventDefinition) {
+                        processDiagramCanvas.drawErrorStartEvent(graphicInfo, scaleFactor);
+                    } else if (eventDefinition instanceof EscalationEventDefinition) {
+                        processDiagramCanvas.drawEscalationStartEvent(graphicInfo, scaleFactor);
+                    } else if (eventDefinition instanceof ConditionalEventDefinition) {
+                        processDiagramCanvas.drawConditionalStartEvent(graphicInfo, scaleFactor);
+                    } else if (eventDefinition instanceof SignalEventDefinition) {
+                        processDiagramCanvas.drawSignalStartEvent(graphicInfo, scaleFactor);
+                    } else if (eventDefinition instanceof MessageEventDefinition) {
+                        processDiagramCanvas.drawMessageStartEvent(graphicInfo, scaleFactor);
+                    } else {
+                        processDiagramCanvas.drawNoneStartEvent(graphicInfo);
+                    }
+                } else {
+                    List<ExtensionElement> eventTypeElements = startEvent.getExtensionElements().get("eventType");
+                    if (eventTypeElements != null && eventTypeElements.size() > 0) {
+                        processDiagramCanvas.drawEventRegistryStartEvent(graphicInfo, scaleFactor);
+
+                    } else {
+                        processDiagramCanvas.drawNoneStartEvent(graphicInfo);
+                    }
+                }
+            }
+        });
+
+        // signal catch
+        activityDrawInstructions.put(IntermediateCatchEvent.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent) flowNode;
+                if (intermediateCatchEvent.getEventDefinitions() != null && !intermediateCatchEvent.getEventDefinitions().isEmpty()) {
+
+                    if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
+                        processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
+                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) {
+                        processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
+                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) {
+                        processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
+                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof ConditionalEventDefinition) {
+                        processDiagramCanvas.drawCatchingConditionalEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
+                    }
+                }
+            }
+        });
+
+        // signal throw
+        activityDrawInstructions.put(ThrowEvent.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                ThrowEvent throwEvent = (ThrowEvent) flowNode;
+                if (throwEvent.getEventDefinitions() != null && !throwEvent.getEventDefinitions().isEmpty()) {
+                    if (throwEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
+                        processDiagramCanvas.drawThrowingSignalEvent(graphicInfo, scaleFactor);
+                    } else if (throwEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) {
+                        processDiagramCanvas.drawThrowingEscalationEvent(graphicInfo, scaleFactor);
+                    } else if (throwEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) {
+                        processDiagramCanvas.drawThrowingCompensateEvent(graphicInfo, scaleFactor);
+                    } else {
+                        processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);
+                    }
+                } else {
+                    processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);
+                }
+            }
+        });
+
+        // end event
+        activityDrawInstructions.put(EndEvent.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                EndEvent endEvent = (EndEvent) flowNode;
+                if (endEvent.getEventDefinitions() != null && !endEvent.getEventDefinitions().isEmpty()) {
+                    if (endEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {
+                        processDiagramCanvas.drawErrorEndEvent(flowNode.getName(), graphicInfo, scaleFactor);
+                    } else if (endEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) {
+                        processDiagramCanvas.drawEscalationEndEvent(flowNode.getName(), graphicInfo, scaleFactor);
+                    } else {
+                        processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);
+                    }
+                } else {
+                    processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);
+                }
+            }
+        });
+
+        // task
+        activityDrawInstructions.put(Task.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // user task
+        activityDrawInstructions.put(UserTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawUserTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // script task
+        activityDrawInstructions.put(ScriptTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawScriptTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // service task
+        activityDrawInstructions.put(ServiceTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                ServiceTask serviceTask = (ServiceTask) flowNode;
+                if ("camel".equalsIgnoreCase(serviceTask.getType())) {
+                    processDiagramCanvas.drawCamelTask(serviceTask.getName(), graphicInfo, scaleFactor);
+                }else if (ServiceTask.HTTP_TASK.equalsIgnoreCase(serviceTask.getType())) {
+                    processDiagramCanvas.drawHttpTask(serviceTask.getName(), graphicInfo, scaleFactor);
+                } else if (ServiceTask.DMN_TASK.equalsIgnoreCase(serviceTask.getType())) {
+                    processDiagramCanvas.drawDMNTask(serviceTask.getName(), graphicInfo, scaleFactor);
+                } else if (ServiceTask.SHELL_TASK.equalsIgnoreCase(serviceTask.getType())) {
+                    processDiagramCanvas.drawShellTask(serviceTask.getName(), graphicInfo, scaleFactor);
+                } else {
+                    processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor);
+                }
+            }
+        });
+
+        // http service task
+        activityDrawInstructions.put(HttpServiceTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawHttpTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // receive task
+        activityDrawInstructions.put(ReceiveTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawReceiveTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // send task
+        activityDrawInstructions.put(SendTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawSendTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // manual task
+        activityDrawInstructions.put(ManualTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawManualTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // send event service task
+        activityDrawInstructions.put(SendEventServiceTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawSendEventServiceTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // external worker service task
+        activityDrawInstructions.put(ExternalWorkerServiceTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                ServiceTask serviceTask = (ServiceTask) flowNode;
+                processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // case service task
+        activityDrawInstructions.put(CaseServiceTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawCaseServiceTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // businessRuleTask task
+        activityDrawInstructions.put(BusinessRuleTask.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawBusinessRuleTask(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // exclusive gateway
+        activityDrawInstructions.put(ExclusiveGateway.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawExclusiveGateway(graphicInfo, scaleFactor);
+            }
+        });
+
+        // inclusive gateway
+        activityDrawInstructions.put(InclusiveGateway.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawInclusiveGateway(graphicInfo, scaleFactor);
+            }
+        });
+
+        // parallel gateway
+        activityDrawInstructions.put(ParallelGateway.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawParallelGateway(graphicInfo, scaleFactor);
+            }
+        });
+
+        // event based gateway
+        activityDrawInstructions.put(EventGateway.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawEventBasedGateway(graphicInfo, scaleFactor);
+            }
+        });
+
+        // Boundary timer
+        activityDrawInstructions.put(BoundaryEvent.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                BoundaryEvent boundaryEvent = (BoundaryEvent) flowNode;
+                if (boundaryEvent.getEventDefinitions() != null && !boundaryEvent.getEventDefinitions().isEmpty()) {
+                    EventDefinition eventDefinition = boundaryEvent.getEventDefinitions().get(0);
+                    if (eventDefinition instanceof TimerEventDefinition) {
+                        processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+                    } else if (eventDefinition instanceof ConditionalEventDefinition) {
+                        processDiagramCanvas.drawCatchingConditionalEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+                    } else if (eventDefinition instanceof ErrorEventDefinition) {
+                        processDiagramCanvas.drawCatchingErrorEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+                    } else if (eventDefinition instanceof EscalationEventDefinition) {
+                        processDiagramCanvas.drawCatchingEscalationEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+                    } else if (eventDefinition instanceof SignalEventDefinition) {
+                        processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+                    } else if (eventDefinition instanceof MessageEventDefinition) {
+                        processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+                    } else if (eventDefinition instanceof CompensateEventDefinition) {
+                        processDiagramCanvas.drawCatchingCompensateEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+                    }
+
+                } else {
+                    List<ExtensionElement> eventTypeElements = boundaryEvent.getExtensionElements().get("eventType");
+                    if (eventTypeElements != null && eventTypeElements.size() > 0) {
+                        processDiagramCanvas.drawCatchingEventRegistryEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+                    }
+                }
+            }
+        });
+
+        // subprocess
+        activityDrawInstructions.put(SubProcess.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
+                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
+                } else {
+                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
+                }
+            }
+        });
+
+        // transaction
+        activityDrawInstructions.put(Transaction.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
+                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
+                } else {
+                    processDiagramCanvas.drawExpandedTransaction(flowNode.getName(), graphicInfo, scaleFactor);
+                }
+            }
+        });
+
+        // Event subprocess
+        activityDrawInstructions.put(EventSubProcess.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
+                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor);
+                } else {
+                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor);
+                }
+            }
+        });
+
+        // Adhoc subprocess
+        activityDrawInstructions.put(AdhocSubProcess.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
+                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
+                } else {
+                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
+                }
+            }
+        });
+
+        // call activity
+        activityDrawInstructions.put(CallActivity.class, new ActivityDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+                processDiagramCanvas.drawCollapsedCallActivity(flowNode.getName(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // text annotation
+        artifactDrawInstructions.put(TextAnnotation.class, new ArtifactDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(artifact.getId());
+                TextAnnotation textAnnotation = (TextAnnotation) artifact;
+                processDiagramCanvas.drawTextAnnotation(textAnnotation.getText(), graphicInfo, scaleFactor);
+            }
+        });
+
+        // association
+        artifactDrawInstructions.put(Association.class, new ArtifactDrawInstruction() {
+
+            @Override
+            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
+                Association association = (Association) artifact;
+                String sourceRef = association.getSourceRef();
+                String targetRef = association.getTargetRef();
+
+                // source and target can be instance of FlowElement or Artifact
+                BaseElement sourceElement = bpmnModel.getFlowElement(sourceRef);
+                BaseElement targetElement = bpmnModel.getFlowElement(targetRef);
+                if (sourceElement == null) {
+                    sourceElement = bpmnModel.getArtifact(sourceRef);
+                }
+                if (targetElement == null) {
+                    targetElement = bpmnModel.getArtifact(targetRef);
+                }
+                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
+                graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
+                int[] xPoints = new int[graphicInfoList.size()];
+                int[] yPoints = new int[graphicInfoList.size()];
+                for (int i = 1; i < graphicInfoList.size(); i++) {
+                    GraphicInfo graphicInfo = graphicInfoList.get(i);
+                    GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
+
+                    if (i == 1) {
+                        xPoints[0] = (int) previousGraphicInfo.getX();
+                        yPoints[0] = (int) previousGraphicInfo.getY();
+                    }
+                    xPoints[i] = (int) graphicInfo.getX();
+                    yPoints[i] = (int) graphicInfo.getY();
+                }
+
+                AssociationDirection associationDirection = association.getAssociationDirection();
+                processDiagramCanvas.drawAssociation(xPoints, yPoints, associationDirection, false, scaleFactor);
+            }
+        });
+    }
+
+    @Override
+    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows,
+                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+
+        return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
+            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateImage(imageType);
+    }
+
+    @Override
+    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows, boolean drawSequenceFlowNameWithNoLabelDI) {
+        return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, 1.0, drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType,
+                                       List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+        return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, boolean drawSequenceFlowNameWithNoLabelDI) {
+        return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+        return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
+                                       String labelFontName, String annotationFontName, ClassLoader customClassLoader, boolean drawSequenceFlowNameWithNoLabelDI) {
+
+        return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(),
+            activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0, drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
+                                       String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+
+        return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(),
+            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public InputStream generatePngDiagram(BpmnModel bpmnModel, boolean drawSequenceFlowNameWithNoLabelDI) {
+        return generatePngDiagram(bpmnModel, 1.0, drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+        return generateDiagram(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public InputStream generateJpgDiagram(BpmnModel bpmnModel) {
+        return generateJpgDiagram(bpmnModel, 1.0, false);
+    }
+
+    @Override
+    public InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+        return generateDiagram(bpmnModel, "jpg", Collections.emptyList(), Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows,
+                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+
+        return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
+            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateBufferedImage(imageType);
+    }
+
+    public BufferedImage generateImage(BpmnModel bpmnModel, String imageType,
+                                       List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+
+        return generateImage(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
+    }
+
+    @Override
+    public BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor) {
+        return generateImage(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, false);
+    }
+
+    protected CustomDefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
+                                                                       List<String> highLightedActivities, List<String> highLightedFlows,
+                                                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
+
+        prepareBpmnModel(bpmnModel);
+
+        CustomDefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
+
+        // Draw pool shape, if process is participant in collaboration
+        for (Pool pool : bpmnModel.getPools()) {
+            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
+            processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo, scaleFactor);
+        }
+
+        // Draw lanes
+        for (Process process : bpmnModel.getProcesses()) {
+            for (Lane lane : process.getLanes()) {
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());
+                processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo, scaleFactor);
+            }
+        }
+
+        // Draw activities and their sequence-flows
+        for (Process process : bpmnModel.getProcesses()) {
+            for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) {
+                if (!isPartOfCollapsedSubProcess(flowNode, bpmnModel)) {
+                    drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
+                }
+            }
+        }
+
+        // Draw artifacts
+        for (Process process : bpmnModel.getProcesses()) {
+
+            for (Artifact artifact : process.getArtifacts()) {
+                drawArtifact(processDiagramCanvas, bpmnModel, artifact);
+            }
+
+            List<SubProcess> subProcesses = process.findFlowElementsOfType(SubProcess.class, true);
+            if (subProcesses != null) {
+                for (SubProcess subProcess : subProcesses) {
+
+                    GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(subProcess.getId());
+                    if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
+                        continue;
+                    }
+
+                    if (!isPartOfCollapsedSubProcess(subProcess, bpmnModel)) {
+                        for (Artifact subProcessArtifact : subProcess.getArtifacts()) {
+                            drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
+                        }
+                    }
+                }
+            }
+        }
+
+        return processDiagramCanvas;
+    }
+
+    protected void prepareBpmnModel(BpmnModel bpmnModel) {
+
+        // Need to make sure all elements have positive x and y.
+        // Check all graphicInfo and update the elements accordingly
+
+        List<GraphicInfo> allGraphicInfos = new ArrayList<>();
+        if (bpmnModel.getLocationMap() != null) {
+            allGraphicInfos.addAll(bpmnModel.getLocationMap().values());
+        }
+        if (bpmnModel.getLabelLocationMap() != null) {
+            allGraphicInfos.addAll(bpmnModel.getLabelLocationMap().values());
+        }
+        if (bpmnModel.getFlowLocationMap() != null) {
+            for (List<GraphicInfo> flowGraphicInfos : bpmnModel.getFlowLocationMap().values()) {
+                allGraphicInfos.addAll(flowGraphicInfos);
+            }
+        }
+
+        if (allGraphicInfos.size() > 0) {
+
+            boolean needsTranslationX = false;
+            boolean needsTranslationY = false;
+
+            double lowestX = 0.0;
+            double lowestY = 0.0;
+
+            // Collect lowest x and y
+            for (GraphicInfo graphicInfo : allGraphicInfos) {
+
+                double x = graphicInfo.getX();
+                double y = graphicInfo.getY();
+
+                if (x < lowestX) {
+                    needsTranslationX = true;
+                    lowestX = x;
+                }
+                if (y < lowestY) {
+                    needsTranslationY = true;
+                    lowestY = y;
+                }
+
+            }
+
+            // Update all graphicInfo objects
+            if (needsTranslationX || needsTranslationY) {
+
+                double translationX = Math.abs(lowestX);
+                double translationY = Math.abs(lowestY);
+
+                for (GraphicInfo graphicInfo : allGraphicInfos) {
+                    if (needsTranslationX) {
+                        graphicInfo.setX(graphicInfo.getX() + translationX);
+                    }
+                    if (needsTranslationY) {
+                        graphicInfo.setY(graphicInfo.getY() + translationY);
+                    }
+                }
+            }
+
+        }
+
+    }
+
+    protected void drawActivity(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
+                                FlowNode flowNode, List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) {
+
+        ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
+        if (drawInstruction != null) {
+
+            drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);
+
+            // Gather info on the multi instance marker
+            boolean multiInstanceSequential = false;
+            boolean multiInstanceParallel = false;
+            boolean collapsed = false;
+            if (flowNode instanceof Activity) {
+                Activity activity = (Activity) flowNode;
+                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();
+                if (multiInstanceLoopCharacteristics != null) {
+                    multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
+                    multiInstanceParallel = !multiInstanceSequential;
+                }
+            }
+
+            // Gather info on the collapsed marker
+            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+            if (flowNode instanceof SubProcess) {
+                collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
+            } else if (flowNode instanceof CallActivity) {
+                collapsed = true;
+            }
+
+            if (scaleFactor == 1.0) {
+                // Actually draw the markers
+                processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),
+                    multiInstanceSequential, multiInstanceParallel, collapsed);
+            }
+
+            // Draw highlighted activities
+            if (highLightedActivities.contains(flowNode.getId())) {
+                drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
+            } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) {
+                drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
+            }
+
+        } else if (flowNode instanceof Task) {
+            activityDrawInstructions.get(Task.class).draw(processDiagramCanvas, bpmnModel, flowNode);
+
+            if (highLightedActivities.contains(flowNode.getId())) {
+                drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
+            } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) {
+                drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
+            }
+        }
+
+        // Outgoing transitions of activity
+        for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
+            boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
+            String defaultFlow = null;
+            if (flowNode instanceof Activity) {
+                defaultFlow = ((Activity) flowNode).getDefaultFlow();
+            } else if (flowNode instanceof Gateway) {
+                defaultFlow = ((Gateway) flowNode).getDefaultFlow();
+            }
+
+            boolean isDefault = false;
+            if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) {
+                isDefault = true;
+            }
+            boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && sequenceFlow.getConditionExpression().trim().length() > 0 && !(flowNode instanceof Gateway);
+
+            String sourceRef = sequenceFlow.getSourceRef();
+            String targetRef = sequenceFlow.getTargetRef();
+            FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
+            FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
+            List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
+            if (graphicInfoList != null && graphicInfoList.size() > 0) {
+                graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
+                int[] xPoints = new int[graphicInfoList.size()];
+                int[] yPoints = new int[graphicInfoList.size()];
+
+                for (int i = 1; i < graphicInfoList.size(); i++) {
+                    GraphicInfo graphicInfo = graphicInfoList.get(i);
+                    GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
+
+                    if (i == 1) {
+                        xPoints[0] = (int) previousGraphicInfo.getX();
+                        yPoints[0] = (int) previousGraphicInfo.getY();
+                    }
+                    xPoints[i] = (int) graphicInfo.getX();
+                    yPoints[i] = (int) graphicInfo.getY();
+
+                }
+
+                processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor);
+
+                // Draw sequenceflow label
+                GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId());
+                if (labelGraphicInfo != null) {
+                    processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
+                } else {
+                    if (drawSequenceFlowNameWithNoLabelDI) {
+                        GraphicInfo lineCenter = getLineCenter(graphicInfoList);
+                        processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false);
+                    }
+
+                }
+            }
+        }
+
+        // Nested elements
+        if (flowNode instanceof FlowElementsContainer) {
+            for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {
+                if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) {
+                    drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement,
+                        highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
+                }
+            }
+        }
+    }
+
+    /**
+     * This method makes coordinates of connection flow better.
+     *
+     * @param processDiagramCanvas
+     * @param bpmnModel
+     * @param sourceElement
+     * @param targetElement
+     * @param graphicInfoList
+     * @return
+     */
+    protected static List<GraphicInfo> connectionPerfectionizer(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, BaseElement sourceElement, BaseElement targetElement, List<GraphicInfo> graphicInfoList) {
+        GraphicInfo sourceGraphicInfo = bpmnModel.getGraphicInfo(sourceElement.getId());
+        GraphicInfo targetGraphicInfo = bpmnModel.getGraphicInfo(targetElement.getId());
+
+        CustomDefaultProcessDiagramCanvas.SHAPE_TYPE sourceShapeType = getShapeType(sourceElement);
+        CustomDefaultProcessDiagramCanvas.SHAPE_TYPE targetShapeType = getShapeType(targetElement);
+
+        return processDiagramCanvas.connectionPerfectionizer(sourceShapeType, targetShapeType, sourceGraphicInfo, targetGraphicInfo, graphicInfoList);
+    }
+
+    /**
+     * This method returns shape type of base element.<br>
+     * Each element can be presented as rectangle, rhombus, or ellipse.
+     *
+     * @param baseElement
+     * @return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE
+     */
+    protected static CustomDefaultProcessDiagramCanvas.SHAPE_TYPE getShapeType(BaseElement baseElement) {
+        if (baseElement instanceof Task || baseElement instanceof Activity || baseElement instanceof TextAnnotation) {
+            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rectangle;
+        } else if (baseElement instanceof Gateway) {
+            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rhombus;
+        } else if (baseElement instanceof Event) {
+            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Ellipse;
+        } else {
+            // unknown source element, just do not correct coordinates
+        }
+        return null;
+    }
+
+    protected static GraphicInfo getLineCenter(List<GraphicInfo> graphicInfoList) {
+        GraphicInfo gi = new GraphicInfo();
+
+        int[] xPoints = new int[graphicInfoList.size()];
+        int[] yPoints = new int[graphicInfoList.size()];
+
+        double length = 0;
+        double[] lengths = new double[graphicInfoList.size()];
+        lengths[0] = 0;
+        double m;
+        for (int i = 1; i < graphicInfoList.size(); i++) {
+            GraphicInfo graphicInfo = graphicInfoList.get(i);
+            GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
+
+            if (i == 1) {
+                xPoints[0] = (int) previousGraphicInfo.getX();
+                yPoints[0] = (int) previousGraphicInfo.getY();
+            }
+            xPoints[i] = (int) graphicInfo.getX();
+            yPoints[i] = (int) graphicInfo.getY();
+
+            length += Math.sqrt(
+                Math.pow((int) graphicInfo.getX() - (int) previousGraphicInfo.getX(), 2) +
+                    Math.pow((int) graphicInfo.getY() - (int) previousGraphicInfo.getY(), 2));
+            lengths[i] = length;
+        }
+        m = length / 2;
+        int p1 = 0;
+        int p2 = 1;
+        for (int i = 1; i < lengths.length; i++) {
+            double len = lengths[i];
+            p1 = i - 1;
+            p2 = i;
+            if (len > m) {
+                break;
+            }
+        }
+
+        GraphicInfo graphicInfo1 = graphicInfoList.get(p1);
+        GraphicInfo graphicInfo2 = graphicInfoList.get(p2);
+
+        double AB = (int) graphicInfo2.getX() - (int) graphicInfo1.getX();
+        double OA = (int) graphicInfo2.getY() - (int) graphicInfo1.getY();
+        double OB = lengths[p2] - lengths[p1];
+        double ob = m - lengths[p1];
+        double ab = AB * ob / OB;
+        double oa = OA * ob / OB;
+
+        double mx = graphicInfo1.getX() + ab;
+        double my = graphicInfo1.getY() + oa;
+
+        gi.setX(mx);
+        gi.setY(my);
+        return gi;
+    }
+
+    protected void drawArtifact(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
+
+        ArtifactDrawInstruction drawInstruction = artifactDrawInstructions.get(artifact.getClass());
+        if (drawInstruction != null) {
+            drawInstruction.draw(processDiagramCanvas, bpmnModel, artifact);
+        }
+    }
+
+    private static void drawHighLight(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
+        processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
+
+    }
+
+    private static void drawHighLightRed(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
+        processDiagramCanvas.drawHighLightRed((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
+
+    }
+
+    protected static CustomDefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType,
+                                                                                String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
+
+        // We need to calculate maximum values to know how big the image will be in its entirety
+        double minX = Double.MAX_VALUE;
+        double maxX = 0;
+        double minY = Double.MAX_VALUE;
+        double maxY = 0;
+
+        for (Pool pool : bpmnModel.getPools()) {
+            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
+            minX = graphicInfo.getX();
+            maxX = graphicInfo.getX() + graphicInfo.getWidth();
+            minY = graphicInfo.getY();
+            maxY = graphicInfo.getY() + graphicInfo.getHeight();
+        }
+
+        List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);
+        for (FlowNode flowNode : flowNodes) {
+
+            GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+
+            // width
+            if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {
+                maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
+            }
+            if (flowNodeGraphicInfo.getX() < minX) {
+                minX = flowNodeGraphicInfo.getX();
+            }
+            // height
+            if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
+                maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
+            }
+            if (flowNodeGraphicInfo.getY() < minY) {
+                minY = flowNodeGraphicInfo.getY();
+            }
+
+            for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
+                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
+                if (graphicInfoList != null) {
+                    for (GraphicInfo graphicInfo : graphicInfoList) {
+                        // width
+                        if (graphicInfo.getX() > maxX) {
+                            maxX = graphicInfo.getX();
+                        }
+                        if (graphicInfo.getX() < minX) {
+                            minX = graphicInfo.getX();
+                        }
+                        // height
+                        if (graphicInfo.getY() > maxY) {
+                            maxY = graphicInfo.getY();
+                        }
+                        if (graphicInfo.getY() < minY) {
+                            minY = graphicInfo.getY();
+                        }
+                    }
+                }
+            }
+        }
+
+        List<Artifact> artifacts = gatherAllArtifacts(bpmnModel);
+        for (Artifact artifact : artifacts) {
+
+            GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId());
+
+            if (artifactGraphicInfo != null) {
+                // width
+                if (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) {
+                    maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth();
+                }
+                if (artifactGraphicInfo.getX() < minX) {
+                    minX = artifactGraphicInfo.getX();
+                }
+                // height
+                if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) {
+                    maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight();
+                }
+                if (artifactGraphicInfo.getY() < minY) {
+                    minY = artifactGraphicInfo.getY();
+                }
+            }
+
+            List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
+            if (graphicInfoList != null) {
+                for (GraphicInfo graphicInfo : graphicInfoList) {
+                    // width
+                    if (graphicInfo.getX() > maxX) {
+                        maxX = graphicInfo.getX();
+                    }
+                    if (graphicInfo.getX() < minX) {
+                        minX = graphicInfo.getX();
+                    }
+                    // height
+                    if (graphicInfo.getY() > maxY) {
+                        maxY = graphicInfo.getY();
+                    }
+                    if (graphicInfo.getY() < minY) {
+                        minY = graphicInfo.getY();
+                    }
+                }
+            }
+        }
+
+        int nrOfLanes = 0;
+        for (Process process : bpmnModel.getProcesses()) {
+            for (Lane l : process.getLanes()) {
+
+                nrOfLanes++;
+
+                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
+                // // width
+                if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
+                    maxX = graphicInfo.getX() + graphicInfo.getWidth();
+                }
+                if (graphicInfo.getX() < minX) {
+                    minX = graphicInfo.getX();
+                }
+                // height
+                if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
+                    maxY = graphicInfo.getY() + graphicInfo.getHeight();
+                }
+                if (graphicInfo.getY() < minY) {
+                    minY = graphicInfo.getY();
+                }
+            }
+        }
+
+        // Special case, see https://activiti.atlassian.net/browse/ACT-1431
+        if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) {
+            // Nothing to show
+            minX = 0;
+            minY = 0;
+        }
+
+        return new CustomDefaultProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY,
+            imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
+    }
+
+    protected static List<Artifact> gatherAllArtifacts(BpmnModel bpmnModel) {
+        List<Artifact> artifacts = new ArrayList<>();
+        for (Process process : bpmnModel.getProcesses()) {
+            artifacts.addAll(process.getArtifacts());
+        }
+        return artifacts;
+    }
+
+    protected static List<FlowNode> gatherAllFlowNodes(BpmnModel bpmnModel) {
+        List<FlowNode> flowNodes = new ArrayList<>();
+        for (Process process : bpmnModel.getProcesses()) {
+            flowNodes.addAll(gatherAllFlowNodes(process));
+        }
+        return flowNodes;
+    }
+
+    protected static List<FlowNode> gatherAllFlowNodes(FlowElementsContainer flowElementsContainer) {
+        List<FlowNode> flowNodes = new ArrayList<>();
+        for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
+            if (flowElement instanceof FlowNode) {
+                flowNodes.add((FlowNode) flowElement);
+            }
+            if (flowElement instanceof FlowElementsContainer) {
+                flowNodes.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));
+            }
+        }
+        return flowNodes;
+    }
+
+    protected boolean isPartOfCollapsedSubProcess(FlowElement flowElement, BpmnModel model) {
+        SubProcess subProcess = flowElement.getSubProcess();
+        if (subProcess != null) {
+            GraphicInfo graphicInfo = model.getGraphicInfo(subProcess.getId());
+            if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
+                return true;
+            }
+
+            return isPartOfCollapsedSubProcess(subProcess, model);
+        }
+
+        return false;
+    }
+
+    public Map<Class<? extends BaseElement>, ActivityDrawInstruction> getActivityDrawInstructions() {
+        return activityDrawInstructions;
+    }
+
+    public void setActivityDrawInstructions(
+        Map<Class<? extends BaseElement>, ActivityDrawInstruction> activityDrawInstructions) {
+        this.activityDrawInstructions = activityDrawInstructions;
+    }
+
+    public Map<Class<? extends BaseElement>, ArtifactDrawInstruction> getArtifactDrawInstructions() {
+        return artifactDrawInstructions;
+    }
+
+    public void setArtifactDrawInstructions(
+        Map<Class<? extends BaseElement>, ArtifactDrawInstruction> artifactDrawInstructions) {
+        this.artifactDrawInstructions = artifactDrawInstructions;
+    }
+
+    protected interface ActivityDrawInstruction {
+        void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode);
+    }
+
+    protected interface ArtifactDrawInstruction {
+        void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact);
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java
new file mode 100644
index 0000000..3cdfcf4
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java
@@ -0,0 +1,61 @@
+package org.dromara.workflow.flowable.cmd;
+
+import cn.hutool.core.collection.CollUtil;
+import org.flowable.common.engine.impl.interceptor.Command;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
+import org.flowable.engine.impl.util.CommandContextUtil;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.dromara.workflow.common.constant.FlowConstant.NUMBER_OF_INSTANCES;
+
+/**
+ * 涓茶鍔犵
+ *
+ * @author may
+ */
+public class AddSequenceMultiInstanceCmd implements Command<Void> {
+
+    /**
+     * 鎵цid
+     */
+    private final String executionId;
+
+    /**
+     * 浼氱浜哄憳闆嗗悎KEY
+     */
+    private final String assigneeList;
+
+    /**
+     * 鍔犵浜哄憳
+     */
+    private final List<Long> assignees;
+
+    public AddSequenceMultiInstanceCmd(String executionId, String assigneeList, List<Long> assignees) {
+        this.executionId = executionId;
+        this.assigneeList = assigneeList;
+        this.assignees = assignees;
+    }
+
+    @Override
+    public Void execute(CommandContext commandContext) {
+        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
+        ExecutionEntity entity = executionEntityManager.findById(executionId);
+        // 澶氬疄渚嬩换鍔℃�绘暟鍔� assignees.size()
+        if (entity.getVariable(NUMBER_OF_INSTANCES) instanceof Integer nrOfInstances) {
+            entity.setVariable(NUMBER_OF_INSTANCES, nrOfInstances + assignees.size());
+        }
+        // 璁剧疆娴佺▼鍙橀噺
+        if (entity.getVariable(assigneeList) instanceof List<?> userIds) {
+            CollUtil.addAll(userIds, assignees);
+            Map<String, Object> variables = new HashMap<>(16);
+            variables.put(assigneeList, userIds);
+            entity.setVariables(variables);
+        }
+        return null;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java
new file mode 100644
index 0000000..dd9849a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java
@@ -0,0 +1,64 @@
+package org.dromara.workflow.flowable.cmd;
+
+import cn.hutool.core.collection.CollUtil;
+import org.dromara.common.core.domain.dto.OssDTO;
+import org.dromara.common.core.service.OssService;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.flowable.common.engine.impl.interceptor.Command;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.persistence.entity.AttachmentEntity;
+import org.flowable.engine.impl.persistence.entity.AttachmentEntityManager;
+import org.flowable.engine.impl.util.CommandContextUtil;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 闄勪欢涓婁紶
+ *
+ * @author may
+ */
+public class AttachmentCmd implements Command<Boolean> {
+
+    private final String fileId;
+
+    private final String taskId;
+
+    private final String processInstanceId;
+
+    public AttachmentCmd(String fileId, String taskId, String processInstanceId) {
+        this.fileId = fileId;
+        this.taskId = taskId;
+        this.processInstanceId = processInstanceId;
+    }
+
+    @Override
+    public Boolean execute(CommandContext commandContext) {
+        try {
+            if (StringUtils.isNotBlank(fileId)) {
+                List<OssDTO> ossList = SpringUtils.getBean(OssService.class).selectByIds(fileId);
+                if (CollUtil.isNotEmpty(ossList)) {
+                    for (OssDTO oss : ossList) {
+                        AttachmentEntityManager attachmentEntityManager = CommandContextUtil.getAttachmentEntityManager();
+                        AttachmentEntity attachmentEntity = attachmentEntityManager.create();
+                        attachmentEntity.setRevision(1);
+                        attachmentEntity.setUserId(LoginHelper.getUserId().toString());
+                        attachmentEntity.setName(oss.getOriginalName());
+                        attachmentEntity.setDescription(oss.getOriginalName());
+                        attachmentEntity.setType(oss.getFileSuffix());
+                        attachmentEntity.setTaskId(taskId);
+                        attachmentEntity.setProcessInstanceId(processInstanceId);
+                        attachmentEntity.setContentId(oss.getOssId().toString());
+                        attachmentEntity.setTime(new Date());
+                        attachmentEntityManager.insert(attachmentEntity);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        return true;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java
new file mode 100644
index 0000000..215d310
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java
@@ -0,0 +1,36 @@
+package org.dromara.workflow.flowable.cmd;
+
+import org.flowable.common.engine.impl.interceptor.Command;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
+import org.flowable.engine.impl.util.CommandContextUtil;
+
+import java.io.Serializable;
+
+/**
+ * 鍒犻櫎鎵ц鏁版嵁
+ *
+ * @author may
+ */
+public class DeleteExecutionCmd implements Command<Void>, Serializable {
+
+    /**
+     * 鎵цid
+     */
+    private final String executionId;
+
+    public DeleteExecutionCmd(String executionId) {
+        this.executionId = executionId;
+    }
+
+    @Override
+    public Void execute(CommandContext commandContext) {
+        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
+        ExecutionEntity entity = executionEntityManager.findById(executionId);
+        if (entity != null) {
+            executionEntityManager.deleteExecutionAndRelatedData(entity, "", false, false);
+        }
+        return null;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java
new file mode 100644
index 0000000..6773eef
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java
@@ -0,0 +1,82 @@
+package org.dromara.workflow.flowable.cmd;
+
+import cn.hutool.core.util.ObjectUtil;
+import lombok.AllArgsConstructor;
+import org.flowable.common.engine.impl.interceptor.Command;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
+import org.flowable.engine.impl.util.CommandContextUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.dromara.workflow.common.constant.FlowConstant.LOOP_COUNTER;
+import static org.dromara.workflow.common.constant.FlowConstant.NUMBER_OF_INSTANCES;
+
+
+/**
+ * 涓茶鍑忕
+ *
+ * @author may
+ */
+@AllArgsConstructor
+public class DeleteSequenceMultiInstanceCmd implements Command<Void> {
+
+    /**
+     * 褰撳墠鑺傜偣瀹℃壒浜哄憳id
+     */
+    private final String currentUserId;
+
+    /**
+     * 鎵цid
+     */
+    private final String executionId;
+
+    /**
+     * 浼氱浜哄憳闆嗗悎KEY
+     */
+    private final String assigneeList;
+
+    /**
+     * 鍑忕浜哄憳
+     */
+    private final List<Long> assignees;
+
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Void execute(CommandContext commandContext) {
+        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
+        ExecutionEntity entity = executionEntityManager.findById(executionId);
+        // 璁剧疆娴佺▼鍙橀噺
+        List<Long> userIds = new ArrayList<>();
+        List<Object> variable = (List<Object>) entity.getVariable(assigneeList);
+        for (Object o : variable) {
+            userIds.add(Long.valueOf(o.toString()));
+        }
+        List<Long> userIdList = new ArrayList<>();
+        userIds.forEach(e -> {
+            Long userId = assignees.stream().filter(id -> ObjectUtil.equals(id, e)).findFirst().orElse(null);
+            if (userId == null) {
+                userIdList.add(e);
+            }
+        });
+        // 褰撳墠浠诲姟鎵ц浣嶇疆
+        int loopCounterIndex = -1;
+        for (int i = 0; i < userIdList.size(); i++) {
+            Long userId = userIdList.get(i);
+            if (currentUserId.equals(userId.toString())) {
+                loopCounterIndex = i;
+            }
+        }
+        Map<String, Object> variables = new HashMap<>(16);
+        variables.put(NUMBER_OF_INSTANCES, userIdList.size());
+        variables.put(assigneeList, userIdList);
+        variables.put(LOOP_COUNTER, loopCounterIndex);
+        entity.setVariables(variables);
+        return null;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java
new file mode 100644
index 0000000..1f3088b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java
@@ -0,0 +1,39 @@
+package org.dromara.workflow.flowable.cmd;
+
+import org.dromara.common.core.utils.StreamUtils;
+import org.flowable.common.engine.impl.interceptor.Command;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
+import org.flowable.engine.impl.util.CommandContextUtil;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 鑾峰彇骞惰缃戝叧鎵ц鍚庝繚鐣欑殑鎵ц瀹炰緥鏁版嵁
+ *
+ * @author may
+ */
+public class ExecutionChildByExecutionIdCmd implements Command<List<ExecutionEntity>>, Serializable {
+
+    /**
+     * 褰撳墠浠诲姟鎵ц瀹炰緥id
+     */
+    private final String executionId;
+
+    public ExecutionChildByExecutionIdCmd(String executionId) {
+        this.executionId = executionId;
+    }
+
+    @Override
+    public List<ExecutionEntity> execute(CommandContext commandContext) {
+        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
+        // 鑾峰彇褰撳墠鎵ц鏁版嵁
+        ExecutionEntity executionEntity = executionEntityManager.findById(executionId);
+        // 閫氳繃褰撳墠鎵ц鏁版嵁鐨勭埗鎵ц锛屾煡璇㈡墍鏈夊瓙鎵ц鏁版嵁
+        List<ExecutionEntity> allChildrenExecution =
+            executionEntityManager.collectChildren(executionEntity.getParent());
+        return StreamUtils.filter(allChildrenExecution, e -> !e.isActive());
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java
new file mode 100644
index 0000000..3ba120a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java
@@ -0,0 +1,37 @@
+package org.dromara.workflow.flowable.cmd;
+
+import org.dromara.common.core.exception.ServiceException;
+import org.flowable.common.engine.impl.interceptor.Command;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntity;
+import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityManager;
+import org.flowable.engine.impl.util.CommandContextUtil;
+
+/**
+ * 淇敼娴佺▼鐘舵��
+ *
+ * @author may
+ */
+public class UpdateBusinessStatusCmd implements Command<Boolean> {
+
+    private final String processInstanceId;
+    private final String status;
+
+    public UpdateBusinessStatusCmd(String processInstanceId, String status) {
+        this.processInstanceId = processInstanceId;
+        this.status = status;
+    }
+
+    @Override
+    public Boolean execute(CommandContext commandContext) {
+        try {
+            HistoricProcessInstanceEntityManager manager = CommandContextUtil.getHistoricProcessInstanceEntityManager();
+            HistoricProcessInstanceEntity processInstance = manager.findById(processInstanceId);
+            processInstance.setBusinessStatus(status);
+            manager.update(processInstance);
+            return true;
+        } catch (Exception e) {
+            throw new ServiceException(e.getMessage());
+        }
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java
new file mode 100644
index 0000000..42f6d1c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java
@@ -0,0 +1,51 @@
+package org.dromara.workflow.flowable.cmd;
+
+import org.dromara.common.core.exception.ServiceException;
+import org.flowable.common.engine.impl.interceptor.Command;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.task.service.HistoricTaskService;
+import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * 淇敼娴佺▼鍘嗗彶
+ *
+ * @author may
+ */
+public class UpdateHiTaskInstCmd implements Command<Boolean> {
+
+    private final List<String> taskIds;
+
+    private final String processDefinitionId;
+
+    private final String processInstanceId;
+
+    public UpdateHiTaskInstCmd(List<String> taskIds, String processDefinitionId, String processInstanceId) {
+        this.taskIds = taskIds;
+        this.processDefinitionId = processDefinitionId;
+        this.processInstanceId = processInstanceId;
+    }
+
+    @Override
+    public Boolean execute(CommandContext commandContext) {
+        try {
+            HistoricTaskService historicTaskService = CommandContextUtil.getHistoricTaskService();
+            for (String taskId : taskIds) {
+                HistoricTaskInstanceEntity historicTask = historicTaskService.getHistoricTask(taskId);
+                if (historicTask != null) {
+                    historicTask.setProcessDefinitionId(processDefinitionId);
+                    historicTask.setProcessInstanceId(processInstanceId);
+                    historicTask.setCreateTime(new Date());
+                    CommandContextUtil.getHistoricTaskService().updateHistoricTask(historicTask, true);
+                }
+            }
+            return true;
+        } catch (Exception e) {
+            throw new ServiceException(e.getMessage());
+        }
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java
new file mode 100644
index 0000000..1494bf3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java
@@ -0,0 +1,32 @@
+package org.dromara.workflow.flowable.config;
+
+import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
+import org.dromara.workflow.flowable.handler.TaskTimeoutJobHandler;
+import org.flowable.spring.SpringProcessEngineConfiguration;
+import org.flowable.spring.boot.EngineConfigurationConfigurer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Collections;
+
+
+/**
+ * flowable閰嶇疆
+ *
+ * @author may
+ */
+@Configuration
+public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
+
+    @Autowired
+    private GlobalFlowableListener globalFlowableListener;
+    @Autowired
+    private IdentifierGenerator identifierGenerator;
+
+    @Override
+    public void configure(SpringProcessEngineConfiguration processEngineConfiguration) {
+        processEngineConfiguration.setIdGenerator(() -> identifierGenerator.nextId(null).toString());
+        processEngineConfiguration.setEventListeners(Collections.singletonList(globalFlowableListener));
+        processEngineConfiguration.addCustomJobHandler(new TaskTimeoutJobHandler());
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java
new file mode 100644
index 0000000..9bb971a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java
@@ -0,0 +1,139 @@
+package org.dromara.workflow.flowable.config;
+
+import cn.hutool.core.collection.CollUtil;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.workflow.common.enums.TaskStatusEnum;
+import org.dromara.workflow.flowable.handler.TaskTimeoutJobHandler;
+import org.dromara.workflow.utils.QueryUtils;
+import org.flowable.bpmn.model.BoundaryEvent;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.common.engine.api.delegate.event.*;
+import org.flowable.common.engine.impl.cfg.TransactionState;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.engine.runtime.Execution;
+import org.flowable.engine.task.Comment;
+import org.flowable.job.service.TimerJobService;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
+import org.flowable.task.api.Task;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * 寮曟搸璋冨害鐩戝惉
+ *
+ * @author may
+ */
+@Component
+public class GlobalFlowableListener implements FlowableEventListener {
+
+    @Autowired
+    @Lazy
+    private TaskService taskService;
+
+    @Autowired
+    @Lazy
+    private RuntimeService runtimeService;
+
+    @Autowired
+    @Lazy
+    private RepositoryService repositoryService;
+
+    @Value("${flowable.async-executor-activate}")
+    private boolean asyncExecutorActivate;
+
+    @Override
+    public void onEvent(FlowableEvent flowableEvent) {
+        if (flowableEvent instanceof FlowableEngineEvent flowableEngineEvent) {
+            FlowableEngineEventType engineEventType = (FlowableEngineEventType) flowableEvent.getType();
+            switch (engineEventType) {
+                case JOB_EXECUTION_SUCCESS -> jobExecutionSuccess((FlowableEngineEntityEvent) flowableEngineEvent);
+                case TASK_DUEDATE_CHANGED, TASK_CREATED -> {
+                    FlowableEntityEvent flowableEntityEvent = (FlowableEntityEvent) flowableEngineEvent;
+                    Object entityObject = flowableEntityEvent.getEntity();
+                    TaskEntity task = (TaskEntity) entityObject;
+                    if (asyncExecutorActivate && task.getDueDate() != null && task.getDueDate().after(new Date())) {
+                        //鍒犻櫎涔嬪墠宸茬粡瀛樺湪鐨勫畾鏃朵换鍔�
+                        TimerJobService timerJobService = CommandContextUtil.getTimerJobService();
+                        List<TimerJobEntity> timerJobEntityList = timerJobService.findTimerJobsByProcessInstanceId(task.getProcessInstanceId());
+                        if (!CollUtil.isEmpty(timerJobEntityList)) {
+                            for (TimerJobEntity timerJobEntity : timerJobEntityList) {
+                                String taskId = timerJobEntity.getJobHandlerConfiguration();
+                                if (task.getId().equals(taskId)) {
+                                    timerJobService.deleteTimerJob(timerJobEntity);
+                                }
+                            }
+                        }
+                        //鍒涘缓job瀵硅薄
+                        TimerJobEntity timer = timerJobService.createTimerJob();
+                        timer.setTenantId(TenantHelper.getTenantId());
+                        //璁剧疆job绫诲瀷
+                        timer.setJobType(JobEntity.JOB_TYPE_TIMER);
+                        timer.setJobHandlerType(TaskTimeoutJobHandler.TYPE);
+                        timer.setDuedate(task.getDueDate());
+                        timer.setProcessInstanceId(task.getProcessInstanceId());
+                        //璁剧疆浠诲姟id
+                        timer.setJobHandlerConfiguration(task.getId());
+                        //淇濆瓨骞惰Е鍙戜簨浠�
+                        timerJobService.scheduleTimerJob(timer);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean isFailOnException() {
+        return true;
+    }
+
+    @Override
+    public boolean isFireOnTransactionLifecycleEvent() {
+        return false;
+    }
+
+    @Override
+    public String getOnTransaction() {
+        return TransactionState.COMMITTED.name();
+    }
+
+    /**
+     * 澶勭悊杈圭晫瀹氭椂浜嬩欢鑷姩瀹℃壒璁板綍
+     *
+     * @param event 浜嬩欢
+     */
+    protected void jobExecutionSuccess(FlowableEngineEntityEvent event) {
+        if (event != null && StringUtils.isNotBlank(event.getExecutionId())) {
+            Execution execution = runtimeService.createExecutionQuery().executionId(event.getExecutionId()).singleResult();
+            if (execution != null) {
+                BpmnModel bpmnModel = repositoryService.getBpmnModel(event.getProcessDefinitionId());
+                FlowElement flowElement = bpmnModel.getFlowElement(execution.getActivityId());
+                if (flowElement instanceof BoundaryEvent) {
+                    String attachedToRefId = ((BoundaryEvent) flowElement).getAttachedToRefId();
+                    List<Execution> list = runtimeService.createExecutionQuery().activityId(attachedToRefId).list();
+                    for (Execution ex : list) {
+                        Task task = QueryUtils.taskQuery().executionId(ex.getId()).singleResult();
+                        if (task != null) {
+                            List<Comment> taskComments = taskService.getTaskComments(task.getId());
+                            if (CollUtil.isEmpty(taskComments)) {
+                                taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), "瓒呮椂鑷姩瀹℃壒!");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/TaskTimeoutJobHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/TaskTimeoutJobHandler.java
new file mode 100644
index 0000000..7685423
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/TaskTimeoutJobHandler.java
@@ -0,0 +1,38 @@
+package org.dromara.workflow.flowable.handler;
+
+import org.dromara.workflow.common.enums.TaskStatusEnum;
+import org.flowable.common.engine.impl.interceptor.CommandContext;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.impl.jobexecutor.TimerEventHandler;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.job.service.JobHandler;
+import org.flowable.job.service.impl.persistence.entity.JobEntity;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.TaskQuery;
+import org.flowable.variable.api.delegate.VariableScope;
+
+/**
+ * 鍔炵悊瓒呮椂(杩囨湡)浠诲姟
+ *
+ * @author may
+ */
+public class TaskTimeoutJobHandler extends TimerEventHandler implements JobHandler {
+
+    public static final String TYPE = "taskTimeout";
+
+    @Override
+    public String getType() {
+        return TYPE;
+    }
+
+    @Override
+    public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
+        TaskService taskService = CommandContextUtil.getProcessEngineConfiguration(commandContext)
+            .getTaskService();
+        Task task = taskService.createTaskQuery().taskId(configuration).singleResult();
+        if (task != null) {
+            taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.TIMEOUT.getStatus(), "瓒呮椂鑷姩瀹℃壒!");
+            taskService.complete(configuration);
+        }
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowEventStrategy.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowEventStrategy.java
new file mode 100644
index 0000000..9da5776
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowEventStrategy.java
@@ -0,0 +1,73 @@
+package org.dromara.workflow.flowable.strategy;
+
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.workflow.annotation.FlowListenerAnnotation;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 娴佺▼浠诲姟鐩戝惉绛栫暐
+ *
+ * @author may
+ * @date 2023-12-27
+ */
+@Component
+public class FlowEventStrategy implements BeanPostProcessor {
+
+    private final Map<String, FlowTaskEventHandler> flowTaskEventHandlers = new HashMap<>();
+    private final Map<String, FlowProcessEventHandler> flowProcessEventHandlers = new HashMap<>();
+
+    @Override
+    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+        if (bean instanceof FlowTaskEventHandler) {
+            FlowListenerAnnotation annotation = bean.getClass().getAnnotation(FlowListenerAnnotation.class);
+            if (null != annotation) {
+                if (StringUtils.isNotBlank(annotation.processDefinitionKey()) && StringUtils.isNotBlank(annotation.taskDefId())) {
+                    String id = annotation.processDefinitionKey() + "_" + annotation.taskDefId();
+                    if (!flowTaskEventHandlers.containsKey(id)) {
+                        flowTaskEventHandlers.put(id, (FlowTaskEventHandler) bean);
+                    }
+                }
+            }
+        }
+        if (bean instanceof FlowProcessEventHandler) {
+            FlowListenerAnnotation annotation = bean.getClass().getAnnotation(FlowListenerAnnotation.class);
+            if (null != annotation) {
+                if (StringUtils.isNotBlank(annotation.processDefinitionKey()) && StringUtils.isBlank(annotation.taskDefId())) {
+                    if (!flowProcessEventHandlers.containsKey(annotation.processDefinitionKey())) {
+                        flowProcessEventHandlers.put(annotation.processDefinitionKey(), (FlowProcessEventHandler) bean);
+                    }
+                }
+            }
+        }
+        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
+    }
+
+    /**
+     * 鑾峰彇鍙墽琛宐ean
+     *
+     * @param key key
+     */
+    public FlowTaskEventHandler getTaskHandler(String key) {
+        if (!flowTaskEventHandlers.containsKey(key)) {
+            return null;
+        }
+        return flowTaskEventHandlers.get(key);
+    }
+
+    /**
+     * 鑾峰彇鍙墽琛宐ean
+     *
+     * @param key key
+     */
+    public FlowProcessEventHandler getProcessHandler(String key) {
+        if (!flowProcessEventHandlers.containsKey(key)) {
+            return null;
+        }
+        return flowProcessEventHandlers.get(key);
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowProcessEventHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowProcessEventHandler.java
new file mode 100644
index 0000000..4af2c47
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowProcessEventHandler.java
@@ -0,0 +1,20 @@
+package org.dromara.workflow.flowable.strategy;
+
+
+/**
+ * 娴佺▼鐩戝惉
+ *
+ * @author may
+ * @date 2023-12-27
+ */
+public interface FlowProcessEventHandler {
+
+    /**
+     * 鎵ц鍔炵悊浠诲姟鐩戝惉
+     *
+     * @param businessKey 涓氬姟id
+     * @param status      鐘舵��
+     * @param submit      褰撲负true鏃朵负鐢宠浜鸿妭鐐瑰姙鐞�
+     */
+    void handleProcess(String businessKey, String status, boolean submit);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowTaskEventHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowTaskEventHandler.java
new file mode 100644
index 0000000..b338900
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/strategy/FlowTaskEventHandler.java
@@ -0,0 +1,19 @@
+package org.dromara.workflow.flowable.strategy;
+
+
+/**
+ * 娴佺▼浠诲姟鐩戝惉
+ *
+ * @author may
+ * @date 2023-12-27
+ */
+public interface FlowTaskEventHandler {
+
+    /**
+     * 鎵ц鍔炵悊浠诲姟鐩戝惉
+     *
+     * @param taskId      浠诲姟ID
+     * @param businessKey 涓氬姟id
+     */
+    void handleTask(String taskId, String businessKey);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomProcessHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomProcessHandler.java
new file mode 100644
index 0000000..29886d8
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomProcessHandler.java
@@ -0,0 +1,31 @@
+package org.dromara.workflow.listener;
+
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.workflow.annotation.FlowListenerAnnotation;
+import org.dromara.workflow.flowable.strategy.FlowProcessEventHandler;
+import org.springframework.stereotype.Component;
+
+/**
+ * 鑷畾涔夌洃鍚祴璇�
+ *
+ * @author may
+ * @date 2023-12-27
+ */
+@Slf4j
+@Component
+@FlowListenerAnnotation(processDefinitionKey = "leave1")
+public class TestCustomProcessHandler implements FlowProcessEventHandler {
+
+
+    /**
+     * 鎵ц鍔炵悊浠诲姟鐩戝惉
+     *
+     * @param businessKey 涓氬姟id
+     * @param status      鐘舵��
+     * @param submit      褰撲负true鏃朵负鐢宠浜鸿妭鐐瑰姙鐞�
+     */
+    @Override
+    public void handleProcess(String businessKey, String status, boolean submit) {
+        log.info("涓氬姟ID:" + businessKey + ",鐘舵��:" + status);
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomTaskHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomTaskHandler.java
new file mode 100644
index 0000000..d85286d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestCustomTaskHandler.java
@@ -0,0 +1,23 @@
+package org.dromara.workflow.listener;
+
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.workflow.annotation.FlowListenerAnnotation;
+import org.dromara.workflow.flowable.strategy.FlowTaskEventHandler;
+import org.springframework.stereotype.Component;
+
+/**
+ * 鑷畾涔夌洃鍚祴璇�
+ *
+ * @author may
+ * @date 2023-12-27
+ */
+@Slf4j
+@Component
+@FlowListenerAnnotation(processDefinitionKey = "leave1", taskDefId = "Activity_14633hx")
+public class TestCustomTaskHandler implements FlowTaskEventHandler {
+
+    @Override
+    public void handleTask(String taskId, String businessKey) {
+        log.info("浠诲姟ID:" + taskId + ",涓氬姟ID:" + businessKey);
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveExecutionListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveExecutionListener.java
new file mode 100644
index 0000000..11f844e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveExecutionListener.java
@@ -0,0 +1,28 @@
+package org.dromara.workflow.listener;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.workflow.utils.QueryUtils;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.delegate.ExecutionListener;
+import org.flowable.task.api.Task;
+import org.springframework.stereotype.Component;
+
+/**
+ * 娴佺▼瀹炰緥鐩戝惉娴嬭瘯
+ *
+ * @author may
+ * @date 2023-12-12
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Component("testLeaveExecutionListener")
+public class TestLeaveExecutionListener implements ExecutionListener {
+
+    @Override
+    public void notify(DelegateExecution execution) {
+        Task task = QueryUtils.taskQuery().executionId(execution.getId()).singleResult();
+        log.info("鎵ц鐩戝惉銆�" + task.getName() + "銆�");
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveTaskListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveTaskListener.java
new file mode 100644
index 0000000..5c62417
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/TestLeaveTaskListener.java
@@ -0,0 +1,21 @@
+package org.dromara.workflow.listener;
+
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.task.service.delegate.DelegateTask;
+import org.flowable.task.service.delegate.TaskListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * 娴佺▼浠诲姟鐩戝惉娴嬭瘯
+ *
+ * @author may
+ * @date 2023-12-12
+ */
+@Slf4j
+@Component("testLeaveTaskListener")
+public class TestLeaveTaskListener implements TaskListener {
+    @Override
+    public void notify(DelegateTask delegateTask) {
+        log.info("鎵ц鐩戝惉銆�" + delegateTask.getName() + "銆�");
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java
new file mode 100644
index 0000000..a3a41c9
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java
@@ -0,0 +1,16 @@
+package org.dromara.workflow.mapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.workflow.domain.ActHiProcinst;
+
+/**
+ * 娴佺▼瀹炰緥Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2023-07-22
+ */
+@InterceptorIgnore(tenantLine = "true")
+public interface ActHiProcinstMapper extends BaseMapperPlus<ActHiProcinst, ActHiProcinst> {
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java
new file mode 100644
index 0000000..63b394b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java
@@ -0,0 +1,16 @@
+package org.dromara.workflow.mapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import org.dromara.workflow.domain.ActHiTaskinst;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 娴佺▼鍘嗗彶浠诲姟Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-02
+ */
+@InterceptorIgnore(tenantLine = "true")
+public interface ActHiTaskinstMapper extends BaseMapperPlus<ActHiTaskinst, ActHiTaskinst> {
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java
new file mode 100644
index 0000000..63c5ecb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java
@@ -0,0 +1,47 @@
+package org.dromara.workflow.mapper;
+
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Param;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.workflow.domain.vo.TaskVo;
+
+
+/**
+ * 浠诲姟淇℃伅Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-02
+ */
+@InterceptorIgnore(tenantLine = "true")
+public interface ActTaskMapper extends BaseMapperPlus<TaskVo, TaskVo> {
+    /**
+     * 鑾峰彇寰呭姙淇℃伅
+     *
+     * @param page         鍒嗛〉
+     * @param queryWrapper 鏉′欢
+     * @return 缁撴灉
+     */
+    Page<TaskVo> getTaskWaitByPage(@Param("page") Page<TaskVo> page, @Param(Constants.WRAPPER) Wrapper<TaskVo> queryWrapper);
+
+    /**
+     * 鑾峰彇宸插姙
+     *
+     * @param page         鍒嗛〉
+     * @param queryWrapper 鏉′欢
+     * @return 缁撴灉
+     */
+    Page<TaskVo> getTaskFinishByPage(@Param("page") Page<TaskVo> page, @Param(Constants.WRAPPER) Wrapper<TaskVo> queryWrapper);
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勬妱閫�
+     *
+     * @param page         鍒嗛〉
+     * @param queryWrapper 鏉′欢
+     * @return 缁撴灉
+     */
+    Page<TaskVo> getTaskCopyByPage(@Param("page") Page<TaskVo> page, @Param(Constants.WRAPPER) QueryWrapper<TaskVo> queryWrapper);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/TestLeaveMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/TestLeaveMapper.java
new file mode 100644
index 0000000..cd1edba
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/TestLeaveMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.workflow.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.workflow.domain.TestLeave;
+import org.dromara.workflow.domain.vo.TestLeaveVo;
+
+/**
+ * 璇峰亣Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2023-07-21
+ */
+public interface TestLeaveMapper extends BaseMapperPlus<TestLeave, TestLeaveVo> {
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java
new file mode 100644
index 0000000..98aea02
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.workflow.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.workflow.domain.WfCategory;
+import org.dromara.workflow.domain.vo.WfCategoryVo;
+
+/**
+ * 娴佺▼鍒嗙被Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2023-06-27
+ */
+public interface WfCategoryMapper extends BaseMapperPlus<WfCategory, WfCategoryVo> {
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfDefinitionConfigMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfDefinitionConfigMapper.java
new file mode 100644
index 0000000..ee20882
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfDefinitionConfigMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.workflow.mapper;
+
+import org.dromara.workflow.domain.WfDefinitionConfig;
+import org.dromara.workflow.domain.vo.WfDefinitionConfigVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 娴佺▼瀹氫箟閰嶇疆Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-18
+ */
+public interface WfDefinitionConfigMapper extends BaseMapperPlus<WfDefinitionConfig, WfDefinitionConfigVo> {
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfFormManageMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfFormManageMapper.java
new file mode 100644
index 0000000..acf8111
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfFormManageMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.workflow.mapper;
+
+import org.dromara.workflow.domain.WfFormManage;
+import org.dromara.workflow.domain.vo.WfFormManageVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 琛ㄥ崟绠$悊Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-29
+ */
+public interface WfFormManageMapper extends BaseMapperPlus<WfFormManage, WfFormManageVo> {
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfNodeConfigMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfNodeConfigMapper.java
new file mode 100644
index 0000000..d2aecac
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfNodeConfigMapper.java
@@ -0,0 +1,15 @@
+package org.dromara.workflow.mapper;
+
+import org.dromara.workflow.domain.WfNodeConfig;
+import org.dromara.workflow.domain.vo.WfNodeConfigVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 鑺傜偣閰嶇疆Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-30
+ */
+public interface WfNodeConfigMapper extends BaseMapperPlus<WfNodeConfig, WfNodeConfigVo> {
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java
new file mode 100644
index 0000000..9b291fe
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java
@@ -0,0 +1,13 @@
+package org.dromara.workflow.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.workflow.domain.WfTaskBackNode;
+
+/**
+ * 鑺傜偣椹冲洖璁板綍Mapper鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-13
+ */
+public interface WfTaskBackNodeMapper extends BaseMapperPlus<WfTaskBackNode, WfTaskBackNode> {
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java
new file mode 100644
index 0000000..e802c69
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java
@@ -0,0 +1,31 @@
+package org.dromara.workflow.service;
+
+
+import org.dromara.workflow.domain.ActHiProcinst;
+
+import java.util.List;
+
+/**
+ * 娴佺▼瀹炰緥Service鎺ュ彛
+ *
+ * @author may
+ * @date 2023-07-22
+ */
+public interface IActHiProcinstService {
+
+    /**
+     * 鎸夌収涓氬姟id鏌ヨ
+     *
+     * @param businessKeys 涓氬姟id
+     * @return 缁撴灉
+     */
+    List<ActHiProcinst> selectByBusinessKeyIn(List<String> businessKeys);
+
+    /**
+     * 鎸夌収涓氬姟id鏌ヨ
+     *
+     * @param businessKey 涓氬姟id
+     * @return 缁撴灉
+     */
+    ActHiProcinst selectByBusinessKey(String businessKey);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java
new file mode 100644
index 0000000..ad286e2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java
@@ -0,0 +1,11 @@
+package org.dromara.workflow.service;
+
+
+/**
+ * 娴佺▼鍘嗗彶浠诲姟Service鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-02
+ */
+public interface IActHiTaskinstService {
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java
new file mode 100644
index 0000000..4a6d170
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java
@@ -0,0 +1,83 @@
+package org.dromara.workflow.service;
+
+import jakarta.servlet.http.HttpServletResponse;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.workflow.domain.bo.ModelBo;
+import org.dromara.workflow.domain.vo.ModelVo;
+import org.flowable.engine.repository.Model;
+
+import java.util.List;
+
+
+/**
+ * 妯″瀷绠$悊 鏈嶅姟灞�
+ *
+ * @author may
+ */
+public interface IActModelService {
+    /**
+     * 鍒嗛〉鏌ヨ妯″瀷
+     *
+     * @param modelBo   妯″瀷鍙傛暟
+     * @param pageQuery 鍙傛暟
+     * @return 杩斿洖鍒嗛〉鍒楄〃
+     */
+    TableDataInfo<Model> page(ModelBo modelBo, PageQuery pageQuery);
+
+    /**
+     * 鏂板妯″瀷
+     *
+     * @param modelBo 妯″瀷璇锋眰瀵硅薄
+     * @return 缁撴灉
+     */
+    boolean saveNewModel(ModelBo modelBo);
+
+    /**
+     * 鏌ヨ妯″瀷
+     *
+     * @param modelId 妯″瀷id
+     * @return 妯″瀷鏁版嵁
+     */
+    ModelVo getInfo(String modelId);
+
+    /**
+     * 淇敼妯″瀷淇℃伅
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     * @return 缁撴灉
+     */
+    boolean update(ModelBo modelBo);
+
+    /**
+     * 缂栬緫妯″瀷XML
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     * @return 缁撴灉
+     */
+    boolean editModelXml(ModelBo modelBo);
+
+    /**
+     * 妯″瀷閮ㄧ讲
+     *
+     * @param id 妯″瀷id
+     * @return 缁撴灉
+     */
+    boolean modelDeploy(String id);
+
+    /**
+     * 瀵煎嚭妯″瀷zip鍘嬬缉鍖�
+     *
+     * @param modelIds 妯″瀷id
+     * @param response 鍝嶅簲
+     */
+    void exportZip(List<String> modelIds, HttpServletResponse response);
+
+    /**
+     * 澶嶅埗妯″瀷
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     * @return 缁撴灉
+     */
+    boolean copyModel(ModelBo modelBo);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java
new file mode 100644
index 0000000..5d00e41
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java
@@ -0,0 +1,91 @@
+package org.dromara.workflow.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.workflow.domain.bo.ProcessDefinitionBo;
+import org.dromara.workflow.domain.vo.ProcessDefinitionVo;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * 娴佺▼瀹氫箟 鏈嶅姟灞�
+ *
+ * @author may
+ */
+public interface IActProcessDefinitionService {
+    /**
+     * 鍒嗛〉鏌ヨ
+     *
+     * @param processDefinitionBo 鍙傛暟
+     * @param pageQuery           鍒嗛〉
+     * @return 杩斿洖鍒嗛〉鍒楄〃
+     */
+    TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo processDefinitionBo, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ鍘嗗彶娴佺▼瀹氫箟鍒楄〃
+     *
+     * @param key 娴佺▼瀹氫箟key
+     * @return 缁撴灉
+     */
+    List<ProcessDefinitionVo> getListByKey(String key);
+
+    /**
+     * 鏌ョ湅娴佺▼瀹氫箟鍥剧墖
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    String definitionImage(String processDefinitionId);
+
+    /**
+     * 鏌ョ湅娴佺▼瀹氫箟xml鏂囦欢
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    String definitionXml(String processDefinitionId);
+
+    /**
+     * 鍒犻櫎娴佺▼瀹氫箟
+     *
+     * @param deploymentIds        閮ㄧ讲id
+     * @param processDefinitionIds 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    boolean deleteDeployment(List<String> deploymentIds, List<String> processDefinitionIds);
+
+    /**
+     * 婵�娲绘垨鑰呮寕璧锋祦绋嬪畾涔�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    boolean updateDefinitionState(String processDefinitionId);
+
+    /**
+     * 杩佺Щ娴佺▼瀹氫箟
+     *
+     * @param currentProcessDefinitionId 褰撳墠娴佺▼瀹氫箟id
+     * @param fromProcessDefinitionId    闇�瑕佽縼绉诲埌鐨勬祦绋嬪畾涔塱d
+     * @return 缁撴灉
+     */
+    boolean migrationDefinition(String currentProcessDefinitionId, String fromProcessDefinitionId);
+
+    /**
+     * 娴佺▼瀹氫箟杞崲涓烘ā鍨�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    boolean convertToModel(String processDefinitionId);
+
+    /**
+     * 閫氳繃zip鎴杧ml閮ㄧ讲娴佺▼瀹氫箟
+     *
+     * @param file         鏂囦欢
+     * @param categoryCode 鍒嗙被
+     */
+    void deployByFile(MultipartFile file, String categoryCode);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java
new file mode 100644
index 0000000..2fc0c1a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java
@@ -0,0 +1,118 @@
+package org.dromara.workflow.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.workflow.domain.bo.ProcessInstanceBo;
+import org.dromara.workflow.domain.bo.ProcessInvalidBo;
+import org.dromara.workflow.domain.bo.TaskUrgingBo;
+import org.dromara.workflow.domain.vo.ActHistoryInfoVo;
+import org.dromara.workflow.domain.vo.ProcessInstanceVo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 娴佺▼瀹炰緥 鏈嶅姟灞�
+ *
+ * @author may
+ */
+public interface IActProcessInstanceService {
+    /**
+     * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥�
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    String getHistoryImage(String processInstanceId);
+
+    /**
+     * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥捐繍琛屼腑锛屽巻鍙茬瓑鑺傜偣
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    Map<String, Object> getHistoryList(String processInstanceId);
+
+    /**
+     * 鍒嗛〉鏌ヨ姝e湪杩愯鐨勬祦绋嬪疄渚�
+     *
+     * @param processInstanceBo 鍙傛暟
+     * @param pageQuery         鍒嗛〉
+     * @return 缁撴灉
+     */
+    TableDataInfo<ProcessInstanceVo> getPageByRunning(ProcessInstanceBo processInstanceBo, PageQuery pageQuery);
+
+    /**
+     * 鍒嗛〉鏌ヨ宸茬粨鏉熺殑娴佺▼瀹炰緥
+     *
+     * @param processInstanceBo 鍙傛暟
+     * @param pageQuery         鍒嗛〉
+     * @return 缁撴灉
+     */
+    TableDataInfo<ProcessInstanceVo> getPageByFinish(ProcessInstanceBo processInstanceBo, PageQuery pageQuery);
+
+    /**
+     * 鑾峰彇瀹℃壒璁板綍
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    List<ActHistoryInfoVo> getHistoryRecord(String processInstanceId);
+
+    /**
+     * 浣滃簾娴佺▼瀹炰緥锛屼笉浼氬垹闄ゅ巻鍙茶褰�(鍒犻櫎杩愯涓殑瀹炰緥)
+     *
+     * @param processInvalidBo 鍙傛暟
+     * @return 缁撴灉
+     */
+    boolean deleteRunInstance(ProcessInvalidBo processInvalidBo);
+
+    /**
+     * 杩愯涓殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    boolean deleteRunAndHisInstance(List<String> processInstanceIds);
+
+    /**
+     * 鎸夌収涓氬姟id鍒犻櫎 杩愯涓殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param businessKeys 涓氬姟id
+     * @return 缁撴灉
+     */
+    boolean deleteRunAndHisInstanceByBusinessKeys(List<String> businessKeys);
+
+    /**
+     * 宸插畬鎴愮殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    boolean deleteFinishAndHisInstance(List<String> processInstanceIds);
+
+    /**
+     * 鎾ら攢娴佺▼鐢宠
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    boolean cancelProcessApply(String processInstanceId);
+
+    /**
+     * 鍒嗛〉鏌ヨ褰撳墠鐧诲綍浜哄崟鎹�
+     *
+     * @param processInstanceBo 鍙傛暟
+     * @param pageQuery         鍒嗛〉
+     * @return 缁撴灉
+     */
+    TableDataInfo<ProcessInstanceVo> getPageByCurrent(ProcessInstanceBo processInstanceBo, PageQuery pageQuery);
+
+    /**
+     * 浠诲姟鍌姙(缁欏綋鍓嶄换鍔″姙鐞嗕汉鍙戦�佺珯鍐呬俊锛岄偖浠讹紝鐭俊绛�)
+     *
+     * @param taskUrgingBo 浠诲姟鍌姙
+     * @return 缁撴灉
+     */
+    boolean taskUrging(TaskUrgingBo taskUrgingBo);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java
new file mode 100644
index 0000000..8e9f763
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java
@@ -0,0 +1,161 @@
+package org.dromara.workflow.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.workflow.domain.bo.*;
+import org.dromara.workflow.domain.vo.TaskVo;
+import org.dromara.workflow.domain.vo.VariableVo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 浠诲姟 鏈嶅姟灞�
+ *
+ * @author may
+ */
+public interface IActTaskService {
+    /**
+     * 鍚姩浠诲姟
+     *
+     * @param startProcessBo 鍚姩娴佺▼鍙傛暟
+     * @return 缁撴灉
+     */
+    Map<String, Object> startWorkFlow(StartProcessBo startProcessBo);
+
+
+    /**
+     * 鍔炵悊浠诲姟
+     *
+     * @param completeTaskBo 鍔炵悊浠诲姟鍙傛暟
+     * @return 缁撴灉
+     */
+    boolean completeTask(CompleteTaskBo completeTaskBo);
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勫緟鍔炰换鍔�
+     *
+     * @param taskBo    鍙傛暟
+     * @param pageQuery 鍒嗛〉
+     * @return 缁撴灉
+     */
+    TableDataInfo<TaskVo> getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ褰撳墠绉熸埛鎵�鏈夊緟鍔炰换鍔�
+     *
+     * @param taskBo    鍙傛暟
+     * @param pageQuery 鍒嗛〉
+     * @return 缁撴灉
+     */
+    TableDataInfo<TaskVo> getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery);
+
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勫凡鍔炰换鍔�
+     *
+     * @param taskBo    鍙傛暟
+     * @param pageQuery 鍙傛暟
+     * @return 缁撴灉
+     */
+    TableDataInfo<TaskVo> getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勬妱閫�
+     *
+     * @param taskBo    鍙傛暟
+     * @param pageQuery 鍙傛暟
+     * @return 缁撴灉
+     */
+    TableDataInfo<TaskVo> getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ褰撳墠绉熸埛鎵�鏈夊凡鍔炰换鍔�
+     *
+     * @param taskBo    鍙傛暟
+     * @param pageQuery 鍙傛暟
+     * @return 缁撴灉
+     */
+    TableDataInfo<TaskVo> getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery);
+
+    /**
+     * 濮旀淳浠诲姟
+     *
+     * @param delegateBo 鍙傛暟
+     * @return 缁撴灉
+     */
+    boolean delegateTask(DelegateBo delegateBo);
+
+    /**
+     * 缁堟浠诲姟
+     *
+     * @param terminationBo 鍙傛暟
+     * @return 缁撴灉
+     */
+    boolean terminationTask(TerminationBo terminationBo);
+
+    /**
+     * 杞姙浠诲姟
+     *
+     * @param transmitBo 鍙傛暟
+     * @return 缁撴灉
+     */
+    boolean transferTask(TransmitBo transmitBo);
+
+    /**
+     * 浼氱浠诲姟鍔犵
+     *
+     * @param addMultiBo 鍙傛暟
+     * @return 缁撴灉
+     */
+    boolean addMultiInstanceExecution(AddMultiBo addMultiBo);
+
+    /**
+     * 浼氱浠诲姟鍑忕
+     *
+     * @param deleteMultiBo 鍙傛暟
+     * @return 缁撴灉
+     */
+    boolean deleteMultiInstanceExecution(DeleteMultiBo deleteMultiBo);
+
+    /**
+     * 椹冲洖瀹℃壒
+     *
+     * @param backProcessBo 鍙傛暟
+     * @return 娴佺▼瀹炰緥id
+     */
+    String backProcess(BackProcessBo backProcessBo);
+
+    /**
+     * 淇敼浠诲姟鍔炵悊浜�
+     *
+     * @param taskIds 浠诲姟id
+     * @param userId  鍔炵悊浜篿d
+     * @return 缁撴灉
+     */
+    boolean updateAssignee(String[] taskIds, String userId);
+
+    /**
+     * 鏌ヨ娴佺▼鍙橀噺
+     *
+     * @param taskId 浠诲姟id
+     * @return 缁撴灉
+     */
+    List<VariableVo> getInstanceVariable(String taskId);
+
+    /**
+     * 鏌ヨ宸ヤ綔娴佷换鍔$敤鎴烽�夋嫨鍔犵浜哄憳
+     *
+     * @param taskId 浠诲姟id
+     * @return 缁撴灉
+     */
+    String getTaskUserIdsByAddMultiInstance(String taskId);
+
+    /**
+     * 鏌ヨ宸ヤ綔娴侀�夋嫨鍑忕浜哄憳
+     *
+     * @param taskId 浠诲姟id
+     * @return 缁撴灉
+     */
+    List<TaskVo> getListByDeleteMultiInstance(String taskId);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java
new file mode 100644
index 0000000..5c95547
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/ITestLeaveService.java
@@ -0,0 +1,49 @@
+package org.dromara.workflow.service;
+
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.workflow.domain.TestLeave;
+import org.dromara.workflow.domain.bo.TestLeaveBo;
+import org.dromara.workflow.domain.vo.TestLeaveVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 璇峰亣Service鎺ュ彛
+ *
+ * @author may
+ * @date 2023-07-21
+ */
+public interface ITestLeaveService {
+
+    /**
+     * 鏌ヨ璇峰亣
+     */
+    TestLeaveVo queryById(Long id);
+
+    /**
+     * 鏌ヨ璇峰亣鍒楄〃
+     */
+    TableDataInfo<TestLeaveVo> queryPageList(TestLeaveBo bo, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ璇峰亣鍒楄〃
+     */
+    List<TestLeaveVo> queryList(TestLeaveBo bo);
+
+    /**
+     * 鏂板璇峰亣
+     */
+    TestLeaveVo insertByBo(TestLeaveBo bo);
+
+    /**
+     * 淇敼璇峰亣
+     */
+    TestLeaveVo updateByBo(TestLeaveBo bo);
+
+    /**
+     * 鏍¢獙骞舵壒閲忓垹闄よ鍋囦俊鎭�
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java
new file mode 100644
index 0000000..acf0aa2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java
@@ -0,0 +1,51 @@
+package org.dromara.workflow.service;
+
+import org.dromara.workflow.domain.WfCategory;
+import org.dromara.workflow.domain.bo.WfCategoryBo;
+import org.dromara.workflow.domain.vo.WfCategoryVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 娴佺▼鍒嗙被Service鎺ュ彛
+ *
+ * @author may
+ * @date 2023-06-28
+ */
+public interface IWfCategoryService {
+
+    /**
+     * 鏌ヨ娴佺▼鍒嗙被
+     */
+    WfCategoryVo queryById(Long id);
+
+
+    /**
+     * 鏌ヨ娴佺▼鍒嗙被鍒楄〃
+     */
+    List<WfCategoryVo> queryList(WfCategoryBo bo);
+
+    /**
+     * 鏂板娴佺▼鍒嗙被
+     */
+    Boolean insertByBo(WfCategoryBo bo);
+
+    /**
+     * 淇敼娴佺▼鍒嗙被
+     */
+    Boolean updateByBo(WfCategoryBo bo);
+
+    /**
+     * 鏍¢獙骞舵壒閲忓垹闄ゆ祦绋嬪垎绫讳俊鎭�
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 鎸夌収绫诲埆缂栫爜鏌ヨ
+     *
+     * @param categoryCode 鍒嗙被姣斿悧
+     * @return 缁撴灉
+     */
+    WfCategory queryByCategoryCode(String categoryCode);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfDefinitionConfigService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfDefinitionConfigService.java
new file mode 100644
index 0000000..fe5cf7a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfDefinitionConfigService.java
@@ -0,0 +1,83 @@
+package org.dromara.workflow.service;
+
+import org.dromara.workflow.domain.vo.WfDefinitionConfigVo;
+import org.dromara.workflow.domain.bo.WfDefinitionConfigBo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 娴佺▼瀹氫箟閰嶇疆Service鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-18
+ */
+public interface IWfDefinitionConfigService {
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆
+     *
+     * @param definitionId 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    WfDefinitionConfigVo getByDefId(String definitionId);
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆
+     *
+     * @param tableName 琛ㄥ悕
+     * @return 缁撴灉
+     */
+    WfDefinitionConfigVo getByTableNameLastVersion(String tableName);
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆
+     *
+     * @param definitionId 娴佺▼瀹氫箟id
+     * @param tableName    琛ㄥ悕
+     * @return 缁撴灉
+     */
+    WfDefinitionConfigVo getByDefIdAndTableName(String definitionId, String tableName);
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆鎺掗櫎褰撳墠鏌ヨ鐨勬祦绋嬪畾涔�
+     *
+     * @param definitionId 娴佺▼瀹氫箟id
+     * @param tableName    琛ㄥ悕
+     * @return 缁撴灉
+     */
+    List<WfDefinitionConfigVo> getByTableNameNotDefId(String tableName, String definitionId);
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆鍒楄〃
+     *
+     * @param definitionIds 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    List<WfDefinitionConfigVo> queryList(List<String> definitionIds);
+
+
+    /**
+     * 鏂板娴佺▼瀹氫箟閰嶇疆
+     *
+     * @param bo 鍙傛暟
+     * @return 缁撴灉
+     */
+    Boolean saveOrUpdate(WfDefinitionConfigBo bo);
+
+    /**
+     * 鍒犻櫎
+     *
+     * @param ids id
+     * @return 缁撴灉
+     */
+    Boolean deleteByIds(Collection<Long> ids);
+
+    /**
+     * 鎸夌収娴佺▼瀹氫箟id鍒犻櫎
+     *
+     * @param ids 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    Boolean deleteByDefIds(Collection<String> ids);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfFormManageService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfFormManageService.java
new file mode 100644
index 0000000..2ca2264
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfFormManageService.java
@@ -0,0 +1,81 @@
+package org.dromara.workflow.service;
+
+import org.dromara.workflow.domain.vo.WfFormManageVo;
+import org.dromara.workflow.domain.bo.WfFormManageBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 琛ㄥ崟绠$悊Service鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-29
+ */
+public interface IWfFormManageService {
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊
+     *
+     * @param id 涓婚敭
+     * @return 缁撴灉
+     */
+    WfFormManageVo queryById(Long id);
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊
+     *
+     * @param ids 涓婚敭
+     * @return 缁撴灉
+     */
+    List<WfFormManageVo> queryByIds(List<Long> ids);
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+     *
+     * @param bo        鍙傛暟
+     * @param pageQuery 鍒嗛〉
+     * @return 缁撴灉
+     */
+    TableDataInfo<WfFormManageVo> queryPageList(WfFormManageBo bo, PageQuery pageQuery);
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+     *
+     * @return 缁撴灉
+     */
+    List<WfFormManageVo> selectList();
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+     *
+     * @param bo 鍙傛暟
+     * @return 缁撴灉
+     */
+    List<WfFormManageVo> queryList(WfFormManageBo bo);
+
+    /**
+     * 鏂板琛ㄥ崟绠$悊
+     *
+     * @param bo 鍙傛暟
+     * @return 缁撴灉
+     */
+    Boolean insertByBo(WfFormManageBo bo);
+
+    /**
+     * 淇敼琛ㄥ崟绠$悊
+     *
+     * @param bo 鍙傛暟
+     * @return 缁撴灉
+     */
+    Boolean updateByBo(WfFormManageBo bo);
+
+    /**
+     * 鎵归噺鍒犻櫎琛ㄥ崟绠$悊淇℃伅
+     *
+     * @param ids 涓婚敭
+     * @return 缁撴灉
+     */
+    Boolean deleteByIds(Collection<Long> ids);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfNodeConfigService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfNodeConfigService.java
new file mode 100644
index 0000000..5e64d64
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfNodeConfigService.java
@@ -0,0 +1,56 @@
+package org.dromara.workflow.service;
+
+import org.dromara.workflow.domain.WfNodeConfig;
+import org.dromara.workflow.domain.vo.WfNodeConfigVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 鑺傜偣閰嶇疆Service鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-30
+ */
+public interface IWfNodeConfigService {
+
+    /**
+     * 鏌ヨ鑺傜偣閰嶇疆
+     *
+     * @param id 涓婚敭
+     * @return 缁撴灉
+     */
+    WfNodeConfigVo queryById(Long id);
+
+    /**
+     * 淇濆瓨鑺傜偣閰嶇疆
+     *
+     * @param list 鍙傛暟
+     * @return 缁撴灉
+     */
+    Boolean saveOrUpdate(List<WfNodeConfig> list);
+
+    /**
+     * 鎵归噺鍒犻櫎鑺傜偣閰嶇疆淇℃伅
+     *
+     * @param ids 涓婚敭
+     * @return 缁撴灉
+     */
+    Boolean deleteByIds(Collection<Long> ids);
+
+    /**
+     * 鎸夌収娴佺▼瀹氫箟id鍒犻櫎
+     *
+     * @param ids 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    Boolean deleteByDefIds(Collection<String> ids);
+
+    /**
+     * 鎸夌収娴佺▼瀹氫箟id鏌ヨ
+     *
+     * @param ids 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    List<WfNodeConfigVo> selectByDefIds(Collection<String> ids);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java
new file mode 100644
index 0000000..97f9406
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java
@@ -0,0 +1,65 @@
+package org.dromara.workflow.service;
+
+
+import org.dromara.workflow.domain.WfTaskBackNode;
+import org.flowable.task.api.Task;
+
+import java.util.List;
+
+/**
+ * 鑺傜偣椹冲洖璁板綍Service鎺ュ彛
+ *
+ * @author may
+ * @date 2024-03-13
+ */
+public interface IWfTaskBackNodeService {
+
+    /**
+     * 璁板綍瀹℃壒鑺傜偣
+     *
+     * @param task 浠诲姟
+     */
+    void recordExecuteNode(Task task);
+
+    /**
+     * 鎸夋祦绋嬪疄渚媔d鏌ヨ
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    List<WfTaskBackNode> getListByInstanceId(String processInstanceId);
+
+    /**
+     * 鎸夌収娴佺▼瀹炰緥id锛岃妭鐐筰d鏌ヨ
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     * @param nodeId            鑺傜偣id
+     * @return 缁撴灉
+     */
+    WfTaskBackNode getListByInstanceIdAndNodeId(String processInstanceId, String nodeId);
+
+    /**
+     * 鍒犻櫎椹冲洖鍚庣殑鑺傜偣
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     * @param targetActivityId  鑺傜偣id
+     * @return 缁撴灉
+     */
+    boolean deleteBackTaskNode(String processInstanceId, String targetActivityId);
+
+    /**
+     * 鎸夋祦绋嬪疄渚媔d鍒犻櫎
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    boolean deleteByInstanceId(String processInstanceId);
+
+    /**
+     * 鎸夋祦绋嬪疄渚媔d鍒犻櫎
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     * @return 缁撴灉
+     */
+    boolean deleteByInstanceIds(List<String> processInstanceIds);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java
new file mode 100644
index 0000000..06d607b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java
@@ -0,0 +1,51 @@
+package org.dromara.workflow.service.impl;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.workflow.domain.ActHiProcinst;
+import org.dromara.workflow.mapper.ActHiProcinstMapper;
+import org.dromara.workflow.service.IActHiProcinstService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+
+/**
+ * 娴佺▼瀹炰緥Service涓氬姟灞傚鐞�
+ *
+ * @author may
+ * @date 2023-07-22
+ */
+@RequiredArgsConstructor
+@Service
+public class ActHiProcinstServiceImpl implements IActHiProcinstService {
+
+    private final ActHiProcinstMapper baseMapper;
+
+    /**
+     * 鎸夌収涓氬姟id鏌ヨ
+     *
+     * @param businessKeys 涓氬姟id
+     */
+    @Override
+    public List<ActHiProcinst> selectByBusinessKeyIn(List<String> businessKeys) {
+        return baseMapper.selectList(new LambdaQueryWrapper<ActHiProcinst>()
+            .in(ActHiProcinst::getBusinessKey, businessKeys)
+            .eq(TenantHelper.isEnable(), ActHiProcinst::getTenantId, TenantHelper.getTenantId()));
+    }
+
+    /**
+     * 鎸夌収涓氬姟id鏌ヨ
+     *
+     * @param businessKey 涓氬姟id
+     */
+    @Override
+    public ActHiProcinst selectByBusinessKey(String businessKey) {
+        return baseMapper.selectOne(new LambdaQueryWrapper<ActHiProcinst>()
+            .eq(ActHiProcinst::getBusinessKey, businessKey)
+            .eq(TenantHelper.isEnable(), ActHiProcinst::getTenantId, TenantHelper.getTenantId()));
+
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java
new file mode 100644
index 0000000..5548f22
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java
@@ -0,0 +1,18 @@
+package org.dromara.workflow.service.impl;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.dromara.workflow.service.IActHiTaskinstService;
+
+
+/**
+ * 娴佺▼鍘嗗彶浠诲姟Service涓氬姟灞傚鐞�
+ *
+ * @author may
+ * @date 2024-03-02
+ */
+@RequiredArgsConstructor
+@Service
+public class ActHiTaskinstServiceImpl implements IActHiTaskinstService {
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java
new file mode 100644
index 0000000..7ea53d8
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java
@@ -0,0 +1,424 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Validator;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.ZipUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.excel.util.StringUtils;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.apache.batik.transcoder.TranscoderInput;
+import org.apache.batik.transcoder.TranscoderOutput;
+import org.apache.batik.transcoder.image.PNGTranscoder;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.workflow.common.constant.FlowConstant;
+import org.dromara.workflow.domain.WfNodeConfig;
+import org.dromara.workflow.domain.bo.ModelBo;
+import org.dromara.workflow.domain.bo.WfDefinitionConfigBo;
+import org.dromara.workflow.domain.vo.ModelVo;
+import org.dromara.workflow.domain.vo.WfDefinitionConfigVo;
+import org.dromara.workflow.service.IActModelService;
+import org.dromara.workflow.service.IWfDefinitionConfigService;
+import org.dromara.workflow.service.IWfNodeConfigService;
+import org.dromara.workflow.utils.ModelUtils;
+import org.dromara.workflow.utils.QueryUtils;
+import org.flowable.bpmn.converter.BpmnXMLConverter;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.Process;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.engine.repository.Model;
+import org.flowable.engine.repository.ModelQuery;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.validation.ValidationError;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * 妯″瀷绠$悊 鏈嶅姟灞傚疄鐜�
+ *
+ * @author may
+ */
+@RequiredArgsConstructor
+@Service
+public class ActModelServiceImpl implements IActModelService {
+
+    private final RepositoryService repositoryService;
+    private final IWfNodeConfigService wfNodeConfigService;
+    private final IWfDefinitionConfigService wfDefinitionConfigService;
+
+    /**
+     * 鍒嗛〉鏌ヨ妯″瀷
+     *
+     * @param modelBo 妯″瀷鍙傛暟
+     * @return 杩斿洖鍒嗛〉鍒楄〃
+     */
+    @Override
+    public TableDataInfo<Model> page(ModelBo modelBo, PageQuery pageQuery) {
+        ModelQuery query = QueryUtils.modelQuery();
+        if (StringUtils.isNotBlank(modelBo.getName())) {
+            query.modelNameLike("%" + modelBo.getName() + "%");
+        }
+        if (StringUtils.isNotBlank(modelBo.getKey())) {
+            query.modelKey(modelBo.getKey());
+        }
+        if (StringUtils.isNotBlank(modelBo.getCategoryCode())) {
+            query.modelCategory(modelBo.getCategoryCode());
+        }
+        query.orderByLastUpdateTime().desc();
+        // 鍒涘缓鏃堕棿闄嶅簭鎺掑垪
+        query.orderByCreateTime().desc();
+        // 鍒嗛〉鏌ヨ
+        List<Model> modelList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        // 鎬昏褰曟暟
+        long total = query.count();
+        TableDataInfo<Model> build = TableDataInfo.build();
+        build.setRows(modelList);
+        build.setTotal(total);
+        return build;
+    }
+
+    /**
+     * 鏂板妯″瀷
+     *
+     * @param modelBo 妯″瀷璇锋眰瀵硅薄
+     * @return 缁撴灉
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean saveNewModel(ModelBo modelBo) {
+        try {
+            int version = 0;
+            String key = modelBo.getKey();
+            String name = modelBo.getName();
+            String description = modelBo.getDescription();
+            String categoryCode = modelBo.getCategoryCode();
+            String xml = modelBo.getXml();
+            Model checkModel = QueryUtils.modelQuery().modelKey(key).singleResult();
+            if (ObjectUtil.isNotNull(checkModel)) {
+                throw new ServiceException("妯″瀷key宸插瓨鍦紒");
+            }
+            //鍒濆绌虹殑妯″瀷
+            Model model = repositoryService.newModel();
+            model.setKey(key);
+            model.setName(name);
+            model.setVersion(version);
+            model.setCategory(categoryCode);
+            model.setMetaInfo(description);
+            model.setTenantId(TenantHelper.getTenantId());
+            //淇濆瓨鍒濆鍖栫殑妯″瀷鍩烘湰淇℃伅鏁版嵁
+            repositoryService.saveModel(model);
+            repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml));
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 鏌ヨ妯″瀷
+     *
+     * @param id 妯″瀷id
+     * @return 妯″瀷鏁版嵁
+     */
+    @Override
+    public ModelVo getInfo(String id) {
+        ModelVo modelVo = new ModelVo();
+        Model model = repositoryService.getModel(id);
+        if (model != null) {
+            try {
+                byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
+                modelVo.setXml(StrUtil.utf8Str(modelEditorSource));
+                modelVo.setId(model.getId());
+                modelVo.setKey(model.getKey());
+                modelVo.setName(model.getName());
+                modelVo.setCategoryCode(model.getCategory());
+                modelVo.setDescription(model.getMetaInfo());
+                return modelVo;
+            } catch (Exception e) {
+                throw new ServiceException(e.getMessage());
+            }
+        }
+        return modelVo;
+    }
+
+    /**
+     * 淇敼妯″瀷淇℃伅
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     * @return 缁撴灉
+     */
+    @Override
+    public boolean update(ModelBo modelBo) {
+        try {
+            Model model = repositoryService.getModel(modelBo.getId());
+            List<Model> list = QueryUtils.modelQuery().modelKey(modelBo.getKey()).list();
+            list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> {
+                throw new ServiceException("妯″瀷KEY宸插瓨鍦紒");
+            });
+            model.setCategory(modelBo.getCategoryCode());
+            model.setMetaInfo(modelBo.getDescription());
+            repositoryService.saveModel(model);
+        } catch (Exception e) {
+            throw new ServiceException(e.getMessage());
+        }
+        return true;
+    }
+
+    /**
+     * 缂栬緫妯″瀷XML
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     * @return 缁撴灉
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean editModelXml(ModelBo modelBo) {
+        try {
+            String xml = modelBo.getXml();
+            String svg = modelBo.getSvg();
+            String modelId = modelBo.getId();
+            String key = modelBo.getKey();
+            String name = modelBo.getName();
+            BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xml);
+            ModelUtils.checkBpmnModel(bpmnModel);
+            Model model = repositoryService.getModel(modelId);
+            List<Model> list = QueryUtils.modelQuery().modelKey(key).list();
+            list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> {
+                throw new ServiceException("妯″瀷KEY宸插瓨鍦紒");
+            });
+            // 鏍¢獙key鍛藉悕瑙勮寖
+            if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, key)) {
+                throw new ServiceException("妯″瀷鏍囪瘑KEY鍙兘瀛楃鎴栬�呬笅鍒掔嚎寮�澶达紒");
+            }
+            model.setKey(key);
+            model.setName(name);
+            model.setVersion(model.getVersion() + 1);
+            repositoryService.saveModel(model);
+            repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml));
+            // 杞崲鍥剧墖
+            InputStream svgStream = new ByteArrayInputStream(StrUtil.utf8Bytes(svg));
+            TranscoderInput input = new TranscoderInput(svgStream);
+
+            PNGTranscoder transcoder = new PNGTranscoder();
+            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+            TranscoderOutput output = new TranscoderOutput(outStream);
+
+            transcoder.transcode(input, output);
+            final byte[] result = outStream.toByteArray();
+            repositoryService.addModelEditorSourceExtra(model.getId(), result);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 妯″瀷閮ㄧ讲
+     *
+     * @param id 妯″瀷id
+     * @return 缁撴灉
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean modelDeploy(String id) {
+        try {
+            // 鏌ヨ娴佺▼瀹氫箟妯″瀷xml
+            byte[] xmlBytes = repositoryService.getModelEditorSource(id);
+            if (ArrayUtil.isEmpty(xmlBytes)) {
+                throw new ServiceException("妯″瀷鏁版嵁涓虹┖锛岃鍏堣璁℃祦绋嬪畾涔夋ā鍨嬶紝鍐嶈繘琛岄儴缃诧紒");
+            }
+            if (JSONUtil.isTypeJSON(new String(xmlBytes, StandardCharsets.UTF_8))) {
+                byte[] bytes = ModelUtils.bpmnJsonToXmlBytes(xmlBytes);
+                if (ArrayUtil.isEmpty(bytes)) {
+                    throw new ServiceException("妯″瀷涓嶈兘涓虹┖锛岃鑷冲皯璁捐涓�鏉′富绾挎祦绋嬶紒");
+                }
+            }
+            BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xmlBytes);
+            // 鏍¢獙妯″瀷
+            ModelUtils.checkBpmnModel(bpmnModel);
+            List<ValidationError> validationErrors = repositoryService.validateProcess(bpmnModel);
+            if (CollUtil.isNotEmpty(validationErrors)) {
+                String errorMsg = validationErrors.stream().map(ValidationError::getProblem).distinct().collect(Collectors.joining(","));
+                throw new ServiceException(errorMsg);
+            }
+            // 鏌ヨ妯″瀷鐨勫熀鏈俊鎭�
+            Model model = repositoryService.getModel(id);
+            ProcessDefinition processDefinition = QueryUtils.definitionQuery().processDefinitionKey(model.getKey()).latestVersion().singleResult();
+            // xml璧勬簮鐨勫悕绉� 锛屽搴攁ct_ge_bytearray琛ㄤ腑鐨刵ame_瀛楁
+            String processName = model.getName() + ".bpmn20.xml";
+            // 璋冪敤閮ㄧ讲鐩稿叧鐨刟pi鏂规硶杩涜閮ㄧ讲娴佺▼瀹氫箟
+            Deployment deployment = repositoryService.createDeployment()
+                // 閮ㄧ讲鍚嶇О
+                .name(model.getName())
+                // 閮ㄧ讲鏍囪瘑key
+                .key(model.getKey())
+                // 閮ㄧ讲娴佺▼鍒嗙被
+                .category(model.getCategory())
+                // bpmn20.xml璧勬簮
+                .addBytes(processName, xmlBytes)
+                // 绉熸埛id
+                .tenantId(TenantHelper.getTenantId())
+                .deploy();
+
+            // 鏇存柊 閮ㄧ讲id 鍒版祦绋嬪畾涔夋ā鍨嬫暟鎹〃涓�
+            model.setDeploymentId(deployment.getId());
+            repositoryService.saveModel(model);
+            // 鏇存柊鍒嗙被
+            ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult();
+            repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory());
+            //鏇存柊娴佺▼瀹氫箟閰嶇疆
+            if (processDefinition != null) {
+                WfDefinitionConfigVo definitionVo = wfDefinitionConfigService.getByDefId(processDefinition.getId());
+                if (definitionVo != null) {
+                    wfDefinitionConfigService.deleteByDefIds(Collections.singletonList(processDefinition.getId()));
+                    WfDefinitionConfigBo wfFormDefinition = new WfDefinitionConfigBo();
+                    wfFormDefinition.setDefinitionId(definition.getId());
+                    wfFormDefinition.setProcessKey(definition.getKey());
+                    wfFormDefinition.setTableName(definitionVo.getTableName());
+                    wfFormDefinition.setVersion(definition.getVersion());
+                    wfFormDefinition.setRemark(definitionVo.getRemark());
+                    wfDefinitionConfigService.saveOrUpdate(wfFormDefinition);
+                }
+            }
+            //鏇存柊娴佺▼鑺傜偣閰嶇疆琛ㄥ崟
+            List<UserTask> userTasks = ModelUtils.getUserTaskFlowElements(definition.getId());
+            UserTask applyUserTask = ModelUtils.getApplyUserTask(definition.getId());
+            List<WfNodeConfig> wfNodeConfigList = new ArrayList<>();
+            for (UserTask userTask : userTasks) {
+                if (StringUtils.isNotBlank(userTask.getFormKey()) && userTask.getFormKey().contains(StrUtil.COLON)) {
+                    WfNodeConfig wfNodeConfig = new WfNodeConfig();
+                    wfNodeConfig.setNodeId(userTask.getId());
+                    wfNodeConfig.setNodeName(userTask.getName());
+                    wfNodeConfig.setDefinitionId(definition.getId());
+                    String[] split = userTask.getFormKey().split(StrUtil.COLON);
+                    wfNodeConfig.setFormType(split[0]);
+                    wfNodeConfig.setFormId(Long.valueOf(split[1]));
+                    wfNodeConfig.setApplyUserTask(applyUserTask.getId().equals(userTask.getId()) ? FlowConstant.TRUE : FlowConstant.FALSE);
+                    wfNodeConfigList.add(wfNodeConfig);
+                }
+            }
+            if (CollUtil.isNotEmpty(wfNodeConfigList)) {
+                wfNodeConfigService.saveOrUpdate(wfNodeConfigList);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 瀵煎嚭妯″瀷zip鍘嬬缉鍖�
+     *
+     * @param modelIds 妯″瀷id
+     * @param response 鐩稿簲
+     */
+    @Override
+    public void exportZip(List<String> modelIds, HttpServletResponse response) {
+        try (ZipOutputStream zos = ZipUtil.getZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8)) {
+            // 鍘嬬缉鍖呮枃浠跺悕
+            String zipName = "妯″瀷涓嶅瓨鍦�";
+            // 鏌ヨ妯″瀷鍩烘湰淇℃伅
+            for (String modelId : modelIds) {
+                Model model = repositoryService.getModel(modelId);
+                byte[] xmlBytes = repositoryService.getModelEditorSource(modelId);
+                if (ObjectUtil.isNotNull(model)) {
+                    if (JSONUtil.isTypeJSON(new String(xmlBytes, StandardCharsets.UTF_8)) && ArrayUtil.isEmpty(ModelUtils.bpmnJsonToXmlBytes(xmlBytes))) {
+                        zipName = "妯″瀷涓嶈兘涓虹┖锛岃鑷冲皯璁捐涓�鏉′富绾挎祦绋嬶紒";
+                        zos.putNextEntry(new ZipEntry(zipName + ".txt"));
+                        zos.write(zipName.getBytes(StandardCharsets.UTF_8));
+                    } else if (ArrayUtil.isEmpty(xmlBytes)) {
+                        zipName = "妯″瀷鏁版嵁涓虹┖锛岃鍏堣璁℃祦绋嬪畾涔夋ā鍨嬶紝鍐嶈繘琛岄儴缃诧紒";
+                        zos.putNextEntry(new ZipEntry(zipName + ".txt"));
+                        zos.write(zipName.getBytes(StandardCharsets.UTF_8));
+                    } else {
+                        String fileName = model.getName() + "-" + model.getKey();
+                        // 鍘嬬缉鍖呮枃浠跺悕
+                        zipName = fileName + ".zip";
+                        // 灏唜ml娣诲姞鍒板帇缂╁寘涓�(鎸囧畾xml鏂囦欢鍚嶏細璇峰亣娴佺▼.bpmn20.xml
+                        zos.putNextEntry(new ZipEntry(fileName + ".bpmn20.xml"));
+                        zos.write(xmlBytes);
+                    }
+                }
+            }
+            response.setHeader("Content-Disposition",
+                "attachment; filename=" + URLEncoder.encode(zipName, StandardCharsets.UTF_8) + ".zip");
+            response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            // 鍒峰嚭鍝嶅簲娴�
+            response.flushBuffer();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 澶嶅埗妯″瀷
+     *
+     * @param modelBo 妯″瀷鏁版嵁
+     * @return 缁撴灉
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean copyModel(ModelBo modelBo) {
+        try {
+            String key = modelBo.getKey();
+            if (StringUtils.isNotBlank(key)) {
+                // 鏌ヨ妯″瀷
+                Model model = repositoryService.createModelQuery().modelId(modelBo.getId()).singleResult();
+                if (ObjectUtil.isNotNull(model)) {
+                    byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId());
+                    List<Model> list = QueryUtils.modelQuery().modelKey(key).list();
+                    if (CollUtil.isNotEmpty(list)) {
+                        throw new ServiceException("妯″瀷KEY宸插瓨鍦紒");
+                    }
+                    // 鏍¢獙key鍛藉悕瑙勮寖
+                    if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, key)) {
+                        throw new ServiceException("妯″瀷鏍囪瘑KEY鍙兘瀛楃鎴栬�呬笅鍒掔嚎寮�澶达紒");
+                    }
+                    // 澶嶅埗妯″瀷鏁版嵁
+                    Model newModel = repositoryService.newModel();
+                    newModel.setKey(modelBo.getKey());
+                    newModel.setName(modelBo.getName());
+                    newModel.setCategory(modelBo.getCategoryCode());
+                    newModel.setVersion(1);
+                    newModel.setMetaInfo(modelBo.getDescription());
+                    newModel.setTenantId(TenantHelper.getTenantId());
+                    String xml = StrUtil.utf8Str(modelEditorSource);
+                    BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xml);
+                    Process mainProcess = bpmnModel.getMainProcess();
+                    mainProcess.setId(modelBo.getKey());
+                    mainProcess.setName(modelBo.getName());
+                    byte[] xmlBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
+                    repositoryService.saveModel(newModel);
+                    repositoryService.addModelEditorSource(newModel.getId(), xmlBytes);
+                }
+            }
+        } catch (Exception e) {
+            throw new ServiceException(e.getMessage());
+        }
+        return true;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java
new file mode 100644
index 0000000..c37e0d0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java
@@ -0,0 +1,438 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.workflow.common.constant.FlowConstant;
+import org.dromara.workflow.domain.WfCategory;
+import org.dromara.workflow.domain.WfDefinitionConfig;
+import org.dromara.workflow.domain.WfNodeConfig;
+import org.dromara.workflow.domain.bo.ProcessDefinitionBo;
+import org.dromara.workflow.domain.bo.WfDefinitionConfigBo;
+import org.dromara.workflow.domain.vo.ProcessDefinitionVo;
+import org.dromara.workflow.domain.vo.WfDefinitionConfigVo;
+import org.dromara.workflow.mapper.WfDefinitionConfigMapper;
+import org.dromara.workflow.service.IActProcessDefinitionService;
+import org.dromara.workflow.service.IWfCategoryService;
+import org.dromara.workflow.service.IWfDefinitionConfigService;
+import org.dromara.workflow.service.IWfNodeConfigService;
+import org.dromara.workflow.utils.ModelUtils;
+import org.dromara.workflow.utils.QueryUtils;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.engine.ProcessMigrationService;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.impl.bpmn.deployer.ResourceNameUtil;
+import org.flowable.engine.repository.*;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * 娴佺▼瀹氫箟 鏈嶅姟灞傚疄鐜�
+ *
+ * @author may
+ */
+@RequiredArgsConstructor
+@Service
+public class ActProcessDefinitionServiceImpl implements IActProcessDefinitionService {
+
+    private final RepositoryService repositoryService;
+    private final ProcessMigrationService processMigrationService;
+    private final IWfCategoryService wfCategoryService;
+    private final IWfDefinitionConfigService wfDefinitionConfigService;
+    private final WfDefinitionConfigMapper wfDefinitionConfigMapper;
+    private final IWfNodeConfigService wfNodeConfigService;
+
+    /**
+     * 鍒嗛〉鏌ヨ
+     *
+     * @param bo 鍙傛暟
+     * @return 杩斿洖鍒嗛〉鍒楄〃
+     */
+    @Override
+    public TableDataInfo<ProcessDefinitionVo> page(ProcessDefinitionBo bo, PageQuery pageQuery) {
+        ProcessDefinitionQuery query = QueryUtils.definitionQuery();
+        if (StringUtils.isNotEmpty(bo.getKey())) {
+            query.processDefinitionKey(bo.getKey());
+        }
+        if (StringUtils.isNotEmpty(bo.getCategoryCode())) {
+            query.processDefinitionCategory(bo.getCategoryCode());
+        }
+        if (StringUtils.isNotEmpty(bo.getName())) {
+            query.processDefinitionNameLike("%" + bo.getName() + "%");
+        }
+        query.orderByDeploymentId().desc();
+        // 鍒嗛〉鏌ヨ
+        List<ProcessDefinitionVo> processDefinitionVoList = new ArrayList<>();
+        List<ProcessDefinition> definitionList = query.latestVersion().listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        List<Deployment> deploymentList = null;
+        if (CollUtil.isNotEmpty(definitionList)) {
+            List<String> deploymentIds = StreamUtils.toList(definitionList, ProcessDefinition::getDeploymentId);
+            deploymentList = QueryUtils.deploymentQuery(deploymentIds).list();
+        }
+        if (CollUtil.isNotEmpty(definitionList)) {
+            List<String> ids = StreamUtils.toList(definitionList, ProcessDefinition::getId);
+            List<WfDefinitionConfigVo> wfDefinitionConfigVos = wfDefinitionConfigService.queryList(ids);
+            for (ProcessDefinition processDefinition : definitionList) {
+                ProcessDefinitionVo processDefinitionVo = BeanUtil.toBean(processDefinition, ProcessDefinitionVo.class);
+                if (CollUtil.isNotEmpty(deploymentList)) {
+                    // 閮ㄧ讲鏃堕棿
+                    deploymentList.stream().filter(e -> e.getId().equals(processDefinition.getDeploymentId())).findFirst().ifPresent(e -> {
+                        processDefinitionVo.setDeploymentTime(e.getDeploymentTime());
+                    });
+                }
+                if (CollUtil.isNotEmpty(wfDefinitionConfigVos)) {
+                    wfDefinitionConfigVos.stream().filter(e -> e.getDefinitionId().equals(processDefinition.getId())).findFirst().ifPresent(processDefinitionVo::setWfDefinitionConfigVo);
+                }
+                processDefinitionVoList.add(processDefinitionVo);
+            }
+        }
+        // 鎬昏褰曟暟
+        long total = query.count();
+        TableDataInfo<ProcessDefinitionVo> build = TableDataInfo.build();
+        build.setRows(processDefinitionVoList);
+        build.setTotal(total);
+        return build;
+    }
+
+    /**
+     * 鏌ヨ鍘嗗彶娴佺▼瀹氫箟鍒楄〃
+     *
+     * @param key 娴佺▼瀹氫箟key
+     */
+    @Override
+    public List<ProcessDefinitionVo> getListByKey(String key) {
+        List<ProcessDefinitionVo> processDefinitionVoList = new ArrayList<>();
+        ProcessDefinitionQuery query = QueryUtils.definitionQuery();
+        List<ProcessDefinition> definitionList = query.processDefinitionKey(key).list();
+        List<Deployment> deploymentList = null;
+        if (CollUtil.isNotEmpty(definitionList)) {
+            List<String> deploymentIds = StreamUtils.toList(definitionList, ProcessDefinition::getDeploymentId);
+            deploymentList = QueryUtils.deploymentQuery(deploymentIds).list();
+        }
+        if (CollUtil.isNotEmpty(definitionList)) {
+            List<String> ids = StreamUtils.toList(definitionList, ProcessDefinition::getId);
+            List<WfDefinitionConfigVo> wfDefinitionConfigVos = wfDefinitionConfigService.queryList(ids);
+            for (ProcessDefinition processDefinition : definitionList) {
+                ProcessDefinitionVo processDefinitionVo = BeanUtil.toBean(processDefinition, ProcessDefinitionVo.class);
+                if (CollUtil.isNotEmpty(deploymentList)) {
+                    // 閮ㄧ讲鏃堕棿
+                    deploymentList.stream().filter(e -> e.getId().equals(processDefinition.getDeploymentId())).findFirst().ifPresent(e -> {
+                        processDefinitionVo.setDeploymentTime(e.getDeploymentTime());
+                    });
+                    if (CollUtil.isNotEmpty(wfDefinitionConfigVos)) {
+                        wfDefinitionConfigVos.stream().filter(e -> e.getDefinitionId().equals(processDefinition.getId())).findFirst().ifPresent(processDefinitionVo::setWfDefinitionConfigVo);
+                    }
+                }
+                processDefinitionVoList.add(processDefinitionVo);
+            }
+        }
+        return CollUtil.reverse(processDefinitionVoList);
+    }
+
+    /**
+     * 鏌ョ湅娴佺▼瀹氫箟鍥剧墖
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    @SneakyThrows
+    @Override
+    public String definitionImage(String processDefinitionId) {
+        InputStream inputStream = repositoryService.getProcessDiagram(processDefinitionId);
+        return Base64.encode(IoUtil.readBytes(inputStream));
+    }
+
+    /**
+     * 鏌ョ湅娴佺▼瀹氫箟xml鏂囦欢
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    @Override
+    public String definitionXml(String processDefinitionId) {
+        StringBuilder xml = new StringBuilder();
+        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
+        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
+        xml.append(IoUtil.read(inputStream, StandardCharsets.UTF_8));
+        return xml.toString();
+    }
+
+    /**
+     * 鍒犻櫎娴佺▼瀹氫箟
+     *
+     * @param deploymentIds        閮ㄧ讲id
+     * @param processDefinitionIds 娴佺▼瀹氫箟id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteDeployment(List<String> deploymentIds, List<String> processDefinitionIds) {
+        try {
+            List<HistoricProcessInstance> historicProcessInstances = QueryUtils.hisInstanceQuery().deploymentIdIn(deploymentIds).list();
+            if (CollUtil.isNotEmpty(historicProcessInstances)) {
+                Set<String> defIds = StreamUtils.toSet(historicProcessInstances, HistoricProcessInstance::getProcessDefinitionId);
+                List<ProcessDefinition> processDefinitions = QueryUtils.definitionQuery().processDefinitionIds(defIds).list();
+                if (CollUtil.isNotEmpty(processDefinitions)) {
+                    Set<String> keys = StreamUtils.toSet(processDefinitions, ProcessDefinition::getKey);
+                    throw new ServiceException("褰撳墠銆�" + String.join(",", keys) + "銆戞祦绋嬪畾涔夊凡琚娇鐢ㄤ笉鍙垹闄わ紒");
+                }
+            }
+            //鍒犻櫎娴佺▼瀹氫箟
+            for (String deploymentId : deploymentIds) {
+                repositoryService.deleteDeployment(deploymentId);
+            }
+            //鍒犻櫎娴佺▼瀹氫箟閰嶇疆
+            wfDefinitionConfigService.deleteByDefIds(processDefinitionIds);
+            //鍒犻櫎鑺傜偣閰嶇疆
+            wfNodeConfigService.deleteByDefIds(processDefinitionIds);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 婵�娲绘垨鑰呮寕璧锋祦绋嬪畾涔�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    @Override
+    public boolean updateDefinitionState(String processDefinitionId) {
+        try {
+            ProcessDefinition processDefinition = QueryUtils.definitionQuery()
+                .processDefinitionId(processDefinitionId).singleResult();
+            //灏嗗綋鍓嶄负鎸傝捣鐘舵�佹洿鏂颁负婵�娲荤姸鎬�
+            //鍙傛暟璇存槑锛氬弬鏁�1锛氭祦绋嬪畾涔塱d,鍙傛暟2锛氭槸鍚︽縺娲伙紙true鏄惁绾ц仈瀵瑰簲娴佺▼瀹炰緥锛屾縺娲讳簡鍒欏搴旀祦绋嬪疄渚嬮兘鍙互瀹℃壒锛夛紝
+            //鍙傛暟3锛氫粈涔堟椂鍊欐縺娲伙紝濡傛灉涓簄ull鍒欑珛鍗虫縺娲伙紝濡傛灉涓哄叿浣撴椂闂村垯鍒拌揪姝ゆ椂闂村悗婵�娲�
+            if (processDefinition.isSuspended()) {
+                repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
+            } else {
+                repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException("鎿嶄綔澶辫触:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 杩佺Щ娴佺▼瀹氫箟
+     *
+     * @param currentProcessDefinitionId 褰撳墠娴佺▼瀹氫箟id
+     * @param fromProcessDefinitionId    闇�瑕佽縼绉诲埌鐨勬祦绋嬪畾涔塱d
+     */
+
+    @Override
+    public boolean migrationDefinition(String currentProcessDefinitionId, String fromProcessDefinitionId) {
+        try {
+            // 杩佺Щ楠岃瘉
+            boolean migrationValid = processMigrationService.createProcessInstanceMigrationBuilder()
+                .migrateToProcessDefinition(currentProcessDefinitionId)
+                .validateMigrationOfProcessInstances(fromProcessDefinitionId)
+                .isMigrationValid();
+            if (!migrationValid) {
+                throw new ServiceException("娴佺▼瀹氫箟宸紓杩囧ぇ鏃犳硶杩佺Щ锛岃淇敼娴佺▼鍥�");
+            }
+            // 宸茬粨鏉熺殑娴佺▼瀹炰緥涓嶄細杩佺Щ
+            processMigrationService.createProcessInstanceMigrationBuilder()
+                .migrateToProcessDefinition(currentProcessDefinitionId)
+                .migrateProcessInstances(fromProcessDefinitionId);
+            return true;
+        } catch (Exception e) {
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 娴佺▼瀹氫箟杞崲涓烘ā鍨�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    @Override
+    public boolean convertToModel(String processDefinitionId) {
+        ProcessDefinition pd = QueryUtils.definitionQuery()
+            .processDefinitionId(processDefinitionId).singleResult();
+        InputStream inputStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getResourceName());
+        ModelQuery query = QueryUtils.modelQuery();
+        Model model = query.modelKey(pd.getKey()).singleResult();
+        try {
+            if (ObjectUtil.isNotNull(model)) {
+                repositoryService.addModelEditorSource(model.getId(), IoUtil.readBytes(inputStream));
+            } else {
+                Model modelData = repositoryService.newModel();
+                modelData.setKey(pd.getKey());
+                modelData.setName(pd.getName());
+                modelData.setTenantId(pd.getTenantId());
+                repositoryService.saveModel(modelData);
+                repositoryService.addModelEditorSource(modelData.getId(), IoUtil.readBytes(inputStream));
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 閫氳繃zip鎴杧ml閮ㄧ讲娴佺▼瀹氫箟
+     *
+     * @param file         鏂囦欢
+     * @param categoryCode 鍒嗙被
+     */
+    @SneakyThrows
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deployByFile(MultipartFile file, String categoryCode) {
+
+        WfCategory wfCategory = wfCategoryService.queryByCategoryCode(categoryCode);
+        if (wfCategory == null) {
+            throw new ServiceException("娴佺▼鍒嗙被涓嶅瓨鍦�");
+        }
+        // 鏂囦欢鍚庣紑鍚�
+        String suffix = FileUtil.extName(file.getOriginalFilename());
+        InputStream inputStream = file.getInputStream();
+        if (FlowConstant.ZIP.equalsIgnoreCase(suffix)) {
+            ZipInputStream zipInputStream = null;
+            try {
+                zipInputStream = new ZipInputStream(inputStream);
+                ZipEntry zipEntry;
+                while ((zipEntry = zipInputStream.getNextEntry()) != null) {
+                    String filename = zipEntry.getName();
+                    String[] splitFilename = filename.substring(0, filename.lastIndexOf(".")).split("-");
+                    //娴佺▼鍚嶇О
+                    String processName = splitFilename[0];
+                    //娴佺▼key
+                    String processKey = splitFilename[1];
+                    ProcessDefinition oldProcessDefinition = QueryUtils.definitionQuery().processDefinitionKey(processKey).latestVersion().singleResult();
+                    DeploymentBuilder builder = repositoryService.createDeployment();
+                    Deployment deployment = builder.addInputStream(filename, zipInputStream)
+                        .tenantId(TenantHelper.getTenantId())
+                        .name(processName).key(processKey).category(categoryCode).deploy();
+                    ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult();
+                    repositoryService.setProcessDefinitionCategory(definition.getId(), categoryCode);
+                    setWfConfig(oldProcessDefinition, definition);
+                    zipInputStream.closeEntry();
+                }
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } finally {
+                if (zipInputStream != null) {
+                    zipInputStream.close();
+                }
+            }
+            //鍒濆鍖栭厤缃暟鎹紙demo浣跨敤锛屼笉鐢ㄥ彲鍒犻櫎锛�
+            initWfDefConfig();
+        } else {
+            String originalFilename = file.getOriginalFilename();
+            String bpmnResourceSuffix = ResourceNameUtil.BPMN_RESOURCE_SUFFIXES[0];
+            if (originalFilename.contains(bpmnResourceSuffix)) {
+                // 鏂囦欢鍚� = 娴佺▼鍚嶇О-娴佺▼key
+                String[] splitFilename = originalFilename.substring(0, originalFilename.lastIndexOf(".")).split("-");
+                if (splitFilename.length < 2) {
+                    throw new ServiceException("鏂囦欢鍚� = 娴佺▼鍚嶇О-娴佺▼KEY");
+                }
+                //娴佺▼鍚嶇О
+                String processName = splitFilename[0];
+                //娴佺▼key
+                String processKey = splitFilename[1];
+                ProcessDefinition oldProcessDefinition = QueryUtils.definitionQuery().processDefinitionKey(processKey).latestVersion().singleResult();
+                DeploymentBuilder builder = repositoryService.createDeployment();
+                Deployment deployment = builder.addInputStream(originalFilename, inputStream)
+                    .tenantId(TenantHelper.getTenantId())
+                    .name(processName).key(processKey).category(categoryCode).deploy();
+                // 鏇存柊鍒嗙被
+                ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult();
+                repositoryService.setProcessDefinitionCategory(definition.getId(), categoryCode);
+                setWfConfig(oldProcessDefinition, definition);
+            } else {
+                throw new ServiceException("鏂囦欢绫诲瀷涓婁紶閿欒锛�");
+            }
+        }
+
+    }
+
+    /**
+     * 鍒濆鍖栭厤缃暟鎹紙demo浣跨敤锛屼笉鐢ㄥ彲鍒犻櫎锛�
+     */
+    private void initWfDefConfig() {
+        List<WfDefinitionConfig> wfDefinitionConfigs = wfDefinitionConfigMapper.selectList();
+        if (CollUtil.isEmpty(wfDefinitionConfigs)) {
+            ProcessDefinition processDefinition = QueryUtils.definitionQuery().processDefinitionKey("leave1").latestVersion().singleResult();
+            if (processDefinition != null) {
+                WfDefinitionConfigBo wfDefinitionConfigBo = new WfDefinitionConfigBo();
+                wfDefinitionConfigBo.setDefinitionId(processDefinition.getId());
+                wfDefinitionConfigBo.setProcessKey(processDefinition.getKey());
+                wfDefinitionConfigBo.setTableName("test_leave");
+                wfDefinitionConfigBo.setVersion(processDefinition.getVersion());
+                wfDefinitionConfigService.saveOrUpdate(wfDefinitionConfigBo);
+            }
+        }
+
+    }
+
+    /**
+     * 璁剧疆琛ㄥ崟鍐呭
+     *
+     * @param oldProcessDefinition 閮ㄧ讲鍓嶆渶鏂版祦绋嬪畾涔�
+     * @param definition           閮ㄧ讲鍚庢渶鏂版祦绋嬪畾涔�
+     */
+    private void setWfConfig(ProcessDefinition oldProcessDefinition, ProcessDefinition definition) {
+        //鏇存柊娴佺▼瀹氫箟琛ㄥ崟
+        if (oldProcessDefinition != null) {
+            WfDefinitionConfigVo definitionVo = wfDefinitionConfigService.getByDefId(oldProcessDefinition.getId());
+            if (definitionVo != null) {
+                wfDefinitionConfigService.deleteByDefIds(Collections.singletonList(oldProcessDefinition.getId()));
+                WfDefinitionConfigBo wfDefinitionConfigBo = new WfDefinitionConfigBo();
+                wfDefinitionConfigBo.setDefinitionId(definition.getId());
+                wfDefinitionConfigBo.setProcessKey(definition.getKey());
+                wfDefinitionConfigBo.setTableName(definitionVo.getTableName());
+                wfDefinitionConfigBo.setVersion(definition.getVersion());
+                wfDefinitionConfigBo.setRemark(definitionVo.getRemark());
+                wfDefinitionConfigService.saveOrUpdate(wfDefinitionConfigBo);
+            }
+        }
+        //鏇存柊娴佺▼鑺傜偣閰嶇疆琛ㄥ崟
+        List<UserTask> userTasks = ModelUtils.getUserTaskFlowElements(definition.getId());
+        UserTask applyUserTask = ModelUtils.getApplyUserTask(definition.getId());
+        List<WfNodeConfig> wfNodeConfigList = new ArrayList<>();
+        for (UserTask userTask : userTasks) {
+            if (StringUtils.isNotBlank(userTask.getFormKey()) && userTask.getFormKey().contains(StrUtil.COLON)) {
+                WfNodeConfig wfNodeConfig = new WfNodeConfig();
+                wfNodeConfig.setNodeId(userTask.getId());
+                wfNodeConfig.setNodeName(userTask.getName());
+                wfNodeConfig.setDefinitionId(definition.getId());
+                String[] split = userTask.getFormKey().split(StrUtil.COLON);
+                wfNodeConfig.setFormType(split[0]);
+                wfNodeConfig.setFormId(Long.valueOf(split[1]));
+                wfNodeConfig.setApplyUserTask(applyUserTask.getId().equals(userTask.getId()) ? FlowConstant.TRUE : FlowConstant.FALSE);
+                wfNodeConfigList.add(wfNodeConfig);
+            }
+        }
+        if (CollUtil.isNotEmpty(wfNodeConfigList)) {
+            wfNodeConfigService.saveOrUpdate(wfNodeConfigList);
+        }
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java
new file mode 100644
index 0000000..b64c79e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java
@@ -0,0 +1,683 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ObjectUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.workflow.common.constant.FlowConstant;
+import org.dromara.workflow.common.enums.BusinessStatusEnum;
+import org.dromara.workflow.common.enums.TaskStatusEnum;
+import org.dromara.workflow.domain.ActHiProcinst;
+import org.dromara.workflow.domain.bo.ProcessInstanceBo;
+import org.dromara.workflow.domain.bo.ProcessInvalidBo;
+import org.dromara.workflow.domain.bo.TaskUrgingBo;
+import org.dromara.workflow.domain.vo.*;
+import org.dromara.workflow.flowable.CustomDefaultProcessDiagramGenerator;
+import org.dromara.workflow.flowable.cmd.DeleteExecutionCmd;
+import org.dromara.workflow.flowable.cmd.ExecutionChildByExecutionIdCmd;
+import org.dromara.workflow.flowable.strategy.FlowEventStrategy;
+import org.dromara.workflow.flowable.strategy.FlowProcessEventHandler;
+import org.dromara.workflow.service.IActHiProcinstService;
+import org.dromara.workflow.service.IActProcessInstanceService;
+import org.dromara.workflow.service.IWfNodeConfigService;
+import org.dromara.workflow.service.IWfTaskBackNodeService;
+import org.dromara.workflow.utils.QueryUtils;
+import org.dromara.workflow.utils.WorkflowUtils;
+import org.flowable.bpmn.model.*;
+import org.flowable.engine.*;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.history.HistoricProcessInstanceQuery;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.runtime.ProcessInstanceQuery;
+import org.flowable.engine.task.Attachment;
+import org.flowable.engine.task.Comment;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.awt.*;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 娴佺▼瀹炰緥 鏈嶅姟灞傚疄鐜�
+ *
+ * @author may
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class ActProcessInstanceServiceImpl implements IActProcessInstanceService {
+
+    private final RepositoryService repositoryService;
+    private final RuntimeService runtimeService;
+    private final HistoryService historyService;
+    private final TaskService taskService;
+    private final IActHiProcinstService actHiProcinstService;
+    private final ManagementService managementService;
+    private final FlowEventStrategy flowEventStrategy;
+    private final IWfTaskBackNodeService wfTaskBackNodeService;
+    private final IWfNodeConfigService wfNodeConfigService;
+
+    @Value("${flowable.activity-font-name}")
+    private String activityFontName;
+
+    @Value("${flowable.label-font-name}")
+    private String labelFontName;
+
+    @Value("${flowable.annotation-font-name}")
+    private String annotationFontName;
+
+    /**
+     * 鍒嗛〉鏌ヨ姝e湪杩愯鐨勬祦绋嬪疄渚�
+     *
+     * @param bo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<ProcessInstanceVo> getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) {
+        List<ProcessInstanceVo> list = new ArrayList<>();
+        ProcessInstanceQuery query = QueryUtils.instanceQuery();
+        if (StringUtils.isNotBlank(bo.getName())) {
+            query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
+        }
+        if (StringUtils.isNotBlank(bo.getKey())) {
+            query.processDefinitionKey(bo.getKey());
+        }
+        if (StringUtils.isNotBlank(bo.getStartUserId())) {
+            query.startedBy(bo.getStartUserId());
+        }
+        if (StringUtils.isNotBlank(bo.getBusinessKey())) {
+            query.processInstanceBusinessKey(bo.getBusinessKey());
+        }
+        if (StringUtils.isNotBlank(bo.getCategoryCode())) {
+            query.processDefinitionCategory(bo.getCategoryCode());
+        }
+        query.orderByStartTime().desc();
+        List<ProcessInstance> processInstances = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        for (ProcessInstance processInstance : processInstances) {
+            ProcessInstanceVo processInstanceVo = BeanUtil.toBean(processInstance, ProcessInstanceVo.class);
+            processInstanceVo.setIsSuspended(processInstance.isSuspended());
+            processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstance.getBusinessStatus()));
+            list.add(processInstanceVo);
+        }
+        if (CollUtil.isNotEmpty(list)) {
+            List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (ProcessInstanceVo processInstanceVo : list) {
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
+                }
+            }
+        }
+        long count = query.count();
+        TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
+        build.setRows(list);
+        build.setTotal(count);
+        return build;
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ宸茬粨鏉熺殑娴佺▼瀹炰緥
+     *
+     * @param bo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<ProcessInstanceVo> getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) {
+        List<ProcessInstanceVo> list = new ArrayList<>();
+        HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery()
+            .finished().orderByProcessInstanceEndTime().desc();
+        if (StringUtils.isNotEmpty(bo.getName())) {
+            query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
+        }
+        if (StringUtils.isNotBlank(bo.getKey())) {
+            query.processDefinitionKey(bo.getKey());
+        }
+        if (StringUtils.isNotEmpty(bo.getStartUserId())) {
+            query.startedBy(bo.getStartUserId());
+        }
+        if (StringUtils.isNotBlank(bo.getBusinessKey())) {
+            query.processInstanceBusinessKey(bo.getBusinessKey());
+        }
+        if (StringUtils.isNotBlank(bo.getCategoryCode())) {
+            query.processDefinitionCategory(bo.getCategoryCode());
+        }
+        List<HistoricProcessInstance> historicProcessInstances = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
+            ProcessInstanceVo processInstanceVo = BeanUtil.toBean(historicProcessInstance, ProcessInstanceVo.class);
+            processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(historicProcessInstance.getBusinessStatus()));
+            list.add(processInstanceVo);
+        }
+        if (CollUtil.isNotEmpty(list)) {
+            List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (ProcessInstanceVo processInstanceVo : list) {
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
+                }
+            }
+        }
+        long count = query.count();
+        TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
+        build.setRows(list);
+        build.setTotal(count);
+        return build;
+    }
+
+    /**
+     * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥�
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @SneakyThrows
+    @Override
+    public String getHistoryImage(String processInstanceId) {
+        String processDefinitionId;
+        // 鑾峰彇褰撳墠鐨勬祦绋嬪疄渚�
+        ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId).singleResult();
+        // 濡傛灉娴佺▼宸茬粡缁撴潫锛屽垯寰楀埌缁撴潫鑺傜偣
+        if (Objects.isNull(processInstance)) {
+            HistoricProcessInstance pi = QueryUtils.hisInstanceQuery(processInstanceId).singleResult();
+            processDefinitionId = pi.getProcessDefinitionId();
+        } else {
+            // 鏍规嵁娴佺▼瀹炰緥ID鑾峰緱褰撳墠澶勪簬娲诲姩鐘舵�佺殑ActivityId鍚堥泦
+            ProcessInstance pi = QueryUtils.instanceQuery(processInstanceId).singleResult();
+            processDefinitionId = pi.getProcessDefinitionId();
+        }
+
+        // 鑾峰緱娲诲姩鐨勮妭鐐�
+        List<HistoricActivityInstance> highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
+
+        List<String> highLightedFlows = new ArrayList<>();
+        List<String> highLightedNodes = new ArrayList<>();
+        //楂樹寒
+        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
+            if (FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType())) {
+                //楂樹寒绾�
+                highLightedFlows.add(tempActivity.getActivityId());
+            } else {
+                //楂樹寒鑺傜偣
+                if (tempActivity.getEndTime() == null) {
+                    highLightedNodes.add(Color.RED.toString() + tempActivity.getActivityId());
+                } else {
+                    highLightedNodes.add(tempActivity.getActivityId());
+                }
+            }
+        }
+        List<String> highLightedNodeList = new ArrayList<>();
+        //杩愯涓殑鑺傜偣
+        List<String> redNodeCollect = StreamUtils.filter(highLightedNodes, e -> e.contains(Color.RED.toString()));
+        //鎺掗櫎涓庤繍琛屼腑鐩稿悓鐨勮妭鐐�
+        for (String nodeId : highLightedNodes) {
+            if (!nodeId.contains(Color.RED.toString()) && !redNodeCollect.contains(Color.RED + nodeId)) {
+                highLightedNodeList.add(nodeId);
+            }
+        }
+        highLightedNodeList.addAll(redNodeCollect);
+        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
+        CustomDefaultProcessDiagramGenerator diagramGenerator = new CustomDefaultProcessDiagramGenerator();
+        InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodeList, highLightedFlows, activityFontName, labelFontName, annotationFontName, null, 1.0, true);
+        return Base64.encode(IoUtil.readBytes(inputStream));
+    }
+
+    /**
+     * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥捐繍琛屼腑锛屽巻鍙茬瓑鑺傜偣
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @Override
+    public Map<String, Object> getHistoryList(String processInstanceId) {
+        Map<String, Object> map = new HashMap<>();
+        List<Map<String, Object>> taskList = new ArrayList<>();
+        HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(processInstanceId).singleResult();
+        StringBuilder xml = new StringBuilder();
+        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(historicProcessInstance.getProcessDefinitionId());
+        // 鑾峰彇鑺傜偣
+        List<HistoricActivityInstance> highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
+        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
+            Map<String, Object> task = new HashMap<>();
+            if (!FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType()) &&
+                !FlowConstant.PARALLEL_GATEWAY.equals(tempActivity.getActivityType()) &&
+                !FlowConstant.EXCLUSIVE_GATEWAY.equals(tempActivity.getActivityType()) &&
+                !FlowConstant.INCLUSIVE_GATEWAY.equals(tempActivity.getActivityType())
+            ) {
+                task.put("key", tempActivity.getActivityId());
+                task.put("completed", tempActivity.getEndTime() != null);
+                task.put("activityType", tempActivity.getActivityType());
+                taskList.add(task);
+            }
+        }
+        ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId).singleResult();
+        if (processInstance != null) {
+            taskList = taskList.stream().filter(e -> !e.get("activityType").equals(FlowConstant.END_EVENT)).collect(Collectors.toList());
+        }
+        //鏌ヨ鍑鸿繍琛屼腑鑺傜偣
+        List<Map<String, Object>> runtimeNodeList = taskList.stream().filter(e -> !(Boolean) e.get("completed")).collect(Collectors.toList());
+        if (CollUtil.isNotEmpty(runtimeNodeList)) {
+            Iterator<Map<String, Object>> iterator = taskList.iterator();
+            while (iterator.hasNext()) {
+                Map<String, Object> next = iterator.next();
+                runtimeNodeList.stream().filter(t -> t.get("key").equals(next.get("key")) && (Boolean) next.get("completed")).findFirst().ifPresent(t -> iterator.remove());
+            }
+        }
+        map.put("taskList", taskList);
+        List<ActHistoryInfoVo> historyTaskList = getHistoryTaskList(processInstanceId);
+        map.put("historyList", historyTaskList);
+        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
+        xml.append(IoUtil.read(inputStream, StandardCharsets.UTF_8));
+        map.put("xml", xml.toString());
+        return map;
+    }
+
+    /**
+     * 鑾峰彇鍘嗗彶浠诲姟鑺傜偣淇℃伅
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    private List<ActHistoryInfoVo> getHistoryTaskList(String processInstanceId) {
+        //鏌ヨ浠诲姟鍔炵悊璁板綍
+        List<HistoricTaskInstance> list = QueryUtils.hisTaskInstanceQuery(processInstanceId).orderByHistoricTaskInstanceEndTime().desc().list();
+        list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed());
+        List<ActHistoryInfoVo> actHistoryInfoVoList = new ArrayList<>();
+        for (HistoricTaskInstance historicTaskInstance : list) {
+            ActHistoryInfoVo actHistoryInfoVo = new ActHistoryInfoVo();
+            BeanUtils.copyProperties(historicTaskInstance, actHistoryInfoVo);
+            actHistoryInfoVo.setStatus(actHistoryInfoVo.getEndTime() == null ? "寰呭鐞�" : "宸插鐞�");
+            if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) {
+                actHistoryInfoVo.setRunDuration(getDuration(historicTaskInstance.getDurationInMillis()));
+            }
+            actHistoryInfoVoList.add(actHistoryInfoVo);
+        }
+        List<ActHistoryInfoVo> historyInfoVoList = new ArrayList<>();
+        Map<String, List<ActHistoryInfoVo>> groupByKey = StreamUtils.groupByKey(actHistoryInfoVoList, ActHistoryInfoVo::getTaskDefinitionKey);
+        for (Map.Entry<String, List<ActHistoryInfoVo>> entry : groupByKey.entrySet()) {
+            ActHistoryInfoVo historyInfoVo = new ActHistoryInfoVo();
+            BeanUtils.copyProperties(entry.getValue().get(0), historyInfoVo);
+            actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() == null).findFirst()
+                .ifPresent(e -> {
+                    historyInfoVo.setStatus("寰呭鐞�");
+                    historyInfoVo.setStartTime(e.getStartTime());
+                    historyInfoVo.setEndTime(null);
+                    historyInfoVo.setRunDuration(null);
+                    if (ObjectUtil.isEmpty(e.getAssignee())) {
+                        ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(e.getId());
+                        if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) {
+                            historyInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr));
+                        }
+                    }
+                });
+            historyInfoVoList.add(historyInfoVo);
+        }
+        return historyInfoVoList;
+    }
+
+    /**
+     * 鑾峰彇瀹℃壒璁板綍
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @Override
+    public List<ActHistoryInfoVo> getHistoryRecord(String processInstanceId) {
+        // 鏌ヨ浠诲姟鍔炵悊璁板綍
+        List<HistoricTaskInstance> list = QueryUtils.hisTaskInstanceQuery(processInstanceId).orderByHistoricTaskInstanceEndTime().desc().list();
+        list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed());
+        List<ActHistoryInfoVo> actHistoryInfoVoList = new ArrayList<>();
+        List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);
+        //闄勪欢
+        List<Attachment> attachmentList = taskService.getProcessInstanceAttachments(processInstanceId);
+        for (HistoricTaskInstance historicTaskInstance : list) {
+            ActHistoryInfoVo actHistoryInfoVo = new ActHistoryInfoVo();
+            BeanUtils.copyProperties(historicTaskInstance, actHistoryInfoVo);
+            if (actHistoryInfoVo.getEndTime() == null) {
+                actHistoryInfoVo.setStatus(TaskStatusEnum.WAITING.getStatus());
+                actHistoryInfoVo.setStatusName(TaskStatusEnum.WAITING.getDesc());
+            }
+            if (CollUtil.isNotEmpty(processInstanceComments)) {
+                processInstanceComments.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).findFirst().ifPresent(e -> {
+                    actHistoryInfoVo.setComment(e.getFullMessage());
+                    actHistoryInfoVo.setStatus(e.getType());
+                    actHistoryInfoVo.setStatusName(TaskStatusEnum.findByStatus(e.getType()));
+                });
+            }
+            if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) {
+                actHistoryInfoVo.setRunDuration(getDuration(historicTaskInstance.getDurationInMillis()));
+            }
+            //闄勪欢
+            if (CollUtil.isNotEmpty(attachmentList)) {
+                List<Attachment> attachments = attachmentList.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).collect(Collectors.toList());
+                if (CollUtil.isNotEmpty(attachments)) {
+                    actHistoryInfoVo.setAttachmentList(attachments);
+                }
+            }
+            //璁剧疆浜哄憳id
+            if (ObjectUtil.isEmpty(historicTaskInstance.getAssignee())) {
+                ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(historicTaskInstance.getId());
+                if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) {
+                    actHistoryInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr));
+                }
+            }
+            actHistoryInfoVoList.add(actHistoryInfoVo);
+        }
+        // 瀹℃壒璁板綍
+        Map<String, List<ActHistoryInfoVo>> groupByKey = StreamUtils.groupByKey(actHistoryInfoVoList, ActHistoryInfoVo::getTaskDefinitionKey);
+        for (Map.Entry<String, List<ActHistoryInfoVo>> entry : groupByKey.entrySet()) {
+            ActHistoryInfoVo actHistoryInfoVo = BeanUtil.toBean(entry.getValue().get(0), ActHistoryInfoVo.class);
+            actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() != null).findFirst()
+                .ifPresent(e -> {
+                    actHistoryInfoVo.setStatus("宸插鐞�");
+                    actHistoryInfoVo.setStartTime(e.getStartTime());
+                });
+            actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() == null).findFirst()
+                .ifPresent(e -> {
+                    actHistoryInfoVo.setStatus("寰呭鐞�");
+                    actHistoryInfoVo.setStartTime(e.getStartTime());
+                    actHistoryInfoVo.setEndTime(null);
+                    actHistoryInfoVo.setRunDuration(null);
+                });
+        }
+        List<ActHistoryInfoVo> recordList = new ArrayList<>();
+        // 寰呭姙鐞�
+        recordList.addAll(StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() == null));
+        // 宸插姙鐞�
+        recordList.addAll(StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() != null));
+
+        return recordList;
+    }
+
+    /**
+     * 浠诲姟瀹屾垚鏃堕棿澶勭悊
+     *
+     * @param time 鏃堕棿
+     */
+    private String getDuration(long time) {
+
+        long day = time / (24 * 60 * 60 * 1000);
+        long hour = (time / (60 * 60 * 1000) - day * 24);
+        long minute = ((time / (60 * 1000)) - day * 24 * 60 - hour * 60);
+        long second = (time / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);
+
+        if (day > 0) {
+            return day + "澶�" + hour + "灏忔椂" + minute + "鍒嗛挓";
+        }
+        if (hour > 0) {
+            return hour + "灏忔椂" + minute + "鍒嗛挓";
+        }
+        if (minute > 0) {
+            return minute + "鍒嗛挓";
+        }
+        if (second > 0) {
+            return second + "绉�";
+        } else {
+            return 0 + "绉�";
+        }
+    }
+
+    /**
+     * 浣滃簾娴佺▼瀹炰緥锛屼笉浼氬垹闄ゅ巻鍙茶褰�(鍒犻櫎杩愯涓殑瀹炰緥)
+     *
+     * @param processInvalidBo 鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteRunInstance(ProcessInvalidBo processInvalidBo) {
+        try {
+            List<Task> list = QueryUtils.taskQuery(processInvalidBo.getProcessInstanceId()).list();
+            List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
+            if (CollUtil.isNotEmpty(subTasks)) {
+                subTasks.forEach(e -> taskService.deleteTask(e.getId()));
+            }
+            String deleteReason = LoginHelper.getLoginUser().getNickname() + "浣滃簾浜嗗綋鍓嶇敵璇凤紒";
+            if (StringUtils.isNotBlank(processInvalidBo.getDeleteReason())) {
+                deleteReason = LoginHelper.getLoginUser().getNickname() + "浣滃簾鐞嗙敱:" + processInvalidBo.getDeleteReason();
+            }
+            for (Task task : StreamUtils.filter(list, e -> StringUtils.isBlank(e.getParentTaskId()))) {
+                taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.INVALID.getStatus(), deleteReason);
+            }
+            HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(processInvalidBo.getProcessInstanceId()).singleResult();
+            BusinessStatusEnum.checkInvalidStatus(historicProcessInstance.getBusinessStatus());
+            runtimeService.updateBusinessStatus(processInvalidBo.getProcessInstanceId(), BusinessStatusEnum.INVALID.getStatus());
+            runtimeService.deleteProcessInstance(processInvalidBo.getProcessInstanceId(), deleteReason);
+            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(historicProcessInstance.getProcessDefinitionKey());
+            if (processHandler != null) {
+                processHandler.handleProcess(historicProcessInstance.getBusinessKey(), BusinessStatusEnum.INVALID.getStatus(), false);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 杩愯涓殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteRunAndHisInstance(List<String> processInstanceIds) {
+        try {
+            // 1.鍒犻櫎杩愯涓祦绋嬪疄渚�
+            List<Task> list = QueryUtils.taskQuery(processInstanceIds).list();
+            List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
+            if (CollUtil.isNotEmpty(subTasks)) {
+                subTasks.forEach(e -> taskService.deleteTask(e.getId()));
+            }
+            runtimeService.bulkDeleteProcessInstances(processInstanceIds, LoginHelper.getUserId() + "鍒犻櫎浜嗗綋鍓嶆祦绋嬬敵璇�");
+            // 2.鍒犻櫎鍘嗗彶璁板綍
+            List<HistoricProcessInstance> historicProcessInstanceList = QueryUtils.hisInstanceQuery(new HashSet<>(processInstanceIds)).list();
+            if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
+                historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
+            }
+            wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 鎸夌収涓氬姟id鍒犻櫎 杩愯涓殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param businessKeys 涓氬姟id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteRunAndHisInstanceByBusinessKeys(List<String> businessKeys) {
+        try {
+            // 1.鍒犻櫎杩愯涓祦绋嬪疄渚�
+            List<ActHiProcinst> actHiProcinsts = actHiProcinstService.selectByBusinessKeyIn(businessKeys);
+            if (CollUtil.isEmpty(actHiProcinsts)) {
+                log.warn("褰撳墠涓氬姟ID:{}鏌ヨ鍒版祦绋嬪疄渚嬩负绌猴紒", businessKeys);
+                return false;
+            }
+            List<String> processInstanceIds = StreamUtils.toList(actHiProcinsts, ActHiProcinst::getId);
+            List<Task> list = QueryUtils.taskQuery(processInstanceIds).list();
+            List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
+            if (CollUtil.isNotEmpty(subTasks)) {
+                subTasks.forEach(e -> taskService.deleteTask(e.getId()));
+            }
+            runtimeService.bulkDeleteProcessInstances(processInstanceIds, LoginHelper.getUserId() + "鍒犻櫎浜嗗綋鍓嶆祦绋嬬敵璇�");
+            // 2.鍒犻櫎鍘嗗彶璁板綍
+            List<HistoricProcessInstance> historicProcessInstanceList = QueryUtils.hisInstanceQuery(new HashSet<>(processInstanceIds)).list();
+            if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
+                historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
+            }
+            wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 宸插畬鎴愮殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteFinishAndHisInstance(List<String> processInstanceIds) {
+        try {
+            historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
+            wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 鎾ら攢娴佺▼鐢宠
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean cancelProcessApply(String processInstanceId) {
+        try {
+            ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId)
+                .startedBy(String.valueOf(LoginHelper.getUserId())).singleResult();
+            if (ObjectUtil.isNull(processInstance)) {
+                throw new ServiceException("鎮ㄤ笉鏄祦绋嬪彂璧蜂汉,鎾ら攢澶辫触!");
+            }
+            if (processInstance.isSuspended()) {
+                throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+            }
+            BusinessStatusEnum.checkCancelStatus(processInstance.getBusinessStatus());
+            List<Task> taskList = QueryUtils.taskQuery(processInstanceId).list();
+            for (Task task : taskList) {
+                taskService.setAssignee(task.getId(), null);
+                taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.CANCEL.getStatus(), LoginHelper.getLoginUser().getNickname() + "锛氭挙閿�鐢宠");
+            }
+            HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery().finished().orderByHistoricTaskInstanceEndTime().asc().list().get(0);
+            List<String> nodeIds = StreamUtils.toList(taskList, Task::getTaskDefinitionKey);
+            runtimeService.createChangeActivityStateBuilder()
+                .processInstanceId(processInstanceId)
+                .moveActivityIdsToSingleActivityId(nodeIds, historicTaskInstance.getTaskDefinitionKey()).changeState();
+            Task task = QueryUtils.taskQuery(processInstanceId).list().get(0);
+            taskService.setAssignee(task.getId(), historicTaskInstance.getAssignee());
+            //鑾峰彇骞惰缃戝叧鎵ц鍚庝繚鐣欑殑鎵ц瀹炰緥鏁版嵁
+            ExecutionChildByExecutionIdCmd childByExecutionIdCmd = new ExecutionChildByExecutionIdCmd(task.getExecutionId());
+            List<ExecutionEntity> executionEntities = managementService.executeCommand(childByExecutionIdCmd);
+            //鍒犻櫎娴佺▼瀹炰緥鍨冨溇鏁版嵁
+            for (ExecutionEntity executionEntity : executionEntities) {
+                DeleteExecutionCmd deleteExecutionCmd = new DeleteExecutionCmd(executionEntity.getId());
+                managementService.executeCommand(deleteExecutionCmd);
+            }
+            runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.CANCEL.getStatus());
+            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey());
+            if (processHandler != null) {
+                processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.CANCEL.getStatus(), false);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException("鎾ら攢澶辫触:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ褰撳墠鐧诲綍浜哄崟鎹�
+     *
+     * @param bo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<ProcessInstanceVo> getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) {
+        List<ProcessInstanceVo> list = new ArrayList<>();
+        HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery();
+        query.startedBy(String.valueOf(LoginHelper.getUserId()));
+        if (StringUtils.isNotBlank(bo.getName())) {
+            query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
+        }
+        if (StringUtils.isNotBlank(bo.getKey())) {
+            query.processDefinitionKey(bo.getKey());
+        }
+        if (StringUtils.isNotBlank(bo.getBusinessKey())) {
+            query.processInstanceBusinessKey(bo.getBusinessKey());
+        }
+        if (StringUtils.isNotBlank(bo.getCategoryCode())) {
+            query.processDefinitionCategory(bo.getCategoryCode());
+        }
+        query.orderByProcessInstanceStartTime().desc();
+        List<HistoricProcessInstance> historicProcessInstanceList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        List<TaskVo> taskVoList = new ArrayList<>();
+        if (CollUtil.isNotEmpty(historicProcessInstanceList)) {
+            List<String> processInstanceIds = StreamUtils.toList(historicProcessInstanceList, HistoricProcessInstance::getId);
+            List<Task> taskList = QueryUtils.taskQuery(processInstanceIds).list();
+            for (Task task : taskList) {
+                taskVoList.add(BeanUtil.toBean(task, TaskVo.class));
+            }
+        }
+        for (HistoricProcessInstance processInstance : historicProcessInstanceList) {
+            ProcessInstanceVo processInstanceVo = BeanUtil.toBean(processInstance, ProcessInstanceVo.class);
+            processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstance.getBusinessStatus()));
+            if (CollUtil.isNotEmpty(taskVoList)) {
+                List<TaskVo> collect = StreamUtils.filter(taskVoList, e -> e.getProcessInstanceId().equals(processInstance.getId()));
+                processInstanceVo.setTaskVoList(CollUtil.isNotEmpty(collect) ? collect : Collections.emptyList());
+            }
+            list.add(processInstanceVo);
+        }
+        if (CollUtil.isNotEmpty(list)) {
+            List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (ProcessInstanceVo processInstanceVo : list) {
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
+                }
+            }
+        }
+        long count = query.count();
+        TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
+        build.setRows(list);
+        build.setTotal(count);
+        return build;
+    }
+
+    /**
+     * 浠诲姟鍌姙(缁欏綋鍓嶄换鍔″姙鐞嗕汉鍙戦�佺珯鍐呬俊锛岄偖浠讹紝鐭俊绛�)
+     *
+     * @param taskUrgingBo 浠诲姟鍌姙
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean taskUrging(TaskUrgingBo taskUrgingBo) {
+        try {
+            ProcessInstance processInstance = QueryUtils.instanceQuery(taskUrgingBo.getProcessInstanceId()).singleResult();
+            if (processInstance == null) {
+                throw new ServiceException("浠诲姟宸茬粨鏉燂紒");
+            }
+            String message = taskUrgingBo.getMessage();
+            if (StringUtils.isBlank(message)) {
+                message = "鎮ㄧ殑銆�" + processInstance.getName() + "銆戝崟鎹繕鏈鎵癸紝璇锋偍鍙婃椂澶勭悊銆�";
+            }
+            List<Task> list = QueryUtils.taskQuery(taskUrgingBo.getProcessInstanceId()).list();
+            WorkflowUtils.sendMessage(list, processInstance.getName(), taskUrgingBo.getMessageType(), message);
+        } catch (ServiceException e) {
+            throw new ServiceException(e.getMessage());
+        }
+        return true;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java
new file mode 100644
index 0000000..1a4866d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java
@@ -0,0 +1,872 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.domain.dto.RoleDTO;
+import org.dromara.common.core.domain.dto.UserDTO;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.service.UserService;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.workflow.common.constant.FlowConstant;
+import org.dromara.workflow.common.enums.BusinessStatusEnum;
+import org.dromara.workflow.common.enums.TaskStatusEnum;
+import org.dromara.workflow.domain.ActHiTaskinst;
+import org.dromara.workflow.domain.WfTaskBackNode;
+import org.dromara.workflow.domain.bo.*;
+import org.dromara.workflow.domain.vo.*;
+import org.dromara.workflow.flowable.cmd.*;
+import org.dromara.workflow.flowable.strategy.FlowEventStrategy;
+import org.dromara.workflow.flowable.strategy.FlowProcessEventHandler;
+import org.dromara.workflow.flowable.strategy.FlowTaskEventHandler;
+import org.dromara.workflow.mapper.ActHiTaskinstMapper;
+import org.dromara.workflow.mapper.ActTaskMapper;
+import org.dromara.workflow.service.IActTaskService;
+import org.dromara.workflow.service.IWfDefinitionConfigService;
+import org.dromara.workflow.service.IWfNodeConfigService;
+import org.dromara.workflow.service.IWfTaskBackNodeService;
+import org.dromara.workflow.utils.ModelUtils;
+import org.dromara.workflow.utils.QueryUtils;
+import org.dromara.workflow.utils.WorkflowUtils;
+import org.flowable.common.engine.api.FlowableObjectNotFoundException;
+import org.flowable.common.engine.impl.identity.Authentication;
+import org.flowable.engine.*;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.history.HistoricProcessInstanceQuery;
+import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
+import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.identitylink.api.history.HistoricIdentityLink;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.TaskQuery;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+import org.flowable.variable.api.persistence.entity.VariableInstance;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.dromara.workflow.common.constant.FlowConstant.*;
+
+/**
+ * 浠诲姟 鏈嶅姟灞傚疄鐜�
+ *
+ * @author may
+ */
+@RequiredArgsConstructor
+@Service
+public class ActTaskServiceImpl implements IActTaskService {
+
+    private final RuntimeService runtimeService;
+    private final TaskService taskService;
+    private final HistoryService historyService;
+    private final IdentityService identityService;
+    private final ManagementService managementService;
+    private final FlowEventStrategy flowEventStrategy;
+    private final ActTaskMapper actTaskMapper;
+    private final IWfTaskBackNodeService wfTaskBackNodeService;
+    private final ActHiTaskinstMapper actHiTaskinstMapper;
+    private final IWfNodeConfigService wfNodeConfigService;
+    private final IWfDefinitionConfigService wfDefinitionConfigService;
+    private final UserService userService;
+
+    /**
+     * 鍚姩浠诲姟
+     *
+     * @param startProcessBo 鍚姩娴佺▼鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> startWorkFlow(StartProcessBo startProcessBo) {
+        Map<String, Object> map = new HashMap<>();
+        if (StringUtils.isBlank(startProcessBo.getBusinessKey())) {
+            throw new ServiceException("鍚姩宸ヤ綔娴佹椂蹇呴』鍖呭惈涓氬姟ID");
+        }
+        // 鍒ゆ柇褰撳墠涓氬姟鏄惁鍚姩杩囨祦绋�
+        HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery();
+        HistoricProcessInstance historicProcessInstance = query.processInstanceBusinessKey(startProcessBo.getBusinessKey()).singleResult();
+        if (ObjectUtil.isNotEmpty(historicProcessInstance)) {
+            BusinessStatusEnum.checkStartStatus(historicProcessInstance.getBusinessStatus());
+        }
+        List<Task> taskResult = QueryUtils.taskQuery().processInstanceBusinessKey(startProcessBo.getBusinessKey()).list();
+        if (CollUtil.isNotEmpty(taskResult)) {
+            if (CollUtil.isNotEmpty(startProcessBo.getVariables())) {
+                taskService.setVariables(taskResult.get(0).getId(), startProcessBo.getVariables());
+            }
+            map.put(PROCESS_INSTANCE_ID, taskResult.get(0).getProcessInstanceId());
+            map.put("taskId", taskResult.get(0).getId());
+            return map;
+        }
+        WfDefinitionConfigVo wfDefinitionConfigVo = wfDefinitionConfigService.getByTableNameLastVersion(startProcessBo.getTableName());
+        if (wfDefinitionConfigVo == null) {
+            throw new ServiceException("璇峰埌娴佺▼瀹氫箟缁戝畾涓氬姟琛ㄥ悕涓庢祦绋婯EY锛�");
+        }
+        // 璁剧疆鍚姩浜�
+        identityService.setAuthenticatedUserId(String.valueOf(LoginHelper.getUserId()));
+        Authentication.setAuthenticatedUserId(String.valueOf(LoginHelper.getUserId()));
+        // 鍚姩娴佺▼瀹炰緥锛堟彁浜ょ敵璇凤級
+        Map<String, Object> variables = startProcessBo.getVariables();
+        // 鍚姩璺宠繃琛ㄨ揪寮�
+        variables.put(FLOWABLE_SKIP_EXPRESSION_ENABLED, true);
+        // 娴佺▼鍙戣捣浜�
+        variables.put(INITIATOR, (String.valueOf(LoginHelper.getUserId())));
+        ProcessInstance pi;
+        try {
+            if (TenantHelper.isEnable()) {
+                pi = runtimeService.startProcessInstanceByKeyAndTenantId(wfDefinitionConfigVo.getProcessKey(), startProcessBo.getBusinessKey(), variables, TenantHelper.getTenantId());
+            } else {
+                pi = runtimeService.startProcessInstanceByKey(wfDefinitionConfigVo.getProcessKey(), startProcessBo.getBusinessKey(), variables);
+            }
+        } catch (FlowableObjectNotFoundException e) {
+            throw new ServiceException("鎵句笉鍒板綋鍓嶃��" + wfDefinitionConfigVo.getProcessKey() + "銆戞祦绋嬪畾涔夛紒");
+        }
+        // 灏嗘祦绋嬪畾涔夊悕绉� 浣滀负 娴佺▼瀹炰緥鍚嶇О
+        runtimeService.setProcessInstanceName(pi.getProcessInstanceId(), pi.getProcessDefinitionName());
+        // 鐢宠浜烘墽琛屾祦绋�
+        List<Task> taskList = QueryUtils.taskQuery(pi.getId()).list();
+        if (taskList.size() > 1) {
+            throw new ServiceException("璇锋鏌ユ祦绋嬬涓�涓幆鑺傛槸鍚︿负鐢宠浜猴紒");
+        }
+
+        runtimeService.updateBusinessStatus(pi.getProcessInstanceId(), BusinessStatusEnum.DRAFT.getStatus());
+        taskService.setAssignee(taskList.get(0).getId(), LoginHelper.getUserId().toString());
+        taskService.setVariable(taskList.get(0).getId(), PROCESS_INSTANCE_ID, pi.getProcessInstanceId());
+        taskService.setVariable(taskList.get(0).getId(), BUSINESS_KEY, pi.getBusinessKey());
+        map.put("processInstanceId", pi.getProcessInstanceId());
+        map.put("taskId", taskList.get(0).getId());
+        return map;
+    }
+
+    /**
+     * 鍔炵悊浠诲姟
+     *
+     * @param completeTaskBo 鍔炵悊浠诲姟鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean completeTask(CompleteTaskBo completeTaskBo) {
+        try {
+            List<RoleDTO> roles = LoginHelper.getLoginUser().getRoles();
+            String userId = String.valueOf(LoginHelper.getUserId());
+            TaskQuery taskQuery = QueryUtils.taskQuery();
+            taskQuery.taskId(completeTaskBo.getTaskId()).taskCandidateOrAssigned(userId);
+            if (CollUtil.isNotEmpty(roles)) {
+                List<String> groupIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId()));
+                taskQuery.taskCandidateGroupIn(groupIds);
+            }
+            Task task = taskQuery.singleResult();
+            if (task == null) {
+                throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
+            }
+            if (task.isSuspended()) {
+                throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+            }
+            ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult();
+            //鍔炵悊濮旀墭浠诲姟
+            if (ObjectUtil.isNotEmpty(task.getDelegationState()) && FlowConstant.PENDING.equals(task.getDelegationState().name())) {
+                taskService.resolveTask(completeTaskBo.getTaskId());
+                TaskEntity newTask = WorkflowUtils.createNewTask(task);
+                taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), StringUtils.isNotBlank(completeTaskBo.getMessage()) ? completeTaskBo.getMessage() : StrUtil.EMPTY);
+                taskService.complete(newTask.getId());
+                return true;
+            }
+            //闄勪欢涓婁紶
+            AttachmentCmd attachmentCmd = new AttachmentCmd(completeTaskBo.getFileId(), task.getId(), task.getProcessInstanceId());
+            managementService.executeCommand(attachmentCmd);
+            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey());
+            String businessStatus = WorkflowUtils.getBusinessStatus(task.getProcessInstanceId());
+            if (BusinessStatusEnum.DRAFT.getStatus().equals(businessStatus) || BusinessStatusEnum.BACK.getStatus().equals(businessStatus) || BusinessStatusEnum.CANCEL.getStatus().equals(businessStatus)) {
+                if (processHandler != null) {
+                    processHandler.handleProcess(processInstance.getBusinessKey(), businessStatus, true);
+                }
+            }
+            runtimeService.updateBusinessStatus(task.getProcessInstanceId(), BusinessStatusEnum.WAITING.getStatus());
+            String key = processInstance.getProcessDefinitionKey() + "_" + task.getTaskDefinitionKey();
+            FlowTaskEventHandler taskHandler = flowEventStrategy.getTaskHandler(key);
+            if (taskHandler != null) {
+                taskHandler.handleTask(task.getId(), processInstance.getBusinessKey());
+            }
+            //鍔炵悊鎰忚
+            taskService.addComment(completeTaskBo.getTaskId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), StringUtils.isBlank(completeTaskBo.getMessage()) ? "鍚屾剰" : completeTaskBo.getMessage());
+            //鍔炵悊浠诲姟
+            taskService.setAssignee(task.getId(), userId);
+            if (CollUtil.isNotEmpty(completeTaskBo.getVariables())) {
+                taskService.complete(completeTaskBo.getTaskId(), completeTaskBo.getVariables());
+            } else {
+                taskService.complete(completeTaskBo.getTaskId());
+            }
+            //璁板綍鎵ц杩囩殑娴佺▼浠诲姟鑺傜偣
+            wfTaskBackNodeService.recordExecuteNode(task);
+            ProcessInstance pi = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult();
+            if (pi == null) {
+                UpdateBusinessStatusCmd updateBusinessStatusCmd = new UpdateBusinessStatusCmd(task.getProcessInstanceId(), BusinessStatusEnum.FINISH.getStatus());
+                managementService.executeCommand(updateBusinessStatusCmd);
+                if (processHandler != null) {
+                    processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.FINISH.getStatus(), false);
+                }
+            } else {
+                List<Task> list = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
+                for (Task t : list) {
+                    if (ModelUtils.isUserTask(t.getProcessDefinitionId(), t.getTaskDefinitionKey())) {
+                        List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForTask(t.getId());
+                        if (CollUtil.isEmpty(links) && StringUtils.isBlank(t.getAssignee())) {
+                            throw new ServiceException("涓嬩竴鑺傜偣銆�" + t.getName() + "銆戞病鏈夊姙鐞嗕汉!");
+                        }
+                    }
+                }
+
+                if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(completeTaskBo.getWfCopyList())) {
+                    TaskEntity newTask = WorkflowUtils.createNewTask(task);
+                    taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), LoginHelper.getLoginUser().getNickname() + "銆愭妱閫併�戠粰" + String.join(",", StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserName)));
+                    taskService.complete(newTask.getId());
+                    List<Task> taskList = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
+                    WorkflowUtils.createCopyTask(taskList, StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserId));
+                }
+                sendMessage(list, processInstance.getName(), completeTaskBo.getMessageType(), null);
+            }
+            return true;
+        } catch (Exception e) {
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 鍙戦�佹秷鎭�
+     *
+     * @param list        浠诲姟
+     * @param name        娴佺▼鍚嶇О
+     * @param messageType 娑堟伅绫诲瀷
+     * @param message     娑堟伅鍐呭锛屼负绌哄垯鍙戦�侀粯璁ら厤缃殑娑堟伅鍐呭
+     */
+    @Async
+    public void sendMessage(List<Task> list, String name, List<String> messageType, String message) {
+        WorkflowUtils.sendMessage(list, name, messageType, message);
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勫緟鍔炰换鍔�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<TaskVo> getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery) {
+        QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
+        List<RoleDTO> roles = LoginHelper.getLoginUser().getRoles();
+        List<String> roleIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId()));
+        String userId = String.valueOf(LoginHelper.getUserId());
+        queryWrapper.eq("t.business_status_", BusinessStatusEnum.WAITING.getStatus());
+        queryWrapper.eq(TenantHelper.isEnable(), "t.tenant_id_", TenantHelper.getTenantId());
+        queryWrapper.and(w1 -> w1.eq("t.assignee_", userId).or(w2 -> w2.isNull("t.assignee_").apply("exists ( select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = t.ID_ and LINK.TYPE_ = 'candidate' " + "and (LINK.USER_ID_ = {0} or ( LINK.GROUP_ID_ IN " + getInParam(roleIds) + " ) ))", userId)));
+        if (StringUtils.isNotBlank(taskBo.getName())) {
+            queryWrapper.like("t.name_", taskBo.getName());
+        }
+        if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) {
+            queryWrapper.like("t.processDefinitionName", taskBo.getProcessDefinitionName());
+        }
+        if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) {
+            queryWrapper.eq("t.processDefinitionKey", taskBo.getProcessDefinitionKey());
+        }
+        Page<TaskVo> page = actTaskMapper.getTaskWaitByPage(pageQuery.build(), queryWrapper);
+
+        List<TaskVo> taskList = page.getRecords();
+        if (CollUtil.isNotEmpty(taskList)) {
+            List<String> processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (TaskVo task : taskList) {
+                task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
+                task.setParticipantVo(WorkflowUtils.getCurrentTaskParticipant(task.getId()));
+                task.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
+                }
+            }
+        }
+        return TableDataInfo.build(page);
+    }
+
+    private String getInParam(List<String> param) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("(");
+        for (int i = 0; i < param.size(); i++) {
+            sb.append("'").append(param.get(i)).append("'");
+            if (i != param.size() - 1) {
+                sb.append(",");
+            }
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    /**
+     * 鏌ヨ褰撳墠绉熸埛鎵�鏈夊緟鍔炰换鍔�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<TaskVo> getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery) {
+        TaskQuery query = QueryUtils.taskQuery();
+        if (StringUtils.isNotBlank(taskBo.getName())) {
+            query.taskNameLike("%" + taskBo.getName() + "%");
+        }
+        if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) {
+            query.processDefinitionNameLike("%" + taskBo.getProcessDefinitionName() + "%");
+        }
+        if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) {
+            query.processDefinitionKey(taskBo.getProcessDefinitionKey());
+        }
+        query.orderByTaskCreateTime().desc();
+        List<Task> taskList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        List<ProcessInstance> processInstanceList = null;
+        if (CollUtil.isNotEmpty(taskList)) {
+            Set<String> processInstanceIds = StreamUtils.toSet(taskList, Task::getProcessInstanceId);
+            processInstanceList = QueryUtils.instanceQuery(processInstanceIds).list();
+        }
+        List<TaskVo> list = new ArrayList<>();
+        if (CollUtil.isNotEmpty(taskList)) {
+            List<String> processDefinitionIds = StreamUtils.toList(taskList, Task::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (Task task : taskList) {
+                TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
+                if (CollUtil.isNotEmpty(processInstanceList)) {
+                    processInstanceList.stream().filter(e -> e.getId().equals(task.getProcessInstanceId())).findFirst().ifPresent(e -> {
+                        taskVo.setBusinessStatus(e.getBusinessStatus());
+                        taskVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(taskVo.getBusinessStatus()));
+                        taskVo.setProcessDefinitionKey(e.getProcessDefinitionKey());
+                        taskVo.setProcessDefinitionName(e.getProcessDefinitionName());
+                        taskVo.setProcessDefinitionVersion(e.getProcessDefinitionVersion());
+                        taskVo.setBusinessKey(e.getBusinessKey());
+                    });
+                }
+                taskVo.setAssignee(StringUtils.isNotBlank(task.getAssignee()) ? Long.valueOf(task.getAssignee()) : null);
+                taskVo.setParticipantVo(WorkflowUtils.getCurrentTaskParticipant(task.getId()));
+                taskVo.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(taskVo::setWfNodeConfigVo);
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(taskVo::setWfNodeConfigVo);
+                }
+                list.add(taskVo);
+            }
+        }
+        long count = query.count();
+        TableDataInfo<TaskVo> build = TableDataInfo.build();
+        build.setRows(list);
+        build.setTotal(count);
+        return build;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勫凡鍔炰换鍔�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<TaskVo> getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery) {
+        String userId = String.valueOf(LoginHelper.getUserId());
+        QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.like(StringUtils.isNotBlank(taskBo.getName()), "t.name_", taskBo.getName());
+        queryWrapper.like(StringUtils.isNotBlank(taskBo.getProcessDefinitionName()), "t.processDefinitionName", taskBo.getProcessDefinitionName());
+        queryWrapper.eq(StringUtils.isNotBlank(taskBo.getProcessDefinitionKey()), "t.processDefinitionKey", taskBo.getProcessDefinitionKey());
+        queryWrapper.eq("t.assignee_", userId);
+        Page<TaskVo> page = actTaskMapper.getTaskFinishByPage(pageQuery.build(), queryWrapper);
+
+        List<TaskVo> taskList = page.getRecords();
+        if (CollUtil.isNotEmpty(taskList)) {
+            List<String> processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (TaskVo task : taskList) {
+                task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
+                }
+            }
+        }
+        return TableDataInfo.build(page);
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鐨勬妱閫�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<TaskVo> getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery) {
+        QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
+        String userId = String.valueOf(LoginHelper.getUserId());
+        if (StringUtils.isNotBlank(taskBo.getName())) {
+            queryWrapper.like("t.name_", taskBo.getName());
+        }
+        if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) {
+            queryWrapper.like("t.processDefinitionName", taskBo.getProcessDefinitionName());
+        }
+        if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) {
+            queryWrapper.eq("t.processDefinitionKey", taskBo.getProcessDefinitionKey());
+        }
+        queryWrapper.eq("t.assignee_", userId);
+        Page<TaskVo> page = actTaskMapper.getTaskCopyByPage(pageQuery.build(), queryWrapper);
+
+        List<TaskVo> taskList = page.getRecords();
+        if (CollUtil.isNotEmpty(taskList)) {
+            List<String> processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (TaskVo task : taskList) {
+                task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
+                }
+            }
+        }
+        return TableDataInfo.build(page);
+    }
+
+    /**
+     * 鏌ヨ褰撳墠绉熸埛鎵�鏈夊凡鍔炰换鍔�
+     *
+     * @param taskBo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<TaskVo> getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery) {
+        QueryWrapper<TaskVo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.like(StringUtils.isNotBlank(taskBo.getName()), "t.name_", taskBo.getName());
+        queryWrapper.like(StringUtils.isNotBlank(taskBo.getProcessDefinitionName()), "t.processDefinitionName", taskBo.getProcessDefinitionName());
+        queryWrapper.eq(StringUtils.isNotBlank(taskBo.getProcessDefinitionKey()), "t.processDefinitionKey", taskBo.getProcessDefinitionKey());
+        Page<TaskVo> page = actTaskMapper.getTaskFinishByPage(pageQuery.build(), queryWrapper);
+
+        List<TaskVo> taskList = page.getRecords();
+        if (CollUtil.isNotEmpty(taskList)) {
+            List<String> processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (TaskVo task : taskList) {
+                task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus()));
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo);
+                }
+            }
+        }
+        return TableDataInfo.build(page);
+    }
+
+    /**
+     * 濮旀淳浠诲姟
+     *
+     * @param delegateBo 鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean delegateTask(DelegateBo delegateBo) {
+        TaskQuery query = QueryUtils.taskQuery();
+        TaskEntity task = (TaskEntity) query.taskId(delegateBo.getTaskId()).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())).singleResult();
+        if (ObjectUtil.isEmpty(task)) {
+            throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
+        }
+        if (task.isSuspended()) {
+            throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+        }
+        try {
+            TaskEntity newTask = WorkflowUtils.createNewTask(task);
+            taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.PENDING.getStatus(), "銆�" + LoginHelper.getLoginUser().getNickname() + "銆戝娲剧粰銆�" + delegateBo.getNickName() + "銆�");
+            //濮旀墭浠诲姟
+            taskService.delegateTask(delegateBo.getTaskId(), delegateBo.getUserId());
+            //鍔炵悊鐢熸垚鐨勪换鍔¤褰�
+            taskService.complete(newTask.getId());
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 缁堟浠诲姟
+     *
+     * @param terminationBo 鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean terminationTask(TerminationBo terminationBo) {
+        TaskQuery query = QueryUtils.taskQuery();
+        Task task = query.taskId(terminationBo.getTaskId()).singleResult();
+
+        if (ObjectUtil.isEmpty(task)) {
+            throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
+        }
+        if (task.isSuspended()) {
+            throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+        }
+        HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
+        BusinessStatusEnum.checkInvalidStatus(historicProcessInstance.getBusinessStatus());
+        try {
+            if (StringUtils.isBlank(terminationBo.getComment())) {
+                terminationBo.setComment(LoginHelper.getLoginUser().getNickname() + "缁堟浜嗙敵璇�");
+            } else {
+                terminationBo.setComment(LoginHelper.getLoginUser().getNickname() + "缁堟浜嗙敵璇凤細" + terminationBo.getComment());
+            }
+            taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.TERMINATION.getStatus(), terminationBo.getComment());
+            List<Task> list = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
+            if (CollUtil.isNotEmpty(list)) {
+                List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
+                if (CollUtil.isNotEmpty(subTasks)) {
+                    subTasks.forEach(e -> taskService.deleteTask(e.getId()));
+                }
+                runtimeService.updateBusinessStatus(task.getProcessInstanceId(), BusinessStatusEnum.TERMINATION.getStatus());
+                runtimeService.deleteProcessInstance(task.getProcessInstanceId(), StrUtil.EMPTY);
+            }
+            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(historicProcessInstance.getProcessDefinitionKey());
+            if (processHandler != null) {
+                processHandler.handleProcess(historicProcessInstance.getBusinessKey(), BusinessStatusEnum.TERMINATION.getStatus(), false);
+            }
+            return true;
+        } catch (Exception e) {
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 杞姙浠诲姟
+     *
+     * @param transmitBo 鍙傛暟
+     */
+    @Override
+    public boolean transferTask(TransmitBo transmitBo) {
+        Task task = QueryUtils.taskQuery().taskId(transmitBo.getTaskId()).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())).singleResult();
+        if (ObjectUtil.isEmpty(task)) {
+            throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
+        }
+        if (task.isSuspended()) {
+            throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+        }
+        try {
+            TaskEntity newTask = WorkflowUtils.createNewTask(task);
+            taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.TRANSFER.getStatus(), StringUtils.isNotBlank(transmitBo.getComment()) ? transmitBo.getComment() : LoginHelper.getUsername() + "杞姙浜嗕换鍔�");
+            taskService.complete(newTask.getId());
+            taskService.setAssignee(task.getId(), transmitBo.getUserId());
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 浼氱浠诲姟鍔犵
+     *
+     * @param addMultiBo 鍙傛暟
+     */
+    @Override
+    public boolean addMultiInstanceExecution(AddMultiBo addMultiBo) {
+        TaskQuery taskQuery = QueryUtils.taskQuery();
+        taskQuery.taskId(addMultiBo.getTaskId());
+        if (!LoginHelper.isSuperAdmin() && !LoginHelper.isTenantAdmin()) {
+            taskQuery.taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId()));
+        }
+        Task task = taskQuery.singleResult();
+        if (ObjectUtil.isEmpty(task)) {
+            throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
+        }
+        if (task.isSuspended()) {
+            throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+        }
+        String taskDefinitionKey = task.getTaskDefinitionKey();
+        String processInstanceId = task.getProcessInstanceId();
+        String processDefinitionId = task.getProcessDefinitionId();
+
+        try {
+            MultiInstanceVo multiInstanceVo = WorkflowUtils.isMultiInstance(processDefinitionId, taskDefinitionKey);
+            if (multiInstanceVo == null) {
+                throw new ServiceException("褰撳墠鐜妭涓嶆槸浼氱鑺傜偣");
+            }
+            if (multiInstanceVo.getType() instanceof ParallelMultiInstanceBehavior) {
+                for (Long assignee : addMultiBo.getAssignees()) {
+                    runtimeService.addMultiInstanceExecution(taskDefinitionKey, processInstanceId, Collections.singletonMap(multiInstanceVo.getAssignee(), assignee));
+                }
+            } else if (multiInstanceVo.getType() instanceof SequentialMultiInstanceBehavior) {
+                AddSequenceMultiInstanceCmd addSequenceMultiInstanceCmd = new AddSequenceMultiInstanceCmd(task.getExecutionId(), multiInstanceVo.getAssigneeList(), addMultiBo.getAssignees());
+                managementService.executeCommand(addSequenceMultiInstanceCmd);
+            }
+            List<String> assigneeNames = addMultiBo.getAssigneeNames();
+            String username = LoginHelper.getUsername();
+            TaskEntity newTask = WorkflowUtils.createNewTask(task);
+            taskService.addComment(newTask.getId(), processInstanceId, TaskStatusEnum.SIGN.getStatus(), username + "鍔犵銆�" + String.join(StringUtils.SEPARATOR, assigneeNames) + "銆�");
+            taskService.complete(newTask.getId());
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 浼氱浠诲姟鍑忕
+     *
+     * @param deleteMultiBo 鍙傛暟
+     */
+    @Override
+    public boolean deleteMultiInstanceExecution(DeleteMultiBo deleteMultiBo) {
+        TaskQuery taskQuery = QueryUtils.taskQuery();
+        taskQuery.taskId(deleteMultiBo.getTaskId());
+        if (!LoginHelper.isSuperAdmin() && !LoginHelper.isTenantAdmin()) {
+            taskQuery.taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId()));
+        }
+        Task task = taskQuery.singleResult();
+        if (ObjectUtil.isEmpty(task)) {
+            throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
+        }
+        if (task.isSuspended()) {
+            throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+        }
+        String taskDefinitionKey = task.getTaskDefinitionKey();
+        String processInstanceId = task.getProcessInstanceId();
+        String processDefinitionId = task.getProcessDefinitionId();
+        try {
+            MultiInstanceVo multiInstanceVo = WorkflowUtils.isMultiInstance(processDefinitionId, taskDefinitionKey);
+            if (multiInstanceVo == null) {
+                throw new ServiceException("褰撳墠鐜妭涓嶆槸浼氱鑺傜偣");
+            }
+            if (multiInstanceVo.getType() instanceof ParallelMultiInstanceBehavior) {
+                for (String executionId : deleteMultiBo.getExecutionIds()) {
+                    runtimeService.deleteMultiInstanceExecution(executionId, false);
+                }
+                for (String taskId : deleteMultiBo.getTaskIds()) {
+                    historyService.deleteHistoricTaskInstance(taskId);
+                }
+            } else if (multiInstanceVo.getType() instanceof SequentialMultiInstanceBehavior) {
+                DeleteSequenceMultiInstanceCmd deleteSequenceMultiInstanceCmd = new DeleteSequenceMultiInstanceCmd(task.getAssignee(), task.getExecutionId(), multiInstanceVo.getAssigneeList(), deleteMultiBo.getAssigneeIds());
+                managementService.executeCommand(deleteSequenceMultiInstanceCmd);
+            }
+            List<String> assigneeNames = deleteMultiBo.getAssigneeNames();
+            String username = LoginHelper.getUsername();
+            TaskEntity newTask = WorkflowUtils.createNewTask(task);
+            taskService.addComment(newTask.getId(), processInstanceId, TaskStatusEnum.SIGN_OFF.getStatus(), username + "鍑忕銆�" + String.join(StringUtils.SEPARATOR, assigneeNames) + "銆�");
+            taskService.complete(newTask.getId());
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 椹冲洖瀹℃壒
+     *
+     * @param backProcessBo 鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String backProcess(BackProcessBo backProcessBo) {
+        TaskQuery query = QueryUtils.taskQuery();
+        String userId = String.valueOf(LoginHelper.getUserId());
+        Task task = query.taskId(backProcessBo.getTaskId()).taskCandidateOrAssigned(userId).singleResult();
+        if (ObjectUtil.isEmpty(task)) {
+            throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL);
+        }
+        if (task.isSuspended()) {
+            throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+        }
+        try {
+            String processInstanceId = task.getProcessInstanceId();
+            ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult();
+            //鑾峰彇骞惰缃戝叧鎵ц鍚庝繚鐣欑殑鎵ц瀹炰緥鏁版嵁
+            ExecutionChildByExecutionIdCmd childByExecutionIdCmd = new ExecutionChildByExecutionIdCmd(task.getExecutionId());
+            List<ExecutionEntity> executionEntities = managementService.executeCommand(childByExecutionIdCmd);
+            //鏍¢獙鍗曟嵁
+            BusinessStatusEnum.checkBackStatus(processInstance.getBusinessStatus());
+            //鍒ゆ柇鏄惁鏈夊涓换鍔�
+            List<Task> taskList = QueryUtils.taskQuery(processInstanceId).list();
+            String backTaskDefinitionKey = backProcessBo.getTargetActivityId();
+            taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.BACK.getStatus(), StringUtils.isNotBlank(backProcessBo.getMessage()) ? backProcessBo.getMessage() : "閫�鍥�");
+            if (taskList.size() > 1) {
+                //褰撳墠澶氫釜浠诲姟椹冲洖鍒板崟涓妭鐐�
+                runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdsToSingleActivityId(taskList.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList()), backTaskDefinitionKey).changeState();
+                ActHiTaskinst actHiTaskinst = new ActHiTaskinst();
+                actHiTaskinst.setAssignee(userId);
+                actHiTaskinst.setId(task.getId());
+                actHiTaskinstMapper.updateById(actHiTaskinst);
+            } else {
+                //褰撳墠鍗曚釜鑺傜偣椹冲洖鍗曚釜鑺傜偣
+                runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdTo(task.getTaskDefinitionKey(), backTaskDefinitionKey).changeState();
+            }
+            //鍒犻櫎骞惰鐜妭鏈姙鐞嗚褰�
+            MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+            if (multiInstance == null && taskList.size() > 1) {
+                List<Task> tasks = StreamUtils.filter(taskList, e -> !e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey()));
+                actHiTaskinstMapper.deleteBatchIds(StreamUtils.toList(tasks, Task::getId));
+            }
+
+
+            List<HistoricTaskInstance> instanceList = QueryUtils.hisTaskInstanceQuery(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().desc().list();
+            List<Task> list = QueryUtils.taskQuery(processInstanceId).list();
+            for (Task t : list) {
+                instanceList.stream().filter(e -> e.getTaskDefinitionKey().equals(t.getTaskDefinitionKey())).findFirst().ifPresent(e -> {
+                    taskService.setAssignee(t.getId(), e.getAssignee());
+                });
+            }
+            //鍙戦�佹秷鎭�
+            String message = "鎮ㄧ殑銆�" + processInstance.getName() + "銆戝崟鎹凡缁忚椹冲洖锛岃鎮ㄦ敞鎰忔煡鏀躲��";
+            sendMessage(list, processInstance.getName(), backProcessBo.getMessageType(), message);
+            //鍒犻櫎娴佺▼瀹炰緥鍨冨溇鏁版嵁
+            for (ExecutionEntity executionEntity : executionEntities) {
+                DeleteExecutionCmd deleteExecutionCmd = new DeleteExecutionCmd(executionEntity.getId());
+                managementService.executeCommand(deleteExecutionCmd);
+            }
+
+            WfTaskBackNode wfTaskBackNode = wfTaskBackNodeService.getListByInstanceIdAndNodeId(task.getProcessInstanceId(), backProcessBo.getTargetActivityId());
+            if (ObjectUtil.isNotNull(wfTaskBackNode) && wfTaskBackNode.getOrderNo() == 0) {
+                runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.BACK.getStatus());
+                FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey());
+                if (processHandler != null) {
+                    processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.BACK.getStatus(), false);
+                }
+            }
+            //鍒犻櫎椹冲洖鍚庣殑娴佺▼鑺傜偣
+            wfTaskBackNodeService.deleteBackTaskNode(processInstanceId, backProcessBo.getTargetActivityId());
+        } catch (Exception e) {
+            throw new ServiceException(e.getMessage());
+        }
+        return task.getProcessInstanceId();
+    }
+
+    /**
+     * 淇敼浠诲姟鍔炵悊浜�
+     *
+     * @param taskIds 浠诲姟id
+     * @param userId  鍔炵悊浜篿d
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean updateAssignee(String[] taskIds, String userId) {
+        try {
+            List<Task> list = QueryUtils.taskQuery().taskIds(Arrays.asList(taskIds)).list();
+            for (Task task : list) {
+                taskService.setAssignee(task.getId(), userId);
+            }
+        } catch (Exception e) {
+            throw new ServiceException("淇敼澶辫触锛�" + e.getMessage());
+        }
+        return true;
+    }
+
+    /**
+     * 鏌ヨ娴佺▼鍙橀噺
+     *
+     * @param taskId 浠诲姟id
+     */
+    @Override
+    public List<VariableVo> getInstanceVariable(String taskId) {
+        List<VariableVo> variableVoList = new ArrayList<>();
+        Map<String, VariableInstance> variableInstances = taskService.getVariableInstances(taskId);
+        if (CollUtil.isNotEmpty(variableInstances)) {
+            for (Map.Entry<String, VariableInstance> entry : variableInstances.entrySet()) {
+                VariableVo variableVo = new VariableVo();
+                variableVo.setKey(entry.getKey());
+                variableVo.setValue(entry.getValue().getValue().toString());
+                variableVoList.add(variableVo);
+            }
+        }
+        return variableVoList;
+    }
+
+    /**
+     * 鏌ヨ宸ヤ綔娴佷换鍔$敤鎴烽�夋嫨鍔犵浜哄憳
+     *
+     * @param taskId 浠诲姟id
+     * @return
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public String getTaskUserIdsByAddMultiInstance(String taskId) {
+        Task task = QueryUtils.taskQuery().taskId(taskId).singleResult();
+        if (task == null) {
+            throw new ServiceException("浠诲姟涓嶅瓨鍦�");
+        }
+        MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+        if (multiInstance == null) {
+            return "";
+        }
+        List<Long> userIds;
+        if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) {
+            userIds = (List<Long>) runtimeService.getVariable(task.getExecutionId(), multiInstance.getAssigneeList());
+        } else {
+            List<Task> list = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
+            userIds = StreamUtils.toList(list, e -> Long.valueOf(e.getAssignee()));
+        }
+        return StringUtils.join(userIds, StringUtils.SEPARATOR);
+    }
+
+    /**
+     * 鏌ヨ宸ヤ綔娴侀�夋嫨鍑忕浜哄憳
+     *
+     * @param taskId 浠诲姟id 浠诲姟id
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public List<TaskVo> getListByDeleteMultiInstance(String taskId) {
+        Task task = QueryUtils.taskQuery().taskId(taskId).singleResult();
+        List<Task> taskList = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
+        MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+        List<TaskVo> taskListVo = new ArrayList<>();
+        if (multiInstance == null) {
+            return List.of();
+        }
+        List<Long> assigneeList = new ArrayList<>();
+        if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) {
+            List<Object> variable = (List<Object>) runtimeService.getVariable(task.getExecutionId(), multiInstance.getAssigneeList());
+            for (Object o : variable) {
+                assigneeList.add(Long.valueOf(o.toString()));
+            }
+        }
+
+        if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) {
+            List<Long> userIds = StreamUtils.filter(assigneeList, e -> !String.valueOf(e).equals(task.getAssignee()));
+            List<UserDTO> userList = userService.selectListByIds(userIds);
+            for (Long userId : userIds) {
+                TaskVo taskVo = new TaskVo();
+                taskVo.setId("涓茶浼氱");
+                taskVo.setExecutionId("涓茶浼氱");
+                taskVo.setProcessInstanceId(task.getProcessInstanceId());
+                taskVo.setName(task.getName());
+                taskVo.setAssignee(userId);
+                if (CollUtil.isNotEmpty(userList)) {
+                    userList.stream().filter(u -> u.getUserId().toString().equals(userId.toString())).findFirst().ifPresent(u -> taskVo.setAssigneeName(u.getNickName()));
+                }
+                taskListVo.add(taskVo);
+            }
+            return taskListVo;
+        } else if (multiInstance.getType() instanceof ParallelMultiInstanceBehavior) {
+            List<Task> tasks = StreamUtils.filter(taskList, e -> StringUtils.isBlank(e.getParentTaskId()) && !e.getExecutionId().equals(task.getExecutionId()) && e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey()));
+            if (CollUtil.isNotEmpty(tasks)) {
+                List<Long> userIds = StreamUtils.toList(tasks, e -> Long.valueOf(e.getAssignee()));
+                List<UserDTO> userList = userService.selectListByIds(userIds);
+                for (Task t : tasks) {
+                    TaskVo taskVo = new TaskVo();
+                    taskVo.setId(t.getId());
+                    taskVo.setExecutionId(t.getExecutionId());
+                    taskVo.setProcessInstanceId(t.getProcessInstanceId());
+                    taskVo.setName(t.getName());
+                    taskVo.setAssignee(Long.valueOf(t.getAssignee()));
+                    if (CollUtil.isNotEmpty(userList)) {
+                        userList.stream().filter(u -> u.getUserId().toString().equals(t.getAssignee())).findFirst().ifPresent(e -> taskVo.setAssigneeName(e.getNickName()));
+                    }
+                    taskListVo.add(taskVo);
+                }
+                return taskListVo;
+            }
+        }
+        return List.of();
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java
new file mode 100644
index 0000000..bee5627
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java
@@ -0,0 +1,121 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.workflow.domain.TestLeave;
+import org.dromara.workflow.domain.bo.TestLeaveBo;
+import org.dromara.workflow.domain.vo.TestLeaveVo;
+import org.dromara.workflow.mapper.TestLeaveMapper;
+import org.dromara.workflow.service.IActProcessInstanceService;
+import org.dromara.workflow.service.ITestLeaveService;
+import org.dromara.workflow.utils.WorkflowUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 璇峰亣Service涓氬姟灞傚鐞�
+ *
+ * @author may
+ * @date 2023-07-21
+ */
+@RequiredArgsConstructor
+@Service
+public class TestLeaveServiceImpl implements ITestLeaveService {
+
+    private final TestLeaveMapper baseMapper;
+    private final IActProcessInstanceService actProcessInstanceService;
+
+    /**
+     * 鏌ヨ璇峰亣
+     */
+    @Override
+    public TestLeaveVo queryById(Long id) {
+        TestLeaveVo testLeaveVo = baseMapper.selectVoById(id);
+        WorkflowUtils.setProcessInstanceVo(testLeaveVo, String.valueOf(id));
+        return testLeaveVo;
+    }
+
+    /**
+     * 鏌ヨ璇峰亣鍒楄〃
+     */
+    @Override
+    public TableDataInfo<TestLeaveVo> queryPageList(TestLeaveBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<TestLeave> lqw = buildQueryWrapper(bo);
+        Page<TestLeaveVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        TableDataInfo<TestLeaveVo> build = TableDataInfo.build(result);
+        List<TestLeaveVo> rows = build.getRows();
+        if (CollUtil.isNotEmpty(rows)) {
+            List<String> ids = StreamUtils.toList(rows, e -> String.valueOf(e.getId()));
+            WorkflowUtils.setProcessInstanceListVo(rows, ids, "id");
+        }
+        return build;
+    }
+
+    /**
+     * 鏌ヨ璇峰亣鍒楄〃
+     */
+    @Override
+    public List<TestLeaveVo> queryList(TestLeaveBo bo) {
+        LambdaQueryWrapper<TestLeave> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<TestLeave> buildQueryWrapper(TestLeaveBo bo) {
+        LambdaQueryWrapper<TestLeave> lqw = Wrappers.lambdaQuery();
+        lqw.eq(StringUtils.isNotBlank(bo.getLeaveType()), TestLeave::getLeaveType, bo.getLeaveType());
+        lqw.ge(bo.getStartLeaveDays() != null, TestLeave::getLeaveDays, bo.getStartLeaveDays());
+        lqw.le(bo.getEndLeaveDays() != null, TestLeave::getLeaveDays, bo.getEndLeaveDays());
+        lqw.orderByDesc(BaseEntity::getCreateTime);
+        return lqw;
+    }
+
+    /**
+     * 鏂板璇峰亣
+     */
+    @Override
+    public TestLeaveVo insertByBo(TestLeaveBo bo) {
+        TestLeave add = MapstructUtils.convert(bo, TestLeave.class);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        TestLeaveVo testLeaveVo = MapstructUtils.convert(add, TestLeaveVo.class);
+        WorkflowUtils.setProcessInstanceVo(testLeaveVo, String.valueOf(add.getId()));
+        return testLeaveVo;
+    }
+
+    /**
+     * 淇敼璇峰亣
+     */
+    @Override
+    public TestLeaveVo updateByBo(TestLeaveBo bo) {
+        TestLeave update = MapstructUtils.convert(bo, TestLeave.class);
+        baseMapper.updateById(update);
+        TestLeaveVo testLeaveVo = MapstructUtils.convert(update, TestLeaveVo.class);
+        WorkflowUtils.setProcessInstanceVo(testLeaveVo, String.valueOf(update.getId()));
+        return testLeaveVo;
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎璇峰亣
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean deleteWithValidByIds(Collection<Long> ids) {
+        List<String> idList = StreamUtils.toList(ids, String::valueOf);
+        actProcessInstanceService.deleteRunAndHisInstanceByBusinessKeys(idList);
+        return baseMapper.deleteBatchIds(ids) > 0;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java
new file mode 100644
index 0000000..c5cb288
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java
@@ -0,0 +1,129 @@
+package org.dromara.workflow.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.workflow.domain.WfCategory;
+import org.dromara.workflow.domain.bo.WfCategoryBo;
+import org.dromara.workflow.domain.vo.WfCategoryVo;
+import org.dromara.workflow.mapper.WfCategoryMapper;
+import org.dromara.workflow.service.IWfCategoryService;
+import org.dromara.workflow.utils.QueryUtils;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.engine.repository.Model;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 娴佺▼鍒嗙被Service涓氬姟灞傚鐞�
+ *
+ * @author may
+ * @date 2023-06-28
+ */
+@RequiredArgsConstructor
+@Service
+public class WfCategoryServiceImpl implements IWfCategoryService {
+
+    private final WfCategoryMapper baseMapper;
+
+    private final RepositoryService repositoryService;
+
+    /**
+     * 鏌ヨ娴佺▼鍒嗙被
+     */
+    @Override
+    public WfCategoryVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+
+    /**
+     * 鏌ヨ娴佺▼鍒嗙被鍒楄〃
+     */
+    @Override
+    public List<WfCategoryVo> queryList(WfCategoryBo bo) {
+        LambdaQueryWrapper<WfCategory> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<WfCategory> buildQueryWrapper(WfCategoryBo bo) {
+        LambdaQueryWrapper<WfCategory> lqw = Wrappers.lambdaQuery();
+        lqw.like(StringUtils.isNotBlank(bo.getCategoryName()), WfCategory::getCategoryName, bo.getCategoryName());
+        lqw.eq(StringUtils.isNotBlank(bo.getCategoryCode()), WfCategory::getCategoryCode, bo.getCategoryCode());
+        return lqw;
+    }
+
+    /**
+     * 鏂板娴佺▼鍒嗙被
+     */
+    @Override
+    public Boolean insertByBo(WfCategoryBo bo) {
+        WfCategory add = MapstructUtils.convert(bo, WfCategory.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 淇敼娴佺▼鍒嗙被
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateByBo(WfCategoryBo bo) {
+        WfCategory update = MapstructUtils.convert(bo, WfCategory.class);
+        validEntityBeforeSave(update);
+        WfCategoryVo wfCategoryVo = baseMapper.selectVoById(bo.getId());
+        List<ProcessDefinition> processDefinitionList = QueryUtils.definitionQuery().processDefinitionCategory(wfCategoryVo.getCategoryCode()).list();
+        for (ProcessDefinition processDefinition : processDefinitionList) {
+            repositoryService.setProcessDefinitionCategory(processDefinition.getId(), bo.getCategoryCode());
+        }
+        List<Deployment> deploymentList = QueryUtils.deploymentQuery().deploymentCategory(wfCategoryVo.getCategoryCode()).list();
+        for (Deployment deployment : deploymentList) {
+            repositoryService.setDeploymentCategory(deployment.getId(), bo.getCategoryCode());
+        }
+        List<Model> modelList = QueryUtils.modelQuery().modelCategory(wfCategoryVo.getCategoryCode()).list();
+        for (Model model : modelList) {
+            model.setCategory(bo.getCategoryCode());
+            repositoryService.saveModel(model);
+        }
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙
+     */
+    private void validEntityBeforeSave(WfCategory entity) {
+        //TODO 鍋氫竴浜涙暟鎹牎楠�,濡傚敮涓�绾︽潫
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎娴佺▼鍒嗙被
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠�
+        }
+        return baseMapper.deleteBatchIds(ids) > 0;
+    }
+
+    /**
+     * 鎸夌収绫诲埆缂栫爜鏌ヨ
+     *
+     * @param categoryCode 鍒嗙被姣斿悧
+     */
+    @Override
+    public WfCategory queryByCategoryCode(String categoryCode) {
+        return baseMapper.selectOne(new LambdaQueryWrapper<WfCategory>().eq(WfCategory::getCategoryCode, categoryCode));
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java
new file mode 100644
index 0000000..9478825
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java
@@ -0,0 +1,117 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import org.dromara.common.core.utils.MapstructUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import org.dromara.workflow.domain.WfDefinitionConfig;
+import org.dromara.workflow.domain.bo.WfDefinitionConfigBo;
+import org.dromara.workflow.domain.vo.WfDefinitionConfigVo;
+import org.dromara.workflow.service.IWfDefinitionConfigService;
+import org.springframework.stereotype.Service;
+import org.dromara.workflow.mapper.WfDefinitionConfigMapper;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Collection;
+
+/**
+ * 娴佺▼瀹氫箟閰嶇疆Service涓氬姟灞傚鐞�
+ *
+ * @author may
+ * @date 2024-03-18
+ */
+@RequiredArgsConstructor
+@Service
+public class WfDefinitionConfigServiceImpl implements IWfDefinitionConfigService {
+
+    private final WfDefinitionConfigMapper baseMapper;
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆
+     */
+    @Override
+    public WfDefinitionConfigVo getByDefId(String definitionId) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<WfDefinitionConfig>().eq(WfDefinitionConfig::getDefinitionId, definitionId));
+    }
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆
+     *
+     * @param tableName 琛ㄥ悕
+     * @return 缁撴灉
+     */
+    @Override
+    public WfDefinitionConfigVo getByTableNameLastVersion(String tableName) {
+        List<WfDefinitionConfigVo> wfDefinitionConfigVos = baseMapper.selectVoList(
+            new LambdaQueryWrapper<WfDefinitionConfig>().eq(WfDefinitionConfig::getTableName, tableName).orderByDesc(WfDefinitionConfig::getVersion));
+        if (CollUtil.isNotEmpty(wfDefinitionConfigVos)) {
+            return wfDefinitionConfigVos.get(0);
+        }
+        return null;
+    }
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆
+     *
+     * @param definitionId 娴佺▼瀹氫箟id
+     * @param tableName    琛ㄥ悕
+     * @return 缁撴灉
+     */
+    @Override
+    public WfDefinitionConfigVo getByDefIdAndTableName(String definitionId, String tableName) {
+        return baseMapper.selectVoOne(new LambdaQueryWrapper<WfDefinitionConfig>()
+            .eq(WfDefinitionConfig::getDefinitionId, definitionId)
+            .eq(WfDefinitionConfig::getTableName, tableName));
+    }
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆鎺掗櫎褰撳墠鏌ヨ鐨勬祦绋嬪畾涔�
+     *
+     * @param tableName    琛ㄥ悕
+     * @param definitionId 娴佺▼瀹氫箟id
+     */
+    @Override
+    public List<WfDefinitionConfigVo> getByTableNameNotDefId(String tableName, String definitionId) {
+        return baseMapper.selectVoList(new LambdaQueryWrapper<WfDefinitionConfig>()
+            .eq(WfDefinitionConfig::getTableName, tableName)
+            .ne(WfDefinitionConfig::getDefinitionId, definitionId));
+    }
+
+    /**
+     * 鏌ヨ娴佺▼瀹氫箟閰嶇疆鍒楄〃
+     */
+    @Override
+    public List<WfDefinitionConfigVo> queryList(List<String> definitionIds) {
+        return baseMapper.selectVoList(new LambdaQueryWrapper<WfDefinitionConfig>().in(WfDefinitionConfig::getDefinitionId, definitionIds));
+    }
+
+    /**
+     * 鏂板娴佺▼瀹氫箟閰嶇疆
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean saveOrUpdate(WfDefinitionConfigBo bo) {
+        WfDefinitionConfig add = MapstructUtils.convert(bo, WfDefinitionConfig.class);
+        baseMapper.delete(new LambdaQueryWrapper<WfDefinitionConfig>().eq(WfDefinitionConfig::getTableName, bo.getTableName()));
+        add.setTableName(add.getTableName().toLowerCase());
+        boolean flag = baseMapper.insertOrUpdate(add);
+        if (baseMapper.insertOrUpdate(add)) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎娴佺▼瀹氫箟閰嶇疆
+     */
+    @Override
+    public Boolean deleteByIds(Collection<Long> ids) {
+        return baseMapper.deleteBatchIds(ids) > 0;
+    }
+
+    @Override
+    public Boolean deleteByDefIds(Collection<String> ids) {
+        return baseMapper.delete(new LambdaQueryWrapper<WfDefinitionConfig>().in(WfDefinitionConfig::getDefinitionId, ids)) > 0;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java
new file mode 100644
index 0000000..e0850d7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java
@@ -0,0 +1,111 @@
+package org.dromara.workflow.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import org.dromara.workflow.common.enums.FormTypeEnum;
+import org.springframework.stereotype.Service;
+import org.dromara.workflow.domain.bo.WfFormManageBo;
+import org.dromara.workflow.domain.vo.WfFormManageVo;
+import org.dromara.workflow.domain.WfFormManage;
+import org.dromara.workflow.mapper.WfFormManageMapper;
+import org.dromara.workflow.service.IWfFormManageService;
+
+import java.util.List;
+import java.util.Collection;
+
+/**
+ * 琛ㄥ崟绠$悊Service涓氬姟灞傚鐞�
+ *
+ * @author may
+ * @date 2024-03-29
+ */
+@RequiredArgsConstructor
+@Service
+public class WfFormManageServiceImpl implements IWfFormManageService {
+
+    private final WfFormManageMapper baseMapper;
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊
+     */
+    @Override
+    public WfFormManageVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    @Override
+    public List<WfFormManageVo> queryByIds(List<Long> ids) {
+        return baseMapper.selectVoBatchIds(ids);
+    }
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+     */
+    @Override
+    public TableDataInfo<WfFormManageVo> queryPageList(WfFormManageBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<WfFormManage> lqw = buildQueryWrapper(bo);
+        Page<WfFormManageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    @Override
+    public List<WfFormManageVo> selectList() {
+        List<WfFormManageVo> wfFormManageVos = baseMapper.selectVoList(new LambdaQueryWrapper<WfFormManage>().orderByDesc(WfFormManage::getUpdateTime));
+        for (WfFormManageVo wfFormManageVo : wfFormManageVos) {
+            wfFormManageVo.setFormTypeName(FormTypeEnum.findByType(wfFormManageVo.getFormType()));
+        }
+        return wfFormManageVos;
+    }
+
+    /**
+     * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+     */
+    @Override
+    public List<WfFormManageVo> queryList(WfFormManageBo bo) {
+        LambdaQueryWrapper<WfFormManage> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<WfFormManage> buildQueryWrapper(WfFormManageBo bo) {
+        LambdaQueryWrapper<WfFormManage> lqw = Wrappers.lambdaQuery();
+        lqw.like(StringUtils.isNotBlank(bo.getFormName()), WfFormManage::getFormName, bo.getFormName());
+        lqw.eq(StringUtils.isNotBlank(bo.getFormType()), WfFormManage::getFormType, bo.getFormType());
+        return lqw;
+    }
+
+    /**
+     * 鏂板琛ㄥ崟绠$悊
+     */
+    @Override
+    public Boolean insertByBo(WfFormManageBo bo) {
+        WfFormManage add = MapstructUtils.convert(bo, WfFormManage.class);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 淇敼琛ㄥ崟绠$悊
+     */
+    @Override
+    public Boolean updateByBo(WfFormManageBo bo) {
+        WfFormManage update = MapstructUtils.convert(bo, WfFormManage.class);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎琛ㄥ崟绠$悊
+     */
+    @Override
+    public Boolean deleteByIds(Collection<Long> ids) {
+        return baseMapper.deleteBatchIds(ids) > 0;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfNodeConfigServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfNodeConfigServiceImpl.java
new file mode 100644
index 0000000..c677102
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfNodeConfigServiceImpl.java
@@ -0,0 +1,75 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.workflow.domain.vo.WfFormManageVo;
+import org.dromara.workflow.service.IWfFormManageService;
+import org.springframework.stereotype.Service;
+import org.dromara.workflow.domain.vo.WfNodeConfigVo;
+import org.dromara.workflow.domain.WfNodeConfig;
+import org.dromara.workflow.mapper.WfNodeConfigMapper;
+import org.dromara.workflow.service.IWfNodeConfigService;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 鑺傜偣閰嶇疆Service涓氬姟灞傚鐞�
+ *
+ * @author may
+ * @date 2024-03-30
+ */
+@RequiredArgsConstructor
+@Service
+public class WfNodeConfigServiceImpl implements IWfNodeConfigService {
+
+    private final WfNodeConfigMapper baseMapper;
+    private final IWfFormManageService wfFormManageService;
+
+    /**
+     * 鏌ヨ鑺傜偣閰嶇疆
+     */
+    @Override
+    public WfNodeConfigVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 淇濆瓨鑺傜偣閰嶇疆
+     */
+    @Override
+    public Boolean saveOrUpdate(List<WfNodeConfig> list) {
+        return baseMapper.insertOrUpdateBatch(list);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎鑺傜偣閰嶇疆
+     */
+    @Override
+    public Boolean deleteByIds(Collection<Long> ids) {
+        return baseMapper.deleteBatchIds(ids) > 0;
+    }
+
+
+
+    @Override
+    public Boolean deleteByDefIds(Collection<String> ids) {
+        return baseMapper.delete(new LambdaQueryWrapper<WfNodeConfig>().in(WfNodeConfig::getDefinitionId, ids)) > 0;
+    }
+
+    @Override
+    public List<WfNodeConfigVo> selectByDefIds(Collection<String> ids) {
+        List<WfNodeConfigVo> wfNodeConfigVos = baseMapper.selectVoList(new LambdaQueryWrapper<WfNodeConfig>().in(WfNodeConfig::getDefinitionId, ids));
+        if (CollUtil.isNotEmpty(wfNodeConfigVos)) {
+            List<Long> formIds = StreamUtils.toList(wfNodeConfigVos, WfNodeConfigVo::getFormId);
+            List<WfFormManageVo> wfFormManageVos = wfFormManageService.queryByIds(formIds);
+            for (WfNodeConfigVo wfNodeConfigVo : wfNodeConfigVos) {
+                wfFormManageVos.stream().filter(e -> ObjectUtil.equals(e.getId(), wfNodeConfigVo.getFormId())).findFirst().ifPresent(wfNodeConfigVo::setWfFormManageVo);
+            }
+        }
+        return wfNodeConfigVos;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java
new file mode 100644
index 0000000..0992f6f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java
@@ -0,0 +1,141 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.satoken.utils.LoginHelper;
+import org.dromara.workflow.domain.WfTaskBackNode;
+import org.dromara.workflow.domain.vo.MultiInstanceVo;
+import org.dromara.workflow.mapper.WfTaskBackNodeMapper;
+import org.dromara.workflow.service.IWfTaskBackNodeService;
+import org.dromara.workflow.utils.WorkflowUtils;
+import org.flowable.task.api.Task;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.dromara.workflow.common.constant.FlowConstant.MULTI_INSTANCE;
+import static org.dromara.workflow.common.constant.FlowConstant.USER_TASK;
+
+
+/**
+ * 鑺傜偣椹冲洖璁板綍Service涓氬姟灞傚鐞�
+ *
+ * @author may
+ * @date 2024-03-13
+ */
+@RequiredArgsConstructor
+@Service
+public class WfTaskBackNodeServiceImpl implements IWfTaskBackNodeService {
+
+    private final WfTaskBackNodeMapper wfTaskBackNodeMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void recordExecuteNode(Task task) {
+        List<WfTaskBackNode> list = getListByInstanceId(task.getProcessInstanceId());
+        WfTaskBackNode wfTaskBackNode = new WfTaskBackNode();
+        wfTaskBackNode.setNodeId(task.getTaskDefinitionKey());
+        wfTaskBackNode.setNodeName(task.getName());
+        wfTaskBackNode.setInstanceId(task.getProcessInstanceId());
+        wfTaskBackNode.setAssignee(String.valueOf(LoginHelper.getUserId()));
+        MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
+        if (ObjectUtil.isNotEmpty(multiInstance)) {
+            wfTaskBackNode.setTaskType(MULTI_INSTANCE);
+        } else {
+            wfTaskBackNode.setTaskType(USER_TASK);
+        }
+        if (CollUtil.isEmpty(list)) {
+            wfTaskBackNode.setOrderNo(0);
+            wfTaskBackNodeMapper.insert(wfTaskBackNode);
+        } else {
+            WfTaskBackNode taskNode = list.stream().filter(e -> e.getNodeId().equals(wfTaskBackNode.getNodeId()) && e.getOrderNo() == 0).findFirst().orElse(null);
+            if (ObjectUtil.isEmpty(taskNode)) {
+                wfTaskBackNode.setOrderNo(list.get(0).getOrderNo() + 1);
+                WfTaskBackNode node = getListByInstanceIdAndNodeId(wfTaskBackNode.getInstanceId(), wfTaskBackNode.getNodeId());
+                if (ObjectUtil.isNotEmpty(node)) {
+                    node.setAssignee(node.getAssignee() + StringUtils.SEPARATOR + LoginHelper.getUserId());
+                    wfTaskBackNodeMapper.updateById(node);
+                } else {
+                    wfTaskBackNodeMapper.insert(wfTaskBackNode);
+                }
+            }
+        }
+    }
+
+    @Override
+    public List<WfTaskBackNode> getListByInstanceId(String processInstanceId) {
+        LambdaQueryWrapper<WfTaskBackNode> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
+        wrapper.orderByDesc(WfTaskBackNode::getOrderNo);
+        return wfTaskBackNodeMapper.selectList(wrapper);
+    }
+
+    @Override
+    public WfTaskBackNode getListByInstanceIdAndNodeId(String processInstanceId, String nodeId) {
+        LambdaQueryWrapper<WfTaskBackNode> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
+        queryWrapper.eq(WfTaskBackNode::getNodeId, nodeId);
+        return wfTaskBackNodeMapper.selectOne(queryWrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteBackTaskNode(String processInstanceId, String targetActivityId) {
+        try {
+            LambdaQueryWrapper<WfTaskBackNode> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
+            queryWrapper.eq(WfTaskBackNode::getNodeId, targetActivityId);
+            WfTaskBackNode actTaskNode = wfTaskBackNodeMapper.selectOne(queryWrapper);
+            if (ObjectUtil.isNotNull(actTaskNode)) {
+                Integer orderNo = actTaskNode.getOrderNo();
+                List<WfTaskBackNode> taskNodeList = getListByInstanceId(processInstanceId);
+                List<Long> ids = new ArrayList<>();
+                if (CollUtil.isNotEmpty(taskNodeList)) {
+                    for (WfTaskBackNode taskNode : taskNodeList) {
+                        if (taskNode.getOrderNo() >= orderNo) {
+                            ids.add(taskNode.getId());
+                        }
+                    }
+                }
+                if (CollUtil.isNotEmpty(ids)) {
+                    wfTaskBackNodeMapper.deleteBatchIds(ids);
+                }
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException("鍒犻櫎澶辫触");
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteByInstanceId(String processInstanceId) {
+        LambdaQueryWrapper<WfTaskBackNode> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
+        List<WfTaskBackNode> list = wfTaskBackNodeMapper.selectList(wrapper);
+        int delete = wfTaskBackNodeMapper.delete(wrapper);
+        if (list.size() != delete) {
+            throw new ServiceException("鍒犻櫎澶辫触");
+        }
+        return true;
+    }
+
+    @Override
+    public boolean deleteByInstanceIds(List<String> processInstanceIds) {
+        LambdaQueryWrapper<WfTaskBackNode> wrapper = new LambdaQueryWrapper<>();
+        wrapper.in(WfTaskBackNode::getInstanceId, processInstanceIds);
+        List<WfTaskBackNode> list = wfTaskBackNodeMapper.selectList(wrapper);
+        int delete = wfTaskBackNodeMapper.delete(wrapper);
+        if (list.size() != delete) {
+            throw new ServiceException("鍒犻櫎澶辫触");
+        }
+        return true;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java
new file mode 100644
index 0000000..7c5377e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java
@@ -0,0 +1,289 @@
+package org.dromara.workflow.utils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.json.utils.JsonUtils;
+import org.dromara.workflow.domain.vo.MultiInstanceVo;
+import org.flowable.bpmn.converter.BpmnXMLConverter;
+import org.flowable.bpmn.model.*;
+import org.flowable.bpmn.model.Process;
+import org.flowable.editor.language.json.converter.BpmnJsonConverter;
+import org.flowable.engine.ProcessEngine;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.rmi.ServerException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 妯″瀷宸ュ叿
+ *
+ * @author may
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class ModelUtils {
+
+    private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);
+
+    public static BpmnModel xmlToBpmnModel(String xml) throws IOException {
+        if (xml == null) {
+            throw new ServerException("xml涓嶈兘涓虹┖");
+        }
+        try {
+            InputStream inputStream = new ByteArrayInputStream(StrUtil.utf8Bytes(xml));
+            XMLInputFactory factory = XMLInputFactory.newInstance();
+            XMLStreamReader reader = factory.createXMLStreamReader(inputStream);
+            return new BpmnXMLConverter().convertToBpmnModel(reader);
+        } catch (XMLStreamException e) {
+            throw new ServerException(e.getMessage());
+        }
+    }
+
+    /**
+     * bpmnModel杞负xml
+     *
+     * @param jsonBytes json
+     */
+    public static byte[] bpmnJsonToXmlBytes(byte[] jsonBytes) throws IOException {
+        if (jsonBytes == null) {
+            return new byte[0];
+        }
+        // 1. json瀛楄妭鐮佽浆鎴� BpmnModel 瀵硅薄
+        ObjectMapper objectMapper = JsonUtils.getObjectMapper();
+        JsonNode jsonNode = objectMapper.readTree(jsonBytes);
+        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
+
+        if (bpmnModel.getProcesses().isEmpty()) {
+            return new byte[0];
+        }
+        // 2.灏哹pmnModel杞负xml
+        return new BpmnXMLConverter().convertToXML(bpmnModel);
+    }
+
+    /**
+     * xml杞负bpmnModel
+     *
+     * @param xmlBytes xml
+     */
+    public static BpmnModel xmlToBpmnModel(byte[] xmlBytes) throws XMLStreamException {
+        ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(xmlBytes);
+        XMLInputFactory xif = XMLInputFactory.newInstance();
+        XMLStreamReader xtr = xif.createXMLStreamReader(byteArrayInputStream);
+        return new BpmnXMLConverter().convertToBpmnModel(xtr);
+    }
+
+    /**
+     * 鏍¢獙妯″瀷
+     *
+     * @param bpmnModel bpmn妯″瀷
+     */
+    public static void checkBpmnModel(BpmnModel bpmnModel) throws ServerException {
+        Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
+
+        checkBpmnNode(flowElements, false);
+
+        List<SubProcess> subProcessList = flowElements.stream().filter(SubProcess.class::isInstance).map(SubProcess.class::cast).collect(Collectors.toList());
+        if (!CollUtil.isEmpty(subProcessList)) {
+            for (SubProcess subProcess : subProcessList) {
+                Collection<FlowElement> subProcessFlowElements = subProcess.getFlowElements();
+                checkBpmnNode(subProcessFlowElements, true);
+            }
+        }
+        List<MultiInstanceVo> multiInstanceVoList = new ArrayList<>();
+        for (FlowElement flowElement : flowElements) {
+            if (flowElement instanceof UserTask && ObjectUtil.isNotEmpty(((UserTask) flowElement).getLoopCharacteristics()) && StringUtils.isNotBlank(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem())) {
+                MultiInstanceVo multiInstanceVo = new MultiInstanceVo();
+                multiInstanceVo.setAssigneeList(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem());
+                multiInstanceVoList.add(multiInstanceVo);
+            }
+        }
+
+        if (CollectionUtil.isNotEmpty(multiInstanceVoList) && multiInstanceVoList.size() > 1) {
+            Map<String, List<MultiInstanceVo>> assigneeListGroup = StreamUtils.groupByKey(multiInstanceVoList, MultiInstanceVo::getAssigneeList);
+            for (Map.Entry<String, List<MultiInstanceVo>> entry : assigneeListGroup.entrySet()) {
+                List<MultiInstanceVo> value = entry.getValue();
+                if (CollectionUtil.isNotEmpty(value) && value.size() > 1) {
+                    String key = entry.getKey();
+                    throw new ServerException("浼氱浜哄憳闆嗗悎銆�" + key + "銆戦噸澶�,璇烽噸鏂拌缃泦鍚圞EY");
+                }
+            }
+        }
+    }
+
+    /**
+     * 鏍¢獙bpmn鑺傜偣鏄惁鍚堟硶
+     *
+     * @param flowElements 鑺傜偣闆嗗悎
+     * @param subtask      鏄惁瀛愭祦绋�
+     */
+    private static void checkBpmnNode(Collection<FlowElement> flowElements, boolean subtask) throws ServerException {
+
+        if (CollUtil.isEmpty(flowElements)) {
+            throw new ServerException(subtask ? "瀛愭祦绋嬪繀椤诲瓨鍦ㄨ妭鐐�" : "蹇呴』瀛樺湪鑺傜偣锛�");
+        }
+
+        List<StartEvent> startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList());
+        if (CollUtil.isEmpty(startEventList)) {
+            throw new ServerException(subtask ? "瀛愭祦绋嬪繀椤诲瓨鍦ㄥ紑濮嬭妭鐐�" : "蹇呴』瀛樺湪寮�濮嬭妭鐐癸紒");
+        }
+
+        if (startEventList.size() > 1) {
+            throw new ServerException(subtask ? "瀛愭祦绋嬪彧鑳藉瓨鍦ㄤ竴涓紑濮嬭妭鐐�" : "鍙兘瀛樺湪涓�涓紑濮嬭妭鐐癸紒");
+        }
+
+        StartEvent startEvent = startEventList.get(0);
+        List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows();
+        if (CollUtil.isEmpty(outgoingFlows)) {
+            throw new ServerException(subtask ? "瀛愭祦绋嬫祦绋嬭妭鐐逛负绌猴紝璇疯嚦灏戣璁′竴鏉′富绾挎祦绋嬶紒" : "娴佺▼鑺傜偣涓虹┖锛岃鑷冲皯璁捐涓�鏉′富绾挎祦绋嬶紒");
+        }
+
+        FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();
+        if (!(targetFlowElement instanceof UserTask) && !subtask) {
+            throw new ServerException("寮�濮嬭妭鐐瑰悗绗竴涓妭鐐瑰繀椤绘槸鐢ㄦ埛浠诲姟锛�");
+        }
+        //寮�濮嬭妭鐐瑰悗绗竴涓妭鐐圭敵璇蜂汉鑺傜偣
+        if ((targetFlowElement instanceof UserTask) && !subtask) {
+            UserTask userTask = (UserTask) targetFlowElement;
+            if (StringUtils.isBlank(userTask.getFormKey())) {
+                throw new ServerException("鐢宠浜鸿妭鐐瑰繀椤婚�夋嫨琛ㄥ崟锛�");
+            }
+        }
+        List<EndEvent> endEventList = flowElements.stream().filter(EndEvent.class::isInstance).map(EndEvent.class::cast).collect(Collectors.toList());
+        if (CollUtil.isEmpty(endEventList)) {
+            throw new ServerException(subtask ? "瀛愭祦绋嬪繀椤诲瓨鍦ㄧ粨鏉熻妭鐐癸紒" : "蹇呴』瀛樺湪缁撴潫鑺傜偣锛�");
+        }
+    }
+
+    /**
+     * 鑾峰彇娴佺▼鍏ㄩ儴鐢ㄦ埛鑺傜偣
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    public static List<UserTask> getUserTaskFlowElements(String processDefinitionId) {
+        BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
+        List<UserTask> list = new ArrayList<>();
+        List<Process> processes = bpmnModel.getProcesses();
+        Collection<FlowElement> flowElements = processes.get(0).getFlowElements();
+        buildUserTaskFlowElements(flowElements, list);
+        return list;
+    }
+
+    /**
+     * 閫掑綊鑾峰彇鎵�鏈夎妭鐐�
+     *
+     * @param flowElements 鑺傜偣淇℃伅
+     * @param list         闆嗗悎
+     */
+    private static void buildUserTaskFlowElements(Collection<FlowElement> flowElements, List<UserTask> list) {
+        for (FlowElement flowElement : flowElements) {
+            if (flowElement instanceof SubProcess) {
+                Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements();
+                buildUserTaskFlowElements(subFlowElements, list);
+            } else if (flowElement instanceof UserTask) {
+                list.add((UserTask) flowElement);
+            }
+        }
+    }
+
+    /**
+     * 鑾峰彇娴佺▼鍏ㄩ儴鑺傜偣
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    public static List<FlowElement> getFlowElements(String processDefinitionId) {
+        BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
+        List<FlowElement> list = new ArrayList<>();
+        List<Process> processes = bpmnModel.getProcesses();
+        Collection<FlowElement> flowElements = processes.get(0).getFlowElements();
+        buildFlowElements(flowElements, list);
+        return list;
+    }
+
+    /**
+     * 閫掑綊鑾峰彇鎵�鏈夎妭鐐�
+     *
+     * @param flowElements 鑺傜偣淇℃伅
+     * @param list         闆嗗悎
+     */
+    private static void buildFlowElements(Collection<FlowElement> flowElements, List<FlowElement> list) {
+        for (FlowElement flowElement : flowElements) {
+            list.add(flowElement);
+            if (flowElement instanceof SubProcess) {
+                Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements();
+                buildFlowElements(subFlowElements, list);
+            }
+        }
+    }
+
+    /**
+     * 鑾峰彇鍏ㄩ儴鎵╁睍淇℃伅
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     */
+    public static Map<String, List<ExtensionElement>> getExtensionElements(String processDefinitionId) {
+        Map<String, List<ExtensionElement>> map = new HashMap<>();
+        List<FlowElement> flowElements = getFlowElements(processDefinitionId);
+        for (FlowElement flowElement : flowElements) {
+            if (flowElement instanceof UserTask && CollUtil.isNotEmpty(flowElement.getExtensionElements())) {
+                map.putAll(flowElement.getExtensionElements());
+            }
+        }
+        return map;
+    }
+
+    /**
+     * 鑾峰彇鏌愪釜鑺傜偣鐨勬墿灞曚俊鎭�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     * @param flowElementId       鑺傜偣id
+     */
+    public static Map<String, List<ExtensionElement>> getExtensionElement(String processDefinitionId, String flowElementId) {
+        BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
+        Process process = bpmnModel.getMainProcess();
+        FlowElement flowElement = process.getFlowElement(flowElementId);
+        return flowElement.getExtensionElements();
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠鑺傜偣鏄惁涓虹敤鎴蜂换鍔�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     * @param taskDefinitionKey   娴佺▼瀹氫箟id
+     */
+    public static boolean isUserTask(String processDefinitionId, String taskDefinitionKey) {
+        BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
+        FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey);
+        return flowNode instanceof UserTask;
+    }
+
+    /**
+     * 鑾峰彇鐢宠浜鸿妭鐐�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     * @return 缁撴灉
+     */
+    public static UserTask getApplyUserTask(String processDefinitionId) {
+        BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
+        Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
+        List<StartEvent> startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList());
+        StartEvent startEvent = startEventList.get(0);
+        List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows();
+        FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();
+        return (UserTask) targetFlowElement;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/QueryUtils.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/QueryUtils.java
new file mode 100644
index 0000000..944d9d7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/QueryUtils.java
@@ -0,0 +1,154 @@
+package org.dromara.workflow.utils;
+
+import cn.hutool.core.bean.BeanUtil;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.workflow.domain.vo.TaskVo;
+import org.flowable.engine.ProcessEngine;
+import org.flowable.engine.history.HistoricActivityInstanceQuery;
+import org.flowable.engine.history.HistoricProcessInstanceQuery;
+import org.flowable.engine.repository.DeploymentQuery;
+import org.flowable.engine.repository.ModelQuery;
+import org.flowable.engine.repository.ProcessDefinitionQuery;
+import org.flowable.engine.runtime.ProcessInstanceQuery;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.TaskQuery;
+import org.flowable.task.api.history.HistoricTaskInstanceQuery;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 鏌ヨ宸ュ叿
+ *
+ * @author Lion Li
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class QueryUtils {
+
+    private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);
+
+    public static ModelQuery modelQuery() {
+        ModelQuery query = PROCESS_ENGINE.getRepositoryService().createModelQuery();
+        if (TenantHelper.isEnable()) {
+            query.modelTenantId(TenantHelper.getTenantId());
+        }
+        return query;
+    }
+
+    public static ProcessDefinitionQuery definitionQuery() {
+        ProcessDefinitionQuery query = PROCESS_ENGINE.getRepositoryService().createProcessDefinitionQuery();
+        if (TenantHelper.isEnable()) {
+            query.processDefinitionTenantId(TenantHelper.getTenantId());
+        }
+        return query;
+    }
+
+    public static DeploymentQuery deploymentQuery() {
+        DeploymentQuery query = PROCESS_ENGINE.getRepositoryService().createDeploymentQuery();
+        if (TenantHelper.isEnable()) {
+            query.deploymentTenantId(TenantHelper.getTenantId());
+        }
+        return query;
+    }
+
+    public static DeploymentQuery deploymentQuery(String deploymentId) {
+        return deploymentQuery().deploymentId(deploymentId);
+    }
+
+    public static DeploymentQuery deploymentQuery(List<String> deploymentIds) {
+        return deploymentQuery().deploymentIds(deploymentIds);
+    }
+
+    public static HistoricTaskInstanceQuery hisTaskInstanceQuery() {
+        HistoricTaskInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricTaskInstanceQuery();
+        if (TenantHelper.isEnable()) {
+            query.taskTenantId(TenantHelper.getTenantId());
+        }
+        return query;
+    }
+
+    public static HistoricTaskInstanceQuery hisTaskInstanceQuery(String processInstanceId) {
+        return hisTaskInstanceQuery().processInstanceId(processInstanceId);
+    }
+
+    public static ProcessInstanceQuery instanceQuery() {
+        ProcessInstanceQuery query = PROCESS_ENGINE.getRuntimeService().createProcessInstanceQuery();
+        if (TenantHelper.isEnable()) {
+            query.processInstanceTenantId(TenantHelper.getTenantId());
+        }
+        return query;
+    }
+
+    public static ProcessInstanceQuery instanceQuery(String processInstanceId) {
+        return instanceQuery().processInstanceId(processInstanceId);
+    }
+
+    public static ProcessInstanceQuery instanceQuery(Set<String> processInstanceIds) {
+        return instanceQuery().processInstanceIds(processInstanceIds);
+    }
+
+    public static HistoricProcessInstanceQuery hisInstanceQuery() {
+        HistoricProcessInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricProcessInstanceQuery();
+        if (TenantHelper.isEnable()) {
+            query.processInstanceTenantId(TenantHelper.getTenantId());
+        }
+        return query;
+    }
+
+    public static HistoricProcessInstanceQuery hisInstanceQuery(String processInstanceId) {
+        return hisInstanceQuery().processInstanceId(processInstanceId);
+    }
+
+    public static HistoricProcessInstanceQuery hisInstanceQuery(Set<String> processInstanceIds) {
+        return hisInstanceQuery().processInstanceIds(processInstanceIds);
+    }
+
+    public static HistoricActivityInstanceQuery hisActivityInstanceQuery() {
+        HistoricActivityInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricActivityInstanceQuery();
+        if (TenantHelper.isEnable()) {
+            query.activityTenantId(TenantHelper.getTenantId());
+        }
+        return query;
+    }
+
+    public static HistoricActivityInstanceQuery hisActivityInstanceQuery(String processInstanceId) {
+        return hisActivityInstanceQuery().processInstanceId(processInstanceId);
+    }
+
+    public static TaskQuery taskQuery() {
+        TaskQuery query = PROCESS_ENGINE.getTaskService().createTaskQuery();
+        if (TenantHelper.isEnable()) {
+            query.taskTenantId(TenantHelper.getTenantId());
+        }
+        return query;
+    }
+
+    public static TaskQuery taskQuery(String processInstanceId) {
+        return taskQuery().processInstanceId(processInstanceId);
+    }
+
+    public static TaskQuery taskQuery(Collection<String> processInstanceIds) {
+        return taskQuery().processInstanceIdIn(processInstanceIds);
+    }
+
+    /**
+     * 鎸夌収浠诲姟id鏌ヨ褰撳墠浠诲姟
+     *
+     * @param taskId 浠诲姟id
+     */
+    public static TaskVo getTask(String taskId) {
+        Task task = PROCESS_ENGINE.getTaskService().createTaskQuery().taskId(taskId).singleResult();
+        if (task == null) {
+            return null;
+        }
+        TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
+        taskVo.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null);
+        String businessStatus = WorkflowUtils.getBusinessStatus(taskVo.getProcessInstanceId());
+        taskVo.setBusinessStatus(businessStatus);
+        return taskVo;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java
new file mode 100644
index 0000000..f475f19
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java
@@ -0,0 +1,343 @@
+package org.dromara.workflow.utils;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.dromara.common.core.domain.dto.UserDTO;
+import org.dromara.common.core.service.UserService;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.core.utils.reflect.ReflectUtils;
+import org.dromara.common.mail.utils.MailUtils;
+import org.dromara.common.tenant.helper.TenantHelper;
+import org.dromara.common.websocket.dto.WebSocketMessageDto;
+import org.dromara.common.websocket.utils.WebSocketUtils;
+import org.dromara.workflow.common.constant.FlowConstant;
+import org.dromara.workflow.common.enums.BusinessStatusEnum;
+import org.dromara.workflow.common.enums.MessageTypeEnum;
+import org.dromara.workflow.common.enums.TaskStatusEnum;
+import org.dromara.workflow.domain.ActHiProcinst;
+import org.dromara.workflow.domain.ActHiTaskinst;
+import org.dromara.workflow.domain.vo.MultiInstanceVo;
+import org.dromara.workflow.domain.vo.ParticipantVo;
+import org.dromara.workflow.domain.vo.ProcessInstanceVo;
+import org.dromara.workflow.flowable.cmd.UpdateHiTaskInstCmd;
+import org.dromara.workflow.mapper.ActHiTaskinstMapper;
+import org.dromara.workflow.service.IActHiProcinstService;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.bpmn.model.FlowNode;
+import org.flowable.common.engine.api.delegate.Expression;
+import org.flowable.engine.ProcessEngine;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
+import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
+import org.flowable.identitylink.api.history.HistoricIdentityLink;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.flowable.task.service.impl.persistence.entity.TaskEntity;
+
+import java.util.*;
+
+import static org.dromara.workflow.common.constant.FlowConstant.PROCESS_INSTANCE_VO;
+
+/**
+ * 宸ヤ綔娴佸伐鍏�
+ *
+ * @author may
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class WorkflowUtils {
+
+    private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class);
+    private static final UserService USER_SERVICE = SpringUtils.getBean(UserService.class);
+    private static final IActHiProcinstService ACT_HI_PROCINST_SERVICE = SpringUtils.getBean(IActHiProcinstService.class);
+    private static final ActHiTaskinstMapper ACT_HI_TASKINST_MAPPER = SpringUtils.getBean(ActHiTaskinstMapper.class);
+
+    /**
+     * 鍒涘缓涓�涓柊浠诲姟
+     *
+     * @param currentTask 鍙傛暟
+     */
+    public static TaskEntity createNewTask(Task currentTask) {
+        TaskEntity task = null;
+        if (ObjectUtil.isNotEmpty(currentTask)) {
+            task = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask();
+            task.setCategory(currentTask.getCategory());
+            task.setDescription(currentTask.getDescription());
+            task.setAssignee(currentTask.getAssignee());
+            task.setName(currentTask.getName());
+            task.setProcessDefinitionId(currentTask.getProcessDefinitionId());
+            task.setProcessInstanceId(currentTask.getProcessInstanceId());
+            task.setTaskDefinitionKey(currentTask.getTaskDefinitionKey());
+            task.setPriority(currentTask.getPriority());
+            task.setCreateTime(new Date());
+            task.setTenantId(TenantHelper.getTenantId());
+            PROCESS_ENGINE.getTaskService().saveTask(task);
+        }
+        if (ObjectUtil.isNotNull(task)) {
+            UpdateHiTaskInstCmd updateHiTaskInstCmd = new UpdateHiTaskInstCmd(Collections.singletonList(task.getId()), task.getProcessDefinitionId(), task.getProcessInstanceId());
+            PROCESS_ENGINE.getManagementService().executeCommand(updateHiTaskInstCmd);
+        }
+        return task;
+    }
+
+    /**
+     * 鎶勯�佷换鍔�
+     *
+     * @param parentTaskList 鐖剁骇浠诲姟
+     * @param userIds        浜哄憳id
+     */
+    public static void createCopyTask(List<Task> parentTaskList, List<Long> userIds) {
+        List<Task> list = new ArrayList<>();
+        String tenantId = TenantHelper.getTenantId();
+        for (Task parentTask : parentTaskList) {
+            for (Long userId : userIds) {
+                TaskEntity newTask = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask();
+                newTask.setParentTaskId(parentTask.getId());
+                newTask.setAssignee(userId.toString());
+                newTask.setName("銆愭妱閫併��-" + parentTask.getName());
+                newTask.setProcessDefinitionId(parentTask.getProcessDefinitionId());
+                newTask.setProcessInstanceId(parentTask.getProcessInstanceId());
+                newTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
+                newTask.setTenantId(tenantId);
+                list.add(newTask);
+            }
+        }
+        PROCESS_ENGINE.getTaskService().bulkSaveTasks(list);
+        if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(parentTaskList)) {
+            String processInstanceId = parentTaskList.get(0).getProcessInstanceId();
+            String processDefinitionId = parentTaskList.get(0).getProcessDefinitionId();
+            List<String> taskIds = StreamUtils.toList(list, Task::getId);
+            ActHiTaskinst actHiTaskinst = new ActHiTaskinst();
+            actHiTaskinst.setProcDefId(processDefinitionId);
+            actHiTaskinst.setProcInstId(processInstanceId);
+            actHiTaskinst.setScopeType(TaskStatusEnum.COPY.getStatus());
+            actHiTaskinst.setTenantId(tenantId);
+            LambdaUpdateWrapper<ActHiTaskinst> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.in(ActHiTaskinst::getId, taskIds);
+            ACT_HI_TASKINST_MAPPER.update(actHiTaskinst, updateWrapper);
+            for (Task task : list) {
+                PROCESS_ENGINE.getTaskService().addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), StrUtil.EMPTY);
+            }
+        }
+    }
+
+    /**
+     * 鑾峰彇褰撳墠浠诲姟鍙備笌鑰�
+     *
+     * @param taskId 浠诲姟id
+     */
+    public static ParticipantVo getCurrentTaskParticipant(String taskId) {
+        ParticipantVo participantVo = new ParticipantVo();
+        List<HistoricIdentityLink> linksForTask = PROCESS_ENGINE.getHistoryService().getHistoricIdentityLinksForTask(taskId);
+        Task task = QueryUtils.taskQuery().taskId(taskId).singleResult();
+        if (task != null && CollUtil.isNotEmpty(linksForTask)) {
+            List<HistoricIdentityLink> groupList = StreamUtils.filter(linksForTask, e -> StringUtils.isNotBlank(e.getGroupId()));
+            if (CollUtil.isNotEmpty(groupList)) {
+                List<Long> groupIds = StreamUtils.toList(groupList, e -> Long.valueOf(e.getGroupId()));
+                List<Long> userIds = USER_SERVICE.selectUserIdsByRoleIds(groupIds);
+                if (CollUtil.isNotEmpty(userIds)) {
+                    participantVo.setGroupIds(groupIds);
+                    List<UserDTO> userList = USER_SERVICE.selectListByIds(userIds);
+                    if (CollUtil.isNotEmpty(userList)) {
+                        List<Long> userIdList = StreamUtils.toList(userList, UserDTO::getUserId);
+                        List<String> nickNames = StreamUtils.toList(userList, UserDTO::getNickName);
+                        participantVo.setCandidate(userIdList);
+                        participantVo.setCandidateName(nickNames);
+                        participantVo.setClaim(!StringUtils.isBlank(task.getAssignee()));
+                    }
+                }
+            } else {
+                List<HistoricIdentityLink> candidateList = StreamUtils.filter(linksForTask, e -> FlowConstant.CANDIDATE.equals(e.getType()));
+                List<Long> userIdList = new ArrayList<>();
+                for (HistoricIdentityLink historicIdentityLink : linksForTask) {
+                    try {
+                        userIdList.add(Long.valueOf(historicIdentityLink.getUserId()));
+                    } catch (NumberFormatException ignored) {
+
+                    }
+                }
+                List<UserDTO> userList = USER_SERVICE.selectListByIds(userIdList);
+                if (CollUtil.isNotEmpty(userList)) {
+                    List<Long> userIds = StreamUtils.toList(userList, UserDTO::getUserId);
+                    List<String> nickNames = StreamUtils.toList(userList, UserDTO::getNickName);
+                    participantVo.setCandidate(userIds);
+                    participantVo.setCandidateName(nickNames);
+                    // 鍒ゆ柇褰撳墠浠诲姟鏄惁鍏锋湁澶氫釜鍔炵悊浜�
+                    if (CollUtil.isNotEmpty(candidateList) && candidateList.size() > 1) {
+                        // 濡傛灉 assignee 瀛樺湪锛屽垯璁剧疆褰撳墠浠诲姟宸茬粡琚棰�
+                        participantVo.setClaim(StringUtils.isNotBlank(task.getAssignee()));
+                    }
+                }
+            }
+        }
+        return participantVo;
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠鑺傜偣鏄惁涓轰細绛捐妭鐐�
+     *
+     * @param processDefinitionId 娴佺▼瀹氫箟id
+     * @param taskDefinitionKey   娴佺▼瀹氫箟id
+     */
+    public static MultiInstanceVo isMultiInstance(String processDefinitionId, String taskDefinitionKey) {
+        BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId);
+        FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey);
+        MultiInstanceVo multiInstanceVo = new MultiInstanceVo();
+        //鍒ゆ柇鏄惁涓哄苟琛屼細绛捐妭鐐�
+        if (flowNode.getBehavior() instanceof ParallelMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) {
+            Expression collectionExpression = behavior.getCollectionExpression();
+            String assigneeList = collectionExpression.getExpressionText();
+            String assignee = behavior.getCollectionElementVariable();
+            multiInstanceVo.setType(behavior);
+            multiInstanceVo.setAssignee(assignee);
+            multiInstanceVo.setAssigneeList(assigneeList);
+            return multiInstanceVo;
+            //鍒ゆ柇鏄惁涓轰覆琛屼細绛捐妭鐐�
+        } else if (flowNode.getBehavior() instanceof SequentialMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) {
+            Expression collectionExpression = behavior.getCollectionExpression();
+            String assigneeList = collectionExpression.getExpressionText();
+            String assignee = behavior.getCollectionElementVariable();
+            multiInstanceVo.setType(behavior);
+            multiInstanceVo.setAssignee(assignee);
+            multiInstanceVo.setAssigneeList(assigneeList);
+            return multiInstanceVo;
+        }
+        return null;
+    }
+
+    /**
+     * 鑾峰彇褰撳墠娴佺▼鐘舵��
+     *
+     * @param taskId 浠诲姟id
+     */
+    public static String getBusinessStatusByTaskId(String taskId) {
+        HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery().taskId(taskId).singleResult();
+        HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(historicTaskInstance.getProcessInstanceId()).singleResult();
+        return historicProcessInstance.getBusinessStatus();
+    }
+
+    /**
+     * 鑾峰彇褰撳墠娴佺▼鐘舵��
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    public static String getBusinessStatus(String processInstanceId) {
+        HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(processInstanceId).singleResult();
+        return historicProcessInstance.getBusinessStatus();
+    }
+
+    /**
+     * 璁剧疆娴佺▼瀹炰緥瀵硅薄
+     *
+     * @param obj         涓氬姟瀵硅薄
+     * @param businessKey 涓氬姟id
+     */
+    public static void setProcessInstanceVo(Object obj, String businessKey) {
+        if (StringUtils.isBlank(businessKey) || obj == null) {
+            return;
+        }
+        ActHiProcinst actHiProcinst = ACT_HI_PROCINST_SERVICE.selectByBusinessKey(businessKey);
+        if (actHiProcinst == null) {
+            ProcessInstanceVo processInstanceVo = new ProcessInstanceVo();
+            processInstanceVo.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus());
+            ReflectUtils.invokeSetter(obj, PROCESS_INSTANCE_VO, processInstanceVo);
+            return;
+        }
+        ProcessInstanceVo processInstanceVo = BeanUtil.toBean(actHiProcinst, ProcessInstanceVo.class);
+        processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstanceVo.getBusinessStatus()));
+        ReflectUtils.invokeSetter(obj, PROCESS_INSTANCE_VO, processInstanceVo);
+    }
+
+    /**
+     * 璁剧疆娴佺▼瀹炰緥瀵硅薄
+     *
+     * @param obj       涓氬姟瀵硅薄
+     * @param idList    涓氬姟id
+     * @param fieldName 涓婚敭灞炴�у悕绉�
+     */
+    public static void setProcessInstanceListVo(Object obj, List<String> idList, String fieldName) {
+        if (CollUtil.isEmpty(idList) || obj == null) {
+            return;
+        }
+        List<ActHiProcinst> actHiProcinstList = ACT_HI_PROCINST_SERVICE.selectByBusinessKeyIn(idList);
+        if (obj instanceof Collection<?> collection) {
+            for (Object o : collection) {
+                String fieldValue = ReflectUtils.invokeGetter(o, fieldName).toString();
+                if (CollUtil.isEmpty(actHiProcinstList)) {
+                    ProcessInstanceVo processInstanceVo = new ProcessInstanceVo();
+                    processInstanceVo.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus());
+                    processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstanceVo.getBusinessStatus()));
+                    ReflectUtils.invokeSetter(o, PROCESS_INSTANCE_VO, processInstanceVo);
+                } else {
+                    ActHiProcinst actHiProcinst = actHiProcinstList.stream().filter(e -> e.getBusinessKey().equals(fieldValue)).findFirst().orElse(null);
+                    if (ObjectUtil.isNotEmpty(actHiProcinst)) {
+                        ProcessInstanceVo processInstanceVo = BeanUtil.toBean(actHiProcinst, ProcessInstanceVo.class);
+                        processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstanceVo.getBusinessStatus()));
+                        ReflectUtils.invokeSetter(o, PROCESS_INSTANCE_VO, processInstanceVo);
+                    } else {
+                        ProcessInstanceVo processInstanceVo = new ProcessInstanceVo();
+                        processInstanceVo.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus());
+                        processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstanceVo.getBusinessStatus()));
+                        ReflectUtils.invokeSetter(o, PROCESS_INSTANCE_VO, processInstanceVo);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 鍙戦�佹秷鎭�
+     *
+     * @param list        浠诲姟
+     * @param name        娴佺▼鍚嶇О
+     * @param messageType 娑堟伅绫诲瀷
+     * @param message     娑堟伅鍐呭锛屼负绌哄垯鍙戦�侀粯璁ら厤缃殑娑堟伅鍐呭
+     */
+    public static void sendMessage(List<Task> list, String name, List<String> messageType, String message) {
+        Set<Long> userIds = new HashSet<>();
+        if (StringUtils.isBlank(message)) {
+            message = "鏈夋柊鐨勩��" + name + "銆戝崟鎹凡缁忔彁浜よ嚦鎮ㄧ殑寰呭姙锛岃鎮ㄥ強鏃跺鐞嗐��";
+        }
+        for (Task t : list) {
+            ParticipantVo taskParticipant = WorkflowUtils.getCurrentTaskParticipant(t.getId());
+            if (CollUtil.isNotEmpty(taskParticipant.getGroupIds())) {
+                List<Long> userIdList = USER_SERVICE.selectUserIdsByRoleIds(taskParticipant.getGroupIds());
+                if (CollUtil.isNotEmpty(userIdList)) {
+                    userIds.addAll(userIdList);
+                }
+            }
+            List<Long> candidate = taskParticipant.getCandidate();
+            if (CollUtil.isNotEmpty(candidate)) {
+                userIds.addAll(candidate);
+            }
+        }
+        if (CollUtil.isNotEmpty(userIds)) {
+            List<UserDTO> userList = USER_SERVICE.selectListByIds(new ArrayList<>(userIds));
+            for (String code : messageType) {
+                MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code);
+                if (ObjectUtil.isNotEmpty(messageTypeEnum)) {
+                    switch (messageTypeEnum) {
+                        case SYSTEM_MESSAGE:
+                            WebSocketMessageDto dto = new WebSocketMessageDto();
+                            dto.setSessionKeys(new ArrayList<>(userIds));
+                            dto.setMessage(message);
+                            WebSocketUtils.publishMessage(dto);
+                            break;
+                        case EMAIL_MESSAGE:
+                            MailUtils.sendText(StreamUtils.join(userList, UserDTO::getEmail), "鍗曟嵁瀹℃壒鎻愰啋", message);
+                            break;
+                        case SMS_MESSAGE:
+                            //todo 鐭俊鍙戦��
+                            break;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md
new file mode 100644
index 0000000..c938b1e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md
@@ -0,0 +1,3 @@
+java鍖呬娇鐢� `.` 鍒嗗壊 resource 鐩綍浣跨敤 `/` 鍒嗗壊
+<br>
+姝ゆ枃浠剁洰鐨� 闃叉鏂囦欢澶圭矘杩炴壘涓嶅埌 `xml` 鏂囦欢
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml
new file mode 100644
index 0000000..dd05785
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.ActHiProcinstMapper">
+
+</mapper>
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml
new file mode 100644
index 0000000..7e73b60
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.ActHiTaskinstMapper">
+
+</mapper>
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml
new file mode 100644
index 0000000..d1508ab
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.ActTaskMapper">
+    <resultMap type="org.dromara.workflow.domain.vo.TaskVo" id="TaskVoResult">
+        <result property="id" column="ID_"/>
+        <result property="name" column="NAME_"/>
+        <result property="description" column="DESCRIPTION_"/>
+        <result property="priority" column="PRIORITY_"/>
+        <result property="owner" column="OWNER_"/>
+        <result property="assignee" column="ASSIGNEE_"/>
+        <result property="processInstanceId" column="PROC_INST_ID_"/>
+        <result property="executionId" column="EXECUTION_ID_"/>
+        <result property="taskDefinitionId" column="TASK_DEF_ID_"/>
+        <result property="processDefinitionId" column="PROC_DEF_ID_"/>
+        <result property="createTime" column="CREATE_TIME_"/>
+        <result property="startTime" column="START_TIME_"/>
+        <result property="endTime" column="END_TIME_"/>
+        <result property="taskDefinitionKey" column="TASK_DEF_KEY_"/>
+        <result property="dueDate" column="DUE_DATE_"/>
+        <result property="category" column="CATEGORY_"/>
+        <result property="parentTaskId" column="PARENT_TASK_ID_"/>
+        <result property="tenantId" column="TENANT_ID_"/>
+        <result property="claimTime" column="CLAIM_TIME"/>
+        <result property="businessStatus" column="BUSINESS_STATUS_"/>
+        <result property="processDefinitionName" column="processDefinitionName"/>
+        <result property="processDefinitionKey" column="processDefinitionKey"/>
+        <result property="processDefinitionVersion" column="processDefinitionVersion"/>
+        <result property="businessKey" column="BUSINESS_KEY_"/>
+
+    </resultMap>
+    <select id="getTaskWaitByPage" resultMap="TaskVoResult">
+        select *
+        from (SELECT RES.*,
+                     AHP.BUSINESS_STATUS_,
+                     AHP.BUSINESS_KEY_,
+                     ARP.NAME_ AS processDefinitionName,
+                     ARP.KEY_  AS processDefinitionKey,
+                     ARP.VERSION_  AS processDefinitionVersion
+              FROM ACT_RU_TASK RES
+                       INNER JOIN ACT_HI_PROCINST AHP ON RES.PROC_INST_ID_ = AHP.PROC_INST_ID_
+                       INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = RES.PROC_DEF_ID_
+              WHERE RES.PARENT_TASK_ID_ IS NULL
+              ORDER BY RES.CREATE_TIME_ DESC) t ${ew.getCustomSqlSegment}
+    </select>
+
+    <select id="getTaskFinishByPage" resultMap="TaskVoResult">
+        select *
+        from (SELECT HTI.*,
+                     AHP.BUSINESS_STATUS_,
+                     AHP.BUSINESS_KEY_,
+                     ARP.NAME_ AS processDefinitionName,
+                     ARP.KEY_  AS processDefinitionKey,
+                     ARP.VERSION_  AS processDefinitionVersion
+              FROM ACT_HI_TASKINST HTI
+                       INNER JOIN ACT_HI_PROCINST AHP ON HTI.PROC_INST_ID_ = AHP.PROC_INST_ID_
+                       INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = HTI.PROC_DEF_ID_
+              WHERE HTI.PARENT_TASK_ID_ IS NULL AND HTI.END_TIME_ IS NOT NULL
+              ORDER BY HTI.START_TIME_ DESC) t ${ew.getCustomSqlSegment}
+    </select>
+
+    <select id="getTaskCopyByPage" resultMap="TaskVoResult">
+        select *
+        from (SELECT AHT.*,
+                     AHP.BUSINESS_STATUS_,
+                     AHP.BUSINESS_KEY_,
+                     ARP.NAME_ as processDefinitionName,
+                     ARP.KEY_  as processDefinitionKey,
+                     ARP.VERSION_  AS processDefinitionVersion
+              FROM ACT_HI_TASKINST AHT
+                       INNER JOIN ACT_HI_PROCINST AHP ON AHT.PROC_INST_ID_ = AHP.PROC_INST_ID_
+                       INNER JOIN ACT_RE_PROCDEF ARP ON ARP.ID_ = AHT.PROC_DEF_ID_
+              WHERE AHT.PARENT_TASK_ID_ IS NOT NULL
+                and AHT.scope_type_ = 'copy'
+              ORDER BY AHT.START_TIME_ DESC) t ${ew.getCustomSqlSegment}
+    </select>
+</mapper>
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml
new file mode 100644
index 0000000..d52f6b0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/TestLeaveMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.TestLeaveMapper">
+
+</mapper>
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml
new file mode 100644
index 0000000..4375cb2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.WfCategoryMapper">
+
+</mapper>
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfDefinitionConfigMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfDefinitionConfigMapper.xml
new file mode 100644
index 0000000..8d579f7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfDefinitionConfigMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.WfDefinitionConfigMapper">
+
+</mapper>
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfFormManageMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfFormManageMapper.xml
new file mode 100644
index 0000000..59221f8
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfFormManageMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.WfFormManageMapper">
+
+</mapper>
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfNodeConfigMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfNodeConfigMapper.xml
new file mode 100644
index 0000000..b65194f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfNodeConfigMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.WfNodeConfigMapper">
+
+</mapper>
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml
new file mode 100644
index 0000000..4a9179b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.workflow.mapper.WfTaskBackNodeMapper">
+
+</mapper>
diff --git a/script/bin/ry.bat b/script/bin/ry.bat
index d214af4..ea98cbe 100644
--- a/script/bin/ry.bat
+++ b/script/bin/ry.bat
@@ -5,7 +5,7 @@
 set AppName=ruoyi-admin.jar
 
 rem JVM鍙傛暟
-set JVM_OPTS="-Dname=%AppName%  -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC"
+set JVM_OPTS="-Dname=%AppName%  -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC"
 
 
 ECHO.
diff --git a/script/bin/ry.sh b/script/bin/ry.sh
index 0df25e7..a6f5d9c 100644
--- a/script/bin/ry.sh
+++ b/script/bin/ry.sh
@@ -3,7 +3,7 @@
 AppName=ruoyi-admin.jar
 
 # JVM鍙傛暟
-JVM_OPTS="-Dname=$AppName  -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC"
+JVM_OPTS="-Dname=$AppName  -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC"
 APP_HOME=`pwd`
 LOG_PATH=$APP_HOME/logs/$AppName.log
 
diff --git "a/script/bpmn/\346\250\241\345\236\213.zip" "b/script/bpmn/\346\250\241\345\236\213.zip"
new file mode 100644
index 0000000..d886e0f
--- /dev/null
+++ "b/script/bpmn/\346\250\241\345\236\213.zip"
Binary files differ
diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml
index 63d819e..97024a0 100644
--- a/script/docker/docker-compose.yml
+++ b/script/docker/docker-compose.yml
@@ -100,7 +100,7 @@
     network_mode: "host"
 
   ruoyi-server1:
-    image: ruoyi/ruoyi-server:5.1.2
+    image: ruoyi/ruoyi-server:5.2.0
     container_name: ruoyi-server1
     environment:
       # 鏃跺尯涓婃捣
@@ -115,7 +115,7 @@
     network_mode: "host"
 
   ruoyi-server2:
-    image: ruoyi/ruoyi-server:5.1.2
+    image: ruoyi/ruoyi-server:5.2.0
     container_name: ruoyi-server2
     environment:
       # 鏃跺尯涓婃捣
@@ -130,7 +130,7 @@
     network_mode: "host"
 
   ruoyi-monitor-admin:
-    image: ruoyi/ruoyi-monitor-admin:5.1.2
+    image: ruoyi/ruoyi-monitor-admin:5.2.0
     container_name: ruoyi-monitor-admin
     environment:
       # 鏃跺尯涓婃捣
@@ -141,18 +141,16 @@
     privileged: true
     network_mode: "host"
 
-  ruoyi-powerjob-server:
-    image: ruoyi/ruoyi-powerjob-server:5.1.2
-    container_name: ruoyi-powerjob-server
+  ruoyi-snailjob-server:
+    image: ruoyi/ruoyi-snailjob-server:5.2.0
+    container_name: ruoyi-snailjob-server
     environment:
       # 鏃跺尯涓婃捣
       TZ: Asia/Shanghai
     ports:
-      - "7700:7700"
-      - "10086:10086"
-      - "10010:10010"
+      - "8800:8800"
+      - "1788:1788"
     volumes:
-      - /docker/powerjob/logs/:/ruoyi/powerjob/logs
+      - /docker/snailjob/logs/:/ruoyi/snailjob/logs
     privileged: true
     network_mode: "host"
-
diff --git a/script/docker/nginx/conf/nginx.conf b/script/docker/nginx/conf/nginx.conf
index 2aa6587..dcc4525 100644
--- a/script/docker/nginx/conf/nginx.conf
+++ b/script/docker/nginx/conf/nginx.conf
@@ -31,8 +31,8 @@
         server 127.0.0.1:9090;
     }
 
-    upstream powerjob-server {
-        server 127.0.0.1:7700;
+    upstream snailjob-server {
+        server 127.0.0.1:8800;
     }
 
     server {
@@ -96,20 +96,12 @@
             proxy_pass http://monitor-admin/admin/;
         }
 
-        # https 浼氭嫤鎴唴閾炬墍鏈夌殑 http 璇锋眰 閫犳垚鍔熻兘鏃犳硶浣跨敤
-        # 瑙e喅鏂规1 灏� powerjob 鏈嶅姟 涔熼厤缃垚 https
-        # 瑙e喅鏂规2 灏嗚彍鍗曢厤缃负澶栭摼璁块棶 璧扮嫭绔嬮〉闈� http 璁块棶
-        location /powerjob/ {
+        location /snail-job/ {
             proxy_set_header Host $http_host;
             proxy_set_header X-Real-IP $remote_addr;
             proxy_set_header REMOTE-HOST $remote_addr;
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-            proxy_pass http://powerjob-server/;
-        }
-
-        # 瑙e喅 powerjob 浠g悊涔嬪悗闈欐�佹枃浠舵棤娉曡闂殑闂 璇峰嬁淇敼涔卞姩
-        location ~ ^/(js|css|jpg|png|svg|woff|ttf|ico|img)/ {
-            proxy_pass http://powerjob-server;
+            proxy_pass http://snailjob-server/snail-job/;
         }
 
         error_page   500 502 503 504  /50x.html;
diff --git a/script/sql/flowable.sql b/script/sql/flowable.sql
new file mode 100644
index 0000000..c3f7428
--- /dev/null
+++ b/script/sql/flowable.sql
@@ -0,0 +1,175 @@
+insert into sys_menu values('11616', '宸ヤ綔娴�'  , '0',    '6', 'workflow',          '',                                 '', '1', '0', 'M', '0', '0', '',                       'workflow', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11617', '妯″瀷绠$悊', '11616', '2', 'model',             'workflow/model/index',             '', '1', '1', 'C', '0', '0', 'workflow:model:list',    'model', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11618', '鎴戠殑浠诲姟', '0', '7', 'task',              '',                                 '', '1', '0', 'M', '0', '0', '',                       'my-task', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11619', '鎴戠殑寰呭姙', '11618', '2', 'taskWaiting',       'workflow/task/taskWaiting',              '', '1', '1', 'C', '0', '0', '',                       'waiting', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11632', '鎴戠殑宸插姙', '11618', '3', 'taskFinish',       'workflow/task/taskFinish',              '', '1', '1', 'C', '0', '0', '',                       'finish', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11633', '鎴戠殑鎶勯��', '11618', '4', 'taskCopyList',       'workflow/task/taskCopyList',              '', '1', '1', 'C', '0', '0', '',                       'my-copy', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11620', '娴佺▼瀹氫箟', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '',                       'process-definition', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11621', '娴佺▼瀹炰緥', '11630', '1', 'processInstance',   'workflow/processInstance/index',   '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11622', '娴佺▼鍒嗙被', '11616', '1', 'category',          'workflow/category/index',          '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11629', '鎴戝彂璧风殑', '11618', '1', 'myDocument',        'workflow/task/myDocument',         '', '1', '1', 'C', '0', '0', '',                       'guide', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11630', '娴佺▼鐩戞帶', '11616', '4', 'monitor',           '',                                 '', '1', '0', 'M', '0', '0', '',                       'monitor', 103, 1, sysdate(), NULL, NULL, '');
+insert into sys_menu values('11631', '寰呭姙浠诲姟', '11630', '2', 'allTaskWaiting',    'workflow/task/allTaskWaiting',     '', '1', '1', 'C', '0', '0', '',                       'waiting', 103, 1, sysdate(), NULL, NULL, '');
+
+
+-- 娴佺▼鍒嗙被绠$悊鐩稿叧鎸夐挳
+insert into sys_menu values ('11623', '娴佺▼鍒嗙被鏌ヨ', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11624', '娴佺▼鍒嗙被鏂板', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add',   '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11625', '娴佺▼鍒嗙被淇敼', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit',  '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11626', '娴佺▼鍒嗙被鍒犻櫎', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values ('11627', '娴佺▼鍒嗙被瀵煎嚭', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, sysdate(), null, null, '');
+-- 璇峰亣鍗曚俊鎭�
+create table test_leave
+(
+    id          bigint                       not null comment '涓婚敭',
+    leave_type  varchar(255)                 not null comment '璇峰亣绫诲瀷',
+    start_date   datetime                     not null comment '寮�濮嬫椂闂�',
+    end_date     datetime                     not null comment '缁撴潫鏃堕棿',
+    leave_days  int(10)                      not null comment '璇峰亣澶╂暟',
+    remark      varchar(255)                 null comment '璇峰亣鍘熷洜',
+    create_dept bigint                       null comment '鍒涘缓閮ㄩ棬',
+    create_by   bigint                       null comment '鍒涘缓鑰�',
+    create_time datetime                     null comment '鍒涘缓鏃堕棿',
+    update_by   bigint                       null comment '鏇存柊鑰�',
+    update_time datetime                     null comment '鏇存柊鏃堕棿',
+    tenant_id   varchar(20)                  null comment '绉熸埛缂栧彿',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB COMMENT = '璇峰亣鐢宠琛�';
+
+-- 娴佺▼鍒嗙被淇℃伅琛�
+create table wf_category
+(
+    id            bigint                       not null comment '涓婚敭'
+        primary key,
+    category_name varchar(255)                 null comment '鍒嗙被鍚嶇О',
+    category_code varchar(255)                 null comment '鍒嗙被缂栫爜',
+    parent_id     bigint                       null comment '鐖剁骇id',
+    sort_num      int(19)                      null comment '鎺掑簭',
+    tenant_id     varchar(20)                  null comment '绉熸埛缂栧彿',
+    create_dept   bigint                       null comment '鍒涘缓閮ㄩ棬',
+    create_by     bigint                       null comment '鍒涘缓鑰�',
+    create_time   datetime                     null comment '鍒涘缓鏃堕棿',
+    update_by     bigint                       null comment '鏇存柊鑰�',
+    update_time   datetime                     null comment '鏇存柊鏃堕棿',
+    constraint uni_category_code
+        unique (category_code)
+) engine=innodb comment= '娴佺▼鍒嗙被';
+INSERT INTO wf_category values (1, 'OA', 'OA', 0, 0, '000000', 103, 1, sysdate(), 1, sysdate());
+
+create table wf_task_back_node
+(
+    id          bigint                       not null
+        primary key,
+    node_id     varchar(255)                 not null comment '鑺傜偣id',
+    node_name   varchar(255)                 not null comment '鑺傜偣鍚嶇О',
+    order_no    int                          not null comment '鎺掑簭',
+    instance_id varchar(255)                 null comment '娴佺▼瀹炰緥id',
+    task_type   varchar(255)                 not null comment '鑺傜偣绫诲瀷',
+    assignee    varchar(2000)                not null comment '瀹℃壒浜�',
+    tenant_id   varchar(20)                  null comment '绉熸埛缂栧彿',
+    create_dept bigint                       null comment '鍒涘缓閮ㄩ棬',
+    create_by   bigint                       null comment '鍒涘缓鑰�',
+    create_time datetime                     null comment '鍒涘缓鏃堕棿',
+    update_by   bigint                       null comment '鏇存柊鑰�',
+    update_time datetime                     null comment '鏇存柊鏃堕棿'
+)
+    comment '鑺傜偣瀹℃壒璁板綍';
+
+create table wf_definition_config
+(
+    id            bigint                        not null comment '涓婚敭'
+        primary key,
+    table_name    varchar(255)                  not null comment '琛ㄥ悕',
+    definition_id varchar(255)                  not null comment '娴佺▼瀹氫箟ID',
+    process_key   varchar(255)                  not null comment '娴佺▼KEY',
+    version       int(10)                       not null comment '娴佺▼鐗堟湰',
+    create_dept   bigint                        null comment '鍒涘缓閮ㄩ棬',
+    create_by     bigint                        null comment '鍒涘缓鑰�',
+    create_time   datetime                      null comment '鍒涘缓鏃堕棿',
+    update_by     bigint                        null comment '鏇存柊鑰�',
+    update_time   datetime                      null comment '鏇存柊鏃堕棿',
+    remark        varchar(500) default ''       null comment '澶囨敞',
+    tenant_id     varchar(20)                   null comment '绉熸埛缂栧彿',
+    constraint uni_definition_id
+        unique (definition_id)
+)
+    comment '娴佺▼瀹氫箟閰嶇疆';
+
+create table wf_form_manage
+(
+    id          bigint       not null comment '涓婚敭'
+        primary key,
+    form_name   varchar(255) not null comment '琛ㄥ崟鍚嶇О',
+    form_type   varchar(255) not null comment '琛ㄥ崟绫诲瀷',
+    router      varchar(255) not null comment '璺敱鍦板潃/琛ㄥ崟ID',
+    remark      varchar(500) null comment '澶囨敞',
+    tenant_id   varchar(20)  null comment '绉熸埛缂栧彿',
+    create_dept bigint       null comment '鍒涘缓閮ㄩ棬',
+    create_by   bigint       null comment '鍒涘缓鑰�',
+    create_time datetime     null comment '鍒涘缓鏃堕棿',
+    update_by   bigint       null comment '鏇存柊鑰�',
+    update_time datetime     null comment '鏇存柊鏃堕棿'
+)
+    comment '琛ㄥ崟绠$悊';
+
+insert into wf_form_manage(id, form_name, form_type, router, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time) VALUES (1, '璇峰亣鐢宠', 'static', '/demo/leaveEdit/index', NULL, '000000', 103, 1, sysdate(), 1, sysdate());
+
+create table wf_node_config
+(
+    id               bigint       not null comment '涓婚敭'
+        primary key,
+    form_id          bigint       null comment '琛ㄥ崟id',
+    form_type        varchar(255) null comment '琛ㄥ崟绫诲瀷',
+    node_name        varchar(255) not null comment '鑺傜偣鍚嶇О',
+    node_id          varchar(255) not null comment '鑺傜偣id',
+    definition_id    varchar(255) not null comment '娴佺▼瀹氫箟id',
+    apply_user_task  char(1)      default '0'     comment '鏄惁涓虹敵璇蜂汉鑺傜偣 锛�0鏄� 1鍚︼級',
+    create_dept      bigint       null comment '鍒涘缓閮ㄩ棬',
+    create_by        bigint       null comment '鍒涘缓鑰�',
+    create_time      datetime     null comment '鍒涘缓鏃堕棿',
+    update_by        bigint       null comment '鏇存柊鑰�',
+    update_time      datetime     null comment '鏇存柊鏃堕棿',
+    tenant_id        varchar(20)  null comment '绉熸埛缂栧彿'
+)
+    comment '鑺傜偣閰嶇疆';
+
+
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '璇峰亣鐢宠', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, sysdate(), NULL, NULL, '璇峰亣鐢宠鑿滃崟');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '璇峰亣鐢宠鏌ヨ', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, sysdate(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '璇峰亣鐢宠鏂板', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:add', '#', 103, 1, sysdate(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '璇峰亣鐢宠淇敼', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:edit', '#', 103, 1, sysdate(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '璇峰亣鐢宠鍒犻櫎', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:remove', '#', 103, 1, sysdate(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '璇峰亣鐢宠瀵煎嚭', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:export', '#', 103, 1, sysdate(), NULL, NULL, '');
+
+INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (13, '000000', '涓氬姟鐘舵��', 'wf_business_status', 103, 1, sysdate(), NULL, NULL, '涓氬姟鐘舵�佸垪琛�');
+INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (14, '000000', '琛ㄥ崟绫诲瀷', 'wf_form_type', 103, 1, sysdate(), NULL, NULL, '琛ㄥ崟绫诲瀷鍒楄〃');
+
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (39, '000000', 1, '宸叉挙閿�', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '宸叉挙閿�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (40, '000000', 2, '鑽夌', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, sysdate(), NULL, NULL, '鑽夌');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (41, '000000', 3, '寰呭鏍�', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1,sysdate(), NULL, NULL, '寰呭鏍�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (42, '000000', 4, '宸插畬鎴�', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '宸插畬鎴�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (43, '000000', 5, '宸蹭綔搴�', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '宸蹭綔搴�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (44, '000000', 6, '宸查��鍥�', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '宸查��鍥�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (45, '000000', 7, '宸茬粓姝�', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1,sysdate(), NULL, NULL, '宸茬粓姝�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (46, '000000', 1, '鑷畾涔夎〃鍗�', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '鑷畾涔夎〃鍗�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (47, '000000', 2, '鍔ㄦ�佽〃鍗�', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '鍔ㄦ�佽〃鍗�');
+
+-- 琛ㄥ崟绠$悊 SQL
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11628, '琛ㄥ崟绠$悊', '11616', '5', 'formManage', 'workflow/formManage/index', 1, 0, 'C', '0', '0', 'workflow:formManage:list', 'tree-table', 103, 1, sysdate(), null, null, '琛ㄥ崟绠$悊鑿滃崟');
+
+-- 琛ㄥ崟绠$悊鎸夐挳 SQL
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11644, '琛ㄥ崟绠$悊鏌ヨ', 11628, '1',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:query',        '', 103, 1, sysdate(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11645, '琛ㄥ崟绠$悊鏂板', 11628, '2',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:add',          '', 103, 1, sysdate(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11646, '琛ㄥ崟绠$悊淇敼', 11628, '3',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:edit',         '', 103, 1, sysdate(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11647, '琛ㄥ崟绠$悊鍒犻櫎', 11628, '4',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:remove',       '', 103, 1, sysdate(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11648, '琛ㄥ崟绠$悊瀵煎嚭', 11628, '5',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:export',       'tree-table', 103, 1, sysdate(), null, null, '');
diff --git a/script/sql/oracle/flowable.sql b/script/sql/oracle/flowable.sql
new file mode 100644
index 0000000..afd5c0b
--- /dev/null
+++ b/script/sql/oracle/flowable.sql
@@ -0,0 +1,259 @@
+insert into sys_menu values('11616', '宸ヤ綔娴�'  , '0',    '6', 'workflow',          '',                                 '', '1', '0', 'M', '0', '0', '',                       'workflow', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11617', '妯″瀷绠$悊', '11616', '2', 'model',             'workflow/model/index',             '', '1', '1', 'C', '0', '0', 'workflow:model:list',    'model', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11618', '鎴戠殑浠诲姟', '0', '7', 'task',              '',                                 '', '1', '0', 'M', '0', '0', '',                       'my-task', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11619', '鎴戠殑寰呭姙', '11618', '2', 'taskWaiting',       'workflow/task/taskWaiting',              '', '1', '1', 'C', '0', '0', '',                       'waiting', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11632', '鎴戠殑宸插姙', '11618', '3', 'taskFinish',       'workflow/task/taskFinish',              '', '1', '1', 'C', '0', '0', '',                       'finish', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11633', '鎴戠殑鎶勯��', '11618', '4', 'taskCopyList',       'workflow/task/taskCopyList',              '', '1', '1', 'C', '0', '0', '',                       'my-copy', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11620', '娴佺▼瀹氫箟', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '',                       'process-definition', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11621', '娴佺▼瀹炰緥', '11630', '1', 'processInstance',   'workflow/processInstance/index',   '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11622', '娴佺▼鍒嗙被', '11616', '1', 'category',          'workflow/category/index',          '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11629', '鎴戝彂璧风殑', '11618', '1', 'myDocument',        'workflow/task/myDocument',         '', '1', '1', 'C', '0', '0', '',                       'guide', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11630', '娴佺▼鐩戞帶', '11616', '4', 'monitor',           '',                                 '', '1', '0', 'M', '0', '0', '',                       'monitor', 103, 1, sysdate, NULL, NULL, '');
+insert into sys_menu values('11631', '寰呭姙浠诲姟', '11630', '2', 'allTaskWaiting',    'workflow/task/allTaskWaiting',     '', '1', '1', 'C', '0', '0', '',                       'waiting', 103, 1, sysdate, NULL, NULL, '');
+
+
+-- 娴佺▼鍒嗙被绠$悊鐩稿叧鎸夐挳
+insert into sys_menu values ('11623', '娴佺▼鍒嗙被鏌ヨ', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values ('11624', '娴佺▼鍒嗙被鏂板', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add',   '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values ('11625', '娴佺▼鍒嗙被淇敼', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit',  '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values ('11626', '娴佺▼鍒嗙被鍒犻櫎', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values ('11627', '娴佺▼鍒嗙被瀵煎嚭', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, sysdate, null, null, '');
+
+-- 璇峰亣鍗曚俊鎭�
+create table TEST_LEAVE
+(
+    ID          NUMBER(20) not null
+        constraint PK_TEST_LEAVE
+        primary key,
+    LEAVE_TYPE  VARCHAR2(255),
+    START_DATE  DATE,
+    END_DATE    DATE,
+    LEAVE_DAYS  NUMBER(10),
+    REMARK      VARCHAR2(255),
+    CREATE_DEPT NUMBER(20),
+    CREATE_BY   NUMBER(20),
+    CREATE_TIME DATE,
+    UPDATE_BY   NUMBER(20),
+    UPDATE_TIME DATE,
+    TENANT_ID   VARCHAR2(20)
+);
+
+comment on table TEST_LEAVE is '璇峰亣鐢宠琛�';
+comment on column TEST_LEAVE.ID is '涓婚敭';
+comment on column TEST_LEAVE.LEAVE_TYPE is '璇峰亣绫诲瀷';
+comment on column TEST_LEAVE.START_DATE is '寮�濮嬫椂闂�';
+comment on column TEST_LEAVE.END_DATE is '缁撴潫鏃堕棿';
+comment on column TEST_LEAVE.LEAVE_DAYS is '璇峰亣澶╂暟';
+comment on column TEST_LEAVE.REMARK is '璇峰亣鍘熷洜';
+comment on column TEST_LEAVE.CREATE_DEPT is '鍒涘缓閮ㄩ棬';
+comment on column TEST_LEAVE.CREATE_BY is '鍒涘缓鑰�';
+comment on column TEST_LEAVE.CREATE_TIME is '鍒涘缓鏃堕棿';
+comment on column TEST_LEAVE.UPDATE_BY is '鏇存柊鑰�';
+comment on column TEST_LEAVE.UPDATE_TIME is '鏇存柊鏃堕棿';
+comment on column TEST_LEAVE.TENANT_ID is '绉熸埛缂栧彿';
+
+-- 娴佺▼鍒嗙被淇℃伅琛�
+create table WF_CATEGORY
+(
+    ID            NUMBER(20) not null
+        constraint PK_WF_CATEGORY
+        primary key,
+    CATEGORY_NAME VARCHAR2(255),
+    CATEGORY_CODE VARCHAR2(255)
+        constraint UNI_CATEGORY_CODE
+        unique,
+    PARENT_ID     NUMBER(20),
+    SORT_NUM      NUMBER(10),
+    TENANT_ID     VARCHAR2(20),
+    CREATE_DEPT   NUMBER(20),
+    CREATE_BY     NUMBER(20),
+    CREATE_TIME   DATE,
+    UPDATE_BY     NUMBER(20),
+    UPDATE_TIME   DATE
+);
+
+comment on table WF_CATEGORY is '娴佺▼鍒嗙被';
+comment on column WF_CATEGORY.ID is '涓婚敭';
+comment on column WF_CATEGORY.CATEGORY_NAME is '鍒嗙被鍚嶇О';
+comment on column WF_CATEGORY.CATEGORY_CODE is '鍒嗙被缂栫爜';
+comment on column WF_CATEGORY.PARENT_ID is '鐖剁骇id';
+comment on column WF_CATEGORY.SORT_NUM is '鎺掑簭';
+comment on column WF_CATEGORY.TENANT_ID is '绉熸埛缂栧彿';
+comment on column WF_CATEGORY.CREATE_DEPT is '鍒涘缓閮ㄩ棬';
+comment on column WF_CATEGORY.CREATE_BY is '鍒涘缓鑰�';
+comment on column WF_CATEGORY.CREATE_TIME is '鍒涘缓鏃堕棿';
+comment on column WF_CATEGORY.UPDATE_BY is '鏇存柊鑰�';
+comment on column WF_CATEGORY.UPDATE_TIME is '鏇存柊鏃堕棿';
+INSERT INTO wf_category values (1, 'OA', 'OA', 0, 0, '000000', 103, 1, sysdate, 1, sysdate);
+
+create table WF_TASK_BACK_NODE
+(
+    ID            NUMBER(20) not null
+        constraint PK_WF_TASK_BACK_NODE
+        primary key,
+    NODE_ID       VARCHAR2(255) not null,
+    NODE_NAME     VARCHAR2(255) not null,
+    ORDER_NO      NUMBER(20) not null,
+    INSTANCE_ID   VARCHAR2(255) not null,
+    TASK_TYPE     VARCHAR2(255) not null,
+    ASSIGNEE      VARCHAR2(2000) not null,
+    TENANT_ID     VARCHAR2(20),
+    CREATE_DEPT   NUMBER(20),
+    CREATE_BY     NUMBER(20),
+    CREATE_TIME   DATE,
+    UPDATE_BY     NUMBER(20),
+    UPDATE_TIME   DATE
+);
+comment on table WF_TASK_BACK_NODE is '鑺傜偣瀹℃壒璁板綍';
+comment on column WF_TASK_BACK_NODE.ID is '涓婚敭';
+comment on column WF_TASK_BACK_NODE.NODE_ID is '鑺傜偣id';
+comment on column WF_TASK_BACK_NODE.NODE_NAME is '鑺傜偣鍚嶇О';
+comment on column WF_TASK_BACK_NODE.ORDER_NO is '鎺掑簭';
+comment on column WF_TASK_BACK_NODE.INSTANCE_ID is '娴佺▼瀹炰緥id';
+comment on column WF_TASK_BACK_NODE.TASK_TYPE is '鑺傜偣绫诲瀷';
+comment on column WF_TASK_BACK_NODE.ASSIGNEE is '瀹℃壒浜�';
+comment on column WF_TASK_BACK_NODE.TENANT_ID is '绉熸埛缂栧彿';
+comment on column WF_TASK_BACK_NODE.CREATE_DEPT is '鍒涘缓閮ㄩ棬';
+comment on column WF_TASK_BACK_NODE.CREATE_BY is '鍒涘缓鑰�';
+comment on column WF_TASK_BACK_NODE.CREATE_TIME is '鍒涘缓鏃堕棿';
+comment on column WF_TASK_BACK_NODE.UPDATE_BY is '鏇存柊鑰�';
+comment on column WF_TASK_BACK_NODE.UPDATE_TIME is '鏇存柊鏃堕棿';
+
+create table WF_DEFINITION_CONFIG
+(
+    ID            NUMBER(20) NOT NULL
+        CONSTRAINT PK_WF_DEFINITION_CONFIG
+        PRIMARY KEY,
+    TABLE_NAME    VARCHAR2(255) NOT NULL,
+    DEFINITION_ID VARCHAR2(255) NOT NULL,
+    PROCESS_KEY   VARCHAR2(255) NOT NULL,
+    VERSION       NUMBER(10)    NOT NULL,
+    REMARK        VARCHAR2(500),
+    TENANT_ID     VARCHAR2(20),
+    CREATE_DEPT   NUMBER(20),
+    CREATE_BY     NUMBER(20),
+    CREATE_TIME   DATE,
+    UPDATE_BY     NUMBER(20),
+    UPDATE_TIME   DATE,
+    constraint uni_definition_id
+        unique (definition_id)
+);
+comment on table WF_DEFINITION_CONFIG is '娴佺▼瀹氫箟閰嶇疆';
+comment on column WF_DEFINITION_CONFIG.ID is '涓婚敭';
+comment on column WF_DEFINITION_CONFIG.TABLE_NAME is '琛ㄥ悕';
+comment on column WF_DEFINITION_CONFIG.DEFINITION_ID is '娴佺▼瀹氫箟ID';
+comment on column WF_DEFINITION_CONFIG.PROCESS_KEY is '娴佺▼KEY';
+comment on column WF_DEFINITION_CONFIG.VERSION is '娴佺▼鐗堟湰';
+comment on column WF_DEFINITION_CONFIG.TENANT_ID is '绉熸埛缂栧彿';
+comment on column WF_DEFINITION_CONFIG.REMARK is '澶囨敞';
+comment on column WF_DEFINITION_CONFIG.CREATE_DEPT is '鍒涘缓閮ㄩ棬';
+comment on column WF_DEFINITION_CONFIG.CREATE_BY is '鍒涘缓鑰�';
+comment on column WF_DEFINITION_CONFIG.CREATE_TIME is '鍒涘缓鏃堕棿';
+comment on column WF_DEFINITION_CONFIG.UPDATE_BY is '鏇存柊鑰�';
+comment on column WF_DEFINITION_CONFIG.UPDATE_TIME is '鏇存柊鏃堕棿';
+
+create table WF_FORM_MANAGE
+(
+    ID            NUMBER(20) NOT NULL
+        CONSTRAINT PK_WF_FORM_MANAGE
+        PRIMARY KEY,
+    FORM_NAME     VARCHAR2(255) NOT NULL,
+    FORM_TYPE     VARCHAR2(255) NOT NULL,
+    ROUTER        VARCHAR2(255) NOT NULL,
+    REMARK        VARCHAR2(500),
+    TENANT_ID     VARCHAR2(20),
+    CREATE_DEPT   NUMBER(20),
+    CREATE_BY     NUMBER(20),
+    CREATE_TIME   DATE,
+    UPDATE_BY     NUMBER(20),
+    UPDATE_TIME   DATE
+);
+
+comment on table WF_FORM_MANAGE is '琛ㄥ崟绠$悊';
+comment on column WF_FORM_MANAGE.ID is '涓婚敭';
+comment on column WF_FORM_MANAGE.FORM_NAME is '琛ㄥ崟鍚嶇О';
+comment on column WF_FORM_MANAGE.FORM_TYPE is '琛ㄥ崟绫诲瀷';
+comment on column WF_FORM_MANAGE.ROUTER is '璺敱鍦板潃/琛ㄥ崟ID';
+comment on column WF_FORM_MANAGE.REMARK is '澶囨敞';
+comment on column WF_FORM_MANAGE.TENANT_ID is '绉熸埛缂栧彿';
+comment on column WF_FORM_MANAGE.CREATE_DEPT is '鍒涘缓閮ㄩ棬';
+comment on column WF_FORM_MANAGE.CREATE_BY is '鍒涘缓鑰�';
+comment on column WF_FORM_MANAGE.CREATE_TIME is '鍒涘缓鏃堕棿';
+comment on column WF_FORM_MANAGE.UPDATE_BY is '鏇存柊鑰�';
+comment on column WF_FORM_MANAGE.UPDATE_TIME is '鏇存柊鏃堕棿';
+
+insert into wf_form_manage(id, form_name, form_type, router, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time) VALUES (1, '璇峰亣鐢宠', 'static', '/demo/leaveEdit/index', NULL, '000000', 103, 1, sysdate, 1, sysdate);
+
+create table WF_NODE_CONFIG
+(
+    ID               NUMBER(20) NOT NULL
+        CONSTRAINT PK_WF_NODE_CONFIG
+        PRIMARY KEY,
+    FORM_ID          NUMBER(20),
+    FORM_TYPE        VARCHAR2(255),
+    NODE_NAME        VARCHAR2(255) NOT NULL,
+    NODE_ID          VARCHAR2(255) NOT NULL,
+    DEFINITION_ID    VARCHAR2(255) NOT NULL,
+    APPLY_USER_TASK  CHAR(1) DEFAULT '0',
+    TENANT_ID        VARCHAR2(20),
+    CREATE_DEPT      NUMBER(20),
+    CREATE_BY        NUMBER(20),
+    CREATE_TIME      DATE,
+    UPDATE_BY        NUMBER(20),
+    UPDATE_TIME      DATE
+);
+
+comment on table WF_NODE_CONFIG is '鑺傜偣閰嶇疆';
+comment on column WF_NODE_CONFIG.ID is '涓婚敭';
+comment on column WF_NODE_CONFIG.FORM_ID is '琛ㄥ崟id';
+comment on column WF_NODE_CONFIG.FORM_TYPE is '琛ㄥ崟绫诲瀷';
+comment on column WF_NODE_CONFIG.NODE_ID is '鑺傜偣id';
+comment on column WF_NODE_CONFIG.NODE_NAME is '鑺傜偣鍚嶇О';
+comment on column WF_NODE_CONFIG.DEFINITION_ID is '娴佺▼瀹氫箟id';
+comment on column WF_NODE_CONFIG.APPLY_USER_TASK is '鏄惁涓虹敵璇蜂汉鑺傜偣 锛�0鏄� 1鍚︼級';
+comment on column WF_NODE_CONFIG.TENANT_ID is '绉熸埛缂栧彿';
+comment on column WF_NODE_CONFIG.CREATE_DEPT is '鍒涘缓閮ㄩ棬';
+comment on column WF_NODE_CONFIG.CREATE_BY is '鍒涘缓鑰�';
+comment on column WF_NODE_CONFIG.CREATE_TIME is '鍒涘缓鏃堕棿';
+comment on column WF_NODE_CONFIG.UPDATE_BY is '鏇存柊鑰�';
+comment on column WF_NODE_CONFIG.UPDATE_TIME is '鏇存柊鏃堕棿';
+
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '璇峰亣鐢宠', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, sysdate, NULL, NULL, '璇峰亣鐢宠鑿滃崟');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '璇峰亣鐢宠鏌ヨ', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, sysdate, NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '璇峰亣鐢宠鏂板', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:add', '#', 103, 1, sysdate, NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '璇峰亣鐢宠淇敼', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:edit', '#', 103, 1, sysdate, NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '璇峰亣鐢宠鍒犻櫎', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:remove', '#', 103, 1, sysdate, NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '璇峰亣鐢宠瀵煎嚭', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:export', '#', 103, 1, sysdate, NULL, NULL, '');
+
+INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (13, '000000', '涓氬姟鐘舵��', 'wf_business_status', 103, 1, sysdate, NULL, NULL, '涓氬姟鐘舵�佸垪琛�');
+INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (14, '000000', '琛ㄥ崟绫诲瀷', 'wf_form_type', 103, 1, sysdate, NULL, NULL, '琛ㄥ崟绫诲瀷鍒楄〃');
+
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (39, '000000', 1, '宸叉挙閿�', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate, NULL, NULL, '宸叉挙閿�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (40, '000000', 2, '鑽夌', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, sysdate, NULL, NULL, '鑽夌');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (41, '000000', 3, '寰呭鏍�', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1,sysdate, NULL, NULL, '寰呭鏍�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (42, '000000', 4, '宸插畬鎴�', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, sysdate, NULL, NULL, '宸插畬鎴�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (43, '000000', 5, '宸蹭綔搴�', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate, NULL, NULL, '宸蹭綔搴�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (44, '000000', 6, '宸查��鍥�', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate, NULL, NULL, '宸查��鍥�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (45, '000000', 7, '宸茬粓姝�', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1,sysdate, NULL, NULL, '宸茬粓姝�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (46, '000000', 1, '鑷畾涔夎〃鍗�', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, sysdate, NULL, NULL, '鑷畾涔夎〃鍗�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (47, '000000', 2, '鍔ㄦ�佽〃鍗�', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, sysdate, NULL, NULL, '鍔ㄦ�佽〃鍗�');
+
+-- 琛ㄥ崟绠$悊 SQL
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11628, '琛ㄥ崟绠$悊', '11616', '5', 'formManage', 'workflow/formManage/index', 1, 0, 'C', '0', '0', 'workflow:formManage:list', 'tree-table', 103, 1, sysdate, null, null, '琛ㄥ崟绠$悊鑿滃崟');
+
+-- 琛ㄥ崟绠$悊鎸夐挳 SQL
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11644, '琛ㄥ崟绠$悊鏌ヨ', 11628, '1',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:query',        '', 103, 1, sysdate, null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11645, '琛ㄥ崟绠$悊鏂板', 11628, '2',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:add',          '', 103, 1, sysdate, null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11646, '琛ㄥ崟绠$悊淇敼', 11628, '3',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:edit',         '', 103, 1, sysdate, null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11647, '琛ㄥ崟绠$悊鍒犻櫎', 11628, '4',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:remove',       '', 103, 1, sysdate, null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11648, '琛ㄥ崟绠$悊瀵煎嚭', 11628, '5',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:export',       'tree-table', 103, 1, sysdate, null, null, '');
diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql
index ef45c5f..06ea5fe 100644
--- a/script/sql/oracle/oracle_ry_vue_5.X.sql
+++ b/script/sql/oracle/oracle_ry_vue_5.X.sql
@@ -20,7 +20,7 @@
     union_id           varchar2(255)     default null,
     scope              varchar2(255)     default null,
     token_type         varchar2(255)     default null,
-    id_token           varchar2(255)     default null,
+    id_token           varchar2(2000)    default null,
     mac_algorithm      varchar2(255)     default null,
     mac_key            varchar2(255)     default null,
     code               varchar2(255)     default null,
@@ -165,6 +165,7 @@
   parent_id         number(20)      default 0,
   ancestors         varchar2(500)   default '',
   dept_name         varchar2(30)    default '',
+  dept_category     varchar2(100)   default null,
   order_num         number(4)       default 0,
   leader            number(20)     default null,
   phone             varchar2(11)    default null,
@@ -186,6 +187,7 @@
 comment on column sys_dept.parent_id    is '鐖堕儴闂╥d';
 comment on column sys_dept.ancestors    is '绁栫骇鍒楄〃';
 comment on column sys_dept.dept_name    is '閮ㄩ棬鍚嶇О';
+comment on column sys_dept.dept_category is '閮ㄩ棬绫诲埆缂栫爜';
 comment on column sys_dept.order_num    is '鏄剧ず椤哄簭';
 comment on column sys_dept.leader       is '璐熻矗浜�';
 comment on column sys_dept.phone        is '鑱旂郴鐢佃瘽';
@@ -201,16 +203,17 @@
 -- ----------------------------
 -- 鍒濆鍖�-閮ㄩ棬琛ㄦ暟鎹�
 -- ----------------------------
-insert into sys_dept values(100, '000000', 0,   '0',          'XXX绉戞妧',   0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(101, '000000', 100, '0,100',      '娣卞湷鎬诲叕鍙�', 1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(102, '000000', 100, '0,100',      '闀挎矙鍒嗗叕鍙�', 2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(103, '000000', 101, '0,100,101',  '鐮斿彂閮ㄩ棬',   1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(104, '000000', 101, '0,100,101',  '甯傚満閮ㄩ棬',   2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(105, '000000', 101, '0,100,101',  '娴嬭瘯閮ㄩ棬',   3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(106, '000000', 101, '0,100,101',  '璐㈠姟閮ㄩ棬',   4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(107, '000000', 101, '0,100,101',  '杩愮淮閮ㄩ棬',   5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(108, '000000', 102, '0,100,102',  '甯傚満閮ㄩ棬',   1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
-insert into sys_dept values(109, '000000', 102, '0,100,102',  '璐㈠姟閮ㄩ棬',   2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+
+insert into sys_dept values(100, '000000', 0,   '0',          'XXX绉戞妧',   null,0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(101, '000000', 100, '0,100',      '娣卞湷鎬诲叕鍙�', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(102, '000000', 100, '0,100',      '闀挎矙鍒嗗叕鍙�', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(103, '000000', 101, '0,100,101',  '鐮斿彂閮ㄩ棬',   null,1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(104, '000000', 101, '0,100,101',  '甯傚満閮ㄩ棬',   null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(105, '000000', 101, '0,100,101',  '娴嬭瘯閮ㄩ棬',   null,3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(106, '000000', 101, '0,100,101',  '璐㈠姟閮ㄩ棬',   null,4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(107, '000000', 101, '0,100,101',  '杩愮淮閮ㄩ棬',   null,5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(108, '000000', 102, '0,100,102',  '甯傚満閮ㄩ棬',   null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
+insert into sys_dept values(109, '000000', 102, '0,100,102',  '璐㈠姟閮ㄩ棬',   null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate, null, null);
 
 
 -- ----------------------------
@@ -269,8 +272,8 @@
 -- 鍒濆鍖�-鐢ㄦ埛淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
 insert into sys_user values(1, '000000', 103, 'admin', '鐤媯鐨勭嫯瀛怢i', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, '绠$悊鍛�');
-insert into sys_user values(2, '000000', 105, 'lionli', '鐤媯鐨勭嫯瀛怢i', 'sys_user', 'crazyLionLi@qq.com',  '15666666666', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, '娴嬭瘯鍛�');
-
+insert into sys_user values(3, '000000', 108, 'test', '鏈儴闂ㄥ強浠ヤ笅 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, '');
+insert into sys_user values(4, '000000', 102, 'test1', '浠呮湰浜� 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, null, null, '');
 
 -- ----------------------------
 -- 3銆佸矖浣嶄俊鎭〃
@@ -278,7 +281,9 @@
 create table sys_post (
   post_id           number(20)      not null,
   tenant_id         varchar2(20)    default '000000',
+  dept_id           number(20)      not null,
   post_code         varchar2(64)    not null,
+  post_category     varchar2(64)    default null,
   post_name         varchar2(50)    not null,
   post_sort         number(4)       not null,
   status            char(1)         not null,
@@ -295,7 +300,9 @@
 comment on table  sys_post              is '宀椾綅淇℃伅琛�';
 comment on column sys_post.post_id      is '宀椾綅ID';
 comment on column sys_post.tenant_id    is '绉熸埛缂栧彿';
+comment on column sys_post.dept_id      is '閮ㄩ棬id';
 comment on column sys_post.post_code    is '宀椾綅缂栫爜';
+comment on column sys_post.post_category is '宀椾綅绫诲埆缂栫爜';
 comment on column sys_post.post_name    is '宀椾綅鍚嶇О';
 comment on column sys_post.post_sort    is '鏄剧ず椤哄簭';
 comment on column sys_post.status       is '鐘舵�侊紙0姝e父 1鍋滅敤锛�';
@@ -309,10 +316,10 @@
 -- ----------------------------
 -- 鍒濆鍖�-宀椾綅淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
-insert into sys_post values(1, '000000', 'ceo',  '钁d簨闀�',    1, '0', 103, 1, sysdate, null, null, '');
-insert into sys_post values(2, '000000', 'se',   '椤圭洰缁忕悊',  2, '0', 103, 1, sysdate, null, null, '');
-insert into sys_post values(3, '000000', 'hr',   '浜哄姏璧勬簮',  3, '0', 103, 1, sysdate, null, null, '');
-insert into sys_post values(4, '000000', 'user', '鏅�氬憳宸�',  4, '0', 103, 1, sysdate, null, null, '');
+insert into sys_post values(1, '000000', 103, 'ceo',  null, '钁d簨闀�',    1, '0', 103, 1, sysdate, null, null, '');
+insert into sys_post values(2, '000000', 100, 'se',   null, '椤圭洰缁忕悊',  2, '0', 103, 1, sysdate, null, null, '');
+insert into sys_post values(3, '000000', 100, 'hr',   null, '浜哄姏璧勬簮',  3, '0', 103, 1, sysdate, null, null, '');
+insert into sys_post values(4, '000000', 100, 'user', null, '鏅�氬憳宸�',  4, '0', 103, 1, sysdate, null, null, '');
 
 
 -- ----------------------------
@@ -361,8 +368,8 @@
 -- 鍒濆鍖�-瑙掕壊淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
 insert into sys_role values('1', '000000', '瓒呯骇绠$悊鍛�',  'superadmin',  1, 1, 1, 1, '0', '0', 103, 1, sysdate, null, null, '瓒呯骇绠$悊鍛�');
-insert into sys_role values('2', '000000', '鏅�氳鑹�',    'common', 2, 2, 1, 1, '0', '0', 103, 1, sysdate, null, null, '鏅�氳鑹�');
-
+insert into sys_role values('3', '000000', '鏈儴闂ㄥ強浠ヤ笅', 'test1', 3, 4, 1, 1, '0', '0', 103, 1, sysdate, null, null, null);
+insert into sys_role values('4', '000000', '浠呮湰浜�',      'test2', 4, 5, 1, 1, '0', '0', 103, 1, sysdate, null, null, null);
 
 -- ----------------------------
 -- 5銆佽彍鍗曟潈闄愯〃
@@ -423,6 +430,7 @@
 insert into sys_menu values('2', '绯荤粺鐩戞帶', '0', '3', 'monitor',          null, '', 1, 0, 'M', '0', '0', '', 'monitor',  103, 1, sysdate, null, null, '绯荤粺鐩戞帶鐩綍');
 insert into sys_menu values('3', '绯荤粺宸ュ叿', '0', '4', 'tool',             null, '', 1, 0, 'M', '0', '0', '', 'tool',     103, 1, sysdate, null, null, '绯荤粺宸ュ叿鐩綍');
 insert into sys_menu values('4', 'PLUS瀹樼綉', '0', '5', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide',    103, 1, sysdate, null, null, 'RuoYi-Vue-Plus瀹樼綉鍦板潃');
+insert into sys_menu values('5', '娴嬭瘯鑿滃崟', '0', '5', 'demo',             null, '', 1, 0, 'M', '0', '0', null, 'star', 103, 1, sysdate, null, null, '');
 -- 浜岀骇鑿滃崟
 insert into sys_menu values('100',  '鐢ㄦ埛绠$悊',     '1',   '1', 'user',             'system/user/index',            '', 1, 0, 'C', '0', '0', 'system:user:list',            'user',          103, 1, sysdate, null, null, '鐢ㄦ埛绠$悊鑿滃崟');
 insert into sys_menu values('101',  '瑙掕壊绠$悊',     '1',   '2', 'role',             'system/role/index',            '', 1, 0, 'C', '0', '0', 'system:role:list',            'peoples',       103, 1, sysdate, null, null, '瑙掕壊绠$悊鑿滃崟');
@@ -444,8 +452,8 @@
 insert into sys_menu values('117',  'Admin鐩戞帶',   '2',    '5', 'Admin',            'monitor/admin/index',         '', 1, 0, 'C', '0', '0', 'monitor:admin:list',          'dashboard',     103, 1, sysdate, null, null, 'Admin鐩戞帶鑿滃崟');
 -- oss鑿滃崟
 insert into sys_menu values('118',  '鏂囦欢绠$悊',     '1',    '10', 'oss',             'system/oss/index',            '', 1, 0, 'C', '0', '0', 'system:oss:list',             'upload',        103, 1, sysdate, null, null, '鏂囦欢绠$悊鑿滃崟');
--- powerjob server鎺у埗鍙�
-insert into sys_menu values('120',  '浠诲姟璋冨害涓績',  '2',    '5', 'powerjob',           'monitor/powerjob/index',        '', 1, 0, 'C', '0', '0', 'monitor:powerjob:list',         'job',           103, 1, sysdate, null, null, 'PowerJob鎺у埗鍙拌彍鍗�');
+-- snail-job server鎺у埗鍙�
+insert into sys_menu values('120',  '浠诲姟璋冨害涓績',  '2',    '5', 'snailjob',           'monitor/snailjob/index',        '', 1, 0, 'C', '0', '0', 'monitor:snailjob:list', 'job',           103, 1, sysdate, null, null, 'snailjob鎺у埗鍙拌彍鍗�');
 
 -- 涓夌骇鑿滃崟
 insert into sys_menu values('500',  '鎿嶄綔鏃ュ織', '108', '1', 'operlog',    'monitor/operlog/index',    '', 1, 0, 'C', '0', '0', 'monitor:operlog:list',    'form',          103, 1, sysdate, null, null, '鎿嶄綔鏃ュ織鑿滃崟');
@@ -544,6 +552,20 @@
 insert into sys_menu values('1063', '瀹㈡埛绔鐞嗕慨鏀�', '123', '3',  '#', '', '', 1, 0, 'F', '0', '0', 'system:client:edit',         '#', 103, 1, sysdate, null, null, '');
 insert into sys_menu values('1064', '瀹㈡埛绔鐞嗗垹闄�', '123', '4',  '#', '', '', 1, 0, 'F', '0', '0', 'system:client:remove',       '#', 103, 1, sysdate, null, null, '');
 insert into sys_menu values('1065', '瀹㈡埛绔鐞嗗鍑�', '123', '5',  '#', '', '', 1, 0, 'F', '0', '0', 'system:client:export',       '#', 103, 1, sysdate, null, null, '');
+-- 娴嬭瘯鑿滃崟
+insert into sys_menu values('1500', '娴嬭瘯鍗曡〃',     '5',   '1', 'demo', 'demo/demo/index', '',  1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, sysdate, null, null, '娴嬭瘯鍗曡〃鑿滃崟');
+insert into sys_menu values('1501', '娴嬭瘯鍗曡〃鏌ヨ', '1500', '1', '#', '', '',  1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1502', '娴嬭瘯鍗曡〃鏂板', '1500', '2', '#', '', '',  1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1503', '娴嬭瘯鍗曡〃淇敼', '1500', '3', '#', '', '',  1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1504', '娴嬭瘯鍗曡〃鍒犻櫎', '1500', '4', '#', '', '',  1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1505', '娴嬭瘯鍗曡〃瀵煎嚭', '1500', '5', '#', '', '',  1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1506', '娴嬭瘯鏍戣〃',     '5',   '1', 'tree', 'demo/tree/index', '',  1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, sysdate, null, null, '娴嬭瘯鏍戣〃鑿滃崟');
+insert into sys_menu values('1507', '娴嬭瘯鏍戣〃鏌ヨ', '1506', '1', '#', '', '',  1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1508', '娴嬭瘯鏍戣〃鏂板', '1506', '2', '#', '', '',  1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1509', '娴嬭瘯鏍戣〃淇敼', '1506', '3', '#', '', '',  1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1510', '娴嬭瘯鏍戣〃鍒犻櫎', '1506', '4', '#', '', '',  1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, sysdate, null, null, '');
+insert into sys_menu values('1511', '娴嬭瘯鏍戣〃瀵煎嚭', '1506', '5', '#', '', '',  1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, sysdate, null, null, '');
+
 
 -- ----------------------------
 -- 6銆佺敤鎴峰拰瑙掕壊鍏宠仈琛�  鐢ㄦ埛N-1瑙掕壊
@@ -563,8 +585,8 @@
 -- 鍒濆鍖�-鐢ㄦ埛鍜岃鑹插叧鑱旇〃鏁版嵁
 -- ----------------------------
 insert into sys_user_role values ('1', '1');
-insert into sys_user_role values ('2', '2');
-
+insert into sys_user_role values ('3', '3');
+insert into sys_user_role values ('4', '4');
 
 -- ----------------------------
 -- 7銆佽鑹插拰鑿滃崟鍏宠仈琛�  瑙掕壊1-N鑿滃崟
@@ -583,90 +605,89 @@
 -- ----------------------------
 -- 鍒濆鍖�-瑙掕壊鍜岃彍鍗曞叧鑱旇〃鏁版嵁
 -- ----------------------------
-insert into sys_role_menu values ('2', '1');
-insert into sys_role_menu values ('2', '2');
-insert into sys_role_menu values ('2', '3');
-insert into sys_role_menu values ('2', '4');
-insert into sys_role_menu values ('2', '100');
-insert into sys_role_menu values ('2', '101');
-insert into sys_role_menu values ('2', '102');
-insert into sys_role_menu values ('2', '103');
-insert into sys_role_menu values ('2', '104');
-insert into sys_role_menu values ('2', '105');
-insert into sys_role_menu values ('2', '106');
-insert into sys_role_menu values ('2', '107');
-insert into sys_role_menu values ('2', '108');
-insert into sys_role_menu values ('2', '109');
-insert into sys_role_menu values ('2', '110');
-insert into sys_role_menu values ('2', '111');
-insert into sys_role_menu values ('2', '112');
-insert into sys_role_menu values ('2', '113');
-insert into sys_role_menu values ('2', '114');
-insert into sys_role_menu values ('2', '115');
-insert into sys_role_menu values ('2', '116');
-insert into sys_role_menu values ('2', '500');
-insert into sys_role_menu values ('2', '501');
-insert into sys_role_menu values ('2', '1000');
-insert into sys_role_menu values ('2', '1001');
-insert into sys_role_menu values ('2', '1002');
-insert into sys_role_menu values ('2', '1003');
-insert into sys_role_menu values ('2', '1004');
-insert into sys_role_menu values ('2', '1005');
-insert into sys_role_menu values ('2', '1006');
-insert into sys_role_menu values ('2', '1007');
-insert into sys_role_menu values ('2', '1008');
-insert into sys_role_menu values ('2', '1009');
-insert into sys_role_menu values ('2', '1010');
-insert into sys_role_menu values ('2', '1011');
-insert into sys_role_menu values ('2', '1012');
-insert into sys_role_menu values ('2', '1013');
-insert into sys_role_menu values ('2', '1014');
-insert into sys_role_menu values ('2', '1015');
-insert into sys_role_menu values ('2', '1016');
-insert into sys_role_menu values ('2', '1017');
-insert into sys_role_menu values ('2', '1018');
-insert into sys_role_menu values ('2', '1019');
-insert into sys_role_menu values ('2', '1020');
-insert into sys_role_menu values ('2', '1021');
-insert into sys_role_menu values ('2', '1022');
-insert into sys_role_menu values ('2', '1023');
-insert into sys_role_menu values ('2', '1024');
-insert into sys_role_menu values ('2', '1025');
-insert into sys_role_menu values ('2', '1026');
-insert into sys_role_menu values ('2', '1027');
-insert into sys_role_menu values ('2', '1028');
-insert into sys_role_menu values ('2', '1029');
-insert into sys_role_menu values ('2', '1030');
-insert into sys_role_menu values ('2', '1031');
-insert into sys_role_menu values ('2', '1032');
-insert into sys_role_menu values ('2', '1033');
-insert into sys_role_menu values ('2', '1034');
-insert into sys_role_menu values ('2', '1035');
-insert into sys_role_menu values ('2', '1036');
-insert into sys_role_menu values ('2', '1037');
-insert into sys_role_menu values ('2', '1038');
-insert into sys_role_menu values ('2', '1039');
-insert into sys_role_menu values ('2', '1040');
-insert into sys_role_menu values ('2', '1041');
-insert into sys_role_menu values ('2', '1042');
-insert into sys_role_menu values ('2', '1043');
-insert into sys_role_menu values ('2', '1044');
-insert into sys_role_menu values ('2', '1045');
-insert into sys_role_menu values ('2', '1050');
-insert into sys_role_menu values ('2', '1046');
-insert into sys_role_menu values ('2', '1047');
-insert into sys_role_menu values ('2', '1048');
-insert into sys_role_menu values ('2', '1055');
-insert into sys_role_menu values ('2', '1056');
-insert into sys_role_menu values ('2', '1057');
-insert into sys_role_menu values ('2', '1058');
-insert into sys_role_menu values ('2', '1059');
-insert into sys_role_menu values ('2', '1060');
-insert into sys_role_menu values ('2', '1061');
-insert into sys_role_menu values ('2', '1062');
-insert into sys_role_menu values ('2', '1063');
-insert into sys_role_menu values ('2', '1064');
-insert into sys_role_menu values ('2', '1065');
+insert into sys_role_menu values ('3', '1');
+insert into sys_role_menu values ('3', '5');
+insert into sys_role_menu values ('3', '100');
+insert into sys_role_menu values ('3', '101');
+insert into sys_role_menu values ('3', '102');
+insert into sys_role_menu values ('3', '103');
+insert into sys_role_menu values ('3', '104');
+insert into sys_role_menu values ('3', '105');
+insert into sys_role_menu values ('3', '106');
+insert into sys_role_menu values ('3', '107');
+insert into sys_role_menu values ('3', '108');
+insert into sys_role_menu values ('3', '500');
+insert into sys_role_menu values ('3', '501');
+insert into sys_role_menu values ('3', '1001');
+insert into sys_role_menu values ('3', '1002');
+insert into sys_role_menu values ('3', '1003');
+insert into sys_role_menu values ('3', '1004');
+insert into sys_role_menu values ('3', '1005');
+insert into sys_role_menu values ('3', '1006');
+insert into sys_role_menu values ('3', '1007');
+insert into sys_role_menu values ('3', '1008');
+insert into sys_role_menu values ('3', '1009');
+insert into sys_role_menu values ('3', '1010');
+insert into sys_role_menu values ('3', '1011');
+insert into sys_role_menu values ('3', '1012');
+insert into sys_role_menu values ('3', '1013');
+insert into sys_role_menu values ('3', '1014');
+insert into sys_role_menu values ('3', '1015');
+insert into sys_role_menu values ('3', '1016');
+insert into sys_role_menu values ('3', '1017');
+insert into sys_role_menu values ('3', '1018');
+insert into sys_role_menu values ('3', '1019');
+insert into sys_role_menu values ('3', '1020');
+insert into sys_role_menu values ('3', '1021');
+insert into sys_role_menu values ('3', '1022');
+insert into sys_role_menu values ('3', '1023');
+insert into sys_role_menu values ('3', '1024');
+insert into sys_role_menu values ('3', '1025');
+insert into sys_role_menu values ('3', '1026');
+insert into sys_role_menu values ('3', '1027');
+insert into sys_role_menu values ('3', '1028');
+insert into sys_role_menu values ('3', '1029');
+insert into sys_role_menu values ('3', '1030');
+insert into sys_role_menu values ('3', '1031');
+insert into sys_role_menu values ('3', '1032');
+insert into sys_role_menu values ('3', '1033');
+insert into sys_role_menu values ('3', '1034');
+insert into sys_role_menu values ('3', '1035');
+insert into sys_role_menu values ('3', '1036');
+insert into sys_role_menu values ('3', '1037');
+insert into sys_role_menu values ('3', '1038');
+insert into sys_role_menu values ('3', '1039');
+insert into sys_role_menu values ('3', '1040');
+insert into sys_role_menu values ('3', '1041');
+insert into sys_role_menu values ('3', '1042');
+insert into sys_role_menu values ('3', '1043');
+insert into sys_role_menu values ('3', '1044');
+insert into sys_role_menu values ('3', '1045');
+insert into sys_role_menu values ('3', '1500');
+insert into sys_role_menu values ('3', '1501');
+insert into sys_role_menu values ('3', '1502');
+insert into sys_role_menu values ('3', '1503');
+insert into sys_role_menu values ('3', '1504');
+insert into sys_role_menu values ('3', '1505');
+insert into sys_role_menu values ('3', '1506');
+insert into sys_role_menu values ('3', '1507');
+insert into sys_role_menu values ('3', '1508');
+insert into sys_role_menu values ('3', '1509');
+insert into sys_role_menu values ('3', '1510');
+insert into sys_role_menu values ('3', '1511');
+insert into sys_role_menu values ('4', '5');
+insert into sys_role_menu values ('4', '1500');
+insert into sys_role_menu values ('4', '1501');
+insert into sys_role_menu values ('4', '1502');
+insert into sys_role_menu values ('4', '1503');
+insert into sys_role_menu values ('4', '1504');
+insert into sys_role_menu values ('4', '1505');
+insert into sys_role_menu values ('4', '1506');
+insert into sys_role_menu values ('4', '1507');
+insert into sys_role_menu values ('4', '1508');
+insert into sys_role_menu values ('4', '1509');
+insert into sys_role_menu values ('4', '1510');
+insert into sys_role_menu values ('4', '1511');
 
 -- ----------------------------
 -- 8銆佽鑹插拰閮ㄩ棬鍏宠仈琛�  瑙掕壊1-N閮ㄩ棬
@@ -681,13 +702,6 @@
 comment on table  sys_role_dept              is '瑙掕壊鍜岄儴闂ㄥ叧鑱旇〃';
 comment on column sys_role_dept.role_id      is '瑙掕壊ID';
 comment on column sys_role_dept.dept_id      is '閮ㄩ棬ID';
-
--- ----------------------------
--- 鍒濆鍖�-瑙掕壊鍜岄儴闂ㄥ叧鑱旇〃鏁版嵁
--- ----------------------------
-insert into sys_role_dept values ('2', '100');
-insert into sys_role_dept values ('2', '101');
-insert into sys_role_dept values ('2', '105');
 
 
 -- ----------------------------
@@ -708,8 +722,6 @@
 -- 鍒濆鍖�-鐢ㄦ埛涓庡矖浣嶅叧鑱旇〃鏁版嵁
 -- ----------------------------
 insert into sys_user_post values ('1', '1');
-insert into sys_user_post values ('2', '2');
-
 
 -- ----------------------------
 -- 10銆佹搷浣滄棩蹇楄褰�
@@ -1174,7 +1186,7 @@
 alter table sys_oss_config add constraint pk_sys_oss_config primary key (oss_config_id);
 
 comment on table sys_oss_config                 is '瀵硅薄瀛樺偍閰嶇疆琛�';
-comment on column sys_oss_config.oss_config_id  is '涓诲缓';
+comment on column sys_oss_config.oss_config_id  is '涓婚敭';
 comment on column sys_oss_config.tenant_id      is '绉熸埛缂栫爜';
 comment on column sys_oss_config.config_key     is '閰嶇疆key';
 comment on column sys_oss_config.access_key     is 'accesskey';
@@ -1225,7 +1237,7 @@
 alter table sys_client add constraint pk_sys_client primary key (id);
 
 comment on table sys_client                         is '绯荤粺鎺堟潈琛�';
-comment on column sys_client.id                     is '涓诲缓';
+comment on column sys_client.id                     is '涓婚敭';
 comment on column sys_client.client_id              is '瀹㈡埛绔痠d';
 comment on column sys_client.client_key             is '瀹㈡埛绔痥ey';
 comment on column sys_client.client_secret          is '瀹㈡埛绔閽�';
@@ -1244,6 +1256,102 @@
 insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, sysdate, 1, sysdate);
 insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, sysdate, 1, sysdate);
 
+create table test_demo (
+    id          number(20)      not null,
+    tenant_id   varchar2(20)    default '000000',
+    dept_id     number(20)      default null,
+    user_id     number(20)      default null,
+    order_num   number(10)      default 0,
+    test_key    varchar2(255)   default null,
+    value       varchar2(255)   default null,
+    version     number(10)      default 0,
+    create_dept number(20)      default null,
+    create_time date,
+    create_by   number(20)      default null,
+    update_time date,
+    update_by   number(20)      default null,
+    del_flag    number(2)       default 0
+);
+
+alter table test_demo add constraint pk_test_demo primary key (id);
+
+comment on table  test_demo              is '娴嬭瘯鍗曡〃';
+comment on column test_demo.id           is '涓婚敭';
+comment on column test_demo.tenant_id    is '绉熸埛缂栧彿';
+comment on column test_demo.dept_id      is '閮ㄩ棬id';
+comment on column test_demo.user_id      is '鐢ㄦ埛id';
+comment on column test_demo.order_num    is '鎺掑簭鍙�';
+comment on column test_demo.test_key     is 'key閿�';
+comment on column test_demo.value        is '鍊�';
+comment on column test_demo.version      is '鐗堟湰';
+comment on column test_demo.create_dept  is '鍒涘缓閮ㄩ棬';
+comment on column test_demo.create_time  is '鍒涘缓鏃堕棿';
+comment on column test_demo.create_by    is '鍒涘缓浜�';
+comment on column test_demo.update_time  is '鏇存柊鏃堕棿';
+comment on column test_demo.update_by    is '鏇存柊浜�';
+comment on column test_demo.del_flag     is '鍒犻櫎鏍囧織';
+
+create table test_tree (
+    id          number(20)      not null,
+    tenant_id   varchar2(20)    default '000000',
+    parent_id   number(20)      default 0,
+    dept_id     number(20)      default null,
+    user_id     number(20)      default null,
+    tree_name   varchar2(255)   default null,
+    version     number(10)      default 0,
+    create_dept number(20)      default null,
+    create_time date,
+    create_by   number(20)      default null,
+    update_time date,
+    update_by   number(20)      default null,
+    del_flag    number(2)       default 0
+);
+
+alter table test_tree add constraint pk_test_tree primary key (id);
+
+comment on table  test_tree              is '娴嬭瘯鏍戣〃';
+comment on column test_tree.id           is '涓婚敭';
+comment on column test_tree.tenant_id    is '绉熸埛缂栧彿';
+comment on column test_tree.parent_id    is '鐖秈d';
+comment on column test_tree.dept_id      is '閮ㄩ棬id';
+comment on column test_tree.user_id      is '鐢ㄦ埛id';
+comment on column test_tree.tree_name    is '鍊�';
+comment on column test_tree.version      is '鐗堟湰';
+comment on column test_tree.create_dept  is '鍒涘缓閮ㄩ棬';
+comment on column test_tree.create_time  is '鍒涘缓鏃堕棿';
+comment on column test_tree.create_by    is '鍒涘缓浜�';
+comment on column test_tree.update_time  is '鏇存柊鏃堕棿';
+comment on column test_tree.update_by    is '鏇存柊浜�';
+comment on column test_tree.del_flag     is '鍒犻櫎鏍囧織';
+
+insert into test_demo values (1, '000000', 102, 4, 1, '娴嬭瘯鏁版嵁鏉冮檺', '娴嬭瘯', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (2, '000000', 102, 3, 2, '瀛愯妭鐐�1', '111', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (3, '000000', 102, 3, 3, '瀛愯妭鐐�2', '222', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (4, '000000', 108, 4, 4, '娴嬭瘯鏁版嵁', 'demo', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (5, '000000', 108, 3, 13, '瀛愯妭鐐�11', '1111', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (6, '000000', 108, 3, 12, '瀛愯妭鐐�22', '2222', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (7, '000000', 108, 3, 11, '瀛愯妭鐐�33', '3333', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (8, '000000', 108, 3, 10, '瀛愯妭鐐�44', '4444', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (9, '000000', 108, 3, 9, '瀛愯妭鐐�55', '5555', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (10, '000000', 108, 3, 8, '瀛愯妭鐐�66', '6666', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (11, '000000', 108, 3, 7, '瀛愯妭鐐�77', '7777', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (12, '000000', 108, 3, 6, '瀛愯妭鐐�88', '8888', 0, 103, sysdate, 1, null, null, 0);
+insert into test_demo values (13, '000000', 108, 3, 5, '瀛愯妭鐐�99', '9999', 0, 103, sysdate, 1, null, null, 0);
+
+insert into test_tree values (1, '000000', 0, 102, 4, '娴嬭瘯鏁版嵁鏉冮檺', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (2, '000000', 1, 102, 3, '瀛愯妭鐐�1', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (3, '000000', 2, 102, 3, '瀛愯妭鐐�2', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (4, '000000', 0, 108, 4, '娴嬭瘯鏍�1', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (5, '000000', 4, 108, 3, '瀛愯妭鐐�11', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (6, '000000', 4, 108, 3, '瀛愯妭鐐�22', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (7, '000000', 4, 108, 3, '瀛愯妭鐐�33', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (8, '000000', 5, 108, 3, '瀛愯妭鐐�44', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (9, '000000', 6, 108, 3, '瀛愯妭鐐�55', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (10, '000000', 7, 108, 3, '瀛愯妭鐐�66', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (11, '000000', 7, 108, 3, '瀛愯妭鐐�77', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (12, '000000', 10, 108, 3, '瀛愯妭鐐�88', 0, 103, sysdate, 1, null, null, 0);
+insert into test_tree values (13, '000000', 10, 108, 3, '瀛愯妭鐐�99', 0, 103, sysdate, 1, null, null, 0);
+
 
 -- ----------------------------
 -- 閽╁瓙 锛岀敤浜巗ession杩炴帴涔嬪悗 鑷姩璁剧疆榛樿鐨刣ate绫诲瀷鏍煎紡鍖� 绠�鍖栨椂闂存煡璇�
diff --git a/script/sql/oracle/oracle_test.sql b/script/sql/oracle/oracle_test.sql
deleted file mode 100644
index fd93786..0000000
--- a/script/sql/oracle/oracle_test.sql
+++ /dev/null
@@ -1,204 +0,0 @@
-create table test_demo (
-    id          number(20)      not null,
-    tenant_id   varchar2(20)    default '000000',
-    dept_id     number(20)      default null,
-    user_id     number(20)      default null,
-    order_num   number(10)      default 0,
-    test_key    varchar2(255)   default null,
-    value       varchar2(255)   default null,
-    version     number(10)      default 0,
-    create_dept number(20)      default null,
-    create_time date,
-    create_by   number(20)      default null,
-    update_time date,
-    update_by   number(20)      default null,
-    del_flag    number(2)       default 0
-);
-
-alter table test_demo add constraint pk_test_demo primary key (id);
-
-comment on table  test_demo              is '娴嬭瘯鍗曡〃';
-comment on column test_demo.id           is '涓婚敭';
-comment on column test_demo.tenant_id    is '绉熸埛缂栧彿';
-comment on column test_demo.dept_id      is '閮ㄩ棬id';
-comment on column test_demo.user_id      is '鐢ㄦ埛id';
-comment on column test_demo.order_num    is '鎺掑簭鍙�';
-comment on column test_demo.test_key     is 'key閿�';
-comment on column test_demo.value        is '鍊�';
-comment on column test_demo.version      is '鐗堟湰';
-comment on column test_demo.create_dept  is '鍒涘缓閮ㄩ棬';
-comment on column test_demo.create_time  is '鍒涘缓鏃堕棿';
-comment on column test_demo.create_by    is '鍒涘缓浜�';
-comment on column test_demo.update_time  is '鏇存柊鏃堕棿';
-comment on column test_demo.update_by    is '鏇存柊浜�';
-comment on column test_demo.del_flag     is '鍒犻櫎鏍囧織';
-
-create table test_tree (
-    id          number(20)      not null,
-    tenant_id   varchar2(20)    default '000000',
-    parent_id   number(20)      default 0,
-    dept_id     number(20)      default null,
-    user_id     number(20)      default null,
-    tree_name   varchar2(255)   default null,
-    version     number(10)      default 0,
-    create_dept number(20)      default null,
-    create_time date,
-    create_by   number(20)      default null,
-    update_time date,
-    update_by   number(20)      default null,
-    del_flag    number(2)       default 0
-);
-
-alter table test_tree add constraint pk_test_tree primary key (id);
-
-comment on table  test_tree              is '娴嬭瘯鏍戣〃';
-comment on column test_tree.id           is '涓婚敭';
-comment on column test_tree.tenant_id    is '绉熸埛缂栧彿';
-comment on column test_tree.parent_id    is '鐖秈d';
-comment on column test_tree.dept_id      is '閮ㄩ棬id';
-comment on column test_tree.user_id      is '鐢ㄦ埛id';
-comment on column test_tree.tree_name    is '鍊�';
-comment on column test_tree.version      is '鐗堟湰';
-comment on column test_tree.create_dept  is '鍒涘缓閮ㄩ棬';
-comment on column test_tree.create_time  is '鍒涘缓鏃堕棿';
-comment on column test_tree.create_by    is '鍒涘缓浜�';
-comment on column test_tree.update_time  is '鏇存柊鏃堕棿';
-comment on column test_tree.update_by    is '鏇存柊浜�';
-comment on column test_tree.del_flag     is '鍒犻櫎鏍囧織';
-
-insert into sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) values (3, '000000', 108, 'test', '鏈儴闂ㄥ強浠ヤ笅 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, 3, sysdate, null);
-insert into sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) values (4, '000000', 102, 'test1', '浠呮湰浜� 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate, 103, 1, sysdate, 4, sysdate, null);
-
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (5, '娴嬭瘯鑿滃崟', 0, 5, 'demo', null, 1, 0, 'M', '0', '0', null, 'star', 103, 1, sysdate, 1, sysdate, '');
-
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1500, '娴嬭瘯鍗曡〃', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, sysdate, null, null, '娴嬭瘯鍗曡〃鑿滃崟');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1501, '娴嬭瘯鍗曡〃鏌ヨ', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1502, '娴嬭瘯鍗曡〃鏂板', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1503, '娴嬭瘯鍗曡〃淇敼', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1504, '娴嬭瘯鍗曡〃鍒犻櫎', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1505, '娴嬭瘯鍗曡〃瀵煎嚭', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, sysdate, null, null, '');
-
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1506, '娴嬭瘯鏍戣〃', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, sysdate, null, null, '娴嬭瘯鏍戣〃鑿滃崟');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1507, '娴嬭瘯鏍戣〃鏌ヨ', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1508, '娴嬭瘯鏍戣〃鏂板', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1509, '娴嬭瘯鏍戣〃淇敼', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1510, '娴嬭瘯鏍戣〃鍒犻櫎', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, sysdate, null, null, '');
-insert into sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) values (1511, '娴嬭瘯鏍戣〃瀵煎嚭', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, sysdate, null, null, '');
-
-insert into sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) values (3, '000000', '鏈儴闂ㄥ強浠ヤ笅', 'test1', 3, '4', 1, 1, '0', '0', 103, 1, sysdate, null, null, null);
-insert into sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) values (4, '000000', '浠呮湰浜�',      'test2', 4, '5', 1, 1, '0', '0', 103, 1, sysdate, null, null, null);
-
-insert into sys_role_menu(role_id, menu_id) values (3, 1);
-insert into sys_role_menu(role_id, menu_id) values (3, 5);
-insert into sys_role_menu(role_id, menu_id) values (3, 100);
-insert into sys_role_menu(role_id, menu_id) values (3, 101);
-insert into sys_role_menu(role_id, menu_id) values (3, 102);
-insert into sys_role_menu(role_id, menu_id) values (3, 103);
-insert into sys_role_menu(role_id, menu_id) values (3, 104);
-insert into sys_role_menu(role_id, menu_id) values (3, 105);
-insert into sys_role_menu(role_id, menu_id) values (3, 106);
-insert into sys_role_menu(role_id, menu_id) values (3, 107);
-insert into sys_role_menu(role_id, menu_id) values (3, 108);
-insert into sys_role_menu(role_id, menu_id) values (3, 500);
-insert into sys_role_menu(role_id, menu_id) values (3, 501);
-insert into sys_role_menu(role_id, menu_id) values (3, 1001);
-insert into sys_role_menu(role_id, menu_id) values (3, 1002);
-insert into sys_role_menu(role_id, menu_id) values (3, 1003);
-insert into sys_role_menu(role_id, menu_id) values (3, 1004);
-insert into sys_role_menu(role_id, menu_id) values (3, 1005);
-insert into sys_role_menu(role_id, menu_id) values (3, 1006);
-insert into sys_role_menu(role_id, menu_id) values (3, 1007);
-insert into sys_role_menu(role_id, menu_id) values (3, 1008);
-insert into sys_role_menu(role_id, menu_id) values (3, 1009);
-insert into sys_role_menu(role_id, menu_id) values (3, 1010);
-insert into sys_role_menu(role_id, menu_id) values (3, 1011);
-insert into sys_role_menu(role_id, menu_id) values (3, 1012);
-insert into sys_role_menu(role_id, menu_id) values (3, 1013);
-insert into sys_role_menu(role_id, menu_id) values (3, 1014);
-insert into sys_role_menu(role_id, menu_id) values (3, 1015);
-insert into sys_role_menu(role_id, menu_id) values (3, 1016);
-insert into sys_role_menu(role_id, menu_id) values (3, 1017);
-insert into sys_role_menu(role_id, menu_id) values (3, 1018);
-insert into sys_role_menu(role_id, menu_id) values (3, 1019);
-insert into sys_role_menu(role_id, menu_id) values (3, 1020);
-insert into sys_role_menu(role_id, menu_id) values (3, 1021);
-insert into sys_role_menu(role_id, menu_id) values (3, 1022);
-insert into sys_role_menu(role_id, menu_id) values (3, 1023);
-insert into sys_role_menu(role_id, menu_id) values (3, 1024);
-insert into sys_role_menu(role_id, menu_id) values (3, 1025);
-insert into sys_role_menu(role_id, menu_id) values (3, 1026);
-insert into sys_role_menu(role_id, menu_id) values (3, 1027);
-insert into sys_role_menu(role_id, menu_id) values (3, 1028);
-insert into sys_role_menu(role_id, menu_id) values (3, 1029);
-insert into sys_role_menu(role_id, menu_id) values (3, 1030);
-insert into sys_role_menu(role_id, menu_id) values (3, 1031);
-insert into sys_role_menu(role_id, menu_id) values (3, 1032);
-insert into sys_role_menu(role_id, menu_id) values (3, 1033);
-insert into sys_role_menu(role_id, menu_id) values (3, 1034);
-insert into sys_role_menu(role_id, menu_id) values (3, 1035);
-insert into sys_role_menu(role_id, menu_id) values (3, 1036);
-insert into sys_role_menu(role_id, menu_id) values (3, 1037);
-insert into sys_role_menu(role_id, menu_id) values (3, 1038);
-insert into sys_role_menu(role_id, menu_id) values (3, 1039);
-insert into sys_role_menu(role_id, menu_id) values (3, 1040);
-insert into sys_role_menu(role_id, menu_id) values (3, 1041);
-insert into sys_role_menu(role_id, menu_id) values (3, 1042);
-insert into sys_role_menu(role_id, menu_id) values (3, 1043);
-insert into sys_role_menu(role_id, menu_id) values (3, 1044);
-insert into sys_role_menu(role_id, menu_id) values (3, 1045);
-insert into sys_role_menu(role_id, menu_id) values (3, 1500);
-insert into sys_role_menu(role_id, menu_id) values (3, 1501);
-insert into sys_role_menu(role_id, menu_id) values (3, 1502);
-insert into sys_role_menu(role_id, menu_id) values (3, 1503);
-insert into sys_role_menu(role_id, menu_id) values (3, 1504);
-insert into sys_role_menu(role_id, menu_id) values (3, 1505);
-insert into sys_role_menu(role_id, menu_id) values (3, 1506);
-insert into sys_role_menu(role_id, menu_id) values (3, 1507);
-insert into sys_role_menu(role_id, menu_id) values (3, 1508);
-insert into sys_role_menu(role_id, menu_id) values (3, 1509);
-insert into sys_role_menu(role_id, menu_id) values (3, 1510);
-insert into sys_role_menu(role_id, menu_id) values (3, 1511);
-insert into sys_role_menu(role_id, menu_id) values (4, 5);
-insert into sys_role_menu(role_id, menu_id) values (4, 1500);
-insert into sys_role_menu(role_id, menu_id) values (4, 1501);
-insert into sys_role_menu(role_id, menu_id) values (4, 1502);
-insert into sys_role_menu(role_id, menu_id) values (4, 1503);
-insert into sys_role_menu(role_id, menu_id) values (4, 1504);
-insert into sys_role_menu(role_id, menu_id) values (4, 1505);
-insert into sys_role_menu(role_id, menu_id) values (4, 1506);
-insert into sys_role_menu(role_id, menu_id) values (4, 1507);
-insert into sys_role_menu(role_id, menu_id) values (4, 1508);
-insert into sys_role_menu(role_id, menu_id) values (4, 1509);
-insert into sys_role_menu(role_id, menu_id) values (4, 1510);
-insert into sys_role_menu(role_id, menu_id) values (4, 1511);
-
-insert into sys_user_role(user_id, role_id) values (3, 3);
-insert into sys_user_role(user_id, role_id) values (4, 4);
-
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (1, '000000', 102, 4, 1, '娴嬭瘯鏁版嵁鏉冮檺', '娴嬭瘯', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (2, '000000', 102, 3, 2, '瀛愯妭鐐�1', '111', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (3, '000000', 102, 3, 3, '瀛愯妭鐐�2', '222', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (4, '000000', 108, 4, 4, '娴嬭瘯鏁版嵁', 'demo', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (5, '000000', 108, 3, 13, '瀛愯妭鐐�11', '1111', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (6, '000000', 108, 3, 12, '瀛愯妭鐐�22', '2222', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (7, '000000', 108, 3, 11, '瀛愯妭鐐�33', '3333', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (8, '000000', 108, 3, 10, '瀛愯妭鐐�44', '4444', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (9, '000000', 108, 3, 9, '瀛愯妭鐐�55', '5555', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (10, '000000', 108, 3, 8, '瀛愯妭鐐�66', '6666', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (11, '000000', 108, 3, 7, '瀛愯妭鐐�77', '7777', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (12, '000000', 108, 3, 6, '瀛愯妭鐐�88', '8888', 0, 103, sysdate, 1, null, null, 0);
-insert into test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (13, '000000', 108, 3, 5, '瀛愯妭鐐�99', '9999', 0, 103, sysdate, 1, null, null, 0);
-
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (1, '000000', 0, 102, 4, '娴嬭瘯鏁版嵁鏉冮檺', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (2, '000000', 1, 102, 3, '瀛愯妭鐐�1', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (3, '000000', 2, 102, 3, '瀛愯妭鐐�2', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (4, '000000', 0, 108, 4, '娴嬭瘯鏍�1', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (5, '000000', 4, 108, 3, '瀛愯妭鐐�11', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (6, '000000', 4, 108, 3, '瀛愯妭鐐�22', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (7, '000000', 4, 108, 3, '瀛愯妭鐐�33', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (8, '000000', 5, 108, 3, '瀛愯妭鐐�44', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (9, '000000', 6, 108, 3, '瀛愯妭鐐�55', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (10, '000000', 7, 108, 3, '瀛愯妭鐐�66', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (11, '000000', 7, 108, 3, '瀛愯妭鐐�77', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (12, '000000', 10, 108, 3, '瀛愯妭鐐�88', 0, 103, sysdate, 1, null, null, 0);
-insert into test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) values (13, '000000', 10, 108, 3, '瀛愯妭鐐�99', 0, 103, sysdate, 1, null, null, 0);
diff --git a/script/sql/oracle/powerjob.sql b/script/sql/oracle/powerjob.sql
deleted file mode 100644
index 7a5419d..0000000
--- a/script/sql/oracle/powerjob.sql
+++ /dev/null
@@ -1,694 +0,0 @@
--- ----------------------------
--- Table structure for PJ_APP_INFO
--- ----------------------------
-CREATE TABLE "PJ_APP_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "APP_NAME" VARCHAR2(255 CHAR) VISIBLE ,
-  "CURRENT_SERVER" VARCHAR2(255 CHAR) VISIBLE ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "PASSWORD" VARCHAR2(255 CHAR) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Records of "PJ_APP_INFO"
--- ----------------------------
-INSERT INTO "PJ_APP_INFO" VALUES ('1', 'ruoyi-worker', '127.0.0.1:10010', NULL, NULL, '123456');
-
--- ----------------------------
--- Table structure for PJ_CONTAINER_INFO
--- ----------------------------
-CREATE TABLE "PJ_CONTAINER_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "APP_ID" NUMBER(19) VISIBLE ,
-  "CONTAINER_NAME" VARCHAR2(255 CHAR) VISIBLE ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "LAST_DEPLOY_TIME" TIMESTAMP(6) VISIBLE ,
-  "SOURCE_INFO" VARCHAR2(255 CHAR) VISIBLE ,
-  "SOURCE_TYPE" NUMBER(10) VISIBLE ,
-  "STATUS" NUMBER(10) VISIBLE ,
-  "VERSION" VARCHAR2(255 CHAR) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Table structure for PJ_INSTANCE_INFO
--- ----------------------------
-CREATE TABLE "PJ_INSTANCE_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "ACTUAL_TRIGGER_TIME" NUMBER(19) VISIBLE ,
-  "APP_ID" NUMBER(19) VISIBLE ,
-  "EXPECTED_TRIGGER_TIME" NUMBER(19) VISIBLE ,
-  "FINISHED_TIME" NUMBER(19) VISIBLE ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "INSTANCE_ID" NUMBER(19) VISIBLE ,
-  "INSTANCE_PARAMS" CLOB VISIBLE ,
-  "JOB_ID" NUMBER(19) VISIBLE ,
-  "JOB_PARAMS" CLOB VISIBLE ,
-  "LAST_REPORT_TIME" NUMBER(19) VISIBLE ,
-  "RESULT" CLOB VISIBLE ,
-  "RUNNING_TIMES" NUMBER(19) VISIBLE ,
-  "STATUS" NUMBER(10) VISIBLE ,
-  "TASK_TRACKER_ADDRESS" VARCHAR2(255 CHAR) VISIBLE ,
-  "TYPE" NUMBER(10) VISIBLE ,
-  "WF_INSTANCE_ID" NUMBER(19) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Table structure for PJ_JOB_INFO
--- ----------------------------
-CREATE TABLE "PJ_JOB_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "ALARM_CONFIG" VARCHAR2(255 CHAR) VISIBLE ,
-  "APP_ID" NUMBER(19) VISIBLE ,
-  "CONCURRENCY" NUMBER(10) VISIBLE ,
-  "DESIGNATED_WORKERS" VARCHAR2(255 CHAR) VISIBLE ,
-  "DISPATCH_STRATEGY" NUMBER(10) VISIBLE ,
-  "EXECUTE_TYPE" NUMBER(10) VISIBLE ,
-  "EXTRA" VARCHAR2(255 CHAR) VISIBLE ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "INSTANCE_RETRY_NUM" NUMBER(10) VISIBLE ,
-  "INSTANCE_TIME_LIMIT" NUMBER(19) VISIBLE ,
-  "JOB_DESCRIPTION" VARCHAR2(255 CHAR) VISIBLE ,
-  "JOB_NAME" VARCHAR2(255 CHAR) VISIBLE ,
-  "JOB_PARAMS" CLOB VISIBLE ,
-  "LIFECYCLE" VARCHAR2(255 CHAR) VISIBLE ,
-  "LOG_CONFIG" VARCHAR2(255 CHAR) VISIBLE ,
-  "MAX_INSTANCE_NUM" NUMBER(10) VISIBLE ,
-  "MAX_WORKER_COUNT" NUMBER(10) VISIBLE ,
-  "MIN_CPU_CORES" FLOAT(126) VISIBLE NOT NULL ,
-  "MIN_DISK_SPACE" FLOAT(126) VISIBLE NOT NULL ,
-  "MIN_MEMORY_SPACE" FLOAT(126) VISIBLE NOT NULL ,
-  "NEXT_TRIGGER_TIME" NUMBER(19) VISIBLE ,
-  "NOTIFY_USER_IDS" VARCHAR2(255 CHAR) VISIBLE ,
-  "PROCESSOR_INFO" VARCHAR2(255 CHAR) VISIBLE ,
-  "PROCESSOR_TYPE" NUMBER(10) VISIBLE ,
-  "STATUS" NUMBER(10) VISIBLE ,
-  "TAG" VARCHAR2(255 CHAR) VISIBLE ,
-  "TASK_RETRY_NUM" NUMBER(10) VISIBLE ,
-  "TIME_EXPRESSION" VARCHAR2(255 CHAR) VISIBLE ,
-  "TIME_EXPRESSION_TYPE" NUMBER(10) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Records of "PJ_JOB_INFO"
--- ----------------------------
-INSERT INTO "PJ_JOB_INFO" VALUES ('1', '{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', '1', '5', NULL, '2', '1', NULL, NULL, NULL, '1', '0', NULL, '鍗曟満澶勭悊鍣ㄦ墽琛屾祴璇�', NULL, '{}', '{"type":1}', '0', '0', '0.0000000000000000', '0.0000000000000000', '0.0000000000000000', NULL, NULL, 'org.dromara.job.processors.StandaloneProcessorDemo', '1', '2', NULL, '1', '30000', '3');
-INSERT INTO "PJ_JOB_INFO" VALUES ('2', '{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', '1', '5', NULL, '1', '2', NULL, NULL, NULL, '0', '0', NULL, '骞挎挱澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{"type":1}', '0', '0', '0.0000000000000000', '0.0000000000000000', '0.0000000000000000', NULL, NULL, 'org.dromara.job.processors.BroadcastProcessorDemo', '1', '2', NULL, '1', '30000', '3');
-INSERT INTO "PJ_JOB_INFO" VALUES ('3', '{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', '1', '5', NULL, '1', '4', NULL, NULL, NULL, '0', '0', NULL, 'Map澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{"type":1}', '0', '0', '0.0000000000000000', '0.0000000000000000', '0.0000000000000000', NULL, NULL, 'org.dromara.job.processors.MapProcessorDemo', '1', '2', NULL, '1', '1000', '3');
-INSERT INTO "PJ_JOB_INFO" VALUES ('4', '{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', '1', '5', NULL, '1', '3', NULL, NULL, NULL, '0', '0', NULL, 'MapReduce澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{"type":1}', '0', '0', '0.0000000000000000', '0.0000000000000000', '0.0000000000000000', NULL, NULL, 'org.dromara.job.processors.MapReduceProcessorDemo', '1', '2', NULL, '1', '1000', '3');
-
--- ----------------------------
--- Table structure for PJ_OMS_LOCK
--- ----------------------------
-CREATE TABLE "PJ_OMS_LOCK" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "LOCK_NAME" VARCHAR2(255 CHAR) VISIBLE ,
-  "MAX_LOCK_TIME" NUMBER(19) VISIBLE ,
-  "OWNERIP" VARCHAR2(255 CHAR) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Table structure for PJ_SERVER_INFO
--- ----------------------------
-CREATE TABLE "PJ_SERVER_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "IP" VARCHAR2(255 CHAR) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Table structure for PJ_USER_INFO
--- ----------------------------
-CREATE TABLE "PJ_USER_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "EMAIL" VARCHAR2(255 CHAR) VISIBLE ,
-  "EXTRA" VARCHAR2(255 CHAR) VISIBLE ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "PASSWORD" VARCHAR2(255 CHAR) VISIBLE ,
-  "PHONE" VARCHAR2(255 CHAR) VISIBLE ,
-  "USERNAME" VARCHAR2(255 CHAR) VISIBLE ,
-  "WEB_HOOK" VARCHAR2(255 CHAR) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Table structure for PJ_WORKFLOW_INFO
--- ----------------------------
-CREATE TABLE "PJ_WORKFLOW_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "APP_ID" NUMBER(19) VISIBLE ,
-  "EXTRA" VARCHAR2(255 CHAR) VISIBLE ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "LIFECYCLE" VARCHAR2(255 CHAR) VISIBLE ,
-  "MAX_WF_INSTANCE_NUM" NUMBER(10) VISIBLE ,
-  "NEXT_TRIGGER_TIME" NUMBER(19) VISIBLE ,
-  "NOTIFY_USER_IDS" VARCHAR2(255 CHAR) VISIBLE ,
-  "PEDAG" CLOB VISIBLE ,
-  "STATUS" NUMBER(10) VISIBLE ,
-  "TIME_EXPRESSION" VARCHAR2(255 CHAR) VISIBLE ,
-  "TIME_EXPRESSION_TYPE" NUMBER(10) VISIBLE ,
-  "WF_DESCRIPTION" VARCHAR2(255 CHAR) VISIBLE ,
-  "WF_NAME" VARCHAR2(255 CHAR) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Table structure for PJ_WORKFLOW_INSTANCE_INFO
--- ----------------------------
-CREATE TABLE "PJ_WORKFLOW_INSTANCE_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "ACTUAL_TRIGGER_TIME" NUMBER(19) VISIBLE ,
-  "APP_ID" NUMBER(19) VISIBLE ,
-  "DAG" CLOB VISIBLE ,
-  "EXPECTED_TRIGGER_TIME" NUMBER(19) VISIBLE ,
-  "FINISHED_TIME" NUMBER(19) VISIBLE ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE ,
-  "PARENT_WF_INSTANCE_ID" NUMBER(19) VISIBLE ,
-  "RESULT" CLOB VISIBLE ,
-  "STATUS" NUMBER(10) VISIBLE ,
-  "WF_CONTEXT" CLOB VISIBLE ,
-  "WF_INIT_PARAMS" CLOB VISIBLE ,
-  "WF_INSTANCE_ID" NUMBER(19) VISIBLE ,
-  "WORKFLOW_ID" NUMBER(19) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Table structure for PJ_WORKFLOW_NODE_INFO
--- ----------------------------
-CREATE TABLE "PJ_WORKFLOW_NODE_INFO" (
-  "ID" NUMBER(19) VISIBLE NOT NULL ,
-  "APP_ID" NUMBER(19) VISIBLE NOT NULL ,
-  "ENABLE" NUMBER(1) VISIBLE NOT NULL ,
-  "EXTRA" CLOB VISIBLE ,
-  "GMT_CREATE" TIMESTAMP(6) VISIBLE NOT NULL ,
-  "GMT_MODIFIED" TIMESTAMP(6) VISIBLE NOT NULL ,
-  "JOB_ID" NUMBER(19) VISIBLE ,
-  "NODE_NAME" VARCHAR2(255 CHAR) VISIBLE ,
-  "NODE_PARAMS" CLOB VISIBLE ,
-  "SKIP_WHEN_FAILED" NUMBER(1) VISIBLE NOT NULL ,
-  "TYPE" NUMBER(10) VISIBLE ,
-  "WORKFLOW_ID" NUMBER(19) VISIBLE
-)
-TABLESPACE "ROOT"
-LOGGING
-NOCOMPRESS
-PCTFREE 10
-INITRANS 1
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-)
-PARALLEL 1
-NOCACHE
-DISABLE ROW MOVEMENT
-;
-
--- ----------------------------
--- Primary Key structure for table PJ_APP_INFO
--- ----------------------------
-ALTER TABLE "PJ_APP_INFO" ADD CONSTRAINT "SYS_C0012204" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Uniques structure for table PJ_APP_INFO
--- ----------------------------
-ALTER TABLE "PJ_APP_INFO" ADD CONSTRAINT "UIDX01_APP_INFO" UNIQUE ("APP_NAME") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Checks structure for table PJ_APP_INFO
--- ----------------------------
-ALTER TABLE "PJ_APP_INFO" ADD CONSTRAINT "SYS_C0012203" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Primary Key structure for table PJ_CONTAINER_INFO
--- ----------------------------
-ALTER TABLE "PJ_CONTAINER_INFO" ADD CONSTRAINT "SYS_C0012206" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Checks structure for table PJ_CONTAINER_INFO
--- ----------------------------
-ALTER TABLE "PJ_CONTAINER_INFO" ADD CONSTRAINT "SYS_C0012205" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Indexes structure for table PJ_CONTAINER_INFO
--- ----------------------------
-CREATE INDEX "IDX01_CONTAINER_INFO"
-  ON "PJ_CONTAINER_INFO" ("APP_ID" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-
--- ----------------------------
--- Primary Key structure for table PJ_INSTANCE_INFO
--- ----------------------------
-ALTER TABLE "PJ_INSTANCE_INFO" ADD CONSTRAINT "SYS_C0012208" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Checks structure for table PJ_INSTANCE_INFO
--- ----------------------------
-ALTER TABLE "PJ_INSTANCE_INFO" ADD CONSTRAINT "SYS_C0012207" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Indexes structure for table PJ_INSTANCE_INFO
--- ----------------------------
-CREATE INDEX "IDX01_INSTANCE_INFO"
-  ON "PJ_INSTANCE_INFO" ("JOB_ID" ASC, "STATUS" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-CREATE INDEX "IDX02_INSTANCE_INFO"
-  ON "PJ_INSTANCE_INFO" ("APP_ID" ASC, "STATUS" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-CREATE INDEX "IDX03_INSTANCE_INFO"
-  ON "PJ_INSTANCE_INFO" ("INSTANCE_ID" ASC, "STATUS" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-
--- ----------------------------
--- Primary Key structure for table PJ_JOB_INFO
--- ----------------------------
-ALTER TABLE "PJ_JOB_INFO" ADD CONSTRAINT "SYS_C0012213" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Checks structure for table PJ_JOB_INFO
--- ----------------------------
-ALTER TABLE "PJ_JOB_INFO" ADD CONSTRAINT "SYS_C0012209" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-ALTER TABLE "PJ_JOB_INFO" ADD CONSTRAINT "SYS_C0012210" CHECK ("MIN_CPU_CORES" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-ALTER TABLE "PJ_JOB_INFO" ADD CONSTRAINT "SYS_C0012211" CHECK ("MIN_DISK_SPACE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-ALTER TABLE "PJ_JOB_INFO" ADD CONSTRAINT "SYS_C0012212" CHECK ("MIN_MEMORY_SPACE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Indexes structure for table PJ_JOB_INFO
--- ----------------------------
-CREATE INDEX "IDX01_JOB_INFO"
-  ON "PJ_JOB_INFO" ("APP_ID" ASC, "NEXT_TRIGGER_TIME" ASC, "TIME_EXPRESSION_TYPE" ASC, "STATUS" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-
--- ----------------------------
--- Primary Key structure for table PJ_OMS_LOCK
--- ----------------------------
-ALTER TABLE "PJ_OMS_LOCK" ADD CONSTRAINT "SYS_C0012215" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Uniques structure for table PJ_OMS_LOCK
--- ----------------------------
-ALTER TABLE "PJ_OMS_LOCK" ADD CONSTRAINT "UIDX01_OMS_LOCK" UNIQUE ("LOCK_NAME") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Checks structure for table PJ_OMS_LOCK
--- ----------------------------
-ALTER TABLE "PJ_OMS_LOCK" ADD CONSTRAINT "SYS_C0012214" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Primary Key structure for table PJ_SERVER_INFO
--- ----------------------------
-ALTER TABLE "PJ_SERVER_INFO" ADD CONSTRAINT "SYS_C0012217" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Uniques structure for table PJ_SERVER_INFO
--- ----------------------------
-ALTER TABLE "PJ_SERVER_INFO" ADD CONSTRAINT "UIDX01_SERVER_INFO" UNIQUE ("IP") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Checks structure for table PJ_SERVER_INFO
--- ----------------------------
-ALTER TABLE "PJ_SERVER_INFO" ADD CONSTRAINT "SYS_C0012216" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Indexes structure for table PJ_SERVER_INFO
--- ----------------------------
-CREATE INDEX "IDX01_SERVER_INFO"
-  ON "PJ_SERVER_INFO" ("GMT_MODIFIED" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-
--- ----------------------------
--- Primary Key structure for table PJ_USER_INFO
--- ----------------------------
-ALTER TABLE "PJ_USER_INFO" ADD CONSTRAINT "SYS_C0012219" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Checks structure for table PJ_USER_INFO
--- ----------------------------
-ALTER TABLE "PJ_USER_INFO" ADD CONSTRAINT "SYS_C0012218" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Indexes structure for table PJ_USER_INFO
--- ----------------------------
-CREATE INDEX "UIDX01_USER_INFO"
-  ON "PJ_USER_INFO" ("USERNAME" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-CREATE INDEX "UIDX02_USER_INFO"
-  ON "PJ_USER_INFO" ("EMAIL" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-
--- ----------------------------
--- Primary Key structure for table PJ_WORKFLOW_INFO
--- ----------------------------
-ALTER TABLE "PJ_WORKFLOW_INFO" ADD CONSTRAINT "SYS_C0012221" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Checks structure for table PJ_WORKFLOW_INFO
--- ----------------------------
-ALTER TABLE "PJ_WORKFLOW_INFO" ADD CONSTRAINT "SYS_C0012220" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Indexes structure for table PJ_WORKFLOW_INFO
--- ----------------------------
-CREATE INDEX "IDX01_WORKFLOW_INFO"
-  ON "PJ_WORKFLOW_INFO" ("APP_ID" ASC, "NEXT_TRIGGER_TIME" ASC, "TIME_EXPRESSION_TYPE" ASC, "STATUS" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-
--- ----------------------------
--- Primary Key structure for table PJ_WORKFLOW_INSTANCE_INFO
--- ----------------------------
-ALTER TABLE "PJ_WORKFLOW_INSTANCE_INFO" ADD CONSTRAINT "SYS_C0012223" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Uniques structure for table PJ_WORKFLOW_INSTANCE_INFO
--- ----------------------------
-ALTER TABLE "PJ_WORKFLOW_INSTANCE_INFO" ADD CONSTRAINT "UIDX01_WF_INSTANCE" UNIQUE ("WF_INSTANCE_ID") NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Checks structure for table PJ_WORKFLOW_INSTANCE_INFO
--- ----------------------------
-ALTER TABLE "PJ_WORKFLOW_INSTANCE_INFO" ADD CONSTRAINT "SYS_C0012222" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Indexes structure for table PJ_WORKFLOW_INSTANCE_INFO
--- ----------------------------
-CREATE INDEX "IDX01_WF_INSTANCE"
-  ON "PJ_WORKFLOW_INSTANCE_INFO" ("WORKFLOW_ID" ASC, "EXPECTED_TRIGGER_TIME" ASC, "APP_ID" ASC, "STATUS" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
-
--- ----------------------------
--- Primary Key structure for table PJ_WORKFLOW_NODE_INFO
--- ----------------------------
-ALTER TABLE "PJ_WORKFLOW_NODE_INFO" ADD CONSTRAINT "SYS_C0012230" PRIMARY KEY ("ID");
-
--- ----------------------------
--- Checks structure for table PJ_WORKFLOW_NODE_INFO
--- ----------------------------
-ALTER TABLE "PJ_WORKFLOW_NODE_INFO" ADD CONSTRAINT "SYS_C0012224" CHECK ("ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-ALTER TABLE "PJ_WORKFLOW_NODE_INFO" ADD CONSTRAINT "SYS_C0012225" CHECK ("APP_ID" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-ALTER TABLE "PJ_WORKFLOW_NODE_INFO" ADD CONSTRAINT "SYS_C0012226" CHECK ("ENABLE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-ALTER TABLE "PJ_WORKFLOW_NODE_INFO" ADD CONSTRAINT "SYS_C0012227" CHECK ("GMT_CREATE" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-ALTER TABLE "PJ_WORKFLOW_NODE_INFO" ADD CONSTRAINT "SYS_C0012228" CHECK ("GMT_MODIFIED" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-ALTER TABLE "PJ_WORKFLOW_NODE_INFO" ADD CONSTRAINT "SYS_C0012229" CHECK ("SKIP_WHEN_FAILED" IS NOT NULL) NOT DEFERRABLE INITIALLY IMMEDIATE NORELY VALIDATE;
-
--- ----------------------------
--- Indexes structure for table PJ_WORKFLOW_NODE_INFO
--- ----------------------------
-CREATE INDEX "IDX01_WORKFLOW_NODE_INFO"
-  ON "PJ_WORKFLOW_NODE_INFO" ("GMT_CREATE" ASC, "WORKFLOW_ID" ASC)
-  LOGGING
-  TABLESPACE "ROOT"
-  VISIBLE
-PCTFREE 10
-INITRANS 2
-STORAGE (
-  INITIAL 65536
-  NEXT 1048576
-  MINEXTENTS 1
-  MAXEXTENTS 2147483645
-  BUFFER_POOL DEFAULT
-  FLASH_CACHE DEFAULT
-)
-   USABLE;
diff --git a/script/sql/oracle/snail_job_oracle.sql b/script/sql/oracle/snail_job_oracle.sql
new file mode 100644
index 0000000..19aa07e
--- /dev/null
+++ b/script/sql/oracle/snail_job_oracle.sql
@@ -0,0 +1,894 @@
+/*
+ SnailJob Database Transfer Tool
+ Source Server Type    : MySQL
+ Target Server Type    : Oracle
+ Date: 2024-05-14 23:36:38
+*/
+
+
+-- sj_namespace
+CREATE TABLE sj_namespace
+(
+    id          number GENERATED ALWAYS AS IDENTITY,
+    name        varchar2(64)                            NULL,
+    unique_id   varchar2(64)                            NULL,
+    description varchar2(256) DEFAULT ''                NULL,
+    deleted     smallint      DEFAULT 0                 NOT NULL,
+    create_dt   date          DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    update_dt   date          DEFAULT CURRENT_TIMESTAMP NOT NULL
+);
+
+ALTER TABLE sj_namespace
+    ADD CONSTRAINT pk_sj_namespace PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_namespace_01 ON sj_namespace (name);
+
+COMMENT ON COLUMN sj_namespace.id IS '涓婚敭';
+COMMENT ON COLUMN sj_namespace.name IS '鍚嶇О';
+COMMENT ON COLUMN sj_namespace.unique_id IS '鍞竴id';
+COMMENT ON COLUMN sj_namespace.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_namespace.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_namespace.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_namespace.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_namespace IS '鍛藉悕绌洪棿';
+
+INSERT INTO sj_namespace(name, unique_id, description, deleted, create_dt, update_dt) VALUES ('Development', 'dev', '', 0, sysdate, sysdate);
+INSERT INTO sj_namespace(name, unique_id, description, deleted, create_dt, update_dt) VALUES ('Production', 'prod', '', 0, sysdate, sysdate);
+
+-- sj_group_config
+CREATE TABLE sj_group_config
+(
+    id                number GENERATED ALWAYS AS IDENTITY,
+    namespace_id      varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a'    NULL,
+    group_name        varchar2(64)  DEFAULT ''                                    NULL,
+    description       varchar2(256) DEFAULT ''                                    NULL,
+    token             varchar2(64)  DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT' NULL,
+    group_status      smallint      DEFAULT 0                                     NOT NULL,
+    version           number                                                      NOT NULL,
+    group_partition   number                                                      NOT NULL,
+    id_generator_mode smallint      DEFAULT 1                                     NOT NULL,
+    init_scene        smallint      DEFAULT 0                                     NOT NULL,
+    bucket_index      number        DEFAULT 0                                     NOT NULL,
+    create_dt         date          DEFAULT CURRENT_TIMESTAMP                     NOT NULL,
+    update_dt         date          DEFAULT CURRENT_TIMESTAMP                     NOT NULL
+);
+
+ALTER TABLE sj_group_config
+    ADD CONSTRAINT pk_sj_group_config PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_group_config_01 ON sj_group_config (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_group_config.id IS '涓婚敭';
+COMMENT ON COLUMN sj_group_config.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_group_config.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_group_config.description IS '缁勬弿杩�';
+COMMENT ON COLUMN sj_group_config.token IS 'token';
+COMMENT ON COLUMN sj_group_config.group_status IS '缁勭姸鎬� 0銆佹湭鍚敤 1銆佸惎鐢�';
+COMMENT ON COLUMN sj_group_config.version IS '鐗堟湰鍙�';
+COMMENT ON COLUMN sj_group_config.group_partition IS '鍒嗗尯';
+COMMENT ON COLUMN sj_group_config.id_generator_mode IS '鍞竴id鐢熸垚妯″紡 榛樿鍙锋妯″紡';
+COMMENT ON COLUMN sj_group_config.init_scene IS '鏄惁鍒濆鍖栧満鏅� 0:鍚� 1:鏄�';
+COMMENT ON COLUMN sj_group_config.bucket_index IS 'bucket';
+COMMENT ON COLUMN sj_group_config.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_group_config.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_group_config IS '缁勯厤缃�';
+
+INSERT INTO sj_group_config (namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, bucket_index, create_dt, update_dt) VALUES ('dev', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, sysdate, sysdate);
+
+-- sj_notify_config
+CREATE TABLE sj_notify_config
+(
+    id                     number GENERATED ALWAYS AS IDENTITY,
+    namespace_id           varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name             varchar2(64)                                             NULL,
+    business_id            varchar2(64)                                             NULL,
+    system_task_type       smallint      DEFAULT 3                                  NOT NULL,
+    notify_status          smallint      DEFAULT 0                                  NOT NULL,
+    recipient_ids          varchar2(128)                                            NULL,
+    notify_threshold       number        DEFAULT 0                                  NOT NULL,
+    notify_scene           smallint      DEFAULT 0                                  NOT NULL,
+    rate_limiter_status    smallint      DEFAULT 0                                  NOT NULL,
+    rate_limiter_threshold number        DEFAULT 0                                  NOT NULL,
+    description            varchar2(256) DEFAULT ''                                 NULL,
+    create_dt              date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt              date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_notify_config
+    ADD CONSTRAINT pk_sj_notify_config PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_notify_config_01 ON sj_notify_config (namespace_id, group_name, business_id);
+
+COMMENT ON COLUMN sj_notify_config.id IS '涓婚敭';
+COMMENT ON COLUMN sj_notify_config.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_notify_config.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_notify_config.business_id IS '涓氬姟id  ( job_id鎴杦orkflow_id鎴杝cene_name ) ';
+COMMENT ON COLUMN sj_notify_config.system_task_type IS '浠诲姟绫诲瀷 1. 閲嶈瘯浠诲姟 2. 閲嶈瘯鍥炶皟 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟';
+COMMENT ON COLUMN sj_notify_config.notify_status IS '閫氱煡鐘舵�� 0銆佹湭鍚敤 1銆佸惎鐢�';
+COMMENT ON COLUMN sj_notify_config.recipient_ids IS '鎺ユ敹浜篿d鍒楄〃';
+COMMENT ON COLUMN sj_notify_config.notify_threshold IS '閫氱煡闃堝��';
+COMMENT ON COLUMN sj_notify_config.notify_scene IS '閫氱煡鍦烘櫙';
+COMMENT ON COLUMN sj_notify_config.rate_limiter_status IS '闄愭祦鐘舵�� 0銆佹湭鍚敤 1銆佸惎鐢�';
+COMMENT ON COLUMN sj_notify_config.rate_limiter_threshold IS '姣忕闄愭祦闃堝��';
+COMMENT ON COLUMN sj_notify_config.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_notify_config.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_notify_config.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_notify_config IS '閫氱煡閰嶇疆';
+
+-- sj_notify_recipient
+CREATE TABLE sj_notify_recipient
+(
+    id               number GENERATED ALWAYS AS IDENTITY,
+    namespace_id     varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    recipient_name   varchar2(64)                                             NULL,
+    notify_type      smallint      DEFAULT 0                                  NOT NULL,
+    notify_attribute varchar2(512)                                            NULL,
+    description      varchar2(256) DEFAULT ''                                 NULL,
+    create_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_notify_recipient
+    ADD CONSTRAINT pk_sj_notify_recipient PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_notify_recipient_01 ON sj_notify_recipient (namespace_id);
+
+COMMENT ON COLUMN sj_notify_recipient.id IS '涓婚敭';
+COMMENT ON COLUMN sj_notify_recipient.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_notify_recipient.recipient_name IS '鎺ユ敹浜哄悕绉�';
+COMMENT ON COLUMN sj_notify_recipient.notify_type IS '閫氱煡绫诲瀷 1銆侀拤閽� 2銆侀偖浠� 3銆佷紒涓氬井淇� 4 椋炰功';
+COMMENT ON COLUMN sj_notify_recipient.notify_attribute IS '閰嶇疆灞炴��';
+COMMENT ON COLUMN sj_notify_recipient.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_notify_recipient.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_notify_recipient.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_notify_recipient IS '鍛婅閫氱煡鎺ユ敹浜�';
+
+-- sj_retry_dead_letter_0
+CREATE TABLE sj_retry_dead_letter_0
+(
+    id            number GENERATED ALWAYS AS IDENTITY,
+    namespace_id  varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    unique_id     varchar2(64)                                             NULL,
+    group_name    varchar2(64)                                             NULL,
+    scene_name    varchar2(64)                                             NULL,
+    idempotent_id varchar2(64)                                             NULL,
+    biz_no        varchar2(64)  DEFAULT ''                                 NULL,
+    executor_name varchar2(512) DEFAULT ''                                 NULL,
+    args_str      clob                                                     NULL,
+    ext_attrs     clob                                                     NULL,
+    task_type     smallint      DEFAULT 1                                  NOT NULL,
+    create_dt     date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_retry_dead_letter_0
+    ADD CONSTRAINT pk_sj_retry_dead_letter_0 PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_retry_dead_letter_0_01 ON sj_retry_dead_letter_0 (namespace_id, group_name, unique_id);
+
+CREATE INDEX idx_sj_retry_dead_letter_0_01 ON sj_retry_dead_letter_0 (namespace_id, group_name, scene_name);
+CREATE INDEX idx_sj_retry_dead_letter_0_02 ON sj_retry_dead_letter_0 (idempotent_id);
+CREATE INDEX idx_sj_retry_dead_letter_0_03 ON sj_retry_dead_letter_0 (biz_no);
+CREATE INDEX idx_sj_retry_dead_letter_0_04 ON sj_retry_dead_letter_0 (create_dt);
+
+COMMENT ON COLUMN sj_retry_dead_letter_0.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_dead_letter_0.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_dead_letter_0.unique_id IS '鍚岀粍涓媔d鍞竴';
+COMMENT ON COLUMN sj_retry_dead_letter_0.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_dead_letter_0.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_dead_letter_0.idempotent_id IS '骞傜瓑id';
+COMMENT ON COLUMN sj_retry_dead_letter_0.biz_no IS '涓氬姟缂栧彿';
+COMMENT ON COLUMN sj_retry_dead_letter_0.executor_name IS '鎵ц鍣ㄥ悕绉�';
+COMMENT ON COLUMN sj_retry_dead_letter_0.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_retry_dead_letter_0.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_retry_dead_letter_0.task_type IS '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�';
+COMMENT ON COLUMN sj_retry_dead_letter_0.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON TABLE sj_retry_dead_letter_0 IS '姝讳俊闃熷垪琛�';
+
+-- sj_retry_task_0
+CREATE TABLE sj_retry_task_0
+(
+    id              number GENERATED ALWAYS AS IDENTITY,
+    namespace_id    varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    unique_id       varchar2(64)                                             NULL,
+    group_name      varchar2(64)                                             NULL,
+    scene_name      varchar2(64)                                             NULL,
+    idempotent_id   varchar2(64)                                             NULL,
+    biz_no          varchar2(64)  DEFAULT ''                                 NULL,
+    executor_name   varchar2(512) DEFAULT ''                                 NULL,
+    args_str        clob                                                     NULL,
+    ext_attrs       clob                                                     NULL,
+    next_trigger_at date                                                     NOT NULL,
+    retry_count     number        DEFAULT 0                                  NOT NULL,
+    retry_status    smallint      DEFAULT 0                                  NOT NULL,
+    task_type       smallint      DEFAULT 1                                  NOT NULL,
+    create_dt       date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt       date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_retry_task_0
+    ADD CONSTRAINT pk_sj_retry_task_0 PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_retry_task_0_01 ON sj_retry_task_0 (namespace_id, group_name, unique_id);
+
+CREATE INDEX idx_sj_retry_task_0_01 ON sj_retry_task_0 (namespace_id, group_name, scene_name);
+CREATE INDEX idx_sj_retry_task_0_02 ON sj_retry_task_0 (namespace_id, group_name, task_type);
+CREATE INDEX idx_sj_retry_task_0_03 ON sj_retry_task_0 (namespace_id, group_name, retry_status);
+CREATE INDEX idx_sj_retry_task_0_04 ON sj_retry_task_0 (idempotent_id);
+CREATE INDEX idx_sj_retry_task_0_05 ON sj_retry_task_0 (biz_no);
+CREATE INDEX idx_sj_retry_task_0_06 ON sj_retry_task_0 (create_dt);
+
+COMMENT ON COLUMN sj_retry_task_0.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_task_0.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_task_0.unique_id IS '鍚岀粍涓媔d鍞竴';
+COMMENT ON COLUMN sj_retry_task_0.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_task_0.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_task_0.idempotent_id IS '骞傜瓑id';
+COMMENT ON COLUMN sj_retry_task_0.biz_no IS '涓氬姟缂栧彿';
+COMMENT ON COLUMN sj_retry_task_0.executor_name IS '鎵ц鍣ㄥ悕绉�';
+COMMENT ON COLUMN sj_retry_task_0.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_retry_task_0.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_retry_task_0.next_trigger_at IS '涓嬫瑙﹀彂鏃堕棿';
+COMMENT ON COLUMN sj_retry_task_0.retry_count IS '閲嶈瘯娆℃暟';
+COMMENT ON COLUMN sj_retry_task_0.retry_status IS '閲嶈瘯鐘舵�� 0銆侀噸璇曚腑 1銆佹垚鍔� 2銆佹渶澶ч噸璇曟鏁�';
+COMMENT ON COLUMN sj_retry_task_0.task_type IS '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�';
+COMMENT ON COLUMN sj_retry_task_0.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_retry_task_0.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_retry_task_0 IS '浠诲姟琛�';
+
+-- sj_retry_task_log
+CREATE TABLE sj_retry_task_log
+(
+    id            number GENERATED ALWAYS AS IDENTITY,
+    namespace_id  varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    unique_id     varchar2(64)                                             NULL,
+    group_name    varchar2(64)                                             NULL,
+    scene_name    varchar2(64)                                             NULL,
+    idempotent_id varchar2(64)                                             NULL,
+    biz_no        varchar2(64)  DEFAULT ''                                 NULL,
+    executor_name varchar2(512) DEFAULT ''                                 NULL,
+    args_str      clob                                                     NULL,
+    ext_attrs     clob                                                     NULL,
+    retry_status  smallint      DEFAULT 0                                  NOT NULL,
+    task_type     smallint      DEFAULT 1                                  NOT NULL,
+    create_dt     date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt     date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_retry_task_log
+    ADD CONSTRAINT pk_sj_retry_task_log PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_retry_task_log_01 ON sj_retry_task_log (namespace_id, group_name, scene_name);
+CREATE INDEX idx_sj_retry_task_log_02 ON sj_retry_task_log (retry_status);
+CREATE INDEX idx_sj_retry_task_log_03 ON sj_retry_task_log (idempotent_id);
+CREATE INDEX idx_sj_retry_task_log_04 ON sj_retry_task_log (unique_id);
+CREATE INDEX idx_sj_retry_task_log_05 ON sj_retry_task_log (biz_no);
+CREATE INDEX idx_sj_retry_task_log_06 ON sj_retry_task_log (create_dt);
+
+COMMENT ON COLUMN sj_retry_task_log.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_task_log.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_task_log.unique_id IS '鍚岀粍涓媔d鍞竴';
+COMMENT ON COLUMN sj_retry_task_log.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_task_log.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_task_log.idempotent_id IS '骞傜瓑id';
+COMMENT ON COLUMN sj_retry_task_log.biz_no IS '涓氬姟缂栧彿';
+COMMENT ON COLUMN sj_retry_task_log.executor_name IS '鎵ц鍣ㄥ悕绉�';
+COMMENT ON COLUMN sj_retry_task_log.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_retry_task_log.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_retry_task_log.retry_status IS '閲嶈瘯鐘舵�� 0銆侀噸璇曚腑 1銆佹垚鍔� 2銆佹渶澶ф鏁�';
+COMMENT ON COLUMN sj_retry_task_log.task_type IS '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�';
+COMMENT ON COLUMN sj_retry_task_log.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_retry_task_log.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_retry_task_log IS '浠诲姟鏃ュ織鍩虹淇℃伅琛�';
+
+-- sj_retry_task_log_message
+CREATE TABLE sj_retry_task_log_message
+(
+    id           number GENERATED ALWAYS AS IDENTITY,
+    namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name   varchar2(64)                                            NULL,
+    unique_id    varchar2(64)                                            NULL,
+    message      clob                                                    NULL,
+    log_num      number       DEFAULT 1                                  NOT NULL,
+    real_time    number       DEFAULT 0                                  NOT NULL,
+    create_dt    date         DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_retry_task_log_message
+    ADD CONSTRAINT pk_sj_retry_task_log_message PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_retry_task_log_message_01 ON sj_retry_task_log_message (namespace_id, group_name, unique_id);
+CREATE INDEX idx_sj_retry_task_log_message_02 ON sj_retry_task_log_message (create_dt);
+
+COMMENT ON COLUMN sj_retry_task_log_message.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_task_log_message.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_task_log_message.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_task_log_message.unique_id IS '鍚岀粍涓媔d鍞竴';
+COMMENT ON COLUMN sj_retry_task_log_message.message IS '寮傚父淇℃伅';
+COMMENT ON COLUMN sj_retry_task_log_message.log_num IS '鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_task_log_message.real_time IS '涓婃姤鏃堕棿';
+COMMENT ON COLUMN sj_retry_task_log_message.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON TABLE sj_retry_task_log_message IS '浠诲姟璋冨害鏃ュ織淇℃伅璁板綍琛�';
+
+-- sj_retry_scene_config
+CREATE TABLE sj_retry_scene_config
+(
+    id               number GENERATED ALWAYS AS IDENTITY,
+    namespace_id     varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    scene_name       varchar2(64)                                             NULL,
+    group_name       varchar2(64)                                             NULL,
+    scene_status     smallint      DEFAULT 0                                  NOT NULL,
+    max_retry_count  number        DEFAULT 5                                  NOT NULL,
+    back_off         smallint      DEFAULT 1                                  NOT NULL,
+    trigger_interval varchar2(16)  DEFAULT ''                                 NULL,
+    deadline_request number        DEFAULT 60000                              NOT NULL,
+    executor_timeout number        DEFAULT 5                                  NOT NULL,
+    route_key        smallint      DEFAULT 4                                  NOT NULL,
+    description      varchar2(256) DEFAULT ''                                 NULL,
+    create_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_retry_scene_config
+    ADD CONSTRAINT pk_sj_retry_scene_config PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_retry_scene_config_01 ON sj_retry_scene_config (namespace_id, group_name, scene_name);
+
+COMMENT ON COLUMN sj_retry_scene_config.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_scene_config.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_scene_config.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_scene_config.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_scene_config.scene_status IS '缁勭姸鎬� 0銆佹湭鍚敤 1銆佸惎鐢�';
+COMMENT ON COLUMN sj_retry_scene_config.max_retry_count IS '鏈�澶ч噸璇曟鏁�';
+COMMENT ON COLUMN sj_retry_scene_config.back_off IS '1銆侀粯璁ょ瓑绾� 2銆佸浐瀹氶棿闅旀椂闂� 3銆丆RON 琛ㄨ揪寮�';
+COMMENT ON COLUMN sj_retry_scene_config.trigger_interval IS '闂撮殧鏃堕暱';
+COMMENT ON COLUMN sj_retry_scene_config.deadline_request IS 'Deadline Request 璋冪敤閾捐秴鏃� 鍗曚綅姣';
+COMMENT ON COLUMN sj_retry_scene_config.executor_timeout IS '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇';
+COMMENT ON COLUMN sj_retry_scene_config.route_key IS '璺敱绛栫暐';
+COMMENT ON COLUMN sj_retry_scene_config.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_retry_scene_config.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_retry_scene_config.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_retry_scene_config IS '鍦烘櫙閰嶇疆';
+
+-- sj_server_node
+CREATE TABLE sj_server_node
+(
+    id           number GENERATED ALWAYS AS IDENTITY,
+    namespace_id varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name   varchar2(64)                                             NULL,
+    host_id      varchar2(64)                                             NULL,
+    host_ip      varchar2(64)                                             NULL,
+    host_port    number                                                   NOT NULL,
+    expire_at    date                                                     NOT NULL,
+    node_type    smallint                                                 NOT NULL,
+    ext_attrs    varchar2(256) DEFAULT ''                                 NULL,
+    create_dt    date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt    date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_server_node
+    ADD CONSTRAINT pk_sj_server_node PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_server_node_01 ON sj_server_node (host_id, host_ip);
+
+CREATE INDEX idx_sj_server_node_01 ON sj_server_node (namespace_id, group_name);
+CREATE INDEX idx_sj_server_node_02 ON sj_server_node (expire_at, node_type);
+
+COMMENT ON COLUMN sj_server_node.id IS '涓婚敭';
+COMMENT ON COLUMN sj_server_node.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_server_node.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_server_node.host_id IS '涓绘満id';
+COMMENT ON COLUMN sj_server_node.host_ip IS '鏈哄櫒ip';
+COMMENT ON COLUMN sj_server_node.host_port IS '鏈哄櫒绔彛';
+COMMENT ON COLUMN sj_server_node.expire_at IS '杩囨湡鏃堕棿';
+COMMENT ON COLUMN sj_server_node.node_type IS '鑺傜偣绫诲瀷 1銆佸鎴风 2銆佹槸鏈嶅姟绔�';
+COMMENT ON COLUMN sj_server_node.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_server_node.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_server_node.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_server_node IS '鏈嶅姟鍣ㄨ妭鐐�';
+
+-- sj_distributed_lock
+CREATE TABLE sj_distributed_lock
+(
+    id         number GENERATED ALWAYS AS IDENTITY,
+    name       varchar2(64)                              NULL,
+    lock_until timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL,
+    locked_at  timestamp(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL,
+    locked_by  varchar2(255)                             NULL,
+    create_dt  date         DEFAULT CURRENT_TIMESTAMP    NOT NULL,
+    update_dt  date         DEFAULT CURRENT_TIMESTAMP    NOT NULL
+);
+
+ALTER TABLE sj_distributed_lock
+    ADD CONSTRAINT pk_sj_distributed_lock PRIMARY KEY (id);
+
+COMMENT ON COLUMN sj_distributed_lock.id IS '涓婚敭';
+COMMENT ON COLUMN sj_distributed_lock.name IS '閿佸悕绉�';
+COMMENT ON COLUMN sj_distributed_lock.lock_until IS '閿佸畾鏃堕暱';
+COMMENT ON COLUMN sj_distributed_lock.locked_at IS '閿佸畾鏃堕棿';
+COMMENT ON COLUMN sj_distributed_lock.locked_by IS '閿佸畾鑰�';
+COMMENT ON COLUMN sj_distributed_lock.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_distributed_lock.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_distributed_lock IS '閿佸畾琛�';
+
+-- sj_system_user
+CREATE TABLE sj_system_user
+(
+    id        number GENERATED ALWAYS AS IDENTITY,
+    username  varchar2(64)                       NULL,
+    password  varchar2(128)                      NULL,
+    role      smallint DEFAULT 0                 NOT NULL,
+    create_dt date     DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    update_dt date     DEFAULT CURRENT_TIMESTAMP NOT NULL
+);
+
+ALTER TABLE sj_system_user
+    ADD CONSTRAINT pk_sj_system_user PRIMARY KEY (id);
+
+COMMENT ON COLUMN sj_system_user.id IS '涓婚敭';
+COMMENT ON COLUMN sj_system_user.username IS '璐﹀彿';
+COMMENT ON COLUMN sj_system_user.password IS '瀵嗙爜';
+COMMENT ON COLUMN sj_system_user.role IS '瑙掕壊锛�1-鏅�氱敤鎴枫��2-绠$悊鍛�';
+COMMENT ON COLUMN sj_system_user.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_system_user.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_system_user IS '绯荤粺鐢ㄦ埛琛�';
+
+-- pwd: admin
+INSERT INTO sj_system_user(username, password, role, create_dt, update_dt) VALUES ('admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', 2, sysdate, sysdate);
+
+-- sj_system_user_permission
+CREATE TABLE sj_system_user_permission
+(
+    id             number GENERATED ALWAYS AS IDENTITY,
+    group_name     varchar2(64)                                            NULL,
+    namespace_id   varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    system_user_id number                                                  NOT NULL,
+    create_dt      date         DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt      date         DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_system_user_permission
+    ADD CONSTRAINT pk_sj_system_user_permission PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_system_user_permission_01 ON sj_system_user_permission (namespace_id, group_name, system_user_id);
+
+COMMENT ON COLUMN sj_system_user_permission.id IS '涓婚敭';
+COMMENT ON COLUMN sj_system_user_permission.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_system_user_permission.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_system_user_permission.system_user_id IS '绯荤粺鐢ㄦ埛id';
+COMMENT ON COLUMN sj_system_user_permission.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_system_user_permission.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_system_user_permission IS '绯荤粺鐢ㄦ埛鏉冮檺琛�';
+
+-- sj_sequence_alloc
+CREATE TABLE sj_sequence_alloc
+(
+    id           number GENERATED ALWAYS AS IDENTITY,
+    namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name   varchar2(64) DEFAULT ''                                 NULL,
+    max_id       number       DEFAULT 1                                  NOT NULL,
+    step         number       DEFAULT 100                                NOT NULL,
+    update_dt    date         DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_sequence_alloc
+    ADD CONSTRAINT pk_sj_sequence_alloc PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_sequence_alloc_01 ON sj_sequence_alloc (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_sequence_alloc.id IS '涓婚敭';
+COMMENT ON COLUMN sj_sequence_alloc.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_sequence_alloc.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_sequence_alloc.max_id IS '鏈�澶d';
+COMMENT ON COLUMN sj_sequence_alloc.step IS '姝ラ暱';
+COMMENT ON COLUMN sj_sequence_alloc.update_dt IS '鏇存柊鏃堕棿';
+COMMENT ON TABLE sj_sequence_alloc IS '鍙锋妯″紡搴忓彿ID鍒嗛厤琛�';
+
+-- sj_job
+CREATE TABLE sj_job
+(
+    id               number GENERATED ALWAYS AS IDENTITY,
+    namespace_id     varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name       varchar2(64)                                             NULL,
+    job_name         varchar2(64)                                             NULL,
+    args_str         clob          DEFAULT NULL                               NULL,
+    args_type        smallint      DEFAULT 1                                  NOT NULL,
+    next_trigger_at  number                                                   NOT NULL,
+    job_status       smallint      DEFAULT 1                                  NOT NULL,
+    task_type        smallint      DEFAULT 1                                  NOT NULL,
+    route_key        smallint      DEFAULT 4                                  NOT NULL,
+    executor_type    smallint      DEFAULT 1                                  NOT NULL,
+    executor_info    varchar2(255) DEFAULT NULL                               NULL,
+    trigger_type     smallint                                                 NOT NULL,
+    trigger_interval varchar2(255)                                            NULL,
+    block_strategy   smallint      DEFAULT 1                                  NOT NULL,
+    executor_timeout number        DEFAULT 0                                  NOT NULL,
+    max_retry_times  number        DEFAULT 0                                  NOT NULL,
+    parallel_num     number        DEFAULT 1                                  NOT NULL,
+    retry_interval   number        DEFAULT 0                                  NOT NULL,
+    bucket_index     number        DEFAULT 0                                  NOT NULL,
+    resident         smallint      DEFAULT 0                                  NOT NULL,
+    description      varchar2(256) DEFAULT ''                                 NULL,
+    ext_attrs        varchar2(256) DEFAULT ''                                 NULL,
+    deleted          smallint      DEFAULT 0                                  NOT NULL,
+    create_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_job
+    ADD CONSTRAINT pk_sj_job PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name);
+CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index);
+CREATE INDEX idx_sj_job_03 ON sj_job (create_dt);
+
+COMMENT ON COLUMN sj_job.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job.job_name IS '鍚嶇О';
+COMMENT ON COLUMN sj_job.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_job.args_type IS '鍙傛暟绫诲瀷 ';
+COMMENT ON COLUMN sj_job.next_trigger_at IS '涓嬫瑙﹀彂鏃堕棿';
+COMMENT ON COLUMN sj_job.job_status IS '浠诲姟鐘舵�� 0銆佸叧闂��1銆佸紑鍚�';
+COMMENT ON COLUMN sj_job.task_type IS '浠诲姟绫诲瀷 1銆侀泦缇� 2銆佸箍鎾� 3銆佸垏鐗�';
+COMMENT ON COLUMN sj_job.route_key IS '璺敱绛栫暐';
+COMMENT ON COLUMN sj_job.executor_type IS '鎵ц鍣ㄧ被鍨�';
+COMMENT ON COLUMN sj_job.executor_info IS '鎵ц鍣ㄥ悕绉�';
+COMMENT ON COLUMN sj_job.trigger_type IS '瑙﹀彂绫诲瀷 1.CRON 琛ㄨ揪寮� 2. 鍥哄畾鏃堕棿';
+COMMENT ON COLUMN sj_job.trigger_interval IS '闂撮殧鏃堕暱';
+COMMENT ON COLUMN sj_job.block_strategy IS '闃诲绛栫暐 1銆佷涪寮� 2銆佽鐩� 3銆佸苟琛�';
+COMMENT ON COLUMN sj_job.executor_timeout IS '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇';
+COMMENT ON COLUMN sj_job.max_retry_times IS '鏈�澶ч噸璇曟鏁�';
+COMMENT ON COLUMN sj_job.parallel_num IS '骞惰鏁�';
+COMMENT ON COLUMN sj_job.retry_interval IS '閲嶈瘯闂撮殧 ( s ) ';
+COMMENT ON COLUMN sj_job.bucket_index IS 'bucket';
+COMMENT ON COLUMN sj_job.resident IS '鏄惁鏄父椹讳换鍔�';
+COMMENT ON COLUMN sj_job.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_job.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_job.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_job.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_job.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_job IS '浠诲姟淇℃伅';
+
+INSERT INTO sj_job(namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', '', 0, sysdate, sysdate);
+
+-- sj_job_log_message
+CREATE TABLE sj_job_log_message
+(
+    id            number GENERATED ALWAYS AS IDENTITY,
+    namespace_id  varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name    varchar2(64)                                             NULL,
+    job_id        number                                                   NOT NULL,
+    task_batch_id number                                                   NOT NULL,
+    task_id       number                                                   NOT NULL,
+    message       clob                                                     NULL,
+    log_num       number        DEFAULT 1                                  NOT NULL,
+    real_time     number        DEFAULT 0                                  NOT NULL,
+    ext_attrs     varchar2(256) DEFAULT ''                                 NULL,
+    create_dt     date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_job_log_message
+    ADD CONSTRAINT pk_sj_job_log_message PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_job_log_message_01 ON sj_job_log_message (task_batch_id, task_id);
+CREATE INDEX idx_sj_job_log_message_02 ON sj_job_log_message (create_dt);
+CREATE INDEX idx_sj_job_log_message_03 ON sj_job_log_message (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_job_log_message.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job_log_message.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job_log_message.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job_log_message.job_id IS '浠诲姟淇℃伅id';
+COMMENT ON COLUMN sj_job_log_message.task_batch_id IS '浠诲姟鎵规id';
+COMMENT ON COLUMN sj_job_log_message.task_id IS '璋冨害浠诲姟id';
+COMMENT ON COLUMN sj_job_log_message.message IS '璋冨害淇℃伅';
+COMMENT ON COLUMN sj_job_log_message.log_num IS '鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_log_message.real_time IS '涓婃姤鏃堕棿';
+COMMENT ON COLUMN sj_job_log_message.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_job_log_message.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON TABLE sj_job_log_message IS '璋冨害鏃ュ織';
+
+-- sj_job_task
+CREATE TABLE sj_job_task
+(
+    id             number GENERATED ALWAYS AS IDENTITY,
+    namespace_id   varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name     varchar2(64)                                             NULL,
+    job_id         number                                                   NOT NULL,
+    task_batch_id  number                                                   NOT NULL,
+    parent_id      number        DEFAULT 0                                  NOT NULL,
+    task_status    smallint      DEFAULT 0                                  NOT NULL,
+    retry_count    number        DEFAULT 0                                  NOT NULL,
+    client_info    varchar2(128) DEFAULT NULL                               NULL,
+    result_message clob                                                     NULL,
+    args_str       clob          DEFAULT NULL                               NULL,
+    args_type      smallint      DEFAULT 1                                  NOT NULL,
+    ext_attrs      varchar2(256) DEFAULT ''                                 NULL,
+    create_dt      date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt      date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_job_task
+    ADD CONSTRAINT pk_sj_job_task PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_job_task_01 ON sj_job_task (task_batch_id, task_status);
+CREATE INDEX idx_sj_job_task_02 ON sj_job_task (create_dt);
+CREATE INDEX idx_sj_job_task_03 ON sj_job_task (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_job_task.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job_task.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job_task.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job_task.job_id IS '浠诲姟淇℃伅id';
+COMMENT ON COLUMN sj_job_task.task_batch_id IS '璋冨害浠诲姟id';
+COMMENT ON COLUMN sj_job_task.parent_id IS '鐖舵墽琛屽櫒id';
+COMMENT ON COLUMN sj_job_task.task_status IS '鎵ц鐨勭姸鎬� 0銆佸け璐� 1銆佹垚鍔�';
+COMMENT ON COLUMN sj_job_task.retry_count IS '閲嶈瘯娆℃暟';
+COMMENT ON COLUMN sj_job_task.client_info IS '瀹㈡埛绔湴鍧� clientId#ip:port';
+COMMENT ON COLUMN sj_job_task.result_message IS '鎵ц缁撴灉';
+COMMENT ON COLUMN sj_job_task.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_job_task.args_type IS '鍙傛暟绫诲瀷 ';
+COMMENT ON COLUMN sj_job_task.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_job_task.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_job_task.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_job_task IS '浠诲姟瀹炰緥';
+
+-- sj_job_task_batch
+CREATE TABLE sj_job_task_batch
+(
+    id                      number GENERATED ALWAYS AS IDENTITY,
+    namespace_id            varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name              varchar2(64)                                             NULL,
+    job_id                  number                                                   NOT NULL,
+    workflow_node_id        number        DEFAULT 0                                  NOT NULL,
+    parent_workflow_node_id number        DEFAULT 0                                  NOT NULL,
+    workflow_task_batch_id  number        DEFAULT 0                                  NOT NULL,
+    task_batch_status       smallint      DEFAULT 0                                  NOT NULL,
+    operation_reason        smallint      DEFAULT 0                                  NOT NULL,
+    execution_at            number        DEFAULT 0                                  NOT NULL,
+    system_task_type        smallint      DEFAULT 3                                  NOT NULL,
+    parent_id               varchar2(64)  DEFAULT ''                                 NULL,
+    ext_attrs               varchar2(256) DEFAULT ''                                 NULL,
+    deleted                 smallint      DEFAULT 0                                  NOT NULL,
+    create_dt               date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt               date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_job_task_batch
+    ADD CONSTRAINT pk_sj_job_task_batch PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_job_task_batch_01 ON sj_job_task_batch (job_id, task_batch_status);
+CREATE INDEX idx_sj_job_task_batch_02 ON sj_job_task_batch (create_dt);
+CREATE INDEX idx_sj_job_task_batch_03 ON sj_job_task_batch (namespace_id, group_name);
+CREATE INDEX idx_sj_job_task_batch_04 ON sj_job_task_batch (workflow_task_batch_id, workflow_node_id);
+
+COMMENT ON COLUMN sj_job_task_batch.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job_task_batch.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job_task_batch.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job_task_batch.job_id IS '浠诲姟id';
+COMMENT ON COLUMN sj_job_task_batch.workflow_node_id IS '宸ヤ綔娴佽妭鐐筰d';
+COMMENT ON COLUMN sj_job_task_batch.parent_workflow_node_id IS '宸ヤ綔娴佷换鍔$埗鎵规id';
+COMMENT ON COLUMN sj_job_task_batch.workflow_task_batch_id IS '宸ヤ綔娴佷换鍔℃壒娆d';
+COMMENT ON COLUMN sj_job_task_batch.task_batch_status IS '浠诲姟鎵规鐘舵�� 0銆佸け璐� 1銆佹垚鍔�';
+COMMENT ON COLUMN sj_job_task_batch.operation_reason IS '鎿嶄綔鍘熷洜';
+COMMENT ON COLUMN sj_job_task_batch.execution_at IS '浠诲姟鎵ц鏃堕棿';
+COMMENT ON COLUMN sj_job_task_batch.system_task_type IS '浠诲姟绫诲瀷 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟';
+COMMENT ON COLUMN sj_job_task_batch.parent_id IS '鐖惰妭鐐�';
+COMMENT ON COLUMN sj_job_task_batch.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_job_task_batch.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_job_task_batch.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_job_task_batch.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_job_task_batch IS '浠诲姟鎵规';
+
+-- sj_job_summary
+CREATE TABLE sj_job_summary
+(
+    id               number GENERATED ALWAYS AS IDENTITY,
+    namespace_id     varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name       varchar2(64)  DEFAULT ''                                 NULL,
+    business_id      number                                                   NOT NULL,
+    system_task_type smallint      DEFAULT 3                                  NOT NULL,
+    trigger_at       date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    success_num      number        DEFAULT 0                                  NOT NULL,
+    fail_num         number        DEFAULT 0                                  NOT NULL,
+    fail_reason      varchar2(512) DEFAULT ''                                 NULL,
+    stop_num         number        DEFAULT 0                                  NOT NULL,
+    stop_reason      varchar2(512) DEFAULT ''                                 NULL,
+    cancel_num       number        DEFAULT 0                                  NOT NULL,
+    cancel_reason    varchar2(512) DEFAULT ''                                 NULL,
+    create_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_job_summary
+    ADD CONSTRAINT pk_sj_job_summary PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_job_summary_01 ON sj_job_summary (trigger_at, system_task_type, business_id);
+
+CREATE INDEX idx_sj_job_summary_01 ON sj_job_summary (namespace_id, group_name, business_id);
+
+COMMENT ON COLUMN sj_job_summary.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job_summary.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job_summary.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job_summary.business_id IS '涓氬姟id  ( job_id鎴杦orkflow_id ) ';
+COMMENT ON COLUMN sj_job_summary.system_task_type IS '浠诲姟绫诲瀷 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟';
+COMMENT ON COLUMN sj_job_summary.trigger_at IS '缁熻鏃堕棿';
+COMMENT ON COLUMN sj_job_summary.success_num IS '鎵ц鎴愬姛-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_summary.fail_num IS '鎵ц澶辫触-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_summary.fail_reason IS '澶辫触鍘熷洜';
+COMMENT ON COLUMN sj_job_summary.stop_num IS '鎵ц澶辫触-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_summary.stop_reason IS '澶辫触鍘熷洜';
+COMMENT ON COLUMN sj_job_summary.cancel_num IS '鎵ц澶辫触-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_summary.cancel_reason IS '澶辫触鍘熷洜';
+COMMENT ON COLUMN sj_job_summary.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_job_summary.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_job_summary IS 'DashBoard_Job';
+
+-- sj_retry_summary
+CREATE TABLE sj_retry_summary
+(
+    id            number GENERATED ALWAYS AS IDENTITY,
+    namespace_id  varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name    varchar2(64) DEFAULT ''                                 NULL,
+    scene_name    varchar2(50) DEFAULT ''                                 NULL,
+    trigger_at    date         DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    running_num   number       DEFAULT 0                                  NOT NULL,
+    finish_num    number       DEFAULT 0                                  NOT NULL,
+    max_count_num number       DEFAULT 0                                  NOT NULL,
+    suspend_num   number       DEFAULT 0                                  NOT NULL,
+    create_dt     date         DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt     date         DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_retry_summary
+    ADD CONSTRAINT pk_sj_retry_summary PRIMARY KEY (id);
+
+CREATE UNIQUE INDEX uk_sj_retry_summary_01 ON sj_retry_summary (namespace_id, group_name, scene_name, trigger_at);
+
+CREATE INDEX idx_sj_retry_summary_01 ON sj_retry_summary (trigger_at);
+
+COMMENT ON COLUMN sj_retry_summary.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_summary.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_summary.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_summary.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_summary.trigger_at IS '缁熻鏃堕棿';
+COMMENT ON COLUMN sj_retry_summary.running_num IS '閲嶈瘯涓�-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_summary.finish_num IS '閲嶈瘯瀹屾垚-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_summary.max_count_num IS '閲嶈瘯鍒拌揪鏈�澶ф鏁�-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_summary.suspend_num IS '鏆傚仠閲嶈瘯-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_summary.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_retry_summary.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_retry_summary IS 'DashBoard_Retry';
+
+-- sj_workflow
+CREATE TABLE sj_workflow
+(
+    id               number GENERATED ALWAYS AS IDENTITY,
+    workflow_name    varchar2(64)                                             NULL,
+    namespace_id     varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name       varchar2(64)                                             NULL,
+    workflow_status  smallint      DEFAULT 1                                  NOT NULL,
+    trigger_type     smallint                                                 NOT NULL,
+    trigger_interval varchar2(255)                                            NULL,
+    next_trigger_at  number                                                   NOT NULL,
+    block_strategy   smallint      DEFAULT 1                                  NOT NULL,
+    executor_timeout number        DEFAULT 0                                  NOT NULL,
+    description      varchar2(256) DEFAULT ''                                 NULL,
+    flow_info        clob          DEFAULT NULL                               NULL,
+    bucket_index     number        DEFAULT 0                                  NOT NULL,
+    version          number                                                   NOT NULL,
+    ext_attrs        varchar2(256) DEFAULT ''                                 NULL,
+    deleted          smallint      DEFAULT 0                                  NOT NULL,
+    create_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt        date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_workflow
+    ADD CONSTRAINT pk_sj_workflow PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt);
+CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_workflow.id IS '涓婚敭';
+COMMENT ON COLUMN sj_workflow.workflow_name IS '宸ヤ綔娴佸悕绉�';
+COMMENT ON COLUMN sj_workflow.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_workflow.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_workflow.workflow_status IS '宸ヤ綔娴佺姸鎬� 0銆佸叧闂��1銆佸紑鍚�';
+COMMENT ON COLUMN sj_workflow.trigger_type IS '瑙﹀彂绫诲瀷 1.CRON 琛ㄨ揪寮� 2. 鍥哄畾鏃堕棿';
+COMMENT ON COLUMN sj_workflow.trigger_interval IS '闂撮殧鏃堕暱';
+COMMENT ON COLUMN sj_workflow.next_trigger_at IS '涓嬫瑙﹀彂鏃堕棿';
+COMMENT ON COLUMN sj_workflow.block_strategy IS '闃诲绛栫暐 1銆佷涪寮� 2銆佽鐩� 3銆佸苟琛�';
+COMMENT ON COLUMN sj_workflow.executor_timeout IS '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇';
+COMMENT ON COLUMN sj_workflow.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_workflow.flow_info IS '娴佺▼淇℃伅';
+COMMENT ON COLUMN sj_workflow.bucket_index IS 'bucket';
+COMMENT ON COLUMN sj_workflow.version IS '鐗堟湰鍙�';
+COMMENT ON COLUMN sj_workflow.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_workflow.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_workflow.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_workflow.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_workflow IS '宸ヤ綔娴�';
+
+-- sj_workflow_node
+CREATE TABLE sj_workflow_node
+(
+    id                   number GENERATED ALWAYS AS IDENTITY,
+    namespace_id         varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    node_name            varchar2(64)                                             NULL,
+    group_name           varchar2(64)                                             NULL,
+    job_id               number                                                   NOT NULL,
+    workflow_id          number                                                   NOT NULL,
+    node_type            smallint      DEFAULT 1                                  NOT NULL,
+    expression_type      smallint      DEFAULT 0                                  NOT NULL,
+    fail_strategy        smallint      DEFAULT 1                                  NOT NULL,
+    workflow_node_status smallint      DEFAULT 1                                  NOT NULL,
+    priority_level       number        DEFAULT 1                                  NOT NULL,
+    node_info            clob          DEFAULT NULL                               NULL,
+    version              number                                                   NOT NULL,
+    ext_attrs            varchar2(256) DEFAULT ''                                 NULL,
+    deleted              smallint      DEFAULT 0                                  NOT NULL,
+    create_dt            date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt            date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_workflow_node
+    ADD CONSTRAINT pk_sj_workflow_node PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_workflow_node_01 ON sj_workflow_node (create_dt);
+CREATE INDEX idx_sj_workflow_node_02 ON sj_workflow_node (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_workflow_node.id IS '涓婚敭';
+COMMENT ON COLUMN sj_workflow_node.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_workflow_node.node_name IS '鑺傜偣鍚嶇О';
+COMMENT ON COLUMN sj_workflow_node.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_workflow_node.job_id IS '浠诲姟淇℃伅id';
+COMMENT ON COLUMN sj_workflow_node.workflow_id IS '宸ヤ綔娴両D';
+COMMENT ON COLUMN sj_workflow_node.node_type IS '1銆佷换鍔¤妭鐐� 2銆佹潯浠惰妭鐐�';
+COMMENT ON COLUMN sj_workflow_node.expression_type IS '1銆丼pEl銆�2銆丄viator 3銆丵L';
+COMMENT ON COLUMN sj_workflow_node.fail_strategy IS '澶辫触绛栫暐 1銆佽烦杩� 2銆侀樆濉�';
+COMMENT ON COLUMN sj_workflow_node.workflow_node_status IS '宸ヤ綔娴佽妭鐐圭姸鎬� 0銆佸叧闂��1銆佸紑鍚�';
+COMMENT ON COLUMN sj_workflow_node.priority_level IS '浼樺厛绾�';
+COMMENT ON COLUMN sj_workflow_node.node_info IS '鑺傜偣淇℃伅 ';
+COMMENT ON COLUMN sj_workflow_node.version IS '鐗堟湰鍙�';
+COMMENT ON COLUMN sj_workflow_node.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_workflow_node.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_workflow_node.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_workflow_node.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_workflow_node IS '宸ヤ綔娴佽妭鐐�';
+
+-- sj_workflow_task_batch
+CREATE TABLE sj_workflow_task_batch
+(
+    id                number GENERATED ALWAYS AS IDENTITY,
+    namespace_id      varchar2(64)  DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
+    group_name        varchar2(64)                                             NULL,
+    workflow_id       number                                                   NOT NULL,
+    task_batch_status smallint      DEFAULT 0                                  NOT NULL,
+    operation_reason  smallint      DEFAULT 0                                  NOT NULL,
+    flow_info         clob          DEFAULT NULL                               NULL,
+    execution_at      number        DEFAULT 0                                  NOT NULL,
+    ext_attrs         varchar2(256) DEFAULT ''                                 NULL,
+    deleted           smallint      DEFAULT 0                                  NOT NULL,
+    create_dt         date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL,
+    update_dt         date          DEFAULT CURRENT_TIMESTAMP                  NOT NULL
+);
+
+ALTER TABLE sj_workflow_task_batch
+    ADD CONSTRAINT pk_sj_workflow_task_batch PRIMARY KEY (id);
+
+CREATE INDEX idx_sj_workflow_task_batch_01 ON sj_workflow_task_batch (workflow_id, task_batch_status);
+CREATE INDEX idx_sj_workflow_task_batch_02 ON sj_workflow_task_batch (create_dt);
+CREATE INDEX idx_sj_workflow_task_batch_03 ON sj_workflow_task_batch (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_workflow_task_batch.id IS '涓婚敭';
+COMMENT ON COLUMN sj_workflow_task_batch.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_workflow_task_batch.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_workflow_task_batch.workflow_id IS '宸ヤ綔娴佷换鍔d';
+COMMENT ON COLUMN sj_workflow_task_batch.task_batch_status IS '浠诲姟鎵规鐘舵�� 0銆佸け璐� 1銆佹垚鍔�';
+COMMENT ON COLUMN sj_workflow_task_batch.operation_reason IS '鎿嶄綔鍘熷洜';
+COMMENT ON COLUMN sj_workflow_task_batch.flow_info IS '娴佺▼淇℃伅';
+COMMENT ON COLUMN sj_workflow_task_batch.execution_at IS '浠诲姟鎵ц鏃堕棿';
+COMMENT ON COLUMN sj_workflow_task_batch.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_workflow_task_batch.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_workflow_task_batch.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_workflow_task_batch.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_workflow_task_batch IS '宸ヤ綔娴佹壒娆�';
+
diff --git a/script/sql/postgres/flowable.sql b/script/sql/postgres/flowable.sql
new file mode 100644
index 0000000..eff4eeb
--- /dev/null
+++ b/script/sql/postgres/flowable.sql
@@ -0,0 +1,341 @@
+insert into sys_menu values('11616', '宸ヤ綔娴�'  , '0',    '6', 'workflow',          '',                                 '', '1', '0', 'M', '0', '0', '',                       'workflow', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11617', '妯″瀷绠$悊', '11616', '2', 'model',             'workflow/model/index',             '', '1', '1', 'C', '0', '0', 'workflow:model:list',    'model', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11618', '鎴戠殑浠诲姟', '0', '7', 'task',              '',                                 '', '1', '0', 'M', '0', '0', '',                       'my-task', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11619', '鎴戠殑寰呭姙', '11618', '2', 'taskWaiting',       'workflow/task/taskWaiting',              '', '1', '1', 'C', '0', '0', '',                       'waiting', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11632', '鎴戠殑宸插姙', '11618', '3', 'taskFinish',       'workflow/task/taskFinish',              '', '1', '1', 'C', '0', '0', '',                       'finish', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11633', '鎴戠殑鎶勯��', '11618', '4', 'taskCopyList',       'workflow/task/taskCopyList',              '', '1', '1', 'C', '0', '0', '',                       'my-copy', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11620', '娴佺▼瀹氫箟', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '',                       'process-definition', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11621', '娴佺▼瀹炰緥', '11630', '1', 'processInstance',   'workflow/processInstance/index',   '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11622', '娴佺▼鍒嗙被', '11616', '1', 'category',          'workflow/category/index',          '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11629', '鎴戝彂璧风殑', '11618', '1', 'myDocument',        'workflow/task/myDocument',         '', '1', '1', 'C', '0', '0', '',                       'guide', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11630', '娴佺▼鐩戞帶', '11616', '4', 'monitor',           '',                                 '', '1', '0', 'M', '0', '0', '',                       'monitor', 103, 1, now(), NULL, NULL, '');
+insert into sys_menu values('11631', '寰呭姙浠诲姟', '11630', '2', 'allTaskWaiting',    'workflow/task/allTaskWaiting',     '', '1', '1', 'C', '0', '0', '',                       'waiting', 103, 1, now(), NULL, NULL, '');
+
+
+-- 娴佺▼鍒嗙被绠$悊鐩稿叧鎸夐挳
+insert into sys_menu values ('11623', '娴佺▼鍒嗙被鏌ヨ', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, now(), null, null, '');
+insert into sys_menu values ('11624', '娴佺▼鍒嗙被鏂板', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add',   '#', 103, 1, now(), null, null, '');
+insert into sys_menu values ('11625', '娴佺▼鍒嗙被淇敼', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit',  '#', 103, 1, now(), null, null, '');
+insert into sys_menu values ('11626', '娴佺▼鍒嗙被鍒犻櫎', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, now(), null, null, '');
+insert into sys_menu values ('11627', '娴佺▼鍒嗙被瀵煎嚭', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, now(), null, null, '');
+-- 璇峰亣鍗曚俊鎭�
+create table test_leave
+(
+    id          bigint not null
+        constraint test_leave_pk
+            primary key,
+    leave_type  varchar(255),
+    start_date  timestamp,
+    end_date    timestamp,
+    leave_days  bigint,
+    remark      varchar(255),
+    create_dept bigint,
+    create_by   bigint,
+    create_time timestamp,
+    update_by   bigint,
+    update_time timestamp,
+    tenant_id   varchar(20)
+);
+
+comment on table test_leave is '璇峰亣鐢宠琛�';
+
+comment on column test_leave.id is '涓婚敭';
+
+comment on column test_leave.leave_type is '璇峰亣绫诲瀷';
+
+comment on column test_leave.start_date is '寮�濮嬫椂闂�';
+
+comment on column test_leave.end_date is '缁撴潫鏃堕棿';
+
+comment on column test_leave.remark is '璇峰亣鍘熷洜';
+
+comment on column test_leave.create_dept is '鍒涘缓閮ㄩ棬';
+
+comment on column test_leave.create_by is '鍒涘缓鑰�';
+
+comment on column test_leave.create_time is '鍒涘缓鏃堕棿';
+
+comment on column test_leave.update_by is '鏇存柊鑰�';
+
+comment on column test_leave.update_time is '鏇存柊鏃堕棿';
+
+comment on column test_leave.tenant_id is '绉熸埛缂栫爜';
+
+alter table test_leave
+    owner to postgres;
+
+-- 娴佺▼鍒嗙被淇℃伅琛�
+create table wf_category
+(
+    id            bigint not null
+        constraint wf_category_pk
+            primary key,
+    category_name varchar(255),
+    category_code varchar(255),
+    parent_id     bigint,
+    sort_num      bigint,
+    tenant_id     varchar(20),
+    create_dept   bigint,
+    create_by     bigint,
+    create_time   timestamp,
+    update_by     bigint,
+    update_time   timestamp
+);
+
+comment on table wf_category is '娴佺▼鍒嗙被';
+
+comment on column wf_category.id is '涓婚敭';
+
+comment on column wf_category.category_name is '鍒嗙被鍚嶇О';
+
+comment on column wf_category.category_code is '鍒嗙被缂栫爜';
+
+comment on column wf_category.parent_id is '鐖剁骇id';
+
+comment on column wf_category.sort_num is '鎺掑簭';
+
+comment on column wf_category.tenant_id is '绉熸埛id';
+
+comment on column wf_category.create_dept is '鍒涘缓閮ㄩ棬';
+
+comment on column wf_category.create_by is '鍒涘缓鑰�';
+
+comment on column wf_category.create_time is '鍒涘缓鏃堕棿';
+
+comment on column wf_category.update_by is '淇敼鑰�';
+
+comment on column wf_category.update_time is '淇敼鏃堕棿';
+
+alter table wf_category
+    owner to postgres;
+
+create unique index uni_category_code
+    on wf_category (category_code);
+
+INSERT INTO wf_category values (1, 'OA', 'OA', 0, 0, '000000', 103, 1, now(), 1, now());
+
+create table wf_task_back_node
+(
+    id            bigint not null
+        constraint pk_wf_task_back_node
+        primary key,
+    node_id       varchar(255) not null,
+    node_name     varchar(255) not null,
+    order_no      bigint not null,
+    instance_id   varchar(255) not null,
+    task_type     varchar(255) not null,
+    assignee      varchar(2000) not null,
+    tenant_id     varchar(20),
+    create_dept   bigint,
+    create_by     bigint,
+    create_time   timestamp,
+    update_by     bigint,
+    update_time   timestamp
+);
+
+comment on table wf_task_back_node is '鑺傜偣瀹℃壒璁板綍';
+
+comment on column wf_task_back_node.id is '涓婚敭';
+
+comment on column wf_task_back_node.node_id is '鑺傜偣id';
+
+comment on column wf_task_back_node.node_name is '鑺傜偣鍚嶇О';
+
+comment on column wf_task_back_node.order_no is '鎺掑簭';
+
+comment on column wf_task_back_node.instance_id is '娴佺▼瀹炰緥id';
+
+comment on column wf_task_back_node.task_type is '鑺傜偣绫诲瀷';
+
+comment on column wf_task_back_node.assignee is '瀹℃壒浜�';
+
+comment on column wf_task_back_node.tenant_id is '绉熸埛id';
+
+comment on column wf_task_back_node.create_dept is '鍒涘缓閮ㄩ棬';
+
+comment on column wf_task_back_node.create_by is '鍒涘缓鑰�';
+
+comment on column wf_task_back_node.create_time is '鍒涘缓鏃堕棿';
+
+comment on column wf_task_back_node.update_by is '淇敼鑰�';
+
+comment on column wf_task_back_node.update_time is '淇敼鏃堕棿';
+
+alter table wf_task_back_node
+    owner to postgres;
+
+create table wf_definition_config
+(
+    id            bigint not null
+        constraint pk_wf_definition_config
+        primary key,
+    table_name    varchar(255) not null,
+    definition_id varchar(255) not null,
+    process_key   varchar(255) not null,
+    version       bigint       not null,
+    tenant_id     varchar(20),
+    create_dept   bigint,
+    create_by     bigint,
+    create_time   timestamp,
+    update_by     bigint,
+    update_time   timestamp
+);
+
+comment on table wf_definition_config is '娴佺▼瀹氫箟閰嶇疆';
+
+comment on column wf_definition_config.id is '涓婚敭';
+
+comment on column wf_definition_config.table_name is '琛ㄥ悕';
+
+comment on column wf_definition_config.definition_id is '娴佺▼瀹氫箟ID';
+
+comment on column wf_definition_config.process_key is '娴佺▼KEY';
+
+comment on column wf_definition_config.version is '娴佺▼鐗堟湰';
+
+comment on column wf_definition_config.tenant_id is '绉熸埛id';
+
+comment on column wf_definition_config.create_dept is '鍒涘缓閮ㄩ棬';
+
+comment on column wf_definition_config.create_by is '鍒涘缓鑰�';
+
+comment on column wf_definition_config.create_time is '鍒涘缓鏃堕棿';
+
+comment on column wf_definition_config.update_by is '淇敼鑰�';
+
+comment on column wf_definition_config.update_time is '淇敼鏃堕棿';
+
+alter table wf_definition_config
+    owner to postgres;
+create unique index uni_definition_id
+    on wf_definition_config (definition_id);
+
+create table wf_form_manage
+(
+    id            bigint not null
+        constraint pk_wf_form_manage
+        primary key,
+    form_name     varchar(255) not null,
+    form_type     varchar(255) not null,
+    router        varchar(255) not null,
+    remark        varchar(500),
+    tenant_id     varchar(20),
+    create_dept   bigint,
+    create_by     bigint,
+    create_time   timestamp,
+    update_by     bigint,
+    update_time   timestamp
+);
+
+comment on table wf_form_manage is '琛ㄥ崟绠$悊';
+
+comment on column wf_form_manage.id is '涓婚敭';
+
+comment on column wf_form_manage.form_name is '琛ㄥ崟鍚嶇О';
+
+comment on column wf_form_manage.form_type is '琛ㄥ崟绫诲瀷';
+
+comment on column wf_form_manage.router is '璺敱鍦板潃/琛ㄥ崟ID';
+
+comment on column wf_form_manage.remark is '澶囨敞';
+
+comment on column wf_form_manage.tenant_id is '绉熸埛id';
+
+comment on column wf_form_manage.create_dept is '鍒涘缓閮ㄩ棬';
+
+comment on column wf_form_manage.create_by is '鍒涘缓鑰�';
+
+comment on column wf_form_manage.create_time is '鍒涘缓鏃堕棿';
+
+comment on column wf_form_manage.update_by is '淇敼鑰�';
+
+comment on column wf_form_manage.update_time is '淇敼鏃堕棿';
+
+insert into wf_form_manage(id, form_name, form_type, router, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time) VALUES (1, '璇峰亣鐢宠', 'static', '/demo/leaveEdit/index', NULL, '000000', 103, 1, now(), 1, now());
+
+create table wf_node_config
+(
+    id               bigint not null
+        constraint pk_wf_node_config
+            primary key,
+    form_id          bigint,
+    form_type        varchar(255),
+    node_name        varchar(255) not null,
+    node_id          varchar(255) not null,
+    definition_id    varchar(255) not null,
+    apply_user_task  char(1) default '0',
+    tenant_id        varchar(20),
+    create_dept      bigint,
+    create_by        bigint,
+    create_time      timestamp,
+    update_by        bigint,
+    update_time      timestamp
+);
+
+comment on table wf_node_config is '鑺傜偣閰嶇疆';
+
+comment on column wf_node_config.id is '涓婚敭';
+
+comment on column wf_node_config.form_id is '琛ㄥ崟id';
+
+comment on column wf_node_config.form_type is '琛ㄥ崟绫诲瀷';
+
+comment on column wf_node_config.node_id is '鑺傜偣id';
+
+comment on column wf_node_config.node_name is '鑺傜偣鍚嶇О';
+
+comment on column wf_node_config.definition_id is '娴佺▼瀹氫箟id';
+
+comment on column wf_node_config.apply_user_task is '鏄惁涓虹敵璇蜂汉鑺傜偣 锛�0鏄� 1鍚︼級';
+
+comment on column wf_node_config.tenant_id is '绉熸埛id';
+
+comment on column wf_node_config.create_dept is '鍒涘缓閮ㄩ棬';
+
+comment on column wf_node_config.create_by is '鍒涘缓鑰�';
+
+comment on column wf_node_config.create_time is '鍒涘缓鏃堕棿';
+
+comment on column wf_node_config.update_by is '淇敼鑰�';
+
+comment on column wf_node_config.update_time is '淇敼鏃堕棿';
+
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '璇峰亣鐢宠', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, now(), NULL, NULL, '璇峰亣鐢宠鑿滃崟');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '璇峰亣鐢宠鏌ヨ', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '璇峰亣鐢宠鏂板', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:add', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '璇峰亣鐢宠淇敼', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:edit', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '璇峰亣鐢宠鍒犻櫎', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:remove', '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '璇峰亣鐢宠瀵煎嚭', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:export', '#', 103, 1, now(), NULL, NULL, '');
+
+INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (13, '000000', '涓氬姟鐘舵��', 'wf_business_status', 103, 1, now(), NULL, NULL, '涓氬姟鐘舵�佸垪琛�');
+INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (14, '000000', '琛ㄥ崟绫诲瀷', 'wf_form_type', 103, 1, now(), NULL, NULL, '琛ㄥ崟绫诲瀷鍒楄〃');
+
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (39, '000000', 1, '宸叉挙閿�', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '宸叉挙閿�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (40, '000000', 2, '鑽夌', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, now(), NULL, NULL, '鑽夌');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (41, '000000', 3, '寰呭鏍�', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1,now(), NULL, NULL, '寰呭鏍�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (42, '000000', 4, '宸插畬鎴�', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, now(), NULL, NULL, '宸插畬鎴�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (43, '000000', 5, '宸蹭綔搴�', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '宸蹭綔搴�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (44, '000000', 6, '宸查��鍥�', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '宸查��鍥�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (45, '000000', 7, '宸茬粓姝�', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1,now(), NULL, NULL, '宸茬粓姝�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (46, '000000', 1, '鑷畾涔夎〃鍗�', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, now(), NULL, NULL, '鑷畾涔夎〃鍗�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (47, '000000', 2, '鍔ㄦ�佽〃鍗�', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '鍔ㄦ�佽〃鍗�');
+
+-- 琛ㄥ崟绠$悊 SQL
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11628, '琛ㄥ崟绠$悊', '11616', '5', 'formManage', 'workflow/formManage/index', 1, 0, 'C', '0', '0', 'workflow:formManage:list', 'tree-table', 103, 1, now(), null, null, '琛ㄥ崟绠$悊鑿滃崟');
+
+-- 琛ㄥ崟绠$悊鎸夐挳 SQL
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11644, '琛ㄥ崟绠$悊鏌ヨ', 11628, '1',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:query',        '', 103, 1, now(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11645, '琛ㄥ崟绠$悊鏂板', 11628, '2',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:add',          '', 103, 1, now(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11646, '琛ㄥ崟绠$悊淇敼', 11628, '3',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:edit',         '', 103, 1, now(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11647, '琛ㄥ崟绠$悊鍒犻櫎', 11628, '4',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:remove',       '', 103, 1, now(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11648, '琛ㄥ崟绠$悊瀵煎嚭', 11628, '5',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:export',       'tree-table', 103, 1, now(), null, null, '');
diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql
index a6d6f1a..9b2169e 100644
--- a/script/sql/postgres/postgres_ry_vue_5.X.sql
+++ b/script/sql/postgres/postgres_ry_vue_5.X.sql
@@ -20,7 +20,7 @@
     union_id           varchar(255)     default null::varchar,
     scope              varchar(255)     default null::varchar,
     token_type         varchar(255)     default null::varchar,
-    id_token           varchar(255)     default null::varchar,
+    id_token           varchar(2000)    default null::varchar,
     mac_algorithm      varchar(255)     default null::varchar,
     mac_key            varchar(255)     default null::varchar,
     code               varchar(255)     default null::varchar,
@@ -69,7 +69,6 @@
 -- ----------------------------
 -- 绉熸埛琛�
 -- ----------------------------
-drop table if exists sys_tenant;
 create table if not exists sys_tenant
 (
     id                int8,
@@ -128,7 +127,6 @@
 -- ----------------------------
 -- 绉熸埛濂楅琛�
 -- ----------------------------
-drop table if exists sys_tenant_package;
 create table if not exists sys_tenant_package
 (
     package_id          int8,
@@ -164,7 +162,6 @@
 -- ----------------------------
 -- 1銆侀儴闂ㄨ〃
 -- ----------------------------
-drop table if exists sys_dept;
 create table if not exists sys_dept
 (
     dept_id     int8,
@@ -172,6 +169,7 @@
     parent_id   int8        default 0,
     ancestors   varchar(500)default ''::varchar,
     dept_name   varchar(30) default ''::varchar,
+    dept_category varchar(100) default null::varchar,
     order_num   int4        default 0,
     leader      int8        default null,
     phone       varchar(11) default null::varchar,
@@ -192,6 +190,7 @@
 comment on column sys_dept.parent_id    is '鐖堕儴闂↖D';
 comment on column sys_dept.ancestors    is '绁栫骇鍒楄〃';
 comment on column sys_dept.dept_name    is '閮ㄩ棬鍚嶇О';
+comment on column sys_dept.dept_category    is '閮ㄩ棬绫诲埆缂栫爜';
 comment on column sys_dept.order_num    is '鏄剧ず椤哄簭';
 comment on column sys_dept.leader       is '璐熻矗浜�';
 comment on column sys_dept.phone        is '鑱旂郴鐢佃瘽';
@@ -207,21 +206,20 @@
 -- ----------------------------
 -- 鍒濆鍖�-閮ㄩ棬琛ㄦ暟鎹�
 -- ----------------------------
-insert into sys_dept values(100, '000000', 0,   '0',          'XXX绉戞妧',   0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(101, '000000', 100, '0,100',      '娣卞湷鎬诲叕鍙�', 1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(102, '000000', 100, '0,100',      '闀挎矙鍒嗗叕鍙�', 2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(103, '000000', 101, '0,100,101',  '鐮斿彂閮ㄩ棬',   1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(104, '000000', 101, '0,100,101',  '甯傚満閮ㄩ棬',   2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(105, '000000', 101, '0,100,101',  '娴嬭瘯閮ㄩ棬',   3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(106, '000000', 101, '0,100,101',  '璐㈠姟閮ㄩ棬',   4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(107, '000000', 101, '0,100,101',  '杩愮淮閮ㄩ棬',   5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(108, '000000', 102, '0,100,102',  '甯傚満閮ㄩ棬',   1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
-insert into sys_dept values(109, '000000', 102, '0,100,102',  '璐㈠姟閮ㄩ棬',   2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(100, '000000', 0,   '0',          'XXX绉戞妧',   null,0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(101, '000000', 100, '0,100',      '娣卞湷鎬诲叕鍙�', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(102, '000000', 100, '0,100',      '闀挎矙鍒嗗叕鍙�', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(103, '000000', 101, '0,100,101',  '鐮斿彂閮ㄩ棬',   null,1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(104, '000000', 101, '0,100,101',  '甯傚満閮ㄩ棬',   null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(105, '000000', 101, '0,100,101',  '娴嬭瘯閮ㄩ棬',   null,3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(106, '000000', 101, '0,100,101',  '璐㈠姟閮ㄩ棬',   null,4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(107, '000000', 101, '0,100,101',  '杩愮淮閮ㄩ棬',   null,5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(108, '000000', 102, '0,100,102',  '甯傚満閮ㄩ棬',   null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
+insert into sys_dept values(109, '000000', 102, '0,100,102',  '璐㈠姟閮ㄩ棬',   null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, now(), null, null);
 
 -- ----------------------------
 -- 2銆佺敤鎴蜂俊鎭〃
 -- ----------------------------
-drop table if exists sys_user;
 create table if not exists sys_user
 (
     user_id     int8,
@@ -276,18 +274,19 @@
 -- 鍒濆鍖�-鐢ㄦ埛淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
 insert into sys_user values(1, '000000', 103, 'admin', '鐤媯鐨勭嫯瀛怢i', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 103, 1, now(), null, null, '绠$悊鍛�');
-insert into sys_user values(2, '000000', 105, 'lionli', '鐤媯鐨勭嫯瀛怢i', 'sys_user', 'crazyLionLi@qq.com',  '15666666666', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', now(), 103, 1, now(), null, null, '娴嬭瘯鍛�');
-
+insert into sys_user VALUES(3, '000000', 108, 'test', '鏈儴闂ㄥ強浠ヤ笅 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 103, 1, now(), 3, now(), NULL);
+insert into sys_user VALUES(4, '000000', 102, 'test1', '浠呮湰浜� 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 103, 1, now(), 4, now(), NULL);
 
 -- ----------------------------
 -- 3銆佸矖浣嶄俊鎭〃
 -- ----------------------------
-drop table if exists sys_post;
 create table if not exists sys_post
 (
     post_id     int8,
     tenant_id   varchar(20) default '000000'::varchar,
+    dept_id     int8,
     post_code   varchar(64) not null,
+    post_category   varchar(100) default null,
     post_name   varchar(50) not null,
     post_sort   int4        not null,
     status      char        not null,
@@ -303,7 +302,9 @@
 comment on table sys_post               is '宀椾綅淇℃伅琛�';
 comment on column sys_post.post_id      is '宀椾綅ID';
 comment on column sys_post.tenant_id    is '绉熸埛缂栧彿';
+comment on column sys_post.dept_id      is '閮ㄩ棬id';
 comment on column sys_post.post_code    is '宀椾綅缂栫爜';
+comment on column sys_post.post_category is '宀椾綅绫诲埆缂栫爜';
 comment on column sys_post.post_name    is '宀椾綅鍚嶇О';
 comment on column sys_post.post_sort    is '鏄剧ず椤哄簭';
 comment on column sys_post.status       is '鐘舵�侊紙0姝e父 1鍋滅敤锛�';
@@ -317,15 +318,14 @@
 -- ----------------------------
 -- 鍒濆鍖�-宀椾綅淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
-insert into sys_post values(1, '000000', 'ceo',  '钁d簨闀�',    1, '0', 103, 1, now(), null, null, '');
-insert into sys_post values(2, '000000', 'se',   '椤圭洰缁忕悊',  2, '0', 103, 1, now(), null, null, '');
-insert into sys_post values(3, '000000', 'hr',   '浜哄姏璧勬簮',  3, '0', 103, 1, now(), null, null, '');
-insert into sys_post values(4, '000000', 'user', '鏅�氬憳宸�',  4, '0', 103, 1, now(), null, null, '');
+insert into sys_post values(1, '000000', 103, 'ceo',  null, '钁d簨闀�',    1, '0', 103, 1, now(), null, null, '');
+insert into sys_post values(2, '000000', 100, 'se',   null, '椤圭洰缁忕悊',  2, '0', 103, 1, now(), null, null, '');
+insert into sys_post values(3, '000000', 100, 'hr',   null, '浜哄姏璧勬簮',  3, '0', 103, 1, now(), null, null, '');
+insert into sys_post values(4, '000000', 100, 'user', null, '鏅�氬憳宸�',  4, '0', 103, 1, now(), null, null, '');
 
 -- ----------------------------
 -- 4銆佽鑹蹭俊鎭〃
 -- ----------------------------
-drop table if exists sys_role;
 create table if not exists sys_role
 (
     role_id             int8,
@@ -369,13 +369,12 @@
 -- 鍒濆鍖�-瑙掕壊淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
 insert into sys_role values('1', '000000', '瓒呯骇绠$悊鍛�',  'superadmin',  1, '1', 't', 't', '0', '0', 103, 1, now(), null, null, '瓒呯骇绠$悊鍛�');
-insert into sys_role values('2', '000000', '鏅�氳鑹�',    'common', 2, '2', 't', 't', '0', '0', 103, 1, now(), null, null, '鏅�氳鑹�');
-
+insert into sys_role values('3', '000000', '鏈儴闂ㄥ強浠ヤ笅', 'test1', 3, '4', 't', 't', '0', '0', 103, 1, now(), NULL, NULL, '');
+insert into sys_role values('4', '000000', '浠呮湰浜�', 'test2', 4, '5', 't', 't', '0', '0', 103, 1, now(), NULL, NULL, '');
 
 -- ----------------------------
 -- 5銆佽彍鍗曟潈闄愯〃
 -- ----------------------------
-drop table if exists sys_menu;
 create table if not exists sys_menu
 (
     menu_id     int8,
@@ -432,6 +431,7 @@
 insert into sys_menu values('2', '绯荤粺鐩戞帶', '0', '3', 'monitor',          null, '', '1', '0', 'M', '0', '0', '', 'monitor',  103, 1, now(), null, null, '绯荤粺鐩戞帶鐩綍');
 insert into sys_menu values('3', '绯荤粺宸ュ叿', '0', '4', 'tool',             null, '', '1', '0', 'M', '0', '0', '', 'tool',     103, 1, now(), null, null, '绯荤粺宸ュ叿鐩綍');
 insert into sys_menu values('4', 'PLUS瀹樼綉', '0', '5', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', '0', '0', 'M', '0', '0', '', 'guide',    103, 1, now(), null, null, 'RuoYi-Vue-Plus瀹樼綉鍦板潃');
+insert into sys_menu VALUES('5', '娴嬭瘯鑿滃崟', '0', '5', 'demo',             null, '', '1', '0', 'M', '0', '0', null, 'star',       103, 1, now(), null, null, '娴嬭瘯鑿滃崟');
 -- 浜岀骇鑿滃崟
 insert into sys_menu values('100',  '鐢ㄦ埛绠$悊',     '1',   '1', 'user',             'system/user/index',            '', '1', '0', 'C', '0', '0', 'system:user:list',            'user',          103, 1, now(), null, null, '鐢ㄦ埛绠$悊鑿滃崟');
 insert into sys_menu values('101',  '瑙掕壊绠$悊',     '1',   '2', 'role',             'system/role/index',            '', '1', '0', 'C', '0', '0', 'system:role:list',            'peoples',       103, 1, now(), null, null, '瑙掕壊绠$悊鑿滃崟');
@@ -454,8 +454,8 @@
 insert into sys_menu values('117',  'Admin鐩戞帶',   '2',   '5',  'Admin',            'monitor/admin/index',         '', '1', '0', 'C', '0', '0', 'monitor:admin:list',          'dashboard',     103, 1, now(), null, null, 'Admin鐩戞帶鑿滃崟');
 -- oss鑿滃崟
 insert into sys_menu values('118',  '鏂囦欢绠$悊',     '1',   '10', 'oss',              'system/oss/index',            '', '1', '0', 'C', '0', '0', 'system:oss:list',             'upload',        103, 1, now(), null, null, '鏂囦欢绠$悊鑿滃崟');
--- powerjob server鎺у埗鍙�
-insert into sys_menu values('120',  '浠诲姟璋冨害涓績',  '2',   '5',  'powerjob',           'monitor/powerjob/index',        '', '1', '0', 'C', '0', '0', 'monitor:powerjob:list',         'job',           103, 1, now(), null, null, 'PowerJob鎺у埗鍙拌彍鍗�');
+-- snail-job server鎺у埗鍙�
+insert into sys_menu values('120',  '浠诲姟璋冨害涓績',  '2',   '6',  'snailjob',     'monitor/snailjob/index',    '', '1', '0', 'C', '0', '0', 'monitor:snailjob:list',          'job',           103, 1, now(), null, null, 'SnailJob鎺у埗鍙拌彍鍗�');
 
 -- 涓夌骇鑿滃崟
 insert into sys_menu values('500',  '鎿嶄綔鏃ュ織', '108', '1', 'operlog',    'monitor/operlog/index',    '', '1', '0', 'C', '0', '0', 'monitor:operlog:list',    'form',          103, 1, now(), null, null, '鎿嶄綔鏃ュ織鑿滃崟');
@@ -554,11 +554,24 @@
 insert into sys_menu values('1063', '瀹㈡埛绔鐞嗕慨鏀�', '123', '3',  '#', '', '', '1', '0', 'F', '0', '0', 'system:client:edit',         '#', 103, 1, now(), null, null, '');
 insert into sys_menu values('1064', '瀹㈡埛绔鐞嗗垹闄�', '123', '4',  '#', '', '', '1', '0', 'F', '0', '0', 'system:client:remove',       '#', 103, 1, now(), null, null, '');
 insert into sys_menu values('1065', '瀹㈡埛绔鐞嗗鍑�', '123', '5',  '#', '', '', '1', '0', 'F', '0', '0', 'system:client:export',       '#', 103, 1, now(), null, null, '');
+-- 娴嬭瘯鑿滃崟
+INSERT INTO sys_menu VALUES('1500', '娴嬭瘯鍗曡〃',     '5',   '1', 'demo', 'demo/demo/index', '',  '1', '0', 'C', '0', '0', 'demo:demo:list', '#', 103, 1, now(), NULL, NULL, '娴嬭瘯鍗曡〃鑿滃崟');
+INSERT INTO sys_menu VALUES('1501', '娴嬭瘯鍗曡〃鏌ヨ', '1500', '1', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:demo:query',                  '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1502', '娴嬭瘯鍗曡〃鏂板', '1500', '2', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:demo:add',                    '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1503', '娴嬭瘯鍗曡〃淇敼', '1500', '3', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:demo:edit',                   '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1504', '娴嬭瘯鍗曡〃鍒犻櫎', '1500', '4', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:demo:remove',                 '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1505', '娴嬭瘯鍗曡〃瀵煎嚭', '1500', '5', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:demo:export',                 '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1506', '娴嬭瘯鏍戣〃',     '5',   '1', 'tree', 'demo/tree/index', '',  '1', '0', 'C', '0', '0', 'demo:tree:list', '#', 103, 1, now(), NULL, NULL, '娴嬭瘯鏍戣〃鑿滃崟');
+INSERT INTO sys_menu VALUES('1507', '娴嬭瘯鏍戣〃鏌ヨ', '1506', '1', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:tree:query',                  '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1508', '娴嬭瘯鏍戣〃鏂板', '1506', '2', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:tree:add',                    '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1509', '娴嬭瘯鏍戣〃淇敼', '1506', '3', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:tree:edit',                   '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1510', '娴嬭瘯鏍戣〃鍒犻櫎', '1506', '4', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:tree:remove',                 '#', 103, 1, now(), NULL, NULL, '');
+INSERT INTO sys_menu VALUES('1511', '娴嬭瘯鏍戣〃瀵煎嚭', '1506', '5', '#', '', '',  '1', '0', 'F', '0', '0', 'demo:tree:export',                 '#', 103, 1, now(), NULL, NULL, '');
+
 
 -- ----------------------------
 -- 6銆佺敤鎴峰拰瑙掕壊鍏宠仈琛�  鐢ㄦ埛N-1瑙掕壊
 -- ----------------------------
-drop table if exists sys_user_role;
 create table if not exists sys_user_role
 (
     user_id int8 not null,
@@ -574,13 +587,12 @@
 -- 鍒濆鍖�-鐢ㄦ埛鍜岃鑹插叧鑱旇〃鏁版嵁
 -- ----------------------------
 insert into sys_user_role values ('1', '1');
-insert into sys_user_role values ('2', '2');
-
+insert into sys_user_role values ('3', '3');
+insert into sys_user_role values ('4', '4');
 
 -- ----------------------------
 -- 7銆佽鑹插拰鑿滃崟鍏宠仈琛�  瑙掕壊1-N鑿滃崟
 -- ----------------------------
-drop table if exists sys_role_menu;
 create table if not exists sys_role_menu
 (
     role_id int8 not null,
@@ -595,95 +607,93 @@
 -- ----------------------------
 -- 鍒濆鍖�-瑙掕壊鍜岃彍鍗曞叧鑱旇〃鏁版嵁
 -- ----------------------------
-insert into sys_role_menu values ('2', '1');
-insert into sys_role_menu values ('2', '2');
-insert into sys_role_menu values ('2', '3');
-insert into sys_role_menu values ('2', '4');
-insert into sys_role_menu values ('2', '100');
-insert into sys_role_menu values ('2', '101');
-insert into sys_role_menu values ('2', '102');
-insert into sys_role_menu values ('2', '103');
-insert into sys_role_menu values ('2', '104');
-insert into sys_role_menu values ('2', '105');
-insert into sys_role_menu values ('2', '106');
-insert into sys_role_menu values ('2', '107');
-insert into sys_role_menu values ('2', '108');
-insert into sys_role_menu values ('2', '109');
-insert into sys_role_menu values ('2', '110');
-insert into sys_role_menu values ('2', '111');
-insert into sys_role_menu values ('2', '112');
-insert into sys_role_menu values ('2', '113');
-insert into sys_role_menu values ('2', '114');
-insert into sys_role_menu values ('2', '115');
-insert into sys_role_menu values ('2', '116');
-insert into sys_role_menu values ('2', '500');
-insert into sys_role_menu values ('2', '501');
-insert into sys_role_menu values ('2', '1000');
-insert into sys_role_menu values ('2', '1001');
-insert into sys_role_menu values ('2', '1002');
-insert into sys_role_menu values ('2', '1003');
-insert into sys_role_menu values ('2', '1004');
-insert into sys_role_menu values ('2', '1005');
-insert into sys_role_menu values ('2', '1006');
-insert into sys_role_menu values ('2', '1007');
-insert into sys_role_menu values ('2', '1008');
-insert into sys_role_menu values ('2', '1009');
-insert into sys_role_menu values ('2', '1010');
-insert into sys_role_menu values ('2', '1011');
-insert into sys_role_menu values ('2', '1012');
-insert into sys_role_menu values ('2', '1013');
-insert into sys_role_menu values ('2', '1014');
-insert into sys_role_menu values ('2', '1015');
-insert into sys_role_menu values ('2', '1016');
-insert into sys_role_menu values ('2', '1017');
-insert into sys_role_menu values ('2', '1018');
-insert into sys_role_menu values ('2', '1019');
-insert into sys_role_menu values ('2', '1020');
-insert into sys_role_menu values ('2', '1021');
-insert into sys_role_menu values ('2', '1022');
-insert into sys_role_menu values ('2', '1023');
-insert into sys_role_menu values ('2', '1024');
-insert into sys_role_menu values ('2', '1025');
-insert into sys_role_menu values ('2', '1026');
-insert into sys_role_menu values ('2', '1027');
-insert into sys_role_menu values ('2', '1028');
-insert into sys_role_menu values ('2', '1029');
-insert into sys_role_menu values ('2', '1030');
-insert into sys_role_menu values ('2', '1031');
-insert into sys_role_menu values ('2', '1032');
-insert into sys_role_menu values ('2', '1033');
-insert into sys_role_menu values ('2', '1034');
-insert into sys_role_menu values ('2', '1035');
-insert into sys_role_menu values ('2', '1036');
-insert into sys_role_menu values ('2', '1037');
-insert into sys_role_menu values ('2', '1038');
-insert into sys_role_menu values ('2', '1039');
-insert into sys_role_menu values ('2', '1040');
-insert into sys_role_menu values ('2', '1041');
-insert into sys_role_menu values ('2', '1042');
-insert into sys_role_menu values ('2', '1043');
-insert into sys_role_menu values ('2', '1044');
-insert into sys_role_menu values ('2', '1045');
-insert into sys_role_menu values ('2', '1050');
-insert into sys_role_menu values ('2', '1046');
-insert into sys_role_menu values ('2', '1047');
-insert into sys_role_menu values ('2', '1048');
-insert into sys_role_menu values ('2', '1055');
-insert into sys_role_menu values ('2', '1056');
-insert into sys_role_menu values ('2', '1057');
-insert into sys_role_menu values ('2', '1058');
-insert into sys_role_menu values ('2', '1059');
-insert into sys_role_menu values ('2', '1060');
-insert into sys_role_menu values ('2', '1061');
-insert into sys_role_menu values ('2', '1062');
-insert into sys_role_menu values ('2', '1063');
-insert into sys_role_menu values ('2', '1064');
-insert into sys_role_menu values ('2', '1065');
+insert into sys_role_menu values ('3', '1');
+insert into sys_role_menu values ('3', '5');
+insert into sys_role_menu values ('3', '100');
+insert into sys_role_menu values ('3', '101');
+insert into sys_role_menu values ('3', '102');
+insert into sys_role_menu values ('3', '103');
+insert into sys_role_menu values ('3', '104');
+insert into sys_role_menu values ('3', '105');
+insert into sys_role_menu values ('3', '106');
+insert into sys_role_menu values ('3', '107');
+insert into sys_role_menu values ('3', '108');
+insert into sys_role_menu values ('3', '500');
+insert into sys_role_menu values ('3', '501');
+insert into sys_role_menu values ('3', '1001');
+insert into sys_role_menu values ('3', '1002');
+insert into sys_role_menu values ('3', '1003');
+insert into sys_role_menu values ('3', '1004');
+insert into sys_role_menu values ('3', '1005');
+insert into sys_role_menu values ('3', '1006');
+insert into sys_role_menu values ('3', '1007');
+insert into sys_role_menu values ('3', '1008');
+insert into sys_role_menu values ('3', '1009');
+insert into sys_role_menu values ('3', '1010');
+insert into sys_role_menu values ('3', '1011');
+insert into sys_role_menu values ('3', '1012');
+insert into sys_role_menu values ('3', '1013');
+insert into sys_role_menu values ('3', '1014');
+insert into sys_role_menu values ('3', '1015');
+insert into sys_role_menu values ('3', '1016');
+insert into sys_role_menu values ('3', '1017');
+insert into sys_role_menu values ('3', '1018');
+insert into sys_role_menu values ('3', '1019');
+insert into sys_role_menu values ('3', '1020');
+insert into sys_role_menu values ('3', '1021');
+insert into sys_role_menu values ('3', '1022');
+insert into sys_role_menu values ('3', '1023');
+insert into sys_role_menu values ('3', '1024');
+insert into sys_role_menu values ('3', '1025');
+insert into sys_role_menu values ('3', '1026');
+insert into sys_role_menu values ('3', '1027');
+insert into sys_role_menu values ('3', '1028');
+insert into sys_role_menu values ('3', '1029');
+insert into sys_role_menu values ('3', '1030');
+insert into sys_role_menu values ('3', '1031');
+insert into sys_role_menu values ('3', '1032');
+insert into sys_role_menu values ('3', '1033');
+insert into sys_role_menu values ('3', '1034');
+insert into sys_role_menu values ('3', '1035');
+insert into sys_role_menu values ('3', '1036');
+insert into sys_role_menu values ('3', '1037');
+insert into sys_role_menu values ('3', '1038');
+insert into sys_role_menu values ('3', '1039');
+insert into sys_role_menu values ('3', '1040');
+insert into sys_role_menu values ('3', '1041');
+insert into sys_role_menu values ('3', '1042');
+insert into sys_role_menu values ('3', '1043');
+insert into sys_role_menu values ('3', '1044');
+insert into sys_role_menu values ('3', '1045');
+insert into sys_role_menu values ('3', '1500');
+insert into sys_role_menu values ('3', '1501');
+insert into sys_role_menu values ('3', '1502');
+insert into sys_role_menu values ('3', '1503');
+insert into sys_role_menu values ('3', '1504');
+insert into sys_role_menu values ('3', '1505');
+insert into sys_role_menu values ('3', '1506');
+insert into sys_role_menu values ('3', '1507');
+insert into sys_role_menu values ('3', '1508');
+insert into sys_role_menu values ('3', '1509');
+insert into sys_role_menu values ('3', '1510');
+insert into sys_role_menu values ('3', '1511');
+insert into sys_role_menu values ('4', '5');
+insert into sys_role_menu values ('4', '1500');
+insert into sys_role_menu values ('4', '1501');
+insert into sys_role_menu values ('4', '1502');
+insert into sys_role_menu values ('4', '1503');
+insert into sys_role_menu values ('4', '1504');
+insert into sys_role_menu values ('4', '1505');
+insert into sys_role_menu values ('4', '1506');
+insert into sys_role_menu values ('4', '1507');
+insert into sys_role_menu values ('4', '1508');
+insert into sys_role_menu values ('4', '1509');
+insert into sys_role_menu values ('4', '1510');
+insert into sys_role_menu values ('4', '1511');
 
 -- ----------------------------
 -- 8銆佽鑹插拰閮ㄩ棬鍏宠仈琛�  瑙掕壊1-N閮ㄩ棬
 -- ----------------------------
-drop table if exists sys_role_dept;
 create table if not exists sys_role_dept
 (
     role_id int8 not null,
@@ -695,18 +705,10 @@
 comment on column sys_role_dept.role_id     is '瑙掕壊ID';
 comment on column sys_role_dept.dept_id     is '閮ㄩ棬ID';
 
--- ----------------------------
--- 鍒濆鍖�-瑙掕壊鍜岄儴闂ㄥ叧鑱旇〃鏁版嵁
--- ----------------------------
-insert into sys_role_dept values ('2', '100');
-insert into sys_role_dept values ('2', '101');
-insert into sys_role_dept values ('2', '105');
-
 
 -- ----------------------------
 -- 9銆佺敤鎴蜂笌宀椾綅鍏宠仈琛�  鐢ㄦ埛1-N宀椾綅
 -- ----------------------------
-drop table if exists sys_user_post;
 create table if not exists sys_user_post
 (
     user_id int8 not null,
@@ -722,13 +724,10 @@
 -- 鍒濆鍖�-鐢ㄦ埛涓庡矖浣嶅叧鑱旇〃鏁版嵁
 -- ----------------------------
 insert into sys_user_post values ('1', '1');
-insert into sys_user_post values ('2', '2');
-
 
 -- ----------------------------
 -- 10銆佹搷浣滄棩蹇楄褰�
 -- ----------------------------
-drop table if exists sys_oper_log;
 create table if not exists sys_oper_log
 (
     oper_id        int8,
@@ -779,7 +778,6 @@
 -- ----------------------------
 -- 11銆佸瓧鍏哥被鍨嬭〃
 -- ----------------------------
-drop table if exists sys_dict_type;
 create table if not exists sys_dict_type
 (
     dict_id     int8,
@@ -823,7 +821,6 @@
 -- ----------------------------
 -- 12銆佸瓧鍏告暟鎹〃
 -- ----------------------------
-drop table if exists sys_dict_data;
 create table if not exists sys_dict_data
 (
     dict_code   int8,
@@ -900,7 +897,6 @@
 -- ----------------------------
 -- 13銆佸弬鏁伴厤缃〃
 -- ----------------------------
-drop table if exists sys_config;
 create table if not exists sys_config
 (
     config_id    int8,
@@ -942,7 +938,6 @@
 -- ----------------------------
 -- 14銆佺郴缁熻闂褰�
 -- ----------------------------
-drop table if exists sys_logininfor;
 create table if not exists sys_logininfor
 (
     info_id        int8,
@@ -980,7 +975,6 @@
 -- ----------------------------
 -- 17銆侀�氱煡鍏憡琛�
 -- ----------------------------
-drop table if exists sys_notice;
 create table if not exists sys_notice
 (
     notice_id      int8,
@@ -1022,7 +1016,6 @@
 -- ----------------------------
 -- 18銆佷唬鐮佺敓鎴愪笟鍔¤〃
 -- ----------------------------
-drop table if exists gen_table;
 create table if not exists gen_table
 (
     table_id          int8,
@@ -1077,7 +1070,6 @@
 -- ----------------------------
 -- 19銆佷唬鐮佺敓鎴愪笟鍔¤〃瀛楁
 -- ----------------------------
-drop table if exists gen_table_column;
 create table if not exists gen_table_column
 (
     column_id      int8,
@@ -1134,7 +1126,6 @@
 -- ----------------------------
 -- OSS瀵硅薄瀛樺偍琛�
 -- ----------------------------
-drop table if exists sys_oss;
 create table if not exists sys_oss
 (
     oss_id        int8,
@@ -1169,7 +1160,6 @@
 -- ----------------------------
 -- OSS瀵硅薄瀛樺偍鍔ㄦ�侀厤缃〃
 -- ----------------------------
-drop table if exists sys_oss_config;
 create table if not exists sys_oss_config
 (
     oss_config_id int8,
@@ -1196,7 +1186,7 @@
 );
 
 comment on table sys_oss_config                 is '瀵硅薄瀛樺偍閰嶇疆琛�';
-comment on column sys_oss_config.oss_config_id  is '涓诲缓';
+comment on column sys_oss_config.oss_config_id  is '涓婚敭';
 comment on column sys_oss_config.tenant_id      is '绉熸埛缂栫爜';
 comment on column sys_oss_config.config_key     is '閰嶇疆key';
 comment on column sys_oss_config.access_key     is 'accessKey';
@@ -1226,7 +1216,6 @@
 -- ----------------------------
 -- 绯荤粺鎺堟潈琛�
 -- ----------------------------
-drop table if exists sys_client;
 create table sys_client (
     id                  int8,
     client_id           varchar(64)   default ''::varchar,
@@ -1247,7 +1236,7 @@
 );
 
 comment on table sys_client                         is '绯荤粺鎺堟潈琛�';
-comment on column sys_client.id                     is '涓诲缓';
+comment on column sys_client.id                     is '涓婚敭';
 comment on column sys_client.client_id              is '瀹㈡埛绔痠d';
 comment on column sys_client.client_key             is '瀹㈡埛绔痥ey';
 comment on column sys_client.client_secret          is '瀹㈡埛绔閽�';
@@ -1266,6 +1255,100 @@
 insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, now(), 1, now());
 insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, now(), 1, now());
 
+create table if not exists test_demo
+(
+    id          int8,
+    tenant_id   varchar(20)     default '000000',
+    dept_id     int8,
+    user_id     int8,
+    order_num   int4            default 0,
+    test_key    varchar(255),
+    value       varchar(255),
+    version     int4            default 0,
+    create_dept int8,
+    create_time timestamp,
+    create_by   int8,
+    update_time timestamp,
+    update_by   int8,
+    del_flag    int4            default 0
+);
+
+comment on table test_demo is '娴嬭瘯鍗曡〃';
+comment on column test_demo.id is '涓婚敭';
+comment on column test_demo.tenant_id is '绉熸埛缂栧彿';
+comment on column test_demo.dept_id is '閮ㄩ棬id';
+comment on column test_demo.user_id is '鐢ㄦ埛id';
+comment on column test_demo.order_num is '鎺掑簭鍙�';
+comment on column test_demo.test_key is 'key閿�';
+comment on column test_demo.value is '鍊�';
+comment on column test_demo.version is '鐗堟湰';
+comment on column test_demo.create_dept  is '鍒涘缓閮ㄩ棬';
+comment on column test_demo.create_time is '鍒涘缓鏃堕棿';
+comment on column test_demo.create_by is '鍒涘缓浜�';
+comment on column test_demo.update_time is '鏇存柊鏃堕棿';
+comment on column test_demo.update_by is '鏇存柊浜�';
+comment on column test_demo.del_flag is '鍒犻櫎鏍囧織';
+
+create table if not exists test_tree
+(
+    id          int8,
+    tenant_id   varchar(20)     default '000000',
+    parent_id   int8            default 0,
+    dept_id     int8,
+    user_id     int8,
+    tree_name   varchar(255),
+    version     int4            default 0,
+    create_dept int8,
+    create_time timestamp,
+    create_by   int8,
+    update_time timestamp,
+    update_by   int8,
+    del_flag    integer         default 0
+);
+
+comment on table test_tree is '娴嬭瘯鏍戣〃';
+comment on column test_tree.id is '涓婚敭';
+comment on column test_tree.tenant_id is '绉熸埛缂栧彿';
+comment on column test_tree.parent_id is '鐖秈d';
+comment on column test_tree.dept_id is '閮ㄩ棬id';
+comment on column test_tree.user_id is '鐢ㄦ埛id';
+comment on column test_tree.tree_name is '鍊�';
+comment on column test_tree.version is '鐗堟湰';
+comment on column test_tree.create_dept  is '鍒涘缓閮ㄩ棬';
+comment on column test_tree.create_time is '鍒涘缓鏃堕棿';
+comment on column test_tree.create_by is '鍒涘缓浜�';
+comment on column test_tree.update_time is '鏇存柊鏃堕棿';
+comment on column test_tree.update_by is '鏇存柊浜�';
+comment on column test_tree.del_flag is '鍒犻櫎鏍囧織';
+
+INSERT INTO test_demo VALUES (1, '000000', 102, 4, 1, '娴嬭瘯鏁版嵁鏉冮檺', '娴嬭瘯', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (2, '000000', 102, 3, 2, '瀛愯妭鐐�1', '111', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (3, '000000', 102, 3, 3, '瀛愯妭鐐�2', '222', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (4, '000000', 108, 4, 4, '娴嬭瘯鏁版嵁', 'demo', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (5, '000000', 108, 3, 13, '瀛愯妭鐐�11', '1111', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (6, '000000', 108, 3, 12, '瀛愯妭鐐�22', '2222', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (7, '000000', 108, 3, 11, '瀛愯妭鐐�33', '3333', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (8, '000000', 108, 3, 10, '瀛愯妭鐐�44', '4444', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (9, '000000', 108, 3, 9, '瀛愯妭鐐�55', '5555', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (10, '000000', 108, 3, 8, '瀛愯妭鐐�66', '6666', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (11, '000000', 108, 3, 7, '瀛愯妭鐐�77', '7777', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (12, '000000', 108, 3, 6, '瀛愯妭鐐�88', '8888', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (13, '000000', 108, 3, 5, '瀛愯妭鐐�99', '9999', 0, 103, now(), 1, NULL, NULL, 0);
+
+INSERT INTO test_tree VALUES (1, '000000', 0, 102, 4, '娴嬭瘯鏁版嵁鏉冮檺', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (2, '000000', 1, 102, 3, '瀛愯妭鐐�1', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (3, '000000', 2, 102, 3, '瀛愯妭鐐�2', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (4, '000000', 0, 108, 4, '娴嬭瘯鏍�1', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (5, '000000', 4, 108, 3, '瀛愯妭鐐�11', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (6, '000000', 4, 108, 3, '瀛愯妭鐐�22', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (7, '000000', 4, 108, 3, '瀛愯妭鐐�33', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (8, '000000', 5, 108, 3, '瀛愯妭鐐�44', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (9, '000000', 6, 108, 3, '瀛愯妭鐐�55', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (10, '000000', 7, 108, 3, '瀛愯妭鐐�66', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (11, '000000', 7, 108, 3, '瀛愯妭鐐�77', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (12, '000000', 10, 108, 3, '瀛愯妭鐐�88', 0, 103, now(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (13, '000000', 10, 108, 3, '瀛愯妭鐐�99', 0, 103, now(), 1, NULL, NULL, 0);
+
 -- 瀛楃涓茶嚜鍔ㄨ浆鏃堕棿 閬垮厤妗嗘灦鏃堕棿鏌ヨ鎶ラ敊闂
 create or replace function cast_varchar_to_timestamp(varchar) returns timestamptz as $$
 select to_timestamp($1, 'yyyy-mm-dd hh24:mi:ss');
diff --git a/script/sql/postgres/postgres_test.sql b/script/sql/postgres/postgres_test.sql
deleted file mode 100644
index 179096b..0000000
--- a/script/sql/postgres/postgres_test.sql
+++ /dev/null
@@ -1,204 +0,0 @@
-DROP TABLE if EXISTS test_demo;
-create table if not exists test_demo
-(
-    id          int8,
-    tenant_id   varchar(20)     default '000000',
-    dept_id     int8,
-    user_id     int8,
-    order_num   int4            default 0,
-    test_key    varchar(255),
-    value       varchar(255),
-    version     int4            default 0,
-    create_time timestamp,
-    create_dept int8,
-    create_by   int8,
-    update_time timestamp,
-    update_by   int8,
-    del_flag    int4            default 0
-);
-
-comment on table test_demo is '娴嬭瘯鍗曡〃';
-comment on column test_demo.id is '涓婚敭';
-comment on column test_demo.tenant_id is '绉熸埛缂栧彿';
-comment on column test_demo.dept_id is '閮ㄩ棬id';
-comment on column test_demo.user_id is '鐢ㄦ埛id';
-comment on column test_demo.order_num is '鎺掑簭鍙�';
-comment on column test_demo.test_key is 'key閿�';
-comment on column test_demo.value is '鍊�';
-comment on column test_demo.version is '鐗堟湰';
-comment on column test_demo.create_dept  is '鍒涘缓閮ㄩ棬';
-comment on column test_demo.create_time is '鍒涘缓鏃堕棿';
-comment on column test_demo.create_by is '鍒涘缓浜�';
-comment on column test_demo.update_time is '鏇存柊鏃堕棿';
-comment on column test_demo.update_by is '鏇存柊浜�';
-comment on column test_demo.del_flag is '鍒犻櫎鏍囧織';
-
-DROP TABLE if EXISTS test_tree;
-create table if not exists test_tree
-(
-    id          int8,
-    tenant_id   varchar(20)     default '000000',
-    parent_id   int8            default 0,
-    dept_id     int8,
-    user_id     int8,
-    tree_name   varchar(255),
-    version     int4            default 0,
-    create_time timestamp,
-    create_dept int8,
-    create_by   int8,
-    update_time timestamp,
-    update_by   int8,
-    del_flag    integer         default 0
-);
-
-comment on table test_tree is '娴嬭瘯鏍戣〃';
-comment on column test_tree.id is '涓婚敭';
-comment on column test_tree.tenant_id is '绉熸埛缂栧彿';
-comment on column test_tree.parent_id is '鐖秈d';
-comment on column test_tree.dept_id is '閮ㄩ棬id';
-comment on column test_tree.user_id is '鐢ㄦ埛id';
-comment on column test_tree.tree_name is '鍊�';
-comment on column test_tree.version is '鐗堟湰';
-comment on column test_tree.create_dept  is '鍒涘缓閮ㄩ棬';
-comment on column test_tree.create_time is '鍒涘缓鏃堕棿';
-comment on column test_tree.create_by is '鍒涘缓浜�';
-comment on column test_tree.update_time is '鏇存柊鏃堕棿';
-comment on column test_tree.update_by is '鏇存柊浜�';
-comment on column test_tree.del_flag is '鍒犻櫎鏍囧織';
-
-INSERT INTO sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (3, '000000', 108, 'test', '鏈儴闂ㄥ強浠ヤ笅 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 103, 1, now(), 3, now(), NULL);
-INSERT INTO sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (4, '000000', 102, 'test1', '浠呮湰浜� 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', now(), 103, 1, now(), 4, now(), NULL);
-
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (5, '娴嬭瘯鑿滃崟', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 103, 1, now(), NULL, NULL, '');
-
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1500, '娴嬭瘯鍗曡〃', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, now(), NULL, NULL, '娴嬭瘯鍗曡〃鑿滃崟');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1501, '娴嬭瘯鍗曡〃鏌ヨ', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1502, '娴嬭瘯鍗曡〃鏂板', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1503, '娴嬭瘯鍗曡〃淇敼', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1504, '娴嬭瘯鍗曡〃鍒犻櫎', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1505, '娴嬭瘯鍗曡〃瀵煎嚭', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, now(), NULL, NULL, '');
-
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1506, '娴嬭瘯鏍戣〃', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, now(), NULL, NULL, '娴嬭瘯鏍戣〃鑿滃崟');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1507, '娴嬭瘯鏍戣〃鏌ヨ', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1508, '娴嬭瘯鏍戣〃鏂板', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1509, '娴嬭瘯鏍戣〃淇敼', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1510, '娴嬭瘯鏍戣〃鍒犻櫎', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, now(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1511, '娴嬭瘯鏍戣〃瀵煎嚭', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, now(), NULL, NULL, '');
-
-INSERT INTO sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (3, '000000', '鏈儴闂ㄥ強浠ヤ笅', 'test1', 3, '4', 't', 't', '0', '0', 103, 1, now(), 1,  NULL, NULL);
-INSERT INTO sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (4, '000000', '浠呮湰浜�', 'test2', 4, '5', 't', 't', '0', '0', 103, 1, now(), 1,  NULL, NULL);
-
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511);
-
-INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3);
-INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4);
-
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (1, '000000', 102, 4, 1, '娴嬭瘯鏁版嵁鏉冮檺', '娴嬭瘯', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (2, '000000', 102, 3, 2, '瀛愯妭鐐�1', '111', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (3, '000000', 102, 3, 3, '瀛愯妭鐐�2', '222', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (4, '000000', 108, 4, 4, '娴嬭瘯鏁版嵁', 'demo', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (5, '000000', 108, 3, 13, '瀛愯妭鐐�11', '1111', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (6, '000000', 108, 3, 12, '瀛愯妭鐐�22', '2222', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (7, '000000', 108, 3, 11, '瀛愯妭鐐�33', '3333', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (8, '000000', 108, 3, 10, '瀛愯妭鐐�44', '4444', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (9, '000000', 108, 3, 9, '瀛愯妭鐐�55', '5555', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (10, '000000', 108, 3, 8, '瀛愯妭鐐�66', '6666', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (11, '000000', 108, 3, 7, '瀛愯妭鐐�77', '7777', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (12, '000000', 108, 3, 6, '瀛愯妭鐐�88', '8888', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (13, '000000', 108, 3, 5, '瀛愯妭鐐�99', '9999', 0, 103, now(), 1, NULL, NULL, 0);
-
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (1, '000000', 0, 102, 4, '娴嬭瘯鏁版嵁鏉冮檺', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (2, '000000', 1, 102, 3, '瀛愯妭鐐�1', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (3, '000000', 2, 102, 3, '瀛愯妭鐐�2', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (4, '000000', 0, 108, 4, '娴嬭瘯鏍�1', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (5, '000000', 4, 108, 3, '瀛愯妭鐐�11', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (6, '000000', 4, 108, 3, '瀛愯妭鐐�22', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (7, '000000', 4, 108, 3, '瀛愯妭鐐�33', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (8, '000000', 5, 108, 3, '瀛愯妭鐐�44', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (9, '000000', 6, 108, 3, '瀛愯妭鐐�55', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (10, '000000', 7, 108, 3, '瀛愯妭鐐�66', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (11, '000000', 7, 108, 3, '瀛愯妭鐐�77', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (12, '000000', 10, 108, 3, '瀛愯妭鐐�88', 0, 103, now(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (13, '000000', 10, 108, 3, '瀛愯妭鐐�99', 0, 103, now(), 1, NULL, NULL, 0);
diff --git a/script/sql/postgres/powerjob.sql b/script/sql/postgres/powerjob.sql
deleted file mode 100644
index 56d3bcb..0000000
--- a/script/sql/postgres/powerjob.sql
+++ /dev/null
@@ -1,258 +0,0 @@
--- pj_app_info definition
-
--- Drop table
-
--- DROP TABLE pj_app_info;
-
-CREATE TABLE pj_app_info (
-    id int8 NOT NULL,
-    app_name varchar(255) NULL,
-    current_server varchar(255) NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    "password" varchar(255) NULL,
-    CONSTRAINT pj_app_info_pkey PRIMARY KEY (id),
-    CONSTRAINT uidx01_app_info UNIQUE (app_name)
-);
-
-INSERT INTO pj_app_info VALUES(1, 'ruoyi-worker', '127.0.0.1:10010', '2023-06-13 16:32:59.263', '2023-07-04 17:25:49.798', '123456');
-
-
-
--- pj_container_info definition
-
--- Drop table
-
--- DROP TABLE pj_container_info;
-
-CREATE TABLE pj_container_info (
-    id int8 NOT NULL,
-    app_id int8 NULL,
-    container_name varchar(255) NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    last_deploy_time timestamp NULL,
-    source_info varchar(255) NULL,
-    source_type int4 NULL,
-    status int4 NULL,
-    "version" varchar(255) NULL,
-    CONSTRAINT pj_container_info_pkey PRIMARY KEY (id)
-);
-CREATE INDEX idx01_container_info ON pj_container_info USING btree (app_id);
-
-
--- pj_instance_info definition
-
--- Drop table
-
--- DROP TABLE pj_instance_info;
-
-CREATE TABLE pj_instance_info (
-    id int8 NOT NULL,
-    actual_trigger_time int8 NULL,
-    app_id int8 NULL,
-    expected_trigger_time int8 NULL,
-    finished_time int8 NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    instance_id int8 NULL,
-    instance_params oid NULL,
-    job_id int8 NULL,
-    job_params oid NULL,
-    last_report_time int8 NULL,
-    "result" oid NULL,
-    running_times int8 NULL,
-    status int4 NULL,
-    task_tracker_address varchar(255) NULL,
-    "type" int4 NULL,
-    wf_instance_id int8 NULL,
-    CONSTRAINT pj_instance_info_pkey PRIMARY KEY (id)
-);
-CREATE INDEX idx01_instance_info ON pj_instance_info USING btree (job_id, status);
-CREATE INDEX idx02_instance_info ON pj_instance_info USING btree (app_id, status);
-CREATE INDEX idx03_instance_info ON pj_instance_info USING btree (instance_id, status);
-
-
--- pj_job_info definition
-
--- Drop table
-
--- DROP TABLE pj_job_info;
-
-CREATE TABLE pj_job_info (
-    id int8 NOT NULL,
-    alarm_config varchar(255) NULL,
-    app_id int8 NULL,
-    concurrency int4 NULL,
-    designated_workers varchar(255) NULL,
-    dispatch_strategy int4 NULL,
-    execute_type int4 NULL,
-    extra varchar(255) NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    instance_retry_num int4 NULL,
-    instance_time_limit int8 NULL,
-    job_description varchar(255) NULL,
-    job_name varchar(255) NULL,
-    job_params oid NULL,
-    lifecycle varchar(255) NULL,
-    log_config varchar(255) NULL,
-    max_instance_num int4 NULL,
-    max_worker_count int4 NULL,
-    min_cpu_cores float8 NOT NULL,
-    min_disk_space float8 NOT NULL,
-    min_memory_space float8 NOT NULL,
-    next_trigger_time int8 NULL,
-    notify_user_ids varchar(255) NULL,
-    processor_info varchar(255) NULL,
-    processor_type int4 NULL,
-    status int4 NULL,
-    tag varchar(255) NULL,
-    task_retry_num int4 NULL,
-    time_expression varchar(255) NULL,
-    time_expression_type int4 NULL,
-    CONSTRAINT pj_job_info_pkey PRIMARY KEY (id)
-);
-CREATE INDEX idx01_job_info ON pj_job_info USING btree (app_id, status, time_expression_type, next_trigger_time);
-
-
-INSERT INTO pj_job_info VALUES(1, '{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', 1, 5, '', 2, 1, NULL, '2023-06-02 15:01:27.717', '2023-07-04 17:22:12.374', 1, 0, '', '鍗曟満澶勭悊鍣ㄦ墽琛屾祴璇�', NULL, '{}', '{"type":1}', 0, 0, 0.0, 0.0, 0.0, NULL, NULL, 'org.dromara.job.processors.StandaloneProcessorDemo', 1, 2, NULL, 1, '30000', 3);
-INSERT INTO pj_job_info VALUES(2, '{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', 1, 5, '', 1, 2, NULL, '2023-06-02 15:04:45.342', '2023-07-04 17:22:12.816', 0, 0, NULL, '骞挎挱澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{"type":1}', 0, 0, 0.0, 0.0, 0.0, NULL, NULL, 'org.dromara.job.processors.BroadcastProcessorDemo', 1, 2, NULL, 1, '30000', 3);
-INSERT INTO pj_job_info VALUES(3, '{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', 1, 5, '', 1, 4, NULL, '2023-06-02 15:13:23.519', '2023-06-02 16:03:22.421', 0, 0, NULL, 'Map澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{"type":1}', 0, 0, 0.0, 0.0, 0.0, NULL, NULL, 'org.dromara.job.processors.MapProcessorDemo', 1, 2, NULL, 1, '1000', 3);
-INSERT INTO pj_job_info VALUES(4, '{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', 1, 5, '', 1, 3, NULL, '2023-06-02 15:45:25.896', '2023-06-02 16:03:23.125', 0, 0, NULL, 'MapReduce澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{"type":1}', 0, 0, 0.0, 0.0, 0.0, NULL, NULL, 'org.dromara.job.processors.MapReduceProcessorDemo', 1, 2, NULL, 1, '1000', 3);
-
-
--- pj_oms_lock definition
-
--- Drop table
-
--- DROP TABLE pj_oms_lock;
-
-CREATE TABLE pj_oms_lock (
-    id int8 NOT NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    lock_name varchar(255) NULL,
-    max_lock_time int8 NULL,
-    ownerip varchar(255) NULL,
-    CONSTRAINT pj_oms_lock_pkey PRIMARY KEY (id),
-    CONSTRAINT uidx01_oms_lock UNIQUE (lock_name)
-);
-
-
--- pj_server_info definition
-
--- Drop table
-
--- DROP TABLE pj_server_info;
-
-CREATE TABLE pj_server_info (
-    id int8 NOT NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    ip varchar(255) NULL,
-    CONSTRAINT pj_server_info_pkey PRIMARY KEY (id),
-    CONSTRAINT uidx01_server_info UNIQUE (ip)
-);
-CREATE INDEX idx01_server_info ON pj_server_info USING btree (gmt_modified);
-
-
--- pj_user_info definition
-
--- Drop table
-
--- DROP TABLE pj_user_info;
-
-CREATE TABLE pj_user_info (
-    id int8 NOT NULL,
-    email varchar(255) NULL,
-    extra varchar(255) NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    "password" varchar(255) NULL,
-    phone varchar(255) NULL,
-    username varchar(255) NULL,
-    web_hook varchar(255) NULL,
-    CONSTRAINT pj_user_info_pkey PRIMARY KEY (id)
-);
-CREATE INDEX uidx01_user_info ON pj_user_info USING btree (username);
-CREATE INDEX uidx02_user_info ON pj_user_info USING btree (email);
-
-
--- pj_workflow_info definition
-
--- Drop table
-
--- DROP TABLE pj_workflow_info;
-
-CREATE TABLE pj_workflow_info (
-    id int8 NOT NULL,
-    app_id int8 NULL,
-    extra varchar(255) NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    lifecycle varchar(255) NULL,
-    max_wf_instance_num int4 NULL,
-    next_trigger_time int8 NULL,
-    notify_user_ids varchar(255) NULL,
-    pedag oid NULL,
-    status int4 NULL,
-    time_expression varchar(255) NULL,
-    time_expression_type int4 NULL,
-    wf_description varchar(255) NULL,
-    wf_name varchar(255) NULL,
-    CONSTRAINT pj_workflow_info_pkey PRIMARY KEY (id)
-);
-CREATE INDEX idx01_workflow_info ON pj_workflow_info USING btree (app_id, status, time_expression_type, next_trigger_time);
-
-
--- pj_workflow_instance_info definition
-
--- Drop table
-
--- DROP TABLE pj_workflow_instance_info;
-
-CREATE TABLE pj_workflow_instance_info (
-    id int8 NOT NULL,
-    actual_trigger_time int8 NULL,
-    app_id int8 NULL,
-    dag oid NULL,
-    expected_trigger_time int8 NULL,
-    finished_time int8 NULL,
-    gmt_create timestamp NULL,
-    gmt_modified timestamp NULL,
-    parent_wf_instance_id int8 NULL,
-    "result" oid NULL,
-    status int4 NULL,
-    wf_context oid NULL,
-    wf_init_params oid NULL,
-    wf_instance_id int8 NULL,
-    workflow_id int8 NULL,
-    CONSTRAINT pj_workflow_instance_info_pkey PRIMARY KEY (id),
-    CONSTRAINT uidx01_wf_instance UNIQUE (wf_instance_id)
-);
-CREATE INDEX idx01_wf_instance ON pj_workflow_instance_info USING btree (workflow_id, status, app_id, expected_trigger_time);
-
-
--- pj_workflow_node_info definition
-
--- Drop table
-
--- DROP TABLE pj_workflow_node_info;
-
-CREATE TABLE pj_workflow_node_info (
-    id int8 NOT NULL,
-    app_id int8 NOT NULL,
-    "enable" bool NOT NULL,
-    extra oid NULL,
-    gmt_create timestamp NOT NULL,
-    gmt_modified timestamp NOT NULL,
-    job_id int8 NULL,
-    node_name varchar(255) NULL,
-    node_params oid NULL,
-    skip_when_failed bool NOT NULL,
-    "type" int4 NULL,
-    workflow_id int8 NULL,
-    CONSTRAINT pj_workflow_node_info_pkey PRIMARY KEY (id)
-);
-CREATE INDEX idx01_workflow_node_info ON pj_workflow_node_info USING btree (workflow_id, gmt_create);
diff --git a/script/sql/postgres/snail_job_postgre.sql b/script/sql/postgres/snail_job_postgre.sql
new file mode 100644
index 0000000..30a871e
--- /dev/null
+++ b/script/sql/postgres/snail_job_postgre.sql
@@ -0,0 +1,825 @@
+/*
+ SnailJob Database Transfer Tool
+ Source Server Type    : MySQL
+ Target Server Type    : PostgreSQL
+ Date: 2024-05-13 22:49:34
+*/
+
+
+-- sj_namespace
+CREATE TABLE sj_namespace
+(
+    id          bigserial PRIMARY KEY,
+    name        varchar(64)  NOT NULL,
+    unique_id   varchar(64)  NOT NULL,
+    description varchar(256) NOT NULL DEFAULT '',
+    deleted     smallint     NOT NULL DEFAULT 0,
+    create_dt   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt   timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_namespace_01 ON sj_namespace (name);
+
+COMMENT ON COLUMN sj_namespace.id IS '涓婚敭';
+COMMENT ON COLUMN sj_namespace.name IS '鍚嶇О';
+COMMENT ON COLUMN sj_namespace.unique_id IS '鍞竴id';
+COMMENT ON COLUMN sj_namespace.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_namespace.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_namespace.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_namespace.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_namespace IS '鍛藉悕绌洪棿';
+
+INSERT INTO sj_namespace VALUES (1, 'Development', 'dev', '', 0, now(), now());
+INSERT INTO sj_namespace VALUES (2, 'Production', 'prod', '', 0, now(), now());
+
+-- sj_group_config
+CREATE TABLE sj_group_config
+(
+    id                bigserial PRIMARY KEY,
+    namespace_id      varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name        varchar(64)  NOT NULL DEFAULT '',
+    description       varchar(256) NOT NULL DEFAULT '',
+    token             varchar(64)  NOT NULL DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT',
+    group_status      smallint     NOT NULL DEFAULT 0,
+    version           int          NOT NULL,
+    group_partition   int          NOT NULL,
+    id_generator_mode smallint     NOT NULL DEFAULT 1,
+    init_scene        smallint     NOT NULL DEFAULT 0,
+    bucket_index      int          NOT NULL DEFAULT 0,
+    create_dt         timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt         timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_group_config_01 ON sj_group_config (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_group_config.id IS '涓婚敭';
+COMMENT ON COLUMN sj_group_config.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_group_config.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_group_config.description IS '缁勬弿杩�';
+COMMENT ON COLUMN sj_group_config.token IS 'token';
+COMMENT ON COLUMN sj_group_config.group_status IS '缁勭姸鎬� 0銆佹湭鍚敤 1銆佸惎鐢�';
+COMMENT ON COLUMN sj_group_config.version IS '鐗堟湰鍙�';
+COMMENT ON COLUMN sj_group_config.group_partition IS '鍒嗗尯';
+COMMENT ON COLUMN sj_group_config.id_generator_mode IS '鍞竴id鐢熸垚妯″紡 榛樿鍙锋妯″紡';
+COMMENT ON COLUMN sj_group_config.init_scene IS '鏄惁鍒濆鍖栧満鏅� 0:鍚� 1:鏄�';
+COMMENT ON COLUMN sj_group_config.bucket_index IS 'bucket';
+COMMENT ON COLUMN sj_group_config.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_group_config.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_group_config IS '缁勯厤缃�';
+
+INSERT INTO sj_group_config VALUES (1, 'dev', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, now(), now());
+
+-- sj_notify_config
+CREATE TABLE sj_notify_config
+(
+    id                     bigserial PRIMARY KEY,
+    namespace_id           varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name             varchar(64)  NOT NULL,
+    business_id            varchar(64)  NOT NULL,
+    system_task_type       smallint     NOT NULL DEFAULT 3,
+    notify_status          smallint     NOT NULL DEFAULT 0,
+    recipient_ids          varchar(128) NOT NULL,
+    notify_threshold       int          NOT NULL DEFAULT 0,
+    notify_scene           smallint     NOT NULL DEFAULT 0,
+    rate_limiter_status    smallint     NOT NULL DEFAULT 0,
+    rate_limiter_threshold int          NOT NULL DEFAULT 0,
+    description            varchar(256) NOT NULL DEFAULT '',
+    create_dt              timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt              timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_notify_config_01 ON sj_notify_config (namespace_id, group_name, business_id);
+
+COMMENT ON COLUMN sj_notify_config.id IS '涓婚敭';
+COMMENT ON COLUMN sj_notify_config.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_notify_config.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_notify_config.business_id IS '涓氬姟id  ( job_id鎴杦orkflow_id鎴杝cene_name ) ';
+COMMENT ON COLUMN sj_notify_config.system_task_type IS '浠诲姟绫诲瀷 1. 閲嶈瘯浠诲姟 2. 閲嶈瘯鍥炶皟 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟';
+COMMENT ON COLUMN sj_notify_config.notify_status IS '閫氱煡鐘舵�� 0銆佹湭鍚敤 1銆佸惎鐢�';
+COMMENT ON COLUMN sj_notify_config.recipient_ids IS '鎺ユ敹浜篿d鍒楄〃';
+COMMENT ON COLUMN sj_notify_config.notify_threshold IS '閫氱煡闃堝��';
+COMMENT ON COLUMN sj_notify_config.notify_scene IS '閫氱煡鍦烘櫙';
+COMMENT ON COLUMN sj_notify_config.rate_limiter_status IS '闄愭祦鐘舵�� 0銆佹湭鍚敤 1銆佸惎鐢�';
+COMMENT ON COLUMN sj_notify_config.rate_limiter_threshold IS '姣忕闄愭祦闃堝��';
+COMMENT ON COLUMN sj_notify_config.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_notify_config.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_notify_config.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_notify_config IS '閫氱煡閰嶇疆';
+
+-- sj_notify_recipient
+CREATE TABLE sj_notify_recipient
+(
+    id               bigserial PRIMARY KEY,
+    namespace_id     varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    recipient_name   varchar(64)  NOT NULL,
+    notify_type      smallint     NOT NULL DEFAULT 0,
+    notify_attribute varchar(512) NOT NULL,
+    description      varchar(256) NOT NULL DEFAULT '',
+    create_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_notify_recipient_01 ON sj_notify_recipient (namespace_id);
+
+COMMENT ON COLUMN sj_notify_recipient.id IS '涓婚敭';
+COMMENT ON COLUMN sj_notify_recipient.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_notify_recipient.recipient_name IS '鎺ユ敹浜哄悕绉�';
+COMMENT ON COLUMN sj_notify_recipient.notify_type IS '閫氱煡绫诲瀷 1銆侀拤閽� 2銆侀偖浠� 3銆佷紒涓氬井淇� 4 椋炰功';
+COMMENT ON COLUMN sj_notify_recipient.notify_attribute IS '閰嶇疆灞炴��';
+COMMENT ON COLUMN sj_notify_recipient.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_notify_recipient.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_notify_recipient.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_notify_recipient IS '鍛婅閫氱煡鎺ユ敹浜�';
+
+-- sj_retry_dead_letter_0
+CREATE TABLE sj_retry_dead_letter_0
+(
+    id            bigserial PRIMARY KEY,
+    namespace_id  varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    unique_id     varchar(64)  NOT NULL,
+    group_name    varchar(64)  NOT NULL,
+    scene_name    varchar(64)  NOT NULL,
+    idempotent_id varchar(64)  NOT NULL,
+    biz_no        varchar(64)  NOT NULL DEFAULT '',
+    executor_name varchar(512) NOT NULL DEFAULT '',
+    args_str      text         NOT NULL,
+    ext_attrs     text         NOT NULL,
+    task_type     smallint     NOT NULL DEFAULT 1,
+    create_dt     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_retry_dead_letter_0_01 ON sj_retry_dead_letter_0 (namespace_id, group_name, unique_id);
+
+CREATE INDEX idx_sj_retry_dead_letter_0_01 ON sj_retry_dead_letter_0 (namespace_id, group_name, scene_name);
+CREATE INDEX idx_sj_retry_dead_letter_0_02 ON sj_retry_dead_letter_0 (idempotent_id);
+CREATE INDEX idx_sj_retry_dead_letter_0_03 ON sj_retry_dead_letter_0 (biz_no);
+CREATE INDEX idx_sj_retry_dead_letter_0_04 ON sj_retry_dead_letter_0 (create_dt);
+
+COMMENT ON COLUMN sj_retry_dead_letter_0.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_dead_letter_0.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_dead_letter_0.unique_id IS '鍚岀粍涓媔d鍞竴';
+COMMENT ON COLUMN sj_retry_dead_letter_0.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_dead_letter_0.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_dead_letter_0.idempotent_id IS '骞傜瓑id';
+COMMENT ON COLUMN sj_retry_dead_letter_0.biz_no IS '涓氬姟缂栧彿';
+COMMENT ON COLUMN sj_retry_dead_letter_0.executor_name IS '鎵ц鍣ㄥ悕绉�';
+COMMENT ON COLUMN sj_retry_dead_letter_0.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_retry_dead_letter_0.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_retry_dead_letter_0.task_type IS '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�';
+COMMENT ON COLUMN sj_retry_dead_letter_0.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON TABLE sj_retry_dead_letter_0 IS '姝讳俊闃熷垪琛�';
+
+-- sj_retry_task_0
+CREATE TABLE sj_retry_task_0
+(
+    id              bigserial PRIMARY KEY,
+    namespace_id    varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    unique_id       varchar(64)  NOT NULL,
+    group_name      varchar(64)  NOT NULL,
+    scene_name      varchar(64)  NOT NULL,
+    idempotent_id   varchar(64)  NOT NULL,
+    biz_no          varchar(64)  NOT NULL DEFAULT '',
+    executor_name   varchar(512) NOT NULL DEFAULT '',
+    args_str        text         NOT NULL,
+    ext_attrs       text         NOT NULL,
+    next_trigger_at timestamp    NOT NULL,
+    retry_count     int          NOT NULL DEFAULT 0,
+    retry_status    smallint     NOT NULL DEFAULT 0,
+    task_type       smallint     NOT NULL DEFAULT 1,
+    create_dt       timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt       timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_retry_task_0_01 ON sj_retry_task_0 (namespace_id, group_name, unique_id);
+
+CREATE INDEX idx_sj_retry_task_0_01 ON sj_retry_task_0 (namespace_id, group_name, scene_name);
+CREATE INDEX idx_sj_retry_task_0_02 ON sj_retry_task_0 (namespace_id, group_name, task_type);
+CREATE INDEX idx_sj_retry_task_0_03 ON sj_retry_task_0 (namespace_id, group_name, retry_status);
+CREATE INDEX idx_sj_retry_task_0_04 ON sj_retry_task_0 (idempotent_id);
+CREATE INDEX idx_sj_retry_task_0_05 ON sj_retry_task_0 (biz_no);
+CREATE INDEX idx_sj_retry_task_0_06 ON sj_retry_task_0 (create_dt);
+
+COMMENT ON COLUMN sj_retry_task_0.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_task_0.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_task_0.unique_id IS '鍚岀粍涓媔d鍞竴';
+COMMENT ON COLUMN sj_retry_task_0.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_task_0.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_task_0.idempotent_id IS '骞傜瓑id';
+COMMENT ON COLUMN sj_retry_task_0.biz_no IS '涓氬姟缂栧彿';
+COMMENT ON COLUMN sj_retry_task_0.executor_name IS '鎵ц鍣ㄥ悕绉�';
+COMMENT ON COLUMN sj_retry_task_0.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_retry_task_0.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_retry_task_0.next_trigger_at IS '涓嬫瑙﹀彂鏃堕棿';
+COMMENT ON COLUMN sj_retry_task_0.retry_count IS '閲嶈瘯娆℃暟';
+COMMENT ON COLUMN sj_retry_task_0.retry_status IS '閲嶈瘯鐘舵�� 0銆侀噸璇曚腑 1銆佹垚鍔� 2銆佹渶澶ч噸璇曟鏁�';
+COMMENT ON COLUMN sj_retry_task_0.task_type IS '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�';
+COMMENT ON COLUMN sj_retry_task_0.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_retry_task_0.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_retry_task_0 IS '浠诲姟琛�';
+
+-- sj_retry_task_log
+CREATE TABLE sj_retry_task_log
+(
+    id            bigserial PRIMARY KEY,
+    namespace_id  varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    unique_id     varchar(64)  NOT NULL,
+    group_name    varchar(64)  NOT NULL,
+    scene_name    varchar(64)  NOT NULL,
+    idempotent_id varchar(64)  NOT NULL,
+    biz_no        varchar(64)  NOT NULL DEFAULT '',
+    executor_name varchar(512) NOT NULL DEFAULT '',
+    args_str      text         NOT NULL,
+    ext_attrs     text         NOT NULL,
+    retry_status  smallint     NOT NULL DEFAULT 0,
+    task_type     smallint     NOT NULL DEFAULT 1,
+    create_dt     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_retry_task_log_01 ON sj_retry_task_log (namespace_id, group_name, scene_name);
+CREATE INDEX idx_sj_retry_task_log_02 ON sj_retry_task_log (retry_status);
+CREATE INDEX idx_sj_retry_task_log_03 ON sj_retry_task_log (idempotent_id);
+CREATE INDEX idx_sj_retry_task_log_04 ON sj_retry_task_log (unique_id);
+CREATE INDEX idx_sj_retry_task_log_05 ON sj_retry_task_log (biz_no);
+CREATE INDEX idx_sj_retry_task_log_06 ON sj_retry_task_log (create_dt);
+
+COMMENT ON COLUMN sj_retry_task_log.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_task_log.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_task_log.unique_id IS '鍚岀粍涓媔d鍞竴';
+COMMENT ON COLUMN sj_retry_task_log.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_task_log.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_task_log.idempotent_id IS '骞傜瓑id';
+COMMENT ON COLUMN sj_retry_task_log.biz_no IS '涓氬姟缂栧彿';
+COMMENT ON COLUMN sj_retry_task_log.executor_name IS '鎵ц鍣ㄥ悕绉�';
+COMMENT ON COLUMN sj_retry_task_log.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_retry_task_log.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_retry_task_log.retry_status IS '閲嶈瘯鐘舵�� 0銆侀噸璇曚腑 1銆佹垚鍔� 2銆佹渶澶ф鏁�';
+COMMENT ON COLUMN sj_retry_task_log.task_type IS '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�';
+COMMENT ON COLUMN sj_retry_task_log.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_retry_task_log.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_retry_task_log IS '浠诲姟鏃ュ織鍩虹淇℃伅琛�';
+
+-- sj_retry_task_log_message
+CREATE TABLE sj_retry_task_log_message
+(
+    id           bigserial PRIMARY KEY,
+    namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name   varchar(64) NOT NULL,
+    unique_id    varchar(64) NOT NULL,
+    message      text        NOT NULL,
+    log_num      int         NOT NULL DEFAULT 1,
+    real_time    bigint      NOT NULL DEFAULT 0,
+    create_dt    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_retry_task_log_message_01 ON sj_retry_task_log_message (namespace_id, group_name, unique_id);
+CREATE INDEX idx_sj_retry_task_log_message_02 ON sj_retry_task_log_message (create_dt);
+
+COMMENT ON COLUMN sj_retry_task_log_message.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_task_log_message.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_task_log_message.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_task_log_message.unique_id IS '鍚岀粍涓媔d鍞竴';
+COMMENT ON COLUMN sj_retry_task_log_message.message IS '寮傚父淇℃伅';
+COMMENT ON COLUMN sj_retry_task_log_message.log_num IS '鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_task_log_message.real_time IS '涓婃姤鏃堕棿';
+COMMENT ON COLUMN sj_retry_task_log_message.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON TABLE sj_retry_task_log_message IS '浠诲姟璋冨害鏃ュ織淇℃伅璁板綍琛�';
+
+-- sj_retry_scene_config
+CREATE TABLE sj_retry_scene_config
+(
+    id               bigserial PRIMARY KEY,
+    namespace_id     varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    scene_name       varchar(64)  NOT NULL,
+    group_name       varchar(64)  NOT NULL,
+    scene_status     smallint     NOT NULL DEFAULT 0,
+    max_retry_count  int          NOT NULL DEFAULT 5,
+    back_off         smallint     NOT NULL DEFAULT 1,
+    trigger_interval varchar(16)  NOT NULL DEFAULT '',
+    deadline_request bigint       NOT NULL DEFAULT 60000,
+    executor_timeout int          NOT NULL DEFAULT 5,
+    route_key        smallint     NOT NULL DEFAULT 4,
+    description      varchar(256) NOT NULL DEFAULT '',
+    create_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_retry_scene_config_01 ON sj_retry_scene_config (namespace_id, group_name, scene_name);
+
+COMMENT ON COLUMN sj_retry_scene_config.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_scene_config.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_scene_config.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_scene_config.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_scene_config.scene_status IS '缁勭姸鎬� 0銆佹湭鍚敤 1銆佸惎鐢�';
+COMMENT ON COLUMN sj_retry_scene_config.max_retry_count IS '鏈�澶ч噸璇曟鏁�';
+COMMENT ON COLUMN sj_retry_scene_config.back_off IS '1銆侀粯璁ょ瓑绾� 2銆佸浐瀹氶棿闅旀椂闂� 3銆丆RON 琛ㄨ揪寮�';
+COMMENT ON COLUMN sj_retry_scene_config.trigger_interval IS '闂撮殧鏃堕暱';
+COMMENT ON COLUMN sj_retry_scene_config.deadline_request IS 'Deadline Request 璋冪敤閾捐秴鏃� 鍗曚綅姣';
+COMMENT ON COLUMN sj_retry_scene_config.executor_timeout IS '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇';
+COMMENT ON COLUMN sj_retry_scene_config.route_key IS '璺敱绛栫暐';
+COMMENT ON COLUMN sj_retry_scene_config.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_retry_scene_config.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_retry_scene_config.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_retry_scene_config IS '鍦烘櫙閰嶇疆';
+
+-- sj_server_node
+CREATE TABLE sj_server_node
+(
+    id           bigserial PRIMARY KEY,
+    namespace_id varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name   varchar(64)  NOT NULL,
+    host_id      varchar(64)  NOT NULL,
+    host_ip      varchar(64)  NOT NULL,
+    host_port    int          NOT NULL,
+    expire_at    timestamp    NOT NULL,
+    node_type    smallint     NOT NULL,
+    ext_attrs    varchar(256) NULL     DEFAULT '',
+    create_dt    timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt    timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_server_node_01 ON sj_server_node (host_id, host_ip);
+
+CREATE INDEX idx_sj_server_node_01 ON sj_server_node (namespace_id, group_name);
+CREATE INDEX idx_sj_server_node_02 ON sj_server_node (expire_at, node_type);
+
+COMMENT ON COLUMN sj_server_node.id IS '涓婚敭';
+COMMENT ON COLUMN sj_server_node.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_server_node.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_server_node.host_id IS '涓绘満id';
+COMMENT ON COLUMN sj_server_node.host_ip IS '鏈哄櫒ip';
+COMMENT ON COLUMN sj_server_node.host_port IS '鏈哄櫒绔彛';
+COMMENT ON COLUMN sj_server_node.expire_at IS '杩囨湡鏃堕棿';
+COMMENT ON COLUMN sj_server_node.node_type IS '鑺傜偣绫诲瀷 1銆佸鎴风 2銆佹槸鏈嶅姟绔�';
+COMMENT ON COLUMN sj_server_node.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_server_node.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_server_node.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_server_node IS '鏈嶅姟鍣ㄨ妭鐐�';
+
+-- sj_distributed_lock
+CREATE TABLE sj_distributed_lock
+(
+    id         bigserial PRIMARY KEY,
+    name       varchar(64)  NOT NULL,
+    lock_until timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
+    locked_at  timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
+    locked_by  varchar(255) NOT NULL,
+    create_dt  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt  timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+COMMENT ON COLUMN sj_distributed_lock.id IS '涓婚敭';
+COMMENT ON COLUMN sj_distributed_lock.name IS '閿佸悕绉�';
+COMMENT ON COLUMN sj_distributed_lock.lock_until IS '閿佸畾鏃堕暱';
+COMMENT ON COLUMN sj_distributed_lock.locked_at IS '閿佸畾鏃堕棿';
+COMMENT ON COLUMN sj_distributed_lock.locked_by IS '閿佸畾鑰�';
+COMMENT ON COLUMN sj_distributed_lock.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_distributed_lock.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_distributed_lock IS '閿佸畾琛�';
+
+-- sj_system_user
+CREATE TABLE sj_system_user
+(
+    id        bigserial PRIMARY KEY,
+    username  varchar(64)  NOT NULL,
+    password  varchar(128) NOT NULL,
+    role      smallint     NOT NULL DEFAULT 0,
+    create_dt timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+COMMENT ON COLUMN sj_system_user.id IS '涓婚敭';
+COMMENT ON COLUMN sj_system_user.username IS '璐﹀彿';
+COMMENT ON COLUMN sj_system_user.password IS '瀵嗙爜';
+COMMENT ON COLUMN sj_system_user.role IS '瑙掕壊锛�1-鏅�氱敤鎴枫��2-绠$悊鍛�';
+COMMENT ON COLUMN sj_system_user.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_system_user.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_system_user IS '绯荤粺鐢ㄦ埛琛�';
+
+-- pwd: admin
+INSERT INTO sj_system_user VALUES (1, 'admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', 2, now(), now());
+
+-- sj_system_user_permission
+CREATE TABLE sj_system_user_permission
+(
+    id             bigserial PRIMARY KEY,
+    group_name     varchar(64) NOT NULL,
+    namespace_id   varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    system_user_id bigint      NOT NULL,
+    create_dt      timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt      timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_system_user_permission_01 ON sj_system_user_permission (namespace_id, group_name, system_user_id);
+
+COMMENT ON COLUMN sj_system_user_permission.id IS '涓婚敭';
+COMMENT ON COLUMN sj_system_user_permission.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_system_user_permission.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_system_user_permission.system_user_id IS '绯荤粺鐢ㄦ埛id';
+COMMENT ON COLUMN sj_system_user_permission.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_system_user_permission.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_system_user_permission IS '绯荤粺鐢ㄦ埛鏉冮檺琛�';
+
+-- sj_sequence_alloc
+CREATE TABLE sj_sequence_alloc
+(
+    id           bigserial PRIMARY KEY,
+    namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name   varchar(64) NOT NULL DEFAULT '',
+    max_id       bigint      NOT NULL DEFAULT 1,
+    step         int         NOT NULL DEFAULT 100,
+    update_dt    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_sequence_alloc_01 ON sj_sequence_alloc (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_sequence_alloc.id IS '涓婚敭';
+COMMENT ON COLUMN sj_sequence_alloc.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_sequence_alloc.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_sequence_alloc.max_id IS '鏈�澶d';
+COMMENT ON COLUMN sj_sequence_alloc.step IS '姝ラ暱';
+COMMENT ON COLUMN sj_sequence_alloc.update_dt IS '鏇存柊鏃堕棿';
+COMMENT ON TABLE sj_sequence_alloc IS '鍙锋妯″紡搴忓彿ID鍒嗛厤琛�';
+
+-- sj_job
+CREATE TABLE sj_job
+(
+    id               bigserial PRIMARY KEY,
+    namespace_id     varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name       varchar(64)  NOT NULL,
+    job_name         varchar(64)  NOT NULL,
+    args_str         text         NULL     DEFAULT NULL,
+    args_type        smallint     NOT NULL DEFAULT 1,
+    next_trigger_at  bigint       NOT NULL,
+    job_status       smallint     NOT NULL DEFAULT 1,
+    task_type        smallint     NOT NULL DEFAULT 1,
+    route_key        smallint     NOT NULL DEFAULT 4,
+    executor_type    smallint     NOT NULL DEFAULT 1,
+    executor_info    varchar(255) NULL     DEFAULT NULL,
+    trigger_type     smallint     NOT NULL,
+    trigger_interval varchar(255) NOT NULL,
+    block_strategy   smallint     NOT NULL DEFAULT 1,
+    executor_timeout int          NOT NULL DEFAULT 0,
+    max_retry_times  int          NOT NULL DEFAULT 0,
+    parallel_num     int          NOT NULL DEFAULT 1,
+    retry_interval   int          NOT NULL DEFAULT 0,
+    bucket_index     int          NOT NULL DEFAULT 0,
+    resident         smallint     NOT NULL DEFAULT 0,
+    description      varchar(256) NOT NULL DEFAULT '',
+    ext_attrs        varchar(256) NULL     DEFAULT '',
+    deleted          smallint     NOT NULL DEFAULT 0,
+    create_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name);
+CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index);
+CREATE INDEX idx_sj_job_03 ON sj_job (create_dt);
+
+COMMENT ON COLUMN sj_job.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job.job_name IS '鍚嶇О';
+COMMENT ON COLUMN sj_job.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_job.args_type IS '鍙傛暟绫诲瀷 ';
+COMMENT ON COLUMN sj_job.next_trigger_at IS '涓嬫瑙﹀彂鏃堕棿';
+COMMENT ON COLUMN sj_job.job_status IS '浠诲姟鐘舵�� 0銆佸叧闂��1銆佸紑鍚�';
+COMMENT ON COLUMN sj_job.task_type IS '浠诲姟绫诲瀷 1銆侀泦缇� 2銆佸箍鎾� 3銆佸垏鐗�';
+COMMENT ON COLUMN sj_job.route_key IS '璺敱绛栫暐';
+COMMENT ON COLUMN sj_job.executor_type IS '鎵ц鍣ㄧ被鍨�';
+COMMENT ON COLUMN sj_job.executor_info IS '鎵ц鍣ㄥ悕绉�';
+COMMENT ON COLUMN sj_job.trigger_type IS '瑙﹀彂绫诲瀷 1.CRON 琛ㄨ揪寮� 2. 鍥哄畾鏃堕棿';
+COMMENT ON COLUMN sj_job.trigger_interval IS '闂撮殧鏃堕暱';
+COMMENT ON COLUMN sj_job.block_strategy IS '闃诲绛栫暐 1銆佷涪寮� 2銆佽鐩� 3銆佸苟琛�';
+COMMENT ON COLUMN sj_job.executor_timeout IS '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇';
+COMMENT ON COLUMN sj_job.max_retry_times IS '鏈�澶ч噸璇曟鏁�';
+COMMENT ON COLUMN sj_job.parallel_num IS '骞惰鏁�';
+COMMENT ON COLUMN sj_job.retry_interval IS '閲嶈瘯闂撮殧 ( s ) ';
+COMMENT ON COLUMN sj_job.bucket_index IS 'bucket';
+COMMENT ON COLUMN sj_job.resident IS '鏄惁鏄父椹讳换鍔�';
+COMMENT ON COLUMN sj_job.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_job.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_job.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_job.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_job.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_job IS '浠诲姟淇℃伅';
+
+INSERT INTO sj_job VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', '', 0, now(), now());
+
+-- sj_job_log_message
+CREATE TABLE sj_job_log_message
+(
+    id            bigserial PRIMARY KEY,
+    namespace_id  varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name    varchar(64)  NOT NULL,
+    job_id        bigint       NOT NULL,
+    task_batch_id bigint       NOT NULL,
+    task_id       bigint       NOT NULL,
+    message       text         NOT NULL,
+    log_num       int          NOT NULL DEFAULT 1,
+    real_time     bigint       NOT NULL DEFAULT 0,
+    ext_attrs     varchar(256) NULL     DEFAULT '',
+    create_dt     timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_job_log_message_01 ON sj_job_log_message (task_batch_id, task_id);
+CREATE INDEX idx_sj_job_log_message_02 ON sj_job_log_message (create_dt);
+CREATE INDEX idx_sj_job_log_message_03 ON sj_job_log_message (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_job_log_message.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job_log_message.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job_log_message.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job_log_message.job_id IS '浠诲姟淇℃伅id';
+COMMENT ON COLUMN sj_job_log_message.task_batch_id IS '浠诲姟鎵规id';
+COMMENT ON COLUMN sj_job_log_message.task_id IS '璋冨害浠诲姟id';
+COMMENT ON COLUMN sj_job_log_message.message IS '璋冨害淇℃伅';
+COMMENT ON COLUMN sj_job_log_message.log_num IS '鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_log_message.real_time IS '涓婃姤鏃堕棿';
+COMMENT ON COLUMN sj_job_log_message.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_job_log_message.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON TABLE sj_job_log_message IS '璋冨害鏃ュ織';
+
+-- sj_job_task
+CREATE TABLE sj_job_task
+(
+    id             bigserial PRIMARY KEY,
+    namespace_id   varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name     varchar(64)  NOT NULL,
+    job_id         bigint       NOT NULL,
+    task_batch_id  bigint       NOT NULL,
+    parent_id      bigint       NOT NULL DEFAULT 0,
+    task_status    smallint     NOT NULL DEFAULT 0,
+    retry_count    int          NOT NULL DEFAULT 0,
+    client_info    varchar(128) NULL     DEFAULT NULL,
+    result_message text         NOT NULL,
+    args_str       text         NULL     DEFAULT NULL,
+    args_type      smallint     NOT NULL DEFAULT 1,
+    ext_attrs      varchar(256) NULL     DEFAULT '',
+    create_dt      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt      timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_job_task_01 ON sj_job_task (task_batch_id, task_status);
+CREATE INDEX idx_sj_job_task_02 ON sj_job_task (create_dt);
+CREATE INDEX idx_sj_job_task_03 ON sj_job_task (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_job_task.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job_task.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job_task.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job_task.job_id IS '浠诲姟淇℃伅id';
+COMMENT ON COLUMN sj_job_task.task_batch_id IS '璋冨害浠诲姟id';
+COMMENT ON COLUMN sj_job_task.parent_id IS '鐖舵墽琛屽櫒id';
+COMMENT ON COLUMN sj_job_task.task_status IS '鎵ц鐨勭姸鎬� 0銆佸け璐� 1銆佹垚鍔�';
+COMMENT ON COLUMN sj_job_task.retry_count IS '閲嶈瘯娆℃暟';
+COMMENT ON COLUMN sj_job_task.client_info IS '瀹㈡埛绔湴鍧� clientId#ip:port';
+COMMENT ON COLUMN sj_job_task.result_message IS '鎵ц缁撴灉';
+COMMENT ON COLUMN sj_job_task.args_str IS '鎵ц鏂规硶鍙傛暟';
+COMMENT ON COLUMN sj_job_task.args_type IS '鍙傛暟绫诲瀷 ';
+COMMENT ON COLUMN sj_job_task.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_job_task.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_job_task.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_job_task IS '浠诲姟瀹炰緥';
+
+-- sj_job_task_batch
+CREATE TABLE sj_job_task_batch
+(
+    id                      bigserial PRIMARY KEY,
+    namespace_id            varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name              varchar(64)  NOT NULL,
+    job_id                  bigint       NOT NULL,
+    workflow_node_id        bigint       NOT NULL DEFAULT 0,
+    parent_workflow_node_id bigint       NOT NULL DEFAULT 0,
+    workflow_task_batch_id  bigint       NOT NULL DEFAULT 0,
+    task_batch_status       smallint     NOT NULL DEFAULT 0,
+    operation_reason        smallint     NOT NULL DEFAULT 0,
+    execution_at            bigint       NOT NULL DEFAULT 0,
+    system_task_type        smallint     NOT NULL DEFAULT 3,
+    parent_id               varchar(64)  NOT NULL DEFAULT '',
+    ext_attrs               varchar(256) NULL     DEFAULT '',
+    deleted                 smallint     NOT NULL DEFAULT 0,
+    create_dt               timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt               timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_job_task_batch_01 ON sj_job_task_batch (job_id, task_batch_status);
+CREATE INDEX idx_sj_job_task_batch_02 ON sj_job_task_batch (create_dt);
+CREATE INDEX idx_sj_job_task_batch_03 ON sj_job_task_batch (namespace_id, group_name);
+CREATE INDEX idx_sj_job_task_batch_04 ON sj_job_task_batch (workflow_task_batch_id, workflow_node_id);
+
+COMMENT ON COLUMN sj_job_task_batch.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job_task_batch.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job_task_batch.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job_task_batch.job_id IS '浠诲姟id';
+COMMENT ON COLUMN sj_job_task_batch.workflow_node_id IS '宸ヤ綔娴佽妭鐐筰d';
+COMMENT ON COLUMN sj_job_task_batch.parent_workflow_node_id IS '宸ヤ綔娴佷换鍔$埗鎵规id';
+COMMENT ON COLUMN sj_job_task_batch.workflow_task_batch_id IS '宸ヤ綔娴佷换鍔℃壒娆d';
+COMMENT ON COLUMN sj_job_task_batch.task_batch_status IS '浠诲姟鎵规鐘舵�� 0銆佸け璐� 1銆佹垚鍔�';
+COMMENT ON COLUMN sj_job_task_batch.operation_reason IS '鎿嶄綔鍘熷洜';
+COMMENT ON COLUMN sj_job_task_batch.execution_at IS '浠诲姟鎵ц鏃堕棿';
+COMMENT ON COLUMN sj_job_task_batch.system_task_type IS '浠诲姟绫诲瀷 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟';
+COMMENT ON COLUMN sj_job_task_batch.parent_id IS '鐖惰妭鐐�';
+COMMENT ON COLUMN sj_job_task_batch.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_job_task_batch.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_job_task_batch.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_job_task_batch.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_job_task_batch IS '浠诲姟鎵规';
+
+-- sj_job_summary
+CREATE TABLE sj_job_summary
+(
+    id               bigserial PRIMARY KEY,
+    namespace_id     varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name       varchar(64)  NOT NULL DEFAULT '',
+    business_id      bigint       NOT NULL,
+    system_task_type smallint     NOT NULL DEFAULT 3,
+    trigger_at       timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    success_num      int          NOT NULL DEFAULT 0,
+    fail_num         int          NOT NULL DEFAULT 0,
+    fail_reason      varchar(512) NOT NULL DEFAULT '',
+    stop_num         int          NOT NULL DEFAULT 0,
+    stop_reason      varchar(512) NOT NULL DEFAULT '',
+    cancel_num       int          NOT NULL DEFAULT 0,
+    cancel_reason    varchar(512) NOT NULL DEFAULT '',
+    create_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_job_summary_01 ON sj_job_summary (trigger_at, system_task_type, business_id);
+
+CREATE INDEX idx_sj_job_summary_01 ON sj_job_summary (namespace_id, group_name, business_id);
+
+COMMENT ON COLUMN sj_job_summary.id IS '涓婚敭';
+COMMENT ON COLUMN sj_job_summary.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_job_summary.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_job_summary.business_id IS '涓氬姟id  ( job_id鎴杦orkflow_id ) ';
+COMMENT ON COLUMN sj_job_summary.system_task_type IS '浠诲姟绫诲瀷 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟';
+COMMENT ON COLUMN sj_job_summary.trigger_at IS '缁熻鏃堕棿';
+COMMENT ON COLUMN sj_job_summary.success_num IS '鎵ц鎴愬姛-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_summary.fail_num IS '鎵ц澶辫触-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_summary.fail_reason IS '澶辫触鍘熷洜';
+COMMENT ON COLUMN sj_job_summary.stop_num IS '鎵ц澶辫触-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_summary.stop_reason IS '澶辫触鍘熷洜';
+COMMENT ON COLUMN sj_job_summary.cancel_num IS '鎵ц澶辫触-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_job_summary.cancel_reason IS '澶辫触鍘熷洜';
+COMMENT ON COLUMN sj_job_summary.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_job_summary.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_job_summary IS 'DashBoard_Job';
+
+-- sj_retry_summary
+CREATE TABLE sj_retry_summary
+(
+    id            bigserial PRIMARY KEY,
+    namespace_id  varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name    varchar(64) NOT NULL DEFAULT '',
+    scene_name    varchar(50) NOT NULL DEFAULT '',
+    trigger_at    timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    running_num   int         NOT NULL DEFAULT 0,
+    finish_num    int         NOT NULL DEFAULT 0,
+    max_count_num int         NOT NULL DEFAULT 0,
+    suspend_num   int         NOT NULL DEFAULT 0,
+    create_dt     timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt     timestamp   NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE UNIQUE INDEX uk_sj_retry_summary_01 ON sj_retry_summary (namespace_id, group_name, scene_name, trigger_at);
+
+CREATE INDEX idx_sj_retry_summary_01 ON sj_retry_summary (trigger_at);
+
+COMMENT ON COLUMN sj_retry_summary.id IS '涓婚敭';
+COMMENT ON COLUMN sj_retry_summary.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_retry_summary.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_retry_summary.scene_name IS '鍦烘櫙鍚嶇О';
+COMMENT ON COLUMN sj_retry_summary.trigger_at IS '缁熻鏃堕棿';
+COMMENT ON COLUMN sj_retry_summary.running_num IS '閲嶈瘯涓�-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_summary.finish_num IS '閲嶈瘯瀹屾垚-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_summary.max_count_num IS '閲嶈瘯鍒拌揪鏈�澶ф鏁�-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_summary.suspend_num IS '鏆傚仠閲嶈瘯-鏃ュ織鏁伴噺';
+COMMENT ON COLUMN sj_retry_summary.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_retry_summary.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_retry_summary IS 'DashBoard_Retry';
+
+-- sj_workflow
+CREATE TABLE sj_workflow
+(
+    id               bigserial PRIMARY KEY,
+    workflow_name    varchar(64)  NOT NULL,
+    namespace_id     varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name       varchar(64)  NOT NULL,
+    workflow_status  smallint     NOT NULL DEFAULT 1,
+    trigger_type     smallint     NOT NULL,
+    trigger_interval varchar(255) NOT NULL,
+    next_trigger_at  bigint       NOT NULL,
+    block_strategy   smallint     NOT NULL DEFAULT 1,
+    executor_timeout int          NOT NULL DEFAULT 0,
+    description      varchar(256) NOT NULL DEFAULT '',
+    flow_info        text         NULL     DEFAULT NULL,
+    bucket_index     int          NOT NULL DEFAULT 0,
+    version          int          NOT NULL,
+    ext_attrs        varchar(256) NULL     DEFAULT '',
+    deleted          smallint     NOT NULL DEFAULT 0,
+    create_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt);
+CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_workflow.id IS '涓婚敭';
+COMMENT ON COLUMN sj_workflow.workflow_name IS '宸ヤ綔娴佸悕绉�';
+COMMENT ON COLUMN sj_workflow.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_workflow.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_workflow.workflow_status IS '宸ヤ綔娴佺姸鎬� 0銆佸叧闂��1銆佸紑鍚�';
+COMMENT ON COLUMN sj_workflow.trigger_type IS '瑙﹀彂绫诲瀷 1.CRON 琛ㄨ揪寮� 2. 鍥哄畾鏃堕棿';
+COMMENT ON COLUMN sj_workflow.trigger_interval IS '闂撮殧鏃堕暱';
+COMMENT ON COLUMN sj_workflow.next_trigger_at IS '涓嬫瑙﹀彂鏃堕棿';
+COMMENT ON COLUMN sj_workflow.block_strategy IS '闃诲绛栫暐 1銆佷涪寮� 2銆佽鐩� 3銆佸苟琛�';
+COMMENT ON COLUMN sj_workflow.executor_timeout IS '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇';
+COMMENT ON COLUMN sj_workflow.description IS '鎻忚堪';
+COMMENT ON COLUMN sj_workflow.flow_info IS '娴佺▼淇℃伅';
+COMMENT ON COLUMN sj_workflow.bucket_index IS 'bucket';
+COMMENT ON COLUMN sj_workflow.version IS '鐗堟湰鍙�';
+COMMENT ON COLUMN sj_workflow.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_workflow.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_workflow.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_workflow.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_workflow IS '宸ヤ綔娴�';
+
+-- sj_workflow_node
+CREATE TABLE sj_workflow_node
+(
+    id                   bigserial PRIMARY KEY,
+    namespace_id         varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    node_name            varchar(64)  NOT NULL,
+    group_name           varchar(64)  NOT NULL,
+    job_id               bigint       NOT NULL,
+    workflow_id          bigint       NOT NULL,
+    node_type            smallint     NOT NULL DEFAULT 1,
+    expression_type      smallint     NOT NULL DEFAULT 0,
+    fail_strategy        smallint     NOT NULL DEFAULT 1,
+    workflow_node_status smallint     NOT NULL DEFAULT 1,
+    priority_level       int          NOT NULL DEFAULT 1,
+    node_info            text         NULL     DEFAULT NULL,
+    version              int          NOT NULL,
+    ext_attrs            varchar(256) NULL     DEFAULT '',
+    deleted              smallint     NOT NULL DEFAULT 0,
+    create_dt            timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt            timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_workflow_node_01 ON sj_workflow_node (create_dt);
+CREATE INDEX idx_sj_workflow_node_02 ON sj_workflow_node (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_workflow_node.id IS '涓婚敭';
+COMMENT ON COLUMN sj_workflow_node.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_workflow_node.node_name IS '鑺傜偣鍚嶇О';
+COMMENT ON COLUMN sj_workflow_node.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_workflow_node.job_id IS '浠诲姟淇℃伅id';
+COMMENT ON COLUMN sj_workflow_node.workflow_id IS '宸ヤ綔娴両D';
+COMMENT ON COLUMN sj_workflow_node.node_type IS '1銆佷换鍔¤妭鐐� 2銆佹潯浠惰妭鐐�';
+COMMENT ON COLUMN sj_workflow_node.expression_type IS '1銆丼pEl銆�2銆丄viator 3銆丵L';
+COMMENT ON COLUMN sj_workflow_node.fail_strategy IS '澶辫触绛栫暐 1銆佽烦杩� 2銆侀樆濉�';
+COMMENT ON COLUMN sj_workflow_node.workflow_node_status IS '宸ヤ綔娴佽妭鐐圭姸鎬� 0銆佸叧闂��1銆佸紑鍚�';
+COMMENT ON COLUMN sj_workflow_node.priority_level IS '浼樺厛绾�';
+COMMENT ON COLUMN sj_workflow_node.node_info IS '鑺傜偣淇℃伅 ';
+COMMENT ON COLUMN sj_workflow_node.version IS '鐗堟湰鍙�';
+COMMENT ON COLUMN sj_workflow_node.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_workflow_node.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_workflow_node.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_workflow_node.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_workflow_node IS '宸ヤ綔娴佽妭鐐�';
+
+-- sj_workflow_task_batch
+CREATE TABLE sj_workflow_task_batch
+(
+    id                bigserial PRIMARY KEY,
+    namespace_id      varchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name        varchar(64)  NOT NULL,
+    workflow_id       bigint       NOT NULL,
+    task_batch_status smallint     NOT NULL DEFAULT 0,
+    operation_reason  smallint     NOT NULL DEFAULT 0,
+    flow_info         text         NULL     DEFAULT NULL,
+    execution_at      bigint       NOT NULL DEFAULT 0,
+    ext_attrs         varchar(256) NULL     DEFAULT '',
+    deleted           smallint     NOT NULL DEFAULT 0,
+    create_dt         timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt         timestamp    NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX idx_sj_workflow_task_batch_01 ON sj_workflow_task_batch (workflow_id, task_batch_status);
+CREATE INDEX idx_sj_workflow_task_batch_02 ON sj_workflow_task_batch (create_dt);
+CREATE INDEX idx_sj_workflow_task_batch_03 ON sj_workflow_task_batch (namespace_id, group_name);
+
+COMMENT ON COLUMN sj_workflow_task_batch.id IS '涓婚敭';
+COMMENT ON COLUMN sj_workflow_task_batch.namespace_id IS '鍛藉悕绌洪棿id';
+COMMENT ON COLUMN sj_workflow_task_batch.group_name IS '缁勫悕绉�';
+COMMENT ON COLUMN sj_workflow_task_batch.workflow_id IS '宸ヤ綔娴佷换鍔d';
+COMMENT ON COLUMN sj_workflow_task_batch.task_batch_status IS '浠诲姟鎵规鐘舵�� 0銆佸け璐� 1銆佹垚鍔�';
+COMMENT ON COLUMN sj_workflow_task_batch.operation_reason IS '鎿嶄綔鍘熷洜';
+COMMENT ON COLUMN sj_workflow_task_batch.flow_info IS '娴佺▼淇℃伅';
+COMMENT ON COLUMN sj_workflow_task_batch.execution_at IS '浠诲姟鎵ц鏃堕棿';
+COMMENT ON COLUMN sj_workflow_task_batch.ext_attrs IS '鎵╁睍瀛楁';
+COMMENT ON COLUMN sj_workflow_task_batch.deleted IS '閫昏緫鍒犻櫎 1銆佸垹闄�';
+COMMENT ON COLUMN sj_workflow_task_batch.create_dt IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN sj_workflow_task_batch.update_dt IS '淇敼鏃堕棿';
+COMMENT ON TABLE sj_workflow_task_batch IS '宸ヤ綔娴佹壒娆�';
+
diff --git a/script/sql/powerjob.sql b/script/sql/powerjob.sql
deleted file mode 100644
index 9381b88..0000000
--- a/script/sql/powerjob.sql
+++ /dev/null
@@ -1,233 +0,0 @@
--- ----------------------------
--- Table structure for pj_app_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_app_info`;
-CREATE TABLE `pj_app_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `app_name` varchar(255) NULL DEFAULT NULL,
-    `current_server` varchar(255) NULL DEFAULT NULL,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `password` varchar(255) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    UNIQUE INDEX `uidx01_app_info`(`app_name`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 2 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Records of pj_app_info
--- ----------------------------
-INSERT INTO `pj_app_info` VALUES (1, 'ruoyi-worker', '127.0.0.1:10010', '2023-06-13 16:32:59.263000', '2023-07-04 17:25:49.798000', '123456');
-
--- ----------------------------
--- Table structure for pj_container_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_container_info`;
-CREATE TABLE `pj_container_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `app_id` bigint(0) NULL DEFAULT NULL,
-    `container_name` varchar(255) NULL DEFAULT NULL,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `last_deploy_time` datetime(6) NULL DEFAULT NULL,
-    `source_info` varchar(255) NULL DEFAULT NULL,
-    `source_type` int(0) NULL DEFAULT NULL,
-    `status` int(0) NULL DEFAULT NULL,
-    `version` varchar(255) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    INDEX `idx01_container_info`(`app_id`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Table structure for pj_instance_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_instance_info`;
-CREATE TABLE `pj_instance_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `actual_trigger_time` bigint(0) NULL DEFAULT NULL,
-    `app_id` bigint(0) NULL DEFAULT NULL,
-    `expected_trigger_time` bigint(0) NULL DEFAULT NULL,
-    `finished_time` bigint(0) NULL DEFAULT NULL,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `instance_id` bigint(0) NULL DEFAULT NULL,
-    `instance_params` longtext NULL,
-    `job_id` bigint(0) NULL DEFAULT NULL,
-    `job_params` longtext NULL,
-    `last_report_time` bigint(0) NULL DEFAULT NULL,
-    `result` longtext NULL,
-    `running_times` bigint(0) NULL DEFAULT NULL,
-    `status` int(0) NULL DEFAULT NULL,
-    `task_tracker_address` varchar(255) NULL DEFAULT NULL,
-    `type` int(0) NULL DEFAULT NULL,
-    `wf_instance_id` bigint(0) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    INDEX `idx01_instance_info`(`job_id`, `status`) USING BTREE,
-    INDEX `idx02_instance_info`(`app_id`, `status`) USING BTREE,
-    INDEX `idx03_instance_info`(`instance_id`, `status`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 4 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Table structure for pj_job_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_job_info`;
-CREATE TABLE `pj_job_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `alarm_config` varchar(255) NULL DEFAULT NULL,
-    `app_id` bigint(0) NULL DEFAULT NULL,
-    `concurrency` int(0) NULL DEFAULT NULL,
-    `designated_workers` varchar(255) NULL DEFAULT NULL,
-    `dispatch_strategy` int(0) NULL DEFAULT NULL,
-    `execute_type` int(0) NULL DEFAULT NULL,
-    `extra` varchar(255) NULL DEFAULT NULL,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `instance_retry_num` int(0) NULL DEFAULT NULL,
-    `instance_time_limit` bigint(0) NULL DEFAULT NULL,
-    `job_description` varchar(255) NULL DEFAULT NULL,
-    `job_name` varchar(255) NULL DEFAULT NULL,
-    `job_params` longtext NULL,
-    `lifecycle` varchar(255) NULL DEFAULT NULL,
-    `log_config` varchar(255) NULL DEFAULT NULL,
-    `max_instance_num` int(0) NULL DEFAULT NULL,
-    `max_worker_count` int(0) NULL DEFAULT NULL,
-    `min_cpu_cores` double NOT NULL,
-    `min_disk_space` double NOT NULL,
-    `min_memory_space` double NOT NULL,
-    `next_trigger_time` bigint(0) NULL DEFAULT NULL,
-    `notify_user_ids` varchar(255) NULL DEFAULT NULL,
-    `processor_info` varchar(255) NULL DEFAULT NULL,
-    `processor_type` int(0) NULL DEFAULT NULL,
-    `status` int(0) NULL DEFAULT NULL,
-    `tag` varchar(255) NULL DEFAULT NULL,
-    `task_retry_num` int(0) NULL DEFAULT NULL,
-    `time_expression` varchar(255) NULL DEFAULT NULL,
-    `time_expression_type` int(0) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    INDEX `idx01_job_info`(`app_id`, `status`, `time_expression_type`, `next_trigger_time`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 5 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Records of pj_job_info
--- ----------------------------
-INSERT INTO `pj_job_info` VALUES (1, '{\"alertThreshold\":0,\"silenceWindowLen\":0,\"statisticWindowLen\":0}', 1, 5, '', 2, 1, NULL, '2023-06-02 15:01:27.717000', '2023-07-04 17:22:12.374000', 1, 0, '', '鍗曟満澶勭悊鍣ㄦ墽琛屾祴璇�', NULL, '{}', '{\"type\":1}', 0, 0, 0, 0, 0, NULL, NULL, 'org.dromara.job.processors.StandaloneProcessorDemo', 1, 2, NULL, 1, '30000', 3);
-INSERT INTO `pj_job_info` VALUES (2, '{\"alertThreshold\":0,\"silenceWindowLen\":0,\"statisticWindowLen\":0}', 1, 5, '', 1, 2, NULL, '2023-06-02 15:04:45.342000', '2023-07-04 17:22:12.816000', 0, 0, NULL, '骞挎挱澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{\"type\":1}', 0, 0, 0, 0, 0, NULL, NULL, 'org.dromara.job.processors.BroadcastProcessorDemo', 1, 2, NULL, 1, '30000', 3);
-INSERT INTO `pj_job_info` VALUES (3, '{\"alertThreshold\":0,\"silenceWindowLen\":0,\"statisticWindowLen\":0}', 1, 5, '', 1, 4, NULL, '2023-06-02 15:13:23.519000', '2023-06-02 16:03:22.421000', 0, 0, NULL, 'Map澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{\"type\":1}', 0, 0, 0, 0, 0, NULL, NULL, 'org.dromara.job.processors.MapProcessorDemo', 1, 2, NULL, 1, '1000', 3);
-INSERT INTO `pj_job_info` VALUES (4, '{\"alertThreshold\":0,\"silenceWindowLen\":0,\"statisticWindowLen\":0}', 1, 5, '', 1, 3, NULL, '2023-06-02 15:45:25.896000', '2023-06-02 16:03:23.125000', 0, 0, NULL, 'MapReduce澶勭悊鍣ㄦ祴璇�', NULL, '{}', '{\"type\":1}', 0, 0, 0, 0, 0, NULL, NULL, 'org.dromara.job.processors.MapReduceProcessorDemo', 1, 2, NULL, 1, '1000', 3);
-
--- ----------------------------
--- Table structure for pj_oms_lock
--- ----------------------------
-DROP TABLE IF EXISTS `pj_oms_lock`;
-CREATE TABLE `pj_oms_lock`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `lock_name` varchar(255) NULL DEFAULT NULL,
-    `max_lock_time` bigint(0) NULL DEFAULT NULL,
-    `ownerip` varchar(255) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    UNIQUE INDEX `uidx01_oms_lock`(`lock_name`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 6 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Table structure for pj_server_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_server_info`;
-CREATE TABLE `pj_server_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `ip` varchar(255) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    UNIQUE INDEX `uidx01_server_info`(`ip`) USING BTREE,
-    INDEX `idx01_server_info`(`gmt_modified`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 2 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Table structure for pj_user_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_user_info`;
-CREATE TABLE `pj_user_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `email` varchar(255) NULL DEFAULT NULL,
-    `extra` varchar(255) NULL DEFAULT NULL,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `password` varchar(255) NULL DEFAULT NULL,
-    `phone` varchar(255) NULL DEFAULT NULL,
-    `username` varchar(255) NULL DEFAULT NULL,
-    `web_hook` varchar(255) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    INDEX `uidx01_user_info`(`username`) USING BTREE,
-    INDEX `uidx02_user_info`(`email`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Table structure for pj_workflow_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_workflow_info`;
-CREATE TABLE `pj_workflow_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `app_id` bigint(0) NULL DEFAULT NULL,
-    `extra` varchar(255) NULL DEFAULT NULL,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `lifecycle` varchar(255) NULL DEFAULT NULL,
-    `max_wf_instance_num` int(0) NULL DEFAULT NULL,
-    `next_trigger_time` bigint(0) NULL DEFAULT NULL,
-    `notify_user_ids` varchar(255) NULL DEFAULT NULL,
-    `pedag` longtext NULL,
-    `status` int(0) NULL DEFAULT NULL,
-    `time_expression` varchar(255) NULL DEFAULT NULL,
-    `time_expression_type` int(0) NULL DEFAULT NULL,
-    `wf_description` varchar(255) NULL DEFAULT NULL,
-    `wf_name` varchar(255) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    INDEX `idx01_workflow_info`(`app_id`, `status`, `time_expression_type`, `next_trigger_time`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Table structure for pj_workflow_instance_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_workflow_instance_info`;
-CREATE TABLE `pj_workflow_instance_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `actual_trigger_time` bigint(0) NULL DEFAULT NULL,
-    `app_id` bigint(0) NULL DEFAULT NULL,
-    `dag` longtext NULL,
-    `expected_trigger_time` bigint(0) NULL DEFAULT NULL,
-    `finished_time` bigint(0) NULL DEFAULT NULL,
-    `gmt_create` datetime(6) NULL DEFAULT NULL,
-    `gmt_modified` datetime(6) NULL DEFAULT NULL,
-    `parent_wf_instance_id` bigint(0) NULL DEFAULT NULL,
-    `result` longtext NULL,
-    `status` int(0) NULL DEFAULT NULL,
-    `wf_context` longtext NULL,
-    `wf_init_params` longtext NULL,
-    `wf_instance_id` bigint(0) NULL DEFAULT NULL,
-    `workflow_id` bigint(0) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    UNIQUE INDEX `uidx01_wf_instance`(`wf_instance_id`) USING BTREE,
-    INDEX `idx01_wf_instance`(`workflow_id`, `status`, `app_id`, `expected_trigger_time`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1 ROW_FORMAT = Dynamic;
-
--- ----------------------------
--- Table structure for pj_workflow_node_info
--- ----------------------------
-DROP TABLE IF EXISTS `pj_workflow_node_info`;
-CREATE TABLE `pj_workflow_node_info`  (
-    `id` bigint(0) NOT NULL AUTO_INCREMENT,
-    `app_id` bigint(0) NOT NULL,
-    `enable` bit(1) NOT NULL,
-    `extra` longtext NULL,
-    `gmt_create` datetime(6) NULL,
-    `gmt_modified` datetime(6) NULL,
-    `job_id` bigint(0) NULL DEFAULT NULL,
-    `node_name` varchar(255) NULL DEFAULT NULL,
-    `node_params` longtext NULL,
-    `skip_when_failed` bit(1) NOT NULL,
-    `type` int(0) NULL DEFAULT NULL,
-    `workflow_id` bigint(0) NULL DEFAULT NULL,
-    PRIMARY KEY (`id`) USING BTREE,
-    INDEX `idx01_workflow_node_info`(`workflow_id`, `gmt_create`) USING BTREE
-) ENGINE = InnoDB AUTO_INCREMENT = 1 ROW_FORMAT = Dynamic;
diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql
index ea365bd..396178f 100644
--- a/script/sql/ry_vue_5.X.sql
+++ b/script/sql/ry_vue_5.X.sql
@@ -1,7 +1,6 @@
 -- ----------------------------
 -- 绗笁鏂瑰钩鍙版巿鏉冭〃
 -- ----------------------------
-drop table if exists sys_social;
 create table sys_social
 (
     id                 bigint           not null        comment '涓婚敭',
@@ -21,7 +20,7 @@
     union_id           varchar(255)     default null    comment '鐢ㄦ埛鐨� unionid',
     scope              varchar(255)     default null    comment '鎺堜簣鐨勬潈闄愶紝閮ㄥ垎骞冲彴鍙兘娌℃湁',
     token_type         varchar(255)     default null    comment '涓埆骞冲彴鐨勬巿鏉冧俊鎭紝閮ㄥ垎骞冲彴鍙兘娌℃湁',
-    id_token           varchar(255)     default null    comment 'id token锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�',
+    id_token           varchar(2000)    default null    comment 'id token锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�',
     mac_algorithm      varchar(255)     default null    comment '灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁',
     mac_key            varchar(255)     default null    comment '灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁',
     code               varchar(255)     default null    comment '鐢ㄦ埛鐨勬巿鏉僣ode锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�',
@@ -40,7 +39,6 @@
 -- ----------------------------
 -- 绉熸埛琛�
 -- ----------------------------
-drop table if exists sys_tenant;
 create table sys_tenant
 (
     id                bigint(20)    not null        comment 'id',
@@ -77,7 +75,6 @@
 -- ----------------------------
 -- 绉熸埛濂楅琛�
 -- ----------------------------
-drop table if exists sys_tenant_package;
 create table sys_tenant_package (
     package_id              bigint(20)     not null    comment '绉熸埛濂楅id',
     package_name            varchar(20)                comment '濂楅鍚嶇О',
@@ -98,13 +95,13 @@
 -- ----------------------------
 -- 1銆侀儴闂ㄨ〃
 -- ----------------------------
-drop table if exists sys_dept;
 create table sys_dept (
     dept_id           bigint(20)      not null                   comment '閮ㄩ棬id',
     tenant_id         varchar(20)     default '000000'           comment '绉熸埛缂栧彿',
     parent_id         bigint(20)      default 0                  comment '鐖堕儴闂╥d',
     ancestors         varchar(500)    default ''                 comment '绁栫骇鍒楄〃',
     dept_name         varchar(30)     default ''                 comment '閮ㄩ棬鍚嶇О',
+    dept_category     varchar(100)    default null               comment '閮ㄩ棬绫诲埆缂栫爜',
     order_num         int(4)          default 0                  comment '鏄剧ず椤哄簭',
     leader            bigint(20)      default null               comment '璐熻矗浜�',
     phone             varchar(11)     default null               comment '鑱旂郴鐢佃瘽',
@@ -124,22 +121,21 @@
 -- ----------------------------
 
 
-insert into sys_dept values(100, '000000', 0,   '0',          'XXX绉戞妧',   0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(101, '000000', 100, '0,100',      '娣卞湷鎬诲叕鍙�', 1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(102, '000000', 100, '0,100',      '闀挎矙鍒嗗叕鍙�', 2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(103, '000000', 101, '0,100,101',  '鐮斿彂閮ㄩ棬',   1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(104, '000000', 101, '0,100,101',  '甯傚満閮ㄩ棬',   2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(105, '000000', 101, '0,100,101',  '娴嬭瘯閮ㄩ棬',   3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(106, '000000', 101, '0,100,101',  '璐㈠姟閮ㄩ棬',   4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(107, '000000', 101, '0,100,101',  '杩愮淮閮ㄩ棬',   5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(108, '000000', 102, '0,100,102',  '甯傚満閮ㄩ棬',   1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
-insert into sys_dept values(109, '000000', 102, '0,100,102',  '璐㈠姟閮ㄩ棬',   2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(100, '000000', 0,   '0',          'XXX绉戞妧',   null,0, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(101, '000000', 100, '0,100',      '娣卞湷鎬诲叕鍙�', null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(102, '000000', 100, '0,100',      '闀挎矙鍒嗗叕鍙�', null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(103, '000000', 101, '0,100,101',  '鐮斿彂閮ㄩ棬',   null,1, 1, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(104, '000000', 101, '0,100,101',  '甯傚満閮ㄩ棬',   null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(105, '000000', 101, '0,100,101',  '娴嬭瘯閮ㄩ棬',   null,3, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(106, '000000', 101, '0,100,101',  '璐㈠姟閮ㄩ棬',   null,4, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(107, '000000', 101, '0,100,101',  '杩愮淮閮ㄩ棬',   null,5, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(108, '000000', 102, '0,100,102',  '甯傚満閮ㄩ棬',   null,1, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
+insert into sys_dept values(109, '000000', 102, '0,100,102',  '璐㈠姟閮ㄩ棬',   null,2, null, '15888888888', 'xxx@qq.com', '0', '0', 103, 1, sysdate(), null, null);
 
 
 -- ----------------------------
 -- 2銆佺敤鎴蜂俊鎭〃
 -- ----------------------------
-drop table if exists sys_user;
 create table sys_user (
     user_id           bigint(20)      not null                   comment '鐢ㄦ埛ID',
     tenant_id         varchar(20)     default '000000'           comment '绉熸埛缂栧彿',
@@ -169,18 +165,19 @@
 -- 鍒濆鍖�-鐢ㄦ埛淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
 insert into sys_user values(1, '000000', 103, 'admin', '鐤媯鐨勭嫯瀛怢i', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), null, null, '绠$悊鍛�');
-insert into sys_user values(2, '000000', 105, 'lionli', '鐤媯鐨勭嫯瀛怢i', 'sys_user', 'crazyLionLi@qq.com',  '15666666666', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), null, null, '娴嬭瘯鍛�');
-
+insert into sys_user values(3, '000000', 108, 'test', '鏈儴闂ㄥ強浠ヤ笅 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 3, sysdate(), null);
+insert into sys_user values(4, '000000', 102, 'test1', '浠呮湰浜� 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 4, sysdate(), null);
 
 -- ----------------------------
 -- 3銆佸矖浣嶄俊鎭〃
 -- ----------------------------
-drop table if exists sys_post;
 create table sys_post
 (
     post_id       bigint(20)      not null                   comment '宀椾綅ID',
     tenant_id     varchar(20)     default '000000'           comment '绉熸埛缂栧彿',
+    dept_id       bigint(20)      not null                   comment '閮ㄩ棬id',
     post_code     varchar(64)     not null                   comment '宀椾綅缂栫爜',
+    post_category varchar(100)    default null               comment '宀椾綅绫诲埆缂栫爜',
     post_name     varchar(50)     not null                   comment '宀椾綅鍚嶇О',
     post_sort     int(4)          not null                   comment '鏄剧ず椤哄簭',
     status        char(1)         not null                   comment '鐘舵�侊紙0姝e父 1鍋滅敤锛�',
@@ -196,16 +193,15 @@
 -- ----------------------------
 -- 鍒濆鍖�-宀椾綅淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
-insert into sys_post values(1, '000000', 'ceo',  '钁d簨闀�',    1, '0', 103, 1, sysdate(), null, null, '');
-insert into sys_post values(2, '000000', 'se',   '椤圭洰缁忕悊',  2, '0', 103, 1, sysdate(), null, null, '');
-insert into sys_post values(3, '000000', 'hr',   '浜哄姏璧勬簮',  3, '0', 103, 1, sysdate(), null, null, '');
-insert into sys_post values(4, '000000', 'user', '鏅�氬憳宸�',  4, '0', 103, 1, sysdate(), null, null, '');
+insert into sys_post values(1, '000000', 103, 'ceo',  null, '钁d簨闀�',    1, '0', 103, 1, sysdate(), null, null, '');
+insert into sys_post values(2, '000000', 100, 'se',   null, '椤圭洰缁忕悊',  2, '0', 103, 1, sysdate(), null, null, '');
+insert into sys_post values(3, '000000', 100, 'hr',   null, '浜哄姏璧勬簮',  3, '0', 103, 1, sysdate(), null, null, '');
+insert into sys_post values(4, '000000', 100, 'user', null, '鏅�氬憳宸�',  4, '0', 103, 1, sysdate(), null, null, '');
 
 
 -- ----------------------------
 -- 4銆佽鑹蹭俊鎭〃
 -- ----------------------------
-drop table if exists sys_role;
 create table sys_role (
     role_id              bigint(20)      not null                   comment '瑙掕壊ID',
     tenant_id            varchar(20)     default '000000'           comment '绉熸埛缂栧彿',
@@ -230,13 +226,12 @@
 -- 鍒濆鍖�-瑙掕壊淇℃伅琛ㄦ暟鎹�
 -- ----------------------------
 insert into sys_role values(1, '000000', '瓒呯骇绠$悊鍛�',  'superadmin',  1, 1, 1, 1, '0', '0', 103, 1, sysdate(), null, null, '瓒呯骇绠$悊鍛�');
-insert into sys_role values(2, '000000', '鏅�氳鑹�',    'common', 2, 2, 1, 1, '0', '0', 103, 1, sysdate(), null, null, '鏅�氳鑹�');
-
+insert into sys_role values(3, '000000', '鏈儴闂ㄥ強浠ヤ笅', 'test1', 3, 4, 1, 1, '0', '0', 103, 1, sysdate(), null, null, '');
+insert into sys_role values(4, '000000', '浠呮湰浜�',      'test2', 4, 5, 1, 1, '0', '0', 103, 1, sysdate(), null, null, '');
 
 -- ----------------------------
 -- 5銆佽彍鍗曟潈闄愯〃
 -- ----------------------------
-drop table if exists sys_menu;
 create table sys_menu (
     menu_id           bigint(20)      not null                   comment '鑿滃崟ID',
     menu_name         varchar(50)     not null                   comment '鑿滃崟鍚嶇О',
@@ -270,6 +265,7 @@
 insert into sys_menu values('2', '绯荤粺鐩戞帶', '0', '3', 'monitor',          null, '', 1, 0, 'M', '0', '0', '', 'monitor',  103, 1, sysdate(), null, null, '绯荤粺鐩戞帶鐩綍');
 insert into sys_menu values('3', '绯荤粺宸ュ叿', '0', '4', 'tool',             null, '', 1, 0, 'M', '0', '0', '', 'tool',     103, 1, sysdate(), null, null, '绯荤粺宸ュ叿鐩綍');
 insert into sys_menu values('4', 'PLUS瀹樼綉', '0', '5', 'https://gitee.com/dromara/RuoYi-Vue-Plus', null, '', 0, 0, 'M', '0', '0', '', 'guide',    103, 1, sysdate(), null, null, 'RuoYi-Vue-Plus瀹樼綉鍦板潃');
+insert into sys_menu values('5', '娴嬭瘯鑿滃崟', '0', '5', 'demo',             null, '', 1, 0, 'M', '0', '0', '', 'star',     103, 1, sysdate(), null, null, '娴嬭瘯鑿滃崟');
 -- 浜岀骇鑿滃崟
 insert into sys_menu values('100',  '鐢ㄦ埛绠$悊',     '1',   '1', 'user',             'system/user/index',            '', 1, 0, 'C', '0', '0', 'system:user:list',            'user',          103, 1, sysdate(), null, null, '鐢ㄦ埛绠$悊鑿滃崟');
 insert into sys_menu values('101',  '瑙掕壊绠$悊',     '1',   '2', 'role',             'system/role/index',            '', 1, 0, 'C', '0', '0', 'system:role:list',            'peoples',       103, 1, sysdate(), null, null, '瑙掕壊绠$悊鑿滃崟');
@@ -292,8 +288,8 @@
 insert into sys_menu values('117',  'Admin鐩戞帶',   '2',   '5',  'Admin',            'monitor/admin/index',         '', 1, 0, 'C', '0', '0', 'monitor:admin:list',           'dashboard',     103, 1, sysdate(), null, null, 'Admin鐩戞帶鑿滃崟');
 -- oss鑿滃崟
 insert into sys_menu values('118',  '鏂囦欢绠$悊',     '1',   '10', 'oss',              'system/oss/index',            '', 1, 0, 'C', '0', '0', 'system:oss:list',              'upload',        103, 1, sysdate(), null, null, '鏂囦欢绠$悊鑿滃崟');
--- powerjob server鎺у埗鍙�
-insert into sys_menu values('120',  '浠诲姟璋冨害涓績',  '2',   '5',  'powerjob',           'monitor/powerjob/index',        '', 1, 0, 'C', '0', '0', 'monitor:powerjob:list',          'job',           103, 1, sysdate(), null, null, 'PowerJob鎺у埗鍙拌彍鍗�');
+-- snail-job server鎺у埗鍙�
+insert into sys_menu values('120',  '浠诲姟璋冨害涓績',  '2',   '6',  'snailjob',     'monitor/snailjob/index',    '', 1, 0, 'C', '0', '0', 'monitor:snailjob:list',          'job',           103, 1, sysdate(), null, null, 'SnailJob鎺у埗鍙拌彍鍗�');
 
 -- 涓夌骇鑿滃崟
 insert into sys_menu values('500',  '鎿嶄綔鏃ュ織', '108', '1', 'operlog',    'monitor/operlog/index',    '', 1, 0, 'C', '0', '0', 'monitor:operlog:list',    'form',          103, 1, sysdate(), null, null, '鎿嶄綔鏃ュ織鑿滃崟');
@@ -393,11 +389,23 @@
 insert into sys_menu values('1063', '瀹㈡埛绔鐞嗕慨鏀�', '123', '3',  '#', '', '', 1, 0, 'F', '0', '0', 'system:client:edit',         '#', 103, 1, sysdate(), null, null, '');
 insert into sys_menu values('1064', '瀹㈡埛绔鐞嗗垹闄�', '123', '4',  '#', '', '', 1, 0, 'F', '0', '0', 'system:client:remove',       '#', 103, 1, sysdate(), null, null, '');
 insert into sys_menu values('1065', '瀹㈡埛绔鐞嗗鍑�', '123', '5',  '#', '', '', 1, 0, 'F', '0', '0', 'system:client:export',       '#', 103, 1, sysdate(), null, null, '');
+-- 娴嬭瘯鑿滃崟
+insert into sys_menu values('1500', '娴嬭瘯鍗曡〃',      '5',    '1', 'demo', 'demo/demo/index', '',  1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, sysdate(), null, null, '娴嬭瘯鍗曡〃鑿滃崟');
+insert into sys_menu values('1501', '娴嬭瘯鍗曡〃鏌ヨ',   '1500', '1', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:demo:query',               '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1502', '娴嬭瘯鍗曡〃鏂板',   '1500', '2', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:demo:add',                 '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1503', '娴嬭瘯鍗曡〃淇敼',   '1500', '3', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:demo:edit',                '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1504', '娴嬭瘯鍗曡〃鍒犻櫎',   '1500', '4', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:demo:remove',              '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1505', '娴嬭瘯鍗曡〃瀵煎嚭',   '1500', '5', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:demo:export',              '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1506', '娴嬭瘯鏍戣〃',      '5',    '1', 'tree', 'demo/tree/index', '',  1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, sysdate(), null, null, '娴嬭瘯鏍戣〃鑿滃崟');
+insert into sys_menu values('1507', '娴嬭瘯鏍戣〃鏌ヨ',   '1506', '1', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:tree:query',               '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1508', '娴嬭瘯鏍戣〃鏂板',   '1506', '2', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:tree:add',                 '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1509', '娴嬭瘯鏍戣〃淇敼',   '1506', '3', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:tree:edit',                '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1510', '娴嬭瘯鏍戣〃鍒犻櫎',   '1506', '4', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:tree:remove',              '#', 103, 1, sysdate(), null, null, '');
+insert into sys_menu values('1511', '娴嬭瘯鏍戣〃瀵煎嚭',   '1506', '5', '#',    '', '',  1, 0, 'F', '0', '0', 'demo:tree:export',              '#', 103, 1, sysdate(), null, null, '');
 
 -- ----------------------------
 -- 6銆佺敤鎴峰拰瑙掕壊鍏宠仈琛�  鐢ㄦ埛N-1瑙掕壊
 -- ----------------------------
-drop table if exists sys_user_role;
 create table sys_user_role (
     user_id   bigint(20) not null comment '鐢ㄦ埛ID',
     role_id   bigint(20) not null comment '瑙掕壊ID',
@@ -408,13 +416,12 @@
 -- 鍒濆鍖�-鐢ㄦ埛鍜岃鑹插叧鑱旇〃鏁版嵁
 -- ----------------------------
 insert into sys_user_role values ('1', '1');
-insert into sys_user_role values ('2', '2');
-
+insert into sys_user_role values ('3', '3');
+insert into sys_user_role values ('4', '4');
 
 -- ----------------------------
 -- 7銆佽鑹插拰鑿滃崟鍏宠仈琛�  瑙掕壊1-N鑿滃崟
 -- ----------------------------
-drop table if exists sys_role_menu;
 create table sys_role_menu (
     role_id   bigint(20) not null comment '瑙掕壊ID',
     menu_id   bigint(20) not null comment '鑿滃崟ID',
@@ -424,95 +431,93 @@
 -- ----------------------------
 -- 鍒濆鍖�-瑙掕壊鍜岃彍鍗曞叧鑱旇〃鏁版嵁
 -- ----------------------------
-insert into sys_role_menu values ('2', '1');
-insert into sys_role_menu values ('2', '2');
-insert into sys_role_menu values ('2', '3');
-insert into sys_role_menu values ('2', '4');
-insert into sys_role_menu values ('2', '100');
-insert into sys_role_menu values ('2', '101');
-insert into sys_role_menu values ('2', '102');
-insert into sys_role_menu values ('2', '103');
-insert into sys_role_menu values ('2', '104');
-insert into sys_role_menu values ('2', '105');
-insert into sys_role_menu values ('2', '106');
-insert into sys_role_menu values ('2', '107');
-insert into sys_role_menu values ('2', '108');
-insert into sys_role_menu values ('2', '109');
-insert into sys_role_menu values ('2', '110');
-insert into sys_role_menu values ('2', '111');
-insert into sys_role_menu values ('2', '112');
-insert into sys_role_menu values ('2', '113');
-insert into sys_role_menu values ('2', '114');
-insert into sys_role_menu values ('2', '115');
-insert into sys_role_menu values ('2', '116');
-insert into sys_role_menu values ('2', '500');
-insert into sys_role_menu values ('2', '501');
-insert into sys_role_menu values ('2', '1000');
-insert into sys_role_menu values ('2', '1001');
-insert into sys_role_menu values ('2', '1002');
-insert into sys_role_menu values ('2', '1003');
-insert into sys_role_menu values ('2', '1004');
-insert into sys_role_menu values ('2', '1005');
-insert into sys_role_menu values ('2', '1006');
-insert into sys_role_menu values ('2', '1007');
-insert into sys_role_menu values ('2', '1008');
-insert into sys_role_menu values ('2', '1009');
-insert into sys_role_menu values ('2', '1010');
-insert into sys_role_menu values ('2', '1011');
-insert into sys_role_menu values ('2', '1012');
-insert into sys_role_menu values ('2', '1013');
-insert into sys_role_menu values ('2', '1014');
-insert into sys_role_menu values ('2', '1015');
-insert into sys_role_menu values ('2', '1016');
-insert into sys_role_menu values ('2', '1017');
-insert into sys_role_menu values ('2', '1018');
-insert into sys_role_menu values ('2', '1019');
-insert into sys_role_menu values ('2', '1020');
-insert into sys_role_menu values ('2', '1021');
-insert into sys_role_menu values ('2', '1022');
-insert into sys_role_menu values ('2', '1023');
-insert into sys_role_menu values ('2', '1024');
-insert into sys_role_menu values ('2', '1025');
-insert into sys_role_menu values ('2', '1026');
-insert into sys_role_menu values ('2', '1027');
-insert into sys_role_menu values ('2', '1028');
-insert into sys_role_menu values ('2', '1029');
-insert into sys_role_menu values ('2', '1030');
-insert into sys_role_menu values ('2', '1031');
-insert into sys_role_menu values ('2', '1032');
-insert into sys_role_menu values ('2', '1033');
-insert into sys_role_menu values ('2', '1034');
-insert into sys_role_menu values ('2', '1035');
-insert into sys_role_menu values ('2', '1036');
-insert into sys_role_menu values ('2', '1037');
-insert into sys_role_menu values ('2', '1038');
-insert into sys_role_menu values ('2', '1039');
-insert into sys_role_menu values ('2', '1040');
-insert into sys_role_menu values ('2', '1041');
-insert into sys_role_menu values ('2', '1042');
-insert into sys_role_menu values ('2', '1043');
-insert into sys_role_menu values ('2', '1044');
-insert into sys_role_menu values ('2', '1045');
-insert into sys_role_menu values ('2', '1050');
-insert into sys_role_menu values ('2', '1046');
-insert into sys_role_menu values ('2', '1047');
-insert into sys_role_menu values ('2', '1048');
-insert into sys_role_menu values ('2', '1055');
-insert into sys_role_menu values ('2', '1056');
-insert into sys_role_menu values ('2', '1057');
-insert into sys_role_menu values ('2', '1058');
-insert into sys_role_menu values ('2', '1059');
-insert into sys_role_menu values ('2', '1060');
-insert into sys_role_menu values ('2', '1061');
-insert into sys_role_menu values ('2', '1062');
-insert into sys_role_menu values ('2', '1063');
-insert into sys_role_menu values ('2', '1064');
-insert into sys_role_menu values ('2', '1065');
+insert into sys_role_menu values ('3', '1');
+insert into sys_role_menu values ('3', '5');
+insert into sys_role_menu values ('3', '100');
+insert into sys_role_menu values ('3', '101');
+insert into sys_role_menu values ('3', '102');
+insert into sys_role_menu values ('3', '103');
+insert into sys_role_menu values ('3', '104');
+insert into sys_role_menu values ('3', '105');
+insert into sys_role_menu values ('3', '106');
+insert into sys_role_menu values ('3', '107');
+insert into sys_role_menu values ('3', '108');
+insert into sys_role_menu values ('3', '500');
+insert into sys_role_menu values ('3', '501');
+insert into sys_role_menu values ('3', '1001');
+insert into sys_role_menu values ('3', '1002');
+insert into sys_role_menu values ('3', '1003');
+insert into sys_role_menu values ('3', '1004');
+insert into sys_role_menu values ('3', '1005');
+insert into sys_role_menu values ('3', '1006');
+insert into sys_role_menu values ('3', '1007');
+insert into sys_role_menu values ('3', '1008');
+insert into sys_role_menu values ('3', '1009');
+insert into sys_role_menu values ('3', '1010');
+insert into sys_role_menu values ('3', '1011');
+insert into sys_role_menu values ('3', '1012');
+insert into sys_role_menu values ('3', '1013');
+insert into sys_role_menu values ('3', '1014');
+insert into sys_role_menu values ('3', '1015');
+insert into sys_role_menu values ('3', '1016');
+insert into sys_role_menu values ('3', '1017');
+insert into sys_role_menu values ('3', '1018');
+insert into sys_role_menu values ('3', '1019');
+insert into sys_role_menu values ('3', '1020');
+insert into sys_role_menu values ('3', '1021');
+insert into sys_role_menu values ('3', '1022');
+insert into sys_role_menu values ('3', '1023');
+insert into sys_role_menu values ('3', '1024');
+insert into sys_role_menu values ('3', '1025');
+insert into sys_role_menu values ('3', '1026');
+insert into sys_role_menu values ('3', '1027');
+insert into sys_role_menu values ('3', '1028');
+insert into sys_role_menu values ('3', '1029');
+insert into sys_role_menu values ('3', '1030');
+insert into sys_role_menu values ('3', '1031');
+insert into sys_role_menu values ('3', '1032');
+insert into sys_role_menu values ('3', '1033');
+insert into sys_role_menu values ('3', '1034');
+insert into sys_role_menu values ('3', '1035');
+insert into sys_role_menu values ('3', '1036');
+insert into sys_role_menu values ('3', '1037');
+insert into sys_role_menu values ('3', '1038');
+insert into sys_role_menu values ('3', '1039');
+insert into sys_role_menu values ('3', '1040');
+insert into sys_role_menu values ('3', '1041');
+insert into sys_role_menu values ('3', '1042');
+insert into sys_role_menu values ('3', '1043');
+insert into sys_role_menu values ('3', '1044');
+insert into sys_role_menu values ('3', '1045');
+insert into sys_role_menu values ('3', '1500');
+insert into sys_role_menu values ('3', '1501');
+insert into sys_role_menu values ('3', '1502');
+insert into sys_role_menu values ('3', '1503');
+insert into sys_role_menu values ('3', '1504');
+insert into sys_role_menu values ('3', '1505');
+insert into sys_role_menu values ('3', '1506');
+insert into sys_role_menu values ('3', '1507');
+insert into sys_role_menu values ('3', '1508');
+insert into sys_role_menu values ('3', '1509');
+insert into sys_role_menu values ('3', '1510');
+insert into sys_role_menu values ('3', '1511');
+insert into sys_role_menu values ('4', '5');
+insert into sys_role_menu values ('4', '1500');
+insert into sys_role_menu values ('4', '1501');
+insert into sys_role_menu values ('4', '1502');
+insert into sys_role_menu values ('4', '1503');
+insert into sys_role_menu values ('4', '1504');
+insert into sys_role_menu values ('4', '1505');
+insert into sys_role_menu values ('4', '1506');
+insert into sys_role_menu values ('4', '1507');
+insert into sys_role_menu values ('4', '1508');
+insert into sys_role_menu values ('4', '1509');
+insert into sys_role_menu values ('4', '1510');
+insert into sys_role_menu values ('4', '1511');
 
 -- ----------------------------
 -- 8銆佽鑹插拰閮ㄩ棬鍏宠仈琛�  瑙掕壊1-N閮ㄩ棬
 -- ----------------------------
-drop table if exists sys_role_dept;
 create table sys_role_dept (
     role_id   bigint(20) not null comment '瑙掕壊ID',
     dept_id   bigint(20) not null comment '閮ㄩ棬ID',
@@ -520,17 +525,8 @@
 ) engine=innodb comment = '瑙掕壊鍜岄儴闂ㄥ叧鑱旇〃';
 
 -- ----------------------------
--- 鍒濆鍖�-瑙掕壊鍜岄儴闂ㄥ叧鑱旇〃鏁版嵁
--- ----------------------------
-insert into sys_role_dept values ('2', '100');
-insert into sys_role_dept values ('2', '101');
-insert into sys_role_dept values ('2', '105');
-
-
--- ----------------------------
 -- 9銆佺敤鎴蜂笌宀椾綅鍏宠仈琛�  鐢ㄦ埛1-N宀椾綅
 -- ----------------------------
-drop table if exists sys_user_post;
 create table sys_user_post
 (
     user_id   bigint(20) not null comment '鐢ㄦ埛ID',
@@ -542,13 +538,10 @@
 -- 鍒濆鍖�-鐢ㄦ埛涓庡矖浣嶅叧鑱旇〃鏁版嵁
 -- ----------------------------
 insert into sys_user_post values ('1', '1');
-insert into sys_user_post values ('2', '2');
-
 
 -- ----------------------------
 -- 10銆佹搷浣滄棩蹇楄褰�
 -- ----------------------------
-drop table if exists sys_oper_log;
 create table sys_oper_log (
     oper_id           bigint(20)      not null                   comment '鏃ュ織涓婚敭',
     tenant_id         varchar(20)     default '000000'           comment '绉熸埛缂栧彿',
@@ -578,7 +571,6 @@
 -- ----------------------------
 -- 11銆佸瓧鍏哥被鍨嬭〃
 -- ----------------------------
-drop table if exists sys_dict_type;
 create table sys_dict_type
 (
     dict_id          bigint(20)      not null                   comment '瀛楀吀涓婚敭',
@@ -610,7 +602,6 @@
 -- ----------------------------
 -- 12銆佸瓧鍏告暟鎹〃
 -- ----------------------------
-drop table if exists sys_dict_data;
 create table sys_dict_data
 (
     dict_code        bigint(20)      not null                   comment '瀛楀吀缂栫爜',
@@ -670,7 +661,6 @@
 -- ----------------------------
 -- 13銆佸弬鏁伴厤缃〃
 -- ----------------------------
-drop table if exists sys_config;
 create table sys_config (
     config_id         bigint(20)      not null                   comment '鍙傛暟涓婚敭',
     tenant_id         varchar(20)     default '000000'           comment '绉熸埛缂栧彿',
@@ -697,7 +687,6 @@
 -- ----------------------------
 -- 14銆佺郴缁熻闂褰�
 -- ----------------------------
-drop table if exists sys_logininfor;
 create table sys_logininfor (
     info_id        bigint(20)     not null                  comment '璁块棶ID',
     tenant_id      varchar(20)    default '000000'          comment '绉熸埛缂栧彿',
@@ -720,7 +709,6 @@
 -- ----------------------------
 -- 17銆侀�氱煡鍏憡琛�
 -- ----------------------------
-drop table if exists sys_notice;
 create table sys_notice (
     notice_id         bigint(20)      not null                   comment '鍏憡ID',
     tenant_id         varchar(20)     default '000000'           comment '绉熸埛缂栧彿',
@@ -747,7 +735,6 @@
 -- ----------------------------
 -- 18銆佷唬鐮佺敓鎴愪笟鍔¤〃
 -- ----------------------------
-drop table if exists gen_table;
 create table gen_table (
     table_id          bigint(20)      not null                   comment '缂栧彿',
     data_name         varchar(200)    default ''                 comment '鏁版嵁婧愬悕绉�',
@@ -778,7 +765,6 @@
 -- ----------------------------
 -- 19銆佷唬鐮佺敓鎴愪笟鍔¤〃瀛楁
 -- ----------------------------
-drop table if exists gen_table_column;
 create table gen_table_column (
     column_id         bigint(20)      not null                   comment '缂栧彿',
     table_id          bigint(20)                                 comment '褰掑睘琛ㄧ紪鍙�',
@@ -809,7 +795,6 @@
 -- ----------------------------
 -- OSS瀵硅薄瀛樺偍琛�
 -- ----------------------------
-drop table if exists sys_oss;
 create table sys_oss (
     oss_id          bigint(20)   not null                   comment '瀵硅薄瀛樺偍涓婚敭',
     tenant_id       varchar(20)           default '000000'  comment '绉熸埛缂栧彿',
@@ -829,9 +814,8 @@
 -- ----------------------------
 -- OSS瀵硅薄瀛樺偍鍔ㄦ�侀厤缃〃
 -- ----------------------------
-drop table if exists sys_oss_config;
 create table sys_oss_config (
-    oss_config_id   bigint(20)    not null                  comment '涓诲缓',
+    oss_config_id   bigint(20)    not null                  comment '涓婚敭',
     tenant_id       varchar(20)             default '000000'comment '绉熸埛缂栧彿',
     config_key      varchar(20)   not null  default ''      comment '閰嶇疆key',
     access_key      varchar(255)            default ''      comment 'accessKey',
@@ -863,7 +847,6 @@
 -- ----------------------------
 -- 绯荤粺鎺堟潈琛�
 -- ----------------------------
-drop table if exists sys_client;
 create table sys_client (
     id                  bigint(20)    not null            comment 'id',
     client_id           varchar(64)   default null        comment '瀹㈡埛绔痠d',
@@ -885,3 +868,69 @@
 
 insert into sys_client values (1, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 103, 1, sysdate(), 1, sysdate());
 insert into sys_client values (2, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 103, 1, sysdate(), 1, sysdate());
+
+
+CREATE TABLE test_demo
+(
+    id          bigint(0)    NOT NULL COMMENT '涓婚敭',
+    tenant_id   varchar(20)  NULL DEFAULT '000000' COMMENT '绉熸埛缂栧彿',
+    dept_id     bigint(0)    NULL DEFAULT NULL COMMENT '閮ㄩ棬id',
+    user_id     bigint(0)    NULL DEFAULT NULL COMMENT '鐢ㄦ埛id',
+    order_num   int(0)       NULL DEFAULT 0 COMMENT '鎺掑簭鍙�',
+    test_key    varchar(255) NULL DEFAULT NULL COMMENT 'key閿�',
+    value       varchar(255) NULL DEFAULT NULL COMMENT '鍊�',
+    version     int(0)       NULL DEFAULT 0 COMMENT '鐗堟湰',
+    create_dept bigint(0)    NULL DEFAULT NULL COMMENT '鍒涘缓閮ㄩ棬',
+    create_time datetime(0)  NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+    create_by   bigint(0)    NULL DEFAULT NULL COMMENT '鍒涘缓浜�',
+    update_time datetime(0)  NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+    update_by   bigint(0)    NULL DEFAULT NULL COMMENT '鏇存柊浜�',
+    del_flag    int(0)       NULL DEFAULT 0 COMMENT '鍒犻櫎鏍囧織',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB COMMENT = '娴嬭瘯鍗曡〃';
+
+CREATE TABLE test_tree
+(
+    id          bigint(0)    NOT NULL COMMENT '涓婚敭',
+    tenant_id   varchar(20)  NULL DEFAULT '000000' COMMENT '绉熸埛缂栧彿',
+    parent_id   bigint(0)    NULL DEFAULT 0 COMMENT '鐖秈d',
+    dept_id     bigint(0)    NULL DEFAULT NULL COMMENT '閮ㄩ棬id',
+    user_id     bigint(0)    NULL DEFAULT NULL COMMENT '鐢ㄦ埛id',
+    tree_name   varchar(255) NULL DEFAULT NULL COMMENT '鍊�',
+    version     int(0)       NULL DEFAULT 0 COMMENT '鐗堟湰',
+    create_dept bigint(0)    NULL DEFAULT NULL COMMENT '鍒涘缓閮ㄩ棬',
+    create_time datetime(0)  NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+    create_by   bigint(0)    NULL DEFAULT NULL COMMENT '鍒涘缓浜�',
+    update_time datetime(0)  NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+    update_by   bigint(0)    NULL DEFAULT NULL COMMENT '鏇存柊浜�',
+    del_flag    int(0)       NULL DEFAULT 0 COMMENT '鍒犻櫎鏍囧織',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB COMMENT = '娴嬭瘯鏍戣〃';
+
+INSERT INTO test_demo VALUES (1, '000000', 102, 4, 1, '娴嬭瘯鏁版嵁鏉冮檺', '娴嬭瘯', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (2, '000000', 102, 3, 2, '瀛愯妭鐐�1', '111', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (3, '000000', 102, 3, 3, '瀛愯妭鐐�2', '222', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (4, '000000', 108, 4, 4, '娴嬭瘯鏁版嵁', 'demo', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (5, '000000', 108, 3, 13, '瀛愯妭鐐�11', '1111', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (6, '000000', 108, 3, 12, '瀛愯妭鐐�22', '2222', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (7, '000000', 108, 3, 11, '瀛愯妭鐐�33', '3333', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (8, '000000', 108, 3, 10, '瀛愯妭鐐�44', '4444', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (9, '000000', 108, 3, 9, '瀛愯妭鐐�55', '5555', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (10, '000000', 108, 3, 8, '瀛愯妭鐐�66', '6666', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (11, '000000', 108, 3, 7, '瀛愯妭鐐�77', '7777', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (12, '000000', 108, 3, 6, '瀛愯妭鐐�88', '8888', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_demo VALUES (13, '000000', 108, 3, 5, '瀛愯妭鐐�99', '9999', 0, 103, sysdate(), 1, NULL, NULL, 0);
+
+INSERT INTO test_tree VALUES (1, '000000', 0, 102, 4, '娴嬭瘯鏁版嵁鏉冮檺', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (2, '000000', 1, 102, 3, '瀛愯妭鐐�1', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (3, '000000', 2, 102, 3, '瀛愯妭鐐�2', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (4, '000000', 0, 108, 4, '娴嬭瘯鏍�1', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (5, '000000', 4, 108, 3, '瀛愯妭鐐�11', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (6, '000000', 4, 108, 3, '瀛愯妭鐐�22', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (7, '000000', 4, 108, 3, '瀛愯妭鐐�33', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (8, '000000', 5, 108, 3, '瀛愯妭鐐�44', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (9, '000000', 6, 108, 3, '瀛愯妭鐐�55', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (10, '000000', 7, 108, 3, '瀛愯妭鐐�66', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (11, '000000', 7, 108, 3, '瀛愯妭鐐�77', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (12, '000000', 10, 108, 3, '瀛愯妭鐐�88', 0, 103, sysdate(), 1, NULL, NULL, 0);
+INSERT INTO test_tree VALUES (13, '000000', 10, 108, 3, '瀛愯妭鐐�99', 0, 103, sysdate(), 1, NULL, NULL, 0);
diff --git a/script/sql/snail_job.sql b/script/sql/snail_job.sql
new file mode 100644
index 0000000..ce93e11
--- /dev/null
+++ b/script/sql/snail_job.sql
@@ -0,0 +1,509 @@
+SET NAMES utf8mb4;
+
+CREATE TABLE `sj_namespace`
+(
+    `id`          bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `name`        varchar(64)         NOT NULL COMMENT '鍚嶇О',
+    `unique_id`   varchar(64)         NOT NULL COMMENT '鍞竴id',
+    `description` varchar(256)        NOT NULL DEFAULT '' COMMENT '鎻忚堪',
+    `deleted`     tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫昏緫鍒犻櫎 1銆佸垹闄�',
+    `create_dt`   datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`   datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_name` (`name`),
+    UNIQUE KEY `uk_unique_id` (`unique_id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4 COMMENT ='鍛藉悕绌洪棿';
+
+INSERT INTO `sj_namespace` VALUES (1, 'Development', 'dev', '', 0, now(), now());
+INSERT INTO `sj_namespace` VALUES (2, 'Production', 'prod', '', 0, now(), now());
+
+CREATE TABLE `sj_group_config`
+(
+    `id`                bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`      varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`        varchar(64)         NOT NULL DEFAULT '' COMMENT '缁勫悕绉�',
+    `description`       varchar(256)        NOT NULL DEFAULT '' COMMENT '缁勬弿杩�',
+    `token`             varchar(64)         NOT NULL DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT' COMMENT 'token',
+    `group_status`      tinyint(4)          NOT NULL DEFAULT 0 COMMENT '缁勭姸鎬� 0銆佹湭鍚敤 1銆佸惎鐢�',
+    `version`           int(11)             NOT NULL COMMENT '鐗堟湰鍙�',
+    `group_partition`   int(11)             NOT NULL COMMENT '鍒嗗尯',
+    `id_generator_mode` tinyint(4)          NOT NULL DEFAULT 1 COMMENT '鍞竴id鐢熸垚妯″紡 榛樿鍙锋妯″紡',
+    `init_scene`        tinyint(4)          NOT NULL DEFAULT 0 COMMENT '鏄惁鍒濆鍖栧満鏅� 0:鍚� 1:鏄�',
+    `bucket_index`      int(11)             NOT NULL DEFAULT 0 COMMENT 'bucket',
+    `create_dt`         datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`         datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_namespace_id_group_name` (`namespace_id`, `group_name`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='缁勯厤缃�';
+
+INSERT INTO `sj_group_config` VALUES (1, 'dev', 'ruoyi_group', '', 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', 1, 1, 0, 1, 1, 4, now(), now());
+
+CREATE TABLE `sj_notify_config`
+(
+    `id`                     bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`           varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`             varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `business_id`            varchar(64)         NOT NULL COMMENT '涓氬姟id (job_id鎴杦orkflow_id鎴杝cene_name)',
+    `system_task_type`       tinyint(4)          NOT NULL DEFAULT 3 COMMENT '浠诲姟绫诲瀷 1. 閲嶈瘯浠诲姟 2. 閲嶈瘯鍥炶皟 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟',
+    `notify_status`          tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫氱煡鐘舵�� 0銆佹湭鍚敤 1銆佸惎鐢�',
+    `recipient_ids`          varchar(128)        NOT NULL COMMENT '鎺ユ敹浜篿d鍒楄〃',
+    `notify_threshold`       int(11)             NOT NULL DEFAULT 0 COMMENT '閫氱煡闃堝��',
+    `notify_scene`           tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫氱煡鍦烘櫙',
+    `rate_limiter_status`    tinyint(4)          NOT NULL DEFAULT 0 COMMENT '闄愭祦鐘舵�� 0銆佹湭鍚敤 1銆佸惎鐢�',
+    `rate_limiter_threshold` int(11)             NOT NULL DEFAULT 0 COMMENT '姣忕闄愭祦闃堝��',
+    `description`            varchar(256)        NOT NULL DEFAULT '' COMMENT '鎻忚堪',
+    `create_dt`              datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`              datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `business_id`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='閫氱煡閰嶇疆';
+
+CREATE TABLE `sj_notify_recipient`
+(
+    `id`               bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`     varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `recipient_name`   varchar(64)         NOT NULL COMMENT '鎺ユ敹浜哄悕绉�',
+    `notify_type`      tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫氱煡绫诲瀷 1銆侀拤閽� 2銆侀偖浠� 3銆佷紒涓氬井淇� 4 椋炰功',
+    `notify_attribute` varchar(512)        NOT NULL COMMENT '閰嶇疆灞炴��',
+    `description`      varchar(256)        NOT NULL DEFAULT '' COMMENT '鎻忚堪',
+    `create_dt`        datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`        datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_namespace_id` (`namespace_id`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='鍛婅閫氱煡鎺ユ敹浜�';
+
+CREATE TABLE `sj_retry_dead_letter_0`
+(
+    `id`            bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`  varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `unique_id`     varchar(64)         NOT NULL COMMENT '鍚岀粍涓媔d鍞竴',
+    `group_name`    varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `scene_name`    varchar(64)         NOT NULL COMMENT '鍦烘櫙鍚嶇О',
+    `idempotent_id` varchar(64)         NOT NULL COMMENT '骞傜瓑id',
+    `biz_no`        varchar(64)         NOT NULL DEFAULT '' COMMENT '涓氬姟缂栧彿',
+    `executor_name` varchar(512)        NOT NULL DEFAULT '' COMMENT '鎵ц鍣ㄥ悕绉�',
+    `args_str`      text                NOT NULL COMMENT '鎵ц鏂规硶鍙傛暟',
+    `ext_attrs`     text                NOT NULL COMMENT '鎵╁睍瀛楁',
+    `task_type`     tinyint(4)          NOT NULL DEFAULT 1 COMMENT '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�',
+    `create_dt`     datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`),
+    KEY `idx_idempotent_id` (`idempotent_id`),
+    KEY `idx_biz_no` (`biz_no`),
+    KEY `idx_create_dt` (`create_dt`),
+    UNIQUE KEY `uk_namespace_id_group_name_unique_id` (`namespace_id`, `group_name`, `unique_id`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='姝讳俊闃熷垪琛�';
+
+CREATE TABLE `sj_retry_task_0`
+(
+    `id`              bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`    varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `unique_id`       varchar(64)         NOT NULL COMMENT '鍚岀粍涓媔d鍞竴',
+    `group_name`      varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `scene_name`      varchar(64)         NOT NULL COMMENT '鍦烘櫙鍚嶇О',
+    `idempotent_id`   varchar(64)         NOT NULL COMMENT '骞傜瓑id',
+    `biz_no`          varchar(64)         NOT NULL DEFAULT '' COMMENT '涓氬姟缂栧彿',
+    `executor_name`   varchar(512)        NOT NULL DEFAULT '' COMMENT '鎵ц鍣ㄥ悕绉�',
+    `args_str`        text                NOT NULL COMMENT '鎵ц鏂规硶鍙傛暟',
+    `ext_attrs`       text                NOT NULL COMMENT '鎵╁睍瀛楁',
+    `next_trigger_at` datetime            NOT NULL COMMENT '涓嬫瑙﹀彂鏃堕棿',
+    `retry_count`     int(11)             NOT NULL DEFAULT 0 COMMENT '閲嶈瘯娆℃暟',
+    `retry_status`    tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閲嶈瘯鐘舵�� 0銆侀噸璇曚腑 1銆佹垚鍔� 2銆佹渶澶ч噸璇曟鏁�',
+    `task_type`       tinyint(4)          NOT NULL DEFAULT 1 COMMENT '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�',
+    `create_dt`       datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`       datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`),
+    KEY `idx_namespace_id_group_name_task_type` (`namespace_id`, `group_name`, `task_type`),
+    KEY `idx_namespace_id_group_name_retry_status` (`namespace_id`, `group_name`, `retry_status`),
+    KEY `idx_idempotent_id` (`idempotent_id`),
+    KEY `idx_biz_no` (`biz_no`),
+    KEY `idx_create_dt` (`create_dt`),
+    UNIQUE KEY `uk_name_unique_id` (`namespace_id`, `group_name`, `unique_id`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='浠诲姟琛�';
+
+CREATE TABLE `sj_retry_task_log`
+(
+    `id`            bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`  varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `unique_id`     varchar(64)         NOT NULL COMMENT '鍚岀粍涓媔d鍞竴',
+    `group_name`    varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `scene_name`    varchar(64)         NOT NULL COMMENT '鍦烘櫙鍚嶇О',
+    `idempotent_id` varchar(64)         NOT NULL COMMENT '骞傜瓑id',
+    `biz_no`        varchar(64)         NOT NULL DEFAULT '' COMMENT '涓氬姟缂栧彿',
+    `executor_name` varchar(512)        NOT NULL DEFAULT '' COMMENT '鎵ц鍣ㄥ悕绉�',
+    `args_str`      text                NOT NULL COMMENT '鎵ц鏂规硶鍙傛暟',
+    `ext_attrs`     text                NOT NULL COMMENT '鎵╁睍瀛楁',
+    `retry_status`  tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閲嶈瘯鐘舵�� 0銆侀噸璇曚腑 1銆佹垚鍔� 2銆佹渶澶ф鏁�',
+    `task_type`     tinyint(4)          NOT NULL DEFAULT 1 COMMENT '浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�',
+    `create_dt`     datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`     datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`),
+    KEY `idx_retry_status` (`retry_status`),
+    KEY `idx_idempotent_id` (`idempotent_id`),
+    KEY `idx_unique_id` (`unique_id`),
+    KEY `idx_biz_no` (`biz_no`),
+    KEY `idx_create_dt` (`create_dt`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='浠诲姟鏃ュ織鍩虹淇℃伅琛�';
+
+CREATE TABLE `sj_retry_task_log_message`
+(
+    `id`           bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id` varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`   varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `unique_id`    varchar(64)         NOT NULL COMMENT '鍚岀粍涓媔d鍞竴',
+    `message`      longtext            NOT NULL COMMENT '寮傚父淇℃伅',
+    `log_num`      int(11)             NOT NULL DEFAULT 1 COMMENT '鏃ュ織鏁伴噺',
+    `real_time`    bigint(13)          NOT NULL DEFAULT 0 COMMENT '涓婃姤鏃堕棿',
+    `create_dt`    datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `unique_id`),
+    KEY `idx_create_dt` (`create_dt`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='浠诲姟璋冨害鏃ュ織淇℃伅璁板綍琛�';
+
+CREATE TABLE `sj_retry_scene_config`
+(
+    `id`               bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`     varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `scene_name`       varchar(64)         NOT NULL COMMENT '鍦烘櫙鍚嶇О',
+    `group_name`       varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `scene_status`     tinyint(4)          NOT NULL DEFAULT 0 COMMENT '缁勭姸鎬� 0銆佹湭鍚敤 1銆佸惎鐢�',
+    `max_retry_count`  int(11)             NOT NULL DEFAULT 5 COMMENT '鏈�澶ч噸璇曟鏁�',
+    `back_off`         tinyint(4)          NOT NULL DEFAULT 1 COMMENT '1銆侀粯璁ょ瓑绾� 2銆佸浐瀹氶棿闅旀椂闂� 3銆丆RON 琛ㄨ揪寮�',
+    `trigger_interval` varchar(16)         NOT NULL DEFAULT '' COMMENT '闂撮殧鏃堕暱',
+    `deadline_request` bigint(20) unsigned NOT NULL DEFAULT 60000 COMMENT 'Deadline Request 璋冪敤閾捐秴鏃� 鍗曚綅姣',
+    `executor_timeout` int(11) unsigned    NOT NULL DEFAULT 5 COMMENT '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇',
+    `route_key`        tinyint(4)          NOT NULL DEFAULT 4 COMMENT '璺敱绛栫暐',
+    `description`      varchar(256)        NOT NULL DEFAULT '' COMMENT '鎻忚堪',
+    `create_dt`        datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`        datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_namespace_id_group_name_scene_name` (`namespace_id`, `group_name`, `scene_name`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='鍦烘櫙閰嶇疆';
+
+CREATE TABLE `sj_server_node`
+(
+    `id`           bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id` varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`   varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `host_id`      varchar(64)         NOT NULL COMMENT '涓绘満id',
+    `host_ip`      varchar(64)         NOT NULL COMMENT '鏈哄櫒ip',
+    `host_port`    int(16)             NOT NULL COMMENT '鏈哄櫒绔彛',
+    `expire_at`    datetime            NOT NULL COMMENT '杩囨湡鏃堕棿',
+    `node_type`    tinyint(4)          NOT NULL COMMENT '鑺傜偣绫诲瀷 1銆佸鎴风 2銆佹槸鏈嶅姟绔�',
+    `ext_attrs`    varchar(256)        NULL     DEFAULT '' COMMENT '鎵╁睍瀛楁',
+    `create_dt`    datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`    datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`),
+    KEY `idx_expire_at_node_type` (`expire_at`, `node_type`),
+    UNIQUE KEY `uk_host_id_host_ip` (`host_id`, `host_ip`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='鏈嶅姟鍣ㄨ妭鐐�';
+
+CREATE TABLE `sj_distributed_lock`
+(
+    `id`         bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `name`       varchar(64)         NOT NULL COMMENT '閿佸悕绉�',
+    `lock_until` timestamp(3)        NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '閿佸畾鏃堕暱',
+    `locked_at`  timestamp(3)        NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '閿佸畾鏃堕棿',
+    `locked_by`  varchar(255)        NOT NULL COMMENT '閿佸畾鑰�',
+    `create_dt`  datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`  datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_name` (`name`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='閿佸畾琛�';
+
+CREATE TABLE `sj_system_user`
+(
+    `id`        bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `username`  varchar(64)         NOT NULL COMMENT '璐﹀彿',
+    `password`  varchar(128)        NOT NULL COMMENT '瀵嗙爜',
+    `role`      tinyint(4)          NOT NULL DEFAULT 0 COMMENT '瑙掕壊锛�1-鏅�氱敤鎴枫��2-绠$悊鍛�',
+    `create_dt` datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt` datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_username` (`username`) USING BTREE
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4 COMMENT ='绯荤粺鐢ㄦ埛琛�';
+
+-- pwd: admin
+INSERT INTO `sj_system_user` VALUES (1, 'admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', 2, now(), now());
+
+CREATE TABLE `sj_system_user_permission`
+(
+    `id`             bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `group_name`     varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `namespace_id`   varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `system_user_id` bigint(20)          NOT NULL COMMENT '绯荤粺鐢ㄦ埛id',
+    `create_dt`      datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`      datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_namespace_id_group_name_system_user_id` (`namespace_id`, `group_name`, `system_user_id`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4 COMMENT ='绯荤粺鐢ㄦ埛鏉冮檺琛�';
+
+CREATE TABLE `sj_sequence_alloc`
+(
+    `id`           bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id` varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`   varchar(64)         NOT NULL DEFAULT '' COMMENT '缁勫悕绉�',
+    `max_id`       bigint(20)          NOT NULL DEFAULT 1 COMMENT '鏈�澶d',
+    `step`         int(11)             NOT NULL DEFAULT 100 COMMENT '姝ラ暱',
+    `update_dt`    datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_namespace_id_group_name` (`namespace_id`, `group_name`)
+) ENGINE = InnoDB
+  DEFAULT CHARSET = utf8mb4 COMMENT ='鍙锋妯″紡搴忓彿ID鍒嗛厤琛�';
+
+-- 鍒嗗竷寮忚皟搴DL
+CREATE TABLE `sj_job`
+(
+    `id`               bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`     varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`       varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `job_name`         varchar(64)         NOT NULL COMMENT '鍚嶇О',
+    `args_str`         text                         DEFAULT NULL COMMENT '鎵ц鏂规硶鍙傛暟',
+    `args_type`        tinyint(4)          NOT NULL DEFAULT 1 COMMENT '鍙傛暟绫诲瀷 ',
+    `next_trigger_at`  bigint(13)          NOT NULL COMMENT '涓嬫瑙﹀彂鏃堕棿',
+    `job_status`       tinyint(4)          NOT NULL DEFAULT 1 COMMENT '浠诲姟鐘舵�� 0銆佸叧闂��1銆佸紑鍚�',
+    `task_type`        tinyint(4)          NOT NULL DEFAULT 1 COMMENT '浠诲姟绫诲瀷 1銆侀泦缇� 2銆佸箍鎾� 3銆佸垏鐗�',
+    `route_key`        tinyint(4)          NOT NULL DEFAULT 4 COMMENT '璺敱绛栫暐',
+    `executor_type`    tinyint(4)          NOT NULL DEFAULT 1 COMMENT '鎵ц鍣ㄧ被鍨�',
+    `executor_info`    varchar(255)                 DEFAULT NULL COMMENT '鎵ц鍣ㄥ悕绉�',
+    `trigger_type`     tinyint(4)          NOT NULL COMMENT '瑙﹀彂绫诲瀷 1.CRON 琛ㄨ揪寮� 2. 鍥哄畾鏃堕棿',
+    `trigger_interval` varchar(255)        NOT NULL COMMENT '闂撮殧鏃堕暱',
+    `block_strategy`   tinyint(4)          NOT NULL DEFAULT 1 COMMENT '闃诲绛栫暐 1銆佷涪寮� 2銆佽鐩� 3銆佸苟琛�',
+    `executor_timeout` int(11)             NOT NULL DEFAULT 0 COMMENT '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇',
+    `max_retry_times`  int(11)             NOT NULL DEFAULT 0 COMMENT '鏈�澶ч噸璇曟鏁�',
+    `parallel_num`     int(11)             NOT NULL DEFAULT 1 COMMENT '骞惰鏁�',
+    `retry_interval`   int(11)             NOT NULL DEFAULT 0 COMMENT '閲嶈瘯闂撮殧(s)',
+    `bucket_index`     int(11)             NOT NULL DEFAULT 0 COMMENT 'bucket',
+    `resident`         tinyint(4)          NOT NULL DEFAULT 0 COMMENT '鏄惁鏄父椹讳换鍔�',
+    `description`      varchar(256)        NOT NULL DEFAULT '' COMMENT '鎻忚堪',
+    `ext_attrs`        varchar(256)        NULL     DEFAULT '' COMMENT '鎵╁睍瀛楁',
+    `deleted`          tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫昏緫鍒犻櫎 1銆佸垹闄�',
+    `create_dt`        datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`        datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`),
+    KEY `idx_job_status_bucket_index` (`job_status`, `bucket_index`),
+    KEY `idx_create_dt` (`create_dt`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='浠诲姟淇℃伅';
+
+INSERT INTO `sj_job` VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', '', 0 , now(), now());
+
+CREATE TABLE `sj_job_log_message`
+(
+    `id`            bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`  varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`    varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `job_id`        bigint(20)          NOT NULL COMMENT '浠诲姟淇℃伅id',
+    `task_batch_id` bigint(20)          NOT NULL COMMENT '浠诲姟鎵规id',
+    `task_id`       bigint(20)          NOT NULL COMMENT '璋冨害浠诲姟id',
+    `message`       longtext            NOT NULL COMMENT '璋冨害淇℃伅',
+    `log_num`       int(11)             NOT NULL DEFAULT 1 COMMENT '鏃ュ織鏁伴噺',
+    `real_time`     bigint(13)          NOT NULL DEFAULT 0 COMMENT '涓婃姤鏃堕棿',
+    `ext_attrs`     varchar(256)        NULL     DEFAULT '' COMMENT '鎵╁睍瀛楁',
+    `create_dt`     datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_task_batch_id_task_id` (`task_batch_id`, `task_id`),
+    KEY `idx_create_dt` (`create_dt`),
+    KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='璋冨害鏃ュ織';
+
+CREATE TABLE `sj_job_task`
+(
+    `id`             bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`   varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`     varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `job_id`         bigint(20)          NOT NULL COMMENT '浠诲姟淇℃伅id',
+    `task_batch_id`  bigint(20)          NOT NULL COMMENT '璋冨害浠诲姟id',
+    `parent_id`      bigint(20)          NOT NULL DEFAULT 0 COMMENT '鐖舵墽琛屽櫒id',
+    `task_status`    tinyint(4)          NOT NULL DEFAULT 0 COMMENT '鎵ц鐨勭姸鎬� 0銆佸け璐� 1銆佹垚鍔�',
+    `retry_count`    int(11)             NOT NULL DEFAULT 0 COMMENT '閲嶈瘯娆℃暟',
+    `client_info`    varchar(128)                 DEFAULT NULL COMMENT '瀹㈡埛绔湴鍧� clientId#ip:port',
+    `result_message` text                NOT NULL COMMENT '鎵ц缁撴灉',
+    `args_str`       text                         DEFAULT NULL COMMENT '鎵ц鏂规硶鍙傛暟',
+    `args_type`      tinyint(4)          NOT NULL DEFAULT 1 COMMENT '鍙傛暟绫诲瀷 ',
+    `ext_attrs`      varchar(256)        NULL     DEFAULT '' COMMENT '鎵╁睍瀛楁',
+    `create_dt`      datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`      datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_task_batch_id_task_status` (`task_batch_id`, `task_status`),
+    KEY `idx_create_dt` (`create_dt`),
+    KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='浠诲姟瀹炰緥';
+
+CREATE TABLE `sj_job_task_batch`
+(
+    `id`                      bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`            varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`              varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `job_id`                  bigint(20)          NOT NULL COMMENT '浠诲姟id',
+    `workflow_node_id`        bigint(20)          NOT NULL DEFAULT 0 COMMENT '宸ヤ綔娴佽妭鐐筰d',
+    `parent_workflow_node_id` bigint(20)          NOT NULL DEFAULT 0 COMMENT '宸ヤ綔娴佷换鍔$埗鎵规id',
+    `workflow_task_batch_id`  bigint(20)          NOT NULL DEFAULT 0 COMMENT '宸ヤ綔娴佷换鍔℃壒娆d',
+    `task_batch_status`       tinyint(4)          NOT NULL DEFAULT 0 COMMENT '浠诲姟鎵规鐘舵�� 0銆佸け璐� 1銆佹垚鍔�',
+    `operation_reason`        tinyint(4)          NOT NULL DEFAULT 0 COMMENT '鎿嶄綔鍘熷洜',
+    `execution_at`            bigint(13)          NOT NULL DEFAULT 0 COMMENT '浠诲姟鎵ц鏃堕棿',
+    `system_task_type`        tinyint(4)          NOT NULL DEFAULT 3 COMMENT '浠诲姟绫诲瀷 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟',
+    `parent_id`               varchar(64)         NOT NULL DEFAULT '' COMMENT '鐖惰妭鐐�',
+    `ext_attrs`               varchar(256)        NULL     DEFAULT '' COMMENT '鎵╁睍瀛楁',
+    `deleted`                 tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫昏緫鍒犻櫎 1銆佸垹闄�',
+    `create_dt`               datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`               datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_job_id_task_batch_status` (`job_id`, `task_batch_status`),
+    KEY `idx_create_dt` (`create_dt`),
+    KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`),
+    KEY `idx_workflow_task_batch_id_workflow_node_id` (`workflow_task_batch_id`, `workflow_node_id`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='浠诲姟鎵规';
+
+CREATE TABLE `sj_job_summary`
+(
+    `id`               bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`     VARCHAR(64)     NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`       VARCHAR(64)     NOT NULL DEFAULT '' COMMENT '缁勫悕绉�',
+    `business_id`      bigint          NOT NULL COMMENT '涓氬姟id (job_id鎴杦orkflow_id)',
+    `system_task_type` tinyint(4)      NOT NULL DEFAULT 3 COMMENT '浠诲姟绫诲瀷 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟',
+    `trigger_at`       datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '缁熻鏃堕棿',
+    `success_num`      int             NOT NULL DEFAULT 0 COMMENT '鎵ц鎴愬姛-鏃ュ織鏁伴噺',
+    `fail_num`         int             NOT NULL DEFAULT 0 COMMENT '鎵ц澶辫触-鏃ュ織鏁伴噺',
+    `fail_reason`      varchar(512)    NOT NULL DEFAULT '' COMMENT '澶辫触鍘熷洜',
+    `stop_num`         int             NOT NULL DEFAULT 0 COMMENT '鎵ц澶辫触-鏃ュ織鏁伴噺',
+    `stop_reason`      varchar(512)    NOT NULL DEFAULT '' COMMENT '澶辫触鍘熷洜',
+    `cancel_num`       int             NOT NULL DEFAULT 0 COMMENT '鎵ц澶辫触-鏃ュ織鏁伴噺',
+    `cancel_reason`    varchar(512)    NOT NULL DEFAULT '' COMMENT '澶辫触鍘熷洜',
+    `create_dt`        datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`        datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_namespace_id_group_name_business_id` (`namespace_id`, `group_name`, business_id),
+    UNIQUE KEY `uk_trigger_at_system_task_type_business_id` (`trigger_at`, `system_task_type`, `business_id`) USING BTREE
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 1
+  DEFAULT CHARSET = utf8mb4 COMMENT ='DashBoard_Job';
+
+CREATE TABLE `sj_retry_summary`
+(
+    `id`            bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`  VARCHAR(64)     NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`    VARCHAR(64)     NOT NULL DEFAULT '' COMMENT '缁勫悕绉�',
+    `scene_name`    VARCHAR(50)     NOT NULL DEFAULT '' COMMENT '鍦烘櫙鍚嶇О',
+    `trigger_at`    datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '缁熻鏃堕棿',
+    `running_num`   int             NOT NULL DEFAULT 0 COMMENT '閲嶈瘯涓�-鏃ュ織鏁伴噺',
+    `finish_num`    int             NOT NULL DEFAULT 0 COMMENT '閲嶈瘯瀹屾垚-鏃ュ織鏁伴噺',
+    `max_count_num` int             NOT NULL DEFAULT 0 COMMENT '閲嶈瘯鍒拌揪鏈�澶ф鏁�-鏃ュ織鏁伴噺',
+    `suspend_num`   int             NOT NULL DEFAULT 0 COMMENT '鏆傚仠閲嶈瘯-鏃ュ織鏁伴噺',
+    `create_dt`     datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`     datetime        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_trigger_at` (`trigger_at`),
+    UNIQUE KEY `uk_scene_name_trigger_at` (`namespace_id`, `group_name`, `scene_name`, `trigger_at`) USING BTREE
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 1
+  DEFAULT CHARSET = utf8mb4 COMMENT ='DashBoard_Retry';
+
+CREATE TABLE `sj_workflow`
+(
+    `id`               bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `workflow_name`    varchar(64)         NOT NULL COMMENT '宸ヤ綔娴佸悕绉�',
+    `namespace_id`     varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`       varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `workflow_status`  tinyint(4)          NOT NULL DEFAULT 1 COMMENT '宸ヤ綔娴佺姸鎬� 0銆佸叧闂��1銆佸紑鍚�',
+    `trigger_type`     tinyint(4)          NOT NULL COMMENT '瑙﹀彂绫诲瀷 1.CRON 琛ㄨ揪寮� 2. 鍥哄畾鏃堕棿',
+    `trigger_interval` varchar(255)        NOT NULL COMMENT '闂撮殧鏃堕暱',
+    `next_trigger_at`  bigint(13)          NOT NULL COMMENT '涓嬫瑙﹀彂鏃堕棿',
+    `block_strategy`   tinyint(4)          NOT NULL DEFAULT 1 COMMENT '闃诲绛栫暐 1銆佷涪寮� 2銆佽鐩� 3銆佸苟琛�',
+    `executor_timeout` int(11)             NOT NULL DEFAULT 0 COMMENT '浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇',
+    `description`      varchar(256)        NOT NULL DEFAULT '' COMMENT '鎻忚堪',
+    `flow_info`        text                         DEFAULT NULL COMMENT '娴佺▼淇℃伅',
+    `bucket_index`     int(11)             NOT NULL DEFAULT 0 COMMENT 'bucket',
+    `version`          int(11)             NOT NULL COMMENT '鐗堟湰鍙�',
+    `ext_attrs`        varchar(256)        NULL     DEFAULT '' COMMENT '鎵╁睍瀛楁',
+    `deleted`          tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫昏緫鍒犻櫎 1銆佸垹闄�',
+    `create_dt`        datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`        datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_create_dt` (`create_dt`),
+    KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='宸ヤ綔娴�';
+
+CREATE TABLE `sj_workflow_node`
+(
+    `id`                   bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`         varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `node_name`            varchar(64)         NOT NULL COMMENT '鑺傜偣鍚嶇О',
+    `group_name`           varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `job_id`               bigint(20)          NOT NULL COMMENT '浠诲姟淇℃伅id',
+    `workflow_id`          bigint(20)          NOT NULL COMMENT '宸ヤ綔娴両D',
+    `node_type`            tinyint(4)          NOT NULL DEFAULT 1 COMMENT '1銆佷换鍔¤妭鐐� 2銆佹潯浠惰妭鐐�',
+    `expression_type`      tinyint(4)          NOT NULL DEFAULT 0 COMMENT '1銆丼pEl銆�2銆丄viator 3銆丵L',
+    `fail_strategy`        tinyint(4)          NOT NULL DEFAULT 1 COMMENT '澶辫触绛栫暐 1銆佽烦杩� 2銆侀樆濉�',
+    `workflow_node_status` tinyint(4)          NOT NULL DEFAULT 1 COMMENT '宸ヤ綔娴佽妭鐐圭姸鎬� 0銆佸叧闂��1銆佸紑鍚�',
+    `priority_level`       int(11)             NOT NULL DEFAULT 1 COMMENT '浼樺厛绾�',
+    `node_info`            text                         DEFAULT NULL COMMENT '鑺傜偣淇℃伅 ',
+    `version`              int(11)             NOT NULL COMMENT '鐗堟湰鍙�',
+    `ext_attrs`            varchar(256)        NULL     DEFAULT '' COMMENT '鎵╁睍瀛楁',
+    `deleted`              tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫昏緫鍒犻櫎 1銆佸垹闄�',
+    `create_dt`            datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`            datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_create_dt` (`create_dt`),
+    KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='宸ヤ綔娴佽妭鐐�';
+
+CREATE TABLE `sj_workflow_task_batch`
+(
+    `id`                bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    `namespace_id`      varchar(64)         NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '鍛藉悕绌洪棿id',
+    `group_name`        varchar(64)         NOT NULL COMMENT '缁勫悕绉�',
+    `workflow_id`       bigint(20)          NOT NULL COMMENT '宸ヤ綔娴佷换鍔d',
+    `task_batch_status` tinyint(4)          NOT NULL DEFAULT 0 COMMENT '浠诲姟鎵规鐘舵�� 0銆佸け璐� 1銆佹垚鍔�',
+    `operation_reason`  tinyint(4)          NOT NULL DEFAULT 0 COMMENT '鎿嶄綔鍘熷洜',
+    `flow_info`         text                         DEFAULT NULL COMMENT '娴佺▼淇℃伅',
+    `execution_at`      bigint(13)          NOT NULL DEFAULT 0 COMMENT '浠诲姟鎵ц鏃堕棿',
+    `ext_attrs`         varchar(256)        NULL     DEFAULT '' COMMENT '鎵╁睍瀛楁',
+    `deleted`           tinyint(4)          NOT NULL DEFAULT 0 COMMENT '閫昏緫鍒犻櫎 1銆佸垹闄�',
+    `create_dt`         datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `update_dt`         datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '淇敼鏃堕棿',
+    PRIMARY KEY (`id`),
+    KEY `idx_job_id_task_batch_status` (`workflow_id`, `task_batch_status`),
+    KEY `idx_create_dt` (`create_dt`),
+    KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`)
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 0
+  DEFAULT CHARSET = utf8mb4 COMMENT ='宸ヤ綔娴佹壒娆�';
diff --git a/script/sql/sqlserver/flowable.sql b/script/sql/sqlserver/flowable.sql
new file mode 100644
index 0000000..11ee2be
--- /dev/null
+++ b/script/sql/sqlserver/flowable.sql
@@ -0,0 +1,447 @@
+insert into sys_menu values('11616', '宸ヤ綔娴�'  , '0',    '6', 'workflow',          '',                                 '', '1', '0', 'M', '0', '0', '',                       'workflow', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11617', '妯″瀷绠$悊', '11616', '2', 'model',             'workflow/model/index',             '', '1', '1', 'C', '0', '0', 'workflow:model:list',    'model', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11618', '鎴戠殑浠诲姟', '0', '7', 'task',              '',                                 '', '1', '0', 'M', '0', '0', '',                       'my-task', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11619', '鎴戠殑寰呭姙', '11618', '2', 'taskWaiting',       'workflow/task/taskWaiting',              '', '1', '1', 'C', '0', '0', '',                       'waiting', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11632', '鎴戠殑宸插姙', '11618', '3', 'taskFinish',       'workflow/task/taskFinish',              '', '1', '1', 'C', '0', '0', '',                       'finish', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11633', '鎴戠殑鎶勯��', '11618', '4', 'taskCopyList',       'workflow/task/taskCopyList',              '', '1', '1', 'C', '0', '0', '',                       'my-copy', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11620', '娴佺▼瀹氫箟', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '',                       'process-definition', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11621', '娴佺▼瀹炰緥', '11630', '1', 'processInstance',   'workflow/processInstance/index',   '', '1', '1', 'C', '0', '0', '',                       'tree-table', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11622', '娴佺▼鍒嗙被', '11616', '1', 'category',          'workflow/category/index',          '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11629', '鎴戝彂璧风殑', '11618', '1', 'myDocument',        'workflow/task/myDocument',         '', '1', '1', 'C', '0', '0', '',                       'guide', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11630', '娴佺▼鐩戞帶', '11616', '4', 'monitor',           '',                                 '', '1', '0', 'M', '0', '0', '',                       'monitor', 103, 1, getdate(), NULL, NULL, '');
+insert into sys_menu values('11631', '寰呭姙浠诲姟', '11630', '2', 'allTaskWaiting',    'workflow/task/allTaskWaiting',     '', '1', '1', 'C', '0', '0', '',                       'waiting', 103, 1, getdate(), NULL, NULL, '');
+
+
+-- 娴佺▼鍒嗙被绠$悊鐩稿叧鎸夐挳
+insert into sys_menu values ('11623', '娴佺▼鍒嗙被鏌ヨ', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, getdate(), null, null, '');
+insert into sys_menu values ('11624', '娴佺▼鍒嗙被鏂板', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add',   '#', 103, 1, getdate(), null, null, '');
+insert into sys_menu values ('11625', '娴佺▼鍒嗙被淇敼', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit',  '#', 103, 1, getdate(), null, null, '');
+insert into sys_menu values ('11626', '娴佺▼鍒嗙被鍒犻櫎', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, getdate(), null, null, '');
+insert into sys_menu values ('11627', '娴佺▼鍒嗙被瀵煎嚭', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, getdate(), null, null, '');
+-- 璇峰亣鍗曚俊鎭�
+create table test_leave
+(
+    id          bigint        not null
+        primary key,
+    leave_type  nvarchar(255) not null,
+    start_date  datetime2     not null,
+    end_date    datetime2     not null,
+    leave_days  int           not null,
+    remark      nvarchar(255),
+    create_dept bigint,
+    create_by   bigint,
+    create_time datetime2,
+    update_by   bigint,
+    update_time datetime2,
+    tenant_id   nvarchar(20)
+)
+go
+
+exec sp_addextendedproperty 'MS_Description', N'璇峰亣鐢宠琛�', 'SCHEMA', 'dbo', 'TABLE', 'test_leave'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'涓婚敭', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'璇峰亣绫诲瀷', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
+     'leave_type'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'寮�濮嬫椂闂�', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
+     'start_date'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'缁撴潫鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'end_date'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'璇峰亣澶╂暟', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
+     'leave_days'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'璇峰亣鍘熷洜', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'remark'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓閮ㄩ棬', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
+     'create_dept'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鑰�', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'create_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
+     'create_time'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鑰�', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'update_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN',
+     'update_time'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'绉熸埛缂栧彿', 'SCHEMA', 'dbo', 'TABLE', 'test_leave', 'COLUMN', 'tenant_id'
+go
+
+-- 娴佺▼鍒嗙被淇℃伅琛�
+create table wf_category
+(
+    id            bigint not null
+        primary key,
+    category_name nvarchar(255),
+    category_code nvarchar(255)
+        constraint uni_category_code
+        unique,
+    parent_id     bigint,
+    sort_num      int,
+    tenant_id     nvarchar(20),
+    create_dept   bigint,
+    create_by     bigint,
+    create_time   datetime2,
+    update_by     bigint,
+    update_time   datetime2
+)
+go
+
+exec sp_addextendedproperty 'MS_Description', N'娴佺▼鍒嗙被', 'SCHEMA', 'dbo', 'TABLE', 'wf_category'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'涓婚敭', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒嗙被鍚嶇О', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
+     'category_name'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒嗙被缂栫爜', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
+     'category_code'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鐖剁骇id', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'parent_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鎺掑簭', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'sort_num'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'绉熸埛缂栧彿', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
+     'tenant_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓閮ㄩ棬', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
+     'create_dept'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'create_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
+     'create_time'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN', 'update_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_category', 'COLUMN',
+     'update_time'
+go
+
+INSERT INTO wf_category values (1, 'OA', 'OA', 0, 0, '000000', 103, 1, getdate(), 1, getdate());
+
+create table wf_task_back_node
+(
+    id            bigint not null primary key,
+    node_id       nvarchar(255) not null,
+    node_name     nvarchar(255) not null,
+    order_no      int not null,
+    instance_id   nvarchar(255) not null,
+    task_type     nvarchar(255) not null,
+    assignee      nvarchar(2000) not null,
+    tenant_id     nvarchar(20),
+    create_dept   bigint,
+    create_by     bigint,
+    create_time   datetime2,
+    update_by     bigint,
+    update_time   datetime2
+)
+
+go
+exec sp_addextendedproperty 'MS_Description', N'鑺傜偣瀹℃壒璁板綍', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'涓婚敭', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN', 'id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鑺傜偣id', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN',
+     'node_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鑺傜偣鍚嶇О', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN',
+     'node_name'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鎺掑簭', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN', 'order_no'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'娴佺▼瀹炰緥id', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN', 'instance_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鑺傜偣绫诲瀷', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN', 'task_type'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'瀹℃壒浜�', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN', 'assignee'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'绉熸埛缂栧彿', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN',
+     'tenant_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓閮ㄩ棬', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN',
+     'create_dept'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN', 'create_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN',
+     'create_time'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN', 'update_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_task_back_node', 'COLUMN',
+     'update_time'
+go
+
+create table wf_definition_config
+(
+    id            bigint not null primary key,
+    table_name    nvarchar(255)  not null,
+    definition_id nvarchar(255)  not null
+        constraint uni_definition_id
+        unique,
+    process_key   nvarchar(255)  not null,
+    version       bigint         not null,
+    tenant_id     nvarchar(20),
+    create_dept   bigint,
+    create_by     bigint,
+    create_time   datetime2,
+    update_by     bigint,
+    update_time   datetime2
+)
+
+go
+exec sp_addextendedproperty 'MS_Description', N'娴佺▼瀹氫箟閰嶇疆', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'涓婚敭', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN', 'id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'琛ㄥ悕', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN',
+     'table_name'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'娴佺▼瀹氫箟ID', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN',
+     'definition_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'娴佺▼KEY', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN',
+     'process_key'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'娴佺▼鐗堟湰', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN',
+     'version'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'绉熸埛缂栧彿', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN',
+     'tenant_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓閮ㄩ棬', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN',
+     'create_dept'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN', 'create_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN',
+     'create_time'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN', 'update_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_definition_config', 'COLUMN',
+     'update_time'
+go
+
+create table wf_form_manage
+(
+    id            bigint not null primary key,
+    form_name     nvarchar(255) not null,
+    form_type     nvarchar(255) not null,
+    router        nvarchar(255) not null,
+    remark        nvarchar(500) null,
+    tenant_id     nvarchar(20),
+    create_dept   bigint,
+    create_by     bigint,
+    create_time   datetime2,
+    update_by     bigint,
+    update_time   datetime2
+)
+
+go
+exec sp_addextendedproperty 'MS_Description', N'琛ㄥ崟绠$悊', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'涓婚敭', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN', 'id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'琛ㄥ崟鍚嶇О', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN',
+     'form_name'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'琛ㄥ崟绫诲瀷', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN',
+     'form_type'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'璺敱鍦板潃/琛ㄥ崟ID', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN',
+     'router'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'澶囨敞', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN',
+     'remark'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'绉熸埛缂栧彿', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN',
+     'tenant_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓閮ㄩ棬', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN',
+     'create_dept'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN', 'create_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN',
+     'create_time'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN', 'update_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_form_manage', 'COLUMN',
+     'update_time'
+go
+
+insert into wf_form_manage(id, form_name, form_type, router, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time) VALUES (1, '璇峰亣鐢宠', 'static', '/demo/leaveEdit/index', NULL, '000000', 103, 1, getdate(), 1, getdate());
+
+create table wf_node_config
+(
+    id               bigint not null primary key,
+    form_id          bigint,
+    form_type        nvarchar(255) ,
+    node_name        nvarchar(255) not null,
+    node_id          nvarchar(255) not null,
+    definition_id    nvarchar(255) not null,
+    apply_user_task  nchar default ('0')  null,
+    tenant_id        nvarchar(20),
+    create_dept      bigint,
+    create_by        bigint,
+    create_time      datetime2,
+    update_by        bigint,
+    update_time      datetime2
+)
+
+go
+exec sp_addextendedproperty 'MS_Description', N'鑺傜偣閰嶇疆', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'涓婚敭', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN', 'id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'琛ㄥ崟id', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'form_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'琛ㄥ崟绫诲瀷', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'form_type'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鑺傜偣鍚嶇О', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'node_name'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鑺傜偣id', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'node_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'娴佺▼瀹氫箟id', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'definition_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏄惁涓虹敵璇蜂汉鑺傜偣 锛�0鏄� 1鍚︼級', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'apply_user_task'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'绉熸埛缂栧彿', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'tenant_id'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓閮ㄩ棬', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'create_dept'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN', 'create_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鍒涘缓鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'create_time'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鑰�', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN', 'update_by'
+go
+
+exec sp_addextendedproperty 'MS_Description', N'鏇存柊鏃堕棿', 'SCHEMA', 'dbo', 'TABLE', 'wf_node_config', 'COLUMN',
+     'update_time'
+go
+
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '璇峰亣鐢宠', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, getdate(), NULL, NULL, '璇峰亣鐢宠鑿滃崟');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '璇峰亣鐢宠鏌ヨ', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, getdate(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '璇峰亣鐢宠鏂板', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:add', '#', 103, 1, getdate(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '璇峰亣鐢宠淇敼', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:edit', '#', 103, 1, getdate(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '璇峰亣鐢宠鍒犻櫎', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:remove', '#', 103, 1, getdate(), NULL, NULL, '');
+INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '璇峰亣鐢宠瀵煎嚭', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:export', '#', 103, 1, getdate(), NULL, NULL, '');
+
+INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (13, '000000', '涓氬姟鐘舵��', 'wf_business_status', 103, 1, getdate(), NULL, NULL, '涓氬姟鐘舵�佸垪琛�');
+INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (14, '000000', '琛ㄥ崟绫诲瀷', 'wf_form_type', 103, 1, getdate(), NULL, NULL, '琛ㄥ崟绫诲瀷鍒楄〃');
+
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (39, '000000', 1, '宸叉挙閿�', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, getdate(), NULL, NULL, '宸叉挙閿�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (40, '000000', 2, '鑽夌', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, getdate(), NULL, NULL, '鑽夌');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (41, '000000', 3, '寰呭鏍�', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1,getdate(), NULL, NULL, '寰呭鏍�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (42, '000000', 4, '宸插畬鎴�', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, getdate(), NULL, NULL, '宸插畬鎴�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (43, '000000', 5, '宸蹭綔搴�', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, getdate(), NULL, NULL, '宸蹭綔搴�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (44, '000000', 6, '宸查��鍥�', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, getdate(), NULL, NULL, '宸查��鍥�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (45, '000000', 7, '宸茬粓姝�', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1,getdate(), NULL, NULL, '宸茬粓姝�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (46, '000000', 1, '鑷畾涔夎〃鍗�', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, getdate(), NULL, NULL, '鑷畾涔夎〃鍗�');
+INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (47, '000000', 2, '鍔ㄦ�佽〃鍗�', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, getdate(), NULL, NULL, '鍔ㄦ�佽〃鍗�');
+
+-- 琛ㄥ崟绠$悊 SQL
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11628, '琛ㄥ崟绠$悊', '11616', '5', 'formManage', 'workflow/formManage/index', 1, 0, 'C', '0', '0', 'workflow:formManage:list', 'tree-table', 103, 1, getdate(), null, null, '琛ㄥ崟绠$悊鑿滃崟');
+
+-- 琛ㄥ崟绠$悊鎸夐挳 SQL
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11644, '琛ㄥ崟绠$悊鏌ヨ', 11628, '1',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:query',        '', 103, 1, getdate(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11645, '琛ㄥ崟绠$悊鏂板', 11628, '2',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:add',          '', 103, 1, getdate(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11646, '琛ㄥ崟绠$悊淇敼', 11628, '3',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:edit',         '', 103, 1, getdate(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11647, '琛ㄥ崟绠$悊鍒犻櫎', 11628, '4',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:remove',       '', 103, 1, getdate(), null, null, '');
+
+insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
+values(11648, '琛ㄥ崟绠$悊瀵煎嚭', 11628, '5',  '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:export',       'tree-table', 103, 1, getdate(), null, null, '');
diff --git a/script/sql/sqlserver/powerjob.sql b/script/sql/sqlserver/powerjob.sql
deleted file mode 100644
index 1148b2c..0000000
--- a/script/sql/sqlserver/powerjob.sql
+++ /dev/null
@@ -1,517 +0,0 @@
--- ----------------------------
--- Table structure for pj_app_info
--- ----------------------------
-
-CREATE TABLE [pj_app_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [app_name] varchar(255)   NULL,
-  [current_server] varchar(255)   NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [password] varchar(255)   NULL
-)
-GO
-
-ALTER TABLE [pj_app_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Records of [pj_app_info]
--- ----------------------------
-SET IDENTITY_INSERT [pj_app_info] ON
-GO
-
-INSERT INTO [pj_app_info] ([id], [app_name], [current_server], [gmt_create], [gmt_modified], [password]) VALUES (N'1', N'ruoyi-worker', N'192.168.31.100:10010', N'2023-06-13 16:32:59.2630000', N'2023-07-04 17:25:49.7980000', N'123456')
-GO
-
-SET IDENTITY_INSERT [pj_app_info] OFF
-GO
-
-
--- ----------------------------
--- Table structure for pj_container_info
--- ----------------------------
-
-CREATE TABLE [pj_container_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [app_id] bigint  NULL,
-  [container_name] varchar(255)   NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [last_deploy_time] datetime2(7)  NULL,
-  [source_info] varchar(255)   NULL,
-  [source_type] int  NULL,
-  [status] int  NULL,
-  [version] varchar(255)   NULL
-)
-GO
-
-ALTER TABLE [pj_container_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Table structure for pj_instance_info
--- ----------------------------
-
-CREATE TABLE [pj_instance_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [actual_trigger_time] bigint  NULL,
-  [app_id] bigint  NULL,
-  [expected_trigger_time] bigint  NULL,
-  [finished_time] bigint  NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [instance_id] bigint  NULL,
-  [instance_params] varchar(max)   NULL,
-  [job_id] bigint  NULL,
-  [job_params] varchar(max)   NULL,
-  [last_report_time] bigint  NULL,
-  [result] varchar(max)   NULL,
-  [running_times] bigint  NULL,
-  [status] int  NULL,
-  [task_tracker_address] varchar(255)   NULL,
-  [type] int  NULL,
-  [wf_instance_id] bigint  NULL
-)
-GO
-
-ALTER TABLE [pj_instance_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Table structure for pj_job_info
--- ----------------------------
-
-CREATE TABLE [pj_job_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [alarm_config] varchar(255)   NULL,
-  [app_id] bigint  NULL,
-  [concurrency] int  NULL,
-  [designated_workers] varchar(255)   NULL,
-  [dispatch_strategy] int  NULL,
-  [execute_type] int  NULL,
-  [extra] varchar(255)   NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [instance_retry_num] int  NULL,
-  [instance_time_limit] bigint  NULL,
-  [job_description] varchar(255)   NULL,
-  [job_name] varchar(255)   NULL,
-  [job_params] varchar(max)   NULL,
-  [lifecycle] varchar(255)   NULL,
-  [log_config] varchar(255)   NULL,
-  [max_instance_num] int  NULL,
-  [max_worker_count] int  NULL,
-  [min_cpu_cores] float(53)  NOT NULL,
-  [min_disk_space] float(53)  NOT NULL,
-  [min_memory_space] float(53)  NOT NULL,
-  [next_trigger_time] bigint  NULL,
-  [notify_user_ids] varchar(255)   NULL,
-  [processor_info] varchar(255)   NULL,
-  [processor_type] int  NULL,
-  [status] int  NULL,
-  [tag] varchar(255)   NULL,
-  [task_retry_num] int  NULL,
-  [time_expression] varchar(255)   NULL,
-  [time_expression_type] int  NULL
-)
-GO
-
-ALTER TABLE [pj_job_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Records of [pj_job_info]
--- ----------------------------
-SET IDENTITY_INSERT [pj_job_info] ON
-GO
-
-INSERT INTO [pj_job_info] ([id], [alarm_config], [app_id], [concurrency], [designated_workers], [dispatch_strategy], [execute_type], [extra], [gmt_create], [gmt_modified], [instance_retry_num], [instance_time_limit], [job_description], [job_name], [job_params], [lifecycle], [log_config], [max_instance_num], [max_worker_count], [min_cpu_cores], [min_disk_space], [min_memory_space], [next_trigger_time], [notify_user_ids], [processor_info], [processor_type], [status], [tag], [task_retry_num], [time_expression], [time_expression_type]) VALUES (N'1', N'{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', N'1', N'5', N'', N'2', N'1', NULL, N'2023-06-02 15:01:27.7170000', N'2023-07-04 17:22:12.3740000', N'1', N'0', N'', N'?????????', NULL, N'{}', N'{"type":1}', N'0', N'0', N'0.000000000000000', N'0.000000000000000', N'0.000000000000000', NULL, NULL, N'org.dromara.job.processors.StandaloneProcessorDemo', N'1', N'2', NULL, N'1', N'30000', N'3')
-GO
-
-INSERT INTO [pj_job_info] ([id], [alarm_config], [app_id], [concurrency], [designated_workers], [dispatch_strategy], [execute_type], [extra], [gmt_create], [gmt_modified], [instance_retry_num], [instance_time_limit], [job_description], [job_name], [job_params], [lifecycle], [log_config], [max_instance_num], [max_worker_count], [min_cpu_cores], [min_disk_space], [min_memory_space], [next_trigger_time], [notify_user_ids], [processor_info], [processor_type], [status], [tag], [task_retry_num], [time_expression], [time_expression_type]) VALUES (N'2', N'{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', N'1', N'5', N'', N'1', N'2', NULL, N'2023-06-02 15:04:45.3420000', N'2023-07-04 17:22:12.8160000', N'0', N'0', NULL, N'???????', NULL, N'{}', N'{"type":1}', N'0', N'0', N'0.000000000000000', N'0.000000000000000', N'0.000000000000000', NULL, NULL, N'org.dromara.job.processors.BroadcastProcessorDemo', N'1', N'2', NULL, N'1', N'30000', N'3')
-GO
-
-INSERT INTO [pj_job_info] ([id], [alarm_config], [app_id], [concurrency], [designated_workers], [dispatch_strategy], [execute_type], [extra], [gmt_create], [gmt_modified], [instance_retry_num], [instance_time_limit], [job_description], [job_name], [job_params], [lifecycle], [log_config], [max_instance_num], [max_worker_count], [min_cpu_cores], [min_disk_space], [min_memory_space], [next_trigger_time], [notify_user_ids], [processor_info], [processor_type], [status], [tag], [task_retry_num], [time_expression], [time_expression_type]) VALUES (N'3', N'{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', N'1', N'5', N'', N'1', N'4', NULL, N'2023-06-02 15:13:23.5190000', N'2023-06-02 16:03:22.4210000', N'0', N'0', NULL, N'Map?????', NULL, N'{}', N'{"type":1}', N'0', N'0', N'0.000000000000000', N'0.000000000000000', N'0.000000000000000', NULL, NULL, N'org.dromara.job.processors.MapProcessorDemo', N'1', N'2', NULL, N'1', N'1000', N'3')
-GO
-
-INSERT INTO [pj_job_info] ([id], [alarm_config], [app_id], [concurrency], [designated_workers], [dispatch_strategy], [execute_type], [extra], [gmt_create], [gmt_modified], [instance_retry_num], [instance_time_limit], [job_description], [job_name], [job_params], [lifecycle], [log_config], [max_instance_num], [max_worker_count], [min_cpu_cores], [min_disk_space], [min_memory_space], [next_trigger_time], [notify_user_ids], [processor_info], [processor_type], [status], [tag], [task_retry_num], [time_expression], [time_expression_type]) VALUES (N'4', N'{"alertThreshold":0,"silenceWindowLen":0,"statisticWindowLen":0}', N'1', N'5', N'', N'1', N'3', NULL, N'2023-06-02 15:45:25.8960000', N'2023-06-02 16:03:23.1250000', N'0', N'0', NULL, N'MapReduce?????', NULL, N'{}', N'{"type":1}', N'0', N'0', N'0.000000000000000', N'0.000000000000000', N'0.000000000000000', NULL, NULL, N'org.dromara.job.processors.MapReduceProcessorDemo', N'1', N'2', NULL, N'1', N'1000', N'3')
-GO
-
-SET IDENTITY_INSERT [pj_job_info] OFF
-GO
-
-
--- ----------------------------
--- Table structure for pj_oms_lock
--- ----------------------------
-
-CREATE TABLE [pj_oms_lock] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [lock_name] varchar(255)   NULL,
-  [max_lock_time] bigint  NULL,
-  [ownerip] varchar(255)   NULL
-)
-GO
-
-ALTER TABLE [pj_oms_lock] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Table structure for pj_server_info
--- ----------------------------
-
-CREATE TABLE [pj_server_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [ip] varchar(255)   NULL
-)
-GO
-
-ALTER TABLE [pj_server_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Table structure for pj_user_info
--- ----------------------------
-
-CREATE TABLE [pj_user_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [email] varchar(255)   NULL,
-  [extra] varchar(255)   NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [password] varchar(255)   NULL,
-  [phone] varchar(255)   NULL,
-  [username] varchar(255)   NULL,
-  [web_hook] varchar(255)   NULL
-)
-GO
-
-ALTER TABLE [pj_user_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Table structure for pj_workflow_info
--- ----------------------------
-
-CREATE TABLE [pj_workflow_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [app_id] bigint  NULL,
-  [extra] varchar(255)   NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [lifecycle] varchar(255)   NULL,
-  [max_wf_instance_num] int  NULL,
-  [next_trigger_time] bigint  NULL,
-  [notify_user_ids] varchar(255)   NULL,
-  [pedag] varchar(max)   NULL,
-  [status] int  NULL,
-  [time_expression] varchar(255)   NULL,
-  [time_expression_type] int  NULL,
-  [wf_description] varchar(255)   NULL,
-  [wf_name] varchar(255)   NULL
-)
-GO
-
-ALTER TABLE [pj_workflow_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Table structure for pj_workflow_instance_info
--- ----------------------------
-
-CREATE TABLE [pj_workflow_instance_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [actual_trigger_time] bigint  NULL,
-  [app_id] bigint  NULL,
-  [dag] varchar(max)   NULL,
-  [expected_trigger_time] bigint  NULL,
-  [finished_time] bigint  NULL,
-  [gmt_create] datetime2(7)  NULL,
-  [gmt_modified] datetime2(7)  NULL,
-  [parent_wf_instance_id] bigint  NULL,
-  [result] varchar(max)   NULL,
-  [status] int  NULL,
-  [wf_context] varchar(max)   NULL,
-  [wf_init_params] varchar(max)   NULL,
-  [wf_instance_id] bigint  NULL,
-  [workflow_id] bigint  NULL
-)
-GO
-
-ALTER TABLE [pj_workflow_instance_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Table structure for pj_workflow_node_info
--- ----------------------------
-
-CREATE TABLE [pj_workflow_node_info] (
-  [id] bigint  IDENTITY(1,1) NOT NULL,
-  [app_id] bigint  NOT NULL,
-  [enable] bit  NOT NULL,
-  [extra] varchar(max)   NULL,
-  [gmt_create] datetime2(7)  NOT NULL,
-  [gmt_modified] datetime2(7)  NOT NULL,
-  [job_id] bigint  NULL,
-  [node_name] varchar(255)   NULL,
-  [node_params] varchar(max)   NULL,
-  [skip_when_failed] bit  NOT NULL,
-  [type] int  NULL,
-  [workflow_id] bigint  NULL
-)
-GO
-
-ALTER TABLE [pj_workflow_node_info] SET (LOCK_ESCALATION = TABLE)
-GO
-
-
--- ----------------------------
--- Uniques structure for table pj_app_info
--- ----------------------------
-ALTER TABLE [pj_app_info] ADD CONSTRAINT [uidx01_app_info] UNIQUE NONCLUSTERED ([app_name] ASC)
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_app_info
--- ----------------------------
-ALTER TABLE [pj_app_info] ADD CONSTRAINT [PK__pj_app_i__3213E83FDD7E2005] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Indexes structure for table pj_container_info
--- ----------------------------
-CREATE NONCLUSTERED INDEX [idx01_container_info]
-ON [pj_container_info] (
-  [app_id] ASC
-)
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_container_info
--- ----------------------------
-ALTER TABLE [pj_container_info] ADD CONSTRAINT [PK__pj_conta__3213E83FE1AAA8BE] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Indexes structure for table pj_instance_info
--- ----------------------------
-CREATE NONCLUSTERED INDEX [idx01_instance_info]
-ON [pj_instance_info] (
-  [job_id] ASC,
-  [status] ASC
-)
-GO
-
-CREATE NONCLUSTERED INDEX [idx02_instance_info]
-ON [pj_instance_info] (
-  [app_id] ASC,
-  [status] ASC
-)
-GO
-
-CREATE NONCLUSTERED INDEX [idx03_instance_info]
-ON [pj_instance_info] (
-  [instance_id] ASC,
-  [status] ASC
-)
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_instance_info
--- ----------------------------
-ALTER TABLE [pj_instance_info] ADD CONSTRAINT [PK__pj_insta__3213E83F6F188642] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Indexes structure for table pj_job_info
--- ----------------------------
-CREATE NONCLUSTERED INDEX [idx01_job_info]
-ON [pj_job_info] (
-  [app_id] ASC,
-  [status] ASC,
-  [time_expression_type] ASC,
-  [next_trigger_time] ASC
-)
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_job_info
--- ----------------------------
-ALTER TABLE [pj_job_info] ADD CONSTRAINT [PK__pj_job_i__3213E83FBFBCD483] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Uniques structure for table pj_oms_lock
--- ----------------------------
-ALTER TABLE [pj_oms_lock] ADD CONSTRAINT [uidx01_oms_lock] UNIQUE NONCLUSTERED ([lock_name] ASC)
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_oms_lock
--- ----------------------------
-ALTER TABLE [pj_oms_lock] ADD CONSTRAINT [PK__pj_oms_l__3213E83F31F31A08] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Indexes structure for table pj_server_info
--- ----------------------------
-CREATE NONCLUSTERED INDEX [idx01_server_info]
-ON [pj_server_info] (
-  [gmt_modified] ASC
-)
-GO
-
-
--- ----------------------------
--- Uniques structure for table pj_server_info
--- ----------------------------
-ALTER TABLE [pj_server_info] ADD CONSTRAINT [uidx01_server_info] UNIQUE NONCLUSTERED ([ip] ASC)
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_server_info
--- ----------------------------
-ALTER TABLE [pj_server_info] ADD CONSTRAINT [PK__pj_serve__3213E83F75246E89] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Indexes structure for table pj_user_info
--- ----------------------------
-CREATE NONCLUSTERED INDEX [uidx01_user_info]
-ON [pj_user_info] (
-  [username] ASC
-)
-GO
-
-CREATE NONCLUSTERED INDEX [uidx02_user_info]
-ON [pj_user_info] (
-  [email] ASC
-)
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_user_info
--- ----------------------------
-ALTER TABLE [pj_user_info] ADD CONSTRAINT [PK__pj_user___3213E83FB78DE8FD] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Indexes structure for table pj_workflow_info
--- ----------------------------
-CREATE NONCLUSTERED INDEX [idx01_workflow_info]
-ON [pj_workflow_info] (
-  [app_id] ASC,
-  [status] ASC,
-  [time_expression_type] ASC,
-  [next_trigger_time] ASC
-)
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_workflow_info
--- ----------------------------
-ALTER TABLE [pj_workflow_info] ADD CONSTRAINT [PK__pj_workf__3213E83F790DC98A] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Indexes structure for table pj_workflow_instance_info
--- ----------------------------
-CREATE NONCLUSTERED INDEX [idx01_wf_instance]
-ON [pj_workflow_instance_info] (
-  [workflow_id] ASC,
-  [status] ASC,
-  [app_id] ASC,
-  [expected_trigger_time] ASC
-)
-GO
-
-
--- ----------------------------
--- Uniques structure for table pj_workflow_instance_info
--- ----------------------------
-ALTER TABLE [pj_workflow_instance_info] ADD CONSTRAINT [uidx01_wf_instance] UNIQUE NONCLUSTERED ([wf_instance_id] ASC)
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_workflow_instance_info
--- ----------------------------
-ALTER TABLE [pj_workflow_instance_info] ADD CONSTRAINT [PK__pj_workf__3213E83F5AF8A72D] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
-
--- ----------------------------
--- Indexes structure for table pj_workflow_node_info
--- ----------------------------
-CREATE NONCLUSTERED INDEX [idx01_workflow_node_info]
-ON [pj_workflow_node_info] (
-  [workflow_id] ASC,
-  [gmt_create] ASC
-)
-GO
-
-
--- ----------------------------
--- Primary Key structure for table pj_workflow_node_info
--- ----------------------------
-ALTER TABLE [pj_workflow_node_info] ADD CONSTRAINT [PK__pj_workf__3213E83FD000EE6D] PRIMARY KEY CLUSTERED ([id])
-WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-ON [PRIMARY]
-GO
-
diff --git a/script/sql/sqlserver/snail_job_sqlserver.sql b/script/sql/sqlserver/snail_job_sqlserver.sql
new file mode 100644
index 0000000..d6fa1ae
--- /dev/null
+++ b/script/sql/sqlserver/snail_job_sqlserver.sql
@@ -0,0 +1,2697 @@
+/*
+ SnailJob Database Transfer Tool
+ Source Server Type    : MySQL
+ Target Server Type    : Microsoft SQL Server
+ Date: 2024-05-13 23:03:34
+*/
+
+
+-- sj_namespace
+CREATE TABLE sj_namespace
+(
+    id          bigint        NOT NULL PRIMARY KEY IDENTITY,
+    name        nvarchar(64)  NOT NULL,
+    unique_id   nvarchar(64)  NOT NULL,
+    description nvarchar(256) NOT NULL DEFAULT '',
+    deleted     tinyint       NOT NULL DEFAULT 0,
+    create_dt   datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt   datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_namespace_01 ON sj_namespace (name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_namespace',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍚嶇О',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_namespace',
+     'COLUMN', N'name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍞竴id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_namespace',
+     'COLUMN', N'unique_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎻忚堪',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_namespace',
+     'COLUMN', N'description'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫昏緫鍒犻櫎 1銆佸垹闄�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_namespace',
+     'COLUMN', N'deleted'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_namespace',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_namespace',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_namespace'
+GO
+
+INSERT INTO sj_namespace(name, unique_id, description, deleted, create_dt, update_dt) VALUES (N'Development', N'dev', N'', 0, getdate(), getdate())
+GO
+INSERT INTO sj_namespace(name, unique_id, description, deleted, create_dt, update_dt) VALUES (N'Production', N'prod', N'', 0, getdate(), getdate())
+GO
+
+-- sj_group_config
+CREATE TABLE sj_group_config
+(
+    id                bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id      nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name        nvarchar(64)  NOT NULL DEFAULT '',
+    description       nvarchar(256) NOT NULL DEFAULT '',
+    token             nvarchar(64)  NOT NULL DEFAULT 'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT',
+    group_status      tinyint       NOT NULL DEFAULT 0,
+    version           int           NOT NULL,
+    group_partition   int           NOT NULL,
+    id_generator_mode tinyint       NOT NULL DEFAULT 1,
+    init_scene        tinyint       NOT NULL DEFAULT 0,
+    bucket_index      int           NOT NULL DEFAULT 0,
+    create_dt         datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt         datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_group_config_01 ON sj_group_config (namespace_id, group_name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勬弿杩�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'description'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'token',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'token'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勭姸鎬� 0銆佹湭鍚敤 1銆佸惎鐢�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'group_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鐗堟湰鍙�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'version'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒嗗尯',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'group_partition'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍞竴id鐢熸垚妯″紡 榛樿鍙锋妯″紡',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'id_generator_mode'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏄惁鍒濆鍖栧満鏅� 0:鍚� 1:鏄�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'init_scene'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'bucket',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'bucket_index'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勯厤缃�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_group_config'
+GO
+
+INSERT INTO sj_group_config(namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, bucket_index, create_dt, update_dt) VALUES (N'dev', N'ruoyi_group', N'', N'SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT', N'1', N'1', N'0', N'1', N'1', N'4', getdate(), getdate())
+GO
+
+-- sj_notify_config
+CREATE TABLE sj_notify_config
+(
+    id                     bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id           nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name             nvarchar(64)  NOT NULL,
+    business_id            nvarchar(64)  NOT NULL,
+    system_task_type       tinyint       NOT NULL DEFAULT 3,
+    notify_status          tinyint       NOT NULL DEFAULT 0,
+    recipient_ids          nvarchar(128) NOT NULL,
+    notify_threshold       int           NOT NULL DEFAULT 0,
+    notify_scene           tinyint       NOT NULL DEFAULT 0,
+    rate_limiter_status    tinyint       NOT NULL DEFAULT 0,
+    rate_limiter_threshold int           NOT NULL DEFAULT 0,
+    description            nvarchar(256) NOT NULL DEFAULT '',
+    create_dt              datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt              datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_notify_config_01 ON sj_notify_config (namespace_id, group_name, business_id)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓氬姟id  ( job_id鎴杦orkflow_id鎴杝cene_name ) ',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'business_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟绫诲瀷 1. 閲嶈瘯浠诲姟 2. 閲嶈瘯鍥炶皟 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'system_task_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫氱煡鐘舵�� 0銆佹湭鍚敤 1銆佸惎鐢�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'notify_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎺ユ敹浜篿d鍒楄〃',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'recipient_ids'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫氱煡闃堝��',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'notify_threshold'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫氱煡鍦烘櫙',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'notify_scene'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'闄愭祦鐘舵�� 0銆佹湭鍚敤 1銆佸惎鐢�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'rate_limiter_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'姣忕闄愭祦闃堝��',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'rate_limiter_threshold'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎻忚堪',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'description'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫氱煡閰嶇疆',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_config'
+GO
+
+-- sj_notify_recipient
+CREATE TABLE sj_notify_recipient
+(
+    id               bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id     nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    recipient_name   nvarchar(64)  NOT NULL,
+    notify_type      tinyint       NOT NULL DEFAULT 0,
+    notify_attribute nvarchar(512) NOT NULL,
+    description      nvarchar(256) NOT NULL DEFAULT '',
+    create_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_notify_recipient_01 ON sj_notify_recipient (namespace_id)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎺ユ敹浜哄悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient',
+     'COLUMN', N'recipient_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫氱煡绫诲瀷 1銆侀拤閽� 2銆侀偖浠� 3銆佷紒涓氬井淇� 4 椋炰功',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient',
+     'COLUMN', N'notify_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閰嶇疆灞炴��',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient',
+     'COLUMN', N'notify_attribute'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎻忚堪',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient',
+     'COLUMN', N'description'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛婅閫氱煡鎺ユ敹浜�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_notify_recipient'
+GO
+
+-- sj_retry_dead_letter_0
+CREATE TABLE sj_retry_dead_letter_0
+(
+    id            bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id  nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    unique_id     nvarchar(64)  NOT NULL,
+    group_name    nvarchar(64)  NOT NULL,
+    scene_name    nvarchar(64)  NOT NULL,
+    idempotent_id nvarchar(64)  NOT NULL,
+    biz_no        nvarchar(64)  NOT NULL DEFAULT '',
+    executor_name nvarchar(512) NOT NULL DEFAULT '',
+    args_str      nvarchar(max) NOT NULL,
+    ext_attrs     nvarchar(max) NOT NULL,
+    task_type     tinyint       NOT NULL DEFAULT 1,
+    create_dt     datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_retry_dead_letter_0_01 ON sj_retry_dead_letter_0 (namespace_id, group_name, unique_id)
+GO
+
+CREATE INDEX idx_sj_retry_dead_letter_0_01 ON sj_retry_dead_letter_0 (namespace_id, group_name, scene_name)
+GO
+CREATE INDEX idx_sj_retry_dead_letter_0_02 ON sj_retry_dead_letter_0 (idempotent_id)
+GO
+CREATE INDEX idx_sj_retry_dead_letter_0_03 ON sj_retry_dead_letter_0 (biz_no)
+GO
+CREATE INDEX idx_sj_retry_dead_letter_0_04 ON sj_retry_dead_letter_0 (create_dt)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍚岀粍涓媔d鍞竴',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'unique_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍦烘櫙鍚嶇О',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'scene_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'骞傜瓑id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'idempotent_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓氬姟缂栧彿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'biz_no'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鍣ㄥ悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'executor_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鏂规硶鍙傛暟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'args_str'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'task_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'姝讳俊闃熷垪琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_dead_letter_0'
+GO
+
+-- sj_retry_task_0
+CREATE TABLE sj_retry_task_0
+(
+    id              bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id    nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    unique_id       nvarchar(64)  NOT NULL,
+    group_name      nvarchar(64)  NOT NULL,
+    scene_name      nvarchar(64)  NOT NULL,
+    idempotent_id   nvarchar(64)  NOT NULL,
+    biz_no          nvarchar(64)  NOT NULL DEFAULT '',
+    executor_name   nvarchar(512) NOT NULL DEFAULT '',
+    args_str        nvarchar(max) NOT NULL,
+    ext_attrs       nvarchar(max) NOT NULL,
+    next_trigger_at datetime2     NOT NULL,
+    retry_count     int           NOT NULL DEFAULT 0,
+    retry_status    tinyint       NOT NULL DEFAULT 0,
+    task_type       tinyint       NOT NULL DEFAULT 1,
+    create_dt       datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt       datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_retry_task_0_01 ON sj_retry_task_0 (namespace_id, group_name, unique_id)
+GO
+
+CREATE INDEX idx_sj_retry_task_0_01 ON sj_retry_task_0 (namespace_id, group_name, scene_name)
+GO
+CREATE INDEX idx_sj_retry_task_0_02 ON sj_retry_task_0 (namespace_id, group_name, task_type)
+GO
+CREATE INDEX idx_sj_retry_task_0_03 ON sj_retry_task_0 (namespace_id, group_name, retry_status)
+GO
+CREATE INDEX idx_sj_retry_task_0_04 ON sj_retry_task_0 (idempotent_id)
+GO
+CREATE INDEX idx_sj_retry_task_0_05 ON sj_retry_task_0 (biz_no)
+GO
+CREATE INDEX idx_sj_retry_task_0_06 ON sj_retry_task_0 (create_dt)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍚岀粍涓媔d鍞竴',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'unique_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍦烘櫙鍚嶇О',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'scene_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'骞傜瓑id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'idempotent_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓氬姟缂栧彿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'biz_no'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鍣ㄥ悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'executor_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鏂规硶鍙傛暟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'args_str'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓嬫瑙﹀彂鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'next_trigger_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閲嶈瘯娆℃暟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'retry_count'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閲嶈瘯鐘舵�� 0銆侀噸璇曚腑 1銆佹垚鍔� 2銆佹渶澶ч噸璇曟鏁�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'retry_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'task_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_0'
+GO
+
+-- sj_retry_task_log
+CREATE TABLE sj_retry_task_log
+(
+    id            bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id  nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    unique_id     nvarchar(64)  NOT NULL,
+    group_name    nvarchar(64)  NOT NULL,
+    scene_name    nvarchar(64)  NOT NULL,
+    idempotent_id nvarchar(64)  NOT NULL,
+    biz_no        nvarchar(64)  NOT NULL DEFAULT '',
+    executor_name nvarchar(512) NOT NULL DEFAULT '',
+    args_str      nvarchar(max) NOT NULL,
+    ext_attrs     nvarchar(max) NOT NULL,
+    retry_status  tinyint       NOT NULL DEFAULT 0,
+    task_type     tinyint       NOT NULL DEFAULT 1,
+    create_dt     datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt     datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_retry_task_log_01 ON sj_retry_task_log (namespace_id, group_name, scene_name)
+GO
+CREATE INDEX idx_sj_retry_task_log_02 ON sj_retry_task_log (retry_status)
+GO
+CREATE INDEX idx_sj_retry_task_log_03 ON sj_retry_task_log (idempotent_id)
+GO
+CREATE INDEX idx_sj_retry_task_log_04 ON sj_retry_task_log (unique_id)
+GO
+CREATE INDEX idx_sj_retry_task_log_05 ON sj_retry_task_log (biz_no)
+GO
+CREATE INDEX idx_sj_retry_task_log_06 ON sj_retry_task_log (create_dt)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍚岀粍涓媔d鍞竴',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'unique_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍦烘櫙鍚嶇О',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'scene_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'骞傜瓑id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'idempotent_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓氬姟缂栧彿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'biz_no'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鍣ㄥ悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'executor_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鏂规硶鍙傛暟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'args_str'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閲嶈瘯鐘舵�� 0銆侀噸璇曚腑 1銆佹垚鍔� 2銆佹渶澶ф鏁�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'retry_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟绫诲瀷 1銆侀噸璇曟暟鎹� 2銆佸洖璋冩暟鎹�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'task_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鏃ュ織鍩虹淇℃伅琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log'
+GO
+
+-- sj_retry_task_log_message
+CREATE TABLE sj_retry_task_log_message
+(
+    id           bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name   nvarchar(64)  NOT NULL,
+    unique_id    nvarchar(64)  NOT NULL,
+    message      nvarchar(max) NOT NULL,
+    log_num      int           NOT NULL DEFAULT 1,
+    real_time    bigint        NOT NULL DEFAULT 0,
+    create_dt    datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_retry_task_log_message_01 ON sj_retry_task_log_message (namespace_id, group_name, unique_id)
+GO
+CREATE INDEX idx_sj_retry_task_log_message_02 ON sj_retry_task_log_message (create_dt)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍚岀粍涓媔d鍞竴',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message',
+     'COLUMN', N'unique_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'寮傚父淇℃伅',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message',
+     'COLUMN', N'message'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message',
+     'COLUMN', N'log_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婃姤鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message',
+     'COLUMN', N'real_time'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟璋冨害鏃ュ織淇℃伅璁板綍琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_task_log_message'
+GO
+
+-- sj_retry_scene_config
+CREATE TABLE sj_retry_scene_config
+(
+    id               bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id     nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    scene_name       nvarchar(64)  NOT NULL,
+    group_name       nvarchar(64)  NOT NULL,
+    scene_status     tinyint       NOT NULL DEFAULT 0,
+    max_retry_count  int           NOT NULL DEFAULT 5,
+    back_off         tinyint       NOT NULL DEFAULT 1,
+    trigger_interval nvarchar(16)  NOT NULL DEFAULT '',
+    deadline_request bigint        NOT NULL DEFAULT 60000,
+    executor_timeout int           NOT NULL DEFAULT 5,
+    route_key        tinyint       NOT NULL DEFAULT 4,
+    description      nvarchar(256) NOT NULL DEFAULT '',
+    create_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_retry_scene_config_01 ON sj_retry_scene_config (namespace_id, group_name, scene_name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍦烘櫙鍚嶇О',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'scene_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勭姸鎬� 0銆佹湭鍚敤 1銆佸惎鐢�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'scene_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏈�澶ч噸璇曟鏁�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'max_retry_count'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'1銆侀粯璁ょ瓑绾� 2銆佸浐瀹氶棿闅旀椂闂� 3銆丆RON 琛ㄨ揪寮�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'back_off'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'闂撮殧鏃堕暱',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'trigger_interval'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'Deadline Request 璋冪敤閾捐秴鏃� 鍗曚綅姣',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'deadline_request'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'executor_timeout'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'璺敱绛栫暐',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'route_key'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎻忚堪',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'description'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍦烘櫙閰嶇疆',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_scene_config'
+GO
+
+-- sj_server_node
+CREATE TABLE sj_server_node
+(
+    id           bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name   nvarchar(64)  NOT NULL,
+    host_id      nvarchar(64)  NOT NULL,
+    host_ip      nvarchar(64)  NOT NULL,
+    host_port    int           NOT NULL,
+    expire_at    datetime2     NOT NULL,
+    node_type    tinyint       NOT NULL,
+    ext_attrs    nvarchar(256) NULL     DEFAULT '',
+    create_dt    datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt    datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_server_node_01 ON sj_server_node (host_id, host_ip)
+GO
+
+CREATE INDEX idx_sj_server_node_01 ON sj_server_node (namespace_id, group_name)
+GO
+CREATE INDEX idx_sj_server_node_02 ON sj_server_node (expire_at, node_type)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓绘満id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'host_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏈哄櫒ip',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'host_ip'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏈哄櫒绔彛',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'host_port'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'杩囨湡鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'expire_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鑺傜偣绫诲瀷 1銆佸鎴风 2銆佹槸鏈嶅姟绔�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'node_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏈嶅姟鍣ㄨ妭鐐�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_server_node'
+GO
+
+-- sj_distributed_lock
+CREATE TABLE sj_distributed_lock
+(
+    id         bigint        NOT NULL PRIMARY KEY IDENTITY,
+    name       nvarchar(64)  NOT NULL,
+    lock_until datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    locked_at  datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    locked_by  nvarchar(255) NOT NULL,
+    create_dt  datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt  datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_distributed_lock',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閿佸悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_distributed_lock',
+     'COLUMN', N'name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閿佸畾鏃堕暱',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_distributed_lock',
+     'COLUMN', N'lock_until'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閿佸畾鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_distributed_lock',
+     'COLUMN', N'locked_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閿佸畾鑰�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_distributed_lock',
+     'COLUMN', N'locked_by'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_distributed_lock',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_distributed_lock',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閿佸畾琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_distributed_lock'
+GO
+
+-- sj_system_user
+CREATE TABLE sj_system_user
+(
+    id        bigint        NOT NULL PRIMARY KEY IDENTITY,
+    username  nvarchar(64)  NOT NULL,
+    password  nvarchar(128) NOT NULL,
+    role      tinyint       NOT NULL DEFAULT 0,
+    create_dt datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'璐﹀彿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user',
+     'COLUMN', N'username'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'瀵嗙爜',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user',
+     'COLUMN', N'password'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'瑙掕壊锛�1-鏅�氱敤鎴枫��2-绠$悊鍛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user',
+     'COLUMN', N'role'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'绯荤粺鐢ㄦ埛琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user'
+GO
+
+-- pwd: admin
+INSERT INTO sj_system_user(username, password, role, create_dt, update_dt) VALUES (N'admin', N'465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173ac', N'2', getdate(), getdate())
+GO
+
+-- sj_system_user_permission
+CREATE TABLE sj_system_user_permission
+(
+    id             bigint       NOT NULL PRIMARY KEY IDENTITY,
+    group_name     nvarchar(64) NOT NULL,
+    namespace_id   nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    system_user_id bigint       NOT NULL,
+    create_dt      datetime2    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt      datetime2    NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_system_user_permission_01 ON sj_system_user_permission (namespace_id, group_name, system_user_id)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user_permission',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user_permission',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user_permission',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'绯荤粺鐢ㄦ埛id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user_permission',
+     'COLUMN', N'system_user_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user_permission',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user_permission',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'绯荤粺鐢ㄦ埛鏉冮檺琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_system_user_permission'
+GO
+
+-- sj_sequence_alloc
+CREATE TABLE sj_sequence_alloc
+(
+    id           bigint       NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name   nvarchar(64) NOT NULL DEFAULT '',
+    max_id       bigint       NOT NULL DEFAULT 1,
+    step         int          NOT NULL DEFAULT 100,
+    update_dt    datetime2    NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_sequence_alloc_01 ON sj_sequence_alloc (namespace_id, group_name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_sequence_alloc',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_sequence_alloc',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_sequence_alloc',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏈�澶d',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_sequence_alloc',
+     'COLUMN', N'max_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'姝ラ暱',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_sequence_alloc',
+     'COLUMN', N'step'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏇存柊鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_sequence_alloc',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍙锋妯″紡搴忓彿ID鍒嗛厤琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_sequence_alloc'
+GO
+
+-- sj_job
+CREATE TABLE sj_job
+(
+    id               bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id     nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name       nvarchar(64)  NOT NULL,
+    job_name         nvarchar(64)  NOT NULL,
+    args_str         nvarchar(max) NULL     DEFAULT NULL,
+    args_type        tinyint       NOT NULL DEFAULT 1,
+    next_trigger_at  bigint        NOT NULL,
+    job_status       tinyint       NOT NULL DEFAULT 1,
+    task_type        tinyint       NOT NULL DEFAULT 1,
+    route_key        tinyint       NOT NULL DEFAULT 4,
+    executor_type    tinyint       NOT NULL DEFAULT 1,
+    executor_info    nvarchar(255) NULL     DEFAULT NULL,
+    trigger_type     tinyint       NOT NULL,
+    trigger_interval nvarchar(255) NOT NULL,
+    block_strategy   tinyint       NOT NULL DEFAULT 1,
+    executor_timeout int           NOT NULL DEFAULT 0,
+    max_retry_times  int           NOT NULL DEFAULT 0,
+    parallel_num     int           NOT NULL DEFAULT 1,
+    retry_interval   int           NOT NULL DEFAULT 0,
+    bucket_index     int           NOT NULL DEFAULT 0,
+    resident         tinyint       NOT NULL DEFAULT 0,
+    description      nvarchar(256) NOT NULL DEFAULT '',
+    ext_attrs        nvarchar(256) NULL     DEFAULT '',
+    deleted          tinyint       NOT NULL DEFAULT 0,
+    create_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name)
+GO
+CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index)
+GO
+CREATE INDEX idx_sj_job_03 ON sj_job (create_dt)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍚嶇О',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'job_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鏂规硶鍙傛暟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'args_str'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍙傛暟绫诲瀷 ',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'args_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓嬫瑙﹀彂鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'next_trigger_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鐘舵�� 0銆佸叧闂��1銆佸紑鍚�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'job_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟绫诲瀷 1銆侀泦缇� 2銆佸箍鎾� 3銆佸垏鐗�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'task_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'璺敱绛栫暐',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'route_key'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鍣ㄧ被鍨�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'executor_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鍣ㄥ悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'executor_info'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'瑙﹀彂绫诲瀷 1.CRON 琛ㄨ揪寮� 2. 鍥哄畾鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'trigger_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'闂撮殧鏃堕暱',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'trigger_interval'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'闃诲绛栫暐 1銆佷涪寮� 2銆佽鐩� 3銆佸苟琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'block_strategy'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'executor_timeout'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏈�澶ч噸璇曟鏁�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'max_retry_times'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'骞惰鏁�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'parallel_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閲嶈瘯闂撮殧 ( s ) ',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'retry_interval'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'bucket',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'bucket_index'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏄惁鏄父椹讳换鍔�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'resident'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎻忚堪',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'description'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫昏緫鍒犻櫎 1銆佸垹闄�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'deleted'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟淇℃伅',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job'
+GO
+
+INSERT INTO sj_job (namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, description, ext_attrs, deleted, create_dt, update_dt) VALUES (N'dev', N'ruoyi_group', N'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, N'testJobExecutor', 2, N'60', 1, 60, 3, 1, 1, 116, 0, N'', N'', 0, getdate(), getdate())
+GO
+
+-- sj_job_log_message
+CREATE TABLE sj_job_log_message
+(
+    id            bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id  nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name    nvarchar(64)  NOT NULL,
+    job_id        bigint        NOT NULL,
+    task_batch_id bigint        NOT NULL,
+    task_id       bigint        NOT NULL,
+    message       nvarchar(max) NOT NULL,
+    log_num       int           NOT NULL DEFAULT 1,
+    real_time     bigint        NOT NULL DEFAULT 0,
+    ext_attrs     nvarchar(256) NULL     DEFAULT '',
+    create_dt     datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_job_log_message_01 ON sj_job_log_message (task_batch_id, task_id)
+GO
+CREATE INDEX idx_sj_job_log_message_02 ON sj_job_log_message (create_dt)
+GO
+CREATE INDEX idx_sj_job_log_message_03 ON sj_job_log_message (namespace_id, group_name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟淇℃伅id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'job_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵规id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'task_batch_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'璋冨害浠诲姟id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'task_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'璋冨害淇℃伅',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'message'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'log_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婃姤鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'real_time'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'璋冨害鏃ュ織',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_log_message'
+GO
+
+-- sj_job_task
+CREATE TABLE sj_job_task
+(
+    id             bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id   nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name     nvarchar(64)  NOT NULL,
+    job_id         bigint        NOT NULL,
+    task_batch_id  bigint        NOT NULL,
+    parent_id      bigint        NOT NULL DEFAULT 0,
+    task_status    tinyint       NOT NULL DEFAULT 0,
+    retry_count    int           NOT NULL DEFAULT 0,
+    client_info    nvarchar(128) NULL     DEFAULT NULL,
+    result_message nvarchar(max) NOT NULL,
+    args_str       nvarchar(max) NULL     DEFAULT NULL,
+    args_type      tinyint       NOT NULL DEFAULT 1,
+    ext_attrs      nvarchar(256) NULL     DEFAULT '',
+    create_dt      datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt      datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_job_task_01 ON sj_job_task (task_batch_id, task_status)
+GO
+CREATE INDEX idx_sj_job_task_02 ON sj_job_task (create_dt)
+GO
+CREATE INDEX idx_sj_job_task_03 ON sj_job_task (namespace_id, group_name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟淇℃伅id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'job_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'璋冨害浠诲姟id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'task_batch_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鐖舵墽琛屽櫒id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'parent_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鐨勭姸鎬� 0銆佸け璐� 1銆佹垚鍔�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'task_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閲嶈瘯娆℃暟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'retry_count'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'瀹㈡埛绔湴鍧� clientId#ip:port',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'client_info'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц缁撴灉',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'result_message'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鏂规硶鍙傛暟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'args_str'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍙傛暟绫诲瀷 ',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'args_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟瀹炰緥',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task'
+GO
+
+-- sj_job_task_batch
+CREATE TABLE sj_job_task_batch
+(
+    id                      bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id            nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name              nvarchar(64)  NOT NULL,
+    job_id                  bigint        NOT NULL,
+    workflow_node_id        bigint        NOT NULL DEFAULT 0,
+    parent_workflow_node_id bigint        NOT NULL DEFAULT 0,
+    workflow_task_batch_id  bigint        NOT NULL DEFAULT 0,
+    task_batch_status       tinyint       NOT NULL DEFAULT 0,
+    operation_reason        tinyint       NOT NULL DEFAULT 0,
+    execution_at            bigint        NOT NULL DEFAULT 0,
+    system_task_type        tinyint       NOT NULL DEFAULT 3,
+    parent_id               nvarchar(64)  NOT NULL DEFAULT '',
+    ext_attrs               nvarchar(256) NULL     DEFAULT '',
+    deleted                 tinyint       NOT NULL DEFAULT 0,
+    create_dt               datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt               datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_job_task_batch_01 ON sj_job_task_batch (job_id, task_batch_status)
+GO
+CREATE INDEX idx_sj_job_task_batch_02 ON sj_job_task_batch (create_dt)
+GO
+CREATE INDEX idx_sj_job_task_batch_03 ON sj_job_task_batch (namespace_id, group_name)
+GO
+CREATE INDEX idx_sj_job_task_batch_04 ON sj_job_task_batch (workflow_task_batch_id, workflow_node_id)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'job_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佽妭鐐筰d',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'workflow_node_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佷换鍔$埗鎵规id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'parent_workflow_node_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佷换鍔℃壒娆d',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'workflow_task_batch_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵规鐘舵�� 0銆佸け璐� 1銆佹垚鍔�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'task_batch_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎿嶄綔鍘熷洜',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'operation_reason'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵ц鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'execution_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟绫诲瀷 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'system_task_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鐖惰妭鐐�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'parent_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫昏緫鍒犻櫎 1銆佸垹闄�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'deleted'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵规',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_task_batch'
+GO
+
+-- sj_job_summary
+CREATE TABLE sj_job_summary
+(
+    id               bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id     nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name       nvarchar(64)  NOT NULL DEFAULT '',
+    business_id      bigint        NOT NULL,
+    system_task_type tinyint       NOT NULL DEFAULT 3,
+    trigger_at       datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    success_num      int           NOT NULL DEFAULT 0,
+    fail_num         int           NOT NULL DEFAULT 0,
+    fail_reason      nvarchar(512) NOT NULL DEFAULT '',
+    stop_num         int           NOT NULL DEFAULT 0,
+    stop_reason      nvarchar(512) NOT NULL DEFAULT '',
+    cancel_num       int           NOT NULL DEFAULT 0,
+    cancel_reason    nvarchar(512) NOT NULL DEFAULT '',
+    create_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_job_summary_01 ON sj_job_summary (trigger_at, system_task_type, business_id)
+GO
+
+CREATE INDEX idx_sj_job_summary_01 ON sj_job_summary (namespace_id, group_name, business_id)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓氬姟id  ( job_id鎴杦orkflow_id ) ',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'business_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟绫诲瀷 3銆丣OB浠诲姟 4銆乄ORKFLOW浠诲姟',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'system_task_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁熻鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'trigger_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц鎴愬姛-鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'success_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц澶辫触-鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'fail_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'澶辫触鍘熷洜',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'fail_reason'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц澶辫触-鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'stop_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'澶辫触鍘熷洜',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'stop_reason'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵ц澶辫触-鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'cancel_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'澶辫触鍘熷洜',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'cancel_reason'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'DashBoard_Job',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_job_summary'
+GO
+
+-- sj_retry_summary
+CREATE TABLE sj_retry_summary
+(
+    id            bigint       NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id  nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name    nvarchar(64) NOT NULL DEFAULT '',
+    scene_name    nvarchar(50) NOT NULL DEFAULT '',
+    trigger_at    datetime2    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    running_num   int          NOT NULL DEFAULT 0,
+    finish_num    int          NOT NULL DEFAULT 0,
+    max_count_num int          NOT NULL DEFAULT 0,
+    suspend_num   int          NOT NULL DEFAULT 0,
+    create_dt     datetime2    NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt     datetime2    NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE UNIQUE INDEX uk_sj_retry_summary_01 ON sj_retry_summary (namespace_id, group_name, scene_name, trigger_at)
+GO
+
+CREATE INDEX idx_sj_retry_summary_01 ON sj_retry_summary (trigger_at)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍦烘櫙鍚嶇О',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'scene_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁熻鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'trigger_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閲嶈瘯涓�-鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'running_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閲嶈瘯瀹屾垚-鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'finish_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閲嶈瘯鍒拌揪鏈�澶ф鏁�-鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'max_count_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鏆傚仠閲嶈瘯-鏃ュ織鏁伴噺',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'suspend_num'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'DashBoard_Retry',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_retry_summary'
+GO
+
+-- sj_workflow
+CREATE TABLE sj_workflow
+(
+    id               bigint        NOT NULL PRIMARY KEY IDENTITY,
+    workflow_name    nvarchar(64)  NOT NULL,
+    namespace_id     nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name       nvarchar(64)  NOT NULL,
+    workflow_status  tinyint       NOT NULL DEFAULT 1,
+    trigger_type     tinyint       NOT NULL,
+    trigger_interval nvarchar(255) NOT NULL,
+    next_trigger_at  bigint        NOT NULL,
+    block_strategy   tinyint       NOT NULL DEFAULT 1,
+    executor_timeout int           NOT NULL DEFAULT 0,
+    description      nvarchar(256) NOT NULL DEFAULT '',
+    flow_info        nvarchar(max) NULL     DEFAULT NULL,
+    bucket_index     int           NOT NULL DEFAULT 0,
+    version          int           NOT NULL,
+    ext_attrs        nvarchar(256) NULL     DEFAULT '',
+    deleted          tinyint       NOT NULL DEFAULT 0,
+    create_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt        datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt)
+GO
+CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佸悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'workflow_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佺姸鎬� 0銆佸叧闂��1銆佸紑鍚�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'workflow_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'瑙﹀彂绫诲瀷 1.CRON 琛ㄨ揪寮� 2. 鍥哄畾鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'trigger_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'闂撮殧鏃堕暱',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'trigger_interval'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓嬫瑙﹀彂鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'next_trigger_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'闃诲绛栫暐 1銆佷涪寮� 2銆佽鐩� 3銆佸苟琛�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'block_strategy'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵ц瓒呮椂鏃堕棿锛屽崟浣嶇',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'executor_timeout'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎻忚堪',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'description'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'娴佺▼淇℃伅',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'flow_info'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'bucket',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'bucket_index'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鐗堟湰鍙�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'version'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫昏緫鍒犻櫎 1銆佸垹闄�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'deleted'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow'
+GO
+
+-- sj_workflow_node
+CREATE TABLE sj_workflow_node
+(
+    id                   bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id         nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    node_name            nvarchar(64)  NOT NULL,
+    group_name           nvarchar(64)  NOT NULL,
+    job_id               bigint        NOT NULL,
+    workflow_id          bigint        NOT NULL,
+    node_type            tinyint       NOT NULL DEFAULT 1,
+    expression_type      tinyint       NOT NULL DEFAULT 0,
+    fail_strategy        tinyint       NOT NULL DEFAULT 1,
+    workflow_node_status tinyint       NOT NULL DEFAULT 1,
+    priority_level       int           NOT NULL DEFAULT 1,
+    node_info            nvarchar(max) NULL     DEFAULT NULL,
+    version              int           NOT NULL,
+    ext_attrs            nvarchar(256) NULL     DEFAULT '',
+    deleted              tinyint       NOT NULL DEFAULT 0,
+    create_dt            datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt            datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_workflow_node_01 ON sj_workflow_node (create_dt)
+GO
+CREATE INDEX idx_sj_workflow_node_02 ON sj_workflow_node (namespace_id, group_name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鑺傜偣鍚嶇О',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'node_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟淇℃伅id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'job_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴両D',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'workflow_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'1銆佷换鍔¤妭鐐� 2銆佹潯浠惰妭鐐�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'node_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'1銆丼pEl銆�2銆丄viator 3銆丵L',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'expression_type'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'澶辫触绛栫暐 1銆佽烦杩� 2銆侀樆濉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'fail_strategy'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佽妭鐐圭姸鎬� 0銆佸叧闂��1銆佸紑鍚�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'workflow_node_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浼樺厛绾�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'priority_level'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鑺傜偣淇℃伅 ',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'node_info'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鐗堟湰鍙�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'version'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫昏緫鍒犻櫎 1銆佸垹闄�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'deleted'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佽妭鐐�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_node'
+GO
+
+-- sj_workflow_task_batch
+CREATE TABLE sj_workflow_task_batch
+(
+    id                bigint        NOT NULL PRIMARY KEY IDENTITY,
+    namespace_id      nvarchar(64)  NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
+    group_name        nvarchar(64)  NOT NULL,
+    workflow_id       bigint        NOT NULL,
+    task_batch_status tinyint       NOT NULL DEFAULT 0,
+    operation_reason  tinyint       NOT NULL DEFAULT 0,
+    flow_info         nvarchar(max) NULL     DEFAULT NULL,
+    execution_at      bigint        NOT NULL DEFAULT 0,
+    ext_attrs         nvarchar(256) NULL     DEFAULT '',
+    deleted           tinyint       NOT NULL DEFAULT 0,
+    create_dt         datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_dt         datetime2     NOT NULL DEFAULT CURRENT_TIMESTAMP
+)
+GO
+
+CREATE INDEX idx_sj_workflow_task_batch_01 ON sj_workflow_task_batch (workflow_id, task_batch_status)
+GO
+CREATE INDEX idx_sj_workflow_task_batch_02 ON sj_workflow_task_batch (create_dt)
+GO
+CREATE INDEX idx_sj_workflow_task_batch_03 ON sj_workflow_task_batch (namespace_id, group_name)
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'涓婚敭',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍛藉悕绌洪棿id',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'namespace_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'缁勫悕绉�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'group_name'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佷换鍔d',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'workflow_id'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵规鐘舵�� 0銆佸け璐� 1銆佹垚鍔�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'task_batch_status'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎿嶄綔鍘熷洜',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'operation_reason'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'娴佺▼淇℃伅',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'flow_info'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'浠诲姟鎵ц鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'execution_at'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鎵╁睍瀛楁',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'ext_attrs'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'閫昏緫鍒犻櫎 1銆佸垹闄�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'deleted'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'鍒涘缓鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'create_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'淇敼鏃堕棿',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch',
+     'COLUMN', N'update_dt'
+GO
+
+EXEC sp_addextendedproperty
+     'MS_Description', N'宸ヤ綔娴佹壒娆�',
+     'SCHEMA', N'dbo',
+     'TABLE', N'sj_workflow_task_batch'
+GO
+
diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
index a27ac5d..00f0771 100644
--- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
+++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql
@@ -17,7 +17,7 @@
     union_id           nvarchar(255)     NULL,
     scope              nvarchar(255)     NULL,
     token_type         nvarchar(255)     NULL,
-    id_token           nvarchar(255)     NULL,
+    id_token           nvarchar(2000)    NULL,
     mac_algorithm      nvarchar(255)     NULL,
     mac_key            nvarchar(255)     NULL,
     code               nvarchar(255)     NULL,
@@ -926,7 +926,8 @@
     tenant_id   nvarchar(20) DEFAULT ('000000') NULL,
     parent_id   bigint       DEFAULT ((0)) NULL,
     ancestors   nvarchar(500)DEFAULT ''    NULL,
-    dept_name   nvarchar(30) DEFAULT ''    NULL,
+    dept_name   nvarchar(30)               NULL,
+    dept_category nvarchar(100) DEFAULT '' NULL,
     order_num   int          DEFAULT ((0)) NULL,
     leader      bigint                     NULL,
     phone       nvarchar(11)               NULL,
@@ -974,6 +975,12 @@
     'SCHEMA', N'dbo',
     'TABLE', N'sys_dept',
     'COLUMN', N'dept_name'
+GO
+EXEC sys.sp_addextendedproperty
+    'MS_Description', N'閮ㄩ棬绫诲埆缂栫爜' ,
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_dept',
+    'COLUMN', N'dept_category'
 GO
 EXEC sys.sp_addextendedproperty
     'MS_Description', N'鏄剧ず椤哄簭' ,
@@ -1047,25 +1054,25 @@
     'TABLE', N'sys_dept'
 GO
 
-INSERT sys_dept VALUES (100, N'000000', 0, N'0', N'XXX绉戞妧', 0, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (100, N'000000', 0, N'0', N'XXX绉戞妧', NULL, 0, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (101, N'000000', 100, N'0,100', N'娣卞湷鎬诲叕鍙�', 1, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (101, N'000000', 100, N'0,100', N'娣卞湷鎬诲叕鍙�', NULL, 1, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (102, N'000000', 100, N'0,100', N'闀挎矙鍒嗗叕鍙�', 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (102, N'000000', 100, N'0,100', N'闀挎矙鍒嗗叕鍙�', NULL, 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (103, N'000000', 101, N'0,100,101', N'鐮斿彂閮ㄩ棬', 1, 1, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (103, N'000000', 101, N'0,100,101', N'鐮斿彂閮ㄩ棬', NULL, 1, 1, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (104, N'000000', 101, N'0,100,101', N'甯傚満閮ㄩ棬', 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (104, N'000000', 101, N'0,100,101', N'甯傚満閮ㄩ棬', NULL, 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (105, N'000000', 101, N'0,100,101', N'娴嬭瘯閮ㄩ棬', 3, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (105, N'000000', 101, N'0,100,101', N'娴嬭瘯閮ㄩ棬', NULL, 3, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (106, N'000000', 101, N'0,100,101', N'璐㈠姟閮ㄩ棬', 4, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (106, N'000000', 101, N'0,100,101', N'璐㈠姟閮ㄩ棬', NULL, 4, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (107, N'000000', 101, N'0,100,101', N'杩愮淮閮ㄩ棬', 5, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (107, N'000000', 101, N'0,100,101', N'杩愮淮閮ㄩ棬', NULL, 5, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (108, N'000000', 102, N'0,100,102', N'甯傚満閮ㄩ棬', 1, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (108, N'000000', 102, N'0,100,102', N'甯傚満閮ㄩ棬', NULL, 1, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
-INSERT sys_dept VALUES (109, N'000000', 102, N'0,100,102', N'璐㈠姟閮ㄩ棬', 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
+INSERT sys_dept VALUES (109, N'000000', 102, N'0,100,102', N'璐㈠姟閮ㄩ棬', NULL, 2, NULL, N'15888888888', N'xxx@qq.com', N'0', N'0', 103, 1, getdate(), NULL, NULL)
 GO
 
 CREATE TABLE sys_dict_data
@@ -1647,6 +1654,8 @@
 GO
 INSERT sys_menu VALUES (4, N'PLUS瀹樼綉', 0, 5, N'https://gitee.com/dromara/RuoYi-Vue-Plus', null, N'', 0, 0, N'M', N'0', N'0', N'', N'guide', 103, 1, getdate(), null, null, N'RuoYi-Vue-Plus瀹樼綉鍦板潃');
 GO
+INSERT sys_menu VALUES (5, N'娴嬭瘯鑿滃崟', 0, 5, N'demo', NULL, N'', 1, 0, N'M', N'0', N'0', NULL, N'star', 103, 1, getdate(), NULL, NULL, N'');
+GO
 INSERT sys_menu VALUES (100, N'鐢ㄦ埛绠$悊', 1, 1, N'user', N'system/user/index', N'', 1, 0, N'C', N'0', N'0', N'system:user:list', N'user', 103, 1, getdate(), NULL, NULL, N'鐢ㄦ埛绠$悊鑿滃崟')
 GO
 INSERT sys_menu VALUES (101, N'瑙掕壊绠$悊', 1, 2, N'role', N'system/role/index', N'', 1, 0, N'C', N'0', N'0', N'system:role:list', N'peoples', 103, 1, getdate(), NULL, NULL, N'瑙掕壊绠$悊鑿滃崟')
@@ -1683,7 +1692,7 @@
 GO
 INSERT sys_menu VALUES (118, N'鏂囦欢绠$悊', 1, 10, N'oss', N'system/oss/index', N'', 1, 0, N'C', '0', N'0', N'system:oss:list', N'upload', 103, 1, getdate(), NULL, NULL, N'鏂囦欢绠$悊鑿滃崟');
 GO
-INSERT sys_menu VALUES (120, N'浠诲姟璋冨害涓績', 2, 5, N'powerjob', N'monitor/powerjob/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:powerjob:list', N'job', 103, 1, getdate(), NULL, NULL, N'PowerJob鎺у埗鍙拌彍鍗�');
+INSERT sys_menu VALUES (120, N'浠诲姟璋冨害涓績', 2, 5, N'snailjob', N'monitor/snailjob/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:snailjob:list', N'job', 103, 1, getdate(), NULL, NULL, N'SnailJob鎺у埗鍙拌彍鍗�');
 GO
 INSERT sys_menu VALUES (500, N'鎿嶄綔鏃ュ織', 108, 1, N'operlog', N'monitor/operlog/index', N'', 1, 0, N'C', N'0', N'0', N'monitor:operlog:list', N'form', 103, 1, getdate(), NULL, NULL, N'鎿嶄綔鏃ュ織鑿滃崟')
 GO
@@ -1848,6 +1857,32 @@
 INSERT sys_menu VALUES (1064, N'瀹㈡埛绔鐞嗗垹闄�', 123, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:remove', N'#', 103, 1, getdate(), NULL, NULL, N'');
 GO
 INSERT sys_menu VALUES (1065, N'瀹㈡埛绔鐞嗗鍑�', 123, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'system:client:export', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+-- 娴嬭瘯鑿滃崟
+INSERT sys_menu VALUES (1500, N'娴嬭瘯鍗曡〃', 5, 1, N'demo', N'demo/demo/index', N'', 1, 0, N'C', N'0', N'0', N'demo:demo:list', N'#', 103, 1, getdate(), NULL, NULL, N'娴嬭瘯鍗曡〃鑿滃崟');
+GO
+INSERT sys_menu VALUES (1501, N'娴嬭瘯鍗曡〃鏌ヨ', 1500, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:query', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (1502, N'娴嬭瘯鍗曡〃鏂板', 1500, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:add', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (1503, N'娴嬭瘯鍗曡〃淇敼', 1500, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:edit', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (1504, N'娴嬭瘯鍗曡〃鍒犻櫎', 1500, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:remove', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (1505, N'娴嬭瘯鍗曡〃瀵煎嚭', 1500, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:export', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+
+INSERT sys_menu VALUES (1506, N'娴嬭瘯鏍戣〃', 5, 1, N'tree', N'demo/tree/index', N'', 1, 0, N'C', N'0', N'0', N'demo:tree:list', N'#', 103, 1, getdate(), NULL, NULL, N'娴嬭瘯鏍戣〃鑿滃崟');
+GO
+INSERT sys_menu VALUES (1507, N'娴嬭瘯鏍戣〃鏌ヨ', 1506, 1, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:query', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (1508, N'娴嬭瘯鏍戣〃鏂板', 1506, 2, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:add', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (1509, N'娴嬭瘯鏍戣〃淇敼', 1506, 3, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:edit', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (1510, N'娴嬭瘯鏍戣〃鍒犻櫎', 1506, 4, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:remove', N'#', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_menu VALUES (1511, N'娴嬭瘯鏍戣〃瀵煎嚭', 1506, 5, N'#', N'', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:export', N'#', 103, 1, getdate(), NULL, NULL, N'');
 GO
 
 CREATE TABLE sys_notice
@@ -2107,7 +2142,9 @@
 (
     post_id     bigint                          NOT NULL,
     tenant_id   nvarchar(20) DEFAULT ('000000') NULL,
+    dept_id     bigint                          NOT NULL,
     post_code   nvarchar(64)                    NOT NULL,
+    post_category nvarchar(100)                 NULL,
     post_name   nvarchar(50)                    NOT NULL,
     post_sort   int                             NOT NULL,
     status      nchar(1)                        NOT NULL,
@@ -2137,10 +2174,22 @@
     'COLUMN', N'tenant_id'
 GO
 EXEC sys.sp_addextendedproperty
+    'MS_Description', N'閮ㄩ棬id' ,
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_post',
+    'COLUMN', N'dept_id'
+GO
+EXEC sys.sp_addextendedproperty
     'MS_Description', N'宀椾綅缂栫爜' ,
     'SCHEMA', N'dbo',
     'TABLE', N'sys_post',
     'COLUMN', N'post_code'
+GO
+EXEC sys.sp_addextendedproperty
+    'MS_Description', N'宀椾綅绫诲埆缂栫爜' ,
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_post',
+    'COLUMN', N'post_category'
 GO
 EXEC sys.sp_addextendedproperty
     'MS_Description', N'宀椾綅鍚嶇О' ,
@@ -2202,13 +2251,13 @@
     'TABLE', N'sys_post'
 GO
 
-INSERT sys_post VALUES (1, N'000000', N'ceo', N'钁d簨闀�', 1, N'0', 103, 1, getdate(), NULL, NULL, N'')
+INSERT sys_post VALUES (1, N'000000', 103, N'ceo', NULL,  N'钁d簨闀�', 1, N'0', 103, 1, getdate(), NULL, NULL, N'')
 GO
-INSERT sys_post VALUES (2, N'000000', N'se', N'椤圭洰缁忕悊', 2, N'0', 103, 1, getdate(), NULL, NULL, N'')
+INSERT sys_post VALUES (2, N'000000', 100, N'se', NULL,  N'椤圭洰缁忕悊', 2, N'0', 103, 1, getdate(), NULL, NULL, N'')
 GO
-INSERT sys_post VALUES (3, N'000000', N'hr', N'浜哄姏璧勬簮', 3, N'0', 103, 1, getdate(), NULL, NULL, N'')
+INSERT sys_post VALUES (3, N'000000', 100, N'hr', NULL,  N'浜哄姏璧勬簮', 3, N'0', 103, 1, getdate(), NULL, NULL, N'')
 GO
-INSERT sys_post VALUES (4, N'000000', N'user', N'鏅�氬憳宸�', 4, N'0', 103, 1, getdate(), NULL, NULL, N'')
+INSERT sys_post VALUES (4, N'000000', 100, N'user', NULL,  N'鏅�氬憳宸�', 4, N'0', 103, 1, getdate(), NULL, NULL, N'')
 GO
 
 CREATE TABLE sys_role
@@ -2340,7 +2389,9 @@
 
 INSERT sys_role VALUES (1, N'000000', N'瓒呯骇绠$悊鍛�', N'superadmin', 1, N'1', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N'瓒呯骇绠$悊鍛�')
 GO
-INSERT sys_role VALUES (2, N'000000', N'鏅�氳鑹�', N'common', 2, N'2', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N'鏅�氳鑹�')
+INSERT sys_role VALUES (3, N'000000', N'鏈儴闂ㄥ強浠ヤ笅', N'test1', 3, N'4', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N'');
+GO
+INSERT sys_role VALUES (4, N'000000', N'浠呮湰浜�', N'test2', 4, N'5', 1, 1, N'0', N'0', 103, 1, getdate(), NULL, NULL, N'');
 GO
 
 CREATE TABLE sys_role_dept
@@ -2372,13 +2423,6 @@
     'TABLE', N'sys_role_dept'
 GO
 
-INSERT sys_role_dept VALUES (2, 100)
-GO
-INSERT sys_role_dept VALUES (2, 101)
-GO
-INSERT sys_role_dept VALUES (2, 105)
-GO
-
 CREATE TABLE sys_role_menu
 (
     role_id bigint NOT NULL,
@@ -2408,179 +2452,174 @@
     'TABLE', N'sys_role_menu'
 GO
 
-INSERT sys_role_menu VALUES (2, 1)
+-- ----------------------------
+-- 鍒濆鍖�-瑙掕壊鍜岃彍鍗曞叧鑱旇〃鏁版嵁
+-- ----------------------------
+INSERT sys_role_menu VALUES (3, 1);
 GO
-INSERT sys_role_menu VALUES (2, 2)
+INSERT sys_role_menu VALUES (3, 5);
 GO
-INSERT sys_role_menu VALUES (2, 3)
+INSERT sys_role_menu VALUES (3, 100);
 GO
-INSERT sys_role_menu VALUES (2, 100)
+INSERT sys_role_menu VALUES (3, 101);
 GO
-INSERT sys_role_menu VALUES (2, 101)
+INSERT sys_role_menu VALUES (3, 102);
 GO
-INSERT sys_role_menu VALUES (2, 102)
+INSERT sys_role_menu VALUES (3, 103);
 GO
-INSERT sys_role_menu VALUES (2, 103)
+INSERT sys_role_menu VALUES (3, 104);
 GO
-INSERT sys_role_menu VALUES (2, 104)
+INSERT sys_role_menu VALUES (3, 105);
 GO
-INSERT sys_role_menu VALUES (2, 105)
+INSERT sys_role_menu VALUES (3, 106);
 GO
-INSERT sys_role_menu VALUES (2, 106)
+INSERT sys_role_menu VALUES (3, 107);
 GO
-INSERT sys_role_menu VALUES (2, 107)
+INSERT sys_role_menu VALUES (3, 108);
 GO
-INSERT sys_role_menu VALUES (2, 108)
+INSERT sys_role_menu VALUES (3, 500);
 GO
-INSERT sys_role_menu VALUES (2, 109)
+INSERT sys_role_menu VALUES (3, 501);
 GO
-INSERT sys_role_menu VALUES (2, 110)
+INSERT sys_role_menu VALUES (3, 1001);
 GO
-INSERT sys_role_menu VALUES (2, 111)
+INSERT sys_role_menu VALUES (3, 1002);
 GO
-INSERT sys_role_menu VALUES (2, 112)
+INSERT sys_role_menu VALUES (3, 1003);
 GO
-INSERT sys_role_menu VALUES (2, 113)
+INSERT sys_role_menu VALUES (3, 1004);
 GO
-INSERT sys_role_menu VALUES (2, 114)
+INSERT sys_role_menu VALUES (3, 1005);
 GO
-INSERT sys_role_menu VALUES (2, 115)
+INSERT sys_role_menu VALUES (3, 1006);
 GO
-INSERT sys_role_menu VALUES (2, 116)
+INSERT sys_role_menu VALUES (3, 1007);
 GO
-INSERT sys_role_menu VALUES (2, 500)
+INSERT sys_role_menu VALUES (3, 1008);
 GO
-INSERT sys_role_menu VALUES (2, 501)
+INSERT sys_role_menu VALUES (3, 1009);
 GO
-INSERT sys_role_menu VALUES (2, 1001)
+INSERT sys_role_menu VALUES (3, 1010);
 GO
-INSERT sys_role_menu VALUES (2, 1002)
+INSERT sys_role_menu VALUES (3, 1011);
 GO
-INSERT sys_role_menu VALUES (2, 1003)
+INSERT sys_role_menu VALUES (3, 1012);
 GO
-INSERT sys_role_menu VALUES (2, 1004)
+INSERT sys_role_menu VALUES (3, 1013);
 GO
-INSERT sys_role_menu VALUES (2, 1005)
+INSERT sys_role_menu VALUES (3, 1014);
 GO
-INSERT sys_role_menu VALUES (2, 1006)
+INSERT sys_role_menu VALUES (3, 1015);
 GO
-INSERT sys_role_menu VALUES (2, 1007)
+INSERT sys_role_menu VALUES (3, 1016);
 GO
-INSERT sys_role_menu VALUES (2, 1008)
+INSERT sys_role_menu VALUES (3, 1017);
 GO
-INSERT sys_role_menu VALUES (2, 1009)
+INSERT sys_role_menu VALUES (3, 1018);
 GO
-INSERT sys_role_menu VALUES (2, 1010)
+INSERT sys_role_menu VALUES (3, 1019);
 GO
-INSERT sys_role_menu VALUES (2, 1011)
+INSERT sys_role_menu VALUES (3, 1020);
 GO
-INSERT sys_role_menu VALUES (2, 1012)
+INSERT sys_role_menu VALUES (3, 1021);
 GO
-INSERT sys_role_menu VALUES (2, 1013)
+INSERT sys_role_menu VALUES (3, 1022);
 GO
-INSERT sys_role_menu VALUES (2, 1014)
+INSERT sys_role_menu VALUES (3, 1023);
 GO
-INSERT sys_role_menu VALUES (2, 1015)
+INSERT sys_role_menu VALUES (3, 1024);
 GO
-INSERT sys_role_menu VALUES (2, 1016)
+INSERT sys_role_menu VALUES (3, 1025);
 GO
-INSERT sys_role_menu VALUES (2, 1017)
+INSERT sys_role_menu VALUES (3, 1026);
 GO
-INSERT sys_role_menu VALUES (2, 1018)
+INSERT sys_role_menu VALUES (3, 1027);
 GO
-INSERT sys_role_menu VALUES (2, 1019)
+INSERT sys_role_menu VALUES (3, 1028);
 GO
-INSERT sys_role_menu VALUES (2, 1020)
+INSERT sys_role_menu VALUES (3, 1029);
 GO
-INSERT sys_role_menu VALUES (2, 1021)
-GO
-INSERT sys_role_menu VALUES (2, 1022)
-GO
-INSERT sys_role_menu VALUES (2, 1023)
-GO
-INSERT sys_role_menu VALUES (2, 1024)
-GO
-INSERT sys_role_menu VALUES (2, 1025)
+INSERT sys_role_menu VALUES (3, 1030);
 GO
-INSERT sys_role_menu VALUES (2, 1026)
+INSERT sys_role_menu VALUES (3, 1031);
 GO
-INSERT sys_role_menu VALUES (2, 1027)
+INSERT sys_role_menu VALUES (3, 1032);
 GO
-INSERT sys_role_menu VALUES (2, 1028)
+INSERT sys_role_menu VALUES (3, 1033);
 GO
-INSERT sys_role_menu VALUES (2, 1029)
+INSERT sys_role_menu VALUES (3, 1034);
 GO
-INSERT sys_role_menu VALUES (2, 1030)
+INSERT sys_role_menu VALUES (3, 1035);
 GO
-INSERT sys_role_menu VALUES (2, 1031)
+INSERT sys_role_menu VALUES (3, 1036);
 GO
-INSERT sys_role_menu VALUES (2, 1032)
+INSERT sys_role_menu VALUES (3, 1037);
 GO
-INSERT sys_role_menu VALUES (2, 1033)
+INSERT sys_role_menu VALUES (3, 1038);
 GO
-INSERT sys_role_menu VALUES (2, 1034)
+INSERT sys_role_menu VALUES (3, 1039);
 GO
-INSERT sys_role_menu VALUES (2, 1035)
+INSERT sys_role_menu VALUES (3, 1040);
 GO
-INSERT sys_role_menu VALUES (2, 1036)
+INSERT sys_role_menu VALUES (3, 1041);
 GO
-INSERT sys_role_menu VALUES (2, 1037)
+INSERT sys_role_menu VALUES (3, 1042);
 GO
-INSERT sys_role_menu VALUES (2, 1038)
+INSERT sys_role_menu VALUES (3, 1043);
 GO
-INSERT sys_role_menu VALUES (2, 1039)
+INSERT sys_role_menu VALUES (3, 1044);
 GO
-INSERT sys_role_menu VALUES (2, 1040)
+INSERT sys_role_menu VALUES (3, 1045);
 GO
-INSERT sys_role_menu VALUES (2, 1041)
+INSERT sys_role_menu VALUES (3, 1500);
 GO
-INSERT sys_role_menu VALUES (2, 1042)
+INSERT sys_role_menu VALUES (3, 1501);
 GO
-INSERT sys_role_menu VALUES (2, 1043)
+INSERT sys_role_menu VALUES (3, 1502);
 GO
-INSERT sys_role_menu VALUES (2, 1044)
+INSERT sys_role_menu VALUES (3, 1503);
 GO
-INSERT sys_role_menu VALUES (2, 1045)
+INSERT sys_role_menu VALUES (3, 1504);
 GO
-INSERT sys_role_menu VALUES (2, 1046)
+INSERT sys_role_menu VALUES (3, 1505);
 GO
-INSERT sys_role_menu VALUES (2, 1047)
+INSERT sys_role_menu VALUES (3, 1506);
 GO
-INSERT sys_role_menu VALUES (2, 1048)
+INSERT sys_role_menu VALUES (3, 1507);
 GO
-INSERT sys_role_menu VALUES (2, 1049)
+INSERT sys_role_menu VALUES (3, 1508);
 GO
-INSERT sys_role_menu VALUES (2, 1050)
+INSERT sys_role_menu VALUES (3, 1509);
 GO
-INSERT sys_role_menu VALUES (2, 1051)
+INSERT sys_role_menu VALUES (3, 1510);
 GO
-INSERT sys_role_menu VALUES (2, 1052)
+INSERT sys_role_menu VALUES (3, 1511);
 GO
-INSERT sys_role_menu VALUES (2, 1053)
+INSERT sys_role_menu VALUES (4, 5);
 GO
-INSERT sys_role_menu VALUES (2, 1054)
+INSERT sys_role_menu VALUES (4, 1500);
 GO
-INSERT sys_role_menu VALUES (2, 1055)
+INSERT sys_role_menu VALUES (4, 1501);
 GO
-INSERT sys_role_menu VALUES (2, 1056)
+INSERT sys_role_menu VALUES (4, 1502);
 GO
-INSERT sys_role_menu VALUES (2, 1057)
+INSERT sys_role_menu VALUES (4, 1503);
 GO
-INSERT sys_role_menu VALUES (2, 1058)
+INSERT sys_role_menu VALUES (4, 1504);
 GO
-INSERT sys_role_menu VALUES (2, 1059)
+INSERT sys_role_menu VALUES (4, 1505);
 GO
-INSERT sys_role_menu VALUES (2, 1060)
+INSERT sys_role_menu VALUES (4, 1506);
 GO
-INSERT sys_role_menu VALUES (2, 1061)
+INSERT sys_role_menu VALUES (4, 1507);
 GO
-INSERT sys_role_menu VALUES (2, 1062)
+INSERT sys_role_menu VALUES (4, 1508);
 GO
-INSERT sys_role_menu VALUES (2, 1063)
+INSERT sys_role_menu VALUES (4, 1509);
 GO
-INSERT sys_role_menu VALUES (2, 1064)
+INSERT sys_role_menu VALUES (4, 1510);
 GO
-INSERT sys_role_menu VALUES (2, 1065)
+INSERT sys_role_menu VALUES (4, 1511);
 GO
 
 CREATE TABLE sys_user
@@ -2747,7 +2786,9 @@
 
 INSERT sys_user VALUES (1, N'000000', 103,  N'admin', N'鐤媯鐨勭嫯瀛怢i', N'sys_user', N'crazyLionLi@163.com', N'15888888888', N'1', NULL, N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), NULL, NULL, N'绠$悊鍛�')
 GO
-INSERT sys_user VALUES (2, N'000000', 105,  N'lionli', N'鐤媯鐨勭嫯瀛怢i', N'sys_user', N'crazyLionLi@qq.com', N'15666666666', N'1', NULL, N'$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), NULL, NULL, N'娴嬭瘯鍛�')
+INSERT sys_user VALUES (3, N'000000', 108, N'test', N'鏈儴闂ㄥ強浠ヤ笅 瀵嗙爜666666', N'sys_user', N'', N'', N'0', NULL, N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), 3, getdate(), NULL);
+GO
+INSERT sys_user VALUES (4, N'000000', 102, N'test1', N'浠呮湰浜� 瀵嗙爜666666', N'sys_user', N'', N'', N'0', NULL, N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), 4, getdate(), NULL);
 GO
 
 CREATE TABLE sys_user_post
@@ -2781,8 +2822,6 @@
 
 INSERT sys_user_post VALUES (1, 1)
 GO
-INSERT sys_user_post VALUES (2, 2)
-GO
 
 CREATE TABLE sys_user_role
 (
@@ -2815,7 +2854,9 @@
 
 INSERT sys_user_role VALUES (1, 1)
 GO
-INSERT sys_user_role VALUES (2, 2)
+INSERT sys_user_role VALUES (3, 3);
+GO
+INSERT sys_user_role VALUES (4, 4);
 GO
 
 CREATE TABLE sys_oss
@@ -2947,7 +2988,7 @@
 GO
 
 EXEC sp_addextendedproperty
-    'MS_Description', N'涓诲缓',
+    'MS_Description', N'涓婚敭',
     'SCHEMA', N'dbo',
     'TABLE', N'sys_oss_config',
     'COLUMN', N'oss_config_id'
@@ -3109,102 +3150,402 @@
 GO
 
 EXEC sp_addextendedproperty
-'MS_Description', N'涓诲缓',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'id'
+    'MS_Description', N'涓婚敭',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'id'
 GO
 EXEC sys.sp_addextendedproperty
-'MS_Description', N'瀹㈡埛绔痠d' ,
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'client_id'
+    'MS_Description', N'瀹㈡埛绔痠d' ,
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'client_id'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'瀹㈡埛绔痥ey',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'client_key'
+    'MS_Description', N'瀹㈡埛绔痥ey',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'client_key'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'瀹㈡埛绔閽�',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'client_secret'
+    'MS_Description', N'瀹㈡埛绔閽�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'client_secret'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'鎺堟潈绫诲瀷',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'grant_type'
+    'MS_Description', N'鎺堟潈绫诲瀷',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'grant_type'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'璁惧绫诲瀷',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'device_type'
+    'MS_Description', N'璁惧绫诲瀷',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'device_type'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'token娲昏穬瓒呮椂鏃堕棿',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'active_timeout'
+    'MS_Description', N'token娲昏穬瓒呮椂鏃堕棿',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'active_timeout'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'token鍥哄畾瓒呮椂',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'timeout'
+    'MS_Description', N'token鍥哄畾瓒呮椂',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'timeout'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'鐘舵�侊紙0姝e父 1鍋滅敤锛�',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'status'
+    'MS_Description', N'鐘舵�侊紙0姝e父 1鍋滅敤锛�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'status'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'del_flag'
+    'MS_Description', N'鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'del_flag'
 GO
 EXEC sys.sp_addextendedproperty
-'MS_Description', N'鍒涘缓閮ㄩ棬' ,
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'create_dept'
+    'MS_Description', N'鍒涘缓閮ㄩ棬' ,
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'create_dept'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'鍒涘缓鑰�',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'create_by'
+    'MS_Description', N'鍒涘缓鑰�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'create_by'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'鍒涘缓鏃堕棿',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'create_time'
+    'MS_Description', N'鍒涘缓鏃堕棿',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'create_time'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'鏇存柊鑰�',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'update_by'
+    'MS_Description', N'鏇存柊鑰�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'update_by'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'鏇存柊鏃堕棿',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client',
-'COLUMN', N'update_time'
+    'MS_Description', N'鏇存柊鏃堕棿',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client',
+    'COLUMN', N'update_time'
 GO
 EXEC sp_addextendedproperty
-'MS_Description', N'绯荤粺鎺堟潈琛�',
-'SCHEMA', N'dbo',
-'TABLE', N'sys_client'
+    'MS_Description', N'绯荤粺鎺堟潈琛�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_client'
 GO
 
 INSERT INTO sys_client VALUES (N'1', N'e5cd7e4891bf95d1d19206ce24a7b32e', N'pc', N'pc123', N'password,social', N'pc', 1800, 604800, N'0', N'0', 103, 1, getdate(), 1, getdate())
 GO
 INSERT INTO sys_client VALUES (N'2', N'428a8310cd442757ae699df5d894f051', N'app', N'app123', N'password,sms,social', N'android', 1800, 604800, N'0', N'0', 103, 1, getdate(), 1, getdate())
 GO
+
+CREATE TABLE test_demo
+(
+    id          bigint            NOT NULL,
+    tenant_id   nvarchar(20)      DEFAULT ('000000') NULL,
+    dept_id     bigint            NULL,
+    user_id     bigint            NULL,
+    order_num   int DEFAULT ((0)) NULL,
+    test_key    nvarchar(255)     NULL,
+    value       nvarchar(255)     NULL,
+    version     int DEFAULT ((0)) NULL,
+    create_dept bigint            NULL,
+    create_time datetime2(0)      NULL,
+    create_by   bigint            NULL,
+    update_time datetime2(0)      NULL,
+    update_by   bigint            NULL,
+    del_flag    int DEFAULT ((0)) NULL,
+    CONSTRAINT PK__test_dem__3213E83F176051C8 PRIMARY KEY CLUSTERED (id)
+        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
+        ON [PRIMARY]
+)
+ON [PRIMARY]
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'涓婚敭',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'绉熸埛id',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'tenant_id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'閮ㄩ棬id',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'dept_id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鐢ㄦ埛id',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'user_id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鎺掑簭鍙�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'order_num'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'key閿�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'test_key'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鍊�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'value'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鐗堟湰',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'version'
+GO
+
+EXEC sys.sp_addextendedproperty
+    'MS_Description', N'鍒涘缓閮ㄩ棬' ,
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'create_dept'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鍒涘缓鏃堕棿',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'create_time'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鍒涘缓浜�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'create_by'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鏇存柊鏃堕棿',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'update_time'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鏇存柊浜�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'update_by'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鍒犻櫎鏍囧織',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo',
+    'COLUMN', N'del_flag'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'娴嬭瘯鍗曡〃',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_demo'
+GO
+
+CREATE TABLE test_tree
+(
+    id          bigint               NOT NULL,
+    tenant_id   nvarchar(20)         DEFAULT ('000000') NULL,
+    parent_id   bigint DEFAULT ((0)) NULL,
+    dept_id     bigint               NULL,
+    user_id     bigint               NULL,
+    tree_name   nvarchar(255)        NULL,
+    version     int    DEFAULT ((0)) NULL,
+    create_dept bigint               NULL,
+    create_time datetime2(0)         NULL,
+    create_by   bigint               NULL,
+    update_time datetime2(0)         NULL,
+    update_by   bigint               NULL,
+    del_flag    int    DEFAULT ((0)) NULL,
+    CONSTRAINT PK__test_tre__3213E83FC75A1B63 PRIMARY KEY CLUSTERED (id)
+        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
+        ON [PRIMARY]
+)
+ON [PRIMARY]
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'涓婚敭',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'绉熸埛id',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'tenant_id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鐖秈d',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'parent_id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'閮ㄩ棬id',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'dept_id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鐢ㄦ埛id',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'user_id'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鍊�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'tree_name'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鐗堟湰',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'version'
+GO
+
+EXEC sys.sp_addextendedproperty
+    'MS_Description', N'鍒涘缓閮ㄩ棬' ,
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'create_dept'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鍒涘缓鏃堕棿',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'create_time'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鍒涘缓浜�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'create_by'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鏇存柊鏃堕棿',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'update_time'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鏇存柊浜�',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'update_by'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'鍒犻櫎鏍囧織',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree',
+    'COLUMN', N'del_flag'
+GO
+
+EXEC sp_addextendedproperty
+    'MS_Description', N'娴嬭瘯鏍戣〃',
+    'SCHEMA', N'dbo',
+    'TABLE', N'test_tree'
+GO
+
+INSERT test_demo VALUES (1, N'000000', 102, 4, 1, N'娴嬭瘯鏁版嵁鏉冮檺', N'娴嬭瘯', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (2, N'000000', 102, 3, 2, N'瀛愯妭鐐�1', N'111', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (3, N'000000', 102, 3, 3, N'瀛愯妭鐐�2', N'222', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (4, N'000000', 108, 4, 4, N'娴嬭瘯鏁版嵁', N'demo', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (5, N'000000', 108, 3, 13, N'瀛愯妭鐐�11', N'1111', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (6, N'000000', 108, 3, 12, N'瀛愯妭鐐�22', N'2222', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (7, N'000000', 108, 3, 11, N'瀛愯妭鐐�33', N'3333', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (8, N'000000', 108, 3, 10, N'瀛愯妭鐐�44', N'4444', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (9, N'000000', 108, 3, 9, N'瀛愯妭鐐�55', N'5555', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (10, N'000000', 108, 3, 8, N'瀛愯妭鐐�66', N'6666', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (11, N'000000', 108, 3, 7, N'瀛愯妭鐐�77', N'7777', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (12, N'000000', 108, 3, 6, N'瀛愯妭鐐�88', N'8888', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_demo VALUES (13, N'000000', 108, 3, 5, N'瀛愯妭鐐�99', N'9999', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+
+INSERT test_tree VALUES (1, N'000000', 0, 102, 4, N'娴嬭瘯鏁版嵁鏉冮檺', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (2, N'000000', 1, 102, 3, N'瀛愯妭鐐�1', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (3, N'000000', 2, 102, 3, N'瀛愯妭鐐�2', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (4, N'000000', 0, 108, 4, N'娴嬭瘯鏍�1', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (5, N'000000', 4, 108, 3, N'瀛愯妭鐐�11', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (6, N'000000', 4, 108, 3, N'瀛愯妭鐐�22', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (7, N'000000', 4, 108, 3, N'瀛愯妭鐐�33', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (8, N'000000', 5, 108, 3, N'瀛愯妭鐐�44', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (9, N'000000', 6, 108, 3, N'瀛愯妭鐐�55', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (10, N'000000', 7, 108, 3, N'瀛愯妭鐐�66', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (11, N'000000', 7, 108, 3, N'瀛愯妭鐐�77', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (12, N'000000', 10, 108, 3, N'瀛愯妭鐐�88', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
+INSERT test_tree VALUES (13, N'000000', 10, 108, 3, N'瀛愯妭鐐�99', 0, 103, getdate(), 1, NULL, NULL, 0);
+GO
diff --git a/script/sql/sqlserver/sqlserver_test.sql b/script/sql/sqlserver/sqlserver_test.sql
deleted file mode 100644
index 87628bd..0000000
--- a/script/sql/sqlserver/sqlserver_test.sql
+++ /dev/null
@@ -1,510 +0,0 @@
-CREATE TABLE test_demo
-(
-    id          bigint            NOT NULL,
-    tenant_id   nvarchar(20)      DEFAULT ('000000') NULL,
-    dept_id     bigint            NULL,
-    user_id     bigint            NULL,
-    order_num   int DEFAULT ((0)) NULL,
-    test_key    nvarchar(255)     NULL,
-    value       nvarchar(255)     NULL,
-    version     int DEFAULT ((0)) NULL,
-    create_dept bigint            NULL,
-    create_time datetime2(0)      NULL,
-    create_by   bigint            NULL,
-    update_time datetime2(0)      NULL,
-    update_by   bigint            NULL,
-    del_flag    int DEFAULT ((0)) NULL,
-    CONSTRAINT PK__test_dem__3213E83F176051C8 PRIMARY KEY CLUSTERED (id)
-        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-        ON [PRIMARY]
-)
-ON [PRIMARY]
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'涓婚敭',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'id'
-GO
-
-EXEC sp_addextendedproperty
-    'MS_Description', N'绉熸埛id',
-    'SCHEMA', N'dbo',
-    'TABLE', N'test_demo',
-    'COLUMN', N'tenant_id'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'閮ㄩ棬id',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'dept_id'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鐢ㄦ埛id',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'user_id'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鎺掑簭鍙�',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'order_num'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'key閿�',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'test_key'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鍊�',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'value'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鐗堟湰',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'version'
-GO
-
-EXEC sys.sp_addextendedproperty
-    'MS_Description', N'鍒涘缓閮ㄩ棬' ,
-    'SCHEMA', N'dbo',
-    'TABLE', N'test_demo',
-    'COLUMN', N'create_dept'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鍒涘缓鏃堕棿',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'create_time'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鍒涘缓浜�',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'create_by'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鏇存柊鏃堕棿',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'update_time'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鏇存柊浜�',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'update_by'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鍒犻櫎鏍囧織',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo',
-     'COLUMN', N'del_flag'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'娴嬭瘯鍗曡〃',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_demo'
-GO
-
-CREATE TABLE test_tree
-(
-    id          bigint               NOT NULL,
-    tenant_id   nvarchar(20)         DEFAULT ('000000') NULL,
-    parent_id   bigint DEFAULT ((0)) NULL,
-    dept_id     bigint               NULL,
-    user_id     bigint               NULL,
-    tree_name   nvarchar(255)        NULL,
-    version     int    DEFAULT ((0)) NULL,
-    create_dept bigint               NULL,
-    create_time datetime2(0)         NULL,
-    create_by   bigint               NULL,
-    update_time datetime2(0)         NULL,
-    update_by   bigint               NULL,
-    del_flag    int    DEFAULT ((0)) NULL,
-    CONSTRAINT PK__test_tre__3213E83FC75A1B63 PRIMARY KEY CLUSTERED (id)
-        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
-        ON [PRIMARY]
-)
-ON [PRIMARY]
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'涓婚敭',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'id'
-GO
-
-EXEC sp_addextendedproperty
-    'MS_Description', N'绉熸埛id',
-    'SCHEMA', N'dbo',
-    'TABLE', N'test_tree',
-    'COLUMN', N'tenant_id'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鐖秈d',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'parent_id'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'閮ㄩ棬id',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'dept_id'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鐢ㄦ埛id',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'user_id'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鍊�',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'tree_name'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鐗堟湰',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'version'
-GO
-
-EXEC sys.sp_addextendedproperty
-    'MS_Description', N'鍒涘缓閮ㄩ棬' ,
-    'SCHEMA', N'dbo',
-    'TABLE', N'test_tree',
-    'COLUMN', N'create_dept'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鍒涘缓鏃堕棿',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'create_time'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鍒涘缓浜�',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'create_by'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鏇存柊鏃堕棿',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'update_time'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鏇存柊浜�',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'update_by'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'鍒犻櫎鏍囧織',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree',
-     'COLUMN', N'del_flag'
-GO
-
-EXEC sp_addextendedproperty
-     'MS_Description', N'娴嬭瘯鏍戣〃',
-     'SCHEMA', N'dbo',
-     'TABLE', N'test_tree'
-GO
-
-INSERT sys_user VALUES (3, N'000000', 108, N'test', N'鏈儴闂ㄥ強浠ヤ笅 瀵嗙爜666666', N'sys_user', N'', N'', N'0', NULL, N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), 3, getdate(), NULL);
-GO
-INSERT sys_user VALUES (4, N'000000', 102, N'test1', N'浠呮湰浜� 瀵嗙爜666666', N'sys_user', N'', N'', N'0', NULL, N'$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', N'0', N'0', N'127.0.0.1', getdate(), 103, 1, getdate(), 4, getdate(), NULL);
-GO
-
-INSERT sys_menu VALUES (5, N'娴嬭瘯鑿滃崟', 0, 5, N'demo', NULL, 1, 0, N'M', N'0', N'0', NULL, N'star', 103, 1, getdate(), NULL, NULL, N'');
-GO
-
-INSERT sys_menu VALUES (1500, N'娴嬭瘯鍗曡〃', 5, 1, N'demo', N'demo/demo/index', 1, 0, N'C', N'0', N'0', N'demo:demo:list', N'#', 103, 1, getdate(), NULL, NULL, N'娴嬭瘯鍗曡〃鑿滃崟');
-GO
-INSERT sys_menu VALUES (1501, N'娴嬭瘯鍗曡〃鏌ヨ', 1500, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:query', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-INSERT sys_menu VALUES (1502, N'娴嬭瘯鍗曡〃鏂板', 1500, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:add', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-INSERT sys_menu VALUES (1503, N'娴嬭瘯鍗曡〃淇敼', 1500, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:edit', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-INSERT sys_menu VALUES (1504, N'娴嬭瘯鍗曡〃鍒犻櫎', 1500, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:remove', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-INSERT sys_menu VALUES (1505, N'娴嬭瘯鍗曡〃瀵煎嚭', 1500, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:demo:export', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-
-INSERT sys_menu VALUES (1506, N'娴嬭瘯鏍戣〃', 5, 1, N'tree', N'demo/tree/index', 1, 0, N'C', N'0', N'0', N'demo:tree:list', N'#', 103, 1, getdate(), NULL, NULL, N'娴嬭瘯鏍戣〃鑿滃崟');
-GO
-INSERT sys_menu VALUES (1507, N'娴嬭瘯鏍戣〃鏌ヨ', 1506, 1, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:query', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-INSERT sys_menu VALUES (1508, N'娴嬭瘯鏍戣〃鏂板', 1506, 2, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:add', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-INSERT sys_menu VALUES (1509, N'娴嬭瘯鏍戣〃淇敼', 1506, 3, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:edit', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-INSERT sys_menu VALUES (1510, N'娴嬭瘯鏍戣〃鍒犻櫎', 1506, 4, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:remove', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-INSERT sys_menu VALUES (1511, N'娴嬭瘯鏍戣〃瀵煎嚭', 1506, 5, N'#', N'', 1, 0, N'F', N'0', N'0', N'demo:tree:export', N'#', 103, 1, getdate(), NULL, NULL, N'');
-GO
-
-INSERT sys_role VALUES (3, N'000000', N'鏈儴闂ㄥ強浠ヤ笅', N'test1', 3, N'4', 1, 1, N'0', N'0', 103, 1, getdate(), 1, NULL, NULL);
-GO
-INSERT sys_role VALUES (4, N'000000', N'浠呮湰浜�', N'test2', 4, N'5', 1, 1, N'0', N'0', 103, 1, getdate(), 1, NULL, NULL);
-GO
-
-INSERT sys_role_menu VALUES (3, 1);
-GO
-INSERT sys_role_menu VALUES (3, 5);
-GO
-INSERT sys_role_menu VALUES (3, 100);
-GO
-INSERT sys_role_menu VALUES (3, 101);
-GO
-INSERT sys_role_menu VALUES (3, 102);
-GO
-INSERT sys_role_menu VALUES (3, 103);
-GO
-INSERT sys_role_menu VALUES (3, 104);
-GO
-INSERT sys_role_menu VALUES (3, 105);
-GO
-INSERT sys_role_menu VALUES (3, 106);
-GO
-INSERT sys_role_menu VALUES (3, 107);
-GO
-INSERT sys_role_menu VALUES (3, 108);
-GO
-INSERT sys_role_menu VALUES (3, 500);
-GO
-INSERT sys_role_menu VALUES (3, 501);
-GO
-INSERT sys_role_menu VALUES (3, 1001);
-GO
-INSERT sys_role_menu VALUES (3, 1002);
-GO
-INSERT sys_role_menu VALUES (3, 1003);
-GO
-INSERT sys_role_menu VALUES (3, 1004);
-GO
-INSERT sys_role_menu VALUES (3, 1005);
-GO
-INSERT sys_role_menu VALUES (3, 1006);
-GO
-INSERT sys_role_menu VALUES (3, 1007);
-GO
-INSERT sys_role_menu VALUES (3, 1008);
-GO
-INSERT sys_role_menu VALUES (3, 1009);
-GO
-INSERT sys_role_menu VALUES (3, 1010);
-GO
-INSERT sys_role_menu VALUES (3, 1011);
-GO
-INSERT sys_role_menu VALUES (3, 1012);
-GO
-INSERT sys_role_menu VALUES (3, 1013);
-GO
-INSERT sys_role_menu VALUES (3, 1014);
-GO
-INSERT sys_role_menu VALUES (3, 1015);
-GO
-INSERT sys_role_menu VALUES (3, 1016);
-GO
-INSERT sys_role_menu VALUES (3, 1017);
-GO
-INSERT sys_role_menu VALUES (3, 1018);
-GO
-INSERT sys_role_menu VALUES (3, 1019);
-GO
-INSERT sys_role_menu VALUES (3, 1020);
-GO
-INSERT sys_role_menu VALUES (3, 1021);
-GO
-INSERT sys_role_menu VALUES (3, 1022);
-GO
-INSERT sys_role_menu VALUES (3, 1023);
-GO
-INSERT sys_role_menu VALUES (3, 1024);
-GO
-INSERT sys_role_menu VALUES (3, 1025);
-GO
-INSERT sys_role_menu VALUES (3, 1026);
-GO
-INSERT sys_role_menu VALUES (3, 1027);
-GO
-INSERT sys_role_menu VALUES (3, 1028);
-GO
-INSERT sys_role_menu VALUES (3, 1029);
-GO
-INSERT sys_role_menu VALUES (3, 1030);
-GO
-INSERT sys_role_menu VALUES (3, 1031);
-GO
-INSERT sys_role_menu VALUES (3, 1032);
-GO
-INSERT sys_role_menu VALUES (3, 1033);
-GO
-INSERT sys_role_menu VALUES (3, 1034);
-GO
-INSERT sys_role_menu VALUES (3, 1035);
-GO
-INSERT sys_role_menu VALUES (3, 1036);
-GO
-INSERT sys_role_menu VALUES (3, 1037);
-GO
-INSERT sys_role_menu VALUES (3, 1038);
-GO
-INSERT sys_role_menu VALUES (3, 1039);
-GO
-INSERT sys_role_menu VALUES (3, 1040);
-GO
-INSERT sys_role_menu VALUES (3, 1041);
-GO
-INSERT sys_role_menu VALUES (3, 1042);
-GO
-INSERT sys_role_menu VALUES (3, 1043);
-GO
-INSERT sys_role_menu VALUES (3, 1044);
-GO
-INSERT sys_role_menu VALUES (3, 1045);
-GO
-INSERT sys_role_menu VALUES (3, 1500);
-GO
-INSERT sys_role_menu VALUES (3, 1501);
-GO
-INSERT sys_role_menu VALUES (3, 1502);
-GO
-INSERT sys_role_menu VALUES (3, 1503);
-GO
-INSERT sys_role_menu VALUES (3, 1504);
-GO
-INSERT sys_role_menu VALUES (3, 1505);
-GO
-INSERT sys_role_menu VALUES (3, 1506);
-GO
-INSERT sys_role_menu VALUES (3, 1507);
-GO
-INSERT sys_role_menu VALUES (3, 1508);
-GO
-INSERT sys_role_menu VALUES (3, 1509);
-GO
-INSERT sys_role_menu VALUES (3, 1510);
-GO
-INSERT sys_role_menu VALUES (3, 1511);
-GO
-INSERT sys_role_menu VALUES (4, 5);
-GO
-INSERT sys_role_menu VALUES (4, 1500);
-GO
-INSERT sys_role_menu VALUES (4, 1501);
-GO
-INSERT sys_role_menu VALUES (4, 1502);
-GO
-INSERT sys_role_menu VALUES (4, 1503);
-GO
-INSERT sys_role_menu VALUES (4, 1504);
-GO
-INSERT sys_role_menu VALUES (4, 1505);
-GO
-INSERT sys_role_menu VALUES (4, 1506);
-GO
-INSERT sys_role_menu VALUES (4, 1507);
-GO
-INSERT sys_role_menu VALUES (4, 1508);
-GO
-INSERT sys_role_menu VALUES (4, 1509);
-GO
-INSERT sys_role_menu VALUES (4, 1510);
-GO
-INSERT sys_role_menu VALUES (4, 1511);
-GO
-
-INSERT sys_user_role VALUES (3, 3);
-GO
-INSERT sys_user_role VALUES (4, 4);
-GO
-
-INSERT test_demo VALUES (1, N'000000', 102, 4, 1, N'娴嬭瘯鏁版嵁鏉冮檺', N'娴嬭瘯', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (2, N'000000', 102, 3, 2, N'瀛愯妭鐐�1', N'111', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (3, N'000000', 102, 3, 3, N'瀛愯妭鐐�2', N'222', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (4, N'000000', 108, 4, 4, N'娴嬭瘯鏁版嵁', N'demo', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (5, N'000000', 108, 3, 13, N'瀛愯妭鐐�11', N'1111', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (6, N'000000', 108, 3, 12, N'瀛愯妭鐐�22', N'2222', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (7, N'000000', 108, 3, 11, N'瀛愯妭鐐�33', N'3333', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (8, N'000000', 108, 3, 10, N'瀛愯妭鐐�44', N'4444', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (9, N'000000', 108, 3, 9, N'瀛愯妭鐐�55', N'5555', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (10, N'000000', 108, 3, 8, N'瀛愯妭鐐�66', N'6666', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (11, N'000000', 108, 3, 7, N'瀛愯妭鐐�77', N'7777', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (12, N'000000', 108, 3, 6, N'瀛愯妭鐐�88', N'8888', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_demo VALUES (13, N'000000', 108, 3, 5, N'瀛愯妭鐐�99', N'9999', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-
-INSERT test_tree VALUES (1, N'000000', 0, 102, 4, N'娴嬭瘯鏁版嵁鏉冮檺', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (2, N'000000', 1, 102, 3, N'瀛愯妭鐐�1', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (3, N'000000', 2, 102, 3, N'瀛愯妭鐐�2', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (4, N'000000', 0, 108, 4, N'娴嬭瘯鏍�1', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (5, N'000000', 4, 108, 3, N'瀛愯妭鐐�11', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (6, N'000000', 4, 108, 3, N'瀛愯妭鐐�22', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (7, N'000000', 4, 108, 3, N'瀛愯妭鐐�33', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (8, N'000000', 5, 108, 3, N'瀛愯妭鐐�44', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (9, N'000000', 6, 108, 3, N'瀛愯妭鐐�55', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (10, N'000000', 7, 108, 3, N'瀛愯妭鐐�66', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (11, N'000000', 7, 108, 3, N'瀛愯妭鐐�77', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (12, N'000000', 10, 108, 3, N'瀛愯妭鐐�88', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
-INSERT test_tree VALUES (13, N'000000', 10, 108, 3, N'瀛愯妭鐐�99', 0, 103, getdate(), 1, NULL, NULL, 0);
-GO
diff --git a/script/sql/test.sql b/script/sql/test.sql
deleted file mode 100644
index d4cea05..0000000
--- a/script/sql/test.sql
+++ /dev/null
@@ -1,175 +0,0 @@
-DROP TABLE if EXISTS test_demo;
-CREATE TABLE test_demo
-(
-    id          bigint(0)    NOT NULL COMMENT '涓婚敭',
-    tenant_id   varchar(20)  NULL DEFAULT '000000' COMMENT '绉熸埛缂栧彿',
-    dept_id     bigint(0)    NULL DEFAULT NULL COMMENT '閮ㄩ棬id',
-    user_id     bigint(0)    NULL DEFAULT NULL COMMENT '鐢ㄦ埛id',
-    order_num   int(0)       NULL DEFAULT 0 COMMENT '鎺掑簭鍙�',
-    test_key    varchar(255) NULL DEFAULT NULL COMMENT 'key閿�',
-    value       varchar(255) NULL DEFAULT NULL COMMENT '鍊�',
-    version     int(0)       NULL DEFAULT 0 COMMENT '鐗堟湰',
-    create_dept bigint(0)    NULL DEFAULT NULL COMMENT '鍒涘缓閮ㄩ棬',
-    create_time datetime(0)  NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
-    create_by   bigint(0)    NULL DEFAULT NULL COMMENT '鍒涘缓浜�',
-    update_time datetime(0)  NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
-    update_by   bigint(0)    NULL DEFAULT NULL COMMENT '鏇存柊浜�',
-    del_flag    int(0)       NULL DEFAULT 0 COMMENT '鍒犻櫎鏍囧織',
-    PRIMARY KEY (id) USING BTREE
-) ENGINE = InnoDB COMMENT = '娴嬭瘯鍗曡〃';
-
-DROP TABLE if EXISTS test_tree;
-CREATE TABLE test_tree
-(
-    id          bigint(0)    NOT NULL COMMENT '涓婚敭',
-    tenant_id   varchar(20)  NULL DEFAULT '000000' COMMENT '绉熸埛缂栧彿',
-    parent_id   bigint(0)    NULL DEFAULT 0 COMMENT '鐖秈d',
-    dept_id     bigint(0)    NULL DEFAULT NULL COMMENT '閮ㄩ棬id',
-    user_id     bigint(0)    NULL DEFAULT NULL COMMENT '鐢ㄦ埛id',
-    tree_name   varchar(255) NULL DEFAULT NULL COMMENT '鍊�',
-    version     int(0)       NULL DEFAULT 0 COMMENT '鐗堟湰',
-    create_dept bigint(0)    NULL DEFAULT NULL COMMENT '鍒涘缓閮ㄩ棬',
-    create_time datetime(0)  NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
-    create_by   bigint(0)    NULL DEFAULT NULL COMMENT '鍒涘缓浜�',
-    update_time datetime(0)  NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
-    update_by   bigint(0)    NULL DEFAULT NULL COMMENT '鏇存柊浜�',
-    del_flag    int(0)       NULL DEFAULT 0 COMMENT '鍒犻櫎鏍囧織',
-    PRIMARY KEY (id) USING BTREE
-) ENGINE = InnoDB COMMENT = '娴嬭瘯鏍戣〃';
-
-INSERT INTO sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (3, '000000', 108, 'test', '鏈儴闂ㄥ強浠ヤ笅 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 3, sysdate(), NULL);
-INSERT INTO sys_user(user_id, tenant_id, dept_id, user_name, nick_name, user_type, email, phonenumber, sex, avatar, password, status, del_flag, login_ip, login_date, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (4, '000000', 102, 'test1', '浠呮湰浜� 瀵嗙爜666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 4, sysdate(), NULL);
-
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (5, '娴嬭瘯鑿滃崟', 0, 5, 'demo', NULL, 1, 0, 'M', '0', '0', NULL, 'star', 103, 1, sysdate(), NULL, NULL, '');
-
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1500, '娴嬭瘯鍗曡〃', 5, 1, 'demo', 'demo/demo/index', 1, 0, 'C', '0', '0', 'demo:demo:list', '#', 103, 1, sysdate(), NULL, NULL, '娴嬭瘯鍗曡〃鑿滃崟');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1501, '娴嬭瘯鍗曡〃鏌ヨ', 1500, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:query', '#', 103, 1, sysdate(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1502, '娴嬭瘯鍗曡〃鏂板', 1500, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:add', '#', 103, 1, sysdate(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1503, '娴嬭瘯鍗曡〃淇敼', 1500, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:edit', '#', 103, 1, sysdate(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1504, '娴嬭瘯鍗曡〃鍒犻櫎', 1500, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:remove', '#', 103, 1, sysdate(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1505, '娴嬭瘯鍗曡〃瀵煎嚭', 1500, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:demo:export', '#', 103, 1, sysdate(), NULL, NULL, '');
-
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1506, '娴嬭瘯鏍戣〃', 5, 1, 'tree', 'demo/tree/index', 1, 0, 'C', '0', '0', 'demo:tree:list', '#', 103, 1, sysdate(), NULL, NULL, '娴嬭瘯鏍戣〃鑿滃崟');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1507, '娴嬭瘯鏍戣〃鏌ヨ', 1506, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:query', '#', 103, 1, sysdate(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1508, '娴嬭瘯鏍戣〃鏂板', 1506, 2, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:add', '#', 103, 1, sysdate(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1509, '娴嬭瘯鏍戣〃淇敼', 1506, 3, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:edit', '#', 103, 1, sysdate(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1510, '娴嬭瘯鏍戣〃鍒犻櫎', 1506, 4, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:remove', '#', 103, 1, sysdate(), NULL, NULL, '');
-INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (1511, '娴嬭瘯鏍戣〃瀵煎嚭', 1506, 5, '#', '', 1, 0, 'F', '0', '0', 'demo:tree:export', '#', 103, 1, sysdate(), NULL, NULL, '');
-
-INSERT INTO sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (3, '000000', '鏈儴闂ㄥ強浠ヤ笅', 'test1', 3, '4', 1, 1, '0', '0', 103, 1, sysdate(), 1, NULL, NULL);
-INSERT INTO sys_role(role_id, tenant_id, role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (4, '000000', '浠呮湰浜�', 'test2', 4, '5', 1, 1, '0', '0', 103, 1, sysdate(), 1, NULL, NULL);
-
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 5);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 100);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 101);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 102);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 103);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 104);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 105);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 106);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 107);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 108);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 500);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 501);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1001);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1002);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1003);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1004);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1005);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1006);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1007);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1008);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1009);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1010);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1011);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1012);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1013);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1014);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1015);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1016);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1017);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1018);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1019);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1020);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1021);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1022);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1023);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1024);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1025);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1026);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1027);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1028);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1029);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1030);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1031);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1032);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1033);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1034);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1035);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1036);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1037);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1038);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1039);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1040);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1041);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1042);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1043);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1044);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1045);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1500);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1501);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1502);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1503);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1504);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1505);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1506);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1507);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1508);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1509);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1510);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (3, 1511);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 5);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1500);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1501);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1502);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1503);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1504);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1505);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1506);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1507);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1508);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1509);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1510);
-INSERT INTO sys_role_menu(role_id, menu_id) VALUES (4, 1511);
-
-INSERT INTO sys_user_role(user_id, role_id) VALUES (3, 3);
-INSERT INTO sys_user_role(user_id, role_id) VALUES (4, 4);
-
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (1, '000000', 102, 4, 1, '娴嬭瘯鏁版嵁鏉冮檺', '娴嬭瘯', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (2, '000000', 102, 3, 2, '瀛愯妭鐐�1', '111', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (3, '000000', 102, 3, 3, '瀛愯妭鐐�2', '222', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (4, '000000', 108, 4, 4, '娴嬭瘯鏁版嵁', 'demo', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (5, '000000', 108, 3, 13, '瀛愯妭鐐�11', '1111', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (6, '000000', 108, 3, 12, '瀛愯妭鐐�22', '2222', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (7, '000000', 108, 3, 11, '瀛愯妭鐐�33', '3333', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (8, '000000', 108, 3, 10, '瀛愯妭鐐�44', '4444', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (9, '000000', 108, 3, 9, '瀛愯妭鐐�55', '5555', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (10, '000000', 108, 3, 8, '瀛愯妭鐐�66', '6666', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (11, '000000', 108, 3, 7, '瀛愯妭鐐�77', '7777', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (12, '000000', 108, 3, 6, '瀛愯妭鐐�88', '8888', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_demo(id, tenant_id, dept_id, user_id, order_num, test_key, value, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (13, '000000', 108, 3, 5, '瀛愯妭鐐�99', '9999', 0, 103, sysdate(), 1, NULL, NULL, 0);
-
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (1, '000000', 0, 102, 4, '娴嬭瘯鏁版嵁鏉冮檺', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (2, '000000', 1, 102, 3, '瀛愯妭鐐�1', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (3, '000000', 2, 102, 3, '瀛愯妭鐐�2', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (4, '000000', 0, 108, 4, '娴嬭瘯鏍�1', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (5, '000000', 4, 108, 3, '瀛愯妭鐐�11', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (6, '000000', 4, 108, 3, '瀛愯妭鐐�22', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (7, '000000', 4, 108, 3, '瀛愯妭鐐�33', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (8, '000000', 5, 108, 3, '瀛愯妭鐐�44', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (9, '000000', 6, 108, 3, '瀛愯妭鐐�55', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (10, '000000', 7, 108, 3, '瀛愯妭鐐�66', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (11, '000000', 7, 108, 3, '瀛愯妭鐐�77', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (12, '000000', 10, 108, 3, '瀛愯妭鐐�88', 0, 103, sysdate(), 1, NULL, NULL, 0);
-INSERT INTO test_tree(id, tenant_id, parent_id, dept_id, user_id, tree_name, version, create_dept, create_time, create_by, update_time, update_by, del_flag) VALUES (13, '000000', 10, 108, 3, '瀛愯妭鐐�99', 0, 103, sysdate(), 1, NULL, NULL, 0);
diff --git a/script/sql/update/oracle/update_5.0-5.1.sql b/script/sql/update/oracle/update_5.0-5.1.sql
index 363d4ab..09cfae8 100644
--- a/script/sql/update/oracle/update_5.0-5.1.sql
+++ b/script/sql/update/oracle/update_5.0-5.1.sql
@@ -98,7 +98,7 @@
 alter table sys_client add constraint pk_sys_client primary key (id);
 
 comment on table sys_client                         is '绯荤粺鎺堟潈琛�';
-comment on column sys_client.id                     is '涓诲缓';
+comment on column sys_client.id                     is '涓婚敭';
 comment on column sys_client.client_id              is '瀹㈡埛绔痠d';
 comment on column sys_client.client_key             is '瀹㈡埛绔痥ey';
 comment on column sys_client.client_secret          is '瀹㈡埛绔閽�';
diff --git a/script/sql/update/oracle/update_5.1.0-5.1.1.sql b/script/sql/update/oracle/update_5.1.0-5.1.1.sql
index 5382583..979a4bd 100644
--- a/script/sql/update/oracle/update_5.1.0-5.1.1.sql
+++ b/script/sql/update/oracle/update_5.1.0-5.1.1.sql
@@ -1,5 +1,5 @@
-ALTER TABLE sys_logininfor ADD (client_key VARCHAR(32) DEFAULT '');
+ALTER TABLE sys_logininfor ADD (client_key varchar2(32) DEFAULT '');
 COMMENT ON COLUMN sys_logininfor.client_key IS '瀹㈡埛绔�';
 
-ALTER TABLE sys_logininfor ADD (device_type VARCHAR(32) DEFAULT '');
+ALTER TABLE sys_logininfor ADD (device_type varchar2(32) DEFAULT '');
 COMMENT ON COLUMN sys_logininfor.device_type IS '璁惧绫诲瀷';
diff --git a/script/sql/update/oracle/update_5.1.1-5.1.2.sql b/script/sql/update/oracle/update_5.1.1-5.1.2.sql
index dcb5cbb..d7c030c 100644
--- a/script/sql/update/oracle/update_5.1.1-5.1.2.sql
+++ b/script/sql/update/oracle/update_5.1.1-5.1.2.sql
@@ -3,3 +3,4 @@
 insert into sys_menu values('1621', '閰嶇疆娣诲姞', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:add',    '#', 103, 1, sysdate, null, null, '');
 insert into sys_menu values('1622', '閰嶇疆缂栬緫', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:edit',   '#', 103, 1, sysdate, null, null, '');
 insert into sys_menu values('1623', '閰嶇疆鍒犻櫎', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, sysdate, null, null, '');
+
diff --git a/script/sql/update/oracle/update_5.1.2-5.2.0.sql b/script/sql/update/oracle/update_5.1.2-5.2.0.sql
new file mode 100644
index 0000000..0f87fd6
--- /dev/null
+++ b/script/sql/update/oracle/update_5.1.2-5.2.0.sql
@@ -0,0 +1,9 @@
+ALTER TABLE sys_dept ADD (dept_category varchar2(100) DEFAULT NULL) COMMENT '閮ㄩ棬绫诲埆缂栫爜';
+COMMENT ON COLUMN sys_dept.dept_category IS '閮ㄩ棬绫诲埆缂栫爜';
+ALTER TABLE sys_post ADD (dept_id number(20) NOT NULL) COMMENT '閮ㄩ棬id';
+COMMENT ON COLUMN sys_post.dept_id IS '閮ㄩ棬id';
+ALTER TABLE sys_post ADD (post_category VARCHAR2(100) DEFAULT NULL) COMMENT '宀椾綅绫诲埆缂栫爜';
+COMMENT ON COLUMN sys_post.post_category IS '宀椾綅绫诲埆缂栫爜';
+UPDATE sys_post SET dept_id = 100;
+UPDATE sys_post SET dept_id = 103 where post_id = 1;
+UPDATE sys_menu SET path = 'snailjob', component = 'monitor/snailjob/index', perms = 'monitor:snailjob:list', remark = 'SnailJob鎺у埗鍙拌彍鍗�' WHERE menu_id = 120;
diff --git a/script/sql/update/postgres/update_5.0-5.1.sql b/script/sql/update/postgres/update_5.0-5.1.sql
index fa2eb3e..f5f0a5c 100644
--- a/script/sql/update/postgres/update_5.0-5.1.sql
+++ b/script/sql/update/postgres/update_5.0-5.1.sql
@@ -97,7 +97,7 @@
 );
 
 comment on table sys_client                         is '绯荤粺鎺堟潈琛�';
-comment on column sys_client.id                     is '涓诲缓';
+comment on column sys_client.id                     is '涓婚敭';
 comment on column sys_client.client_id              is '瀹㈡埛绔痠d';
 comment on column sys_client.client_key             is '瀹㈡埛绔痥ey';
 comment on column sys_client.client_secret          is '瀹㈡埛绔閽�';
diff --git a/script/sql/update/postgres/update_5.1.1-5.1.2.sql b/script/sql/update/postgres/update_5.1.1-5.1.2.sql
index 0e212de..62eb836 100644
--- a/script/sql/update/postgres/update_5.1.1-5.1.2.sql
+++ b/script/sql/update/postgres/update_5.1.1-5.1.2.sql
@@ -3,4 +3,3 @@
 insert into sys_menu values('1621', '閰嶇疆娣诲姞', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:add',    '#', 103, 1, now(), null, null, '');
 insert into sys_menu values('1622', '閰嶇疆缂栬緫', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:edit',   '#', 103, 1, now(), null, null, '');
 insert into sys_menu values('1623', '閰嶇疆鍒犻櫎', '118', '6', '#', '', '', '1', '0', 'F', '0', '0', 'system:ossConfig:remove', '#', 103, 1, now(), null, null, '');
-
diff --git a/script/sql/update/postgres/update_5.1.2-5.2.0.sql b/script/sql/update/postgres/update_5.1.2-5.2.0.sql
new file mode 100644
index 0000000..dc36ebf
--- /dev/null
+++ b/script/sql/update/postgres/update_5.1.2-5.2.0.sql
@@ -0,0 +1,9 @@
+ALTER TABLE sys_dept ADD COLUMN dept_category varchar(100) default null::varchar;
+COMMENT ON COLUMN sys_dept.dept_category IS '瀹㈡埛绔�';
+ALTER TABLE sys_post ADD COLUMN dept_id int8 NOT NULL;
+COMMENT ON COLUMN sys_post.dept_id IS '閮ㄩ棬id';
+ALTER TABLE sys_post ADD COLUMN post_category varchar(100) default null::varchar;
+COMMENT ON COLUMN sys_post.post_category IS '宀椾綅绫诲埆缂栫爜';
+UPDATE sys_post SET dept_id = 100;
+UPDATE sys_post SET dept_id = 103 where post_id = 1;
+UPDATE sys_menu SET path = 'snailjob', component = 'monitor/snailjob/index', perms = 'monitor:snailjob:list', remark = 'SnailJob鎺у埗鍙拌彍鍗�' WHERE menu_id = 120;
diff --git a/script/sql/update/sqlserver/update_5.0-5.1.sql b/script/sql/update/sqlserver/update_5.0-5.1.sql
index 605f02f..bde3813 100644
--- a/script/sql/update/sqlserver/update_5.0-5.1.sql
+++ b/script/sql/update/sqlserver/update_5.0-5.1.sql
@@ -250,7 +250,7 @@
 GO
 
 EXEC sp_addextendedproperty
-'MS_Description', N'涓诲缓',
+'MS_Description', N'涓婚敭',
 'SCHEMA', N'dbo',
 'TABLE', N'sys_client',
 'COLUMN', N'id'
diff --git a/script/sql/update/sqlserver/update_5.1.2-5.2.0.sql b/script/sql/update/sqlserver/update_5.1.2-5.2.0.sql
new file mode 100644
index 0000000..bf69f10
--- /dev/null
+++ b/script/sql/update/sqlserver/update_5.1.2-5.2.0.sql
@@ -0,0 +1,29 @@
+ALTER TABLE sys_dept ADD dept_category nvarchar(100) DEFAULT NULL
+EXEC sp_addextendedproperty
+    'MS_Description', N'閮ㄩ棬绫诲埆缂栫爜',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_dept',
+    'COLUMN', N'dept_category'
+GO
+ALTER TABLE sys_post ADD dept_id bigint NOT NULL
+GO
+ALTER TABLE sys_post ADD post_category nvarchar(100) DEFAULT NULL
+GO
+EXEC sp_addextendedproperty
+    'MS_Description', N'閮ㄩ棬id',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_post',
+    'COLUMN', N'dept_id'
+GO
+EXEC sp_addextendedproperty
+    'MS_Description', N'宀椾綅绫诲埆缂栫爜',
+    'SCHEMA', N'dbo',
+    'TABLE', N'sys_post',
+    'COLUMN', N'post_category'
+GO
+UPDATE sys_post SET dept_id = 100
+GO
+UPDATE sys_post SET dept_id = 103 where post_id = 1
+GO
+UPDATE sys_menu SET path = N'snailjob', component = N'monitor/snailjob/index', perms = N'monitor:snailjob:list', remark = N'SnailJob鎺у埗鍙拌彍鍗�' WHERE menu_id = 120
+GO
\ No newline at end of file
diff --git a/script/sql/update/update_5.1.1-5.1.2.sql b/script/sql/update/update_5.1.1-5.1.2.sql
index 6813949..314743f 100644
--- a/script/sql/update/update_5.1.1-5.1.2.sql
+++ b/script/sql/update/update_5.1.1-5.1.2.sql
@@ -3,4 +3,3 @@
 insert into sys_menu values('1621', '閰嶇疆娣诲姞', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:add',         '#', 103, 1, sysdate(), null, null, '');
 insert into sys_menu values('1622', '閰嶇疆缂栬緫', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:edit',        '#', 103, 1, sysdate(), null, null, '');
 insert into sys_menu values('1623', '閰嶇疆鍒犻櫎', '118', '6', '#', '', '', 1, 0, 'F', '0', '0', 'system:ossConfig:remove',      '#', 103, 1, sysdate(), null, null, '');
-
diff --git a/script/sql/update/update_5.1.2-5.2.0.sql b/script/sql/update/update_5.1.2-5.2.0.sql
new file mode 100644
index 0000000..11d71c8
--- /dev/null
+++ b/script/sql/update/update_5.1.2-5.2.0.sql
@@ -0,0 +1,5 @@
+ALTER TABLE sys_dept ADD dept_category VARCHAR(100) DEFAULT NULL COMMENT '閮ㄩ棬绫诲埆缂栫爜';
+ALTER TABLE sys_post ADD dept_id BIGINT(20) NOT NULL COMMENT '閮ㄩ棬id', ADD post_category VARCHAR(100) DEFAULT NULL COMMENT '宀椾綅绫诲埆缂栫爜';
+UPDATE sys_post SET dept_id = 100;
+UPDATE sys_post SET dept_id = 103 where post_id = 1;
+UPDATE sys_menu SET path = 'snailjob', component = 'monitor/snailjob/index', perms = 'monitor:snailjob:list', remark = 'SnailJob鎺у埗鍙拌彍鍗�' WHERE menu_id = 120;

--
Gitblit v1.9.3