From 731352fd04a6e8b483a8490fe1a833439302591c Mon Sep 17 00:00:00 2001 From: ageer Date: Mon, 14 Apr 2025 00:22:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20mcp=E6=B5=8B=E8=AF=95=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 61 +- ruoyi-admin/pom.xml | 42 +- .../org/ruoyi/controller/AuthController.java | 17 +- .../org/ruoyi/controller/IndexController.java | 14 +- .../src/main/resources/application-dev.yml | 22 - .../src/main/resources/application.yml | 17 +- .../src/main/resources/static/.gitignore | 46 - .../src/main/resources/static/.nojekyll | 0 ruoyi-admin/src/main/resources/static/CNAME | 1 - .../src/main/resources/static/README.md | 74 - .../src/main/resources/static/_coverpage.md | 32 - .../src/main/resources/static/_footer.md | 2 - .../src/main/resources/static/_navbar.md | 9 - .../src/main/resources/static/_sidebar.md | 16 - .../main/resources/static/common/add_group.md | 27 - .../main/resources/static/common/blacklist.md | 7 - .../main/resources/static/common/column.md | 18 - .../resources/static/common/contribution.md | 69 - .../resources/static/common/demo_system.md | 13 - .../src/main/resources/static/common/pr.md | 37 - .../resources/static/common/user_register.md | 80 - .../src/main/resources/static/common/video.md | 85 - .../src/main/resources/static/index.html | 74 - .../main/resources/static/plus-ui/_sidebar.md | 22 - .../static/plus-ui/devdoc/common_func.md | 234 -- .../static/plus-ui/devdoc/component_use.md | 55 - .../static/plus-ui/devdoc/content_copy.md | 4 - .../static/plus-ui/devdoc/dev_norm.md | 16 - .../static/plus-ui/devdoc/dict_use.md | 4 - .../plus-ui/devdoc/exception_handling.md | 4 - .../static/plus-ui/devdoc/icon_use.md | 4 - .../static/plus-ui/devdoc/page_cache.md | 4 - .../static/plus-ui/devdoc/param_use.md | 4 - .../static/plus-ui/devdoc/permissions_use.md | 4 - .../static/plus-ui/devdoc/request_process.md | 65 - .../static/plus-ui/devdoc/router_use.md | 82 - .../src/main/resources/static/plus-ui/home.md | 53 - .../resources/static/questions/_sidebar.md | 34 - .../resources/static/questions/api_encrypt.md | 148 -- .../resources/static/questions/bean_null.md | 10 - .../resources/static/questions/deploy_vue.md | 13 - .../static/questions/domestic_databases.md | 41 - .../resources/static/questions/dubbo_ip.md | 18 - .../static/questions/https_config.md | 27 - .../static/questions/identify_fail.md | 10 - .../static/questions/import_excel.md | 4 - .../static/questions/jar_run_fail.md | 12 - .../resources/static/questions/jce_cannot.md | 3 - .../resources/static/questions/kinfe4j.md | 66 - .../resources/static/questions/login_step.md | 69 - .../main/resources/static/questions/lombok.md | 4 - .../nacos_naming_instance_metadata.md | 35 - .../static/questions/nacos_read_fail.md | 15 - .../static/questions/only_one_subscriber.md | 11 - .../static/questions/parse_exception.md | 40 - .../static/questions/permission_denied.md | 15 - .../static/questions/read_metadata.md | 11 - .../static/questions/sentinel_404.md | 8 - .../static/questions/st_not_support.md | 11 - .../resources/static/questions/swagger.md | 3 - .../static/questions/synchronous_update.md | 3 - .../resources/static/questions/use_druid.md | 20 - .../resources/static/questions/use_tomcat.md | 9 - .../static/ruoyi-cloud-plus/_sidebar.md | 70 - .../static/ruoyi-cloud-plus/changlog.md | 1385 ----------- .../ruoyi-cloud-plus/extend-function/elk.md | 37 - .../ruoyi-cloud-plus/extend-function/es.md | 26 - .../ruoyi-cloud-plus/extend-function/kafka.md | 9 - .../extend-function/maxkey.md | 20 - .../ruoyi-cloud-plus/extend-function/nacos.md | 13 - .../extend-function/prometheus_grafana.md | 45 - .../extend-function/rabbitmq.md | 10 - .../extend-function/rocketmq.md | 9 - .../extend-function/shardingproxy.md | 75 - .../extend-function/skywalking.md | 41 - .../framework/architecture_diagram.md | 3 - .../framework/association/collaboration.md | 27 - .../framework/association/doc.md | 88 - .../framework/association/i18n.md | 31 - .../association/inner_authentication.md | 19 - .../framework/association/new_module.md | 39 - .../association/update_package_name.md | 33 - .../framework/association/update_url.md | 25 - .../framework/basic/client.md | 85 - .../framework/basic/code_generate.md | 86 - .../framework/basic/export.md | 250 -- .../framework/basic/import.md | 202 -- .../ruoyi-cloud-plus/framework/basic/oss.md | 124 - .../ruoyi-cloud-plus/framework/basic/page.md | 29 - .../framework/basic/param_check.md | 158 -- .../framework/basic/permissions.md | 144 -- .../framework/basic/permissions_control.md | 178 -- .../framework/basic/router_release.md | 26 - .../framework/basic/social.md | 68 - .../framework/basic/tenant.md | 121 - .../ruoyi-cloud-plus/framework/basic/user.md | 85 - .../framework/explain/about_join.md | 12 - .../ruoyi-cloud-plus/framework/explain/key.md | 19 - .../framework/explain/test.md | 6 - .../framework/explain/transaction.md | 45 - .../framework/extend/api_encrypt.md | 39 - .../framework/extend/dynamic_datasource.md | 45 - .../framework/extend/encrypt.md | 38 - .../framework/extend/idempotent.md | 29 - .../ruoyi-cloud-plus/framework/extend/mail.md | 15 - .../framework/extend/sensitive.md | 33 - .../ruoyi-cloud-plus/framework/extend/sms.md | 51 - .../ruoyi-cloud-plus/framework/extend/sse.md | 24 - .../framework/extend/translation.md | 44 - .../framework/extend/websocket.md | 39 - .../static/ruoyi-cloud-plus/framework/tree.md | 91 - .../resources/static/ruoyi-cloud-plus/home.md | 137 -- .../ruoyi-cloud-plus/quickstart/1.Xinit.md | 87 - .../ruoyi-cloud-plus/quickstart/deploy.md | 118 - .../quickstart/extend_project.md | 42 - .../quickstart/idea_environment.md | 38 - .../ruoyi-cloud-plus/quickstart/init.md | 102 - .../quickstart/power_job_init.md | 33 - .../quickstart/snail_job_init.md | 38 - .../quickstart/worker_init.md | 52 - .../static/ruoyi-vue-plus/_sidebar.md | 64 - .../static/ruoyi-vue-plus/changlog.md | 2028 ----------------- .../framework/architecture_diagram.md | 3 - .../framework/association/doc.md | 89 - .../framework/association/i18n.md | 33 - .../framework/association/new_module.md | 15 - .../association/update_package_name.md | 33 - .../framework/association/update_url.md | 26 - .../ruoyi-vue-plus/framework/basic/client.md | 85 - .../framework/basic/code_generate.md | 85 - .../ruoyi-vue-plus/framework/basic/export.md | 249 -- .../ruoyi-vue-plus/framework/basic/import.md | 202 -- .../framework/basic/interface_release.md | 25 - .../ruoyi-vue-plus/framework/basic/oss.md | 124 - .../ruoyi-vue-plus/framework/basic/page.md | 32 - .../framework/basic/param_check.md | 158 -- .../framework/basic/permissions.md | 144 -- .../framework/basic/permissions_control.md | 180 -- .../ruoyi-vue-plus/framework/basic/social.md | 68 - .../ruoyi-vue-plus/framework/basic/tenant.md | 121 - .../ruoyi-vue-plus/framework/basic/user.md | 85 - .../framework/explain/about_join.md | 14 - .../ruoyi-vue-plus/framework/explain/key.md | 19 - .../ruoyi-vue-plus/framework/explain/test.md | 6 - .../framework/explain/transaction.md | 45 - .../framework/extend/api_encrypt.md | 38 - .../framework/extend/dynamic_datasource.md | 45 - .../framework/extend/encrypt.md | 28 - .../framework/extend/idempotent.md | 29 - .../ruoyi-vue-plus/framework/extend/mail.md | 17 - .../ruoyi-vue-plus/framework/extend/maxkey.md | 20 - .../framework/extend/sensitive.md | 33 - .../framework/extend/skywalking.md | 20 - .../ruoyi-vue-plus/framework/extend/sms.md | 51 - .../ruoyi-vue-plus/framework/extend/sse.md | 22 - .../ruoyi-vue-plus/framework/extend/topiam.md | 30 - .../framework/extend/translation.md | 34 - .../framework/extend/websocket.md | 37 - .../static/ruoyi-vue-plus/framework/tree.md | 61 - .../resources/static/ruoyi-vue-plus/home.md | 127 -- .../ruoyi-vue-plus/quickstart/4.Xinit.md | 67 - .../ruoyi-vue-plus/quickstart/5.Xnew.md | 5 - .../ruoyi-vue-plus/quickstart/admin_init.md | 32 - .../ruoyi-vue-plus/quickstart/deploy.md | 126 - .../quickstart/extend_project.md | 49 - .../quickstart/idea_environment.md | 50 - .../static/ruoyi-vue-plus/quickstart/init.md | 73 - .../quickstart/power_job_init.md | 48 - .../quickstart/snail_job_init.md | 52 - .../ruoyi-vue-plus/quickstart/worker_init.md | 43 - .../ruoyi-vue-plus/quickstart/xxl_job_init.md | 43 - .../main/resources/static/static/css/vue.css | 1 - .../resources/static/static/image/favicon.ico | Bin 8131 -> 0 bytes .../resources/static/static/image/logo.png | Bin 119264 -> 0 bytes .../static/static/image/ruoyicloudplus.png | Bin 49107 -> 0 bytes .../static/static/image/ruoyivueplus.png | Bin 54652 -> 0 bytes .../static/static/js/docsify-copy-code.min.js | 9 - .../static/static/js/docsify-footer.min.js | 4 - .../static/js/docsify-pagination.min.js | 1 - .../static/js/docsify-scroll-to-top.min.js | 1 - .../resources/static/static/js/docsify.min.js | 1 - .../resources/static/static/js/emoji.min.js | 1 - .../static/static/js/prism-bash.min.js | 1 - .../static/static/js/prism-typescript.min.js | 1 - .../resources/static/static/js/search.min.js | 1 - .../static/static/js/zoom-image.min.js | 1 - ruoyi-common/ruoyi-common-core/pom.xml | 3 + .../common/core/constant/RegexConstants.java | 59 + .../common/core/constant/SystemConstants.java | 80 + .../core/factory/RegexPatternPoolFactory.java | 53 + .../factory/YmlPropertySourceFactory.java | 32 + .../ruoyi/common/core/utils/ObjectUtils.java | 60 + .../ruoyi/common/core/utils/SpringUtils.java | 9 +- ruoyi-common/ruoyi-common-encrypt/pom.xml | 22 +- ruoyi-common/ruoyi-common-live/pom.xml | 2 +- ruoyi-common/ruoyi-common-mybatis/pom.xml | 10 +- .../jakarta/DsJakartaHeaderProcessor.java | 45 - .../jakarta/DsJakartaSessionProcessor.java | 46 - .../java/org/ruoyi/annotation/DataColumn.java | 40 + .../org/ruoyi/annotation/DataPermission.java | 30 + .../ruoyi/aspect/DataPermissionAspect.java | 50 + .../common/mybatis/annotation/DataColumn.java | 28 - .../mybatis/annotation/DataPermission.java | 18 - .../mybatis/core/mapper/BaseMapperPlus.java | 198 -- .../common/mybatis/enums/DataScopeType.java | 73 - .../handler/InjectionMetaObjectHandler.java | 81 - .../handler/PlusDataPermissionHandler.java | 198 -- .../mybatis/helper/DataPermissionHelper.java | 93 - .../PlusDataPermissionInterceptor.java | 107 - .../jakarta/DsJakartaHeaderProcessor.java | 45 - .../jakarta/DsJakartaSessionProcessor.java | 46 - .../config/MybatisPlusConfig.java | 52 +- .../mybatis => }/core/domain/BaseEntity.java | 3 +- .../org/ruoyi/core/mapper/BaseMapperPlus.java | 335 +++ .../mybatis => }/core/page/PageQuery.java | 17 +- .../mybatis => }/core/page/TableDataInfo.java | 14 +- .../mybatis => }/enums/DataBaseType.java | 12 +- .../java/org/ruoyi/enums/DataScopeType.java | 87 + .../handler/InjectionMetaObjectHandler.java | 103 + .../handler/MybatisExceptionHandler.java | 8 +- .../handler/PlusDataPermissionHandler.java | 359 +++ .../handler/PlusPostInitTableInfoHandler.java | 28 + .../mybatis => }/helper/DataBaseHelper.java | 10 +- .../ruoyi/helper/DataPermissionHelper.java | 176 ++ .../PlusDataPermissionInterceptor.java | 181 ++ ...ot.autoconfigure.AutoConfiguration.imports | 2 +- .../src/main/resources/common-mybatis.yml | 33 + .../src/main/resources/spy.properties | 20 + .../common/satoken/utils/LoginHelper.java | 12 + .../common/tenant/config/TenantConfig.java | 3 +- .../common/tenant/core/TenantEntity.java | 3 +- .../common/tenant/helper/TenantHelper.java | 35 + ruoyi-extend/pom.xml | 20 + .../ruoyi-mcp-server/pom.xml | 29 +- .../org/ruoyi/mcp/McpServerApplication.java | 0 .../org/ruoyi/mcp/config/McpServerConfig.java | 0 .../ruoyi/mcp/service/McpCustomService.java | 27 + .../src/main/resources/application-dev.yml | 97 + .../src/main/resources/application-mcp.yml | 0 .../src/main/resources/application.yml | 332 +++ ruoyi-modules-api/ruoyi-chat-api/pom.xml | 41 +- .../org/ruoyi/domain/ChatAgentManage.java | 2 +- .../java/org/ruoyi/domain/ChatAppStore.java | 2 +- .../main/java/org/ruoyi/domain/ChatGpts.java | 2 +- .../java/org/ruoyi/domain/ChatMessage.java | 2 +- .../main/java/org/ruoyi/domain/ChatModel.java | 2 +- .../org/ruoyi/domain/ChatPackagePlan.java | 2 +- .../java/org/ruoyi/domain/ChatPayOrder.java | 2 +- .../java/org/ruoyi/domain/ChatPlugin.java | 2 +- .../java/org/ruoyi/domain/ChatRobConfig.java | 2 +- .../java/org/ruoyi/domain/ChatUsageToken.java | 2 +- .../org/ruoyi/domain/ChatVisitorUsage.java | 2 +- .../java/org/ruoyi/domain/ChatVoucher.java | 2 +- .../ruoyi/domain/bo/ChatAgentManageBo.java | 2 +- .../org/ruoyi/domain/bo/ChatAppStoreBo.java | 2 +- .../java/org/ruoyi/domain/bo/ChatGptsBo.java | 2 +- .../org/ruoyi/domain/bo/ChatMessageBo.java | 2 +- .../java/org/ruoyi/domain/bo/ChatModelBo.java | 2 +- .../ruoyi/domain/bo/ChatPackagePlanBo.java | 2 +- .../org/ruoyi/domain/bo/ChatPayOrderBo.java | 2 +- .../org/ruoyi/domain/bo/ChatPluginBo.java | 2 +- .../org/ruoyi/domain/bo/ChatRobConfigBo.java | 2 +- .../org/ruoyi/domain/bo/ChatUsageTokenBo.java | 2 +- .../ruoyi/domain/bo/ChatVisitorUsageBo.java | 2 +- .../org/ruoyi/domain/bo/ChatVoucherBo.java | 2 +- .../org/ruoyi/domain/vo/CacheListInfoVo.java | 23 - .../java/org/ruoyi/domain/vo/CaptchaVo.java | 25 - .../ruoyi/mapper/ChatAgentManageMapper.java | 2 +- .../org/ruoyi/mapper/ChatAppStoreMapper.java | 2 +- .../java/org/ruoyi/mapper/ChatGptsMapper.java | 2 +- .../org/ruoyi/mapper/ChatMessageMapper.java | 2 +- .../org/ruoyi/mapper/ChatModelMapper.java | 2 +- .../ruoyi/mapper/ChatPackagePlanMapper.java | 2 +- .../org/ruoyi/mapper/ChatPayOrderMapper.java | 2 +- .../org/ruoyi/mapper/ChatPluginMapper.java | 2 +- .../org/ruoyi/mapper/ChatRobConfigMapper.java | 2 +- .../org/ruoyi/mapper/ChatTokenMapper.java | 2 +- .../ruoyi/mapper/ChatUsageTokenMapper.java | 2 +- .../ruoyi/mapper/ChatVisitorUsageMapper.java | 2 +- .../org/ruoyi/mapper/ChatVoucherMapper.java | 2 +- .../service/IChatAgentManageService.java | 4 +- .../ruoyi/service/IChatAppStoreService.java | 4 +- .../org/ruoyi/service/IChatGptsService.java | 4 +- .../ruoyi/service/IChatMessageService.java | 4 +- .../org/ruoyi/service/IChatModelService.java | 4 +- .../service/IChatPackagePlanService.java | 4 +- .../ruoyi/service/IChatPayOrderService.java | 4 +- .../org/ruoyi/service/IChatPluginService.java | 4 +- .../ruoyi/service/IChatUsageTokenService.java | 4 +- .../service/IChatVisitorUsageService.java | 4 +- .../ruoyi/service/IChatVoucherService.java | 4 +- .../impl/ChatAgentManageServiceImpl.java | 4 +- .../service/impl/ChatAppStoreServiceImpl.java | 4 +- .../service/impl/ChatGptsServiceImpl.java | 4 +- .../service/impl/ChatMessageServiceImpl.java | 4 +- .../service/impl/ChatModelServiceImpl.java | 4 +- .../impl/ChatPackagePlanServiceImpl.java | 4 +- .../service/impl/ChatPayOrderServiceImpl.java | 4 +- .../service/impl/ChatPluginServiceImpl.java | 4 +- .../service/impl/ChatTokenServiceImpl.java | 56 + .../impl/ChatUsageTokenServiceImpl.java | 4 +- .../service/impl/ChatVoucherServiceImpl.java | 4 +- .../org/ruoyi/domain/KnowledgeAttach.java | 2 +- .../org/ruoyi/domain/KnowledgeFragment.java | 2 +- .../java/org/ruoyi/domain/KnowledgeInfo.java | 2 +- .../ruoyi/domain/bo/KnowledgeAttachBo.java | 2 +- .../ruoyi/domain/bo/KnowledgeFragmentBo.java | 2 +- .../org/ruoyi/domain/bo/KnowledgeInfoBo.java | 2 +- .../ruoyi/mapper/KnowledgeAttachMapper.java | 2 +- .../ruoyi/mapper/KnowledgeFragmentMapper.java | 2 +- .../org/ruoyi/mapper/KnowledgeInfoMapper.java | 2 +- .../service/IKnowledgeAttachService.java | 4 +- .../service/IKnowledgeFragmentService.java | 4 +- .../ruoyi/service/IKnowledgeInfoService.java | 4 +- .../impl/KnowledgeAttachServiceImpl.java | 4 +- .../impl/KnowledgeFragmentServiceImpl.java | 4 +- .../impl/KnowledgeInfoServiceImpl.java | 4 +- .../service/impl/WeaviateVectorStoreImpl.java | 4 +- .../org/ruoyi/system/domain/ChatConfig.java | 2 +- .../java/org/ruoyi/system/domain/SysDept.java | 2 +- .../java/org/ruoyi/system/domain/SysMenu.java | 2 +- .../ruoyi/system/domain/SysNoticeState.java | 2 +- .../org/ruoyi/system/domain/SysTenant.java | 2 +- .../ruoyi/system/domain/SysTenantPackage.java | 2 +- .../ruoyi/system/domain/bo/ChatConfigBo.java | 3 +- .../ruoyi/system/domain/bo/SysConfigBo.java | 3 +- .../org/ruoyi/system/domain/bo/SysDeptBo.java | 2 +- .../ruoyi/system/domain/bo/SysDictDataBo.java | 2 +- .../ruoyi/system/domain/bo/SysDictTypeBo.java | 2 +- .../org/ruoyi/system/domain/bo/SysMenuBo.java | 2 +- .../ruoyi/system/domain/bo/SysNoticeBo.java | 2 +- .../system/domain/bo/SysNoticeStateBo.java | 2 +- .../org/ruoyi/system/domain/bo/SysOssBo.java | 2 +- .../system/domain/bo/SysOssConfigBo.java | 2 +- .../org/ruoyi/system/domain/bo/SysPostBo.java | 2 +- .../org/ruoyi/system/domain/bo/SysRoleBo.java | 2 +- .../ruoyi/system/domain/bo/SysTenantBo.java | 2 +- .../system/domain/bo/SysTenantPackageBo.java | 2 +- .../org/ruoyi/system/domain/bo/SysUserBo.java | 2 +- .../system/domain/bo/SysUserProfileBo.java | 2 +- .../ruoyi/system/mapper/ChatConfigMapper.java | 2 +- .../ruoyi/system/mapper/SysConfigMapper.java | 2 +- .../ruoyi/system/mapper/SysDeptMapper.java | 6 +- .../system/mapper/SysDictDataMapper.java | 2 +- .../system/mapper/SysDictTypeMapper.java | 2 +- .../system/mapper/SysLogininforMapper.java | 2 +- .../ruoyi/system/mapper/SysMenuMapper.java | 2 +- .../ruoyi/system/mapper/SysNoticeMapper.java | 2 +- .../system/mapper/SysNoticeStateMapper.java | 2 +- .../ruoyi/system/mapper/SysOperLogMapper.java | 2 +- .../system/mapper/SysOssConfigMapper.java | 2 +- .../org/ruoyi/system/mapper/SysOssMapper.java | 2 +- .../ruoyi/system/mapper/SysPostMapper.java | 2 +- .../system/mapper/SysRoleDeptMapper.java | 2 +- .../ruoyi/system/mapper/SysRoleMapper.java | 6 +- .../system/mapper/SysRoleMenuMapper.java | 2 +- .../ruoyi/system/mapper/SysTenantMapper.java | 2 +- .../system/mapper/SysTenantPackageMapper.java | 2 +- .../ruoyi/system/mapper/SysUserMapper.java | 6 +- .../system/mapper/SysUserPostMapper.java | 2 +- .../system/mapper/SysUserRoleMapper.java | 2 +- .../system/service/IChatConfigService.java | 4 +- .../system/service/ISysConfigService.java | 4 +- .../system/service/ISysDictDataService.java | 4 +- .../system/service/ISysDictTypeService.java | 4 +- .../system/service/ISysLogininforService.java | 4 +- .../system/service/ISysNoticeService.java | 4 +- .../service/ISysNoticeStateService.java | 4 +- .../system/service/ISysOperLogService.java | 4 +- .../system/service/ISysOssConfigService.java | 4 +- .../ruoyi/system/service/ISysOssService.java | 4 +- .../ruoyi/system/service/ISysPostService.java | 4 +- .../ruoyi/system/service/ISysRoleService.java | 4 +- .../service/ISysTenantPackageService.java | 4 +- .../system/service/ISysTenantService.java | 13 +- .../ruoyi/system/service/ISysUserService.java | 4 +- .../service/impl/ChatConfigServiceImpl.java | 4 +- .../service/impl/SysConfigServiceImpl.java | 4 +- .../service/impl/SysDataScopeServiceImpl.java | 2 +- .../service/impl/SysDeptServiceImpl.java | 2 +- .../service/impl/SysDictDataServiceImpl.java | 4 +- .../service/impl/SysDictTypeServiceImpl.java | 4 +- .../impl/SysLogininforServiceImpl.java | 4 +- .../service/impl/SysMenuServiceImpl.java | 8 +- .../service/impl/SysNoticeServiceImpl.java | 4 +- .../impl/SysNoticeStateServiceImpl.java | 4 +- .../service/impl/SysOperLogServiceImpl.java | 4 +- .../service/impl/SysOssConfigServiceImpl.java | 4 +- .../service/impl/SysOssServiceImpl.java | 4 +- .../service/impl/SysPostServiceImpl.java | 4 +- .../service/impl/SysRoleServiceImpl.java | 4 +- .../impl/SysTenantPackageServiceImpl.java | 4 +- .../service/impl/SysTenantServiceImpl.java | 170 +- .../service/impl/SysUserServiceImpl.java | 6 +- ruoyi-modules/pom.xml | 2 - ruoyi-modules/ruoyi-chat/pom.xml | 6 + .../chat/ChatAgentManageController.java | 4 +- .../chat/ChatAppStoreController.java | 4 +- .../controller/chat/ChatConfigController.java | 6 +- .../{api => chat}/ChatController.java | 6 +- .../controller/chat/ChatGptsController.java | 4 +- .../chat/ChatMessageController.java | 4 +- .../controller/chat/ChatModelController.java | 4 +- .../chat/ChatPackagePlanController.java | 4 +- .../chat/ChatPayOrderController.java | 4 +- .../controller/chat/ChatPluginController.java | 4 +- .../chat/ChatVoucherController.java | 4 +- .../{api => tripartite}/FaceController.java | 2 +- .../{api => tripartite}/LumaController.java | 2 +- .../{api => tripartite}/SubmitController.java | 2 +- .../{api => tripartite}/SunoController.java | 2 +- .../{api => tripartite}/TaskController.java | 2 +- .../ruoyi/chat/factory/SseServiceFactory.java | 24 - .../chat/factory/VectorStoreFactory.java | 44 - .../chat/factory/VectorizationFactory.java | 10 +- .../service/chat/impl/OllamaServiceImpl.java | 21 +- .../service/chat/impl/OpenAIServiceImpl.java | 114 - .../service/chat/impl/SseServiceImpl.java | 9 +- ...on.java => BgeLargeVectorizationImpl.java} | 6 +- ...tion.java => OpenAiVectorizationImpl.java} | 4 +- .../knowledge/VectorizationWrapper.java | 30 + .../vectorstore/MilvusVectorStore.java | 387 ---- .../demo/controller/TestDemoController.java | 4 +- .../org/ruoyi/demo/domain/bo/TestDemoBo.java | 2 +- .../org/ruoyi/demo/domain/bo/TestTreeBo.java | 2 +- .../demo/mapper/TestDemoEncryptMapper.java | 2 +- .../org/ruoyi/demo/mapper/TestDemoMapper.java | 6 +- .../org/ruoyi/demo/mapper/TestTreeMapper.java | 6 +- .../ruoyi/demo/service/ITestDemoService.java | 4 +- .../service/impl/TestDemoServiceImpl.java | 4 +- .../generator/controller/GenController.java | 6 +- .../org/ruoyi/generator/domain/GenTable.java | 2 +- .../generator/domain/GenTableColumn.java | 2 +- .../mapper/GenTableColumnMapper.java | 2 +- .../generator/mapper/GenTableMapper.java | 2 +- .../service/GenTableServiceImpl.java | 4 +- .../generator/service/IGenTableService.java | 4 +- .../ruoyi/generator/util/VelocityUtils.java | 2 +- .../mapper/generator/GenTableColumnMapper.xml | 8 +- .../mapper/generator/GenTableMapper.xml | 24 +- .../src/main/resources/vm/java/bo.java.vm | 2 +- .../main/resources/vm/java/controller.java.vm | 4 +- .../src/main/resources/vm/java/mapper.java.vm | 2 +- .../main/resources/vm/java/service.java.vm | 4 +- .../resources/vm/java/serviceImpl.java.vm | 4 +- .../ruoyi/mcp/service/McpCustomService.java | 24 - .../monitor/SysLogininforController.java | 4 +- .../monitor/SysOperlogController.java | 4 +- .../monitor/SysUserOnlineController.java | 2 +- .../system/SysConfigController.java | 4 +- .../system/SysDictDataController.java | 4 +- .../system/SysDictTypeController.java | 4 +- .../system/SysNoticeController.java | 4 +- .../system/SysNoticeStateController.java | 4 +- .../system/SysOssConfigController.java | 4 +- .../controller/system/SysOssController.java | 4 +- .../controller/system/SysPostController.java | 4 +- .../controller/system/SysRoleController.java | 4 +- .../system/SysTenantController.java | 38 +- .../system/SysTenantPackageController.java | 4 +- .../controller/system/SysUserController.java | 4 +- 461 files changed, 3110 insertions(+), 13838 deletions(-) delete mode 100644 ruoyi-admin/src/main/resources/static/.gitignore delete mode 100644 ruoyi-admin/src/main/resources/static/.nojekyll delete mode 100644 ruoyi-admin/src/main/resources/static/CNAME delete mode 100644 ruoyi-admin/src/main/resources/static/README.md delete mode 100644 ruoyi-admin/src/main/resources/static/_coverpage.md delete mode 100644 ruoyi-admin/src/main/resources/static/_footer.md delete mode 100644 ruoyi-admin/src/main/resources/static/_navbar.md delete mode 100644 ruoyi-admin/src/main/resources/static/_sidebar.md delete mode 100644 ruoyi-admin/src/main/resources/static/common/add_group.md delete mode 100644 ruoyi-admin/src/main/resources/static/common/blacklist.md delete mode 100644 ruoyi-admin/src/main/resources/static/common/column.md delete mode 100644 ruoyi-admin/src/main/resources/static/common/contribution.md delete mode 100644 ruoyi-admin/src/main/resources/static/common/demo_system.md delete mode 100644 ruoyi-admin/src/main/resources/static/common/pr.md delete mode 100644 ruoyi-admin/src/main/resources/static/common/user_register.md delete mode 100644 ruoyi-admin/src/main/resources/static/common/video.md delete mode 100644 ruoyi-admin/src/main/resources/static/index.html delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/_sidebar.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/common_func.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/component_use.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/content_copy.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/dev_norm.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/dict_use.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/exception_handling.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/icon_use.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/page_cache.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/param_use.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/permissions_use.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/request_process.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/devdoc/router_use.md delete mode 100644 ruoyi-admin/src/main/resources/static/plus-ui/home.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/_sidebar.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/api_encrypt.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/bean_null.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/deploy_vue.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/domestic_databases.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/dubbo_ip.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/https_config.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/identify_fail.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/import_excel.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/jar_run_fail.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/jce_cannot.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/kinfe4j.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/login_step.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/lombok.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/nacos_naming_instance_metadata.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/nacos_read_fail.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/only_one_subscriber.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/parse_exception.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/permission_denied.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/read_metadata.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/sentinel_404.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/st_not_support.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/swagger.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/synchronous_update.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/use_druid.md delete mode 100644 ruoyi-admin/src/main/resources/static/questions/use_tomcat.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/_sidebar.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/changlog.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/elk.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/es.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/kafka.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/maxkey.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/nacos.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/prometheus_grafana.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/rabbitmq.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/rocketmq.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/shardingproxy.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/skywalking.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/architecture_diagram.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/collaboration.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/doc.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/i18n.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/inner_authentication.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/new_module.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/update_package_name.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/update_url.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/client.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/code_generate.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/export.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/import.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/oss.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/page.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/param_check.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/permissions.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/permissions_control.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/router_release.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/social.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/tenant.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/user.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/about_join.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/key.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/test.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/transaction.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/api_encrypt.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/dynamic_datasource.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/encrypt.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/idempotent.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/mail.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sensitive.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sms.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sse.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/translation.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/websocket.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/tree.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/home.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/1.Xinit.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/deploy.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/extend_project.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/idea_environment.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/power_job_init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/snail_job_init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/worker_init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/_sidebar.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/changlog.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/architecture_diagram.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/doc.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/i18n.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/new_module.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/update_package_name.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/update_url.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/client.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/code_generate.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/export.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/import.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/interface_release.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/oss.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/page.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/param_check.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/permissions.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/permissions_control.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/social.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/tenant.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/user.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/about_join.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/key.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/test.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/transaction.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/api_encrypt.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/dynamic_datasource.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/encrypt.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/idempotent.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/mail.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/maxkey.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sensitive.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/skywalking.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sms.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sse.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/topiam.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/translation.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/websocket.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/tree.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/home.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/4.Xinit.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/5.Xnew.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/admin_init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/deploy.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/extend_project.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/idea_environment.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/power_job_init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/snail_job_init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/worker_init.md delete mode 100644 ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/xxl_job_init.md delete mode 100644 ruoyi-admin/src/main/resources/static/static/css/vue.css delete mode 100644 ruoyi-admin/src/main/resources/static/static/image/favicon.ico delete mode 100644 ruoyi-admin/src/main/resources/static/static/image/logo.png delete mode 100644 ruoyi-admin/src/main/resources/static/static/image/ruoyicloudplus.png delete mode 100644 ruoyi-admin/src/main/resources/static/static/image/ruoyivueplus.png delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/docsify-copy-code.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/docsify-footer.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/docsify-pagination.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/docsify-scroll-to-top.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/docsify.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/emoji.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/prism-bash.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/prism-typescript.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/search.min.js delete mode 100644 ruoyi-admin/src/main/resources/static/static/js/zoom-image.min.js create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/constant/RegexConstants.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/constant/SystemConstants.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/factory/RegexPatternPoolFactory.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/factory/YmlPropertySourceFactory.java create mode 100644 ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/utils/ObjectUtils.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/annotation/DataColumn.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/annotation/DataPermission.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/aspect/DataPermissionAspect.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/annotation/DataColumn.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/annotation/DataPermission.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/mapper/BaseMapperPlus.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/enums/DataScopeType.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/InjectionMetaObjectHandler.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/PlusDataPermissionHandler.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/helper/DataPermissionHelper.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/interceptor/PlusDataPermissionInterceptor.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/jakarta/DsJakartaHeaderProcessor.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/jakarta/DsJakartaSessionProcessor.java rename ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/{common/mybatis => }/config/MybatisPlusConfig.java (66%) rename ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/{common/mybatis => }/core/domain/BaseEntity.java (96%) create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/mapper/BaseMapperPlus.java rename ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/{common/mybatis => }/core/page/PageQuery.java (90%) rename ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/{common/mybatis => }/core/page/TableDataInfo.java (85%) rename ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/{common/mybatis => }/enums/DataBaseType.java (74%) create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/enums/DataScopeType.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/InjectionMetaObjectHandler.java rename ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/{common/mybatis => }/handler/MybatisExceptionHandler.java (90%) create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/PlusDataPermissionHandler.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/PlusPostInitTableInfoHandler.java rename ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/{common/mybatis => }/helper/DataBaseHelper.java (91%) create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/helper/DataPermissionHelper.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/interceptor/PlusDataPermissionInterceptor.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/resources/common-mybatis.yml create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/resources/spy.properties create mode 100644 ruoyi-extend/pom.xml rename {ruoyi-modules => ruoyi-extend}/ruoyi-mcp-server/pom.xml (71%) rename {ruoyi-modules => ruoyi-extend}/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/McpServerApplication.java (100%) rename {ruoyi-modules => ruoyi-extend}/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/config/McpServerConfig.java (100%) create mode 100644 ruoyi-extend/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/service/McpCustomService.java create mode 100644 ruoyi-extend/ruoyi-mcp-server/src/main/resources/application-dev.yml rename ruoyi-modules/ruoyi-mcp-server/src/main/resources/application.yaml => ruoyi-extend/ruoyi-mcp-server/src/main/resources/application-mcp.yml (100%) create mode 100644 ruoyi-extend/ruoyi-mcp-server/src/main/resources/application.yml delete mode 100644 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/CacheListInfoVo.java delete mode 100644 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/CaptchaVo.java create mode 100644 ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatTokenServiceImpl.java rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/vectorstore/WeaviateVectorStore.java => ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/WeaviateVectorStoreImpl.java (99%) rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/{api => chat}/ChatController.java (95%) rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/{api => tripartite}/FaceController.java (97%) rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/{api => tripartite}/LumaController.java (97%) rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/{api => tripartite}/SubmitController.java (99%) rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/{api => tripartite}/SunoController.java (98%) rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/{api => tripartite}/TaskController.java (97%) delete mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/factory/SseServiceFactory.java delete mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/factory/VectorStoreFactory.java delete mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/OpenAIServiceImpl.java rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/{vectorizer/BgeLargeVectorization.java => BgeLargeVectorizationImpl.java} (94%) rename ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/{vectorizer/OpenAiVectorization.java => OpenAiVectorizationImpl.java} (96%) create mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/VectorizationWrapper.java delete mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/vectorstore/MilvusVectorStore.java delete mode 100644 ruoyi-modules/ruoyi-mcp-server/src/main/java/org/ruoyi/mcp/service/McpCustomService.java diff --git a/pom.xml b/pom.xml index 6798c157..fde84309 100644 --- a/pom.xml +++ b/pom.xml @@ -14,26 +14,26 @@ 1.0.0 - 3.0.6 + 3.4.4 UTF-8 UTF-8 17 8.0.33 - 3.0.1 + 3.5.16 2.1.0 0.15.0 5.2.3 3.2.1 2.3 1.34.0 - 3.5.3.1 + 3.5.11 3.9.1 - 5.8.18 + 5.8.35 4.10.0 + 4.3.1 3.0.3 3.20.1 2.2.4 - 3.6.1 2.14.2 2.4.0 1.2.1 @@ -42,10 +42,6 @@ 1.72 2.7.0 - - - 1.33 - 1.12.400 @@ -202,22 +198,22 @@ ${satoken.version} - - - com.baomidou - dynamic-datasource-spring-boot-starter - ${dynamic-ds.version} - - org.mybatis.spring.boot - mybatis-spring-boot-starter - ${spring-boot.mybatis} + org.mybatis + mybatis + ${mybatis.version} com.baomidou - mybatis-plus-boot-starter + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + + + + com.baomidou + mybatis-plus-jsqlparser ${mybatis-plus.version} @@ -227,6 +223,13 @@ ${mybatis-plus.version} + + + com.baomidou + dynamic-datasource-spring-boot3-starter + ${dynamic-ds.version} + + p6spy @@ -277,13 +280,6 @@ ${alibaba-ttl.version} - - - org.yaml - snakeyaml - ${snakeyaml.version} - - org.bouncycastle @@ -341,11 +337,11 @@ ${revision} - - org.ruoyi - ruoyi-demo - ${revision} - + + + + + @@ -354,7 +350,8 @@ ruoyi-common ruoyi-modules ruoyi-modules-api - ruoyi-modules/ruoyi-mcp-server + ruoyi-admin + ruoyi-extend pom diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index a8276a4c..89003c2a 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -42,10 +42,11 @@ mssql-jdbc - - org.ruoyi - ruoyi-common-doc - + + + + + org.ruoyi @@ -54,38 +55,7 @@ org.ruoyi - ruoyi-fusion - - - - org.ruoyi - ruoyi-knowledge - - - - - - org.ruoyi - ruoyi-demo - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - net.coobird - thumbnailator - 0.4.11 - - - io.github.ollama4j - ollama4j - 1.0.79 - compile + ruoyi-chat diff --git a/ruoyi-admin/src/main/java/org/ruoyi/controller/AuthController.java b/ruoyi-admin/src/main/java/org/ruoyi/controller/AuthController.java index e78d09c0..5a7a514c 100644 --- a/ruoyi-admin/src/main/java/org/ruoyi/controller/AuthController.java +++ b/ruoyi-admin/src/main/java/org/ruoyi/controller/AuthController.java @@ -14,18 +14,17 @@ import org.ruoyi.common.core.utils.StreamUtils; import org.ruoyi.common.core.utils.StringUtils; import org.ruoyi.common.satoken.utils.LoginHelper; import org.ruoyi.common.tenant.helper.TenantHelper; -import org.ruoyi.system.domain.bo.SysTenantBo; -import org.ruoyi.system.domain.vo.LoginTenantVo; -import org.ruoyi.system.domain.vo.SysTenantVo; -import org.ruoyi.system.domain.vo.TenantListVo; -import org.ruoyi.system.service.ISysTenantService; - -import org.ruoyi.system.service.SysLoginService; -import org.ruoyi.system.service.SysRegisterService; -import org.ruoyi.system.domain.vo.LoginVo; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; +import org.ruoyi.system.domain.bo.SysTenantBo; +import org.ruoyi.system.domain.vo.LoginTenantVo; +import org.ruoyi.system.domain.vo.LoginVo; +import org.ruoyi.system.domain.vo.SysTenantVo; +import org.ruoyi.system.domain.vo.TenantListVo; +import org.ruoyi.system.service.ISysTenantService; +import org.ruoyi.system.service.SysLoginService; +import org.ruoyi.system.service.SysRegisterService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; diff --git a/ruoyi-admin/src/main/java/org/ruoyi/controller/IndexController.java b/ruoyi-admin/src/main/java/org/ruoyi/controller/IndexController.java index 4da12bc8..2a4ee1f0 100644 --- a/ruoyi-admin/src/main/java/org/ruoyi/controller/IndexController.java +++ b/ruoyi-admin/src/main/java/org/ruoyi/controller/IndexController.java @@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaIgnore; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; /** * 首页 @@ -11,8 +12,7 @@ import org.springframework.web.bind.annotation.GetMapping; * @author Lion Li */ @SaIgnore -@RequiredArgsConstructor -@Controller +@RestController public class IndexController { /** @@ -20,17 +20,9 @@ public class IndexController { */ @GetMapping("/") public String index() { - return "index.html"; + return "RuoYi AI启动成功!"; } - @GetMapping("/success") - public String success(){ - return "paySuccess.html"; - } - @GetMapping("/cancel") - public String cancel(){ - return "cancel"; - } } diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 09c609c3..66a63e71 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -60,9 +60,6 @@ spring.data: # password: 123456 # 连接超时时间 timeout: 10S - # 是否开启ssl - ssl: false - redisson: # redis key前缀 keyPrefix: @@ -97,22 +94,3 @@ sms: # 腾讯专用 sdkAppId: -spring: - ai: - openai: - api-key: sk-xX - base-url: https://api.pandarobot.chat - ollama: - base-url: http://localhost:11434 - init: - pull-model-strategy: always - timeout: 60s - max-retries: 1 - mcp: - client: - enabled: true - name: call-mcp-server - sse: - connections: - server1: - url: http://127.0.0.1:8080 diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 26173564..84be163e 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -49,8 +49,10 @@ server: # 日志配置 logging: level: - org.ruoyi: @logging.level@ + org.dromara: @logging.level@ org.springframework: warn + org.mybatis.spring.mapper: error + org.apache.fury: warn config: classpath:logback-plus.xml # 用户配置 @@ -318,5 +320,16 @@ wechat: token: '' aesKey: '' - +spring: + ai: + ollama: + base-url: http://localhost:11434 + mcp: + client: + enabled: true + name: call-mcp-server + sse: + connections: + server1: + url: http://127.0.0.1:6040 diff --git a/ruoyi-admin/src/main/resources/static/.gitignore b/ruoyi-admin/src/main/resources/static/.gitignore deleted file mode 100644 index 9e339689..00000000 --- a/ruoyi-admin/src/main/resources/static/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -###################################################################### -# Build Tools - -.gradle -/build/ -!gradle/wrapper/gradle-wrapper.jar - -target/ -!.mvn/wrapper/maven-wrapper.jar - -###################################################################### -# IDE - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### JRebel ### -rebel.xml - -### NetBeans ### -nbproject/private/ -build/* -nbbuild/ -nbdist/ -.nb-gradle/ - -###################################################################### -# Others -*.log -*.xml.versionsBackup -*.swp - -!*/build/*.java -!*/build/*.html -!*/build/*.xml diff --git a/ruoyi-admin/src/main/resources/static/.nojekyll b/ruoyi-admin/src/main/resources/static/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/ruoyi-admin/src/main/resources/static/CNAME b/ruoyi-admin/src/main/resources/static/CNAME deleted file mode 100644 index 3a194c70..00000000 --- a/ruoyi-admin/src/main/resources/static/CNAME +++ /dev/null @@ -1 +0,0 @@ -plus-doc.dromara.org \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/README.md b/ruoyi-admin/src/main/resources/static/README.md deleted file mode 100644 index b50e2523..00000000 --- a/ruoyi-admin/src/main/resources/static/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# 框架介绍 -- - - -- `RuoYi-Vue-Plus` 分布式集群框架 [文档跳转](/ruoyi-vue-plus/home.md) -- `RuoYi-Cloud-Plus` 微服务框架 [文档跳转](/ruoyi-cloud-plus/home.md) -- `plus-ui` 统一 Vue3 前端项目 [文档跳转](/plus-ui/home.md) -- `plus-doc` 统一文档项目 - -## 特别赞助 - - - -
- - -
- - -[如何成为赞助商 加群联系作者详谈](/common/add_group.md) - -## 代码地址 - -| 介绍 | 项目名 | 项目地址 | 注意事项 | -|------------|:-----------------|------------------------------------------------------------------------------------------------------------------------|----------------------------| -| 🔥 分布式集群框架 | RuoYi-Vue-Plus | - [Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus)
- [GitHub](https://github.com/dromara/RuoYi-Vue-Plus) | 重写RuoYi-Vue全方位升级(不兼容原框架) | -| 🔥 微服务框架 | RuoYi-Cloud-Plus | - [Gitee](https://gitee.com/dromara/RuoYi-Cloud-Plus)
- [GitHub](https://github.com/dromara/RuoYi-Cloud-Plus) | 重写RuoYi-Cloud全方位升级(不兼容原框架) | -| 🔥 统一前端项目 | plus-ui | - [Gitee](https://gitee.com/JavaLionLi/plus-ui)
- [GitHub](https://github.com/JavaLionLi/plus-ui) | Vue与Cloud项目通用前端 | -| 🔥 统一文档项目 | plus-doc | - [Gitee](https://gitee.com/dromara/plus-doc)
- [GitHub](https://github.com/dromara/plus-doc) | 通用文档 | - - -## 业务功能 - -| 功能 | 介绍 | -|-------|---------------------------------------| -| 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能。 | -| 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置。 | -| 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 | -| 岗位管理 | 配置系统用户所属担任职务。 | -| 菜单管理 | 配置系统菜单,操作权限,按钮权限标识等。 | -| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分。 | -| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护。 | -| 参数管理 | 对系统动态配置常用参数。 | -| 通知公告 | 系统通知公告信息发布维护。 | -| 操作日志 | 系统正常操作日志记录和查询;系统异常信息日志记录和查询。 | -| 登录日志 | 系统登录日志记录查询包含登录异常。 | -| 文件管理 | 系统文件上传、下载等管理。 | -| 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志。 | -| 代码生成 | 前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 | -| 系统接口 | 根据业务代码自动生成相关的api接口文档。 | -| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等。 | -| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | -| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | -| 使用案例 | 系统的一些功能案例 | - -## 关注作者 - -作者博客: [https://lionli.blog.csdn.net/?type=blog](https://lionli.blog.csdn.net/?type=blog) - -公众号: **<狮子领域 程序圈>** -
-![输入图片说明](https://foruda.gitee.com/images/1678975769377570440/507062df_1766278.png "屏幕截图") - -## 捐献作者 - -**作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭** -
- - -## Dromara 全家福 - -社区仓库地址: [dromara开源社区](https://gitee.com/organizations/dromara/projects) - -![输入图片说明](https://foruda.gitee.com/images/1706071866226295002/68cffcf6_1766278.png "屏幕截图") - - diff --git a/ruoyi-admin/src/main/resources/static/_coverpage.md b/ruoyi-admin/src/main/resources/static/_coverpage.md deleted file mode 100644 index d84bf0dc..00000000 --- a/ruoyi-admin/src/main/resources/static/_coverpage.md +++ /dev/null @@ -1,32 +0,0 @@ - - - -
-
-
百搭AI
- - -[![码云Gitee](https://gitee.com/ageerle/ruoyi-ai/badge/star.svg?theme=blue)](https://gitee.com/ageerle/ruoyi-ai) -[![GitHub](https://img.shields.io/github/stars/ageerle/ruoyi-ai.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/ageerle/ruoyi-ai/blob/master/LICENSE) -
-[![ruoyi-ai](https://img.shields.io/badge/ruoyi-ai-5.2.2-success.svg)](https://gitee.com/ageerle/ruoyi-ai) -[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.2-blue.svg)]() - -
-
- -
- -
- - -> 百搭AI是一个整合了多种大语言模型API的开源平台,实现了AI对话、绘图、声音克隆和私有知识库等功能。 -> -> 平台配备管理后台,支持微信支付、微信公众号、微信多开、Stripe国际支付和百度文本审核等运营功能。 -> -> 项目采用Java+Vue+Vben5技术栈构建,遵循MIT License,允许二次开发并用于商业销售。 - -Copyright © 2023-2024 版权所有:ageerle@163.com 备案号:鄂ICP备2023007672号 - -[开始使用 Let's Go](/README.md) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/_footer.md b/ruoyi-admin/src/main/resources/static/_footer.md deleted file mode 100644 index 7f5f9a3f..00000000 --- a/ruoyi-admin/src/main/resources/static/_footer.md +++ /dev/null @@ -1,2 +0,0 @@ - -对文档有疑问?欢迎您帮助我们 [完善此文档](https://gitee.com/JavaLionLi/plus-doc) ! \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/_navbar.md b/ruoyi-admin/src/main/resources/static/_navbar.md deleted file mode 100644 index e893825f..00000000 --- a/ruoyi-admin/src/main/resources/static/_navbar.md +++ /dev/null @@ -1,9 +0,0 @@ - - -* [文档导航](/README.md) -* [Vue版本](/ruoyi-vue-plus/home.md) -* [Cloud版本](/ruoyi-cloud-plus/home.md) -* [前端文档](/plus-ui/home.md) -* [常见问题](/questions/lombok.md) -* [视频教程](/common/video.md) -* [演示系统](/common/demo_system.md) diff --git a/ruoyi-admin/src/main/resources/static/_sidebar.md b/ruoyi-admin/src/main/resources/static/_sidebar.md deleted file mode 100644 index 0446edc6..00000000 --- a/ruoyi-admin/src/main/resources/static/_sidebar.md +++ /dev/null @@ -1,16 +0,0 @@ - -- **特别赞助** -- [![输入图片说明](https://foruda.gitee.com/images/1704162419429172656/d0521e59_1766278.png "2024-01-02=>2028-01-02")](http://ccflow.org/?frm=ryPlus) -- [![输入图片说明](https://foruda.gitee.com/images/1705569347386939952/3f187980_1766278.jpeg "2024-01-18=>2025-01-18")](http://www.shuduokeji.com) -- [![输入图片说明](https://foruda.gitee.com/images/1711681233267310022/2ffbcff2_1766278.png "2024-03-29=>2025-03-29")](https://www.jnpfsoft.com/index.html?from=plus-doc) - -- **开始** - - [框架介绍](/README.md) - - [演示系统](/common/demo_system.md) - - [官方视频教程](/common/video.md) - - [粉丝专栏](/common/column.md) - - [参与贡献项目](/common/contribution.md) - - [如何提交PR](/common/pr.md) - - [如何加群](/common/add_group.md) - - [使用者登记](/common/user_register.md) - - [黑名单](/common/blacklist.md) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/common/add_group.md b/ruoyi-admin/src/main/resources/static/common/add_group.md deleted file mode 100644 index f6957bc4..00000000 --- a/ruoyi-admin/src/main/resources/static/common/add_group.md +++ /dev/null @@ -1,27 +0,0 @@ -# 加群方式 -- - - -### 交流群(不提供任何问题解答 纯交流) - -**加 <小助手> 微信备注 <加群>**
-**视频课程咨询或其他问题咨询请查看下方信息(小助手是机器人)** - - - -### VIP群(付费加群 提供问题解答、技术支持、技术分享) - -首先感谢 `RuoYi` 提供分享开源 框架基于 `RuoYi` 重写大部分功能实现
-项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可
-VIP群是作者提供的私人服务 不代表着项目收费 - -> 问问题等于做习题 听作者解答问题等于习题讲解
-> 一个人接触的问题有限 一群人接触的问题无限 早进群早接触更多的问题(每天99+)
-> 承诺: 看见必回复 让你感受作者有多话痨
- -两种途径: -1. 购买官方视频进群 [官方视频](/common/video.md) -2. 扫描下方二维码付款进群(无视频) - -支付后申请加群即可 QQ群号 : **<637757165>**
- -**加群扫码**
- diff --git a/ruoyi-admin/src/main/resources/static/common/blacklist.md b/ruoyi-admin/src/main/resources/static/common/blacklist.md deleted file mode 100644 index 67594bd3..00000000 --- a/ruoyi-admin/src/main/resources/static/common/blacklist.md +++ /dev/null @@ -1,7 +0,0 @@ -# 黑名单 -- - - - -地址: https://github.com/QNAV/RuoYi-X-Plus -
-上榜缘由 使用本框架二次开源并未有任何声明与标注 将所有代码的作者名全都改成了自己 剽窃本框架代码 - diff --git a/ruoyi-admin/src/main/resources/static/common/column.md b/ruoyi-admin/src/main/resources/static/common/column.md deleted file mode 100644 index b2155a3c..00000000 --- a/ruoyi-admin/src/main/resources/static/common/column.md +++ /dev/null @@ -1,18 +0,0 @@ -# 粉丝专栏 -- - - -**由上到下 从易到难** - -> 粉丝整理 欢迎投稿 - -| 作者 | 文档地址 | 说明 | -|---------------|---------------------------------------------------------------|--------------------| -| 抓蛙师 | https://www.bilibili.com/video/BV1TG41157Ef/ | 学会问问题(小白必看) | -| 抓蛙师 | https://www.bilibili.com/video/BV1mr4y1j75M | Vue框架基础视频专栏(新人必看) | -| 抓蛙师 | https://www.bilibili.com/video/BV1Na411u7eC | Vue框架改造视频专栏(新人必看) | -| 抓蛙师 | https://www.bilibili.com/video/BV1te4y1D7hi | 小程序鉴权与uniapp联动 | -| 抓蛙师 | https://www.bilibili.com/video/BV1zt4y137UP | 公众号集成 | -| mayuanfei | https://note.youdao.com/s/XpvKnxAb | 入门专栏(新人必看) | -| 程序猿一枚_ | https://blog.csdn.net/zhaozhiqiang1981/category_12221291.html | 玩转RuoYi-Cloud-Plus | -| 程序猿一枚_ | https://www.bilibili.com/video/BV1yA411r7ji/ | Cloud环境搭建以及进阶开发 | -| MichelleChung | https://blog.csdn.net/michelle_zhong/category_11109741.html | 源码解析专栏(进阶必看) | -| MichelleChung | https://blog.csdn.net/michelle_zhong/category_12058476.html | Cloud源码解析专栏 | diff --git a/ruoyi-admin/src/main/resources/static/common/contribution.md b/ruoyi-admin/src/main/resources/static/common/contribution.md deleted file mode 100644 index 6b5909e6..00000000 --- a/ruoyi-admin/src/main/resources/static/common/contribution.md +++ /dev/null @@ -1,69 +0,0 @@ -# 参与贡献的方式 -- - - -参与贡献开源的方式有很多种 听作者来介绍 - -## 为开源项目点一个Star - -> Star的多少关系到项目能否被更多人看到 -
-同时Star也是作者前进的动力(作者每天都在盯着Star 涨了会开心 跌了会失落) -
-
-> 大家在寻找开源项目的时候, 大多数情况也是会先看Star比较多的项目 -
-所以请给您觉得好的开源项目点一个小小的Star, 让好的项目能够被更多人看到 -
- -![输入图片说明](https://foruda.gitee.com/images/1678934493115487351/0c45121e_1766278.png "屏幕截图") -
-Vue版本: [Gitee我要点Star](https://gitee.com/dromara/RuoYi-Vue-Plus/stargazers) [Github我要点Star](https://github.com/dromara/RuoYi-Vue-Plus) -
-Cloud版本: [Gitee我要点Star](https://gitee.com/dromara/RuoYi-Cloud-Plus/stargazers) [Github我要点Star](https://github.com/dromara/RuoYi-Cloud-Plus) - -## 为社区处理问题 - -> Issue是社区的交流地 大家会在这里提出自己的问题 或者是项目的功能异常 - -> 提问的规范在Issue的模板里已经写好了 按照模板填写有助于作者或者其他社区人员快速有效的回答问题 -![输入图片说明](https://foruda.gitee.com/images/1678935068341532603/4b9d7af9_1766278.png "屏幕截图") - -> 为提出问题的小伙伴答疑 可以有效降的帮助别人
-> 而且可以降低社区人员的精力分散 使精力全部投入到项目设计研发中 -![输入图片说明](https://foruda.gitee.com/images/1678935380481365514/dddc9ce9_1766278.png "屏幕截图") - -## 改进社区文档 - -> 大家都知道 我们程序员都不擅长写作
-> 有时候作者把文档写完了也不知道用户是什么感觉 是否能看懂
- -> 所以参与社区文档建设绝对是一件意义重大的事情
-> 大家可以在Issue提出观后感 觉得哪看不懂 觉得哪应该详细说明
-> 当然了 大家也可以对文档进行改进后提交PR修改申请 - -文档仓库: [plus-doc](https://gitee.com/JavaLionLi/plus-doc) 👈点他点他 -![输入图片说明](https://foruda.gitee.com/images/1678935992827063291/d7c4dc5b_1766278.png "屏幕截图") - -## 贡献代码 - -> 想参与贡献代码的小伙伴 重点来了: 作者会经常在Issue里发布需求认领
-> 觉得自己能做的可以在Issue里跟作者讨论 如需求还不够清晰 或者做的过程中遇到了什么问题 - - - -> 需求确定了以后就可以开始专注的写代码了
-> 但在开始写代码之前 一定要先看一下如何正确的提交PR - -一点要仔细看: [如何提交PR](/common/pr.md) 👈点他点他 - -## 如何成为项目成员 - -> 1.对框架有重大贡献者(由作者与团队成员判定)
-> 2.完成社区发布的两项复杂任务
-> 3.持续完成社区发布的简单任务若干(作者会关注到)
-> 4.持续为社区优化文档或处理issue若干(作者会关注到)
- -## 项目成员待遇 - -> 1.可免费进入vip收费群
-> 2.每年还会发放IDEA正版授权
- diff --git a/ruoyi-admin/src/main/resources/static/common/demo_system.md b/ruoyi-admin/src/main/resources/static/common/demo_system.md deleted file mode 100644 index 18424e90..00000000 --- a/ruoyi-admin/src/main/resources/static/common/demo_system.md +++ /dev/null @@ -1,13 +0,0 @@ -# 系统演示(请大家不要乱改数据 影响他人体验 谢谢配合) -- - - -**感谢 `孤舟烟雨` 贡献的演示服务器** - -**1核2G 小服务器 经不起压测 请理性操作 违者直接封IP** - -> 访问地址: [http://43.138.9.96/](http://43.138.9.96/) - -> 登录账户 admin/admin123 - -> Admin监控中心 ruoyi/123456 - -> 任务调度中心 admin/123456 diff --git a/ruoyi-admin/src/main/resources/static/common/pr.md b/ruoyi-admin/src/main/resources/static/common/pr.md deleted file mode 100644 index e9684dd4..00000000 --- a/ruoyi-admin/src/main/resources/static/common/pr.md +++ /dev/null @@ -1,37 +0,0 @@ -# 如何提交PR贡献代码 -- - - -### 步骤一 Fork项目到自己仓库 - -![输入图片说明](https://foruda.gitee.com/images/1673427084798343408/142a55d0_1766278.png "屏幕截图") - -### 步骤二 基于dev分支 新建一个此PR功能点的专属分支 - -![输入图片说明](https://foruda.gitee.com/images/1673427220695789412/14c4f4ff_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1673427193964585607/16ea99d9_1766278.png "屏幕截图") - -### 步骤三 使用Git工具 将自己仓库的项目拉去到本地做代码编写 - -![输入图片说明](https://foruda.gitee.com/images/1673427313201488937/f2df59bf_1766278.png "屏幕截图") - -### 步骤四 使用Idea打开项目 切换到新建的功能分支 - -![输入图片说明](https://foruda.gitee.com/images/1673427394686229310/c479a5a5_1766278.png "屏幕截图") - -### 步骤五 将编写好的代码 提交到自己的远程仓库 - -![输入图片说明](https://foruda.gitee.com/images/1673427519150795591/d88c2bc9_1766278.png "屏幕截图") - -### 步骤六 创建PR申请(此操作在自己仓库或者要PR的仓库都可以) - -![输入图片说明](https://foruda.gitee.com/images/1673427616155043776/fe2ce097_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1673427865031025513/0f58a137_1766278.png "屏幕截图") - -### 步骤七 等待作者评审 按要求更改 直到没有问题后被作者合并 - -![输入图片说明](https://foruda.gitee.com/images/1673428029932524584/93234628_1766278.png "屏幕截图") - -### 评审期间 如需对PR内容做更改 直接在新功能分支提交代码即可 -### 无需重复提交PR申请 这边会自动比对两个分支的差异 - -![输入图片说明](https://foruda.gitee.com/images/1673428054139366497/4ecb6e98_1766278.png "屏幕截图") - diff --git a/ruoyi-admin/src/main/resources/static/common/user_register.md b/ruoyi-admin/src/main/resources/static/common/user_register.md deleted file mode 100644 index 9c221cd1..00000000 --- a/ruoyi-admin/src/main/resources/static/common/user_register.md +++ /dev/null @@ -1,80 +0,0 @@ -# 使用者登记 -- - - -**使用此开源项目的公司或者组织** -> Vue版本登记地址: https://gitee.com/dromara/RuoYi-Vue-Plus/issues/I4QP39 - -> Cloud版本登记地址: https://gitee.com/dromara/RuoYi-Cloud-Plus/issues/I4VJ7G - -| 公司名 | 官网 | LOGO | -|-------------------|:-------------------------------|----------------------------------------------------------------------------------------------------------------| -| 中国联通(长春分公司) | http://www.10010.com | | -| 中国电信(湖南分公司) | http://www.189.cn/hn/ | | -| 南京感知信息技术有限公司 | https://njgzxx.cn/ | | -| 陕西骏景索道运营管理有限公司 | https://www.junjingsuodao.com/ | | -| 悠码科技有限公司 | https://orise.trytowish.cn/ | | -| 苏州龙的信息系统股份有限公司 | http://www.longdayinfo.com/ | | -| 北京数通智达科技有限公司 | http://www.bzdtech.com/ | | -| 广州六六七七科技有限公司 | https://artiversehub.ai/ | | -| 宁波三品软件科技有限公司 | http://nbsanpin.com/ | | -| 北京御一科技信息技术有限公司 | https://www.yudoctor.com | | -| 成都卡恩特医疗科技有限公司 | http://www.scknot.com | | -| 无锡科艾思科技有限公司 | https://www.kyoeis.com | | -| 深圳市海联天下科技有限公司 | www.sealinkin.com | | -| 上海非定义旅游服务有限公司 | http://www.anonymity.love/ | | -| 重庆威爱云科技有限公司 | https://www.51vive.com | | -| 中城智联(成都)创新科技有限公司 | http://www.zc-zl.com/ | | -| 浙江海亮股份有限公司 | https://www.hailiangstock.com | | -| 河北雄安山禾咨询工程有限公司 | https://shanheqei.club/ | | -| 数舵(河北雄安)信息科技有限公司 | http://www.shuduokeji.com | | -| 南昌鼎欣科技股份有限公司 | https://www.openzt.com | | -| 东莞市码载网络科技有限公司 | https://www.codeload.top | | -| 北京农信通科技有限责任公司 | http://www.nxt.com.cn | | -| 中康腾华网络科技(重庆)有限公司 | https://www.zkthwlkj.com/ | | -| 杭州码恒信息科技有限公司 | http://www.mh-barcode.com/ | | -| 南京晶益科技有限公司 | https://www.nanjingjingyi.com/ | | -| 合肥智享亿云科技有限公司 | http://www.izxyy.com | | -| 锡简科技 | https://www.xj-fast.com | | -| 福建亘前科技有限公司 | https://genqian.top | | -| 北京联宇信通科技有限公司 | http://www.lyxtkj.com/ | | -| 厦门市熵时光科技有限公司 | https://www.xetsoft.com | | -| 广州润沁教育科技有限公司 | https://www.ca163.net | | -| 广东乐善智能装备股份有限公司 | https://www.china-leshan.com/ | | -| 数字江西科技有限公司 | https://www.digitaljx.com/ | | -| 上海极锐星瀚传感技术有限公司 | http://www.jrsensing.com/ | | -| 北京数影互联科技有限公司 | http://www.dataflying.top/ | | -| 广州创服信息科技有限公司 | https://www.cfkjcloud.com | | -| 茂名云智科技有限公司 | http://www.winzkj.com | | -| 成都时光旅迹科技有限公司 | https://www.ttmup.com/ | | -| 成都炫影全息科技有限公司 | http://xyqxgs.com | | -| 中山厚德快速模具有限公司 | http://hordrt.com | | -| 深圳市深南夙星科技有限公司 | http://www.szsnsx.com/ | | -| 陕西华恒军创信息科技有限公司 | http://hhjc.cc | | -| 河南小牛信息科技有限公司 | http://www.hnxn888.com/ | | -| 武汉华智讯网络信息技术有限公司 | http://www.xun188.com | | -| 易税信息技术有限公司 | https://www.etax.top | | -| 广西华景城建筑设计有限公司 | http://www.hjcadc.com | | -| 铭创科技有限公司 | https://www.mcck.cn/ | | -| 西安鼎慧网络科技有限公司 | | | -| 营口鼎瑞网络科技有限公司 | | | -| 南昌漫库书店有限公司 | | | -| 广西文韬智能科技有限公司 | | | -| 贵州亿瑞祺科技有限公司 | | -| 贵州新绿视界环保科技有限公司 | | -| 湖南智才伯乐数据科技有限公司 | | -| 德州商储超市有限公司 | | -| 曲沃亿分科技中心 | | -| 南京杰度信息技术有限公司 | | -| 武汉忆秋科技有限公司 | | -| 济南千惠网络科技有限公司 | | -| 江苏泛联科技有限公司 | | -| 沈阳市果冻网络信息科技有限责任公司 | | -| 灵劲科技有限公司 | | -| 亿世达餐饮管理(北京)有限公司 | | -| 深圳市凯帝电子商务有限公司 | | -| 成都数智源蓉卡科技有限公司 | | -| 上海振福信息科技有限公司 | | -| 重庆六客会科技有限公司 | | -| 无限创优(西安)科技有限公司 | | -| 惠族网络科技发展有限公司 | | -| 纳森科技有限公司 | | - diff --git a/ruoyi-admin/src/main/resources/static/common/video.md b/ruoyi-admin/src/main/resources/static/common/video.md deleted file mode 100644 index 14fc2752..00000000 --- a/ruoyi-admin/src/main/resources/static/common/video.md +++ /dev/null @@ -1,85 +0,0 @@ -# 视频教程(联合出品) - -### 主讲与后期剪辑: `抓蛙师` - -抓蛙师简介: B站知名UP主 B站首页: https://space.bilibili.com/520725002 - -### 知识点统筹与内容审核: `疯狂的狮子Li` - -疯狂的狮子Li简介: RuoYi-Vue-Plus 与 RuoYi-Cloud-Plus 作者 - -## 已完结🎉🎉🎉 优惠价: 598(仅限前500名) ~~原价: 698~~ - -**注意: 视频采用 RuoYi-Vue-Plus 版本 4.X 分支讲解!!! (内容为通用技术与版本关联性不大)**
-**内容为框架内所用到的技术与设计原理(打破不知道、不会用、不知应用场景等问题)** - -课程简介: https://www.bilibili.com/video/BV16j411D7BX/ -
-试看课程: https://www.bilibili.com/video/BV1uS411P7JD/ -
-试看课程: https://www.bilibili.com/video/BV1vLbNeuESn/ -
-试看课程: https://www.bilibili.com/video/BV1xV4y127KM/ -
-试看课程: https://www.bilibili.com/video/BV1W5v8eBEgs/ -
-课程总结: https://www.bilibili.com/video/BV1734y1g7fk/ -
- -## 购买方式 - -**小本生意 用心录制 拒绝砍价 已更新到 236 集 课程完结**
-> 课程咨询或购买请联系 价格598
-> QQ: 906670865 (疯狂的狮子Li)
-> QQ: 770492966 (抓蛙师) - -## 购买前常见问题答疑 -> 问题1: 购买后是否有群可以解答问题
-> 答: 购买后有专属课程付费群(千人大群)讲师在线答疑 -> -> 问题2: 是否持续更新 如新版本功能
-> 答: 课程目录即为全部课程内容 以课程目录为准 明年大概会出二期来讲新版本内容
-> 因为持续更新会导致前面的技术老旧 新购买的人无法及时学习新技术
-> 故而采用分期出课程制度 已经购买过的老客户 再次购买下一次会给力度非常大的折扣 -> -> 问题3: 目前视频未全部录制完成 后续更新是否二次收费
-> 答: 视频目录即为全部视频内容 一次收费后续更新仍然可看直到视频全部更新完成(明年出二期课程不算在内) -> -> 问题4: 视频如何下载如何观看
-> 答: 视频文件已加密 采用专门的播放器(播放器只限制截图录屏等不限制其他软件使用) 由管理员发放授权码观看
-> 支持通过 百度云 或者 阿里云 网盘下载视频资源 -> -> 问题5: 视频平均时长和总时长大概多久
-> 答: 视频每集短的大概10分钟以上 长的大概40个分钟左右 平均时长20多分钟每集
-> 目前已经录制了236集总时长为80多个小时 -> -> 问题6: 是否有讲解 Cloud 版本相关内容
-> 答: 视频主要讲解内容为框架内所用到的技术与设计原理 无论什么版本 功能和设计都是一样的
-> Cloud 版本只是多了 alibaba 的几个组件完全可以B站自学 - -## 课程目录 - -![输入图片说明](https://foruda.gitee.com/images/1695105467795304336/58fcd6db_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105494170842444/10f98fed_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105523526589287/f131c614_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105547992880680/9f4137f3_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105560849590514/d19fad6a_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105586641712428/349a971b_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105595501187093/fb819d35_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105609163585390/833dd89c_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105630469565265/8dbba1d2_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695105659037093525/09a4f6e1_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1695714493079698007/311980ee_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1697446957351573520/cab3617d_1766278.png "屏幕截图") - -## 学员观后感 - -| | | -|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| -| ![输入图片说明](https://foruda.gitee.com/images/1691386100129796781/44b69dae_1766278.jpeg "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1691386076834242484/a6073f7d_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1691386089186649583/98ac8b7c_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1691386108722171132/b937b23a_1766278.jpeg "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1695714607596127461/513b6893_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1692804549604261480/09ef12f6_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1692804541482477905/578e5448_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1695714614517941469/cac681fb_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1698225407961714462/4d271901_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1698225416488201339/30572e7f_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1698807198508085566/16c37a1b_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1698807208125772586/ceed632e_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1698807214013013096/ad3bc016_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1698807221010472627/72b10901_1766278.png "屏幕截图") | diff --git a/ruoyi-admin/src/main/resources/static/index.html b/ruoyi-admin/src/main/resources/static/index.html deleted file mode 100644 index 76e44328..00000000 --- a/ruoyi-admin/src/main/resources/static/index.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - plus-doc - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/_sidebar.md b/ruoyi-admin/src/main/resources/static/plus-ui/_sidebar.md deleted file mode 100644 index 00197250..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/_sidebar.md +++ /dev/null @@ -1,22 +0,0 @@ - -- **特别赞助** -- [![输入图片说明](https://foruda.gitee.com/images/1704162419429172656/d0521e59_1766278.png "2024-01-02=>2028-01-02")](http://ccflow.org/?frm=ryPlus) -- [![输入图片说明](https://foruda.gitee.com/images/1705569347386939952/3f187980_1766278.jpeg "2024-01-18=>2025-01-18")](http://www.shuduokeji.com) -- [![输入图片说明](https://foruda.gitee.com/images/1711681233267310022/2ffbcff2_1766278.png "2024-03-29=>2025-03-29")](https://www.jnpfsoft.com/index.html?from=plus-doc) - - -* **简介** - * [项目简介](/plus-ui/home.md) -* **开发文档** - * [通用方法](/plus-ui/devdoc/common_func.md) - * [开发规范](/plus-ui/devdoc/dev_norm.md) - * [请求流程](/plus-ui/devdoc/request_process.md) - * [路由使用](/plus-ui/devdoc/router_use.md) - * [组件使用](/plus-ui/devdoc/component_use.md) - * [权限使用](/plus-ui/devdoc/permissions_use.md) - * [页签缓存](/plus-ui/devdoc/page_cache.md) - * [使用图标](/plus-ui/devdoc/icon_use.md) - * [使用字典](/plus-ui/devdoc/dict_use.md) - * [使用参数](/plus-ui/devdoc/param_use.md) - * [异常处理](/plus-ui/devdoc/exception_handling.md) - * [内容复制](/plus-ui/devdoc/content_copy.md) diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/common_func.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/common_func.md deleted file mode 100644 index e359c03c..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/common_func.md +++ /dev/null @@ -1,234 +0,0 @@ -# 通用方法 -- - - - -### $tab对象 -> `$tab`对象用于做页签操作、刷新页签、关闭页签、打开页签、修改页签等,它定义在`plugins/tab.ts`文件中,它有如下方法 -* 打开页签 - -```typescript -// 打开页签 -proxy?.$tab.openPage('/system/user'); -// 打开页签并指定页签标题 -proxy?.$tab.openPage('/system/user', '用户管理'); -proxy?.$tab.openPage('/system/user', '用户管理').then(() => { - // 执行结束的逻辑 -}) -``` - -* 修改页签 - -```typescript -// 修改当前页签 -const obj = Object.assign({}, route, { title: '自定义标题' }); -proxy?.$tab.updatePage(obj); -``` -* 关闭页签 - -```typescript -// 关闭当前 -proxy?.$tab.closePage(); -// 关闭指定页签 -const obj = { path: "/system/user", name: "User" }; -proxy?.$tab.closePage(obj); - -proxy?.$tab.closePage(obj).then(() => { - // 执行结束的逻辑 -}) -``` - -* 刷新页签 - -```typescript -// 刷新当前页签 -proxy?.$tab.refreshPage(); - -// 刷新指定页签 -const obj = { path: "/system/user", name: "User" }; -proxy?.$tab.refreshPage(obj); - -proxy?.$tab.refreshPage(obj).then(() => { - // 执行结束的逻辑 -}) -``` - -* 关闭所有页签 - -```typescript -proxy?.$tab.closeAllPage(); - -proxy?.$tab.closeAllPage().then(() => { - // 执行结束的逻辑 -}) -``` - -* 关闭左侧页签 - -```typescript -// 关闭当前页签的左侧页签 -proxy?.$tab.closeLeftPage(); - -// 关闭指定页签的左侧页签 -const obj = { path: "/system/user", name: "User" }; -proxy?.$tab.closeLeftPage(obj); - -proxy?.$tab.closeLeftPage(obj).then(() => { - // 执行结束的逻辑 -}) -``` - -* 关闭右侧页签 - -```typescript -// 关闭当前页签的右侧页签 -proxy?.$tab.closeRightPage(); - -// 关闭指定页签的右侧页签 -const obj = { path: "/system/user", name: "User" }; -proxy?.$tab.closeRightPage(obj); - -proxy?.$tab.closeRightPage(obj).then(() => { - // 执行结束的逻辑 -}) -``` - -* 关闭其他页签 - -```typescript -proxy?.$tab.closeOtherPage(); - -const obj = { path: "/system/user", name: "User" }; -proxy?.$tab.closeOtherPage(obj); - -proxy?.$tab.closeOtherPage(obj).then(() => { - // 执行结束的逻辑 -}) -``` - -### $modal对象 -> `$modal`对象用于做消息提示、通知提示、对话框提醒、二次确认、遮罩等,它定义在`plugins/modal.ts`文件中,它有如下方法 - -* 提供成功、警告和错误等反馈信息 - -```typescript -proxy?.$modal.msg("默认反馈"); -proxy?.$modal.msgError("错误反馈"); -proxy?.$modal.msgSuccess("成功反馈"); -proxy?.$modal.msgWarning("警告反馈"); -``` - -* 提供成功、警告和错误等提示信息 - -```typescript -proxy?.$modal.alert("默认提示"); -proxy?.$modal.alertError("错误提示"); -proxy?.$modal.alertSuccess("成功提示"); -proxy?.$modal.alertWarning("警告提示"); -``` - -* 提供成功、警告和错误等通知信息 - -```typescript -proxy?.$modal.notify("默认通知"); -proxy?.$modal.notifyError("错误通知"); -proxy?.$modal.notifySuccess("成功通知"); -proxy?.$modal.notifyWarning("警告通知"); -``` - -* 提供确认窗体信息 - -```typescript -proxy?.$modal.confirm('确认信息').then(function() { - ... -}).then(() => { - ... -}).catch(() => {}); -``` - -* 提供遮罩层信息 - -```typescript -// 打开遮罩层 -proxy?.$modal.loading("正在导出数据,请稍后..."); - -// 关闭遮罩层 -proxy?.$modal.closeLoading(); -``` - -### $auth对象 -> `$auth`对象用于验证用户是否拥有某(些)权限或角色,它定义在`plugins/auth.ts`文件中,它有如下方法 - -* 验证用户权限 - -```typescript -// 验证用户是否具备某权限 -proxy?.$auth.hasPermi("system:user:add"); -// 验证用户是否含有指定权限,只需包含其中一个 -proxy?.$auth.hasPermiOr(["system:user:add", "system:user:update"]); -// 验证用户是否含有指定权限,必须全部拥有 -proxy?.$auth.hasPermiAnd(["system:user:add", "system:user:update"]); -``` - -* 验证用户角色 - -```typescript -// 验证用户是否具备某角色 -proxy?.$auth.hasRole("admin"); -// 验证用户是否含有指定角色,只需包含其中一个 -proxy?.$auth.hasRoleOr(["admin", "common"]); -// 验证用户是否含有指定角色,必须全部拥有 -proxy?.$auth.hasRoleAnd(["admin", "common"]); -``` - -### $cache对象 -> `$cache`对象用于处理缓存。我们并不建议您直接使用`sessionStorage`或`localStorage`(vue3版本推荐使用useStorage),因为项目的缓存策略可能发生变化,通过`$cache`对象做一层调用代理则是一个不错的选择。`$cache`提供`session`和`local`两种级别的缓存,如下: - -| 对象名称 | 缓存类型 | -| -------- | ---------------------------------- | -| session | 会话级缓存,通过sessionStorage实现 | -| local | 本地级缓存,通过localStorage实现 | - - -**示例** - -```typescript -// local 普通值 -proxy?.$cache.local.set('key', 'local value') -console.log(proxy?.$cache.local.get('key')) // 输出'local value' - -// session 普通值 -proxy?.$cache.session.set('key', 'session value') -console.log(proxy?.$cache.session.get('key')) // 输出'session value' - -// local JSON值 -proxy?.$cache.local.setJSON('jsonKey', { localProp: 1 }) -console.log(proxy?.$cache.local.getJSON('jsonKey')) // 输出'{localProp: 1}' - -// session JSON值 -proxy?.$cache.session.setJSON('jsonKey', { sessionProp: 1 }) -console.log(proxy?.$cache.session.getJSON('jsonKey')) // 输出'{sessionProp: 1}' - -// 删除值 -proxy?.$cache.local.remove('key') -proxy?.$cache.session.remove('key') -``` - -### $download对象 - -> `$download`对象用于文件下载,它定义在`plugins/download.ts`文件中,它有如下方法 - -* 通过ossId从存储中下载文件 - -``` typescript -// 默认下载方法 -proxy?.$download.oss(ossId); -``` - -* 根据请求地址下载zip包 - -```typescript -const url = '/tool/gen/batchGenCode?tables=' + tableNames; -const name = 'ruoyi'; - -// 默认方法 -proxy?.$download.zip(url, name); -``` diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/component_use.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/component_use.md deleted file mode 100644 index 18886db5..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/component_use.md +++ /dev/null @@ -1,55 +0,0 @@ -# 组件使用 -- - - - -vue 注册组件的两种方式 -在 `@/components` 下创建的.vue文件自动为全局组件,可直接在任意位置使用。 - -### 局部注册 -在对应页使用`components`注册组件。 -```typescript - - - -``` - -### 全局注册 -我们可以使用[ Vue 应用实例](https://cn.vuejs.org/guide/essentials/application.html)的 `.component()` 方法,让组件在当前 Vue 应用中全局可用。 -```typescript -import { createApp } from 'vue' - -const app = createApp({}) - -app.component( - // 注册的名字 - 'MyComponent', - // 组件的实现 - { - /* ... */ - } -) -``` -如果使用单文件组件,你可以注册被导入的 `.vue` 文件: -```typescript -import MyComponent from './App.vue' - -app.component('MyComponent', MyComponent) -``` -`.component()` 方法可以被链式调用: -```typescript -app - .component('ComponentA', ComponentA) - .component('ComponentB', ComponentB) - .component('ComponentC', ComponentC) -``` -全局注册的组件可以在此应用的任意组件的模板中使用: -```Typescript -// 这在当前应用的任意组件中都可用 - - - -``` -所有的子组件也可以使用全局注册的组件,这意味着这三个组件也都可以在彼此内部使用。 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/content_copy.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/content_copy.md deleted file mode 100644 index a4150d72..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/content_copy.md +++ /dev/null @@ -1,4 +0,0 @@ -# 内容复制 -- - - - -文档建设中 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/dev_norm.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/dev_norm.md deleted file mode 100644 index de714a13..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/dev_norm.md +++ /dev/null @@ -1,16 +0,0 @@ -# 开发规范 -- - - - -### 新增view -> 在`@/views`文件下创建对应的文件夹,一般性一个路由对应一个文件, 该模块下的功能就建议在本文件夹下创建一个新文件夹,各个功能模块维护自己的`utils`或`components`组件。 - -### 新增api -> 在`@/api`文件夹下创建本模块对应的api服务。 -> 在api服务同级创建`types.ts`类型声明文件。 - -### 新增组件 -> 在全局的`@/components`写一些全局的组件,如富文本,各种搜索组件,封装的分页组件等等能被公用的组件。 每个页面或者模块特定的业务组件则会写在当前`@/views`下面。 -如:`@/views/system/user/components/xxx.vue`。这样拆分大大减轻了维护成本。 - -### 新增样式 -> 页面的样式和组件是一个道理,全局的`@/style`放置一下全局公用的样式,每一个页面的样式就写在当前 views下面,请记住加上scoped 就只会作用在当前组件内了,避免造成全局的样式污染。 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/dict_use.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/dict_use.md deleted file mode 100644 index 7c6f9ba5..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/dict_use.md +++ /dev/null @@ -1,4 +0,0 @@ -# 使用字典 -- - - - -文档建设中 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/exception_handling.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/exception_handling.md deleted file mode 100644 index 8de87fa2..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/exception_handling.md +++ /dev/null @@ -1,4 +0,0 @@ -# 异常处理 -- - - - -文档建设中 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/icon_use.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/icon_use.md deleted file mode 100644 index 923e66ae..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/icon_use.md +++ /dev/null @@ -1,4 +0,0 @@ -# 使用图标 -- - - - -文档建设中 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/page_cache.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/page_cache.md deleted file mode 100644 index 0531b465..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/page_cache.md +++ /dev/null @@ -1,4 +0,0 @@ -# 页签缓存 -- - - - -文档建设中 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/param_use.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/param_use.md deleted file mode 100644 index 0cd93759..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/param_use.md +++ /dev/null @@ -1,4 +0,0 @@ -# 使用参数 -- - - - -文档建设中 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/permissions_use.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/permissions_use.md deleted file mode 100644 index e18642f4..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/permissions_use.md +++ /dev/null @@ -1,4 +0,0 @@ -# 权限使用 -- - - - -文档建设中 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/request_process.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/request_process.md deleted file mode 100644 index 146f7a8c..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/request_process.md +++ /dev/null @@ -1,65 +0,0 @@ -# 请求流程 -- - - - -### 交互流程 -一个完整的前端UI交互到服务器端处理流程是这样的: - -1. UI 组件交互操作; -2. 调用统一管理的 api service 请求函数; -3. 使用封装的 request.js 发送请求; -4. 获取服务端返回; -5. 更新 data; - -为了方便管理维护,统一的请求处理都放在`@/src/api`文件夹中,并且一般按照`model`维度进行拆分文件,如: -``` -api/ - system/ - user/ - index.ts - types.ts - role/ - index.ts - types.ts - monitor/ - operlog/ - index.ts - types.ts - logininfor/ - index.ts - types.ts - ... -``` -> **提示** -> 其中`@/src/utils/request.ts`是基于 axios 的封装,便于统一处理 POST,GET 等请求参数,请求头,以及错误提示信息等。 它封装了全局request拦截器、response拦截器、统一的错误处理、统一做了超时处理、baseURL设置等。 - -### 请求示例 -```typescript -// @/api/system/user/index.ts -import request from '@/utils/request'; -import { AxiosPromise } from 'axios'; -import { UserQuery, UserVO } from './types'; - -export const listUser = (query: UserQuery): AxiosPromise => { - return request({ - url: '/system/user/list', - method: 'get', - params: query - }); -}; - -// @/views/system/user/index.vue -import api from '@/api/system/user'; -const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value)); -``` -> **提示** -> 如果有不同的`baseURL`,直接通过覆盖的方式,让它具有不同的`baseURL`。 -> ```typescript -> export const listUser = (query: UserQuery): AxiosPromise => { -> return request({ -> url: '/system/user/list', -> method: 'get', -> params: query, -> baseURL: process.env.BASE_API -> }); -> }; -> ``` diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/router_use.md b/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/router_use.md deleted file mode 100644 index b13812bc..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/devdoc/router_use.md +++ /dev/null @@ -1,82 +0,0 @@ -# 路由使用 -- - - - -框架的核心是通过路由自动生成对应导航,所以除了路由的基本配置,还需要了解框架提供了哪些配置项。 -### 路由配置 -```typescript -// 当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 -hidden: true // (默认 false) - -//当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 -redirect: 'noRedirect' - -// 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 -// 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 -// 若你想不管路由下面的 children 声明的个数都显示你的根路由 -// 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 -alwaysShow: true - -name: 'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 -query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 -roles: ['admin', 'common'] // 访问路由的角色权限 -permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 - -meta: { - title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 - icon: 'svg-name' // 设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon - noCache: true // 如果设置为true,则不会被 缓存(默认 false) - breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示(默认 true) - affix: true // 如果设置为true,它则会固定在tags-view中(默认 false) - - // 当路由设置了该属性,则会高亮相对应的侧边栏。 - // 这在某些场景非常有用,比如:一个文章的列表页路由为:/article/list - // 点击文章进入文章详情页,这时候路由为/article/1,但你想在侧边栏高亮文章列表的路由,就可以进行如下设置 - activeMenu: '/article/list' -} -``` -**普通示例** -```json -{ - path: '/system/test', - component: Layout, - redirect: 'noRedirect', - hidden: false, - alwaysShow: true, - meta: { title: '系统管理', icon : "system" }, - children: [{ - path: 'index', - component: (resolve) => require(['@/views/index'], resolve), - name: 'Test', - meta: { - title: '测试管理', - icon: 'user' - } - }] -} -``` -**外链示例** -```json -{ - path: 'http://ruoyi.vip', - meta: { title: '若依官网', icon : "guide" } -} -``` -### 静态路由 -代表那些不需要动态判断权限的路由,如登录页、404、等通用页面,在`@/router/index.ts`配置对应的公共路由。 -### 动态路由 -代表那些需要根据用户动态判断权限并通过addRoutes动态添加的页面,在`@/store/modules/permission.ts`加载后端接口路由配置。 -> **提示** -> * 动态路由可以在系统管理-菜单管理进行新增和修改操作,前端加载会自动请求接口获取菜单信息并转换成前端对应的路由。 -> * 动态路由在生产环境下会默认使用路由懒加载,实现方式参考loadView方法的判断。 -### 常用方法 -想要跳转到不同的页面,使用`router.push`方法 -```Typescript -const router = useRouter(); -router.push({ path: "/system/user" }); -``` -跳转页面并设置请求参数,使用`query`属性 -```Typescript -const router = useRouter(); -router.push({ path: "/system/user", query: {id: "1", name: "若依"} }); -``` -更多使用可以参考[vue-router](https://router.vuejs.org/zh/)官方文档。 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/plus-ui/home.md b/ruoyi-admin/src/main/resources/static/plus-ui/home.md deleted file mode 100644 index b389b5ca..00000000 --- a/ruoyi-admin/src/main/resources/static/plus-ui/home.md +++ /dev/null @@ -1,53 +0,0 @@ -# 项目简介 - ---- - -## 平台简介 - -- 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。 -- 配套后端代码仓库地址 -- [RuoYi-Vue-Plus 5.X(注意版本号)](https://gitee.com/dromara/RuoYi-Vue-Plus) -- [RuoYi-Cloud-Plus 2.X(注意版本号)](https://gitee.com/dromara/RuoYi-Cloud-Plus) - -## 前端运行 - -```bash -# 克隆项目 -git clone https://gitee.com/JavaLionLi/plus-ui.git - -# 安装依赖 -npm install --registry=https://registry.npmmirror.com - -# 启动服务 -npm run dev - -# 推荐使用yarn或pnpm包管理工具 -# 构建测试环境 yarn build:stage -# 构建生产环境 yarn build:prod -# 前端访问地址 http://localhost:80 -``` - -## 后端改造 - -参考后端代码内 `ruoyi-gen/resources/vm/vue/v3/readme.txt` 说明 - -## 内置功能 - -1. 租户管理:配置系统租户,支持 SaaS 场景下的多租户功能。 -2. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 -3. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 -4. 岗位管理:配置系统用户所属担任职务。 -5. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 -6. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 -7. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 -8. 参数管理:对系统动态配置常用参数。 -9. 通知公告:系统通知公告信息发布维护。 -10. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 -11. 登录日志:系统登录日志记录查询包含登录异常。 -12. 在线用户:当前系统中活跃用户状态监控。 -13. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 -14. 代码生成:前后端代码的生成(java、html、xml、sql)支持 CRUD 下载 。 -15. 系统接口:根据业务代码自动生成相关的 api 接口文档。 -16. 服务监控:监视当前系统 CPU、内存、磁盘、堆栈等相关信息。 -17. 缓存监控:对系统的缓存信息查询,命令统计等。 -18. 在线构建器:拖动表单元素生成相应的 HTML 代码。(TS 版本正在开发中。) diff --git a/ruoyi-admin/src/main/resources/static/questions/_sidebar.md b/ruoyi-admin/src/main/resources/static/questions/_sidebar.md deleted file mode 100644 index 2d74fb20..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/_sidebar.md +++ /dev/null @@ -1,34 +0,0 @@ - -- **特别赞助** -- [![输入图片说明](https://foruda.gitee.com/images/1704162419429172656/d0521e59_1766278.png "2024-01-02=>2028-01-02")](http://ccflow.org/?frm=ryPlus) -- [![输入图片说明](https://foruda.gitee.com/images/1705569347386939952/3f187980_1766278.jpeg "2024-01-18=>2025-01-18")](http://www.shuduokeji.com) -- [![输入图片说明](https://foruda.gitee.com/images/1711681233267310022/2ffbcff2_1766278.png "2024-03-29=>2025-03-29")](https://www.jnpfsoft.com/index.html?from=plus-doc) - - - -* **常见问题** - * [Lombok注解爆红](/questions/lombok.md) - * [如何使用Tomcat](/questions/use_tomcat.md) - * [如何使用druid连接池](/questions/use_druid.md) - * [vue与boot整合部署](/questions/deploy_vue.md) - * [导入excel实体类为空](/questions/import_excel.md) - * [如何同步项目更新](/questions/synchronous_update.md) - * [ParseException SQL解析异常](/questions/parse_exception.md) - * [swagger相关问题](/questions/swagger.md) - * [实体bean为空问题](/questions/bean_null.md) - * [Redis 报错 Permission denied](/questions/permission_denied.md) - * [关于HTTPS配置](/questions/https_config.md) - * [放行接口提示认证失败](/questions/identify_fail.md) - * [打包jar运行报错](/questions/jar_run_fail.md) - * [如何指定dubbo注册ip](/questions/dubbo_ip.md) - * [Sentinel页面404问题](/questions/sentinel_404.md) - * [无法读取nacos配置](/questions/nacos_read_fail.md) - * [接口文档对接knife4j](/questions/kinfe4j.md) - * [不支持ST请求](/questions/st_not_support.md) - * [Only one connection receive subscriber allowed](/questions/only_one_subscriber.md) - * [nacos 报错 The Raft Group [naming_instance_metadata]](/questions/nacos_naming_instance_metadata.md) - * [unable to read meta-data for class xxx](/questions/read_metadata.md) - * [JCE cannot authenticate the provider BC](/questions/jce_cannot.md) - * [关于请求响应参数解密](/questions/api_encrypt.md) - * [关于登录调试步骤](/questions/login_step.md) - * [如何对接国产数据库](/questions/domestic_databases.md) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/api_encrypt.md b/ruoyi-admin/src/main/resources/static/questions/api_encrypt.md deleted file mode 100644 index 95b516d4..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/api_encrypt.md +++ /dev/null @@ -1,148 +0,0 @@ -# 关于请求响应参数解密 ---- -## 1:前端加密请求 - -![输入图片说明](https://foruda.gitee.com/images/1717033672316716771/8e30a2f1_4959041.png "屏幕截图") - -通过控制台获取加密结果: - -![输入图片说明](https://foruda.gitee.com/images/1717033792384655437/900a0e0d_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1717033896868612970/55581f0a_4959041.png "屏幕截图") - - -加密密钥: - -``` -PAg/fZzpV/cz0T1fMUJMJo/LEZvwVLb4bZgtCHkbB6FQAJWlLm/RLKtQ5fOo1blMjAkY+9ryWhsAfCqoMPTU4w== -``` - -请求参数加密结果: - -``` -F+Qxq6PzShcudDsUZHhp50lA67eBeTe63x5uGbdm/HJGgcDmjKncUk5VQm0evD8pz1sbmCbmmSl3X1D07K/qgHvP1YhjYSRBJf/M0GTfMkfOZqIkOtvfE5Z6fSFd8RYf6ji/qYxAmCiRmP/uADyJUAoBY1gMi5+zuvyHH3In/FyoFeD0rmJWvO4o4fn3n5GElHMWbP0O/HWPfgHFfg1F7bZQPuf4zAuDKQIqUG3jJTem3O97kAbTWw6lSSuYi1/8tV4cE9rq8SMSjx36/ZLSog== -``` - -### 解密步骤 - -1. 使用配置文件私钥对加密密钥解密 - -```java -// 参数说明: -// requestKey:即请求标头加密密钥 -// privateKey:application.yml 配置文件私钥 -String decryptByRsa = EncryptUtils.decryptByRsa(requestKey, privateKey); -``` - -2. 对步骤一结果进行 Base64 解密,得到 AES 加密密钥 - -```java -String aesPassword = EncryptUtils.decryptByBase64(decryptByRsa); -``` - -3. 使用步骤二得到的密钥,对请求参数进行解密 - -```java -String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword); -``` - -得到解密请求参数(已格式化): - -```json -{ - "tenantId": "000000", - "username": "admin", - "password": "admin123", - "rememberMe": false, - "uuid": "a39962b22c874f60872ef5db1cd811f5", - "code": "5", - "clientId": "e5cd7e4891bf95d1d19206ce24a7b32e", - "grantType": "password" -} -``` - -|参数名|说明| -|---|---| -|tenantId| 租户id | -|username| 用户名 | -|password| 密码 | -|rememberMe| 记住密码 | -|uuid| - | -|code| 验证码结果 | -|clientId| 客户端id(表 sys_client) | -|grantType| 授权类型(表 sys_client) | - -## 2:后端加密响应 - -对请求使用了注解 `@ApiEncrypt(response = true)` - -![输入图片说明](https://foruda.gitee.com/images/1717035066844744866/2286b394_4959041.png "屏幕截图") - -通过控制台获取加密结果: - -![输入图片说明](https://foruda.gitee.com/images/1717035156784270596/156f2aa7_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1717035193189175688/214631e5_4959041.png "屏幕截图") - -加密密钥: - -``` -MXnKYnXcXeFYWKZg8utuhDtbz54cPDcov11E1KT5l19/vMt37d4NhzzwBWnqug72SOgOK5URGaWPJSs9VdaP0Q== -``` - -响应参数加密结果: - -``` -70 O63EMmwvbAyWPqDDmVOGTy+BOQnIVgKInMFNRtp8Zwzs8DEL20VgL2IslYrL8bc1u7lPhYNU/6 Q3iTYebm4EokwiG+styaT+LO3M9bUimggoAGpBTW8gCRF/34 kJaOITSRqYqYcXIJKn73+Gqn7jevyKUHyRXog/3 q/PlBdmUjNiB4gtxlOO/Vm+4 o+0 W4jcEe0xwwzV91+Ze3S6Eu/1 XN21g0iOsYT34emv/vhd9Hy3p5LfJlAHvn96x/c3MQBQUU32uM3Vkk3o6IpVHjJljE64gnGximSwB9vrmMA21xX+fq9HYioumknmDDbaY/JAKh32CDgn5M5hdaIklf08sU38r1IyvipySzrHX+ci9GmOZhP2ttCtoZ7SGvFFbNEuyojssxwxXEmJHAsG/OhIAeRXMUr3+dzDJ++XvvMuMgNJR0BMldNydFAjNOQEszgcVM1QEGwxfW5rElW8VxQaaqPyDATX+y2JrK1vdKxxdI/hF5dGpQMdU4FAEhHIftoIbD/FH4XcWJamZjJpbVtZvTkFYpbhiU7sz9MICSuKwaoSFJ8JGANc0bDdVoWpA8sXi7a27IM0pDzk9gD/FADcFGHXxPYUhENkXiUcnmg5LSdigiY4J6HrqEJdH6zNSwoGubcsXhiPdlB3V0DqcLAHFt+GYj5lcxZeqUAmixGVGCV7gSBWNiyo9/NnXcynA/EIlV3OZIvgzjWxiKzcVJ1HOKoXGEcg3Q54QNh5pCqEa7AtqVkKO7/Ffgg8nSEeCdJPzTV7zmr3n94Hn671OL8A== -``` - -### 解密步骤 - -1. 使用前端配置文件私钥对加密密钥解密 - -```java -// 参数说明: -// responseKey:即响应标头加密密钥 -// privateKey:前端 .env.development | .env.production 配置文件私钥,注意和后端私钥区分 -String decryptByRsa = EncryptUtils.decryptByRsa(responseKey, privateKey); -``` - -2. 对步骤一结果进行 Base64 解密,得到 AES 加密密钥 - -```java -String aesPassword = EncryptUtils.decryptByBase64(decryptByRsa); -``` - -3. 使用步骤二得到的密钥,对响应参数进行解密 - -```java -String decryptBody = EncryptUtils.decryptByAes(responseBody, aesPassword); -``` - -得到解密请求参数(已格式化): - -```json -{ - "code": 200, - "msg": "操作成功", - "data": { - "scope": null, - "openid": null, - "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJzeXNfdXNlcjoxIiwicm5TdHIiOiJjOVNWU1hRRVY4QVhFRkt4b2FrbndSSWxPczd4ajdRZCIsImNsaWVudGlkIjoiZTVjZDdlNDg5MWJmOTVkMWQxOTIwNmNlMjRhN2IzMmUiLCJ0ZW5hbnRJZCI6IjAwMDAwMCIsInVzZXJJZCI6MSwidXNlck5hbWUiOiJhZG1pbiIsImRlcHRJZCI6MTAzLCJkZXB0TmFtZSI6IueglOWPkemDqOmXqCJ9.YuaXPu6eTzJVkLyQC3ekzmPS_jXp50ykaIB2nWy11qM", - "refresh_token": null, - "expire_in": 604799, - "refresh_expire_in": null, - "client_id": "e5cd7e4891bf95d1d19206ce24a7b32e" - } -} -``` - -|参数名|说明| -|---|---| -|scope| 令牌权限 | -|openid| 用户 openid | -|access_token| 授权令牌 | -|refresh_token| 刷新令牌 | -|expire_in| 授权令牌 access_token 的有效期 | -|refresh_expire_in| 刷新令牌 refresh_token 的有效期 | -|clientId| 客户端id(表 sys_client) | \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/bean_null.md b/ruoyi-admin/src/main/resources/static/questions/bean_null.md deleted file mode 100644 index bf04048c..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/bean_null.md +++ /dev/null @@ -1,10 +0,0 @@ -# 实体bean为空问题 -- - - -### 问题排查 - -检查是否存在 `链式调用` 注解 `@Accessors(chain = true)` 删除即可 - -### 原因 -java 规范 set 返回值为 `void` 链式调用 set 返回值为 `this`
-故多数框架底层使用 jdk 工具导致找不到 set 方法
-例如: `easyexcel` `cglib` `mybatis` 等 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/deploy_vue.md b/ruoyi-admin/src/main/resources/static/questions/deploy_vue.md deleted file mode 100644 index 348a1680..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/deploy_vue.md +++ /dev/null @@ -1,13 +0,0 @@ -# 关于vue与boot整合部署 -- - - -* [前端静态资源如何整合到后端访问](https://doc.ruoyi.vip/ruoyi-vue/other/faq.html#前端静态资源如何整合到后端访问) - -3.X 需在 `pom.xml` 增加资源过滤排除 - -```xml - - src/main/resources/页面目录 - - false - -``` diff --git a/ruoyi-admin/src/main/resources/static/questions/domestic_databases.md b/ruoyi-admin/src/main/resources/static/questions/domestic_databases.md deleted file mode 100644 index e79762ba..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/domestic_databases.md +++ /dev/null @@ -1,41 +0,0 @@ -# 如何对接国产数据库 - -> 1. 框架采用 mybatis-plus 几乎支持大部分市面上的数据库且框架内几乎没有sql语句存在 -
-所以不用担心兼容性问题(顶多就是有一些关键字什么的 对接很简单) -
-> 2. 国产数据库大多都兼容主流三大数据库 mysql oracle postgresql -
-例如 达梦兼容oracle 人大金仓兼容mysql oceanbase兼容mysql 等等 - -# 对接方式 - -### 这里用 `达梦` 数据库为例 - -1.首先增加 jdbc依赖包 `vue版本在ruoyi-admin模块下` `cloud版本在ruoyi-common-mybatis模块下` - -![输入图片说明](https://foruda.gitee.com/images/1723288594335994875/216ae8e7_1766278.png "屏幕截图") - -2.在配置文件yml内配置数据库连接 - -![输入图片说明](https://foruda.gitee.com/images/1723288760519808620/3db91ba5_1766278.png "屏幕截图") - -3.sql脚本使用框架内自带的sql文件根据兼容的数据库模式 例如 达梦用oracle的sql脚本 - -![输入图片说明](https://foruda.gitee.com/images/1723289018873298537/4d95c892_1766278.png "屏幕截图") - -4.在代码生成器内 增加对应的数据库生成器依赖 代码生成器使用 anyline 支持几百种数据库只需要增加对应的依赖即可 - -![输入图片说明](https://foruda.gitee.com/images/1723288974693848785/3e8fc61f_1766278.png "屏幕截图") - -这样基本就完成了所有需要做的事可以尝试启动项目了 - -5.如果项目启或者运行动过程中有sql报错 不要慌基本上都是一些关键字引起的 -
-例如 达梦内的`domain`就是关键字 在我们的`SysOssConfig`表内使用`domain`进行自定义的域名存储 -
-我们只需要在`SysOssConfig`实体类的`domain`属性增加一个注解即可解决此问题 -
-**注意: 各种数据库处理关键字的标识符不一样注意替换** - -![输入图片说明](https://foruda.gitee.com/images/1723289232470339283/480d5172_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/questions/dubbo_ip.md b/ruoyi-admin/src/main/resources/static/questions/dubbo_ip.md deleted file mode 100644 index 43cb2346..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/dubbo_ip.md +++ /dev/null @@ -1,18 +0,0 @@ -# 如何指定dubbo注册ip -- - - -## 重点说明 -以下方法指定IP必须是本地有网卡的自己可以访问的IP 不可以随意乱写
-(云服务器公网IP是没有网卡的) - -## 在`nacos`指定协议IP地址(全局生效) -```yml -dubbo: - protocol: - # 指定dubbo协议注册ip - host: 192.168.0.100 -``` - -## docker指定dubbo环境变量(单服务生效) - -![输入图片说明](https://foruda.gitee.com/images/1678981332028792584/7eeef9c5_1766278.png "屏幕截图") - diff --git a/ruoyi-admin/src/main/resources/static/questions/https_config.md b/ruoyi-admin/src/main/resources/static/questions/https_config.md deleted file mode 100644 index e590729e..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/https_config.md +++ /dev/null @@ -1,27 +0,0 @@ -# 关于HTTPS配置 -- - - -### 后端 HTTPS 改造 - -将申请的 `https` 证书放置到 `nginx` 对应目录内
-根据框架 `nginx https` 示例 更改后端代理为 `https`
- -![输入图片说明](https://foruda.gitee.com/images/1678981283573122208/87cf19ad_1766278.png "屏幕截图") - -### 监控中心 与 任务调度中心 改造 - -`监控中心` 与 `任务调度中心` 属于系统管控服务
-应在内网使用 不应该暴漏到外网 也无需配置 `https` - -更改 `系统 -> 菜单管理 -> 监控中心 与 任务调度中心` 菜单配置
-将其改为 `外链访问` 访问路径为 **注意: 如果是外网使用 url需配置为 http://外网ip:端口** - -![输入图片说明](https://foruda.gitee.com/images/1678981287686638349/3734f085_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678981292545287978/f2471f97_1766278.png "屏幕截图") - -`nginx` 配置 `独立的端口` 进行反向代理即可访问(代理编写方式参考后端反向代理) - -### Minio https 改造 - -下方链接包含 minio+nginx 与 minio本身配置https 两种方案
-[终极版minio配置https教程](https://blog.csdn.net/Michelle_Zhong/article/details/126484358) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/identify_fail.md b/ruoyi-admin/src/main/resources/static/questions/identify_fail.md deleted file mode 100644 index 4e258067..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/identify_fail.md +++ /dev/null @@ -1,10 +0,0 @@ -# 放行接口提示认证失败 -- - - -## 可能的原因 -接口放行后不需要token即可访问
-但是没有token也就无法获取用户信息与鉴权 - -## 解决方案 -删除接口上的鉴权注解
-删除接口内获取用户信息功能
-删除数据库实体类 自动注入 `createBy` `updateBy` 因为会获取用户数据 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/import_excel.md b/ruoyi-admin/src/main/resources/static/questions/import_excel.md deleted file mode 100644 index 431863e8..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/import_excel.md +++ /dev/null @@ -1,4 +0,0 @@ -# 关于导入excel实体类为空 -- - - -* 禁止在导入实体使用 `lombok` 链式调用注解 `@Accessors(chain = true)` -* 会导致找不到 `set` 方法无法注入内容 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/jar_run_fail.md b/ruoyi-admin/src/main/resources/static/questions/jar_run_fail.md deleted file mode 100644 index cef9bcd2..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/jar_run_fail.md +++ /dev/null @@ -1,12 +0,0 @@ -# 打包jar运行报错问题 -- - - - -**常见于 windows 平台以命令方式启动** - -windows 平台默认编码为 GBK 所以读取到所有的配置都是乱码 - -## 解决方案 - -需要在命令增加 `-Dfile.encoding=utf-8` 指定文件编码 - -例如: `java -Dfile.encoding=utf-8 -jar ruoyi-xxx.jar` \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/jce_cannot.md b/ruoyi-admin/src/main/resources/static/questions/jce_cannot.md deleted file mode 100644 index a1baf9dd..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/jce_cannot.md +++ /dev/null @@ -1,3 +0,0 @@ -# 问题说明 由于 OracleJDK 强校验加密证书导致 - -解决方案 禁止使用 oraclejdk 更换为其他例如 openjdk \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/kinfe4j.md b/ruoyi-admin/src/main/resources/static/questions/kinfe4j.md deleted file mode 100644 index 5051a782..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/kinfe4j.md +++ /dev/null @@ -1,66 +0,0 @@ -# 对接前声明 - -经常有小伙伴希望可以对接 knife4j - -那么这里将介绍如何使用 框架生成的 openapi 对接 knife4j - -# 如何对接 - -**重点声明: 本框架生成标准openapi结构 如对接后遇到不好用等问题 皆与本框架无关** - -knife4j 本身提供了独立的文档中间件 可以零成本的介入 openapi - -文档地址: https://doc.xiaominfo.com/docs/middleware-sources - -**注意: 此组件应单独搞一个boot项目 不要往框架里做任何代码上的更改** - -使用文档提供的 Cloud 模式 对接咱们框架的 openapi 地址即可完成对接 - -![输入图片说明](https://foruda.gitee.com/images/1685953873117929554/22dce56e_1766278.png "屏幕截图") - -vue版本对接配置如下: - -```yml -knife4j: - enable-aggregation: true - cloud: - enable: true - routes: - - name: 演示模块 - uri: localhost:8080 - location: /v3/api-docs/1.演示模块 - - name: 系统模块 - uri: localhost:8080 - location: /v3/api-docs/2.系统模块 - - name: 代码生成模块 - uri: localhost:8080 - location: /v3/api-docs/3.代码生成模块 -``` - -cloud版本对接配置如下: - -```yml -knife4j: - enable-aggregation: true - cloud: - enable: true - routes: - - name: 演示模块 - uri: localhost:8080 - location: /demo/v3/api-docs - - name: 认证服务 - uri: localhost:8080 - location: /auth/v3/api-docs - - name: 资源服务 - uri: localhost:8080 - location: /resource/v3/api-docs - - name: 系统服务 - uri: localhost:8080 - location: /system/v3/api-docs - - name: 监控服务 - uri: localhost:8080 - location: /monitor/v3/api-docs - - name: 代码生成服务 - uri: localhost:8080 - location: /gen/v3/api-docs -``` \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/login_step.md b/ruoyi-admin/src/main/resources/static/questions/login_step.md deleted file mode 100644 index c1d4fcc9..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/login_step.md +++ /dev/null @@ -1,69 +0,0 @@ -# 关于登录调试步骤 - -## 1:关闭 api 接口加密 - -1. 修改后端配置文件 `application.yml` - -![输入图片说明](https://foruda.gitee.com/images/1717037518256330645/c5a9f0fc_4959041.png "屏幕截图") - -2. 修改前端配置文件 `.env.development` | `.env.production` - -![输入图片说明](https://foruda.gitee.com/images/1717037555118359683/0e73a369_4959041.png "屏幕截图") - -## 2:登录参数 - -![输入图片说明](https://foruda.gitee.com/images/1717038201634120005/e02882d3_4959041.png "屏幕截图") - -|参数名|说明| -|---|---| -|tenantId| 租户id | -|username| 用户名 | -|password| 密码 | -|rememberMe| 记住密码 | -|uuid| - | -|code| 验证码结果 | -|clientId| 客户端id(表 sys_client) | -|grantType| 授权类型(表 sys_client) | - -## 3:使用接口文档调试 - -### 3.1:使用接口文档请求 - -1. 配置接口文档([参考文档](/ruoyi-vue-plus/framework/association/doc)) -2. 请求接口 `http://localhost:8080/auth/login` - -![输入图片说明](https://foruda.gitee.com/images/1717039200581756307/97efbc9c_4959041.png "屏幕截图") - -### 3.2:使用 idea 请求 - -![输入图片说明](https://foruda.gitee.com/images/1717039459944753490/040d2b9d_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1717039534863944601/df91df67_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1717039598067298052/cc9fe61b_4959041.png "屏幕截图") - -### 3.3:获取验证码以及 uuid - -!> 验证码以及 uuid 获取方式: Redis | 控制台 - -方式一、Redis: - -![输入图片说明](https://foruda.gitee.com/images/1717040260329977942/42f7ed62_4959041.png "屏幕截图") - -> **如果没有验证码相关 key,说明已经过期被清理了,去前端页面刷新一下即可。** - -方式二、控制台: - -![输入图片说明](https://foruda.gitee.com/images/1717040428227070908/1ef7562a_4959041.png "屏幕截图") - -### 3.4:关闭验证码 - -如果嫌验证码太麻烦,可以关闭,修改后端配置文件 `application.yml` - -![输入图片说明](https://foruda.gitee.com/images/1717040533266608114/054fd984_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1717040745251872562/374267e8_4959041.png "屏幕截图") - -请求参数: - -![输入图片说明](https://foruda.gitee.com/images/1717040762860943102/81c9b44a_4959041.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/lombok.md b/ruoyi-admin/src/main/resources/static/questions/lombok.md deleted file mode 100644 index 47125153..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/lombok.md +++ /dev/null @@ -1,4 +0,0 @@ -# 关于lombok注解爆红 -- - - -* 已知 lombok 插件与 idea中文插件 存在兼容性问题 -* 移除中文插件或手动关闭idea检查 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/nacos_naming_instance_metadata.md b/ruoyi-admin/src/main/resources/static/questions/nacos_naming_instance_metadata.md deleted file mode 100644 index ae35d937..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/nacos_naming_instance_metadata.md +++ /dev/null @@ -1,35 +0,0 @@ -# nacos 报错 The Raft Group [naming_instance_metadata] -- - - -## Nacos 服务下线报错问题 - -问题描述: - -Nacos 服务管理 > 服务列表 > 详情 > 下线 报错 - - - -报错详情: - -``` -caused: errCode: 500, errMsg: do metadata operation failed ;caused: com.alibaba.nacos.consistency.exception.ConsistencyException: The Raft Group [naming_instance_metadata] did not find the Leader node;caused: The Raft Group [naming_instance_metadata] did not find the Leader node; -``` - - - -解决方案: - -**删除 Nacos 根目录下 data 文件夹下的 protocol 文件夹** - -(推荐使用全局搜索软件查询,windows 环境根目录一般在 C:\Users\用户名\nacos) - - - -问题原因: - -> Nacos 采用 raft 算法来计算 Leader,并且会记录上次启动的集群地址,所以当我们自己的服务器 IP 改变时(网络环境不稳定,如WIFI, IP 地址也经常变化),导致 raft 记录的集群地址失效,导致选 Leader 出现问题。 - - - -参考目录: - -[解决疑难问题之服务下线报:The Raft Group naming_instance_metadata\] did not find the Leader node; - 嘉美祥瑞 - 博客园 (cnblogs.com)](https://www.cnblogs.com/whl-jx911/p/16736625.html) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/nacos_read_fail.md b/ruoyi-admin/src/main/resources/static/questions/nacos_read_fail.md deleted file mode 100644 index f6cc36d9..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/nacos_read_fail.md +++ /dev/null @@ -1,15 +0,0 @@ -# 无法读取nacos配置 -- - - -### 检查 `group` 与 `namespace` 是否一致 - -如果未使用框架自带 `ry-config.sql` 文件进行配置 会导致 `namespace` 不一致 无法查询配置 - -### 检查 `8848` `9848` `9849` 端口是否开启可用 - -### 检查配置文件名是否一致 例如: "xxx" 与 "xxx.yml" 的区别 - -### 检查是否手动改过 `nacos` 数据库数据 - -`nacos` 数据表层层关联 不要自作聪明手动改数据库 - -已经改过的 需要重新导入 `ry-config.sql` 之后在页面进行改数据操作 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/only_one_subscriber.md b/ruoyi-admin/src/main/resources/static/questions/only_one_subscriber.md deleted file mode 100644 index f8d690e1..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/only_one_subscriber.md +++ /dev/null @@ -1,11 +0,0 @@ -# Only one connection receive subscriber allowed -- - - -## 问题原因 -**经多人反馈 共同点为全都是做`小程序开发`使用的`uniapp`发送的网络请求而出现这种问题** - -`uniapp` 错误设置 `Content-Type` 将所有请求类型全都设置成了 `json` 导致不该读body的请求也读取了body 最终导致报错 - -## 解决方案 - -方案1: 升级 1.4.0 已经对这种不合规发的请求做了兼容处理(被迫)
-方案2: `uniapp` 内的请求设置正确的 `Content-Type` diff --git a/ruoyi-admin/src/main/resources/static/questions/parse_exception.md b/ruoyi-admin/src/main/resources/static/questions/parse_exception.md deleted file mode 100644 index 67a7a114..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/parse_exception.md +++ /dev/null @@ -1,40 +0,0 @@ -# ParseException SQL解析异常 -- - - -## 异常内容 - -`net.sf.jsqlparser.parser.ParseException: Encountered unexpected token:` - -![输入图片说明](https://foruda.gitee.com/images/1678981169309778625/a17ff852_1766278.png "屏幕截图") - -此异常为 SQL 解析异常, 应检查 SQL 语句内是否包含 SQL 关键字 - -异常通常都会提供坐标 - -![输入图片说明](https://foruda.gitee.com/images/1678981173813116217/a6f9ee32_1766278.png "屏幕截图") - -检查报错 SQL 相关坐标位置 - -![输入图片说明](https://foruda.gitee.com/images/1678981179153564043/bf4912b4_1766278.png "屏幕截图") - -## 异常由来 -由 Mybatis-Plus 拦截器进行 SQL 解析导致
-常见拦截器导致问题 `TenantLineInnerInterceptor` `DataPermissionInterceptor` - -## 解决方案 - -> 将关键字增加标识符区别开 - -1.实体类字段处理(以下仅限于mysql 其他数据库方法各不相同) - -![输入图片说明](https://foruda.gitee.com/images/1678981183515542682/fccd85ad_1766278.png "屏幕截图") - -2.自定义 SQL 或 XML 处理 - -![输入图片说明](https://foruda.gitee.com/images/1678981187926917963/38437edb_1766278.png "屏幕截图") - -3.Mapper排除 -> 查看具体使用了哪些拦截器导致问题 使用忽略注解依次进行排除即可 - -![输入图片说明](https://foruda.gitee.com/images/1678981192902044584/fb1c41eb_1766278.png "屏幕截图") - - diff --git a/ruoyi-admin/src/main/resources/static/questions/permission_denied.md b/ruoyi-admin/src/main/resources/static/questions/permission_denied.md deleted file mode 100644 index 76d955db..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/permission_denied.md +++ /dev/null @@ -1,15 +0,0 @@ -# Redis 报错 Permission denied -- - - -### 此报错为无权限 - -需确保 redis 数据存储文件夹具有写权限 - -```shell -chmod 777 /docker/redis/data -``` - -没有写权限无法对数据进行存储 - -### 关于RDB报错 `/etc` 无权限问题 - -增加redis密码校验 无密码导致配置不安全 diff --git a/ruoyi-admin/src/main/resources/static/questions/read_metadata.md b/ruoyi-admin/src/main/resources/static/questions/read_metadata.md deleted file mode 100644 index b91165fd..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/read_metadata.md +++ /dev/null @@ -1,11 +0,0 @@ -# unable to read meta-data for class xxx -- - - -## 问题原因 - -此问题由改包名导致框架内组件 spring 的 spi 配置文件包名被改乱套 - -## 解决方案 - -更正组件包下的 spring spi 配置文件内的类包名 - -![输入图片说明](https://foruda.gitee.com/images/1668608724503582409/50a77b4b_1766278.jpeg "test.jpg") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/sentinel_404.md b/ruoyi-admin/src/main/resources/static/questions/sentinel_404.md deleted file mode 100644 index 7e51fb58..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/sentinel_404.md +++ /dev/null @@ -1,8 +0,0 @@ -# Sentinel页面404问题 -- - - -## 原因 -检查 `webapp` 目录是否为资源目录 低版本 `idea` 不会自动解析 -## 解决方案 -手动设置 `webapp` 为资源目录即可
- -![输入图片说明](https://foruda.gitee.com/images/1678981354612151228/52f2a886_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/questions/st_not_support.md b/ruoyi-admin/src/main/resources/static/questions/st_not_support.md deleted file mode 100644 index a3012805..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/st_not_support.md +++ /dev/null @@ -1,11 +0,0 @@ -# 不支持ST请求 -- - - -## 问题原因 -**经多人反馈 共同点为全都是做`小程序开发`使用的`uniapp`发送的网络请求而出现这种问题** - -`uniapp` 错误设置 `Content-Type` 将所有请求类型全都设置成了 `json` 导致不该读body的请求也读取了body 最终导致报错 - -## 解决方案 - -方案1: 升级 1.4.0 已经对这种不合规发的请求做了兼容处理(被迫)
-方案2: `uniapp` 内的请求设置正确的 `Content-Type` \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/swagger.md b/ruoyi-admin/src/main/resources/static/questions/swagger.md deleted file mode 100644 index 8b0a6e01..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/swagger.md +++ /dev/null @@ -1,3 +0,0 @@ -# 框架内没有任何swagger - -想使用接口文档功能 请查看框架接口文档说明 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/synchronous_update.md b/ruoyi-admin/src/main/resources/static/questions/synchronous_update.md deleted file mode 100644 index 70803f58..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/synchronous_update.md +++ /dev/null @@ -1,3 +0,0 @@ -# 如何同步项目更新 -- - - -参考文章: [关于如何同步更新开源项目](https://blog.csdn.net/qq_31360283/article/details/118345795) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/use_druid.md b/ruoyi-admin/src/main/resources/static/questions/use_druid.md deleted file mode 100644 index 77f3f605..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/use_druid.md +++ /dev/null @@ -1,20 +0,0 @@ -# 如何使用druid连接池 -- - - -## 为何移除druid - -性能低下 bug频发 内含fastjson问题众多 监控不支持集群(鸡肋) 不支持一些高版本数据库 社区活跃度冰点 - -### 性能对比图 -![输入图片说明](https://foruda.gitee.com/images/1667888745256002635/1bbd3481_1766278.png "屏幕截图") -### 包大小对比图 -![输入图片说明](https://foruda.gitee.com/images/1667888760611300040/87af8d82_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1667888766932068690/7b379298_1766278.png "屏幕截图") - -## 为何使用hikari(中文: 光) - -spring默认自带 代码量少结构简单 稳定可靠 性能突出(自行百度一堆测评) - -## 参考提交记录反向操作即可 - -https://gitee.com/dromara/RuoYi-Vue-Plus/commit/1f42bd3d22c104aaa2d780c20a555b5e467858bf
-https://gitee.com/dromara/RuoYi-Vue-Plus/commit/a63abbf268e4c0a60344f63b5cba828a1347e178 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/questions/use_tomcat.md b/ruoyi-admin/src/main/resources/static/questions/use_tomcat.md deleted file mode 100644 index 113d239f..00000000 --- a/ruoyi-admin/src/main/resources/static/questions/use_tomcat.md +++ /dev/null @@ -1,9 +0,0 @@ -# 关于如何使用Tomcat -- - - -### 查看ruoyi-framework模块的pom.xml文件,根据注释更改依赖 - -![输入图片说明](https://foruda.gitee.com/images/1678981109106652929/0803004d_1766278.png "屏幕截图") - -### 查看ruoyi-admin模块中的application.yml文件,根据注释更改配置 - -![输入图片说明](https://foruda.gitee.com/images/1678981112652965294/dda8df86_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/_sidebar.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/_sidebar.md deleted file mode 100644 index 4580b86c..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/_sidebar.md +++ /dev/null @@ -1,70 +0,0 @@ - -- **特别赞助** -- [![输入图片说明](https://foruda.gitee.com/images/1704162419429172656/d0521e59_1766278.png "2024-01-02=>2028-01-02")](http://ccflow.org/?frm=ryPlus) -- [![输入图片说明](https://foruda.gitee.com/images/1705569347386939952/3f187980_1766278.jpeg "2024-01-18=>2025-01-18")](http://www.shuduokeji.com) -- [![输入图片说明](https://foruda.gitee.com/images/1711681233267310022/2ffbcff2_1766278.png "2024-03-29=>2025-03-29")](https://www.jnpfsoft.com/index.html?from=plus-doc) - - -* **简介** - * [项目简介](/ruoyi-cloud-plus/home.md) - * [更新日志](/ruoyi-cloud-plus/changlog.md) -* **快速开始** - * [项目初始化](/ruoyi-cloud-plus/quickstart/init.md) - * [1.X项目初始化](/ruoyi-cloud-plus/quickstart/1.Xinit.md) - * [工作流初始化](/ruoyi-cloud-plus/quickstart/worker_init.md) - * [idea环境配置](/ruoyi-cloud-plus/quickstart/idea_environment.md) - * [应用部署](/ruoyi-cloud-plus/quickstart/deploy.md) - * [扩展项目](/ruoyi-cloud-plus/quickstart/extend_project.md) - * [搭建SnailJob调度中心](/ruoyi-cloud-plus/quickstart/snail_job_init.md) - * [(废弃)搭建PowerJob调度中心](/ruoyi-cloud-plus/quickstart/power_job_init.md) -* **框架功能** - * [项目结构](/ruoyi-cloud-plus/framework/tree.md) - * [软件架构图](/ruoyi-cloud-plus/framework/architecture_diagram.md) - * 框架相关 - * [创建新服务](/ruoyi-cloud-plus/framework/association/new_module.md) - * [修改包名](/ruoyi-cloud-plus/framework/association/update_package_name.md) - * [接口文档](/ruoyi-cloud-plus/framework/association/doc.md) - * [修改应用路径](/ruoyi-cloud-plus/framework/association/update_url.md) - * [国际化](/ruoyi-cloud-plus/framework/association/i18n.md) - * [多团队开发](/ruoyi-cloud-plus/framework/association/collaboration.md) - * [内网鉴权](/ruoyi-cloud-plus/framework/association/inner_authentication.md) - * 基础功能 - * [系统用户相关](/ruoyi-cloud-plus/framework/basic/user.md) - * [权限控制](/ruoyi-cloud-plus/framework/basic/permissions_control.md) - * [导出功能](/ruoyi-cloud-plus/framework/basic/export.md) - * [导入功能](/ruoyi-cloud-plus/framework/basic/import.md) - * [参数校验](/ruoyi-cloud-plus/framework/basic/param_check.md) - * [代码生成](/ruoyi-cloud-plus/framework/basic/code_generate.md) - * [分页功能](/ruoyi-cloud-plus/framework/basic/page.md) - * [OSS功能](/ruoyi-cloud-plus/framework/basic/oss.md) - * [数据权限](/ruoyi-cloud-plus/framework/basic/permissions.md) - * [网关路由与放行](/ruoyi-cloud-plus/framework/basic/router_release.md) - * [多租户功能](/ruoyi-cloud-plus/framework/basic/tenant.md) - * [第三方授权功能](/ruoyi-cloud-plus/framework/basic/social.md) - * [客户端管理功能](/ruoyi-cloud-plus/framework/basic/client.md) - * 扩展功能 - * [多数据源](/ruoyi-cloud-plus/framework/extend/dynamic_datasource.md) - * [短信模块](/ruoyi-cloud-plus/framework/extend/sms.md) - * [邮件功能](/ruoyi-cloud-plus/framework/extend/mail.md) - * [防重幂等](/ruoyi-cloud-plus/framework/extend/idempotent.md) - * [数据脱敏](/ruoyi-cloud-plus/framework/extend/sensitive.md) - * [API加解密](/ruoyi-cloud-plus/framework/extend/api_encrypt.md) - * [数据加解密](/ruoyi-cloud-plus/framework/extend/encrypt.md) - * [翻译功能](/ruoyi-cloud-plus/framework/extend/translation.md) - * [WebSocket功能](/ruoyi-cloud-plus/framework/extend/websocket.md) - * 功能说明 - * [事务相关](/ruoyi-cloud-plus/framework/explain/transaction.md) - * [单元测试](/ruoyi-cloud-plus/framework/explain/test.md) - * [主键使用说明](/ruoyi-cloud-plus/framework/explain/key.md) - * [关于多表查询](/ruoyi-cloud-plus/framework/explain/about_join.md) -* **扩展功能** - * [ELK搭建](/ruoyi-cloud-plus/extend-function/elk.md) - * [ES搜索引擎](/ruoyi-cloud-plus/extend-function/es.md) - * [RabbitMQ搭建](/ruoyi-cloud-plus/extend-function/rabbitmq.md) - * [RocketMQ搭建](/ruoyi-cloud-plus/extend-function/rocketmq.md) - * [Kafka搭建](/ruoyi-cloud-plus/extend-function/kafka.md) - * [Nacos集群搭建](/ruoyi-cloud-plus/extend-function/nacos.md) - * [SkyWalking搭建与集成](/ruoyi-cloud-plus/extend-function/skywalking.md) - * [Prometheus+Grafana搭建](/ruoyi-cloud-plus/extend-function/prometheus_grafana.md) - * [Sharding-Proxy搭建分库分表](/ruoyi-cloud-plus/extend-function/shardingproxy.md) - * [对接MaxKey单点登录](/ruoyi-cloud-plus/extend-function/maxkey.md) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/changlog.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/changlog.md deleted file mode 100644 index 368844cb..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/changlog.md +++ /dev/null @@ -1,1385 +0,0 @@ -# 更新日志 -- - - - -## v2.2.1 - 2024-08-26 - -### 重大改动 - -* 增加 ruoyi-common-sse 模块 支持SSE推送 比ws更轻量更稳定的推送 -* 增加 springboot snailjob 等 actuator 账号密码认证 杜绝内外网信息泄漏问题 -* 增加 重构代码生成器 集成anyline开源框架 支持400+种数据库适配 - -### 依赖升级 - -* update springboot 3.2.6 => 3.2.9 -* update snailjob 1.0.1 => 1.1.2 -* update mapstruct-plus 1.4.3 => 1.4.4 -* update hutool 5.8.27 => 5.8.31 解决hutool不兼容jakarta问题 -* update anyline 8.7.2-20240808 -* update sms4j 3.2.1 => 3.3.2 -* update redisson 3.31.0 => 3.34.1 -* update mapstruct-plus 1.3.6 => 1.4.3 -* update lombok 1.18.32 => 1.18.34 -* update easyexcel 3.3.4 => 4.0.2 -* update springdoc 2.5.0 => 2.6.0 -* update flowable 7.0.0 => 7.0.1 - -### cloud内容更新 - -* update springcloud 2023.0.2 => 2023.0.3 -* update springcloud-alibaba 2023.0.1.0 => 2023.0.1.2 -* update redis 6.2.7 => 6.2.12 解决订阅key报错问题 -* update 优化 seata dockerfile 增加环境变量 -* update 优化 增加日志处理器顺序说明 -* update 优化 使用 seata-server 官方依赖简化seata集成方式 -* update 优化 屏蔽 sentinel 心跳日志 -* update 优化 dubbo元数据注册redis支持timeout(注意时间必须使用数字) -* update 优化 调整sentinel日志级别 屏蔽心跳日志 -* update 优化 sky-agent 默认开启即使连不上服务端也跟踪配置 (有些人就爱这么用) -* update 优化 kafka 自动创建 topic 部分人副本数不够报错问题 -* add 增加 nacos sentinel snailjob 健康检查 actuator 账号密码认证 -* fix 修复 dubbo redis元数据中心 获取监听器null问题 -* fix 修复 nacos sentinel seata 不适配新版undertow问题 先换回tomcat -* fix 修复 依赖漏洞 限制部分依赖版本 -* fix 修复 由于alibaba sentinel 初始化机制变更导致的无法连接问题 -* fix 修复 dubbo 日志输出异常判断错误 -* remove 删除 kafka-streams 所有人都不会用也不学怎么用 删除了事 - -### 功能更新 - -* update 优化 去除日志部署环境判断 通过日志级别控制 -* update 优化 忽略租户与忽略数据权限支持嵌套使用(感谢 amadeus5201) -* update 优化 租户相关controller 增加租户开关配置控制是否注册 -* update 优化 移除 alibaba ttl 与线程池搭配有问题(可传递但无法清除与更新) -* update 优化 个人中心编辑 忽略数据权限 -* update 优化 兼容部分用户不想给用户分配角色与部门的场景 -* update 优化 租户套餐重名校验 -* update 优化 部门下存在岗位不允许删除 -* update 优化 角色编辑状态未校验问题 -* update 优化 用户脱敏增加编辑权限标识符 -* update 优化 代码生成器 自动适配oss翻译 -* update 优化 临时升级 undertow 版本 解决虚拟线程溢出问题 -* update 优化 支持通过配置文件关闭工作流 -* update 优化 增加mybatis-plus填充器兜底策略 -* update 优化 TenantSpringCacheManager 处理逻辑 -* update 优化 角色权限判断 -* update 优化 增加删除标志位常量优化查询代码 -* update 优化 监控使用独立web依赖 -* update 优化 更多脱敏策略(感谢 hemengji) -* update 优化 设置nginx sse相关代理参数 -* update 优化 调整默认推送使用SSE -* update 优化 Monitor监控服务通知分类打印(感谢 AprilWind) -* update 优化 限流注解 又写key又不是表达式的情况 -* update 优化 WorkflowUtils查询用户信息发送消息未查询邮件和手机号(感谢 yanzy) -* update 优化 注释掉其他数据库 jdbc 依赖 由用户手动添加 -* update 优化 oracle snailjob 兼容低版本oracle索引名称长度限制 -* update 优化 数据权限支持通过菜单标识符获取数据所有权 -* update 优化 数据权限支持自定义连接符 -* update 优化 TestDemo 删除前校验数据权限 -* update 优化 更换docker镜像底层系统 避免无字体情况 - -### 问题修复 - -* fix 修复 三方登录构建去除无用代码 -* fix 修复 多线程对同一个session发送ws消息报错问题 -* fix 修复 依赖漏洞 限制部分依赖版本 -* fix 修复 excel 基于其他字段 合并错误问题 -* fix 修复 一级缓存key未区分租户问题 -* fix 修复 id字符串格式转换错误问题 -* fix 修复 登出无法正确删除对应的租户数据问题 -* fix 修复 登录错误锁定不区分租户问题 -* fix 修复 转换模型缺少分类字段 -* fix 修复 权限标识符处理未设置成功状态问题 -* fix 修复 无法导入 bpmn 类型文件问题 - -### 前端改动 - -* update element-plus 2.7.5 => 2.7.8 -* update vue 3.4.25 => 3.4.34 -* update vite 5.2.10 => 5.2.12 -* add 增加 使用 vueuse 编写 sse 推送功能 -* update 优化 使用匹配模式简化预编译配置 -* update 优化 时间搜索组件统一 -* update 优化 oss 配置按钮 使用ossConfig权限标识符与oss权限分离 -* update 优化 类型报错问题 -* update 优化 切换租户后刷新首页 -* update 优化 实现表格行选中切换 -* update 优化 使用 vueuse 重构 websocket 实现 -* update 优化 代码生成器编辑页禁用缓存 防止同步后页面不更新问题 -* update 优化 调整默认推送使用SSE -* fix 修复 租户套餐导出路径错误问题 -* fix 修复 登出后重新登录 sse推送报错问题 - - -## v2.2.0 - 2024-07-09 - -### 重大更新 - -* [重大更新] 使用 caffeine 重构 PlusSaTokenDao 层实现 减少将近90%的redis查询提高性能 -* [重大更新] 新增 PlusCacheWrapper 装饰器 为 SpringCache 增加本地缓存减少redis查询提高性能 -* [重大更新] 升级 awsS3 到2.X版本 支持异步与自动分片上传下载(感谢 AprilWind) -* [重大更新] 新增 flowable 工作流功能(感谢 May) -* [重大更新] 新增 snailjob 调度中心 移除 powerjob (投诉的人太多) (感谢 dhb52) -* [重大更新] 重构 将spring-cloud-stream改为普通的mq依赖用法(感谢 Xbhog) -* [重大更新] 新增 ruoyi-common-bus 消息总线组件 基于MQ跨服务投递事件消息 - -### 依赖升级 - -* update springboot 3.1.7 => 3.2.6 支持虚拟线程 -* update springboot-admin 3.1.8 => 3.2.3 -* update springdoc 2.2.0 => 2.5.0 -* update redisson 3.24.3 => 3.29.0 支持虚拟线程 -* update hutool 5.8.22 => 5.8.26 -* update dynamic-ds 4.2.0 => 4.3.0 -* update mybatis-plus 3.5.4 => 3.5.7 修复与boot代码冲突问题 -* update lock4j 2.2.5 => 2.2.7 消除启动警告 -* update sms4j 2.2.0 => 3.2.1 支持自定义配置key 可用于多厂商多租户等 -* update mapstruct-plus 1.3.5 => 1.3.6 -* update easyexcel 3.3.3 => 3.3.4 -* update lombok 1.18.30 => 1.18.32 -* update satoken 1.37.0 -> 1.38.0 -* update aws-oss 1.12.600 => 2.25.15 - -### 功能更新 - -* update 优化 StreamUtils 抽取 findFirst findAny 方法 -* update 优化 更新使用 Spring 官方推荐 JDK -* update 优化 webscoket 配置与异常拦截 -* update 优化 isTenantAdmin 空校验 -* update 优化 修改路由name命名规则(感谢 玲娜贝er) -* update 优化 大数据量下join卡顿问题 使用子查询提高性能 -* update 优化 用户ID查询角色列表(感谢 AprilWind) -* update 优化 获取用户账户(感谢 AprilWind) -* update 优化 租户列表接口 避免登录之后列表被域名过滤 -* update 优化 三方登录不同域名获取不到租户id问题 -* update 优化 获取aop代理的方式 减少与其他使用aop的功能冲突的概率 -* update 优化 临时解决 spring 启动报 warn 问题 -* update 优化 移除表单构建菜单(没有可用组件 用处不大以后再考虑) -* update 优化 修改用户信息接口(感谢 AprilWind) -* update 优化 切换动态租户 默认线程内切换(如需全局 手动传参) -* update 优化 适配最新前端代码生成模板 -* update 优化 代码生成 el-radio 标签过期属性 -* update 优化 文件下载(使用对流传递 降低内存使用量)(感谢 秋辞未寒) -* update 优化 去除gc日志参数(有需要自己加) -* update 优化 拆分异常处理器 -* update 优化 常规web异常状态码 -* update 优化 设置静态资源路径防止所有请求都可以访问静态资源 -* update 优化 代码生成表导入 排除工作流相关表 -* update 优化 redis 对Long值的存储类型不同问题 -* update 优化 去除加密请求类型限制 -* update 优化 mp多租户插件注入逻辑 -* update 优化 移除删表语句 用户自行处理 -* update 优化 RedisUtils 支持忽略租户 -* update 更新 ip地址 xdb文件 -* update 优化 新增修改菜单权限字符校验 -* update 优化 验证码背景色改为浅灰色 -* update 优化 更新 mybatis 多包扫描配置 -* update 优化 RateLimiter 注解使用体验(感谢 ly-chn) -* update 优化 GET 方法响应体支持加密 -* update 优化 excel 单元格合并可以基于注解选择需要依赖哪些字段(感谢 司猫子) -* update 优化 OssFactory 获取实例锁性能(感谢 fanc) -* update 优化 登录消息 支持集群发送 -* update 优化 数据权限 使用预扫描mapper注解提升代码性能 -* update 优化 数据加密 使用预扫描实体类提升代码性能(感谢 老马) -* update 优化 Async 针对虚拟线程配置 与其他注意事项注释 -* update 优化 框架整体sql提高查询性能 -* update 优化 将p6spy配置文件统一放置到 common-mybatis 插件包内 -* update 优化 使用翻译注解简化用户查询 调整用户查询逻辑 - - -### 新增功能 - -* add 新增 SMS异常处理器(感谢 AprilWind) -* add 新增 在线设备管理(个人中心)(感谢 AprilWind) -* add 新增 岗位编码与部门编码 并将岗位放到部门下(感谢 秋辞未寒) -* add 新增 分布式锁Lock4j异常拦截(感谢 AprilWind) -* add 新增 BaseMapperPlus提供一组可选是否抛出异常的selectVoOne方法(感谢 秋辞未寒) -* add 新增 用户、部门、角色、岗位 下拉选接口与代码实现优化 -* add 新增 JustAuth 整合 TopIam 单点登录(感谢 马铃薯头) -* add 新增 StringUtils.isVirtual 方法 -* add 新增 正则工具类 字符串提取 字符串校验 - -### 问题修复 - -* fix 修复 isLogin 方法抛异常无法正常返回值问题 -* fix 修复 spring路径规则 导致 actuator 被特殊方式访问问题 -* fix 修复token无效时关闭ws(感谢 AprilWind) -* fix 修复 oss未使用租户 拼接租户id null问题 -* fix 修复 用户昵称修改后未清除对应缓存问题 -* fix 修复 文件上传图片预览问题 -* fix 修复 三方账号可以被同一个用户多次绑定问题 -* fix 修复 兼容redis5.0出现的问题 -* fix 修复 字典键值可重复配置问题 -* fix 修复 部分浏览器无法获取加密响应头问题 -* fix 修复 用户未设置部门 登录报错问题 -* fix 修复 全局异常处理器 空指针null问题 -* fix 修复 excel 表达式字典 下拉框导出格式错误 -* fix 修复 InjectionMetaObjectHandler 已存在数据依旧会获取用户信息报异常问题 -* fix 修复 关闭租户功能 三方登录报错问题 -* fix 修复 部门树排序问题 -* fix 修复 CryptoFilter 代码逻辑问题 - -### 前端改动 - -* update 升级 element vite 版本 最低nodejs版本提升到18.18.0 -* update 优化 更改客户端状态接口 使用clientId传参 -* update 优化 ws开关改为常开(vite5修复了崩溃bug) -* update 优化 移除cjs -* update 优化 对Volar支持 -* update 优化 富文本组件,修复两个组件上传图片位置错乱问题 -* update 优化 request请求类判断请求头方式 -* update 优化 密码校验策略增加非法字符限制 -* update 优化 支持全局开启或关闭接口加密功能 -* update 优化 暗黑模式,增加vxe的暗黑模式 -* update 优化 首页打开topNav不展开菜单问题 -* update 优化 el-select 与 el-input 全局样式 -* update 优化 跟密码相关的默认前端关闭防重功能 -* add 新增 社交登录整合 TopIam -* add 新增 图片上传组件增加压缩功能支持,可自行开关 -* add 新增 vxe-table依赖支持 -* add 新增 全局用户选择组件 -* add 新增 工作流相关页面与组件 -* add 新增 使用bpmnjs流程预览 -* add 新增 在线登录设备管理(感谢 AprilWind) -* add 新增 用户选择角色时 可搜索功能(感谢 追梦稻草人Li) -* fix 修复 登录失效,重新登录丢失参数问题(感谢 爱宇阳) -* fix 修复 websocket 非index页面刷新无法重连问题 -* fix 修复 全局属性找不到的问题(感谢 ahaos) -* fix 修复 vue 类型识别问题 -* fix 修复 富文本编辑器 单页面多实例图片混乱问题 -* fix 修复 i18n无感刷新问题 -* fix 修复 文件预览大写后缀不展示的问题(感谢 北桥) -* fix 修复 面板因为min width原因收缩不全 -* fix 修复 移动端下 无法展开菜单问题 -* fix 修复 菜单搜索下方出现白色区域 -* fix 修复 el-tag标签类型不一致问题 -* fix 修复 角色必填*号 - -### 微服务修改 - -* update springcloud 2022.0.4 => 2023.0.2 -* update springcloud-alibaba 2022.0.0.0 => 2023.0.1.0 -* update dubbo 3.2.7 => 3.2.14 -* update easy-es 2.0.0-beta4 => 2.0.0 正式版 -* update nacos 2.2.1 => 2.3.2 默认开启nacos服务端授权认证 (感谢 OldDriver9527) -* update rocketmq 4.9.4 => 5.2.0 docker镜像升级 -* update kafka 3.2.0 => 3.6.2 docker镜像升级 -* update rabbitmq 3.10.6 => 3.13.3 docker镜像升级 -* update sentinel 1.8.6 => 1.8.8 -* update skywalking 9.3.0 => 9.7.0 -* update skywalking-agent 8.16.0 => 9.2.0 -* update 优化 dubbo 使用 redis 作为元数据中心管理 支持过期时间 避免过期数据堆积 解放nacos存储空间 -* update 优化 调整配置文件语法 -* update 优化 使用spring工具自定义dubbo ip获取方法(针对多网卡ip获取不正确问题) -* update 优化 common-dubbo 删除无用依赖 -* update 优化 去除重复的扫描器 @EnableDubbo 会自行扫描包 -* update 优化 加密组件 mp依赖改为可选 -* update 优化 mybatis依赖设置为可选依赖 避免出现不应该注入的情况 -* fix 修复 sentinel-dashboard的pom引入logaback冲突问题 -* fix 修复 nacos 不兼容 logback 1.4 新版本问题 -* fix 修复 开启数据库加密 auth服务报错问题 -* fix 修复 gateway sentinel 限流报错问题(临时方案) https://github.com/alibaba/Sentinel/issues/3298 - - -## v2.1.2 - 2023-12-22 - -### 依赖升级 - -* update springboot 3.1.5 => 3.1.7 -* update springboot 2.7.17 => 2.7.18(扩展服务升级到boot2最终版本) -* update mybatis-boot 3.0.2 => 3.0.3 优化依赖传递 -* update powerjob 4.3.3 => 4.3.6 -* update easyexcel 3.3.2 => 3.3.3 -* update transmittable-thread-local 2.14.2 => 2.14.4 -* update justauth 1.16.5 => 1.16.6 -* update redisson 3.24.1 => 3.24.3 修复订阅重启连接超时问题 -* update easy-es 1.1.1 => 2.0.0-beta4 - -### 功能更新 - -* update 优化 oss 远程调用 支持降级处理 -* update 优化 丰富RedisUtils对List Set类型的操作 -* update 优化 为 admin 模块 单独增加ratelimiter模块 -* update 优化 验证码接口 增加限流配置 -* update 优化 excel合并注解会根据第一合并列的结果来决定后续的列合并 (感谢 Simple) -* update 优化 SocialUtils 代码 -* update 优化 删除无用异常类 -* update 优化 补全三方登录校验国际化 -* update 优化 sms组件 预留自动配置类 -* update 更新 关于数据库的说明 -* update 优化 sms组件 预留自动配置类 -* update 优化 将 OSS配置 改为全局模式 降低使用难度 保留sql便于用户自行扩展(常规项目用不上配置分多租户) -* update 优化 细化oss配置管理权限控制 -* update 优化 开启 redisson 脚本缓存 减少网络传输 -* update 优化 删除 hikaricp 官方不推荐使用的配置 jdbc4 协议自带校验方法 -* update 优化 减少 PlusSaTokenDao 不必要的查询优化性能 -* update 优化 更新用户异常提示 使用登录账号 -* update 优化 使用登录用户判断是否登录 提高效率 -* update 优化 重构 LoginHelper 将本地存储代码操作封装 -* update 优化 getTenantId 判断是否开启多租户 -* update 优化 Dockerfile 使用shell模式 支持环境变量传入jvm参数 -* update 优化 WebSocketUtils 连接关闭改为警告 -* update 优化 excel多sheet页导出 (感谢 May) -* update 优化 删除无用接口实现 -* update 优化 jvm参数调整 全面启用zgc -* update 优化 使用动态租户重构业务对租户的逻辑 -* update 优化 TenantHelper 动态租户支持函数式方法 -* update 优化 支持多租户绑定相同的三方登录 -* update 优化 更新用户登录信息方法忽略数据权限 -* update 优化 补全三方绑定时间字段 删除无用excel注解 -* update 优化 将登录记录抽取到监听器统一处理 -* update 优化 登录消息推送异常拦截(未启动resource也不耽误用) -* update 优化 租户插件 ignoreTable 方法支持动态租户 - -### 新增功能 - -* add 新增 RedisUtils.setObjectIfExists 如果存在则设置方法 -* add 新增 丰富RedisUtils对List Set类型的操作 -* add 新增 翻译组件 用户昵称翻译实现 -* add 新增 响应加密功能 支持注解强制加密接口数据 (感谢 MichelleChung) -* add 新增 common-ratelimiter 限流模块 用于自定义业务限流 与 sentinel不冲突 - -### 问题修复 - -* fix 修复 stream-mq 测试服务未导入租户模块 导致鉴权不一致问题 -* fix 修复 使用zgc导致seata报错(未知原因 将alibaba组件全还原) -* fix 修复 sentinel 镜像添加了多余接口参数 -* fix 修复 注册接口获取开关未在租户范围内问题 -* fix 修复 seata-server logback版本冲突问题 -* fix 修复 selectDictTypeByType 查询方法错误问题 -* fix 修复 一些不正常类无法加载报错问题 -* fix 修复 powerjob sql脚本针对其他数据库转义符问题 (感谢 branches) -* fix 修复 MybatisSystemException 空指针问题 -* fix 修复 excel合并注解会根据第一合并列的结果来决定后续的列合并 -* fix 修复 session 多账号共用覆盖问题 改为 tokenSession 独立存储 -* fix 修复 token 失效后 登录获取用户null问题 -* fix 修复 powerjob部署方案 高版本nginx不生效问题 -* fix 修复 OssFactory 并发多创建实例问题 -* fix 修复 延迟队列在投递消息未到达时间的时候 服务死机导致重启收不到消息 - -### 前端改动 - -* update 优化 用户头像 img 变量无确定类型问题 -* update 优化 细化oss配置管理权限控制 -* update 优化 明确打包命令 -* update 优化 代码中存在的警告 -* update 优化 前端白名单页面放行逻辑 -* update 优化 页面关于权限标识符说明 -* fix 修复 append-to-body 编写错误 (感谢 Ai3_刘小龙) -* fix 关闭动态路由tab页签时不清理组件缓存 (感谢 NickLuo) -* fix 删除重复环境变量ElUploadInstance (感谢 棉花) -* fix 修复 在线用户 强推按钮点击取消控制台警告问题 -* fix 修复 字典使用 default 样式报警告问题 - -## v1.8.2 - 2023-11-27 - -### 依赖升级 - -* update springboot 2.7.16 => 2.7.18 升级到2.X最终版本(官方停更) -* update mybatis-plus 3.5.3.2 => 3.5.4 -* update satoken 1.36.0 => 1.37.0 -* update hutool 5.8.20 => 5.8.22 -* update aws-java-sdk-s3 1.12.400 => 1.12.540 -* update vue-quill 1.1.0 => 1.2.0 - -### 功能更新 - -* update 优化 页面关于权限标识符说明 -* update 优化 数据权限拦截器优先判断方法是否有效 提高性能减少无用sql解析 -* update 优化 部门数据权限使用默认兜底方案 -* update 优化 补全代码生成 columnList 接口参数注解缺失 -* update 优化 AddressUtils 兼容linux系统本地ip -* update 优化 操作日志 部门信息完善 -* update 优化 数据权限 减少二次校验查询 -* update 修改 获取用户token和后端不一致的问题 (感谢 bestrevens) -* update 优化 vue3 版本用户初始密码从字典查询 -* update 优化 富文本Editor组件检验图片格式 -* update 优化 操作日志列表新增IP地址查询 -* update 优化 全局数据存储用户编号 -* update 优化 菜单管理类型为按钮状态可选 - -### 问题修复 - -* fix 修复 OssFactory 并发多创建实例问题 -* fix 修复 demo页面字段编写错误 -* fix 修复 数据权限优化后 update delete 报null问题 -* fix 修复 五级路由缓存无效问题 -* fix 修复 oss服务无法连接 -* fix 修复 内链iframe没有传递参数问题 -* fix 修复 外链带端口出现的异常 -* fix 修复 普通角色编辑使用内置管理员code越权问题 -* fix 修复 seata XA模式缺失druid工具问题 -* fix 修复 代码生成 是否必填与数据库不匹配问题 -* fix 修复 富文本上传接口地址错误 -* fix 修复 HeaderSearch组件跳转query参数丢失问题 -* fix 修复树结构代码生成新增方法赋值错误 - -## v2.1.1 - 2023-11-14 - -### 依赖升级 - -* update springboot 3.1.3 => 3.1.5 -* update springboot 2.7.14 => 2.7.17(扩展服务) -* update springboot-admin 3.1.5 => 3.1.7 -* update satoken 1.35.0.RC => 1.37.0 -* update mybatis-plus 3.5.3.2 => 3.5.4 适配mp新版本改动 -* update dynamic-ds 4.1.3 => 4.2.0 -* update bouncycastle 1.72 => 1.76 -* update poi 5.2.3 => 5.2.4 -* update redisson 3.23.2 => 3.24.1 -* update hutool 5.8.20 => 5.8.22 -* update lombok 1.18.26 => 1.18.30(适配支持jdk21) -* update vue-quill 1.1.0 => 1.2.0 -* update seata 1.7.0 => 1.7.1 -* update dubbo 3.2.5 => 3.2.7 - -### 功能更新 - -* update 优化 移除不合理的方法 携带附件的邮件建议直接集成插件发送 -* update 优化 携带 clientid 跨域问题 -* update 优化 数据权限拦截器优先判断方法是否有效 提高性能减少无用sql解析 -* update 优化 适配 maxkey 新版本 -* update 优化 @Sensitive脱敏增加角色和权限校验 (感谢 盘古给你一斧) -* update 优化 部门数据权限使用默认兜底方案 -* update 优化 更改默认日志等级为info 避免日志过多(按需开启debug) -* update 优化 登录策略代码优化(感谢 David Wei) -* update 优化 补全代码生成 columnList 接口参数注解缺失 -* update 优化 nginx 配置支持 websocket -* update 优化 notice 新增通知公告发送ws推送 -* update 优化 websocket 模块减少日志输出 增加登录推送 -* update 优化 重构登录策略增加扩展性降低复杂度 -* update 优化 addressUtils 兼容linux系统本地ip -* update 优化 补全操作日志部门数据 -* update 优化 支持数据库操作在非web环境下切换租户 -* update 优化 排除powerjob无用的依赖 减少打包30M体积 -* update 优化 删除 satoken yml 时间配置 此功能已迁移至客户端管理 -* update 优化 redis 集群模式注释说明 -* update 优化 客户端禁用限制 -* update 优化 登录日志, 在线用户展示信息(增加 客户端, 设备类型)(感谢 MichelleChung) -* update 优化 限制框架中的fastjson版本 -* update 优化 数据权限 减少二次校验查询 -* update 优化 将部门id存入token避免过度查询redis -* update 优化 增加租户ID为Null错误日志 -* update 优化 操作日志列表新增IP地址查询 -* update 优化 通过参数键名获取键值接口的返回体(感谢 David Wei) -* update 优化 为 sys_grant_type 字典增加样式 -* update 优化 代码生成 页面输入框样式 -* update 优化 全业务分页查询增加排序规则避免因where条件导致乱序问题 -* update 优化 登录接口租户id被强制校验问题 -* update 优化 加密模块 支持父类统一使用加密注解(感谢 Tyler Ge) -* update 优化 将graalvm镜像更新为openjdk镜像 需要的人自行切换即可 -* update 优化 部分使用者乱设权限导致无法获取用户信息 增加权限提示 -* update 优化 表格列的显示与隐藏小组件(感谢 bestrevens) -* update 优化 增加表单构建不能使用说明 -* update 优化 富文本Editor组件检验图片格式 -* update 优化 操作日志列表新增IP地址查询 -* update 优化 菜单管理类型为按钮状态可选 -* update 优化 用户初始密码从参数配置查询 -* update 优化 通过参数键名获取键值接口的返回体(感谢 David Wei) -* update 优化 字典标签支持数组和多标签(感谢 抓蛙师) - -### 新增功能 - -* add 新增 websocket 群发功能 -* add 新增 前端接入websocket接收消息(感谢 三个三) -* add 增加 rpc消息推送接口与实现 -* add 新增 CacheController Redis 缓存监控接口(感谢 Michelle.Chung) - -### 问题修复 - -* fix 修复 因扩展服务不支持boot3导致无法引入common-web包 日志写出不生效问题 -* fix 修复 seata XA模式缺失druid工具问题 -* fix 修复 oss服务无法连接 导致业务异常问题 查询不应该影响业务 -* fix 修复 租户id为null 无法匹配字符串导致的嵌套key问题 -* fix 修复 部门管理orderNum排序失效问题 -* fix 修复 外链带端口出现的异常 -* fix 修复 普通角色编辑使用内置管理员code越权问题 -* fix 修复 代码生成 是否必填与数据库不匹配问题 -* fix 修复 用户注册接口校验用户名不区分租户问题 -* fix 修复 错误增加组导致的校验不生效问题 -* fix 修复 新增校验主键id问题 -* fix 修复 powerjob 使用 nginx 部署无法访问的问题 -* fix 修复 SysUserMapper 内标签使用错误(不影响使用) -* fix 修复 新增或编辑 SysOssConfig 数据后 推送到 redis 数据不完整 -* fix 修复 树表生成查询变量使用错误 -* fix 修复 个人信息修改密码接口隐藏新旧密码参数明文(感谢 bleachtred) -* fix 修复 删除字段后 * update sql 未更新问题 -* fix 修复 三方登录支付宝source与实际支付宝业务code不匹配问题 -* fix 修复 五级路由缓存无效问题 -* fix 修复 内链iframe没有传递参数问题 -* fix 修复 绑定第三方帐号参数“wechar”更正为“wechat” (感谢 scmiot) -* fix 修复 用户注册缺失 clientid 问题 -* fix 修复 HeaderSearch组件跳转query参数丢失问题 -* fix 修复 自定义字典样式不生效的问题 -* fix 修复 login 页面 loading 未关闭问题 - -## v1.8.1 - 2023-09-26 - -### 依赖升级 - -* update springboot 2.7.14 => 2.7.16 -* update springboot-admin 2.7.10 => 2.7.11 -* update satoken 1.35.0.RC => 1.36.0 -* update lombok 1.18.26 =. 1.18.30 -* update springboot 2.7.13 => 2.7.14 -* update mybatis-plus 3.5.3.1 => 3.5.3.2 -* update easyexcel 3.3.1 => 3.3.2 -* update hutool 5.8.18 => 5.8.20 -* update dubbo 3.1.8 => 3.1.11 - -### 功能更新 - -* update 优化 代码生成 vo实体类序列化 -* update 优化 excel 导出不必要的请求头 -* update 优化 字典标签支持传分隔符分隔的字符串和数组 -* update 优化 控制台debuger位置错误问题 -* update 优化 TopNav 菜单样式 -* update 优化 注册用户异常报错不正确问题 -* update 优化 全局异常处理器 业务异常不输出具体堆栈信息 减少无用日志存储 -* update 优化 用户管理 只查询未禁用的部门角色岗位数据 -* update 优化 岗位如果绑定了用户则不允许禁用 -* update 优化 部门与角色如果绑定了用户则不允许禁用 -* update 优化 加密实现 使用 EncryptUtils 统一处理 -* update 优化 适配 mysql 8.0.34 升级连接机制 -* update 优化 excel导出字典转下拉框 无需标记index自动处理 -* update 优化 excel 导出字典默认转为下拉框 -* update 删除一些跟swagger有关的字眼 避免误解 -* update 优化 角色权限支持仅本人权限查看 解决无法查看自己创建的角色问题 -* update 优化 xxljob 端口随着主应用端口飘逸 避免集群冲突 - -### 问题修复 - -* fix 修复 自定义字典样式不生效的问题 -* fix 修复 新建用户可能会存在的越权行为 -* fix 修复 字典缓存删除方法参数错误问题 -* fix 修复 修复树模板父级编码变量错误 -* fix 修复 demo 模块缺少 security 依赖问题 -* fix 修复 升级 mp 版本导致的问题 -* fix 修复 加密模块数据转换异常问题 -* fix 修复 动态设置 token 有效期不生效问题 -* fix 修复 token 过期登出无法清理在线用户问题 - - -## v2.1.0 - 2023-09-06 - -# 开发历程 - -* 2023年5月 开始 2.1.0 计划 历经1个月的设计与讨论 -* 2023年6月 开始着手开发 历经2个多月的开发 特别感谢团队的小伙伴与一些热心的粉丝 参与功能开发与测试 -* 2023年8月 开始公测 历经将近1个月的公测与修复工作(期间成功支持多位使用者生产使用) -* 2023年9月初 正式发布(经过多个小伙伴的生产实践 已基本可尝试生产使用) -> 关于1.X的说明 由于SpringBoot2.X与vue2.X均在11月底停止维护
-> 故而咱们vue版本1.X也无法再继续更新
-> 介于1.X的用户量特别庞大 功能也非常的稳定
-> 计划于11月底同Boot2.X一同停止更新但还会持续维护修复bug(修复的形式为直接提交到1.X分支停止发版)
- -# 视频介绍 - -为了更好的让大家了解 2.1.0 作者录制了相关的视频 供大家快速了解上手 - -* 2.1.0 新功能与变更介绍: https://www.bilibili.com/video/BV1fj411y71X/ - -# 更新日志 - -### 重大更新 - -* [重大更新] 优化 相关代码 完成代码生成多数据源统一存储(感谢 WangBQ) -* [不兼容更新] 移除 原短信功能 集成更强大的 sms4j 短信工具包(感谢 友杰) -* [不兼容更新] 对接 powerjob 实现分布式任务调度 删除原有 xxljob 原因为社区不更新功能太少只支持mysql(感谢 yhan219) -* [重大更新] 新增 三方授权绑定登录功能 基于 justauth 支持市面上大部分三方登录(感谢 三个三) -* [不兼容更新] 新增 客户端授权功能 不需要更改任何代码即可完成多端动态对接(感谢 Michelle.Chung) -* [重大更新] 新增 前后端接口请求加密传输 基于AES+RSA动态高强度加密(感谢 wdhcr) -* [重大更新] 新增 三方授权登录 对接 maxkey 单点登录 -* [不兼容更新] 优化 redis序列化配置 更改为通用格式(升级需清除redis所有数据) -* [重大更新] 新增 通过 sharding-proxy 实现分库分表(感谢 rice666 !pr94) - -### 依赖升级 - -* update springboot 3.0.7 => 3.1.3 -* update springboot-admin 3.1.3 => 3.1.5 -* update springcloud 2022.0.2 => 2022.0.4 -* update springcloud-alibaba 2022.0.0.0-RC2 => 2022.0.0.0 -* update springdoc 2.1.0 => 2.2.0 -* update spring-mybatis 3.0.1 => 3.0.2 -* update mybatis-plus 3.5.3.1 => 3.5.3.2 -* update easyexcel 3.2.1 => 3.3.2 -* update mapstruct-plus 1.2.3 => 1.3.5 解决修改实体类 idea不编译问题 -* update satoken 1.34.0 => 1.35.0.RC 优化过期配置 支持多端token自定义有效期 -* update dynamic-ds 3.6.1 => 4.1.3 支持 SpringBoot3 -* update sms4j 2.2.0 -* update hutool 5.8.18 => 5.8.20 -* update redisson 3.20.1 => 3.23.4 -* update lock4j 2.2.4 => 2.2.5 -* update aws-java-sdk-s3 1.12.400 => 1.12.540 -* update maven-surefire-plugin 3.0.0 => 3.1.2 -* update seata 1.6.1 => 1.7.0 -* update sharding-proxy 5.4.0 -* update dubbo 3.2.2 => 3.2.5 -* update skywalking-toolkit 8.14.0 => 8.16.0 -* update logstash 7.2 => 7.4 - -### 功能更新 - -* update 优化 与 vue 版本同步代码结构 -* update 优化 放行springboot默认error接口 -* update 优化 RepeatSubmitAspect 逻辑避免并发请求问题 -* update 调整 gateway 访问日志输出等级 -* update 优化 修改角色如果未绑定用户则无需清理 -* update 优化 用户昵称非空校验 -* update 优化 在全局异常拦截器中增加两类异常处理 -* update 优化 StreamUtils 方法过滤null值 -* update 优化 powerjob 端口随着主应用端口飘逸 避免集群冲突 -* update 优化 角色权限支持仅本人权限查看 解决无法查看自己创建的角色问题 -* update 修改代码生成模版,日期范围统一采用addDateRange方法 -* update 优化 树表生成前端缺少 children 字段 -* update 优化 ruoyi-resource 服务添加 websocket 模块 -* update 优化 放行验证码接口、第三方登录请求与回调 -* update 更新 GlobalLogFilter#filter 根据请求头判断加密参数(感谢 Michelle.Chung !pr100) -* update 优化 SaReactorFilter 过滤器判断 token 客户端 id 是否有效(感谢 Michelle.Chung !pr101) -* update 删除一些跟swagger有关的字眼 避免误解 -* update 优化 兼容 clientid 通过 param 传输 -* update 优化 excel导出字典转下拉框 无需标记index自动处理(感谢 一夏coco) -* update 优化 增加线程池销毁配置 -* update 优化 屏蔽 powerjob 无用的心跳日志 -* update 优化 适配 mysql 8.0.34 升级连接机制 -* update 优化 加密实现 使用 EncryptUtils 统一处理 -* update 优化 删除字典无用状态字段(基本用不上 禁用后还会导致回显问题) -* update 优化 部门与角色如果绑定了用户则不允许禁用 -* update 优化 岗位如果绑定了用户则不允许禁用 -* update 优化 用户管理 只查询未禁用的部门角色岗位数据 -* update 优化 登录用户增加昵称返回 -* update 优化 全局异常处理器 业务异常不输出具体堆栈信息 减少无用日志存储 -* update 优化 将部门管理 负责人选项改为下拉框选择 -* update 优化 登录用户缓存 去除冗余统一存储 -* update 优化 注册用户异常报错不正确问题 -* update 优化 放宽菜单权限 角色关联菜单无需管理员 - -### 新增功能 - -* add 增加 RedisUtils 批量删除 hash key 方法 -* add 新增 Oss 上传 File 文件方法(感谢 jenn) -* add 增加 excel 导出下拉框功能 -* add 新增 RedisUtils.setObjectIfAbsent 如果不存在则设置方法 - -### 修复问题 - -* fix 修复 用户重名登录报错问题 -* fix 修复 服务未添加 common-security 模块导致异常拦截器不生效问题 -* fix 修复 用户篡改管理员角色标识符越权问题 -* fix 修复 文件管理 创建人未翻译问题 -* fix 修复 monitor 监控无法展示数据问题 -* fix 修复 更换 satoken dubbo 插件导致包名不一致问题 -* fix 修复 字典缓存注解使用错误问题 -* fix 修复 接口文档未拼接服务路径问题 -* fix 修复 excel 枚举反向解析失败问题 -* fix 修复 查询部门下拉树未过滤数据权限问题 -* fix 修复 CacheName 缓存key存储错误问题 -* fix 修复 oss 列表 用户名回显错误 -* fix 修复 不同vo相同字段mybatis会自动赋值问题 -* fix 修复 删除 skywalking dubbo 2.X 插件避免与 3.X 出现兼容性问题 -* fix 修复 新增角色使用内置管理员标识符问题 -* fix 修复 token 过期登出无法清理在线用户问题 -* fix 修复 动态设置 token 有效期不生效问题 -* fix 修复 加密模块数据转换异常问题 -* fix 修复 dubbo 更改内部序列化方式 导致异常类无法反序列化问题 -* fix 修复 客户端编辑时授权类型变更未保存的问题 -* fix 修正 缺失 SysClientVoConvert 导致转换异常(感谢 Michelle.Chung) -* fix 修正 auth 模块缺失引用导致解密异常(感谢 Michelle.Chung) -* fix 修复 demo 与 stream-mq 模块缺少 security 依赖问题 -* fix 修复 导入用户数据 变量使用错误问题 -* fix 修复 验证码开关未动态刷新问题 -* fix 修复 自动填充数据 loginUser 为 null(感谢 charles !pr108) -* fix 修复 修复树模板父级编码变量错误 -* fix 修复 部署部分系统出现乱码问题 -* fix 修复 一级菜单无法显示问题 -* fix 修复 新建用户可能会存在的越权行为 -* fix 修复 代码生成页面参数缺少逗号问题 - -### 移除功能 - -* remove 移除原有短信功能(建议使用sms4j) -* remove 移除xxljob功能(建议使用powerjob) - - -## v1.8.0 - 2023-07-11 - -### 重大更新 - -* [重大更新] 新增 sms4j 短信融合框架整合(支持数十种短信厂商接入、发送限制、负载均衡等功能) -* [不兼容更新] 移除 原短信功能(建议使用新 sms4j 功能) -* [重要迁移] 迁移 vue3 前端到主仓库统一维护 - -### 依赖升级 - -* update springboot 2.7.11 => 2.7.13 -* update spring-cloud 2021.0.7 => 2021.0.8 -* update satoken 1.34.0 => 1.35.0.RC -* update easyexcel 3.2.1 => 3.3.1 -* update sms4j 2.2.0 -* update element 2.15.12 => 2.15.13 - -### 功能更新 - -* update 优化 StreamUtils 方法过滤null值 -* update 优化 页签在Firefox浏览器被遮挡 -* update 优化 在全局异常拦截器中增加两类异常处理 -* update 优化 下载zip方法增加遮罩层(感谢@梁剑锋) -* update 优化 用户昵称非空校验 -* update 优化 修改角色如果未绑定用户则无需清理 -* update 优化 RepeatSubmitAspect 逻辑避免并发请求问题 -* update 优化 satoken 过期配置 支持多端token自定义有效期 -* update 优化 加密注解注释错误 -* update 优化 切换 maven 仓库到华为云(aliyun 不可用) -* update 优化 excel 导出存在合并项时在初始化类时进行数据的处理避免多次调用(感谢@yueye) -* update 优化 重构 CellMergeStrategy 支持多级表头修复一些小问题 整理代码结构 -* update 补全 SysLogininforMapper.xml 缺失字段 -* update 优化 demo 模块 路径适配统一前端 -* update 调整 gateway 访问日志输出等级 - -### 新增功能 - -* add 新增 RedisUtils.setObjectIfAbsent 不存在则设置方法 -* add 新增 Excel 导出附带有下拉框(字典自动导出为下拉框) 可自定义多级下拉框(感谢@Emil.Zhang) -* add 新增 OssClient File 文件上传方法 -* add 增加 RedisUtils 批量删除 hash key 方法 - -### 问题修复 - -* fix 修复 sa-token.check-same-token 开关对网关鉴权无效问题 -* fix 修复 服务未添加 common-security 模块导致异常拦截器不生效问题 -* fix 修复 删除 skywalking dubbo 2.X 插件避免与 3.X 出现兼容性问题 -* fix 修复 excel 枚举反向解析失败问题 -* fix 修复 字典缓存注解使用错误问题 -* fix 修复 新增角色使用内置管理员标识符问题 -* fix 修复 缓存监控图表 支持跟随屏幕大小自适应调整(感谢@抓蛙师) -* fix 修复 防重组件 错删注解问题 -* fix 修复 CacheName 缓存key存储错误问题 -* fix 修复 字典缓存注解使用错误问题 -* fix 修复 用户篡改管理员角色标识符越权问题 -* fix 修复 登录校验错误次数未达到上限时 错误次数缓存未设置有效时间问题 -* fix 修复 OssClient 切换服务 实例不正确问题 -* fix 修复 element ui 因版本而未被工具识别问题(感谢@梁剑锋) -* fix 修复 admin监控 切换tab页需要重复登录问题 -* fix 修复 个人中心tab栏关闭页面内容压缩问题 - -## v2.0.0 - 2023-06-15 - -**重点说明: 由于 SpringCloudAlibaba 一直未发布正式版 导致系统底层组件可能存在些许问题 故而不建议生产使用 框架也将直接开启后续 2.1.0 的开发工作** - -### 重大更新 - -* [不兼容升级] java 版本从 jdk 8 升级到 jdk 17 且需要使用 graalvm 运行(暂时未解决原生jdk存在的问题) -* [不兼容升级] springboot 升级 3.0 版本 -* [不兼容升级] 重构 项目模块结构 采用插件化结构 易扩展易解耦 -* [不兼容升级] com.sun.mail 更改为 jakarta.mail 修改最新写法 -* [不兼容升级] javax.servlet 替换为 jakarta.servlet 更新所有代码 -* [简化性升级] 默认开启复杂结构 resultMap 自动映射 简化xml编码(多结构实体需带上主键id) -* [数据库改动] 更新 create_by update_by 字段类型 (保存用户id) -* [数据库改动] 新增 create_dept 字段 (保存创建部门id) -* [不兼容更新] system 模块 所有实体类均使用 bo|vo 规范化 -* [重大更新] 新增 多租户功能设计 整体框架代码结构与数据库更改 -* [重大更新] 新增 mapstruct-plus 替换 BeanUtil 与 BeanCopyUtils 工具 -* [不兼容更新] 重构 登录注解接口与cloud版本统一接口路径 -* [不兼容更新] 重构 BaseMapperPlus接口 去除 `@param Mapper` 泛型 -* [不兼容更新] 移除 vue2 前端工程 全面启用 vue3 -* [重大更新] 新增 vue3 + TS 版本前端(独立仓库后续与Cloud版本共用) -* [重大更新] 增加 websocket 模块 支持token鉴权 支持分布式集群消息同步 -* [重大更新] 框架文档全面翻新 https://plus-doc.dromara.org -* [不兼容更新] 代码生成 支持代码生成多数据源统一存储(主库存储子库的表 无需子库加gen表了) -* [不兼容更新] 重构 将系统内置配置放置到common包内独立加载 不允许用户随意修改 - -### 依赖升级 - -* update java 1.8 => 17 -* update springboot 2.7.7 => 3.0.7 -* update springcloud 2021.0.6 => 2022.0.2 -* update springcloud-alibaba 2022.0.0.0-RC2 -* update springboot-admin 2.7.10 => 3.0.4 -* update springdoc 1.6.14 => 2.1.0 -* udpate dubbo 3.1.8 => 3.2.2 -* update lock4j 2.2.3 => 2.2.4 -* update dynamic-ds 3.5.2 => 3.6.1 -* update easyexcel 3.1.5 => 3.2.1 -* update hutool 5.8.11 => 5.8.18 -* update redisson 3.19.2 => 3.20.1 -* update lombok 1.18.24 => 1.18.26 -* update spring-boot.mybatis 2.2.2 => 3.0.1 -* update mapstruct-plus 1.2.3 -* update maven-compiler-plugin 3.10.1 => 3.11.0 -* update maven-surefire-plugin 3.0.0-M7 => 3.0.0 -* update docker mysql 8.0.31 => 8.0.33 -* update docker nginx 1.22.1 => 1.32.4 -* update docker redis 6.2.7 => 6.2.12 -* update docker minio RELEASE.2023-04-13T03-08-07Z - -### 功能更新 - -* update 适配 AsyncConfig 替换过期继承类改为实现 AsyncConfigurer 接口 -* update 适配 redis 新版本配置文件写法 -* update 适配 获取redis 监控参数接口 替换过期语法 -* update 适配 sa-token 替换新依赖 sa-token-spring-boot3-starter -* update 适配 springboot-admin 改为最新 spring-security 写法 -* update 适配 springdoc 新版本配置方式 -* update 适配 ServletUtils 更换继承 JakartaServletUtil -* update 适配 新序列化注解 -* update 优化 利用 resultMap 自动映射配置 简化 xml (非嵌套) -* update 优化 调整 system entity 实体与 controller 包结构 -* update 优化 实体类中校验注解的提示信息 -* update 优化 使用 jdk17 语法优化代码 -* update 优化 所有 properties 文件改为注解启用 -* update 更新 docker 基础镜像 graalvm java17 -* update 优化 用户头像 改为存储 ossId 使用转换模块转为 url 展示 -* update 优化 重构 CellMergeStrategy 支持多级表头修复一些小问题 整理代码结构 -* update 优化 登录流程代码注释 -* update 优化 将框架内的swagger命名更改为springdoc命名避免误解 - -### 新增功能 - -* add 新增 flatten-maven-plugin 插件统一版本号管理 -* add 新增 ip2region 实现离线IP地址定位库 - -### 移除功能 - -* remove 移除 BeanCopyUtils 工具类 与 JDK17 不兼容 -* remove 移除 devtools 依赖 并不好用(建议直接用idea自带的热更) -* remove 移除 vue2 前端工程 统一使用 vue3 工程 - -### 修复功能 - -* fix 修复 根据 seata 官方提交记录 临时修复 seata 关于jdk17代理的bug -* fix 修复 登录校验错误次数未达到上限时 错误次数缓存未设置有效时间问题 -* fix 修复 common-core 包使用aop注解 但未添加aop实现类导致单独使用报错问题 - -## v1.7.0 - 2023-05-10 - -### 依赖升级 - -* update springboot 2.7.9 => 2.7.11 修复 DoS 漏洞 修复CVE漏洞 -* update springcloud 2021.0.6 => 2021.0.7 -* update springcloud-alibaba 2021.0.4.0 => 2021.0.5.0 -* update dubbo 3.1.7 => 3.1.10 -* update nacos 2.2.0 => 2.2.1 -* update xxljob 2.3.1 => 2.4.0 -* update minio 升级至最新版 避免低版本信息泄漏问题 -* update hutool 5.8.15 => 5.8.18 -* update redisson 3.20.0 => 3.20.1 -* update lombok 1.18.24 => 1.18.26 - -### 功能更新 - -* update 优化 更改 sys_oss_config 表注释 避免误解 -* update 优化 sys_logininfor 丰富多种信息 -* update 项目正式入驻 dromara 开源社区 更改项目地址 -* update 全新 logo 全新背景图(设计师打造) -* update 优化 代码生成模块的数据同步功能 -* update 修改多团队开发插件,支持多网卡 -* update 修改controller中校验直接返回R.fail -* update 优化 角色sort值一样的排序问题 -* update 更换默认用户头像 -* update 优化 WebFluxUtils.getOriginalRequestUrl 方法获取空路径报错问题 -* update 去除same-token有限期配置,使用默认配置(一天) -* update 优化固定头部页签滚动条被隐藏的问题 -* update delete vue-multiselect style -* update 按代码规范补全重写注解 -* update 优化 极端情况获取LoginUser可能为null问题 -* update 优化 更改系统所有服务日志配置文件命名为 logback-plus.xml 避免与其他框架默认配置冲突 -* update 优化 skywalking-agent 探针日志等级调整为 WARN 减少无用日志输出 -* update 优化 加解密模块 将null判断下推防止任何可能的null出现 -* update 优化 在线用户token获取方式 -* update 优化 用户更改角色 踢掉角色相关所有在线用户 - -### 新功能 - -* add 集成 ip2region 实现离线IP地址定位库 -* add 增加 邮箱验证码发送接口 -* add 增加 邮箱登陆接口 -* add 增加 EncryptUtils 加解密安全工具类 可以处理base64,aes,sm4,sm2,rsa,md5,sha256加解密 -* add 增加 EncryptUtils 类中增加国密sm3的不可逆加密算法 -* add 新增 忽略数据权限写法 防止异常不执行关闭问题 - -### 问题修复 - -* fix 修复 MybatisExceptionHandler 未自动装载问题 -* fix 修复 代码生成 点选按钮不生效问题 -* fix 修复 Nacos 服务 SpringBoot-admin 客户端功能失效问题 -* fix 修复 findInSet 在mysql下方法搜索非数字字段时 无引号报错问题 -* fix 修复 ruoyi-demo postgres 数据库用户名密码变量错误 -* fix 修复 oracle postgres 数据库日志表索引创建错误 -* fix 修复 无法注入 mailProperties 导致 resource 模块无法启动问题 -* fix 修复tab栏”关闭其他“异常的问题 -* fix 修复 加解密拦截器 对象属性为null问题 -* fix 修复 取消oss预览状态修改 图标变化不正常问题 -* fix 修复 nacos 新版本升级后 与 docker 基础镜像系统存在兼容性问题 - - -## v1.6.0 - 2023-03-14 - -### 重大更新 - -[重大更新] add 新增 通用翻译模块 `ruoyi-common-translation` 实现(部门名、字典、oss、用户名) -[重大更新] add 新增 数据加解密模块 `ruoyi-common-encrypt` - - -### 依赖升级 - -* update springboot 2.7.7 => 2.7.9 -* update springcloud 2021.0.5 => 2021.0.6 -* update easyexcel 3.1.5 => 3.2.1 -* update redisson 3.19.1 => 3.20.0 -* update springdoc 1.6.14 => 1.6.15 -* update hutool 5.8.12 => 5.8.15 (13与14有问题勿使用) -* update logstash-sdk 7.1.1 => 7.2 -* update aws-java-sdk-s3 1.12.373 => 1.12.400 -* update tencent-sms 3.1.660 => 3.1.687 -* update skywalking 8.9.1 => 9.3.0 -* update skywalking-agent 8.13.0 => 8.14.0 -* update dubbo 3.1.4 => 3.1.7 解决dubbo报一些无用警告问题 -* update element-ui 2.15.10 => 2.15.12 - -### 功能更新 - -* update 优化 修改 oss 配置页面开关说明 避免造成误解 -* update 优化 `gateway` 对接 `sentinel` 使用网关特定模式 -* update 优化 转移 `logback-common` 配置到 `common-web` 模块 `gateway` 单独处理 -* update 优化 调整连接池默认参数 -* update 优化 `zookeeper` 自带控制台占用 `8080` 端口 -* update 优化 `DictDataMapper` 注解标注过期 推荐使用 `@Translation` 注解 -* update 优化 获取菜单数据权限接口 删除无用角色属性与逻辑 -* update 优化 调整连接池最长生命周期 防止出现警告 -* update 优化 连接池增加 `keepaliveTime` 探活参数 -* update 优化 `DataPermissionHelper` 增加 `开启/关闭` 忽略数据权限功能 -* update 重构 `OssFactory` 加载方式 改为每次比对配置做实例更新 -* update 优化 更新角色后踢掉所有相关的登录用户 用户量过大会导致redis阻塞卡顿(应粉丝要求) -* update 优化 翻译组件 支持返回值泛型 支持多种类型数据翻译(例如: 根据主键翻译成对象) -* update 优化 `tagsView` 右选框,首页不应该存在关闭左侧选项 -* update 优化 `copyright 2023` -* update 优化 日志注解支持排除指定的请求参数 -* update 优化 业务校验优化代码 -* update 优化 日志管理使用索引提升查询性能 -* update 优化 框架时间检索使用时间默认值 `00:00:00 - 23:59:59` -* update 优化 oss 预览使用 `ImagePreview` 组件 -* update 优化 统一登录接口令牌key - - -### 新功能 - -* add 新增 数据加解密模块 测试案例 -* add 新增 `StringUtils` `splitTo` 与 `splitList` 方法 优化业务代码 - -### 问题修复 - -* fix 修复 vue3模板 删除功能书写错误 -* fix 修复 部分服务未开启日志存储 -* fix 修复 接口问题开关不生效问题 -* fix 修复 优化文件下载出现的异常 -* fix 修复 修改密码日志存储明文问题 -* fix 修复 代码生成 `postgreSQL` 查出多余的已删除字段 - -## v1.5.0 - 2023-01-13 - -### 重大更新 - -* [重大更新] 框架主体业务与代码生成器 完成 oracle postgres 多数据库类型支持(中间件不支持) -* [重大更新] 使用 spring 事件发布机制 重构登录日志与操作日志 支持多事件监听无入侵扩展 -* 例如: 可以增加一个监听者将日志上传至ES等存储 对原有逻辑无影响 - -### 依赖升级 - -* update springboot 2.7.6 => 2.7.7 -* update springboot-admin 2.7.7 => 2.7.10 -* update dubbo 3.1.3 => 3.1.4 -* update seata 1.5.2 => 1.6.1 适配升级 -* update nacos 2.1.2 => 2.2.0 适配升级 -* update mybatis-plus 3.5.2 => 3.5.3.1 -* update sa-token 1.33.0 => 1.34.0 -* update springdoc 1.6.13 => 1.6.14 -* update snakeyaml 1.32 => 1.33 -* update easyexcel 3.1.3 => 3.1.5 -* update redisson 3.18.0 => 3.19.1 -* update easy-es 1.1.0 => 1.1.1 -* update hutool 5.8.10 => 5.8.11 -* update aws-s3 1.12.349 => 1.12.373 -* update aliyun-sms 2.0.22 => 2.0.23 -* update tencent-sms 3.1.635 => 3.1.660 -* update echarts 4.9.0 => 5.4.0 - -### 功能更新 - -* update 优化 BaseMapperPlus 使用 MP V3.5.3 新工具类 Db 简化批处理操作实现 -* update 优化 demo服务 过滤健康检查 sql 打印 -* update 优化 代码生成与框架主体使用相同的主键生成器 全局统一避免问题 -* update 优化 系统登录 使用单表查询校验用户 避免多次join查询 -* update 优化 适配框架多数据库支持 完成 oracle postgres 数据库适配(放弃 sqlserver 适配 原因: 基础中间件均不支持) -* update 优化 删除主 sql 内无用数据 -* update 优化 删除 vue3 模板无用参数 -* update 优化 重构 ExcelUtil 全导出方法支持 OutputStream 流导出 不局限于 response -* update 优化 maven 地址切换回 aliyun 仓库 -* update 优化 springdoc 配置鉴权头写死问题 增加持久化鉴权头配置 -* update 优化 actuator 依赖整合到 common-web 模块 -* update 优化 验证码结果使用 spel 引擎自动计算 -* update 优化 数据权限处理器 变量命名错误 -* update 优化 去除 RedisUtils 无用继承 -* update 优化 弹窗内容过多展示不全问题 -* update 优化 删除 fuse 无效选项 maxPatternLength -* update 优化 minio 安装警告 使用新版本参数 -* update 优化 使用 spring 事件发布机制 重构登录日志与操作日志 -* update 优化 使用 spring 事件机制 重构 OssConfig 缓存更新 -* update 优化 单元格合并判断cellValue是否相等方法 -* update 优化 调整 gateway 拦截器执行顺序 优先处理 xss 过滤 然后进行缓存处理 - -### 新功能 - -* add 增加 GET 请求提交日期参数 默认格式化配置 -* add 增加 RedisUtils 检查缓存对象是否存在方法 -* add 增加 oracle postgres docker编排 -* add 新增 代码生成器适配 多数据库可切换生成代码 -* add 新增 oracle postgres 数据库框架sql脚本 -* add 增加 DataBaseHelper 数据库助手 用于适配多类型数据库 -* add 新增 BeanCopyUtils#mapToMap 方法 - -### 问题修复 - -* fix 修复 注册页面 验证码开关不生效问题 -* fix 修复 新版本 dubbo-filter-seata 插件内核与seata不一致问题(临时) -* fix 修复 根据 key 更新参数配置报 null 问题 -* fix 修复 用户注册 用户类型字段书写错误 -* fix 修复 代码生成图片/文件/单选时选择必填无法校验问题 -* fix 修复 修改参数键名时 未移除过期缓存配置 -* fix 修复 内网鉴权 Filter 优先级问题 导致 websocket 连接失败 -* fix 修复 gateway 流控规则生效但不显示问题 -* fix 修复 新版本 Redisson 存在与 boot 2.X 的兼容性问题 - -## v1.4.0 - 2022-12-01 - -### 重大更新 -* [重大更新] 新增 对接 skywalking 全功能(详细看下方新功能列表) -* [重大更新] 重构 ruoyi-nacos 使用官方依赖整合 解决一些问题 并升级 2.1.2 版本 -* [重大更新] 新增 oss 私有库功能(数据库结构改动 需执行升级sql) -* [重大更新] 优化 数据源连接池从 druid 切换到 hikari(原因看文档) -* [重大更新] 新增 对接 prometheus + grafana 全功能(详细看下方新功能列表) - -### 依赖升级 -* update springcloud 2021.0.4 => 2021.0.5 -* update springboot 2.7.4 => 2.7.6 -* update springboot-admin 2.7.5 => 2.7.7 -* update springdoc 1.6.11 => 1.6.13 -* update poi 5.2.2 => 5.2.3 -* update hutool 5.8.6 => 5.8.10 -* update aliyun-sms 2.0.18 => 2.0.22 -* update tencent-sms 3.1.591 => 3.1.611 -* update sa-token 1.30.0 => 1.33.0 -* update redisson 3.17.6 => 3.18.0 -* update easy-es 1.0.2 => 1.1.0 -* update easyexcel 3.1.1 => 3.1.3 -* update lock4j 2.2.2 => 2.2.3 -* update s3-adk 1.12.300 => 1.12.349 -* update sentinel 1.8.5 => 1.8.6 -* update nacos 2.1.1 => 2.1.2 -* update ELK 7.17.2 => 7.17.6 升级镜像版本 -* update nginx 1.21.6 => 1.22.1 修复漏洞 -* update mysql-docker 8.0.29 => 8.0.31 - -### 功能更新 -* update 优化 分页对象 PageQuery 支持多排序 适配 文件管理 页面支持多排序 -* update 优化 获取用户信息getInfo接口 使用缓存数据获取 -* update 优化 rpc文件上传 增加 ossId 数据返回 -* update 优化 nacos 集群模式搭建 关于 nacos.home 注释说明 -* update 优化 修改头像在小屏幕上页面布局错位的问题 -* update 优化 oss 云厂商增加 华为obs关键字 -* update 优化 重置时取消部门选中 -* update 优化 新增返回警告消息提示 -* update 优化 抽取 logback 通用配置 logback-common.xml 简化其他服务日志文件书写 -* update 更改 nacos 配置文件目录 从dev文件夹迁移到nacos文件夹与其他配置区分 -* update 优化 gateway 只缓存body -* update 优化 Dockerfile 创建目录命令简化操作 -* update 优化 gateway filter顺序 与 代码工具封装 -* update 优化 将空 catch 块形参重命名为 ignored -* update 优化 satoken 依赖传递 -* update 优化 重写字典查询 使用本地缓存优化网络开销 提升到上级实现减少rpc调用频率 使用流处理减少字符串操作 -* update 优化 减小腾讯短信引入jar包的体积 -* update 优化 简化一些方法的写法 -* update 优化 消除Vue3控制台出现的警告信息 -* update 优化 忽略不必要的属性数据返回 -* update 优化 重构 mysql-jdbc 依赖到 mybatis 包内 替换为最新坐标 - -### 新功能 -* add 新增 所有服务 docker 部署对接 skywalking -* add 新增 三大 mq 整合 skywalking -* add 新增 seata 整合 skywalking 手动编译 seata 插件包 -* add 新增 ruoyi-common-skylog 整合 skywalking 日志推送 -* add 增加 skywalking docker编排 -* add 增加 ruoyi-seata-server redis模式配置 -* add 新增 ruoyi-common-prometheus 模块 用于对接 prometheus 监控 -* add 新增 docker prometheus + grafana 容器编排 -* add 新增 ruoyi-monitor 监控服务 提供 prometheus http_sd 服务发现功能 -* add 新增 所有服务整合 ruoyi-common-prometheus 模块 -* add 新增 grafana 监控大屏配置文件(框架定制) -* add 新增 使用 mica-metrics 为 undertow 提供健康检查 -* add 新增 字典数据映射翻译注解 -* add 增加 RedisUtils 获取缓存Map的key列表 - -### 问题修复 -* fix 修复 开启账号同端互斥登录 被顶掉后登出报null异常问题 -* fix 修复 设置NameMapper导致队列功能异常问题 -* fix 修复 EnvironmentPostProcessor 不生效问题 -* fix 修复 文件上传组件格式验证问题 -* fix 修复 ruoyi-xxl-job-admin 服务健康检查配置缺失问题 -* fix 修复 Excel导出字典值转换方法由于内部调用缓存不生效bug -* fix 修复 SysOss 方法内部调用导致缓存不生效 bug -* fix 修复 主题颜色在Drawer组件不会加载问题 -* fix 修复 修改用户信息 校验用户名未排除当前用户问题 -* fix 修复 升级 nginx 修复漏洞 https://www.oschina.net/news/214309 -* fix 修复 用户编辑时角色和部门存在无法修改情况 -* fix 修复 RemoteDictServiceImpl 代理对象获取异常bug -* fix 修复 菜单激活无法填充颜色 去除某些svg图标的fill属性 -* fix 修复 使用透明底png图片时, 自动填充黑色背景 -* fix 修复 table中更多按钮切换主题色未生效修复问题 -* fix 修复 dubbo 使用 tri 协议 header 请求头变为小写导致无法获取参数问题 -* fix 修复 DubboRequestFilter 优先级过高导致的 skywalking tid 取不到问题 -* fix 修复 前端脚本乱码问题 -* fix 修复 WebFluxUtils 读取空 body 报 null 问题 -* fix 修复 Log注解GET请求记录不到参数问题 -* fix 修复 某些特性的环境生成代码变乱码TXT文件问题 -* fix 修复 开启TopNav没有子菜单隐藏侧边栏 -* fix 修复 回显数据字典数组异常问题 -* fix 修复 升级 satoken 导致白名单热更不生效问题 -* fix 修复 swagger 版本与 springdoc 版本不一致导致找不到class问题 -* fix 修复 grafana 监控模板绑定数据源ID 导致无法正常读取数据问题 - -## v1.3.0 - 2022-09-29 - -### 重大更新 - -* [重大更新] 新增 ruoyi-nacos 源码集成 nacos 服务端控制台 支持单机/集群模式 -* [重大更新] 重写 spring-cache 实现 更人性化的操作 支持注解指定ttl等一些参数 -* [重大更新] 新增 RuoYi-Cloud-Plus-UI 项目 Vue3 前端分支 -* [重大更新] 移除maven docker插件 过于老旧功能缺陷大 使用idea自带的docker插件替代 -* [重大更新] 优化 ruoyi-common-job 支持通过调度中心服务名注册 xxl-job-admin -* [重大更新] 新增 ruoyi-common-sentinel 模块 支持使用服务名注册 sentinel 控制台 - -### 依赖升级 - -* update spring-cloud 2021.0.3 => 2021.0.4 -* update springboot 2.7.2 => 2.7.4 -* update springboot-admin 2.7.3 => 2.7.5 -* update sentinel 1.8.4 => 1.8.5 集成新 dubbo3 插件 -* update springdoc 1.6.9 => 1.6.11 -* update easy-es 0.9.80 => 1.0.2 -* update dubbo 3.0.10 => 3.1.1 -* update redisson 3.17.5 => 3.17.6 -* update druid 1.2.11 => 1.2.12 -* update hutool 5.8.5 => 5.8.6 -* update dynamic-ds 3.5.1 => 3.5.2 -* update aws-java-sdk-s3 1.12.264 => 1.12.300 -* update aliyun-sms 2.0.16 => 2.0.18 -* update tencent-sms 3.1.555 => 3.1.591 -* update snakeyaml 1.30 => 1.32 - -### 功能更新 - -* update 优化 getLoginId 增加必要参数空校验 -* update 优化 将 elasticsearch 解压后放入 避免造成用户误解 -* update 优化 修改资料头像与部门被覆盖的问题 -* update 优化 字典管理操作类型新增其他 -* update 优化 使用 spring-cache 注解优化缓存 -* update 优化 easy-es.enable=false 关闭 actuator 健康检查 -* update 优化 优化多角色数据权限匹配规则 -* update dubbo 升级 3.1.0 删除自行处理的源码修复 采用官方修复后的代码 -* update 优化 页面内嵌iframe切换tab不刷新数据 -* update 优化 调整 oss表key 与 ossconfig的service 字段长度不匹配 -* update 优化 操作日志密码脱敏 -* update 优化 补全缺失的接口 更改更新日志链接 -* update 优化 插入 SysOperLog 时, 限制 operUrl 属性的长度 -* update 优化 satoken 鉴权拦截器 优化多次校验 - -### 新功能 - -* add 增加 项目中使用到的请求头放行跨域 -* add 新增 获取oss对象元数据方法 -* add 新增 字典管理操作类型 其他 - -### 问题修复 - -* fix 修复 个人中心卡死或鼠标点击和键盘输入无效 -* fix 修复 BaseMapperPlus 方法命令不一致问题 -* fix 修复 图片预览组件src属性为null值控制台报错问 -* fix 修复 短信功能是否启用判断不生效 -* fix 修复 web模块 不引入nacos依赖报错问题 -* fix 修复 sentinel 构建无法读取webapp目录问题 -* fix 修复 菜单管理遗漏的prop属性 -* fix 修复 minio配置https遇到的问题 -* fix 修复 点击删除后点击取消控制台报错问题 -* fix 修复 文件/图片上传组件 第一次上传报错导致后续上传无限loading问题 -* fix 修复 ruoyi-auth 服务与 elasticsearch 端口号冲突问题 -* fix 修复 ruoyi-resource 服务与 elasticsearch 端口号冲突问题 -* fix 修复 角色部门状态字典错误 与 菜单注释错误 -* fix 修复 hutool 存在多版本问题 -* fix 修复 openapi结构体 因springdoc缓存导致多次拼接接口路径问题 -* fix 修复 oss配置删除内部数据id匹配类型问题 -* fix 修复 没有权限的用户编辑部门缺少数据 -* fix 修复 用户导入存在则更新不生效 -* fix 修复 日志转换非json数据导致报错 -* fix 修复 p6spy输出sql语句时间格式化不正确问题 -* fix 修复 不同网段因reset请求头导致下载导出跨域问题 -* fix 修复 在线用户设置永不过期 超时时间-1推送redis无效问题 -* fix 修复 snakeyaml 1.31 依旧存在漏洞 升级 1.32 - -## v1.2.0 - 2022-08-09 - -### 重大更新 - -* [重大更新] 新增 ruoyi-common-elasticsearch 模块 集成 easy-es 傻瓜式操作搜索引擎 -* [重大更新] 新增 ruoyi-common-doc 整合 springdoc 基于 javadoc 实现无注解零入侵生成接口文档 -* [不兼容更新] 移除 swagger 所属 ruoyi-doc ruoyi-common-swagger 两个模块 建议使用 ruoyi-common-doc 模块 - -### 依赖升级 - -* update springboot 2.6.9 => 2.7.2 重构使用最新自动配置方式 -* update springboot-admin 2.6.7 => 2.7.3 -* update dubbo 3.0.9 => 3.0.10 -* update redisson 3.17.4 => 3.17.5 -* update hutool 5.8.3 => 5.8.5 -* update okhttp 4.9.1 => 4.10.0 -* update aws-java-sdk-s3 1.12.248 => 1.12.264 修复依赖安全漏洞 -* update aliyun.sms 2.0.9 => 2.0.16 -* update tencent.sms 3.1.537 => 3.1.555 -* update guava 30.0-jre => 31.1-jre - -### 功能更新 - -* update 修改 资源服务 不提供默认短信 sdk 依赖 -* update 优化表格上右侧工具条(搜索按钮显隐&右侧样式凸出) -* update 优化 前后端多环境部署保持一致 删除无用环境文件 -* update 优化 错误登录锁定与新增解锁功能 -* update 优化字典数据使用store存取 -* update 优化布局设置使用el-drawer抽屉显示 -* update 更新框架文档 专栏与视频 链接地址 -* update 优化 对象上传 主动设置文件公共读 解决天翼云OSS文件私有问题 -* update 优化 网关验证码过滤器 路径匹配改为严格匹配 -* update 优化 数据导致权限生成 SQL 重复问题 - -### 新功能 - -* add 增加 全局跨域过滤器 处理跨域请求 适配移动端访问 -* add 增加 搜索引擎 crud 演示案例 - -### 问题修复 - -* fix 防止date-picker组件报错,降级element-ui版本 -* fix 修复 RedisUtils 并发 set ttl 错误问题 -* fix 防止vue3主键字段名与row或ids一致导致报错的问题 -* fix 修复 幂等组件 逻辑问题导致线程变量未清除 -* fix 修复 图片回显查询 路径错误问题 -* fix 修复 脱敏没有实现类导致返回数据异常问题 -* fix 修复 xxljob 错误导入配置文件引发的问题 -* fix 修复 gateway模块 dockerfile 端口编写错误 -* fix 修复用户导出字典使用错误 -* fix 修复 demo 模块 远程调用失败问题 -* fix 修复 sentinel 控制台未适配 springboot 2.6 新路由策略导致无法登录问题 - -## v1.1.0 - 2022-07-18 - -### 重大更新 - -* [重大更新] 新增 ELK 分布式日志中心整合 -* [重大更新] 新增 ruoyi-stream-mq 演示模块 完成 RabbitMQ RocketMQ Kafka 整合 -* [重大更新] 优化 docker 部署方式 使用 host 模式简化部署流程 降低使用成本 -* [重大更新] 调整 dubbo 服务注册命名空间与 cloud 服务保持一致 通过注册组区分访问服务 -* [安全性] 优化 nginx 限制外网访问内网 actuator 相关路径 建议升级 - -### 依赖升级 - -* update springboot 2.6.8 => 2.6.9 -* update easyexcel 3.1.0 => 3.1.1 -* update hutool 5.8.2 => 5.8.3 -* update redisson 3.17.2 => 3.17.4 -* update aws-java-sdk-s3 1.12.215 => 1.12.248 -* update tencentcloud-sdk-java 3.1.500 => 3.1.537 -* update dubbo 3.0.8 => 3.0.9 -* update seata 1.5.1 => 1.5.2 - -### 功能更新 - -* update 增加 redisson key 前缀配置 -* update 优化 DateColumn 支持单模板多key场景 -* update 优化部署脚本 增加 elk kafka rabbitmq rocketmq 等配置 -* update 修改 oss 客户端自定义域名 统一使用https开关控制协议头 -* update 优化 使用 StreamUtils 简化业务流操纵 -* update 优化 ruoyi-demo 模块 去除用不上的 seata 依赖 -* update 优化 接口文档 接口地址与服务地址不匹配问题 -* update 优化字典数据回显样式下拉框显示值 -* update 默认不启用压缩文件缓存防止node_modules过大 -* update 优化登出方法 - -### 新功能 - -* add 增加 rocketmq docker编排 -* add 新增 rabbitmq docker编排 包含延迟插件 -* add 新增 kafka docker编排 -* add 增加 es ik 分词器插件集成 -* add 增加 StreamUtils 流工具 简化 stream 流操纵 - -### 问题修复 - -* fix 修复 获取 SensitiveService 空问题 增加空兼容 -* fix 修复 演示页面导出路径错误 -* fix 修复 minio 上传自定义域名回显路径错误问题 -* fix 修复 hutool 工具返回不可操纵类型 导致报错问题 -* fix 修复 远程调用短信功能返回实体 SysSms 序列化报错问题 -* fix 修复 复制过程错误 导致演示excel文件损坏问题 -* fix 修复 dubbo 注册组不生效问题 通过覆盖源码方式 -* fix 修复代码生成首字母大写问题 - - -## v1.0.0 - 2022-06-20 - -### 新增/优化 工程模块 - -* add 新增 ruoyi-common-alibaba-bom 工程管理 alibaba 相关依赖 -* add 新增 ruoyi-common-bom 工程管理 ruoyi-common 相关依赖 -* add 新增 ruoyi-api-bom 工程管理 ruoyi-api 依赖项 -* add 新增 ruoyi-api-resource 模块 规范用法 移除 ruoyi-file 模块 -* add 新增 ruoyi-common-web 模块 使用 undertow 替换 tomcat -* add 新增 ruoyi-common-dubbo 整合 dubbo 3.X 实现高性能 rpc 远程调用 替换 feign -* add 新增 ruoyi-common-dict 实现字典多服务调用 -* add 新增 ruoyi-common-loadbalancer 自定义负载均衡模块 用于多团队开发 -* add 新增 ruoyi-common-excel 模块 集成 Alibaba EasyExcel 替换 自带excel实现 -* add 新增 ruoyi-common-oss 模块 支持 AWS S3 协议 分布式文件存储 -* add 新增 ruoyi-common-mail 邮件模块 -* add 新增 ruoyi-common-sms 短信模块 整合 阿里云、腾讯云 短信功能 -* add 新增 ruoyi-common-idempotent 分布式幂等模块 -* add 新增 ruoyi-common-satoken 整合 sa-token 重写所有权限 -* add 新增 ruoyi-xxl-job-admin 整合 xxljob 替换 quartz 支持分布式任务调度 -* add 新增 ruoyi-job 模块 统一远程处理任务 规范用法 -* add 新增 ruoyi-doc 模块 集成 Knife4j 替换 swagger -* add 新增 ruoyi-seata-server 源码集成 Seata 1.5.X 服务端 -* add 新增 ruoyi-sentinel-dashboard 模块 源码集成 sentinel 控制台 -* update 抽取所有公用配置到 maven profile 管理 - -### 代码依赖改动 - -* update SpringCloud 2021.0.3 -* update 适配 SpringCloudAlibaba 2021.0.1.0 全新配置方式 -* update poi 4.1.2 => 5.2.2 性能大幅提升 -* update 重构 整合 jackson 替换 fastjson -* update 重构 整合 redisson 客户端 -* update 重构 整合 mybatis-plus -* update 重写 数据权限实现 基于 mybatis-plus -* add 增加 lombok 优化原生代码 -* add 整合 hutool 优化相关代码 -* add 新增 国际化 功能 -* add 新增 lock4j 分布式锁 -* add 增加监控中心 在线日志监控 优化日志文件格式 -* add 适配 docker 部署方式 - -### 后续/进行中计划 - -* 增加 Vue3 前端工程 -* 应用模块 适配 Oracle、PostgreSQL、SQLServer -* 增加 SpringCloud Stream 支持 -* 适配 Apache Kafka、Apache RocketMQ、RabbitMQ -* 适配 ElasticSearch 分布式搜索引擎 -* 适配 Alibaba Canal 分布式数据同步中心 -* 适配 Apache SkyWalking 分布式链路追踪监控中心 -* 适配 ELK 分布式日志中心 -* 适配 Prometheus、Grafana 分布式全方位数据大屏监控 diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/elk.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/elk.md deleted file mode 100644 index 58c2eda8..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/elk.md +++ /dev/null @@ -1,37 +0,0 @@ -# ELK搭建 -- - - -# 环境搭建 - -项目内置 `ELK` 的 `docker-compose` 编排 可查看 `/docker/docker-compose.yml` 文件下方扩展编排 - -**注意: `/docker/elk/elasticsearch/` 目录下所有文件夹 均需要写权限** - -`chmod 777 /docker/elk/elasticsearch/data`
-`chmod 777 /docker/elk/elasticsearch/logs`
-`chmod 777 /docker/elk/elasticsearch/plugins`
-**注意: es插件需要解压后放入 `plugins` 目录** - -# 运行命令 - -```shell -docker-compose up -d elasticsearch kibana logstash -``` - -# 参考文章 -[docker-compose 搭建 ELK 7.X 并整合 SpringBoot](https://lionli.blog.csdn.net/article/details/125743132) - -# 项目内配置 - -服务引入依赖项 - -```xml - - - com.ruoyi - ruoyi-common-logstash - -``` - -更改主 `pom` 文件 `logstash.address` 地址
- -![输入图片说明](https://foruda.gitee.com/images/1678981534923588112/ba6cb5b7_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/es.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/es.md deleted file mode 100644 index 65d9c4d4..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/es.md +++ /dev/null @@ -1,26 +0,0 @@ -# ES搜索引擎 -- - - -## 环境搭建(如果已经搭建了ELK则跳过) - -项目内置 `ELK` 的 `docker-compose` 编排 可查看 `/docker/docker-compose.yml` 文件下方扩展编排 - -**注意: `/docker/elk/elasticsearch/` 目录下所有文件夹 均需要写权限** - -`chmod 777 /docker/elk/elasticsearch/data`
-`chmod 777 /docker/elk/elasticsearch/logs`
-`chmod 777 /docker/elk/elasticsearch/plugins`
-**注意: es插件需要解压后放入 `plugins` 目录** - -## 运行命令 - -```shell -docker-compose up -d elasticsearch -``` - -## Easy-ES 文档 -[Easy-ES 文档](https://www.easy-es.cn/) - -## 用法 - -基本配置和用法可参考 `ruoyi-demo` 模块 更多高级用法请参考 Easy-ES 文档
-![输入图片说明](https://foruda.gitee.com/images/1660030085169129908/屏幕截图.png "屏幕截图.png") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/kafka.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/kafka.md deleted file mode 100644 index 78521431..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/kafka.md +++ /dev/null @@ -1,9 +0,0 @@ -# Kafka搭建 -- - - -## 环境搭建 -参考文章: [docker-compose 安装 Kafka 3.X 附带可视化界面](https://lionli.blog.csdn.net/article/details/125855550) - -## 用法参考 -参考 `ruoyi-stream-mq` 模块内的测试案例 - -![输入图片说明](https://foruda.gitee.com/images/1660031528265343174/屏幕截图.png "屏幕截图.png") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/maxkey.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/maxkey.md deleted file mode 100644 index 4b4b4305..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/maxkey.md +++ /dev/null @@ -1,20 +0,0 @@ -# 对接 MaxKey 单点登录 -- - - - -# 安装 MaxKey 应用服务 - -参考 MaxKey 官方文档安装 [MaxKey安装部署](http://www.maxkey.top/doc/docs/intro/) - -# 配置应用 OAuth2.0 认证注册 - -![输入图片说明](https://foruda.gitee.com/images/1693377802128677240/0927270a_1766278.png "屏幕截图") - -# 配置后端服务 - -找到 `Nacos` 内的 `ruoyi-auth.yml` 配置文件 - -修改 `maxkey` 对应的 `client-id` 与 `client-secret` - -![输入图片说明](https://foruda.gitee.com/images/1693378118762354596/2f02c8a3_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1693378168538263792/24476d2a_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/nacos.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/nacos.md deleted file mode 100644 index 18e6aefb..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/nacos.md +++ /dev/null @@ -1,13 +0,0 @@ -# Nacos集群搭建 -- - - -## 集群搭建两种方式 -### 文件寻址集群 -[【RuoYi-Cloud-Plus】学习笔记 02 - Nacos(二)寻址机制之文件寻址分析](https://blog.csdn.net/Michelle_Zhong/article/details/127423521) - -### 地址服务器寻址集群(推荐) -[【RuoYi-Cloud-Plus】学习笔记 03 - Nacos(三)使用 Nginx 实现地址服务器寻址及其原理分析](https://blog.csdn.net/Michelle_Zhong/article/details/127474238) - -## 集群路由代理设置 -[【RuoYi-Cloud-Plus】学习笔记 04 - Nacos(四)使用 Nginx 简单实现 Nacos 集群负载均衡](https://blog.csdn.net/Michelle_Zhong/article/details/127486350) - -设置好代理之后 跟单机用法一致 后端nacos地址写代理 `ip:端口` 即可 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/prometheus_grafana.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/prometheus_grafana.md deleted file mode 100644 index 2df4870b..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/prometheus_grafana.md +++ /dev/null @@ -1,45 +0,0 @@ -# Prometheus+Grafana搭建 -- - - -## 基础搭建 - -参考文章: https://lionli.blog.csdn.net/article/details/127959009 - -## 框架内扩展 - -框架已经包含了 docker-compose 编排 执行如下命令启动容器即可 - -```shell -docker-compose up -d prometheus grafana -``` - -## 应用配置 - -各个服务引入 `ruoyi-common-prometheus` 模块 - -![输入图片说明](https://foruda.gitee.com/images/1668998415863943539/413dc560_1766278.png "屏幕截图") - -修改 `prometheus.yml` 配置采集数据源 - -![输入图片说明](https://foruda.gitee.com/images/1668998433756761442/bf31c212_1766278.png "屏幕截图") - -修改 `Nacos` 地址 与 `SpringBoot-Admin` 监控地址 用于数据采集
-如都为本地应用则无需更改 - -![输入图片说明](https://foruda.gitee.com/images/1668998317973042740/2d3590ec_1766278.png "屏幕截图") - -## 导入框架特制模板 -**注意: 此处数据源名称必须与图片保持一致 不然会和模板对应不上导致无法读取数据**
-![输入图片说明](https://foruda.gitee.com/images/1669866309495145064/1de987ce_1766278.png "屏幕截图") - -> 找到框架内的特制模板json文件 在grafana点击上传json文件 导入模板
- -![输入图片说明](https://foruda.gitee.com/images/1668998149634542527/f0881c8e_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1668998179391197847/b1d3a630_1766278.png "屏幕截图") - -## 选择查看监控 - -点击右侧菜单浏览 选择想要查看的监控即可 - -![输入图片说明](https://foruda.gitee.com/images/1668998515814170229/817ac8b0_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1668998567335384306/acdf2833_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1668998616894681785/ac27538b_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/rabbitmq.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/rabbitmq.md deleted file mode 100644 index 75e0187c..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/rabbitmq.md +++ /dev/null @@ -1,10 +0,0 @@ -# RabbitMQ搭建 -- - - -## 环境搭建 - -参考文章: [docker-compose 安装 RabbitMQ 3.X 附带延迟队列插件](https://lionli.blog.csdn.net/article/details/125855177) - -## 用法参考 -参考 `ruoyi-stream-mq` 模块内的测试案例 - -![输入图片说明](https://foruda.gitee.com/images/1660031371503504748/屏幕截图.png "屏幕截图.png") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/rocketmq.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/rocketmq.md deleted file mode 100644 index 98d50bb7..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/rocketmq.md +++ /dev/null @@ -1,9 +0,0 @@ -# RocketMQ搭建 -- - - -## 环境搭建 -参考文章: [docker-compose 安装 RocketMQ 4.9.X (apache官方镜像) namesrv broker 与可视化控制台 console](https://lionli.blog.csdn.net/article/details/125798865) - -## 用法参考 -参考 `ruoyi-stream-mq` 模块内的测试案例 - -![输入图片说明](https://foruda.gitee.com/images/1660031496623275622/屏幕截图.png "屏幕截图.png") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/shardingproxy.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/shardingproxy.md deleted file mode 100644 index ebe461bc..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/shardingproxy.md +++ /dev/null @@ -1,75 +0,0 @@ -# Sharding-Proxy搭建分库分表 -- - - - -# 如何使用 - -查看 `ruoyi-demo` 服务 `TestShardingController` - -![输入图片说明](https://foruda.gitee.com/images/1688014028842337522/cd26026a_1766278.png "屏幕截图") - -## 首先在 MySQL 创建两个库 - -创建两个库 `data-center_0` `data-center_1` 分别执行如下SQL - -```sql -CREATE TABLE `t_order_0` ( - `order_id` bigint(20) UNSIGNED NOT NULL COMMENT '主键ID', - `user_id` bigint(20) UNSIGNED NOT NULL COMMENT '用户ID', - `total_money` int(10) UNSIGNED NOT NULL COMMENT '订单总金额', - PRIMARY KEY (`order_id`), - KEY `idx_user_id` (`user_id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单总表'; - -CREATE TABLE `t_order_1` ( - `order_id` bigint(20) UNSIGNED NOT NULL COMMENT '主键ID', - `user_id` bigint(20) UNSIGNED NOT NULL COMMENT '用户ID', - `total_money` int(10) UNSIGNED NOT NULL COMMENT '订单总金额', - PRIMARY KEY (`order_id`), - KEY `idx_user_id` (`user_id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单总表'; - -CREATE TABLE `t_order_item_0` ( - `order_item_id` bigint(20) UNSIGNED NOT NULL COMMENT '子订单ID', - `order_id` bigint(20) UNSIGNED NOT NULL COMMENT '主键ID', - `user_id` bigint(20) UNSIGNED NOT NULL COMMENT '用户ID', - `money` int(10) UNSIGNED NOT NULL COMMENT '子订单金额', - PRIMARY KEY (`order_item_id`), - KEY `idx_order_id` (`order_id`) USING BTREE, - KEY `idx_user_id` (`user_id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单子表'; - -CREATE TABLE `t_order_item_1` ( - `order_item_id` bigint(20) UNSIGNED NOT NULL COMMENT '子订单ID', - `order_id` bigint(20) UNSIGNED NOT NULL COMMENT '主键ID', - `user_id` bigint(20) UNSIGNED NOT NULL COMMENT '用户ID', - `money` int(10) UNSIGNED NOT NULL COMMENT '子订单金额', - PRIMARY KEY (`order_item_id`), - KEY `idx_order_id` (`order_id`) USING BTREE, - KEY `idx_user_id` (`user_id`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单子表'; - -``` - -## 然后更改配置文件 - -更改 `config-sharding.yaml` 配置文件内的数据库连接地址与用户名密码 - -## 服务搭建 - -参考部署文档上传 docker 文件夹 内部包含 `shardingproxy` 配置文件 - -![输入图片说明](https://foruda.gitee.com/images/1688013921062151295/89652dda_1766278.png "屏幕截图") - -框架已经包含了 docker-compose 编排 执行如下命令启动容器即可 - -```shell -docker-compose up -d shardingproxy -``` - -## 最后运行 demo - -运行 demo 提供的 controller 代码查看数据库内数据即可 - -## 用法参考视频(略有不同 理性观看) - -用法参考视频: https://www.bilibili.com/video/BV1XN411A7Tv/ diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/skywalking.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/skywalking.md deleted file mode 100644 index 6ad0aecc..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/extend-function/skywalking.md +++ /dev/null @@ -1,41 +0,0 @@ -# SkyWalking搭建与集成 -- - - -## 服务搭建 -参考文章: [SpringBoot 整合 SkyWalking 8.X (包含 Logback 日志采集)](https://lionli.blog.csdn.net/article/details/127656534) - -框架已经包含了 docker-compose 编排 执行如下命令启动容器即可 - -```shell -docker-compose up -d elasticsearch sky-oap sky-ui -``` - -### 本地开发使用 -参考上方文章 - -### docker部署使用 -上传探针到服务器 `/docker/skywalking/agent` 目录
-**不要使用网上下载的 请使用框架自带的 内含一些官网没有的插件**
-![输入图片说明](https://foruda.gitee.com/images/1667453098143152651/f1b4f492_1766278.png "屏幕截图") - -在对应服务的`dockerfile`内 打开 `skywalking` 相关参数注释
-![输入图片说明](https://foruda.gitee.com/images/1667452514896786032/f4322fb9_1766278.png "屏幕截图") - -服务编排增加探针路径映射
-![输入图片说明](https://foruda.gitee.com/images/1667453276389844864/7e139aa9_1766278.png "屏幕截图") - - -### 对接日志推送(不推荐 建议使用ELK收集日志) - -框架已经封装好了对应的依赖和配置 在服务内添加如下依赖 - -```xml - - - com.ruoyi - ruoyi-common-skylog - -``` - -在 `logback.xml` 日志配置文件内引入 `skylog` 配置文件 - -![输入图片说明](https://foruda.gitee.com/images/1667452697748002725/a18212cd_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/architecture_diagram.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/architecture_diagram.md deleted file mode 100644 index 05db07a3..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/architecture_diagram.md +++ /dev/null @@ -1,3 +0,0 @@ -# 软件架构图 -- - - -![输入图片说明](https://foruda.gitee.com/images/1722569321458793955/8672b1fc_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/collaboration.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/collaboration.md deleted file mode 100644 index 569aed75..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/collaboration.md +++ /dev/null @@ -1,27 +0,0 @@ -# 多团队开发 -- - - -## 功能介绍 - -> 多人员/团队开发往往会出现 调试程序 被负载均衡到别人那里 自己抓不到请求等问题
-> 正确团队开发模式 `测试机一台` 公共服务都放到测试机上
-> 本地开发人员 需启动 `ruoyi-gateway` 与 其他 调试的业务模块
-> 将所有服务都统一指向同一个 Nacos 服务
-> 前端连接本机 `ruoyi-gateway` 网关调试程序
- -框架提供了 `ruoyi-common-loadbalancer` 多团队 负载均衡模块 可以将网关的请求锁定到与网关相同的IP服务 - -需要在 `ruoyi-gateway` `ruoyi-auth` `ruoyi-modules` 引入 `ruoyi-common-loadbalancer` 模块 - -![输入图片说明](https://foruda.gitee.com/images/1678980590168990366/afa2fdf6_1766278.png "屏幕截图") - -启动前端访问本机 `ruoyi-gateway` 网关在请求转发 和 `dubbo` 进行 RPC 调用时
-会获取与本机IP地址相同的服务优先调用(如未找到 会随机返回) - -# 重点说明 - -请检查本机是否有虚机网卡IP 如有多网卡获取IP地址会不准确 - -可使用如下代码检查本机IP是否正常 -```java -InetAddress.getLocalHost().getHostAddress() -``` \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/doc.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/doc.md deleted file mode 100644 index 422aeb9e..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/doc.md +++ /dev/null @@ -1,88 +0,0 @@ -# 接口文档 -- - - -## 版本 >= `1.2.0` -## 说明 -由于 `springfox` 与 `knife4j` 均停止维护 bug众多
-故从 `1.2.0` 开始 迁移到 `springdoc` 框架
-基于 `javadoc` 无注解零入侵生成规范的 `openapi` 结构体
-由于框架自带文档UI功能单一扩展性差 故移除自带UI 建议使用外置文档工具 - -## 文档工具使用 -由于框架采用 `openapi` 行业规范 故市面上大部分的框架均支持 可自行选择
-例如: `apifox` `apipost` `postman` `torna` `knife4j` 等 根据对应工具的文档接入即可 - -## Swagger升级SpringDoc指南 - -常见功能如下 其他功能自行挖掘
-**注意: `javadoc` 只能替换基础功能 特殊功能还需要使用注解实现** - -| swagger | springdoc | javadoc | -|----------------------------------|---------------------------------|--------------------| -| @Api(name = "xxx") | @Tag(name = "xxx") | java类注释第一行 | -| @Api(description= "xxx") | @Tag(description= "xxx") | java类注释 | -| @ApiOperation | @Operation | java方法注释 | -| @ApiIgnore | @Hidden | 无 | -| @ApiParam | @Parameter | java方法@param参数注释 | -| @ApiImplicitParam | @Parameter | java方法@param参数注释 | -| @ApiImplicitParams | @Parameters | 多个@param参数注释 | -| @ApiModel | @Schema | java实体类注释 | -| @ApiModelProperty | @Schema | java属性注释 | -| @ApiModelProperty(hidden = true) | @Schema(accessMode = READ_ONLY) | 无 | -| @ApiResponse | @ApiResponse | java方法@return返回值注释 | - -# 建议使用 `Apifox`(常见问题有其他对接方式) - -官网连接: [https://www.apifox.cn/](https://www.apifox.cn/)
-视频教程: [springdoc与apifox配合使用](https://www.bilibili.com/video/BV1mr4y1j75M?p=8&vd_source=8f52c77be3233dbdd1c5e332d4d45bfb) - -![输入图片说明](https://foruda.gitee.com/images/1678976476639902970/f1617b40_1766278.png "屏幕截图") - -支持 文档编写 接口调试 Mock 接口压测 自动化测试 等一系列功能 - -### 接入框架 - -> 1.下载或使用web在线版 创建一个自己的项目
- -![输入图片说明](https://foruda.gitee.com/images/1678976502850663851/7bbd8728_1766278.png "屏幕截图") - -> 2.进入项目 选择项目设置 找到自动同步
- -![输入图片说明](https://foruda.gitee.com/images/1678976508918240326/6a4a61a8_1766278.png "屏幕截图") - -> 3.根据项目内所有文档组完成所有数据源创建(拉取后端`openapi`结构体)
-数据源URL格式 `http://网关ip:端口/服务路径/v3/api-docs`
-项目内所需:
-`http://localhost:8080/demo/v3/api-docs` 演示服务
-`http://localhost:8080/auth/v3/api-docs` 认证服务
-`http://localhost:8080/resource/v3/api-docs` 资源服务
-`http://localhost:8080/system/v3/api-docs` 系统服务
-`http://localhost:8080/code/v3/api-docs` 代码生成服务
- -![输入图片说明](https://foruda.gitee.com/images/1678980352012289965/24e0e4da_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678980368645148754/62308680_1766278.png "屏幕截图") - -> 4.选择 接口管理 项目概览 点击立即导入 并等待导入完成
-后续会根据策略每3个小时自动导入一次
-每次重新进入apifox也会自动同步一次
-后端有改动也可以手动点击导入
- -![输入图片说明](https://foruda.gitee.com/images/1678980393851604773/a0c657d3_1766278.png "屏幕截图") - -> 5.(注意版本号)设置鉴权 选择接口管理 项目概览 找到Auth 按照如下配置
- -**版本号: >= 2.X** - -![输入图片说明](https://foruda.gitee.com/images/1690966897370710566/6a688aea_1766278.png "屏幕截图") - -**版本号: 1.X** - -![输入图片说明](https://foruda.gitee.com/images/1678980398409729963/db4502a0_1766278.png "屏幕截图") - -> key对应项目配置 默认为 `Authorization`
- -![输入图片说明](https://foruda.gitee.com/images/1678976544342001474/c2ff85d3_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678976549237304743/bcdfadda_1766278.png "屏幕截图") - - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/i18n.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/i18n.md deleted file mode 100644 index 304d3e04..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/i18n.md +++ /dev/null @@ -1,31 +0,0 @@ -# 国际化方案 -- - - -* 前端国际化参考 [ruoyi前端国际化文档](http://doc.ruoyi.vip/ruoyi-vue/document/htsc.html#前端国际化流程)
-* 参考 `demo` 模块 `TestI18nController` 国际化演示案例 - 在 `Header` 请求头 增加上下文语言参数 `content-language` 参数需与国际化配置文件后缀对应 - 如 `zh_CN` `en_US` 等
- -![输入图片说明](https://foruda.gitee.com/images/1678976722892396585/60917594_1766278.png "屏幕截图") - -## 获取 `code` 对应国际化内容 - -![输入图片说明](https://foruda.gitee.com/images/1678976728533100954/0ab8e36a_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976733019209506/a16574d6_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976738409745057/a073b425_1766278.png "屏幕截图") - -## 使用 `Validator` 框架校验 `controller` 参数返回国际化 - -`controller` 校验接口参数 需要在类增加 `@Validated` 注解
-![输入图片说明](https://foruda.gitee.com/images/1678976741834729507/6c19b9cc_1766278.png "屏幕截图")
-参数对应校验注解 使用 `{code}` 形式标注使用国际化处理
-![输入图片说明](https://foruda.gitee.com/images/1678976746093285542/ad0989db_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976750822808564/56bd60d7_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976754755107198/b89bf173_1766278.png "屏幕截图") - -## 使用 `Validator` 框架校验 `Bean` 返回国际化 - -`Bean` 校验需要在接口校验 `Bean` 参数使用 `@Validated` 注解
-![输入图片说明](https://foruda.gitee.com/images/1678976761015767874/729da3bc_1766278.png "屏幕截图")
-`Bean` 内属性校验注解 使用 `{code}` 形式标注使用国际化处理
-![输入图片说明](https://foruda.gitee.com/images/1678976765122587920/0b1027af_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976769965314387/0c416ede_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/inner_authentication.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/inner_authentication.md deleted file mode 100644 index c90d2c61..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/inner_authentication.md +++ /dev/null @@ -1,19 +0,0 @@ -# 内网鉴权 -- - - -## 功能介绍 - -此功能用于防止外部请求访问内部服务应用
-在请求经过 `gateway网关` 会生成一个 `id-token` 携带到后续服务进行校验
-若未经过 `gateway网关` 调用内网服务 会出现 `id-token无效` 异常
-有效防止非法请求直接访问内网服务
- -## 开启/关闭内网鉴权 - -更改 `application-common.yml` 配置文件的 `sa-token.check-id-token` 配置即可 - -![输入图片说明](https://foruda.gitee.com/images/1678980608778275681/9a2c1054_1766278.png "屏幕截图") - -## 放行内网鉴权 -进入 `ruoyi-common-security` 模块找到 `SecurityConfiguration` 类 增加排除路径即可 - -![输入图片说明](https://foruda.gitee.com/images/1678980612657326393/cea32a8c_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/new_module.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/new_module.md deleted file mode 100644 index 0d75525b..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/new_module.md +++ /dev/null @@ -1,39 +0,0 @@ -# 创建新服务 -- - - -### 最简单的方式 -> 找个配置好的 例如 `ruoyi-system` 直接copy一份 - -> 将 `pom` 名称改掉
- -![输入图片说明](https://foruda.gitee.com/images/1678980168782983123/c717e9ba_1766278.png "屏幕截图") - -> 服务启动类 名称改掉
- -![输入图片说明](https://foruda.gitee.com/images/1678980179829877203/f89d5c18_1766278.png "屏幕截图") - -> `application.yml` 配置服务应用名 改掉
- -![输入图片说明](https://foruda.gitee.com/images/1678980184047648028/e4c6c6cc_1766278.png "屏幕截图") - -> `nacos` 新建一份新的 对应新模块名称的 配置文件
-![输入图片说明](https://foruda.gitee.com/images/1678980188806372269/cfd9731a_1766278.png "屏幕截图") - -更改 `nacos` 上的 `ruoyi-gateway.yml` 增加新服务路由
-新服务访问路径 `网关ip:端口/服务路径/controller路径/接口路径`
-例子: `http://localhost:8080/system/user/list`
- -![输入图片说明](https://foruda.gitee.com/images/1666861595048863422/9e9755b3_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1666861629037264535/bdfd5484_1766278.png "屏幕截图") - -### 注意事项 -如果是两个不同包名的模块 需要修改如下配置 - -![输入图片说明](https://foruda.gitee.com/images/1719813861680271619/82435586_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1692006501957936219/059f8526_1766278.png "屏幕截图") - -如果新服务需要使用 `seata` 分布式事务
-需要在 `nacos` 上的 `seata-server.properties` 文件内增加服务组 - -![输入图片说明](https://foruda.gitee.com/images/1692006825427360840/5b9e410c_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/update_package_name.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/update_package_name.md deleted file mode 100644 index 2dcab848..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/update_package_name.md +++ /dev/null @@ -1,33 +0,0 @@ -# 关于修改包名 -- - - - -**注意: 老包名为 com.ruoyi** - -## 1.随便找个地方新建 org.dromara 包 -![输入图片说明](https://foruda.gitee.com/images/1708491220807198688/b95c0c34_1766278.png "屏幕截图") - -## 2.在包上右键选择 refactor -> rename 选择 All Directories -![输入图片说明](https://foruda.gitee.com/images/1683276891079076405/79808b22_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1708491697128844860/1e87ad39_1766278.png "屏幕截图") - -**因为dromara组织下有很多依赖导致idea无法识别完整包名** -
-![输入图片说明](https://foruda.gitee.com/images/1708490576909691001/692e5b37_1766278.png "屏幕截图") - -**需要先将dromara修改为 例如: ruoyi 然后重复上述步骤 这样就可以整包修改了** -
-![输入图片说明](https://foruda.gitee.com/images/1708490906933084793/ff104cd7_1766278.png "屏幕截图") - -## 3.使用IDEA全局替换 org.dromara 替换为 com.xxx - -![输入图片说明](https://foruda.gitee.com/images/1708491055347995519/dedda0d1_1766278.png "屏幕截图") - -**注意: 由于dromara组织下项目很多 非本框架的依赖模块 请勿修改 例如上图中的 org.dromara.sms4j** - -## 4.如有需要 将所有模块名逐一修改即可 - -## 5.修改完成后需查看所有common包下模块spi文件是否修改正确 - -**老版本idea或者未按照教程修改包名可能导致文件丢包问题** - -![输入图片说明](https://foruda.gitee.com/images/1708491365841192006/8bc337c2_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/update_url.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/update_url.md deleted file mode 100644 index d36012f5..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/association/update_url.md +++ /dev/null @@ -1,25 +0,0 @@ -# 修改应用路径 -- - - -# 修改访问后端接口路径 - -更改 前端环境配置文件 `VITE_APP_BASE_API` 代理路径 - -![输入图片说明](https://foruda.gitee.com/images/1661824572484410642/14265f05_1766278.png "屏幕截图")
- -![输入图片说明](https://foruda.gitee.com/images/1724317552931269967/f7515655_1766278.png "屏幕截图") - -`prod` 生产环境需修改 `nginx.conf` 后端代理路径(上述配置文件也要改) - -![输入图片说明](https://foruda.gitee.com/images/1678980501204821424/d3340308_1766278.png "屏幕截图") - -# 修改前端页面访问路径 -修改对应环境的 `.env.环境` 文件内的 `VITE_APP_CONTEXT_PATH` 应用访问路径即可 - -![输入图片说明](https://foruda.gitee.com/images/1661824572484410642/14265f05_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1724317049535973756/0a2cc43b_1766278.png "屏幕截图") - -生产环境 `nginx.conf` 与之对应修改即可
-**注意: 文件真实目录为 `/usr/share/nginx/html/admin/index.html` 此功能一般为多项目部署需要 故会增加一层目录 如不需要可以自行修改**
- -![输入图片说明](https://foruda.gitee.com/images/1678976662194341301/2720b7e9_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/client.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/client.md deleted file mode 100644 index 66ae32a9..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/client.md +++ /dev/null @@ -1,85 +0,0 @@ -# 客户端管理功能 -- - - -## 版本 >= 2.X - -## 客户端管理页面 - -![输入图片说明](https://foruda.gitee.com/images/1690961819029076660/c44374ac_4959041.png "屏幕截图") - -### 客户端管理字段说明 -| 字段名称 | 取值说明 | 注意事项 | -|----------------|----------------------------|--------------------------------| -| 客户端id | 由后端生成,用于前端登录校验以及接口数据加密 | 无法修改,不要删除默认数据,否则会报错 | -| 客户端key | 前端自定义 | 无法修改,不要删除默认数据,否则会报错 | -| 客户端秘钥 | 前端自定义 | 无法修改,不要删除默认数据,否则会报错 | -| 授权类型 | 密码认证、短信认证、邮件认证、小程序认证、第三方认证 | 根据授权类型判断当前客户端是否支持该登录方式 | -| 设备类型 | PC端、APP端 | | -| Token活跃超时时间 | 自定义 | 指定时间无操作则过期(单位:秒),默认30分钟(1800秒) | -| Token固定超时时间 | 自定义 | 指定时间必定过期(单位:秒),默认七天(604800秒) | - -### 前后端使用新的客户端id - -步骤如下: -1. 前端管理页面生成新的客户端id。 -2. 将新的客户端id复制到前端配置文件。 - -![输入图片说明](https://foruda.gitee.com/images/1690962894318847386/133d2f90_4959041.png "屏幕截图") - -## 新增自定义客户端 - -### 步骤一:新增客户端数据(例如增加小程序端) - -![输入图片说明](https://foruda.gitee.com/images/1690965463070099188/baeb4441_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1690965508836621042/df06248f_4959041.png "屏幕截图") - -### 步骤二:配置前端请求头信息 - -需要在全局请求头 header 中增加 cientid
-确保客户端所有请求都携带此id 可参考项目 `request.ts` - -![输入图片说明](https://foruda.gitee.com/images/1690965768235114596/980b88d2_4959041.png "屏幕截图") - -`VITE_APP_CLIENT_ID` 即配置文件中的客户端id。 - -**重点:不同客户端登录获取到的token不同与其他端不互通(例如: app登录获取到的token无法用于pc端接口查询)** - -## 新增自定义登录方式授权类型 - -**重点说明: 不要单独增加登录接口 系统全局统一只有一个登录接口 只需增加不同的鉴权方式即可** - -如何调试使用登录看这里 -> [关于登录调试步骤](/questions/login_step.md) - -### 步骤一:新增字典数据 - -![输入图片说明](https://foruda.gitee.com/images/1690968849418013624/3b28417e_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1690968865819397010/64529fad_4959041.png "屏幕截图") - -### 步骤二:新增/修改客户端数据 - -### 步骤三:后端新增认证策略 - -新增策略实现类实现 `IAuthStrategy` 接口。
- -![输入图片说明](https://foruda.gitee.com/images/1690972828588111954/7614a4c5_4959041.png "屏幕截图") - -参照已有策略实现类实现自定义参数校验登录方法逻辑。
- -![输入图片说明](https://foruda.gitee.com/images/1718951146945578143/789c80e4_1766278.png "屏幕截图") - -**注意修改 `@Service` 名称保证规范性** - -![输入图片说明](https://foruda.gitee.com/images/1718951179571300385/8db730b9_1766278.png "屏幕截图") - -`LoginBody` 校验参数(可自定义)
- -![输入图片说明](https://foruda.gitee.com/images/1718951237123374392/f7840db2_1766278.png "屏幕截图") - -例如 扩展小程序登录参数 只需要继承 `LoginBody
- -![输入图片说明](https://foruda.gitee.com/images/1718951283931895761/e6348be5_1766278.png "屏幕截图")` - -校验分组(可自定义)
- -![输入图片说明](https://foruda.gitee.com/images/1718951343601334215/8ef404b4_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/code_generate.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/code_generate.md deleted file mode 100644 index 742ef939..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/code_generate.md +++ /dev/null @@ -1,86 +0,0 @@ -# 代码生成 -- - - -## 功能介绍 - -### 数据源配置 - -![输入图片说明](https://foruda.gitee.com/images/1678976867341325193/a2be0608_1766278.png "屏幕截图") - -**项目适配多种类型数据库 可以在代码生成页面切换**
- -> 填写对应的数据源名称 点击搜索按钮 即可切换到对应的数据源
- -![输入图片说明](https://foruda.gitee.com/images/1678976876081856486/4ef4841c_1766278.png "屏幕截图") - -**>= 2.2.1版本 项目支持100+种数据库适配 在代码生成模块增加对应的数据库依赖即可**
- -![输入图片说明](https://foruda.gitee.com/images/1722396530340741054/3914eb72_1766278.png "屏幕截图") - - -### 导入数据表 - -> 点击导入按钮 会加载系统数据库所有的表
- -![输入图片说明](https://foruda.gitee.com/images/1678976880393939803/3ecf1dcc_1766278.png "屏幕截图") - -> 选择需要的表 点击确定即可
- -![输入图片说明](https://foruda.gitee.com/images/1678976885370716109/4834faa5_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976891856866728/853420d9_1766278.png "屏幕截图") - -### 编辑表生成结构 - -> 点击表对应的编辑按钮
- -![输入图片说明](https://foruda.gitee.com/images/1678976899111822310/aeaa33f9_1766278.png "屏幕截图") - -> 更改要生成表的数据
- -![输入图片说明](https://foruda.gitee.com/images/1678976903345795925/4326f6ee_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976908897387614/4cdf939b_1766278.png "屏幕截图") - -### 生成条件影响 - -![输入图片说明](https://foruda.gitee.com/images/1678976913809284051/24da09b0_1766278.png "屏幕截图") - - -* `插入` `编辑` 影响生成 BO 类 与 前端添加编辑页面 是否有该字段 -* `列表` 影响生成 VO 类 与 前端列表页面展示 是否有该字段 -* `查询` 影响 前端页面是否有该字段的搜索框 与 后端代码是否生成对应的查询条件 -* `查询方式` 影响生成查询条件的类型 -* `必填` 影响 BO 类 与 页面是否强制校验 -* `显示类型` 影响生成页面使用何种展示组件 -* `字典类型` 影响页面是否生成与字典的关联 - -### 树表配置 - -> 编辑表生成信息 生成模板为 `树表` 填写对应数据即可
- -![输入图片说明](https://foruda.gitee.com/images/1678976917918548901/f5886c5c_1766278.png "屏幕截图") - -### 主子表说明 - -框架不支持也不推荐使用主子表
-原因一般业务场景 基本都是一对N表 多表关联场景
-还有一些 主 => 子 <= 主 场景 需求很复杂 很少有单纯主子表场景出现
-另外主子表关联 很容易出现 笛卡尔积 或者数据错乱等问题 需要自行sql调优场景
-所以建议大家都按照 单表生成 自行编写业务逻辑 - -### 预览功能 - -> 配置好生成信息后 可以点击预览按钮
- -![输入图片说明](https://foruda.gitee.com/images/1678976924411765532/2e9747df_1766278.png "屏幕截图") - -> 系统会根据已经配置好的数据 生成对应的代码预览
-> 可以再此处观察代码的生成结构和数据是否正确等
- -![输入图片说明](https://foruda.gitee.com/images/1678976945982406065/ca7383bb_1766278.png "屏幕截图") - - -### 代码结构同步 - -> 实际开发中 难免会有表结构更改的需求
-> 这时可以使用 同步功能 点击同步按钮 即可与实时数据库表进行字段同步
- -![输入图片说明](https://foruda.gitee.com/images/1678976952919156537/3c47c078_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/export.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/export.md deleted file mode 100644 index fa901792..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/export.md +++ /dev/null @@ -1,250 +0,0 @@ -# 导出功能 - -- - - - -在本框架中引入了 `Easy Excel` 依赖(对 `Apache POI`进行了封装以及扩展),可以对数据进行导出操作(即写 Excel)。 - -[EasyExcel 文档地址](https://easyexcel.opensource.alibaba.com/) - -## 导出功能使用流程说明 - -### 步骤一:定义导出实体对象 - -以框架中 `SysUserExportVo` 为例: - -```Java - /** - * 用户ID - */ - @ExcelProperty(value = "用户序号") - private Long userId; - - // ....................... - - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_user_sex") - private String sex; - - /** - * 帐号状态(0正常 1停用) - */ - @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_normal_disable") - private String status; -``` - -> 说明:
-> 1. 使用 `@ExcelProperty` 注解标注需要导出的属性。 -> 2. 注解 `@ExcelProperty` 中 `value` 属性代表表格头部标题字段,`converter` 代表使用的转换器,后面会详细说明。 -> 3. 注解 `@ExcelDictFormat` 为自定义注解,与自定义转换器结合使用,同样在后面进行详细说明。 - -### 步骤二:使用导出方法 - -以框架中 `SysUserController#export` 方法为例: - -```Java - /** - * 导出用户列表 - */ - @PostMapping("/export") - public void export(SysUserBo user, HttpServletResponse response) { - // 根据参数查询导出的用户列表数据 - List list = userService.selectUserList(user); - // 将列表转换为导出对象列表 - List listVo = MapstructUtils.convert(list, SysUserExportVo.class); - // 导出方法 - ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response); - } -``` - -> 说明:
-> 使用 `ExcelUtil.exportExcel` 方法完成导出功能,上述 Demo 传入参数分别是:导出对象集合,Excel sheet 表名称,导出对象类型,response。 - -## 框架工具使用说明 - -### 1:字典转换器 - -字典转换器 `ExcelDictConvert` 与自定义注解 `@ExcelDictFormat` 结合使用,标注在需要转换的属性上。 - -使用方式一: - -```Java - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_user_sex") - private String sex; -``` - -使用方式二: - -```Java - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(readConverterExp="0=男,1=女,2=未知", separator=",") - private String sex; -``` - -`@ExcelDictFormat` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|------------------|--------|-----|-----------------------------------| -| dictType | String | "" | 字典的type值 (如: sys_user_sex) | -| readConverterExp | String | "" | 读取内容转表达式 (如: 0=男,1=女,2=未知) | -| separator | String | "," | 与 readConverterExp 属性结合使用,表达式的分隔符 | - -### 2:枚举转换器 - -字典转换器 `ExcelEnumConvert` 与自定义注解 `@ExcelEnumFormat` 结合使用,标注在需要转换的属性上。 - -使用方式: - -```Java - /** - * 用户类型 - *

- * 使用ExcelEnumFormat注解需要进行下拉选的部分 - */ - @ExcelProperty(value = "用户类型", index = 1, converter = ExcelEnumConvert.class) - @ExcelEnumFormat(enumClass = UserStatus.class, textField = "info") - private String userStatus; -``` - -`@ExcelEnumFormat` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|-----------|------------|------|------------------------------| -| enumClass | Enum Class | - | 字典枚举类型 | -| codeField | String | code | 字典枚举类中对应的 code 属性名称,默认为 code | -| textField | String | text | 字典枚举类中对应的 text 属性名称,默认为 text | - -### 3:合并单元格 - -`@CellMerge` 注解用于合并相同的列数据,需要结合 `CellMergeStrategy` 策略使用,标注在需要转换的属性上。 - -使用方式: - -步骤一:在属性标注 `@CellMerge` 注解: -```Java - /** - * 部门id - */ - @CellMerge - @ExcelProperty(value = "部门id") - private Long deptId; -``` - -`@CellMerge` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|---------|----------|-----|------------------------------| -| index | int | -1 | 合并列的下标,建议使用默认值 | -| mergeBy | String[] | {} | 合并需要依赖的其他字段名称(基于这个字段内容做合并条件) | - - -步骤二:导出方法开启合并: -```Java - /** - * 导出测试单表列表 - */ - @PostMapping("/export") - public void export(@Validated TestDemoBo bo, HttpServletResponse response) { - List list = testDemoService.queryList(bo); - // 参数 true 表示开启合并单元格策略 - ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, true, response); - } -``` -![输入图片说明](https://foruda.gitee.com/images/1700128921644543994/e8d4704f_1766278.png "屏幕截图") - -### 4:复杂 Excel 导出示例 -`TestExcelController` 提供了几种导出示例,如果需要可以参照相应方法进行导出。 - -#### 4.1:单列表多数据导出(模板导出) - -模板内容: - -![输入图片说明](https://foruda.gitee.com/images/1700124852002972562/d9f57a8c_4959041.png "屏幕截图") - -模板位置:`ruoyi-example/ruoyi-demo/src/main/resources/excel/` - -导出示例代码:参考 demo 模块 `TestExcelController` 模板写法请查看 `EasyExcel` 文档 - -导出结果: - -![输入图片说明](https://foruda.gitee.com/images/1700124885532359879/0d011d05_4959041.png "屏幕截图") - -#### 4.2:多列表多数据导出(模板导出) - -模板内容: - -![输入图片说明](https://foruda.gitee.com/images/1700125025931981176/105dbaaa_4959041.png "屏幕截图") - -模板位置:`ruoyi-example/ruoyi-demo/src/main/resources/excel/` - -导出示例代码:参考 demo 模块 `TestExcelController` 模板写法请查看 `EasyExcel` 文档 - -导出结果: - -![输入图片说明](https://foruda.gitee.com/images/1700125054011300002/71869c1d_4959041.png "屏幕截图") - -#### 4.3:导出下拉框 - -`ExcelDictFormat` 注解指定的字典项默认都会转换成下拉框 - -自定义导出省市区下拉框示例代码:参考 demo 模块 `TestExcelController` - -导出结果: - -![输入图片说明](https://foruda.gitee.com/images/1700125265411678973/7f767719_4959041.png "屏幕截图") - -## Easy Excel 常用注解 - -`Easy Excel` 提供了丰富的注解可以对导出对象进行定制化操作,这里的注解说明针对的是原生注解,自定义注解会结合转换器一起进行说明。 - -| 类型 | 注解名称 | 使用举例 | 说明 | -|-------|-------------------------|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| -| 格式化注解 | @DateTimeFormat | @DateTimeFormat(value=格式化值) | 对字符串进行日期格式化 (参照 `java.text.SimpleDateFormat` 书写即可) | -| 格式化注解 | @NumberFormat | @NumberFormat(value=格式化值, roundingMode=舍入模式) | 对字符串进行数值格式化 (参照 `java.text.DecimalFormat` 书写即可, `roundingMode` 默认 `RoundingMode.HALF_UP`) | -| 样式注解 | @ColumnWidth | @ColumnWidth(value=值) | 设置列宽 | -| 样式注解 | @ContentFontStyle | @ContentFontStyle(color=颜色) | 可以设置字体类型,颜色,粗细,是否斜体,下划线等,具体可查看注解 `@ContentFontStyle` | -| 样式注解 | @ContentLoopMerge | @ContentLoopMerge(eachRow=行值, columnExtend=列值) | 设置循环合并的区域 | -| 样式注解 | @ContentRowHeight | @ContentRowHeight(value=值) | 设置内容行高 | -| 样式注解 | @ContentStyle | - | 设置单元格样式,具体可查看注解 `@ContentStyle` | -| 样式注解 | @HeadFontStyle | @HeadFontStyle(color=颜色) | 设置表头字体格式,类似 `@ContentFontStyle`,具体可查看注解 `@HeadFontStyle` | -| 样式注解 | @HeadRowHeight | @HeadRowHeight(value=值) | 设置表头行高 | -| 样式注解 | @HeadStyle | - | 设置表头样式,具体可查看注解 `@HeadStyle` | -| 样式注解 | @OnceAbsoluteMerge | @OnceAbsoluteMerge(firstRowIndex=开始行下标, lastRowIndex=结束行下标, firstColumnIndex=开始列下标, lastColumnIndex=结束列下标) | 根据设置值合并单元格 | -| 属性注解 | @ExcelIgnore | @ExcelIgnore | 导出忽略该字段 | -| 属性注解 | @ExcelIgnoreUnannotated | @ExcelIgnoreUnannotated | 默认不管加不加 `@ExcelProperty` 的注解的所有字段都会参与读写,加了 `@ExcelIgnoreUnannotated` 注解以后,不加 `@ExcelProperty` 注解的字段就不会参与 | -| 属性注解 | @ExcelProperty | @ExcelProperty(value=值, order=排序值, index=下标, converter=转换器) | 默认按照对象属性顺序导出,如果设置了 `order` 以及 `index`,优先级 `index` > `order` > 默认;converter 可以自定义 | - -## 扩展说明 - -### 自定义转换器实现 - -由于业务需要,原生注解不一定能够符合需要,因而衍生出了自定义转换器。能够实现定制化的内容转换需要。 -以下以框架中的字典转换器 `ExcelDictConvert` 为例进行说明。 - -字典转换器 `ExcelDictConvert`,字典转换器使用了自定义注解 `@ExcelDictFormat` 配合使用。 - -_**注:自定义转换器并非一定需要自定义注解,也可以针对已有的注解进行自定义转换实现。**_ - -#### 实现方式 - -自定义转换器需要实现 `com.alibaba.excel.converters.Converter` 接口,实现接口中的方法。 - -![输入图片说明](https://foruda.gitee.com/images/1700104014304819918/33eb0c42_4959041.png "屏幕截图") - -转换方法 `ExcelDictConvert#convertToExcelData` : - -![输入图片说明](https://foruda.gitee.com/images/1700104426131801297/72931ef0_4959041.png "屏幕截图") - -## 更多功能 - -更多导出功能使用可以参照 `Easy Excel` [官方文档](https://easyexcel.opensource.alibaba.com/docs/current/api/write)。 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/import.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/import.md deleted file mode 100644 index f1bbca7d..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/import.md +++ /dev/null @@ -1,202 +0,0 @@ -# 导入功能 -- - - - -在本框架中引入了 `Easy Excel` 依赖(对 `Apache POI`进行了封装以及扩展),可以对数据进行导入操作(即读 Excel)。 - -## 导入功能使用流程说明 - -### 步骤一:定义导入实体对象 - -以框架中 `SysUserImportVo` 为例: - -```java - /** - * 用户ID - */ - @ExcelProperty(value = "用户序号") - private Long userId; - - // ....................... - - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_user_sex") - private String sex; - - /** - * 帐号状态(0正常 1停用) - */ - @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_normal_disable") - private String status; -``` - -> 说明:
-> 1. 使用 `@ExcelProperty` 注解标注需要导入的属性。 -> 2. 注解 `@ExcelProperty` 中 `value` 属性代表表格头部标题字段,`converter` 代表使用的转换器,后面会详细说明。 -> 3. 注解 `@ExcelDictFormat` 为自定义注解,与自定义转换器结合使用,同样在后面进行详细说明。 -> 4. 对象禁止使用链式注解 `@Accessors(chain = true)`,会找不到set方法。 - -### 步骤二:使用导入方法 - -以框架中 `SysUserController#importData` 方法为例: - -```Java - /** - * 导入数据 - * - * @param file 导入文件 - * @param updateSupport 是否更新已存在数据 - */ - @Log(title = "用户管理", businessType = BusinessType.IMPORT) - @SaCheckPermission("system:user:import") - @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public R importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception { - // 导入方法 - ExcelResult result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport)); - return R.ok(result.getAnalysis()); - } -``` -> 说明:
-> 使用 `ExcelUtil.importExcel` 方法完成导出功能,上述 Demo 传入参数分别是:导入文件流,导入对象类型,导入监听器 `SysUserImportListener`。 - -## 框架工具使用说明 - -### 1:字典转换器 - -字典转换器 `ExcelDictConvert` 与自定义注解 `@ExcelDictFormat` 结合使用,标注在需要转换的属性上。 - -使用方式一: - -```Java - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_user_sex") - private String sex; -``` - -使用方式二: - -```Java - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(readConverterExp="0=男,1=女,2=未知", separator=",") - private String sex; -``` - -`@ExcelDictFormat` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|------------------|--------|-----|-----------------------------------| -| dictType | String | "" | 字典的type值 (如: sys_user_sex) | -| readConverterExp | String | "" | 读取内容转表达式 (如: 0=男,1=女,2=未知) | -| separator | String | "," | 与 readConverterExp 属性结合使用,表达式的分隔符 | - -### 2:枚举转换器 - -字典转换器 `ExcelEnumConvert` 与自定义注解 `@ExcelEnumFormat` 结合使用,标注在需要转换的属性上。 - -使用方式: - -```Java - /** - * 用户类型 - *

- * 使用ExcelEnumFormat注解需要进行下拉选的部分 - */ - @ExcelProperty(value = "用户类型", index = 1, converter = ExcelEnumConvert.class) - @ExcelEnumFormat(enumClass = UserStatus.class, textField = "info") - private String userStatus; -``` - -`@ExcelEnumFormat` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|-----------|------------|------|------------------------------| -| enumClass | Enum Class | - | 字典枚举类型 | -| codeField | String | code | 字典枚举类中对应的 code 属性名称,默认为 code | -| textField | String | text | 字典枚举类中对应的 text 属性名称,默认为 text | - - -### 3:导入监听器 - -#### 3.1:ExcelListener 监听器接口 - -`ExcelListener` 扩展了 `ReadListener` 接口,增加了获取结果方法。 - -![输入图片说明](https://foruda.gitee.com/images/1700181723794469524/99bf83c9_4959041.png "屏幕截图") - -#### 3.2:DefaultExcelListener 默认监听器 - -`DefaultExcelListener` 默认监听器在读 Excel 时调用,主要对数据进行校验、解析、异常处理、返回结果等。导入操作时如果没有特别指定则使用该监听器。 - -#### 3.3:SysUserImportListener 用户导入监听器 - -`SysUserImportListener` 用户导入监听器是在用户导入时调用的监听器。 - -该监听器重写了 `invoke` 反射接口,对导入的用户数据进行了校验;重写了 `getExcelResult` 获取结果接口,返回结果数据。 - -#### 3.4:ExportDemoListener 带下拉框的导入监听器 - -`ExportDemoListener` 是对带有下拉框的 Excel 进行处理的导入监听器。 - -## Easy Excel 常用注解 - -`Easy Excel` 提供了丰富的注解可以对导出对象进行定制化操作,这里的注解说明针对的是原生注解。 - -| 类型 | 注解名称 | 使用举例 | 说明 | -|-------|-------------------------|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| -| 格式化注解 | @DateTimeFormat | @DateTimeFormat(value=格式化值) | 对字符串进行日期格式化 (参照 `java.text.SimpleDateFormat` 书写即可) | -| 格式化注解 | @NumberFormat | @NumberFormat(value=格式化值, roundingMode=舍入模式) | 对字符串进行数值格式化 (参照 `java.text.DecimalFormat` 书写即可, `roundingMode` 默认 `RoundingMode.HALF_UP`) | -| 属性注解 | @ExcelIgnore | @ExcelIgnore | 导出忽略该字段 | -| 属性注解 | @ExcelIgnoreUnannotated | @ExcelIgnoreUnannotated | 默认不管加不加 `@ExcelProperty` 的注解的所有字段都会参与读写,加了 `@ExcelIgnoreUnannotated` 注解以后,不加 `@ExcelProperty` 注解的字段就不会参与 | -| 属性注解 | @ExcelProperty | @ExcelProperty(value=值, order=排序值, index=下标, converter=转换器) | 默认按照对象属性顺序导出,如果设置了 `order` 以及 `index`,优先级 `index` > `order` > 默认;converter 可以自定义 | - -## 扩展使用 - -### 扩展一:自定义转换器实现 - -由于业务需要,原生注解不一定能够符合需要,因而衍生出了自定义转换器。能够实现定制化的内容转换需要。 -以下以框架中的字典转换器 `ExcelDictConvert` 为例进行说明。 - -字典转换器 `ExcelDictConvert`,字典转换器使用了自定义注解 `@ExcelDictFormat` 配合使用。 - -_**注:自定义转换器并非一定需要自定义注解,也可以针对已有的注解进行自定义转换实现。**_ - -#### 实现方式 - -自定义转换器需要实现 `com.alibaba.excel.converters.Converter` 接口,实现接口中的方法。 - -![输入图片说明](https://foruda.gitee.com/images/1700104014304819918/33eb0c42_4959041.png "屏幕截图") - -转换方法 `ExcelDictConvert#convertToJavaData` : - -![输入图片说明](https://foruda.gitee.com/images/1700182975516396213/d3c020f9_4959041.png "屏幕截图") - -### 扩展二:自定义监听器实现 - -自定义监听器主要用于在读取解析 Excel 数据时进行自定义操作。 -以下以框架中的用户导入监听器 `SysUserImportListener` 为例进行说明。 - -#### 实现方式 -1. 继承分析事件监听器 `AnalysisEventListener` 以及实现 Excel 监听器 `ExcelListener`。 - -![输入图片说明](https://foruda.gitee.com/images/1700184652693497753/09333dac_4959041.png "屏幕截图") - -2. 显示使用构造函数,否则将导致空指针。 - -![输入图片说明](https://foruda.gitee.com/images/1700184759075616584/cf05b0ed_4959041.png "屏幕截图") - -3. 实现 `invoke` 方法,对数据进行解析操作,可以在此方法对数据进行合法性判断。 - -4. 实现 `getExcelResult` 方法,对结果进行操作,例如返回成功、失败的统计数据。 - -## 更多功能 - -更多导入功能使用可以参照 `Easy Excel` [官方文档](https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read)。 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/oss.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/oss.md deleted file mode 100644 index 4cb42769..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/oss.md +++ /dev/null @@ -1,124 +0,0 @@ -# 关于OSS模块使用 -- - - -## 重点注意事项 - -`桶/存储区域` 系统会根据配置自行创建分配权限
-~~如手动配置需要设置 `公有读` 权限 否则文件无法访问~~(`aliyun` 还需开通跨域配置)
-1.4.0 版本支持配置`公有/私有`权限(`aliyun` 还需开通跨域配置)
-访问站点 后严禁携带其他 `url` 例如: `/`, `/ruoyi` 等
-**阿里云与腾讯云SDK访问站点中不能包含桶名 系统会自动处理**
-**minio 站点不允许使用 localhost 请使用 127.0.0.1**
-**访问站点与自定义域名 都不要包含 `http` `https` 前缀 设置`https`请使用选项处理** - -## 代码使用 - -> 参考 `SysOssService.upload` 用法
-> 使用 `OssFactory.instance()` 获取当前启用的 `OssClient` 实例
-> 进行功能调用 获取返回值后 存储到对应的业务表 - -![输入图片说明](https://foruda.gitee.com/images/1678978345529639839/d350ec0b_1766278.png "屏幕截图") - - -## 功能配置 - -### 配置OSS - -> 进入 `系统管理 -> 文件管理 -> 配置管理` 填写对应的OSS服务相关配置
- -![输入图片说明](https://foruda.gitee.com/images/1678978349820700551/1f91a237_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978354387669856/3a91a3a9_1766278.png "屏幕截图")
**重点说明** - -> 云厂商只需修改 `访问站点`对应的域 切勿乱改(云厂商强烈建议绑定自定义域名使用 七牛云必须绑定[官方规定])
- -![输入图片说明](https://foruda.gitee.com/images/1678978362358100362/5c2c4d20_1766278.png "屏幕截图") - -> 七牛云 访问站点
- - -![输入图片说明](https://foruda.gitee.com/images/1678978366254745764/e93a65ff_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978369853348732/79e8950e_1766278.png "屏幕截图") - -> 阿里云 访问站点 - -![输入图片说明](https://foruda.gitee.com/images/1678978373981462025/56a70398_1766278.png "屏幕截图") - -> 腾讯云 访问站点 - -![输入图片说明](https://foruda.gitee.com/images/1678978378697093134/785517f3_1766278.png "屏幕截图") - -### MinIO 使用 https访问站点 - -**注意:S3 API 签名计算算法不支持托管 MinIO Server API 的代理方案** - -[ minio https 配置方式](https://blog.csdn.net/Michelle_Zhong/article/details/126484358) - -### 切换OSS - -> 再配置列表点击 `状态` 按钮开启即可(注意: 只能开启一个OSS默认配置)
-> 手动使用 `OssFactory.instance("configKey")`
- -![输入图片说明](https://foruda.gitee.com/images/1678978383700118702/7f3fa0c5_1766278.png "屏幕截图") - -### 扩展分类 - -> 如有文件分类 建议创建多个 oss配置 进行切换存储
- -例如: 创建一个 图片存储的 oss配置
-指定唯一的 `configKey` 与 `前缀目录` 或 直接使用独立的`桶`
-独立桶的特点 可以自定义访问权限
-例如: 创建一个私有文件存储桶 不对外开放
- -![输入图片说明](https://foruda.gitee.com/images/1678978389139754119/140be1df_1766278.png "屏幕截图") - -> 指定需要使用的配置
-> 使用 `OssFactory.instance("image")` 获取的 `OssClient` 会加载上图的配置 从而达到上传不同的目录或桶 - - -![输入图片说明](https://foruda.gitee.com/images/1678978397550123641/1b536881_1766278.png "屏幕截图") - - -### 上传图片或文件 - -> 进入 `系统管理 -> 文件管理` 点击 `上传文件` 或 `上传图片` 根据选项选择即可 会对应上传到配置开启的OSS内
- -![输入图片说明](https://foruda.gitee.com/images/1678978401028132972/445d058e_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978404388284503/5459da29_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978408761764835/c81651fc_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978412748494539/7bae621f_1766278.png "屏幕截图") - -### 列表展示 - -> 默认展示图片(可预览) 文件会展示路径
- -![输入图片说明](https://foruda.gitee.com/images/1678978416327601385/af1ecb3b_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978422249633007/19d68eaa_1766278.png "屏幕截图") - -> 可以点击 `预览禁用启用` 按钮对是否展示进行更改 - -![输入图片说明](https://foruda.gitee.com/images/1678978426017014926/4f7fa3f3_1766278.png "屏幕截图") - -> 点击禁用后 图片会变成路径展示 - -![输入图片说明](https://foruda.gitee.com/images/1678978429692592556/0231d778_1766278.png "屏幕截图") - -> 也可再 `参数设置` 更改预览状态 将 `OSS预览列表资源` 改为 `false` 即可关闭预览 - -![输入图片说明](https://foruda.gitee.com/images/1678978433769403801/7d480e76_1766278.png "屏幕截图") - -### 删除功能 - -> 点击列表上方或后方 `删除` 按钮 会根据OSS服务商类型 调用对应的删除(注意: 需确保对应的服务商配置正确)
-> 可勾选多服务商类型的文件进行删除 系统会自动判断 - -![输入图片说明](https://foruda.gitee.com/images/1678978438265941745/f32edc72_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678978441938542080/43ed7c3d_1766278.png "屏幕截图") - -### 下载功能 - -> 点击列表后方对应资源的 `下载` 按钮 根据需求填写文件名 点击确认即可完成下载 - -![输入图片说明](https://foruda.gitee.com/images/1678978448927336261/409af888_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678978452761792483/ed0a4a72_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/page.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/page.md deleted file mode 100644 index 2aafe47b..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/page.md +++ /dev/null @@ -1,29 +0,0 @@ -# 分页功能 -- - - - -## 重点说明 - -> 项目使用 `mybatis-plus` 分页插件 实现分页功能 大致用法与 MP 一致 [MP分页文档](https://baomidou.com/pages/97710a/)
-> 项目已配置分页合理化 页数溢出 例如: 一共5页 查了第6页 默认返回第一页
- -![输入图片说明](https://foruda.gitee.com/images/1678977804058241635/b5cb362d_1766278.png "屏幕截图") - -## 代码用法 - -> `Controller` 使用 `PageQuery` 接收分页参数 具体参数参考 `PageQuery` - -![输入图片说明](https://foruda.gitee.com/images/1678977844048821356/1f994221_1766278.png "屏幕截图") - -> 构建 `Mybatis-Plus` 分页对象
-> 使用 `PageQuery#build()` 方法 可快速(基于当前对象数据)构建 `MP` 分页对象 - -![输入图片说明](https://foruda.gitee.com/images/1678977862816976499/b82c1638_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678977876194578744/eaa7b854_1766278.png "屏幕截图")
- -具体用法与 `MP` 一致 - -> 自定义 `SQL` 方法分页
-> 只需在 `Mapper` 方法第一个参数和返回值 重点: 第一个参数 标注分页对象 - -![输入图片说明](https://foruda.gitee.com/images/1678977898181729571/6e102731_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678977906788451483/70979292_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/param_check.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/param_check.md deleted file mode 100644 index 95ee19d8..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/param_check.md +++ /dev/null @@ -1,158 +0,0 @@ -# 参数校验 -- - - - -参数校验在日常开发中十分常见,在本框架中引入了 `spring-boot-starter-validation` 依赖,底层基于 `hibernate-validator`,可以对参数进行校验。 - -## 参数校验使用 - -### 方法一:使用 `@Validated` 注解 - -#### 步骤一:标注 `@Validated` - -`@Validated` 可以标注在类上,或者是参数前。 - -```Java -/** 标注在类上 **/ -@Validated -@RestController -@RequestMapping("/auth") -public class AuthController { - - @PostMapping("/login") - public R login(@RequestBody LoginBody body) { - // ... - } - -} -``` - -```Java -/** 标注在参数前 **/ -@PostMapping -public R add(@Validated @RequestBody SysUserBo user) { - // ... -} -``` - -#### 步骤二:标注校验注解 - -在参数中加入校验注解。 - -```Java -public class SysUserBo { - - @NotBlank(message = "用户账号不能为空") - @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") - private String userName; - - @NotBlank(message = "用户昵称不能为空") - @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符") - private String nickName; - - @Email(message = "邮箱格式不正确") - @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") - private String email; - -} -``` - -常见校验注解见文末附表。 - -_注:message 支持 EL 表达式,{max} 直接读取前面的参数值。_ - -### 方法二:使用校验工具类 `ValidatorUtils` - -`org.dromara.common.core.utils.ValidatorUtils` - -![输入图片说明](https://foruda.gitee.com/images/1700050047426137432/206bd032_4959041.png "屏幕截图") - -使用方式 1:校验所有带有校验注解的属性 - -```Java -// 校验所有带有校验注解的属性 -ValidatorUtils.validate(object); -``` - -使用方式 2:按照分组校验属性(可以传多个分组) - -```Java -// 按照分组校验属性(可以传多个分组) -ValidatorUtils.validate(object, group); -``` - -## 扩展使用 - -### 扩展一:自定义校验注解 - -除了已有的校验注解以外,可以结合业务进行自定义。 - -以框架中的 `@Xss` 注解为例进行说明。 - -```Java -@Xss(message = "用户账号不能包含脚本字符") -@NotBlank(message = "用户账号不能为空") -@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") -private String userName; -``` - -#### 1:新增 `@Xss` 注解 - -`org.dromara.common.core.xss.Xss` - -![输入图片说明](https://foruda.gitee.com/images/1700048074014527096/b4e230c2_4959041.png "屏幕截图") - -#### 2:自定义校验器 - -自定义校验器实现 `jakarta.validation.ConstraintValidator` 接口。 - -`org.dromara.common.core.xss.XssValidator` - -![输入图片说明](https://foruda.gitee.com/images/1700048474563719650/f9172bdc_4959041.png "屏幕截图") - -### 扩展二:自定义分组校验 - -同一个对象在不同的请求中需要校验的参数不同,则可以使用分组校验。 - -#### 1:自定义分组 - -![输入图片说明](https://foruda.gitee.com/images/1700049439236073123/9e0d2e16_4959041.png "屏幕截图") - -#### 2:`@Validated` 注解指定分组 - -![输入图片说明](https://foruda.gitee.com/images/1700049302803077030/c2a985aa_4959041.png "屏幕截图") - -#### 3:校验注解中指定分组 - -![输入图片说明](https://foruda.gitee.com/images/1700049205699437759/96babbd6_4959041.png "屏幕截图") - -## 附录:常用校验注解 - -| 注解 | 使用(只列举特殊参数值) | 参数类型 | 说明 | -|------------------|--------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------| -| @AssertFalse | @AssertFalse | boolean / Boolean | 元素值必须为 false | -| @AssertTrue | @AssertTrue | boolean / Boolean | 元素值必须为 true | -| @DecimalMax | @DecimalMax(value=值) | - BigDecimal
- BigInteger
- CharSequence
- byte, short, int, long 及其包装类 | 元素必须是一个数字,其值必须小于或等于指定的最大值 | -| @DecimalMin | @DecimalMin(value=值) | - BigDecimal
- BigInteger
- CharSequence
- byte, short, int, long 及其包装类 | 元素必须是一个数字,其值必须大于或等于指定的最小值 | -| @Digits | @Digits(integer=整数位值, fraction=小数位值) | - BigDecimal
- BigInteger
- CharSequence
- byte, short, int, long 及其包装类 | 元素必须符合整数位以及小数位范围值 | -| @Email | @Email(regexp=正则表达式, flags=标志) | CharSequence | 元素是否符合正则表达式(正则表达式非必传) | -| @Future | @Future | - java.util.Date
- java.util.Calendar
- java.time.Instant
- java.time.LocalDate
- java.time.LocalDateTime
- java.time.LocalTime
- java.time.MonthDay
- java.time.OffsetDateTime
- java.time.OffsetTime
- java.time.Year
- java.time.YearMonth
- java.time.ZonedDateTime
- java.time.chrono.HijrahDate
- java.time.chrono.JapaneseDate
- java.time.chrono.MinguoDate
- java.time.chrono.ThaiBuddhistDate | 元素必须是未来的时刻、日期或时间 | -| @FutureOrPresent | @FutureOrPresent | 同 @Future | 元素必须是当前或未来的时刻、日期或时间 | -| @Length | @Length(min=最小值, max=最大值) | - CharSequence | 验证字符串是否在包含的 min 和 max 之间 | -| @Max | @Max(value=值) | - BigDecimal
- BigInteger
- byte, short, int, long 及其包装类 | 元素必须是一个数字,其值必须小于或等于指定的最大值 | -| @Min | @Min(value=值) | - BigDecimal
- BigInteger
- byte, short, int, long 及其包装类 | 元素必须是一个数字,其值必须大于或等于指定的最小值 | -| @Negative | @Negative | - BigDecimal
- BigInteger
- byte,short,int,long,float,double 及其包装类 | 元素必须是一个严格的负数(即 0 被视为无效值) | -| @NegativeOrZero | @NegativeOrZero | - BigDecimal
- BigInteger
- byte,short,int,long,float,double 及其包装类 | 元素必须为负数或 0 | -| @NotBlank | @NotBlank | CharSequence | 元素不能为 null,并且必须至少包含一个非空白字符 | -| @NotEmpty | @NotEmpty | - CharSequence
- Collection
- Map
- Array | 元素不能为 null 或空集合 | -| @NotNull | @NotNull | 不限类型 | 元素不能为 null | -| @Null | @Null | 不限类型 | 元素必须为 null | -| @Past | @Past | 同 @Future | 元素必须是过去的瞬间、日期或时间 | -| @PastOrPresent | @PastOrPresent | 同 @Future | 元素必须是过去或现在的瞬间、日期或时间 | -| @Pattern | @Pattern(regexp=正则表达式, flags=标志) | CharSequence | 元素必须与指定的正则表达式匹配(正则表达式遵循 Java 正则表达式约定) | -| @Positive | @Positive | - BigDecimal
- BigInteger
- byte,short,int,long,float,double 及其包装类 | 元素必须是一个严格的正数(即 0 被视为无效值) | -| @PositiveOrZero | @PositiveOrZero | - BigDecimal
- BigInteger
- byte,short,int,long,float,double 及其包装类 | 元素必须为正数或 0 | -| @Range | @Range(min=最小值, max=最大值) | - BigDecimal
- BigInteger
- CharSequence
- byte, short, int, long 及其包装类 | 验证元素是否在包含的 min 和 max 之间 | -| @Size | @Size(min=最小值, max=最大值) | - CharSequence
- Collection
- Map
- Array | 验证元素是否在包含的 min 和 max 之间 | -| @Valid | @Valid | 对象 | 级联验证 | - -更多注解可参考包: `org.hibernate.validator` \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/permissions.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/permissions.md deleted file mode 100644 index 62ce2946..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/permissions.md +++ /dev/null @@ -1,144 +0,0 @@ -# 关于数据权限 -- - - -* 参考 demo 模块用法(需导入 test.sql 文件) - -### 新版数据权限功能: -1.支持自动注入 sql 数据过滤
-2.查询、更新、删除 限制
-3.支持自定义数据字段过滤
-4.模板支持 spel 语法实现动态 Bean 处理
-5.支持与菜单权限标识符联合使用(2.2.X新功能) - -### 数据权限相关代码 - -| 类 | 说明 | 功能 | -|-------------------------------|-----------------|----------------------------------------| -| DataScopeType | 数据权限模板定义 | 用于定义数据权限模板 | -| DataPermission | 数据权限组注解 | 用于标注开启数据权限 (默认过滤部门权限) | -| DataColumn | 具体的数据权限字段标注 | 用于替换数据权限模板内的 key 变量 | -| PlusDataPermissionInterceptor | 数据权限 sql 拦截器 | 用于拦截所有 sql 检查是否标注了 `DataPermission` 注解 | -| PlusDataPermissionHandler | 数据权限处理器 | 用于处理被拦截到的 sql 为其添加数据权限过滤条件 | -| DataPermissionHelper | 数据权限助手 | 操作数据权限上下文变量 | -| SysDataScopeService | 自定义 Bean 处理数据权限 | 用于自定义扩展 | - -## 忽略数据权限 - -1.如果需要指定单独 SQL 不开启过滤,可在对应的 Mapper 接口添加如下忽略注解: -``` -@InterceptorIgnore(dataPermission = "true") -``` - -2.如果需要在业务层忽略数据权限,可调用以下方法: -``` -# 无返回值 -DataPermissionHelper.ignore(() -> { 业务代码 }); -# 有返回值 -Class result = DataPermissionHelper.ignore(() -> { return 业务代码 }); -``` - -### 使用方式 `参考demo模块` -数据权限体系 `用户 -> 多角色 => 角色 -> 单数据权限` -> 例子: 用户A 拥有两个角色
-> 角色A 部门经理 可查看 本部门及以下部门的数据
-> 角色B 兼职开发 可查看 仅自己的数据 - -> 创建角色 test1 为 本部门及以下 - -![输入图片说明](https://foruda.gitee.com/images/1678978669666831574/b51ed0a3_1766278.png "屏幕截图") - -> 创建角色 test2 为 仅本人 - -![输入图片说明](https://foruda.gitee.com/images/1678978674159035056/69cf32ad_1766278.png "屏幕截图") - -> 将其分配给用户 test - -![输入图片说明](https://foruda.gitee.com/images/1678978680492570269/a47b6afc_1766278.png "屏幕截图") - -### 编写列表查询(注意: 数据权限注解只能在 Mapper 层使用) - -> 标注数据权限注解 `dept_id` 为过滤部门字段 `user_id` 为过滤创建用户 - -![输入图片说明](https://foruda.gitee.com/images/1678978687179608427/d6b83c30_1766278.png "屏幕截图") - -### 重点注意: 如下情况不生效 - -> 有自定义实现方法 最终执行的mapper不是这个方法 所以无法生效 -> -> 解决方案: 一直往下点 找到最终的执行mapper重写即可 - -![输入图片说明](https://foruda.gitee.com/images/1678978692558777291/78b0a3dd_1766278.png "屏幕截图") - -### 编写数据权限模板 - -![输入图片说明](https://foruda.gitee.com/images/1678978697141183499/cfc1cb6a_1766278.png "屏幕截图") - -1.`code` 为关联角色的数据权限 `code`
-2.`sqlTemplate` 为 sql 模板
-`#{#deptName}` 为模板变量 对应权限注解的 `key`
-`#{@sdss}` 为模板 Bean 调用 调用其 Bean 的处理方法
-3.`elseSql` 为兜底 sql 处理当前角色与标注的注解 无对应的情况
-例如 数据权限为仅本人 且 方法并未标注具体过滤注解 则 填充 `1 = 0` 使条件不满足 不允许查看
-更详细用法可以参考 `DataScopeType` 注释 - -### 测试代码 - -> 使用 `管理员` 用户优先测试 - -![输入图片说明](https://foruda.gitee.com/images/1678978703250082481/e93a68a5_1766278.png "屏幕截图") - -> 使用 `test` 用户测试 - -![输入图片说明](https://foruda.gitee.com/images/1678978710644676604/d7f80487_1766278.png "屏幕截图") - -> 使用 `test` 删除一条不属于自己的数据 -> sql执行为不满足条件 不允许删除 - -![输入图片说明](https://foruda.gitee.com/images/1678978715711122947/441d61f7_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678978720298532619/a35b1147_1766278.png "屏幕截图") - - -> 使用 `test` 修改与删除同理
-> 具体实现为 更新和删除方法 标注数据权限注解 - -![输入图片说明](https://foruda.gitee.com/images/1678978725329242504/a70491a1_1766278.png "屏幕截图") - -### 自定义SQL模板 - -> 1.首先在角色管理 数据权限下拉框 添加自定义模板
-> 为什么不放置到系统字典问题: 因数据权限与模板绑定 不应随意改动 最好事先定义好 - -![输入图片说明](https://foruda.gitee.com/images/1678978730563169865/3459ee17_1766278.png "屏幕截图") - -> 2.代码 `DataScopeType` 自定义一个SQL模板 - -![输入图片说明](https://foruda.gitee.com/images/1678978735588305505/3f030c67_1766278.png "屏幕截图") - -> 3.标注权限注解 - -![输入图片说明](https://foruda.gitee.com/images/1678978742259837391/eabe5caa_1766278.png "屏幕截图") - -> 4.设置数据权限变量 - -![输入图片说明](https://foruda.gitee.com/images/1678978746778429543/e211201f_1766278.png "屏幕截图") - -> 5.测试 - -![输入图片说明](https://foruda.gitee.com/images/1678978751875467640/7d210cf4_1766278.png "屏幕截图") - -### mybatis-plus 原生方法 增加数据权限过滤 - -> 首先查看需要重写的方法源码 重点`方法源码` `方法源码` `方法源码`
-> 例如重写 `selectPage` 方法
- -![输入图片说明](https://foruda.gitee.com/images/1678978757955000897/8315695c_1766278.png "屏幕截图") - -> 复制源码到自己的 `Mapper` 并增加数据权限注解 注意左边出现重写图标 即为重写成功
- -![输入图片说明](https://foruda.gitee.com/images/1678978763224011694/bbea25a1_1766278.png "屏幕截图") - -### 支持类标注 - -> 获取规则 `方法 > 类` 注意: 类标注后 所有方法(包括父类方法) 都会进行数据权限过滤 - -![输入图片说明](https://foruda.gitee.com/images/1678978767336534896/fb13ee99_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/permissions_control.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/permissions_control.md deleted file mode 100644 index 6931ca7a..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/permissions_control.md +++ /dev/null @@ -1,178 +0,0 @@ -# 权限控制 -- - - - -本文采用 `Sa-Token` 框架实现权限控制。[官方文档传送门](https://sa-token.cc/doc.html#/) - -## 权限校验 -权限校验指的是校验用户是否拥有访问某个 API 的能力。 - -通常情况下,一个 API 对应一个权限码,如果用户具备当前 API 的权限码,即代表有能力访问该 API。 - -### 1:权限标识 -在本系统中,每一个菜单功能都有对应的权限标识,可以在菜单管理中进行设置。 - -> 注: -> 1. 前后端的权限标识要保持一致。 -> 2. 权限标识可以使用通配符`*`。 - -![输入图片说明](https://foruda.gitee.com/images/1701086497939145368/133fb327_4959041.png "屏幕截图") - - -### 2:校验方法 -#### 2.1:使用 `@SaCheckPermission` 注解进行校验 -`@SaCheckPermission` 注解是由 `Sa-Token` 框架提供的角色校验注解,可以标注在方法上或类上。 - -- 单个权限校验: - -```Java -@SaCheckPermission("system:user:list") -``` - -- 多个权限校验(或模式,满足任意一个权限即可): - -```Java -@SaCheckPermission( - value = { - "system:user:list", - "system:user:query" - }, - mode = SaMode.OR -) -``` - -- 多个权限校验(与模式,必须满足所有权限): - -```Java -@SaCheckPermission( - value = { - "system:user:list", - "system:user:query" - }, - mode = SaMode.AND -) -``` - -#### 2.2:使用 `StpUtil` 工具类校验 -`StpUtil` 工具类是由 `Sa-Token` 框架提供的权限工具类,提供了常用的校验方法。 - -- 判断当前用户是否拥有某个权限(返回 `boolean`): - -```Java -StpUtil.hasPermission("system:user:list"); -``` - -- 单个权限校验: - -```Java -StpUtil.checkPermission("system:user:list"); -``` -如果验证未通过,则抛出异常: `NotPermissionException` - -- 多个权限校验(或模式,满足任意一个权限即可): - -```Java -StpUtil.checkPermissionOr("system:user:list", "system:user:query"); -``` -如果验证未通过,则抛出异常: `NotPermissionException` - -- 多个权限校验(与模式,必须满足所有权限): - -```Java -StpUtil.checkPermissionAnd("system:user:list", "system:user:query"); -``` -如果验证未通过,则抛出异常: `NotPermissionException` - -## 角色校验 -角色校验指的是校验用户是否拥有某个指定角色。 - -### 1:权限标识 -在本系统中,每个角色都拥有唯一的权限字符。 - -除了超级管理员角色外,其他角色的权限字符可以通过角色管理进行设置。 - -![输入图片说明](https://foruda.gitee.com/images/1701085080527279823/3255961d_4959041.png "屏幕截图") - -### 2:校验方法 -#### 2.1:使用 `@SaCheckRole` 注解校验 -`@SaCheckRole` 注解是由 `Sa-Token` 框架提供的角色校验注解,可以标注在方法上或类上。 - -- 单个角色校验 - -```Java -@SaCheckRole("superadmin") -``` - -- 多个角色校验(或模式,满足任意一个角色即可): - -```Java -@SaCheckRole( - value = { - "superadmin", - "admin" - }, - mode = SaMode.OR -) -``` - -- 多个角色校验(与模式,必须满足所有角色): - -```Java -@SaCheckRole( - value = { - "superadmin", - "admin" - }, - mode = SaMode.AND -) -``` - -#### 2.2:使用 `StpUtil` 工具类校验 -`StpUtil` 工具类是由 `Sa-Token` 框架提供的权限工具类,提供了常用的校验方法。 - -- 判断当前用户是否拥有某个角色(返回 `boolean`): - -```Java -StpUtil.hasRole("superadmin") -``` - -- 单个权限校验: - -```Java -StpUtil.checkRole("system:user:list"); -``` -如果验证未通过,则抛出异常: `NotRoleException` - -- 多个权限校验(或模式,满足任意一个角色即可): - -```Java -StpUtil.checkRoleOr("system:user:list", "system:user:query"); -``` -如果验证未通过,则抛出异常: `NotRoleException` - -- 多个权限校验(与模式,必须满足所有角色): - -```Java -StpUtil.checkRoleAnd("system:user:list", "system:user:query"); -``` -如果验证未通过,则抛出异常: `NotRoleException` - -## 角色权限双重 `OR` 校验 -除了分开校验以外,权限和角色也可以进行组合,表示备选校验。 - -简单举个例子: - -假设某个 API 的权限码为 `system:user:list`,角色 `admin` 可以调用,则可以这样写: - -```Java -@SaCheckPermission(value = "system:user:list", orRole = "admin") -``` - -以上权限只需要满足任意一项即可。更多写法可以参考 `Sa-Token` [官方文档](https://sa-token.cc/doc.html#/use/at-check?id=_4%e3%80%81%e8%a7%92%e8%89%b2%e6%9d%83%e9%99%90%e5%8f%8c%e9%87%8d-or%e6%a0%a1%e9%aa%8c)。 - -## 当前用户的所有权限 -本系统中实现了 `StpInterface` 接口,可以对用户的权限以及角色进行管理,并且可以根据不同的用户类型进行设置。 - -具体参考类:`org.dromara.common.satoken.core.service.SaPermissionImpl` - -## 忽略权限校验 -请参考文档:[网关路由与放行](/ruoyi-cloud-plus/framework/basic/router_release?id=网关路由与放行) diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/router_release.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/router_release.md deleted file mode 100644 index 9b08f069..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/router_release.md +++ /dev/null @@ -1,26 +0,0 @@ -# 网关路由与放行 -- - - - -## 新增路由 -`ruoyi-gateway.yml` 配置文件 增加 `routers` 配置
-**注意: 路径格式为 `/服务路径/controller路径/接口方法路径` `*代表任意一级 **代表任意所有级`**
-下图代表 `resource/**` 将所有 `resource开头的路径` 都路由到 `ruoyi-resource` 服务
-例如: `/resource/sms/code` `resource路由到ruoyi-resource服务` `sms路由到对应的contrller` `code 路由到对应的接口`
-![输入图片说明](https://foruda.gitee.com/images/1669623462957266512/c282932b_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1669623527799049459/201a52db_1766278.png "屏幕截图") - -## 放行使用方式 -nacos 中 `ruoyi-gateway.yml` 白名单放行
-**注意: 放行路径格式为 `/服务路径/controller路径/接口方法路径` `*代表任意一级 **代表任意所有级`**
-示例: `/resource/sms/code` 代表 `ruoyi-resource服务 sms的controller code接口`
-![输入图片说明](https://foruda.gitee.com/images/1660622672461635175/屏幕截图.png "屏幕截图.png") - -## 注意事项 - -接口放行后不需要token即可访问
-但是没有token也就无法获取用户信息与鉴权 - -### 解决方案 -删除接口上的鉴权注解
-删除接口内获取用户信息功能
-删除数据库实体类 自动注入 `createBy` `updateBy` 因为会获取用户数据 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/social.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/social.md deleted file mode 100644 index 2e3cd739..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/social.md +++ /dev/null @@ -1,68 +0,0 @@ -# 第三方授权功能 -- - - -## 版本 >= 2.X - -## 前置说明 -1. 该功能基于 `JustAuth` 实现,支持多家平台实现第三方授权登录。 -2. 以 `Gitee` 授权登录为例进行本功能的使用说明。 -3. 其他第三方授权配置信息获取方式可参考 `JustAuth` [官方文档](https://www.justauth.cn/guide/)。
- - ![输入图片说明](https://foruda.gitee.com/images/1690937097426867003/91d80587_4959041.png "屏幕截图") - -## 第三方授权配置 - -### 申请三方应用(以gitee为例) - -![输入图片说明](https://foruda.gitee.com/images/1700641775779304627/1cf1b56f_1766278.png "屏幕截图") - -### 更改后端配置 `application-dev.yml` - -![输入图片说明](https://foruda.gitee.com/images/1690936741844431943/580f8998_4959041.png "屏幕截图") - -**注:内网地址无法回调,请使用外网可以访问的地址。** - -![输入图片说明](https://foruda.gitee.com/images/1690940457570856867/ce22df18_4959041.png "屏幕截图") - -### 更改前端配置 `login.vue` - -![输入图片说明](https://foruda.gitee.com/images/1690937306197173754/5c1ece29_4959041.png "屏幕截图") - -## 授权登录(未绑定第三方平台) - -### 步骤一:个人中心授权第三方应用 - -![输入图片说明](https://foruda.gitee.com/images/1690938449386201097/ea375106_4959041.png "屏幕截图") - -### 步骤二:同意授权 - -![输入图片说明](https://foruda.gitee.com/images/1690938522418523183/81b327bf_4959041.png "屏幕截图") - -顶部出现授权成功,并跳转到系统首页。
- -![输入图片说明](https://foruda.gitee.com/images/1690938559178527841/563168e4_4959041.png "屏幕截图")
- -![输入图片说明](https://foruda.gitee.com/images/1690938636375977741/8ceb77cf_4959041.png "屏幕截图") - -查看第三方应用可看到授权成功的个人信息。
- -![输入图片说明](https://foruda.gitee.com/images/1690938725512311321/5532a2a9_4959041.png "屏幕截图") - -## 授权登录(已绑定第三方平台) - -### 步骤一:点击登录页面图标 - -![输入图片说明](https://foruda.gitee.com/images/1690938908352243992/fd044381_4959041.png "屏幕截图") - -### 步骤二:同意授权 - -![输入图片说明](https://foruda.gitee.com/images/1690938522418523183/81b327bf_4959041.png "屏幕截图") - -## 解除授权绑定 - -### 步骤一:个人中心点击解绑第三方应用 - -![输入图片说明](https://foruda.gitee.com/images/1690939087877969002/4ef324e7_4959041.png "屏幕截图") - -### 步骤二:点击确定完成解绑 - -![输入图片说明](https://foruda.gitee.com/images/1690939108017661775/7236088d_4959041.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/tenant.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/tenant.md deleted file mode 100644 index b57c3f63..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/tenant.md +++ /dev/null @@ -1,121 +0,0 @@ -# 多租户功能 -- - - -## 版本 >= 2.X - -## 前置说明(重要) -1. 本框架多租户功能的实现是基于 [MyBatis-Plus 多租户插件](https://baomidou.com/pages/aef2f2/#tenantlineinnerinterceptor) 的,只支持最简单的隔离。 -2. 本系统默认开启多租户功能。 -3. 多租户业务表建表需要加上租户id `tenant_id`,可参考其他系统表。 -4. 非多租户表可在配置文件进行配置排除。 -5. 只有超级管理员支持切换租户。 - -## 多租户使用流程(先说结论再展开!) -0. 开启多租户配置(系统默认已经开启) -1. 登录界面(可以选择不同租户) -> 注:如果为租户设置了绑定域名,则只能选择当前域名相关的租户列表。 -2. 设置多租户套餐 -3. 新增/修改租户(需要选择套餐) -4. 切换租户(仅超级管理员可操作) - -## 多租户配置 -`application-common.yml`
- -> 开关 `enable` 节点不用废话。
-> 如果不需要过滤租户的表可在 `excludes` 节点下添加。 - -**注意: 如果已经基于租户模式启动了程序 关闭租户必须删除mysql与redis内的相关数据重新导入sql** - -![输入图片说明](https://foruda.gitee.com/images/1680168468127690787/2cd3279e_4959041.png "屏幕截图") - -## 忽略租户 - -1.如果需要指定单独 SQL 不开启过滤,可在对应的 Mapper 接口添加如下忽略注解: -``` -@InterceptorIgnore(tenantLine = "true", dataPermission = "false") -``` -**此处注意事项 使用此注解如果需要开启数据权限 dataPermission = "false" 必须添加 mp的注解默认是忽略数据权限的 会导致数据权限失效** - -2.如果需要在业务层忽略多租户,可调用以下方法(推荐使用): -``` -# 无返回值 -TenantHelper.ignore(() -> { 业务代码 }); -# 有返回值 -Class result = TenantHelper.ignore(() -> { return 业务代码 }); -``` - -## 动态切换租户 - -**仅适用于特殊需求业务(例如: 创建租户时, 对该租户操作一些数据, 或者需要去其他租户查一些数据等) 禁止乱用后果自负** - -``` -# 无返回值 -TenantHelper.dynamic(租户id, () -> { 业务代码 }); -# 有返回值 -Class result = TenantHelper.dynamic(租户id, () -> { return 业务代码 }); -``` - -## 登录界面 - -![输入图片说明](https://foruda.gitee.com/images/1680173982933030545/bca146d7_4959041.png "屏幕截图") - -> 注:如果为租户设置了绑定域名,则只能选择当前域名相关的租户列表。 - -## 租户套餐管理 -### 租户套餐新增 -![输入图片说明](https://foruda.gitee.com/images/1680174317475230288/352957a1_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680174602877523112/fc194f17_4959041.png "屏幕截图") - -> 注: -> 1、先新增套餐再新增租户,因为租户新增之后无法修改所选套餐。 -> 2、租户所关联的套餐如果后续有修改可以进行同步。 - - -## 租户管理 -### 默认租户 -> 注:默认租户无法修改 - -![输入图片说明](https://foruda.gitee.com/images/1680174738913576400/b6aca11a_4959041.png "屏幕截图") - -### 新增租户 -#### 填写表单 -![输入图片说明](https://foruda.gitee.com/images/1680174945220618443/f7181b51_4959041.png "屏幕截图") - -#### 选择新增的租户套餐 -![输入图片说明](https://foruda.gitee.com/images/1680174991869792688/0dbaadd6_4959041.png "屏幕截图") - -#### 新增完成 -![输入图片说明](https://foruda.gitee.com/images/1680175033853525725/42e64b4d_4959041.png "屏幕截图") - -#### 登录租户 -![输入图片说明](https://foruda.gitee.com/images/1680176145378931134/e05f347e_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680176208161104366/44a935f1_4959041.png "屏幕截图") - -### 修改租户 -#### 配置域名 -![输入图片说明](https://foruda.gitee.com/images/1680175251192690133/141fa6a6_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680175431036971650/db522d39_4959041.png "屏幕截图") - -#### 没有配置域名 -![输入图片说明](https://foruda.gitee.com/images/1680175541165540240/95e211f7_4959041.png "屏幕截图") - -#### 强调一下:这不是bug! -> 注:域名的配置就是为了绑定特定租户! - -### 同步套餐 -应用场景:租户套餐进行了修改,配置的菜单需要同步到特定租户。 -(不是所有租户都有更新套餐的权利, 这是跟钱挂钩的) - -> 点一下按钮的事,图略。 - -## 切换租户(仅超级管理员) -> 注:管理员切换租户不是切换用户,切换的只是数据,管理员拥有所有权限。 - -![输入图片说明](https://foruda.gitee.com/images/1680176324802967804/5c5d6fc3_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680176431031189788/0c3f924c_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680176496555243569/624ec677_4959041.png "屏幕截图") - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/user.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/user.md deleted file mode 100644 index 99050fa7..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/basic/user.md +++ /dev/null @@ -1,85 +0,0 @@ -# 系统用户相关 -- - - - -> 框架采用sa-token控制权限 并对sa-token的api做了一定的业务封装
- -## 用户登录 - -> 参考自带多种登录实现 不限制用户数据来源 只需要构建 LoginUser 即可完成登录
-> 例如: `同表不同类型` `不同表` `同表+扩展表`
- -![输入图片说明](https://foruda.gitee.com/images/1699590555824776931/63d493fc_1766278.png "屏幕截图") - -## 获取用户信息 - -> 完成登录后会生成登录token返回给前端 前端需要再请求头携带token 后端方可获取到对应的用户信息 - -请求头传递格式: `Authorization: Bearer token` - -后端获取用户信息: -```java -LoginUser user = LoginHelper.getLoginUser(); -``` - -## 获取用户信息(基于token) -```java -LoginUser user = LoginHelper.getLoginUser(token); -``` - -## 获取登录用户id -```java -Long userId = LoginHelper.getUserId(); -``` - -## 获取登录用户账户名 -```java -String username = LoginHelper.getUsername(); -``` - -## 获取登录用户所属租户id -```java -String tenantId = LoginHelper.getTenantId(); -``` - -## 获取登录用户所属部门id -```java -Long deptId = LoginHelper.getDeptId(); -``` - -## 获取登录用户类型 -```java -UserType userType = LoginHelper.getUserType(); -``` - -## 获取登录用户其他扩展属性 -```java -Object obj = LoginHelper.getExtra(key); -``` - -## 设置登录用户其他扩展属性 - -参考登录设置 `clientId` 属性 - -![输入图片说明](https://foruda.gitee.com/images/1699591164562734430/42730add_1766278.png "屏幕截图") - -## 判断用户是否为超级管理员 - -```java -// 判断当前登录用户 -boolean b = LoginHelper.isSuperAdmin(); -// 判断用户基于id -boolean b = LoginHelper.isSuperAdmin(userId); -``` - -## 判断用户是否为租户管理员 - -```java -// 判断当前登录用户 -boolean b = LoginHelper.isTenantAdmin(); -// 判断用户基于角色组 -boolean b = LoginHelper.isSuperAdmin(rolePermission); -``` - -## 其他更多操作 -[Sa-Token 官方文档 - 登录认证](https://sa-token.cc/doc.html#/use/login-auth) - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/about_join.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/about_join.md deleted file mode 100644 index 77cde6ef..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/about_join.md +++ /dev/null @@ -1,12 +0,0 @@ -# 关于多表查询 -- - - -## 建议单表查询 - -文章连接: [大连接查询分解好处](https://java.isture.com/db/mysql/mysql-x-optimize-decompose-connection.html) - -![输入图片说明](https://foruda.gitee.com/images/1678979482724037085/1e74f3e1_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1666336728402711844/52788205_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1666336945935088277/f60e3288_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1666336954686520161/c6c83adc_1766278.png "屏幕截图")
-**(上图出自 <高性能MySql>)** \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/key.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/key.md deleted file mode 100644 index c960140d..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/key.md +++ /dev/null @@ -1,19 +0,0 @@ -# 主键使用说明 -- - - -## 关于如何使用分布式id或雪花id - -参考 `MybatisPlusConfig` 如需自定义 修改 `Bean` 实现即可 - -![输入图片说明](https://foruda.gitee.com/images/1678979401707903546/e25f6c06_1766278.png "屏幕截图") - -框架默认集成 雪花ID 只需全局更改 主键类型即可 - -![输入图片说明](https://foruda.gitee.com/images/1678979411517764918/1470df04_1766278.png "屏幕截图") - -如单表使用 可单独配置注解 - -![输入图片说明](https://foruda.gitee.com/images/1678979416033986923/2a4c3736_1766278.png "屏幕截图") - -### 重点说明 -* 由于雪花id位数过长 `Long` 类型在前端会失真 -* 框架已配置序列化方案 超越 `JS` 最大值自动转字符串 参考 `BigNumberSerializer` 类 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/test.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/test.md deleted file mode 100644 index 4c521ad1..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/test.md +++ /dev/null @@ -1,6 +0,0 @@ -# 单元测试 -- - - -## 参考文章 -[SpringBoot 2.X 整合 JUnit5 及全方位使用手册](https://lionli.blog.csdn.net/article/details/127576604) -## 参考代码(1.4.0新增) -![输入图片说明](https://foruda.gitee.com/images/1666973151030696086/6d44f4c2_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/transaction.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/transaction.md deleted file mode 100644 index 2b4966dc..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/explain/transaction.md +++ /dev/null @@ -1,45 +0,0 @@ -# 事务相关 -- - - -若依文档对事务注解的描述 [关于事务](https://doc.ruoyi.vip/ruoyi/document/htsc.html#%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86) 以下对多数据源事务做补充: - - -## 多服务多数据源事务(框架已默认对接 直接使用seata注解即可) - -框架支持对接 `seata` 保证分布式多数据源事务
-详情参考多数据源框架文档连接: https://www.kancloud.cn/tracy5546/dynamic-datasource/2268607 - -## 本地多数据源事务 -请使用 `@DSTransactional` 注解 会代理 `@DS` 注解切换后的数据源事务做回滚处理
-只要 `@DSTransactional` 注解下任一环节发生异常,则全局多数据源事务回滚。
-如果BC上也有 `@DSTransactional` 会有影响吗?答:没有影响的。 - -```java -//如AService调用BService和CService的方法,A,B,C分别对应不同数据源。 - -public class AService { - - @DS("a")//如果a是默认数据源则不需要DS注解。 - @DSTransactional - public void dosomething(){ - BService.dosomething(); - CService.dosomething(); - } -} - -public class BService { - - @DS("b") - public void dosomething(){ - //dosomething - } -} - -public class CService { - - @DS("c") - public void dosomething(){ - //dosomething - } -} -``` - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/api_encrypt.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/api_encrypt.md deleted file mode 100644 index be4ce445..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/api_encrypt.md +++ /dev/null @@ -1,39 +0,0 @@ -# 数据加解密 -- - - - -## 1:API 加密注解 `@ApiEncrypt` -1. 对于标注了 `@ApiEncrypt` 注解的接口,请求参数都必须进行加密。 -2. 注解的参数 `response` 为响应加密标识,默认 `false` 不加密,为 `true` 表示响应加密。 -3. 加密解密逻辑由过滤器实现,详情可参考 `org.dromara.common.encrypt.filter.CryptoFilter`。 - -## 2:API 加密配置 -`application-common.yml` - -![输入图片说明](https://foruda.gitee.com/images/1701133628809355269/8979704a_4959041.png "屏幕截图") - -`.env.development` / `.env.production` - -![输入图片说明](https://foruda.gitee.com/images/1709533252413969800/1d0dff25_1766278.png "屏幕截图") - -> 注: -> 1. 注意修改 Nacos 配置。 -> 2. 公私钥与前端配置文件互为配对,如果需要更换请一同更换。 -> 3. 后端公钥对应前端私钥;后端私钥对应前端公钥。 - -## 3:前端开启加密 -如果需要开启 API 加密,则需要修改 `request` 的 `headers` 内容: -```Javascript -headers: { - isEncrypt: true -} -``` - -![输入图片说明](https://foruda.gitee.com/images/1701137141916998346/5e839bbe_4959041.png "屏幕截图") - -## 4.关于请求响应参数加解密说明 - -如何加解密请求响应参数看这里 -> [关于请求响应参数解密](/questions/api_encrypt.md) - -## 密钥生成说明 - -![输入图片说明](https://foruda.gitee.com/images/1675577852271308699/9b30258e_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/dynamic_datasource.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/dynamic_datasource.md deleted file mode 100644 index 21114cd4..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/dynamic_datasource.md +++ /dev/null @@ -1,45 +0,0 @@ -# 多数据源 -- - - - -### 框架默认 mysql 其他数据库使用说明 - -找到 `ruoyi-common-mybatis` 模块在 pom 文件内增加对应的jdbc依赖 - -![输入图片说明](https://foruda.gitee.com/images/1721098535176969987/d42870ca_1766278.png "屏幕截图") - - -### 关于多数据源事务 具体参考 `事务相关` 文档说明 - -### 多数据源框架功能介绍 -多数据源框架官方文档: [dynamic-datasource文档](https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611) - -* 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。 -* 支持数据库敏感配置信息 加密 ENC()。 -* 支持每个数据库独立初始化表结构schema和数据库database。 -* 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。 -* 支持 自定义注解 ,需继承DS(3.2.0+)。 -* 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。 -* 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。 -* 提供 自定义数据源来源 方案(如全从数据库加载)。 -* 提供项目启动后 动态增加移除数据源 方案。 -* 提供Mybatis环境下的 纯读写分离 方案。 -* 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。 -* 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。 -* 提供 基于seata的分布式事务方案。 -* 提供 本地多数据源事务方案。 附:不能和原生spring事务混用。 - -### 用法说明 - -> 加载顺序 `方法 => 类 => 默认`
- -![输入图片说明](https://foruda.gitee.com/images/1678979069737596299/abe8ae7f_1766278.png "屏幕截图") - -### 配置方式 - -![输入图片说明](https://foruda.gitee.com/images/1678979074000345758/b9238f0b_1766278.png "屏幕截图") - -### 数据库异构 - -例如: `mysql + oracle` 参考对应多数据源框架文档 [dynamic-ds文档](https://www.kancloud.cn/tracy5546/dynamic-datasource) - -![输入图片说明](https://foruda.gitee.com/images/1678979078387192317/2de94a78_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/encrypt.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/encrypt.md deleted file mode 100644 index 729a3039..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/encrypt.md +++ /dev/null @@ -1,38 +0,0 @@ -# 数据加解密 -- - - -## 框架版本 >= 1.6.0 - -## 引入依赖 - -```xml - - com.ruoyi - ruoyi-common-encrypt - -``` - -## 功能说明 - -数据库 数据存储加密 查询解密功能
-支持加密算法: `BASE64` `AES` `RSA` `SM2` `SM4` - -## 注解 `@EncryptField` - -![输入图片说明](https://foruda.gitee.com/images/1675577493013639395/cd920f15_1766278.png "屏幕截图") - -## 用法说明 - -**详细用法可参考案例 TestEncryptController 测试数据库加解密功能** - -全局默认加密配置(如果注解不配置则使用全局配置) - -![输入图片说明](https://foruda.gitee.com/images/1675577674063566357/dee94786_1766278.png "屏幕截图") - -注解可自定义算法与配置 - -![输入图片说明](https://foruda.gitee.com/images/1675577725117970708/7ee7a833_1766278.png "屏幕截图") - -## 密钥生成说明 - -![输入图片说明](https://foruda.gitee.com/images/1675577852271308699/9b30258e_1766278.png "屏幕截图") - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/idempotent.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/idempotent.md deleted file mode 100644 index 5f706710..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/idempotent.md +++ /dev/null @@ -1,29 +0,0 @@ -# 防重幂等 -- - - -## 功能介绍 - -防重功能为防止两条相同的数据重复提交导致脏数据或业务错乱
-**注意: 重复提交属于小概率事件 请不要拿并发压测与之相提并论**
-框架防重功能参考 `美团GTIS防重系统` 使用 请求参数与用户Token或URL 生成全局业务ID
-有效防止 `同一个用户` 在 `限制时间` 内对 `同一个业务` 提交 `相同的数据` - -框架防重处理 `支持业务失败或异常` 快速释放限制
-业务处理成功后 会在设置时间内 限制同一条数据的提交
-**注意: 只对同一个用户的同一个接口提交相同的数据有效** - - - - -### 美团GTIS系统流程图 - -[美团 分布式系统互斥性与幂等性问题的分析与解决](https://tech.meituan.com/2016/09/29/distributed-system-mutually-exclusive-idempotence-cerberus-gtis.html) - -![输入图片说明](https://foruda.gitee.com/images/1678979231862359032/34f030c5_1766278.png "屏幕截图") - -### 使用方法 - -在Controller标注 `@RepeatSubmit` 注解即可 - -![输入图片说明](https://foruda.gitee.com/images/1678979236772683145/9fa27e5b_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678979240831458322/8e1fac4b_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/mail.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/mail.md deleted file mode 100644 index ce089802..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/mail.md +++ /dev/null @@ -1,15 +0,0 @@ -# 邮件功能 -- - - -## 配置功能 - -修改配置文件 - -![输入图片说明](https://foruda.gitee.com/images/1663555260932007318/fabb2bfa_1766278.png "屏幕截图") - -* `enabled` 为邮件功能开关 - -## 功能使用 - -参考 `demo` 模块 `MailController` 邮件演示案例 - -![输入图片说明](https://foruda.gitee.com/images/1663555374113593089/885b4db2_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sensitive.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sensitive.md deleted file mode 100644 index 3e2d92ba..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sensitive.md +++ /dev/null @@ -1,33 +0,0 @@ -# 数据脱敏 -- - - -## 功能说明 - -系统使用 `Jackson` 序列化策略 对标注了 `Sensitive` 注解的属性进行脱敏处理 - -## 使用教程 - -> 使用注解标注需要脱敏的字段 选择对应的策略 - -![输入图片说明](https://foruda.gitee.com/images/1699523591703893602/ffd6dba2_1766278.png "屏幕截图") - -* strategy 脱敏策略 -* roleKey 角色code(判断用户是否拥有角色权限) -* perms 权限code(判断用户是否拥有标识符权限) - -![输入图片说明](https://foruda.gitee.com/images/1678979315796014155/614adf91_1766278.png "屏幕截图") - -> 可再 `SensitiveStrategy` 内自定义策略 - -![输入图片说明](https://foruda.gitee.com/images/1678979319996224858/3b3e3c8b_1766278.png "屏幕截图") - -## 脱敏逻辑修改 - -> 系统使用通用接口处理是否需要脱敏 多个系统可以自定义不同的脱敏逻辑实现 - -![输入图片说明](https://foruda.gitee.com/images/1678979325448998856/b262e425_1766278.png "屏幕截图") - -> 系统默认处理逻辑为 根据角色与标识符或非管理员脱敏 可自行修改默认实现 - -![输入图片说明](https://foruda.gitee.com/images/1699523752627488891/f82f2f50_1766278.png "屏幕截图") - - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sms.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sms.md deleted file mode 100644 index 81fb345a..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sms.md +++ /dev/null @@ -1,51 +0,0 @@ -# 短信模块 -- - - - -# 配置功能 - -### 版本: >= v2.1.0 - -已完成 sms4j 项目整合 文档地址: https://sms4j.com/doc3 - -配置方式 具体厂商配置扩展 可以查看sms4j文档 - -![输入图片说明](https://foruda.gitee.com/images/1705573035997239848/2ca8512d_1766278.png "屏幕截图") - -使用方式 参考文档各种写法 下方为 demo 模块提供示例 - -![输入图片说明](https://foruda.gitee.com/images/1705573001447394180/2bd726d0_1766278.png "屏幕截图") - -### 版本: v1.2.0 提供短信模块 - -短信模块采用SPI加载
-使用哪家的短信 引入哪家的依赖 即可动态加载
-目前支持: `阿里云` `腾讯云` 欢迎扩展PR其他 - -> 参考 `ruoyi-demo` pom文件写法 - -![输入图片说明](https://foruda.gitee.com/images/1678979157797419426/cc9b7444_1766278.png "屏幕截图") - -> 修改配置文件 - -![输入图片说明](https://foruda.gitee.com/images/1678979163029635375/e5fd6e20_1766278.png "屏幕截图") - -* `enabled` 为短信功能开关 -* `endpoint` 为域名 各厂家域名固定 按照文档配置即可 -* `accessKeyId` 密钥id -* `accessKeySecret` 密钥密匙 -* `signName` 签名 -* `sdkAppId` 应用id 腾讯专用 - -## 功能使用 - -参考 `demo` 模块 `SmsController` 短信演示案例
-功能采用 `模板模式` 动态加载对应厂家的工具模板
-引入 `SmsTemplate` 即可使用 - -![输入图片说明](https://foruda.gitee.com/images/1678979168699323982/e9301e84_1766278.png "屏幕截图") - -## 重点须知 - -由于各厂家参数解析不一致 请遵守以下规则 - -![输入图片说明](https://foruda.gitee.com/images/1678979172581090456/ac1f10e8_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sse.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sse.md deleted file mode 100644 index ec51df7c..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/sse.md +++ /dev/null @@ -1,24 +0,0 @@ -# SSE功能 -- - - - -## 框架版本 >= 2.2.1 - -## 配置说明 - -配置在 `ruoyi-resource` 目录下 远程调用可直接使用 `RemoteMessageService` 接口 - -![输入图片说明](https://foruda.gitee.com/images/1721986989993234455/4214cbbd_1766278.png "屏幕截图") - -* enabled 是否开启此功能 -* path 应用路径 - -## 使用方法 - -前端连接方式: `http://后端ip:端口/resource/sse?clientid=import.meta.env.VITE_APP_CLIENT_ID&Authorization=Bearer eyJ0eXAiO......` - -其中 `Authorization` 为请求token需要登录后获取 连接成功之后 与框架内其他获取登录用户方式一致 - -`SseMessageUtils.sendMessage` 推送单机消息(特殊需求使用)
-`SseMessageUtils.subscribeMessage` 订阅分布式消息(框架初始化已订阅)
-`SseMessageUtils.publishMessage` 发布分布式消息(推荐使用 所有集群内寻找到接收人)
-`SseMessageUtils.publishAll` 群发消息给所有连接人
\ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/translation.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/translation.md deleted file mode 100644 index d0834ab8..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/translation.md +++ /dev/null @@ -1,44 +0,0 @@ -# 翻译功能 -- - - -## 框架版本 >= 1.6.0 - -## 引入依赖包 - -```xml - - com.ruoyi - ruoyi-common-translation - -``` - -## 注解 - -![输入图片说明](https://foruda.gitee.com/images/1675575648043199227/d04b3e21_1766278.png "屏幕截图") - -`@Translation` 翻译注解 用于实体类字段上
-`@TranslationType` 翻译类别注解 用于实现类上标注与 `@Translation` 注解相同的 `type` 类型 实现翻译功能 - - -## 用法说明 - -默认提供功能 `用户id转账号(用户名)` `部门id转名称` `字典type转label` `ossId转url` - -![输入图片说明](https://foruda.gitee.com/images/1675575977860232549/143b74f8_1766278.png "屏幕截图") - -用户名翻译(映射翻译) 根据另一个映射字段 翻译保存到此字段 - -![输入图片说明](https://foruda.gitee.com/images/1675576044011477847/13eb9f57_1766278.png "屏幕截图") - -ossUrl翻译(直接翻译) 直接根据此字段值翻译后替换此字段值 - -![输入图片说明](https://foruda.gitee.com/images/1675576265894720924/70792f66_1766278.png "屏幕截图") - -字典翻译(其他扩展条件翻译) 根据`other`条件 自行定义如何使用 例如字典翻译`other`条件就是字典的唯一值 - -![输入图片说明](https://foruda.gitee.com/images/1675576391012282823/f95c5d78_1766278.png "屏幕截图") - -## 自定义扩展 - -实现接口 `TranslationInterface` 标注注解 `@TranslationType` 可参考框架默认实现 - -![输入图片说明](https://foruda.gitee.com/images/1676735454308997001/cfcf3590_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/websocket.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/websocket.md deleted file mode 100644 index 55145bad..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/extend/websocket.md +++ /dev/null @@ -1,39 +0,0 @@ -# WebSocket功能 -- - - - -## 框架版本 >= 2.1.0 - -## 配置说明 - -配置在 `ruoyi-resource` 目录下 - -![输入图片说明](https://foruda.gitee.com/images/1688356273985385949/5e4d1de8_1766278.png "屏幕截图") - -* enabled 是否开启此功能 -* path 应用路径 -* allowedOrigins 设置访问源地址 - -**重点: 如关闭ws功能需连同前端ws开关一同关闭 不然前端启动会报错** - -![输入图片说明](https://foruda.gitee.com/images/1700644877512019497/052d2f46_1766278.png "屏幕截图") - -## 使用方法 - -前端连接方式: `ws://后端ip:端口/resource/websocket?clientid=import.meta.env.VITE_APP_CLIENT_ID&Authorization=Bearer eyJ0eXAiO......` - -**由于js不支持请求头传输故而采用参数传输 如支持请求头传输建议使用请求头传输** - -传输方式: -```js -headers: { - Authorization: "Bearer " + getToken(), - clientid: import.meta.env.VITE_APP_CLIENT_ID -} -``` - -其中 `Authorization` 为请求token需要登录后获取 连接成功之后 与框架内其他获取登录用户方式一致 - -`WebSocketUtils.sendMessage` 推送单机消息(特殊需求使用)
-`WebSocketUtils.subscribeMessage` 订阅分布式消息(框架初始化已订阅)
-`WebSocketUtils.publishMessage` 发布分布式消息(推荐使用 所有集群内寻找到接收人)
-`WebSocketUtils.publishAll` 群发消息给所有连接人
\ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/tree.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/tree.md deleted file mode 100644 index f649197c..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/framework/tree.md +++ /dev/null @@ -1,91 +0,0 @@ -# 项目结构 -- - - -## 目录结构 -v2.2.1 -~~~ -RuoYi-Cloud-Plus -├─ ruoyi-api // api模块 -│ └─ ruoyi-api-bom // api模块依赖管理 -│ └─ ruoyi-api-resource // 资源api模块 -│ └─ ruoyi-api-system // 系统api模块 -│ └─ ruoyi-api-workflow // 工作流api模块 -├─ ruoyi-auth // 认证服务 [9210] -├─ ruoyi-common // 通用模块 -│ └─ ruoyi-common-alibaba-bom // alibaba 依赖管理 -│ └─ ruoyi-common-bom // common 依赖管理 -│ └─ ruoyi-common-bus // 消息总线模块 -│ └─ ruoyi-common-core // 核心功能模块 -│ └─ ruoyi-common-dict // 字典集成模块 -│ └─ ruoyi-common-doc // 文档集成模块 -│ └─ ruoyi-common-dubbo // dubbo集成模块 -│ └─ ruoyi-common-elasticsearch // ES集成模块 -│ └─ ruoyi-common-encrypt // 数据加解密模块 -│ └─ ruoyi-common-excel // excel集成模块 -│ └─ ruoyi-common-idempotent // 幂等功能模块 -│ └─ ruoyi-common-job // job定时任务集成模块 -│ └─ ruoyi-common-json // json集成模块 -│ └─ ruoyi-common-loadbalancer // 团队负载均衡集成模块 -│ └─ ruoyi-common-log // 日志集成模块 -│ └─ ruoyi-common-logstash // elk日志集成模块 -│ └─ ruoyi-common-mail // 邮件集成模块 -│ └─ ruoyi-common-mybatis // mybatis数据库相关集成模块 -│ └─ ruoyi-common-oss // oss相关集成模块 -│ └─ ruoyi-common-prometheus // prometheus监控 -│ └─ ruoyi-common-redis // redis集成模块 -│ └─ ruoyi-common-satoken // satoken集成模块 -│ └─ ruoyi-common-seata // seata分布式事务集成模块 -│ └─ ruoyi-common-security // 框架权限鉴权集成模块 -│ └─ ruoyi-common-sensitive // 脱敏功能模块 -│ └─ ruoyi-common-sentinel // sentinel集成模块 -│ └─ ruoyi-common-skylog // skywalking日志收集模块 -│ └─ ruoyi-common-sms // 短信集成模块 -│ └─ ruoyi-common-social // 社交三方功能模块 -│ └─ ruoyi-common-sse // sse流推送模块 -│ └─ ruoyi-common-tenant // 租户功能模块 -│ └─ ruoyi-common-translation // 通用翻译功能 -│ └─ ruoyi-common-web // web服务集成模块 -│ └─ ruoyi-common-websocket // websocket服务集成模块 -├─ ruoyi-example // 例子模块 -│ └─ ruoyi-demo // 演示模块 [9401] -│ └─ ruoyi-test-mq // mq演示模块 [9402] -├─ ruoyi-gateway // 网关模块 [8080] -├─ ruoyi-modules // 功能模块 -│ └─ ruoyi-gen // 代码生成模块 [9202] -│ └─ ruoyi-job // 任务调度模块 [9203,9901] -│ └─ ruoyi-resource // 资源模块 [9204] -│ └─ ruoyi-system // 系统模块 [9201] -│ └─ ruoyi-workflow // 工作流模块 [9205] -├─ ruoyi-visual // 可视化模块 -│ └─ ruoyi-monitor // 服务监控模块 [9100] -│ └─ ruoyi-nacos // nacos服务模块 [8848,9848,9849] -│ └─ ruoyi-seata-server // seata服务模块 [7091,8091] -│ └─ ruoyi-sentinel-dashboard // sentinel控制台模块 [8718] -│ └─ ruoyi-snailjob-server // 任务调度控制台模块 [8800,17888] -├─ plus-ui // 前端框架 [80] -├─ config/nacos // nacos配置文件(需复制到nacos配置中心使用) -│ └─ sentinel-ruoyi-gateway.json // sentinel对接gateway限流配置文件 -│ └─ seata-server.properties // seata服务配置文件 -│ └─ application-common.yml // 所有应用主共享配置文件 -│ └─ datasource.yml // 所有应用共享数据源配置文件 -│ └─ ruoyi-auth.yml // auth 模块配置文件 -│ └─ ruoyi-gateway.yml // gateway 模块配置文件 -│ └─ ruoyi-gen.yml // gen 模块配置文件 -│ └─ ruoyi-job.yml // job 模块配置文件 -│ └─ ruoyi-monitor.yml // monitor 模块配置文件 -│ └─ ruoyi-resource.yml // resource 模块配置文件 -│ └─ ruoyi-sentinel-dashboard.yml // sentinel 控制台 模块配置文件 -│ └─ ruoyi-snailjob-server.yml // snailjob 控制台 模块配置文件 -│ └─ ruoyi-system.yml // systen 模块配置文件 -│ └─ ruoyi-workflow.yml // workflow 模块配置文件 -├─ config/grafana // grafana配置文件(需复制到grafana使用) -│ └─ Nacos.json // Nacos监控页面 -│ └─ SLS JVM监控大盘.json // JVM监控页面 -│ └─ Spring Boot 2.1 Statistics.json // SpringBoot监控页面 -├─ sql // sql脚本 -├─ docker // docker 配置脚本 -├─ .run // 执行脚本文件 -├─ .editorconfig // 编辑器编码格式配置 -├─ LICENSE // 开源协议 -├─ pom.xml // 公共依赖 -├─ README.md // 框架说明文件 -~~~ \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/home.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/home.md deleted file mode 100644 index 290a4f9c..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/home.md +++ /dev/null @@ -1,137 +0,0 @@ - -
- -- - - -# 平台简介 -
- -[![码云Gitee](https://gitee.com/dromara/RuoYi-Cloud-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Cloud-Plus) -[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Cloud-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Cloud-Plus) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Cloud-Plus/blob/master/LICENSE) -[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Cloud-Plus) -
-[![RuoYi-Cloud-Plus](https://img.shields.io/badge/RuoYi_Cloud_Plus-2.2.1-success.svg)](https://gitee.com/dromara/RuoYi-Cloud-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)]() - -> RuoYi-Cloud-Plus `微服务通用权限管理系统` 重写 RuoYi-Cloud 全方位升级(不兼容原框架) - -> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可
-活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 - -> 系统演示: [传送门](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4836388&doc_id=1469725) 分布式集群版本(功能一致) - -# 本框架与RuoYi的功能差异 - -| 功能 | 本框架 | RuoYi | -|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| -| 前端项目 | 采用 Vue3 + TS + ElementPlus 重写 | 基于Vue2/Vue3 + JS | -| 后端项目结构 | 采用插件化 + 扩展包形式 结构解耦 易于扩展 | 模块相互注入耦合严重难以扩展 | -| 后端代码风格 | 严格遵守Alibaba规范与项目统一配置的代码格式化 | 代码书写与常规结构不同阅读障碍大 | -| 分布式注册中心 | 采用 Alibaba Nacos 源码集成便于调试扩展与二次开发 框架还为其增加了各种监控 | 采用 Alibaba Nacos 自行搭建纯官方版本不可靠 | -| 分布式配置中心 | 采用 Alibaba Nacos 源码集成便于调试扩展与二次开发 框架还为其增加了各种监控 | 采用 Alibaba Nacos 自行搭建纯官方版本不可靠 | -| 服务网关 | 采用 SpringCloud Gateway 框架扩展了多种功能
例如:内网鉴权、请求体缓存、跨域配置、请求响应日志等 | 采用 SpringCloud Gateway 功能单一 | -| 负载均衡 | 采用 SpringCloud Loadbalancer 扩展支持了开发团队路由 便于多团队开发调试 | 采用 SpringCloud Loadbalancer 功能单一 | -| RPC远程调用 | 采用 全新 Apache Dubbo 3.X 历史悠远不用多说 | 采用 feign 功能有限编写方式 网络波动大 不稳定 | -| 分布式限流熔断 | 采用 Alibaba Sentinel 源码集成便于调试扩展与二次开发 框架还为其增加了各种监控 | 采用 Alibaba Sentinel 自行搭建纯官方版本不可靠 | -| 分布式事务 | 采用 Alibaba Seata 源码集成对接了Nacos与各种监控 简化了搭建部署流程 | 采用 Alibaba Seata 自行搭建纯官方版本 搭建繁琐与Nacos不挂钩 代码内使用方式怪异等 | -| Web容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat | -| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 | -| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验
角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 | -| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer
可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例) | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 | -| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 | -| Redis客户端 | 采用 Redisson Redis官方推荐 基于Netty的客户端工具
支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan
支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐
连接池采用 common-pool Bug多经常性出问题 | -| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能
例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写Redis代码逻辑 | -| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多
例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL | -| SQL监控 | 采用 p6spy 可输出完整SQL与执行时间监控 | log输出 需手动拼接sql与参数无法快速查看调试问题 | -| 数据分页 | 采用 Mybatis-Plus 分页插件
框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序 | 采用 PageHelper 仅支持单查询分页 参数只能从param传 只能单排序 功能扩展性差 体验不好 | -| 数据权限 | 采用 Mybatis-Plus 插件 自行分析拼接SQL 无感式过滤
只需为Mapper设置好注解条件 支持多种自定义 不限于部门角色 | 采用 注解+aop 实现 基于部门角色 生成的sql兼容性差 不支持其他业务扩展
生成sql后需手动拼接到具体业务sql上 对于多个Mapper查询不起作用 | -| 数据脱敏 | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件
支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展 | 无 | -| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密
支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 | -| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译
支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 | -| 多数据源框架 | 采用 dynamic-datasource 支持市面大部分数据库
通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源
支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | -| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 | -| 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 | -| 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 | -| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物 | 无 | -| SSE推送 | 采用 Spring SSE 实现 扩展了Token鉴权与分布式会话同步 | 无 | -| 序列化 | 采用 Jackson Spring官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 | -| 分布式幂等 | 参考美团GTIS防重系统简化实现(细节可看文档) | 手动编写注解基于aop实现 | -| 分布式任务调度 | 采用 SnailJob 天生支持分布式 统一的管理中心 支持多种数据库 支持分片重试DAG任务流等 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 | -| 分布式日志中心 | 采用 ELK 业界成熟解决方案 实时收集所有服务的运行日志 快速发现定位问题 | 无 | -| 分布式搜索引擎 | 采用 ElasticSearch、Easy-Es 以 Mybatis-Plus 方式操作 ElasticSearch | 无 | -| 分布式消息队列 | 采用 支持 Kafka、RocketMQ、RabbitMQ 各种 延迟消息 事务消息 流消息 | 无 | -| 分布式消息总线 | 采用 SpringCloud Bus 实现事件总线 跨服务通知 支持 Kafka、RocketMQ、RabbitMQ | 无 | -| 分库分表功能 | 采用 Apache Sharding-Proxy 代理服务无入侵支持分库分表 只需编写分库分表规则即可 | 无 | -| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储
支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 | -| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家 | 不支持 | -| 短信 | 支持 阿里、腾讯 只需在yml配置好厂家密钥即可使用 接口化支持扩展其他厂家 | 不支持 | -| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 | -| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释
只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 | -| 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 | -| Excel框架 | 采用 Alibaba EasyExcel 基于插件化
框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | -| 工作流支持 | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能 | 无 | -| 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 | -| 服务监控框架 | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制
实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 | -| 全方位监控报警 | 采用 Prometheus、Grafana 多样化采集 多模板大屏展示 实时报警监控 提供详细的搭建文档 | 无 | -| 链路追踪 | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗
用了它即可实时查看请求经过的每一处每一个节点 | 无 | -| 代码生成器 | 只需设计好表结构 一键生成所有crud代码与页面
降低80%的开发量 把精力都投入到业务设计上
框架为其适配MP、SpringDoc规范化代码 同时支持动态多数据源代码生成 | 代码生成原生结构 只支持单数据源生成 | -| 部署方式 | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼 | 原生jar部署 其他环境需手动下载安装 自行搭建 | -| 项目路径修改 | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的 | 需要做很多改造 文档说明有限 | -| 国际化 | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化 | 只提供基础功能 其他需自行编写扩展 | -| 代码单例测试 | 提供单例测试 使用方式编写方法与maven多环境单测插件 | 只提供基础功能 其他需自行编写扩展 | -| Demo案例 | 提供框架功能的实际使用案例 单独一个模块提供了很多很全 | 无 | - -## 本框架与RuoYi的业务差异 - -| 业务 | 功能说明 | 本框架 | RuoYi | -|--------|-----------------------------------------|-----|------------------| -| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 | -| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 | -| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 | -| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 | -| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 | -| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 | -| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 | -| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 | -| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 | -| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 | -| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 | -| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 | -| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 | -| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 | -| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 | -| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 | -| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载 | 支持 | 仅支持单数据源 | -| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 | -| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 | -| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 | -| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 | -| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 | - -## 演示图例 - -| | | -|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| -| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") | - - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/1.Xinit.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/1.Xinit.md deleted file mode 100644 index 19492b1e..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/1.Xinit.md +++ /dev/null @@ -1,87 +0,0 @@ -# 1.X项目初始化 -- - - -### 项目分支说明 -`master` 主分支 稳定发布分支
-`dev` 开发分支 代码随时更新 不推荐使用 经测试后会发布到主分支
-`2.X` 新2.X大版本分支
-`future/*` 新功能预览分支 - -### 项目必备环境 -> 推荐使用 `docker` 安装 项目内置 `docker` 编排文件 - -* oracle jdk 8 11 (暂时不支持 17 不支持大于 jdk8_202 因为202是最后一个免费版本) -* mysql 5.7 8.0 (5.6未适配可能会有问题) -* oracle 11g 12c -* postgres 13 14 -* redis 5.X 6.X 7.X 由于框架大量使用了redis特性 版本必须 >= 5.X ([win redis 下载地址](https://github.com/zkteco-home/redis-windows)) -* minio 本地文件存储 或 阿里云 腾讯云 七牛云等一切支持S3协议的云存储 -* maven 3.6.3 3.8.X -* nodejs >= 12 -* npm 6.X 8.X (7.X确认有问题) -* nacos >= 2.X(框架1.3.0内置nacos) -* sentinel 框架内置 -* seata 框架内置 - -### 需勾选 maven 对应环境 -![输入图片说明](https://foruda.gitee.com/images/1678976284045210056/a2f28d33_1766278.png "屏幕截图") - -### 默认 `JDK8` 如有变动 需更改以下配置 - -![输入图片说明](https://foruda.gitee.com/images/1686813181851830778/2dd7954c_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1686813189749486666/c526486c_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1686813196981178511/cd218705_1766278.png "屏幕截图") - -### sql导入 -将sql导入到与sql文件名对应的数据库(不要放到一个库下)
- -![输入图片说明](https://foruda.gitee.com/images/1678981513725772842/8097a816_1766278.png "屏幕截图") - -### 使用内置 `ruoyi-naocs` 服务 从这开始 - -> 更改 ruoyi-nacos 数据库地址 - -![输入图片说明](https://foruda.gitee.com/images/1664422006264405180/cac5afc6_1766278.png "屏幕截图") - -**其余流程同下方步骤一致** - -### 自建 Nacos 从这开始 - -**Nacos 数据库指向 ry-config 数据库(此处重点: 此数据库为定制数据 未使用此库会无法读取配置)** - -> 将项目 `config/nacos` 下所有配置 复制到 `nacos` 内(建议手动复制内容 防止编码不一致问题) - -![输入图片说明](https://foruda.gitee.com/images/1678979826345958752/913142c9_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678979856705927770/75cc1e8c_1766278.png "屏幕截图") - -> 更改 `主pom文件` 对应环境的 `nacos` 地址 - -![输入图片说明](https://foruda.gitee.com/images/1678979881888833924/7e6a191f_1766278.png "屏幕截图") - -### 更改 `Nacos` 自定义配置 - -**忠告: 微服务配置相当复杂 请勿在不懂原理的情况下乱改** - -> `application-common.yml` 更改 - -![输入图片说明](https://foruda.gitee.com/images/1678979889410167794/100db4ab_1766278.png "屏幕截图") - -> `datasource.yml` 更改 - -![输入图片说明](https://foruda.gitee.com/images/1678979894464784408/0d020c07_1766278.png "屏幕截图") - -> `seata-server.properties` 更改 - -![输入图片说明](https://foruda.gitee.com/images/1678979902433843257/12da2839_1766278.png "屏幕截图") - -### 使用内置 `ruoyi-seata-server` 服务 从这开始 - -执行 `ry-seata.sql` 文件 初始化服务端数据库
-修改 `nacos` 内的 `seata-server.properties` 的数据库地址
-启动 `ruoyi-seata-server` 服务即可 - -### 服务启动顺序说明 - -1. 必须启动基础建设: mysql redis nacos
-2. 可选启动基础建设: minio(影响文件上传) seata(影响分布式事务 默认开启) sentinel(影响熔断限流) monitor(影响监控) xxljob(影响定时任务)
-3. 必须启动应用服务: gateway auth system
-4. 可选启动应用服务: resource(影响资源使用 文件上传 邮件 短信等) gen(代码生成) job(影响定时任务) demo(影响demo使用) diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/deploy.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/deploy.md deleted file mode 100644 index bdc653bc..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/deploy.md +++ /dev/null @@ -1,118 +0,0 @@ -# 应用部署 -- - - -## 版本 >= 1.3.0 - -### 请优先阅读 [idea环境配置](/ruoyi-cloud-plus/quickstart/idea_environment.md) - -## 手动部署 - -在服务器安装 `mysql` `redis` `nginx` `minio` 等其他组件 - -将项目内 `docker/` 文件夹下的文件内容 放到对应的组件内
-例如: 将项目内 `docker/nginx/nginx.conf` 配置文件 复制到 `nginx` 配置内
-将项目内 `docker/redis/redis.conf` 配置文件 复制到 `redis` 配置内
- -并修改相关参数如 `前端页面存放位置` `后端Ip地址` 等使其生效 - -jar包部署后端服务 打包命令如下 -```mvn -mvn clean install -D maven.test.skip=true -P prod -``` -前端参考下方前端部署章节 - - -## docker 后端部署 - -### 请优先阅读 [idea环境配置](https://gitee.com/dromara/RuoYi-Cloud-Plus/wikis/pages?sort_id=5985190&doc_id=2056143) - -**重点: 一知半解的必看** -> [docker安装](https://lionli.blog.csdn.net/article/details/83153029)
-> [docker-compose安装](https://lionli.blog.csdn.net/article/details/111220320)
-> [docker网络模式讲解](https://lionli.blog.csdn.net/article/details/109603785)
-> [docker 开启端口 2375 供外部程序访问](https://lionli.blog.csdn.net/article/details/92627962) - -### 将配置使用FTP上传到根目录 -idea拖拽文件到远程目录即可上传
-![输入图片说明](https://foruda.gitee.com/images/1662109450908169859/eaac9299_1766278.png "屏幕截图") - -### 给docker分配文件夹权限 -**重点注意: 一定要确保目录 `/docker` 及其所有子目录 具有写权限 如果后续出现权限异常问题 重新执行一遍分配权限** -![输入图片说明](https://foruda.gitee.com/images/1662109847279259882/3a2202c1_1766278.png "屏幕截图") -```shell -chmod -R 777 /docker -``` -### 构建应用镜像 - -**1.需要先使用maven打包成jar包**
-![输入图片说明](https://foruda.gitee.com/images/1662110477410977621/c6931c42_1766278.png "屏幕截图") - -**2.执行构建**
-> 项目初始化后会自动生成构建镜像的运行配置
-配置好docker连接之后 运行如下即可构建对应的应用镜像 - -**重点注意: idea2024及以上版本要求必须在本地安装docker才可以执行如下操作** - -![输入图片说明](https://foruda.gitee.com/images/1662110192257483752/0f754b47_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1662120029312793237/89dee3e5_1766278.png "屏幕截图") - -**3.结构讲解**
-右键编辑 即可看到内部配置
- -![输入图片说明](https://foruda.gitee.com/images/1662458355500139498/eaa26036_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1662458446794722159/32c086a7_1766278.png "屏幕截图") - - -### 创建基础服务 - -```shell -docker-compose up -d mysql nginx-web redis minio -``` - -### 创建中心服务(需要先构建服务镜像) - -1.X -```shell -docker-compose up -d nacos seata-server sentinel ruoyi-monitor ruoyi-xxl-job-admin -``` - -2.X -```shell -docker-compose up -d nacos seata-server sentinel ruoyi-monitor ruoyi-snailjob-server -``` - -### 创建业务服务(需要先构建服务镜像) - -```shell -docker-compose up -d ruoyi-gateway ruoyi-auth ruoyi-system ruoyi-resource -``` - -### docker其他操作(idea的docker插件 推荐使用) -![输入图片说明](https://foruda.gitee.com/images/1662458296425228696/90b4b4f8_1766278.png "屏幕截图") - - -## 前端部署 - -执行打包命令 -```shell -# 打包正式环境 -npm run build:prod -``` -打包后生成打包文件在 `ruoyi-ui/dist` 目录
-将 `dist` 目录下文件(不包含 `dist` 目录) 上传到部署服务器 `docker/nginx/html` 目录下(手动部署放入自己配置的路径即可)
-![输入图片说明](https://foruda.gitee.com/images/1662110914769648699/07f344c4_1766278.png "屏幕截图")
-重启 `nginx` 服务即可 - - -### 如需更改后端代理路径或者后端ip地址的话往下看 - -更改`nginx.conf`配置文件代理路径(注意: /开头/结尾) - -![输入图片说明](https://foruda.gitee.com/images/1660185698211067202/屏幕截图.png "屏幕截图.png") - -更改前端`.env.环境` 文件内的 `VITE_APP_BASE_API` - -![输入图片说明](https://foruda.gitee.com/images/1724318035232137124/5d035a09_1766278.png "屏幕截图") - -更改`nginx.conf`配置文件后端ip地址 - -![输入图片说明](https://foruda.gitee.com/images/1660185711265558730/屏幕截图.png "屏幕截图.png") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/extend_project.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/extend_project.md deleted file mode 100644 index 8d68fa4d..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/extend_project.md +++ /dev/null @@ -1,42 +0,0 @@ -# 基于 RuoYi-Cloud-Plus 的扩展项目列表 -- - - -### 精品PR 欢迎投稿 -| 功能介绍 | PR地址 | -|-------------------------------------|------------------------------------------------------| -| 拖拽图片调整显示顺序 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/173 | -| 增加Jasypt加密库对配置文件加密 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/177 | -| 使用富文本wangeditor5替换Quill | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/213 | -| sentinel持久化nacos动态更改 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/37 | -| 集成screw数据库文档功能模块 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/42 | -| Excel导入模板增加批注支持 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/222 | -| 压缩包处理工具 支持本地文件/目录+oss文件/网络文件混合 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/44 | -| 添加websocket模块 支持satoken鉴权 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/65 | -| 数据库字段加解密(支持 base64 aes rsa sm2 sm4) | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/274 | -| 增加liquibase迁移数据库 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/299 | -| 增加OSS模块支持本地环境 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/353 | -| 扩展模块独立集成flyway | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/439 | -| 扩展模块独立集成go-view大屏看板 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/445 | -| 基于AmazonS3协议的分片上传 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/130 | -| 扩展forest http客户端 声明式http请求 二次封装像工具类 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/547 | -| 增加短链接生成工具 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/515 | -| 新增oss预签名上传工具组合使用异步客户端分片 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/522 | -| 新增规则引擎LiteFlow,SQL持久化接入,支持可视化页面 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/552 | -| 一键部署到私有Nexus仓库 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/181 | -| 服务状态监控发送邮件钉钉等 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/568 | -| 登录验证支持2FA验证 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/578 | - -### 欢迎投稿 项目介绍+项目地址 - - -| 项目介绍 | 项目地址 | -|--------------------------------|-------------------------------------------------------------------------| -| 分布式集群扩展 | https://gitee.com/dromara/RuoYi-Vue-Plus | -| Plus学习笔记(常规功能) | https://zhonglingyuxiu1028.github.io/zlyx-space/#/ruoyi-vue-plus/home | -| Plus学习笔记(微服务组件) | https://zhonglingyuxiu1028.github.io/zlyx-space/#/ruoyi-cloud-plus/home | -| 基于uniapp+TmUI从0开发 支持H5/小程序/安卓 | https://gitee.com/dapppp/ruoyi-plus-miniapp | -| 基于RuoYi-App框架二次修改使用Uniapp+Vue3 | https://gitee.com/wangying110166/ruo-yi-uni-app-plus | -| 基于RuoYi-App框架对接Plus后端 | https://gitee.com/FnTop/RuoYi-App-Plus | -| 基于vben(ant-design-vue)前端项目 | https://gitee.com/dapppp/ruoyi-plus-vben | -| 基于vue-next-admin的vue3+ts前端 | https://gitee.com/thiszhc/RuoYi-Vue3-UI | -| MybatisFlex版本 | https://gitee.com/yhan219/ruoyi-cloud-flex | - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/idea_environment.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/idea_environment.md deleted file mode 100644 index 25a5fa3c..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/idea_environment.md +++ /dev/null @@ -1,38 +0,0 @@ -# idea环境配置 -- - - -## 配置项目编码 -![输入图片说明](https://foruda.gitee.com/images/1662107706295343419/e27065a9_1766278.png "屏幕截图") - -## 配置运行看板 -![输入图片说明](https://foruda.gitee.com/images/1662108673306567278/8af97b47_1766278.png "屏幕截图") -### 配置spring与docker看板 -![输入图片说明](https://foruda.gitee.com/images/1662111392476935892/6b6760fb_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1662108865191892425/3c045999_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1662108877322329668/ddb6d93d_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1662108894122798039/6a53a38c_1766278.png "屏幕截图") - -## 配置服务器SSH连接 -进入 `Settings -> Tools -> SSH Configurations` 点击加号创建SSH连接配置
-填写 服务器IP 用户名 密码 端口号 点击 Test Connection 测试连接
-![输入图片说明](https://foruda.gitee.com/images/1662107776533098115/bd78467b_1766278.png "屏幕截图") -使用Terminal 工具 点击箭头找到上方创建的SSH连接配置
-选择即可进入SSH连接界面 在这里可以对服务器进行命令操作
-![输入图片说明](https://foruda.gitee.com/images/1662108010120640495/c70f9f9a_1766278.png "屏幕截图") - -## 配置服务器FTP连接 -进入 `Settings -> Build-> Deployment` 点击加号 选择SFTP 创建 FTP 连接配置
-选择之前创建好的SSH配置 点击 Test Connection 测试连接
-![输入图片说明](https://foruda.gitee.com/images/1662107899553257979/e2eeb7fd_1766278.png "屏幕截图") -在IDEA上方工具栏 找到 `Tools -> Deployment -> Browse Remote Host` 打开远程界面
-点击箭头找到我们上方配置的SFTP连接配置 即可连接到服务器的文件目录
-![输入图片说明](https://foruda.gitee.com/images/1662107974682787233/b8a601fd_1766278.png "屏幕截图") - -## 配置Docker连接 -### 可操作远程docker与构建上传docker镜像(代替原来maven docker插件) -tcp连接需要开放服务器2375端口
-ssh需要使用上方的SSH连接配置
-建议使用SSH连接
-![输入图片说明](https://foruda.gitee.com/images/1662108188005932060/75872bf8_1766278.png "屏幕截图") -配置好之后 在运行窗口会多出一个Docker图标 双击即可连接远程docker
-可以查看容器实时日志 启动 重启 停止 等操作
-![输入图片说明](https://foruda.gitee.com/images/1662108250902891875/b82d022b_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/init.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/init.md deleted file mode 100644 index f6d4fadc..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/init.md +++ /dev/null @@ -1,102 +0,0 @@ -# 2.X项目初始化 -- - - -### 项目分支说明 - -`2.X` 主分支 新2.X版本 稳定发布分支
-`dev` 开发分支 代码随时更新 不推荐使用 经测试后会发布到主分支
-`future/*` 新功能预览分支 - -### 项目必备环境 -> 推荐使用 `docker` 安装 项目内置 `docker` 编排文件 - -**注意: 禁止使用 `oraclejdk`(由于spring的bug导致打包运行会报错)** - -**Spring官方推荐使用JDK https://bell-sw.com/pages/downloads/** - -![输入图片说明](https://foruda.gitee.com/images/1720080025744223375/0213a652_1766278.png "屏幕截图") - -* openjdk-17/21 或 graalvm-community-jdk-17/21 [下载地址](https://github.com/graalvm/graalvm-ce-builds/releases) 版本 -* mysql 5.7 8.0 (其他版本未测试 如其他版本没问题 可以告知咱们) -* oracle >= 12c (其他版本未测试 如其他版本没问题 可以告知咱们) -* postgres 13 14 (其他版本未测试 如其他版本没问题 可以告知咱们) -* redis 5.X 6.X 7.X 由于框架大量使用了redis特性 版本必须 >= 5.X ([win redis 下载地址](https://github.com/zkteco-home/redis-windows)) -* minio 本地文件存储 或 阿里云 腾讯云 七牛云等一切支持S3协议的云存储 -* maven >= 3.8.X -* nodejs 18.18 (其他版本未测试 如其他版本没问题 可以告知咱们) -* npm >= 8.X (7.X确认有问题) -* idea 2022 2024 (一定不要使用2023后果自负 bug太多影响项目开发) -* nacos >= 2.X(框架已经内置 采用nacos官方jar包) -* sentinel 框架内置(采用sentinel官方jar包) -* seata 框架内置(采用seata官方jar包) - -### 需勾选 maven 对应环境 - -![输入图片说明](https://foruda.gitee.com/images/1678976284045210056/a2f28d33_1766278.png "屏幕截图") - -### 默认 `JDK17` 如有变动 需更改以下配置 - -![输入图片说明](https://foruda.gitee.com/images/1678941027820943505/c688e01e_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678941120518807034/4d56fcc9_1766278.png "屏幕截图") - -### sql导入 - -将sql导入到与sql文件名对应的数据库(不要放到一个库下)
-默认数据库为mysql如需使用其他数据库 看这里 => [多数据库数据源](../framework/extend/dynamic_datasource.md)
- -![输入图片说明](https://foruda.gitee.com/images/1717122730708924506/7f3aaecf_1766278.png "屏幕截图") - -### 使用内置 `ruoyi-naocs` 服务 从这开始 - -> 更改 ruoyi-nacos 数据库地址 - -![输入图片说明](https://foruda.gitee.com/images/1664422006264405180/cac5afc6_1766278.png "屏幕截图") - -**其余流程同下方步骤一致** - -### 自建 Nacos 从这开始 - -**Nacos 数据库指向 ry-config 数据库(此处重点: 此数据库为定制数据 未使用此库会无法读取配置)** - -> 将项目 `config/nacos` 下所有配置 复制到 `nacos` 内(建议手动复制内容 防止编码不一致问题) - -**注意: 不懂就不要乱改配置文件内容 框架内所有功能都是配置好的!!!不要画蛇添足**
-**注意: 不懂就不要乱改配置文件内容 框架内所有功能都是配置好的!!!不要画蛇添足**
-**注意: 不懂就不要乱改配置文件内容 框架内所有功能都是配置好的!!!不要画蛇添足**
- -![输入图片说明](https://foruda.gitee.com/images/1678979826345958752/913142c9_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678979856705927770/75cc1e8c_1766278.png "屏幕截图") - -> 更改 `主pom文件` 对应环境的 `nacos` 地址 - -![输入图片说明](https://foruda.gitee.com/images/1678979881888833924/7e6a191f_1766278.png "屏幕截图") - -### 更改 `Nacos` 自定义配置 - -**忠告: 微服务配置相当复杂 请勿在不懂原理的情况下乱改**
-**忠告: 微服务配置相当复杂 请勿在不懂原理的情况下乱改**
-**忠告: 微服务配置相当复杂 请勿在不懂原理的情况下乱改**
- -> `application-common.yml` 更改 - -![输入图片说明](https://foruda.gitee.com/images/1678979889410167794/100db4ab_1766278.png "屏幕截图") - -> `datasource.yml` 更改 - -![输入图片说明](https://foruda.gitee.com/images/1678979894464784408/0d020c07_1766278.png "屏幕截图") - -> `seata-server.properties` 更改 - -![输入图片说明](https://foruda.gitee.com/images/1678979902433843257/12da2839_1766278.png "屏幕截图") - -### 使用内置 `ruoyi-seata-server` 服务 从这开始 - -执行 `ry-seata.sql` 文件 初始化服务端数据库
-修改 `nacos` 内的 `seata-server.properties` 的数据库地址
-启动 `ruoyi-seata-server` 服务即可 - -### 服务启动顺序说明 - -1. 必须启动基础建设: mysql redis nacos
-2. 可选启动基础建设: minio(影响文件上传) seata(影响分布式事务 默认开启) sentinel(影响熔断限流) monitor(影响监控) snailjob(影响定时任务)
-3. 必须启动应用服务: gateway auth system
-4. 可选启动应用服务: resource(影响资源使用 websocket 文件上传 邮件 短信等) workflow(工作流) gen(代码生成) job(影响定时任务) demo(影响demo使用) diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/power_job_init.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/power_job_init.md deleted file mode 100644 index 907c7fc7..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/power_job_init.md +++ /dev/null @@ -1,33 +0,0 @@ -# 搭建PowerJob任务调度中心(2.X分支已废弃) -- - - -### 废弃原因 - -接到大量投诉 使用困难 用法诡异 各种问题等 - -### 配置调度中心客户端 -> 查看ruoyi-job配置文件(默认情况下无需做任何更改) -> -![输入图片说明](https://foruda.gitee.com/images/1688013407489024239/9b619e0d_1766278.png "屏幕截图") - -* `enabled` 可启用或关闭客户端注册 -* `server-address` 为调度中心地址 -* `server-name` 为调度中心服务名 -* `app-name` 为执行器组账户名(需在调度中心注册方可登录查看) - -### 启用调度中心 -**需执行 ry-job.sql 默认账号密码 `ruoyi-worker` `123456` 账号在数据库里 可以在页面修改密码** -
- -![输入图片说明](https://foruda.gitee.com/images/1688634898607827011/8853b387_1766278.png "屏幕截图") - -> 在 `ruoyi-visual -> ruoyi-powerjob-server` 启动 -> -![输入图片说明](https://foruda.gitee.com/images/1688013606234848334/cf2028cd_1766278.png "屏幕截图") - -> 需修改配置文件数据库连接地址(**注意: 此处为ruoyi-powerjob-server服务的配置文件**) -> -![输入图片说明](https://foruda.gitee.com/images/1688013663152608235/6c5d6a9c_1766278.png "屏幕截图") - -> 也可配置邮件发送 钉钉推送 和 mongodb存储 -> -![输入图片说明](https://foruda.gitee.com/images/1687335842722317559/f875c07a_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/snail_job_init.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/snail_job_init.md deleted file mode 100644 index 25c21ebb..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/snail_job_init.md +++ /dev/null @@ -1,38 +0,0 @@ -# 搭建SnailJob任务调度中心(2.2.0新功能) -- - - - -### 视频介绍 - -[Snail job任务调度中心:轻松掌握任务管理、重试机制和任务编排](https://www.bilibili.com/video/BV19i421m7GL/) - -### 配置调度中心客户端 -> 修改主服务配置文件 -> - -![输入图片说明](https://foruda.gitee.com/images/1716175076777941469/db565dc1_1766278.png "屏幕截图") - -* `enabled` 可启用或关闭客户端注册 -* `server.server-name` 为调度中心服务名(自动从Nacos获取服务 支持动态扩容调度中心) -* `server.address` 为调度中心地址(服务名优先 ip垫底) -* `server.port` 为调度中心通信端口 -* `token` 为组通信校验token(可在调度中心组配置更换) -* `group-name` 为执行器组 -* `namespace` 作用域(不同作用域相互隔离请勿填错) - -### 启用调度中心 -**需执行 ruoyi-job.sql 默认账号密码 `admin` `admin` 账号在数据库里 可以在页面修改密码** -
- -![输入图片说明](https://foruda.gitee.com/images/1688634898607827011/8853b387_1766278.png "屏幕截图") - -> 在 `ruoyi-visual -> ruoyi-snailjob-server` 模块启动 -> -![输入图片说明](https://foruda.gitee.com/images/1716175119324078438/ca667a0c_1766278.png "屏幕截图") - -> 需修改配置文件数据库连接地址(**注意: 此处为ruoyi-snailjob-server服务的配置文件 支持多种不同数据库**) -> -![输入图片说明](https://foruda.gitee.com/images/1688013663152608235/6c5d6a9c_1766278.png "屏幕截图") - -### 快速入门 - -[Snailjob快速入门 基本使用介绍](https://juejin.cn/post/7412955032092442675) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/worker_init.md b/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/worker_init.md deleted file mode 100644 index 121a094d..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-cloud-plus/quickstart/worker_init.md +++ /dev/null @@ -1,52 +0,0 @@ -# 工作流初始化 -- - - - -### 注意事项 - -设置 RabbitMQ 配置 `application-common.yml` 配置文件 (可使用其他例如 kafka、rocketmq 详见 ruoyi-common-bus 模块) - -此功能用于跨服务同步流程与业务状态 MQ安装方式可参考文档扩展功能 - -![输入图片说明](https://foruda.gitee.com/images/1718728432072816698/47eadbb1_1766278.png "屏幕截图") - - -### 工作流使用及配置方式 - -1.找到项目中bpmn文件夹 - -![输入图片说明](https://foruda.gitee.com/images/1714211764058540441/5c8b97af_5363069.png "屏幕截图") - -2.启动项目找到流程定义通过**部署流程文件**将bpmn文件夹下**模型.zip**上传 - -![输入图片说明](https://foruda.gitee.com/images/1714211950485333575/1e2b3ff4_5363069.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714212113004821592/96586e69_5363069.png "屏幕截图") - -3.导入**模型.zip**后将会出现以下列表,默认使用**leave1**,test_leave为请假申请表名称 - -![输入图片说明](https://foruda.gitee.com/images/1714212222766335759/1227bbd6_5363069.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714212493602552742/9e0258b1_5363069.png "屏幕截图") - -**此处表名由来与表单源码内编写的表名保持一致方可互相绑定** - -![输入图片说明](https://foruda.gitee.com/images/1716447357161482917/2c9b1639_1766278.png "屏幕截图") - - -4.新增一条请假申请,提交后将会得到如下信息 - -![输入图片说明](https://foruda.gitee.com/images/1714212617432902105/3609f6ef_5363069.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714212630860787365/2922d38e_5363069.png "屏幕截图") - -5.关于如何切换一个新的流程使用,当前默认使用得KEY为leave1 ,我们切换到leave2使用,我们只需点击绑定业务将表名绑定,重新发起一个新的请假申请就可以得到一个新的流程信息 - -![输入图片说明](https://foruda.gitee.com/images/1714212876442323110/4554ea95_5363069.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714213037864274694/613149f5_5363069.png "屏幕截图") - -**此处表名由来与表单源码内编写的表名保持一致方可互相绑定** - -![输入图片说明](https://foruda.gitee.com/images/1716447357161482917/2c9b1639_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714212963457174382/add768db_5363069.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/_sidebar.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/_sidebar.md deleted file mode 100644 index 9fd7aad1..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/_sidebar.md +++ /dev/null @@ -1,64 +0,0 @@ - -- **特别赞助** -- [![输入图片说明](https://foruda.gitee.com/images/1704162419429172656/d0521e59_1766278.png "2024-01-02=>2028-01-02")](http://ccflow.org/?frm=ryPlus) -- [![输入图片说明](https://foruda.gitee.com/images/1705569347386939952/3f187980_1766278.jpeg "2024-01-18=>2025-01-18")](http://www.shuduokeji.com) -- [![输入图片说明](https://foruda.gitee.com/images/1711681233267310022/2ffbcff2_1766278.png "2024-03-29=>2025-03-29")](https://www.jnpfsoft.com/index.html?from=plus-doc) - - -* **简介** - * [项目简介](/ruoyi-vue-plus/home.md) - * [更新日志](/ruoyi-vue-plus/changlog.md) -* **快速开始** - * [项目初始化](/ruoyi-vue-plus/quickstart/init.md) - * [5.X新功能介绍](/ruoyi-vue-plus/quickstart/5.Xnew.md) - * [4.X项目初始化](/ruoyi-vue-plus/quickstart/4.Xinit.md) - * [工作流初始化](/ruoyi-vue-plus/quickstart/worker_init.md) - * [搭建Admin监控](/ruoyi-vue-plus/quickstart/admin_init.md) - * [搭建SnailJob调度中心](/ruoyi-vue-plus/quickstart/snail_job_init.md) - * [(废弃)搭建PowerJob调度中心](/ruoyi-vue-plus/quickstart/power_job_init.md) - * [(废弃)搭建XXLJob调度中心](/ruoyi-vue-plus/quickstart/xxl_job_init.md) - * [idea环境配置](/ruoyi-vue-plus/quickstart/idea_environment.md) - * [应用部署](/ruoyi-vue-plus/quickstart/deploy.md) - * [扩展项目](/ruoyi-vue-plus/quickstart/extend_project.md) -* **框架功能** - * [项目结构](/ruoyi-vue-plus/framework/tree.md) - * [软件架构图](/ruoyi-vue-plus/framework/architecture_diagram.md) - * 框架相关 - * [创建新模块](/ruoyi-vue-plus/framework/association/new_module.md) - * [修改包名](/ruoyi-vue-plus/framework/association/update_package_name.md) - * [接口文档](/ruoyi-vue-plus/framework/association/doc.md) - * [修改应用路径](/ruoyi-vue-plus/framework/association/update_url.md) - * [国际化](/ruoyi-vue-plus/framework/association/i18n.md) - * 基础功能 - * [系统用户相关](/ruoyi-vue-plus/framework/basic/user.md) - * [权限控制](/ruoyi-vue-plus/framework/basic/permissions_control.md) - * [导出功能](/ruoyi-vue-plus/framework/basic/export.md) - * [导入功能](/ruoyi-vue-plus/framework/basic/import.md) - * [参数校验](/ruoyi-vue-plus/framework/basic/param_check.md) - * [代码生成](/ruoyi-vue-plus/framework/basic/code_generate.md) - * [分页功能](/ruoyi-vue-plus/framework/basic/page.md) - * [OSS功能](/ruoyi-vue-plus/framework/basic/oss.md) - * [数据权限](/ruoyi-vue-plus/framework/basic/permissions.md) - * [接口放行](/ruoyi-vue-plus/framework/basic/interface_release.md) - * [多租户功能](/ruoyi-vue-plus/framework/basic/tenant.md) - * [第三方授权功能](/ruoyi-vue-plus/framework/basic/social.md) - * [客户端管理功能](/ruoyi-vue-plus/framework/basic/client.md) - * 扩展功能 - * [多数据源](/ruoyi-vue-plus/framework/extend/dynamic_datasource.md) - * [短信模块](/ruoyi-vue-plus/framework/extend/sms.md) - * [邮件功能](/ruoyi-vue-plus/framework/extend/mail.md) - * [防重幂等](/ruoyi-vue-plus/framework/extend/idempotent.md) - * [数据脱敏](/ruoyi-vue-plus/framework/extend/sensitive.md) - * [API加解密](/ruoyi-vue-plus/framework/extend/api_encrypt.md) - * [数据加解密](/ruoyi-vue-plus/framework/extend/encrypt.md) - * [翻译功能](/ruoyi-vue-plus/framework/extend/translation.md) - * [WebSocket功能](/ruoyi-vue-plus/framework/extend/websocket.md) - * [SSE功能](/ruoyi-vue-plus/framework/extend/sse.md) - * [Skywalking链路监控](/ruoyi-vue-plus/framework/extend/skywalking.md) - * [对接MaxKey单点登录](/ruoyi-vue-plus/framework/extend/maxkey.md) - * [对接TOPIAM单点登录](/ruoyi-vue-plus/framework/extend/topiam.md) - * 功能说明 - * [事务相关](/ruoyi-vue-plus/framework/explain/transaction.md) - * [单元测试](/ruoyi-vue-plus/framework/explain/test.md) - * [主键使用说明](/ruoyi-vue-plus/framework/explain/key.md) - * [关于多表查询](/ruoyi-vue-plus/framework/explain/about_join.md) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/changlog.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/changlog.md deleted file mode 100644 index ac8eb907..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/changlog.md +++ /dev/null @@ -1,2028 +0,0 @@ -# 更新日志 -- - - - -## v5.2.2 - 2024-08-26 - -### 重大改动 - -* 增加 ruoyi-common-sse 模块 支持SSE推送 比ws更轻量更稳定的推送 -* 增加 springboot snailjob 等 actuator 账号密码认证 杜绝内外网信息泄漏问题 -* 增加 重构代码生成器 集成anyline开源框架 支持400+种数据库适配 - -### 依赖升级 - -* update springboot 3.2.6 => 3.2.9 -* update snailjob 1.0.1 => 1.1.2 -* update mapstruct-plus 1.4.3 => 1.4.4 -* update hutool 5.8.27 => 5.8.31 解决hutool不兼容jakarta问题 -* update anyline 8.7.2-20240808 -* update sms4j 3.2.1 => 3.3.2 -* update redisson 3.31.0 => 3.34.1 -* update mapstruct-plus 1.3.6 => 1.4.3 -* update lombok 1.18.32 => 1.18.34 -* update easyexcel 3.3.4 => 4.0.2 -* update springdoc 2.5.0 => 2.6.0 -* update flowable 7.0.0 => 7.0.1 - -### 功能更新 - -* update 优化 去除日志部署环境判断 通过日志级别控制 -* update 优化 忽略租户与忽略数据权限支持嵌套使用(感谢 amadeus5201) -* update 优化 租户相关controller 增加租户开关配置控制是否注册 -* update 优化 移除 alibaba ttl 与线程池搭配有问题(可传递但无法清除与更新) -* update 优化 个人中心编辑 忽略数据权限 -* update 优化 兼容部分用户不想给用户分配角色与部门的场景 -* update 优化 租户套餐重名校验 -* update 优化 部门下存在岗位不允许删除 -* update 优化 角色编辑状态未校验问题 -* update 优化 用户脱敏增加编辑权限标识符 -* update 优化 代码生成器 自动适配oss翻译 -* update 优化 临时升级 undertow 版本 解决虚拟线程溢出问题 -* update 优化 支持通过配置文件关闭工作流 -* update 优化 增加mybatis-plus填充器兜底策略 -* update 优化 TenantSpringCacheManager 处理逻辑 -* update 优化 角色权限判断 -* update 优化 增加删除标志位常量优化查询代码 -* update 优化 监控使用独立web依赖 -* update 优化 更多脱敏策略(感谢 hemengji) -* update 优化 设置nginx sse相关代理参数 -* update 优化 调整默认推送使用SSE -* update 优化 Monitor监控服务通知分类打印(感谢 AprilWind) -* update 优化 限流注解 又写key又不是表达式的情况 -* update 优化 WorkflowUtils查询用户信息发送消息未查询邮件和手机号(感谢 yanzy) -* update 优化 注释掉其他数据库 jdbc 依赖 由用户手动添加 -* update 优化 oracle snailjob 兼容低版本oracle索引名称长度限制 -* update 优化 数据权限支持通过菜单标识符获取数据所有权 -* update 优化 数据权限支持自定义连接符 -* update 优化 TestDemo 删除前校验数据权限 -* update 优化 更换docker镜像底层系统 避免无字体情况 - -### 问题修复 - -* fix 修复 三方登录构建去除无用代码 -* fix 修复 多线程对同一个session发送ws消息报错问题 -* fix 修复 依赖漏洞 限制部分依赖版本 -* fix 修复 excel 基于其他字段 合并错误问题 -* fix 修复 一级缓存key未区分租户问题 -* fix 修复 id字符串格式转换错误问题 -* fix 修复 登出无法正确删除对应的租户数据问题 -* fix 修复 登录错误锁定不区分租户问题 -* fix 修复 转换模型缺少分类字段 -* fix 修复 权限标识符处理未设置成功状态问题 -* fix 修复 无法导入 bpmn 类型文件问题 - -### 前端改动 - -* update element-plus 2.7.5 => 2.7.8 -* update vue 3.4.25 => 3.4.34 -* update vite 5.2.10 => 5.2.12 -* add 增加 使用 vueuse 编写 sse 推送功能 -* update 优化 使用匹配模式简化预编译配置 -* update 优化 时间搜索组件统一 -* update 优化 oss 配置按钮 使用ossConfig权限标识符与oss权限分离 -* update 优化 类型报错问题 -* update 优化 切换租户后刷新首页 -* update 优化 实现表格行选中切换 -* update 优化 使用 vueuse 重构 websocket 实现 -* update 优化 代码生成器编辑页禁用缓存 防止同步后页面不更新问题 -* update 优化 调整默认推送使用SSE -* fix 修复 租户套餐导出路径错误问题 -* fix 修复 登出后重新登录 sse推送报错问题 - - -## v5.2.1 - 2024-07-09 - -### 功能更新 - -* update 优化 更改prod环境 snailjob状态 默认启用 -* update 优化 替换过期方法 -* update 优化 租户列表接口 避免登录之后列表被域名过滤 -* update 优化 获取用户账户方法 LoginHelper#getUsername(感谢 AprilWind) -* update 优化 用户ID查询角色列表代码实现(感谢 AprilWind) -* update 优化 大数据量下join卡顿问题 使用子查询提高性能 -* update 优化 修改路由name命名规则 防止重复路由覆盖问题(感谢 玲娜贝er) -* update 优化 修改 snailjob 默认端口 避免与系统内置端口冲突问题 -* update 优化 isTenantAdmin 空校验 -* update 优化 webscoket 配置与异常拦截 -* update 优化 更新 redis 密码策略(密码必填 升级需注意) -* update 优化 更新使用 Spring 官方推荐 JDK -* update 优化 StreamUtils 抽取 findFirst findAny 方法 -* update 优化 工作流相关代码方法 - -### 问题修复 - -* fix 修复 postgres flowable sql 缺失字段问题 -* fix 修复 新版上传未设置acl问题 -* fix 修复 get路径特殊规则 导致 actuator 泄漏问题 [issue#4f9ceb0a](https://gitee.com/dromara/RuoYi-Vue-Plus/commit/4f9ceb0a8057284a0d9d69da58df630d8bc2e84f) -* fix 修复 pg数据库 用户查询报错问题 -* fix 修复 isLogin 方法抛异常无法正常返回值问题 - -### 前端改动 - -* update 优化 工作流选人改为懒加载窗口 -* update 优化 路由name重复检查 -* update 优化 eslint 语法 -* update 优化 动态创建组件实例时, 设置路由name为组件名 解决缓存问题 -* fix 修复 由于没有await 导致执行顺序不可控 -* fix 修复 富文本编辑器 添加之后内容未清理问题 - -## v5.2.0 - 2024-06-20 - -### 重大改动 - -* 集成 flowable 增加工作流相关功能(感谢 May) -* 集成 snailjob 移除 powerjob(投诉的人太多使用成本太高)(感谢 dhb52) -* 升级 aws s3 升级到 2.X 性能大幅提升 -* 优化 数据权限 数据加密 使用预扫描mapper注解提升代码性能(感谢 老马) -* 新增 caffeine 减少将近90%的redis查询提高性能 - -### 依赖升级 - -* update springboot 3.1.7 => 3.2.6 支持虚拟线程 -* update springboot-admin 3.1.8 => 3.2.3 -* update mybatis-plus 3.5.4 => 3.5.7 适配更改代码 -* update springdoc 2.2.0 => 2.5.0 -* update easyexcel 3.3.3 => 3.3.4 -* update redisson 3.24.3 => 3.31.0 -* update lombok 1.18.30 => 1.18.32 -* update sms4j 2.2.0 => 3.2.1 支持自定义配置key 可用于多厂商多租户等 -* update satoken 1.37.0 -> 1.38.0 -* update hutool 5.8.22 => 5.8.26 -* update mapstruct-plus 1.3.5 => 1.3.6 -* update lock4j 2.2.5 => 2.2.7 -* update dynamic-ds 4.2.0 => 4.3.1 - -### 功能更新 - -* update 优化 三方登录不同域名问题 采用新方案 -* update 优化 获取aop代理的方式 减少与其他使用aop的功能冲突的概率 -* update 优化 token无效时关闭ws连接(感谢 AprilWind) -* update 优化 移除表单构建菜单(没有可用组件 用处不大以后再考虑) -* update 优化 切换动态租户 默认线程内切换(如需全局 手动传参) -* update 优化 代码生成注释,删除无用引入(感谢 AprilWind) -* update 优化 代码生成 el-radio 标签过期属性 -* update 优化 异常处理器自动配置 -* update 优化 文件下载使用对流下载降低内存使用(感谢 PhoenixL) -* update 优化 去除gc日志参数(有需要自己加) -* update 优化 拆分异常处理器 -* update 优化 常规web异常状态码 -* update 优化 设置静态资源路径防止所有请求都可以访问静态资源 -* update 优化 redis 对Long值的存储类型不同问题 -* update 优化 去除加密请求类型限制 -* update 优化 mp多租户插件注入逻辑 -* update 优化 RedisUtils 支持忽略租户 -* update 优化 更新ip地址xdb文件 -* update 优化 验证码背景色改为浅灰色 -* update 优化 mybatis依赖设置为可选依赖 避免出现不应该注入的情况 -* update 优化 GET 方法响应体支持加密 -* update 优化 excel插件合并策略 去除被合并单元格的非首行内容(感谢 司猫子) -* update 优化 下拉选接口数据权限 -* update 优化 OssFactory 获取实例锁性能 -* update 优化 使用翻译注解简化用户查询 调整用户查询逻辑 -* update 优化 框架整体提高查询性能 -* update 优化 将p6spy配置文件统一放置到 common-mybatis 插件包内 - -### 新增功能 - -* add 新增 分布式锁Lock4j异常拦截器 -* add 新增 个人中心-在线设备管理 -* add 新增 岗位编码与部门编码并将岗位调整到部门下(感谢 AprilWind) -* add 新增 BaseMapperPlus提供可选是否抛异常selectVoOne方法(感谢 秋辞未寒) -* add 新增 用户、部门、角色、岗位 下拉选接口与代码实现优化 -* add 增加 StringUtils.isVirtual 方法 -* add 增加 JustAuth 整合 TopIam 单点登录 - -### 问题修复 - -* fix 修复 websocket clientid 参数不走mvc拦截器 无法生效问题 -* fix 修复 oss未使用租户 拼接租户id null问题 -* fix 修复 用户昵称修改后未清除对应缓存问题(感谢 zhuweitung) -* fix 修复 图片预览问题(感谢 AprilWind) -* fix 修复 三方账号可以绑定多平台账号问题 -* fix 修复 主建错别字(感谢 good) -* fix 修复 兼容redis5.0出现的问题 -* fix 修复 部分浏览器无法获取加密响应头问题 -* fix 修复 用户未设置部门 登录报错问题 -* fix 修复 excel 表达式字典 下拉框导出格式错误 -* fix 修复 提升锁的作用域 并采用双重校验锁(感谢 fanc) -* fix 修复 用户登录查询部门缓存无法获取租户id问题 -* fix 修复 关闭租户功能 三方登录报错问题 - - -### 前端改动 - -* update element-plus 2.7.5 -* update vite 5.2.10 -* update vue 3.4.25 -* update vue-router 4.3.2 -* update nodejs 升级到最低 18.18.0 -* update 优化 跟密码相关的默认前端关闭防重功能 -* update 优化 点击左边菜单时页面空白或者刷新整个页面的问题 -* update 优化 el-select 与 el-input 全局样式 -* update 优化 首页打开topNav不展开菜单问题 -* update 优化 支持全局开启或关闭接口加密功能 -* update 优化 密码校验策略增加非法字符限制 -* update 优化 图片上传组件增加压缩功能支持 可自行开关(感谢 fengheguai) -* update 优化 request请求类判断请求头方式 -* update 优化 更改客户端状态接口 使用clientId传参 -* update 优化 ws开关改为常开(vite5修复了崩溃bug) -* fix 修复 移动端下 无法展开菜单问题 -* fix 修复 面板因为min width原因收缩不全 -* fix 修复 文件预览大写后缀不展示的问题(感谢 北桥) -* fix 修复 i18n无感刷新问题 -* fix 修复 websocket 非index页面刷新无法重连问题 - -## v5.1.2 - 2023-12-22 - -### 依赖升级 - -* update springboot 3.1.5 => 3.1.7 -* update mybatis-boot 3.0.2 => 3.0.3 优化依赖传递 -* update powerjob 4.3.3 => 4.3.6 -* update easyexcel 3.3.2 => 3.3.3 -* update transmittable-thread-local 2.14.2 => 2.14.4 -* update justauth 1.16.5 => 1.16.6 -* update redisson 3.24.1 => 3.24.3 修复订阅重启连接超时问题 - -### 功能更新 - -* update 优化 为 admin 模块 单独增加 ratelimiter 模块 -* update 优化 验证码接口 增加限流配置 -* update 优化 excel合并注解会根据第一合并列的结果来决定后续的列合并 (感谢 Simple) -* update 优化 SocialUtils 代码 -* update 优化 删除无用异常类 -* update 优化 补全三方登录校验国际化 -* update 优化 sms组件 预留自动配置类 -* update 更新 关于数据库的说明 -* update 优化 sms组件 预留自动配置类 -* update 优化 将 OSS配置 改为全局模式 降低使用难度 保留sql便于用户自行扩展(常规项目用不上配置分多租户) -* update 优化 细化oss配置管理权限控制 -* update 优化 开启 redisson 脚本缓存 减少网络传输 -* update 优化 删除 hikaricp 官方不推荐使用的配置 jdbc4 协议自带校验方法 -* update 优化 减少 PlusSaTokenDao 不必要的查询优化性能 -* update 优化 更新用户异常提示 使用登录账号 -* update 优化 使用登录用户判断是否登录 提高效率 -* update 优化 重构 LoginHelper 将本地存储代码操作封装 -* update 优化 getTenantId 判断是否开启多租户 -* update 优化 Dockerfile 使用shell模式 支持环境变量传入jvm参数 -* update 优化 WebSocketUtils 连接关闭改为警告 -* update 优化 excel多sheet页导出 (感谢 May) -* update 优化 删除无用接口实现 -* update 优化 jvm参数调整 全面启用zgc -* update 优化 使用动态租户重构业务对租户的逻辑 -* update 优化 TenantHelper 动态租户支持函数式方法 -* update 优化 支持多租户绑定相同的三方登录 -* update 优化 更新用户登录信息方法忽略数据权限 -* update 优化 补全三方绑定时间字段 删除无用excel注解 -* update 优化 将登录记录抽取到监听器统一处理 -* update 优化 租户插件 ignoreTable 方法支持动态租户 - -### 新增功能 - -* add 新增 RedisUtils.setObjectIfExists 如果存在则设置方法 -* add 新增 丰富RedisUtils对List Set类型的操作 -* add 新增 翻译组件 用户昵称翻译实现 -* add 新增 响应加密功能 支持注解强制加密接口数据 (感谢 MichelleChung) - -### 问题修复 - -* fix 修复 selectDictTypeByType 查询方法错误问题 -* fix 修复 一些不正常类无法加载报错问题 -* fix 修复 powerjob sql脚本针对其他数据库转义符问题 (感谢 branches) -* fix 修复 MybatisSystemException 空指针问题 -* fix 修复 excel合并注解会根据第一合并列的结果来决定后续的列合并 -* fix 修复 session 多账号共用覆盖问题 改为 tokenSession 独立存储 -* fix 修复 token 失效后 登录获取用户null问题 -* fix 修复 powerjob部署方案 高版本nginx不生效问题 -* fix 修复 OssFactory 并发多创建实例问题 -* fix 修复 延迟队列在投递消息未到达时间的时候 服务死机导致重启收不到消息 - -### 前端改动 - -* update 优化 用户头像 img 变量无确定类型问题 -* update 优化 细化oss配置管理权限控制 -* update 优化 明确打包命令 -* update 优化 代码中存在的警告 -* update 优化 前端白名单页面放行逻辑 -* update 优化 页面关于权限标识符说明 -* fix 修复 append-to-body 编写错误 (感谢 Ai3_刘小龙) -* fix 关闭动态路由tab页签时不清理组件缓存 (感谢 NickLuo) -* fix 删除重复环境变量ElUploadInstance (感谢 棉花) -* fix 修复 在线用户 强推按钮点击取消控制台警告问题 -* fix 修复 字典使用 default 样式报警告问题 - -## v4.8.2 - 2023-11-27 - -### 依赖升级 - -* update springboot 2.7.17 => 2.7.18 升级到2.X最终版本(官方停更) -* update mybatis-plus 3.5.3.2 => 3.5.4 -* update springboot 2.7.14 => 2.7.17 -* update satoken 1.36.0 => 1.37.0 -* update aws-java-sdk-s3 1.12.400 => 1.12.540 -* update vue-quill 1.1.0 => 1.2.0 - -### 功能更新 - -* update 优化 页面关于权限标识符说明 -* update 优化 数据权限拦截器优先判断方法是否有效 提高性能减少无用sql解析 -* update 优化 部门数据权限使用默认兜底方案 -* update 优化 更改默认日志等级为info 避免日志过多(按需开启debug) -* update 优化 补全代码生成 columnList 接口参数注解缺失 -* update 优化 操作日志 部门信息完善 vue3页面 -* update 优化 AddressUtils 兼容linux系统本地ip -* update 优化 操作日志 部门信息完善 (感谢 柏竹) -* update 优化 数据权限 减少二次校验查询 -* update 优化 vue3 版本用户初始密码从字典查询 -* update 优化 富文本Editor组件检验图片格式 -* update 优化 操作日志列表新增IP地址查询 -* update 优化 全局数据存储用户编号 -* update 优化 菜单管理类型为按钮状态可选 - -### 问题修复 - -* fix 修复 OssFactory 并发多创建实例问题 -* fix 修复 demo的form字段有误 (感谢 dhb52) -* fix 修复 延迟队列在投递消息未到达时间的时候 服务死机导致重启收不到消息 -* fix 修复 数据权限优化后 update delete 报null问题 -* fix 修复 五级路由缓存无效问题 -* fix 修复 oss服务无法连接 导致业务异常问题 查询不应该影响业务 -* fix 修复 内链iframe没有传递参数问题 -* fix 修复 外链带端口出现的异常 -* fix 修复 普通角色编辑使用内置管理员code越权问题 -* fix 修复 代码生成 是否必填与数据库不匹配问题 -* fix 修复 HeaderSearch组件跳转query参数丢失问题 -* fix 修复 树结构代码生成新增方法赋值错误 (感谢 这夏天依然平凡) - -## v5.1.1 - 2023-11-14 - -### 依赖升级 - -* update springboot 3.1.3 => 3.1.5 -* update springboot 2.7.14 => 2.7.17(扩展服务) -* update springboot-admin 3.1.5 => 3.1.7 -* update satoken 1.35.0.RC => 1.37.0 -* update mybatis-plus 3.5.3.2 => 3.5.4 适配mp新版本改动 -* update dynamic-ds 4.1.3 => 4.2.0 -* update bouncycastle 1.72 => 1.76 -* update poi 5.2.3 => 5.2.4 -* update redisson 3.23.2 => 3.24.1 -* update hutool 5.8.20 => 5.8.22 -* update lombok 1.18.26 => 1.18.30(适配支持jdk21) -* update vue-quill 1.1.0 => 1.2.0 - -### 功能更新 - -* update 优化 数据权限拦截器优先判断方法是否有效 提高性能减少无用sql解析 -* update 优化 适配 maxkey 新版本 -* update 优化 @Sensitive脱敏增加角色和权限校验 (感谢 盘古给你一斧) -* update 优化 部门数据权限使用默认兜底方案 -* update 优化 更改默认日志等级为info 避免日志过多(按需开启debug) -* update 优化 登录策略代码优化(感谢 David Wei) -* update 优化 补全代码生成 columnList 接口参数注解缺失 -* update 优化 nginx 配置支持 websocket -* update 优化 notice 新增通知公告发送ws推送 -* update 优化 websocket 模块减少日志输出 增加登录推送 -* update 优化 重构登录策略增加扩展性降低复杂度 -* update 优化 addressUtils 兼容linux系统本地ip -* update 优化 补全操作日志部门数据 -* update 优化 支持数据库操作在非web环境下切换租户 -* update 优化 排除powerjob无用的依赖 减少打包30M体积 -* update 优化 删除 satoken yml 时间配置 此功能已迁移至客户端管理 -* update 优化 redis 集群模式注释说明 -* update 优化 客户端禁用限制 -* update 优化 登录日志, 在线用户展示信息(增加 客户端, 设备类型)(感谢 MichelleChung) -* update 优化 限制框架中的fastjson版本 -* update 优化 数据权限 减少二次校验查询 -* update 优化 将部门id存入token避免过度查询redis -* update 优化 增加租户ID为Null错误日志 -* update 优化 操作日志列表新增IP地址查询 -* update 优化 通过参数键名获取键值接口的返回体(感谢 David Wei) -* update 优化 为 sys_grant_type 字典增加样式 -* update 优化 代码生成 页面输入框样式 -* update 优化 全业务分页查询增加排序规则避免因where条件导致乱序问题 -* update 优化 登录接口租户id被强制校验问题 -* update 优化 加密模块 支持父类统一使用加密注解(感谢 Tyler Ge) -* update 优化 将graalvm镜像更新为openjdk镜像 需要的人自行切换即可 -* update 优化 部分使用者乱设权限导致无法获取用户信息 增加权限提示 -* update 优化 表格列的显示与隐藏小组件(感谢 bestrevens) -* update 优化 增加表单构建不能使用说明 -* update 优化 富文本Editor组件检验图片格式 -* update 优化 操作日志列表新增IP地址查询 -* update 优化 菜单管理类型为按钮状态可选 -* update 优化 用户初始密码从参数配置查询 -* update 优化 通过参数键名获取键值接口的返回体(感谢 David Wei) -* update 优化 字典标签支持数组和多标签(感谢 抓蛙师) - -### 新增功能 - -* add 新增 websocket 群发功能 -* add 新增 前端接入websocket接收消息(感谢 三个三) - -### 问题修复 - -* fix 修复 oss服务无法连接 导致业务异常问题 查询不应该影响业务 -* fix 修复 租户id为null 无法匹配字符串导致的嵌套key问题 -* fix 修复 部门管理orderNum排序失效问题 -* fix 修复 外链带端口出现的异常 -* fix 修复 普通角色编辑使用内置管理员code越权问题 -* fix 修复 代码生成 是否必填与数据库不匹配问题 -* fix 修复 用户注册接口校验用户名不区分租户问题 -* fix 修复 错误增加组导致的校验不生效问题 -* fix 修复 新增校验主键id问题 -* fix 修复 powerjob 使用 nginx 部署无法访问的问题 -* fix 修复 SysUserMapper 内标签使用错误(不影响使用) -* fix 修复 新增或编辑 SysOssConfig 数据后 推送到 redis 数据不完整 -* fix 修复 树表生成查询变量使用错误 -* fix 修复 个人信息修改密码接口隐藏新旧密码参数明文(感谢 bleachtred) -* fix 修复 删除字段后 * update sql 未更新问题 -* fix 修复 三方登录支付宝source与实际支付宝业务code不匹配问题 -* fix 修复 五级路由缓存无效问题 -* fix 修复 内链iframe没有传递参数问题 -* fix 修复 绑定第三方帐号参数“wechar”更正为“wechat” (感谢 scmiot) -* fix 修复 用户注册缺失 clientid 问题 -* fix 修复 HeaderSearch组件跳转query参数丢失问题 -* fix 修复 自定义字典样式不生效的问题 -* fix 修复 login 页面 loading 未关闭问题 - -## v4.8.1 - 2023-09-25 - -### 依赖升级 - -* update springboot 2.7.15 => 2.7.16 -* update springboot-admin 2.7.10 => 2.7.11 -* update satoken 1.35.0.RC => 1.36.0 -* update lombok 1.18.26 =. 1.18.30 -* update mybatis-plus 3.5.3.1 => 3.5.3.2 -* update easyexcel 3.3.1 => 3.3.2 -* update hutool 5.8.18 => 5.8.20 - -### 功能更新 - -* update 优化 重置密码注释参数中文解释错误 -* update 优化 getTokenActivityTimeout => getTokenActiveTimeout -* update 优化字典标签支持传分隔符分隔的字符串和数组,优化渲染效果 -* update 优化 控制台debuger位置错误问题 -* update 优化 TopNav 菜单样式 -* update 优化 全局异常处理器 业务异常不输出具体堆栈信息 减少无用日志存储 -* update 优化 用户管理 只查询未禁用的部门角色岗位数据 -* update 优化 岗位如果绑定了用户则不允许禁用 -* update 优化 部门与角色如果绑定了用户则不允许禁用 -* update 优化 加密实现 使用 EncryptUtils 统一处理 -* update 优化 excel导出字典转下拉框 无需标记index自动处理 -* update 优化 excel 导出字典默认转为下拉框 -* update 优化 删除一些跟 swagger 有关的字眼 避免误解 -* update 优化 角色权限支持仅本人权限查看 解决无法查看自己创建的角色问题 -* update 优化 RedisCacheController 注释错误 -* update 优化 xxljob 端口随着主应用端口飘逸 避免集群冲突 -* update 优化 powerjob 端口随着主应用端口飘逸 避免集群冲突 - -### 问题修复 - -* fix 修复 代码生成后 vo 定义 'serialVersionUID' 字段的不可序列化类 -* fix 修复 自定义字典样式不生效的问题 -* fix 修复 布局配置失效问题 -* fix 修复 新建用户可能会存在的越权行为 -* fix 修复 字典缓存删除方法参数错误问题 -* fix 修复 修复树模板父级编码变量错误 -* fix 修复 有界队列与优先队列 错误使用问题 -* fix 修复 升级 mp 版本导致的问题 -* fix 修复 vue3 版本注册页验证码不显示问题 -* fix 修复 加密模块数据转换异常问题 -* fix 修复 动态设置 token 有效期不生效问题 -* fix 修复 token 过期登出无法清理在线用户问题 - - -## v5.1.0 - 2023-09-05 - -# 开发历程 - -* 2023年5月 开始 5.1.0 计划 历经1个月的设计与讨论 -* 2023年6月 开始着手开发 历经2个多月的开发 特别感谢团队的小伙伴与一些热心的粉丝 参与功能开发与测试 -* 2023年8月 开始公测 历经将近1个月的公测与修复工作(期间成功支持多位使用者生产使用) -* 2023年9月初 正式发布(经过多个小伙伴的生产实践 已基本可尝试生产使用) -> 关于4.X的说明 由于SpringBoot2.X与vue2.X均在11月底停止维护
-> 故而咱们vue版本4.X也无法再继续更新
-> 介于4.X的用户量特别庞大 功能也非常的稳定
-> 计划于11月底同Boot2.X一同停止更新但还会持续维护修复bug(修复的形式为直接提交到4.X分支停止发版)
- -# 视频介绍 - -为了更好的让大家了解 5.1.0 作者录制了相关的视频 供大家快速了解上手 - -* 5.1.0 新功能与变更介绍: https://www.bilibili.com/video/BV1fj411y71X/ -* 搭建与运行: https://www.bilibili.com/video/BV1Fg4y137JK/ -* 生产环境搭建部署: https://www.bilibili.com/video/BV1mL411e7ha/ - -# 更新日志 - -### 重大更新 - -* [重大更新] 优化 相关代码 完成代码生成多数据源统一存储(感谢 WangBQ !pr349) -* [不兼容更新] 移除 原短信功能 集成更强大的 sms4j 短信工具包(感谢 友杰 !pr367) -* [不兼容更新] 对接 powerjob 实现分布式任务调度 删除原有 xxljob 原因为社区不更新功能太少只支持mysql(感谢 yhan219 !pr359) -* [重大更新] 新增 三方授权绑定登录功能 基于 justauth 支持市面上大部分三方登录(感谢 三个三 !pr370) -* [不兼容更新] 新增 客户端授权功能 不需要更改任何代码即可完成多端动态对接(感谢 Michelle.Chung !pr379) -* [重大更新] 新增 前后端接口请求加密传输 基于AES+RSA动态高强度加密(感谢 wdhcr !pr377) -* [重大更新] 新增 三方授权登录 对接 maxkey 单点登录 -* [不兼容更新] 优化 redis序列化配置 更改为通用格式(升级需清除redis所有数据) - -### 依赖升级 - -* update springboot 3.0.7 => 3.1.3 -* update springboot-admin 3.1.3 => 3.1.5 -* update springdoc 2.1.0 => 2.2.0 -* update spring-mybatis 3.0.1 => 3.0.2 -* update mybatis-plus 3.5.3.1 => 3.5.3.2 -* update easyexcel 3.2.1 => 3.3.2 -* update mapstruct-plus 1.2.3 => 1.3.5 解决修改实体类 idea不编译问题 -* update satoken 1.34.0 => 1.35.0.RC 优化过期配置 支持多端token自定义有效期 -* update dynamic-ds 3.6.1 => 4.1.3 支持 SpringBoot3 -* update sms4j 2.2.0 -* update hutool 5.8.18 => 5.8.20 -* update redisson 3.20.1 => 3.23.4 -* update lock4j 2.2.4 => 2.2.5 -* update aws-java-sdk-s3 1.12.400 => 1.12.540 -* update maven-surefire-plugin 3.0.0 => 3.1.2 - -### 功能更新 - -* update 优化 excel 导出合并 在初始化类时进行数据的处理 -* update 优化 简化 flatten 插件语法写法 -* update 优化 支持本地虚拟域名调试(感谢 代星登 !pr363) -* update 重构 将框架内的 swagger 命名更改为 springdoc 命名避免误解 -* update 重构 将系统内置配置放置到 common 包内独立加载 不允许用户随意修改 -* update 优化 切换 maven 仓库到 华为云(aliyun依赖不更新拉取不到) -* update 优化 升级 satoken 支持多端 token 自定义有效期功能 -* update 优化 RepeatSubmitAspect 逻辑避免并发请求问题 -* update 优化 在全局异常拦截器中增加两类异常处理 -* update 优化 提供powerjob完整sql脚本 降低用户使用难度 -* update 优化 StreamUtils 其他方法过滤null值(感谢 bleachtred !pr390) -* update 优化 powerjob 端口随着主应用端口飘逸 避免集群冲突 -* update 优化 角色权限支持仅本人权限查看 解决无法查看自己创建的角色问题 -* update 修改代码生成模版,日期范围统一采用addDateRange方法(感谢 LiuHao !pr397) -* update 优化 树表生成前端缺少 children 字段 -* update 优化 CryptoFilter null判断工具 -* update 优化 websocket 路径与 cloud 版本保持一致 -* update 优化 更新登录策略返回值(感谢 zlyx) -* update 修改代码生成模板,调整列表打开对话框和接口请求顺序 -* update 优化 SaInterceptor 拦截器判断 token 客户端id是否有效(感谢 zlyx !pr402) -* update 优化 excel 导出字典默认转为下拉框 -* update 优化 兼容 clientid 通过 param 传输 -* update 优化 excel导出字典转下拉框 无需标记index自动处理(感谢 一夏coco) -* update 优化 简化线程池配置 -* update 优化 屏蔽 powerjob 无用的心跳日志 -* update 优化 适配 mysql 8.0.34 升级连接机制 -* update 优化 加密实现 使用 EncryptUtils 统一处理 -* update 优化 删除字典无用状态字段(基本用不上 禁用后还会导致回显问题) -* update 优化 部门与角色如果绑定了用户则不允许禁用 -* update 优化 岗位如果绑定了用户则不允许禁用 -* update 优化 用户管理 只查询未禁用的部门角色岗位数据 -* update 优化 登录用户增加昵称返回 -* update 优化 将部门管理 负责人选项改为下拉框选择(感谢 Lionel !pr410) -* update 优化 全局异常处理器 业务异常不输出具体堆栈信息 减少无用日志存储 -* update 优化 登录用户缓存 去除冗余统一存储 -* update 优化 放宽菜单权限 角色关联菜单无需管理员 - -### 新增功能 - -* add 增加 RedisUtils 批量删除 hash key 方法 -* add 新增 Oss 上传 File 文件方法(感谢 jenn !pr362) -* add 增加 excel 导出下拉框功能 -* add 新增 RedisUtils.setObjectIfAbsent 如果不存在则设置方法 - -### 修复问题 - -* fix 修复 脱敏注解标记位置错误 -* fix 修复 OssClient 实例多租户相同key缓存覆盖问题 -* fix 修复 关闭多租户 脱敏判断问题 -* fix 修复 OssClient 切换服务 实例不正确问题(感谢 jenn !pr360) -* fix 修复 传参类型不正确导致 postgreSql 同步套餐报错问题 -* fix 修复 参数类型修改 未修改校验注解 -* fix 修复 登录校验错误次数未达到上限时 错误次数缓存未设置有效时间问题(感谢 konbai !pr366) -* fix 修复 common-core 包使用aop注解 但未添加aop实现类导致单独使用报错问题 -* fix 修复 Mapper 多参数未加 @Param 注解问题 -* fix 修复 邮箱登录 查询值错误问题 -* fix 修复 用户篡改管理员角色标识符越权问题 -* fix 修复 字典缓存注解使用错误问题 -* fix 修复 查询部门下拉树未过滤数据权限问题 -* fix 修复 CacheName 缓存key存储错误问题 -* fix 修复 代码生成 前端添加或修改表单修改列生成问题 -* fix 修复 新增角色使用内置管理员标识符问题 -* fix 修复 代码生成 前端添加或修改表单修改列生成问题 -* fix 修复 token 过期登出无法清理在线用户问题 -* fix 修复 加密模块数据转换异常问题 -* fix 修复 可能导致异常类无法反序列化问题 -* fix 修复 代码生成 编辑按钮刷新列表问题 -* fix 修复 客户端编辑时授权类型变更未保存的问题(感谢 David Wei !pr400) -* fix 修复 有界队列与优先队列 错误使用问题 -* fix 修复 修复树模板父级编码变量错误 -* fix 修复 部署部分系统出现乱码问题 -* fix 修复 一级菜单无法显示问题 -* fix 修复 可能会存在的越权行为(感谢 丶Stone !pr416) -* fix 修复 代码生成页面参数缺少逗号问题 - -### 移除功能 - -* remove 移除原有短信功能(建议使用sms4j) -* remove 移除xxljob功能(建议使用powerjob) - - -## v4.8.0 - 2023-07-10 - -### 重大更新 - -* [重大更新] 新增 sms4j 短信融合框架整合(支持数十种短信厂商接入、发送限制、负载均衡等功能) -* [不兼容更新] 移除 原短信功能(建议使用新 sms4j 功能) -* [重要迁移] 迁移 vue3 前端到主仓库统一维护 - -### 依赖升级 - -* update springboot 2.7.11 => 2.7.13 -* update satoken 1.34.0 => 1.35.0.RC -* update easyexcel 3.2.1 => 3.3.1 -* update sms4j 2.2.0 - -### 功能更新 - -* update 优化 StreamUtils 方法过滤null值 -* update 优化 页签在Firefox浏览器被遮挡 -* update 优化 在全局异常拦截器中增加两类异常处理 -* update 优化 下载zip方法增加遮罩层(感谢@梁剑锋) -* update 优化 用户昵称非空校验 -* update 优化 修改角色如果未绑定用户则无需清理 -* update 优化 RepeatSubmitAspect 逻辑避免并发请求问题 -* update 优化 satoken 过期配置 支持多端token自定义有效期 -* update 优化 加密注解注释错误 -* update 优化 切换 maven 仓库到华为云(aliyun 不可用) -* update 优化 excel 导出存在合并项时在初始化类时进行数据的处理避免多次调用(感谢@yueye) -* update 优化 重构 CellMergeStrategy 支持多级表头修复一些小问题 整理代码结构 - -### 新增功能 - -* add 新增 RedisUtils.setObjectIfAbsent 不存在则设置方法 -* add 新增 Excel 导出附带有下拉框(字典自动导出为下拉框) 可自定义多级下拉框(感谢@Emil.Zhang) -* add 新增 OssClient File 文件上传方法 -* add 增加 RedisUtils 批量删除 hash key 方法 - -### 问题修复 - -* fix 修复 新增角色使用内置管理员标识符问题 -* fix 修复 缓存监控图表 支持跟随屏幕大小自适应调整(感谢@抓蛙师) -* fix 修复 防重组件 错删注解问题 -* fix 修复 CacheName 缓存key存储错误问题 -* fix 修复 字典缓存注解使用错误问题 -* fix 修复 用户篡改管理员角色标识符越权问题 -* fix 修复 登录校验错误次数未达到上限时 错误次数缓存未设置有效时间问题 -* fix 修复 OssClient 切换服务 实例不正确问题 -* fix 修复 element ui 因版本而未被工具识别问题(感谢@梁剑锋) -* fix 修复 admin监控 切换tab页需要重复登录问题 - -## v5.0.0 - 2023-05-19 - -# 开发历程 - -* 2022年11月 开始5.X计划 历经2个月的设计与讨论 -* 2023年1月 开始着手开发 历经3个月的开发 特别感谢团队的小伙伴与一些热心的粉丝 参与功能开发与测试 -* 2023年4月 开始公测 历经将近2个月的公测与修复工作(期间成功支持多位使用者生产使用) -* 2023年5月底 正式发布 虽然已经有生产实践 但是springboot3.0与jdk17使用者还处于少数 另外5.X后续还有一些不兼容更新 求稳者建议在等一等 -* 关于4.X的说明 由于springboot2.X 与 vue2.X 匀在年底停止维护 故此4.X也将于年底同boot2一同停止维护 - -# 视频介绍 - -为了更好的让大家了解 5.X 作者录制了相关的视频 供大家快速了解上手 - -* 搭建与运行: https://www.bilibili.com/video/BV1Fg4y137JK/ -* 新功能与变更介绍: https://www.bilibili.com/video/BV1Us4y1m7ky/ -* 生产环境搭建部署: https://www.bilibili.com/video/BV1mL411e7ha/ - -# 更新日志 - -### 重大更新 - -* [不兼容升级] java 版本从 jdk 8 升级到 jdk 17 且需要使用 graalvm 运行(暂时未解决原生jdk存在的问题) -* [不兼容升级] springboot 升级 3.0 版本 -* [不兼容升级] 重构 项目模块结构 采用插件化结构 易扩展易解耦 -* [不兼容升级] com.sun.mail 更改为 jakarta.mail 修改最新写法 -* [不兼容升级] javax.servlet 替换为 jakarta.servlet 更新所有代码 -* [简化性升级] 默认开启复杂结构 resultMap 自动映射 简化xml编码(多结构实体需带上主键id) -* [数据库改动] 更新 create_by update_by 字段类型 (保存用户id) -* [数据库改动] 新增 create_dept 字段 (保存创建部门id) -* [不兼容更新] system 模块 所有实体类均使用 bo|vo 规范化 -* [重大更新] 新增 多租户功能设计 整体框架代码结构与数据库更改 -* [重大更新] 新增 mapstruct-plus 替换 BeanUtil 与 BeanCopyUtils 工具 -* [不兼容更新] 重构 登录注解接口与cloud版本统一接口路径 -* [不兼容更新] 重构 BaseMapperPlus接口 去除 `@param Mapper` 泛型 -* [不兼容更新] 移除 vue2 前端工程 全面启用 vue3 -* [重大更新] 新增 vue3 + TS 版本前端(独立仓库后续与Cloud版本共用) -* [重大更新] 增加 websocket 模块 支持token鉴权 支持分布式集群消息同步 -* [重大更新] 框架文档全面翻新 https://plus-doc.dromara.org - -### 依赖升级 - -* update java 1.8 => 17 -* update springboot 2.7.7 => 3.0.7 -* update springboot-admin 2.7.10 => 3.0.4 -* update springdoc 1.6.14 => 2.1.0 -* update lock4j 2.2.3 => 2.2.4 -* update dynamic-ds 3.5.2 => 3.6.1 -* update easyexcel 3.1.5 => 3.2.1 -* update hutool 5.8.11 => 5.8.18 -* update redisson 3.19.2 => 3.20.1 -* update lombok 1.18.24 => 1.18.26 -* update spring-boot.mybatis 2.2.2 => 3.0.1 -* update mapstruct-plus 1.2.3 -* update maven-compiler-plugin 3.10.1 => 3.11.0 -* update maven-surefire-plugin 3.0.0-M7 => 3.0.0 -* update docker mysql 8.0.31 => 8.0.33 -* update docker nginx 1.22.1 => 1.32.4 -* update docker redis 6.2.7 => 6.2.12 -* update docker minio RELEASE.2023-04-13T03-08-07Z - -### 功能更新 - -* update 适配 AsyncConfig 替换过期继承类改为实现 AsyncConfigurer 接口 -* update 适配 redis 新版本配置文件写法 -* update 适配 获取redis 监控参数接口 替换过期语法 -* update 适配 sa-token 替换新依赖 sa-token-spring-boot3-starter -* update 适配 springboot-admin 改为最新 spring-security 写法 -* update 适配 springdoc 新版本配置方式 -* update 适配 ServletUtils 更换继承 JakartaServletUtil -* update 适配 新序列化注解 -* update 优化 利用 resultMap 自动映射配置 简化 xml (非嵌套) -* update 优化 调整 system entity 实体与 controller 包结构 -* update 优化 实体类中校验注解的提示信息 -* update 优化 使用 jdk17 语法优化代码 -* update 优化 所有 properties 文件改为注解启用 -* update 更新 docker 基础镜像 graalvm java17 -* update 优化 用户头像 改为存储 ossId 使用转换模块转为 url 展示 -* update 优化 重构 CellMergeStrategy 支持多级表头修复一些小问题 整理代码结构 -* update 优化 登录流程代码注释 - -### 新增功能 - -* add 新增 flatten-maven-plugin 插件统一版本号管理 -* add 新增 ip2region 实现离线IP地址定位库 - -### 移除功能 - -* remove 移除 BeanCopyUtils 工具类 与 JDK17 不兼容 -* remove 移除 devtools 依赖 并不好用(建议直接用idea自带的热更) -* remove 移除 vue2 前端工程 统一使用 vue3 工程 - -## v4.7.0 - 2023-05-08 - -### 依赖升级 - -* update springboot 2.7.9 => 2.7.11 修复 DoS 漏洞 -* update xxljob 2.3.1 => 2.4.0 -* update minio 升级至最新版 避免低版本信息泄漏问题 -* update hutool 5.8.15 => 5.8.18 -* update redisson 3.20.0 => 3.20.1 - -### 功能更新 - -* update 优化 更改 sys_oss_config 表注释 避免误解 -* update 项目正式入驻 dromara 开源社区 更改项目地址 -* update 全新 logo 全新背景图(设计师打造) -* update 优化代码生成 同步操作使用批量处理 -* update 重写项目 readme 说明 -* update 修改controller中校验直接返回R.fail -* update 更换默认用户头像 -* update 优化 限流注解 key 支持简单 spel 表达式 -* update 优化弹窗后导航栏偏移的问题 -* update 优化$tab.closePage后存在非首页页签时不应该跳转首页 -* update delete build style -* update 优化选择图标组件 -* update 移除vue-multiselect样式 -* update 优化固定头部页签滚动条被隐藏的问题 -* update 按代码规范补全重写注解 -* update 优化 极端情况获取LoginUser可能为null问题 -* update 优化 更改系统所有服务日志配置文件命名为 logback-plus.xml 避免与其他框架默认配置冲突 -* update 优化 加解密模块 将null判断下推防止任何可能的null出现 -* update 优化 调整配置文件错误注释 -* update 优化 在线用户token获取方式 -* update 优化 用户更改角色 踢掉角色相关所有在线用户 -* update 优化 下拉图标选择组件优化:1.已选择图标高亮回显 2.滚动条采用el-scrollbar -* update 优化 Vue的DictTag组件 当value没有匹配的值时 展示空value -* update 优化 恢复翻页/切换路由滚动功能 - -### 新增功能 - -* add 新增 ip2region 实现离线IP地址定位库 -* add 增加 邮箱验证码发送接口 -* add 增加 邮箱登陆接口 -* add 增加 EncryptUtils 加解密安全工具类 可以处理base64,aes,sm4,sm2,rsa,md5,sha256加解密 -* add 增加 EncryptUtils 类中增加国密sm3的不可逆加密算法 -* add 新增 忽略数据权限写法 防止异常不执行关闭问题 - -### 问题修复 - -* fix 修复 代码生成 点选按钮不生效问题 -* fix 修复 用户密码更新无效问题 -* fix 修复 findInSet 在mysql下方法搜索非数字字段时 无引号报错问题 -* fix 修复 oracle postgres 数据库日志表索引创建错误 -* fix 角色列表关联多表sort值都一样 导致排序不稳定、临时表没有原来的主键顺序 -* fix 修复 DefaultExcelResult 单词拼写错误 -* fix 修复页面切换时布局错乱的问题 -* fix 修复tab栏“关闭其他”异常的问题 -* fix 修复 加解密拦截器 对象属性为null问题 -* fix 修复 取消oss预览状态修改 图标变化不正常问题 -* fix 修复 开启TopNav后一级菜单路由参数设置无效问题 -* fix 修复 路由跳转被阻止时vue-router内部产生报错信息问题 -* fix 修复 缓存列表:多次清除操作,提示不变的问题 - -## v4.6.0 - 2023-03-13 - -### 重大更新 - -[重大更新] add 新增 基于 Mybatis 实现数据库字段加解密功能 -[重大更新] add 新增 通用翻译注解及实现(部门名、字典、oss、用户名) - -### 依赖升级 - -* update springboot 2.7.7 => 2.7.9 -* update easyexcel 3.1.5 => 3.2.1 -* update redisson 3.19.1 => 3.20.0 -* update hutool 5.8.11 => 5.8.15 (13与14有问题勿使用) -* update springdoc 1.6.14 => 1.6.15 -* update aws-java-sdk-s3 1.12.373 => 1.12.400 -* update element-ui 2.15.10 => 2.15.12 -* update lombok 1.18.24 => 1.18.26 - -### 功能更新 - -* update 优化 实体类中校验注解的提示信息 -* update 优化 修改 oss 配置页面开关说明 避免造成误解 -* update 优化 框架代码书写格式 -* update 优化 调整连接池默认参数 -* update 优化 `DictDataMapper` 注解标注过期 推荐使用 `@Translation` 注解 -* update 优化 部门更新接口 清理缓存 -* update 优化 获取菜单数据权限接口 删除无用角色属性与逻辑 -* update 优化 调整连接池最长生命周期 防止出现警告 -* update 优化 连接池增加 `keepaliveTime` 探活参数 -* update 优化 `DataPermissionHelper` 增加 `开启/关闭` 忽略数据权限功能 -* update 重构 `OssFactory` 加载方式 改为每次比对配置做实例更新 -* update 优化 `SaToken` 自定义扩展类 改为配置类注入 便于扩展 -* update 优化 启用 `sqlserver` 高版本语法 简化sql脚本语法 -* update 优化 更新角色后踢掉所有相关的登录用户 用户量过大会导致redis阻塞卡顿(应粉丝要求) -* update 优化 翻译组件 支持返回值泛型 支持多种类型数据翻译(例如: 根据主键翻译成对象) -* update 优化 限流注解使用 `SpringEl` 表达式动态定义 Key 与 message 国际化支持 -* update 优化 限流功能 `redis key` 生成规则 以 `功能头+url+ip+key` 格式 -* update 优化 只拦截系统内存在的路径 减少不必要的拦截造成的性能消耗 -* update 优化 `tagsView` 右选框,首页不应该存在关闭左侧选项 -* update 优化 `copyright 2023` -* update 优化 监控页面图标显示 -* update 优化 日志注解支持排除指定的请求参数 -* update 优化 业务校验优化代码 -* update 优化 日志管理使用索引提升查询性能 -* update 优化 框架时间检索使用时间默认值 `00:00:00 - 23:59:59` -* update 优化 oss 预览使用 `ImagePreview` 组件 - - -### 新增功能 - -* add 新增 `BeanCopyUtils#mapToMap` 方法 -* add 新增 `StringUtils` `splitTo` 与 `splitList` 方法 优化业务代码 -* add 新增 `EasyExcel` `@ExcelEnumFormat` 枚举类数据翻译注解 - - -### 问题修复 - -* fix 修复 新版本 `Redisson` 存在与 `springboot 2.X` 的兼容性问题 -* fix 修复 vue3 模板点击删除按钮后弹框显示`[object Object]`或控制台报错的问题 -* fix 修复 接口问题开关不生效问题 -* fix 修复 前端优化文件下载出现的异常 -* fix 修复 修改密码日志存储明文问题 -* fix 修复 用户密码注解误删暴露问题 -* fix 修复 代码生成 使用 `postgreSQL` 数据库查出已删除的字段 - - -## v4.5.0 - 2023-01-12 - -### 重大更新 - -* [重大更新] 使用 spring 事件发布机制 重构登录日志与操作日志 支持多事件监听无入侵扩展 -* 例如: 可以增加一个监听者将日志上传至ES等存储 对原有逻辑无影响 - -### 依赖升级 - -* update springboot 2.7.6 => 2.7.7 -* update springboot-admin 2.7.7 => 2.7.10 -* update mybatis-plus 3.5.2 => 3.5.3.1 -* update redisson 3.18.0 => 3.19.1 -* update sa-token 1.33.0 => 1.34.0 -* update easyexcel 3.1.3 => 3.1.5 -* update springdoc 1.6.13 => 1.6.14 -* update snakeyaml 1.32 => 1.33 -* update hutool 5.8.10 => 5.8.11 -* update aws-s3 1.12.349 => 1.12.373 -* update aliyun-sms 2.0.22 => 2.0.23 -* update tencent-sms 3.1.635 => 3.1.660 -* update echarts 4.9.0 => 5.4.0 -* update vue3 element-plus 2.2.21 => 2.2.27 - -### 功能更新 - -* update 优化 BaseMapperPlus 使用 MP V3.5.3 新工具类 Db 简化批处理操作实现 -* update 优化 将环境配置放到 pom 文件上方 便于查看使用 -* update 优化 代码生成与框架主体使用相同的主键生成器 全局统一避免问题 -* update 优化 系统登录 使用单表查询校验用户 避免多次 join 查询 -* update 优化 删除 vue3 模板无用参数 -* update 优化 xss 包装器 变量命名错误 -* update 优化 重构 ExcelUtil 全导出方法支持 OutputStream 流导出 不局限于 response -* update 优化 maven 地址切换回 aliyun 仓库 -* update 优化 去除无用 guava 依赖管理 项目中已无此依赖 -* update 优化 springdoc 配置鉴权头写死问题 增加持久化鉴权头配置 -* update 优化 验证码结果使用 spel 引擎自动计算 -* update 优化 弹窗内容过多展示不全问题 -* update 优化 删除 fuse 无效选项 maxPatternLength -* update 优化 minio 安装警告 使用新版本参数 -* update 优化 使用 spring 事件机制 重构 OssConfig 缓存更新 -* update 优化 抽取 SysLoginService recordLogininfor 记录登录信息方法 简化日志记录 -* update 优化 使用 spring 事件发布机制 重构登录日志与操作日志 -* update 优化 单元格合并判断 cellValue 是否相等方法调整 -* update 优化 去除 RedisConfig 无用继承 - -### 新增功能 - -* add 增加 GET 请求提交日期参数 默认格式化配置 -* add 增加 RedisUtils 检查缓存对象是否存在方法 - -### 问题修复 - -* fix 修复 根据 key 更新参数配置报null问题 -* fix 修复 树形下拉不能默认选中 -* fix 修复 读取 generator.yml 中文乱码问题 -* fix 修复 代码生成图片/文件/单选时选择必填无法校验问题 -* fix 修复 修改参数键名时 未移除过期缓存配置 -* fix 修复 用户注册 用户类型字段书写错误 -* fix 修复 文件名包含特殊字符(+、-、*...)的文件无法下载问题 -* fix 修复 短信校验模板参数传参错误 -* fix 修复 vue3 closeSidebar 这个方法定义的参数没有解构问题 - -## v4.4.0 - 2022-11-28 - -### 重大更新 -* [重大更新] 优化支持 oss 私有库功能(数据库字段改动) #cd9c3c3f -* [重大更新] 连接池由 druid 修改为 hikari 更新相关配置(原因可看文档) #1f42bd3d -* [重大更新] 移除 tlog(不支持UI界面 使用的人太少) 建议使用 skywalking -* [重大更新] 增加 skywalking 集成 默认注释不开启(使用看文档) - -### 依赖升级 -* update springboot 2.7.5 => 2.7.6 -* update springboot-admin 2.7.6 => 2.7.7 -* update satoken 1.31.0 => 1.33.0 -* update spring-doc 1.6.12 => 1.6.13 -* update easyexcel 3.1.1 => 3.1.3 -* update hutool 5.8.8 => 5.8.10 -* update redisson 3.17.7 => 3.18.0 -* update lock4j 2.2.2 => 2.2.3 -* update s3-adk 1.12.324 => 1.12.349 -* update mysql-docker 8.0.29 => 8.0.31 - -### 功能更新 -* update 优化 oss 云厂商增加 华为obs关键字 -* update 优化 冗余的三元表达式 -* update 优化 重置时取消部门选中 -* update 优化 新增返回警告消息提示 -* update 优化 hikari 参数顺序 最常用的放上面 删除无用 druid 监控页面 -* update 优化 p6spy 排除健康检查 sql 执行记录 -* update 优化 Dockerfile 创建目录命令 -* update 优化 将空‘catch’块形参重命名为‘ignored’ -* update 优化 使用本地缓存优化 excel 导出 数据量大字典转换慢问题 -* update 优化 字典转换实现 去除字符串查找拼接优化效率 -* update 优化 减小腾讯短信引入jar包的体积 -* update 消除Vue3控制台出现的警告信息 -* update 忽略不必要的属性数据返回 -* update 替换 mysql-jdbc 最新坐标 - -### 新增功能 -* add 新增 junit5 单元测试案例 #6e8ef308 -* add 增加 sys_oss_config access_policy 桶权限类型字段 -* add 增加 4.3-4.4 更新 sql 文件 -* add 新增 字典数据映射注解 #da94e898 -* add 增加 RedisUtils 获取缓存Map的key列表 - -### 问题修复 -* fix 修复 上传png透明图片 生成头像透明部分变成黑色 -* fix 修复 sqlserver sql文件 重复主键数据问题 -* fix 修复 sqlserver 特定情况下报 ssl 证书问题 默认关闭 ssl 认证 -* fix 修复 table中更多按钮切换主题色未生效修复问题 -* fix 修复 菜单激活无法修改其填充颜色 去除某些svg图标的fill="#bfbfbf"属性 -* fix 修复 使用缓冲流 导致上传异常问题 -* fix 修复 过滤器链使用IoUtil.read方法导致request流关闭 -* fix 修复 Log注解GET请求记录不到参数问题 -* fix 修复 某些特性的环境生成代码变乱码TXT文件问题 -* fix 修复 开启TopNav没有子菜单隐藏侧边栏 -* fix 修复 回显数据字典数组异常问题 - -### 移除功能 -* remove 移除过期 Anonymous 注解与其实现代码 -* remove 移除 tlog(不支持UI界面 使用的人太少) 建议使用 skywalking - -## v4.3.1 - 2022-10-24 - -### 依赖升级 -* update springboot 2.7.3 => 2.7.5 -* update springboot-admin 2.7.4 => 2.7.6 -* update sa-token 1.30.0 => 1.31.0 -* update springdoc 1.6.11 => 1.6.12 -* update poi 5.2.2 => 5.2.3 -* update hutool 5.8.6 => 5.8.8 -* update aws-s3 1.12.300 => 1.12.324 -* update aliyun-sms 2.0.18 => 2.0.22 -* update tencent-sms 3.1.591 => 3.1.611 -* update tlog 1.4.3 => 1.5.0 安全性升级 -* update snakeyaml 1.30 => 1.32 存在漏洞 -* update redisson 3.17.6 => 3.17.7 -* update nginx 1.21.6 => 1.22.1 存在漏洞 -* update element-ui 2.15.8 => 2.15.10 -* update core-js 3.19.1 => 3.25.3 - -### 功能更新 -* update 修改 差异命名与镜像名同步 -* update 优化 通用下载方法新增config配置选项 -* update 优化 日志操作中重置按钮时重复查询的问题 -* update 优化 `@Anonymous` 注解标注过期 使用 `@SaIgnore` 替换 -* update 优化 前端可以配置多排序参数支持依次排序 -* update 优化 oss管理 支持时间排序 -* update 优化 替换 sa-token 过期配置 -* update 优化 sa-token 拦截器注册 `SaTokenConfig#addInterceptors` 排除拦截路径配置 -* update 优化 vue3说明文件 编码问题 -* update 优化 导入更新用户数据前校验数据权限 -* update 优化 `R` 类 `isError` 和 `isSuccess` 改为静态方法 -* update 优化 获取用户信息getInfo接口 使用缓存数据获取 -* update 优化 选择按钮宽度 - -### 问题修复 -* fix 修复 用户导入存在则更新不生效 -* fix 修复 日志转换非json数据导致报错 -* fix 修复 控制台SQL日志打印时间格式化问题 -* fix 修复 不同网段因reset请求头导致下载导出跨域问题 -* fix 修复 在线用户设置永不过期 被过滤问题 -* fix 修复 在线用户设置永不过期 超时时间-1推送redis无效问题 -* fix 修复 snakeyaml 漏洞 强制升级依赖版本(临时处理等boot升级) -* fix 修复 开启账号同端互斥登录 被顶掉后登出报null异常问题 -* fix 修复 Redisson 设置 `NameMapper` 导致队列功能异常问题 -* fix 修复 文件上传组件格式验证问题 -* fix 修复 内部调用缓存不生效问题 -* fix 修复 主题颜色在Drawer组件不会加载问题 -* fix 修复 小屏幕上修改头像界面布局错位的问题 -* fix 修复 内链域名特殊字符替换 合并错误导致问题 -* fix 修复 nginx 漏洞 https://www.oschina.net/news/214309 - -## v4.3.0 - 2022-09-14 - -### 重大更新 -* [重大更新] 整合 springdoc 基于 javadoc 实现无注解零入侵生成接口文档 -* [重大更新] 重写 spring-cache 实现 更人性化的操作 支持注解指定ttl等一些参数 -* [不兼容更新] 移除 swagger 所属所有功能 建议使用 springdoc -* [重大更新] 移除maven docker插件 过于老旧功能缺陷大 使用idea自带的docker插件替代 - -### 依赖升级 -* update springboot 2.6.9 => 2.7.3 -* update springboot-admin 2.7.2 => 2.7.4 -* update redisson 3.17.4 => 3.17.6 -* update hutool 5.8.3 => 5.8.6 -* update okhttp 4.9.1 => 4.10.0 -* update lock4j 2.2.1 => 2.2.2 -* update aws-java-sdk-s3 1.12.248 => 1.12.300 修复依赖安全漏洞 -* update aliyun.sms 2.0.9 => 2.0.18 -* update tencent.sms 3.1.537 => 3.1.591 -* update guava 30.0-jre => 31.1-jre -* update springdoc 1.6.9 => 1.6.11 -* update druid 1.2.11 => 1.2.12 -* update dynamic-ds 3.5.1 => 3.5.2 - -### 功能更新 -* update 优化 短信接口实现类 `@Override` 注解 -* update 优化 登出方法代码逻辑 -* update 优化 代码中的一些魔法值 -* update 优化 使用 StreamUtils 简化业务流操纵 -* update 修改 oss 客户端自定义域名 统一使用https开关控制协议头 -* update 更新 监控过时配置 WebSecurityConfigurerAdapter 改为 bean 注入 -* update 修改 生成错误注释 -* update 优化 docker 部署方式 使用 host 模式简化部署流程 降低使用成本 -* update 修改 验证码开关变量名 -* update 优化 DateColumn 支持单模板多key场景 -* update 优化 redission 处理增加前缀 -* update 优化 缓存监控 相关代码 -* update 优化 部署脚本 防止出现权限问题 -* update 优化 多个相同角色数据导致权限SQL重复问题 -* update 优化 字典数据使用store存取 -* update 优化 布局设置使用el-drawer抽屉显示 -* update 更新框架文档 专栏与视频 链接地址 -* update 优化 OSS文件上传 主动设置文件公共读 适配天翼云OSS -* update 优化 表格上右侧工具条(搜索按钮显隐&右侧样式凸出) -* update 优化 前后端多环境部署保持一致 删除无用环境文件 -* update 优化 错误登录锁定与新增解锁功能 -* update 优化 getLoginId 增加必要参数空校验 -* update 使用 SpringCache注解 优化参数管理、字典管理、在线用户等业务缓存 -* update 优化 多角色数据权限匹配规则 -* update 优化 页面内嵌iframe切换tab不刷新数据 -* update 优化 调整 oss表key 与 ossconfig的service 字段长度不匹配 -* update 优化 操作日志密码脱敏 -* update 重构 QueueUtils 抽取通用方法 统一使用 适配优先队列新用法 - -### 新功能 -* add 增加 StreamUtils 流工具 简化 stream 流操纵 -* add 新增 缓存列表菜单功能 -* add 新增 获取oss对象元数据方法 -* add 增加 QueueUtils 操作普通队列的方法 - -### 问题修复 -* fix 修复 mysql sys_notice 与 sys_config 表主键类型长度不够问题 -* fix 修复 获取 SensitiveService 空问题 增加空兼容 -* fix 修复 代码生成首字母大写问题 -* fix 修复 minio 上传自定义域名回显路径错误问题 -* fix 修复 短信功能返回实体 SysSms 序列化问题 -* fix 修复 sqlserver 更新sql错误提交 -* fix 修复 RedisUtils 并发 set ttl 错误问题 -* fix 修复 防止主键字段名与'row'或'ids'一致导致报错的问题 -* fix 修复 幂等组件 逻辑问题导致线程变量未清除 -* fix 修复 脱敏没有实现类导致返回数据异常问题 -* fix 修复 用户导出字典使用错误 -* fix 修复 用户登录与短信登录 国际化格式不一致 -* fix 修复 BaseMapperPlus 方法命令不一致问题 -* fix 修复 短信功能是否启用判断不生效BUG -* fix 修复 xxljob prod 环境配置文件 数据库ip漏改 -* fix 修复 部署脚本 cp 命令缺少参数问题 -* fix 修复 菜单管理的一些操作问题 -* fix 修复 国际化文件提交为特殊编码问题 -* fix 修复 minio配置https遇到的问题 -* fix 修复 点击删除后点击取消控制台报错问题 -* fix 修复 文件/图片上传组件 第一次上传报错导致后续上传无限loading问题 -* fix 修复 postgresql 时间查询类型转换报错问题 -* fix 修复 部门与角色 状态导出字典使用错误 -* fix 修复 openapi结构体 因springdoc缓存导致多次拼接接口路径问题 -* fix 修复 没有权限的用户编辑部门缺少数据 -* fix 修复 oss配置删除内部数据id匹配类型问题 -* fix 修复 用户导入存在则更新不生效 -* fix 修复 日志转换非json数据导致报错 - -## v4.2.0 - 2022-06-28 -### 重大更新 -* [重大更新] 增加 `ruoyi-sms` 短信模块 整合 阿里云、腾讯云 短信功能 -* [重大改动] 基于 `AWS S3` 协议重新实现 OSS模块 支持自定义域名 -* [安全性] 优化 nginx 限制外网访问内网 actuator 相关路径(建议升级) -* [不兼容] 优化 文件与图片上传组件 使用id存储回显(升级的用户需要注意 上传组件返回值变成了 ossid 便于关联) -* [不兼容] 升级 mybatis-plus 3.5.2 解决新版本兼容性问题 关键字冲突修改(新增了很多关键字 升级的需要注意 冲突的关键字建议换一个命名) - -### 依赖升级 -* update springboot-admin 2.6.6 => 2.6.9 -* update springboot-mybatis 2.2.0 => 2.2.2 -* update sa-token 1.29.0 => 1.30.0 -* update hutool 5.7.22 => 5.8.3 -* update druid 1.2.8 => 1.2.11 -* update tlog 1.3.6 => 1.4.3 -* update easyexcel 3.0.5 => 3.1.1 去除cglib 支持jdk17 -* update xxl-job 2.3.0 => 2.3.1 -* update redisson 3.17.0 => 3.17.4 -* update mybatis-plus 3.5.1 => 3.5.2 -* update poi 4.1.2 => 5.2.2 性能大幅提升 -* update docker mysql 8.0.27 => 8.0.29 -* update docker nginx 1.21.3 => 1.21.6 -* update docker redis 6.2.6 => 6.2.7 -* update docker minio 2021-10-27 => 2022-05-26 - -### 功能更新 -* update 优化 redis 序列化 使用系统自带json工具 全局统一 -* update 优化 RedisUtils 重构过期方法 -* update 完善短信验证码发送接口 -* update 优化 弹窗点击遮罩层 默认不关闭 可在 main.js 修改 -* update 调整 CacheManager 使用系统 系统序列化器 -* update 调整 图片预览组件 去除无用根目录拼接 -* update 用户管理左侧树型组件增加选中高亮保持 -* update 优化 DataPermissionHelper 上下文存储 使用 SaToken 的请求存储器 -* update 优化 用户头像上传限制只能为图片格式 -* update 优化 redis 与 jackson 使用自动装配定制器简化配置 -* update 优化 getLoginUser 获取 使用一级缓存 -* update 增加 redis 无密码使用说明 -* update 手动配置 Undertow 缓冲池 消除运行警告 -* update 优化 表单构建按钮不显示正则校验 -* update 优化 oss 回显查询 使用 redis 缓存 -* update 优化 用户列表查询 剔除密码字段 -* update 优化 验证码 登录 登出 注册 等接口 使用匿名注解放行 -* update 修改 代码生成 controller 去除查询校验 由用户自行选择是否校验 -* update 优化 ExcelUtil 工具支持合并处理器 -* update 使用 SaStorage 优化 LoginHelper 一级缓存 避免 ThreadLocal 清理不干净问题 -* update 优化 新增用户与角色信息、用户与岗位信息逻辑 -* update 优化 代码生成 业务接口 增加事务回滚 -* update 优化 logback 删除无用配置 - -### 新功能 -* add 增加 MailUtils 邮件工具 -* add 增加 RedisUtils 操作原子值方法 -* add 增加 demo 短信演示案例 -* add 增加 获取短信验证码接口 -* add 新增 SpringUtils 获取配置文件中的属性值方法 -* add 新增 Anonymous 匿名访问不鉴权注解 -* add 新增 easyexcel 单元格合并注解与处理器 -* add 增加 ExcelUtil 模板导出方法 支持 单列表/多列表 -* add 增加 Excel 模板导出 测试类 - -### 问题修复 -* fix 修复 ExcelUtil 表达式解析 参数添反导致无法解析问题 -* fix 修复 全局线程池配置 核心线程与最大线程 参数填反问题 -* fix 修复 查询未分配用户角色列表 角色无绑定用户情况下 空列表问题 -* fix 修复 sqlserver 新增数据 id 错误 -* fix 修复 token 超时时间设置 -1 导致的单位转换问题 -* fix 修复 编辑 OssConfig 在 postgres 字段重复报错 补全 remark 字段 -* fix 修复 postgres 数据库 菜单部分字段类型无法转换问题 -* fix 修复 脱敏实现逻辑问题 -* fix 修复 登录未选部门报空问题 -* fix 修复 用户注销时记录注销日志异常问题 -* fix 修复 代码生成表字段类型不匹配 导致查询不准确问题 - -## v4.1.0 - 2022-04-24 -### 重大更新 -* [重大更新] 增加应用适配 oracle -* [重大更新] 增加应用适配 SQL Server -* [重大更新] 增加应用适配 postgresql -* [重大更新] 确保更好的适配 多数据库 主键策略统一改为 雪花ID - -### 依赖升级 -* update springboot 2.6.4 => 2.6.7 修复 CVE-2022-22965 漏洞 -* update springboot-admin 2.6.2 => 2.6.6 -* update hutool 5.7.21 => 5.7.22 -* update dynamic-datasource 3.5.0 => 3.5.1 -* update redisson 3.16.8 => 3.17.0 -* update qiniu 7.9.3 => 7.9.5 -* update qcloud 5.6.68 => 5.6.72 -* update minio 8.3.7 => 8.3.8 -* update okhttp 4.9.2 => 4.9.3 - -### 功能更新 -* update 简化查询 部门、菜单、角色、用户、代码生成列表 功能 -* update 优化 部门修改子元素关系 使用批量更新 -* update 优化去除sql差异化 时间范围统一使用 between 处理 -* update 优化 RepeatSubmit 注解 支持业务处理失败 与 异常快速放行 -* update 优化 防重 与 限流 功能支持国际化消息返回 -* update 开启TopNav没有子菜单情况隐藏侧边栏 -* update 更新minio压缩配置 -* update 重命名 菜单字段 query -> query_param 解决系统关键字问题 -* update 使用 in 优化 or 提升索引命中率 -* update 优化 TreeEntity 树实体 去除未知泛型 -* update 优化菜单名称过长悬停显示标题 -* update 优化固定Header后顶部导航栏样式问题 -* update 优化 logback 日志 异步输出 -* update 全局异常处理器引入DuplicateKeyException主键冲突异常拦截 -* update topNav自定义隐藏侧边栏路由 -* update 更名 SaInterfaceImpl 为 SaPermissionImpl 完善相关注释 -* update 优化 sa-token 路由拦截器语法 增加注释 避免误操作 -* update 优化文件上传、图片上传组件 文件列表展示文件原名便于后续处理, 完善组件删除功能 -* update 优化登录失败相关部分代码结构 -* update 使用 spring cglib 替换 停止维护的 cglib -* update 简化 全局线程池配置 使用cpu核心数自动处理 -* update 移除 重复提交 配置文件全局配置 使用注解默认值替代 - -### 新功能 -* add 增加 4.0 升级 4.1 的 sql 脚本(升级需执行此sql) -* add 增加 DataBaseHelper 数据库助手 用于屏蔽多类型数据库sql语句差异 -* add 增加 短信登录 与 小程序登录 示例 -* add 增加 Mybatis 全局异常处理 开启多数据源切换 严格模式 找不到数据源报错 - -### 问题修复 -* fix 修复 数据权限 从 aop 切换到 拦截器 导致获取代理失败问题 -* fix 修复表单清除元素位置未垂直居中问题 -* fix 修复 poi 组件漏洞 与 mysql jdbc 漏洞 -* fix 修复单独访问 接口文档 请求 favicon.ico 报错问题 -* fix 修复 minio 上传, 因 socket 导致 available 获取数值不精确问题 -* fix 修复 cos_api bcprov-jdk15on 漏洞 -* fix 修复 guava 漏洞 统一依赖版本 -* fix 修复 tlog 依赖漏洞 - -## v4.0.1 - 2022-03-01 -### 依赖升级 -* update springboot 2.6.3 => 2.6.4 -* update hutool 5.7.20 => 5.7.21 -* update qiniu 7.9.2 => 7.9.3 -* update minio 8.3.5 => 8.3.7 - -### 功能更新 -* update 图片上传 文件上传 支持并发上传 -* update 组件ImageUpload支持多图同时选择上传 -* udpate 组件fileUpload支持多文件同时选择上传 -* update 优化 R 默认返回 msg -* update 增加 用户注册 用户类型默认值 -* update 增加用户登出日志 -* update 更新 多用户多设备的注释说明 -* update 优化 是否为管理员的判断 -* update 优化 页面若未匹配到字典标签则返回原字典值 -* update 调整用户登录 将日志调整到最后 防止获取不到用户警告 -* update 优化随机数生成方式 避免容易生成两个相同随机数的问题 - -### 问题修复 -* fix 修复代码生成 基于路径生成 路径为空问题 -* fix 恢复误删 `@Async` 注解线程池配置类 -* fix 修复 minio 适配 https 导致的问题 -* fix 修复分页组件请求两次问题 - -## v4.0.0 - 2022-02-18 -### 重大更新 -* [重大更新] 重写项目整体结构 数据处理下沉至Mapper符合MVC规范 减少循环依赖 -* [重磅更新] 主分支与satoken分支合并 权限统一使用 sa-token -* [重磅更新] 适配升级 SpringBoot 2.6 -* [重磅更新] EasyExcel大版本升级3.X -* [重磅更新] 移除链式调用注解 因链式调用不符合java规范 导致很多问题 -* [重磅更新] 增加 轻量级 分布式队列 支持 -* [重磅更新] 增加 数据脱敏注解 使用序列化控制脱敏 支持多种表达式 -* [重磅更新] 重构 使用 Spring 简化 oss 模块代码 -* [重磅更新] 重构 调整返回类型为 R 精简 Controller 代码 - -### 依赖升级 -* update springboot 2.5.8 => 2.6.3 -* update mybatis-plus 3.4.3.4 => 3.5.1 -* update maven-jar-plugin 3.2.0 => 3.2.2 -* update maven-war-plugin 3.2.0 => 3.2.2 -* update maven-compiler-plugin 3.1 => 3.9.0 -* update hutool 5.7.18 => 5.7.20 -* update springboot-admin 2.6.0 => 2.6.2 -* update redisson 3.16.7 => 3.16.8 -* update qiniu 7.9.0 => 7.9.2 -* update aliyun 3.13.1 => 3.14.0 -* update qcloud 5.6.58 => 5.6.68 -* update minio 8.3.4 => 8.3.5 - -### 功能更新 -* update 用户管理部门查询选择节点后分页参数初始 -* update 防重复提交标识组合(key + url + header) -* update 接口文档增加 basic 账号密码验证 -* update 用户修改减少一次角色列表关联查询 -* update 优化部门修改缩放后出现的错位问题 -* update 指定 maven 资源过滤为具体文件 防止错误过滤 -* update hutool 引入改为 bom 依赖项引入 -* update 降低开发环境 redis连接池数量 -* update 升级 springboot 2.6.X 解决 springfox 兼容性问题 -* update 优化多用户体系处理 更名 LoginUtils 为 LoginHelper 支持 LoginUser 多级缓存 -* update 优化加载字典缓存数据 -* update 数据库更改 对接多用户体系 -* update 移除掉 StringUtils 语义不明确的api方法 使用特定工具替换 -* update 优化登录、注册在接口通过`@Validated`注解进行数据基础校验 -* update 优化 查询登录用户数据 统一走缓存 -* update 优化 redisson 配置 去除掉不常用的配置 使用默认配置 -* update 用户访问控制时校验数据权限,防止越权 -* update 修改用户注册报未登录警告 -* update 调整oss预览开关 使用前端直接调用更改配置参数 -* update 使用 satoken 自带的 BCrypt 工具 替换 Security 加密工具 减少依赖 -* update 优化 TreeBuildUtils 工具 使用反射自动获取顶级父id -* update 使用 hutool Dict 优化 JsonUtils 防止类型解析异常 -* update 优化代码生成 使用新 JsonUtils.parseMap 方法 -* update 更新 所有 oss 均支持 https 配置 - -### 新功能 -* add 增加 RedisUtils 工具 hasKey 检查key存在方法 -* add 增加 监控中心 自定义事件通知 -* add 增加 3.X update 4.0 更新sql - -### 问题修复 -* fix 修复登录失效后多次请求提示多次弹窗问题 -* fix 修复 StringUtils 通配符匹配无效 -* fix 修复选项卡点击右键刷新丢失参数问题 -* fix 修复 数据权限 缓存方法名错误问题 -* fix 修复自定义组件`file-upload`无法显示第一个文件,列表显示的文件比实际文件少一个的问题 -* fix 修复因升级 sa-token 导致 doLogin 无法获取 token 问题 -* fix 修复分页组件请求两次问题 - -### 移除功能 -* remove 移除过期代码 分页工具相关 -* remove 移除过期代码 多数据源切换 -* remove 移除过期代码 数据权限 - -### 其他 -* 3.X 版本进入维护阶段 不进行更新 只修复bug 持续维护到2022年10月 -* 4.X 版本公测将近一个月 大部分bug已修复 官网主分支更改为 4.X 版本 推荐使用 - - -## v3.5.0 - 2021-12-28 -### 重大更新 -* [重大更新] 重写数据权限实现 -* [重磅更新] 重构分页 简化使用 -* [重磅更新] 用户登录 支持校验错误次数锁定登录 -* [重磅更新] 增加 jdbc 批处理参数 大幅提升批量操作性能 对原生语句与 MP 均有效 - -### 依赖升级 -* update springboot 2.5.7 => 2.5.8 升级预防 log4j2 问题 -* update springboot-admin 2.5.4 => 2.5.5 -* update hutool 5.7.16 => 5.7.18 -* update redisson 3.16.4 => 3.16.7 -* update dynamic-ds 3.4.1 => 3.5.0 -* update qiniu 7.8.0 => 7.9.0 -* update minio 8.3.3 => 8.3.4 -* update tlog 1.3.4 => 1.3.6 启用 tlog 自动配置 -* update clipboard 2.0.6 => 2.0.8 - -### 功能更新 -* update 多数据源切换标注过期 3.6.0 移除 推荐使用原生注解 -* update 通用权限服务 迁移回 ruoyi-framework 模块 -* update 使用 hutool-jwt 替换老旧 jjwt 依赖 -* update 调整 OSS 表字段内容长度 -* update LoginUser 增加角色缓存 优化角色权限代码 -* update 使用 Cglib 重构 BeanCopyUtils 性能优异 -* update 禁止所有工具类实例化 优化代码书写规范 -* update 优化查询用户的角色组、岗位组代码 -* update 更新 RedisUtils 返回客户端实例 -* update 修改 健康检查权限 改为用户放行 提高安全性 -* update hutool 工具 改为单包引入 减少无用依赖 -* update ServicePlusImpl 功能 下沉到 BaseMapperPlus -* update 去除 jdk17 标签 由于很多组件还未适配 导致一些问题 -* udpate 代码生成预览支持复制内容 -* update 用户导入提示溢出则显示滚动条 -* update 路由支持单独配置菜单或角色权限 -* update 优化web拦截器 使用原生接口处理 默认非生产环境开启 -* update 调整监控依赖 从 common 迁移到 framework - -### 新功能 -* add 新增 Vue3 分支 与 代码生成模板(由于组件还未完善 仅供学习) -* add 增加 RedisUtils 注册监听器方法 -* add 增加 自定义 Xss 校验注解 用户导入增加 Bean 校验 -* add oss下载增加 loading 层 -* add 新增图片预览组件 -* add 集成compression-webpack-plugin插件实现打包Gzip压缩 -* add 新增 SqlUtils 检查关键字方法 - -### 问题修复 -* fix 修复 集群雪花id重复问题 使用网卡信息绑定生成 -* fix 修复 count 语法异常 -* fix 修复更改密码问题 -* fix 修复sql关键字处理 防止解析器报错 -* fix 修复 TreeBuildUtils 顶节点不为 0 问题 -* fix 修复 SysOssConfig 主键类型错误 -* fix 修复代码生成 导出注解错误 -* fix 修复 redisson 集群模式 路径未匹配协议头问题 -* fix 修复打包后字体图标偶现的乱码问题 -* fix 修复版本差异导致的懒加载报错问题 -* fix 修复代码生成字典组重复问题 - -### 移除功能 -* remove 删除 jjwt 无用依赖 -* remove 移除过期 用户导入 -* remove 移除过期工具 DictUtils - -## v3.4.0 - 2021-11-29 - -### 重磅更新 -* update [重磅更新] 重构 Excel 导入 支持 Validator 校验 支持自定义监听器 -* update [重磅更新] Validator 校验框架支持国际化 - -### 依赖升级 -* update springboot 2.5.6 => 2.5.7 -* update hutool 5.7.15 => 5.7.16 -* update okhttp 4.9.1 => 4.9.2 -* update spring-boot-admin 2.5.2 => 2.5.4 -* update redisson 3.16.3 => 3.16.4 -* update tlog 1.3.3 => 1.3.4 -* update axios 0.21.0 => 0.24.0 -* update core-js 3.8.1 => 3.19.1 -* update js-cookie 2.2.1 => 3.0.1 -* update velocity 1.7 => 2.3 -* update 升级 docker 基础镜像 - -### 功能更新 -* update 基于 hutool 封装树构建工具 重构部门与菜单树结构返回 -* update 减少使用特定数据库函数 -* update 配置应用前缀路径 改为配置文件统一配置 -* update 升级 swagger 配置 使用 knife4j 增强模式 -* update 监控中心 集成监控客户端 实现自监控 -* update 调度中心 集成监控客户端 注册到监控中心 -* update 优化 tab 对象简化页签操作 -* update 解耦 LoginUser 与 SysUser 强关联 -* update 更新 RepeatSubmit 注解 aop 处理 针对特殊参数进行过滤 -* update DictUtils 字典工具类 标记过期 3.5.0 版本移除 使用 DictService 代替 -* update 抽象 DictService 通用 字典服务 -* update 抽象 ConfigService 通用 参数配置服务 -* update 基于 DictService 重构 Excel 内字典查询功能 -* update OSS 模块 整体重命名 消除歧义 -* update 更新 redis.conf 存储策略 aof 与 rdb 配置参数 -* update 初始化数据转移到 ApplicationRunner 统一处理 -* update 优化时间查询语句 - -### 新功能 -* add 增加 框架缓存懒加载 开关 -* add 新增 监控中心 Bean 初始化 startup trace 监控插件 -* add 增加 ValidatorUtils 校验框架工具 用于在非 Controller 的地方校验对象 - -### 漏洞修复 -* fix 修复 SysOss、SysOssConfig 未继承 BaseEntity 基础实体问题 -* fix 修复 xxl-job-admin 部署问题 -* fix 修复 回显数据字典键值修正 -* fix 修复 Linux 清除临时目录 导致上传找不到目录报错问题 -* fix 修复通用实体 传参无法接收问题 -* fix 修复 SysLoginController 接口文档书写错误问题 -* fix 修复 用户逻辑删除 差异问题 -* fix 修复 OSS 七牛云 token 过期未刷新问题 -* fix 修复 分页工具 排序字段 null 处理 -* fix 修复 用户导入字典使用错误 -* fix 修复 关闭 xss 功能导致可重复读 RepeatableFilter 失效 -* fix 修复 使用 this.$options.data 报错问题 -* fix 修复 代码生成复选框字典遗漏问题 -* fix 修复 重复提交不生效问题 由于概念不同 使用 RedisUtils 重构 -* fix 修复 OSS 工厂 未实例化服务更新加载问题 - -### 功能移除 -* remove 移除 quartz 相关代码与依赖 -* remove 移除 feign 相关代码与依赖 -* remove 移除 MybatisPlusRedisCache 二级缓存 - -## v3.3.0 - 2021-10-29 - -### 重磅更新 -* add [重磅更新] 增加分布式日志框架 TLog -* add [重磅更新] 增加分布式任务调度系统 Xxl-Job -* add [重大更新] 增加 ruoyi-job 任务调度模块(基于xxl-job) -* update [重大更新]全业务 增加 接口文档注解 格式化代码 - -### 依赖更新 -* update springboot 2.5.5 => 2.5.6 -* update springboot-admin 2.5.1 => 2.5.2 -* update element-ui 2.15.5 => 2.15.6 -* update hutool 5.7.13 => 5.7.15 -* update qcloud.cos 5.6.55 => 5.6.58 -* update minio 8.3.0 => 8.3.3 - -### 功能更新 -* update 更新 element 2.15.6 表格样式 -* update 优化 代码生成常量 关于 BO VO 注释 -* update 优化代码生成 导入表 列表返回 主键默认选中 -* update MybatisPlusRedisCache 标记过期 推荐使用 spring-cache -* update Quartz 标记过期 推荐迁移至新框架 xxl-job -* update Feign 标记过期 -* update 前端增加默认国际化参数 -* update 更新 Admin 监控 注释 避免错误使用 -* update Admin 监控增加日志文件输出 -* update 优化 xxl-job-admin 增加格式化日志输出与 docker 镜像 -* update 更新 xxl-job 执行器开关功能 -* update 代码生成 改为生成抽象实体 -* update 代码生成 搜索框 更新文本域生成 用于模糊查询 -* update 通用数据注入改为适配通用实体类 -* update 使用路由懒加载提升页面响应速度 -* update 迁移所有脚本文件至 script 目录 -* update swagger 组顺序配置 -* update sql 文件更新 xxljob 控制台菜单 -* update 前端增加 任务调度中心页面与环境及 nginx 配置 -* update 合并 oss.sql 至主 sql -* update 补全国际化文件(英文) -* update 更新关于全局路径设置与文档链接 -* update 删除无用 setUsername 使用自动注入 -* update RedisUtils 更新删除 hash 数据方法 - -### 漏洞修复 -* fix 修复 多数据源 aop 语法错误 -* fix 修复 子菜单无 query 参数问题 -* fix 修复 oss 配置删除时删除缓存 bug -* fix 修复无权限获取请求头 download-filename 导致文件名为空问题 - -## v3.2.0 - 2021-9-28 - -### 重大更新 -* update [重大改动]接口文档 支持分组配置 -* update [重大改动]security 路径配置抽取到配置文件 -* update [重大改动] 将 framework 与 system 模块 解耦 调整依赖结构 解决依赖冲突 -* update [重大改动]重写 防重提交实现 使用分布式锁 解决并发问题 压测通过 - -### 依赖更新 -* update springboot 2.5.4 => 2.5.5 bugfix版本 -* update mybatis-plus 3.4.3.3 => 3.4.3.4 bugfix版本 -* update redisson 3.16.2 => 3.16.3 bugfix版本 -* update easyexcel 2.2.10 => 2.2.11 -* update hutool 5.7.11 => 5.7.13 -* update file-saver 2.0.4 => 2.0.5 -* update dart-sass 1.32.0 => 1.32.13 -* update sass-loader 10.1.0 => 10.1.1 - -### 功能更新 -* update 优化代码生成 根据MP生成特性 调整导入表结构默认值合理化 -* update 将所有 云存储字样 改为 对象存储 避免误解 -* update 更新 @Cacheable 错误用法 注意事项 -* update 优化 AddressUtils 空校验处理 -* update 菜单管理支持配置路由参数 -* update 优化aop语法 使用spring自动注入注解 -* update 使用 Redisson 限流工具 重写限流实现 -* update 使用 vue-data-dict 简化数据字典使用 -* update 增加日志注解新增是否保存响应参数开关 -* update 用户未登录日志改为 warn 级别 -* update OSS模块 关于下载403报错信息优化 -* update 更新 Actuator prod 默认暴漏端点 增加暴漏 logfile 日志端点 -* update 默认适配jdk11 测试 jdk17 无异常 -* update 封装通用下载方法简化下载使用 - -### 新功能 -* add 新增通用方法简化模态/缓存使用 -* add 增加 限流演示案例 -* add 增加 redis redisson 集群配置 - -### 漏洞修复 -* fix Cron表达式生成器关闭时销毁,避免再次打开时存在上一次修改的数据 -* fix 全局限流key会多出一个"-" 将其移动到IP后面 去除多余的空格 -* fix 修复多主键代码生成bug -* fix 修复 @Cacheable 与 @DataScope 冲突问题 -* fix 修复代码生成页面数据编辑保存之后总是跳转第一页的问题 - -### 功能移除 -* remove 移除过期工具 RedisCache -* remove 移除无用配置类 ServerConfig -* remove 移除 SysUser 无用字段 salt - -## v3.1.0 - 2021-9-7 - -### 重大更新 -* add [重大改动] 过期 RedisCache 新增 RedisUtils 工具类 新增 发布订阅功能 更灵巧便于使用 -* add [重大改动] 新增 saveOrUpdateAll 方法 可完美替代 saveOrUpdateBatch 高性能 -* update [重大改动] 重写 InsertAll 方法实现 可完美替代 saveBatch 秒级插入上万数据 -* update [重大改动] 更改OSS上传通用路径生成 按照年月日分三级目录 -* update [重大改动] MP字段验证策略更改为 NOT_NULL 个别特殊字段使用注解单独处理 -* update [重大改动] 所有业务适配 RedisUtils 新工具 - -### 依赖升级 -* update springboot 2.5.3 => 2.5.4 -* update spring-boot-admin 2.5.0 => 2.5.1 -* update mybatis-plus 3.4.3 => 3.4.3.3 适配升级 (包含不兼容升级) -* update aliyun.oss 3.13.0 => 3.13.1 -* update qcloud.cos 5.6.47 => 5.6.51 -* update hutool 5.7.9 => 5.7.11 -* update maven-jar-plugin 3.1.1 => 3.2.0 -* update feign-okhttp 11.2 => 11.6 -* update redisson 3.16.1 => 3.16.2 - -### 新功能 -* add 优化 docker 增加 redis 配置文件 -* add 新增暗色菜单风格主题 -* add 菜单&部门新增展开/折叠功能 -* add 页签右键按钮添加图标 页签新增关闭左侧 - -### 功能更新 -* update 优化 OSS 模块与上传组件 异常处理 -* update 更新 jackson 配置 支持 LocalDateTime 全局格式化 -* update 优化 使用权限工具 获取用户信息 -* update 自定义可拖动弹窗宽度指令 -* update 重构 将下载excel工具提取到全局 -* update 定时任务对检查异常进行事务回滚 -* update 优化spy配置文件为 UTF8编码 解决中文注释乱码问题 -* update 修改时检查用户数据权限范围 -* update 解决 logout 写死 无法扩展路径问题 -* update 优化代码生成 导入与同步 批处理效率 -* update 修改时检查用户数据权限范围 -* update 修改代码生成字典回显样式 -* update 修改数据字典回显 -* update 优化验证码配置 使用泛型 防止错误输入 -* update 优化全局线程池配置 使用泛型 防止错误输入 -* update 使用 MP 全局配置分页溢出 -* update 代码生成器 导入表时查询 新创建表的优先排序在前面 -* update 定时任务支持在线生成cron表达式 -* update 自定义弹层溢出滚动样式 -* update 优化分页工具排序处理 -* update 优化 oss配置 使用发布订阅工具 刷新配置 -* update 代码生成 查询数据库列表 按照时间倒序 -* update 使用MP自行判断数据库类型 - -### 漏洞修复 -* fix 修复保存配置主题颜色失效问题 -* fix 修复 导出雪花id excel失真问题 -* fix 修复 druid 监控 集群模式下 无法路由到同一台服务器问题 -* fix 解决搜索校验不通过问题 -* fix 修复定时器工具编写错误问题 -* fix 修复 minio 无 perfix 问题 -* fix 修复 富文本图片路径错误问题 -* fix 修复 OSS配置清空被过滤问题 -* fix 修复 excel 导入与 class 未对应问题 -* fix 修复字典组件值为整形不显示问题 - -## v3.0.0 - 2021-8-18 - -### 重大更新 -* add [重大更新]重写 OSS 模块相关实现 支持动态配置(页面配置) -* add [重大更新]增加 jackson 超出 JS 最大数值自动转字符串(雪花id序列化)处理 -* add [重大更新]重写 防重提交拦截器 支持全局与注解自定义 拦截时间配置配置 优化逻辑 -* add [重大更新]新增是否开启用户注册功能 -* add [重大更新]增加 easyexcel 工具类 -* add [重大更新]集成 性能分析插件 p6spy 更强劲的 SQL 分析 -* add [重大更新]增加 完整国际化解决方案 -* add [重大更新]支持自定义注解实现接口限流 - -### 依赖升级 -* update feign-okhttp 11.0 => 11.2 -* update okhttp 3.19.4 => 4.9.1 -* update minio 8.2.0 => 8.3.0 -* update hutool 5.7.6 => 5.7.7 -* update element-ui 2.15.2 => 2.15.5 -* update springboot admin 2.4.3 => 2.5.0 (新增 Quartz 专属监控页) - -### 新功能 -* add 增加 admin 监控客户端开关 -* add 增加 国际化演示demo - -### 依赖更新 -* update 更新软件架构图 -* update 优化XSS跨站脚本过滤 -* update 优化BLOB下载时清除URL对象引用 -* update 更新 防重提交拦截器 demo演示案例 -* update 日常字符串校验 统一重构到 StringUtils 便于维护扩展 -* update 修改 自动注入器 用户未登录异常拦截抛出警告 返回Null -* update 重构 统一使用 流工具下载 -* update 重写 所有业务导出 适配easyexcel工具 -* update 移动文件存储业务到 system 模块 -* update 代码生成模板 适配新excel导出 -* update 将 Actuator 配置 移动到全局配置 -* update 统一镜像时区配置 移除主机时间映射 -* update 更改多数据源框架更清晰的依赖名 -* update 更新 阿里云 maven源 新地址 -* update 补全基础实体 文档注解 -* update 代码生成文档注解 增加必填判断配置 -* update 注入器 insert 增加 update 字段处理 -* update 默认首页使用keep-alive缓存 - -### 漏洞修复 -* fix 生产minio回显问题 -* fix 修复角色分配用户页面接收参数与传递参数类型不一致导致的错误 -* fix 修复代码生成 删除按钮报错 loading 不取消问题 -* fix 解决登录后浏览器后台Breadcrumb组件报错 -* fix 修复DictUtils方法报错 -* fix 头像上传 未走OSS存储问题 -* fix oss列表 jpeg 不回显问题 -* fix 修复操作日志根据状态查询异常问题 - -### 功能移除 -* remove 移除原生excel工具 -* remove 移除通用上传下载接口与配置 - -## v2.6.0 - 2021-7-28 - -### 重大更新 -* add [重大新增] 增加 OSS 对象存储模块 -* remove [重大改动] 删除 自带通用上传 接口 使用OSS模块替换 -* update [重大改动] 重写VO转换 支持深拷贝 将VO类抽象到 ServicePlus 泛型处理 -* update [重大改动] 多BO合并 使用分组校验 生成BO代码 -* update [重大改动] 重构 IServicePlus 功能 增加 BeanCopyUtils 深拷贝工具 - -### 依赖升级 -* update springboot 2.4.9 => 2.5.3 -* update hutool 5.7.4 => 5.7.6 -* update minio 8.2.2 => 8.3.0 -* update docker plugin 1.2.0 => 1.2.2 -* update redisson 3.16.0 => 3.16.1 -* update datasource 3.4.0 => 3.4.1 -* update element-ui 2.15.2 => 2.15.3 - -### 新功能 -* add 演示Demo增加自定义分页接口案例 -* add 角色&菜单新增字段属性提示信息 - -### 功能更新 -* update 更新druid配置 独立配置更明显 -* update 顶部菜单排除隐藏的默认路由 -* update 富文本新增上传文件大小限制 -* update 导入用户样式调整 -* update 顶部菜单样式调整 -* update 密码框新增显示切换密码图标 -* update 内链设置meta信息 -* update 跳转路由高亮相对应的菜单栏 - -### 漏洞修复 -* fix 修复多数据源druid全局配置缩进错误 引起无效配置问题 -* fix 修复定时任务日志执行状态显示 -* fix 修复 授权角色空数据问题 -* fix 修复 DictData 删除逻辑问题 -* fix 修复任意账户越权漏洞 - -## v2.5.2 - 2021-7-19 - -### 功能更新 -* update 优化代码生成器注释格式 - -### 漏洞修复 -* fix 回滚代码生成 批处理优化 -* fix 代码生成 queryType 重复勾选数据库无默认值问题 -* fix 修复接口单参数校验无效问题 -* fix 代码生成 queryType >= <= 标识符错误问题 -* fix 修复代码生成字典问题 -* fix 修复 thread-pool: enabled 配置不生效问题 - -### 功能移除 -* remove 删除无用文档与脚本 - -## v2.5.1 - 2021-7-13 -* update 验证码开关 转移到表 参数管理 内 -* update 使用hutool重构 判断是否url - -### 漏洞修复 - -### 功能更新 -* fix 修复 docker业务集群部署与文件上传的问题 -* fix 修复代码生成同步表结构id冲突问题 -* fix 修复代码生成选择字典 无法取消问题 -* fix 修复代码生成字典为null问题 -* fix 图片上传 多图时无法删除相应图片修复 - -### 功能移除 -* remove 删除富文本video事件 - -## v2.5.0 - 2021-7-12 - -### 依赖升级 -* update springboot 2.4.7 => 2.4.8 -* update knife4j 3.0.2 => 3.0.3 -* update hutool 5.7.2 => 5.7.4 -* update spring-boot-admin 2.4.1 => 2.4.3 -* update redisson 3.15.2 => 3.16.0 - -### 新功能 -* add 增加 docker 编排 与 shell 脚本 -* add 增加 feign 熔断 自定义结构体解析方法 与 demo 注释 -* add 用户管理新增分配角色功能 -* add 角色管理新增分配用户功能 -* add 增加spring-cache演示案例 - -### 功能更新 -* update 独立 springboot-admin 监控到扩展模块项目 -* update springboot-admin 监控 增加用户登录权限管理 -* update 优化代码生成器 批量导入 -* update 优化 增加MP注入异常拦截 -* update 关闭默认二级缓存 推荐使用 spring-cache 注解手动缓存 -* update FileUpload ImageUpload组件 支持多图片上传 -* update 优化中英文语言配置 -* update 规范maven写法 - -### 漏洞修复 -* fix redis获取map属性bug修复。 -* fix 修复 按钮loading 后端500卡死问题 -* fix 相对路径下载问题 -* fix 修复 hutool 工具返回结果不一致问题 - -## v2.4.0 - 2021-6-24 - -### 依赖升级 -* update springboot 2.3.11 => 2.4.7 -* update springboot-admin 2.3.1 => 2.4.1 -* update feign 2.2.6 => 3.0.3 -* update hutool 5.6.7 => 5.7.2 - -### 功能更新 -* update 多数据源替换成dynamic-datasource -* update 适配 jdk11 -* update 集成 Lock4j 分布式锁 -* update 移除 fastjson 增加 jackson 工具类 重写相关业务 -* update 优化 异步工厂重写 使用 spring 异步处理 -* update 全局挂载字典标签组件 -* update 日志列表支持排序操作 -* update 更新 feign demo 更清晰的用法 -* update 更新多数据源演示案例 - -### 新功能 -* add 增加 ServicePlusImpl 自动以实现类 重写移除事务注解方法 防止多数据源失效 -* add 增加 自定义 批量insert方法 -* add 增加 Swagger3 用法示例 - -### 漏洞修复 -* fix 修复地址ip地址特殊回环问题 - -## v2.3.2 - 2021-6-11 - -### 新功能 -* add redis锁工具类编写 - -### 功能更新 -* update spring-cache 整合 redisson -* update MybatisPlus整合Redis二级缓存 -* update swagger 升级为 3.0.0 使用 OAS_30 协议 -* update 优化 代码生成器 增加表单防重注解 -* update 优化 锁切面代码 key到常量类 - -### 漏洞修复 -* fix 修复相对路径上传异常问题 - -## v2.3.1 - 2021-6-4 - -### 新功能 -* add 增加 redisson 分布式锁 注解与demo案例 -* add 增加 Oracle 分支 - -### 功能更新 -* update 优化 redis 空密码兼容性 -* update 优化前端代码生成按钮增加 loading - -### 漏洞修复 -* fix 修复 redisson 不能批量删除的bug -* fix 修复表单构建选择下拉选择控制台报错问题 -* fix 修复 vo 代码生成 主键列表显示 重复生成bug -* fix 修复上传路径 win 打包编译为 win 路径, linux 报错bug - -## v2.3.0 - 2021-6-1 - -### 新功能 -* add 升级 luttuce 为 redisson 性能更强 工具更全 -* add 增加测试数据sql文件 -* add 增加demo模块 单表演示案例(包含数据权限) - -### 功能更新 -* update 完美修复 数据权限功能(支持单表多表过滤) -* update 优化代码生成模板 -* update 优化 system 模块 批量操作性能 - -## v2.2.1 - 2021-5-29 - -### 新功能 -* add 增加 security 权限框架 `@Async` 异步注解配置 - -### 功能更新 -* update 优化dataScope参数防止注入 -* update 优化参数&字典缓存操作 -* update 增加修改包名文档 -* update 文档增加演示图例 - -### 漏洞修复 -* fix 修复部门类sql符号错误 - -## v2.2.0 - 2021-5-25 - -* 同步升级 RuoYi-Vue 3.5.0 - -### 新功能 -* add 增加验证码开关 -* add 新增IE浏览器版本过低提示页面 - -### 功能更新 -* update 升级druid到最新版本v1.2.6 -* update 升级fastjson到最新版1.2.76 -* update 修改bo加入判断是否设置必填再加载必填注解 -* update 生成vue模板导出按钮点击后添加遮罩 -* update Redis设置HashKey序列化 -* update 优化Redis序列化配置 - -### 漏洞修复 -* fix 修复代码生成器中表字段取消必填无法更新问题 - -## v2.1.2 - 2021-5-21 - -### 功能更新 -* update springboot 升级 2.3.11 -* update mybatis-plus 升级 3.4.3 分页Plus对象适配更新 -* update 验证码生成更新为无符号整数计算 -* update 请求响应对象 与 分页对象 结构修改 适配接口文档配置 -* update swagger增加请求前缀 - -## v2.1.1 - 2021-5-19 - -### 功能更新 -* update 配置统一提取为 properties 配置类 -* update 分页工具 删除过期方法 -* update admin 实时监控日志 改为保留一天 - -### 漏洞修复 -* fix 修复swagger开关无法控制关闭问题 -* fix maven install 异常 - -## v2.1.0 - 2021-5-17 - -### 功能更新 -* update knife4j升级3.0.2 -* update 增强分页工具兼容性 -* update 通用Service接口 增加自定义vo转换函数 - -### 移除功能 -* remove 移除ruoyi自带服务监控(Admin已全部包含) - -## v2.0.0 - 2021-5-15 - -### 依赖升级 -* springboot 升级 2.3.10 依赖全面升级适配 - -### 新功能 -* add 增加分页工具 -* add 增加 增强Mapper 与 增强Service 重写业务适配 -* add 代码生成器 增加校验注解 - -### 功能更新 -* update 代码生成器修改为MP分页 -* update 使用 MP 分页工具 重构业务 -* update 重写文档介绍 - -### 移除功能 -* remove 移除 pagehelper 分页工具 - -### 漏洞修复 -* fix 修复代码生成 数据权限问题 - -## v1.0.2 - 2021-5-13 - -### 功能更新 -* update 更新整合打包文档 重新排版 - -### 漏洞修复 -* fix vue与boot整合打包与admin页面路由冲突 - -## v1.0.1 - 2021-5-11 - -### 依赖更新 -* update 更新banner -* update 配置转移到 yml 文件 统一管理 -* update 上传媒体类型添加视频格式 -* update 树级结构更新子节点使用replaceFirst -* update 删除操作日志记录日志 - -### 漏洞修复 -* fix 修正导入表权限标识 -* fix 文件上传时报错 - -## v1.0.0 - 2021-5-10 -* 基于 ruoyi-vue 3.4.0 发布 v1.0.0 稳定版 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/architecture_diagram.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/architecture_diagram.md deleted file mode 100644 index ef65fef3..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/architecture_diagram.md +++ /dev/null @@ -1,3 +0,0 @@ -# 软件架构图 -- - - -![输入图片说明](https://foruda.gitee.com/images/1722569328704494018/e84442b6_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/doc.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/doc.md deleted file mode 100644 index 06e62dc5..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/doc.md +++ /dev/null @@ -1,89 +0,0 @@ -# 接口文档 -- - - -## 版本 >= `4.3.0` -## 说明 - -由于 `springfox` 与 `knife4j` 均停止维护 bug众多
-故从 `4.3.0` 开始 迁移到 `springdoc` 框架
-基于 `javadoc` 无注解零入侵生成规范的 `openapi` 结构体
-由于框架自带文档UI功能单一扩展性差 故移除自带UI 建议使用外置文档工具 - -## 文档工具使用 -由于框架采用 `openapi` 行业规范 故市面上大部分的框架均支持 可自行选择
-例如: `apifox` `apipost` `postman` `torna` `knife4j` 等 根据对应工具的文档接入即可 - -## Swagger升级SpringDoc指南 - -常见功能如下 其他功能自行挖掘
-**注意: `javadoc` 只能替换基础功能 特殊功能还需要使用注解实现** - -| swagger | springdoc | javadoc | -|----------------------------------|---------------------------------|--------------------| -| @Api(name = "xxx") | @Tag(name = "xxx") | java类注释第一行 | -| @Api(description= "xxx") | @Tag(description= "xxx") | java类注释 | -| @ApiOperation | @Operation | java方法注释 | -| @ApiIgnore | @Hidden | 无 | -| @ApiParam | @Parameter | java方法@param参数注释 | -| @ApiImplicitParam | @Parameter | java方法@param参数注释 | -| @ApiImplicitParams | @Parameters | 多个@param参数注释 | -| @ApiModel | @Schema | java实体类注释 | -| @ApiModelProperty | @Schema | java属性注释 | -| @ApiModelProperty(hidden = true) | @Schema(accessMode = READ_ONLY) | 无 | -| @ApiResponse | @ApiResponse | java方法@return返回值注释 | - -# 建议使用 `Apifox`(常见问题有其他对接方式) - -官网连接: [https://www.apifox.cn/](https://www.apifox.cn/)
-视频教程: [springdoc与apifox配合使用](https://www.bilibili.com/video/BV1mr4y1j75M?p=8&vd_source=8f52c77be3233dbdd1c5e332d4d45bfb) - -![输入图片说明](https://foruda.gitee.com/images/1678976476639902970/f1617b40_1766278.png "屏幕截图") - -支持 文档编写 接口调试 Mock 接口压测 自动化测试 等一系列功能 - -### 接入框架 - -> 1.下载或使用web在线版 创建一个自己的项目 - -![输入图片说明](https://foruda.gitee.com/images/1678976502850663851/7bbd8728_1766278.png "屏幕截图") - -> 2.进入项目 选择项目设置 找到自动同步 - -![输入图片说明](https://foruda.gitee.com/images/1678976508918240326/6a4a61a8_1766278.png "屏幕截图") - -> 3.根据项目内所有文档组完成所有数据源创建(拉取后端`openapi`结构体)
-数据源URL格式 `http://后端ip:端口/v3/api-docs/组名`
-项目内所需:
-`http://localhost:8080/v3/api-docs/1.演示模块`
-`http://localhost:8080/v3/api-docs/2.通用模块`
-`http://localhost:8080/v3/api-docs/3.系统模块`
-`http://localhost:8080/v3/api-docs/4.代码生成模块`
-也可不分组统一导入: `http://localhost:8080/v3/api-docs`
- -![输入图片说明](https://foruda.gitee.com/images/1678976514385097727/05c7e0a6_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1686626073422245046/df4b6a54_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678976527495742967/79836e7f_1766278.png "屏幕截图") - -> 4.选择 接口管理 项目概览 点击立即导入 并等待导入完成
-后续会根据策略每3个小时自动导入一次
-每次重新进入apifox也会自动同步一次
-后端有改动也可以手动点击导入
- -![输入图片说明](https://foruda.gitee.com/images/1678976534677430926/f32c64c5_1766278.png "屏幕截图") - -> 5.(注意版本号)设置鉴权 选择接口管理 项目概览 找到Auth 按照如下配置 - -**版本号: >= 5.X** - -![输入图片说明](https://foruda.gitee.com/images/1690966897370710566/6a688aea_1766278.png "屏幕截图") - -**版本号: 4.X** - -![输入图片说明](https://foruda.gitee.com/images/1678976539608390075/77246461_1766278.png "屏幕截图") - -> key对应项目配置 默认为 `Authorization` - -![输入图片说明](https://foruda.gitee.com/images/1678976544342001474/c2ff85d3_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678976549237304743/bcdfadda_1766278.png "屏幕截图") - - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/i18n.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/i18n.md deleted file mode 100644 index 4052979a..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/i18n.md +++ /dev/null @@ -1,33 +0,0 @@ -# 国际化方案 -- - - -* 前端国际化参考 [ruoyi前端国际化文档](http://doc.ruoyi.vip/ruoyi-vue/document/htsc.html#前端国际化流程)
-* 后端国际化(2.7.0 以上增加) -* 3.4.0 以上支持 `Validator` 校验框架 -* 参考 `demo` 模块 `TestI18nController` 国际化演示案例 - 在 `Header` 请求头 增加上下文语言参数 `content-language` 参数需与国际化配置文件后缀对应 - 如 `zh_CN` `en_US` 等
- -![输入图片说明](https://foruda.gitee.com/images/1678976722892396585/60917594_1766278.png "屏幕截图") - -## 获取 `code` 对应国际化内容 - -![输入图片说明](https://foruda.gitee.com/images/1678976728533100954/0ab8e36a_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976733019209506/a16574d6_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976738409745057/a073b425_1766278.png "屏幕截图") - -## 使用 `Validator` 框架校验 `controller` 参数返回国际化 - -`controller` 校验接口参数 需要在类增加 `@Validated` 注解
-![输入图片说明](https://foruda.gitee.com/images/1678976741834729507/6c19b9cc_1766278.png "屏幕截图")
-参数对应校验注解 使用 `{code}` 形式标注使用国际化处理
-![输入图片说明](https://foruda.gitee.com/images/1678976746093285542/ad0989db_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976750822808564/56bd60d7_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976754755107198/b89bf173_1766278.png "屏幕截图") - -## 使用 `Validator` 框架校验 `Bean` 返回国际化 - -`Bean` 校验需要在接口校验 `Bean` 参数使用 `@Validated` 注解
-![输入图片说明](https://foruda.gitee.com/images/1678976761015767874/729da3bc_1766278.png "屏幕截图")
-`Bean` 内属性校验注解 使用 `{code}` 形式标注使用国际化处理
-![输入图片说明](https://foruda.gitee.com/images/1678976765122587920/0b1027af_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976769965314387/0c416ede_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/new_module.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/new_module.md deleted file mode 100644 index 87b992aa..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/new_module.md +++ /dev/null @@ -1,15 +0,0 @@ -# 关于如何创建新模块 -- - - -* 参考ruoyi-demo模块 -* 需要改动: 父pom 与 admin模块pom - -![输入图片说明](https://foruda.gitee.com/images/1719814203987728184/6940ecf8_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1719814254968294155/70fe6d77_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1719814280768521481/14fb11cd_1766278.png "屏幕截图") - -### 注意事项 -如果是两个不同包名的模块 需要修改如下配置 - -![输入图片说明](https://foruda.gitee.com/images/1719813861680271619/82435586_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1692006501957936219/059f8526_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/update_package_name.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/update_package_name.md deleted file mode 100644 index 2dcab848..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/update_package_name.md +++ /dev/null @@ -1,33 +0,0 @@ -# 关于修改包名 -- - - - -**注意: 老包名为 com.ruoyi** - -## 1.随便找个地方新建 org.dromara 包 -![输入图片说明](https://foruda.gitee.com/images/1708491220807198688/b95c0c34_1766278.png "屏幕截图") - -## 2.在包上右键选择 refactor -> rename 选择 All Directories -![输入图片说明](https://foruda.gitee.com/images/1683276891079076405/79808b22_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1708491697128844860/1e87ad39_1766278.png "屏幕截图") - -**因为dromara组织下有很多依赖导致idea无法识别完整包名** -
-![输入图片说明](https://foruda.gitee.com/images/1708490576909691001/692e5b37_1766278.png "屏幕截图") - -**需要先将dromara修改为 例如: ruoyi 然后重复上述步骤 这样就可以整包修改了** -
-![输入图片说明](https://foruda.gitee.com/images/1708490906933084793/ff104cd7_1766278.png "屏幕截图") - -## 3.使用IDEA全局替换 org.dromara 替换为 com.xxx - -![输入图片说明](https://foruda.gitee.com/images/1708491055347995519/dedda0d1_1766278.png "屏幕截图") - -**注意: 由于dromara组织下项目很多 非本框架的依赖模块 请勿修改 例如上图中的 org.dromara.sms4j** - -## 4.如有需要 将所有模块名逐一修改即可 - -## 5.修改完成后需查看所有common包下模块spi文件是否修改正确 - -**老版本idea或者未按照教程修改包名可能导致文件丢包问题** - -![输入图片说明](https://foruda.gitee.com/images/1708491365841192006/8bc337c2_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/update_url.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/update_url.md deleted file mode 100644 index 0f5f106b..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/association/update_url.md +++ /dev/null @@ -1,26 +0,0 @@ -# 修改应用路径 -- - - -## 修改后端路径 - -更改 `application.yml` 的 `server.servlet.context-path` 即可更改后端容器路径 - -![输入图片说明](https://foruda.gitee.com/images/1724316662536650741/41d534b1_1766278.png "屏幕截图") - -与之对应前端 `dev`环境 需更改 `vite.config.ts` 的代理路径 - -![输入图片说明](https://foruda.gitee.com/images/1724316844091667249/9b0badc5_1766278.png "屏幕截图") - -`prod` 生产环境需修改 `nginx.conf` 后端代理路径 - -![输入图片说明](https://foruda.gitee.com/images/1661823876773225117/f1f912a9_1766278.png "屏幕截图") - -## 修改前端路径 -### 注意: 3.4.0 提供便捷更改方式 -直接修改对应环境的 `.env.环境` 文件内的 `VITE_APP_CONTEXT_PATH` 应用访问路径即可 - -![输入图片说明](https://foruda.gitee.com/images/1661824572484410642/14265f05_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1724317049535973756/0a2cc43b_1766278.png "屏幕截图") - -生产环境 `nginx.conf` 与之对应修改即可
-**注意: 文件真实目录为 `/usr/share/nginx/html/admin/index.html` 此功能一般为多项目部署需要 故会增加一层目录 如不需要可以自行修改**
-![输入图片说明](https://foruda.gitee.com/images/1678976662194341301/2720b7e9_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/client.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/client.md deleted file mode 100644 index bec103d8..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/client.md +++ /dev/null @@ -1,85 +0,0 @@ -# 客户端管理功能 -- - - -## 版本 >= 5.X - -## 客户端管理页面 - -![输入图片说明](https://foruda.gitee.com/images/1690961819029076660/c44374ac_4959041.png "屏幕截图") - -### 客户端管理字段说明 -| 字段名称 | 取值说明 | 注意事项 | -|----------------|----------------------------|--------------------------------| -| 客户端id | 由后端生成,用于前端登录校验以及接口数据加密 | 无法修改,不要删除默认数据,否则会报错 | -| 客户端key | 前端自定义 | 无法修改,不要删除默认数据,否则会报错 | -| 客户端秘钥 | 前端自定义 | 无法修改,不要删除默认数据,否则会报错 | -| 授权类型 | 密码认证、短信认证、邮件认证、小程序认证、第三方认证 | 根据授权类型判断当前客户端是否支持该登录方式 | -| 设备类型 | PC端、APP端 | | -| Token活跃超时时间 | 自定义 | 指定时间无操作则过期(单位:秒),默认30分钟(1800秒) | -| Token固定超时时间 | 自定义 | 指定时间必定过期(单位:秒),默认七天(604800秒) | - -### 前后端使用新的客户端id - -步骤如下: -1. 前端管理页面生成新的客户端id。 -2. 将新的客户端id复制到前端配置文件。 - -![输入图片说明](https://foruda.gitee.com/images/1690962894318847386/133d2f90_4959041.png "屏幕截图") - -## 新增自定义客户端 - -### 步骤一:新增客户端数据(例如增加小程序端) - -![输入图片说明](https://foruda.gitee.com/images/1690965463070099188/baeb4441_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1690965508836621042/df06248f_4959041.png "屏幕截图") - -### 步骤二:配置前端请求头信息 - -需要在全局请求头 header 中增加 cientid
-确保客户端所有请求都携带此id 可参考项目 `request.ts` - -![输入图片说明](https://foruda.gitee.com/images/1690965768235114596/980b88d2_4959041.png "屏幕截图") - -`VITE_APP_CLIENT_ID` 即配置文件中的客户端id。 - -**重点:不同客户端登录获取到的token不同与其他端不互通(例如: app登录获取到的token无法用于pc端接口查询)** - -## 新增自定义登录方式授权类型 - -**重点说明: 不要单独增加登录接口 系统全局统一只有一个登录接口 只需增加不同的鉴权方式即可** - -如何调试使用登录看这里 -> [关于登录调试步骤](/questions/login_step.md) - -### 步骤一:新增字典数据 - -![输入图片说明](https://foruda.gitee.com/images/1690968849418013624/3b28417e_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1690968865819397010/64529fad_4959041.png "屏幕截图") - -### 步骤二:新增/修改客户端数据 - -### 步骤三:后端新增认证策略 - -新增策略实现类实现 `IAuthStrategy` 接口。
- -![输入图片说明](https://foruda.gitee.com/images/1690972828588111954/7614a4c5_4959041.png "屏幕截图") - -参照已有策略实现类实现自定义参数校验登录方法逻辑。
- -![输入图片说明](https://foruda.gitee.com/images/1718951146945578143/789c80e4_1766278.png "屏幕截图") - -**注意修改 `@Service` 名称保证规范性** - -![输入图片说明](https://foruda.gitee.com/images/1718951179571300385/8db730b9_1766278.png "屏幕截图") - -`LoginBody` 校验参数(可自定义)
- -![输入图片说明](https://foruda.gitee.com/images/1718951237123374392/f7840db2_1766278.png "屏幕截图") - -例如 扩展小程序登录参数 只需要继承 `LoginBody
- -![输入图片说明](https://foruda.gitee.com/images/1718951283931895761/e6348be5_1766278.png "屏幕截图")` - -校验分组(可自定义)
- -![输入图片说明](https://foruda.gitee.com/images/1718951343601334215/8ef404b4_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/code_generate.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/code_generate.md deleted file mode 100644 index e20e09ac..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/code_generate.md +++ /dev/null @@ -1,85 +0,0 @@ -# 代码生成 -- - - -## 功能介绍 - -### 数据源配置 - -![输入图片说明](https://foruda.gitee.com/images/1678976867341325193/a2be0608_1766278.png "屏幕截图") - -**>= 4.1.0版本 项目适配多种类型数据库 可以在代码生成页面切换**
- -> 填写对应的数据源名称 点击搜索按钮 即可切换到对应的数据源
- -![输入图片说明](https://foruda.gitee.com/images/1678976876081856486/4ef4841c_1766278.png "屏幕截图") - -**>= 5.2.2版本 项目支持100+种数据库适配 在代码生成模块增加对应的数据库依赖即可**
- -![输入图片说明](https://foruda.gitee.com/images/1722396530340741054/3914eb72_1766278.png "屏幕截图") - -### 导入数据表 - -> 点击导入按钮 会加载系统数据库所有的表
- -![输入图片说明](https://foruda.gitee.com/images/1678976880393939803/3ecf1dcc_1766278.png "屏幕截图") - -> 选择需要的表 点击确定即可
- -![输入图片说明](https://foruda.gitee.com/images/1678976885370716109/4834faa5_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976891856866728/853420d9_1766278.png "屏幕截图") - -### 编辑表生成结构 - -> 点击表对应的编辑按钮
- -![输入图片说明](https://foruda.gitee.com/images/1678976899111822310/aeaa33f9_1766278.png "屏幕截图") - -> 更改要生成表的数据
- -![输入图片说明](https://foruda.gitee.com/images/1678976903345795925/4326f6ee_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678976908897387614/4cdf939b_1766278.png "屏幕截图") - -### 生成条件影响 - -![输入图片说明](https://foruda.gitee.com/images/1678976913809284051/24da09b0_1766278.png "屏幕截图") - - -* `插入` `编辑` 影响生成 BO 类 与 前端添加编辑页面 是否有该字段 -* `列表` 影响生成 VO 类 与 前端列表页面展示 是否有该字段 -* `查询` 影响 前端页面是否有该字段的搜索框 与 后端代码是否生成对应的查询条件 -* `查询方式` 影响生成查询条件的类型 -* `必填` 影响 BO 类 与 页面是否强制校验 -* `显示类型` 影响生成页面使用何种展示组件 -* `字典类型` 影响页面是否生成与字典的关联 - -### 树表配置 - -> 编辑表生成信息 生成模板为 `树表` 填写对应数据即可
- -![输入图片说明](https://foruda.gitee.com/images/1678976917918548901/f5886c5c_1766278.png "屏幕截图") - -### 主子表说明 - -框架不支持也不推荐使用主子表
-原因一般业务场景 基本都是一对N表 多表关联场景
-还有一些 主 => 子 <= 主 场景 需求很复杂 很少有单纯主子表场景出现
-另外主子表关联 很容易出现 笛卡尔积 或者数据错乱等问题 需要自行sql调优场景
-所以建议大家都按照 单表生成 自行编写业务逻辑 - -### 预览功能 - -> 配置好生成信息后 可以点击预览按钮
- -![输入图片说明](https://foruda.gitee.com/images/1678976924411765532/2e9747df_1766278.png "屏幕截图") - -> 系统会根据已经配置好的数据 生成对应的代码预览
-> 可以再此处观察代码的生成结构和数据是否正确等
- -![输入图片说明](https://foruda.gitee.com/images/1678976945982406065/ca7383bb_1766278.png "屏幕截图") - - -### 代码结构同步 - -> 实际开发中 难免会有表结构更改的需求
-> 这时可以使用 同步功能 点击同步按钮 即可与实时数据库表进行字段同步
- -![输入图片说明](https://foruda.gitee.com/images/1678976952919156537/3c47c078_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/export.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/export.md deleted file mode 100644 index 1dd01c17..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/export.md +++ /dev/null @@ -1,249 +0,0 @@ -# 导出功能 - -- - - - -在本框架中引入了 `Easy Excel` 依赖(对 `Apache POI`进行了封装以及扩展),可以对数据进行导出操作(即写 Excel)。 - -[EasyExcel 文档地址](https://easyexcel.opensource.alibaba.com/) - -## 导出功能使用流程说明 - -### 步骤一:定义导出实体对象 - -以框架中 `SysUserExportVo` 为例: - -```Java - /** - * 用户ID - */ - @ExcelProperty(value = "用户序号") - private Long userId; - - // ....................... - - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_user_sex") - private String sex; - - /** - * 帐号状态(0正常 1停用) - */ - @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_normal_disable") - private String status; -``` - -> 说明:
-> 1. 使用 `@ExcelProperty` 注解标注需要导出的属性。 -> 2. 注解 `@ExcelProperty` 中 `value` 属性代表表格头部标题字段,`converter` 代表使用的转换器,后面会详细说明。 -> 3. 注解 `@ExcelDictFormat` 为自定义注解,与自定义转换器结合使用,同样在后面进行详细说明。 - -### 步骤二:使用导出方法 - -以框架中 `SysUserController#export` 方法为例: - -```Java - /** - * 导出用户列表 - */ - @PostMapping("/export") - public void export(SysUserBo user, HttpServletResponse response) { - // 根据参数查询导出的用户列表数据 - List list = userService.selectUserList(user); - // 将列表转换为导出对象列表 - List listVo = MapstructUtils.convert(list, SysUserExportVo.class); - // 导出方法 - ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response); - } -``` - -> 说明:
-> 使用 `ExcelUtil.exportExcel` 方法完成导出功能,上述 Demo 传入参数分别是:导出对象集合,Excel sheet 表名称,导出对象类型,response。 - -## 框架工具使用说明 - -### 1:字典转换器 - -字典转换器 `ExcelDictConvert` 与自定义注解 `@ExcelDictFormat` 结合使用,标注在需要转换的属性上。 - -使用方式一: - -```Java - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_user_sex") - private String sex; -``` - -使用方式二: - -```Java - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(readConverterExp="0=男,1=女,2=未知", separator=",") - private String sex; -``` - -`@ExcelDictFormat` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|------------------|--------|-----|-----------------------------------| -| dictType | String | "" | 字典的type值 (如: sys_user_sex) | -| readConverterExp | String | "" | 读取内容转表达式 (如: 0=男,1=女,2=未知) | -| separator | String | "," | 与 readConverterExp 属性结合使用,表达式的分隔符 | - -### 2:枚举转换器 - -字典转换器 `ExcelEnumConvert` 与自定义注解 `@ExcelEnumFormat` 结合使用,标注在需要转换的属性上。 - -使用方式: - -```Java - /** - * 用户类型 - *

- * 使用ExcelEnumFormat注解需要进行下拉选的部分 - */ - @ExcelProperty(value = "用户类型", index = 1, converter = ExcelEnumConvert.class) - @ExcelEnumFormat(enumClass = UserStatus.class, textField = "info") - private String userStatus; -``` - -`@ExcelEnumFormat` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|-----------|------------|------|------------------------------| -| enumClass | Enum Class | - | 字典枚举类型 | -| codeField | String | code | 字典枚举类中对应的 code 属性名称,默认为 code | -| textField | String | text | 字典枚举类中对应的 text 属性名称,默认为 text | - -### 3:合并单元格 - -`@CellMerge` 注解用于合并相同的列数据,需要结合 `CellMergeStrategy` 策略使用,标注在需要转换的属性上。 - -使用方式: - -步骤一:在属性标注 `@CellMerge` 注解: -```Java - /** - * 部门id - */ - @CellMerge - @ExcelProperty(value = "部门id") - private Long deptId; -``` - -`@CellMerge` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|---------|----------|-----|------------------------------| -| index | int | -1 | 合并列的下标,建议使用默认值 | -| mergeBy | String[] | {} | 合并需要依赖的其他字段名称(基于这个字段内容做合并条件) | - -步骤二:导出方法开启合并: -```Java - /** - * 导出测试单表列表 - */ - @PostMapping("/export") - public void export(@Validated TestDemoBo bo, HttpServletResponse response) { - List list = testDemoService.queryList(bo); - // 参数 true 表示开启合并单元格策略 - ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, true, response); - } -``` -![输入图片说明](https://foruda.gitee.com/images/1700128921644543994/e8d4704f_1766278.png "屏幕截图") - -### 4:复杂 Excel 导出示例 -`TestExcelController` 提供了几种导出示例,如果需要可以参照相应方法进行导出。 - -#### 4.1:单列表多数据导出(模板导出) - -模板内容: - -![输入图片说明](https://foruda.gitee.com/images/1700124852002972562/d9f57a8c_4959041.png "屏幕截图") - -模板位置:`ruoyi-modules/ruoyi-demo/src/main/resources/excel/` - -导出示例代码:参考 demo 模块 `TestExcelController` 模板写法请查看 `EasyExcel` 文档 - -导出结果: - -![输入图片说明](https://foruda.gitee.com/images/1700124885532359879/0d011d05_4959041.png "屏幕截图") - -#### 4.2:多列表多数据导出(模板导出) - -模板内容: - -![输入图片说明](https://foruda.gitee.com/images/1700125025931981176/105dbaaa_4959041.png "屏幕截图") - -模板位置:`ruoyi-modules/ruoyi-demo/src/main/resources/excel/` - -导出示例代码:参考 demo 模块 `TestExcelController` 模板写法请查看 `EasyExcel` 文档 - -导出结果: - -![输入图片说明](https://foruda.gitee.com/images/1700125054011300002/71869c1d_4959041.png "屏幕截图") - -#### 4.3:导出下拉框 - -`ExcelDictFormat` 注解指定的字典项默认都会转换成下拉框 - -自定义导出省市区下拉框示例代码:参考 demo 模块 `TestExcelController` - -导出结果: - -![输入图片说明](https://foruda.gitee.com/images/1700125265411678973/7f767719_4959041.png "屏幕截图") - -## Easy Excel 常用注解 - -`Easy Excel` 提供了丰富的注解可以对导出对象进行定制化操作,这里的注解说明针对的是原生注解,自定义注解会结合转换器一起进行说明。 - -| 类型 | 注解名称 | 使用举例 | 说明 | -|-------|-------------------------|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| -| 格式化注解 | @DateTimeFormat | @DateTimeFormat(value=格式化值) | 对字符串进行日期格式化 (参照 `java.text.SimpleDateFormat` 书写即可) | -| 格式化注解 | @NumberFormat | @NumberFormat(value=格式化值, roundingMode=舍入模式) | 对字符串进行数值格式化 (参照 `java.text.DecimalFormat` 书写即可, `roundingMode` 默认 `RoundingMode.HALF_UP`) | -| 样式注解 | @ColumnWidth | @ColumnWidth(value=值) | 设置列宽 | -| 样式注解 | @ContentFontStyle | @ContentFontStyle(color=颜色) | 可以设置字体类型,颜色,粗细,是否斜体,下划线等,具体可查看注解 `@ContentFontStyle` | -| 样式注解 | @ContentLoopMerge | @ContentLoopMerge(eachRow=行值, columnExtend=列值) | 设置循环合并的区域 | -| 样式注解 | @ContentRowHeight | @ContentRowHeight(value=值) | 设置内容行高 | -| 样式注解 | @ContentStyle | - | 设置单元格样式,具体可查看注解 `@ContentStyle` | -| 样式注解 | @HeadFontStyle | @HeadFontStyle(color=颜色) | 设置表头字体格式,类似 `@ContentFontStyle`,具体可查看注解 `@HeadFontStyle` | -| 样式注解 | @HeadRowHeight | @HeadRowHeight(value=值) | 设置表头行高 | -| 样式注解 | @HeadStyle | - | 设置表头样式,具体可查看注解 `@HeadStyle` | -| 样式注解 | @OnceAbsoluteMerge | @OnceAbsoluteMerge(firstRowIndex=开始行下标, lastRowIndex=结束行下标, firstColumnIndex=开始列下标, lastColumnIndex=结束列下标) | 根据设置值合并单元格 | -| 属性注解 | @ExcelIgnore | @ExcelIgnore | 导出忽略该字段 | -| 属性注解 | @ExcelIgnoreUnannotated | @ExcelIgnoreUnannotated | 默认不管加不加 `@ExcelProperty` 的注解的所有字段都会参与读写,加了 `@ExcelIgnoreUnannotated` 注解以后,不加 `@ExcelProperty` 注解的字段就不会参与 | -| 属性注解 | @ExcelProperty | @ExcelProperty(value=值, order=排序值, index=下标, converter=转换器) | 默认按照对象属性顺序导出,如果设置了 `order` 以及 `index`,优先级 `index` > `order` > 默认;converter 可以自定义 | - -## 扩展说明 - -### 自定义转换器实现 - -由于业务需要,原生注解不一定能够符合需要,因而衍生出了自定义转换器。能够实现定制化的内容转换需要。 -以下以框架中的字典转换器 `ExcelDictConvert` 为例进行说明。 - -字典转换器 `ExcelDictConvert`,字典转换器使用了自定义注解 `@ExcelDictFormat` 配合使用。 - -_**注:自定义转换器并非一定需要自定义注解,也可以针对已有的注解进行自定义转换实现。**_ - -#### 实现方式 - -自定义转换器需要实现 `com.alibaba.excel.converters.Converter` 接口,实现接口中的方法。 - -![输入图片说明](https://foruda.gitee.com/images/1700104014304819918/33eb0c42_4959041.png "屏幕截图") - -转换方法 `ExcelDictConvert#convertToExcelData` : - -![输入图片说明](https://foruda.gitee.com/images/1700104426131801297/72931ef0_4959041.png "屏幕截图") - -## 更多功能 - -更多导出功能使用可以参照 `Easy Excel` [官方文档](https://easyexcel.opensource.alibaba.com/docs/current/api/write)。 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/import.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/import.md deleted file mode 100644 index f1bbca7d..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/import.md +++ /dev/null @@ -1,202 +0,0 @@ -# 导入功能 -- - - - -在本框架中引入了 `Easy Excel` 依赖(对 `Apache POI`进行了封装以及扩展),可以对数据进行导入操作(即读 Excel)。 - -## 导入功能使用流程说明 - -### 步骤一:定义导入实体对象 - -以框架中 `SysUserImportVo` 为例: - -```java - /** - * 用户ID - */ - @ExcelProperty(value = "用户序号") - private Long userId; - - // ....................... - - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_user_sex") - private String sex; - - /** - * 帐号状态(0正常 1停用) - */ - @ExcelProperty(value = "帐号状态", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_normal_disable") - private String status; -``` - -> 说明:
-> 1. 使用 `@ExcelProperty` 注解标注需要导入的属性。 -> 2. 注解 `@ExcelProperty` 中 `value` 属性代表表格头部标题字段,`converter` 代表使用的转换器,后面会详细说明。 -> 3. 注解 `@ExcelDictFormat` 为自定义注解,与自定义转换器结合使用,同样在后面进行详细说明。 -> 4. 对象禁止使用链式注解 `@Accessors(chain = true)`,会找不到set方法。 - -### 步骤二:使用导入方法 - -以框架中 `SysUserController#importData` 方法为例: - -```Java - /** - * 导入数据 - * - * @param file 导入文件 - * @param updateSupport 是否更新已存在数据 - */ - @Log(title = "用户管理", businessType = BusinessType.IMPORT) - @SaCheckPermission("system:user:import") - @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public R importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception { - // 导入方法 - ExcelResult result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport)); - return R.ok(result.getAnalysis()); - } -``` -> 说明:
-> 使用 `ExcelUtil.importExcel` 方法完成导出功能,上述 Demo 传入参数分别是:导入文件流,导入对象类型,导入监听器 `SysUserImportListener`。 - -## 框架工具使用说明 - -### 1:字典转换器 - -字典转换器 `ExcelDictConvert` 与自定义注解 `@ExcelDictFormat` 结合使用,标注在需要转换的属性上。 - -使用方式一: - -```Java - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "sys_user_sex") - private String sex; -``` - -使用方式二: - -```Java - /** - * 用户性别 - */ - @ExcelProperty(value = "用户性别", converter = ExcelDictConvert.class) - @ExcelDictFormat(readConverterExp="0=男,1=女,2=未知", separator=",") - private String sex; -``` - -`@ExcelDictFormat` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|------------------|--------|-----|-----------------------------------| -| dictType | String | "" | 字典的type值 (如: sys_user_sex) | -| readConverterExp | String | "" | 读取内容转表达式 (如: 0=男,1=女,2=未知) | -| separator | String | "," | 与 readConverterExp 属性结合使用,表达式的分隔符 | - -### 2:枚举转换器 - -字典转换器 `ExcelEnumConvert` 与自定义注解 `@ExcelEnumFormat` 结合使用,标注在需要转换的属性上。 - -使用方式: - -```Java - /** - * 用户类型 - *

- * 使用ExcelEnumFormat注解需要进行下拉选的部分 - */ - @ExcelProperty(value = "用户类型", index = 1, converter = ExcelEnumConvert.class) - @ExcelEnumFormat(enumClass = UserStatus.class, textField = "info") - private String userStatus; -``` - -`@ExcelEnumFormat` 注解属性说明: - -| 属性名称 | 属性类型 | 默认值 | 说明 | -|-----------|------------|------|------------------------------| -| enumClass | Enum Class | - | 字典枚举类型 | -| codeField | String | code | 字典枚举类中对应的 code 属性名称,默认为 code | -| textField | String | text | 字典枚举类中对应的 text 属性名称,默认为 text | - - -### 3:导入监听器 - -#### 3.1:ExcelListener 监听器接口 - -`ExcelListener` 扩展了 `ReadListener` 接口,增加了获取结果方法。 - -![输入图片说明](https://foruda.gitee.com/images/1700181723794469524/99bf83c9_4959041.png "屏幕截图") - -#### 3.2:DefaultExcelListener 默认监听器 - -`DefaultExcelListener` 默认监听器在读 Excel 时调用,主要对数据进行校验、解析、异常处理、返回结果等。导入操作时如果没有特别指定则使用该监听器。 - -#### 3.3:SysUserImportListener 用户导入监听器 - -`SysUserImportListener` 用户导入监听器是在用户导入时调用的监听器。 - -该监听器重写了 `invoke` 反射接口,对导入的用户数据进行了校验;重写了 `getExcelResult` 获取结果接口,返回结果数据。 - -#### 3.4:ExportDemoListener 带下拉框的导入监听器 - -`ExportDemoListener` 是对带有下拉框的 Excel 进行处理的导入监听器。 - -## Easy Excel 常用注解 - -`Easy Excel` 提供了丰富的注解可以对导出对象进行定制化操作,这里的注解说明针对的是原生注解。 - -| 类型 | 注解名称 | 使用举例 | 说明 | -|-------|-------------------------|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------| -| 格式化注解 | @DateTimeFormat | @DateTimeFormat(value=格式化值) | 对字符串进行日期格式化 (参照 `java.text.SimpleDateFormat` 书写即可) | -| 格式化注解 | @NumberFormat | @NumberFormat(value=格式化值, roundingMode=舍入模式) | 对字符串进行数值格式化 (参照 `java.text.DecimalFormat` 书写即可, `roundingMode` 默认 `RoundingMode.HALF_UP`) | -| 属性注解 | @ExcelIgnore | @ExcelIgnore | 导出忽略该字段 | -| 属性注解 | @ExcelIgnoreUnannotated | @ExcelIgnoreUnannotated | 默认不管加不加 `@ExcelProperty` 的注解的所有字段都会参与读写,加了 `@ExcelIgnoreUnannotated` 注解以后,不加 `@ExcelProperty` 注解的字段就不会参与 | -| 属性注解 | @ExcelProperty | @ExcelProperty(value=值, order=排序值, index=下标, converter=转换器) | 默认按照对象属性顺序导出,如果设置了 `order` 以及 `index`,优先级 `index` > `order` > 默认;converter 可以自定义 | - -## 扩展使用 - -### 扩展一:自定义转换器实现 - -由于业务需要,原生注解不一定能够符合需要,因而衍生出了自定义转换器。能够实现定制化的内容转换需要。 -以下以框架中的字典转换器 `ExcelDictConvert` 为例进行说明。 - -字典转换器 `ExcelDictConvert`,字典转换器使用了自定义注解 `@ExcelDictFormat` 配合使用。 - -_**注:自定义转换器并非一定需要自定义注解,也可以针对已有的注解进行自定义转换实现。**_ - -#### 实现方式 - -自定义转换器需要实现 `com.alibaba.excel.converters.Converter` 接口,实现接口中的方法。 - -![输入图片说明](https://foruda.gitee.com/images/1700104014304819918/33eb0c42_4959041.png "屏幕截图") - -转换方法 `ExcelDictConvert#convertToJavaData` : - -![输入图片说明](https://foruda.gitee.com/images/1700182975516396213/d3c020f9_4959041.png "屏幕截图") - -### 扩展二:自定义监听器实现 - -自定义监听器主要用于在读取解析 Excel 数据时进行自定义操作。 -以下以框架中的用户导入监听器 `SysUserImportListener` 为例进行说明。 - -#### 实现方式 -1. 继承分析事件监听器 `AnalysisEventListener` 以及实现 Excel 监听器 `ExcelListener`。 - -![输入图片说明](https://foruda.gitee.com/images/1700184652693497753/09333dac_4959041.png "屏幕截图") - -2. 显示使用构造函数,否则将导致空指针。 - -![输入图片说明](https://foruda.gitee.com/images/1700184759075616584/cf05b0ed_4959041.png "屏幕截图") - -3. 实现 `invoke` 方法,对数据进行解析操作,可以在此方法对数据进行合法性判断。 - -4. 实现 `getExcelResult` 方法,对结果进行操作,例如返回成功、失败的统计数据。 - -## 更多功能 - -更多导入功能使用可以参照 `Easy Excel` [官方文档](https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read)。 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/interface_release.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/interface_release.md deleted file mode 100644 index a3e6e6b6..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/interface_release.md +++ /dev/null @@ -1,25 +0,0 @@ -# 接口放行 -- - - -## 使用方式 - -在配置文件填写路径放行
-![输入图片说明](https://foruda.gitee.com/images/1678979039071447990/8b272aba_1766278.png "屏幕截图") - -### 注解放行 -版本 4.3.1 以上 建议使用 `@SaIgnore` 忽略注解
- -支持在类或方法上放行
-**注意: 动态路径会解析成通配符 请设计好接口路径避免问题** - -**例如: `/get/{userId}` 会解析成 `/get/*`**
-![输入图片说明](https://foruda.gitee.com/images/1666595109409104199/5b7d75c7_1766278.png "屏幕截图") - -## 注意事项 - -接口放行后不需要token即可访问
-但是没有token也就无法获取用户信息与鉴权 - -### 解决方案 -删除接口上的鉴权注解
-删除接口内获取用户信息功能
-删除数据库实体类 自动注入 `createBy` `updateBy` 因为会获取用户数据 \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/oss.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/oss.md deleted file mode 100644 index 47484ab2..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/oss.md +++ /dev/null @@ -1,124 +0,0 @@ -# 关于OSS模块使用 -- - - -## 重点注意事项 - -`桶/存储区域` 系统会根据配置自行创建分配权限
-~~如手动配置需要设置 `公有读` 权限 否则文件无法访问~~(`aliyun` 还需开通跨域配置)
-4.4.0 版本支持配置`公有/私有`权限(`aliyun` 还需开通跨域配置)
-访问站点 后严禁携带其他 `url` 例如: `/`, `/ruoyi` 等
-**阿里云与腾讯云SDK访问站点中不能包含桶名 系统会自动处理**
-**minio 站点不允许使用 localhost 请使用 127.0.0.1**
-**访问站点与自定义域名 都不要包含 `http` `https` 前缀 设置`https`请使用选项处理** - -## 代码使用 - -> 参考 `SysOssService.upload` 用法
-> 使用 `OssFactory.instance()` 获取当前启用的 `OssClient` 实例
-> 进行功能调用 获取返回值后 存储到对应的业务表 - -![输入图片说明](https://foruda.gitee.com/images/1678978345529639839/d350ec0b_1766278.png "屏幕截图") - - -## 功能配置 - -### 配置OSS - -> 进入 `系统管理 -> 文件管理 -> 配置管理` 填写对应的OSS服务相关配置
- -![输入图片说明](https://foruda.gitee.com/images/1678978349820700551/1f91a237_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978354387669856/3a91a3a9_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978358019307086/0c2523e4_1766278.png "屏幕截图") - -**重点说明** - -> 云厂商只需修改 `访问站点`对应的域 切勿乱改(云厂商强烈建议绑定自定义域名使用 七牛云必须绑定[官方规定])
- -![输入图片说明](https://foruda.gitee.com/images/1678978362358100362/5c2c4d20_1766278.png "屏幕截图") - -> 七牛云 访问站点
- - -![输入图片说明](https://foruda.gitee.com/images/1678978366254745764/e93a65ff_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978369853348732/79e8950e_1766278.png "屏幕截图") - -> 阿里云 访问站点 - -![输入图片说明](https://foruda.gitee.com/images/1678978373981462025/56a70398_1766278.png "屏幕截图") - -> 腾讯云 访问站点 - -![输入图片说明](https://foruda.gitee.com/images/1678978378697093134/785517f3_1766278.png "屏幕截图") - -### MinIO 使用 https访问站点 - -**注意:S3 API 签名计算算法不支持托管 MinIO Server API 的代理方案** - -[ minio https 配置方式](https://blog.csdn.net/Michelle_Zhong/article/details/126484358) - -### 切换OSS - -> 再配置列表点击 `状态` 按钮开启即可(注意: 只能开启一个OSS默认配置)
-> 手动使用 `OssFactory.instance("configKey")`
- -![输入图片说明](https://foruda.gitee.com/images/1678978383700118702/7f3fa0c5_1766278.png "屏幕截图") - -### 扩展分类 - -> 如有文件分类 建议创建多个 oss配置 进行切换存储
- -例如: 创建一个 图片存储的 oss配置
-指定唯一的 `configKey` 与 `前缀目录` 或 直接使用独立的`桶`
-独立桶的特点 可以自定义访问权限
-例如: 创建一个私有文件存储桶 不对外开放
- -![输入图片说明](https://foruda.gitee.com/images/1678978389139754119/140be1df_1766278.png "屏幕截图") - -> 指定需要使用的配置
-> 使用 `OssFactory.instance("image")` 获取的 `OssClient` 会加载上图的配置 从而达到上传不同的目录或桶 - - -![输入图片说明](https://foruda.gitee.com/images/1678978397550123641/1b536881_1766278.png "屏幕截图") - - -### 上传图片或文件 - -> 进入 `系统管理 -> 文件管理` 点击 `上传文件` 或 `上传图片` 根据选项选择即可 会对应上传到配置开启的OSS内
- -![输入图片说明](https://foruda.gitee.com/images/1678978401028132972/445d058e_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978404388284503/5459da29_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978408761764835/c81651fc_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978412748494539/7bae621f_1766278.png "屏幕截图") - -### 列表展示 - -> 默认展示图片(可预览) 文件会展示路径
- -![输入图片说明](https://foruda.gitee.com/images/1678978416327601385/af1ecb3b_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678978422249633007/19d68eaa_1766278.png "屏幕截图") - -> 可以点击 `预览禁用启用` 按钮对是否展示进行更改 - -![输入图片说明](https://foruda.gitee.com/images/1678978426017014926/4f7fa3f3_1766278.png "屏幕截图") - -> 点击禁用后 图片会变成路径展示 - -![输入图片说明](https://foruda.gitee.com/images/1678978429692592556/0231d778_1766278.png "屏幕截图") - -> 也可再 `参数设置` 更改预览状态 将 `OSS预览列表资源` 改为 `false` 即可关闭预览 - -![输入图片说明](https://foruda.gitee.com/images/1678978433769403801/7d480e76_1766278.png "屏幕截图") - -### 删除功能 - -> 点击列表上方或后方 `删除` 按钮 会根据OSS服务商类型 调用对应的删除(注意: 需确保对应的服务商配置正确)
-> 可勾选多服务商类型的文件进行删除 系统会自动判断 - -![输入图片说明](https://foruda.gitee.com/images/1678978438265941745/f32edc72_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678978441938542080/43ed7c3d_1766278.png "屏幕截图") - -### 下载功能 - -> 点击列表后方对应资源的 `下载` 按钮 根据需求填写文件名 点击确认即可完成下载 - -![输入图片说明](https://foruda.gitee.com/images/1678978448927336261/409af888_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678978452761792483/ed0a4a72_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/page.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/page.md deleted file mode 100644 index 97b6c525..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/page.md +++ /dev/null @@ -1,32 +0,0 @@ -# 分页功能 -- - - -## 对应版本 - -> 3.5.0 版本 - -## 重点说明 - -> 项目使用 `mybatis-plus` 分页插件 实现分页功能 大致用法与 MP 一致 [MP分页文档](https://baomidou.com/pages/97710a/)
-> 项目已配置分页合理化 页数溢出 例如: 一共5页 查了第6页 默认返回第一页
- -![输入图片说明](https://foruda.gitee.com/images/1678977804058241635/b5cb362d_1766278.png "屏幕截图") - -## 代码用法 - -> `Controller` 使用 `PageQuery` 接收分页参数 具体参数参考 `PageQuery` - -![输入图片说明](https://foruda.gitee.com/images/1678977844048821356/1f994221_1766278.png "屏幕截图") - -> 构建 `Mybatis-Plus` 分页对象
-> 使用 `PageQuery#build()` 方法 可快速(基于当前对象数据)构建 `MP` 分页对象 - -![输入图片说明](https://foruda.gitee.com/images/1678977862816976499/b82c1638_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678977876194578744/eaa7b854_1766278.png "屏幕截图")
- -具体用法与 `MP` 一致 - -> 自定义 `SQL` 方法分页
-> 只需在 `Mapper` 方法第一个参数和返回值 重点: 第一个参数 标注分页对象 - -![输入图片说明](https://foruda.gitee.com/images/1678977898181729571/6e102731_1766278.png "屏幕截图")
-![输入图片说明](https://foruda.gitee.com/images/1678977906788451483/70979292_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/param_check.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/param_check.md deleted file mode 100644 index 95ee19d8..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/param_check.md +++ /dev/null @@ -1,158 +0,0 @@ -# 参数校验 -- - - - -参数校验在日常开发中十分常见,在本框架中引入了 `spring-boot-starter-validation` 依赖,底层基于 `hibernate-validator`,可以对参数进行校验。 - -## 参数校验使用 - -### 方法一:使用 `@Validated` 注解 - -#### 步骤一:标注 `@Validated` - -`@Validated` 可以标注在类上,或者是参数前。 - -```Java -/** 标注在类上 **/ -@Validated -@RestController -@RequestMapping("/auth") -public class AuthController { - - @PostMapping("/login") - public R login(@RequestBody LoginBody body) { - // ... - } - -} -``` - -```Java -/** 标注在参数前 **/ -@PostMapping -public R add(@Validated @RequestBody SysUserBo user) { - // ... -} -``` - -#### 步骤二:标注校验注解 - -在参数中加入校验注解。 - -```Java -public class SysUserBo { - - @NotBlank(message = "用户账号不能为空") - @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") - private String userName; - - @NotBlank(message = "用户昵称不能为空") - @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符") - private String nickName; - - @Email(message = "邮箱格式不正确") - @Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符") - private String email; - -} -``` - -常见校验注解见文末附表。 - -_注:message 支持 EL 表达式,{max} 直接读取前面的参数值。_ - -### 方法二:使用校验工具类 `ValidatorUtils` - -`org.dromara.common.core.utils.ValidatorUtils` - -![输入图片说明](https://foruda.gitee.com/images/1700050047426137432/206bd032_4959041.png "屏幕截图") - -使用方式 1:校验所有带有校验注解的属性 - -```Java -// 校验所有带有校验注解的属性 -ValidatorUtils.validate(object); -``` - -使用方式 2:按照分组校验属性(可以传多个分组) - -```Java -// 按照分组校验属性(可以传多个分组) -ValidatorUtils.validate(object, group); -``` - -## 扩展使用 - -### 扩展一:自定义校验注解 - -除了已有的校验注解以外,可以结合业务进行自定义。 - -以框架中的 `@Xss` 注解为例进行说明。 - -```Java -@Xss(message = "用户账号不能包含脚本字符") -@NotBlank(message = "用户账号不能为空") -@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") -private String userName; -``` - -#### 1:新增 `@Xss` 注解 - -`org.dromara.common.core.xss.Xss` - -![输入图片说明](https://foruda.gitee.com/images/1700048074014527096/b4e230c2_4959041.png "屏幕截图") - -#### 2:自定义校验器 - -自定义校验器实现 `jakarta.validation.ConstraintValidator` 接口。 - -`org.dromara.common.core.xss.XssValidator` - -![输入图片说明](https://foruda.gitee.com/images/1700048474563719650/f9172bdc_4959041.png "屏幕截图") - -### 扩展二:自定义分组校验 - -同一个对象在不同的请求中需要校验的参数不同,则可以使用分组校验。 - -#### 1:自定义分组 - -![输入图片说明](https://foruda.gitee.com/images/1700049439236073123/9e0d2e16_4959041.png "屏幕截图") - -#### 2:`@Validated` 注解指定分组 - -![输入图片说明](https://foruda.gitee.com/images/1700049302803077030/c2a985aa_4959041.png "屏幕截图") - -#### 3:校验注解中指定分组 - -![输入图片说明](https://foruda.gitee.com/images/1700049205699437759/96babbd6_4959041.png "屏幕截图") - -## 附录:常用校验注解 - -| 注解 | 使用(只列举特殊参数值) | 参数类型 | 说明 | -|------------------|--------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------| -| @AssertFalse | @AssertFalse | boolean / Boolean | 元素值必须为 false | -| @AssertTrue | @AssertTrue | boolean / Boolean | 元素值必须为 true | -| @DecimalMax | @DecimalMax(value=值) | - BigDecimal
- BigInteger
- CharSequence
- byte, short, int, long 及其包装类 | 元素必须是一个数字,其值必须小于或等于指定的最大值 | -| @DecimalMin | @DecimalMin(value=值) | - BigDecimal
- BigInteger
- CharSequence
- byte, short, int, long 及其包装类 | 元素必须是一个数字,其值必须大于或等于指定的最小值 | -| @Digits | @Digits(integer=整数位值, fraction=小数位值) | - BigDecimal
- BigInteger
- CharSequence
- byte, short, int, long 及其包装类 | 元素必须符合整数位以及小数位范围值 | -| @Email | @Email(regexp=正则表达式, flags=标志) | CharSequence | 元素是否符合正则表达式(正则表达式非必传) | -| @Future | @Future | - java.util.Date
- java.util.Calendar
- java.time.Instant
- java.time.LocalDate
- java.time.LocalDateTime
- java.time.LocalTime
- java.time.MonthDay
- java.time.OffsetDateTime
- java.time.OffsetTime
- java.time.Year
- java.time.YearMonth
- java.time.ZonedDateTime
- java.time.chrono.HijrahDate
- java.time.chrono.JapaneseDate
- java.time.chrono.MinguoDate
- java.time.chrono.ThaiBuddhistDate | 元素必须是未来的时刻、日期或时间 | -| @FutureOrPresent | @FutureOrPresent | 同 @Future | 元素必须是当前或未来的时刻、日期或时间 | -| @Length | @Length(min=最小值, max=最大值) | - CharSequence | 验证字符串是否在包含的 min 和 max 之间 | -| @Max | @Max(value=值) | - BigDecimal
- BigInteger
- byte, short, int, long 及其包装类 | 元素必须是一个数字,其值必须小于或等于指定的最大值 | -| @Min | @Min(value=值) | - BigDecimal
- BigInteger
- byte, short, int, long 及其包装类 | 元素必须是一个数字,其值必须大于或等于指定的最小值 | -| @Negative | @Negative | - BigDecimal
- BigInteger
- byte,short,int,long,float,double 及其包装类 | 元素必须是一个严格的负数(即 0 被视为无效值) | -| @NegativeOrZero | @NegativeOrZero | - BigDecimal
- BigInteger
- byte,short,int,long,float,double 及其包装类 | 元素必须为负数或 0 | -| @NotBlank | @NotBlank | CharSequence | 元素不能为 null,并且必须至少包含一个非空白字符 | -| @NotEmpty | @NotEmpty | - CharSequence
- Collection
- Map
- Array | 元素不能为 null 或空集合 | -| @NotNull | @NotNull | 不限类型 | 元素不能为 null | -| @Null | @Null | 不限类型 | 元素必须为 null | -| @Past | @Past | 同 @Future | 元素必须是过去的瞬间、日期或时间 | -| @PastOrPresent | @PastOrPresent | 同 @Future | 元素必须是过去或现在的瞬间、日期或时间 | -| @Pattern | @Pattern(regexp=正则表达式, flags=标志) | CharSequence | 元素必须与指定的正则表达式匹配(正则表达式遵循 Java 正则表达式约定) | -| @Positive | @Positive | - BigDecimal
- BigInteger
- byte,short,int,long,float,double 及其包装类 | 元素必须是一个严格的正数(即 0 被视为无效值) | -| @PositiveOrZero | @PositiveOrZero | - BigDecimal
- BigInteger
- byte,short,int,long,float,double 及其包装类 | 元素必须为正数或 0 | -| @Range | @Range(min=最小值, max=最大值) | - BigDecimal
- BigInteger
- CharSequence
- byte, short, int, long 及其包装类 | 验证元素是否在包含的 min 和 max 之间 | -| @Size | @Size(min=最小值, max=最大值) | - CharSequence
- Collection
- Map
- Array | 验证元素是否在包含的 min 和 max 之间 | -| @Valid | @Valid | 对象 | 级联验证 | - -更多注解可参考包: `org.hibernate.validator` \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/permissions.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/permissions.md deleted file mode 100644 index 384b7749..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/permissions.md +++ /dev/null @@ -1,144 +0,0 @@ -# 关于数据权限 -- - - -* 参考 demo 模块用法(需导入 test.sql 文件) - -### 新版数据权限功能: -1.支持自动注入 sql 数据过滤
-2.查询、更新、删除 限制
-3.支持自定义数据字段过滤
-4.模板支持 spel 语法实现动态 Bean 处理
-5.支持与菜单权限标识符联合使用(5.2.X新功能) - -### 数据权限相关代码 - -| 类 | 说明 | 功能 | -|-------------------------------|-----------------|----------------------------------------| -| DataScopeType | 数据权限模板定义 | 用于定义数据权限模板 | -| DataPermission | 数据权限组注解 | 用于标注开启数据权限 (默认过滤部门权限) | -| DataColumn | 具体的数据权限字段标注 | 用于替换数据权限模板内的 key 变量 | -| PlusDataPermissionInterceptor | 数据权限 sql 拦截器 | 用于拦截所有 sql 检查是否标注了 `DataPermission` 注解 | -| PlusDataPermissionHandler | 数据权限处理器 | 用于处理被拦截到的 sql 为其添加数据权限过滤条件 | -| DataPermissionHelper | 数据权限助手 | 操作数据权限上下文变量 | -| SysDataScopeService | 自定义 Bean 处理数据权限 | 用于自定义扩展 | - -## 忽略数据权限 - -1.如果需要指定单独 SQL 不开启过滤,可在对应的 Mapper 接口添加如下忽略注解: -``` -@InterceptorIgnore(dataPermission = "true") -``` - -2.如果需要在业务层忽略数据权限,可调用以下方法: -``` -# 无返回值 -DataPermissionHelper.ignore(() -> { 业务代码 }); -# 有返回值 -Class result = DataPermissionHelper.ignore(() -> { return 业务代码 }); -``` - -### 使用方式 `参考demo模块` -数据权限体系 `用户 -> 多角色 => 角色 -> 单数据权限` -> 例子: 用户A 拥有两个角色
-> 角色A 部门经理 可查看 本部门及以下部门的数据
-> 角色B 兼职开发 可查看 仅自己的数据 - -> 创建角色 test1 为 本部门及以下 - -![输入图片说明](https://foruda.gitee.com/images/1678978669666831574/b51ed0a3_1766278.png "屏幕截图") - -> 创建角色 test2 为 仅本人 - -![输入图片说明](https://foruda.gitee.com/images/1678978674159035056/69cf32ad_1766278.png "屏幕截图") - -> 将其分配给用户 test - -![输入图片说明](https://foruda.gitee.com/images/1678978680492570269/a47b6afc_1766278.png "屏幕截图") - -### 编写列表查询(注意: 数据权限注解只能在 Mapper 层使用) - -> 标注数据权限注解 `dept_id` 为过滤部门字段 `user_id` 为过滤创建用户 - -![输入图片说明](https://foruda.gitee.com/images/1678978687179608427/d6b83c30_1766278.png "屏幕截图") - -### 重点注意: 如下情况不生效 - -> 有自定义实现方法 最终执行的mapper不是这个方法 所以无法生效 -> -> 解决方案: 一直往下点 找到最终的执行mapper重写即可 - -![输入图片说明](https://foruda.gitee.com/images/1678978692558777291/78b0a3dd_1766278.png "屏幕截图") - -### 编写数据权限模板 - -![输入图片说明](https://foruda.gitee.com/images/1678978697141183499/cfc1cb6a_1766278.png "屏幕截图") - -1.`code` 为关联角色的数据权限 `code`
-2.`sqlTemplate` 为 sql 模板
-`#{#deptName}` 为模板变量 对应权限注解的 `key`
-`#{@sdss}` 为模板 Bean 调用 调用其 Bean 的处理方法
-3.`elseSql` 为兜底 sql 处理当前角色与标注的注解 无对应的情况
-例如 数据权限为仅本人 且 方法并未标注具体过滤注解 则 填充 `1 = 0` 使条件不满足 不允许查看
-更详细用法可以参考 `DataScopeType` 注释 - -### 测试代码 - -> 使用 `管理员` 用户优先测试 - -![输入图片说明](https://foruda.gitee.com/images/1678978703250082481/e93a68a5_1766278.png "屏幕截图") - -> 使用 `test` 用户测试 - -![输入图片说明](https://foruda.gitee.com/images/1678978710644676604/d7f80487_1766278.png "屏幕截图") - -> 使用 `test` 删除一条不属于自己的数据 -> sql执行为不满足条件 不允许删除 - -![输入图片说明](https://foruda.gitee.com/images/1678978715711122947/441d61f7_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678978720298532619/a35b1147_1766278.png "屏幕截图") - - -> 使用 `test` 修改与删除同理
-> 具体实现为 更新和删除方法 标注数据权限注解 - -![输入图片说明](https://foruda.gitee.com/images/1678978725329242504/a70491a1_1766278.png "屏幕截图") - -### 自定义SQL模板 - -> 1.首先在角色管理 数据权限下拉框 添加自定义模板
-> 为什么不放置到系统字典问题: 因数据权限与模板绑定 不应随意改动 最好事先定义好 - -![输入图片说明](https://foruda.gitee.com/images/1678978730563169865/3459ee17_1766278.png "屏幕截图") - -> 2.代码 `DataScopeType` 自定义一个SQL模板 - -![输入图片说明](https://foruda.gitee.com/images/1678978735588305505/3f030c67_1766278.png "屏幕截图") - -> 3.标注权限注解 - -![输入图片说明](https://foruda.gitee.com/images/1678978742259837391/eabe5caa_1766278.png "屏幕截图") - -> 4.设置数据权限变量 - -![输入图片说明](https://foruda.gitee.com/images/1678978746778429543/e211201f_1766278.png "屏幕截图") - -> 5.测试 - -![输入图片说明](https://foruda.gitee.com/images/1678978751875467640/7d210cf4_1766278.png "屏幕截图") - -### mybatis-plus 原生方法 增加数据权限过滤 - -> 首先查看需要重写的方法源码 重点`方法源码` `方法源码` `方法源码`
-> 例如重写 `selectPage` 方法
- -![输入图片说明](https://foruda.gitee.com/images/1678978757955000897/8315695c_1766278.png "屏幕截图") - -> 复制源码到自己的 `Mapper` 并增加数据权限注解 注意左边出现重写图标 即为重写成功
- -![输入图片说明](https://foruda.gitee.com/images/1678978763224011694/bbea25a1_1766278.png "屏幕截图") - -### 支持类标注 - -> 获取规则 `方法 > 类` 注意: 类标注后 所有方法(包括父类方法) 都会进行数据权限过滤 - -![输入图片说明](https://foruda.gitee.com/images/1678978767336534896/fb13ee99_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/permissions_control.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/permissions_control.md deleted file mode 100644 index fa3a079c..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/permissions_control.md +++ /dev/null @@ -1,180 +0,0 @@ -# 权限控制 -- - - - -本文采用 `Sa-Token` 框架实现权限控制。[官方文档传送门](https://sa-token.cc/doc.html#/) - -## 权限校验 -权限校验指的是校验用户是否拥有访问某个 API 的能力。 - -通常情况下,一个 API 对应一个权限码,如果用户具备当前 API 的权限码,即代表有能力访问该 API。 - -### 1:权限标识 -在本系统中,每一个菜单功能都有对应的权限标识,可以在菜单管理中进行设置。 - -> 注: -> 1. 前后端的权限标识要保持一致。 -> 2. 权限标识可以使用通配符`*`。 - -![输入图片说明](https://foruda.gitee.com/images/1701086497939145368/133fb327_4959041.png "屏幕截图") - - -### 2:校验方法 -#### 2.1:使用 `@SaCheckPermission` 注解进行校验 -`@SaCheckPermission` 注解是由 `Sa-Token` 框架提供的角色校验注解,可以标注在方法上或类上。 - -- 单个权限校验: - -```Java -@SaCheckPermission("system:user:list") -``` - -- 多个权限校验(或模式,满足任意一个权限即可): - -```Java -@SaCheckPermission( - value = { - "system:user:list", - "system:user:query" - }, - mode = SaMode.OR -) -``` - -- 多个权限校验(与模式,必须满足所有权限): - -```Java -@SaCheckPermission( - value = { - "system:user:list", - "system:user:query" - }, - mode = SaMode.AND -) -``` - -#### 2.2:使用 `StpUtil` 工具类校验 -`StpUtil` 工具类是由 `Sa-Token` 框架提供的权限工具类,提供了常用的校验方法。 - -- 判断当前用户是否拥有某个权限(返回 `boolean`): - -```Java -StpUtil.hasPermission("system:user:list"); -``` - -- 单个权限校验: - -```Java -StpUtil.checkPermission("system:user:list"); -``` -如果验证未通过,则抛出异常: `NotPermissionException` - -- 多个权限校验(或模式,满足任意一个权限即可): - -```Java -StpUtil.checkPermissionOr("system:user:list", "system:user:query"); -``` -如果验证未通过,则抛出异常: `NotPermissionException` - -- 多个权限校验(与模式,必须满足所有权限): - -```Java -StpUtil.checkPermissionAnd("system:user:list", "system:user:query"); -``` -如果验证未通过,则抛出异常: `NotPermissionException` - -## 角色校验 -角色校验指的是校验用户是否拥有某个指定角色。 - -### 1:权限标识 -在本系统中,每个角色都拥有唯一的权限字符。 - -除了超级管理员角色外,其他角色的权限字符可以通过角色管理进行设置。 - -![输入图片说明](https://foruda.gitee.com/images/1701085080527279823/3255961d_4959041.png "屏幕截图") - -### 2:校验方法 -#### 2.1:使用 `@SaCheckRole` 注解校验 -`@SaCheckRole` 注解是由 `Sa-Token` 框架提供的角色校验注解,可以标注在方法上或类上。 - -- 单个角色校验 - -```Java -@SaCheckRole("superadmin") -``` - -- 多个角色校验(或模式,满足任意一个角色即可): - -```Java -@SaCheckRole( - value = { - "superadmin", - "admin" - }, - mode = SaMode.OR -) -``` - -- 多个角色校验(与模式,必须满足所有角色): - -```Java -@SaCheckRole( - value = { - "superadmin", - "admin" - }, - mode = SaMode.AND -) -``` - -#### 2.2:使用 `StpUtil` 工具类校验 -`StpUtil` 工具类是由 `Sa-Token` 框架提供的权限工具类,提供了常用的校验方法。 - -- 判断当前用户是否拥有某个角色(返回 `boolean`): - -```Java -StpUtil.hasRole("superadmin") -``` - -- 单个权限校验: - -```Java -StpUtil.checkRole("system:user:list"); -``` -如果验证未通过,则抛出异常: `NotRoleException` - -- 多个权限校验(或模式,满足任意一个角色即可): - -```Java -StpUtil.checkRoleOr("system:user:list", "system:user:query"); -``` -如果验证未通过,则抛出异常: `NotRoleException` - -- 多个权限校验(与模式,必须满足所有角色): - -```Java -StpUtil.checkRoleAnd("system:user:list", "system:user:query"); -``` -如果验证未通过,则抛出异常: `NotRoleException` - -## 角色权限双重 `OR` 校验 -除了分开校验以外,权限和角色也可以进行组合,表示备选校验。 - -简单举个例子: - -假设某个 API 的权限码为 `system:user:list`,角色 `admin` 可以调用,则可以这样写: - -```Java -@SaCheckPermission(value = "system:user:list", orRole = "admin") -``` - -以上权限只需要满足任意一项即可。更多写法可以参考 `Sa-Token` [官方文档](https://sa-token.cc/doc.html#/use/at-check?id=_4%e3%80%81%e8%a7%92%e8%89%b2%e6%9d%83%e9%99%90%e5%8f%8c%e9%87%8d-or%e6%a0%a1%e9%aa%8c)。 - -## 当前用户的所有权限 -本系统中实现了 `StpInterface` 接口,可以对用户的权限以及角色进行管理,并且可以根据不同的用户类型进行设置。 - -具体参考类:`org.dromara.common.satoken.core.service.SaPermissionImpl` - -## 忽略权限校验 -请参考文档:[接口放行](/ruoyi-vue-plus/framework/basic/interface_release?id=接口放行) - - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/social.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/social.md deleted file mode 100644 index a004434b..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/social.md +++ /dev/null @@ -1,68 +0,0 @@ -# 第三方授权功能 -- - - -## 版本 >= 5.X - -## 前置说明 -1. 该功能基于 `JustAuth` 实现,支持多家平台实现第三方授权登录。 -2. 以 `Gitee` 授权登录为例进行本功能的使用说明。 -3. 其他第三方授权配置信息获取方式可参考 `JustAuth` [官方文档](https://www.justauth.cn/guide/)。
- - ![输入图片说明](https://foruda.gitee.com/images/1690937097426867003/91d80587_4959041.png "屏幕截图") - -## 第三方授权配置 - -### 申请三方应用(以gitee为例) - -![输入图片说明](https://foruda.gitee.com/images/1700641775779304627/1cf1b56f_1766278.png "屏幕截图") - -### 更改后端配置 `application-dev.yml` - -![输入图片说明](https://foruda.gitee.com/images/1690936741844431943/580f8998_4959041.png "屏幕截图") - -**注:内网地址无法回调,请使用外网可以访问的地址。** - -![输入图片说明](https://foruda.gitee.com/images/1690940457570856867/ce22df18_4959041.png "屏幕截图") - -### 更改前端配置 `login.vue` - -![输入图片说明](https://foruda.gitee.com/images/1690937306197173754/5c1ece29_4959041.png "屏幕截图") - -## 授权登录(未绑定第三方平台) - -### 步骤一:个人中心授权第三方应用 - -![输入图片说明](https://foruda.gitee.com/images/1690938449386201097/ea375106_4959041.png "屏幕截图") - -### 步骤二:同意授权 - -![输入图片说明](https://foruda.gitee.com/images/1690938522418523183/81b327bf_4959041.png "屏幕截图") - -顶部出现授权成功,并跳转到系统首页。
- -![输入图片说明](https://foruda.gitee.com/images/1690938559178527841/563168e4_4959041.png "屏幕截图")
- -![输入图片说明](https://foruda.gitee.com/images/1690938636375977741/8ceb77cf_4959041.png "屏幕截图") - -查看第三方应用可看到授权成功的个人信息。
- -![输入图片说明](https://foruda.gitee.com/images/1690938725512311321/5532a2a9_4959041.png "屏幕截图") - -## 授权登录(已绑定第三方平台) - -### 步骤一:点击登录页面图标 - -![输入图片说明](https://foruda.gitee.com/images/1690938908352243992/fd044381_4959041.png "屏幕截图") - -### 步骤二:同意授权 - -![输入图片说明](https://foruda.gitee.com/images/1690938522418523183/81b327bf_4959041.png "屏幕截图") - -## 解除授权绑定 - -### 步骤一:个人中心点击解绑第三方应用 - -![输入图片说明](https://foruda.gitee.com/images/1690939087877969002/4ef324e7_4959041.png "屏幕截图") - -### 步骤二:点击确定完成解绑 - -![输入图片说明](https://foruda.gitee.com/images/1690939108017661775/7236088d_4959041.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/tenant.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/tenant.md deleted file mode 100644 index 33953086..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/tenant.md +++ /dev/null @@ -1,121 +0,0 @@ -# 多租户功能 -- - - -## 版本 >= 5.X - -## 前置说明(重要) -1. 本框架多租户功能的实现是基于 [MyBatis-Plus 多租户插件](https://baomidou.com/pages/aef2f2/#tenantlineinnerinterceptor) 的,只支持最简单的隔离。 -2. 本系统默认开启多租户功能。 -3. 多租户业务表建表需要加上租户id `tenant_id`,可参考其他系统表。 -4. 非多租户表可在配置文件进行配置排除。 -5. 只有超级管理员支持切换租户。 - -## 多租户使用流程(先说结论再展开!) -0. 开启多租户配置(系统默认已经开启) -1. 登录界面(可以选择不同租户) -> 注:如果为租户设置了绑定域名,则只能选择当前域名相关的租户列表。 -2. 设置多租户套餐 -3. 新增/修改租户(需要选择套餐) -4. 切换租户(仅超级管理员可操作) - -## 多租户配置 -`application.yml`
- -> 开关 `enable` 节点不用废话。
-> 如果不需要过滤租户的表可在 `excludes` 节点下添加。 - -**注意: 如果已经基于租户模式启动了程序 关闭租户必须删除mysql与redis内的相关数据重新导入sql** - -![输入图片说明](https://foruda.gitee.com/images/1680168468127690787/2cd3279e_4959041.png "屏幕截图") - -## 忽略租户 - -1.如果需要指定单独 SQL 不开启过滤,可在对应的 Mapper 接口添加如下忽略注解: -``` -@InterceptorIgnore(tenantLine = "true", dataPermission = "false") -``` -**此处注意事项 使用此注解如果需要开启数据权限 dataPermission = "false" 必须添加 mp的注解默认是忽略数据权限的 会导致数据权限失效** - -2.如果需要在业务层忽略多租户,可调用以下方法(推荐使用): -``` -# 无返回值 -TenantHelper.ignore(() -> { 业务代码 }); -# 有返回值 -Class result = TenantHelper.ignore(() -> { return 业务代码 }); -``` - -## 动态切换租户 - -**仅适用于特殊需求业务(例如: 创建租户时, 对该租户操作一些数据, 或者需要去其他租户查一些数据等) 禁止乱用后果自负** - -``` -# 无返回值 -TenantHelper.dynamic(租户id, () -> { 业务代码 }); -# 有返回值 -Class result = TenantHelper.dynamic(租户id, () -> { return 业务代码 }); -``` - -## 登录界面 - -![输入图片说明](https://foruda.gitee.com/images/1680173982933030545/bca146d7_4959041.png "屏幕截图") - -> 注:如果为租户设置了绑定域名,则只能选择当前域名相关的租户列表。 - -## 租户套餐管理 -### 租户套餐新增 -![输入图片说明](https://foruda.gitee.com/images/1680174317475230288/352957a1_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680174602877523112/fc194f17_4959041.png "屏幕截图") - -> 注: -> 1、先新增套餐再新增租户,因为租户新增之后无法修改所选套餐。 -> 2、租户所关联的套餐如果后续有修改可以进行同步。 - - -## 租户管理 -### 默认租户 -> 注:默认租户无法修改 - -![输入图片说明](https://foruda.gitee.com/images/1680174738913576400/b6aca11a_4959041.png "屏幕截图") - -### 新增租户 -#### 填写表单 -![输入图片说明](https://foruda.gitee.com/images/1680174945220618443/f7181b51_4959041.png "屏幕截图") - -#### 选择新增的租户套餐 -![输入图片说明](https://foruda.gitee.com/images/1680174991869792688/0dbaadd6_4959041.png "屏幕截图") - -#### 新增完成 -![输入图片说明](https://foruda.gitee.com/images/1680175033853525725/42e64b4d_4959041.png "屏幕截图") - -#### 登录租户 -![输入图片说明](https://foruda.gitee.com/images/1680176145378931134/e05f347e_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680176208161104366/44a935f1_4959041.png "屏幕截图") - -### 修改租户 -#### 配置域名 -![输入图片说明](https://foruda.gitee.com/images/1680175251192690133/141fa6a6_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680175431036971650/db522d39_4959041.png "屏幕截图") - -#### 没有配置域名 -![输入图片说明](https://foruda.gitee.com/images/1680175541165540240/95e211f7_4959041.png "屏幕截图") - -#### 强调一下:这不是bug! -> 注:域名的配置就是为了绑定特定租户! - -### 同步套餐 -应用场景:租户套餐进行了修改,配置的菜单需要同步到特定租户。 -(不是所有租户都有更新套餐的权利, 这是跟钱挂钩的) - -> 点一下按钮的事,图略。 - -## 切换租户(仅超级管理员) -> 注:管理员切换租户不是切换用户,切换的只是数据,管理员拥有所有权限。 - -![输入图片说明](https://foruda.gitee.com/images/1680176324802967804/5c5d6fc3_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680176431031189788/0c3f924c_4959041.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1680176496555243569/624ec677_4959041.png "屏幕截图") - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/user.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/user.md deleted file mode 100644 index b665ac5a..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/basic/user.md +++ /dev/null @@ -1,85 +0,0 @@ -# 系统用户相关 -- - - - -> 框架采用sa-token控制权限 并对sa-token的api做了一定的业务封装
- -## 用户登录 - -> 参考自带多种登录实现 不限制用户数据来源 只需要构建 LoginUser 即可完成登录
-> 例如: `同表不同类型` `不同表` `同表+扩展表`
- -![输入图片说明](https://foruda.gitee.com/images/1699590555824776931/63d493fc_1766278.png "屏幕截图") - -## 获取用户信息 - -> 完成登录后会生成登录token返回给前端 前端需要再请求头携带token 后端方可获取到对应的用户信息 - -请求头传递格式: `Authorization: Bearer token` - -后端获取用户信息: -```java -LoginUser user = LoginHelper.getLoginUser(); -``` - -## 获取用户信息(基于token) -```java -LoginUser user = LoginHelper.getLoginUser(token); -``` - -## 获取登录用户id -```java -Long userId = LoginHelper.getUserId(); -``` - -## 获取登录用户账户名 -```java -String username = LoginHelper.getUsername(); -``` - -## 获取登录用户所属租户id -```java -String tenantId = LoginHelper.getTenantId(); -``` - -## 获取登录用户所属部门id -```java -Long deptId = LoginHelper.getDeptId(); -``` - -## 获取登录用户类型 -```java -UserType userType = LoginHelper.getUserType(); -``` - -## 获取登录用户其他扩展属性 -```java -Object obj = LoginHelper.getExtra(key); -``` - -## 设置登录用户其他扩展属性 - -参考登录设置 `clientId` 属性 - -![输入图片说明](https://foruda.gitee.com/images/1699591164562734430/42730add_1766278.png "屏幕截图") - -## 判断用户是否为超级管理员 - -```java -// 判断当前登录用户 -boolean b = LoginHelper.isSuperAdmin(); -// 判断用户基于id -boolean b = LoginHelper.isSuperAdmin(userId); -``` - -## 判断用户是否为租户管理员 - -```java -// 判断当前登录用户 -boolean b = LoginHelper.isTenantAdmin(); -// 判断用户基于角色组 -boolean b = LoginHelper.isSuperAdmin(rolePermission); -``` - -## 其他更多操作 -[Sa-Token 官方文档 - 登录认证](https://sa-token.cc/doc.html#/use/login-auth) - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/about_join.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/about_join.md deleted file mode 100644 index 593129e8..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/about_join.md +++ /dev/null @@ -1,14 +0,0 @@ -# 关于多表查询 -- - - -## 建议单表查询 - -文章连接: [大连接查询分解好处](https://java.isture.com/db/mysql/mysql-x-optimize-decompose-connection.html) -文章连接: [如何用mp多表查询性能测试](https://developer.aliyun.com/article/858927) - -![输入图片说明](https://foruda.gitee.com/images/1678979482724037085/1e74f3e1_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1666336728402711844/52788205_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1666336945935088277/f60e3288_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1666336954686520161/c6c83adc_1766278.png "屏幕截图") - -**(上图出自 <高性能MySql>)** \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/key.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/key.md deleted file mode 100644 index 3ec55fec..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/key.md +++ /dev/null @@ -1,19 +0,0 @@ -# 主键使用说明 -- - - -## 关于如何使用分布式id或雪花id - -参考 `MybatisPlusConfig` 如需自定义 修改 `Bean` 实现即可 - -![输入图片说明](https://foruda.gitee.com/images/1678979401707903546/e25f6c06_1766278.png "屏幕截图") - -框架默认集成 雪花ID 只需全局更改 主键类型即可 - -![输入图片说明](https://foruda.gitee.com/images/1678979411517764918/1470df04_1766278.png "屏幕截图") - -如单表使用 可单独配置注解 - -![输入图片说明](https://foruda.gitee.com/images/1678979416033986923/2a4c3736_1766278.png "屏幕截图") - -### 重点说明 -* 由于雪花id位数过长 `Long` 类型在前端会失真 -* 框架已配置序列化方案 超越 `JS` 最大值自动转字符串 参考 `BigNumberSerializer` 类 (3.0.0 及以上新增) \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/test.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/test.md deleted file mode 100644 index c6dbfe99..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/test.md +++ /dev/null @@ -1,6 +0,0 @@ -# 单元测试 -- - - -## 参考文章 -[SpringBoot 2.X 整合 JUnit5 及全方位使用手册](https://lionli.blog.csdn.net/article/details/127576604) -## 参考代码(4.4.0新增) -![输入图片说明](https://foruda.gitee.com/images/1666973091281055549/6e8f58c3_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/transaction.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/transaction.md deleted file mode 100644 index dfad76f8..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/explain/transaction.md +++ /dev/null @@ -1,45 +0,0 @@ -# 事务相关 -- - - -若依文档对事务注解的描述 [关于事务](https://doc.ruoyi.vip/ruoyi/document/htsc.html#%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86) 以下对多数据源事务做补充: - -## 多后端多数据源事务 - -框架支持对接 `seata` 保证分布式多数据源事务
-详情参考多数据源框架文档连接: https://www.kancloud.cn/tracy5546/dynamic-datasource/2268607 - -## 本地多数据源事务 -请使用 `@DSTransactional` 注解 会代理 `@DS` 注解切换后的数据源事务做回滚处理
-只要 `@DSTransactional` 注解下任一环节发生异常,则全局多数据源事务回滚。
-如果BC上也有 `@DSTransactional` 会有影响吗?答:没有影响的。 - -```java -//如AService调用BService和CService的方法,A,B,C分别对应不同数据源。 - -public class AService { - - @DS("a")//如果a是默认数据源则不需要DS注解。 - @DSTransactional - public void dosomething(){ - BService.dosomething(); - CService.dosomething(); - } -} - -public class BService { - - @DS("b") - public void dosomething(){ - //dosomething - } -} - -public class CService { - - @DS("c") - public void dosomething(){ - //dosomething - } -} -``` - - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/api_encrypt.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/api_encrypt.md deleted file mode 100644 index fc56df31..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/api_encrypt.md +++ /dev/null @@ -1,38 +0,0 @@ -# 数据加解密 -- - - - -## 1:API 加密注解 `@ApiEncrypt` -1. 对于标注了 `@ApiEncrypt` 注解的接口,请求参数都必须进行加密。 -2. 注解的参数 `response` 为响应加密标识,默认 `false` 不加密,为 `true` 表示响应加密。 -3. 加密解密逻辑由过滤器实现,详情可参考 `org.dromara.common.encrypt.filter.CryptoFilter`。 - -## 2:API 加密配置 -`application.yml` - -![输入图片说明](https://foruda.gitee.com/images/1701131796468961065/83c464cd_4959041.png "屏幕截图") - -`.env.development` / `.env.production` - -![输入图片说明](https://foruda.gitee.com/images/1709533252413969800/1d0dff25_1766278.png "屏幕截图") - -> 注: -> 1. 公私钥与前端配置文件互为配对,如果需要更换请一同更换。 -> 2. 后端公钥对应前端私钥;后端私钥对应前端公钥。 - -## 3:前端开启加密 -如果需要开启 API 加密,则需要修改 `request` 的 `headers` 内容: -```Javascript -headers: { - isEncrypt: true -} -``` - -![输入图片说明](https://foruda.gitee.com/images/1701137141916998346/5e839bbe_4959041.png "屏幕截图") - -## 4.关于请求响应参数加解密说明 - -如何加解密请求响应参数看这里 -> [关于请求响应参数解密](/questions/api_encrypt.md) - -## 密钥生成说明 - -![输入图片说明](https://foruda.gitee.com/images/1675577852271308699/9b30258e_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/dynamic_datasource.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/dynamic_datasource.md deleted file mode 100644 index 1e81b5e0..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/dynamic_datasource.md +++ /dev/null @@ -1,45 +0,0 @@ -# 多数据源 -- - - - -### 框架默认 mysql 其他数据库使用说明 - -找到 `ruoyi-admin` 模块在 pom 文件内增加对应的jdbc依赖 - -![输入图片说明](https://foruda.gitee.com/images/1721098535176969987/d42870ca_1766278.png "屏幕截图") - - -### 关于多数据源事务 具体参考 `事务相关` 文档说明 - -### 多数据源框架功能介绍 -多数据源框架官方文档: [dynamic-datasource文档](https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611) - -* 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。 -* 支持数据库敏感配置信息 加密 ENC()。 -* 支持每个数据库独立初始化表结构schema和数据库database。 -* 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。 -* 支持 自定义注解 ,需继承DS(3.2.0+)。 -* 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。 -* 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。 -* 提供 自定义数据源来源 方案(如全从数据库加载)。 -* 提供项目启动后 动态增加移除数据源 方案。 -* 提供Mybatis环境下的 纯读写分离 方案。 -* 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。 -* 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。 -* 提供 基于seata的分布式事务方案。 -* 提供 本地多数据源事务方案。 附:不能和原生spring事务混用。 - -### 用法说明 - -> 加载顺序 `方法 => 类 => 默认`
- -![输入图片说明](https://foruda.gitee.com/images/1678979069737596299/abe8ae7f_1766278.png "屏幕截图") - -### 配置方式 - -![输入图片说明](https://foruda.gitee.com/images/1678979074000345758/b9238f0b_1766278.png "屏幕截图") - -### 数据库异构 - -例如: `mysql + oracle` 参考对应多数据源框架文档 [dynamic-ds文档](https://www.kancloud.cn/tracy5546/dynamic-datasource) - -![输入图片说明](https://foruda.gitee.com/images/1678979078387192317/2de94a78_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/encrypt.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/encrypt.md deleted file mode 100644 index 19d726cd..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/encrypt.md +++ /dev/null @@ -1,28 +0,0 @@ -# 数据加解密 -- - - -## 框架版本 >= 4.6.0 -## 功能说明 - -数据库 数据存储加密 查询解密功能
-支持加密算法: `BASE64` `AES` `RSA` `SM2` `SM4` - -## 注解 `@EncryptField` - -![输入图片说明](https://foruda.gitee.com/images/1675577493013639395/cd920f15_1766278.png "屏幕截图") - -## 用法说明 - -**详细用法可参考案例 TestEncryptController 测试数据库加解密功能** - -全局默认加密配置(如果注解不配置则使用全局配置) - -![输入图片说明](https://foruda.gitee.com/images/1675577674063566357/dee94786_1766278.png "屏幕截图") - -注解可自定义算法与配置 - -![输入图片说明](https://foruda.gitee.com/images/1675577725117970708/7ee7a833_1766278.png "屏幕截图") - -## 密钥生成说明 - -![输入图片说明](https://foruda.gitee.com/images/1675577852271308699/9b30258e_1766278.png "屏幕截图") - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/idempotent.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/idempotent.md deleted file mode 100644 index 46c7f423..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/idempotent.md +++ /dev/null @@ -1,29 +0,0 @@ -# 防重幂等 -- - - -### 功能介绍 - -防重功能为防止两条相同的数据重复提交导致脏数据或业务错乱
-**注意: 重复提交属于小概率事件 请不要拿并发压测与之相提并论**
-框架防重功能参考 `美团GTIS防重系统` 使用 请求参数与用户Token或URL 生成全局业务ID
-有效防止 `同一个用户` 在 `限制时间` 内对 `同一个业务` 提交 `相同的数据` - -框架防重处理 `支持业务失败或异常` 快速释放限制
-业务处理成功后 会在设置时间内 限制同一条数据的提交
-**注意: 只对同一个用户的同一个接口提交相同的数据有效** - - - - -### 美团GTIS系统流程图 - -[美团 分布式系统互斥性与幂等性问题的分析与解决](https://tech.meituan.com/2016/09/29/distributed-system-mutually-exclusive-idempotence-cerberus-gtis.html) - -![输入图片说明](https://foruda.gitee.com/images/1678979231862359032/34f030c5_1766278.png "屏幕截图") - -### 使用方法 - -在Controller标注 `@RepeatSubmit` 注解即可 - -![输入图片说明](https://foruda.gitee.com/images/1678979236772683145/9fa27e5b_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678979240831458322/8e1fac4b_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/mail.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/mail.md deleted file mode 100644 index 6e7413ed..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/mail.md +++ /dev/null @@ -1,17 +0,0 @@ -# 邮件功能 -- - - -## 配置功能 - -版本: v4.2.0 提供邮件功能 - -修改配置文件 - -![输入图片说明](https://foruda.gitee.com/images/1663555260932007318/fabb2bfa_1766278.png "屏幕截图") - -* `enabled` 为邮件功能开关 - -## 功能使用 - -参考 `demo` 模块 `MailController` 邮件演示案例 - -![输入图片说明](https://foruda.gitee.com/images/1663555374113593089/885b4db2_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/maxkey.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/maxkey.md deleted file mode 100644 index a0b51d24..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/maxkey.md +++ /dev/null @@ -1,20 +0,0 @@ -# 对接 MaxKey 单点登录 -- - - - -# 安装 MaxKey 应用服务 - -参考 MaxKey 官方文档安装 [MaxKey安装部署](http://www.maxkey.top/doc/docs/intro/) - -# 配置应用 OAuth2.0 认证注册 - -![输入图片说明](https://foruda.gitee.com/images/1693377802128677240/0927270a_1766278.png "屏幕截图") - -# 配置后端服务 - -找到框架 `application-环境.yml` 配置文件 - -修改 `maxkey` 对应的 `client-id` 与 `client-secret` - -![输入图片说明](https://foruda.gitee.com/images/1693378118762354596/2f02c8a3_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1693378168538263792/24476d2a_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sensitive.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sensitive.md deleted file mode 100644 index 3e2d92ba..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sensitive.md +++ /dev/null @@ -1,33 +0,0 @@ -# 数据脱敏 -- - - -## 功能说明 - -系统使用 `Jackson` 序列化策略 对标注了 `Sensitive` 注解的属性进行脱敏处理 - -## 使用教程 - -> 使用注解标注需要脱敏的字段 选择对应的策略 - -![输入图片说明](https://foruda.gitee.com/images/1699523591703893602/ffd6dba2_1766278.png "屏幕截图") - -* strategy 脱敏策略 -* roleKey 角色code(判断用户是否拥有角色权限) -* perms 权限code(判断用户是否拥有标识符权限) - -![输入图片说明](https://foruda.gitee.com/images/1678979315796014155/614adf91_1766278.png "屏幕截图") - -> 可再 `SensitiveStrategy` 内自定义策略 - -![输入图片说明](https://foruda.gitee.com/images/1678979319996224858/3b3e3c8b_1766278.png "屏幕截图") - -## 脱敏逻辑修改 - -> 系统使用通用接口处理是否需要脱敏 多个系统可以自定义不同的脱敏逻辑实现 - -![输入图片说明](https://foruda.gitee.com/images/1678979325448998856/b262e425_1766278.png "屏幕截图") - -> 系统默认处理逻辑为 根据角色与标识符或非管理员脱敏 可自行修改默认实现 - -![输入图片说明](https://foruda.gitee.com/images/1699523752627488891/f82f2f50_1766278.png "屏幕截图") - - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/skywalking.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/skywalking.md deleted file mode 100644 index 283eb221..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/skywalking.md +++ /dev/null @@ -1,20 +0,0 @@ -# Skywalking链路监控 -- - - -## skywalking服务搭建 - -参考文章: https://lionli.blog.csdn.net/article/details/127656534
-多种搭建方式 也可以参考百度 - -## 代码改动 - -https://gitee.com/dromara/RuoYi-Vue-Plus/commit/4d02466fed4f3ea012a80c3359cde9af0737141f
-根据上方commit提交记录 开启注释掉的代码 - -## 本地使用 - -参考文章: https://lionli.blog.csdn.net/article/details/127656534 - -## docker部署使用 - -完成上方代码改动 将下载好的 `agent` 探针 放入服务器 `/docker/skywalking/agent/` 目录下 赋予所有权限即可
-![输入图片说明](https://foruda.gitee.com/images/1669032573170837535/d9901f53_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sms.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sms.md deleted file mode 100644 index a2308ecd..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sms.md +++ /dev/null @@ -1,51 +0,0 @@ -# 短信模块 -- - - - -# 配置功能 - -### 版本: >= v5.1.0 - -已完成 sms4j 项目整合 文档地址: https://sms4j.com/doc3 - -配置方式 具体厂商配置扩展 可以查看sms4j文档 - -![输入图片说明](https://foruda.gitee.com/images/1705573035997239848/2ca8512d_1766278.png "屏幕截图") - -使用方式 参考文档各种写法 下方为 demo 模块提供示例 - -![输入图片说明](https://foruda.gitee.com/images/1705573001447394180/2bd726d0_1766278.png "屏幕截图") - -### 版本: v4.2.0 提供短信模块 - -短信模块采用SPI加载
-使用哪家的短信 引入哪家的依赖 即可动态加载
-目前支持: `阿里云` `腾讯云` 欢迎扩展PR其他 - -> 参考 `ruoyi-demo` pom文件写法 - -![输入图片说明](https://foruda.gitee.com/images/1678979157797419426/cc9b7444_1766278.png "屏幕截图") - -> 修改配置文件 - -![输入图片说明](https://foruda.gitee.com/images/1678979163029635375/e5fd6e20_1766278.png "屏幕截图") - -* `enabled` 为短信功能开关 -* `endpoint` 为域名 各厂家域名固定 按照文档配置即可 -* `accessKeyId` 密钥id -* `accessKeySecret` 密钥密匙 -* `signName` 签名 -* `sdkAppId` 应用id 腾讯专用 - -## 功能使用 - -参考 `demo` 模块 `SmsController` 短信演示案例
-功能采用 `模板模式` 动态加载对应厂家的工具模板
-引入 `SmsTemplate` 即可使用 - -![输入图片说明](https://foruda.gitee.com/images/1678979168699323982/e9301e84_1766278.png "屏幕截图") - -## 重点须知 - -由于各厂家参数解析不一致 请遵守以下规则 - -![输入图片说明](https://foruda.gitee.com/images/1678979172581090456/ac1f10e8_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sse.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sse.md deleted file mode 100644 index 8a738329..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/sse.md +++ /dev/null @@ -1,22 +0,0 @@ -# SSE功能 -- - - - -## 框架版本 >= 5.2.2 - -## 配置说明 - -![输入图片说明](https://foruda.gitee.com/images/1721986820599622433/1abe5d60_1766278.png "屏幕截图") - -* enabled 是否开启此功能 -* path 应用路径 - -## 使用方法 - -前端连接方式: `http://后端ip:端口/resource/sse?clientid=import.meta.env.VITE_APP_CLIENT_ID&Authorization=Bearer eyJ0eXAiO......` - -其中 `Authorization` 为请求token需要登录后获取 连接成功之后 与框架内其他获取登录用户方式一致 - -`SseMessageUtils.sendMessage` 推送单机消息(特殊需求使用)
-`SseMessageUtils.subscribeMessage` 订阅分布式消息(框架初始化已订阅)
-`SseMessageUtils.publishMessage` 发布分布式消息(推荐使用 所有集群内寻找到接收人)
-`SseMessageUtils.publishAll` 群发消息给所有连接人
\ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/topiam.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/topiam.md deleted file mode 100644 index 4778d0f9..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/topiam.md +++ /dev/null @@ -1,30 +0,0 @@ -# 对接 TOPIAM 单点登录 -- - - - -# 安装 TOPIAM 应用服务 - -参考 TOPIAM 官方文档安装 [TOPIAM安装部署](https://eiam.topiam.cn/docs/deployment/) - -# 配置 OIDC 应用 - -在 `登录 Redirect URI` 中填写 `http://localhost:80/oauth/callback?source=topiam` - -# 配置后端服务 - -找到框架 `application-环境.yml` 配置文件 - -修改 `topiam` 对应的 `client-id` 与 `client-secret` - -```yaml -justauth: - # 前端外网访问地址 - address: http://localhost:80 - type: - 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] -``` \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/translation.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/translation.md deleted file mode 100644 index 547b15ff..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/translation.md +++ /dev/null @@ -1,34 +0,0 @@ -# 翻译功能 -- - - -## 框架版本 >= 4.6.0 -## 注解 - -![输入图片说明](https://foruda.gitee.com/images/1675575648043199227/d04b3e21_1766278.png "屏幕截图") - -`@Translation` 翻译注解 用于实体类字段上
-`@TranslationType` 翻译类别注解 用于实现类上标注与 `@Translation` 注解相同的 `type` 类型 实现翻译功能 - - -## 用法说明 - -默认提供功能 `用户id转账号(用户名)` `部门id转名称` `字典type转label` `ossId转url` - -![输入图片说明](https://foruda.gitee.com/images/1675575977860232549/143b74f8_1766278.png "屏幕截图") - -用户名翻译(映射翻译) 根据另一个映射字段 翻译保存到此字段 - -![输入图片说明](https://foruda.gitee.com/images/1675576044011477847/13eb9f57_1766278.png "屏幕截图") - -ossUrl翻译(直接翻译) 直接根据此字段值翻译后替换此字段值 - -![输入图片说明](https://foruda.gitee.com/images/1675576265894720924/70792f66_1766278.png "屏幕截图") - -字典翻译(其他扩展条件翻译) 根据`other`条件 自行定义如何使用 例如字典翻译`other`条件就是字典的唯一值 - -![输入图片说明](https://foruda.gitee.com/images/1675576391012282823/f95c5d78_1766278.png "屏幕截图") - -## 自定义扩展 - -实现接口 `TranslationInterface` 标注注解 `@TranslationType` 可参考框架默认实现 - -![输入图片说明](https://foruda.gitee.com/images/1676735436673932715/c3caa8d7_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/websocket.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/websocket.md deleted file mode 100644 index 9e74e1e3..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/extend/websocket.md +++ /dev/null @@ -1,37 +0,0 @@ -# WebSocket功能 -- - - - -## 框架版本 >= 5.1.0 - -## 配置说明(默认关闭 推送建议使用SSE) - -![输入图片说明](https://foruda.gitee.com/images/1688356273985385949/5e4d1de8_1766278.png "屏幕截图") - -* enabled 是否开启此功能 -* path 应用路径 -* allowedOrigins 设置访问源地址 - -**重点: 如关闭ws功能需连同前端ws开关一同关闭 不然前端启动会报错** - -![输入图片说明](https://foruda.gitee.com/images/1700644877512019497/052d2f46_1766278.png "屏幕截图") - -## 使用方法 - -前端连接方式: `ws://后端ip:端口/resource/websocket?clientid=import.meta.env.VITE_APP_CLIENT_ID&Authorization=Bearer eyJ0eXAiO......` - -**由于js不支持请求头传输故而采用参数传输 如支持请求头传输建议使用请求头传输** - -传输方式: -```js -headers: { - Authorization: "Bearer " + getToken(), - clientid: import.meta.env.VITE_APP_CLIENT_ID -} -``` - -其中 `Authorization` 为请求token需要登录后获取 连接成功之后 与框架内其他获取登录用户方式一致 - -`WebSocketUtils.sendMessage` 推送单机消息(特殊需求使用)
-`WebSocketUtils.subscribeMessage` 订阅分布式消息(框架初始化已订阅)
-`WebSocketUtils.publishMessage` 发布分布式消息(推荐使用 所有集群内寻找到接收人)
-`WebSocketUtils.publishAll` 群发消息给所有连接人
\ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/tree.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/tree.md deleted file mode 100644 index 329cce21..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/framework/tree.md +++ /dev/null @@ -1,61 +0,0 @@ -# 目录结构 -- - - -v5.2.2 -~~~ -RuoYi-Vue-Plus -├─ ruoyi-admin // 管理模块 [8080,28080] -│ └─ RuoYiApplication // 启动类 -│ └─ RuoYiServletInitializer // 容器部署初始化类 -│ └─ resources // 资源文件 -│ └─ i18n/messages.properties // 国际化配置文件 -│ └─ application.yml // 框架总配置文件 -│ └─ application-dev.yml // 开发环境配置文件 -│ └─ application-prod.yml // 生产环境配置文件 -│ └─ banner.txt // 框架启动图标 -│ └─ logback-plus.xml // 日志配置文件 -│ └─ ip2region.xdb // IP区域地址库 -├─ ruoyi-extend // 扩展模块 -│ └─ ruoyi-monitor-admin // admin监控模块 [9090] -│ └─ ruoyi-snailjob-server // 任务调度中心模块 [8800,17888] -├─ ruoyi-common // 通用模块 -│ └─ ruoyi-common-bom // common依赖包管理 -│ └─ ruoyi-common-core // 核心模块 -│ └─ ruoyi-common-doc // 系统接口模块 -│ └─ ruoyi-common-encrypt // 数据加解密模块 -│ └─ ruoyi-common-excel // excel模块 -│ └─ ruoyi-common-idempotent // 幂等功能模块 -│ └─ ruoyi-common-job // 定时任务模块 -│ └─ ruoyi-common-json // 序列化模块 -│ └─ ruoyi-common-log // 日志模块 -│ └─ ruoyi-common-mail // 邮件模块 -│ └─ ruoyi-common-mybatis // 数据库模块 -│ └─ ruoyi-common-oss // oss服务模块 -│ └─ ruoyi-common-ratelimiter // 限流功能模块 -│ └─ ruoyi-common-redis // 缓存服务模块 -│ └─ ruoyi-common-satoken // satoken模块 -│ └─ ruoyi-common-security // 安全模块 -│ └─ ruoyi-common-sensitive // 脱敏模块 -│ └─ ruoyi-common-sms // 短信模块 -│ └─ ruoyi-common-social // 社交三方模块 -│ └─ ruoyi-common-sse // sse流推送模块 -│ └─ ruoyi-common-tenant // 租户模块 -│ └─ ruoyi-common-translation // 通用翻译模块 -│ └─ ruoyi-common-web // web模块 -│ └─ ruoyi-common-websocket // websocket服务集成模块 -├─ ruoyi-modules // 模块组 -│ └─ ruoyi-demo // 演示模块 -│ └─ ruoyi-generator // 代码生成模块 -│ └─ ruoyi-job // 任务调度服务 -│ └─ ruoyi-system // 业务模块 -│ └─ ruoyi-workflow // 工作流模块 -├─ plus-ui // 前端框架 [80] -├─ script // 系统脚本包 -│ └─ bin // 运行脚本包 -│ └─ docker // docker相关脚本 -│ └─ sql // sql脚本 -├─ .run // 执行脚本文件 -├─ .editorconfig // 编辑器编码格式配置 -├─ LICENSE // 开源协议 -├─ pom.xml // 公共依赖 -├─ README.md // 框架说明文件 -~~~ \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/home.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/home.md deleted file mode 100644 index ff4de9ef..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/home.md +++ /dev/null @@ -1,127 +0,0 @@ - -
- -- - - -# 平台简介 -
- -[![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus) -[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus) -[![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) -
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.2.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) -[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.2-blue.svg)]() -[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() -[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() - -> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架) - -> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可
-活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 - -# 本框架与RuoYi的功能差异 - -| 功能 | 本框架 | RuoYi | -|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| -| 前端项目 | 采用 Vue3 + TS + ElementPlus 重写 | 基于Vue2/Vue3 + JS | -| 后端项目结构 | 采用插件化 + 扩展包形式 结构解耦 易于扩展 | 模块相互注入耦合严重难以扩展 | -| 后端代码风格 | 严格遵守Alibaba规范与项目统一配置的代码格式化 | 代码书写与常规结构不同阅读障碍大 | -| Web容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat | -| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 | -| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验
角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 | -| 三方鉴权 | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证 | 无 | -| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer
可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例) | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 | -| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 | -| Redis客户端 | 采用 Redisson Redis官方推荐 基于Netty的客户端工具
支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan
支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐
连接池采用 common-pool Bug多经常性出问题 | -| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能
例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写Redis代码逻辑 | -| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多
例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL | -| SQL监控 | 采用 p6spy 可输出完整SQL与执行时间监控 | log输出 需手动拼接sql与参数无法快速查看调试问题 | -| 数据分页 | 采用 Mybatis-Plus 分页插件
框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序 | 采用 PageHelper 仅支持单查询分页 参数只能从param传 只能单排序 功能扩展性差 体验不好 | -| 数据权限 | 采用 Mybatis-Plus 插件 自行分析拼接SQL 无感式过滤
只需为Mapper设置好注解条件 支持多种自定义 不限于部门角色 | 采用 注解+aop 实现 基于部门角色 生成的sql兼容性差 不支持其他业务扩展
生成sql后需手动拼接到具体业务sql上 对于多个Mapper查询不起作用 | -| 数据脱敏 | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件
支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展 | 无 | -| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密
支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 | -| 接口传输加密 | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性 | 无 | -| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译
支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 | -| 多数据源框架 | 采用 dynamic-datasource 支持市面大部分数据库
通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源
支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 | -| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 | -| 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 | -| 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 | -| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物 | 无 | -| SSE推送 | 采用 Spring SSE 实现 扩展了Token鉴权与分布式会话同步 | 无 | -| 序列化 | 采用 Jackson Spring官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 | -| 分布式幂等 | 参考美团GTIS防重系统简化实现(细节可看文档) | 手动编写注解基于aop实现 | -| 分布式锁 | 采用 Lock4j 底层基于 Redisson | 无 | -| 分布式任务调度 | 采用 SnailJob 天生支持分布式 统一的管理中心 支持多种数据库 支持分片重试DAG任务流等 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 | -| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储
支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 | -| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家 | 不支持 | -| 短信 | 采用 sms4j 短信融合包 支持数十种短信厂家 只需在yml配置好厂家密钥即可使用 可多厂家共用 | 不支持 | -| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 | -| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释
只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 | -| 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 | -| Excel框架 | 采用 Alibaba EasyExcel 基于插件化
框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 | -| 工作流支持 | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能 | 无 | -| 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 | -| 监控框架 | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制
实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 | -| 链路追踪 | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗
用了它即可实时查看请求经过的每一处每一个节点 | 无 | -| 代码生成器 | 只需设计好表结构 一键生成所有crud代码与页面
降低80%的开发量 把精力都投入到业务设计上
框架为其适配MP、SpringDoc规范化代码 同时支持动态多数据源代码生成 | 代码生成原生结构 只支持单数据源生成 | -| 部署方式 | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼 | 原生jar部署 其他环境需手动下载安装 自行搭建 | -| 项目路径修改 | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的 | 需要做很多改造 文档说明有限 | -| 国际化 | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化 | 只提供基础功能 其他需自行编写扩展 | -| 代码单例测试 | 提供单例测试 使用方式编写方法与maven多环境单测插件 | 只提供基础功能 其他需自行编写扩展 | -| Demo案例 | 提供框架功能的实际使用案例 单独一个模块提供了很多很全 | 无 | - - -## 本框架与RuoYi的业务差异 - -| 业务 | 功能说明 | 本框架 | RuoYi | -|--------|----------------------------------------------------------------------|-----|------------------| -| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 | -| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 | -| 客户端管理 | 系统内对接的所有客户端管理 如: pc端、小程序端等
支持动态授权登录方式 如: 短信登录、密码登录等 支持动态控制token时效 | 支持 | 无 | -| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 | -| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 | -| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 | -| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 | -| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 | -| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 | -| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 | -| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 | -| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 | -| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 | -| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 | -| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 | -| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 | -| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 | -| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载 | 支持 | 仅支持单数据源 | -| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 | -| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 | -| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 | -| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 | -| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 | - - -## 演示图例 - -| | | -|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| -| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png "屏幕截图") | -| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") | - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/4.Xinit.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/4.Xinit.md deleted file mode 100644 index 73a6dbc4..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/4.Xinit.md +++ /dev/null @@ -1,67 +0,0 @@ -# 4.X项目初始化 -- - - -### 项目分支说明 -`4.X` 主分支 4.X版本 稳定发布分支
-`fast` 单体分支 功能与主分支相同 结构为单模块
-`dev` 开发分支 代码随时更新 不推荐使用 经测试后会发布到主分支
-`future/*` 新功能预览分支
- -### 项目必备环境 -> 推荐使用 `docker` 安装 项目内置 `docker` 编排文件 -* oracle jdk 8 11 (暂时不支持 17 不支持大于 jdk8_202 因为202是最后一个免费版本) -* mysql 5.7 8.0 (5.6未适配可能会有问题) -* oracle 11g 12c -* postgres 13 14 -* sqlserver 2017 2019 -* redis 5.X 6.X 由于框架大量使用了redis特性 版本必须 >= 5.X ([win redis 下载地址](https://github.com/tporadowski/redis)) -* minio 本地文件存储 或 阿里云 腾讯云 七牛云等一切支持S3协议的云存储 -* maven 3.6.3 3.8.X -* nodejs >= 12 < 18 -* npm 6.X 8.X (7.X确认有问题) - -### 3.2.0及以上 只需勾选对应环境即可 -![输入图片说明](https://foruda.gitee.com/images/1678976284045210056/a2f28d33_1766278.png "屏幕截图") - -### 默认 `JDK1.8` 如有变动 需更改以下配置 - -![输入图片说明](https://foruda.gitee.com/images/1681017282888708602/09da902f_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1681017287160367631/3a033268_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1681017292933832275/0bdf875e_1766278.png "屏幕截图") - -### sql导入 - -请按照以下顺序依次导入 - -![输入图片说明](https://foruda.gitee.com/images/1681017239000759855/ad43a5b2_1766278.png "屏幕截图") - -默认为 `mysql` 其他数据库需导入对应的sql文件 - -![输入图片说明](https://foruda.gitee.com/images/1681017245687923510/1b444bc4_1766278.png "屏幕截图") - -**多数据库仅支持主应用 扩展应用需自行适配(例如: xxl-job仅支持mysql)** - -### 服务启动顺序说明 - -1. 必须启动基础建设: mysql redis admin
-2. 可选启动基础建设: minio(影响文件上传) monitor(影响监控) xxljob(影响定时任务)
- -![输入图片说明](https://foruda.gitee.com/images/1678976302776168895/7333341c_1766278.png "屏幕截图") - -* `MonitorAdminApplication` 为 Admin监控服务(非必要 可参考对应文档关闭) -* `XxlJobAdminApplication` 为 任务调度中心服务(非必要 可参考对应文档关闭) -* `RuoYiApplication` 为 主应用服务 -> 需优先启动 `MonitorAdminApplication` 与 `XxlJobAdminApplication` 具体配置方式参考对应文档 -> 最后启动 主服务 `RuoYiApplication` - -### 主服务配置方式 - -在勾选对应环境的配置文件内 填写 mysql 与 redis 配置信息 - -![输入图片说明](https://foruda.gitee.com/images/1678941357316005626/70559736_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1681017185596156350/d4607b5b_1766278.png "屏幕截图") - -其他数据库配置 按照系统自带的配置更改即可 - -![输入图片说明](https://foruda.gitee.com/images/1678941444707120259/b274592a_1766278.png "屏幕截图") - - diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/5.Xnew.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/5.Xnew.md deleted file mode 100644 index a966adad..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/5.Xnew.md +++ /dev/null @@ -1,5 +0,0 @@ -### 视频讲解 - -[RuoYi-Vue-Plus 5.0.0 新功能与变更介绍](https://www.bilibili.com/video/BV1Us4y1m7ky/) - -[RuoYi-Vue-Plus 5.1.0 新功能与变更介绍](https://www.bilibili.com/video/BV1fj411y71X/) diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/admin_init.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/admin_init.md deleted file mode 100644 index 0a317a83..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/admin_init.md +++ /dev/null @@ -1,32 +0,0 @@ -# 搭建Admin监控 -- - - -### 配置监控客户端 - -> 修改主服务配置文件 - -![输入图片说明](https://foruda.gitee.com/images/1678941504260707700/68ab99e5_1766278.png "屏幕截图") - -* `enabled` 可启用或关闭客户端注册 -* `url` 为监控中心地址 -* `username 与 password` 为监控中心的账号密码 - -### 启用监控中心 -在 `扩展项目 -> 监控模块` 启动 - -![输入图片说明](https://foruda.gitee.com/images/1678976327174539378/df97e36e_1766278.png "屏幕截图") - -在监控模块对应的 `yml` 配置文件 可设置登录的账号密码与访问路径 - -![输入图片说明](https://foruda.gitee.com/images/1678941572583282843/28117457_1766278.png "屏幕截图") - -### 前端修改admin监控访问路径 -`dev`环境 默认使用 `.env.development` 配置文件内地址 - -![输入图片说明](https://foruda.gitee.com/images/1678941607472644388/460e8eea_1766278.png "屏幕截图") - -`prod`环境 使用 `.env.production` 本机路由 - -![输入图片说明](https://foruda.gitee.com/images/1678941644784144830/6293ab1c_1766278.png "屏幕截图") -故而 `prod` 环境只需更改 `nginx` 反向代理路径即可 - -![输入图片说明](https://foruda.gitee.com/images/1678981483900657668/31fd1aad_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/deploy.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/deploy.md deleted file mode 100644 index 37e184c4..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/deploy.md +++ /dev/null @@ -1,126 +0,0 @@ -# 应用部署 -- - - -## 版本 >= 4.3.0 - -### 请优先阅读 [idea环境配置](/ruoyi-vue-plus/quickstart/idea_environment.md) - -## 手动部署 - -在服务器安装 `mysql` `redis` `nginx` `minio` - -将项目内 `script/docker/nginx/nginx.conf` 配置文件 复制到 `nginx` 配置内
-将项目内 `script/docker/redis/redis.conf` 配置文件 复制到 `redis` 配置内 - -并修改相关参数如 `前端页面存放位置` `后端Ip地址` 等使其生效 - -jar包部署后端服务 打包命令如下 - -3.2.0及以上 -```mvn -mvn clean package -D maven.test.skip=true -P prod -``` -服务器需创建临时文件存储目录与配置文件对应(无此目录上传文件会报错) - -![输入图片说明](https://foruda.gitee.com/images/1659951373949149804/屏幕截图.png "屏幕截图.png") - -前端参考下方前端部署章节 - -## 部署视频 - -[RuoYi-Vue-Plus 5.0 生产环境搭建部署](https://www.bilibili.com/video/BV1mL411e7ha/) - -## docker 后端部署 - -### 请优先阅读 [idea环境配置](/ruoyi-vue-plus/quickstart/idea_environment.md) - -**重点: 一知半解的必看** -> [docker安装](https://lionli.blog.csdn.net/article/details/83153029)
-> [docker-compose安装](https://lionli.blog.csdn.net/article/details/111220320)
-> [docker网络模式讲解](https://lionli.blog.csdn.net/article/details/109603785)
-> [docker 开启端口 2375 供外部程序访问](https://lionli.blog.csdn.net/article/details/92627962) - -### 将配置使用FTP上传到根目录 -idea拖拽文件到远程目录即可上传 - -![输入图片说明](https://foruda.gitee.com/images/1662109450908169859/eaac9299_1766278.png "屏幕截图") - -### 给docker分配文件夹权限 -**重点注意: 一定要确保目录 `/docker` 及其所有子目录 具有写权限 如果后续出现权限异常问题 重新执行一遍分配权限** - -![输入图片说明](https://foruda.gitee.com/images/1662109847279259882/3a2202c1_1766278.png "屏幕截图") -```shell -chmod -R 777 /docker -``` -### 构建应用镜像 - -**1.需要先使用maven打包成jar包** - -![输入图片说明](https://foruda.gitee.com/images/1662110477410977621/c6931c42_1766278.png "屏幕截图") - -**2.执行构建** -> 项目初始化后会自动生成构建镜像的运行配置
-> 配置好docker连接之后 运行如下即可构建对应的应用镜像 - -**重点注意: idea2024及以上版本要求必须在本地安装docker才可以执行如下操作** - -![输入图片说明](https://foruda.gitee.com/images/1662110192257483752/0f754b47_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1662120004773449909/9fdef59c_1766278.png "屏幕截图") - -**3.结构讲解** -右键编辑 即可看到内部配置 - -![输入图片说明](https://foruda.gitee.com/images/1662458355500139498/eaa26036_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1662458446794722159/32c086a7_1766278.png "屏幕截图") - - -### 创建基础服务 - -```shell -docker-compose up -d mysql nginx-web redis minio -``` - -### 创建业务服务(需要先构建服务镜像) - -4.X -```shell -docker-compose up -d ruoyi-monitor-admin ruoyi-xxl-job-admin ruoyi-server1 ruoyi-server2 -``` - -5.X -```shell -docker-compose up -d ruoyi-monitor-admin ruoyi-snailjob-server ruoyi-server1 ruoyi-server2 -``` - -### docker其他操作(idea的docker插件 推荐使用) -![输入图片说明](https://foruda.gitee.com/images/1662458271941863770/cd180a04_1766278.png "屏幕截图") - -## 前端部署 - -执行打包命令 -```shell -# 打包正式环境 -npm run build:prod -``` -打包后生成打包文件在 `ruoyi-ui/dist` 目录 -将 `dist` 目录下文件(不包含 `dist` 目录) 上传到部署服务器 `docker/nginx/html` 目录下(手动部署放入自己配置的路径即可) - -![输入图片说明](https://foruda.gitee.com/images/1662110914769648699/07f344c4_1766278.png "屏幕截图") - -重启 `nginx` 服务即可 - - -### 如需更改后端代理路径或者后端ip地址的话往下看 - -更改`nginx.conf`配置文件代理路径(注意: /开头/结尾) - -![输入图片说明](https://foruda.gitee.com/images/1660185698211067202/屏幕截图.png "屏幕截图.png") - -更改前端`.env.环境` 文件内的 `VITE_APP_BASE_API` - -![输入图片说明](https://foruda.gitee.com/images/1724318035232137124/5d035a09_1766278.png "屏幕截图") - -更改`nginx.conf`配置文件后端ip地址 - -![输入图片说明](https://foruda.gitee.com/images/1660185711265558730/屏幕截图.png "屏幕截图.png") diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/extend_project.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/extend_project.md deleted file mode 100644 index 0c6f33ac..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/extend_project.md +++ /dev/null @@ -1,49 +0,0 @@ -# 基于 RuoYi-Vue-Plus 的扩展项目列表 -- - - -### 精品PR 欢迎投稿 -| 功能介绍 | PR地址 | -|-------------------------------------|------------------------------------------------------| -| 拖拽图片调整显示顺序 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/173 | -| 增加Jasypt加密库对配置文件加密 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/177 | -| 使用富文本wangeditor5替换Quill | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/213 | -| 集成screw数据库文档功能模块 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/42 | -| Excel导入模板增加批注支持 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/222 | -| 压缩包处理工具 支持本地文件/目录+oss文件/网络文件混合 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/44 | -| 添加websocket模块 支持satoken鉴权 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/65 | -| 数据库字段加解密(支持 base64 aes rsa sm2 sm4) | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/274 | -| 增加liquibase迁移数据库 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/299 | -| 增加OSS模块支持本地环境 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/353 | -| 扩展模块独立集成flyway | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/439 | -| 扩展模块独立集成go-view大屏看板 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/445 | -| 基于AmazonS3协议的分片上传 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/130 | -| 扩展forest http客户端 声明式http请求 二次封装像工具类 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/547 | -| 增加短链接生成工具 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/515 | -| 新增oss预签名上传工具组合使用异步客户端分片 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/522 | -| 新增规则引擎LiteFlow,SQL持久化接入,支持可视化页面 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/552 | -| 一键部署到私有Nexus仓库 | https://gitee.com/dromara/RuoYi-Cloud-Plus/pulls/181 | -| 服务状态监控发送邮件钉钉等 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/568 | -| 登录验证支持2FA验证 | https://gitee.com/dromara/RuoYi-Vue-Plus/pulls/578 | - -### 项目介绍+项目地址 欢迎投稿 - - -| 项目介绍 | 项目地址 | -|--------------------------------|---------------------------------------------------------------------------| -| 微服务扩展 | https://gitee.com/dromara/RuoYi-Cloud-Plus | -| Plus学习笔记 | https://zhonglingyuxiu1028.github.io/zlyx-space/#/ruoyi-vue-plus/home | -| 基于uniapp+TmUI从0开发 支持H5/小程序/安卓 | https://gitee.com/dapppp/ruoyi-plus-miniapp | -| 基于RuoYi-App框架二次修改使用Uniapp+Vue3 | https://gitee.com/wangying110166/ruo-yi-uni-app-plus | -| 基于RuoYi-App框架对接Plus后端 | https://gitee.com/FnTop/RuoYi-App-Plus | -| 基于vben(ant-design-vue)前端项目 | https://gitee.com/dapppp/ruoyi-plus-vben | -| 基于vue-next-admin的vue3+ts前端 | https://gitee.com/thiszhc/RuoYi-Vue3-UI | -| 集成GoView版本 | https://gitee.com/kdwqjwgqxx/RuoYi-Vue-Plus-GoView | -| mybatis-flex版本 | https://gitee.com/dataprince/ruoyi-flex | -| blog博客系统 | https://gitee.com/kalashok-pan/zhi-blog-plus | -| tdengine时序数据库扩展 | https://gitee.com/zhangbg/ruoyi-plus-tdengine | -| 重构项目结构(参考springboot源码) | https://gitee.com/denghuafeng/ruoyi-boot-plus | -| Activiti扩展 | https://gitee.com/sgs98/RuoYi-Vue-Plus-Activiti | -| flowable扩展 | https://gitee.com/sgs98/RuoYi-Vue-Plus-Flowable | -| flowable扩展 | https://gitee.com/KonBAI-Q/ruoyi-flowable-plus | -| mybatis-flex版本 | https://gitee.com/yhan219/ruoyi-vue-flex | -| weblog博客系统 | https://gitee.com/fu-zhanshuai/fzshuai-weblog | -| Annlcc博客 | https://gitee.com/ahcode/ann-blog-plus | \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/idea_environment.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/idea_environment.md deleted file mode 100644 index 5995e0ff..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/idea_environment.md +++ /dev/null @@ -1,50 +0,0 @@ -# idea环境配置 -- - - -## 配置项目编码 -![输入图片说明](https://foruda.gitee.com/images/1662107706295343419/e27065a9_1766278.png "屏幕截图") - -## 配置运行看板 -![输入图片说明](https://foruda.gitee.com/images/1662108673306567278/8af97b47_1766278.png "屏幕截图") -### 配置spring与docker看板 -![输入图片说明](https://foruda.gitee.com/images/1662111392476935892/6b6760fb_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1662108865191892425/3c045999_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1662108877322329668/ddb6d93d_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1662108894122798039/6a53a38c_1766278.png "屏幕截图") - -## 配置服务器SSH连接 -进入 `Settings -> Tools -> SSH Configurations` 点击加号创建SSH连接配置
-填写 服务器IP 用户名 密码 端口号 点击 Test Connection 测试连接 - -![输入图片说明](https://foruda.gitee.com/images/1662107776533098115/bd78467b_1766278.png "屏幕截图") - -使用Terminal 工具 点击箭头找到上方创建的SSH连接配置
-选择即可进入SSH连接界面 在这里可以对服务器进行命令操作 - -![输入图片说明](https://foruda.gitee.com/images/1662108010120640495/c70f9f9a_1766278.png "屏幕截图") - -## 配置服务器FTP连接 -进入 `Settings -> Build-> Deployment` 点击加号 选择SFTP 创建 FTP 连接配置
-选择之前创建好的SSH配置 点击 Test Connection 测试连接 - -![输入图片说明](https://foruda.gitee.com/images/1662107899553257979/e2eeb7fd_1766278.png "屏幕截图") - -在IDEA上方工具栏 找到 `Tools -> Deployment -> Browse Remote Host` 打开远程界面
-点击箭头找到我们上方配置的SFTP连接配置 即可连接到服务器的文件目录 - -![输入图片说明](https://foruda.gitee.com/images/1662107974682787233/b8a601fd_1766278.png "屏幕截图") - -## 配置Docker连接 -### 可操作远程docker与构建上传docker镜像(代替原来maven docker插件) -tcp连接需要开放服务器2375端口
-ssh需要使用上方的SSH连接配置
-建议使用SSH连接 - -![输入图片说明](https://foruda.gitee.com/images/1662108188005932060/75872bf8_1766278.png "屏幕截图") - -配置好之后 在运行窗口会多出一个Docker图标 双击即可连接远程docker
-可以查看容器实时日志 启动 重启 停止 等操作 - -![输入图片说明](https://foruda.gitee.com/images/1662108250902891875/b82d022b_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/init.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/init.md deleted file mode 100644 index dba09058..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/init.md +++ /dev/null @@ -1,73 +0,0 @@ -# 5.X项目初始化 -- - - -### 项目分支说明 - -`5.X` 主分支 5.X版本 稳定发布分支
-`dev` 开发分支 代码随时更新 不推荐使用 经测试后会发布到主分支
-`future/*` 新功能预览分支
- -### 项目必备环境 -> 推荐使用 `docker` 安装 项目内置 `docker` 编排文件 - -**注意: 禁止使用 `oraclejdk`(由于spring的bug导致打包运行会报错)** - -**Spring官方推荐使用JDK https://bell-sw.com/pages/downloads/** - -![输入图片说明](https://foruda.gitee.com/images/1720080025744223375/0213a652_1766278.png "屏幕截图") - -* openjdk-17/21 或 graalvm-community-jdk-17/21 [下载地址](https://github.com/graalvm/graalvm-ce-builds/releases) 版本 -* mysql 5.7 8.0 (其他版本未测试 如其他版本没问题 可以告知咱们) -* oracle >= 12c (其他版本未测试 如其他版本没问题 可以告知咱们) -* postgres 13 14 (其他版本未测试 如其他版本没问题 可以告知咱们) -* sqlserver 2017 2019 (其他版本未测试 如其他版本没问题 可以告知咱们) -* redis 5.X 6.X 7.X 由于框架大量使用了redis特性 版本必须 >= 5.X ([win redis 下载地址](https://github.com/zkteco-home/redis-windows)) -* minio 本地文件存储 或 阿里云 腾讯云 七牛云等一切支持S3协议的云存储 -* maven >= 3.8.X -* nodejs >= 18.18 (其他版本未测试 如其他版本没问题 可以告知咱们) -* npm >= 8.X (7.X确认有问题) -* idea 2022 2024 (一定不要使用2023后果自负 bug太多影响项目开发) - -### 搭建视频 - -[RuoYi-Vue-Plus 5.0 搭建与运行](https://www.bilibili.com/video/BV1Fg4y137JK/) - -### 勾选maven对应环境 -![输入图片说明](https://foruda.gitee.com/images/1678976284045210056/a2f28d33_1766278.png "屏幕截图") - -### 默认 `JDK17` 如有变动 需更改以下配置 - -![输入图片说明](https://foruda.gitee.com/images/1678941027820943505/c688e01e_1766278.png "屏幕截图") -![输入图片说明](https://foruda.gitee.com/images/1678941120518807034/4d56fcc9_1766278.png "屏幕截图") - -### sql导入 - -请按照以下顺序依次导入 默认为 `mysql` 其他数据库需导入对应的sql文件
-如需使用其他数据库 看这里 => [多数据库数据源](../framework/extend/dynamic_datasource.md)
- -![输入图片说明](https://foruda.gitee.com/images/1725853192789853346/a0d3f0b7_1766278.png "屏幕截图") - -### 服务启动顺序说明 - -1. 必须启动基础建设: mysql redis admin
-2. 可选启动基础建设: minio(影响文件上传) monitor(影响监控) snailjob(影响定时任务)
- -![输入图片说明](https://foruda.gitee.com/images/1716175484919688429/8b9a79b7_1766278.png "屏幕截图") - -* `MonitorAdminApplication` 为 Admin监控服务(非必要 可参考对应文档关闭 [搭建Admin监控](/ruoyi-vue-plus/quickstart/admin_init.md)) -* `SnailJobServerApplication` 为 任务调度中心服务(非必要 可参考对应文档关闭 [搭建调度中心](/ruoyi-vue-plus/quickstart/snail_job_init.md)) -* `DromaraApplication` 为 主应用服务 -> 需优先启动 `MonitorAdminApplication` 与 `SnailJobServerApplication` 具体配置方式参考对应文档
-> 最后启动 主服务 `DromaraApplication`
-> 工作流相关初始化使用 [工作流初始化](/ruoyi-vue-plus/quickstart/worker_init.md) - -### 主服务配置方式 - -在勾选对应环境的配置文件内 填写 mysql 与 redis 配置信息 - -![输入图片说明](https://foruda.gitee.com/images/1678941357316005626/70559736_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1678941405169571070/0d06a955_1766278.png "屏幕截图") - -其他数据库配置 按照系统自带的配置更改即可 - -![输入图片说明](https://foruda.gitee.com/images/1678941444707120259/b274592a_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/power_job_init.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/power_job_init.md deleted file mode 100644 index 863971b1..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/power_job_init.md +++ /dev/null @@ -1,48 +0,0 @@ -# 搭建PowerJob任务调度中心(5.X分支已废弃) -- - - -### 废弃原因 - -接到大量投诉 使用困难 用法诡异 各种问题等 - -### 配置调度中心客户端 -> 修改主服务配置文件 -> - -![输入图片说明](https://foruda.gitee.com/images/1687656939847353725/951c1af7_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1687335574708412835/41d6c9d7_1766278.png "屏幕截图") - -* `enabled` 可启用或关闭客户端注册 -* `server-address` 为调度中心地址 -* `app-name` 为执行器组账户名(需在调度中心注册方可登录查看) - -### 启用调度中心 -**需执行 powerjob.sql 默认账号密码 `ruoyi-worker` `123456` 账号在数据库里 可以在页面修改密码** -
- -![输入图片说明](https://foruda.gitee.com/images/1688634469876143273/c89455c0_1766278.png "屏幕截图") - -> 在 `扩展项目 -> powerjob-server模块` 启动 -> -![输入图片说明](https://foruda.gitee.com/images/1687335752250147336/17abe410_1766278.png "屏幕截图") - -> 需修改配置文件数据库连接地址(**注意: 此处为ruoyi-powerjob-server服务的配置文件**) -> -![输入图片说明](https://foruda.gitee.com/images/1687335802095066722/569d92be_1766278.png "屏幕截图") - -> 也可配置邮件发送 钉钉推送 和 mongodb存储 -> -![输入图片说明](https://foruda.gitee.com/images/1687335842722317559/f875c07a_1766278.png "屏幕截图") - -### 前端修改任务调度中心访问路径 -`dev`环境 默认使用 `.env.development` 配置文件内地址 - -![输入图片说明](https://foruda.gitee.com/images/1687335909698376722/7efa7539_1766278.png "屏幕截图") - -`prod`环境 使用 `.env.production` 本机路由 - -![输入图片说明](https://foruda.gitee.com/images/1687335937599399056/dd769ef5_1766278.png "屏幕截图") - -故而 `prod` 环境只需更改 `nginx` 反向代理路径即可 - -![输入图片说明](https://foruda.gitee.com/images/1687335979933648639/6a43b749_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/snail_job_init.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/snail_job_init.md deleted file mode 100644 index 4c6d353b..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/snail_job_init.md +++ /dev/null @@ -1,52 +0,0 @@ -# 搭建SnailJob任务调度中心(5.2.0新功能) -- - - - -### 视频介绍 - -[Snail job任务调度中心:轻松掌握任务管理、重试机制和任务编排](https://www.bilibili.com/video/BV19i421m7GL/) - -### 配置调度中心客户端 -> 修改主服务配置文件 -> - -![输入图片说明](https://foruda.gitee.com/images/1687656939847353725/951c1af7_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1716174758437043952/de28db71_1766278.png "屏幕截图") - -* `enabled` 可启用或关闭客户端注册 -* `server.address` 为调度中心地址 -* `server.port` 为调度中心通信端口 -* `token` 为组通信校验token(可在调度中心组配置更换) -* `group-name` 为执行器组 -* `namespace` 作用域(不同作用域相互隔离请勿填错) - -### 启用调度中心 -**需执行 snail_job.sql 默认账号密码 `admin` `admin` 账号在数据库里 可以在页面修改密码** -
- -![输入图片说明](https://foruda.gitee.com/images/1714355875395308961/adc21668_1766278.png "屏幕截图") - -> 在 `ruoyi-extend -> ruoyi-snailjob-server` 模块启动 -> -![输入图片说明](https://foruda.gitee.com/images/1716174842485474283/78cec86d_1766278.png "屏幕截图") - -> 需修改配置文件数据库连接地址(**注意: 此处为ruoyi-snailjob-server服务的配置文件 支持多种不同数据库**) -> -![输入图片说明](https://foruda.gitee.com/images/1714356048711590477/13289085_1766278.png "屏幕截图") - -### 快速入门 - -[Snailjob快速入门 基本使用介绍](https://juejin.cn/post/7412955032092442675) - -### 前端修改任务调度中心访问路径 -`dev`环境 默认使用 `.env.development` 配置文件内地址 - -![输入图片说明](https://foruda.gitee.com/images/1716174933143893408/58d47bbc_1766278.png "屏幕截图") - -`prod`环境 使用 `.env.production` 本机路由 - -![输入图片说明](https://foruda.gitee.com/images/1716174973454805690/0d6f20fb_1766278.png "屏幕截图") - -故而 `prod` 环境只需更改 `nginx` 反向代理路径即可 - -![输入图片说明](https://foruda.gitee.com/images/1716174998979181179/2f9e4e4a_1766278.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/worker_init.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/worker_init.md deleted file mode 100644 index da09117a..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/worker_init.md +++ /dev/null @@ -1,43 +0,0 @@ -# 工作流初始化 -- - - - -### 工作流使用及配置方式 - -1.找到项目中script下bpmn文件夹 - -![输入图片说明](https://foruda.gitee.com/images/1714211764058540441/5c8b97af_5363069.png "屏幕截图") - -2.启动项目找到流程定义通过**部署流程文件**将bpmn文件夹下**模型.zip**上传 - -![输入图片说明](https://foruda.gitee.com/images/1714211950485333575/1e2b3ff4_5363069.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714212113004821592/96586e69_5363069.png "屏幕截图") - -3.导入**模型.zip**后将会出现以下列表,默认使用**leave1**,test_leave为请假申请表名称 - -![输入图片说明](https://foruda.gitee.com/images/1714212222766335759/1227bbd6_5363069.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714212493602552742/9e0258b1_5363069.png "屏幕截图") - -**此处表名由来与表单源码内编写的表名保持一致方可互相绑定** - -![输入图片说明](https://foruda.gitee.com/images/1716447357161482917/2c9b1639_1766278.png "屏幕截图") - - -4.新增一条请假申请,提交后将会得到如下信息 - -![输入图片说明](https://foruda.gitee.com/images/1714212617432902105/3609f6ef_5363069.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714212630860787365/2922d38e_5363069.png "屏幕截图") - -5.关于如何切换一个新的流程使用,当前默认使用得KEY为leave1 ,我们切换到leave2使用,我们只需点击绑定业务将表名绑定,重新发起一个新的请假申请就可以得到一个新的流程信息 - -![输入图片说明](https://foruda.gitee.com/images/1714212876442323110/4554ea95_5363069.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714213037864274694/613149f5_5363069.png "屏幕截图") - -**此处表名由来与表单源码内编写的表名保持一致方可互相绑定** - -![输入图片说明](https://foruda.gitee.com/images/1716447357161482917/2c9b1639_1766278.png "屏幕截图") - -![输入图片说明](https://foruda.gitee.com/images/1714212963457174382/add768db_5363069.png "屏幕截图") \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/xxl_job_init.md b/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/xxl_job_init.md deleted file mode 100644 index f2751e9f..00000000 --- a/ruoyi-admin/src/main/resources/static/ruoyi-vue-plus/quickstart/xxl_job_init.md +++ /dev/null @@ -1,43 +0,0 @@ -# 搭建Xxl-Job任务调度中心(5.X分支已废弃) -- - - -### 废弃原因 - -长时间不维护 社区冰点 不支持jdk17 不支持boot3 不支持其他数据库等 - -### 配置调度中心客户端 -> 修改主服务配置文件 -> -![输入图片说明](https://foruda.gitee.com/images/1678941760168414366/b81e023b_1766278.png "屏幕截图") - -* `enabled` 可启用或关闭客户端注册 -* `admin-addresses` 为调度中心地址 -* `access-token` 为调度中心交互鉴权token -* `executor` 为执行器配置 一个客户端为一个执行器 可配置执行器集群 使用分片任务处理 - -### 启用调度中心 -**默认账号密码 `admin` `123456` 账号在数据库里 可以在页面修改密码** - -> 在 `扩展项目 -> xxl-job-admin模块` 启动 -> -![输入图片说明](https://foruda.gitee.com/images/1678976353500205883/058fef13_1766278.png "屏幕截图") - -> 需修改配置文件数据库连接地址(**注意: 此处为xxl-job-admin服务的配置文件**) -> -![输入图片说明](https://foruda.gitee.com/images/1678941813423551656/04c32a5b_1766278.png "屏幕截图") - -> 也可配置邮件发送 -> -![输入图片说明](https://foruda.gitee.com/images/1678941825447455298/1baa5e43_1766278.png "屏幕截图") - -### 前端修改任务调度中心访问路径 -`dev`环境 默认使用 `.env.development` 配置文件内地址 - -![输入图片说明](https://foruda.gitee.com/images/1678976378255854583/8cdbf4e3_1766278.png "屏幕截图") - -`prod`环境 使用 `.env.production` 本机路由 - -![输入图片说明](https://foruda.gitee.com/images/1678976382819019066/96288331_1766278.png "屏幕截图") - -故而 `prod` 环境只需更改 `nginx` 反向代理路径即可 - -![输入图片说明](https://foruda.gitee.com/images/1678976386764602366/55894f85_1766278.png "屏幕截图") diff --git a/ruoyi-admin/src/main/resources/static/static/css/vue.css b/ruoyi-admin/src/main/resources/static/static/css/vue.css deleted file mode 100644 index 847f385a..00000000 --- a/ruoyi-admin/src/main/resources/static/static/css/vue.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*{-webkit-font-smoothing:antialiased;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none;box-sizing:border-box}body:not(.ready){overflow:hidden}body:not(.ready) .app-nav,body:not(.ready)>nav,body:not(.ready) [data-cloak]{display:none}div#app{font-size:30px;font-weight:lighter;margin:40vh auto;text-align:center}div#app:empty:before{content:"Loading..."}img.emoji{height:1.2em}img.emoji,span.emoji{vertical-align:middle}span.emoji{font-family:Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:1.2em}.progress{background-color:#42b983;background-color:var(--theme-color,#42b983);height:2px;left:0;position:fixed;right:0;top:0;transition:width .2s,opacity .4s;width:0;z-index:999999}.search .search-keyword,.search a:hover{color:#42b983;color:var(--theme-color,#42b983)}.search .search-keyword{font-style:normal;font-weight:700}body,html{height:100%}body{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;color:#34495e;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:15px;letter-spacing:0;margin:0;overflow-x:hidden}img{max-width:100%}a[disabled]{cursor:not-allowed;opacity:.6}kbd{border:1px solid #ccc;border-radius:3px;display:inline-block;font-size:12px!important;line-height:12px;margin-bottom:3px;padding:3px 5px;vertical-align:middle}li input[type=checkbox]{margin:0 .2em .25em 0;vertical-align:middle}.app-nav{margin:25px 60px 0 0;position:absolute;right:0;text-align:right;z-index:10}.app-nav.no-badge{margin-right:25px}.app-nav p{margin:0}.app-nav>a{margin:0 1rem;padding:5px 0}.app-nav li,.app-nav ul{display:inline-block;list-style:none;margin:0}.app-nav a{color:inherit;font-size:16px;text-decoration:none;transition:color .3s}.app-nav a.active,.app-nav a:hover{color:#42b983;color:var(--theme-color,#42b983)}.app-nav a.active{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983)}.app-nav li{display:inline-block;margin:0 1rem;padding:5px 0;position:relative;cursor:pointer}.app-nav li ul{background-color:#fff;border:1px solid;border-color:#ddd #ddd #ccc;border-radius:4px;box-sizing:border-box;display:none;max-height:calc(100vh - 61px);overflow-y:auto;padding:10px 0;position:absolute;right:-15px;text-align:left;top:100%;white-space:nowrap}.app-nav li ul li{display:block;font-size:14px;line-height:1rem;margin:8px 14px;white-space:nowrap}.app-nav li ul a{display:block;font-size:inherit;margin:0;padding:0}.app-nav li ul a.active{border-bottom:0}.app-nav li:hover ul{display:block}.github-corner{border-bottom:0;position:fixed;right:0;text-decoration:none;top:0;z-index:1}.github-corner:hover .octo-arm{animation:octocat-wave .56s ease-in-out}.github-corner svg{color:#fff;fill:#42b983;fill:var(--theme-color,#42b983);height:80px;width:80px}main{display:block;position:relative;width:100vw;height:100%;z-index:0}main.hidden{display:none}.anchor{display:inline-block;text-decoration:none;transition:all .3s}.anchor span{color:#34495e}.anchor:hover{text-decoration:underline}.sidebar{border-right:1px solid rgba(0,0,0,.07);overflow-y:auto;padding:40px 0 0;position:absolute;top:0;bottom:0;left:0;transition:transform .25s ease-out;width:300px;z-index:20}.sidebar>h1{margin:0 auto 1rem;font-size:1.5rem;font-weight:300;text-align:center}.sidebar>h1 a{color:inherit;text-decoration:none}.sidebar>h1 .app-nav{display:block;position:static}.sidebar .sidebar-nav{line-height:2em;padding-bottom:40px}.sidebar li.collapse .app-sub-sidebar{display:none}.sidebar ul{margin:0 0 0 15px;padding:0}.sidebar li>p{font-weight:700;margin:0}.sidebar ul,.sidebar ul li{list-style:none}.sidebar ul li a{border-bottom:none;display:block}.sidebar ul li ul{padding-left:20px}.sidebar::-webkit-scrollbar{width:4px}.sidebar::-webkit-scrollbar-thumb{background:transparent;border-radius:4px}.sidebar:hover::-webkit-scrollbar-thumb{background:hsla(0,0%,53.3%,.4)}.sidebar:hover::-webkit-scrollbar-track{background:hsla(0,0%,53.3%,.1)}.sidebar-toggle{background-color:transparent;background-color:hsla(0,0%,100%,.8);border:0;outline:none;padding:10px;position:absolute;bottom:0;left:0;text-align:center;transition:opacity .3s;width:284px;z-index:30;cursor:pointer}.sidebar-toggle:hover .sidebar-toggle-button{opacity:.4}.sidebar-toggle span{background-color:#42b983;background-color:var(--theme-color,#42b983);display:block;margin-bottom:4px;width:16px;height:2px}body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}.content{padding-top:60px;position:absolute;top:0;right:0;bottom:0;left:300px;transition:left .25s ease}.markdown-section{margin:0 auto;max-width:80%;padding:30px 15px 40px;position:relative}.markdown-section>*{box-sizing:border-box;font-size:inherit}.markdown-section>:first-child{margin-top:0!important}.markdown-section hr{border:none;border-bottom:1px solid #eee;margin:2em 0}.markdown-section iframe{border:1px solid #eee;width:1px;min-width:100%}.markdown-section table{border-collapse:collapse;border-spacing:0;display:block;margin-bottom:1rem;overflow:auto;width:100%}.markdown-section th{font-weight:700}.markdown-section td,.markdown-section th{border:1px solid #ddd;padding:6px 13px}.markdown-section tr{border-top:1px solid #ccc}.markdown-section p.tip,.markdown-section tr:nth-child(2n){background-color:#f8f8f8}.markdown-section p.tip{border-bottom-right-radius:2px;border-left:4px solid #f66;border-top-right-radius:2px;margin:2em 0;padding:12px 24px 12px 30px;position:relative}.markdown-section p.tip:before{background-color:#f66;border-radius:100%;color:#fff;content:"!";font-family:Dosis,Source Sans Pro,Helvetica Neue,Arial,sans-serif;font-size:14px;font-weight:700;left:-12px;line-height:20px;position:absolute;height:20px;width:20px;text-align:center;top:14px}.markdown-section p.tip code{background-color:#efefef}.markdown-section p.tip em{color:#34495e}.markdown-section p.warn{background:rgba(66,185,131,.1);border-radius:2px;padding:1rem}.markdown-section ul.task-list>li{list-style-type:none}body.close .sidebar{transform:translateX(-300px)}body.close .sidebar-toggle{width:auto}body.close .content{left:0}@media print{.app-nav,.github-corner,.sidebar,.sidebar-toggle{display:none}}@media screen and (max-width:768px){.github-corner,.sidebar,.sidebar-toggle{position:fixed}.app-nav{margin-top:16px}.app-nav li ul{top:30px}main{height:auto;min-height:100vh;overflow-x:hidden}.sidebar{left:-300px;transition:transform .25s ease-out}.content{left:0;max-width:100vw;position:static;padding-top:20px;transition:transform .25s ease}.app-nav,.github-corner{transition:transform .25s ease-out}.sidebar-toggle{background-color:transparent;width:auto;padding:30px 30px 10px 10px}body.close .sidebar{transform:translateX(300px)}body.close .sidebar-toggle{background-color:hsla(0,0%,100%,.8);transition:background-color 1s;width:284px;padding:10px}body.close .content{transform:translateX(300px)}body.close .app-nav,body.close .github-corner{display:none}.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave .56s ease-in-out}}@keyframes octocat-wave{0%,to{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}section.cover{position:relative;align-items:center;background-position:50%;background-repeat:no-repeat;background-size:cover;min-height:100vh;width:100%;display:none}section.cover.show{display:flex}section.cover.has-mask .mask{background-color:#fff;opacity:.8;position:absolute;top:0;bottom:0;width:100%}section.cover .cover-main{flex:1;margin:0 16px;text-align:center;position:relative}section.cover a{color:inherit}section.cover a,section.cover a:hover{text-decoration:none}section.cover p{line-height:1.5rem;margin:1em 0}section.cover h1{color:inherit;font-size:2.5rem;font-weight:300;margin:.625rem 0 2.5rem;position:relative;text-align:center}section.cover h1 a{display:block}section.cover h1 small{bottom:-.4375rem;font-size:1rem;position:absolute}section.cover blockquote{font-size:1.5rem;text-align:center}section.cover ul{line-height:1.8;list-style-type:none;margin:1em auto;max-width:500px;padding:0}section.cover .cover-main>p:last-child a{border-radius:2rem;border:1px solid #42b983;border-color:var(--theme-color,#42b983);box-sizing:border-box;color:#42b983;color:var(--theme-color,#42b983);display:inline-block;font-size:1.05rem;letter-spacing:.1rem;margin:.5rem 1rem;padding:.75em 2rem;text-decoration:none;transition:all .15s ease}section.cover .cover-main>p:last-child a:last-child{background-color:#42b983;background-color:var(--theme-color,#42b983);color:#fff}section.cover .cover-main>p:last-child a:last-child:hover{color:inherit;opacity:.8}section.cover .cover-main>p:last-child a:hover{color:inherit}section.cover blockquote>p>a{border-bottom:2px solid #42b983;border-bottom:2px solid var(--theme-color,#42b983);transition:color .3s}section.cover blockquote>p>a:hover{color:#42b983;color:var(--theme-color,#42b983)}.sidebar,body{background-color:#fff}.sidebar{color:#364149}.sidebar li{margin:6px 0}.sidebar ul li a{color:#505d6b;font-size:14px;font-weight:400;overflow:hidden;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.sidebar ul li a:hover{text-decoration:underline}.sidebar ul li ul{padding:0}.sidebar ul li.active>a{border-right:2px solid;color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.app-sub-sidebar li:before{content:"-";padding-right:4px;float:left}.markdown-section h1,.markdown-section h2,.markdown-section h3,.markdown-section h4,.markdown-section strong{color:#2c3e50;font-weight:600}.markdown-section a{color:#42b983;color:var(--theme-color,#42b983);font-weight:600}.markdown-section h1{font-size:2rem;margin:0 0 1rem}.markdown-section h2{font-size:1.75rem;margin:45px 0 .8rem}.markdown-section h3{font-size:1.5rem;margin:40px 0 .6rem}.markdown-section h4{font-size:1.25rem}.markdown-section h5{font-size:1rem}.markdown-section h6{color:#777;font-size:1rem}.markdown-section figure,.markdown-section p{margin:1.2em 0}.markdown-section ol,.markdown-section p,.markdown-section ul{line-height:1.6rem;word-spacing:.05rem}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section blockquote{border-left:4px solid #42b983;border-left:4px solid var(--theme-color,#42b983);color:#858585;margin:2em 0;padding-left:20px}.markdown-section blockquote p{font-weight:600;margin-left:0}.markdown-section iframe{margin:1em 0}.markdown-section em{color:#7f8c8d}.markdown-section code,.markdown-section output:after,.markdown-section pre{font-family:Roboto Mono,Monaco,courier,monospace}.markdown-section code,.markdown-section pre{background-color:#f8f8f8}.markdown-section output,.markdown-section pre{margin:1.2em 0;position:relative}.markdown-section output,.markdown-section pre>code{border-radius:2px;display:block}.markdown-section output:after,.markdown-section pre>code{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.markdown-section code{border-radius:2px;color:#e96900;margin:0 2px;padding:3px 5px;white-space:pre-wrap}.markdown-section>:not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code{font-size:.8rem}.markdown-section pre{padding:0 1.4rem;line-height:1.5rem;overflow:auto;word-wrap:normal}.markdown-section pre>code{color:#525252;font-size:.8rem;padding:2.2em 5px;line-height:inherit;margin:0 2px;max-width:inherit;overflow:inherit;white-space:inherit}.markdown-section output{padding:1.7rem 1.4rem;border:1px dotted #ccc}.markdown-section output>:first-child{margin-top:0}.markdown-section output>:last-child{margin-bottom:0}.markdown-section code:after,.markdown-section code:before,.markdown-section output:after,.markdown-section output:before{letter-spacing:.05rem}.markdown-section output:after,.markdown-section pre:after{color:#ccc;font-size:.6rem;font-weight:600;height:15px;line-height:15px;padding:5px 10px 0;position:absolute;right:0;text-align:right;top:0;content:attr(data-lang)}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8e908c}.token.namespace{opacity:.7}.token.boolean,.token.number{color:#c76b29}.token.punctuation{color:#525252}.token.property{color:#c08b30}.token.tag{color:#2973b7}.token.string{color:#42b983;color:var(--theme-color,#42b983)}.token.selector{color:#6679cc}.token.attr-name{color:#2973b7}.language-css .token.string,.style .token.string,.token.entity,.token.url{color:#22a2c9}.token.attr-value,.token.control,.token.directive,.token.unit{color:#42b983;color:var(--theme-color,#42b983)}.token.function,.token.keyword{color:#e96900}.token.atrule,.token.regex,.token.statement{color:#22a2c9}.token.placeholder,.token.variable{color:#3d8fd1}.token.deleted{text-decoration:line-through}.token.inserted{border-bottom:1px dotted #202746;text-decoration:none}.token.italic{font-style:italic}.token.bold,.token.important{font-weight:700}.token.important{color:#c94922}.token.entity{cursor:help}code .token{-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial;min-height:1.5rem;position:relative;left:auto} \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/static/image/favicon.ico b/ruoyi-admin/src/main/resources/static/static/image/favicon.ico deleted file mode 100644 index 3f919d85a5d1e093fd8d80ebcbe9987a3bdbb54f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8131 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 d+2AD?_zza93G2_wa8hsDrQ(_BS&^YK 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%0Ar%T_GGt_c=};c*I8x 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

dIu3Tej*vo(mDIg8|V;jXGB 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

?JiY|_3D6mUw3i~oxC;N{ z9$nJdQi|ECM*ny`D~waujfdWuQfx6zK!(kiMaXYW4A~WNcBb9a<*)VQPrL3Z0WTs;uqB zi+sbI>J@gakjSUFo-Z!VPG5<|g0T_nf>G@EB*^q5v6+O}pbW4L2e7ro$LD{iSlp8Q zWVQe3If|E$+np7cIm{UD89Y*!Zwv$b<^oA9GRjG!5z2#YxrDb1`QODmo+7&i?IYWdN0b9)U{vhs)t!Hc|IlV3u0pGo^F)RHzJ@v{#d>B)Q z9am{MUAJ^j>*BCB<51t)FVk+tM-$)rjq}q341&08*zl=--p|Ogo&5g7Kky z5;4LlHtCLN0XEEggVOK)zHoj+>3Rnnj+v)_c3eEY^sQJ)W|UU%eIyb~lJ9_$b6UH0 zaDDrBkTa)Mfi&`5gozJf3O$63v(1IwxS$|Ih-)0`i#d7AZN_NpLEYy~fB0n9+v+hz z#HDW4vls%)98+o`P+6+mmitTod%Ja(^T}JqI&Eet1NEP)fi>*njIs?uoU__m=2h>P zk{Z@;#3PsShA;ApvXEYS3G$m5ha=;?+PS(3g9@01#9+9%xIAAVg%D;iPoHEX$9r|1 zCckZ)fYHO9q1ZxfNDic5)ep|Ba3p$T!2spSE7J~%!3a_^oQ91F1^at@#?Row%zwNV z1%rUfw9=!}&tX#c6%|~M!DCkMWgK)~r*PX3jiODWue5eO1}e9Iq1*_w?L0l(-wj~2 z3>3Fa0J^~FVPR2L`RojX;bH|xdwAMGovp`Nx_tJ>YaCh-`QcY^(# z2s#ApPw}Bii)`QZfX=9V#UuWak2S&k+2vcEo2C%)f1<&UbJ6| zesP~yR5`iR$|w$?`TB|e{N}kXEDxeq3_svI4aSWn1i}fDwecw4b6 z%0tfgl~pT%!?z{`zN~MQTxy4TrwMry(htN0iNSv+69=p(HqUZHQ%F>vk2Ge9v(CJ? z#ccMeZlYi+kN}DTP5(DuUMQG$4)K!$iQIh)J>t?{Fvov>a6-Z?>yPu8g4~~gN^7oq zPJ992dfewi!#!|8X(z=X7!nAo>b)MPz4ZlZZj}~~k;OWyS9V@0)-A798Scqs`>79M zzQAY+=>((GCZ>0>fTzZ-SiERw3+rL-%~Wz7PIFvAb=&m&A}i#{D`27%fU>`Wbt4O6 zBV$hLmsp`sI#ddP4f|q2MkCu&TKq9=CxHNCcE6Gh6!5)!)abG=DU?j;9}^NDM`k+~F#Jf?Y&GervK%Uou8^l@$6{ z624pE{=@d6%XmB}V=Q!IlcGZ6=L>ForC<$N9pTmEMc$`@QLGDXHud6JvNmt1J_Oyg zAKWyT-ZoB>vR)Cz^Faau&7wMSnN^<)r)Wkz+m82$XV6abxd{bKti(x+98e@q)^vAn z-t=Bc*>o-_&QO@>7{tEW2J+o}B5n%eZhsJcQCh-iOMUO+459X4lXIgWU08y4&{o$u!thhy5dvoiPm7V6Eq*nGEp25nj zLW)orU*)!Sd(ZH88`%g%)rCEIM1x^r_&-`t4;T3-(+rXdI?<~(<%~HfxP8RK7AXc@ zMbFpvHaKh|r)^4-fFaIxe5Zwr13`2z69_sg*fNh$P|^46ZvN3H>2{4jiET(EWb)w}9cR$C zzR?B0{j-aIFInw7!FrSW?~RIb+xDXRddDeTY+=YUd;=Q!P*r26UDfV9eSL-bH4)oG ztAhR2Mg=!z;i@63MuI>PD_&X%b#?W&bUct)#L0CFriF;Zw#9ONWX3%aKi-`_A$9gW zl6K0@B^Yag&{z^wciL+jHyC6Xpt#q39nibB$IKEa(t+uzHd~Z(vBUTp?L`TR?DOz>6_n)^f{VARH{_aDSz4-rX z`PZg*-}$HxTx$P5Oj~Ms7umu!DJHj=5#xX$Az5(&S5|5WVtpGJ(63i$nnK214Z+n& zlEI1l)O-@gki_lVG?uEQ=;(YyTesE~F2;sm4?eJ;QQp;{Y|>D@Q<1L*?`VX zk!ivHBhADJ0|X0#TLb+mHpAav^>#)%=|}<->f(V6=G9%NXMp>0-uu^N!%(*1+ItGl z)>)Mmt@l+Sp_bMv7w-kPeqk6{D<6&CXQeuTF_3nXVktEh$Y7Z1XX>d~a$CA$A3KM< z+{fF5UAYw)&|g0Yo(Zb|5^YpdYW(dP2D0^LJ6Fgbn``GAlaq_UH zTsDvS=FjR|xj($ib{$|Q5!d%uyy19|#Vw>q9njdr!?!T6bnpMVM-=hn0+QK0aYba! z9_B_bqgesp9T!I@kB4+bdA&nkd3eC8`>Gk?@emqQba zV^-r<{%0oeybi(lu$kag|))o!hzYV&my++pr zi29|6qLrl4dLa{Ftr`KGz{o?#V2`UR5sazuh~UC%9{9|7oZ$0wGNpoV=fXmOa8jLsVOSvE|L#E zp{7$#Sv1@zBvWSdI|~VrfB(9qX6I}3w8KkXT6S^G4NOY2x$9p`eag2y!({BL@ay2_ zNA-JWWFtROzYvYSh^ZXI3PwhXi1R4pYXT9PAtFbZot)fU!3lO>4uLRbC+WMSb&sQB zmI1udSZz8Y?CUE+qBEHksBj(u zh$Rf+@@FvwJgRkFc^~a%7tds;xmt2`INZ!HFxj$k2Ick6*e5_L*iz0$@tMS~=W;Uc z%2p})`s6Gx&ssPA>n@W1^$;~TGUS68WdzvejKxvGDW6ZW+WDM|hj#Joh#^Qe=o#hf zft{MZb_c_tQ7_*tHmq)c#UL{GuLUVKDdVP38&wlP<#omMRM&98 zOg>aT=k2Au_p0lPBFjdX@rd>v7VQ19NUDK!^7P!xAQmT{&x^Hsu*lqEJ7~E&TLfM| zFK1BDWZ3P7o%lCXqFK5*lt;7%CN6-^YuM*O%fsYVP+pVuQT>KN{ipQofrFaY&;TPy z`#1X*kstO2ULXuFy?4_he;s~WHRTn_!U1LsjNMh-(gyt?T>s1#GH z*G$Bv=2-Z>D3+9yhN3DgCWLmT7oely>SSKqs z0=vy-O`qMlh$-WgEPBpE2bbs1gJDJQ>?wu9_ENtI??cg*O$9R#gh$&@P*-SbYU;21 zU$zDjC%bufI=eC2+d(402=(v3t`W-}EsmhhI)~qIXUv(HnA9=?6v)U7qI~S~FRP8S z!P1pE`TVcH(=+d5OC;l4&q;|93MEys&@dsvL(AL!t60n?v?(DWy{IZbXL;OggdK*c z7S}#SmrbM(y&P}yj?K{YCR6wfSyNZ}ezbV?HJwJ7r4{n2qbpHn;UyAy?_AC<%Gv>Vb;T*S1`8A9+|m4;nwqAFq@{< zzC3+jKWbVBa@ilqU-5&K?4)_!Dxc`1yiUt9Zo4$~?#BDrGkG|}VeO)>2%zZ|4O1&rc1ez!Jln1K78p{4|LuaSoES88bp34vlVfEpnV$q_( zY(hHIsV$flDK2EOs5Q88w#w`3HoPuIUdwwz(%dXOq}Pi@*_3i>Wm@(9;80VG-FlUO zh*t8wa|&*DkNcXdY47BoyPb*Ov|Aqy5}-^na`H`IqTyQbe9TI%)cYh`6YjvXP4@&q z?|aKTzYz_7hP}F!@*i?!0Hh6yeo1Zm$m#fjn3&LO^?kb%{pOLl`1t)4&YWV zeW5&L9Ms`LXlNyM3x>D5B+;%SK$Wb%B4aml9(EaDjB!zs4Ox^ar};4HW;^4Um`^MD zXYVE3nTF4%Q$)jVoKf`Mv`VU=)P2u_4uemviOcrIpP(Jj4GWJfi&uETeNmzRi6lp~X@^?)dmgWCm$-6eD~UiIe!GZVnlTz=b7;Q3 zAASk(V_DbXK(ly~ZkPAOTIEmG@v-xIH(cY2G9w9V-oSO10b_F&>7BQ08wS94BDo8*AVz_Tv7y8vg)o?B z$R}n>0}<|zS5(=~8+#k81LAEXZ$15B%I<+RL_`SuEh`$T(iw|};z?=A7R8K@nD7uD z>m8KbO;xX+#7FPe&y0am!b~rrm1Ea2Xk`;9N*yuf+r?8jpZ9~}-U7q`^W0?J#vn5H z+HouI+MJx6<$vm5luqeeEU=!hZzZE0L&11a*O8ad$uQA$o==4%)tTqn1_yHy5jcKV z+I7gTZJ>xu)GzrF$oMeL_YQW}=PG&$2Cy}_z|T$gRXRS*fO?Z70Xe5Bl%zY4J}t6f zPGm@JWEE8K*^`oJ59%;1?ec_0RzCs(<4K;~rN zRg2waH6|t|IiLKjIUUl^2Na5|IGV6Gyupo_VL1N8lN>-@aQkEgv=erZLN+%ntGU8WT(qumZ8T4r#6 z`utM6fk|#}t!rfi8Ot`%2)fb@ck^7B4QA|Cf-P_GzG$Ws{KA*t=gFjo9z1?_NC6`f zdM8aQ4}HD8dmi;V$%ZAt;_tjo+lYVykVMW&U5mPEzPZvopj3q>!Zc5>tgh}GQTu3% z$1z%c|Iw7*iRjTA2f+5OkWJHQUzWe1n4KNd+k=Z)QGur**Lb?nL1Bw~G&n1rbtc97 z`x&*7woJbZyN;*GRHS5B3DlrorC``#eM8CHB9gid@HEZzuad=fu2( z%DQ;tbCa-OxO&S8sGj(#%W8|-4{G)W(X-$^@qU&6MNY59+Uyx2e#A2{Sx--cdZuiU zes@ui15ns7j1Dn~{%4BPUJlVT7ISWnWxU!7w4 zROtK@y?p&3qwx##@48HNrhVj?kOas{Yu0{44j{1&At*h=y`CrxoHyNIUy&K66 zedg3zHJi2AMvK{rA|}%OuGY6mw_NjCG1AnK^c+YK10?Lbwm{mjQ!rh_sJ2XUL}6-o%RpJW@}j(OF<3E>2wPzt?AEKnzY+3TAecguZ9X>R zQ+xL^UWsHD%n9g#I47hc07?+8*kyi{KP^rU3jQXOl}4oa{!;+of;7ZhT-*{N2*n(t zRGB$lUBOti7N)W`2vJcb^?*WdR{Db;UPufzKZ|!>hQTJH0tqX*zcSKVwB|=BUlfb5 zbk&SlQx8mzrI`Z#a;Q={xa53xP?ViQisdtKaQ~#A1o;?oOsUm&$#nsXYb|G>sUqxP z#w9sJqW75}C&K+b?(WZszAKB%;e>_Wr^ncqxY058%DA#V16-c)tJ1FTva0&5HQ}Qv zZQ!3R*JF_0Jbe%%l^R9u00nRl+=tj-0dOJ~D>O8k=&m3_p>uiNe#O4h;s^#nAayZx z@~zsflx1@I_k-R}F3yI#b)9BfYw_9ETr&TL5Q9&BAq$yEllfqOY+}TLJPaqR_1oUn zl>VDmU&mPK+}w(0oU%fri?nWO?w)|PwzP+Oc=HneNwJTf2F?stT!GuSrX2mK?_-e$ zYmn^vS|qp^gb!|2U-{_RM-EX&T&~rr|*zDQSBPs6WB^~B1?qsqt5gr`}4o!NRzA~5|=t^?> zeOxaTiAVZvllx#|V*d<7|3$Fr75po-g}k7K0wbh(3zNHEiG_LnI|BDmLNGPJ-r*z15)u$3i(52>({6_!CHsEF{WR2SGuWvFhlVve zuWGZJMJ+Z>lq)NPLz@0BA{N{@6b-{p770^~qL{mqQUW6@qf$ao&CJ2UL#u&BfC@Zz ziHe>#8^=Z|Q8sJd(7LrnRDt{zMd(c6XfW<|(J4Mf-6(lPWpin5d1Y#CL1}YtT32y( zPE~E*xcrah{2Y5j(Dx=>Ljr6)7?=1M=IU)w_Yyu?NsigjxR9 ziqY+lTtvHRi(n5&{Y~?;r;Zr~hCJWbK#2cTO)JKB|D&iA83(Y5K(-?n#rcS0-^q6a z1TNBuag#dJ@m4x!36IpLMjg5bGbnxPA9n81Y-uw2>8rAHYc@HfE2fK+C$RLmbUQp% zsAlf*YGq7uP%-9Yb|>k3G+_m>OwtELBQk=faY8{vzKp@4p^qned)!h3P0uXX>RxHe z8c*=>po1ZfT8XDraPDjJ*zD%t-*oRL6~Wj`t%wIds|{Fx9X*IDAZVsj{C)LFCvarkKi{< zU-G00>}D5&u{sDe7?kI5f;@6RC}FbEPa3noWrh@oUF**y%K7)RUn>IQ%-4A-6h9r; zc_9(E9I|T+zO%6#j-@c|CJk?GDGykG^2|&f_e&ms2%|#z1*=J$9&?Wvzt9(#gvKML z3QUoDmaH-?D^F2yxszjX-=NpOm@r2QFEQeB+&!5(wjtLPuaVYM%Y%j>F=Z)eYa@I% zW_S=m2>moxn1cO5mnjx!8pfzW0p;#3jBsbROdDIjAag(%m9|6Y=4Kl37if2Gd#v~) z)B)}#<{d24OCt$`%^3^^PYgW7Mi1hZA-q4Yo0L_M{_ntb%VcvuHL{w<&^Idxf+TK%c|knWwmDeG~2fUReuXLa=y6915nex$O|#zpY!3 z2h!~USC41Ui|9c0K-}A{!+}teDyFE2sHiY5tq=UI5r zMPSX}Cp{vv>fhcbMTL>uYdc5uJZ53^8oEw7qdMc@G zH{Wu&goAy~MqnNq|GOH9id*y#yzqJ5G-(6meG%FYBr%{k0y&>{)Y^LPewsHC9 zs-2|&;=>t_-oT6Ca5U~4>;7n1Mi2o70r5Tg^@Hjc1|aMzx*z*6OWFb^)^uWa#lF}V zJk-C{FP`#uBG@@uqk*NTI8QFwqFd*sX(3TpULF`774W%=c(AiD2GmId86DDNVtcMh zxx;`W3OMk8YdRM=4qzMb>KARA56}hBUB&E*99Try^S1m85rI!(Se5GWPVlYof5gSb zpJNq+WvbOoV2GPklI95nD06(T#svN^NO2$L$+=gMpV=?g6hwIynCDoG$8#YfnD&1% zGFngmxYSEaS$Q!`3ZlD;o^l)}f}udtORmWZ2?-ej@zxs{4MY*a^_;pFG7^$JsW%Uq z*9TQ+ka^{VZCbds1lOSdwh|36MW>szxBy5q+n?z3zPO@diZ(SBJtgf`KsTGpKCWtf zfM*4wsGxq7$A_Yrvk|5ccD&SM9|DH6mYP*;?w_e1g zZaeTf6BCs-(vqx7*|Ko3 zu(;t#7ss#*bim6Txpc8h%=9Z2Kz||}lJaXC6}IE8R@#C!5fzo257BNDUgfkdRR$Ic zEY@h{%YC$SA*IA)gtDJp+Y>~%v7M@w5231Jj^II&y*N5XC@mBaxvWM_;?h2!vwl`; z4hMP)xruGJILNJ%z=em0*MBDJDJuG{asvYIN=V@9iA4YlN5Mt?6Nrp2Ri{(6Kd2)k z6M0%q0HLT41lT@AcMgQ+zO;ZVYG@oi8Mx+)&E=TD`#fP(?`2m|!?8-ofeD2Gvaq+! z&VyT(a^hmM#W_WT(DCNG?T5fJ>1EToMf^@*Es8_-j~5@1bf>N#Oa-BNypfbKIODiY~IN$Sq15m>C@2`ww%u;vL|a1D

9hP;&#o0CKS`?+k$2*ghb}ZJG;t0`fs6N(!d}H!GS|C4(N(xXC>ZbDIH5+6Om&oW$%#b4&WUyh>~xrB7SUv zN7{01CRmsXyHH=~Txh3`*Yg?xSWVBTzcYGWu;}u~2?hJyPB5m`3%O&f}Oz5CD3 zc-SQeAt51GK3jbWAOUHSy^E0vzkegAwvZ1u%INA?o?Oby78&5TzVvnY7Rlc()W22J zaY@^3NvbqR+x?b)KhXXSrntrbl?Kv+1^ry-{HZfp<5JN#-eJxSMd0G#r@G#7;Q=ck zS>LzADt|vL)ITr~;*hW?1DG0^Ce>74u&E&W6VkuYAw$+mZOu3(?=T4R`4#Vd<=m-1 zCwF_T{tv85klsX`6^Ym{dwY8`EKV-v1YhxNkCdY*r&zz9|1&%-Ma5WU6El4{091UD zZrI+Y#MYf@-~iiW!irI&hZvSzmE}~bmw^TW*`ne%XP+A>`@!dM3FwgRPr}UvLy~;T zswKA1ST}_LD7p1q;pLZF>$?CCL0Az#pJweg;yi)X5e@+MESCNh?#n@Paf(NCWwp^< zkAAJ6BL?Po4@#j(6tCW5Q>mh?IyNdfcjA%@$FZVYVsnQrwRXKC|XF2~(I zCOZdi+~df!P=HM#8qgSD@vHBxv}9z)Sp4|(tM=AmF)=QQJ=}v5Oz025b#hu{Q)?R8QHk}ibF<`xVEN!?t{OLCYSgy2m(RlNgt<` zsl;4Jny#IyRU>(jouC%h#Ls3J&z6sRL{o!;VO8iHZ*Lw4LRk}YV-jB*b+|lp9#gz5 zSM%-K<nqNlX`wzLD01Kxq-rbmh?}?^t=IWNfE5W*KM7&&Fs8p< zs8tKENB@(M&qDYjbT_n>sfWxz^BkQl_*QGj~KnyVOPpi|JS}4RN9Oe6kF`w*GWpz6E!}lmF$qcQf#u{tW+~2xRE+B6c_gu%ao zvVQt~N_djx_5WM|4-2yS`Pmt{+~y2Q~x9<5H77V_XZ zOW?2UQu>EX;g;2w*3?F0!{|i(4)1K|CrZIk;-cQ6Mf~m&)U$`F{mABjUj78ysUM;j z6)GzJNHfyfP-jmJ0TlG)3lC>(*k_cL@`&?eM`y<0ZyCp^ZSTRds5~Z+2H#fUYAeVR z7OVL|Oi9WhZ#tCK;L_M_FYfyAd1^X3kt(;dX7u9KiADzP-xq>SW33B8Gz7d_enUWb z+FAA>t`!#@YCySjc=azra41(4{?b2t&PS1@mqOo7%z@~oo({npHVx{XwKYNA-{F$VG5kq@_v-nN!u%5P#{-EEJd`x4&0v^0NX3KaKS6LYR^0}{41QJn;H~G z;z@{d`@2h=EP_64ri4xT+o%oJ^SkD7j9zuc7OZe$iXez*f0LGVO7(=^ciQhE%+gass>YoMg2$NPuv3R&M(p;-&E2%@~)b_#t{;7cotVF_57o*!!Bi2%nYTCVLs1{Rc znCW0N@dRs4s-G+MrKgM|LxGfxz)4|g7YgPg`#0^2;8F&=@%bNg$cq=g-{&HT0-3=K zk@gtq#8*V=$2z}z`$$S9wLL6w7yZ)u8w;CLlF;#HGYs*_l>$mQERd-%rBSxOxLTnD zucuMHj<}VzwfE1Mpy^@h?z%ot9aa>@AY}3Y z=lxOKZe|Fj%iF&%tozo%cV!DzcHH(UGimPxjug%kR(yW&u|7mZDzK0cFGpKe`X+2C z$iJV^EW50+@_6dw70q&u)_MIx$No1y8>;{#qx3o0^uq(%!#$2v*Q8|OS6V2s4zU_O z3ZSu2bq@79XDbHzOl=5K=7E2805`+Fq^qu&Gg+};55tH?JW9^lOymJAGHlI^DD!h( zARe=Y(lFzDvMy?17RXN-9uk32H?aP&1o?zHVH#I(2e-U|wKtQgQX1nFd2ym@IeHXx z2X`t(N83&m%h#wu#+s|n&-qj*#bwKG4C1=EPXzP-7-M} zS|LZ{qzE#@qCg1x$Mx|lq_LXdZU+e8(K*{&n{ zl?)-UR1$eta7324-~ZfLa(1(m!ra2(gvL+gP3@rxv7^ zpyDqG>ArgmPNIGu{!8=Ap!*dv@tmz@=3RzSw}YvSuL8Ocur#;m_AG)sZ!7&r6FpSvPQFr9}(*wbUQ+G$mLC z?q>gH@%nIFw$N$fm%0FNWpTC z?^j^?Gs!%MYeCpJhrU=>8%jSgwd!dzQn*gtKU)&$H{aaytpsjUh^B#+g7*$n95y-~ zy_eN;VuhDZv4UkfKM+IW4x<3j9vhZsL>B95tHTIGSqO1xY*;~)rNpp8US9nNBB-^aw+3nQ%`e7LAMOHiHx5rsWQjT7H0mc+Fu|7;(f(>i zt~L~t-w$5oe~w7HnJ^a>$j=^lFH_rz)w(~0c~L$_1&P9lO(rhdTw7T*TkStd}lnrTBd&3wy8Ot2nq&{6-yJ1<}!&k z9x3Ub=o`0&-FL^v_3&A=T3|jo$H>7^+D{g;y})2N;Sm@Aa}a0~)cl7PuNivtt}0GK zR!}B+)_5EXBH65a6 z&WMWPY&Gl_Q=c*vivWHuU@O(Ar-^Lz`MINa>ms$3?4V5<5m}U7CNs9v`XDVPgPGh! zSykb56*#nN!&ZP=h0EiTIXNu~Y-gnGBP>A4H?SI1i0YR3^hV);EYZn(as!qSqawN3zR&^#`1|xDl3?l9d415$EOFpzk(`Qy?c945x?8+hH|#bELSHUWtLI}%hC?suq=)Ii>k;` zM#uH#w3LF4i#yhJKTO1UMr%?OolMb z$XJ^`5Lx(Th_VsoLt~(kB`C{WbeOe8a=_2M=z7(4T_d~ZoB)8&+IV~Wz_B+j)hHon zs!M!0bIz9|{?E=nZE10@3t`K{Nld3y zgj)CxIO8>`FpyGo_gP7CDT|7m`G>&{>u7-FwdW&ryG~3h;EkyVy+*WyCL#ATy~*62 z{kj-x$2a%fqS!p~mT-Niz{pe`@ZsrpJbzibc(^|hB$6Y0$=Cfc4u##k$Fy^F0o3{H zcP5jGpDk`&82cj-H~4Dg49cAZ>ZIv0@9FA#UeRDq3?96p@ot{yPcy0%B_hYywqe$K zy|?(?d&MhF^HZ!Q$uOrU=7QR7A3Y;ma6glGM2W<5i;Oi#0@*FPiR-8Y5!J3|JEA!s z<+~TfW+o40l#+Lp-z_5y6`$b-oQ%#u5EgK6o4B^71IeU1)xb9A zY-J6A#3;Q=q>q3ErJDjndjkV$rMY}RTA9(vSnC3_v8m}7t($2kUU404+p-g-xQF3i zOmy0U6FzdPT46p*#6U{_=W`5w;}fC^Z>YgfB&i7o?#wDQk7pDU z&%pwgZ$*p8m?49g`#gzlKd9xzaDx>bz}=g6^!y_0>uTWS;$h#$Xwk#oe&D25l&~6- zfq?;Ga=Q~XbwzJ0Tnt>C653-?ksV@whu;&P*|Lzd}EdwWtLD{{(2htvM1Wx1^6a7L(TNF=Iq~!@x(XKjT z?mav858Ugo%UddcGEAqL(K6ag?QHG62ns-t8IcK1XUYhdA6sN&R4K!%W5Yzop!Kt5 z>D!R7@ELZ7mu2_F8#n?02X^sO6=1*r&eiLw#U91z*Z`5ihCPE+pNY6#Wi8VyHToFE zv^T94)%r*bTF?9t8L2o|fq)B-T(r6sRCxGQ3iJDk z)m7rszI>bApK1@^Q!(4wq-h=1`=BJRj}IY9)kn8yL6Y3QL`leqv2~+dn5ClmZ_F7* zr8zmy0meWyD@~o#isE5@IOH!-S5;_R6YbdF6OUlVGsPIoUYBk^I7+n|a)+|;AatRR z2nAQP9oG`s#kpG5cvj^nw>gJzwW4#Xtj_2%;NgiPi$^8V$6y3~{+_aCNNj|k{b~8s z&ZpZfg)hSr49t3aSe4^ z7fdEsXyJaL+Z5Obf$ci)ybitv&(zYd`!u#&t4!@}9lHk+xh9vjAfdmyz33PzIA&|a zkX9g4wyg0&Y~P-7jz+{K`_N#u~yfvCPPh|GGZQ>2Yi=mlgQxr~6Y{|JZq zUL;mJ!QXD>h?MZ!z>=MrS?R&7aZ`_(4le-uqZCJKBo9*LXQd}A*KY@a!wj7giz4Ab zRQg$*oEd(Sh%B$pL=xv)ET9NDv%WE2jyh}b7Mr#T;vZGmpazH}d=#J|pWY46^7lmA#X7GKf%sD%e!anzJBYt>zjFnJNGl|M&BqW0Y|M(|~88F;hoO;(&CKaeK<;zWTV` zMC-OKc$kI%{PZgzb!IIL-N-|uJmX4H@PpVxt5k| zgcsWOqsT*5}|frU6Y{R&C$a3Q`s}x*_pl=^lKt+2mTP z`;UhZ8_dAR$FzKY$ux&kcSkFRg2+gjMqg7z_T+D#Fr!*}t9`z zmje|lL2lg8m&=tDkx(f;+iJMm;Y|vDJr)B3NF6|z(gx^c-EPwYEF{+u+g-Va_T1Oj zSPWN*_P2kdH)Ex31uxxV2kgTLS=reXLOG~0bz-FZWgR3CLlJ|y{m=X91pCXvP_KW+ z)KcDk<(oz7Arx(XCExu89Fghr#Ox%&v$*f9Gk>k=n00aHM}ZZtdm)be!2*}|-I!)w z7Ql}r^X0gOYq?>e;G=X?i{RVDRbQwM&nfVN-9G{pF(r@+4(%YWbchBqup71a7YSqQWM}N@1 z^FCa*Kn(nG{mfFwBNxm}L7>qbo&?GD4Qrj1Ck^!tPz6}?3XI|9<>j&Kmfl$1a%P!J`@0^H0q=^yID}nM)Uc?NW2N~LA8GP5mSY&VA`efzx4!s7sx~`%ca($t3K97SLH=b3$9S8?Df%+8|GIhdjcH2!(l3jo8^X%bq z-CJMZ$|ns$+6PhU;qcot1j+8!Bsmn%7f&^qckXGwM$JaS@2eSfm7Vd%-b)*PGG~?$ zIupgj#knZ24q_G&iv#DSj0UHk|CAE=qFNpEZHBH`+D)KYHqNmcWm?k3yr~95)lRRJLl`-zxvtquZ-vT4fZHCDILvTYwqWV8ypI_KcPNUiB+^E5J3U<;n zv(8GNYn+Is@e!C=me+!N&<_Jt999x%tsP#WX=|x{BEG~=H6Os8jynrwTZZ1uZ+g$cjTbh#|O|)G*g9EFo zX8G%NE}b{g&JaZvLw)%^XwgEwjT+&Ty}NKzVWUS&#Ea3G-m>C(wy(&b;9-IhuqFSW zk=qfdZJQm8Vxih9BU%ddrcB1VCbj*%{{+|e*KdQ0KZYneB6h*U!*}!nT@N$Xq%xc- zpY)$({*Zp5r{IduiCS!8?g%ihwzZP=L+9UC4DO}IC-irl9u}TRj8x;&skPX zcQzaHLO!x3T~$@wSKyifE1RGn@-8$LKsAcB5pImQPO_rj1tRz+Sszqnu0_ z?MI-U(G-nrclABUPB*16Q{0{L`3+wl%(2T-wxrg?F(aWAc2HkLlwn@M6SuR?Tu?Sa zDFuk7XK1t9k<5%wURJz?-l#CWVPCo@BU=~#M!f3#3vHpwh})eKsgT^7fWPq>X#t>g z`CYE8(v6dyt%WdD@+`O;9vIFm4#k^B^zoHi5m*R~F=%z__H1BhlaS7gAhExy&%XPN zzJm&?C}qdxX}11usg2~xCbqD(uYWe)8i2O7Wy=`}WOrW^*fX-T{aU@OK|OF}8&rhm zRWQO^j+(o6FrIa1x6oupMSQd>W!ocXsk_t>QpGpP?YrlZsT={U{y;siZkRcII99Ox z6!4~DtLejd+}o|S&UBuVU3w+0CiW;TVa={1#L>8~DBaPBSp#&fp7lMd1=6d1Ppc_V z-j=?Vj7)X^SqXz+`3By}Nl$DLl81d@D}e?u){^w}#>su>VDqD~;itR+&dPT=C<_?3 zO(GKoDG~UrID`t=BcAUdP39-HoR>>b3{Ock=w;*^|Ch;}q|$CkW2<@^HK<=M9ppk8 z4rO*(-EO2_zkciMVH`6n!3TW_QEYdzgrKGTqX0BRpa34Vf14cbqZqPG!d1$6?#2WP z=|8epIJG&0*<5c@(`e{qTVSluX4g+hv5&BBGNL}_8YZNBF?|A{y&-R*{baY{@nMs0 z*j*%MWPOi7w7IVf6DbVuDPQ?DAI9u!T}Yi;?xx4XfHwxG=ZxtC?O^`?{9>v2d~L zkw-d710ySb+n#)+!b9=I zliWyxhA;Wkf{oEr!)Y+a>F~CZ3HO|(7SGGiZHNH3mC7inq$?7&G1b;r`~vZ2@!C&l zVbRU0Yv9pJ%Oo`9iT=cs+wr!X)nU(h*h`>D&Mf-`y&?^|DBDGtT4~23vpZM2IGbSg zq+nsuJHL*E{GzDHg5PGK3Gif>cRx4D*w}^AlSQ(fu!2dyo%_1BoY-#QmhvA2kE}IG zJ8%53o$aeM94Z$Rde9>(-PvLB1woEmLG9D|--`LRio^&S3W*U2c55=58I&z;XT`(494T&>@`ti8`*-3)1eEcy zpBzj3&*&RDg?6pOI2J8DfB7F2b&d_Zhx}NS58l^$7wdLoMvS0h6qWcI#gnRN5-qt~ z2?wRk=d8GdG+ahLaO97o3ChxlB$MwsR)|X|3p&p&7QI>Vq?dn6cPY;88Di z)t?u?KAexA=Yf^Nz_7};0fSZ2c7CC(sqgPfvhlvmL=b$&@r;gGsx=8X_s+j>#5Ev_zORIiq1LKNe_= zn_wb6^R^N#Ae0hVme>#>kiCpVX|8|Lf zQdErWOgwr4yH+XV46k8s_RFUP=C4`km=IA2L%!6Q1OR-=Slp-11&uS(ybO1`7D=hxsokk$Q&t6YW&G+q|d( zTdy%zf&;#!n3VeNIzA;fZ@NEpt9#-4fP$&oZ{3$Qz{Ej}b<+ArJ0G&%S?mFOhLc`U zn^nIUc2k%70-PrCXh0Z-nf zmyrnXcG1bPtz2EWKenpSVYu{aDR4+a2AZ=}7;bLxkr{B+K;N_F-T!4DjL1%;OeaRx zW8hbI{PrO9^}erltRmutU2Yf8xlr+;7M=wua-%F>osb|0f7rJK|Ac$5FOFSSplqPl zB3c#Qr1otBJ=`casNjUi(iS90f4)I8TW1-ughN`U{Vc2W?HIxMbV2z2u z$|e|$IfHG!O_Ij{-hThoT+;q%t)gZ8q5EtYZeOSR+h z)xX;4h+W6YC=(g{hAG$d)ct__+_AeZ2Hwh@asGgC#^H_7q-9!bLgY4+puYa%eK7m8 z+8@e~p8sD9AR!tru+9s5qv$;JrUUj|VYl_7*}UKEPTavZ_4=hT*4m9TgY9p!ZA2D! zVb!IQ2)rNmzs6`WBa*-_71~76pO$J>(AUPpq-B%hi!*r)P(uFP&NCq!7#G$1mD(43 z5t|F0o@yJ>YTbmep!40T`Daq^E~#^g=8YOL{QSO4=Z(z}r3EAd=9fUcw691J*Jmc+ zq7p4$Y9M>PqmKfh>N+dNqUB`Tbg(NSaaozj(D*8mUbZdI`xabe!GHkOTcGbwgHA_p zxnEl?sb$}dq*aiNynVX_zFZ#r4784iN4Q$*2hVTzm?Zc1juKJ7S!xPDMLS9`rHh>t z@+UfqN57F<^fa^3{KYcAsJ3ZfDY{+)u?@i>7YnwO_HuO?iij;^l|l7m+h3&I@_ z8{5d!Wkn3y?q*$1GXo(4!c>~i-AluFGLBK5u*zm%z43y?Z}xv%egRH6ET`qqpp2NY z%}_C_EO4#7k(QoL3G6-iEk@KQOHViDyCbcU!^)~*^HX6VzyQ)su@HFs>7N{!7;*Jg zSVgp=$IW(9h1|Hzt{7?v_5R$4mlB^=NiWal9$3VtwHy#*3XGk(jbekip7u<@>uD0h znzecAIsC3|2Unj5FDmghH-c3q748^^NPVpt%_uM#gpL;u!_P$pRx7GeSyf7bNXXPT z1Rn(^sdx>pQ?&sZfxu zuRv+tbx`QhW(| z39F2FQMsu~{^4%74#|^P(zH~r!Gq1YOh}N8FY_c%JZ|O3z6-Fp7Z4mXVkFBo4aB8Y zSRwKM=6=&&qyEUJ$x{7VWg%p^hR@%krU0#B)wyp99tY_!PQVQW zFI-)Vc1@&#(h3Be1@i8p*LnIvfhota8qHoZY`|H!vhtg{YUTi&)7|{QfV@%?vS<8P z=)$3rot=hI%l7touJDu>4E{}0>*#J98 z5XfAp7^9mv&jO8gKI2#FmQRehnzeCssM{+BaS`S*6e}`Qlws3%_otvMQoz+-{~LA0 zI%Y}}k3l01<;i!XE2?gP+_q*JFSHNm^UUaBJ#~PgoL`2<~X4 zTl3md{fu&3;9viR$}2n|!O?)U{K=wu3>$VB>Dx!FFa7;lW+l^Bk+_s!a&_OhB!71a zr9*6f#I%=|4Z2RfVC!+6e}w{w0uk!5B|}x3(u};);*7lAPExeqwr_dA@xr~uZ5{OO zTq~TJ`P6nb)=~-Zog_bzrOkO8pC37}Ei;%tqUMmPolKESq9yHIoFicUL7Ey7IaL9F z6NpdQUZ2!$#jMYes*WSMMGboi%&E{VvUddaz}i+^+JbZDcD1Wl+8490f%~Xb?ZZ;0 zh_5%NAIXAl%T9!!gq;3W1I(KbRtIpBD%qbS{n&V0d9$D!f40SGMq&$^9o1o(mxggQ zeisi|5yTs5MwbQw28ew~1Y+64 zGBt<>Q+hQ*Gg+s=sbp1JLN13j#RnuE2DeIZIhfG~ZSNXkWN_NFiq$Ns} zJ~w#m=~2&=vj?d&mi-bMZlq5@v>7xnmAJtI;YGk@vK-;elzex|b0rCvL)S;58MS2B zLKk_6h52(oj?v2kPXFz6^U-0KivD4Hh?S{AIY9s&zQh9rv1%KmQ3KOl<_^_DVE3b< z%Q#+c_8k%g(As?Kq*j~z+ye*pN%{*GzF{5^Y%dt~ z`m(k}f+2Bmt#YbknC>NTzf@;vj!&2WP0g|Q!!JlIj zZ@}Y)#;^TkAwj%(ldqCs_3qsUF{oE*5lo+f8scss`DSOQ%&)(!|27Nu*eTNneYZgc z>Y4uUYr~m|2)Gz59;A7?hg4#sY0f(Eu9_K0vu3piUW+73^}>{sMt@3Xn8$)Xf7UG4bB4x7Zp# zAB@_|0vfX%gaCpe!9T7XI<0tAIjZ(#Hj!=P&V7{ zDPiwjD)@2l#@7BgenzQ90C6D0heqgAJoA;h%RH{e?!B$8@T%Y$rTxm!5=P*Y>=pg6 zG$`-9$Ji%|k(l-Oaa;z#5jSK*eH%aFzQ+M~0xbuhFq*woUq`2_Aq}!8Y)|LF$U6-L z5H=w>fvQkaaNY;5uFo3zkZ(dAD9deBxHcBacA@?t5LO7V+2l4XYiVd7hkVH@N5=^r zq;-Ri8&?c>xHYp+gk?65HA3%mVwK&SR5*a^?LXmy%=rLs4|&&{7G?*7`ggj1&$%k9 zq|a~Zp$CPuDCZ}6kOV*H+IjFw>ABn*f^U|B?7BXJKuw{*t9)>sPFHq&dvEd@lO@S#eZ$s1CT)3K=uOlu=~)_ zU+kj8$k-aVYx2PZ< zG`MkEl&N}c{KAKDo+ts4*&L!b2-H>j_GsL0<)?w|+TrkJR8-@ng#}M+WUnUNq&z^& z78EiJ9SmlZf@!$CPrBZ+o#;{GfTDxUQ3KoG%aJWJ+g=H-UeohBuAjGB@L+{sz@LNi z!oxw*MqF1qWuA(tFS5ENWLAC4RL{N;351w6i$qn_jKyqwqrJLc=;dez^$tR0BS66O zfv+s=e$oe?pzQJA&EH%tits(gBc8R8JUD8F(lfH=TI0jhzk-w8&fl`u(a5RDI^mI3 zGo%VRFT}nRI+Ekez1CGV4!aLWf1pr&PUG7CT9md2KEk0^0N3tz^RB-Jg#hhUzzBVB z9osMvfpS~Q9@0^OasqliI;vUnIGi>6M99AHaj3z@$v(HLB&y%y!W-h}^-{A_)HG)5 z*Hr|xUb!r6$$$NivXMynRg_eptmQ{wseOh|F%$zP ziNcPK_!s%_)s3&GiwH+^E5e?A!NIJIWsYwh?{>2)oGhgP@sbzqI~YQKz&$t z6b2mj?gw*VW2dt{au>Y5uIHI?O2P(JP{1e2V>7OOVt2P*VS%5E>l>pw#RKL!I%4wQ z)Bxb1NkOM&q1?s`oq%iq>C$6u$K`LN8P31^AJoegXJaHE9(&#qY>CZJZmpLtj|VFd z6YmJ%WW3}%@DY!Wlxb4{R+K#g+||(Q>aQy7q=6Dwlf<-qAN7o+6*)l0RRzJxg16q{ z<{{lI+h*m6C}W$guEB1dgM4X=iCh9|VLw4ffK0UtN&3dl?H{J^{Oj1@cmDBPAo?2Y zv!H`Qp5Yc)h<-nFw<*)vx-M&s+Ms+!P~$(Mz@xYRk>cil%4D_&6doKMSJ9!94SR(^&BmnYD4y&l6o zX_<+z;vV0N1DxQf;oxamk8t8H1W*SOJirjvHTt4! zwF0rX0|h<+6cXfw+f9j1SDiT1G4-%EHH|gj`wx!=2P2g~)}CS!!FP@YP2YE8%Azk@EBId`wd=a5k<%W#D+|S{q_OG(Tr<6D+7qBu~bxUPVRc zNcBB1hwv=3F_vMihFy~q|Bvvpw+~`_pT4~CQ=1*@52B)QUR&I5-5n)dJM6J2-gs5w z^M(17Z`b*-X~B~*30koh!|#76jXN|i=`Q}VN=`XzR~aWzUcO`_x=ZN@dt-yL=Xft! z(q8kiJu?ZRF0%>*`VGf%pTf8|xX+L^Qk9-dh4dqO<0D(<0^jax$-WoE>OlHsp?3dO zrW)M!W=EAMNhx6Ln&COon(+PG`nd4c07sRB66O*8cFC2cuAOGDVPg+*-`A_+n>hRu z7f8##d$zy-wr3EOq8g0}eiy7(2Z0>mOH((Ldt38tTb*ln`XFI)OhoQA$sgGp^*p(l zvpi1di=R4L9w}WZ{=EJaqE)j9-%L&L z?KydFmdS*sjRg%mYJt}HH;M1Acr0Jl*EI{i2Mo-AqBT-YrXrfYJ<{Onb|#Q|KRmp| z9~6-LJu>C7wDb@_r#gKx@Eq3{^M;A>k!JFpANLM%DaEQnRi=cc85tr784sSo?KiO= z*a6%_Vw})a*U^W4|7n)e<5ZxjLfz8L;T5)<7u8)9fWvYC6~6egx<1-A*GUpn>kj_E z;jDmomCl95Hx@)tBgPim>zW(v%PLKV;dbr^^G~6CMK?s21(Pv zjah-;)z#gf29^!FmpovgQS)9&B3V`O)_h6^`sDIR&lCs2V0Cc066Z{%!6S680w{w?fbY} z8;}hM_52!h-?Rk}7HqWuBPCqwuDcV43}{MOwvUZS&dqN;y13~0eYgaq-mNcCF&+}R zOSpfFPyLPKApifksOHfk2YyzzbFQMQM7jL|=da6j2|@l~sOU z7)i}@5lxi?Bz+fub(J;2Xny+Kv4Uw-LK8%gA$(JKkChK!eIm1i+k+W7(cmJ# zKDnKW?Pms#7bl+8z`zK{*vJ0Y>3CI)jHtKY~kFrMP+8##zBJFxOXtvz(xi0yqfw!O?sX z^fxA_In!kDw~h-emAarLH8p@-U_<6256j#QYtC+}@!{aj3ROwF@>EEAM8Gfk7TAmD zK=wNyiayP<*whn(kQ$9Z@HuUc$6y-&-?@iLZ=R6k?SIn-k5Mo3U{m{Cbjb2}fS?6< zCG~$O1T&xE-&V|uZ4g^aGvpw_to6!yXW!zqSf>=pf@UQ|yxT}C&|WjbjxrJ;X1 zVL=r7cvr+hxRRSzTrrNFuRREpHS-~guSwQa;pMk0FXx+^y4vKFz*Ll36A#+OPq<=s z%1jBPvFaXJAg#~;Rcb^rV!J9U!a;3co$A+a^OU;u$@2g_#ugcgy3TqqEI|z{IBvhj zIFGFRrbi7Cnw@N8m+9Ar6TeMd0_=A!;DE_vT7dekWruUKaSuJ?yN{f$N*D!vz=^Hm zM7HXuAkYu`+u9xwXg(#GB)?Rh2EyPJv3A=zOv28N+G2Vq82R}?F?0dTTBFc0Pw(ltc012)tW*5jEf#Rm(#t=kmwdMCZiNR=cX9KT?fYpq^Ae+P zeZM$n)X<0`p!31u(zhP>SV#XoE7mYc4HsBZnuG1sIof`r%BeQM_FYh3($A$k_THD< z4Hw-hdXL()XROaNGPMyyp$}$=95K6x-tXQs@2}bV=-Wx7YC$YHzG|t_nFVqLW5>mz%r*`)~n|F8A+)v0Nu38?aD$Hf>kO>r3=UbPF zTM7;knKZi)&d8up#bEj9k6f(8*H{KrYFZ;Lwe`0uUg$~j=GGZ<|^pfV_-CscYYaWyI z;e39R`ykg-^Xx&(+1dwVIraBm&OZ`RVImR`ym%-dR*fs!rQ)z7JiltJs!(&ag`eb} zV<-sOTDAr2YrRj^Y|2{KWS(*sY>OjG?T%e_}h-7buS*?7&Qnw#pdXC4Qt^1=9U_&CPzi@6+JAcqUqj3VS z-Q1(kEbcT*pSkX{nekR3u2IvY`n&cmDaFrb3=rNAKFgROt!{YSGDOvs9`g-jYWgk< zR=OH0m=>*CNH37Ic_HAuFcyHO-t$}L+Ei5o;^2{0<|tHTk$$>y<0eAwZFN`4V1L9m zkzxF#4nSm&9lX`_2+>e^HO}Q*ai_JLm{}8f29wE$V;&$xN{1l8$42DeabqQ}GQKq| zb&xPUqEUBqMuYU#su*Ku~gbDEWJX zsYB=4kylsA<@8U#{kPv9^eM*1$M=ssHRk~B@5|L4?B#Ra7dZe1qyfQ4c8ijqE)E3m z7`q@1ml_X?yoL?^wxmdzLjK&bQ87)xlzDSan!t0EjaRfh>6Dn_pVuKs0TU|6+W4q# zXmd&aAGCZGSk1|uH~r4 zbw&TUv9Idt>I+aA$dch+sI6V%4V#9gX*gflx*Bu$gExw*ZDyxz;vpl146I%7dJ{@Lemz#~JTu0fu#v%2d>bWY70V<1$&wfd>e-ESA3#Qt zsp1(efhXu78kYYi%*ZFKDTdj2YeZkB%qYR?ZoTEG;Dd#hyxhZ@Rh1cv0%?ZE;9gVW zOERvo4bvy%-RwgmD8`(6PLhar=DF`GFlFOU%S0sP8fl*&2g`&0iJ#*HUOcA7ZU{)T zF3ayZ7KkE%(AnS#%ac5R|KGMsjx~W=Q)Q;#R+eLcsS)o>02#i8v2sim8L6EVrN<*F zMvcdxP(4vd3Jj8&V|rP9LR-H7@MfN(+x7HKEV_%b!3*C$=DKdXb>`;F;XUFa2N3Iy z|Lcd25oWOD`wt)xYPpKMj4t;J5c<3S-b$4Z{nzAOWYYf|NX%p3x!h~4abfim~T~UZ;!b)g?of?*A zAAKLg3a89TQCpI~<1guUo&niTdUF%UR{0cy-L2@WAQISq!g_xI%>p9nxL`^k#N}22 z7zvzNmX_`%EV6FrN#5R4yBczlfL5kjed^B+*0xoy7_MU`1lRq33Wa0vWPj^w&_&lU zH}WCJb(MUoF;oB|;I~EUbjvOf9bXn?QojG>T*(1%o!C9c>?p3@eK}UeA5jFDY7jk~ zB|vgt$_5BHqD4hHrSlv6`yL zzwV0yz-U&UyR$kxjLtpmU*4H0ngzXv2)O<6KNJFv3qk;xKA;1#;NNnr<)MQF zDGxWCNuwvwD(`_rna&&an)N%X$Dbdgv<*zn%}n`3Um|nCME1^jnO$X47u}51t!zB) zuYqzX>#bDT@0cv%Cnwb$ksMR@!zwT-TJgY zxA~^OT>sMOmTI^Ax1HtUuSyB`4GzQ!mZR{)19*u&yoZAaUFsKg-Y%ablx|tp)fv;b zvGkhWHd^3en>zUTki=b%LV@jFo>3MtEOAAOEEzCivmDv$2c{B8nB<-)h1VBHO3!KV zlA$fzduo4tg3j2^Sk5T{h@66O(;uQ-%WG94v3l#C&QJvJ!kq7I0m5cIU6Z9vhH&RO zk=dpA&Vg!gUC()&aj)^QO|~&bM$1yur(i@=sQyQtouij2W?;vqsb)~82hW2V%nBj*A3ud=3 zh0E|!V@>*S%Yg;+S-hpgDg3MbhvS7BR`5ma9sB zN>=jd`gb~M`vbNLW@y8wB`VeC%(7WiwckaYkh<@ zHf z0HBD&sFd%#%LGT_NLM(OVS~dfNU&xcNXGT^+O%Lwu*dIU_>lDAv0nyFbA;sF`dBpR zUck6Hmo|8dm)htbNyTTVt8U-$3is`SADEzVr`-B>nG={fcy^~@*^F7n%&$@3EstBW z6p->O2)+(0_=9+bCz?(WtQoZt{VxI4k!-QC?2+}+*X9fCW--Q68t<=ngXxsNd#_|e@} z->Ri^&J`*vEdmdN0|N#I1}`QmC=UkqDH8a<3JL=F?Wdoi2>gMz5mmJZ1ET}|eSL7r z=XC-DBLWi>{I2MdcCreoqM(cmdBNt-m!x|hNhI-I_B(P8d#T(k)z5~lc{BBrh9x!X zr4o~46U*|_@+Vd0V?p)uoDX>HvT@}n#f%1!jGg-sRUs5nE1%--@3|O~Y$uw@iMJ=3 z&$C5IQTU*U1CaRt^Q-6^)lcE$1UgL)=eL(fLT4x5>FMdZvhwnYz`#KGvy%>@f8Y6> zwXOfkh!A|;-ye>-AVG?P9Q%88vB7fraQr|-O)Vwm*RKiQkG?t^Esn=QAG(UFs;Y*@ z4`Bc2I>5zRFx6C4uy}SN!@|Rr3M7-SUV-mXF2OyJFJh*;9hfuU79x@3NF9?_G}109 zDN!8wiSNF1xjonmpJw5!tR)t{Wj7ewzvXVh$|;cS`Td_W$lm*B%jI5O*$w-A07vK> z9GsYGpf4DGz9y$XUqy`>w>@NFU_cclnZjpHeH9h+l!JzXdZ1=uVHq0gCop=iMyKIQ zBYxzMP-A8@6Bia1{vj(n32u1v`Ck*Qq6P`W2m4}whJbiWPEF#qB(irm{5 zx7dt3oh?1Y-}||9IZZ3Yde!VHV@CTFIbU~ocS|f-pU?gL{J=EbC6cPGQHxB?xH{J+ z*I;P4G>9%UDURBkT638g8DW6;oKfa{{l|hxM{6yPCTPgWQ|?bUhworMQWMesI#=`Y zSvdty)2DD2G8Xe-4NwL=x>V^8HV{`+XK7@5=>Oy0kLk%c0~& zCU5=NGpS}s(6+?9f7Dkb$xI&4fez1SgxIOXhX)sbA0My^Sqg_!1`9CBwI_60inxWf zLi$JK7Y-IQK}V0@v&9OS=gW=Okl6qA4kW)bjv15n4o|J?{Rv)?!kPM4%udJ?#C7w9 z*9g3_?@H>ZjFkgZ1Y9miq$r^=ixy5bT%MbD%}%+(ku9V9*XiXRZUPq#N_@F9%Watn z**t%4j^RW1o8q4D&)Q?7qc3SFC=Lp^{?7%?rce2E0pDND^nQDkLuXM+N2bQ?=~|3k zPMe!a6FSexvNZanx^OO+TVXSppO-fqf%om|i+}fljdp|8>eDzRp@)qY4Tkzn2K&b^ zmRiuvLE^3F6O2qBYqg}K+%9o;zrVajySlov;34e01#|lSZy-Yv&+<2Na7FfR(sa69 z>akZ3#(F)#Xox(Fw3X+?Ij|$u;2lwyW_!QCamu7~+!BZPo89XK1q66_JX~y!@nug! zdNYE*U`ig#y3A?3`78b*y!r-ZeoU%RAgNkyz1|_kX!)niqq^`os?$-2jxcyMg?6%Apes{ zW>4F1nIeZrMkrsO@87kxwALJ?m;{pSO>j-H4`UlVb-4+DdemUlE+1{;m5_=nPZl|o~W9(c+Hj0{mw5wStZ!N2W|%rT%Tu& z_um{atWJmnL~7+%&6YZ0C1qR;y!8(p1v`PvQhg-ID=*t{f|k5sw@NHo1qF9Hd!xy` zk+^JUALGU2H99<8hcdX`*QjY};q!_oDLd@_ecNp~CKZ?HTvp}s7e7~-Zv}T%Rs>#L zTufzpJsqy+vHkl{vm4id3-=EW9=p1`lTVp2$KHD*LOy@cf-x`6IiO5?bi*|eT!)~F zhmq2kz;hFRu{b=1_p_5pO~oM~@Dd#vMZ!p51TRTBg0ZZmKSW+(xA|COwFa3ZA8kmee1XS{hxgL9TcSCe zFicKX)#>yK8XCH}v})jAN4)uQ%vxFqSnEO}k&c$l);Zw*-U_Us!3gsJ z-L;P&`6RO*@5+k2t1lF_lgs5|BdF1E{ofDgd90fK_9Q1S+N+;{${vsJLmmp|1a4Y?fUMT>wi{`irUgu(j$Y}J>Tn3%1_A^6{TxDh^dVv0;* z|Mm@CyV)+Vyrd*0{mK~)@9DxE>FTp)uDvD2_-J4!k0vAS~*<>quE`Raz__kB35JWMp3FW@(at z+Jb|~1xsTbmdpG5iIA|cBWnu_51x0Yk1t$K1aSuY&XMEwHs`aQHZI*%B=j*fK&Ak} zMf_to;yj8vAXCz@G}+qPa)5PBPET)9mr9|cqQ0Fh*8Z|-);K*0K72(T*UCOnOY+HX z2Xa1|q2qSEAV0Cjq8AvQXCtH_zg$0XsBh-ILGaIUb6y%6X^U}`3U_-uJ5^v=(*pxQ zI9r1Lx!WRpho?=`%galS=;-JVKx5$lRl9>LX>j`%huvm_jxMpais=az1%+{YGFzav zNU@}3ff=FgdX6MZp3|B?Au9t>gjWkzmBHDkTW7}_NGm3yqM|8`&s|2f-ffP@^CErr zf$R)e5q;38U+`d{p}+MQ)z-!>V*Yb|D9T`Yf!w+}=5nA;-G_#TUP?hUtjO6V#*ZKny&3Z;gKMF_R{>^xz3}`qw{r&xiB7%YsgM)*JArTRH z8rs_DV!2ZpGRtyu$o_@FtZ-Oah~SV10_R#B7rVoP566NG9v9t^BhIHw^~JRW_a`S7 z1Fh#Bt(*)Dwh))zi3}E4xEL^hQ;fB`%0%!8FTM|M=bIQ!F=9tY$Fg)fM}0uQ9;K(+ zpXq!F%rhWd1_#r?R9@Gx4h@ysIM|)3Kl~Kx|E{RV!M9E9@zK}E*xK6LZL=pZ_=`-& zOJ#DhYJ1Id_`mLD7lnb7pPyfhS{rh<+Pd%gd}n#y*C%+Ml7a)7V})Ydo`M69ho8K0 zRzLZm zj@q@)nfHEZC3jf4=tg5~H@bA;W z7g00m)2nU*oaBR4VD6y|gkB&?#6>FLssp)$^{NUGp;PUky#ASKT% z{xWxU{kGeS$P35qbn>SW4niQkq~zn;%z)qeiC9+lAW44Y>8)nhhIjin>u-s?1RMMvp;a;Ka(9_HLH@^XaTurKlS z8Iykl{St{05k3qIOb>_XKY3La6S!|Q6qK5mp=i*i|00b_Qy0RMOO20oV!6-F`a4%p zU^t4-Y>^yq8iylQT`3%K;z+$pgT>-KP*Y-y6{!LX27I)(gZS5r@#t-9jxX*W>bYEO z?qF6p93a9JnDVLX508%4)H=Oh{i33x++h|&5ul)V{fwSAv5`>kSrSOBD$S3eq~6P5*CAG>^vk+kM}pF`LTUZ6Ik5SGu?D zEMdsOwPso~b9oL^d%iqWCue78PbaKf{|X6#N?dJq?!?5$f7SBzY$vnDOo*pcxwHKo zuEqU*eg2~Zt>pI^K1{_~#eDUh>!YkEQ|!3C9RXcCj-53zA3t}>>5Y&fm6MYbI^a&u zl6X9y)^TufwkoPJmzI`_6+{0}8s3{tPU#G{;Ugmx=K&iwmhZER%pg{Z#v zA7dktpr@Bb0`aUEEgFZ5uV5w>z(Yk<^Ho7+#W{))mSjBa=BrlQ0h{Gg-H5??6Bj$4 zG-Gh@V@*|+%^VPD!f#h2UxDd>aX@j0+vrQiPmd16Xi-vba=mXcx{Ax@u5)lgHzg z&DQj$r-ta_TK7;3+EkEF*syRjy1XI)xX15xqC*bgT*nI15uNZ1+Y zzhAsV{>)%djs=twDH4+==Sqz;%flM1jVX0h7cQ;JrY3ffM_GJfRaI0*dOG){iaK~2 zO~kI%y`2Y@tSK`VqruQ4n=#ry=_~sza&~eE=m|Q2HdaEqg4oc#H%J^DR!vZ_&qkg# zWNCNZ`~4IVjH9<*Bagb}_wm)M;vvSG=l!bpCGx)`{u*3fkg%YyzLeCMd$a4UtU7g^ zyL(56+q6Ah;;{_>y)36~^UC)=+E)#HQS2JdUv}S#kf7Sy+Vn0N{{QdzTP-xv!^!w& zSz?sGI^$1HW(g=NDuVH3#_bP8;(q9Z19{&Q*fl0AYd|E+hW9hydI@~?x+ zvrh|vzJ9Gd?sHvPHMX-3moD`4_s6Mb_C%yhG#3?qpFECFOjID?cH1$ps01HqS+e^z z&)XQwNAdxS#Z2)N0{1_bzS$$uHKN#3_l_FSIy1s zn)eKgYq|kXcQrQe?01CwquoRR3~etV-XJ6_~8Pj?TlM>EWyB($wel z3_3bn)`kY9^i^$DE{})Fz^2snV&9czx2R;qTN3D-qq$=%@)H<-C`Vk>7arJoy(6jY z8AXTGb9+n8{>YS;t8`<%#{G@j|EU&^as2!sSlIBB;o)Jg&!4#trUWD^ER&Oq#U{45 zw@*cnw1Uwvc1q75OrM|S5-UAMOUoTiCp^iBkX2MwMMhINtvSglLYA>#6O?pyv+JJf z^RqKnmcyhhlQedAcO5GgoFIq;*6e12%T|vqSZoHO*3LU`yrfOpVZoxxRJg2+g}rRe zhKi4uE4J)&t^*;)q4TFq1~|;rI^Z}o2SpVog7{6367nvq&Djfm1fO&;l;e%Ag?hr& zc)qqv%O*ao_P;K-oPz!BFGWq!P;kC+aA1&;k$tyiAaomQ221SV5pdIzlhYNJK3}pP zDUdpUwbgkyf(e{mC?sV6z|;?M1ry%dH<85@MFkb zLskBvt|UTTUg2#_M$S8iC->x<81=R_*-_c4zGwr?OpE zE_Kog?Mh0bp!W>2u{GnA_KJ7s%pe6#_^+=U#>w@QYi*+L}HFYcP#BV7Q4|uw$(s;Hk%g!Nx){^hv0ETKbG6j zFwaUx`P=W0CYbC8Fujf8a!X5lS{fSE75DoN4vW4J?|9oGbs?d^VxWt|GBY1;FW*@q zi4rB%F*i?@7T=G{T1ZjqR%{6~HLk<&yfS|wid=IMcu7<%Wvm-eNb!z|XZG?%W7C*@ z;TV>19V`V$>WFgQ_c$q_V^T1RIG5*zNM9T#f&Y32zN@s*?$Qs7c^T;FJg%`mebj*x zoO65r?TkpGH)JiNtLTuCB?uxvbLJrH*TAXdx|f@8@9{mtZ~csSNU!-PjOB8Nv9y9a z!U+!slV}HadcT49#wN@ZCzM3Vo@Q&pT6S`KfrUyFtk&BEpk3#{;+(D5 zpS8wdeAJ$%x!!RY3nFH;`X$VP3I3lVM)sU}i)hjGH4jpG~t~-gn`(`R5C!{FuX(ln& zbYgfoD4<-!_4XGyvsO4HK9n^NE{dqLyLNkee<(NEjG%tR-pTdp~q=s-9Vujhnb{ZLgH~HD{Hm z$;YN^%4z@jK~+`;G;V4h;I#d+$s zRGo5uFR^`rQQ9nbuh7MrFet#Qj-dBDTKS=dnWv{ikfDZ**}u!|V+=58sn znhq2IaB%QLG>|XfTOtjKWcCat5t_KSdZ_-~(a?dud4k8w6x)`QC|1W#iH4#@akWR1o`<=Kn~BU%+)&AvnH)>oXt(aG7-CR zm?h&#G0V!~2N_XAmzErplI_;lmH9D>y4)ni#Hfyjbh@+Myl(tv68Y(7{Jb?eX`^6& zjZ-t|Bc5-D#MGKpa^?9IHB3-eOI;*Dm;@Z$Uw)e74}6h{sXUH9^_Ic9Z0x57gwBCo zr9$I8C1c}YUX@oV@(yU?8?(e*wtY#0XN9`bUd|5&5W=4~@Ha;9Y{Gl`798s?U>vGZa4$-y{aX;UPhmzU@ z<@rSH_g?J!xzdt_>D5ON+-tNSe{M_+is=?#0JpW@@UZysOEQx)k6RM!uLlk$rlD4+ zq)PvnL1UKbIiuCY%Clki1~?{{f1~*Y zHTA?!n{{{B-apv>{>*_n{(e1|&e<6J{xI?tlV9HT)t&ts##f5ENy$?#1!qL9u>e3t>(GutR>yk3!DF0h9A`M zRxk;I8iI;QS;ug+R{VV0UiTJNK__qqA1a7~1VbjM39_Am`@#rNCk#AE9J^4+SjCJ7 zeq@AjB_Jjsx*HqO_3u7e>k*PZOAl6~5O7ymoO?#zvM42ZY!o&)3}m_GKslR=iFJNs zGFoZD#Wa?&W9t|E1Hu@&|Cr5RD26DND@zfvwWascB+6J23cc#kM~^tz53B$3lR%Q( zjGj1xjhL?1Tsz;>imA3+1>Mn_If|M8G2 zdA!W|HDSv<7_I}Wea9eb|DX(P@WThqZ}etf+O$ev4D)K0eQ2J;eUbTx1%!1-4}&_L zHEHS1PTfH25(W2vy0VZ7r6;s*o(FDhGskWfW?iB%v2;xW(9m#fn>A#%R@SukK&=_2 z703W|4dTzG5*k%m#3LZc$V}x-9xCKX*`vO2Vo@eX%f%IV5`*Y_z@W1iNXY4rygE&+`E_RP)VW{VPQmx3LRFir6`qpF zYRi8Upw@f@c`OQNg-lY=&{)_wIo;Vvz;fIW>GnvAiM2D+03&i>okUB;36!k}ukuUzbJklR$bq{>T-F-yvOc$50-)HBnZqDf}pgTh#3UFgQp?RFsS zui4qzA!jaCP^h51aK3zYP7wKhLy!wQGE0mO01YbqKEQS3CgG|{zw~lg&-7*w6$otc zi&!bes{sdXap*Nn#dV!??z&o)oV$oeW{a6x67E1pK!q+Ucktpq>zB^Vg`&@>X??NQ z5%eH3FvVG}nSpvhk_k5>SAs}?n!5RbMioyI)|Q<8OkbGS z>lo@#H+!HxjxVXCtbn|?yBDE(+rOU~JH>{Nk8jGZtU>&K^hWI`@-gzG4FIIy0x&qS z!3|-G$|HYF!Q}4^oYe)zElsIqxX>JcFTR zcbb+E?i3{S!(XN8c!yv2;!8EAiX3L6e?ds%w(4C*6Nay;ijS9p0B?C|^YMw8EGj7p z!@+GFdv_`a-JBPSh(p%an&v9q)wi^3jG9B- z*DhePzuf$z1^8mWe5z}eS3bSIDlfIx|2t$@A|yE zxk>y=?JTDn*p~Vck_q%cd*i7yX-XvmAc?EX%kI`rP7Tw8Eu4%8>l1w*qX%eQ8t{${ z(^qBKkrKPH7+Ai{9t(L`M}JO~_s)aX;UeOUr=lLS@qWooCAb+) z_jaw{bcrSmX3Ta$#exE~!rQuv++SzsJ%sw@7H2XCpTC*ZH00=#y)QC(Mv5_T24$U7*PQPM>y$krI*zY2~$M)w*HZd1JEq$<3mMmZEvgU>gsj_W(Wp2tnfmaOafGF>_=?AcFPM=eRl5(-j}yh%qI3& z&S=szEi$N2A9ZzfHqaRxFkLTmX7XV%J3wC4dopJe3&zEzG1Z!?H&u)}Dp<#}$RY$Q z(xZu#1BOMD#pjWiQET_-waE6curNDRVW%f293c?3 zNY=n)7S_QZD#M28*j=Anz(!Q&88G1^_DeJ;W0+TGK#-w8xj0(Zui}@(t2tT+JWnoG z!Z@z2bcr@p#{4xG7Z#S|=rS4>&@kXt%$tu!`GXS2q93n0zxg7+Wka)=urLE^5q*3P zhauzL;dt0Zi#q}Zg_&m_9v-sI%}o(`1%<+-RMP|7frH-pG44$#3or{fXGn5#+Ua0s zP%aK5({b0@+FML;ut;lSp*1BXarZ%Awf+gJ_uDtenJ33iRMqgnYU%jw3J4*Z1Sqj{ z7sv-!);5ehBcAw2P`t4W6AL8us~b#U&r+-T0*&hvli6&MiZ7q6I~zzQW2`Np$hzJ} z5-jfw+CLLR($9CiAr4?@Isqy-S6W(H28cdaBA{%*B#wBRIuel*>J3Eyj_ST-k0@d? zL|@aGY;$!5&n-Y?{F&edlWECpIMmWEOp}WF(UtKdD2zF4-;3}vY-?dGy`$sQ%ymqG zIN2B3wspEB=TF1ZnN`oaX6o7TJgM;YJSW@XvQE844NB31Gl!C(3`_vnR zel%3C+0nm-~Dm^I;&f; z_|B;w>B)UDEd4aXA^!DppuZyG1 z;62wu0kkvh{#fe5S$;ed6XQ*T=lklFIRis2C0ZDdC~U#>L&6uD(C7>ZrO50Y6131h zVBC7?DU6@JD&dGM;#7?TUOMI#%HOQ2YMUxk16c$4i5(4_m8%&Iky+yVCa7GG^r{17 z^5wB=EPy60t%AQ026%>Cp~*@WXJ?FIB1YK<)BGG`Tbc3a8MM64sgRE4FQzdQ4ScPOF}g3!*uGHJg6p zIX{zl{e=Ay#4m7p{QLB5YUU~p+wmoZ=8HgXe}N%vM9j67L0Z!rsDps{2*Ed6gDH}p zo?mp6p8lw*k%iu@W$hH-sjCS6`t=QZeJ=an9H_@|kx0I02M0-U>FJgj9UbIV;Bei2 zf=ZONAxkyydtEdT7pD*!Y@(xO=*O#OageQi9!=p6f&vA^;2$DV%>H zs0Ur!($FEPt{_!lxUY!qkP-+jkruBlEG8voYwzgXNn@VL&(AMirrp_L@!qe}URlrq zE~%7kSLT!}S3KP!(ieJB)`CH|BIZ}iMkrgsINKkY8eK{k6&V?+)8+G_@JLXP9rc+nL+ZDnvVx}D!&3KdE%APr8+)e{G7T>8 zr?!*P(4KWOfK65js+oaF>iiwjJIgg+4uAUBgcDOkWZM$MwJrugsNWwO@-phwO)xem z7Fmw3bh&EJ(0~}hK_pd_RGy?W%;WDu6&bNH&Y;}#yCs;2iZ7XIZd7;1)5b>ap*+}k zn?a5B2lX$$$ZfOlM*fpqtQS{~Koy+Ty@C*4JvDz@Ccr{YOliHE0a}{xYHH|kS4;;tzR01Z7rzVdHy4?ZB~zFW%~?~^m*@W)lG$eF$$CQ8 zr(|fKHLsN7qzH%<}%U{^58bXd_y z0PkWoYzURL{{^Gh!8}{P`m~i_11T&u3a1hBkA)lOD%ZYyL7t`2+wY7;4h+B=%22S5^`%A-dU5k3AY9>vFh7u!E;Y)NVL+kD&9=z zYAt+aWiMZBb4B?S;!U#$XJ^?aC+C>~gkcvNsF)x5T)ln*MKerTO=lJ!Q4R#^+f-T* z&)CEGQHQZ*a#+i(IsoBd2;5oa<{q6B{?m)Jv`#=bvKgPVqoWk5m$+2v5sE;Kl>n7S zhdjd5)6)VXJ_p*`{As12aEp~%;~Q5X|J==~rBb^|SS3e1l{+pe>4GO$j<9lSG4V00HhF25#Z+0>k$0r$N<&D^^ao(`(6lKW!TG;QJny);;y{@K@XB9=83wl9Y_+N9v+NeKYH?MK)uz9`lIr-egcnzLsl)~-`3^(51rXnBb;dTEhOKmrO@ytE;x&;lKyNsM;11CP5iI=oD$>$>JmmG z%SWO*f}c2@JXa-!E0eaJ={&4CdAFlbvE&TY_&&5-9%N)>h>=8#UR~%)6?T@RI!1Us zxBC>SD?A-nb~ZFScnG_-j30gNWS7eJF|sC~Swbv)pa(o6#l*a1>c>~807v%N;^qVanX9bx2;JC%4!{OEQoGq$D{i1>aqFvbTzw*YvV%YBEpBnVI z2U}okKVdZITBHQC0ss4?KN!g|SBy`rs_!C@mRCxF9Q^C>{#r{4PrRUrbxu+$ffWda z!XZ}==a^!z36r^joN*u>NF}D?4)ZRX$afw6MKVCJq zW_vM9%W`f7slZMhd2f!$r=uky{QT_kEKlY!mS`LtXd%6+ zdiQVBh!0i>19t5liAuBcm|W$GAE2Q$R^0kyr+5LjIYx%}^=9U8OJ3Xfim2D24YPD) zNBH=qHe}#QJeFL>tCT-CVN2Odstwr^)B~!W0(C0%je;x(37T$rjuhYftXeocAAU`p z-Q|89f{6$r#G{4$^XpD?&-pCqq&8B+^3MnwG8S@wrHsbw&zs(!H%#oilw^t1^xo-q z8kjUfjy3x_ND5;5lHF@kwwg1M{dn$-OZyuJd1Kc=Z4P0s%%60+kgCfE*r)f6j<*Yn z3i)kR^f8a91kk>6&SewfvLZln#WjQwF8}trno%khZW~~sJ?4lryPz#4;XnOD9UnR( z#2yv0)XCcmfC+mNtv*GTh9GZtN##5^7#T%QbMk#sTJ{zXo=7bG=%4+xv-3Mv0O&ca z>K!{rMN(%Oidc8Xgq>h3vB(6_Ix7Mw z-XRAp_`YWi!kW7PTZVxt78|VDZ|H~}O0UQv7W>KPd4IPD4@hySh^^}x0>a2voz|_{ zUluh0V%1+LAi*!DCOiB4&av#B?lR4WdS>Q<^xm7?e`+FC zw6tZNE;hQ60SUH~`&sMzLc2ZCrFXRFYvpSb;^GV4GE{Z?r})QvX=HsCw{n(I@5OjdxdRDj!_kDfD z5dG>Q?u2UDl9&O>B(tw#J>j9gVjR zSGFDCFogKDkW0TVPank1a-Y5RPK}scR=g8|EQx$JqWXdp4e-+zoxTM;n$f;;w4Fcf zNO%}M&IrvVohj&P%uHeL9md=AIk^y5Hoczmj*^koJ_*<0XUxO>wIUjXF#baP$J{T4 z^QY`?`+b3Ae^EAa7TVj((?f6|PdKNy2C2ma2?O}XbZ;_BELA)!-{T`!5}F4p$ya4C z|L!3Uua_po;9k>vxR|05+ zpIV0Tu%JJYOnnv64>f!~#Q2f$9s!?{6{X5Nn|ppnhJ-WGsm~$;?K;+0KI>Igsacyyaw(ialY%G6F~0V;p5=oO9-y ztLx)Q_LcLp6@UDW5~dA$9J|fj+&^ch=L76yrAmDM&@SxM{9Yy zA(fTZcyfCC87M|L@d@+2Db_K%2N%ataig*9oScLm9*;HFz%EMeoz?bPI_Lb{oYEN_ zx|*i9vqUm@;@#1Vx+5|#t`-h<@;S)Z`Wrht zeIp|yxae3|50>9m^KCA!$}dJ1DUibBulZ48ljykPYyp+xs#$?tY-^_XIqE=zd#zU`A9n{{m@yEv*SXHkBGqNn1&Zo6%+M z5spOc`*1hX_n;(gcoHv`kidE(ElMUbZUDt!YRn zo|m6b1_ZxKJs>pY97+88u0Pjpk29Ea-qrBAdvaOY_R(L3CP@94o$tgxW+YHCzw5#~ z+D>epYfsiRf-Ie)N-{FumN|YLj9odOI4cVXpGG)WV{c8(+PZ$sEqMNxbh|8u25mK= z^C!jSWwC}EaQc7~Y8W7&a#-1JaOFI(NXYq4m7hRjhySvQGT-G9g5s{Xez{M(zk|3x z<5i!RanAcJj730vqqYNJoadP3)DXe}_PEZPnl2X4`pX^dk^t2flBqPOG`BRvs_Kgz zBn*{egoEsMf0j;xF+)UMnlcNK!By<9>CUKoF=>v}@C8339(Dwf6V9)+IF4u~bcIC4 zq>;i4(?mGN-Ia(IvcScwOw$|beL#Fr`YfxQ0`oGgfixHIE2y$S0?=V!GXxb8bDm&C zCx!y%IO&RiC@vJ=)lFlJZn)GPCwAl)8qPW5-f3;3`bMWvX;uMarPbUEv0r_7ka&XE z#$YP;4jC51WcAwOd{iX>hLo0;YJAmfLgIN{{T*0q>`}EpeyXDGI-my#c?*HPj~z38 zefuLXC*)6Fm*At%o8D0g?1u#qLiWijRip+i!G zhb5MNwokG~jK|i0E~-#N8hUU%ZLOQ&e{s#=9y{{omuvUFKTG>uYgFjfx_7L46@i{Q zHZCSDP0*>=5A(AmMQ!Qw7_sYq=3HlQ&od8mm}j|Z)=O4awh=EDAc03dnK;c-myiO)nO=}-bG*Ipbp1WCc8_7DsJ=^!>eHa<59F=q}CO%{jm*w zon}QySBMJPsoFq$@~{T66oCW?!SUsrn2}DvCNh7eke1GDyVs&9bOf4%%KBjyVM8=7 zmKc#qKcr;^l7z5MKsK`36&Lf>%}rd8uqi;QgyhT*%Aj?<1Uh%Gf>8Vl*Zf!AVKz2% zQ``W?pwsK+QpCyCwfQ((Ab9@a`FTH@<%mQ~;MOoSCZnIhR&{!v9MRp9Lsan8P3ZU6 z3jbAOd)?FbY2T7FA1JPy4mTmGJK{Yh`Y}HFabG(hGgfNB}qH z>ZBF(MJ6UYA4n`;`Q+&5`o{FKLYpS_H~n0Xm^OMXyFKdM7pVpUqqsP+vrgVdZ+Qy| z0G|p);{IhFDuxwrtz=pa{-*xL05t*YmeKinR!t(Li)kj8uVWg;rKsPft!VQKm3w=G z=PoYfSb)_=J}2qbYk^WgW!oJ^EI{Jv;X!8u?3-w5X&Hv$*g8Ak58u==7nN0j_&+5d zeXLiuRfXr>vubci(?c1~3r_r|Ztj=dL3LnOEDBDPElL9KpTV^)a2KoQ5~mI8+V41x zu?CM2M3(s#5*;1As-_``qXVnNm1I)Bu<^k6(xTCeW_t2dqQ$8r@5=@A~o8L(`5zPSxJ;9RG)-KD|kO^Znai1wf=CYDcM1cX}h zLsH>y|DL$$nSw?Pp_=s!zkFaaZwMEQrP{K{@`h_@^MURX_W02NEZ9#olsFw6bBEhZC($|VJRM-fZUnjZ5pP* zFg;oEyZ_Mwm`hPqV)QrvAP`AkOE?^z)z%z=Oa=TQ_gWl+1QYQgHh4QyGn z+BIu1(It-XxZj;j0)kJcKtVm!^fa+h>2VJ)_fQo$h@~T)iV45CmqH?xP#0j=yn^H@ z2%@ZJs!n@?Cru!`A17r8VK-#X3GKzF0<7gRprpL{KSJon5Poo5^g zaOzFE7U!D|0C2u_g{yj>+i3qw0H4%CNQn2Wpo0V zB4zzCr^5l-fPmfo&LX3rV2=sl`%xSnZ~2Xl_dTeoOFs$-2;`S3&J##}QAjaEPn?U& z%wu-@@q7E6+e@8;XYJ?G*^z(hq zmUerpRI?Y$7ZV$ujW2=Bvv8?nbbx(>fk6WSIdP!BAG@!=zj?O$S5le&Vws`>ph%1(Cy?r+`fftM|iJ# z5$`h2Fr)}N=_vMA#_s?S)KodauoV)PWg7G4uHPf=lctig_ti3!F$0?L#p8;Ymzi0_ zzN2AErv#K>PR<2>u%X*~f0rohDmA^?yClf&FT2x{^QbG}Z|cxuIwFFQ$E%<_p^TiD zAm&2Myk*1A7OJ33NUF@%2)ET%)FcllRo^eFs7{Vlv2HJv&;r9iH{PF5=2ibUlnFHd}$ByLy?Zxk5Jw?{|}N!bya4sgdqZN4izI5e6iN1_=r24oNA20i-3RyGy$BeSZJ7-fyBTVeUQmoPGA$ci+PN z=5q0E$yoh>kY3-0v-byELTk$E@3(qJ?iZHlZ%+kY6Cg0<9_ZiK^JCQhBI(CRtazX7 z|EQXwoP1?6^!gtVw&D90wbXJ?_hIENch))j8JfQ9wZ7H$hV>Y1Nt<`%)xmPoc zI3GUXP)$86Yz4nY=)WLj)xV_aAMhO-9AH=SH8Kha82;*SUT1R-r2pm#jO|Kbq{Ojs zU^sw_pok*_txi{0(z>mOYpt70qnDRrc^+?o%C*HWeAP<;DNZ5sI_K9mh=Tg0k0ZAuun5eu z?cIq5znLzQ-TgJR-P?O@#B=2C6-$HD*zQh#&s`AXp$}X8fs%d6&8E$NNW_cDm6K#4) z_$`rV+1Z$Px7*%THpRN?qz0G*PG2-o3PzQbD%yEBwSJDTi8NOS9si&SsoYZieEHb4 z@%OG{T|Zm=<9bS?;Qh7m2j`t`$-H#DWMJ^8ImpsjUhF%}>k|X-C{9GpcljMl(7*>V z7@Hp$|CgBa`CRXAJj>tqzNxX`#YuWMDt4$rDm{zmd|{VoO$}eIveJ{uY44av=yOXW zO7TCjB?DfY_DWT_heorF19Q!Z4zbX<+vaNT!~7JRkM)jE1^Z_A_8fK@ooAa%>q zKbIHN3J>MYpWL(Gk%{(@vT)A%{#t%Eoc-!)acgtT@Iki~DS0<+$kR8spytvZ)!5U) zbL2bz4K?TKk1+uvJ=?g&xSHV&Ofkrw8-3t_=BQF4HTx2nK|Ake-=)ylUETeqd_}Rl zw;S+7@*yV!OEIwpTo8U!Z? zpZTOZJkyoeHqtO2+hNQ9I|Ieew{p1}-wUfL<>rcKjRT{C?`G*Lh8}I_&w>hh4p@YJ zxgz_}`>A*=2(mNLW1P#ULXxdN51u_Kn|Zfvg(i|YVQ!bbleOq||8*dLS#{)c2hh-s z4w9E!x0M{O{?oK;gMV5-zNLAY%;bXj*<7exd3gbO675|WWh`6`1SXtl0Irie**9S% zYZn}CxSpDa*>(hGIy_0XLe|6RKlk1BzDXPTXvH_ilZrA-%?H%SHSwHd&8?BKK##7j z#HFE*onE_%%ge_4%uBj&!;W4i=3^=NXwVANk{+ErT#sEvGzE4oLI2tVYv@!xf?3_h z&s9m}OU*23H;{62gztNOfXDgAd3R~ilW&vLp)Zcm9G#Zb%5pr{pUj0Om?XimERQ9} zqdP5Dd{_9VpE7yu;53=#gl}at6<>``FD^A!-Nonm{+jg`lXUOAs(`-*n4-l4!um=o zQ1pYnboEn41n@WAyE{S{bN3Ro?2rU2o$wCZ`=MXP^Y+{I|Ab;xH)h}F2kIZ6@GR_3 z0p27}AO$l_+jIg`lIY`sfHRtB7PLCJp?UeQGxiUA$IiUP4yYP*n_Hxe{( z1zZjB#VqelJy9$~{8_qw+Wqx17==)A?w^flw$)dpF zQ0~9uW|Op0<>!ELki68|vC|lrwr~(l*)wr>mHqFXW&UVK@ut6uA)rR)jtwfv`(i_5 zCVsS3dSX$&xs-F6zz5NSE?eRul6ZWrhjZ<6ga`t;Q~8lD4L?3}*#Y|beP7YJ>Lna% z_l+-j%7>g;V!NT+7_(09FM&Qi5+p=cSc>sq=Qn>z%M3|yD%2cw>Z1VlF9lGtyrv8R zZpPHe+gAzMgl1o4;T%cfWb9A?=;9oCL5f{$SE6l#YVz+3H^r;7{&>dS_4f3QtI|Jh zKXT%0%gX95wS1gQOk(xq-CqX|eol=%y6P!T(LDxP{Ei8Ak~#`f?9>kOZAa8SHl%#@ z>Ds6&+I)JU_x{&f%;tscRg6Wc>hG}AD`P;(39SY^&J#B!V z^+n*?DNX&5sMpfxbIIq{jsiTc7WIqfr@*lkv$I|qphxD#E=im@=8tSw`AzKL_H54b z?cMz4mY_GKgV0BsA8A{kIE~Z#dwy@8Sc~6;dh<$1HJg z*|qfg;E!3&yNcPa&%|Q9?k_o*=$l6qfCa z{&SYV^jvM|U@^+rzum{xhT`I%?-yb95%vNFh6Mbo1HG>G>9-%W*`((08#mOnP=3&p zg+dF$!;2!{&<1&igsf^xH#7| z)Gxe-qOkB6{IouTM|qwW7QUX*o-HpMUDnq=2P?^P=5hDWPh0|@#pheQ5gfQh%E$+y z&yp=s=Oq!d^K1^|q5@S=N&LQD=*=b##RdHgPTieo9sUF89s`*0O>s^yF@HY%)34tT z?rkT;k1J#cPUN>l;6QYCwTTQ1iB=uC~r`47@K|jQ;3rn_WK#x}1sdmBmK$W;uG+RUA2Vkbc!RKx%Wm5 z(jmYNs*pDfD7uW3r1s;3YB=wSv!orBM9}c~(O=_{5#sEVj_dtLrSP>QP>72uceXv{ z>7r@1c6`ob7+e|3czM0CmJ^JFngyI*K(R&85ak^TDE?X`Ll$t<^9LnG&;-q3HxqHSS=@q_H!9BFQTxuH2YWo2m4SMr`TG}2|_a><=MJl;*30P^iZEaki75xyck z5k$-a{ulintSXX}R4SXI;@?#zf$v$wH4`x%e!mVA3R+tin??oNBgkao6v`BlP;r!kCg zO@F=0ea47@+`JNz2~7zihRWH$&;n_mEfatd-+d?^eol2c4SA{&zk~)CR3`H*mV4fK z!{g$=Bb*wFh<*<(oWC&pU9{QYCAjEwZuDqmKzugv;I*~L%vy?NcVjQGAo@ZKr!8`m zPh9xhOZ=P`;nZS^o#F|-p<}tT-v)W<YF&SV^Bqm0?+F%6wYhE@ELS%81 ze%5Yx#XPnKE14d)WDWQpwwGZ|}@R4;X`2rL=yIs;g(FyZ5fRQrH*e zJcvE7DJl~4*1q0h_dKtb8CQha4*0&<(q=ZNp+1x+4@~fTg**sy##{JK1{>U3{w`UH zoXJHI3oLWfNQ)>I{+0tLlH2`&3vPr>HRj8MRvB9mUA|`r?jeHC-@ZZ@o5i53Rx(Bb zQEwgdi(We+2{HcIa^DTufivv`(x|%Fdfi#t=_s>PC-G5z^ zw^fQVAQDPvdziJ4mzUyy0`hM663 zGQ$Ml-<}P|HextQ&VP@dFD!_!kH!l`um723B0?QIU2e!T3hRrdoKdwp{qXj0)#5)= zQ!166x%6}E?eU`iD9?0%6vG=T(iGh<4gcms0y5g4t=@IXO1o&PQ1~%_FS}YVo6Vq* zr(WCil1Q+&^V^N#MCN65;6`l|(Mc&aUBu)wWD?Cb&H z9MgDwQ8-a5gt+xhsIdJHaVIlSCn+(3BBj(unFMl!Q4NNcr7yPUh{lNU3Tlh4hSazo z*@Xs$60sMB-(_dFYdCqaz~t)mXyIeq)89W6K`jFVzO}{;*u`#kpOj{(q#2p{f9zTb>yc>bYRiJ5vC0oZxqE%#90)H`KcY8S;Go=9Iy=*5yrzQkay5rCq{6Y89nsrP^-G&pAJ?Zy zV3n506FoeeiZ=_2JJnX|Qoga&|%dP(?^tMIqgoP!cmDp@=e8{dc zkX1Cn*9?t+1J^(ef&`~ZR8-(AmbF(r?i*0erujTb3|aknCI429MQzzoq#>U^W&Rya zHv>Wvt^Jb3^?1LKmKpKRxidepC&Zd7m=x;OUs+m2D@{_ z;6GizzL{m4{|uq`?b(~}5=0)aC*9U?AuP}Kmc%uX;Roq2zJfVdUx>`+I+S1 zclh@wwFaKmuYRciUARs zU_~i())&QPiqVgN5Pu)xFER#QL=;Qq1v7Z1dDt|ybWOHpUn=7w!?i;~%zG6WuH9wd z!Q;yx$?-T55VF6Hr$QG_uUHA8UPcc`i;GwM$?C>rP*f)Wzw zDK?L91PNWzaMJ)IB3&bY+6X=DbKj%DiJ)?i`|m=D?qA_CBK8za5>=Xa9`QL5HQHyh zN7`ozrDYDv{O4&6&DK866k!HQjU0$$AX^eNVz@|79E|n~az~4cw>gUkUOM`Gf}-ra zVk{~SvppRM*EfEV<`}h-1(k~7e^}#;#I?9VF~2Al@#QRJx^#~@9sbfkaFvy>cTvs5 z;abC2S}QP}$Tv0deXxIwf=Zn_RzD@vT<=wOdX!97VShn8`_|h_K^}p&-VlZ6pW;9AcD$-=m9u`|+1r4HE(l?-fqoQkXXS zVe|dbqLoF&G_eSm)V&ertWL1`cbQ8#_^_5)5d69LCG@&2B9?Q+^E8*a)up5N2P^W6IPGD`Oz9)fI7)3>i+nTdP z?5`O)s=V7eZ@bCfo=tL@;!7vB2zzfU_on931W~zWukdtKQ;fe!HnI$FvAr45dHM7- zOeE%UdWyDmUxYG!-^ss<)I2##%3E9Z=in|q$gk4;qoJ)?IHutQ=yMi;oNlBha255@ zLtuf85p1!p6>(&M@jkDxJVXDS-=3a=4~1V@3yBGtaeEGwug)58TyyKKQQvztLm+q$ zhTD1(A50I)seWj`CuWpj_Ib~2Iu9XZyoi-AR0I8BJI!agzQE|i1g|xkkH^MPOkik% zh|)x$w@90}`2k>)l8uf$-k>NWBcCLcB z8T^kJFJEB7NGa1Znk@!srfh8|yiMo)P#P!NpcU!wtSw!LN1eiJwIAG0wU%q2E*Hlv z|6AvTSP&a8ZNoUzo|=|zvCwHnFV!@AQt`djC9e3iEWel&k> zMV^bWDPh-(%r@mAyO11=%@(i2lRc%UV`N8CnUawTqlYgDp>h@!cKe1II@1uHI4c-D z2Y?GhxV~A;I*M=5B$r6Ct}kH*4x7yE2nbKfIPv}Mb3&wohYS0=P)##K9^3g`PI^Tc z{7XT!hg$~gAQ0PYi;GM4+nyxy%{-2rG@7AAWQUkCAgn2QtG@?W&LOuVBJP;rS^23@ zBD3G2Jz<&4-kSMl;UAC+=X}_Dpw8?u;Ply!db{H#cM>95#Oi~FUqWRFcRcCVtB;nl zt_T)WhX!DP&*cHHTGU6x-o%zK1h0@bbx8ugtAQguxg(^O)UF!GJH66GY zF{ocYQ8v>4R@IqzR8#+cZV`VKBW$>&r2h|h#j4K7U2*Ez!eW*Y0Hx6~$rgOR;62Lk zQB)bx?;jklm&YuKuz4#Q$Iz4mE76Qf84lw>4Tt1z`94zv5wU!m4o`OGGIJF#A&0)2 zRd>aXT1Im%RdO-+tt;pRR<4qLK_>|WuH#o>PTvzV6g&)lPKQ7{s)c+KJpcIQt=SQx zN{oY>IWlm}S<JLRQdPQL&qhAt(1jM07c&TZ2}V0qM*n#L0bPN1N8-RN1_Z)nRz34 z=%hK;&x^l(x)l^A+wGx}Rvlhax;Hzq;AZ<0{1A4+@EIPzB0tSV-cAee>lI8OhQXhO z62+J42{}xV=qP8LD&Zd)y2+@Rq3olV)g4qx#bmgAc;qk>c1rmA^|vL?*&j2(267c% z?+7htO~aqCRZSe@sFUo8dtUBIrKa>nvnmcw#SHV_5tozOB?ewDgh7-1!t&N5>nC*^ z04#1u#p+1~vXVf)A_-_S6GPbV z%BF^!I*m+hOBsrby8!`~>%HU(o_-N*cXx5;^%+UaxJ3Q6sJy)dl>xPdEe)N&E~A6R zj5}Yoo75keE!So|M|llEq3Y?-@-kmOzveGsRs>6w9U227{2a~; zIrIotf+eNj&|dfl+@F7oZ4mPP+)ni4xl32xS?wv4)gZ(B2rNsL+#J{+xKuv7*(rjs zz)9)scPaClwY_J`D{vysCO}$m2s#Nd_x5UA$ zK`FhNZo(shw&3m9zud8%yP5bP!UBVTGhhB|#->BCWR$*^0Y%&{hx@*I z5VreCri(0Y4IXYGyF9-}ZwJ@j5xD%K@(q-V4qRA$>ZpCUq|*pkeP(#N)$!#6`E(aW zTED>9c+QX~VoTmuOmR&2$+d_g%;+gvxFPt5 z>8usYI5yEp`k$J!rO4+?`&6$->TXu;Qej|dGI71@FPh0ttI^62g$dkI)R%!?U2 zL0jwXshiWW+aO!%^?OBK-TG(ZC;0i;K(j*wgo>dGm=?zHep@CMHaNu5{L@&I4lyv` z+7xR>_04ZBvdUcvZHAa|mo zD=Z4CJQHZ{DLQfT?jjURY-pr@{6;Dy@jCtdAh2*Y{n{w-i{v@Gg{xN_&{<6W4O^ZM zhrCAaJl89PwrevYa?O}FGur&-QE|YU18yrLwX3dMPW;V&o0T+Q)!+j%pCQDwiZC=I zXS%Zl4;bkys#iXr9t>rqhNQ8;xo=-6fixMTY2id;b3_MpvvwDoq-3I>_0{a21WP@wMG%3VHAA>J0jydU7YUkpN<9&Gpq@G-4_-gUHkXUf#L(ft^7{%+kWx=^ zIyliIu56mx;-Z_eG(c>uJK1d0U5n>KM9;4Ke|)@qs$234y;iY`dO9(G7zyds9uUqL zVP*<~$k6ywIz4z$(Y;bP0%^K6$YO#u3xdlMGr?rYow13Zldo`uC|}$1VwmvG?L^4u)pu4-R;Ps#hUn*z)G�US*X zR2J7Xx_>2k0+$yYebC~8(=*t8?l(Z;iOr^xUv3vhsG3#E5!Z@M!pb9hw$3Vj#w$`( z*bq}x{6{0UzPg4j*O6H?4&?QM&L7lG<1t(8rupy-GN5I`bnx~GAJ8iaV z;e=|4j2128%>d_nvvD@Wr}3IuJ02Hw;_?La-9#Wk7iem)MHI{B%1{9j`JR&%jUI74 z{xN(a_2d5UC`cbd7+AmCpFhPr2a|?mnwe3b?Inn4^PBskL;33d^I8 z&)|-ax7GyAQg$CwByhQNw} zM!d9az&paO@eA;a679QL5Tjq7&49m$5>Y3hGa}H+MLcJp{46Ig zw@w*=&nEeE-t!}A0Tr+ogx?{=n~_%U^M#0vKmduLfmG-?YR^1v58!me%7+j;GRqi~ zKv69fL5y#iHs#RY&OSAs5tm%jBthuuos23m#^6M{;{Yf+Q8?$PXGa|GE8&U`ueYGU z5*z&3Z_B^YpUOivmfC!uqAYS&FKbFm?bF)YBw|Q3-&t5B4!(obI5|4V2kAeF!dVV>&FYj0bY6q85z`d;5m_oqviHSB{j7MB_$areE+YQWG~GNGYHnL?bjLY(p68=!r<8%@QUOew?~qZ2@43aDf!L5S%J z5KZ6%3puXA^m9p3-04szkY7NXbA!uyg+5w{KF&P;-$AM)Bnk zd+1iUE(pumSr`UyEMsB0cm%!s202Ndz9FcWI=G%(Aymi-?FH@KwZZB{=KYfb)^y$-kZA}Fj;M`tWzcXFL8!mSp`#RBADze zKuJAL7cCj&OQvhx7F9enV03OjuXci;jRFuV!KyeR8aDudkb5Osl;>cF!;0WtmKbg# zu5lLd`{zfyv#R%GCmZl>|D8a_@*75Wge^_ca#3lk933;FxF&t$Ga;0p_$n-c6pBnr z)7HMg)dF^VQoxTvMEScM@B_?S+h2I7-&)Tj9v|9O8big6iJ@NWZP(obmhVCJzst%U zX_#FTNx__|FQi|EV}m;dNhCPUsQZo74O>lCV+QFUF=Vls__6 zaPy`N+O1=!dpg?f-OlUC=E@-SCeAlSXZRb-js5H zOr}2+?CdR4%E~FP8!8{3m>oMadqi}%DAw(rt-Rjq?~ooB7eph;td`M1Q#Cd|9(g!X zH1gcV0R;vYGOe+mJ^U1bw|?mv#0lCu()`|J$8V-NPlTBgpJ=vMcilA|)26^| zY)VIfr?9N^@~?ldNl4a>3=Qp=LDVH(qi;;>F5;5EQu9tv$!{5n_9c~sh}uEo8%imB zv`xZWV-95iTNthcmKOf_fW5lqGcpIgJf1>gPO>CO?uOJeB@oul>rD~MG{eBy8#fkM zcM4QwgqdFMAKdbW(Zx@h{gXET-}CmJk8Gt7V#t*LV(uV;pmI;N0B~TI$&9>g@hB{S zntK9G1rE~w=A*=hiPz-7#5*LW%e3x9HKPfvCO8#N^jksOb>`)6L!6BRawkCM{hrbj zPLkO-K~vm;Ym2GL#EAt{8=$PJFgq1CjlPJ}g2B5v5D-x#M1LHT< zgPSIq$9Kzq3e!LqKaL}N{){dZ>@Q`TQ!S*OLgDmaTZx zNY7bq`&M#fo$nhwJvUogwXF4=6%}oss}6(}3%vOHhW~|Vg7$ak)K^4ovwsED*K4lF zTD}BG+?#*gyxSV4OeSGgLx=$H&y)jKfIbIj$$b*1{>1k~=6SiJX5blDpV~;Z{O2&n zgfQOM+1dZhDC{oWkbH8QOCRj8ATYV%Sd4ebaHb71<>WsZ&sh;#yfpb*JRB7~Ua50Q zpId@`<`9>58iCnUasWaJE3MQy{~f0bJ5Rf0N5CbxZZ>;No}+8$d`x@c9YK$P6snk- zB9obL!ZkrKM4E~#P=CE|1RX@ly2dH^o@QoeBklxmD5m$F&P(3WV}lWSp%EYc8L*N= zfh)ma&2-$~=*^Yck_4l^OiWzf>!ihzwx5?hXqG+q+8>O?zP*~r@p%Xj&JQJeHO8o9 zHcpQ)lL%KG_Dbe?SZFv~wWa9HbU_h@G2F)I2+Y|4{7vZ~2>>9Bn%7!SgGA-d;N;L& zpZQl|&Dd(|5`KGx!#2O0%(c$6YYEUQBxHU@FTIHk4GZ&OwYR6oGp5XuEHBRsL4@H# z@%N|ctG*A~+74+??)OPg-n7^$EY2zIszQHvC$sA>jZaJrQV2OO{ORa;qZC-ecR1NJ zu=HzQ!Y?aKQaR5=tDdYXrq50LmQ3lc4-+I8EcD+S^hQX zT$RrVlUw?zd^uE^24l$5PB5UwL2@lG`B`8Lma{u<6#)P$YX54S+WIxv$Sz^+)DsVN zJDvy%!qYX4xca?A2t{InjoK_&+5St?*i{D!3#gMiZG-@eY}faWjpK@X`Y;3*B@2T2 zq`En?C@Wy*r4DgSg(>UzkjJv|GCsh%P6qZ2%*_=-y!tchYj=y0}=U!zLmbwm5|QE`J#U{TR7mzAb^o7 zAfiv5P6243N6zPri115((T2mlUjsGv)Ii%>Id)er?rYREL{;xO8$}|pj?d9{&^DmI z@hVp8pB-SO4#o4( z1z|`y8kMaW14BMv4lcZ7aqx^SK;K}JvY=U%I;GTa4%>=MSD=%C_@)g(eqUz`PVQN% zhIJ8w!Fe|SU59@FEXUNQ3(9x`4*tc+*l^2BPdt73>WeSyugVc$6THKd;pm1Jmytom z$;w(H#u!=@-a3kr_g(vt-yUug8!e?R=25~(8%p$IT;T(^iXI4yy>9aFxnmAo)6S6V z>b8sX@wk4RUnuKfSRwF<14H1aJ}ndCSdj5WbL7u~CDIyh1e#DF1RxWBDVcz<40nQ0 z6BvMvpK&h!W1{qZz!LovMp0^&>un3b)P9cCH0YZ{({#=qE^A;x7?nL&#K{}_={8ln!ki-%YnFYV zIH(?pvGw}+^y)75Q#`=^+oJtp0_tfpKQM{uHqxR|@Oczer2=xN&0josGOFJM=evBs zyD`^Y`!cP1!-PiEe^a0mA7b(G#Ke$d%OLA3=i+HDDgKRJ3(K1cR5|T}3BcctnSg?y zWfr(b%Pl5mOvX_gD|~h6|2i87*p@vkJ8ytCXcP$UB6cV(bx2Oq)yaK(bFJ~|^MA;z zy1pLo^z5v~5V*E9XpsE5fq*)S{$;tmL+e{wZO+%@-UhonXDR*t{Z4F^atB*Zt4F9I zvN;n1RQ$&-JN)t}av)9NP04TwaZOcjF1_gs84z%P`RsiUb^fpK3}6uO<+RwoJK28R zO5wm5$dDLH1I+W_m-l78_ANAvkAB944#(%9v`d`bHO3V{XMpUT-6gb84wYfe+5vE;OsN$O^M9X=x%@d z*~KxKr1q9Y( zi^dqUZ{l4C#&g`@DZAIHS1r@Csy02BdEdW70S4hj85NkB!9>M__2a zABpRpzQ{r_V#en`{`R$jzhUZWK%^Zh8QCTxMdd99uwhidw^%mX($%C(JTJ%=j~B1C;Z|l6+rzK&8$v-pD#OgGE+omxT`jH_O44#Wy76 z&#mn2ejQ|FkT&P6zC`&Ffvm2{Ux(QfEstCfUp*en;{*X2(3S$|sgYmRzu8q{(IJj~ z?5vjX?ClG@h5r^b63Ns8^~Mg~3M4BAMigN7`padU3jyI!yD{hQHfHRLDu*DSzJC=( zKTlq=SZr8sS*R>rdRYXK@w3ZcXxsIZtc;{ZEY&LgT%$^7qCGKH3;Q8BQ5s;l2Oc23 z8HmJAm$BgpMPdR10$Am>0FhN+)^nR~zr0ut4?Sl@eP%MVNQ5khiy}6d zcljqUuwmy4t#{av87o{N_x>L16+{FTkN@Q>^pQrNf<<9?$0}t2D6waB! zqOuYvfl)8BLpdJ9@F;&b{ruVe-wTnGdWGyX6la{x=YeNq5MzuFK-O0Ri`Sa_`T2dL z$O`M!UbDeiHo(uU><%jdXab^ORT4HDfT-B;OsYu`#ghTi{pw)FDf|vKVmMwE3{-&e z7d|QV<7Jj(7{cUK!7tGNE9R0)Mj$L-6MVQ?pOA=k!_u#vph0 z#?)|@0uYFtDh&=WYTgw%dg!g!`1!B}Lu7J?zB3gdG3kK+=zt7I3B>s8&$DP{Rt|aj z@@Jor;WM2{KOv}Wo)4{ZL{NrE+GK!Ep#MjgPI!aEjaZqt0H9yYM(9~1jha_M?=K5L zh4Oc~^61^5+x;JA$CXQ~`E>UEqAUX7^((ip+WK$zzLMvAn1M(qbu_Qs%i_mIJ6U{g z?9TzGkZcnZljdv*-%DfQhK)(hZtUp^mPB=6paD$!*INx+nUn4uiF^l9KAyPShHAD0 z!)AIJ@xx$wrKTe5=VTclVF8g05o3c;>kN^qcg^ z0pK0CieW{t%74AMc$JAk$^Q5>A@AZJ3uxwHgO4{Fxpv%c16&6OjPeY~QCUy`7`XBU z-x^M&(`P6DnX)iCu1W+@In+#TOhKi`ncR>Zim3n^OBV^K&@|B`CCs8)s(z$6N4?E( zeqa)>hyeVn0w#C(vL%gO;IS(Y{-ZiM1L>OQlKon}9VM%efRqcg{fGPkOXr&-EjkQ% z!$FP=nkw|-1y4fp-9kdmGk6@cA#8HvtT4_R#t=zpuM4U-(=c^XrNIPJqyDE-VTiyR z+#2GyItHQTLClTFZ%M+@E{pAbWod?^6f}dsH=U2E?~4ZC#1AeCo7c#WTfPv6$>jsy zQbc17k&uEALa3z=1y*=d_DX)SlKN<)V7~HygD5f(ZhDueg+AU~3-svET172oN+NeO z;N+rW$8TiwPx4PV6Sun0cBp)jy32+;AEv}j0M~%ewb%E?#;f7cQ4LSvt`u*L9ryKa zkvnCe*-LWLuL?9<)`PHOR%QM-Wx^$uC7X`{mzt(duhoAQxn?ht{JPmfk-Hol8)JHo zj*AJrnz#o;j><7J{kbP~2)F;x%E=5SAx$`$qh(;|o*5iuH@=OPla0)Rs!krv3E;+p zLzPGfyeH~vyP_wuH0&C%2^2sPwAON5G(bD0XODsfR($-aJ8Y%DIuQY|C)Z-xmH@2F z#RQLCU5xk!t{TF-1rrHhoeoBwd{AcwO7xv@)LcybL`)^7=!1NSQXx?a#D42c4j$j_ zi~9_qRj;HeLWzJXE~AGI3`qxrW%P(U4x?18Ufl5#OJ7@`-Ra_H_tgtN2<%TwEqMH&@~6H)wPH ztYZO5pC4vi^y!~;f?Oagq0md`-l+{T-O)4^>-(wKVgX@&kH@QB{(x7)LsHl3IS96h zx0q2c=ZI8zQ>9jTUk_mBmp`m=(eNW59;g1XkM2_7y*}8e+tgnz+6V>OTFv7{TtVS-CAR6vTih3W4F7loUeb4wF20B%rOUD?-swzNbqh!l$ELs_o~n+z8O zBZ^Cfp1~O05>XUDil_4{M^Yo!arj%D`!BD3fV|S_YlU%&+;J6?(f>H3!mHD z{3|h9+Kx|4t=^r$^J3_HzL&D?(OJPdWF@a-rbs2k{KNULRysojS)(E9+fV!r90GXv zCj8Vqjy8>d2YW~&2>`j~7+VN2P*f{qL5e++e&}i-MTC#ZmyeeO%P|bDmerzz7DNE7F-4sHh ziPFVIz2i>;6D2^*=K>0ly}N2X1E6Uh$PJ z|1QX0OFF&c=jX4>7B_uiH`ArssE*bdPe#BiXj(P9cn`Px-}dbHMuh*G zB)d*qurbtVzt6AB(5`(N0{f|Ka#mEtBC@ZC{cD<-85!+{QhC;!fv{VOZCe}@J&M22 zd7tpTMVdLF5;(+aP8)Ijie?Hk6g&JU5CKl+-j>l_!52?EK-hP>4h+840AW$OmQ>(? z5ecZKhJx_1msfQcws%N{X9!5}t-ngMBDYWAGc8a~?8&!Z4X)`;?>ksnGx@%tb^`*M zz2r00GBdFTLCOniT!4odQ)LGT<4ZAY4G^BI=_=A#RDUO2|10gI=Q2`G0;1J;5Gp*oNE9v&xSv-`eWI)0zH4nXW*ddM0fNM)xl&O-sl zdS|lV-`PzntEt5l&bConiE(1@k#80EHVi8O?m7OzYl?yKsxIBdn%uskVz0ZWS4dDa z>zbRJDSjNrfr`u4|2~`?S$O!_Q)l+d%I^y~rQ5%=HINtFmX9L80dBdfHcU-IzkNX@ z7ek2O^-M|wO#B?stcVhG<6=eTS)flPC5Hs^xl`}gYH@T;0zh6J{%(REP*}^w+R3Sb zatI^>e7dF;1`#`rLwH&sQ@?8YTxotQBhcTnD|{G(xH?~AFl}i3N`0r`Vx}NLChd~f zrv!L8(C%Pq4sVhRrI8c2eTlC`m(?Z~D9kB6^-lLoKo(b*=V}zI06IP5LO(nPY#I1} zgHa2IPKLHKw0E|=w}7-`^)o57M#uF70IrfzfQtK)-T#A7=RGtfP~30=UIr@+2jV z{lMajOMv_4U{(L++ZZeZTwc|*G--oZSWlNL9!EY0lz6`~a^UfC z`Vm(mRR%z)v{zP^Za>!}kjYh>PLD}%%O+6;Dbmz1A|wF?ovoJd&`LiSr-6=H&lZs5 zI&GNGcWQ0^2F2G%si>+TF}q63ah*>2a`6@O=q7tKvLDY3PH3{Zpy&;%$wmDadY3tM z37L#qTMTfbWPM3Y7y|-#RYz9}KrX=-+2oJQ`AjuAq+k+oFR)`29{{~`fQH>2Kx@&x z1F+lsOMX~W&E@s4cE13@=JuzAo2V!l3ZqDmNhz=^+0NX|%D}pctdlv*zgXMYj&kZX zMpZ^Bqp?0q`9eUGr7S3+%wD#}$gZchLT9GhSL>G9F$zaj@_eJRLJe)t^wznc=P@Qb zA@sbAb!GDP@|O70=MTINtDKaSl)8N7A(@l01bMFOpQVTE@G$v$PcXI07#L(NvtG@mJN%0m?Ty92+%Jm zP)S5nxDFnv{JweN+k1E*BFnSpHM*8Gx}#GH=!FYA-i_Wm8#XE&-kfdw*(FuO_?jo~ z*xg!2^mkU1jz$N~1LV`TxpT)irU7*$`z1CHXeELa7f-{{@t_ZQS$cd5X>)(QE&C;~ z4ZkY8g*xwopQ{Wl|2uK8?(fPapL0oOQ6A&u-gUC!EHTgX`=^H^d^x*>ganX~%Th)T zQ&o^(=AE)7hPOEN02?c`F=2g_CiXBVyE02YPwg>A@Qa!3>FMcDX42~ya>N@_KH^^@ z9aa7O^hC=|RSo^9SYY{f@AfcF1q)-E3WY=5UjE>H59F1l!oj-lQGAtSA5!G4tQfMrLFMMKfm6X< zUzgk|o4``W#tJ9|K3?+MM>;qV{1cP#&b;W7;`&(>Dybey4h{C#v*eZ71Vk>=p1}LV zvW4o|zvcjo`}a332OLxDm`Uz?xRJ&`&_fAcM^uVkN~;H?e&Ky=2`Y_@?AF2(W@)_f zoH#Ds>kPptdXwLDnSv+gwk^gLVw*bpUKJKU%k(Guyk&BWA@XlId?EHgPRdLo z%0r?#q>`yOCpDvdE_gfv4h)8~DTP10OPIli(s2jQZai^841S{L#J$tZDXvuG``}8D zpTF@2NE5RYNy&c!S%bn%D)g@|87uIoV;SbfO6I<)&(*fYyAS2L`Aa*Lv8fQ`JhsyE z@9?sN;pZ%f{JP;qVB%*p-4?Y@CnC((nL7ch)9v8Xr!8 zG`{w3=h6$KWJZvGLwEva0$QmxSd=U+h~cIM`@%OswaqVnMuDMZs%ymo^edYll43=W zmf4Z0gqP7_ZWncAUpdm~8fL66V?4C!;F`Y8NLg&Kp_UU+jAv%iz=-37%F^$+rb0}5 zb8bL}i~!oAkDg~gj%yS3jo(r?^EW{RqVNEoZJ(c!ky8XPDs2TS=%S?CPMo=$+c~_s zSu~MS$T_vbr0LVzcj*v(z&S_X-O8#pSL{mTzSMokgIk}J7m3j(>4x_4^pvF{NDbV+ zWqyr{iVBs@|6-#(Umtknxwp5cURv5TU>viMGRVnM1y?XO>1Ntas+tW<{Hm={h+IFx z7IaxYxtKXw4#G|f3JS9O#7~jFrtqMUmU6V5TX7W}e`g-hw?E@tcW6f2oc1+H$-Yzr z2#T<%sKGRv!26H=z~iwC^kA?6AoZUHiWDmf3d;9f`Gll`f}0xGPd^Fei@7=Lnmuir z50;mGEVOmJfPyeFN>%q!^xZO}5vOF_TZUcrVJNiEr4-)_AUa1!Nc;U`?f`;w{uMyvCQYB&fdwAh zhYwcx5&#&t3#s(H4x=lh{?J(isWARuy7lamegZrHD&5V|RK+(016mBN;e~}CiV|C! zo5@XpQ!38q&!2I;#m%k&kMviLb9Ht@07dVyyT2<@;YDVi*y_aGyuJ6C8u1OT6`L)0 zkYRHCTAyzhG0DNHp{MuNMoTLJT`v81rV6|Iqpx#nPW)Kn>41H`)7zu7nlmMK+VMhS zHL?_6wqx&`1`E1#;NPsC%XW=Q#ZtST9-pE{7Z8F#w!_x7zG4(^GU%i#qFTM{ImP8d zJ1NE}#ZTq|w&%N2W2?mF#dnhI{<(ecyX8&olftGr!sAoPBnzwYCUrM(+d^UpOo>@n$2sQ!;)->mws56OSb* zO_}L@(S%mvHEQnFrO;E-rb_Q`xw3V4QFo=q0oalo0f~$m^~ydidzNv|RitH{oMK}+ zx-K3Luo24iV=~WTX{u?$--2vw{$4~G*J-5%^ZEUgGA>q^_;-H7@@wJ`XKQm7fsynr z50lj!dRi@9ABr5(K!C{jk`o$QRKP1FjJwXavV2rlFGDP4#=m*B8qP>C*S*qER~^EL z^>TRhHQNibtgIvygfq8#W@RV~x04gP>_YGmJsT~AFsU(t?SLe$Bbt;UmEi&!mAvm> zZ@xMU!slL7M7{^C1_=lpXwF+04E5qX|8ra(BXm5S6|3g>RFEJ3F}3x1w9?wf85n6@ zAXN9wm-8%1&&aq)qSbP{0z=P-fH@c0NJ#~4o*(aO;uGSDfXzm zE2PM6{8{aOb5i*D_}DHZD?2MCElp^DeKgM!hC|nWaCG!Y-1*ILK=)>3huxCmmUJ z=i8X4#HRVwMpqB7fL3}pRn)O}p>wMTbQ?-g)ZuwG>u}WJ3yW6J#4s6$X{sv2I9-2$ zVV8ymXUvsmiQx%??t?vNKD}<~^{laS^1itw&fsJk$Ju2S0Hhvzj!*e%_}+ma9ijhS zwYVzmtcBa0f)D-I9S;+0Kd21}t1s*+jCif)59g>>Z-}7v21zBG7T^2?&dhqp3JQQQ zUiieAfk)FeKb#;Jnr@6K-Vp|2BXJrMZ#Ehx2OawJzdDsvZZA6rN5auc8z^n{oHyEU zhn7c0jnRF>O31sxq0M%;UZL3Dv-5gy0LR=if1wx>9{w$$Oq|K#!Z*(3<+##B*EUSC zM|u9tIk~rI(XIzzh|k2HtOIZE^3(mE(Xi5xmlbKLit{W?P21I)td~`TUU;j-0lOhE zl)F-=<0K@BhbzT5KP5UTqBryY%?Ju-&`M&$RB~!6Ou1HzlY+5v!3j_{^uYP7@ojl- zPE8VOPcky6P#(i#a8TsA&rAjvUVFCwboA(Du@kkQsC0N}T@Vh^I>mIzNnG-^*O=!s zfK7UhmRD@SrPf>03JW{e#Kf%L{Jj%d~y{rw+B3D~g7e7fqF;!~1Efz|g_n$z~+5 zmOIn}cu|Uqd{5DWDxgR)d772Z78ko&Wiyj!rE_C%3#eS%mdK}t-Or@~10C@rO`tgO z_ApV@t<-;@fyL|DZsMbmRsob@C5fJd#MmI<`nqT-N60vU9S@bGjnCvh5H9fqdvk+X zPeTNty7T~k;OH08{~Ay%W=o6m@)N7Uuhn*Pp2B;lOy|_bsR?XhMwqFRHMlVW(CIPa zJP{iDKslGvWGwS3p+|pyB#ZCm;b3CoeCzY#_)r#`@6pfZ?I2>h`T6-Z#qS%$VmK9F ziKcscVWJ#W8(qj{EV{Ft2j7KKf8_jH-WWf^;rglMTTkZUIZ9@Sd}-;X9S@9))$PrO zNK!}~nc{iCr}rEjDST;}%YDVi1HJ*{w@lvId#%p>!^1}hBWY}(<6>ihL`cgupMik^ zWooA8`ETnL>`WYVd@@M^4FClEpwDROZ&C^Jx+nX7N-ByV0E%j7J{UdX3wT7W(^GUJ z+=lIhK^3j#B&MPp!{Jn!OZekhT3W&f#|K>C1H1G{r@b0fE&ILQR23#q0kl>$DExNC zD{Y+w(vYkFYh+oo0~2X5IZhGJ*-m<|X3Wy%7Nd(^HAEcj?BTV26B^;o8px4#q=Qn! z&dB0nGs@om1Rz>#lQ%c z$&vp4J)@cZ8Vc3ghgC9pmOJ_yH&fFhBtv!RqT*uZ7x%M?aCN>%b{Tb%ZLPsY!JQIM zwynygD3hvE^OEBJ8p;Eg*5=0erBJ?S&#z}4R;t_oSf%osnn6cL$74r)w}q3LtH_qj z^vrpA&N1Kp{ogkG`U;wG5ToE(&<8xoAt=o#<)+}s{0L1wH~`ASo-dDg4kRELrJ=gX zF2^`~`dV2%#hh)J3Ow6;k#L^Y&gPw@_Mu>akMq6RPj!S4+ z#HvOmP=;uZ2`uP}*QTw>N%D*qD~0Xs;aXsIdnEOtIX}?1xC;0ISYii4D!3uZe8f2_ z+RC#2(SW#cWp+|g^W!%fwrKmC`==o}<-2qYkc*MHJ<;BLr!bQ6%+i@)=OG0cEw%|g zD#EY4aZ|$hr6#hrhx5~e)pf1BRycxzwk0K5@M$kI%YCZ5JKnfCq8}@F>fPO1+$!d8 z`JyF6ZBZbzuW1{Urj^VD6GW!$8!$i-ipG--fKc=u6{5!^{^vH&t$B8>cAO}e-7N+S zU0E|!Dk1B|V0YS@do6U&j>nC&JuQo3zmD=czLP3wb14Rp`rcK5rl3EFYsR)%U8J(4;`iu=%I2T2zs3SS!Sa+ zVmd9fbUTr>9aMDWzPbkSifTiW%j2ll=FVa z8B5ih%NLK`f+GU1?Y-2_9E6WFq0eU-PaE5*5iB(iEvMTiV z;_pt;!r_dhq?+&jK`2-fP=;7-#y)t*c%_Sr%S1t9RV@}_Q5r%j8nA0)c08N$cas02POi-&;+S?3VPmqJxiL4j0o_J<0jz%I)(j>D3bW&{pG zGB2bGLaw)4L}Enu(%-dj< z?I)D&x|7FY&kvp+VpiJAsGFYpW~+NRuhZ0!97S<1YzXcyv^tMwjcM|5x?$&)11!J5pXe?~XnEklnW6yFE0S16P#VRm5XbG3aU9z2G zH@9%`p|a5F)pNCQs;8)@6ws)#THN_)_4)I98$X_Mu}Nu$VL-^Eexf(id>mXRM*Ug| zQd!GsLVzgLCNJAK=s?{l2U`_NoypX{XSmMG`c6|N00Hat%dc}D9TFnWrXcb>da7Z{ z_2-7|ck9~M-6~%g95R{LYDwSRFh8beMJk)PWu@d!1lgptY~3&T`7h3D)T>N_*r%YN zl{5g|%+SV0r*qr>q6yF>4JUI320pkdb8aYCgDgK`&Nv?Kj6uQUVcA*r@8+7%YTsQJ zM@|w!d^T4N)s8 zJ2Y_w7_szxvdnL2`c#KE-xI9dJ8n8By;gOFglHp^dVd^ahe7t$#MD&$$UGq>B?KB< zK~>?k#HrOl5d8k-<>g1tTm{WMc246TPL&7BXW!&ui&lu0Fk}<`jdO3$0x%- zN@@$O4Z@i^>0FMHN zlfK>$Q?grbt~!6s-I7MFA7QBe-HG{8xzqHrGLEkI;p?ylp+hyYFx+!fTS)_IH;!So zxV`NeSIv&67Hq`dG@b(aw9n<1(xSECT-lTnLxi+Jy>b)4AL}X?LksRG*S6+r6n6_< z)D|>Cgm78vM#L2rJH-{_=DQnfOxk97I`$~u*SeVQdwwj7Ne)0W?(QjFPr@FPx;im8 znf{sydgi*z1S<4cA%Q;}H>ys~4BDLNWn)*~rWI&iT%m z<=aA{xa-=Y4lnFHEp~VsxkarTblmVbAjz|AII!fOoz;78aSl0M>v5N<*C6BmXeIuV z*|3z+;yLaf7!KfJHY?kqQx7;74?4SDC`alU3k{WiSb6m5Wrura)Zur=w=3S2MDuu0 zG_=9WU+qu+tYGM*^Zl!L8<;Zg;5soTv#&UpnHue*ik5?%(6I0tRAH((=i7OBHwBW} z78fKVkU<%SGg6HCxuv4?5gBDaDMe-Dp>Q=0`_#y|{G7hVj=7oZn>`9OSfSa~Pus>n zw1uOP1$Qd6AX7+=m`V!`2erIRVy90#TcWG2{%pJ-yu8D{jj`&U-sjdU_TqwQ zRoYQ+9bPhTT#V|V#77W{(u0aDVf)D!h(&96MU}pO&2zfk&hsQ-+Rx4{UC~;tai}aw z+l4~<*QM2$w!YpO29eOVXL*wAr$vJ`I#R0^iq{yuEB8$shQbOlY`i>aYqsbq6p{)){GG}9`F6-pLSVCb=V^#BH#S&G8M5D~Q||Heu6|`t->Wzm~r1l$v@B!z|U{H5;kZ zUw49ej{_@EusrCH6_JRTnO-7zNj<7{u%6H+{ND1DIgV~}TDfkTWf~E4iLihrhN_i* za8OqF3Fo|2yyH3;0fhSXiZCnya$s7J`P9v6dD1LNO~ljO7!V^@EiyM&d*5CUlDO94 z;nbDB3GQ%#7kgY>Jdhyw(2Cw)f){UapogUFi`NN2|2P>bD`Q2_B}ylUi6NnLk}v|O z&$0SaMOM2_HI;^8H=hq)!xQ$d$Mnbo>OV}(8RlbL&}G)u#xU~g-y{ivPeuvZMhWRd zi?UyLR)0TodLNJ~W96UhWFU$Tr!X(c?X?QWe}E47lqOt><&Un@k+Ra4-?zIWD!BEj z6$IjZ5HHZ%hcjjP1gDSnaVI>b42A@W!T18=GBl?ynO@7KjjKfIN|J?h>*F2oL3}m5@kOKA;IWjwbR&m! zaw(ngjd1R|o>mEPy`eS}lhdYf17Q5?teRsLZMR;S^vLjPadC-pih45n;(~KTU2V1G z^!Rv8FSX+5=kD@SXbI^*FaXZ-CMsi^I9Q|A^7_}}Y%dd0GZRRZ)|x{4bPL;u*l0tS zC>P{@-Ya2l9Tl8KqkvQ08kKEU^vjn1Th5t_%5 z2pQcxi+~qL1lLnungKqS5jn`2j?7NUTR6SxL3V8PdX)zQb~g}HG;abb`U9oSO3XV} zf~;s(;7Mus%@0V7DIrSO{p(ixgQabNXK9)>8eNY9n2QdqRx7cnUi-0{GCDX~;C{Yz zeUd^GuA}dooFnIv2@!a^<4Dq^cb~VS2wRS+@((C!TvYkTDBj0}DE8v(<@0*e(wt%9 z4{rP&vQ!?gbWw|D?GOZr^j#X8mpJNoyi|bAQU>bNfvp8(`|+-hjzy6pofE-@08#R} zyU`FG#;=oPLqQ2%airP&z*qVBTDw8m^=^plrR>+5oT;8aUtx(w(GQDVmn7*s`^{X| z!C3l|3aVW!?EHsV9_!b7e3f1qSeaC%T@+bYfu4`Ou_~J~639SfBrA#tz!0Z6{;Zs} zkffufqZ(vsB=;pM>j~dJCIYG_D&dp8I9^(JoQR&kPU9o{{q5KbDoP2}+Gv^u1L4oYicHR6o zfLwmjNFH}NLP9#jo&R!e_Df7`#;&Hy!1{nMfD--@qjM6N8VZbn?d|>jaR+pwgr8YE ztX@-w8y>oYs4T_`<*D^#yM1mBJ+TZfcTbZpm(IGKqI&Ig%g_^NEF9)TIB!y6@iBcD zoFeX>Wv*^UNv3F`OYuSz(|&D9Sm<9lHX4Q%q4;vY9Xlr{S}>`*hozPCw|-odmQ{B7 zkj@U!UvgC9vfHRX9d_}47^OA+mrmLLNlq9Y{@%j<>J^EEgs_0pDjm5;c(nm!r{9;J z>N9}DV8@*reO%j#*!~euY}Q9q7w@)N2sDx)yLYl z09z~dK%Qd8ajcEYA}R<7!W-U?stZqa*J*0A2*jpH$@EaT)i-MbD-`OeqDR$K+A4ze zQAH1YdZ&-MGnqW)3$gT6F6b#3ZB9>xa}Dw0xp{ecw9MR_zWE&cwd_r zoTku7ND+oK`)?<=<2qHwP}eWsGcb&o@vTe@nnn+f@MiJtfwj{zR3^a!SIkDT-79igY8bo%TRa6y_iVf{CFF{17-aD z>}|{!DLY~|)K!$@JL3LGV~uGzLdd(7h4$@gCI0*!tL#jG`~zqb!{^?c*e!yP0lwgv zJZv>_?(b023QR4Ku+fQXBy8gObz0qj^De)s>`PfXskJ7WD2vf{t8XxQ#g$I3F#acU z@(XVUc_zqer+>zSo`ySF9K0LTtiUAbBYfIGN})NQ=$8~jnYOo(H#aX45p#?yq^B|` zZKzF$r3`;NhD3Caf2||vk-RXXb7M3W~7J`{|4#t^;MGNY{gc*Z_Yul z)-{K)rdg$BWPDzg-`onGZ~x3qPQ#1{aQc~=D7ZD%r#Gcb!Ptx!BEcAkTKFyH@VQgr zu*JI-bU&Djn6SlSLUs$`^a<-e>G>PYcB{VJ)v@Ub`*n;#X9MhG7M8%SYL>@!KZKP$ z?27WDo+`XQbILFPZH*5LTP!QEBpZamIg=~ZMF7|{QdnX5)eDuSJYK8k#l%LcYwx`7 zr$O55pnR5In<1SVKR+;wv6==QQ}jrZ6@{kK2@90~po~(PN$J85UV`Fw_v1Wfr>5+k z0Ab%Q8@|~4mUvI10II@;z4U$|7f)ly$c=E+M+hUTUxV&@Rx zM$43PA0v62>Fc8-$T3&N0DIt|qwAxiqi+zRqfhVwR+){sxOmduo+*|YZG)d*H%6;; z{|Jo4*g*eVIwc4M(&F=RZq)U*qe*+)%*+xTy46B))k4REZDR4-K*;Fvpm;>1u}*r# z(5#dH_yBdgZq-K(sIc6*T7v6&XuGmrn>ZCcC5@QA9)|EoA+P+hGi3NO`q+s;$PapR z*#=C*`EOkG9)EON>S$ZT-%*tAOmT4lb+VrTBp)q`CtTc?a=2WbdXnr%vC!i*l+kwVZ!c;hPq<>}Cwkv*Cdw(_ zyVljLi29xa(T|UsEU=L9_lJh3*L&el1X6DCpliG2?#`>&r(Gcf+DAcG;=r1XGQ0-oq;hi;D;MEYyK(-PJY2 zm>!oQgVT|UgkG0_=lkn7nMk)e9a#=9ZGkOVh*d*~I66ZlXmSD2r%i_1&zw4b6 z5)$SU)g(fJh_3QLYzrIZ&m`Pr-&S{jEi4N0C9K5HH}M$6%`jl=YR}dkj9zM2oidJV zcs;IGgin#r9$ifGAuGq}>&3@Ak!ySf5MJcSwwFh>a-0EGz9&xF@-B+~9okk>O- zp90ZHboD-#M~JZlG?X>9ZnF2;<(z?R*JnY6I0Y{M7{h>=?>*#Csw5paCungoJ+VO1 zpl4=0ZQX0tl1unt)?EJXE|*=>HL33eRTQan14tSzQ{LT)e3iJNZ^p5Gm@Cc@djg-c z2Oz5I_Y6P;Mj%W6p`l|fEZ0Q#oEJse0v22}JRNa(@B-`C#o1$u`ILe32UQqhX@I*6 z-7ooTsm*ZhwH-a3PVqWIDZ`%ND4qRyu)SNw29Z>fx1cIJ0tUOE)g}%kXLi*tg^{z;qMhJDDn#}*p%2-H_c!n*M>GSGX zABvg%C{rvm$8ORtchu9y7a6vwV`y5AIF-Nw+o=U7j|;kdDiz@rG}Ol;se35`5x>MF@AFS?x# zj<~ubKhGTUDGulric|dRL=Wo?7~??XAEnmt_V({=%D}k2*E(v*BHPYjh&$sJPg6xa z7`}O&BNEoW3?Mus%B3n#N1K}t6Tg1_*gafv?gm6Ey1;;dFA-y&uP#J6FN{-1OW^P?IAKz`oIi%)mg&JmQ&a8}6Bn?m zj9**p-S9LckY*UF3vPmbQJl&vti+wuuvM!{9L`+CRcm_0UL=%y|I&>+JDe_R-Wc0# ze;1CQc9-KG#cl#d12SXi3O3wN-Si`b-`o5Yl+pbNX}WMOu6hz=wu^P%5R0muQ9C5C zCbZBmm?e3W8~^xN-69+6Yz3r}t4}(^CUxHAjwZsqFgiTY6odq1UfVV3`DtmxN8P`t zC*7fyT;F+kAm+hiZX+jxbVl{%_K;J)sNap?JNKcrTruyTdlCVqiexR>wzS4f$L|bh zrS0z4kxzslczv|m2FP{GhW4&f7x2$*EZEe2tkTOT(^_+3Vq~vt@6Qu z;qgLz{Hu@jZthEe99}e>QqK4^dpRTQ7>qTMCjS{_&#TL?)i+$Q;iN3~I-Az&(X8g8 z;8W+f8lS$;$rxYVftI`|PD1aKE34!5LCV_gNwJm$QUpM%Z#g(PEStW8g_<_hjT$O# zyr!Ynr^i*5FeRo0AQ77!Cbw$FbNUH$?8ZnnJk;X zGg2G>cO*;pG){D?^0`;Ey`6^Z%y^CtfopKQea>@XMiC+;-nSqjxkyRL^=VG^uz?Xh zOW|;x7x|>g8j!&|zFIM|YQN`v_P}DBaWg(HG_sqDw=eI;MUM_g(4!~6zF8dr8<}~; zRth;?balDNNpSE(g2g9nAh0+T%tmrKvywK}Rnn4QPR&ei-t9+$W%HXYwfgmIuiNo5 zG5pt_;=T1pF!jJA+G&lCa)U(P)48$i&a&vbcl}z_%@YlGS*m}4x>@yzs19;~KOAG4 zq!UwnQFO~SWOgV?{@G%PemSOlcuU!gF%~g=5Ci?foQZL{xgz?SH7`(fe}8`_G&GcX z;ZJ8?ysD!RH#rj;{-+oPT@>4DNiISKjXC=7wF{(126GO^Z7UTAw-g;lh7d1YdfPY&a=u8fbVQ8d!OL`L@w*K_}=tTKKN= z5@}_sA?b#tj`gbL*>l*d@i!T=N6*Fd(RDhJZhr2rm6h&~(wxgcseExTi1G(T&Fxv+ zV@XNIV$N9l-at{#D1NHu#OTVX_^qbqnq$aZ+QOrB&t&0v<+3&uXBQ-A>qn3F>jF-V z8c|I|M)%Ep3m(MqSdy)T?L_4a>eB6}HIh!+qr(`H(`s7!@Z=VKc#C<R_Ad&aFin-)yp5%6EwM!Q#+zL7+_*nN8}ed^rwOa6rl!R<1eO1AI#~==k)w=a2$C|a7f^goz>39Hx; zZh7rBQ=!$u$Y^h?q=QbLQU-S3( zHxe@*_6x~E+Lu>PdKrYtN?r|SyvgA8bVD+W5GG+!-4QK(t@Ooa@RcK%GE194Gt3yB z-l3@O)KWjr^c3d#fjI(G0FvVXQrE)T?coz2g&Ph^(3Ol*Xfsr(V&WrVky-+{Jh+JqaWF1FWEk59oh2nswxB26qSQz>$Bvu zaQ>aba#J^LhbdM=t*xL(xobME9Ei7Pj-QObFW*dno1i>huQJVp#qFgKqpsW?_H5^U zE5<2H49Y3AwiAD5{!Wk25|>&O73OD`-kqPbxVY=aC&lTgroSXBD$2Lr%>MZ4#kPQ# z{XA?ERvMQ|Hm#{_@I523{os9#-u>kXjOf)Pnf!dNx%L;zQ~57=8a&T_Fj+12@;oOk@*vIN@G^Jxli)wl8jWUQim;p#-F?lTLp7vkKE7{}~az;Kz)@~VYJ5pjg2H`-mIU|D6|5E2> zp`iO9*m;p0`LGaG!GCO&m0@AkXYG<#PhByVf3 zun%bJP&?lH`S0lp>FCHyON#|PUA3MsX8Hg8iPv16@`!3|d!Cahz8{@${FWG39lKF; zN*Ef;l8|FLju!vxLN~8CzwWx2pMrS$&%}a8^WmSe`TBqt}Q;&el_rc+0@gY>@Y>&7^NJqkRSVm?E^KP{y?!v{BH7$FhSrrU$j zt}S~n*ptP=x`xJUuU1DmS517-e3F^nuYbS4jXl$ePDm&u;q`FoC@nptb98jHk1d(} z{aaYq%8E$+117&9?~UILU0HcxaCq9!NFsRTATR%;1AFkU1QZk$6Un^J|NVsEF-ftC zlL|X)27WrCV_*;|Dk>6fZf<5|>O_9blu#ECfmX5%^yxL_l53~}c18Q~qvXDU@qh&9 zOk$taNbde+A#x*oFdhhQ19+MC;5D1mf0btlc-KX7o%la#j4~HZy z?EPL6seq_Reyct_F}JY^h*R}gx7MclV_GW)hR1UIb-H{E0rEuQ3e?opVs?qQzQuv{ zx9#Mu8jae?2(u*HT@hVhi%g5iMbSb?AS<#XBO^u-vB=hgt*vhfow^@enwpkX<>l|b z&HNPp&kE#y|1FeT0gV5p;%n1Ja%LuPDsF_5rY2X8!M?Z1=Pz<~bv_H1i+?l}hg#fk zDzXz2ls#=~5AWb|r3AD8xRi8b+Y+vjJJ@tI)L*MKrnZxypFARx%DaCG>(N---F1#R zas&6?gO<2>|7$KSh+LxT_YQOF?D(<6R*8h-VzU`AuX+M(yGhX~;*6b*4F!I;AhiFp zgFj4_y8UnS3Vq_`^+Hb@agvpmPR{|_Oe?e+aDL5D*?b#&bk|!$Hc; zB57=Yo+Ow{*j+qV{%Y?f4K+8YQqy?=Ws*2`FE_X5)V8)K48WN;cX#Jv2rVixz>G;t zTT~{COPMsu5c`i^qJLk4Ve@!dV@V;Qq4=Hawo7DRQscRuPrioGDGzMh*M&$_x^id2 zGX$Yc4#-X@$I?=6!hLOSilay*!N=$9TwLUe80KtkYfAzw=|N5|E}g5(OOH7^PSC0O zKi?5@pqedjLk(QWH(T3AIVC0L&V^y3_ROC+Ticj?gYAaf)$7c=I)4^sh7sW5Qw}ya zNHj7u?%rheCvu1rD900jnc3NyaNAc2=hS%P|<0gFKhcT>6ktQQ&V6cldJjhbH|leFr7QN+sj@`z1NDHEBMl{JWw zkx@mWjPXC83+HK{XD$QbPS(S|^sYinV{RzccxRLr((}6O*d2u<)GZ|Nr4s%$(lOtvF|@ zLes*e+YPm>A-fn0-u6_Os)qH8^Ya<+P*565OG_QrY}u)Se_PVm*B{Qv$Ur$ekkahd z$KgM6^foo!h*?QY@1p+IenM`Y7!=f3fX8HP7^@Bcf7eT0!O;~zX=u8xp+R=z%q7dy zcnj*HfcQ0sx~syVwI%j18ywFcKQ776vOKWE>VA!l(cOPp6cZC$90pi<6d!)wV##Sg z=#xHv+3wKz`hR;d>bCCHdvil@nC|cHwBG0I{?}v@NX2PzX+zuQU$S_gWOv@*)Tflm4N zrED`qufuq{T6icO<>#$#x4ZOBEqDCD1o2mCBhNQMiB-lUsdP8Ti#OD-+ZVR5jQD4dWB|#wV5*%L{Lud&wg|?s(!HiT@wKx|I*0vpPA=p>Me{# z^oQTe3vZb5%h|cP9}y74|8tZ-Oksf2Rm245YX1R0#7!}QMRz}(k3Y=%Ui$&<0w$w% z1A$at6Ctc8RiL+304TDGf=Vhpl+UkW^EH=S@(d8CI2_2@<%o6X~be7Z?OMldc=G5IyR7_3?sjL7y7A&N1BX@oQ$kD zQ#5zEX@uufMQL66oy{Ve1I3e*8J^*G@Avk0FG*?XVO+AffloD`|NC9B+6vyT8Ya?Y z4q|{8jja#=n9n5hy*YH8I?Ci3>rG)Wn&|ry6eOgpzt`70;w7Jfy{2}PO6KQ1c?pc0 zVOm|v^6TIuvu*;#@*9Gu7dC8GTK8oNCtosY{_C;e9OI^Sc_aW z-ijHtazf=Yf$hV@K>y$Ki8^naH*P_nGF(?xRpo>EiSyCG?JYC&jKEw|z@M3w3D6#5 z?Ck5?b+f7a1c3-m&(CwHzeh#HGrMBh;%-LlD&QogzYiGNcCxYZ`tNSdcK-Ee56Q#R zGYJ+3=0s6JNglJVLBOF@?w5X5utep{(Q*ApGO|?JG}h1B?@^OcFLc~pT#kTZ)DnNb zf~H%`|6^igT>Q3xtgKZiZ|@g+eFW?uKW#Xig8E-v8%G_b^~zy`aE3_~#}RJ-)mG Oe-a`x!WDwLzW)mfk=W({ diff --git a/ruoyi-admin/src/main/resources/static/static/image/ruoyivueplus.png b/ruoyi-admin/src/main/resources/static/static/image/ruoyivueplus.png deleted file mode 100644 index d0eba10d14c6b8b30f8b7229685da338d2a168b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54652 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 diff --git a/ruoyi-admin/src/main/resources/static/static/js/docsify-copy-code.min.js b/ruoyi-admin/src/main/resources/static/static/js/docsify-copy-code.min.js deleted file mode 100644 index 02a83a31..00000000 --- a/ruoyi-admin/src/main/resources/static/static/js/docsify-copy-code.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * docsify-copy-code - * v3.0.0 - * https://github.com/jperasmus/docsify-copy-code - * (c) 2017-2023 JP Erasmus - * MIT license - */ -!function(){"use strict";function e(o){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(o)}!function(e,o){void 0===o&&(o={});var t=o.insertAt;if(e&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],c=document.createElement("style");c.type="text/css","top"===t&&n.firstChild?n.insertBefore(c,n.firstChild):n.appendChild(c),c.styleSheet?c.styleSheet.cssText=e:c.appendChild(document.createTextNode(e))}}(".docsify-copy-code-button,.docsify-copy-code-button>span{cursor:pointer;transition:all .25s ease}.docsify-copy-code-button{background:grey;background:var(--theme-color,grey);border:0;border-radius:0;color:#fff;font-size:1em;opacity:0;outline:0;overflow:visible;padding:.65em .8em;position:absolute;right:0;top:0;z-index:1}.docsify-copy-code-button>span{background:inherit;border-radius:3px;pointer-events:none}.docsify-copy-code-button>.error,.docsify-copy-code-button>.success{font-size:.825em;opacity:0;padding:.5em .65em;position:absolute;right:0;top:50%;transform:translateY(-50%);z-index:-100}.docsify-copy-code-button.error>.error,.docsify-copy-code-button.success>.success{opacity:1;right:100%;transform:translate(-25%,-50%)}.docsify-copy-code-button:focus,pre:hover .docsify-copy-code-button{opacity:1}.docsify-copy-code-button>[aria-live]{height:1px;left:-10000px;overflow:hidden;position:absolute;top:auto;width:1px}"),document.querySelector('link[href*="docsify-copy-code"]')&&console.warn("[Deprecation] Link to external docsify-copy-code stylesheet is no longer necessary."),window.DocsifyCopyCodePlugin={init:function(){return function(e,o){e.ready((function(){console.warn("[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary.")}))}}},window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(o,t){var n={buttonText:"Copy to clipboard",errorText:"Error",successText:"Copied"};o.doneEach((function(){var o=Array.from(document.querySelectorAll("pre[data-lang]"));t.config.copyCode&&Object.keys(n).forEach((function(o){var c=t.config.copyCode[o];"string"==typeof c?n[o]=c:"object"===e(c)&&Object.keys(c).some((function(e){var t=location.href.indexOf(e)>-1;return n[o]=t?c[e]:n[o],t}))}));var c=['"].join("");o.forEach((function(e){e.insertAdjacentHTML("beforeend",c)}))})),o.mounted((function(){var e=document.querySelector(".content");e&&e.addEventListener("click",(function(e){if(e.target.classList.contains("docsify-copy-code-button")){var o="BUTTON"===e.target.tagName?e.target:e.target.parentNode,t=document.createRange(),c=o.parentNode.querySelector("code"),i=o.querySelector("[aria-live]"),r=window.getSelection();t.selectNode(c),r&&(r.removeAllRanges(),r.addRange(t));try{document.execCommand("copy")&&(o.classList.add("success"),i.innerText=n.successText,setTimeout((function(){o.classList.remove("success"),i.innerText=""}),1e3))}catch(e){console.error("docsify-copy-code: ".concat(e)),o.classList.add("error"),i.innerText=n.errorText,setTimeout((function(){o.classList.remove("error"),i.innerText=""}),1e3)}(r=window.getSelection())&&("function"==typeof r.removeRange?r.removeRange(t):"function"==typeof r.removeAllRanges&&r.removeAllRanges())}}))}))}].concat(window.$docsify.plugins||[])}(); -//# sourceMappingURL=docsify-copy-code.min.js.map \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/static/js/docsify-footer.min.js b/ruoyi-admin/src/main/resources/static/static/js/docsify-footer.min.js deleted file mode 100644 index b4262116..00000000 --- a/ruoyi-admin/src/main/resources/static/static/js/docsify-footer.min.js +++ /dev/null @@ -1,4 +0,0 @@ -parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcelRequire,u="function"==typeof require&&require;function f(t,n){if(!r[t]){if(!e[t]){var i="function"==typeof parcelRequire&&parcelRequire;if(!n&&i)return i(t,!0);if(o)return o(t,!0);if(u&&"string"==typeof t)return u(t);var c=new Error("Cannot find module '"+t+"'");throw c.code="MODULE_NOT_FOUND",c}p.resolve=function(r){return e[t][1][r]||r},p.cache={};var l=r[t]=new f.Module(t);e[t][0].call(l.exports,p,l,l.exports,this)}return r[t].exports;function p(e){return f(p.resolve(e))}}f.isParcelRequire=!0,f.Module=function(e){this.id=e,this.bundle=f,this.exports={}},f.modules=e,f.cache=r,f.parent=o,f.register=function(r,t){e[r]=[function(e,r){r.exports=t},{}]};for(var c=0;c ul > li"),c("p",e)),this.hyperlink=m(t))}var b=function(){return'

'},k=function(t,e){a=e,r=t.route.path,o={},["previousText","nextText"].forEach(function(n){var i=a[n];"string"==typeof i?o[n]=i:Object.keys(i).some(function(t){var e=r&&-1\n \n
\n \n \n \n '+i+'\n
\n
'+t.prev.name+"
\n ",t.prev&&e.crossChapterText&&'
'+t.prev.chapterName+"
",t.prev&&"
\n \n ",t.next&&'\n \n "].filter(Boolean).join("")};window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(t,e){var n=d({},(e.config,{previousText:"PREVIOUS",nextText:"NEXT",crossChapter:!1,crossChapterText:!1}),e.config.pagination||{});function i(){var t=c("."+h);t&&(t.innerHTML=k(function(t,e){e=e.crossChapter;try{var n=t.router.toURL(t.route.path),i=g(c.all(".sidebar-nav li a")).filter(function(t){return!s(t,".section-link")}),a=i.find(x(n)),r=g((p(a,"ul")||{}).children).filter(function(t){return"LI"===t.tagName.toUpperCase()}),o=e?i.findIndex(x(n)):r.findIndex(function(t){t=m(t);return t&&x(n,t)}),l=e?i:r;return{route:t.route,prev:new y(l[o-1]).toJSON(),next:new y(l[o+1]).toJSON()}}catch(t){return{route:{}}}}(e,n),n))}t.afterEach(function(t){return t+b()}),t.doneEach(i)}].concat(window.$docsify.plugins||[])}); \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/static/js/docsify-scroll-to-top.min.js b/ruoyi-admin/src/main/resources/static/static/js/docsify-scroll-to-top.min.js deleted file mode 100644 index c5ed3ca2..00000000 --- a/ruoyi-admin/src/main/resources/static/static/js/docsify-scroll-to-top.min.js +++ /dev/null @@ -1 +0,0 @@ -var CONFIG={auto:true,text:"Top",right:15,bottom:15,offset:500};var install=function(hook,vm){var opts=vm.config.scrollToTop||CONFIG;CONFIG.auto=opts.auto&&typeof opts.auto==="boolean"?opts.auto:CONFIG.auto;CONFIG.text=opts.text&&typeof opts.text==="string"?opts.text:CONFIG.text;CONFIG.right=opts.right&&typeof opts.right==="number"?opts.right:CONFIG.right;CONFIG.bottom=opts.bottom&&typeof opts.bottom==="number"?opts.bottom:CONFIG.bottom;CONFIG.offset=opts.offset&&typeof opts.offset==="number"?opts.offset:CONFIG.offset;var onScroll=function(e){if(!CONFIG.auto){return}var offset=window.document.documentElement.scrollTop;var $scrollBtn=Docsify.dom.find("span.scroll-to-top");$scrollBtn.style.display=offset>=CONFIG.offset?"block":"none"};hook.mounted(function(){var scrollBtn=document.createElement("span");scrollBtn.className="scroll-to-top";scrollBtn.style.display=CONFIG.auto?"none":"block";scrollBtn.style.overflow="hidden";scrollBtn.style.position="fixed";scrollBtn.style.right=CONFIG.right+"px";scrollBtn.style.bottom=CONFIG.bottom+"px";scrollBtn.style.width="50px";scrollBtn.style.height="50px";scrollBtn.style.background="white";scrollBtn.style.color="#666";scrollBtn.style.border="1px solid #ddd";scrollBtn.style.borderRadius="4px";scrollBtn.style.lineHeight="42px";scrollBtn.style.fontSize="16px";scrollBtn.style.textAlign="center";scrollBtn.style.boxShadow="0px 0px 6px #eee";scrollBtn.style.cursor="pointer";var textNode=document.createTextNode(CONFIG.text);scrollBtn.appendChild(textNode);document.body.appendChild(scrollBtn);window.addEventListener("scroll",onScroll);scrollBtn.onclick=function(e){e.stopPropagation();var step=window.scrollY/15;var scroll=function(){window.scrollTo(0,window.scrollY-step);if(window.scrollY>0){setTimeout(scroll,15)}};scroll()}})};$docsify.plugins=[].concat(install,$docsify.plugins); \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/static/js/docsify.min.js b/ruoyi-admin/src/main/resources/static/static/js/docsify.min.js deleted file mode 100644 index 18e85aa9..00000000 --- a/ruoyi-admin/src/main/resources/static/static/js/docsify.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function c(i){var o=Object.create(null);return function(e){var n=f(e)?e:JSON.stringify(e);return o[n]||(o[n]=i(e))}}var a=c(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),u=Object.prototype.hasOwnProperty,m=Object.assign||function(e){for(var n=arguments,i=1;i=e||n.classList.contains("hidden")?S(h,"add","sticky"):S(h,"remove","sticky"))}function ee(e,n,o,i){var t=[];null!=(n=l(n))&&(t=k(n,"a"));var a,r=decodeURI(e.toURL(e.getCurrentPath()));return t.sort(function(e,n){return n.href.length-e.href.length}).forEach(function(e){var n=decodeURI(e.getAttribute("href")),i=o?e.parentNode:e;e.title=e.title||e.innerText,0!==r.indexOf(n)||a?S(i,"remove","active"):(a=e,S(i,"add","active"))}),i&&(v.title=a?a.title||a.innerText+" - "+J:J),a}function ne(e,n){for(var i=0;ithis.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,n,i,o){return(e/=o/2)<1?i/2*e*e+n:-i/2*(--e*(e-2)-1)+n}}]),re);function re(){var e=0c){n=n||p;break}n=p}!n||(r=fe[ve(e,n.getAttribute("data-id"))])&&r!==a&&(a&&a.classList.remove("active"),r.classList.add("active"),a=r,!pe&&h.classList.contains("sticky")&&(e=i.clientHeight,r=a.offsetTop+a.clientHeight+40,a=a.offsetTop>=t.scrollTop&&r<=t.scrollTop+e,i.scrollTop=a?t.scrollTop:+r"']/),xe=/[&<>"']/g,Se=/[<>"']|&(?!#?\w+;)/,Ae=/[<>"']|&(?!#?\w+;)/g,$e={"&":"&","<":"<",">":">",'"':""","'":"'"};var ze=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function Fe(e){return e.replace(ze,function(e,n){return"colon"===(n=n.toLowerCase())?":":"#"===n.charAt(0)?"x"===n.charAt(1)?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1)):""})}var Ee=/(^|[^\[])\^/g;var Re=/[^\w:]/g,Te=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;var Ce={},je=/^[^:]+:\/*[^/]*$/,Le=/^([^:]+:)[\s\S]*$/,Oe=/^([^:]+:\/*[^/]*)[\s\S]*$/;function qe(e,n){Ce[" "+e]||(je.test(e)?Ce[" "+e]=e+"/":Ce[" "+e]=Pe(e,"/",!0));var i=-1===(e=Ce[" "+e]).indexOf(":");return"//"===n.substring(0,2)?i?n:e.replace(Le,"$1")+n:"/"===n.charAt(0)?i?n:e.replace(Oe,"$1")+n:e+n}function Pe(e,n,i){var o=e.length;if(0===o)return"";for(var t=0;tn)i.splice(n);else for(;i.length>=1,e+=e;return i+e},We=we.defaults,Xe=Be,Qe=Ze,Je=Me,Ke=Ve;function en(e,n,i){var o=n.href,t=n.title?Je(n.title):null,n=e[1].replace(/\\([\[\]])/g,"$1");return"!"!==e[0].charAt(0)?{type:"link",raw:i,href:o,title:t,text:n}:{type:"image",raw:i,href:o,title:t,text:Je(n)}}var nn=function(){function e(e){this.options=e||We}return e.prototype.space=function(e){e=this.rules.block.newline.exec(e);if(e)return 1=i.length?e.slice(i.length):e}).join("\n")}(i,n[3]||"");return{type:"code",raw:i,lang:n[2]&&n[2].trim(),text:e}}},e.prototype.heading=function(e){var n=this.rules.block.heading.exec(e);if(n){var i=n[2].trim();return/#$/.test(i)&&(e=Xe(i,"#"),!this.options.pedantic&&e&&!/ $/.test(e)||(i=e.trim())),{type:"heading",raw:n[0],depth:n[1].length,text:i}}},e.prototype.nptable=function(e){e=this.rules.block.nptable.exec(e);if(e){var n={type:"table",header:Qe(e[1].replace(/^ *| *\| *$/g,"")),align:e[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:e[3]?e[3].replace(/\n$/,"").split("\n"):[],raw:e[0]};if(n.header.length===n.align.length){for(var i=n.align.length,o=0;o ?/gm,"");return{type:"blockquote",raw:n[0],text:e}}},e.prototype.list=function(e){e=this.rules.block.list.exec(e);if(e){for(var n,i,o,t,a,r=e[0],c=e[2],u=1s[1].length:o[1].length>s[0].length||3/i.test(e[0])&&(n=!1),!i&&/^<(pre|code|kbd|script)(\s|>)/i.test(e[0])?i=!0:i&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(e[0])&&(i=!1),{type:this.options.sanitize?"text":"html",raw:e[0],inLink:n,inRawBlock:i,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]}},e.prototype.link=function(e){var n=this.rules.inline.link.exec(e);if(n){e=n[2].trim();if(!this.options.pedantic&&/^$/.test(e))return;var i=Xe(e.slice(0,-1),"\\");if((e.length-i.length)%2==0)return}else{var o=Ke(n[2],"()");-1$/.test(e)?i.slice(1):i.slice(1,-1):i)&&i.replace(this.rules.inline._escapes,"$1"),title:o&&o.replace(this.rules.inline._escapes,"$1")},n[0])}},e.prototype.reflink=function(e,n){if((i=this.rules.inline.reflink.exec(e))||(i=this.rules.inline.nolink.exec(e))){var e=(i[2]||i[1]).replace(/\s+/g," ");if((e=n[e.toLowerCase()])&&e.href)return en(i,e,i[0]);var i=i[0].charAt(0);return{type:"text",raw:i,text:i}}},e.prototype.strong=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.strong.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="**"===o[0]?this.rules.inline.strong.endAst:this.rules.inline.strong.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.strong.middle.exec(n.slice(0,o.index+3)))return{type:"strong",raw:e.slice(0,t[0].length),text:e.slice(2,t[0].length-2)}}},e.prototype.em=function(e,n,i){void 0===i&&(i="");var o=this.rules.inline.em.start.exec(e);if(o&&(!o[1]||o[1]&&(""===i||this.rules.inline.punctuation.exec(i)))){n=n.slice(-1*e.length);var t,a="*"===o[0]?this.rules.inline.em.endAst:this.rules.inline.em.endUnd;for(a.lastIndex=0;null!=(o=a.exec(n));)if(t=this.rules.inline.em.middle.exec(n.slice(0,o.index+2)))return{type:"em",raw:e.slice(0,t[0].length),text:e.slice(1,t[0].length-1)}}},e.prototype.codespan=function(e){var n=this.rules.inline.code.exec(e);if(n){var i=n[2].replace(/\n/g," "),o=/[^ ]/.test(i),e=/^ /.test(i)&&/ $/.test(i);return o&&e&&(i=i.substring(1,i.length-1)),i=Je(i,!0),{type:"codespan",raw:n[0],text:i}}},e.prototype.br=function(e){e=this.rules.inline.br.exec(e);if(e)return{type:"br",raw:e[0]}},e.prototype.del=function(e){e=this.rules.inline.del.exec(e);if(e)return{type:"del",raw:e[0],text:e[2]}},e.prototype.autolink=function(e,n){e=this.rules.inline.autolink.exec(e);if(e){var i,n="@"===e[2]?"mailto:"+(i=Je(this.options.mangle?n(e[1]):e[1])):i=Je(e[1]);return{type:"link",raw:e[0],text:i,href:n,tokens:[{type:"text",raw:i,text:i}]}}},e.prototype.url=function(e,n){var i,o,t,a;if(i=this.rules.inline.url.exec(e)){if("@"===i[2])t="mailto:"+(o=Je(this.options.mangle?n(i[0]):i[0]));else{for(;a=i[0],i[0]=this.rules.inline._backpedal.exec(i[0])[0],a!==i[0];);o=Je(i[0]),t="www."===i[1]?"http://"+o:o}return{type:"link",raw:i[0],text:o,href:t,tokens:[{type:"text",raw:o,text:o}]}}},e.prototype.inlineText=function(e,n,i){e=this.rules.inline.text.exec(e);if(e){i=n?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):Je(e[0]):e[0]:Je(this.options.smartypants?i(e[0]):e[0]);return{type:"text",raw:e[0],text:i}}},e}(),Ze=De,Ve=Ne,De=Ue,Ne={newline:/^(?: *(?:\n|$))+/,code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:Ze,table:Ze,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html| +\n)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};Ne.def=Ve(Ne.def).replace("label",Ne._label).replace("title",Ne._title).getRegex(),Ne.bullet=/(?:[*+-]|\d{1,9}[.)])/,Ne.item=/^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/,Ne.item=Ve(Ne.item,"gm").replace(/bull/g,Ne.bullet).getRegex(),Ne.listItemStart=Ve(/^( *)(bull)/).replace("bull",Ne.bullet).getRegex(),Ne.list=Ve(Ne.list).replace(/bull/g,Ne.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+Ne.def.source+")").getRegex(),Ne._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Ne._comment=/|$)/,Ne.html=Ve(Ne.html,"i").replace("comment",Ne._comment).replace("tag",Ne._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Ne.paragraph=Ve(Ne._paragraph).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.blockquote=Ve(Ne.blockquote).replace("paragraph",Ne.paragraph).getRegex(),Ne.normal=De({},Ne),Ne.gfm=De({},Ne.normal,{nptable:"^ *([^|\\n ].*\\|.*)\\n {0,3}([-:]+ *\\|[-| :]*)(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)",table:"^ *\\|(.+)\\n {0,3}\\|?( *[-:]+[-| :]*)(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),Ne.gfm.nptable=Ve(Ne.gfm.nptable).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.gfm.table=Ve(Ne.gfm.table).replace("hr",Ne.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",Ne._tag).getRegex(),Ne.pedantic=De({},Ne.normal,{html:Ve("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",Ne._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:Ze,paragraph:Ve(Ne.normal._paragraph).replace("hr",Ne.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",Ne.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});Ze={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:Ze,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",strong:{start:/^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,middle:/^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,endAst:/[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/},em:{start:/^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,middle:/^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,endAst:/[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:Ze,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~"};Ze.punctuation=Ve(Ze.punctuation).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze._blockSkip="\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>",Ze._overlapSkip="__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*",Ze._comment=Ve(Ne._comment).replace("(?:--\x3e|$)","--\x3e").getRegex(),Ze.em.start=Ve(Ze.em.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.middle=Ve(Ze.em.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.em.endAst=Ve(Ze.em.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.em.endUnd=Ve(Ze.em.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.start=Ve(Ze.strong.start).replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.middle=Ve(Ze.strong.middle).replace(/punctuation/g,Ze._punctuation).replace(/overlapSkip/g,Ze._overlapSkip).getRegex(),Ze.strong.endAst=Ve(Ze.strong.endAst,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.strong.endUnd=Ve(Ze.strong.endUnd,"g").replace(/punctuation/g,Ze._punctuation).getRegex(),Ze.blockSkip=Ve(Ze._blockSkip,"g").getRegex(),Ze.overlapSkip=Ve(Ze._overlapSkip,"g").getRegex(),Ze._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,Ze._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,Ze._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,Ze.autolink=Ve(Ze.autolink).replace("scheme",Ze._scheme).replace("email",Ze._email).getRegex(),Ze._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,Ze.tag=Ve(Ze.tag).replace("comment",Ze._comment).replace("attribute",Ze._attribute).getRegex(),Ze._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Ze._href=/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/,Ze._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,Ze.link=Ve(Ze.link).replace("label",Ze._label).replace("href",Ze._href).replace("title",Ze._title).getRegex(),Ze.reflink=Ve(Ze.reflink).replace("label",Ze._label).getRegex(),Ze.reflinkSearch=Ve(Ze.reflinkSearch,"g").replace("reflink",Ze.reflink).replace("nolink",Ze.nolink).getRegex(),Ze.normal=De({},Ze),Ze.pedantic=De({},Ze.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:Ve(/^!?\[(label)\]\((.*?)\)/).replace("label",Ze._label).getRegex(),reflink:Ve(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Ze._label).getRegex()}),Ze.gfm=De({},Ze.normal,{escape:Ve(Ze.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\'+(i?e:gn(e,!0))+"\n":"
"+(i?e:gn(e,!0))+"
\n"},e.prototype.blockquote=function(e){return"
\n"+e+"
\n"},e.prototype.html=function(e){return e},e.prototype.heading=function(e,n,i,o){return this.options.headerIds?"'+e+"\n":""+e+"\n"},e.prototype.hr=function(){return this.options.xhtml?"
\n":"
\n"},e.prototype.list=function(e,n,i){var o=n?"ol":"ul";return"<"+o+(n&&1!==i?' start="'+i+'"':"")+">\n"+e+"\n"},e.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},e.prototype.checkbox=function(e){return" "},e.prototype.paragraph=function(e){return"

    "+e+"

    \n"},e.prototype.table=function(e,n){return"\n\n"+e+"\n"+(n=n&&""+n+"")+"
    \n"},e.prototype.tablerow=function(e){return"\n"+e+"\n"},e.prototype.tablecell=function(e,n){var i=n.header?"th":"td";return(n.align?"<"+i+' align="'+n.align+'">':"<"+i+">")+e+"\n"},e.prototype.strong=function(e){return""+e+""},e.prototype.em=function(e){return""+e+""},e.prototype.codespan=function(e){return""+e+""},e.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},e.prototype.del=function(e){return""+e+""},e.prototype.link=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;e='"},e.prototype.image=function(e,n,i){if(null===(e=dn(this.options.sanitize,this.options.baseUrl,e)))return i;i=''+i+'":">"},e.prototype.text=function(e){return e},e}(),ln=function(){function e(){}return e.prototype.strong=function(e){return e},e.prototype.em=function(e){return e},e.prototype.codespan=function(e){return e},e.prototype.del=function(e){return e},e.prototype.html=function(e){return e},e.prototype.text=function(e){return e},e.prototype.link=function(e,n,i){return""+i},e.prototype.image=function(e,n,i){return""+i},e.prototype.br=function(){return""},e}(),vn=function(){function e(){this.seen={}}return e.prototype.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},e.prototype.getNextSafeSlug=function(e,n){var i=e,o=0;if(this.seen.hasOwnProperty(i))for(o=this.seen[e];i=e+"-"+ ++o,this.seen.hasOwnProperty(i););return n||(this.seen[e]=o,this.seen[i]=0),i},e.prototype.slug=function(e,n){void 0===n&&(n={});e=this.serialize(e);return this.getNextSafeSlug(e,n.dryrun)},e}(),hn=we.defaults,_n=Ie,mn=function(){function i(e){this.options=e||hn,this.options.renderer=this.options.renderer||new sn,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new ln,this.slugger=new vn}return i.parse=function(e,n){return new i(n).parse(e)},i.parseInline=function(e,n){return new i(n).parseInline(e)},i.prototype.parse=function(e,n){void 0===n&&(n=!0);for(var i,o,t,a,r,c,u,f,p,d,g,s,l,v,h,_="",m=e.length,b=0;bAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}}xn.options=xn.setOptions=function(e){return bn(xn.defaults,e),yn(xn.defaults),xn},xn.getDefaults=Me,xn.defaults=we,xn.use=function(a){var n,e=bn({},a);if(a.renderer){var i,r=xn.defaults.renderer||new sn;for(i in a.renderer)!function(o){var t=r[o];r[o]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.renderer[o].apply(r,e);return i=!1===i?t.apply(r,e):i}}(i);e.renderer=r}if(a.tokenizer){var t,c=xn.defaults.tokenizer||new nn;for(t in a.tokenizer)!function(){var o=c[t];c[t]=function(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];var i=a.tokenizer[t].apply(c,e);return i=!1===i?o.apply(c,e):i}}();e.tokenizer=c}a.walkTokens&&(n=xn.defaults.walkTokens,e.walkTokens=function(e){a.walkTokens(e),n&&n(e)}),xn.setOptions(e)},xn.walkTokens=function(e,n){for(var i=0,o=e;iAn error occurred:

    "+wn(e.message+"",!0)+"
    ";throw e}},xn.Parser=mn,xn.parser=mn.parse,xn.Renderer=sn,xn.TextRenderer=ln,xn.Lexer=fn,xn.lexer=fn.lex,xn.Tokenizer=nn,xn.Slugger=vn;var Sn=xn.parse=xn;function An(e,i){if(void 0===i&&(i='
      {inner}
    '),!e||!e.length)return"";var o="";return e.forEach(function(e){var n=e.title.replace(/(<([^>]+)>)/g,"");o+='
  • '+e.title+"
  • ",e.children&&(o+=An(e.children,i))}),i.replace("{inner}",o)}function $n(e,n){return'

    '+n.slice(5).trim()+"

    "}function zn(e,o){var t=[],a={};return e.forEach(function(e){var n=e.level||1,i=n-1;o?@[\]^`{|}~]/g;function Rn(e){return e.toLowerCase()}function Tn(e){if("string"!=typeof e)return"";var n=e.trim().replace(/[A-Z]+/g,Rn).replace(/<[^>]+>/g,"").replace(En,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),e=Fn[n],e=u.call(Fn,n)?e+1:0;return n=(Fn[n]=e)?n+"-"+e:n}Tn.clear=function(){Fn={}};var Cn={baseURL:"https://github.githubassets.com/images/icons/emoji/",data:{100:"unicode/1f4af.png?v8",1234:"unicode/1f522.png?v8","+1":"unicode/1f44d.png?v8","-1":"unicode/1f44e.png?v8","1st_place_medal":"unicode/1f947.png?v8","2nd_place_medal":"unicode/1f948.png?v8","3rd_place_medal":"unicode/1f949.png?v8","8ball":"unicode/1f3b1.png?v8",a:"unicode/1f170.png?v8",ab:"unicode/1f18e.png?v8",abacus:"unicode/1f9ee.png?v8",abc:"unicode/1f524.png?v8",abcd:"unicode/1f521.png?v8",accept:"unicode/1f251.png?v8",accessibility:"accessibility.png?v8",accordion:"unicode/1fa97.png?v8",adhesive_bandage:"unicode/1fa79.png?v8",adult:"unicode/1f9d1.png?v8",aerial_tramway:"unicode/1f6a1.png?v8",afghanistan:"unicode/1f1e6-1f1eb.png?v8",airplane:"unicode/2708.png?v8",aland_islands:"unicode/1f1e6-1f1fd.png?v8",alarm_clock:"unicode/23f0.png?v8",albania:"unicode/1f1e6-1f1f1.png?v8",alembic:"unicode/2697.png?v8",algeria:"unicode/1f1e9-1f1ff.png?v8",alien:"unicode/1f47d.png?v8",ambulance:"unicode/1f691.png?v8",american_samoa:"unicode/1f1e6-1f1f8.png?v8",amphora:"unicode/1f3fa.png?v8",anatomical_heart:"unicode/1fac0.png?v8",anchor:"unicode/2693.png?v8",andorra:"unicode/1f1e6-1f1e9.png?v8",angel:"unicode/1f47c.png?v8",anger:"unicode/1f4a2.png?v8",angola:"unicode/1f1e6-1f1f4.png?v8",angry:"unicode/1f620.png?v8",anguilla:"unicode/1f1e6-1f1ee.png?v8",anguished:"unicode/1f627.png?v8",ant:"unicode/1f41c.png?v8",antarctica:"unicode/1f1e6-1f1f6.png?v8",antigua_barbuda:"unicode/1f1e6-1f1ec.png?v8",apple:"unicode/1f34e.png?v8",aquarius:"unicode/2652.png?v8",argentina:"unicode/1f1e6-1f1f7.png?v8",aries:"unicode/2648.png?v8",armenia:"unicode/1f1e6-1f1f2.png?v8",arrow_backward:"unicode/25c0.png?v8",arrow_double_down:"unicode/23ec.png?v8",arrow_double_up:"unicode/23eb.png?v8",arrow_down:"unicode/2b07.png?v8",arrow_down_small:"unicode/1f53d.png?v8",arrow_forward:"unicode/25b6.png?v8",arrow_heading_down:"unicode/2935.png?v8",arrow_heading_up:"unicode/2934.png?v8",arrow_left:"unicode/2b05.png?v8",arrow_lower_left:"unicode/2199.png?v8",arrow_lower_right:"unicode/2198.png?v8",arrow_right:"unicode/27a1.png?v8",arrow_right_hook:"unicode/21aa.png?v8",arrow_up:"unicode/2b06.png?v8",arrow_up_down:"unicode/2195.png?v8",arrow_up_small:"unicode/1f53c.png?v8",arrow_upper_left:"unicode/2196.png?v8",arrow_upper_right:"unicode/2197.png?v8",arrows_clockwise:"unicode/1f503.png?v8",arrows_counterclockwise:"unicode/1f504.png?v8",art:"unicode/1f3a8.png?v8",articulated_lorry:"unicode/1f69b.png?v8",artificial_satellite:"unicode/1f6f0.png?v8",artist:"unicode/1f9d1-1f3a8.png?v8",aruba:"unicode/1f1e6-1f1fc.png?v8",ascension_island:"unicode/1f1e6-1f1e8.png?v8",asterisk:"unicode/002a-20e3.png?v8",astonished:"unicode/1f632.png?v8",astronaut:"unicode/1f9d1-1f680.png?v8",athletic_shoe:"unicode/1f45f.png?v8",atm:"unicode/1f3e7.png?v8",atom:"atom.png?v8",atom_symbol:"unicode/269b.png?v8",australia:"unicode/1f1e6-1f1fa.png?v8",austria:"unicode/1f1e6-1f1f9.png?v8",auto_rickshaw:"unicode/1f6fa.png?v8",avocado:"unicode/1f951.png?v8",axe:"unicode/1fa93.png?v8",azerbaijan:"unicode/1f1e6-1f1ff.png?v8",b:"unicode/1f171.png?v8",baby:"unicode/1f476.png?v8",baby_bottle:"unicode/1f37c.png?v8",baby_chick:"unicode/1f424.png?v8",baby_symbol:"unicode/1f6bc.png?v8",back:"unicode/1f519.png?v8",bacon:"unicode/1f953.png?v8",badger:"unicode/1f9a1.png?v8",badminton:"unicode/1f3f8.png?v8",bagel:"unicode/1f96f.png?v8",baggage_claim:"unicode/1f6c4.png?v8",baguette_bread:"unicode/1f956.png?v8",bahamas:"unicode/1f1e7-1f1f8.png?v8",bahrain:"unicode/1f1e7-1f1ed.png?v8",balance_scale:"unicode/2696.png?v8",bald_man:"unicode/1f468-1f9b2.png?v8",bald_woman:"unicode/1f469-1f9b2.png?v8",ballet_shoes:"unicode/1fa70.png?v8",balloon:"unicode/1f388.png?v8",ballot_box:"unicode/1f5f3.png?v8",ballot_box_with_check:"unicode/2611.png?v8",bamboo:"unicode/1f38d.png?v8",banana:"unicode/1f34c.png?v8",bangbang:"unicode/203c.png?v8",bangladesh:"unicode/1f1e7-1f1e9.png?v8",banjo:"unicode/1fa95.png?v8",bank:"unicode/1f3e6.png?v8",bar_chart:"unicode/1f4ca.png?v8",barbados:"unicode/1f1e7-1f1e7.png?v8",barber:"unicode/1f488.png?v8",baseball:"unicode/26be.png?v8",basecamp:"basecamp.png?v8",basecampy:"basecampy.png?v8",basket:"unicode/1f9fa.png?v8",basketball:"unicode/1f3c0.png?v8",basketball_man:"unicode/26f9-2642.png?v8",basketball_woman:"unicode/26f9-2640.png?v8",bat:"unicode/1f987.png?v8",bath:"unicode/1f6c0.png?v8",bathtub:"unicode/1f6c1.png?v8",battery:"unicode/1f50b.png?v8",beach_umbrella:"unicode/1f3d6.png?v8",bear:"unicode/1f43b.png?v8",bearded_person:"unicode/1f9d4.png?v8",beaver:"unicode/1f9ab.png?v8",bed:"unicode/1f6cf.png?v8",bee:"unicode/1f41d.png?v8",beer:"unicode/1f37a.png?v8",beers:"unicode/1f37b.png?v8",beetle:"unicode/1fab2.png?v8",beginner:"unicode/1f530.png?v8",belarus:"unicode/1f1e7-1f1fe.png?v8",belgium:"unicode/1f1e7-1f1ea.png?v8",belize:"unicode/1f1e7-1f1ff.png?v8",bell:"unicode/1f514.png?v8",bell_pepper:"unicode/1fad1.png?v8",bellhop_bell:"unicode/1f6ce.png?v8",benin:"unicode/1f1e7-1f1ef.png?v8",bento:"unicode/1f371.png?v8",bermuda:"unicode/1f1e7-1f1f2.png?v8",beverage_box:"unicode/1f9c3.png?v8",bhutan:"unicode/1f1e7-1f1f9.png?v8",bicyclist:"unicode/1f6b4.png?v8",bike:"unicode/1f6b2.png?v8",biking_man:"unicode/1f6b4-2642.png?v8",biking_woman:"unicode/1f6b4-2640.png?v8",bikini:"unicode/1f459.png?v8",billed_cap:"unicode/1f9e2.png?v8",biohazard:"unicode/2623.png?v8",bird:"unicode/1f426.png?v8",birthday:"unicode/1f382.png?v8",bison:"unicode/1f9ac.png?v8",black_cat:"unicode/1f408-2b1b.png?v8",black_circle:"unicode/26ab.png?v8",black_flag:"unicode/1f3f4.png?v8",black_heart:"unicode/1f5a4.png?v8",black_joker:"unicode/1f0cf.png?v8",black_large_square:"unicode/2b1b.png?v8",black_medium_small_square:"unicode/25fe.png?v8",black_medium_square:"unicode/25fc.png?v8",black_nib:"unicode/2712.png?v8",black_small_square:"unicode/25aa.png?v8",black_square_button:"unicode/1f532.png?v8",blond_haired_man:"unicode/1f471-2642.png?v8",blond_haired_person:"unicode/1f471.png?v8",blond_haired_woman:"unicode/1f471-2640.png?v8",blonde_woman:"unicode/1f471-2640.png?v8",blossom:"unicode/1f33c.png?v8",blowfish:"unicode/1f421.png?v8",blue_book:"unicode/1f4d8.png?v8",blue_car:"unicode/1f699.png?v8",blue_heart:"unicode/1f499.png?v8",blue_square:"unicode/1f7e6.png?v8",blueberries:"unicode/1fad0.png?v8",blush:"unicode/1f60a.png?v8",boar:"unicode/1f417.png?v8",boat:"unicode/26f5.png?v8",bolivia:"unicode/1f1e7-1f1f4.png?v8",bomb:"unicode/1f4a3.png?v8",bone:"unicode/1f9b4.png?v8",book:"unicode/1f4d6.png?v8",bookmark:"unicode/1f516.png?v8",bookmark_tabs:"unicode/1f4d1.png?v8",books:"unicode/1f4da.png?v8",boom:"unicode/1f4a5.png?v8",boomerang:"unicode/1fa83.png?v8",boot:"unicode/1f462.png?v8",bosnia_herzegovina:"unicode/1f1e7-1f1e6.png?v8",botswana:"unicode/1f1e7-1f1fc.png?v8",bouncing_ball_man:"unicode/26f9-2642.png?v8",bouncing_ball_person:"unicode/26f9.png?v8",bouncing_ball_woman:"unicode/26f9-2640.png?v8",bouquet:"unicode/1f490.png?v8",bouvet_island:"unicode/1f1e7-1f1fb.png?v8",bow:"unicode/1f647.png?v8",bow_and_arrow:"unicode/1f3f9.png?v8",bowing_man:"unicode/1f647-2642.png?v8",bowing_woman:"unicode/1f647-2640.png?v8",bowl_with_spoon:"unicode/1f963.png?v8",bowling:"unicode/1f3b3.png?v8",bowtie:"bowtie.png?v8",boxing_glove:"unicode/1f94a.png?v8",boy:"unicode/1f466.png?v8",brain:"unicode/1f9e0.png?v8",brazil:"unicode/1f1e7-1f1f7.png?v8",bread:"unicode/1f35e.png?v8",breast_feeding:"unicode/1f931.png?v8",bricks:"unicode/1f9f1.png?v8",bride_with_veil:"unicode/1f470-2640.png?v8",bridge_at_night:"unicode/1f309.png?v8",briefcase:"unicode/1f4bc.png?v8",british_indian_ocean_territory:"unicode/1f1ee-1f1f4.png?v8",british_virgin_islands:"unicode/1f1fb-1f1ec.png?v8",broccoli:"unicode/1f966.png?v8",broken_heart:"unicode/1f494.png?v8",broom:"unicode/1f9f9.png?v8",brown_circle:"unicode/1f7e4.png?v8",brown_heart:"unicode/1f90e.png?v8",brown_square:"unicode/1f7eb.png?v8",brunei:"unicode/1f1e7-1f1f3.png?v8",bubble_tea:"unicode/1f9cb.png?v8",bucket:"unicode/1faa3.png?v8",bug:"unicode/1f41b.png?v8",building_construction:"unicode/1f3d7.png?v8",bulb:"unicode/1f4a1.png?v8",bulgaria:"unicode/1f1e7-1f1ec.png?v8",bullettrain_front:"unicode/1f685.png?v8",bullettrain_side:"unicode/1f684.png?v8",burkina_faso:"unicode/1f1e7-1f1eb.png?v8",burrito:"unicode/1f32f.png?v8",burundi:"unicode/1f1e7-1f1ee.png?v8",bus:"unicode/1f68c.png?v8",business_suit_levitating:"unicode/1f574.png?v8",busstop:"unicode/1f68f.png?v8",bust_in_silhouette:"unicode/1f464.png?v8",busts_in_silhouette:"unicode/1f465.png?v8",butter:"unicode/1f9c8.png?v8",butterfly:"unicode/1f98b.png?v8",cactus:"unicode/1f335.png?v8",cake:"unicode/1f370.png?v8",calendar:"unicode/1f4c6.png?v8",call_me_hand:"unicode/1f919.png?v8",calling:"unicode/1f4f2.png?v8",cambodia:"unicode/1f1f0-1f1ed.png?v8",camel:"unicode/1f42b.png?v8",camera:"unicode/1f4f7.png?v8",camera_flash:"unicode/1f4f8.png?v8",cameroon:"unicode/1f1e8-1f1f2.png?v8",camping:"unicode/1f3d5.png?v8",canada:"unicode/1f1e8-1f1e6.png?v8",canary_islands:"unicode/1f1ee-1f1e8.png?v8",cancer:"unicode/264b.png?v8",candle:"unicode/1f56f.png?v8",candy:"unicode/1f36c.png?v8",canned_food:"unicode/1f96b.png?v8",canoe:"unicode/1f6f6.png?v8",cape_verde:"unicode/1f1e8-1f1fb.png?v8",capital_abcd:"unicode/1f520.png?v8",capricorn:"unicode/2651.png?v8",car:"unicode/1f697.png?v8",card_file_box:"unicode/1f5c3.png?v8",card_index:"unicode/1f4c7.png?v8",card_index_dividers:"unicode/1f5c2.png?v8",caribbean_netherlands:"unicode/1f1e7-1f1f6.png?v8",carousel_horse:"unicode/1f3a0.png?v8",carpentry_saw:"unicode/1fa9a.png?v8",carrot:"unicode/1f955.png?v8",cartwheeling:"unicode/1f938.png?v8",cat:"unicode/1f431.png?v8",cat2:"unicode/1f408.png?v8",cayman_islands:"unicode/1f1f0-1f1fe.png?v8",cd:"unicode/1f4bf.png?v8",central_african_republic:"unicode/1f1e8-1f1eb.png?v8",ceuta_melilla:"unicode/1f1ea-1f1e6.png?v8",chad:"unicode/1f1f9-1f1e9.png?v8",chains:"unicode/26d3.png?v8",chair:"unicode/1fa91.png?v8",champagne:"unicode/1f37e.png?v8",chart:"unicode/1f4b9.png?v8",chart_with_downwards_trend:"unicode/1f4c9.png?v8",chart_with_upwards_trend:"unicode/1f4c8.png?v8",checkered_flag:"unicode/1f3c1.png?v8",cheese:"unicode/1f9c0.png?v8",cherries:"unicode/1f352.png?v8",cherry_blossom:"unicode/1f338.png?v8",chess_pawn:"unicode/265f.png?v8",chestnut:"unicode/1f330.png?v8",chicken:"unicode/1f414.png?v8",child:"unicode/1f9d2.png?v8",children_crossing:"unicode/1f6b8.png?v8",chile:"unicode/1f1e8-1f1f1.png?v8",chipmunk:"unicode/1f43f.png?v8",chocolate_bar:"unicode/1f36b.png?v8",chopsticks:"unicode/1f962.png?v8",christmas_island:"unicode/1f1e8-1f1fd.png?v8",christmas_tree:"unicode/1f384.png?v8",church:"unicode/26ea.png?v8",cinema:"unicode/1f3a6.png?v8",circus_tent:"unicode/1f3aa.png?v8",city_sunrise:"unicode/1f307.png?v8",city_sunset:"unicode/1f306.png?v8",cityscape:"unicode/1f3d9.png?v8",cl:"unicode/1f191.png?v8",clamp:"unicode/1f5dc.png?v8",clap:"unicode/1f44f.png?v8",clapper:"unicode/1f3ac.png?v8",classical_building:"unicode/1f3db.png?v8",climbing:"unicode/1f9d7.png?v8",climbing_man:"unicode/1f9d7-2642.png?v8",climbing_woman:"unicode/1f9d7-2640.png?v8",clinking_glasses:"unicode/1f942.png?v8",clipboard:"unicode/1f4cb.png?v8",clipperton_island:"unicode/1f1e8-1f1f5.png?v8",clock1:"unicode/1f550.png?v8",clock10:"unicode/1f559.png?v8",clock1030:"unicode/1f565.png?v8",clock11:"unicode/1f55a.png?v8",clock1130:"unicode/1f566.png?v8",clock12:"unicode/1f55b.png?v8",clock1230:"unicode/1f567.png?v8",clock130:"unicode/1f55c.png?v8",clock2:"unicode/1f551.png?v8",clock230:"unicode/1f55d.png?v8",clock3:"unicode/1f552.png?v8",clock330:"unicode/1f55e.png?v8",clock4:"unicode/1f553.png?v8",clock430:"unicode/1f55f.png?v8",clock5:"unicode/1f554.png?v8",clock530:"unicode/1f560.png?v8",clock6:"unicode/1f555.png?v8",clock630:"unicode/1f561.png?v8",clock7:"unicode/1f556.png?v8",clock730:"unicode/1f562.png?v8",clock8:"unicode/1f557.png?v8",clock830:"unicode/1f563.png?v8",clock9:"unicode/1f558.png?v8",clock930:"unicode/1f564.png?v8",closed_book:"unicode/1f4d5.png?v8",closed_lock_with_key:"unicode/1f510.png?v8",closed_umbrella:"unicode/1f302.png?v8",cloud:"unicode/2601.png?v8",cloud_with_lightning:"unicode/1f329.png?v8",cloud_with_lightning_and_rain:"unicode/26c8.png?v8",cloud_with_rain:"unicode/1f327.png?v8",cloud_with_snow:"unicode/1f328.png?v8",clown_face:"unicode/1f921.png?v8",clubs:"unicode/2663.png?v8",cn:"unicode/1f1e8-1f1f3.png?v8",coat:"unicode/1f9e5.png?v8",cockroach:"unicode/1fab3.png?v8",cocktail:"unicode/1f378.png?v8",coconut:"unicode/1f965.png?v8",cocos_islands:"unicode/1f1e8-1f1e8.png?v8",coffee:"unicode/2615.png?v8",coffin:"unicode/26b0.png?v8",coin:"unicode/1fa99.png?v8",cold_face:"unicode/1f976.png?v8",cold_sweat:"unicode/1f630.png?v8",collision:"unicode/1f4a5.png?v8",colombia:"unicode/1f1e8-1f1f4.png?v8",comet:"unicode/2604.png?v8",comoros:"unicode/1f1f0-1f1f2.png?v8",compass:"unicode/1f9ed.png?v8",computer:"unicode/1f4bb.png?v8",computer_mouse:"unicode/1f5b1.png?v8",confetti_ball:"unicode/1f38a.png?v8",confounded:"unicode/1f616.png?v8",confused:"unicode/1f615.png?v8",congo_brazzaville:"unicode/1f1e8-1f1ec.png?v8",congo_kinshasa:"unicode/1f1e8-1f1e9.png?v8",congratulations:"unicode/3297.png?v8",construction:"unicode/1f6a7.png?v8",construction_worker:"unicode/1f477.png?v8",construction_worker_man:"unicode/1f477-2642.png?v8",construction_worker_woman:"unicode/1f477-2640.png?v8",control_knobs:"unicode/1f39b.png?v8",convenience_store:"unicode/1f3ea.png?v8",cook:"unicode/1f9d1-1f373.png?v8",cook_islands:"unicode/1f1e8-1f1f0.png?v8",cookie:"unicode/1f36a.png?v8",cool:"unicode/1f192.png?v8",cop:"unicode/1f46e.png?v8",copyright:"unicode/00a9.png?v8",corn:"unicode/1f33d.png?v8",costa_rica:"unicode/1f1e8-1f1f7.png?v8",cote_divoire:"unicode/1f1e8-1f1ee.png?v8",couch_and_lamp:"unicode/1f6cb.png?v8",couple:"unicode/1f46b.png?v8",couple_with_heart:"unicode/1f491.png?v8",couple_with_heart_man_man:"unicode/1f468-2764-1f468.png?v8",couple_with_heart_woman_man:"unicode/1f469-2764-1f468.png?v8",couple_with_heart_woman_woman:"unicode/1f469-2764-1f469.png?v8",couplekiss:"unicode/1f48f.png?v8",couplekiss_man_man:"unicode/1f468-2764-1f48b-1f468.png?v8",couplekiss_man_woman:"unicode/1f469-2764-1f48b-1f468.png?v8",couplekiss_woman_woman:"unicode/1f469-2764-1f48b-1f469.png?v8",cow:"unicode/1f42e.png?v8",cow2:"unicode/1f404.png?v8",cowboy_hat_face:"unicode/1f920.png?v8",crab:"unicode/1f980.png?v8",crayon:"unicode/1f58d.png?v8",credit_card:"unicode/1f4b3.png?v8",crescent_moon:"unicode/1f319.png?v8",cricket:"unicode/1f997.png?v8",cricket_game:"unicode/1f3cf.png?v8",croatia:"unicode/1f1ed-1f1f7.png?v8",crocodile:"unicode/1f40a.png?v8",croissant:"unicode/1f950.png?v8",crossed_fingers:"unicode/1f91e.png?v8",crossed_flags:"unicode/1f38c.png?v8",crossed_swords:"unicode/2694.png?v8",crown:"unicode/1f451.png?v8",cry:"unicode/1f622.png?v8",crying_cat_face:"unicode/1f63f.png?v8",crystal_ball:"unicode/1f52e.png?v8",cuba:"unicode/1f1e8-1f1fa.png?v8",cucumber:"unicode/1f952.png?v8",cup_with_straw:"unicode/1f964.png?v8",cupcake:"unicode/1f9c1.png?v8",cupid:"unicode/1f498.png?v8",curacao:"unicode/1f1e8-1f1fc.png?v8",curling_stone:"unicode/1f94c.png?v8",curly_haired_man:"unicode/1f468-1f9b1.png?v8",curly_haired_woman:"unicode/1f469-1f9b1.png?v8",curly_loop:"unicode/27b0.png?v8",currency_exchange:"unicode/1f4b1.png?v8",curry:"unicode/1f35b.png?v8",cursing_face:"unicode/1f92c.png?v8",custard:"unicode/1f36e.png?v8",customs:"unicode/1f6c3.png?v8",cut_of_meat:"unicode/1f969.png?v8",cyclone:"unicode/1f300.png?v8",cyprus:"unicode/1f1e8-1f1fe.png?v8",czech_republic:"unicode/1f1e8-1f1ff.png?v8",dagger:"unicode/1f5e1.png?v8",dancer:"unicode/1f483.png?v8",dancers:"unicode/1f46f.png?v8",dancing_men:"unicode/1f46f-2642.png?v8",dancing_women:"unicode/1f46f-2640.png?v8",dango:"unicode/1f361.png?v8",dark_sunglasses:"unicode/1f576.png?v8",dart:"unicode/1f3af.png?v8",dash:"unicode/1f4a8.png?v8",date:"unicode/1f4c5.png?v8",de:"unicode/1f1e9-1f1ea.png?v8",deaf_man:"unicode/1f9cf-2642.png?v8",deaf_person:"unicode/1f9cf.png?v8",deaf_woman:"unicode/1f9cf-2640.png?v8",deciduous_tree:"unicode/1f333.png?v8",deer:"unicode/1f98c.png?v8",denmark:"unicode/1f1e9-1f1f0.png?v8",department_store:"unicode/1f3ec.png?v8",dependabot:"dependabot.png?v8",derelict_house:"unicode/1f3da.png?v8",desert:"unicode/1f3dc.png?v8",desert_island:"unicode/1f3dd.png?v8",desktop_computer:"unicode/1f5a5.png?v8",detective:"unicode/1f575.png?v8",diamond_shape_with_a_dot_inside:"unicode/1f4a0.png?v8",diamonds:"unicode/2666.png?v8",diego_garcia:"unicode/1f1e9-1f1ec.png?v8",disappointed:"unicode/1f61e.png?v8",disappointed_relieved:"unicode/1f625.png?v8",disguised_face:"unicode/1f978.png?v8",diving_mask:"unicode/1f93f.png?v8",diya_lamp:"unicode/1fa94.png?v8",dizzy:"unicode/1f4ab.png?v8",dizzy_face:"unicode/1f635.png?v8",djibouti:"unicode/1f1e9-1f1ef.png?v8",dna:"unicode/1f9ec.png?v8",do_not_litter:"unicode/1f6af.png?v8",dodo:"unicode/1f9a4.png?v8",dog:"unicode/1f436.png?v8",dog2:"unicode/1f415.png?v8",dollar:"unicode/1f4b5.png?v8",dolls:"unicode/1f38e.png?v8",dolphin:"unicode/1f42c.png?v8",dominica:"unicode/1f1e9-1f1f2.png?v8",dominican_republic:"unicode/1f1e9-1f1f4.png?v8",door:"unicode/1f6aa.png?v8",doughnut:"unicode/1f369.png?v8",dove:"unicode/1f54a.png?v8",dragon:"unicode/1f409.png?v8",dragon_face:"unicode/1f432.png?v8",dress:"unicode/1f457.png?v8",dromedary_camel:"unicode/1f42a.png?v8",drooling_face:"unicode/1f924.png?v8",drop_of_blood:"unicode/1fa78.png?v8",droplet:"unicode/1f4a7.png?v8",drum:"unicode/1f941.png?v8",duck:"unicode/1f986.png?v8",dumpling:"unicode/1f95f.png?v8",dvd:"unicode/1f4c0.png?v8","e-mail":"unicode/1f4e7.png?v8",eagle:"unicode/1f985.png?v8",ear:"unicode/1f442.png?v8",ear_of_rice:"unicode/1f33e.png?v8",ear_with_hearing_aid:"unicode/1f9bb.png?v8",earth_africa:"unicode/1f30d.png?v8",earth_americas:"unicode/1f30e.png?v8",earth_asia:"unicode/1f30f.png?v8",ecuador:"unicode/1f1ea-1f1e8.png?v8",egg:"unicode/1f95a.png?v8",eggplant:"unicode/1f346.png?v8",egypt:"unicode/1f1ea-1f1ec.png?v8",eight:"unicode/0038-20e3.png?v8",eight_pointed_black_star:"unicode/2734.png?v8",eight_spoked_asterisk:"unicode/2733.png?v8",eject_button:"unicode/23cf.png?v8",el_salvador:"unicode/1f1f8-1f1fb.png?v8",electric_plug:"unicode/1f50c.png?v8",electron:"electron.png?v8",elephant:"unicode/1f418.png?v8",elevator:"unicode/1f6d7.png?v8",elf:"unicode/1f9dd.png?v8",elf_man:"unicode/1f9dd-2642.png?v8",elf_woman:"unicode/1f9dd-2640.png?v8",email:"unicode/1f4e7.png?v8",end:"unicode/1f51a.png?v8",england:"unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png?v8",envelope:"unicode/2709.png?v8",envelope_with_arrow:"unicode/1f4e9.png?v8",equatorial_guinea:"unicode/1f1ec-1f1f6.png?v8",eritrea:"unicode/1f1ea-1f1f7.png?v8",es:"unicode/1f1ea-1f1f8.png?v8",estonia:"unicode/1f1ea-1f1ea.png?v8",ethiopia:"unicode/1f1ea-1f1f9.png?v8",eu:"unicode/1f1ea-1f1fa.png?v8",euro:"unicode/1f4b6.png?v8",european_castle:"unicode/1f3f0.png?v8",european_post_office:"unicode/1f3e4.png?v8",european_union:"unicode/1f1ea-1f1fa.png?v8",evergreen_tree:"unicode/1f332.png?v8",exclamation:"unicode/2757.png?v8",exploding_head:"unicode/1f92f.png?v8",expressionless:"unicode/1f611.png?v8",eye:"unicode/1f441.png?v8",eye_speech_bubble:"unicode/1f441-1f5e8.png?v8",eyeglasses:"unicode/1f453.png?v8",eyes:"unicode/1f440.png?v8",face_exhaling:"unicode/1f62e-1f4a8.png?v8",face_in_clouds:"unicode/1f636-1f32b.png?v8",face_with_head_bandage:"unicode/1f915.png?v8",face_with_spiral_eyes:"unicode/1f635-1f4ab.png?v8",face_with_thermometer:"unicode/1f912.png?v8",facepalm:"unicode/1f926.png?v8",facepunch:"unicode/1f44a.png?v8",factory:"unicode/1f3ed.png?v8",factory_worker:"unicode/1f9d1-1f3ed.png?v8",fairy:"unicode/1f9da.png?v8",fairy_man:"unicode/1f9da-2642.png?v8",fairy_woman:"unicode/1f9da-2640.png?v8",falafel:"unicode/1f9c6.png?v8",falkland_islands:"unicode/1f1eb-1f1f0.png?v8",fallen_leaf:"unicode/1f342.png?v8",family:"unicode/1f46a.png?v8",family_man_boy:"unicode/1f468-1f466.png?v8",family_man_boy_boy:"unicode/1f468-1f466-1f466.png?v8",family_man_girl:"unicode/1f468-1f467.png?v8",family_man_girl_boy:"unicode/1f468-1f467-1f466.png?v8",family_man_girl_girl:"unicode/1f468-1f467-1f467.png?v8",family_man_man_boy:"unicode/1f468-1f468-1f466.png?v8",family_man_man_boy_boy:"unicode/1f468-1f468-1f466-1f466.png?v8",family_man_man_girl:"unicode/1f468-1f468-1f467.png?v8",family_man_man_girl_boy:"unicode/1f468-1f468-1f467-1f466.png?v8",family_man_man_girl_girl:"unicode/1f468-1f468-1f467-1f467.png?v8",family_man_woman_boy:"unicode/1f468-1f469-1f466.png?v8",family_man_woman_boy_boy:"unicode/1f468-1f469-1f466-1f466.png?v8",family_man_woman_girl:"unicode/1f468-1f469-1f467.png?v8",family_man_woman_girl_boy:"unicode/1f468-1f469-1f467-1f466.png?v8",family_man_woman_girl_girl:"unicode/1f468-1f469-1f467-1f467.png?v8",family_woman_boy:"unicode/1f469-1f466.png?v8",family_woman_boy_boy:"unicode/1f469-1f466-1f466.png?v8",family_woman_girl:"unicode/1f469-1f467.png?v8",family_woman_girl_boy:"unicode/1f469-1f467-1f466.png?v8",family_woman_girl_girl:"unicode/1f469-1f467-1f467.png?v8",family_woman_woman_boy:"unicode/1f469-1f469-1f466.png?v8",family_woman_woman_boy_boy:"unicode/1f469-1f469-1f466-1f466.png?v8",family_woman_woman_girl:"unicode/1f469-1f469-1f467.png?v8",family_woman_woman_girl_boy:"unicode/1f469-1f469-1f467-1f466.png?v8",family_woman_woman_girl_girl:"unicode/1f469-1f469-1f467-1f467.png?v8",farmer:"unicode/1f9d1-1f33e.png?v8",faroe_islands:"unicode/1f1eb-1f1f4.png?v8",fast_forward:"unicode/23e9.png?v8",fax:"unicode/1f4e0.png?v8",fearful:"unicode/1f628.png?v8",feather:"unicode/1fab6.png?v8",feelsgood:"feelsgood.png?v8",feet:"unicode/1f43e.png?v8",female_detective:"unicode/1f575-2640.png?v8",female_sign:"unicode/2640.png?v8",ferris_wheel:"unicode/1f3a1.png?v8",ferry:"unicode/26f4.png?v8",field_hockey:"unicode/1f3d1.png?v8",fiji:"unicode/1f1eb-1f1ef.png?v8",file_cabinet:"unicode/1f5c4.png?v8",file_folder:"unicode/1f4c1.png?v8",film_projector:"unicode/1f4fd.png?v8",film_strip:"unicode/1f39e.png?v8",finland:"unicode/1f1eb-1f1ee.png?v8",finnadie:"finnadie.png?v8",fire:"unicode/1f525.png?v8",fire_engine:"unicode/1f692.png?v8",fire_extinguisher:"unicode/1f9ef.png?v8",firecracker:"unicode/1f9e8.png?v8",firefighter:"unicode/1f9d1-1f692.png?v8",fireworks:"unicode/1f386.png?v8",first_quarter_moon:"unicode/1f313.png?v8",first_quarter_moon_with_face:"unicode/1f31b.png?v8",fish:"unicode/1f41f.png?v8",fish_cake:"unicode/1f365.png?v8",fishing_pole_and_fish:"unicode/1f3a3.png?v8",fishsticks:"fishsticks.png?v8",fist:"unicode/270a.png?v8",fist_left:"unicode/1f91b.png?v8",fist_oncoming:"unicode/1f44a.png?v8",fist_raised:"unicode/270a.png?v8",fist_right:"unicode/1f91c.png?v8",five:"unicode/0035-20e3.png?v8",flags:"unicode/1f38f.png?v8",flamingo:"unicode/1f9a9.png?v8",flashlight:"unicode/1f526.png?v8",flat_shoe:"unicode/1f97f.png?v8",flatbread:"unicode/1fad3.png?v8",fleur_de_lis:"unicode/269c.png?v8",flight_arrival:"unicode/1f6ec.png?v8",flight_departure:"unicode/1f6eb.png?v8",flipper:"unicode/1f42c.png?v8",floppy_disk:"unicode/1f4be.png?v8",flower_playing_cards:"unicode/1f3b4.png?v8",flushed:"unicode/1f633.png?v8",fly:"unicode/1fab0.png?v8",flying_disc:"unicode/1f94f.png?v8",flying_saucer:"unicode/1f6f8.png?v8",fog:"unicode/1f32b.png?v8",foggy:"unicode/1f301.png?v8",fondue:"unicode/1fad5.png?v8",foot:"unicode/1f9b6.png?v8",football:"unicode/1f3c8.png?v8",footprints:"unicode/1f463.png?v8",fork_and_knife:"unicode/1f374.png?v8",fortune_cookie:"unicode/1f960.png?v8",fountain:"unicode/26f2.png?v8",fountain_pen:"unicode/1f58b.png?v8",four:"unicode/0034-20e3.png?v8",four_leaf_clover:"unicode/1f340.png?v8",fox_face:"unicode/1f98a.png?v8",fr:"unicode/1f1eb-1f1f7.png?v8",framed_picture:"unicode/1f5bc.png?v8",free:"unicode/1f193.png?v8",french_guiana:"unicode/1f1ec-1f1eb.png?v8",french_polynesia:"unicode/1f1f5-1f1eb.png?v8",french_southern_territories:"unicode/1f1f9-1f1eb.png?v8",fried_egg:"unicode/1f373.png?v8",fried_shrimp:"unicode/1f364.png?v8",fries:"unicode/1f35f.png?v8",frog:"unicode/1f438.png?v8",frowning:"unicode/1f626.png?v8",frowning_face:"unicode/2639.png?v8",frowning_man:"unicode/1f64d-2642.png?v8",frowning_person:"unicode/1f64d.png?v8",frowning_woman:"unicode/1f64d-2640.png?v8",fu:"unicode/1f595.png?v8",fuelpump:"unicode/26fd.png?v8",full_moon:"unicode/1f315.png?v8",full_moon_with_face:"unicode/1f31d.png?v8",funeral_urn:"unicode/26b1.png?v8",gabon:"unicode/1f1ec-1f1e6.png?v8",gambia:"unicode/1f1ec-1f1f2.png?v8",game_die:"unicode/1f3b2.png?v8",garlic:"unicode/1f9c4.png?v8",gb:"unicode/1f1ec-1f1e7.png?v8",gear:"unicode/2699.png?v8",gem:"unicode/1f48e.png?v8",gemini:"unicode/264a.png?v8",genie:"unicode/1f9de.png?v8",genie_man:"unicode/1f9de-2642.png?v8",genie_woman:"unicode/1f9de-2640.png?v8",georgia:"unicode/1f1ec-1f1ea.png?v8",ghana:"unicode/1f1ec-1f1ed.png?v8",ghost:"unicode/1f47b.png?v8",gibraltar:"unicode/1f1ec-1f1ee.png?v8",gift:"unicode/1f381.png?v8",gift_heart:"unicode/1f49d.png?v8",giraffe:"unicode/1f992.png?v8",girl:"unicode/1f467.png?v8",globe_with_meridians:"unicode/1f310.png?v8",gloves:"unicode/1f9e4.png?v8",goal_net:"unicode/1f945.png?v8",goat:"unicode/1f410.png?v8",goberserk:"goberserk.png?v8",godmode:"godmode.png?v8",goggles:"unicode/1f97d.png?v8",golf:"unicode/26f3.png?v8",golfing:"unicode/1f3cc.png?v8",golfing_man:"unicode/1f3cc-2642.png?v8",golfing_woman:"unicode/1f3cc-2640.png?v8",gorilla:"unicode/1f98d.png?v8",grapes:"unicode/1f347.png?v8",greece:"unicode/1f1ec-1f1f7.png?v8",green_apple:"unicode/1f34f.png?v8",green_book:"unicode/1f4d7.png?v8",green_circle:"unicode/1f7e2.png?v8",green_heart:"unicode/1f49a.png?v8",green_salad:"unicode/1f957.png?v8",green_square:"unicode/1f7e9.png?v8",greenland:"unicode/1f1ec-1f1f1.png?v8",grenada:"unicode/1f1ec-1f1e9.png?v8",grey_exclamation:"unicode/2755.png?v8",grey_question:"unicode/2754.png?v8",grimacing:"unicode/1f62c.png?v8",grin:"unicode/1f601.png?v8",grinning:"unicode/1f600.png?v8",guadeloupe:"unicode/1f1ec-1f1f5.png?v8",guam:"unicode/1f1ec-1f1fa.png?v8",guard:"unicode/1f482.png?v8",guardsman:"unicode/1f482-2642.png?v8",guardswoman:"unicode/1f482-2640.png?v8",guatemala:"unicode/1f1ec-1f1f9.png?v8",guernsey:"unicode/1f1ec-1f1ec.png?v8",guide_dog:"unicode/1f9ae.png?v8",guinea:"unicode/1f1ec-1f1f3.png?v8",guinea_bissau:"unicode/1f1ec-1f1fc.png?v8",guitar:"unicode/1f3b8.png?v8",gun:"unicode/1f52b.png?v8",guyana:"unicode/1f1ec-1f1fe.png?v8",haircut:"unicode/1f487.png?v8",haircut_man:"unicode/1f487-2642.png?v8",haircut_woman:"unicode/1f487-2640.png?v8",haiti:"unicode/1f1ed-1f1f9.png?v8",hamburger:"unicode/1f354.png?v8",hammer:"unicode/1f528.png?v8",hammer_and_pick:"unicode/2692.png?v8",hammer_and_wrench:"unicode/1f6e0.png?v8",hamster:"unicode/1f439.png?v8",hand:"unicode/270b.png?v8",hand_over_mouth:"unicode/1f92d.png?v8",handbag:"unicode/1f45c.png?v8",handball_person:"unicode/1f93e.png?v8",handshake:"unicode/1f91d.png?v8",hankey:"unicode/1f4a9.png?v8",hash:"unicode/0023-20e3.png?v8",hatched_chick:"unicode/1f425.png?v8",hatching_chick:"unicode/1f423.png?v8",headphones:"unicode/1f3a7.png?v8",headstone:"unicode/1faa6.png?v8",health_worker:"unicode/1f9d1-2695.png?v8",hear_no_evil:"unicode/1f649.png?v8",heard_mcdonald_islands:"unicode/1f1ed-1f1f2.png?v8",heart:"unicode/2764.png?v8",heart_decoration:"unicode/1f49f.png?v8",heart_eyes:"unicode/1f60d.png?v8",heart_eyes_cat:"unicode/1f63b.png?v8",heart_on_fire:"unicode/2764-1f525.png?v8",heartbeat:"unicode/1f493.png?v8",heartpulse:"unicode/1f497.png?v8",hearts:"unicode/2665.png?v8",heavy_check_mark:"unicode/2714.png?v8",heavy_division_sign:"unicode/2797.png?v8",heavy_dollar_sign:"unicode/1f4b2.png?v8",heavy_exclamation_mark:"unicode/2757.png?v8",heavy_heart_exclamation:"unicode/2763.png?v8",heavy_minus_sign:"unicode/2796.png?v8",heavy_multiplication_x:"unicode/2716.png?v8",heavy_plus_sign:"unicode/2795.png?v8",hedgehog:"unicode/1f994.png?v8",helicopter:"unicode/1f681.png?v8",herb:"unicode/1f33f.png?v8",hibiscus:"unicode/1f33a.png?v8",high_brightness:"unicode/1f506.png?v8",high_heel:"unicode/1f460.png?v8",hiking_boot:"unicode/1f97e.png?v8",hindu_temple:"unicode/1f6d5.png?v8",hippopotamus:"unicode/1f99b.png?v8",hocho:"unicode/1f52a.png?v8",hole:"unicode/1f573.png?v8",honduras:"unicode/1f1ed-1f1f3.png?v8",honey_pot:"unicode/1f36f.png?v8",honeybee:"unicode/1f41d.png?v8",hong_kong:"unicode/1f1ed-1f1f0.png?v8",hook:"unicode/1fa9d.png?v8",horse:"unicode/1f434.png?v8",horse_racing:"unicode/1f3c7.png?v8",hospital:"unicode/1f3e5.png?v8",hot_face:"unicode/1f975.png?v8",hot_pepper:"unicode/1f336.png?v8",hotdog:"unicode/1f32d.png?v8",hotel:"unicode/1f3e8.png?v8",hotsprings:"unicode/2668.png?v8",hourglass:"unicode/231b.png?v8",hourglass_flowing_sand:"unicode/23f3.png?v8",house:"unicode/1f3e0.png?v8",house_with_garden:"unicode/1f3e1.png?v8",houses:"unicode/1f3d8.png?v8",hugs:"unicode/1f917.png?v8",hungary:"unicode/1f1ed-1f1fa.png?v8",hurtrealbad:"hurtrealbad.png?v8",hushed:"unicode/1f62f.png?v8",hut:"unicode/1f6d6.png?v8",ice_cream:"unicode/1f368.png?v8",ice_cube:"unicode/1f9ca.png?v8",ice_hockey:"unicode/1f3d2.png?v8",ice_skate:"unicode/26f8.png?v8",icecream:"unicode/1f366.png?v8",iceland:"unicode/1f1ee-1f1f8.png?v8",id:"unicode/1f194.png?v8",ideograph_advantage:"unicode/1f250.png?v8",imp:"unicode/1f47f.png?v8",inbox_tray:"unicode/1f4e5.png?v8",incoming_envelope:"unicode/1f4e8.png?v8",india:"unicode/1f1ee-1f1f3.png?v8",indonesia:"unicode/1f1ee-1f1e9.png?v8",infinity:"unicode/267e.png?v8",information_desk_person:"unicode/1f481.png?v8",information_source:"unicode/2139.png?v8",innocent:"unicode/1f607.png?v8",interrobang:"unicode/2049.png?v8",iphone:"unicode/1f4f1.png?v8",iran:"unicode/1f1ee-1f1f7.png?v8",iraq:"unicode/1f1ee-1f1f6.png?v8",ireland:"unicode/1f1ee-1f1ea.png?v8",isle_of_man:"unicode/1f1ee-1f1f2.png?v8",israel:"unicode/1f1ee-1f1f1.png?v8",it:"unicode/1f1ee-1f1f9.png?v8",izakaya_lantern:"unicode/1f3ee.png?v8",jack_o_lantern:"unicode/1f383.png?v8",jamaica:"unicode/1f1ef-1f1f2.png?v8",japan:"unicode/1f5fe.png?v8",japanese_castle:"unicode/1f3ef.png?v8",japanese_goblin:"unicode/1f47a.png?v8",japanese_ogre:"unicode/1f479.png?v8",jeans:"unicode/1f456.png?v8",jersey:"unicode/1f1ef-1f1ea.png?v8",jigsaw:"unicode/1f9e9.png?v8",jordan:"unicode/1f1ef-1f1f4.png?v8",joy:"unicode/1f602.png?v8",joy_cat:"unicode/1f639.png?v8",joystick:"unicode/1f579.png?v8",jp:"unicode/1f1ef-1f1f5.png?v8",judge:"unicode/1f9d1-2696.png?v8",juggling_person:"unicode/1f939.png?v8",kaaba:"unicode/1f54b.png?v8",kangaroo:"unicode/1f998.png?v8",kazakhstan:"unicode/1f1f0-1f1ff.png?v8",kenya:"unicode/1f1f0-1f1ea.png?v8",key:"unicode/1f511.png?v8",keyboard:"unicode/2328.png?v8",keycap_ten:"unicode/1f51f.png?v8",kick_scooter:"unicode/1f6f4.png?v8",kimono:"unicode/1f458.png?v8",kiribati:"unicode/1f1f0-1f1ee.png?v8",kiss:"unicode/1f48b.png?v8",kissing:"unicode/1f617.png?v8",kissing_cat:"unicode/1f63d.png?v8",kissing_closed_eyes:"unicode/1f61a.png?v8",kissing_heart:"unicode/1f618.png?v8",kissing_smiling_eyes:"unicode/1f619.png?v8",kite:"unicode/1fa81.png?v8",kiwi_fruit:"unicode/1f95d.png?v8",kneeling_man:"unicode/1f9ce-2642.png?v8",kneeling_person:"unicode/1f9ce.png?v8",kneeling_woman:"unicode/1f9ce-2640.png?v8",knife:"unicode/1f52a.png?v8",knot:"unicode/1faa2.png?v8",koala:"unicode/1f428.png?v8",koko:"unicode/1f201.png?v8",kosovo:"unicode/1f1fd-1f1f0.png?v8",kr:"unicode/1f1f0-1f1f7.png?v8",kuwait:"unicode/1f1f0-1f1fc.png?v8",kyrgyzstan:"unicode/1f1f0-1f1ec.png?v8",lab_coat:"unicode/1f97c.png?v8",label:"unicode/1f3f7.png?v8",lacrosse:"unicode/1f94d.png?v8",ladder:"unicode/1fa9c.png?v8",lady_beetle:"unicode/1f41e.png?v8",lantern:"unicode/1f3ee.png?v8",laos:"unicode/1f1f1-1f1e6.png?v8",large_blue_circle:"unicode/1f535.png?v8",large_blue_diamond:"unicode/1f537.png?v8",large_orange_diamond:"unicode/1f536.png?v8",last_quarter_moon:"unicode/1f317.png?v8",last_quarter_moon_with_face:"unicode/1f31c.png?v8",latin_cross:"unicode/271d.png?v8",latvia:"unicode/1f1f1-1f1fb.png?v8",laughing:"unicode/1f606.png?v8",leafy_green:"unicode/1f96c.png?v8",leaves:"unicode/1f343.png?v8",lebanon:"unicode/1f1f1-1f1e7.png?v8",ledger:"unicode/1f4d2.png?v8",left_luggage:"unicode/1f6c5.png?v8",left_right_arrow:"unicode/2194.png?v8",left_speech_bubble:"unicode/1f5e8.png?v8",leftwards_arrow_with_hook:"unicode/21a9.png?v8",leg:"unicode/1f9b5.png?v8",lemon:"unicode/1f34b.png?v8",leo:"unicode/264c.png?v8",leopard:"unicode/1f406.png?v8",lesotho:"unicode/1f1f1-1f1f8.png?v8",level_slider:"unicode/1f39a.png?v8",liberia:"unicode/1f1f1-1f1f7.png?v8",libra:"unicode/264e.png?v8",libya:"unicode/1f1f1-1f1fe.png?v8",liechtenstein:"unicode/1f1f1-1f1ee.png?v8",light_rail:"unicode/1f688.png?v8",link:"unicode/1f517.png?v8",lion:"unicode/1f981.png?v8",lips:"unicode/1f444.png?v8",lipstick:"unicode/1f484.png?v8",lithuania:"unicode/1f1f1-1f1f9.png?v8",lizard:"unicode/1f98e.png?v8",llama:"unicode/1f999.png?v8",lobster:"unicode/1f99e.png?v8",lock:"unicode/1f512.png?v8",lock_with_ink_pen:"unicode/1f50f.png?v8",lollipop:"unicode/1f36d.png?v8",long_drum:"unicode/1fa98.png?v8",loop:"unicode/27bf.png?v8",lotion_bottle:"unicode/1f9f4.png?v8",lotus_position:"unicode/1f9d8.png?v8",lotus_position_man:"unicode/1f9d8-2642.png?v8",lotus_position_woman:"unicode/1f9d8-2640.png?v8",loud_sound:"unicode/1f50a.png?v8",loudspeaker:"unicode/1f4e2.png?v8",love_hotel:"unicode/1f3e9.png?v8",love_letter:"unicode/1f48c.png?v8",love_you_gesture:"unicode/1f91f.png?v8",low_brightness:"unicode/1f505.png?v8",luggage:"unicode/1f9f3.png?v8",lungs:"unicode/1fac1.png?v8",luxembourg:"unicode/1f1f1-1f1fa.png?v8",lying_face:"unicode/1f925.png?v8",m:"unicode/24c2.png?v8",macau:"unicode/1f1f2-1f1f4.png?v8",macedonia:"unicode/1f1f2-1f1f0.png?v8",madagascar:"unicode/1f1f2-1f1ec.png?v8",mag:"unicode/1f50d.png?v8",mag_right:"unicode/1f50e.png?v8",mage:"unicode/1f9d9.png?v8",mage_man:"unicode/1f9d9-2642.png?v8",mage_woman:"unicode/1f9d9-2640.png?v8",magic_wand:"unicode/1fa84.png?v8",magnet:"unicode/1f9f2.png?v8",mahjong:"unicode/1f004.png?v8",mailbox:"unicode/1f4eb.png?v8",mailbox_closed:"unicode/1f4ea.png?v8",mailbox_with_mail:"unicode/1f4ec.png?v8",mailbox_with_no_mail:"unicode/1f4ed.png?v8",malawi:"unicode/1f1f2-1f1fc.png?v8",malaysia:"unicode/1f1f2-1f1fe.png?v8",maldives:"unicode/1f1f2-1f1fb.png?v8",male_detective:"unicode/1f575-2642.png?v8",male_sign:"unicode/2642.png?v8",mali:"unicode/1f1f2-1f1f1.png?v8",malta:"unicode/1f1f2-1f1f9.png?v8",mammoth:"unicode/1f9a3.png?v8",man:"unicode/1f468.png?v8",man_artist:"unicode/1f468-1f3a8.png?v8",man_astronaut:"unicode/1f468-1f680.png?v8",man_beard:"unicode/1f9d4-2642.png?v8",man_cartwheeling:"unicode/1f938-2642.png?v8",man_cook:"unicode/1f468-1f373.png?v8",man_dancing:"unicode/1f57a.png?v8",man_facepalming:"unicode/1f926-2642.png?v8",man_factory_worker:"unicode/1f468-1f3ed.png?v8",man_farmer:"unicode/1f468-1f33e.png?v8",man_feeding_baby:"unicode/1f468-1f37c.png?v8",man_firefighter:"unicode/1f468-1f692.png?v8",man_health_worker:"unicode/1f468-2695.png?v8",man_in_manual_wheelchair:"unicode/1f468-1f9bd.png?v8",man_in_motorized_wheelchair:"unicode/1f468-1f9bc.png?v8",man_in_tuxedo:"unicode/1f935-2642.png?v8",man_judge:"unicode/1f468-2696.png?v8",man_juggling:"unicode/1f939-2642.png?v8",man_mechanic:"unicode/1f468-1f527.png?v8",man_office_worker:"unicode/1f468-1f4bc.png?v8",man_pilot:"unicode/1f468-2708.png?v8",man_playing_handball:"unicode/1f93e-2642.png?v8",man_playing_water_polo:"unicode/1f93d-2642.png?v8",man_scientist:"unicode/1f468-1f52c.png?v8",man_shrugging:"unicode/1f937-2642.png?v8",man_singer:"unicode/1f468-1f3a4.png?v8",man_student:"unicode/1f468-1f393.png?v8",man_teacher:"unicode/1f468-1f3eb.png?v8",man_technologist:"unicode/1f468-1f4bb.png?v8",man_with_gua_pi_mao:"unicode/1f472.png?v8",man_with_probing_cane:"unicode/1f468-1f9af.png?v8",man_with_turban:"unicode/1f473-2642.png?v8",man_with_veil:"unicode/1f470-2642.png?v8",mandarin:"unicode/1f34a.png?v8",mango:"unicode/1f96d.png?v8",mans_shoe:"unicode/1f45e.png?v8",mantelpiece_clock:"unicode/1f570.png?v8",manual_wheelchair:"unicode/1f9bd.png?v8",maple_leaf:"unicode/1f341.png?v8",marshall_islands:"unicode/1f1f2-1f1ed.png?v8",martial_arts_uniform:"unicode/1f94b.png?v8",martinique:"unicode/1f1f2-1f1f6.png?v8",mask:"unicode/1f637.png?v8",massage:"unicode/1f486.png?v8",massage_man:"unicode/1f486-2642.png?v8",massage_woman:"unicode/1f486-2640.png?v8",mate:"unicode/1f9c9.png?v8",mauritania:"unicode/1f1f2-1f1f7.png?v8",mauritius:"unicode/1f1f2-1f1fa.png?v8",mayotte:"unicode/1f1fe-1f1f9.png?v8",meat_on_bone:"unicode/1f356.png?v8",mechanic:"unicode/1f9d1-1f527.png?v8",mechanical_arm:"unicode/1f9be.png?v8",mechanical_leg:"unicode/1f9bf.png?v8",medal_military:"unicode/1f396.png?v8",medal_sports:"unicode/1f3c5.png?v8",medical_symbol:"unicode/2695.png?v8",mega:"unicode/1f4e3.png?v8",melon:"unicode/1f348.png?v8",memo:"unicode/1f4dd.png?v8",men_wrestling:"unicode/1f93c-2642.png?v8",mending_heart:"unicode/2764-1fa79.png?v8",menorah:"unicode/1f54e.png?v8",mens:"unicode/1f6b9.png?v8",mermaid:"unicode/1f9dc-2640.png?v8",merman:"unicode/1f9dc-2642.png?v8",merperson:"unicode/1f9dc.png?v8",metal:"unicode/1f918.png?v8",metro:"unicode/1f687.png?v8",mexico:"unicode/1f1f2-1f1fd.png?v8",microbe:"unicode/1f9a0.png?v8",micronesia:"unicode/1f1eb-1f1f2.png?v8",microphone:"unicode/1f3a4.png?v8",microscope:"unicode/1f52c.png?v8",middle_finger:"unicode/1f595.png?v8",military_helmet:"unicode/1fa96.png?v8",milk_glass:"unicode/1f95b.png?v8",milky_way:"unicode/1f30c.png?v8",minibus:"unicode/1f690.png?v8",minidisc:"unicode/1f4bd.png?v8",mirror:"unicode/1fa9e.png?v8",mobile_phone_off:"unicode/1f4f4.png?v8",moldova:"unicode/1f1f2-1f1e9.png?v8",monaco:"unicode/1f1f2-1f1e8.png?v8",money_mouth_face:"unicode/1f911.png?v8",money_with_wings:"unicode/1f4b8.png?v8",moneybag:"unicode/1f4b0.png?v8",mongolia:"unicode/1f1f2-1f1f3.png?v8",monkey:"unicode/1f412.png?v8",monkey_face:"unicode/1f435.png?v8",monocle_face:"unicode/1f9d0.png?v8",monorail:"unicode/1f69d.png?v8",montenegro:"unicode/1f1f2-1f1ea.png?v8",montserrat:"unicode/1f1f2-1f1f8.png?v8",moon:"unicode/1f314.png?v8",moon_cake:"unicode/1f96e.png?v8",morocco:"unicode/1f1f2-1f1e6.png?v8",mortar_board:"unicode/1f393.png?v8",mosque:"unicode/1f54c.png?v8",mosquito:"unicode/1f99f.png?v8",motor_boat:"unicode/1f6e5.png?v8",motor_scooter:"unicode/1f6f5.png?v8",motorcycle:"unicode/1f3cd.png?v8",motorized_wheelchair:"unicode/1f9bc.png?v8",motorway:"unicode/1f6e3.png?v8",mount_fuji:"unicode/1f5fb.png?v8",mountain:"unicode/26f0.png?v8",mountain_bicyclist:"unicode/1f6b5.png?v8",mountain_biking_man:"unicode/1f6b5-2642.png?v8",mountain_biking_woman:"unicode/1f6b5-2640.png?v8",mountain_cableway:"unicode/1f6a0.png?v8",mountain_railway:"unicode/1f69e.png?v8",mountain_snow:"unicode/1f3d4.png?v8",mouse:"unicode/1f42d.png?v8",mouse2:"unicode/1f401.png?v8",mouse_trap:"unicode/1faa4.png?v8",movie_camera:"unicode/1f3a5.png?v8",moyai:"unicode/1f5ff.png?v8",mozambique:"unicode/1f1f2-1f1ff.png?v8",mrs_claus:"unicode/1f936.png?v8",muscle:"unicode/1f4aa.png?v8",mushroom:"unicode/1f344.png?v8",musical_keyboard:"unicode/1f3b9.png?v8",musical_note:"unicode/1f3b5.png?v8",musical_score:"unicode/1f3bc.png?v8",mute:"unicode/1f507.png?v8",mx_claus:"unicode/1f9d1-1f384.png?v8",myanmar:"unicode/1f1f2-1f1f2.png?v8",nail_care:"unicode/1f485.png?v8",name_badge:"unicode/1f4db.png?v8",namibia:"unicode/1f1f3-1f1e6.png?v8",national_park:"unicode/1f3de.png?v8",nauru:"unicode/1f1f3-1f1f7.png?v8",nauseated_face:"unicode/1f922.png?v8",nazar_amulet:"unicode/1f9ff.png?v8",neckbeard:"neckbeard.png?v8",necktie:"unicode/1f454.png?v8",negative_squared_cross_mark:"unicode/274e.png?v8",nepal:"unicode/1f1f3-1f1f5.png?v8",nerd_face:"unicode/1f913.png?v8",nesting_dolls:"unicode/1fa86.png?v8",netherlands:"unicode/1f1f3-1f1f1.png?v8",neutral_face:"unicode/1f610.png?v8",new:"unicode/1f195.png?v8",new_caledonia:"unicode/1f1f3-1f1e8.png?v8",new_moon:"unicode/1f311.png?v8",new_moon_with_face:"unicode/1f31a.png?v8",new_zealand:"unicode/1f1f3-1f1ff.png?v8",newspaper:"unicode/1f4f0.png?v8",newspaper_roll:"unicode/1f5de.png?v8",next_track_button:"unicode/23ed.png?v8",ng:"unicode/1f196.png?v8",ng_man:"unicode/1f645-2642.png?v8",ng_woman:"unicode/1f645-2640.png?v8",nicaragua:"unicode/1f1f3-1f1ee.png?v8",niger:"unicode/1f1f3-1f1ea.png?v8",nigeria:"unicode/1f1f3-1f1ec.png?v8",night_with_stars:"unicode/1f303.png?v8",nine:"unicode/0039-20e3.png?v8",ninja:"unicode/1f977.png?v8",niue:"unicode/1f1f3-1f1fa.png?v8",no_bell:"unicode/1f515.png?v8",no_bicycles:"unicode/1f6b3.png?v8",no_entry:"unicode/26d4.png?v8",no_entry_sign:"unicode/1f6ab.png?v8",no_good:"unicode/1f645.png?v8",no_good_man:"unicode/1f645-2642.png?v8",no_good_woman:"unicode/1f645-2640.png?v8",no_mobile_phones:"unicode/1f4f5.png?v8",no_mouth:"unicode/1f636.png?v8",no_pedestrians:"unicode/1f6b7.png?v8",no_smoking:"unicode/1f6ad.png?v8","non-potable_water":"unicode/1f6b1.png?v8",norfolk_island:"unicode/1f1f3-1f1eb.png?v8",north_korea:"unicode/1f1f0-1f1f5.png?v8",northern_mariana_islands:"unicode/1f1f2-1f1f5.png?v8",norway:"unicode/1f1f3-1f1f4.png?v8",nose:"unicode/1f443.png?v8",notebook:"unicode/1f4d3.png?v8",notebook_with_decorative_cover:"unicode/1f4d4.png?v8",notes:"unicode/1f3b6.png?v8",nut_and_bolt:"unicode/1f529.png?v8",o:"unicode/2b55.png?v8",o2:"unicode/1f17e.png?v8",ocean:"unicode/1f30a.png?v8",octocat:"octocat.png?v8",octopus:"unicode/1f419.png?v8",oden:"unicode/1f362.png?v8",office:"unicode/1f3e2.png?v8",office_worker:"unicode/1f9d1-1f4bc.png?v8",oil_drum:"unicode/1f6e2.png?v8",ok:"unicode/1f197.png?v8",ok_hand:"unicode/1f44c.png?v8",ok_man:"unicode/1f646-2642.png?v8",ok_person:"unicode/1f646.png?v8",ok_woman:"unicode/1f646-2640.png?v8",old_key:"unicode/1f5dd.png?v8",older_adult:"unicode/1f9d3.png?v8",older_man:"unicode/1f474.png?v8",older_woman:"unicode/1f475.png?v8",olive:"unicode/1fad2.png?v8",om:"unicode/1f549.png?v8",oman:"unicode/1f1f4-1f1f2.png?v8",on:"unicode/1f51b.png?v8",oncoming_automobile:"unicode/1f698.png?v8",oncoming_bus:"unicode/1f68d.png?v8",oncoming_police_car:"unicode/1f694.png?v8",oncoming_taxi:"unicode/1f696.png?v8",one:"unicode/0031-20e3.png?v8",one_piece_swimsuit:"unicode/1fa71.png?v8",onion:"unicode/1f9c5.png?v8",open_book:"unicode/1f4d6.png?v8",open_file_folder:"unicode/1f4c2.png?v8",open_hands:"unicode/1f450.png?v8",open_mouth:"unicode/1f62e.png?v8",open_umbrella:"unicode/2602.png?v8",ophiuchus:"unicode/26ce.png?v8",orange:"unicode/1f34a.png?v8",orange_book:"unicode/1f4d9.png?v8",orange_circle:"unicode/1f7e0.png?v8",orange_heart:"unicode/1f9e1.png?v8",orange_square:"unicode/1f7e7.png?v8",orangutan:"unicode/1f9a7.png?v8",orthodox_cross:"unicode/2626.png?v8",otter:"unicode/1f9a6.png?v8",outbox_tray:"unicode/1f4e4.png?v8",owl:"unicode/1f989.png?v8",ox:"unicode/1f402.png?v8",oyster:"unicode/1f9aa.png?v8",package:"unicode/1f4e6.png?v8",page_facing_up:"unicode/1f4c4.png?v8",page_with_curl:"unicode/1f4c3.png?v8",pager:"unicode/1f4df.png?v8",paintbrush:"unicode/1f58c.png?v8",pakistan:"unicode/1f1f5-1f1f0.png?v8",palau:"unicode/1f1f5-1f1fc.png?v8",palestinian_territories:"unicode/1f1f5-1f1f8.png?v8",palm_tree:"unicode/1f334.png?v8",palms_up_together:"unicode/1f932.png?v8",panama:"unicode/1f1f5-1f1e6.png?v8",pancakes:"unicode/1f95e.png?v8",panda_face:"unicode/1f43c.png?v8",paperclip:"unicode/1f4ce.png?v8",paperclips:"unicode/1f587.png?v8",papua_new_guinea:"unicode/1f1f5-1f1ec.png?v8",parachute:"unicode/1fa82.png?v8",paraguay:"unicode/1f1f5-1f1fe.png?v8",parasol_on_ground:"unicode/26f1.png?v8",parking:"unicode/1f17f.png?v8",parrot:"unicode/1f99c.png?v8",part_alternation_mark:"unicode/303d.png?v8",partly_sunny:"unicode/26c5.png?v8",partying_face:"unicode/1f973.png?v8",passenger_ship:"unicode/1f6f3.png?v8",passport_control:"unicode/1f6c2.png?v8",pause_button:"unicode/23f8.png?v8",paw_prints:"unicode/1f43e.png?v8",peace_symbol:"unicode/262e.png?v8",peach:"unicode/1f351.png?v8",peacock:"unicode/1f99a.png?v8",peanuts:"unicode/1f95c.png?v8",pear:"unicode/1f350.png?v8",pen:"unicode/1f58a.png?v8",pencil:"unicode/1f4dd.png?v8",pencil2:"unicode/270f.png?v8",penguin:"unicode/1f427.png?v8",pensive:"unicode/1f614.png?v8",people_holding_hands:"unicode/1f9d1-1f91d-1f9d1.png?v8",people_hugging:"unicode/1fac2.png?v8",performing_arts:"unicode/1f3ad.png?v8",persevere:"unicode/1f623.png?v8",person_bald:"unicode/1f9d1-1f9b2.png?v8",person_curly_hair:"unicode/1f9d1-1f9b1.png?v8",person_feeding_baby:"unicode/1f9d1-1f37c.png?v8",person_fencing:"unicode/1f93a.png?v8",person_in_manual_wheelchair:"unicode/1f9d1-1f9bd.png?v8",person_in_motorized_wheelchair:"unicode/1f9d1-1f9bc.png?v8",person_in_tuxedo:"unicode/1f935.png?v8",person_red_hair:"unicode/1f9d1-1f9b0.png?v8",person_white_hair:"unicode/1f9d1-1f9b3.png?v8",person_with_probing_cane:"unicode/1f9d1-1f9af.png?v8",person_with_turban:"unicode/1f473.png?v8",person_with_veil:"unicode/1f470.png?v8",peru:"unicode/1f1f5-1f1ea.png?v8",petri_dish:"unicode/1f9eb.png?v8",philippines:"unicode/1f1f5-1f1ed.png?v8",phone:"unicode/260e.png?v8",pick:"unicode/26cf.png?v8",pickup_truck:"unicode/1f6fb.png?v8",pie:"unicode/1f967.png?v8",pig:"unicode/1f437.png?v8",pig2:"unicode/1f416.png?v8",pig_nose:"unicode/1f43d.png?v8",pill:"unicode/1f48a.png?v8",pilot:"unicode/1f9d1-2708.png?v8",pinata:"unicode/1fa85.png?v8",pinched_fingers:"unicode/1f90c.png?v8",pinching_hand:"unicode/1f90f.png?v8",pineapple:"unicode/1f34d.png?v8",ping_pong:"unicode/1f3d3.png?v8",pirate_flag:"unicode/1f3f4-2620.png?v8",pisces:"unicode/2653.png?v8",pitcairn_islands:"unicode/1f1f5-1f1f3.png?v8",pizza:"unicode/1f355.png?v8",placard:"unicode/1faa7.png?v8",place_of_worship:"unicode/1f6d0.png?v8",plate_with_cutlery:"unicode/1f37d.png?v8",play_or_pause_button:"unicode/23ef.png?v8",pleading_face:"unicode/1f97a.png?v8",plunger:"unicode/1faa0.png?v8",point_down:"unicode/1f447.png?v8",point_left:"unicode/1f448.png?v8",point_right:"unicode/1f449.png?v8",point_up:"unicode/261d.png?v8",point_up_2:"unicode/1f446.png?v8",poland:"unicode/1f1f5-1f1f1.png?v8",polar_bear:"unicode/1f43b-2744.png?v8",police_car:"unicode/1f693.png?v8",police_officer:"unicode/1f46e.png?v8",policeman:"unicode/1f46e-2642.png?v8",policewoman:"unicode/1f46e-2640.png?v8",poodle:"unicode/1f429.png?v8",poop:"unicode/1f4a9.png?v8",popcorn:"unicode/1f37f.png?v8",portugal:"unicode/1f1f5-1f1f9.png?v8",post_office:"unicode/1f3e3.png?v8",postal_horn:"unicode/1f4ef.png?v8",postbox:"unicode/1f4ee.png?v8",potable_water:"unicode/1f6b0.png?v8",potato:"unicode/1f954.png?v8",potted_plant:"unicode/1fab4.png?v8",pouch:"unicode/1f45d.png?v8",poultry_leg:"unicode/1f357.png?v8",pound:"unicode/1f4b7.png?v8",pout:"unicode/1f621.png?v8",pouting_cat:"unicode/1f63e.png?v8",pouting_face:"unicode/1f64e.png?v8",pouting_man:"unicode/1f64e-2642.png?v8",pouting_woman:"unicode/1f64e-2640.png?v8",pray:"unicode/1f64f.png?v8",prayer_beads:"unicode/1f4ff.png?v8",pregnant_woman:"unicode/1f930.png?v8",pretzel:"unicode/1f968.png?v8",previous_track_button:"unicode/23ee.png?v8",prince:"unicode/1f934.png?v8",princess:"unicode/1f478.png?v8",printer:"unicode/1f5a8.png?v8",probing_cane:"unicode/1f9af.png?v8",puerto_rico:"unicode/1f1f5-1f1f7.png?v8",punch:"unicode/1f44a.png?v8",purple_circle:"unicode/1f7e3.png?v8",purple_heart:"unicode/1f49c.png?v8",purple_square:"unicode/1f7ea.png?v8",purse:"unicode/1f45b.png?v8",pushpin:"unicode/1f4cc.png?v8",put_litter_in_its_place:"unicode/1f6ae.png?v8",qatar:"unicode/1f1f6-1f1e6.png?v8",question:"unicode/2753.png?v8",rabbit:"unicode/1f430.png?v8",rabbit2:"unicode/1f407.png?v8",raccoon:"unicode/1f99d.png?v8",racehorse:"unicode/1f40e.png?v8",racing_car:"unicode/1f3ce.png?v8",radio:"unicode/1f4fb.png?v8",radio_button:"unicode/1f518.png?v8",radioactive:"unicode/2622.png?v8",rage:"unicode/1f621.png?v8",rage1:"rage1.png?v8",rage2:"rage2.png?v8",rage3:"rage3.png?v8",rage4:"rage4.png?v8",railway_car:"unicode/1f683.png?v8",railway_track:"unicode/1f6e4.png?v8",rainbow:"unicode/1f308.png?v8",rainbow_flag:"unicode/1f3f3-1f308.png?v8",raised_back_of_hand:"unicode/1f91a.png?v8",raised_eyebrow:"unicode/1f928.png?v8",raised_hand:"unicode/270b.png?v8",raised_hand_with_fingers_splayed:"unicode/1f590.png?v8",raised_hands:"unicode/1f64c.png?v8",raising_hand:"unicode/1f64b.png?v8",raising_hand_man:"unicode/1f64b-2642.png?v8",raising_hand_woman:"unicode/1f64b-2640.png?v8",ram:"unicode/1f40f.png?v8",ramen:"unicode/1f35c.png?v8",rat:"unicode/1f400.png?v8",razor:"unicode/1fa92.png?v8",receipt:"unicode/1f9fe.png?v8",record_button:"unicode/23fa.png?v8",recycle:"unicode/267b.png?v8",red_car:"unicode/1f697.png?v8",red_circle:"unicode/1f534.png?v8",red_envelope:"unicode/1f9e7.png?v8",red_haired_man:"unicode/1f468-1f9b0.png?v8",red_haired_woman:"unicode/1f469-1f9b0.png?v8",red_square:"unicode/1f7e5.png?v8",registered:"unicode/00ae.png?v8",relaxed:"unicode/263a.png?v8",relieved:"unicode/1f60c.png?v8",reminder_ribbon:"unicode/1f397.png?v8",repeat:"unicode/1f501.png?v8",repeat_one:"unicode/1f502.png?v8",rescue_worker_helmet:"unicode/26d1.png?v8",restroom:"unicode/1f6bb.png?v8",reunion:"unicode/1f1f7-1f1ea.png?v8",revolving_hearts:"unicode/1f49e.png?v8",rewind:"unicode/23ea.png?v8",rhinoceros:"unicode/1f98f.png?v8",ribbon:"unicode/1f380.png?v8",rice:"unicode/1f35a.png?v8",rice_ball:"unicode/1f359.png?v8",rice_cracker:"unicode/1f358.png?v8",rice_scene:"unicode/1f391.png?v8",right_anger_bubble:"unicode/1f5ef.png?v8",ring:"unicode/1f48d.png?v8",ringed_planet:"unicode/1fa90.png?v8",robot:"unicode/1f916.png?v8",rock:"unicode/1faa8.png?v8",rocket:"unicode/1f680.png?v8",rofl:"unicode/1f923.png?v8",roll_eyes:"unicode/1f644.png?v8",roll_of_paper:"unicode/1f9fb.png?v8",roller_coaster:"unicode/1f3a2.png?v8",roller_skate:"unicode/1f6fc.png?v8",romania:"unicode/1f1f7-1f1f4.png?v8",rooster:"unicode/1f413.png?v8",rose:"unicode/1f339.png?v8",rosette:"unicode/1f3f5.png?v8",rotating_light:"unicode/1f6a8.png?v8",round_pushpin:"unicode/1f4cd.png?v8",rowboat:"unicode/1f6a3.png?v8",rowing_man:"unicode/1f6a3-2642.png?v8",rowing_woman:"unicode/1f6a3-2640.png?v8",ru:"unicode/1f1f7-1f1fa.png?v8",rugby_football:"unicode/1f3c9.png?v8",runner:"unicode/1f3c3.png?v8",running:"unicode/1f3c3.png?v8",running_man:"unicode/1f3c3-2642.png?v8",running_shirt_with_sash:"unicode/1f3bd.png?v8",running_woman:"unicode/1f3c3-2640.png?v8",rwanda:"unicode/1f1f7-1f1fc.png?v8",sa:"unicode/1f202.png?v8",safety_pin:"unicode/1f9f7.png?v8",safety_vest:"unicode/1f9ba.png?v8",sagittarius:"unicode/2650.png?v8",sailboat:"unicode/26f5.png?v8",sake:"unicode/1f376.png?v8",salt:"unicode/1f9c2.png?v8",samoa:"unicode/1f1fc-1f1f8.png?v8",san_marino:"unicode/1f1f8-1f1f2.png?v8",sandal:"unicode/1f461.png?v8",sandwich:"unicode/1f96a.png?v8",santa:"unicode/1f385.png?v8",sao_tome_principe:"unicode/1f1f8-1f1f9.png?v8",sari:"unicode/1f97b.png?v8",sassy_man:"unicode/1f481-2642.png?v8",sassy_woman:"unicode/1f481-2640.png?v8",satellite:"unicode/1f4e1.png?v8",satisfied:"unicode/1f606.png?v8",saudi_arabia:"unicode/1f1f8-1f1e6.png?v8",sauna_man:"unicode/1f9d6-2642.png?v8",sauna_person:"unicode/1f9d6.png?v8",sauna_woman:"unicode/1f9d6-2640.png?v8",sauropod:"unicode/1f995.png?v8",saxophone:"unicode/1f3b7.png?v8",scarf:"unicode/1f9e3.png?v8",school:"unicode/1f3eb.png?v8",school_satchel:"unicode/1f392.png?v8",scientist:"unicode/1f9d1-1f52c.png?v8",scissors:"unicode/2702.png?v8",scorpion:"unicode/1f982.png?v8",scorpius:"unicode/264f.png?v8",scotland:"unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png?v8",scream:"unicode/1f631.png?v8",scream_cat:"unicode/1f640.png?v8",screwdriver:"unicode/1fa9b.png?v8",scroll:"unicode/1f4dc.png?v8",seal:"unicode/1f9ad.png?v8",seat:"unicode/1f4ba.png?v8",secret:"unicode/3299.png?v8",see_no_evil:"unicode/1f648.png?v8",seedling:"unicode/1f331.png?v8",selfie:"unicode/1f933.png?v8",senegal:"unicode/1f1f8-1f1f3.png?v8",serbia:"unicode/1f1f7-1f1f8.png?v8",service_dog:"unicode/1f415-1f9ba.png?v8",seven:"unicode/0037-20e3.png?v8",sewing_needle:"unicode/1faa1.png?v8",seychelles:"unicode/1f1f8-1f1e8.png?v8",shallow_pan_of_food:"unicode/1f958.png?v8",shamrock:"unicode/2618.png?v8",shark:"unicode/1f988.png?v8",shaved_ice:"unicode/1f367.png?v8",sheep:"unicode/1f411.png?v8",shell:"unicode/1f41a.png?v8",shield:"unicode/1f6e1.png?v8",shinto_shrine:"unicode/26e9.png?v8",ship:"unicode/1f6a2.png?v8",shipit:"shipit.png?v8",shirt:"unicode/1f455.png?v8",shit:"unicode/1f4a9.png?v8",shoe:"unicode/1f45e.png?v8",shopping:"unicode/1f6cd.png?v8",shopping_cart:"unicode/1f6d2.png?v8",shorts:"unicode/1fa73.png?v8",shower:"unicode/1f6bf.png?v8",shrimp:"unicode/1f990.png?v8",shrug:"unicode/1f937.png?v8",shushing_face:"unicode/1f92b.png?v8",sierra_leone:"unicode/1f1f8-1f1f1.png?v8",signal_strength:"unicode/1f4f6.png?v8",singapore:"unicode/1f1f8-1f1ec.png?v8",singer:"unicode/1f9d1-1f3a4.png?v8",sint_maarten:"unicode/1f1f8-1f1fd.png?v8",six:"unicode/0036-20e3.png?v8",six_pointed_star:"unicode/1f52f.png?v8",skateboard:"unicode/1f6f9.png?v8",ski:"unicode/1f3bf.png?v8",skier:"unicode/26f7.png?v8",skull:"unicode/1f480.png?v8",skull_and_crossbones:"unicode/2620.png?v8",skunk:"unicode/1f9a8.png?v8",sled:"unicode/1f6f7.png?v8",sleeping:"unicode/1f634.png?v8",sleeping_bed:"unicode/1f6cc.png?v8",sleepy:"unicode/1f62a.png?v8",slightly_frowning_face:"unicode/1f641.png?v8",slightly_smiling_face:"unicode/1f642.png?v8",slot_machine:"unicode/1f3b0.png?v8",sloth:"unicode/1f9a5.png?v8",slovakia:"unicode/1f1f8-1f1f0.png?v8",slovenia:"unicode/1f1f8-1f1ee.png?v8",small_airplane:"unicode/1f6e9.png?v8",small_blue_diamond:"unicode/1f539.png?v8",small_orange_diamond:"unicode/1f538.png?v8",small_red_triangle:"unicode/1f53a.png?v8",small_red_triangle_down:"unicode/1f53b.png?v8",smile:"unicode/1f604.png?v8",smile_cat:"unicode/1f638.png?v8",smiley:"unicode/1f603.png?v8",smiley_cat:"unicode/1f63a.png?v8",smiling_face_with_tear:"unicode/1f972.png?v8",smiling_face_with_three_hearts:"unicode/1f970.png?v8",smiling_imp:"unicode/1f608.png?v8",smirk:"unicode/1f60f.png?v8",smirk_cat:"unicode/1f63c.png?v8",smoking:"unicode/1f6ac.png?v8",snail:"unicode/1f40c.png?v8",snake:"unicode/1f40d.png?v8",sneezing_face:"unicode/1f927.png?v8",snowboarder:"unicode/1f3c2.png?v8",snowflake:"unicode/2744.png?v8",snowman:"unicode/26c4.png?v8",snowman_with_snow:"unicode/2603.png?v8",soap:"unicode/1f9fc.png?v8",sob:"unicode/1f62d.png?v8",soccer:"unicode/26bd.png?v8",socks:"unicode/1f9e6.png?v8",softball:"unicode/1f94e.png?v8",solomon_islands:"unicode/1f1f8-1f1e7.png?v8",somalia:"unicode/1f1f8-1f1f4.png?v8",soon:"unicode/1f51c.png?v8",sos:"unicode/1f198.png?v8",sound:"unicode/1f509.png?v8",south_africa:"unicode/1f1ff-1f1e6.png?v8",south_georgia_south_sandwich_islands:"unicode/1f1ec-1f1f8.png?v8",south_sudan:"unicode/1f1f8-1f1f8.png?v8",space_invader:"unicode/1f47e.png?v8",spades:"unicode/2660.png?v8",spaghetti:"unicode/1f35d.png?v8",sparkle:"unicode/2747.png?v8",sparkler:"unicode/1f387.png?v8",sparkles:"unicode/2728.png?v8",sparkling_heart:"unicode/1f496.png?v8",speak_no_evil:"unicode/1f64a.png?v8",speaker:"unicode/1f508.png?v8",speaking_head:"unicode/1f5e3.png?v8",speech_balloon:"unicode/1f4ac.png?v8",speedboat:"unicode/1f6a4.png?v8",spider:"unicode/1f577.png?v8",spider_web:"unicode/1f578.png?v8",spiral_calendar:"unicode/1f5d3.png?v8",spiral_notepad:"unicode/1f5d2.png?v8",sponge:"unicode/1f9fd.png?v8",spoon:"unicode/1f944.png?v8",squid:"unicode/1f991.png?v8",sri_lanka:"unicode/1f1f1-1f1f0.png?v8",st_barthelemy:"unicode/1f1e7-1f1f1.png?v8",st_helena:"unicode/1f1f8-1f1ed.png?v8",st_kitts_nevis:"unicode/1f1f0-1f1f3.png?v8",st_lucia:"unicode/1f1f1-1f1e8.png?v8",st_martin:"unicode/1f1f2-1f1eb.png?v8",st_pierre_miquelon:"unicode/1f1f5-1f1f2.png?v8",st_vincent_grenadines:"unicode/1f1fb-1f1e8.png?v8",stadium:"unicode/1f3df.png?v8",standing_man:"unicode/1f9cd-2642.png?v8",standing_person:"unicode/1f9cd.png?v8",standing_woman:"unicode/1f9cd-2640.png?v8",star:"unicode/2b50.png?v8",star2:"unicode/1f31f.png?v8",star_and_crescent:"unicode/262a.png?v8",star_of_david:"unicode/2721.png?v8",star_struck:"unicode/1f929.png?v8",stars:"unicode/1f320.png?v8",station:"unicode/1f689.png?v8",statue_of_liberty:"unicode/1f5fd.png?v8",steam_locomotive:"unicode/1f682.png?v8",stethoscope:"unicode/1fa7a.png?v8",stew:"unicode/1f372.png?v8",stop_button:"unicode/23f9.png?v8",stop_sign:"unicode/1f6d1.png?v8",stopwatch:"unicode/23f1.png?v8",straight_ruler:"unicode/1f4cf.png?v8",strawberry:"unicode/1f353.png?v8",stuck_out_tongue:"unicode/1f61b.png?v8",stuck_out_tongue_closed_eyes:"unicode/1f61d.png?v8",stuck_out_tongue_winking_eye:"unicode/1f61c.png?v8",student:"unicode/1f9d1-1f393.png?v8",studio_microphone:"unicode/1f399.png?v8",stuffed_flatbread:"unicode/1f959.png?v8",sudan:"unicode/1f1f8-1f1e9.png?v8",sun_behind_large_cloud:"unicode/1f325.png?v8",sun_behind_rain_cloud:"unicode/1f326.png?v8",sun_behind_small_cloud:"unicode/1f324.png?v8",sun_with_face:"unicode/1f31e.png?v8",sunflower:"unicode/1f33b.png?v8",sunglasses:"unicode/1f60e.png?v8",sunny:"unicode/2600.png?v8",sunrise:"unicode/1f305.png?v8",sunrise_over_mountains:"unicode/1f304.png?v8",superhero:"unicode/1f9b8.png?v8",superhero_man:"unicode/1f9b8-2642.png?v8",superhero_woman:"unicode/1f9b8-2640.png?v8",supervillain:"unicode/1f9b9.png?v8",supervillain_man:"unicode/1f9b9-2642.png?v8",supervillain_woman:"unicode/1f9b9-2640.png?v8",surfer:"unicode/1f3c4.png?v8",surfing_man:"unicode/1f3c4-2642.png?v8",surfing_woman:"unicode/1f3c4-2640.png?v8",suriname:"unicode/1f1f8-1f1f7.png?v8",sushi:"unicode/1f363.png?v8",suspect:"suspect.png?v8",suspension_railway:"unicode/1f69f.png?v8",svalbard_jan_mayen:"unicode/1f1f8-1f1ef.png?v8",swan:"unicode/1f9a2.png?v8",swaziland:"unicode/1f1f8-1f1ff.png?v8",sweat:"unicode/1f613.png?v8",sweat_drops:"unicode/1f4a6.png?v8",sweat_smile:"unicode/1f605.png?v8",sweden:"unicode/1f1f8-1f1ea.png?v8",sweet_potato:"unicode/1f360.png?v8",swim_brief:"unicode/1fa72.png?v8",swimmer:"unicode/1f3ca.png?v8",swimming_man:"unicode/1f3ca-2642.png?v8",swimming_woman:"unicode/1f3ca-2640.png?v8",switzerland:"unicode/1f1e8-1f1ed.png?v8",symbols:"unicode/1f523.png?v8",synagogue:"unicode/1f54d.png?v8",syria:"unicode/1f1f8-1f1fe.png?v8",syringe:"unicode/1f489.png?v8","t-rex":"unicode/1f996.png?v8",taco:"unicode/1f32e.png?v8",tada:"unicode/1f389.png?v8",taiwan:"unicode/1f1f9-1f1fc.png?v8",tajikistan:"unicode/1f1f9-1f1ef.png?v8",takeout_box:"unicode/1f961.png?v8",tamale:"unicode/1fad4.png?v8",tanabata_tree:"unicode/1f38b.png?v8",tangerine:"unicode/1f34a.png?v8",tanzania:"unicode/1f1f9-1f1ff.png?v8",taurus:"unicode/2649.png?v8",taxi:"unicode/1f695.png?v8",tea:"unicode/1f375.png?v8",teacher:"unicode/1f9d1-1f3eb.png?v8",teapot:"unicode/1fad6.png?v8",technologist:"unicode/1f9d1-1f4bb.png?v8",teddy_bear:"unicode/1f9f8.png?v8",telephone:"unicode/260e.png?v8",telephone_receiver:"unicode/1f4de.png?v8",telescope:"unicode/1f52d.png?v8",tennis:"unicode/1f3be.png?v8",tent:"unicode/26fa.png?v8",test_tube:"unicode/1f9ea.png?v8",thailand:"unicode/1f1f9-1f1ed.png?v8",thermometer:"unicode/1f321.png?v8",thinking:"unicode/1f914.png?v8",thong_sandal:"unicode/1fa74.png?v8",thought_balloon:"unicode/1f4ad.png?v8",thread:"unicode/1f9f5.png?v8",three:"unicode/0033-20e3.png?v8",thumbsdown:"unicode/1f44e.png?v8",thumbsup:"unicode/1f44d.png?v8",ticket:"unicode/1f3ab.png?v8",tickets:"unicode/1f39f.png?v8",tiger:"unicode/1f42f.png?v8",tiger2:"unicode/1f405.png?v8",timer_clock:"unicode/23f2.png?v8",timor_leste:"unicode/1f1f9-1f1f1.png?v8",tipping_hand_man:"unicode/1f481-2642.png?v8",tipping_hand_person:"unicode/1f481.png?v8",tipping_hand_woman:"unicode/1f481-2640.png?v8",tired_face:"unicode/1f62b.png?v8",tm:"unicode/2122.png?v8",togo:"unicode/1f1f9-1f1ec.png?v8",toilet:"unicode/1f6bd.png?v8",tokelau:"unicode/1f1f9-1f1f0.png?v8",tokyo_tower:"unicode/1f5fc.png?v8",tomato:"unicode/1f345.png?v8",tonga:"unicode/1f1f9-1f1f4.png?v8",tongue:"unicode/1f445.png?v8",toolbox:"unicode/1f9f0.png?v8",tooth:"unicode/1f9b7.png?v8",toothbrush:"unicode/1faa5.png?v8",top:"unicode/1f51d.png?v8",tophat:"unicode/1f3a9.png?v8",tornado:"unicode/1f32a.png?v8",tr:"unicode/1f1f9-1f1f7.png?v8",trackball:"unicode/1f5b2.png?v8",tractor:"unicode/1f69c.png?v8",traffic_light:"unicode/1f6a5.png?v8",train:"unicode/1f68b.png?v8",train2:"unicode/1f686.png?v8",tram:"unicode/1f68a.png?v8",transgender_flag:"unicode/1f3f3-26a7.png?v8",transgender_symbol:"unicode/26a7.png?v8",triangular_flag_on_post:"unicode/1f6a9.png?v8",triangular_ruler:"unicode/1f4d0.png?v8",trident:"unicode/1f531.png?v8",trinidad_tobago:"unicode/1f1f9-1f1f9.png?v8",tristan_da_cunha:"unicode/1f1f9-1f1e6.png?v8",triumph:"unicode/1f624.png?v8",trolleybus:"unicode/1f68e.png?v8",trollface:"trollface.png?v8",trophy:"unicode/1f3c6.png?v8",tropical_drink:"unicode/1f379.png?v8",tropical_fish:"unicode/1f420.png?v8",truck:"unicode/1f69a.png?v8",trumpet:"unicode/1f3ba.png?v8",tshirt:"unicode/1f455.png?v8",tulip:"unicode/1f337.png?v8",tumbler_glass:"unicode/1f943.png?v8",tunisia:"unicode/1f1f9-1f1f3.png?v8",turkey:"unicode/1f983.png?v8",turkmenistan:"unicode/1f1f9-1f1f2.png?v8",turks_caicos_islands:"unicode/1f1f9-1f1e8.png?v8",turtle:"unicode/1f422.png?v8",tuvalu:"unicode/1f1f9-1f1fb.png?v8",tv:"unicode/1f4fa.png?v8",twisted_rightwards_arrows:"unicode/1f500.png?v8",two:"unicode/0032-20e3.png?v8",two_hearts:"unicode/1f495.png?v8",two_men_holding_hands:"unicode/1f46c.png?v8",two_women_holding_hands:"unicode/1f46d.png?v8",u5272:"unicode/1f239.png?v8",u5408:"unicode/1f234.png?v8",u55b6:"unicode/1f23a.png?v8",u6307:"unicode/1f22f.png?v8",u6708:"unicode/1f237.png?v8",u6709:"unicode/1f236.png?v8",u6e80:"unicode/1f235.png?v8",u7121:"unicode/1f21a.png?v8",u7533:"unicode/1f238.png?v8",u7981:"unicode/1f232.png?v8",u7a7a:"unicode/1f233.png?v8",uganda:"unicode/1f1fa-1f1ec.png?v8",uk:"unicode/1f1ec-1f1e7.png?v8",ukraine:"unicode/1f1fa-1f1e6.png?v8",umbrella:"unicode/2614.png?v8",unamused:"unicode/1f612.png?v8",underage:"unicode/1f51e.png?v8",unicorn:"unicode/1f984.png?v8",united_arab_emirates:"unicode/1f1e6-1f1ea.png?v8",united_nations:"unicode/1f1fa-1f1f3.png?v8",unlock:"unicode/1f513.png?v8",up:"unicode/1f199.png?v8",upside_down_face:"unicode/1f643.png?v8",uruguay:"unicode/1f1fa-1f1fe.png?v8",us:"unicode/1f1fa-1f1f8.png?v8",us_outlying_islands:"unicode/1f1fa-1f1f2.png?v8",us_virgin_islands:"unicode/1f1fb-1f1ee.png?v8",uzbekistan:"unicode/1f1fa-1f1ff.png?v8",v:"unicode/270c.png?v8",vampire:"unicode/1f9db.png?v8",vampire_man:"unicode/1f9db-2642.png?v8",vampire_woman:"unicode/1f9db-2640.png?v8",vanuatu:"unicode/1f1fb-1f1fa.png?v8",vatican_city:"unicode/1f1fb-1f1e6.png?v8",venezuela:"unicode/1f1fb-1f1ea.png?v8",vertical_traffic_light:"unicode/1f6a6.png?v8",vhs:"unicode/1f4fc.png?v8",vibration_mode:"unicode/1f4f3.png?v8",video_camera:"unicode/1f4f9.png?v8",video_game:"unicode/1f3ae.png?v8",vietnam:"unicode/1f1fb-1f1f3.png?v8",violin:"unicode/1f3bb.png?v8",virgo:"unicode/264d.png?v8",volcano:"unicode/1f30b.png?v8",volleyball:"unicode/1f3d0.png?v8",vomiting_face:"unicode/1f92e.png?v8",vs:"unicode/1f19a.png?v8",vulcan_salute:"unicode/1f596.png?v8",waffle:"unicode/1f9c7.png?v8",wales:"unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png?v8",walking:"unicode/1f6b6.png?v8",walking_man:"unicode/1f6b6-2642.png?v8",walking_woman:"unicode/1f6b6-2640.png?v8",wallis_futuna:"unicode/1f1fc-1f1eb.png?v8",waning_crescent_moon:"unicode/1f318.png?v8",waning_gibbous_moon:"unicode/1f316.png?v8",warning:"unicode/26a0.png?v8",wastebasket:"unicode/1f5d1.png?v8",watch:"unicode/231a.png?v8",water_buffalo:"unicode/1f403.png?v8",water_polo:"unicode/1f93d.png?v8",watermelon:"unicode/1f349.png?v8",wave:"unicode/1f44b.png?v8",wavy_dash:"unicode/3030.png?v8",waxing_crescent_moon:"unicode/1f312.png?v8",waxing_gibbous_moon:"unicode/1f314.png?v8",wc:"unicode/1f6be.png?v8",weary:"unicode/1f629.png?v8",wedding:"unicode/1f492.png?v8",weight_lifting:"unicode/1f3cb.png?v8",weight_lifting_man:"unicode/1f3cb-2642.png?v8",weight_lifting_woman:"unicode/1f3cb-2640.png?v8",western_sahara:"unicode/1f1ea-1f1ed.png?v8",whale:"unicode/1f433.png?v8",whale2:"unicode/1f40b.png?v8",wheel_of_dharma:"unicode/2638.png?v8",wheelchair:"unicode/267f.png?v8",white_check_mark:"unicode/2705.png?v8",white_circle:"unicode/26aa.png?v8",white_flag:"unicode/1f3f3.png?v8",white_flower:"unicode/1f4ae.png?v8",white_haired_man:"unicode/1f468-1f9b3.png?v8",white_haired_woman:"unicode/1f469-1f9b3.png?v8",white_heart:"unicode/1f90d.png?v8",white_large_square:"unicode/2b1c.png?v8",white_medium_small_square:"unicode/25fd.png?v8",white_medium_square:"unicode/25fb.png?v8",white_small_square:"unicode/25ab.png?v8",white_square_button:"unicode/1f533.png?v8",wilted_flower:"unicode/1f940.png?v8",wind_chime:"unicode/1f390.png?v8",wind_face:"unicode/1f32c.png?v8",window:"unicode/1fa9f.png?v8",wine_glass:"unicode/1f377.png?v8",wink:"unicode/1f609.png?v8",wolf:"unicode/1f43a.png?v8",woman:"unicode/1f469.png?v8",woman_artist:"unicode/1f469-1f3a8.png?v8",woman_astronaut:"unicode/1f469-1f680.png?v8",woman_beard:"unicode/1f9d4-2640.png?v8",woman_cartwheeling:"unicode/1f938-2640.png?v8",woman_cook:"unicode/1f469-1f373.png?v8",woman_dancing:"unicode/1f483.png?v8",woman_facepalming:"unicode/1f926-2640.png?v8",woman_factory_worker:"unicode/1f469-1f3ed.png?v8",woman_farmer:"unicode/1f469-1f33e.png?v8",woman_feeding_baby:"unicode/1f469-1f37c.png?v8",woman_firefighter:"unicode/1f469-1f692.png?v8",woman_health_worker:"unicode/1f469-2695.png?v8",woman_in_manual_wheelchair:"unicode/1f469-1f9bd.png?v8",woman_in_motorized_wheelchair:"unicode/1f469-1f9bc.png?v8",woman_in_tuxedo:"unicode/1f935-2640.png?v8",woman_judge:"unicode/1f469-2696.png?v8",woman_juggling:"unicode/1f939-2640.png?v8",woman_mechanic:"unicode/1f469-1f527.png?v8",woman_office_worker:"unicode/1f469-1f4bc.png?v8",woman_pilot:"unicode/1f469-2708.png?v8",woman_playing_handball:"unicode/1f93e-2640.png?v8",woman_playing_water_polo:"unicode/1f93d-2640.png?v8",woman_scientist:"unicode/1f469-1f52c.png?v8",woman_shrugging:"unicode/1f937-2640.png?v8",woman_singer:"unicode/1f469-1f3a4.png?v8",woman_student:"unicode/1f469-1f393.png?v8",woman_teacher:"unicode/1f469-1f3eb.png?v8",woman_technologist:"unicode/1f469-1f4bb.png?v8",woman_with_headscarf:"unicode/1f9d5.png?v8",woman_with_probing_cane:"unicode/1f469-1f9af.png?v8",woman_with_turban:"unicode/1f473-2640.png?v8",woman_with_veil:"unicode/1f470-2640.png?v8",womans_clothes:"unicode/1f45a.png?v8",womans_hat:"unicode/1f452.png?v8",women_wrestling:"unicode/1f93c-2640.png?v8",womens:"unicode/1f6ba.png?v8",wood:"unicode/1fab5.png?v8",woozy_face:"unicode/1f974.png?v8",world_map:"unicode/1f5fa.png?v8",worm:"unicode/1fab1.png?v8",worried:"unicode/1f61f.png?v8",wrench:"unicode/1f527.png?v8",wrestling:"unicode/1f93c.png?v8",writing_hand:"unicode/270d.png?v8",x:"unicode/274c.png?v8",yarn:"unicode/1f9f6.png?v8",yawning_face:"unicode/1f971.png?v8",yellow_circle:"unicode/1f7e1.png?v8",yellow_heart:"unicode/1f49b.png?v8",yellow_square:"unicode/1f7e8.png?v8",yemen:"unicode/1f1fe-1f1ea.png?v8",yen:"unicode/1f4b4.png?v8",yin_yang:"unicode/262f.png?v8",yo_yo:"unicode/1fa80.png?v8",yum:"unicode/1f60b.png?v8",zambia:"unicode/1f1ff-1f1f2.png?v8",zany_face:"unicode/1f92a.png?v8",zap:"unicode/26a1.png?v8",zebra:"unicode/1f993.png?v8",zero:"unicode/0030-20e3.png?v8",zimbabwe:"unicode/1f1ff-1f1fc.png?v8",zipper_mouth_face:"unicode/1f910.png?v8",zombie:"unicode/1f9df.png?v8",zombie_man:"unicode/1f9df-2642.png?v8",zombie_woman:"unicode/1f9df-2640.png?v8",zzz:"unicode/1f4a4.png?v8"}};function jn(e,t){return e.replace(/<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(//g,function(e){return e.replace(/:/g,"__colon__")}).replace(/([a-z]{2,}:)?\/\/[^\s'">)]+/gi,function(e){return e.replace(/:/g,"__colon__")}).replace(/:([a-z0-9_\-+]+?):/g,function(e,n){return i=e,o=n,e=t,n=Cn.data[o],i,i=n?e&&/unicode/.test(n)?''+n.replace("unicode/","").replace(/\.png.*/,"").split("-").map(function(e){return"&#x"+e+";"}).join("‍").concat("︎")+"":''+o+'':i;var i,o}).replace(/__colon__/g,":")}function Ln(e){var o={};return{str:e=(e=void 0===e?"":e)&&e.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,i){return-1===n.indexOf(":")?(o[n]=i&&i.replace(/"/g,"")||!0,""):e}).trim(),config:o}}function On(e){return(e=void 0===e?"":e).replace(/(<\/?a.*?>)/gi,"")}var qn,Pn=be(function(e){var u,f,p,d,n,g=function(u){var i=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,e={},R={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof T?new T(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=r.reach);m+=_.value.length,_=_.next){var b=_.value;if(i.length>n.length)return;if(!(b instanceof T)){var k,w=1;if(l){if(!(k=C(h,m,n,s))||k.index>=n.length)break;var y=k.index,x=k.index+k[0].length,S=m;for(S+=_.value.length;S<=y;)_=_.next,S+=_.value.length;if(S-=_.value.length,m=S,_.value instanceof T)continue;for(var A=_;A!==i.tail&&(Sr.reach&&(r.reach=E);b=_.prev;z&&(b=j(i,b,z),m+=z.length),L(i,b,w);$=new T(c,g?R.tokenize($,g):$,v,$);_=j(i,b,$),F&&j(i,_,F),1r.reach&&(r.reach=E.reach))}}}}}(e,t,n,t.head,0),function(e){var n=[],i=e.head.next;for(;i!==e.tail;)n.push(i.value),i=i.next;return n}(t)},hooks:{all:{},add:function(e,n){var i=R.hooks.all;i[e]=i[e]||[],i[e].push(n)},run:function(e,n){var i=R.hooks.all[e];if(i&&i.length)for(var o,t=0;o=i[t++];)o(n)}},Token:T};function T(e,n,i,o){this.type=e,this.content=n,this.alias=i,this.length=0|(o||"").length}function C(e,n,i,o){e.lastIndex=n;i=e.exec(i);return i&&o&&i[1]&&(o=i[1].length,i.index+=o,i[0]=i[0].slice(o)),i}function a(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function j(e,n,i){var o=n.next,i={value:i,prev:n,next:o};return n.next=i,o.prev=i,e.length++,i}function L(e,n,i){for(var o=n.next,t=0;t"+t.content+""},!u.document)return u.addEventListener&&(R.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),i=n.language,e=n.code,n=n.immediateClose;u.postMessage(R.highlight(e,R.languages[i],i)),n&&u.close()},!1)),R;var o=R.util.currentScript();function t(){R.manual||R.highlightAll()}return o&&(R.filename=o.src,o.hasAttribute("data-manual")&&(R.manual=!0)),R.manual||("loading"===(e=document.readyState)||"interactive"===e&&o&&o.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)),R}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=g),void 0!==me&&(me.Prism=g),g.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},g.languages.markup.tag.inside["attr-value"].inside.entity=g.languages.markup.entity,g.languages.markup.doctype.inside["internal-subset"].inside=g.languages.markup,g.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(g.languages.markup.tag,"addInlined",{value:function(e,n){var i={};i["language-"+n]={pattern:/(^$)/i,lookbehind:!0,inside:g.languages[n]},i.cdata=/^$/i;i={"included-cdata":{pattern://i,inside:i}};i["language-"+n]={pattern:/[\s\S]+/,inside:g.languages[n]};n={};n[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:i},g.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(g.languages.markup.tag,"addAttribute",{value:function(e,n){g.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[n,"language-"+n],inside:g.languages[n]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),g.languages.html=g.languages.markup,g.languages.mathml=g.languages.markup,g.languages.svg=g.languages.markup,g.languages.xml=g.languages.extend("markup",{}),g.languages.ssml=g.languages.xml,g.languages.atom=g.languages.xml,g.languages.rss=g.languages.xml,function(e){var n=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+n.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+n.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+n.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+n.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:n,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;e=e.languages.markup;e&&(e.tag.addInlined("style","css"),e.tag.addAttribute("style","css"))}(g),g.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},g.languages.javascript=g.languages.extend("clike",{"class-name":[g.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),g.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,g.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:g.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:g.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:g.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:g.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:g.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),g.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:g.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),g.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),g.languages.markup&&(g.languages.markup.tag.addInlined("script","javascript"),g.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),g.languages.js=g.languages.javascript,void 0!==g&&"undefined"!=typeof document&&(Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),u={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},d="pre[data-src]:not(["+(f="data-src-status")+'="loaded"]):not(['+f+'="'+(p="loading")+'"])',g.hooks.add("before-highlightall",function(e){e.selector+=", "+d}),g.hooks.add("before-sanity-check",function(e){var t,n,i,o,a,r,c=e.element;c.matches(d)&&(e.code="",c.setAttribute(f,p),(t=c.appendChild(document.createElement("CODE"))).textContent="Loading…",i=c.getAttribute("data-src"),"none"===(e=e.language)&&(n=(/\.(\w+)$/.exec(i)||[,"none"])[1],e=u[n]||n),g.util.setLanguage(t,e),g.util.setLanguage(c,e),(n=g.plugins.autoloader)&&n.loadLanguages(e),i=i,o=function(e){c.setAttribute(f,"loaded");var n,i,o=function(e){if(i=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||"")){var n=Number(i[1]),e=i[2],i=i[3];return e?i?[n,Number(i)]:[n,void 0]:[n,n]}}(c.getAttribute("data-range"));o&&(n=e.split(/\r\n?|\n/g),i=o[0],o=null==o[1]?n.length:o[1],i<0&&(i+=n.length),i=Math.max(0,Math.min(i-1,n.length)),o<0&&(o+=n.length),o=Math.max(0,Math.min(o,n.length)),e=n.slice(i,o).join("\n"),c.hasAttribute("data-start")||c.setAttribute("data-start",String(i+1))),t.textContent=e,g.highlightElement(t)},a=function(e){c.setAttribute(f,"failed"),t.textContent=e},(r=new XMLHttpRequest).open("GET",i,!0),r.onreadystatechange=function(){4==r.readyState&&(r.status<400&&r.responseText?o(r.responseText):400<=r.status?a("✖ Error "+r.status+" while fetching file: "+r.statusText):a("✖ Error: File does not exist or is empty"))},r.send(null))}),n=!(g.plugins.fileHighlight={highlight:function(e){for(var n,i=(e||document).querySelectorAll(d),o=0;n=i[o++];)g.highlightElement(n)}}),g.fileHighlight=function(){n||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),n=!0),g.plugins.fileHighlight.highlight.apply(this,arguments)})});function Mn(e,n){return"___"+e.toUpperCase()+n+"___"}qn=Prism,Object.defineProperties(qn.languages["markup-templating"]={},{buildPlaceholders:{value:function(o,t,e,a){var r;o.language===t&&(r=o.tokenStack=[],o.code=o.code.replace(e,function(e){if("function"==typeof a&&!a(e))return e;for(var n,i=r.length;-1!==o.code.indexOf(n=Mn(t,i));)++i;return r[i]=e,n}),o.grammar=qn.languages.markup)}},tokenizePlaceholders:{value:function(f,p){var d,g;f.language===p&&f.tokenStack&&(f.grammar=qn.languages[p],d=0,g=Object.keys(f.tokenStack),function e(n){for(var i=0;i=g.length);i++){var o,t,a,r,c,u=n[i];"string"==typeof u||u.content&&"string"==typeof u.content?(t=g[d],a=f.tokenStack[t],o="string"==typeof u?u:u.content,c=Mn(p,t),-1<(r=o.indexOf(c))&&(++d,t=o.substring(0,r),a=new qn.Token(p,qn.tokenize(a,f.grammar),"language-"+p,a),r=o.substring(r+c.length),c=[],t&&c.push.apply(c,e([t])),c.push(a),r&&c.push.apply(c,e([r])),"string"==typeof u?n.splice.apply(n,[i,1].concat(c)):u.content=c)):u.content&&e(u.content)}return n}(f.tokens))}}});function In(t,e){var a=this;this.config=t,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=t.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?t.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var n=this._initRenderer();this.heading=n.heading;var r=o(e=t.markdown||{})?e(Sn,n):(Sn.setOptions(m(e,{renderer:m(n,e.renderer)})),Sn);this._marked=r,this.compile=function(i){var o=!0,e=c(function(e){o=!1;var n="";return i&&(n=f(i)?r(i):r.parser(i),n=t.noEmoji?n:jn(n,t.nativeEmoji),Tn.clear(),n)})(i),n=a.router.parse().file;return o?a.toc=a.cacheTOC[n]:a.cacheTOC[n]=[].concat(a.toc),e}}var Nn={},Hn={markdown:function(e){return{url:e}},mermaid:function(e){return{url:e}},iframe:function(e,n){return{html:'"}},video:function(e,n){return{html:'"}},audio:function(e,n){return{html:'"}},code:function(e,n){var i=e.match(/\.(\w+)$/);return{url:e,lang:i="md"===(i=n||i&&i[1])?"markdown":i}}};In.prototype.compileEmbed=function(e,n){var i,o,t=Ln(n),a=t.str,t=t.config;if(n=a,t.include)return R(e)||(e=q(this.contentBase,C(this.router.getCurrentPath()),e)),t.type&&(o=Hn[t.type])?(i=o.call(this,e,n)).type=t.type:(o="code",/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(i=Hn[o].call(this,e,n)).type=o),i.fragment=t.fragment,i},In.prototype._matchNotCompileLink=function(e){for(var n=this.config.noCompileLinks||[],i=0;i/g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore} --\x3e",""),e.title=On(o),e.ignoreSubHeading=!0),/{docsify-ignore}/g.test(o)&&(o=o.replace("{docsify-ignore}",""),e.title=On(o),e.ignoreSubHeading=!0),//g.test(o)&&(o=o.replace("\x3c!-- {docsify-ignore-all} --\x3e",""),e.title=On(o),e.ignoreAllSubs=!0),/{docsify-ignore-all}/g.test(o)&&(o=o.replace("{docsify-ignore-all}",""),e.title=On(o),e.ignoreAllSubs=!0);i=Tn(t.id||o),t=a.toURL(a.getCurrentPath(),{id:i});return e.slug=t,g.toc.push(e),"'+o+""},t.code={renderer:e}.renderer.code=function(e,n){var i=Pn.languages[n=void 0===n?"markup":n]||Pn.languages.markup;return'
    '+Pn.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),i,n)+"
    "},t.link=(i=(n={renderer:e,router:a,linkTarget:n,linkRel:i,compilerClass:g}).renderer,c=n.router,u=n.linkTarget,n.linkRel,f=n.compilerClass,i.link=function(e,n,i){var o=[],t=Ln(n=void 0===n?"":n),a=t.str,t=t.config;return u=t.target||u,r="_blank"===u?f.config.externalLinkRel||"noopener":"",n=a,R(e)||f._matchNotCompileLink(e)||t.ignore?(R(e)||"./"!==e.slice(0,2)||(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),o.push(0===e.indexOf("mailto:")?"":'target="'+u+'"'),o.push(0!==e.indexOf("mailto:")&&""!==r?' rel="'+r+'"':"")):(e===f.config.homepage&&(e="README"),e=c.toURL(e,null,c.getCurrentPath())),t.disabled&&(o.push("disabled"),e="javascript:void(0)"),t.class&&o.push('class="'+t.class+'"'),t.id&&o.push('id="'+t.id+'"'),n&&o.push('title="'+n+'"'),'"+i+""}),t.paragraph={renderer:e}.renderer.paragraph=function(e){e=/^!>/.test(e)?$n("tip",e):/^\?>/.test(e)?$n("warn",e):"

    "+e+"

    ";return e},t.image=(o=(i={renderer:e,contentBase:o,router:a}).renderer,p=i.contentBase,d=i.router,o.image=function(e,n,i){var o=e,t=[],a=Ln(n),r=a.str,a=a.config;return n=r,a["no-zoom"]&&t.push("data-no-zoom"),n&&t.push('title="'+n+'"'),a.size&&(n=(r=a.size.split("x"))[0],(r=r[1])?t.push('width="'+n+'" height="'+r+'"'):t.push('width="'+n+'"')),a.class&&t.push('class="'+a.class+'"'),a.id&&t.push('id="'+a.id+'"'),R(e)||(o=q(p,C(d.getCurrentPath()),e)),0":''+i+'"}),t.list={renderer:e}.renderer.list=function(e,n,i){n=n?"ol":"ul";return"<"+n+" "+[/
  • /.test(e.split('class="task-list"')[0])?'class="task-list"':"",i&&1"+e+""},t.listitem={renderer:e}.renderer.listitem=function(e){return/^(]*>)/.test(e)?'
  • ":"
  • "+e+"
  • "},e.origin=t,e},In.prototype.sidebar=function(e,n){var i=this.toc,o=this.router.getCurrentPath(),t="";if(e)t=this.compile(e);else{for(var a=0;a{inner}");this.cacheTree[o]=n}return t},In.prototype.subSidebar=function(e){if(e){var n=this.router.getCurrentPath(),i=this.cacheTree,o=this.toc;o[0]&&o[0].ignoreAllSubs&&o.splice(0),o[0]&&1===o[0].level&&o.shift();for(var t=0;t\n'+e+"\n"}]).links={}:(n=[{type:"html",text:e}]).links={}),a({token:t,embedToken:n}),++u>=c&&a({})}}(n);n.embed.url?X(n.embed.url).then(o):o(n.embed.html)}}({compile:i,embedTokens:c,fetch:n},function(e){var n,i=e.embedToken,e=e.token;e?(n=e.index,p.forEach(function(e){n>e.start&&(n+=e.length)}),m(f,i.links),r=r.slice(0,n).concat(i,r.slice(n+1)),p.push({start:n,length:i.length-1})):(Bn[t]=r.concat(),r.links=Bn[t].links=f,o(r))})}function Yn(e,n,i){var o,t,a,r;return n="function"==typeof i?i(n):"string"==typeof i?(a=[],r=0,(o=i).replace(V,function(n,e,i){a.push(o.substring(r,i-1)),r=i+=n.length+1,a.push(t&&t[n]||function(e){return("00"+("string"==typeof Y[n]?e[Y[n]]():Y[n](e))).slice(-n.length)})}),r!==o.length&&a.push(o.substring(r)),function(e){for(var n="",i=0,o=e||new Date;i404 - Not found","Vue"in window)for(var a=0,r=k(".markdown-section > *").filter(n);ascript").filter(function(e){return!/template/.test(e.type)})[0])||(e=e.innerText.trim())&&new Function(e)()),"Vue"in window){var u,f,p=[],d=Object.keys(i.vueComponents||{});2===t&&d.length&&d.forEach(function(e){window.Vue.options.components[e]||window.Vue.component(e,i.vueComponents[e])}),!Un&&i.vueGlobalOptions&&"function"==typeof i.vueGlobalOptions.data&&(Un=i.vueGlobalOptions.data()),p.push.apply(p,Object.keys(i.vueMounts||{}).map(function(e){return[b(o,e),i.vueMounts[e]]}).filter(function(e){var n=e[0];e[1];return n})),(i.vueGlobalOptions||d.length)&&(u=/{{2}[^{}]*}{2}/,f=/<[^>/]+\s([@:]|v-)[\w-:.[\]]+[=>\s]/,p.push.apply(p,k(".markdown-section > *").filter(function(i){return!p.some(function(e){var n=e[0];e[1];return n===i})}).filter(function(e){return e.tagName.toLowerCase()in(i.vueComponents||{})||e.querySelector(d.join(",")||null)||u.test(e.outerHTML)||f.test(e.outerHTML)}).map(function(e){var n=m({},i.vueGlobalOptions||{});return Un&&(n.data=function(){return Un}),[e,n]})));for(var g=0,s=p;g([^<]*?)

    $'))&&("color"===n[2]?o.style.background=n[1]+(n[3]||""):(e=n[1],S(o,"add","has-mask"),R(n[1])||(e=q(this.router.getBasePath(),n[1])),o.style.backgroundImage="url("+e+")",o.style.backgroundSize="cover",o.style.backgroundPosition="center center"),i=i.replace(n[0],"")),this._renderTo(".cover-main",i),K()):S(o,"remove","show")},n.prototype._updateRender=function(){var e,n,i,o;e=this,n=l(".app-name-link"),i=e.config.nameLink,o=e.route.path,n&&(f(e.config.nameLink)?n.setAttribute("href",i):"object"==typeof i&&(e=Object.keys(i).filter(function(e){return-1':"")),e.coverpage&&(f+=(o=", 100%, 85%",'
    \x3c!--cover--\x3e
    ')),e.logo&&(o=/^data:image/.test(e.logo),n=/(?:http[s]?:)?\/\//.test(e.logo),i=/^\./.test(e.logo),o||n||i||(e.logo=q(this.router.getBasePath(),e.logo))),f+=(i=(n=e).name||"","
    "+('')+'
    \x3c!--main--\x3e
    '),this._renderTo(u,f,!0)):this.rendered=!0,e.mergeNavbar&&s?p=b(".sidebar"):(c.classList.add("app-nav"),e.repo||c.classList.add("no-badge")),e.loadNavbar&&y(p,c),e.themeColor&&(v.head.appendChild(w("div","").firstElementChild),a=e.themeColor,window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)")||(e=k("style:not(.inserted),link"),[].forEach.call(e,function(e){"STYLE"===e.nodeName?Q(e,a):"LINK"===e.nodeName&&(e=e.getAttribute("href"),/\.css$/.test(e)&&X(e).then(function(e){e=w("style",e);_.appendChild(e),Q(e,a)}))}))),this._updateRender(),S(h,"ready")},n}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.routes=function(){return this.config.routes||{}},n.prototype.matchVirtualRoute=function(t){var a=this.routes(),r=Object.keys(a),c=function(){return null};function u(){var e=r.shift();if(!e)return c(null);var n=A(o=(i="^",0===(o=e).indexOf(i)?o:"^"+o),"$")?o:o+"$",i=t.match(n);if(!i)return u();var o=a[e];if("string"==typeof o)return c(o);if("function"!=typeof o)return u();n=o,e=Xn(),o=e[0];return(0,e[1])(function(e){return"string"==typeof e?c(e):!1===e?c(null):u()}),n.length<=2?o(n(t,i)):n(t,i,o)}return{then:function(e){c=e,u()}}},n}(function(i){function e(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];i.apply(this,e),this.route={}}return i&&(e.__proto__=i),((e.prototype=Object.create(i&&i.prototype)).constructor=e).prototype.updateRender=function(){this.router.normalize(),this.route=this.router.parse(),h.setAttribute("data-page",this.route.file)},e.prototype.initRouter=function(){var n=this,e=this.config,e=new("history"===(e.routerMode||"hash")&&t?D:H)(e);this.router=e,this.updateRender(),U=this.route,e.onchange(function(e){n.updateRender(),n._updateRender(),U.path!==n.route.path?(n.$fetch(d,n.$resetEvents.bind(n,e.source)),U=n.route):n.$resetEvents(e.source)})},e}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.initLifecycle=function(){var i=this;this._hooks={},this._lifecycle={},["init","mounted","beforeEach","afterEach","doneEach","ready"].forEach(function(e){var n=i._hooks[e]=[];i._lifecycle[e]=function(e){return n.push(e)}})},n.prototype.callHook=function(e,t,a){void 0===a&&(a=d);var r=this._hooks[e],c=this.config.catchPluginErrors,u=function(n){var e=r[n];if(n>=r.length)a(t);else if("function"==typeof e){var i="Docsify plugin error";if(2===e.length)try{e(t,function(e){t=e,u(n+1)})}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}else try{var o=e(t);t=void 0===o?t:o,u(n+1)}catch(e){if(!c)throw e;console.error(i,e),u(n+1)}}else u(n+1)};u(0)},n}(we))))))));function Kn(e,n,i){return Qn&&Qn.abort&&Qn.abort(),Qn=X(e,!0,i)}window.Docsify={util:Me,dom:n,get:X,slugify:Tn,version:"4.13.1"},window.DocsifyCompiler=In,window.marked=Sn,window.Prism=Pn,e(function(e){return new Jn})}(); \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/static/js/emoji.min.js b/ruoyi-admin/src/main/resources/static/static/js/emoji.min.js deleted file mode 100644 index 89151602..00000000 --- a/ruoyi-admin/src/main/resources/static/static/js/emoji.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){var o="https://github.githubassets.com/images/icons/emoji/",i={100:"unicode/1f4af.png?v8",1234:"unicode/1f522.png?v8","+1":"unicode/1f44d.png?v8","-1":"unicode/1f44e.png?v8","1st_place_medal":"unicode/1f947.png?v8","2nd_place_medal":"unicode/1f948.png?v8","3rd_place_medal":"unicode/1f949.png?v8","8ball":"unicode/1f3b1.png?v8",a:"unicode/1f170.png?v8",ab:"unicode/1f18e.png?v8",abacus:"unicode/1f9ee.png?v8",abc:"unicode/1f524.png?v8",abcd:"unicode/1f521.png?v8",accept:"unicode/1f251.png?v8",accessibility:"accessibility.png?v8",accordion:"unicode/1fa97.png?v8",adhesive_bandage:"unicode/1fa79.png?v8",adult:"unicode/1f9d1.png?v8",aerial_tramway:"unicode/1f6a1.png?v8",afghanistan:"unicode/1f1e6-1f1eb.png?v8",airplane:"unicode/2708.png?v8",aland_islands:"unicode/1f1e6-1f1fd.png?v8",alarm_clock:"unicode/23f0.png?v8",albania:"unicode/1f1e6-1f1f1.png?v8",alembic:"unicode/2697.png?v8",algeria:"unicode/1f1e9-1f1ff.png?v8",alien:"unicode/1f47d.png?v8",ambulance:"unicode/1f691.png?v8",american_samoa:"unicode/1f1e6-1f1f8.png?v8",amphora:"unicode/1f3fa.png?v8",anatomical_heart:"unicode/1fac0.png?v8",anchor:"unicode/2693.png?v8",andorra:"unicode/1f1e6-1f1e9.png?v8",angel:"unicode/1f47c.png?v8",anger:"unicode/1f4a2.png?v8",angola:"unicode/1f1e6-1f1f4.png?v8",angry:"unicode/1f620.png?v8",anguilla:"unicode/1f1e6-1f1ee.png?v8",anguished:"unicode/1f627.png?v8",ant:"unicode/1f41c.png?v8",antarctica:"unicode/1f1e6-1f1f6.png?v8",antigua_barbuda:"unicode/1f1e6-1f1ec.png?v8",apple:"unicode/1f34e.png?v8",aquarius:"unicode/2652.png?v8",argentina:"unicode/1f1e6-1f1f7.png?v8",aries:"unicode/2648.png?v8",armenia:"unicode/1f1e6-1f1f2.png?v8",arrow_backward:"unicode/25c0.png?v8",arrow_double_down:"unicode/23ec.png?v8",arrow_double_up:"unicode/23eb.png?v8",arrow_down:"unicode/2b07.png?v8",arrow_down_small:"unicode/1f53d.png?v8",arrow_forward:"unicode/25b6.png?v8",arrow_heading_down:"unicode/2935.png?v8",arrow_heading_up:"unicode/2934.png?v8",arrow_left:"unicode/2b05.png?v8",arrow_lower_left:"unicode/2199.png?v8",arrow_lower_right:"unicode/2198.png?v8",arrow_right:"unicode/27a1.png?v8",arrow_right_hook:"unicode/21aa.png?v8",arrow_up:"unicode/2b06.png?v8",arrow_up_down:"unicode/2195.png?v8",arrow_up_small:"unicode/1f53c.png?v8",arrow_upper_left:"unicode/2196.png?v8",arrow_upper_right:"unicode/2197.png?v8",arrows_clockwise:"unicode/1f503.png?v8",arrows_counterclockwise:"unicode/1f504.png?v8",art:"unicode/1f3a8.png?v8",articulated_lorry:"unicode/1f69b.png?v8",artificial_satellite:"unicode/1f6f0.png?v8",artist:"unicode/1f9d1-1f3a8.png?v8",aruba:"unicode/1f1e6-1f1fc.png?v8",ascension_island:"unicode/1f1e6-1f1e8.png?v8",asterisk:"unicode/002a-20e3.png?v8",astonished:"unicode/1f632.png?v8",astronaut:"unicode/1f9d1-1f680.png?v8",athletic_shoe:"unicode/1f45f.png?v8",atm:"unicode/1f3e7.png?v8",atom:"atom.png?v8",atom_symbol:"unicode/269b.png?v8",australia:"unicode/1f1e6-1f1fa.png?v8",austria:"unicode/1f1e6-1f1f9.png?v8",auto_rickshaw:"unicode/1f6fa.png?v8",avocado:"unicode/1f951.png?v8",axe:"unicode/1fa93.png?v8",azerbaijan:"unicode/1f1e6-1f1ff.png?v8",b:"unicode/1f171.png?v8",baby:"unicode/1f476.png?v8",baby_bottle:"unicode/1f37c.png?v8",baby_chick:"unicode/1f424.png?v8",baby_symbol:"unicode/1f6bc.png?v8",back:"unicode/1f519.png?v8",bacon:"unicode/1f953.png?v8",badger:"unicode/1f9a1.png?v8",badminton:"unicode/1f3f8.png?v8",bagel:"unicode/1f96f.png?v8",baggage_claim:"unicode/1f6c4.png?v8",baguette_bread:"unicode/1f956.png?v8",bahamas:"unicode/1f1e7-1f1f8.png?v8",bahrain:"unicode/1f1e7-1f1ed.png?v8",balance_scale:"unicode/2696.png?v8",bald_man:"unicode/1f468-1f9b2.png?v8",bald_woman:"unicode/1f469-1f9b2.png?v8",ballet_shoes:"unicode/1fa70.png?v8",balloon:"unicode/1f388.png?v8",ballot_box:"unicode/1f5f3.png?v8",ballot_box_with_check:"unicode/2611.png?v8",bamboo:"unicode/1f38d.png?v8",banana:"unicode/1f34c.png?v8",bangbang:"unicode/203c.png?v8",bangladesh:"unicode/1f1e7-1f1e9.png?v8",banjo:"unicode/1fa95.png?v8",bank:"unicode/1f3e6.png?v8",bar_chart:"unicode/1f4ca.png?v8",barbados:"unicode/1f1e7-1f1e7.png?v8",barber:"unicode/1f488.png?v8",baseball:"unicode/26be.png?v8",basecamp:"basecamp.png?v8",basecampy:"basecampy.png?v8",basket:"unicode/1f9fa.png?v8",basketball:"unicode/1f3c0.png?v8",basketball_man:"unicode/26f9-2642.png?v8",basketball_woman:"unicode/26f9-2640.png?v8",bat:"unicode/1f987.png?v8",bath:"unicode/1f6c0.png?v8",bathtub:"unicode/1f6c1.png?v8",battery:"unicode/1f50b.png?v8",beach_umbrella:"unicode/1f3d6.png?v8",bear:"unicode/1f43b.png?v8",bearded_person:"unicode/1f9d4.png?v8",beaver:"unicode/1f9ab.png?v8",bed:"unicode/1f6cf.png?v8",bee:"unicode/1f41d.png?v8",beer:"unicode/1f37a.png?v8",beers:"unicode/1f37b.png?v8",beetle:"unicode/1fab2.png?v8",beginner:"unicode/1f530.png?v8",belarus:"unicode/1f1e7-1f1fe.png?v8",belgium:"unicode/1f1e7-1f1ea.png?v8",belize:"unicode/1f1e7-1f1ff.png?v8",bell:"unicode/1f514.png?v8",bell_pepper:"unicode/1fad1.png?v8",bellhop_bell:"unicode/1f6ce.png?v8",benin:"unicode/1f1e7-1f1ef.png?v8",bento:"unicode/1f371.png?v8",bermuda:"unicode/1f1e7-1f1f2.png?v8",beverage_box:"unicode/1f9c3.png?v8",bhutan:"unicode/1f1e7-1f1f9.png?v8",bicyclist:"unicode/1f6b4.png?v8",bike:"unicode/1f6b2.png?v8",biking_man:"unicode/1f6b4-2642.png?v8",biking_woman:"unicode/1f6b4-2640.png?v8",bikini:"unicode/1f459.png?v8",billed_cap:"unicode/1f9e2.png?v8",biohazard:"unicode/2623.png?v8",bird:"unicode/1f426.png?v8",birthday:"unicode/1f382.png?v8",bison:"unicode/1f9ac.png?v8",black_cat:"unicode/1f408-2b1b.png?v8",black_circle:"unicode/26ab.png?v8",black_flag:"unicode/1f3f4.png?v8",black_heart:"unicode/1f5a4.png?v8",black_joker:"unicode/1f0cf.png?v8",black_large_square:"unicode/2b1b.png?v8",black_medium_small_square:"unicode/25fe.png?v8",black_medium_square:"unicode/25fc.png?v8",black_nib:"unicode/2712.png?v8",black_small_square:"unicode/25aa.png?v8",black_square_button:"unicode/1f532.png?v8",blond_haired_man:"unicode/1f471-2642.png?v8",blond_haired_person:"unicode/1f471.png?v8",blond_haired_woman:"unicode/1f471-2640.png?v8",blonde_woman:"unicode/1f471-2640.png?v8",blossom:"unicode/1f33c.png?v8",blowfish:"unicode/1f421.png?v8",blue_book:"unicode/1f4d8.png?v8",blue_car:"unicode/1f699.png?v8",blue_heart:"unicode/1f499.png?v8",blue_square:"unicode/1f7e6.png?v8",blueberries:"unicode/1fad0.png?v8",blush:"unicode/1f60a.png?v8",boar:"unicode/1f417.png?v8",boat:"unicode/26f5.png?v8",bolivia:"unicode/1f1e7-1f1f4.png?v8",bomb:"unicode/1f4a3.png?v8",bone:"unicode/1f9b4.png?v8",book:"unicode/1f4d6.png?v8",bookmark:"unicode/1f516.png?v8",bookmark_tabs:"unicode/1f4d1.png?v8",books:"unicode/1f4da.png?v8",boom:"unicode/1f4a5.png?v8",boomerang:"unicode/1fa83.png?v8",boot:"unicode/1f462.png?v8",bosnia_herzegovina:"unicode/1f1e7-1f1e6.png?v8",botswana:"unicode/1f1e7-1f1fc.png?v8",bouncing_ball_man:"unicode/26f9-2642.png?v8",bouncing_ball_person:"unicode/26f9.png?v8",bouncing_ball_woman:"unicode/26f9-2640.png?v8",bouquet:"unicode/1f490.png?v8",bouvet_island:"unicode/1f1e7-1f1fb.png?v8",bow:"unicode/1f647.png?v8",bow_and_arrow:"unicode/1f3f9.png?v8",bowing_man:"unicode/1f647-2642.png?v8",bowing_woman:"unicode/1f647-2640.png?v8",bowl_with_spoon:"unicode/1f963.png?v8",bowling:"unicode/1f3b3.png?v8",bowtie:"bowtie.png?v8",boxing_glove:"unicode/1f94a.png?v8",boy:"unicode/1f466.png?v8",brain:"unicode/1f9e0.png?v8",brazil:"unicode/1f1e7-1f1f7.png?v8",bread:"unicode/1f35e.png?v8",breast_feeding:"unicode/1f931.png?v8",bricks:"unicode/1f9f1.png?v8",bride_with_veil:"unicode/1f470-2640.png?v8",bridge_at_night:"unicode/1f309.png?v8",briefcase:"unicode/1f4bc.png?v8",british_indian_ocean_territory:"unicode/1f1ee-1f1f4.png?v8",british_virgin_islands:"unicode/1f1fb-1f1ec.png?v8",broccoli:"unicode/1f966.png?v8",broken_heart:"unicode/1f494.png?v8",broom:"unicode/1f9f9.png?v8",brown_circle:"unicode/1f7e4.png?v8",brown_heart:"unicode/1f90e.png?v8",brown_square:"unicode/1f7eb.png?v8",brunei:"unicode/1f1e7-1f1f3.png?v8",bubble_tea:"unicode/1f9cb.png?v8",bucket:"unicode/1faa3.png?v8",bug:"unicode/1f41b.png?v8",building_construction:"unicode/1f3d7.png?v8",bulb:"unicode/1f4a1.png?v8",bulgaria:"unicode/1f1e7-1f1ec.png?v8",bullettrain_front:"unicode/1f685.png?v8",bullettrain_side:"unicode/1f684.png?v8",burkina_faso:"unicode/1f1e7-1f1eb.png?v8",burrito:"unicode/1f32f.png?v8",burundi:"unicode/1f1e7-1f1ee.png?v8",bus:"unicode/1f68c.png?v8",business_suit_levitating:"unicode/1f574.png?v8",busstop:"unicode/1f68f.png?v8",bust_in_silhouette:"unicode/1f464.png?v8",busts_in_silhouette:"unicode/1f465.png?v8",butter:"unicode/1f9c8.png?v8",butterfly:"unicode/1f98b.png?v8",cactus:"unicode/1f335.png?v8",cake:"unicode/1f370.png?v8",calendar:"unicode/1f4c6.png?v8",call_me_hand:"unicode/1f919.png?v8",calling:"unicode/1f4f2.png?v8",cambodia:"unicode/1f1f0-1f1ed.png?v8",camel:"unicode/1f42b.png?v8",camera:"unicode/1f4f7.png?v8",camera_flash:"unicode/1f4f8.png?v8",cameroon:"unicode/1f1e8-1f1f2.png?v8",camping:"unicode/1f3d5.png?v8",canada:"unicode/1f1e8-1f1e6.png?v8",canary_islands:"unicode/1f1ee-1f1e8.png?v8",cancer:"unicode/264b.png?v8",candle:"unicode/1f56f.png?v8",candy:"unicode/1f36c.png?v8",canned_food:"unicode/1f96b.png?v8",canoe:"unicode/1f6f6.png?v8",cape_verde:"unicode/1f1e8-1f1fb.png?v8",capital_abcd:"unicode/1f520.png?v8",capricorn:"unicode/2651.png?v8",car:"unicode/1f697.png?v8",card_file_box:"unicode/1f5c3.png?v8",card_index:"unicode/1f4c7.png?v8",card_index_dividers:"unicode/1f5c2.png?v8",caribbean_netherlands:"unicode/1f1e7-1f1f6.png?v8",carousel_horse:"unicode/1f3a0.png?v8",carpentry_saw:"unicode/1fa9a.png?v8",carrot:"unicode/1f955.png?v8",cartwheeling:"unicode/1f938.png?v8",cat:"unicode/1f431.png?v8",cat2:"unicode/1f408.png?v8",cayman_islands:"unicode/1f1f0-1f1fe.png?v8",cd:"unicode/1f4bf.png?v8",central_african_republic:"unicode/1f1e8-1f1eb.png?v8",ceuta_melilla:"unicode/1f1ea-1f1e6.png?v8",chad:"unicode/1f1f9-1f1e9.png?v8",chains:"unicode/26d3.png?v8",chair:"unicode/1fa91.png?v8",champagne:"unicode/1f37e.png?v8",chart:"unicode/1f4b9.png?v8",chart_with_downwards_trend:"unicode/1f4c9.png?v8",chart_with_upwards_trend:"unicode/1f4c8.png?v8",checkered_flag:"unicode/1f3c1.png?v8",cheese:"unicode/1f9c0.png?v8",cherries:"unicode/1f352.png?v8",cherry_blossom:"unicode/1f338.png?v8",chess_pawn:"unicode/265f.png?v8",chestnut:"unicode/1f330.png?v8",chicken:"unicode/1f414.png?v8",child:"unicode/1f9d2.png?v8",children_crossing:"unicode/1f6b8.png?v8",chile:"unicode/1f1e8-1f1f1.png?v8",chipmunk:"unicode/1f43f.png?v8",chocolate_bar:"unicode/1f36b.png?v8",chopsticks:"unicode/1f962.png?v8",christmas_island:"unicode/1f1e8-1f1fd.png?v8",christmas_tree:"unicode/1f384.png?v8",church:"unicode/26ea.png?v8",cinema:"unicode/1f3a6.png?v8",circus_tent:"unicode/1f3aa.png?v8",city_sunrise:"unicode/1f307.png?v8",city_sunset:"unicode/1f306.png?v8",cityscape:"unicode/1f3d9.png?v8",cl:"unicode/1f191.png?v8",clamp:"unicode/1f5dc.png?v8",clap:"unicode/1f44f.png?v8",clapper:"unicode/1f3ac.png?v8",classical_building:"unicode/1f3db.png?v8",climbing:"unicode/1f9d7.png?v8",climbing_man:"unicode/1f9d7-2642.png?v8",climbing_woman:"unicode/1f9d7-2640.png?v8",clinking_glasses:"unicode/1f942.png?v8",clipboard:"unicode/1f4cb.png?v8",clipperton_island:"unicode/1f1e8-1f1f5.png?v8",clock1:"unicode/1f550.png?v8",clock10:"unicode/1f559.png?v8",clock1030:"unicode/1f565.png?v8",clock11:"unicode/1f55a.png?v8",clock1130:"unicode/1f566.png?v8",clock12:"unicode/1f55b.png?v8",clock1230:"unicode/1f567.png?v8",clock130:"unicode/1f55c.png?v8",clock2:"unicode/1f551.png?v8",clock230:"unicode/1f55d.png?v8",clock3:"unicode/1f552.png?v8",clock330:"unicode/1f55e.png?v8",clock4:"unicode/1f553.png?v8",clock430:"unicode/1f55f.png?v8",clock5:"unicode/1f554.png?v8",clock530:"unicode/1f560.png?v8",clock6:"unicode/1f555.png?v8",clock630:"unicode/1f561.png?v8",clock7:"unicode/1f556.png?v8",clock730:"unicode/1f562.png?v8",clock8:"unicode/1f557.png?v8",clock830:"unicode/1f563.png?v8",clock9:"unicode/1f558.png?v8",clock930:"unicode/1f564.png?v8",closed_book:"unicode/1f4d5.png?v8",closed_lock_with_key:"unicode/1f510.png?v8",closed_umbrella:"unicode/1f302.png?v8",cloud:"unicode/2601.png?v8",cloud_with_lightning:"unicode/1f329.png?v8",cloud_with_lightning_and_rain:"unicode/26c8.png?v8",cloud_with_rain:"unicode/1f327.png?v8",cloud_with_snow:"unicode/1f328.png?v8",clown_face:"unicode/1f921.png?v8",clubs:"unicode/2663.png?v8",cn:"unicode/1f1e8-1f1f3.png?v8",coat:"unicode/1f9e5.png?v8",cockroach:"unicode/1fab3.png?v8",cocktail:"unicode/1f378.png?v8",coconut:"unicode/1f965.png?v8",cocos_islands:"unicode/1f1e8-1f1e8.png?v8",coffee:"unicode/2615.png?v8",coffin:"unicode/26b0.png?v8",coin:"unicode/1fa99.png?v8",cold_face:"unicode/1f976.png?v8",cold_sweat:"unicode/1f630.png?v8",collision:"unicode/1f4a5.png?v8",colombia:"unicode/1f1e8-1f1f4.png?v8",comet:"unicode/2604.png?v8",comoros:"unicode/1f1f0-1f1f2.png?v8",compass:"unicode/1f9ed.png?v8",computer:"unicode/1f4bb.png?v8",computer_mouse:"unicode/1f5b1.png?v8",confetti_ball:"unicode/1f38a.png?v8",confounded:"unicode/1f616.png?v8",confused:"unicode/1f615.png?v8",congo_brazzaville:"unicode/1f1e8-1f1ec.png?v8",congo_kinshasa:"unicode/1f1e8-1f1e9.png?v8",congratulations:"unicode/3297.png?v8",construction:"unicode/1f6a7.png?v8",construction_worker:"unicode/1f477.png?v8",construction_worker_man:"unicode/1f477-2642.png?v8",construction_worker_woman:"unicode/1f477-2640.png?v8",control_knobs:"unicode/1f39b.png?v8",convenience_store:"unicode/1f3ea.png?v8",cook:"unicode/1f9d1-1f373.png?v8",cook_islands:"unicode/1f1e8-1f1f0.png?v8",cookie:"unicode/1f36a.png?v8",cool:"unicode/1f192.png?v8",cop:"unicode/1f46e.png?v8",copyright:"unicode/00a9.png?v8",corn:"unicode/1f33d.png?v8",costa_rica:"unicode/1f1e8-1f1f7.png?v8",cote_divoire:"unicode/1f1e8-1f1ee.png?v8",couch_and_lamp:"unicode/1f6cb.png?v8",couple:"unicode/1f46b.png?v8",couple_with_heart:"unicode/1f491.png?v8",couple_with_heart_man_man:"unicode/1f468-2764-1f468.png?v8",couple_with_heart_woman_man:"unicode/1f469-2764-1f468.png?v8",couple_with_heart_woman_woman:"unicode/1f469-2764-1f469.png?v8",couplekiss:"unicode/1f48f.png?v8",couplekiss_man_man:"unicode/1f468-2764-1f48b-1f468.png?v8",couplekiss_man_woman:"unicode/1f469-2764-1f48b-1f468.png?v8",couplekiss_woman_woman:"unicode/1f469-2764-1f48b-1f469.png?v8",cow:"unicode/1f42e.png?v8",cow2:"unicode/1f404.png?v8",cowboy_hat_face:"unicode/1f920.png?v8",crab:"unicode/1f980.png?v8",crayon:"unicode/1f58d.png?v8",credit_card:"unicode/1f4b3.png?v8",crescent_moon:"unicode/1f319.png?v8",cricket:"unicode/1f997.png?v8",cricket_game:"unicode/1f3cf.png?v8",croatia:"unicode/1f1ed-1f1f7.png?v8",crocodile:"unicode/1f40a.png?v8",croissant:"unicode/1f950.png?v8",crossed_fingers:"unicode/1f91e.png?v8",crossed_flags:"unicode/1f38c.png?v8",crossed_swords:"unicode/2694.png?v8",crown:"unicode/1f451.png?v8",cry:"unicode/1f622.png?v8",crying_cat_face:"unicode/1f63f.png?v8",crystal_ball:"unicode/1f52e.png?v8",cuba:"unicode/1f1e8-1f1fa.png?v8",cucumber:"unicode/1f952.png?v8",cup_with_straw:"unicode/1f964.png?v8",cupcake:"unicode/1f9c1.png?v8",cupid:"unicode/1f498.png?v8",curacao:"unicode/1f1e8-1f1fc.png?v8",curling_stone:"unicode/1f94c.png?v8",curly_haired_man:"unicode/1f468-1f9b1.png?v8",curly_haired_woman:"unicode/1f469-1f9b1.png?v8",curly_loop:"unicode/27b0.png?v8",currency_exchange:"unicode/1f4b1.png?v8",curry:"unicode/1f35b.png?v8",cursing_face:"unicode/1f92c.png?v8",custard:"unicode/1f36e.png?v8",customs:"unicode/1f6c3.png?v8",cut_of_meat:"unicode/1f969.png?v8",cyclone:"unicode/1f300.png?v8",cyprus:"unicode/1f1e8-1f1fe.png?v8",czech_republic:"unicode/1f1e8-1f1ff.png?v8",dagger:"unicode/1f5e1.png?v8",dancer:"unicode/1f483.png?v8",dancers:"unicode/1f46f.png?v8",dancing_men:"unicode/1f46f-2642.png?v8",dancing_women:"unicode/1f46f-2640.png?v8",dango:"unicode/1f361.png?v8",dark_sunglasses:"unicode/1f576.png?v8",dart:"unicode/1f3af.png?v8",dash:"unicode/1f4a8.png?v8",date:"unicode/1f4c5.png?v8",de:"unicode/1f1e9-1f1ea.png?v8",deaf_man:"unicode/1f9cf-2642.png?v8",deaf_person:"unicode/1f9cf.png?v8",deaf_woman:"unicode/1f9cf-2640.png?v8",deciduous_tree:"unicode/1f333.png?v8",deer:"unicode/1f98c.png?v8",denmark:"unicode/1f1e9-1f1f0.png?v8",department_store:"unicode/1f3ec.png?v8",dependabot:"dependabot.png?v8",derelict_house:"unicode/1f3da.png?v8",desert:"unicode/1f3dc.png?v8",desert_island:"unicode/1f3dd.png?v8",desktop_computer:"unicode/1f5a5.png?v8",detective:"unicode/1f575.png?v8",diamond_shape_with_a_dot_inside:"unicode/1f4a0.png?v8",diamonds:"unicode/2666.png?v8",diego_garcia:"unicode/1f1e9-1f1ec.png?v8",disappointed:"unicode/1f61e.png?v8",disappointed_relieved:"unicode/1f625.png?v8",disguised_face:"unicode/1f978.png?v8",diving_mask:"unicode/1f93f.png?v8",diya_lamp:"unicode/1fa94.png?v8",dizzy:"unicode/1f4ab.png?v8",dizzy_face:"unicode/1f635.png?v8",djibouti:"unicode/1f1e9-1f1ef.png?v8",dna:"unicode/1f9ec.png?v8",do_not_litter:"unicode/1f6af.png?v8",dodo:"unicode/1f9a4.png?v8",dog:"unicode/1f436.png?v8",dog2:"unicode/1f415.png?v8",dollar:"unicode/1f4b5.png?v8",dolls:"unicode/1f38e.png?v8",dolphin:"unicode/1f42c.png?v8",dominica:"unicode/1f1e9-1f1f2.png?v8",dominican_republic:"unicode/1f1e9-1f1f4.png?v8",door:"unicode/1f6aa.png?v8",doughnut:"unicode/1f369.png?v8",dove:"unicode/1f54a.png?v8",dragon:"unicode/1f409.png?v8",dragon_face:"unicode/1f432.png?v8",dress:"unicode/1f457.png?v8",dromedary_camel:"unicode/1f42a.png?v8",drooling_face:"unicode/1f924.png?v8",drop_of_blood:"unicode/1fa78.png?v8",droplet:"unicode/1f4a7.png?v8",drum:"unicode/1f941.png?v8",duck:"unicode/1f986.png?v8",dumpling:"unicode/1f95f.png?v8",dvd:"unicode/1f4c0.png?v8","e-mail":"unicode/1f4e7.png?v8",eagle:"unicode/1f985.png?v8",ear:"unicode/1f442.png?v8",ear_of_rice:"unicode/1f33e.png?v8",ear_with_hearing_aid:"unicode/1f9bb.png?v8",earth_africa:"unicode/1f30d.png?v8",earth_americas:"unicode/1f30e.png?v8",earth_asia:"unicode/1f30f.png?v8",ecuador:"unicode/1f1ea-1f1e8.png?v8",egg:"unicode/1f95a.png?v8",eggplant:"unicode/1f346.png?v8",egypt:"unicode/1f1ea-1f1ec.png?v8",eight:"unicode/0038-20e3.png?v8",eight_pointed_black_star:"unicode/2734.png?v8",eight_spoked_asterisk:"unicode/2733.png?v8",eject_button:"unicode/23cf.png?v8",el_salvador:"unicode/1f1f8-1f1fb.png?v8",electric_plug:"unicode/1f50c.png?v8",electron:"electron.png?v8",elephant:"unicode/1f418.png?v8",elevator:"unicode/1f6d7.png?v8",elf:"unicode/1f9dd.png?v8",elf_man:"unicode/1f9dd-2642.png?v8",elf_woman:"unicode/1f9dd-2640.png?v8",email:"unicode/1f4e7.png?v8",end:"unicode/1f51a.png?v8",england:"unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png?v8",envelope:"unicode/2709.png?v8",envelope_with_arrow:"unicode/1f4e9.png?v8",equatorial_guinea:"unicode/1f1ec-1f1f6.png?v8",eritrea:"unicode/1f1ea-1f1f7.png?v8",es:"unicode/1f1ea-1f1f8.png?v8",estonia:"unicode/1f1ea-1f1ea.png?v8",ethiopia:"unicode/1f1ea-1f1f9.png?v8",eu:"unicode/1f1ea-1f1fa.png?v8",euro:"unicode/1f4b6.png?v8",european_castle:"unicode/1f3f0.png?v8",european_post_office:"unicode/1f3e4.png?v8",european_union:"unicode/1f1ea-1f1fa.png?v8",evergreen_tree:"unicode/1f332.png?v8",exclamation:"unicode/2757.png?v8",exploding_head:"unicode/1f92f.png?v8",expressionless:"unicode/1f611.png?v8",eye:"unicode/1f441.png?v8",eye_speech_bubble:"unicode/1f441-1f5e8.png?v8",eyeglasses:"unicode/1f453.png?v8",eyes:"unicode/1f440.png?v8",face_exhaling:"unicode/1f62e-1f4a8.png?v8",face_in_clouds:"unicode/1f636-1f32b.png?v8",face_with_head_bandage:"unicode/1f915.png?v8",face_with_spiral_eyes:"unicode/1f635-1f4ab.png?v8",face_with_thermometer:"unicode/1f912.png?v8",facepalm:"unicode/1f926.png?v8",facepunch:"unicode/1f44a.png?v8",factory:"unicode/1f3ed.png?v8",factory_worker:"unicode/1f9d1-1f3ed.png?v8",fairy:"unicode/1f9da.png?v8",fairy_man:"unicode/1f9da-2642.png?v8",fairy_woman:"unicode/1f9da-2640.png?v8",falafel:"unicode/1f9c6.png?v8",falkland_islands:"unicode/1f1eb-1f1f0.png?v8",fallen_leaf:"unicode/1f342.png?v8",family:"unicode/1f46a.png?v8",family_man_boy:"unicode/1f468-1f466.png?v8",family_man_boy_boy:"unicode/1f468-1f466-1f466.png?v8",family_man_girl:"unicode/1f468-1f467.png?v8",family_man_girl_boy:"unicode/1f468-1f467-1f466.png?v8",family_man_girl_girl:"unicode/1f468-1f467-1f467.png?v8",family_man_man_boy:"unicode/1f468-1f468-1f466.png?v8",family_man_man_boy_boy:"unicode/1f468-1f468-1f466-1f466.png?v8",family_man_man_girl:"unicode/1f468-1f468-1f467.png?v8",family_man_man_girl_boy:"unicode/1f468-1f468-1f467-1f466.png?v8",family_man_man_girl_girl:"unicode/1f468-1f468-1f467-1f467.png?v8",family_man_woman_boy:"unicode/1f468-1f469-1f466.png?v8",family_man_woman_boy_boy:"unicode/1f468-1f469-1f466-1f466.png?v8",family_man_woman_girl:"unicode/1f468-1f469-1f467.png?v8",family_man_woman_girl_boy:"unicode/1f468-1f469-1f467-1f466.png?v8",family_man_woman_girl_girl:"unicode/1f468-1f469-1f467-1f467.png?v8",family_woman_boy:"unicode/1f469-1f466.png?v8",family_woman_boy_boy:"unicode/1f469-1f466-1f466.png?v8",family_woman_girl:"unicode/1f469-1f467.png?v8",family_woman_girl_boy:"unicode/1f469-1f467-1f466.png?v8",family_woman_girl_girl:"unicode/1f469-1f467-1f467.png?v8",family_woman_woman_boy:"unicode/1f469-1f469-1f466.png?v8",family_woman_woman_boy_boy:"unicode/1f469-1f469-1f466-1f466.png?v8",family_woman_woman_girl:"unicode/1f469-1f469-1f467.png?v8",family_woman_woman_girl_boy:"unicode/1f469-1f469-1f467-1f466.png?v8",family_woman_woman_girl_girl:"unicode/1f469-1f469-1f467-1f467.png?v8",farmer:"unicode/1f9d1-1f33e.png?v8",faroe_islands:"unicode/1f1eb-1f1f4.png?v8",fast_forward:"unicode/23e9.png?v8",fax:"unicode/1f4e0.png?v8",fearful:"unicode/1f628.png?v8",feather:"unicode/1fab6.png?v8",feelsgood:"feelsgood.png?v8",feet:"unicode/1f43e.png?v8",female_detective:"unicode/1f575-2640.png?v8",female_sign:"unicode/2640.png?v8",ferris_wheel:"unicode/1f3a1.png?v8",ferry:"unicode/26f4.png?v8",field_hockey:"unicode/1f3d1.png?v8",fiji:"unicode/1f1eb-1f1ef.png?v8",file_cabinet:"unicode/1f5c4.png?v8",file_folder:"unicode/1f4c1.png?v8",film_projector:"unicode/1f4fd.png?v8",film_strip:"unicode/1f39e.png?v8",finland:"unicode/1f1eb-1f1ee.png?v8",finnadie:"finnadie.png?v8",fire:"unicode/1f525.png?v8",fire_engine:"unicode/1f692.png?v8",fire_extinguisher:"unicode/1f9ef.png?v8",firecracker:"unicode/1f9e8.png?v8",firefighter:"unicode/1f9d1-1f692.png?v8",fireworks:"unicode/1f386.png?v8",first_quarter_moon:"unicode/1f313.png?v8",first_quarter_moon_with_face:"unicode/1f31b.png?v8",fish:"unicode/1f41f.png?v8",fish_cake:"unicode/1f365.png?v8",fishing_pole_and_fish:"unicode/1f3a3.png?v8",fishsticks:"fishsticks.png?v8",fist:"unicode/270a.png?v8",fist_left:"unicode/1f91b.png?v8",fist_oncoming:"unicode/1f44a.png?v8",fist_raised:"unicode/270a.png?v8",fist_right:"unicode/1f91c.png?v8",five:"unicode/0035-20e3.png?v8",flags:"unicode/1f38f.png?v8",flamingo:"unicode/1f9a9.png?v8",flashlight:"unicode/1f526.png?v8",flat_shoe:"unicode/1f97f.png?v8",flatbread:"unicode/1fad3.png?v8",fleur_de_lis:"unicode/269c.png?v8",flight_arrival:"unicode/1f6ec.png?v8",flight_departure:"unicode/1f6eb.png?v8",flipper:"unicode/1f42c.png?v8",floppy_disk:"unicode/1f4be.png?v8",flower_playing_cards:"unicode/1f3b4.png?v8",flushed:"unicode/1f633.png?v8",fly:"unicode/1fab0.png?v8",flying_disc:"unicode/1f94f.png?v8",flying_saucer:"unicode/1f6f8.png?v8",fog:"unicode/1f32b.png?v8",foggy:"unicode/1f301.png?v8",fondue:"unicode/1fad5.png?v8",foot:"unicode/1f9b6.png?v8",football:"unicode/1f3c8.png?v8",footprints:"unicode/1f463.png?v8",fork_and_knife:"unicode/1f374.png?v8",fortune_cookie:"unicode/1f960.png?v8",fountain:"unicode/26f2.png?v8",fountain_pen:"unicode/1f58b.png?v8",four:"unicode/0034-20e3.png?v8",four_leaf_clover:"unicode/1f340.png?v8",fox_face:"unicode/1f98a.png?v8",fr:"unicode/1f1eb-1f1f7.png?v8",framed_picture:"unicode/1f5bc.png?v8",free:"unicode/1f193.png?v8",french_guiana:"unicode/1f1ec-1f1eb.png?v8",french_polynesia:"unicode/1f1f5-1f1eb.png?v8",french_southern_territories:"unicode/1f1f9-1f1eb.png?v8",fried_egg:"unicode/1f373.png?v8",fried_shrimp:"unicode/1f364.png?v8",fries:"unicode/1f35f.png?v8",frog:"unicode/1f438.png?v8",frowning:"unicode/1f626.png?v8",frowning_face:"unicode/2639.png?v8",frowning_man:"unicode/1f64d-2642.png?v8",frowning_person:"unicode/1f64d.png?v8",frowning_woman:"unicode/1f64d-2640.png?v8",fu:"unicode/1f595.png?v8",fuelpump:"unicode/26fd.png?v8",full_moon:"unicode/1f315.png?v8",full_moon_with_face:"unicode/1f31d.png?v8",funeral_urn:"unicode/26b1.png?v8",gabon:"unicode/1f1ec-1f1e6.png?v8",gambia:"unicode/1f1ec-1f1f2.png?v8",game_die:"unicode/1f3b2.png?v8",garlic:"unicode/1f9c4.png?v8",gb:"unicode/1f1ec-1f1e7.png?v8",gear:"unicode/2699.png?v8",gem:"unicode/1f48e.png?v8",gemini:"unicode/264a.png?v8",genie:"unicode/1f9de.png?v8",genie_man:"unicode/1f9de-2642.png?v8",genie_woman:"unicode/1f9de-2640.png?v8",georgia:"unicode/1f1ec-1f1ea.png?v8",ghana:"unicode/1f1ec-1f1ed.png?v8",ghost:"unicode/1f47b.png?v8",gibraltar:"unicode/1f1ec-1f1ee.png?v8",gift:"unicode/1f381.png?v8",gift_heart:"unicode/1f49d.png?v8",giraffe:"unicode/1f992.png?v8",girl:"unicode/1f467.png?v8",globe_with_meridians:"unicode/1f310.png?v8",gloves:"unicode/1f9e4.png?v8",goal_net:"unicode/1f945.png?v8",goat:"unicode/1f410.png?v8",goberserk:"goberserk.png?v8",godmode:"godmode.png?v8",goggles:"unicode/1f97d.png?v8",golf:"unicode/26f3.png?v8",golfing:"unicode/1f3cc.png?v8",golfing_man:"unicode/1f3cc-2642.png?v8",golfing_woman:"unicode/1f3cc-2640.png?v8",gorilla:"unicode/1f98d.png?v8",grapes:"unicode/1f347.png?v8",greece:"unicode/1f1ec-1f1f7.png?v8",green_apple:"unicode/1f34f.png?v8",green_book:"unicode/1f4d7.png?v8",green_circle:"unicode/1f7e2.png?v8",green_heart:"unicode/1f49a.png?v8",green_salad:"unicode/1f957.png?v8",green_square:"unicode/1f7e9.png?v8",greenland:"unicode/1f1ec-1f1f1.png?v8",grenada:"unicode/1f1ec-1f1e9.png?v8",grey_exclamation:"unicode/2755.png?v8",grey_question:"unicode/2754.png?v8",grimacing:"unicode/1f62c.png?v8",grin:"unicode/1f601.png?v8",grinning:"unicode/1f600.png?v8",guadeloupe:"unicode/1f1ec-1f1f5.png?v8",guam:"unicode/1f1ec-1f1fa.png?v8",guard:"unicode/1f482.png?v8",guardsman:"unicode/1f482-2642.png?v8",guardswoman:"unicode/1f482-2640.png?v8",guatemala:"unicode/1f1ec-1f1f9.png?v8",guernsey:"unicode/1f1ec-1f1ec.png?v8",guide_dog:"unicode/1f9ae.png?v8",guinea:"unicode/1f1ec-1f1f3.png?v8",guinea_bissau:"unicode/1f1ec-1f1fc.png?v8",guitar:"unicode/1f3b8.png?v8",gun:"unicode/1f52b.png?v8",guyana:"unicode/1f1ec-1f1fe.png?v8",haircut:"unicode/1f487.png?v8",haircut_man:"unicode/1f487-2642.png?v8",haircut_woman:"unicode/1f487-2640.png?v8",haiti:"unicode/1f1ed-1f1f9.png?v8",hamburger:"unicode/1f354.png?v8",hammer:"unicode/1f528.png?v8",hammer_and_pick:"unicode/2692.png?v8",hammer_and_wrench:"unicode/1f6e0.png?v8",hamster:"unicode/1f439.png?v8",hand:"unicode/270b.png?v8",hand_over_mouth:"unicode/1f92d.png?v8",handbag:"unicode/1f45c.png?v8",handball_person:"unicode/1f93e.png?v8",handshake:"unicode/1f91d.png?v8",hankey:"unicode/1f4a9.png?v8",hash:"unicode/0023-20e3.png?v8",hatched_chick:"unicode/1f425.png?v8",hatching_chick:"unicode/1f423.png?v8",headphones:"unicode/1f3a7.png?v8",headstone:"unicode/1faa6.png?v8",health_worker:"unicode/1f9d1-2695.png?v8",hear_no_evil:"unicode/1f649.png?v8",heard_mcdonald_islands:"unicode/1f1ed-1f1f2.png?v8",heart:"unicode/2764.png?v8",heart_decoration:"unicode/1f49f.png?v8",heart_eyes:"unicode/1f60d.png?v8",heart_eyes_cat:"unicode/1f63b.png?v8",heart_on_fire:"unicode/2764-1f525.png?v8",heartbeat:"unicode/1f493.png?v8",heartpulse:"unicode/1f497.png?v8",hearts:"unicode/2665.png?v8",heavy_check_mark:"unicode/2714.png?v8",heavy_division_sign:"unicode/2797.png?v8",heavy_dollar_sign:"unicode/1f4b2.png?v8",heavy_exclamation_mark:"unicode/2757.png?v8",heavy_heart_exclamation:"unicode/2763.png?v8",heavy_minus_sign:"unicode/2796.png?v8",heavy_multiplication_x:"unicode/2716.png?v8",heavy_plus_sign:"unicode/2795.png?v8",hedgehog:"unicode/1f994.png?v8",helicopter:"unicode/1f681.png?v8",herb:"unicode/1f33f.png?v8",hibiscus:"unicode/1f33a.png?v8",high_brightness:"unicode/1f506.png?v8",high_heel:"unicode/1f460.png?v8",hiking_boot:"unicode/1f97e.png?v8",hindu_temple:"unicode/1f6d5.png?v8",hippopotamus:"unicode/1f99b.png?v8",hocho:"unicode/1f52a.png?v8",hole:"unicode/1f573.png?v8",honduras:"unicode/1f1ed-1f1f3.png?v8",honey_pot:"unicode/1f36f.png?v8",honeybee:"unicode/1f41d.png?v8",hong_kong:"unicode/1f1ed-1f1f0.png?v8",hook:"unicode/1fa9d.png?v8",horse:"unicode/1f434.png?v8",horse_racing:"unicode/1f3c7.png?v8",hospital:"unicode/1f3e5.png?v8",hot_face:"unicode/1f975.png?v8",hot_pepper:"unicode/1f336.png?v8",hotdog:"unicode/1f32d.png?v8",hotel:"unicode/1f3e8.png?v8",hotsprings:"unicode/2668.png?v8",hourglass:"unicode/231b.png?v8",hourglass_flowing_sand:"unicode/23f3.png?v8",house:"unicode/1f3e0.png?v8",house_with_garden:"unicode/1f3e1.png?v8",houses:"unicode/1f3d8.png?v8",hugs:"unicode/1f917.png?v8",hungary:"unicode/1f1ed-1f1fa.png?v8",hurtrealbad:"hurtrealbad.png?v8",hushed:"unicode/1f62f.png?v8",hut:"unicode/1f6d6.png?v8",ice_cream:"unicode/1f368.png?v8",ice_cube:"unicode/1f9ca.png?v8",ice_hockey:"unicode/1f3d2.png?v8",ice_skate:"unicode/26f8.png?v8",icecream:"unicode/1f366.png?v8",iceland:"unicode/1f1ee-1f1f8.png?v8",id:"unicode/1f194.png?v8",ideograph_advantage:"unicode/1f250.png?v8",imp:"unicode/1f47f.png?v8",inbox_tray:"unicode/1f4e5.png?v8",incoming_envelope:"unicode/1f4e8.png?v8",india:"unicode/1f1ee-1f1f3.png?v8",indonesia:"unicode/1f1ee-1f1e9.png?v8",infinity:"unicode/267e.png?v8",information_desk_person:"unicode/1f481.png?v8",information_source:"unicode/2139.png?v8",innocent:"unicode/1f607.png?v8",interrobang:"unicode/2049.png?v8",iphone:"unicode/1f4f1.png?v8",iran:"unicode/1f1ee-1f1f7.png?v8",iraq:"unicode/1f1ee-1f1f6.png?v8",ireland:"unicode/1f1ee-1f1ea.png?v8",isle_of_man:"unicode/1f1ee-1f1f2.png?v8",israel:"unicode/1f1ee-1f1f1.png?v8",it:"unicode/1f1ee-1f1f9.png?v8",izakaya_lantern:"unicode/1f3ee.png?v8",jack_o_lantern:"unicode/1f383.png?v8",jamaica:"unicode/1f1ef-1f1f2.png?v8",japan:"unicode/1f5fe.png?v8",japanese_castle:"unicode/1f3ef.png?v8",japanese_goblin:"unicode/1f47a.png?v8",japanese_ogre:"unicode/1f479.png?v8",jeans:"unicode/1f456.png?v8",jersey:"unicode/1f1ef-1f1ea.png?v8",jigsaw:"unicode/1f9e9.png?v8",jordan:"unicode/1f1ef-1f1f4.png?v8",joy:"unicode/1f602.png?v8",joy_cat:"unicode/1f639.png?v8",joystick:"unicode/1f579.png?v8",jp:"unicode/1f1ef-1f1f5.png?v8",judge:"unicode/1f9d1-2696.png?v8",juggling_person:"unicode/1f939.png?v8",kaaba:"unicode/1f54b.png?v8",kangaroo:"unicode/1f998.png?v8",kazakhstan:"unicode/1f1f0-1f1ff.png?v8",kenya:"unicode/1f1f0-1f1ea.png?v8",key:"unicode/1f511.png?v8",keyboard:"unicode/2328.png?v8",keycap_ten:"unicode/1f51f.png?v8",kick_scooter:"unicode/1f6f4.png?v8",kimono:"unicode/1f458.png?v8",kiribati:"unicode/1f1f0-1f1ee.png?v8",kiss:"unicode/1f48b.png?v8",kissing:"unicode/1f617.png?v8",kissing_cat:"unicode/1f63d.png?v8",kissing_closed_eyes:"unicode/1f61a.png?v8",kissing_heart:"unicode/1f618.png?v8",kissing_smiling_eyes:"unicode/1f619.png?v8",kite:"unicode/1fa81.png?v8",kiwi_fruit:"unicode/1f95d.png?v8",kneeling_man:"unicode/1f9ce-2642.png?v8",kneeling_person:"unicode/1f9ce.png?v8",kneeling_woman:"unicode/1f9ce-2640.png?v8",knife:"unicode/1f52a.png?v8",knot:"unicode/1faa2.png?v8",koala:"unicode/1f428.png?v8",koko:"unicode/1f201.png?v8",kosovo:"unicode/1f1fd-1f1f0.png?v8",kr:"unicode/1f1f0-1f1f7.png?v8",kuwait:"unicode/1f1f0-1f1fc.png?v8",kyrgyzstan:"unicode/1f1f0-1f1ec.png?v8",lab_coat:"unicode/1f97c.png?v8",label:"unicode/1f3f7.png?v8",lacrosse:"unicode/1f94d.png?v8",ladder:"unicode/1fa9c.png?v8",lady_beetle:"unicode/1f41e.png?v8",lantern:"unicode/1f3ee.png?v8",laos:"unicode/1f1f1-1f1e6.png?v8",large_blue_circle:"unicode/1f535.png?v8",large_blue_diamond:"unicode/1f537.png?v8",large_orange_diamond:"unicode/1f536.png?v8",last_quarter_moon:"unicode/1f317.png?v8",last_quarter_moon_with_face:"unicode/1f31c.png?v8",latin_cross:"unicode/271d.png?v8",latvia:"unicode/1f1f1-1f1fb.png?v8",laughing:"unicode/1f606.png?v8",leafy_green:"unicode/1f96c.png?v8",leaves:"unicode/1f343.png?v8",lebanon:"unicode/1f1f1-1f1e7.png?v8",ledger:"unicode/1f4d2.png?v8",left_luggage:"unicode/1f6c5.png?v8",left_right_arrow:"unicode/2194.png?v8",left_speech_bubble:"unicode/1f5e8.png?v8",leftwards_arrow_with_hook:"unicode/21a9.png?v8",leg:"unicode/1f9b5.png?v8",lemon:"unicode/1f34b.png?v8",leo:"unicode/264c.png?v8",leopard:"unicode/1f406.png?v8",lesotho:"unicode/1f1f1-1f1f8.png?v8",level_slider:"unicode/1f39a.png?v8",liberia:"unicode/1f1f1-1f1f7.png?v8",libra:"unicode/264e.png?v8",libya:"unicode/1f1f1-1f1fe.png?v8",liechtenstein:"unicode/1f1f1-1f1ee.png?v8",light_rail:"unicode/1f688.png?v8",link:"unicode/1f517.png?v8",lion:"unicode/1f981.png?v8",lips:"unicode/1f444.png?v8",lipstick:"unicode/1f484.png?v8",lithuania:"unicode/1f1f1-1f1f9.png?v8",lizard:"unicode/1f98e.png?v8",llama:"unicode/1f999.png?v8",lobster:"unicode/1f99e.png?v8",lock:"unicode/1f512.png?v8",lock_with_ink_pen:"unicode/1f50f.png?v8",lollipop:"unicode/1f36d.png?v8",long_drum:"unicode/1fa98.png?v8",loop:"unicode/27bf.png?v8",lotion_bottle:"unicode/1f9f4.png?v8",lotus_position:"unicode/1f9d8.png?v8",lotus_position_man:"unicode/1f9d8-2642.png?v8",lotus_position_woman:"unicode/1f9d8-2640.png?v8",loud_sound:"unicode/1f50a.png?v8",loudspeaker:"unicode/1f4e2.png?v8",love_hotel:"unicode/1f3e9.png?v8",love_letter:"unicode/1f48c.png?v8",love_you_gesture:"unicode/1f91f.png?v8",low_brightness:"unicode/1f505.png?v8",luggage:"unicode/1f9f3.png?v8",lungs:"unicode/1fac1.png?v8",luxembourg:"unicode/1f1f1-1f1fa.png?v8",lying_face:"unicode/1f925.png?v8",m:"unicode/24c2.png?v8",macau:"unicode/1f1f2-1f1f4.png?v8",macedonia:"unicode/1f1f2-1f1f0.png?v8",madagascar:"unicode/1f1f2-1f1ec.png?v8",mag:"unicode/1f50d.png?v8",mag_right:"unicode/1f50e.png?v8",mage:"unicode/1f9d9.png?v8",mage_man:"unicode/1f9d9-2642.png?v8",mage_woman:"unicode/1f9d9-2640.png?v8",magic_wand:"unicode/1fa84.png?v8",magnet:"unicode/1f9f2.png?v8",mahjong:"unicode/1f004.png?v8",mailbox:"unicode/1f4eb.png?v8",mailbox_closed:"unicode/1f4ea.png?v8",mailbox_with_mail:"unicode/1f4ec.png?v8",mailbox_with_no_mail:"unicode/1f4ed.png?v8",malawi:"unicode/1f1f2-1f1fc.png?v8",malaysia:"unicode/1f1f2-1f1fe.png?v8",maldives:"unicode/1f1f2-1f1fb.png?v8",male_detective:"unicode/1f575-2642.png?v8",male_sign:"unicode/2642.png?v8",mali:"unicode/1f1f2-1f1f1.png?v8",malta:"unicode/1f1f2-1f1f9.png?v8",mammoth:"unicode/1f9a3.png?v8",man:"unicode/1f468.png?v8",man_artist:"unicode/1f468-1f3a8.png?v8",man_astronaut:"unicode/1f468-1f680.png?v8",man_beard:"unicode/1f9d4-2642.png?v8",man_cartwheeling:"unicode/1f938-2642.png?v8",man_cook:"unicode/1f468-1f373.png?v8",man_dancing:"unicode/1f57a.png?v8",man_facepalming:"unicode/1f926-2642.png?v8",man_factory_worker:"unicode/1f468-1f3ed.png?v8",man_farmer:"unicode/1f468-1f33e.png?v8",man_feeding_baby:"unicode/1f468-1f37c.png?v8",man_firefighter:"unicode/1f468-1f692.png?v8",man_health_worker:"unicode/1f468-2695.png?v8",man_in_manual_wheelchair:"unicode/1f468-1f9bd.png?v8",man_in_motorized_wheelchair:"unicode/1f468-1f9bc.png?v8",man_in_tuxedo:"unicode/1f935-2642.png?v8",man_judge:"unicode/1f468-2696.png?v8",man_juggling:"unicode/1f939-2642.png?v8",man_mechanic:"unicode/1f468-1f527.png?v8",man_office_worker:"unicode/1f468-1f4bc.png?v8",man_pilot:"unicode/1f468-2708.png?v8",man_playing_handball:"unicode/1f93e-2642.png?v8",man_playing_water_polo:"unicode/1f93d-2642.png?v8",man_scientist:"unicode/1f468-1f52c.png?v8",man_shrugging:"unicode/1f937-2642.png?v8",man_singer:"unicode/1f468-1f3a4.png?v8",man_student:"unicode/1f468-1f393.png?v8",man_teacher:"unicode/1f468-1f3eb.png?v8",man_technologist:"unicode/1f468-1f4bb.png?v8",man_with_gua_pi_mao:"unicode/1f472.png?v8",man_with_probing_cane:"unicode/1f468-1f9af.png?v8",man_with_turban:"unicode/1f473-2642.png?v8",man_with_veil:"unicode/1f470-2642.png?v8",mandarin:"unicode/1f34a.png?v8",mango:"unicode/1f96d.png?v8",mans_shoe:"unicode/1f45e.png?v8",mantelpiece_clock:"unicode/1f570.png?v8",manual_wheelchair:"unicode/1f9bd.png?v8",maple_leaf:"unicode/1f341.png?v8",marshall_islands:"unicode/1f1f2-1f1ed.png?v8",martial_arts_uniform:"unicode/1f94b.png?v8",martinique:"unicode/1f1f2-1f1f6.png?v8",mask:"unicode/1f637.png?v8",massage:"unicode/1f486.png?v8",massage_man:"unicode/1f486-2642.png?v8",massage_woman:"unicode/1f486-2640.png?v8",mate:"unicode/1f9c9.png?v8",mauritania:"unicode/1f1f2-1f1f7.png?v8",mauritius:"unicode/1f1f2-1f1fa.png?v8",mayotte:"unicode/1f1fe-1f1f9.png?v8",meat_on_bone:"unicode/1f356.png?v8",mechanic:"unicode/1f9d1-1f527.png?v8",mechanical_arm:"unicode/1f9be.png?v8",mechanical_leg:"unicode/1f9bf.png?v8",medal_military:"unicode/1f396.png?v8",medal_sports:"unicode/1f3c5.png?v8",medical_symbol:"unicode/2695.png?v8",mega:"unicode/1f4e3.png?v8",melon:"unicode/1f348.png?v8",memo:"unicode/1f4dd.png?v8",men_wrestling:"unicode/1f93c-2642.png?v8",mending_heart:"unicode/2764-1fa79.png?v8",menorah:"unicode/1f54e.png?v8",mens:"unicode/1f6b9.png?v8",mermaid:"unicode/1f9dc-2640.png?v8",merman:"unicode/1f9dc-2642.png?v8",merperson:"unicode/1f9dc.png?v8",metal:"unicode/1f918.png?v8",metro:"unicode/1f687.png?v8",mexico:"unicode/1f1f2-1f1fd.png?v8",microbe:"unicode/1f9a0.png?v8",micronesia:"unicode/1f1eb-1f1f2.png?v8",microphone:"unicode/1f3a4.png?v8",microscope:"unicode/1f52c.png?v8",middle_finger:"unicode/1f595.png?v8",military_helmet:"unicode/1fa96.png?v8",milk_glass:"unicode/1f95b.png?v8",milky_way:"unicode/1f30c.png?v8",minibus:"unicode/1f690.png?v8",minidisc:"unicode/1f4bd.png?v8",mirror:"unicode/1fa9e.png?v8",mobile_phone_off:"unicode/1f4f4.png?v8",moldova:"unicode/1f1f2-1f1e9.png?v8",monaco:"unicode/1f1f2-1f1e8.png?v8",money_mouth_face:"unicode/1f911.png?v8",money_with_wings:"unicode/1f4b8.png?v8",moneybag:"unicode/1f4b0.png?v8",mongolia:"unicode/1f1f2-1f1f3.png?v8",monkey:"unicode/1f412.png?v8",monkey_face:"unicode/1f435.png?v8",monocle_face:"unicode/1f9d0.png?v8",monorail:"unicode/1f69d.png?v8",montenegro:"unicode/1f1f2-1f1ea.png?v8",montserrat:"unicode/1f1f2-1f1f8.png?v8",moon:"unicode/1f314.png?v8",moon_cake:"unicode/1f96e.png?v8",morocco:"unicode/1f1f2-1f1e6.png?v8",mortar_board:"unicode/1f393.png?v8",mosque:"unicode/1f54c.png?v8",mosquito:"unicode/1f99f.png?v8",motor_boat:"unicode/1f6e5.png?v8",motor_scooter:"unicode/1f6f5.png?v8",motorcycle:"unicode/1f3cd.png?v8",motorized_wheelchair:"unicode/1f9bc.png?v8",motorway:"unicode/1f6e3.png?v8",mount_fuji:"unicode/1f5fb.png?v8",mountain:"unicode/26f0.png?v8",mountain_bicyclist:"unicode/1f6b5.png?v8",mountain_biking_man:"unicode/1f6b5-2642.png?v8",mountain_biking_woman:"unicode/1f6b5-2640.png?v8",mountain_cableway:"unicode/1f6a0.png?v8",mountain_railway:"unicode/1f69e.png?v8",mountain_snow:"unicode/1f3d4.png?v8",mouse:"unicode/1f42d.png?v8",mouse2:"unicode/1f401.png?v8",mouse_trap:"unicode/1faa4.png?v8",movie_camera:"unicode/1f3a5.png?v8",moyai:"unicode/1f5ff.png?v8",mozambique:"unicode/1f1f2-1f1ff.png?v8",mrs_claus:"unicode/1f936.png?v8",muscle:"unicode/1f4aa.png?v8",mushroom:"unicode/1f344.png?v8",musical_keyboard:"unicode/1f3b9.png?v8",musical_note:"unicode/1f3b5.png?v8",musical_score:"unicode/1f3bc.png?v8",mute:"unicode/1f507.png?v8",mx_claus:"unicode/1f9d1-1f384.png?v8",myanmar:"unicode/1f1f2-1f1f2.png?v8",nail_care:"unicode/1f485.png?v8",name_badge:"unicode/1f4db.png?v8",namibia:"unicode/1f1f3-1f1e6.png?v8",national_park:"unicode/1f3de.png?v8",nauru:"unicode/1f1f3-1f1f7.png?v8",nauseated_face:"unicode/1f922.png?v8",nazar_amulet:"unicode/1f9ff.png?v8",neckbeard:"neckbeard.png?v8",necktie:"unicode/1f454.png?v8",negative_squared_cross_mark:"unicode/274e.png?v8",nepal:"unicode/1f1f3-1f1f5.png?v8",nerd_face:"unicode/1f913.png?v8",nesting_dolls:"unicode/1fa86.png?v8",netherlands:"unicode/1f1f3-1f1f1.png?v8",neutral_face:"unicode/1f610.png?v8",new:"unicode/1f195.png?v8",new_caledonia:"unicode/1f1f3-1f1e8.png?v8",new_moon:"unicode/1f311.png?v8",new_moon_with_face:"unicode/1f31a.png?v8",new_zealand:"unicode/1f1f3-1f1ff.png?v8",newspaper:"unicode/1f4f0.png?v8",newspaper_roll:"unicode/1f5de.png?v8",next_track_button:"unicode/23ed.png?v8",ng:"unicode/1f196.png?v8",ng_man:"unicode/1f645-2642.png?v8",ng_woman:"unicode/1f645-2640.png?v8",nicaragua:"unicode/1f1f3-1f1ee.png?v8",niger:"unicode/1f1f3-1f1ea.png?v8",nigeria:"unicode/1f1f3-1f1ec.png?v8",night_with_stars:"unicode/1f303.png?v8",nine:"unicode/0039-20e3.png?v8",ninja:"unicode/1f977.png?v8",niue:"unicode/1f1f3-1f1fa.png?v8",no_bell:"unicode/1f515.png?v8",no_bicycles:"unicode/1f6b3.png?v8",no_entry:"unicode/26d4.png?v8",no_entry_sign:"unicode/1f6ab.png?v8",no_good:"unicode/1f645.png?v8",no_good_man:"unicode/1f645-2642.png?v8",no_good_woman:"unicode/1f645-2640.png?v8",no_mobile_phones:"unicode/1f4f5.png?v8",no_mouth:"unicode/1f636.png?v8",no_pedestrians:"unicode/1f6b7.png?v8",no_smoking:"unicode/1f6ad.png?v8","non-potable_water":"unicode/1f6b1.png?v8",norfolk_island:"unicode/1f1f3-1f1eb.png?v8",north_korea:"unicode/1f1f0-1f1f5.png?v8",northern_mariana_islands:"unicode/1f1f2-1f1f5.png?v8",norway:"unicode/1f1f3-1f1f4.png?v8",nose:"unicode/1f443.png?v8",notebook:"unicode/1f4d3.png?v8",notebook_with_decorative_cover:"unicode/1f4d4.png?v8",notes:"unicode/1f3b6.png?v8",nut_and_bolt:"unicode/1f529.png?v8",o:"unicode/2b55.png?v8",o2:"unicode/1f17e.png?v8",ocean:"unicode/1f30a.png?v8",octocat:"octocat.png?v8",octopus:"unicode/1f419.png?v8",oden:"unicode/1f362.png?v8",office:"unicode/1f3e2.png?v8",office_worker:"unicode/1f9d1-1f4bc.png?v8",oil_drum:"unicode/1f6e2.png?v8",ok:"unicode/1f197.png?v8",ok_hand:"unicode/1f44c.png?v8",ok_man:"unicode/1f646-2642.png?v8",ok_person:"unicode/1f646.png?v8",ok_woman:"unicode/1f646-2640.png?v8",old_key:"unicode/1f5dd.png?v8",older_adult:"unicode/1f9d3.png?v8",older_man:"unicode/1f474.png?v8",older_woman:"unicode/1f475.png?v8",olive:"unicode/1fad2.png?v8",om:"unicode/1f549.png?v8",oman:"unicode/1f1f4-1f1f2.png?v8",on:"unicode/1f51b.png?v8",oncoming_automobile:"unicode/1f698.png?v8",oncoming_bus:"unicode/1f68d.png?v8",oncoming_police_car:"unicode/1f694.png?v8",oncoming_taxi:"unicode/1f696.png?v8",one:"unicode/0031-20e3.png?v8",one_piece_swimsuit:"unicode/1fa71.png?v8",onion:"unicode/1f9c5.png?v8",open_book:"unicode/1f4d6.png?v8",open_file_folder:"unicode/1f4c2.png?v8",open_hands:"unicode/1f450.png?v8",open_mouth:"unicode/1f62e.png?v8",open_umbrella:"unicode/2602.png?v8",ophiuchus:"unicode/26ce.png?v8",orange:"unicode/1f34a.png?v8",orange_book:"unicode/1f4d9.png?v8",orange_circle:"unicode/1f7e0.png?v8",orange_heart:"unicode/1f9e1.png?v8",orange_square:"unicode/1f7e7.png?v8",orangutan:"unicode/1f9a7.png?v8",orthodox_cross:"unicode/2626.png?v8",otter:"unicode/1f9a6.png?v8",outbox_tray:"unicode/1f4e4.png?v8",owl:"unicode/1f989.png?v8",ox:"unicode/1f402.png?v8",oyster:"unicode/1f9aa.png?v8",package:"unicode/1f4e6.png?v8",page_facing_up:"unicode/1f4c4.png?v8",page_with_curl:"unicode/1f4c3.png?v8",pager:"unicode/1f4df.png?v8",paintbrush:"unicode/1f58c.png?v8",pakistan:"unicode/1f1f5-1f1f0.png?v8",palau:"unicode/1f1f5-1f1fc.png?v8",palestinian_territories:"unicode/1f1f5-1f1f8.png?v8",palm_tree:"unicode/1f334.png?v8",palms_up_together:"unicode/1f932.png?v8",panama:"unicode/1f1f5-1f1e6.png?v8",pancakes:"unicode/1f95e.png?v8",panda_face:"unicode/1f43c.png?v8",paperclip:"unicode/1f4ce.png?v8",paperclips:"unicode/1f587.png?v8",papua_new_guinea:"unicode/1f1f5-1f1ec.png?v8",parachute:"unicode/1fa82.png?v8",paraguay:"unicode/1f1f5-1f1fe.png?v8",parasol_on_ground:"unicode/26f1.png?v8",parking:"unicode/1f17f.png?v8",parrot:"unicode/1f99c.png?v8",part_alternation_mark:"unicode/303d.png?v8",partly_sunny:"unicode/26c5.png?v8",partying_face:"unicode/1f973.png?v8",passenger_ship:"unicode/1f6f3.png?v8",passport_control:"unicode/1f6c2.png?v8",pause_button:"unicode/23f8.png?v8",paw_prints:"unicode/1f43e.png?v8",peace_symbol:"unicode/262e.png?v8",peach:"unicode/1f351.png?v8",peacock:"unicode/1f99a.png?v8",peanuts:"unicode/1f95c.png?v8",pear:"unicode/1f350.png?v8",pen:"unicode/1f58a.png?v8",pencil:"unicode/1f4dd.png?v8",pencil2:"unicode/270f.png?v8",penguin:"unicode/1f427.png?v8",pensive:"unicode/1f614.png?v8",people_holding_hands:"unicode/1f9d1-1f91d-1f9d1.png?v8",people_hugging:"unicode/1fac2.png?v8",performing_arts:"unicode/1f3ad.png?v8",persevere:"unicode/1f623.png?v8",person_bald:"unicode/1f9d1-1f9b2.png?v8",person_curly_hair:"unicode/1f9d1-1f9b1.png?v8",person_feeding_baby:"unicode/1f9d1-1f37c.png?v8",person_fencing:"unicode/1f93a.png?v8",person_in_manual_wheelchair:"unicode/1f9d1-1f9bd.png?v8",person_in_motorized_wheelchair:"unicode/1f9d1-1f9bc.png?v8",person_in_tuxedo:"unicode/1f935.png?v8",person_red_hair:"unicode/1f9d1-1f9b0.png?v8",person_white_hair:"unicode/1f9d1-1f9b3.png?v8",person_with_probing_cane:"unicode/1f9d1-1f9af.png?v8",person_with_turban:"unicode/1f473.png?v8",person_with_veil:"unicode/1f470.png?v8",peru:"unicode/1f1f5-1f1ea.png?v8",petri_dish:"unicode/1f9eb.png?v8",philippines:"unicode/1f1f5-1f1ed.png?v8",phone:"unicode/260e.png?v8",pick:"unicode/26cf.png?v8",pickup_truck:"unicode/1f6fb.png?v8",pie:"unicode/1f967.png?v8",pig:"unicode/1f437.png?v8",pig2:"unicode/1f416.png?v8",pig_nose:"unicode/1f43d.png?v8",pill:"unicode/1f48a.png?v8",pilot:"unicode/1f9d1-2708.png?v8",pinata:"unicode/1fa85.png?v8",pinched_fingers:"unicode/1f90c.png?v8",pinching_hand:"unicode/1f90f.png?v8",pineapple:"unicode/1f34d.png?v8",ping_pong:"unicode/1f3d3.png?v8",pirate_flag:"unicode/1f3f4-2620.png?v8",pisces:"unicode/2653.png?v8",pitcairn_islands:"unicode/1f1f5-1f1f3.png?v8",pizza:"unicode/1f355.png?v8",placard:"unicode/1faa7.png?v8",place_of_worship:"unicode/1f6d0.png?v8",plate_with_cutlery:"unicode/1f37d.png?v8",play_or_pause_button:"unicode/23ef.png?v8",pleading_face:"unicode/1f97a.png?v8",plunger:"unicode/1faa0.png?v8",point_down:"unicode/1f447.png?v8",point_left:"unicode/1f448.png?v8",point_right:"unicode/1f449.png?v8",point_up:"unicode/261d.png?v8",point_up_2:"unicode/1f446.png?v8",poland:"unicode/1f1f5-1f1f1.png?v8",polar_bear:"unicode/1f43b-2744.png?v8",police_car:"unicode/1f693.png?v8",police_officer:"unicode/1f46e.png?v8",policeman:"unicode/1f46e-2642.png?v8",policewoman:"unicode/1f46e-2640.png?v8",poodle:"unicode/1f429.png?v8",poop:"unicode/1f4a9.png?v8",popcorn:"unicode/1f37f.png?v8",portugal:"unicode/1f1f5-1f1f9.png?v8",post_office:"unicode/1f3e3.png?v8",postal_horn:"unicode/1f4ef.png?v8",postbox:"unicode/1f4ee.png?v8",potable_water:"unicode/1f6b0.png?v8",potato:"unicode/1f954.png?v8",potted_plant:"unicode/1fab4.png?v8",pouch:"unicode/1f45d.png?v8",poultry_leg:"unicode/1f357.png?v8",pound:"unicode/1f4b7.png?v8",pout:"unicode/1f621.png?v8",pouting_cat:"unicode/1f63e.png?v8",pouting_face:"unicode/1f64e.png?v8",pouting_man:"unicode/1f64e-2642.png?v8",pouting_woman:"unicode/1f64e-2640.png?v8",pray:"unicode/1f64f.png?v8",prayer_beads:"unicode/1f4ff.png?v8",pregnant_woman:"unicode/1f930.png?v8",pretzel:"unicode/1f968.png?v8",previous_track_button:"unicode/23ee.png?v8",prince:"unicode/1f934.png?v8",princess:"unicode/1f478.png?v8",printer:"unicode/1f5a8.png?v8",probing_cane:"unicode/1f9af.png?v8",puerto_rico:"unicode/1f1f5-1f1f7.png?v8",punch:"unicode/1f44a.png?v8",purple_circle:"unicode/1f7e3.png?v8",purple_heart:"unicode/1f49c.png?v8",purple_square:"unicode/1f7ea.png?v8",purse:"unicode/1f45b.png?v8",pushpin:"unicode/1f4cc.png?v8",put_litter_in_its_place:"unicode/1f6ae.png?v8",qatar:"unicode/1f1f6-1f1e6.png?v8",question:"unicode/2753.png?v8",rabbit:"unicode/1f430.png?v8",rabbit2:"unicode/1f407.png?v8",raccoon:"unicode/1f99d.png?v8",racehorse:"unicode/1f40e.png?v8",racing_car:"unicode/1f3ce.png?v8",radio:"unicode/1f4fb.png?v8",radio_button:"unicode/1f518.png?v8",radioactive:"unicode/2622.png?v8",rage:"unicode/1f621.png?v8",rage1:"rage1.png?v8",rage2:"rage2.png?v8",rage3:"rage3.png?v8",rage4:"rage4.png?v8",railway_car:"unicode/1f683.png?v8",railway_track:"unicode/1f6e4.png?v8",rainbow:"unicode/1f308.png?v8",rainbow_flag:"unicode/1f3f3-1f308.png?v8",raised_back_of_hand:"unicode/1f91a.png?v8",raised_eyebrow:"unicode/1f928.png?v8",raised_hand:"unicode/270b.png?v8",raised_hand_with_fingers_splayed:"unicode/1f590.png?v8",raised_hands:"unicode/1f64c.png?v8",raising_hand:"unicode/1f64b.png?v8",raising_hand_man:"unicode/1f64b-2642.png?v8",raising_hand_woman:"unicode/1f64b-2640.png?v8",ram:"unicode/1f40f.png?v8",ramen:"unicode/1f35c.png?v8",rat:"unicode/1f400.png?v8",razor:"unicode/1fa92.png?v8",receipt:"unicode/1f9fe.png?v8",record_button:"unicode/23fa.png?v8",recycle:"unicode/267b.png?v8",red_car:"unicode/1f697.png?v8",red_circle:"unicode/1f534.png?v8",red_envelope:"unicode/1f9e7.png?v8",red_haired_man:"unicode/1f468-1f9b0.png?v8",red_haired_woman:"unicode/1f469-1f9b0.png?v8",red_square:"unicode/1f7e5.png?v8",registered:"unicode/00ae.png?v8",relaxed:"unicode/263a.png?v8",relieved:"unicode/1f60c.png?v8",reminder_ribbon:"unicode/1f397.png?v8",repeat:"unicode/1f501.png?v8",repeat_one:"unicode/1f502.png?v8",rescue_worker_helmet:"unicode/26d1.png?v8",restroom:"unicode/1f6bb.png?v8",reunion:"unicode/1f1f7-1f1ea.png?v8",revolving_hearts:"unicode/1f49e.png?v8",rewind:"unicode/23ea.png?v8",rhinoceros:"unicode/1f98f.png?v8",ribbon:"unicode/1f380.png?v8",rice:"unicode/1f35a.png?v8",rice_ball:"unicode/1f359.png?v8",rice_cracker:"unicode/1f358.png?v8",rice_scene:"unicode/1f391.png?v8",right_anger_bubble:"unicode/1f5ef.png?v8",ring:"unicode/1f48d.png?v8",ringed_planet:"unicode/1fa90.png?v8",robot:"unicode/1f916.png?v8",rock:"unicode/1faa8.png?v8",rocket:"unicode/1f680.png?v8",rofl:"unicode/1f923.png?v8",roll_eyes:"unicode/1f644.png?v8",roll_of_paper:"unicode/1f9fb.png?v8",roller_coaster:"unicode/1f3a2.png?v8",roller_skate:"unicode/1f6fc.png?v8",romania:"unicode/1f1f7-1f1f4.png?v8",rooster:"unicode/1f413.png?v8",rose:"unicode/1f339.png?v8",rosette:"unicode/1f3f5.png?v8",rotating_light:"unicode/1f6a8.png?v8",round_pushpin:"unicode/1f4cd.png?v8",rowboat:"unicode/1f6a3.png?v8",rowing_man:"unicode/1f6a3-2642.png?v8",rowing_woman:"unicode/1f6a3-2640.png?v8",ru:"unicode/1f1f7-1f1fa.png?v8",rugby_football:"unicode/1f3c9.png?v8",runner:"unicode/1f3c3.png?v8",running:"unicode/1f3c3.png?v8",running_man:"unicode/1f3c3-2642.png?v8",running_shirt_with_sash:"unicode/1f3bd.png?v8",running_woman:"unicode/1f3c3-2640.png?v8",rwanda:"unicode/1f1f7-1f1fc.png?v8",sa:"unicode/1f202.png?v8",safety_pin:"unicode/1f9f7.png?v8",safety_vest:"unicode/1f9ba.png?v8",sagittarius:"unicode/2650.png?v8",sailboat:"unicode/26f5.png?v8",sake:"unicode/1f376.png?v8",salt:"unicode/1f9c2.png?v8",samoa:"unicode/1f1fc-1f1f8.png?v8",san_marino:"unicode/1f1f8-1f1f2.png?v8",sandal:"unicode/1f461.png?v8",sandwich:"unicode/1f96a.png?v8",santa:"unicode/1f385.png?v8",sao_tome_principe:"unicode/1f1f8-1f1f9.png?v8",sari:"unicode/1f97b.png?v8",sassy_man:"unicode/1f481-2642.png?v8",sassy_woman:"unicode/1f481-2640.png?v8",satellite:"unicode/1f4e1.png?v8",satisfied:"unicode/1f606.png?v8",saudi_arabia:"unicode/1f1f8-1f1e6.png?v8",sauna_man:"unicode/1f9d6-2642.png?v8",sauna_person:"unicode/1f9d6.png?v8",sauna_woman:"unicode/1f9d6-2640.png?v8",sauropod:"unicode/1f995.png?v8",saxophone:"unicode/1f3b7.png?v8",scarf:"unicode/1f9e3.png?v8",school:"unicode/1f3eb.png?v8",school_satchel:"unicode/1f392.png?v8",scientist:"unicode/1f9d1-1f52c.png?v8",scissors:"unicode/2702.png?v8",scorpion:"unicode/1f982.png?v8",scorpius:"unicode/264f.png?v8",scotland:"unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png?v8",scream:"unicode/1f631.png?v8",scream_cat:"unicode/1f640.png?v8",screwdriver:"unicode/1fa9b.png?v8",scroll:"unicode/1f4dc.png?v8",seal:"unicode/1f9ad.png?v8",seat:"unicode/1f4ba.png?v8",secret:"unicode/3299.png?v8",see_no_evil:"unicode/1f648.png?v8",seedling:"unicode/1f331.png?v8",selfie:"unicode/1f933.png?v8",senegal:"unicode/1f1f8-1f1f3.png?v8",serbia:"unicode/1f1f7-1f1f8.png?v8",service_dog:"unicode/1f415-1f9ba.png?v8",seven:"unicode/0037-20e3.png?v8",sewing_needle:"unicode/1faa1.png?v8",seychelles:"unicode/1f1f8-1f1e8.png?v8",shallow_pan_of_food:"unicode/1f958.png?v8",shamrock:"unicode/2618.png?v8",shark:"unicode/1f988.png?v8",shaved_ice:"unicode/1f367.png?v8",sheep:"unicode/1f411.png?v8",shell:"unicode/1f41a.png?v8",shield:"unicode/1f6e1.png?v8",shinto_shrine:"unicode/26e9.png?v8",ship:"unicode/1f6a2.png?v8",shipit:"shipit.png?v8",shirt:"unicode/1f455.png?v8",shit:"unicode/1f4a9.png?v8",shoe:"unicode/1f45e.png?v8",shopping:"unicode/1f6cd.png?v8",shopping_cart:"unicode/1f6d2.png?v8",shorts:"unicode/1fa73.png?v8",shower:"unicode/1f6bf.png?v8",shrimp:"unicode/1f990.png?v8",shrug:"unicode/1f937.png?v8",shushing_face:"unicode/1f92b.png?v8",sierra_leone:"unicode/1f1f8-1f1f1.png?v8",signal_strength:"unicode/1f4f6.png?v8",singapore:"unicode/1f1f8-1f1ec.png?v8",singer:"unicode/1f9d1-1f3a4.png?v8",sint_maarten:"unicode/1f1f8-1f1fd.png?v8",six:"unicode/0036-20e3.png?v8",six_pointed_star:"unicode/1f52f.png?v8",skateboard:"unicode/1f6f9.png?v8",ski:"unicode/1f3bf.png?v8",skier:"unicode/26f7.png?v8",skull:"unicode/1f480.png?v8",skull_and_crossbones:"unicode/2620.png?v8",skunk:"unicode/1f9a8.png?v8",sled:"unicode/1f6f7.png?v8",sleeping:"unicode/1f634.png?v8",sleeping_bed:"unicode/1f6cc.png?v8",sleepy:"unicode/1f62a.png?v8",slightly_frowning_face:"unicode/1f641.png?v8",slightly_smiling_face:"unicode/1f642.png?v8",slot_machine:"unicode/1f3b0.png?v8",sloth:"unicode/1f9a5.png?v8",slovakia:"unicode/1f1f8-1f1f0.png?v8",slovenia:"unicode/1f1f8-1f1ee.png?v8",small_airplane:"unicode/1f6e9.png?v8",small_blue_diamond:"unicode/1f539.png?v8",small_orange_diamond:"unicode/1f538.png?v8",small_red_triangle:"unicode/1f53a.png?v8",small_red_triangle_down:"unicode/1f53b.png?v8",smile:"unicode/1f604.png?v8",smile_cat:"unicode/1f638.png?v8",smiley:"unicode/1f603.png?v8",smiley_cat:"unicode/1f63a.png?v8",smiling_face_with_tear:"unicode/1f972.png?v8",smiling_face_with_three_hearts:"unicode/1f970.png?v8",smiling_imp:"unicode/1f608.png?v8",smirk:"unicode/1f60f.png?v8",smirk_cat:"unicode/1f63c.png?v8",smoking:"unicode/1f6ac.png?v8",snail:"unicode/1f40c.png?v8",snake:"unicode/1f40d.png?v8",sneezing_face:"unicode/1f927.png?v8",snowboarder:"unicode/1f3c2.png?v8",snowflake:"unicode/2744.png?v8",snowman:"unicode/26c4.png?v8",snowman_with_snow:"unicode/2603.png?v8",soap:"unicode/1f9fc.png?v8",sob:"unicode/1f62d.png?v8",soccer:"unicode/26bd.png?v8",socks:"unicode/1f9e6.png?v8",softball:"unicode/1f94e.png?v8",solomon_islands:"unicode/1f1f8-1f1e7.png?v8",somalia:"unicode/1f1f8-1f1f4.png?v8",soon:"unicode/1f51c.png?v8",sos:"unicode/1f198.png?v8",sound:"unicode/1f509.png?v8",south_africa:"unicode/1f1ff-1f1e6.png?v8",south_georgia_south_sandwich_islands:"unicode/1f1ec-1f1f8.png?v8",south_sudan:"unicode/1f1f8-1f1f8.png?v8",space_invader:"unicode/1f47e.png?v8",spades:"unicode/2660.png?v8",spaghetti:"unicode/1f35d.png?v8",sparkle:"unicode/2747.png?v8",sparkler:"unicode/1f387.png?v8",sparkles:"unicode/2728.png?v8",sparkling_heart:"unicode/1f496.png?v8",speak_no_evil:"unicode/1f64a.png?v8",speaker:"unicode/1f508.png?v8",speaking_head:"unicode/1f5e3.png?v8",speech_balloon:"unicode/1f4ac.png?v8",speedboat:"unicode/1f6a4.png?v8",spider:"unicode/1f577.png?v8",spider_web:"unicode/1f578.png?v8",spiral_calendar:"unicode/1f5d3.png?v8",spiral_notepad:"unicode/1f5d2.png?v8",sponge:"unicode/1f9fd.png?v8",spoon:"unicode/1f944.png?v8",squid:"unicode/1f991.png?v8",sri_lanka:"unicode/1f1f1-1f1f0.png?v8",st_barthelemy:"unicode/1f1e7-1f1f1.png?v8",st_helena:"unicode/1f1f8-1f1ed.png?v8",st_kitts_nevis:"unicode/1f1f0-1f1f3.png?v8",st_lucia:"unicode/1f1f1-1f1e8.png?v8",st_martin:"unicode/1f1f2-1f1eb.png?v8",st_pierre_miquelon:"unicode/1f1f5-1f1f2.png?v8",st_vincent_grenadines:"unicode/1f1fb-1f1e8.png?v8",stadium:"unicode/1f3df.png?v8",standing_man:"unicode/1f9cd-2642.png?v8",standing_person:"unicode/1f9cd.png?v8",standing_woman:"unicode/1f9cd-2640.png?v8",star:"unicode/2b50.png?v8",star2:"unicode/1f31f.png?v8",star_and_crescent:"unicode/262a.png?v8",star_of_david:"unicode/2721.png?v8",star_struck:"unicode/1f929.png?v8",stars:"unicode/1f320.png?v8",station:"unicode/1f689.png?v8",statue_of_liberty:"unicode/1f5fd.png?v8",steam_locomotive:"unicode/1f682.png?v8",stethoscope:"unicode/1fa7a.png?v8",stew:"unicode/1f372.png?v8",stop_button:"unicode/23f9.png?v8",stop_sign:"unicode/1f6d1.png?v8",stopwatch:"unicode/23f1.png?v8",straight_ruler:"unicode/1f4cf.png?v8",strawberry:"unicode/1f353.png?v8",stuck_out_tongue:"unicode/1f61b.png?v8",stuck_out_tongue_closed_eyes:"unicode/1f61d.png?v8",stuck_out_tongue_winking_eye:"unicode/1f61c.png?v8",student:"unicode/1f9d1-1f393.png?v8",studio_microphone:"unicode/1f399.png?v8",stuffed_flatbread:"unicode/1f959.png?v8",sudan:"unicode/1f1f8-1f1e9.png?v8",sun_behind_large_cloud:"unicode/1f325.png?v8",sun_behind_rain_cloud:"unicode/1f326.png?v8",sun_behind_small_cloud:"unicode/1f324.png?v8",sun_with_face:"unicode/1f31e.png?v8",sunflower:"unicode/1f33b.png?v8",sunglasses:"unicode/1f60e.png?v8",sunny:"unicode/2600.png?v8",sunrise:"unicode/1f305.png?v8",sunrise_over_mountains:"unicode/1f304.png?v8",superhero:"unicode/1f9b8.png?v8",superhero_man:"unicode/1f9b8-2642.png?v8",superhero_woman:"unicode/1f9b8-2640.png?v8",supervillain:"unicode/1f9b9.png?v8",supervillain_man:"unicode/1f9b9-2642.png?v8",supervillain_woman:"unicode/1f9b9-2640.png?v8",surfer:"unicode/1f3c4.png?v8",surfing_man:"unicode/1f3c4-2642.png?v8",surfing_woman:"unicode/1f3c4-2640.png?v8",suriname:"unicode/1f1f8-1f1f7.png?v8",sushi:"unicode/1f363.png?v8",suspect:"suspect.png?v8",suspension_railway:"unicode/1f69f.png?v8",svalbard_jan_mayen:"unicode/1f1f8-1f1ef.png?v8",swan:"unicode/1f9a2.png?v8",swaziland:"unicode/1f1f8-1f1ff.png?v8",sweat:"unicode/1f613.png?v8",sweat_drops:"unicode/1f4a6.png?v8",sweat_smile:"unicode/1f605.png?v8",sweden:"unicode/1f1f8-1f1ea.png?v8",sweet_potato:"unicode/1f360.png?v8",swim_brief:"unicode/1fa72.png?v8",swimmer:"unicode/1f3ca.png?v8",swimming_man:"unicode/1f3ca-2642.png?v8",swimming_woman:"unicode/1f3ca-2640.png?v8",switzerland:"unicode/1f1e8-1f1ed.png?v8",symbols:"unicode/1f523.png?v8",synagogue:"unicode/1f54d.png?v8",syria:"unicode/1f1f8-1f1fe.png?v8",syringe:"unicode/1f489.png?v8","t-rex":"unicode/1f996.png?v8",taco:"unicode/1f32e.png?v8",tada:"unicode/1f389.png?v8",taiwan:"unicode/1f1f9-1f1fc.png?v8",tajikistan:"unicode/1f1f9-1f1ef.png?v8",takeout_box:"unicode/1f961.png?v8",tamale:"unicode/1fad4.png?v8",tanabata_tree:"unicode/1f38b.png?v8",tangerine:"unicode/1f34a.png?v8",tanzania:"unicode/1f1f9-1f1ff.png?v8",taurus:"unicode/2649.png?v8",taxi:"unicode/1f695.png?v8",tea:"unicode/1f375.png?v8",teacher:"unicode/1f9d1-1f3eb.png?v8",teapot:"unicode/1fad6.png?v8",technologist:"unicode/1f9d1-1f4bb.png?v8",teddy_bear:"unicode/1f9f8.png?v8",telephone:"unicode/260e.png?v8",telephone_receiver:"unicode/1f4de.png?v8",telescope:"unicode/1f52d.png?v8",tennis:"unicode/1f3be.png?v8",tent:"unicode/26fa.png?v8",test_tube:"unicode/1f9ea.png?v8",thailand:"unicode/1f1f9-1f1ed.png?v8",thermometer:"unicode/1f321.png?v8",thinking:"unicode/1f914.png?v8",thong_sandal:"unicode/1fa74.png?v8",thought_balloon:"unicode/1f4ad.png?v8",thread:"unicode/1f9f5.png?v8",three:"unicode/0033-20e3.png?v8",thumbsdown:"unicode/1f44e.png?v8",thumbsup:"unicode/1f44d.png?v8",ticket:"unicode/1f3ab.png?v8",tickets:"unicode/1f39f.png?v8",tiger:"unicode/1f42f.png?v8",tiger2:"unicode/1f405.png?v8",timer_clock:"unicode/23f2.png?v8",timor_leste:"unicode/1f1f9-1f1f1.png?v8",tipping_hand_man:"unicode/1f481-2642.png?v8",tipping_hand_person:"unicode/1f481.png?v8",tipping_hand_woman:"unicode/1f481-2640.png?v8",tired_face:"unicode/1f62b.png?v8",tm:"unicode/2122.png?v8",togo:"unicode/1f1f9-1f1ec.png?v8",toilet:"unicode/1f6bd.png?v8",tokelau:"unicode/1f1f9-1f1f0.png?v8",tokyo_tower:"unicode/1f5fc.png?v8",tomato:"unicode/1f345.png?v8",tonga:"unicode/1f1f9-1f1f4.png?v8",tongue:"unicode/1f445.png?v8",toolbox:"unicode/1f9f0.png?v8",tooth:"unicode/1f9b7.png?v8",toothbrush:"unicode/1faa5.png?v8",top:"unicode/1f51d.png?v8",tophat:"unicode/1f3a9.png?v8",tornado:"unicode/1f32a.png?v8",tr:"unicode/1f1f9-1f1f7.png?v8",trackball:"unicode/1f5b2.png?v8",tractor:"unicode/1f69c.png?v8",traffic_light:"unicode/1f6a5.png?v8",train:"unicode/1f68b.png?v8",train2:"unicode/1f686.png?v8",tram:"unicode/1f68a.png?v8",transgender_flag:"unicode/1f3f3-26a7.png?v8",transgender_symbol:"unicode/26a7.png?v8",triangular_flag_on_post:"unicode/1f6a9.png?v8",triangular_ruler:"unicode/1f4d0.png?v8",trident:"unicode/1f531.png?v8",trinidad_tobago:"unicode/1f1f9-1f1f9.png?v8",tristan_da_cunha:"unicode/1f1f9-1f1e6.png?v8",triumph:"unicode/1f624.png?v8",trolleybus:"unicode/1f68e.png?v8",trollface:"trollface.png?v8",trophy:"unicode/1f3c6.png?v8",tropical_drink:"unicode/1f379.png?v8",tropical_fish:"unicode/1f420.png?v8",truck:"unicode/1f69a.png?v8",trumpet:"unicode/1f3ba.png?v8",tshirt:"unicode/1f455.png?v8",tulip:"unicode/1f337.png?v8",tumbler_glass:"unicode/1f943.png?v8",tunisia:"unicode/1f1f9-1f1f3.png?v8",turkey:"unicode/1f983.png?v8",turkmenistan:"unicode/1f1f9-1f1f2.png?v8",turks_caicos_islands:"unicode/1f1f9-1f1e8.png?v8",turtle:"unicode/1f422.png?v8",tuvalu:"unicode/1f1f9-1f1fb.png?v8",tv:"unicode/1f4fa.png?v8",twisted_rightwards_arrows:"unicode/1f500.png?v8",two:"unicode/0032-20e3.png?v8",two_hearts:"unicode/1f495.png?v8",two_men_holding_hands:"unicode/1f46c.png?v8",two_women_holding_hands:"unicode/1f46d.png?v8",u5272:"unicode/1f239.png?v8",u5408:"unicode/1f234.png?v8",u55b6:"unicode/1f23a.png?v8",u6307:"unicode/1f22f.png?v8",u6708:"unicode/1f237.png?v8",u6709:"unicode/1f236.png?v8",u6e80:"unicode/1f235.png?v8",u7121:"unicode/1f21a.png?v8",u7533:"unicode/1f238.png?v8",u7981:"unicode/1f232.png?v8",u7a7a:"unicode/1f233.png?v8",uganda:"unicode/1f1fa-1f1ec.png?v8",uk:"unicode/1f1ec-1f1e7.png?v8",ukraine:"unicode/1f1fa-1f1e6.png?v8",umbrella:"unicode/2614.png?v8",unamused:"unicode/1f612.png?v8",underage:"unicode/1f51e.png?v8",unicorn:"unicode/1f984.png?v8",united_arab_emirates:"unicode/1f1e6-1f1ea.png?v8",united_nations:"unicode/1f1fa-1f1f3.png?v8",unlock:"unicode/1f513.png?v8",up:"unicode/1f199.png?v8",upside_down_face:"unicode/1f643.png?v8",uruguay:"unicode/1f1fa-1f1fe.png?v8",us:"unicode/1f1fa-1f1f8.png?v8",us_outlying_islands:"unicode/1f1fa-1f1f2.png?v8",us_virgin_islands:"unicode/1f1fb-1f1ee.png?v8",uzbekistan:"unicode/1f1fa-1f1ff.png?v8",v:"unicode/270c.png?v8",vampire:"unicode/1f9db.png?v8",vampire_man:"unicode/1f9db-2642.png?v8",vampire_woman:"unicode/1f9db-2640.png?v8",vanuatu:"unicode/1f1fb-1f1fa.png?v8",vatican_city:"unicode/1f1fb-1f1e6.png?v8",venezuela:"unicode/1f1fb-1f1ea.png?v8",vertical_traffic_light:"unicode/1f6a6.png?v8",vhs:"unicode/1f4fc.png?v8",vibration_mode:"unicode/1f4f3.png?v8",video_camera:"unicode/1f4f9.png?v8",video_game:"unicode/1f3ae.png?v8",vietnam:"unicode/1f1fb-1f1f3.png?v8",violin:"unicode/1f3bb.png?v8",virgo:"unicode/264d.png?v8",volcano:"unicode/1f30b.png?v8",volleyball:"unicode/1f3d0.png?v8",vomiting_face:"unicode/1f92e.png?v8",vs:"unicode/1f19a.png?v8",vulcan_salute:"unicode/1f596.png?v8",waffle:"unicode/1f9c7.png?v8",wales:"unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png?v8",walking:"unicode/1f6b6.png?v8",walking_man:"unicode/1f6b6-2642.png?v8",walking_woman:"unicode/1f6b6-2640.png?v8",wallis_futuna:"unicode/1f1fc-1f1eb.png?v8",waning_crescent_moon:"unicode/1f318.png?v8",waning_gibbous_moon:"unicode/1f316.png?v8",warning:"unicode/26a0.png?v8",wastebasket:"unicode/1f5d1.png?v8",watch:"unicode/231a.png?v8",water_buffalo:"unicode/1f403.png?v8",water_polo:"unicode/1f93d.png?v8",watermelon:"unicode/1f349.png?v8",wave:"unicode/1f44b.png?v8",wavy_dash:"unicode/3030.png?v8",waxing_crescent_moon:"unicode/1f312.png?v8",waxing_gibbous_moon:"unicode/1f314.png?v8",wc:"unicode/1f6be.png?v8",weary:"unicode/1f629.png?v8",wedding:"unicode/1f492.png?v8",weight_lifting:"unicode/1f3cb.png?v8",weight_lifting_man:"unicode/1f3cb-2642.png?v8",weight_lifting_woman:"unicode/1f3cb-2640.png?v8",western_sahara:"unicode/1f1ea-1f1ed.png?v8",whale:"unicode/1f433.png?v8",whale2:"unicode/1f40b.png?v8",wheel_of_dharma:"unicode/2638.png?v8",wheelchair:"unicode/267f.png?v8",white_check_mark:"unicode/2705.png?v8",white_circle:"unicode/26aa.png?v8",white_flag:"unicode/1f3f3.png?v8",white_flower:"unicode/1f4ae.png?v8",white_haired_man:"unicode/1f468-1f9b3.png?v8",white_haired_woman:"unicode/1f469-1f9b3.png?v8",white_heart:"unicode/1f90d.png?v8",white_large_square:"unicode/2b1c.png?v8",white_medium_small_square:"unicode/25fd.png?v8",white_medium_square:"unicode/25fb.png?v8",white_small_square:"unicode/25ab.png?v8",white_square_button:"unicode/1f533.png?v8",wilted_flower:"unicode/1f940.png?v8",wind_chime:"unicode/1f390.png?v8",wind_face:"unicode/1f32c.png?v8",window:"unicode/1fa9f.png?v8",wine_glass:"unicode/1f377.png?v8",wink:"unicode/1f609.png?v8",wolf:"unicode/1f43a.png?v8",woman:"unicode/1f469.png?v8",woman_artist:"unicode/1f469-1f3a8.png?v8",woman_astronaut:"unicode/1f469-1f680.png?v8",woman_beard:"unicode/1f9d4-2640.png?v8",woman_cartwheeling:"unicode/1f938-2640.png?v8",woman_cook:"unicode/1f469-1f373.png?v8",woman_dancing:"unicode/1f483.png?v8",woman_facepalming:"unicode/1f926-2640.png?v8",woman_factory_worker:"unicode/1f469-1f3ed.png?v8",woman_farmer:"unicode/1f469-1f33e.png?v8",woman_feeding_baby:"unicode/1f469-1f37c.png?v8",woman_firefighter:"unicode/1f469-1f692.png?v8",woman_health_worker:"unicode/1f469-2695.png?v8",woman_in_manual_wheelchair:"unicode/1f469-1f9bd.png?v8",woman_in_motorized_wheelchair:"unicode/1f469-1f9bc.png?v8",woman_in_tuxedo:"unicode/1f935-2640.png?v8",woman_judge:"unicode/1f469-2696.png?v8",woman_juggling:"unicode/1f939-2640.png?v8",woman_mechanic:"unicode/1f469-1f527.png?v8",woman_office_worker:"unicode/1f469-1f4bc.png?v8",woman_pilot:"unicode/1f469-2708.png?v8",woman_playing_handball:"unicode/1f93e-2640.png?v8",woman_playing_water_polo:"unicode/1f93d-2640.png?v8",woman_scientist:"unicode/1f469-1f52c.png?v8",woman_shrugging:"unicode/1f937-2640.png?v8",woman_singer:"unicode/1f469-1f3a4.png?v8",woman_student:"unicode/1f469-1f393.png?v8",woman_teacher:"unicode/1f469-1f3eb.png?v8",woman_technologist:"unicode/1f469-1f4bb.png?v8",woman_with_headscarf:"unicode/1f9d5.png?v8",woman_with_probing_cane:"unicode/1f469-1f9af.png?v8",woman_with_turban:"unicode/1f473-2640.png?v8",woman_with_veil:"unicode/1f470-2640.png?v8",womans_clothes:"unicode/1f45a.png?v8",womans_hat:"unicode/1f452.png?v8",women_wrestling:"unicode/1f93c-2640.png?v8",womens:"unicode/1f6ba.png?v8",wood:"unicode/1fab5.png?v8",woozy_face:"unicode/1f974.png?v8",world_map:"unicode/1f5fa.png?v8",worm:"unicode/1fab1.png?v8",worried:"unicode/1f61f.png?v8",wrench:"unicode/1f527.png?v8",wrestling:"unicode/1f93c.png?v8",writing_hand:"unicode/270d.png?v8",x:"unicode/274c.png?v8",yarn:"unicode/1f9f6.png?v8",yawning_face:"unicode/1f971.png?v8",yellow_circle:"unicode/1f7e1.png?v8",yellow_heart:"unicode/1f49b.png?v8",yellow_square:"unicode/1f7e8.png?v8",yemen:"unicode/1f1fe-1f1ea.png?v8",yen:"unicode/1f4b4.png?v8",yin_yang:"unicode/262f.png?v8",yo_yo:"unicode/1fa80.png?v8",yum:"unicode/1f60b.png?v8",zambia:"unicode/1f1ff-1f1f2.png?v8",zany_face:"unicode/1f92a.png?v8",zap:"unicode/26a1.png?v8",zebra:"unicode/1f993.png?v8",zero:"unicode/0030-20e3.png?v8",zimbabwe:"unicode/1f1ff-1f1fc.png?v8",zipper_mouth_face:"unicode/1f910.png?v8",zombie:"unicode/1f9df.png?v8",zombie_man:"unicode/1f9df-2642.png?v8",zombie_woman:"unicode/1f9df-2640.png?v8",zzz:"unicode/1f4a4.png?v8"};window&&window.console&&console.info("Docsify emoji plugin has been deprecated as of v4.13"),window.emojify=function(n,e){return!1===Object.prototype.hasOwnProperty.call(i,e)?n:''+e+''}}(); \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/static/js/prism-bash.min.js b/ruoyi-admin/src/main/resources/static/static/js/prism-bash.min.js deleted file mode 100644 index f1659f1e..00000000 --- a/ruoyi-admin/src/main/resources/static/static/js/prism-bash.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},parameter:{pattern:/(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","parameter","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=n.variable[1].inside,i=0;i]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var s=e.languages.extend("typescript",{});delete s["class-name"],e.languages.typescript["class-name"].inside=s,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:s}}}}),e.languages.ts=e.languages.typescript}(Prism); \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/static/js/search.min.js b/ruoyi-admin/src/main/resources/static/static/js/search.min.js deleted file mode 100644 index 9719f653..00000000 --- a/ruoyi-admin/src/main/resources/static/static/js/search.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(){function u(e){return e.replace(//,"").replace(/{docsify-ignore}/,"").replace(//,"").replace(/{docsify-ignore-all}/,"").trim()}var f={},m={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function g(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function y(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function v(e){return e.text||"list"!==e.type||(e.text=e.raw),e.text}function b(o,e,s,c){void 0===e&&(e="");var d,e=window.marked.lexer(e),l=window.Docsify.slugify,p={},h="";return e.forEach(function(e,n){var t,a,i,r;"heading"===e.type&&e.depth<=c?(t=(a=(i=e.text,r={},{str:i=(i=void 0===i?"":i)&&i.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(r[n]=t&&t.replace(/"/g,"")||!0,""):e}).trim(),config:r})).str,i=a.config,a=u(e.text),d=i.id?s.toURL(o,{id:l(i.id)}):s.toURL(o,{id:l(g(a))}),t&&(h=u(t)),p[d]={slug:d,title:h,body:""}):(0===n&&(d=s.toURL(o),p[d]={slug:d,title:"/"!==o?o.slice(1):"Home Page",body:e.text||""}),d&&(p[d]?p[d].body?(e.text=y(e),e.text=v(e),p[d].body+="\n"+(e.text||"")):(e.text=y(e),e.text=v(e),p[d].body=e.text||""):p[d]={slug:d,title:"",body:""}))}),l.clear(),p}function p(e){return e&&e.normalize?e.normalize("NFD").replace(/[\u0300-\u036f]/g,""):e}function o(e){var n=[],t=[];Object.keys(f).forEach(function(n){t=t.concat(Object.keys(f[n]).map(function(e){return f[n][e]}))});var a=(e=e.trim()).split(/[\s\-,\\/]+/);1!==a.length&&(a=[].concat(e,a));for(var i=0;il.length&&(t=l.length),a=c&&"..."+c.substring(n,t).replace(a,function(e){return''+e+""})+"...",o+=a)}),0\n\n

    '+e.title+"

    \n

    "+e.content+"

    \n
    \n"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=r||'

    '+c+"

    ",s.hideOtherSidebarContent&&(i&&i.classList.add("hide"),n&&n.classList.add("hide"))}function l(e){s=e}function h(e,n){var t,a,i=n.router.parse().query.s;l(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n=Docsify.dom.create("div",'
    \n \n
    \n \n \n \n \n \n
    \n
    \n
    \n '),e=Docsify.dom.find("aside");Docsify.dom.toggleClass(n,"search"),Docsify.dom.before(e,n)}(i),n=Docsify.dom.find("div.search"),a=Docsify.dom.find(n,"input"),e=Docsify.dom.find(n,".input-wrap"),Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(a,"input",function(n){clearTimeout(t),t=setTimeout(function(e){return d(n.target.value.trim())},100)}),Docsify.dom.on(e,"click",function(e){"INPUT"!==e.target.tagName&&(a.value="",d())}),i&&setTimeout(function(e){return d(i)},500)}function x(e,n){var t,a,i,r,o;l(e),t=e.placeholder,a=n.route.path,(r=Docsify.dom.getNode('.search input[type="search"]'))&&("string"==typeof t?r.placeholder=t:(i=Object.keys(t).filter(function(e){return-1u.scrollOffset&&setTimeout(a,150))}),window.addEventListener("resize",a);var f={open:i,close:a,toggle:o,update:function(){var e=0 com.squareup.okhttp3 okhttp + ${okhttp.version}
    com.squareup.okhttp3 okhttp-sse + ${okhttp.version} com.squareup.okhttp3 logging-interceptor + ${okhttp.version} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/constant/RegexConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/constant/RegexConstants.java new file mode 100644 index 00000000..0e9abd8a --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/constant/RegexConstants.java @@ -0,0 +1,59 @@ +package org.ruoyi.common.core.constant; + +import cn.hutool.core.lang.RegexPool; + +/** + * 常用正则表达式字符串 + *

    + * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/ + * + * @author Feng + */ +public interface RegexConstants extends RegexPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; + + /** + * 权限标识必须符合以下格式: + * 1. 标准格式:xxx:yyy:zzz + * - 第一部分(xxx):只能包含字母、数字和下划线(_),不能使用 `*` + * - 第二部分(yyy):可以包含字母、数字、下划线(_)和 `*` + * - 第三部分(zzz):可以包含字母、数字、下划线(_)和 `*` + * 2. 允许空字符串(""),表示没有权限标识 + */ + 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表示正常,1表示停用) + */ + String STATUS = "^[01]$"; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/constant/SystemConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/constant/SystemConstants.java new file mode 100644 index 00000000..adde6d58 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/constant/SystemConstants.java @@ -0,0 +1,80 @@ +package org.ruoyi.common.core.constant; + +/** + * 系统常量信息 + * + * @author Lion Li + */ +public interface SystemConstants { + + /** + * 正常状态 + */ + String NORMAL = "0"; + + /** + * 异常状态 + */ + String DISABLE = "1"; + + /** + * 是否为系统默认(是) + */ + String YES = "Y"; + + /** + * 是否为系统默认(否) + */ + String NO = "N"; + + /** + * 是否菜单外链(是) + */ + String YES_FRAME = "0"; + + /** + * 是否菜单外链(否) + */ + String NO_FRAME = "1"; + + /** + * 菜单类型(目录) + */ + String TYPE_DIR = "M"; + + /** + * 菜单类型(菜单) + */ + String TYPE_MENU = "C"; + + /** + * 菜单类型(按钮) + */ + String TYPE_BUTTON = "F"; + + /** + * Layout组件标识 + */ + String LAYOUT = "Layout"; + + /** + * ParentView组件标识 + */ + String PARENT_VIEW = "ParentView"; + + /** + * InnerLink组件标识 + */ + String INNER_LINK = "InnerLink"; + + /** + * 超级管理员ID + */ + Long SUPER_ADMIN_ID = 1L; + + /** + * 根部门祖级列表 + */ + String ROOT_DEPT_ANCESTORS = "0"; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/factory/RegexPatternPoolFactory.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/factory/RegexPatternPoolFactory.java new file mode 100644 index 00000000..c15bdbac --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/factory/RegexPatternPoolFactory.java @@ -0,0 +1,53 @@ +package org.ruoyi.common.core.factory; + +import cn.hutool.core.lang.PatternPool; +import org.ruoyi.common.core.constant.RegexConstants; + + +import java.util.regex.Pattern; + +/** + * 正则表达式模式池工厂 + *

    初始化的时候将正则表达式加入缓存池当中

    + *

    提高正则表达式的性能,避免重复编译相同的正则表达式

    + * + * @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表示正常,1表示停用) + */ + public static final Pattern STATUS = get(RegexConstants.STATUS); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/factory/YmlPropertySourceFactory.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/factory/YmlPropertySourceFactory.java new file mode 100644 index 00000000..3b356d82 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/factory/YmlPropertySourceFactory.java @@ -0,0 +1,32 @@ +package org.ruoyi.common.core.factory; + + +import org.ruoyi.common.core.utils.StringUtils; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.DefaultPropertySourceFactory; +import org.springframework.core.io.support.EncodedResource; + +import java.io.IOException; + +/** + * yml 配置源工厂 + * + * @author Lion Li + */ +public class YmlPropertySourceFactory extends DefaultPropertySourceFactory { + + @Override + public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { + String sourceName = resource.getResource().getFilename(); + if (StringUtils.isNotBlank(sourceName) && StringUtils.endsWithAny(sourceName, ".yml", ".yaml")) { + YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); + factory.setResources(resource.getResource()); + factory.afterPropertiesSet(); + return new PropertiesPropertySource(sourceName, factory.getObject()); + } + return super.createPropertySource(name, resource); + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/utils/ObjectUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/utils/ObjectUtils.java new file mode 100644 index 00000000..7f7d5910 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/utils/ObjectUtils.java @@ -0,0 +1,60 @@ +package org.ruoyi.common.core.utils; + +import cn.hutool.core.util.ObjectUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.function.Function; + +/** + * 对象工具类 + * + * @author 秋辞未寒 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ObjectUtils extends ObjectUtil { + + /** + * 如果对象不为空,则获取对象中的某个字段 ObjectUtils.notNullGetter(user, User::getName); + * + * @param obj 对象 + * @param func 获取方法 + * @return 对象字段 + */ + public static E notNullGetter(T obj, Function func) { + if (isNotNull(obj) && isNotNull(func)) { + return func.apply(obj); + } + return null; + } + + /** + * 如果对象不为空,则获取对象中的某个字段,否则返回默认值 + * + * @param obj 对象 + * @param func 获取方法 + * @param defaultValue 默认值 + * @return 对象字段 + */ + public static E notNullGetter(T obj, Function func, E defaultValue) { + if (isNotNull(obj) && isNotNull(func)) { + return func.apply(obj); + } + return defaultValue; + } + + /** + * 如果值不为空,则返回值,否则返回默认值 + * + * @param obj 对象 + * @param defaultValue 默认值 + * @return 对象字段 + */ + public static T notNull(T obj, T defaultValue) { + if (isNotNull(obj)) { + return obj; + } + return defaultValue; + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/utils/SpringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/utils/SpringUtils.java index 99937c79..bd5dc544 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/utils/SpringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/ruoyi/common/core/utils/SpringUtils.java @@ -1,9 +1,10 @@ package org.ruoyi.common.core.utils; 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; /** @@ -48,7 +49,7 @@ public final class SpringUtils extends SpringUtil { */ @SuppressWarnings("unchecked") public static T getAopProxy(T invoker) { - return (T) AopContext.currentProxy(); + return (T) getBean(invoker.getClass()); } @@ -59,4 +60,8 @@ public final class SpringUtils extends SpringUtil { return getApplicationContext(); } + public static boolean isVirtual() { + return Threading.VIRTUAL.isActive(getBean(Environment.class)); + } + } diff --git a/ruoyi-common/ruoyi-common-encrypt/pom.xml b/ruoyi-common/ruoyi-common-encrypt/pom.xml index da85259e..28608bd5 100644 --- a/ruoyi-common/ruoyi-common-encrypt/pom.xml +++ b/ruoyi-common/ruoyi-common-encrypt/pom.xml @@ -23,11 +23,6 @@ ruoyi-common-core - - org.mybatis.spring.boot - mybatis-spring-boot-starter - - org.bouncycastle bcprov-jdk15to18 @@ -38,6 +33,23 @@ hutool-crypto + + org.springframework + spring-webmvc + + + + com.baomidou + mybatis-plus-spring-boot3-starter + true + + + org.mybatis + mybatis-spring + + + + diff --git a/ruoyi-common/ruoyi-common-live/pom.xml b/ruoyi-common/ruoyi-common-live/pom.xml index 9b9db268..26082fcd 100644 --- a/ruoyi-common/ruoyi-common-live/pom.xml +++ b/ruoyi-common/ruoyi-common-live/pom.xml @@ -33,7 +33,7 @@ 1.13.0 2.16.0 - 5.8.24 + 5.8.35 4.1.104.Final 1.4.12 1.18.30 diff --git a/ruoyi-common/ruoyi-common-mybatis/pom.xml b/ruoyi-common/ruoyi-common-mybatis/pom.xml index 0f6505e0..138732c5 100644 --- a/ruoyi-common/ruoyi-common-mybatis/pom.xml +++ b/ruoyi-common/ruoyi-common-mybatis/pom.xml @@ -6,7 +6,6 @@ org.ruoyi ruoyi-common ${revision} - ../pom.xml 4.0.0 @@ -30,12 +29,17 @@ com.baomidou - dynamic-datasource-spring-boot-starter + dynamic-datasource-spring-boot3-starter com.baomidou - mybatis-plus-boot-starter + mybatis-plus-spring-boot3-starter + + + + com.baomidou + mybatis-plus-jsqlparser diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java deleted file mode 100644 index f0a50a2d..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaHeaderProcessor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2018 organization baomidou - * - * 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 com.baomidou.dynamic.datasource.processor.jakarta; - -import com.baomidou.dynamic.datasource.processor.DsProcessor; -import jakarta.servlet.http.HttpServletRequest; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -/** - * @author TaoYu - * @since 3.6.0 - */ -public class DsJakartaHeaderProcessor extends DsProcessor { - - /** - * header prefix - */ - private static final String HEADER_PREFIX = "#header"; - - @Override - public boolean matches(String key) { - return key.startsWith(HEADER_PREFIX); - } - - @Override - public String doDetermineDatasource(MethodInvocation invocation, String key) { - HttpServletRequest request = (HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); - return request.getHeader(key.substring(8)); - } -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java deleted file mode 100644 index 0ea8a130..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/com/baomidou/dynamic/datasource/processor/jakarta/DsJakartaSessionProcessor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2018 organization baomidou - * - * 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 com.baomidou.dynamic.datasource.processor.jakarta; - -import com.baomidou.dynamic.datasource.processor.DsProcessor; -import jakarta.servlet.http.HttpServletRequest; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - - -/** - * @author TaoYu - * @since 3.6.0 - */ -public class DsJakartaSessionProcessor extends DsProcessor { - - /** - * session开头 - */ - private static final String SESSION_PREFIX = "#session"; - - @Override - public boolean matches(String key) { - return key.startsWith(SESSION_PREFIX); - } - - @Override - public String doDetermineDatasource(MethodInvocation invocation, String key) { - HttpServletRequest request = (HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); - return request.getSession().getAttribute(key.substring(9)).toString(); - } -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/annotation/DataColumn.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/annotation/DataColumn.java new file mode 100644 index 00000000..706b1e8f --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/annotation/DataColumn.java @@ -0,0 +1,40 @@ +package org.ruoyi.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限注解,用于标记数据权限的占位符关键字和替换值 + *

    + * 一个注解只能对应一个模板 + *

    + * + * @author Lion Li + * @version 3.5.0 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataColumn { + + /** + * 数据权限模板的占位符关键字,默认为 "deptName" + * + * @return 占位符关键字数组 + */ + String[] key() default "deptName"; + + /** + * 数据权限模板的占位符替换值,默认为 "dept_id" + * + * @return 占位符替换值数组 + */ + String[] value() default "dept_id"; + + /** + * 权限标识符 用于通过菜单权限标识符来获取数据权限 + * 拥有此标识符的角色 将不会拼接此角色的数据过滤sql + * + * @return 权限标识符 + */ + String permission() default ""; +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/annotation/DataPermission.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/annotation/DataPermission.java new file mode 100644 index 00000000..24170faa --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/annotation/DataPermission.java @@ -0,0 +1,30 @@ +package org.ruoyi.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限组注解,用于标记数据权限配置数组 + * + * @author Lion Li + * @version 3.5.0 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataPermission { + + /** + * 数据权限配置数组,用于指定数据权限的占位符关键字和替换值 + * + * @return 数据权限配置数组 + */ + DataColumn[] value(); + + /** + * 权限拼接标识符(用于指定连接语句的sql符号) + * 如不填 默认 select 用 OR 其他语句用 AND + * 内容 OR 或者 AND + */ + String joinStr() default ""; + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/aspect/DataPermissionAspect.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/aspect/DataPermissionAspect.java new file mode 100644 index 00000000..f9026e91 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/aspect/DataPermissionAspect.java @@ -0,0 +1,50 @@ +package org.ruoyi.aspect; + +import lombok.extern.slf4j.Slf4j; +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.ruoyi.annotation.DataPermission; +import org.ruoyi.helper.DataPermissionHelper; + +/** + * 数据权限处理 + * + * @author Lion Li + */ +@Slf4j +@Aspect +public class DataPermissionAspect { + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(dataPermission)") + public void doBefore(JoinPoint joinPoint, DataPermission dataPermission) { + DataPermissionHelper.setPermission(dataPermission); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(dataPermission)") + public void doAfterReturning(JoinPoint joinPoint, DataPermission dataPermission) { + DataPermissionHelper.removePermission(); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(dataPermission)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, DataPermission dataPermission, Exception e) { + DataPermissionHelper.removePermission(); + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/annotation/DataColumn.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/annotation/DataColumn.java deleted file mode 100644 index a3419da5..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/annotation/DataColumn.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.ruoyi.common.mybatis.annotation; - -import java.lang.annotation.*; - -/** - * 数据权限 - * - * 一个注解只能对应一个模板 - * - * @author Lion Li - * @version 3.5.0 - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataColumn { - - /** - * 占位符关键字 - */ - String[] key() default "deptName"; - - /** - * 占位符替换值 - */ - String[] value() default "dept_id"; - -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/annotation/DataPermission.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/annotation/DataPermission.java deleted file mode 100644 index 037eb81a..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/annotation/DataPermission.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.ruoyi.common.mybatis.annotation; - -import java.lang.annotation.*; - -/** - * 数据权限组 - * - * @author Lion Li - * @version 3.5.0 - */ -@Target({ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataPermission { - - DataColumn[] value(); - -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/mapper/BaseMapperPlus.java deleted file mode 100644 index b14c14ef..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/mapper/BaseMapperPlus.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.ruoyi.common.mybatis.core.mapper; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.extension.toolkit.Db; -import org.apache.ibatis.logging.Log; -import org.apache.ibatis.logging.LogFactory; -import org.ruoyi.common.core.utils.MapstructUtils; - -import java.io.Serializable; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * 自定义 Mapper 接口, 实现 自定义扩展 - * - * @param table 泛型 - * @param vo 泛型 - * @author Lion Li - * @since 2021-05-13 - */ -@SuppressWarnings("unchecked") -public interface BaseMapperPlus extends BaseMapper { - - Log log = LogFactory.getLog(BaseMapperPlus.class); - - default Class currentVoClass() { - return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1); - } - - default Class currentModelClass() { - return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0); - } - - default List selectList() { - return this.selectList(new QueryWrapper<>()); - } - - /** - * 批量插入 - */ - default boolean insertBatch(Collection entityList) { - return Db.saveBatch(entityList); - } - - /** - * 批量更新 - */ - default boolean updateBatchById(Collection entityList) { - return Db.updateBatchById(entityList); - } - - /** - * 批量插入或更新 - */ - default boolean insertOrUpdateBatch(Collection entityList) { - return Db.saveOrUpdateBatch(entityList); - } - - /** - * 批量插入(包含限制条数) - */ - default boolean insertBatch(Collection entityList, int batchSize) { - return Db.saveBatch(entityList, batchSize); - } - - /** - * 批量更新(包含限制条数) - */ - default boolean updateBatchById(Collection entityList, int batchSize) { - return Db.updateBatchById(entityList, batchSize); - } - - /** - * 批量插入或更新(包含限制条数) - */ - default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { - return Db.saveOrUpdateBatch(entityList, batchSize); - } - - /** - * 插入或更新(包含限制条数) - */ - default boolean insertOrUpdate(T entity) { - return Db.saveOrUpdate(entity); - } - - default V selectVoById(Serializable id) { - return selectVoById(id, this.currentVoClass()); - } - - /** - * 根据 ID 查询 - */ - default C selectVoById(Serializable id, Class voClass) { - T obj = this.selectById(id); - if (ObjectUtil.isNull(obj)) { - return null; - } - return MapstructUtils.convert(obj, voClass); - } - - default List selectVoBatchIds(Collection idList) { - return selectVoBatchIds(idList, this.currentVoClass()); - } - - /** - * 查询(根据ID 批量查询) - */ - default List selectVoBatchIds(Collection idList, Class voClass) { - List list = this.selectBatchIds(idList); - if (CollUtil.isEmpty(list)) { - return CollUtil.newArrayList(); - } - return MapstructUtils.convert(list, voClass); - } - - default List selectVoByMap(Map map) { - return selectVoByMap(map, this.currentVoClass()); - } - - /** - * 查询(根据 columnMap 条件) - */ - default List selectVoByMap(Map map, Class voClass) { - List list = this.selectByMap(map); - if (CollUtil.isEmpty(list)) { - return CollUtil.newArrayList(); - } - return MapstructUtils.convert(list, voClass); - } - - default V selectVoOne(Wrapper wrapper) { - return selectVoOne(wrapper, this.currentVoClass()); - } - - /** - * 根据 entity 条件,查询一条记录 - */ - default C selectVoOne(Wrapper wrapper, Class voClass) { - T obj = this.selectOne(wrapper); - if (ObjectUtil.isNull(obj)) { - return null; - } - return MapstructUtils.convert(obj, voClass); - } - - default List selectVoList() { - return selectVoList(new QueryWrapper<>(), this.currentVoClass()); - } - - default List selectVoList(Wrapper wrapper) { - return selectVoList(wrapper, this.currentVoClass()); - } - - /** - * 根据 entity 条件,查询全部记录 - */ - default List selectVoList(Wrapper wrapper, Class voClass) { - List list = this.selectList(wrapper); - if (CollUtil.isEmpty(list)) { - return CollUtil.newArrayList(); - } - return MapstructUtils.convert(list, voClass); - } - - default

    > P selectVoPage(IPage page, Wrapper wrapper) { - return selectVoPage(page, wrapper, this.currentVoClass()); - } - - /** - * 分页查询VO - */ - default > P selectVoPage(IPage page, Wrapper wrapper, Class voClass) { - IPage pageData = this.selectPage(page, wrapper); - IPage voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal()); - if (CollUtil.isEmpty(pageData.getRecords())) { - return (P) voPage; - } - voPage.setRecords(MapstructUtils.convert(pageData.getRecords(), voClass)); - return (P) voPage; - } - - default List selectObjs(Wrapper wrapper, Function mapper) { - return this.selectObjs(wrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList()); - } - -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/enums/DataScopeType.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/enums/DataScopeType.java deleted file mode 100644 index 65362ad0..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/enums/DataScopeType.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.ruoyi.common.mybatis.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.ruoyi.common.core.utils.StringUtils; -import org.ruoyi.common.mybatis.helper.DataPermissionHelper; - -/** - * 数据权限类型 - *

    - * 语法支持 spel 模板表达式 - *

    - * 内置数据 user 当前用户 内容参考 LoginUser - * 如需扩展数据 可使用 {@link DataPermissionHelper} 操作 - * 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService - * 如需扩展更多自定义服务 可以参考 sdss 自行编写 - * - * @author Lion Li - * @version 3.5.0 - */ -@Getter -@AllArgsConstructor -public enum DataScopeType { - - /** - * 全部数据权限 - */ - ALL("1", "", ""), - - /** - * 自定数据权限 - */ - CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", ""), - - /** - * 部门数据权限 - */ - DEPT("3", " #{#deptName} = #{#user.deptId} ", ""), - - /** - * 部门及以下数据权限 - */ - DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", ""), - - /** - * 仅本人数据权限 - */ - SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "); - - private final String code; - - /** - * 语法 采用 spel 模板表达式 - */ - private final String sqlTemplate; - - /** - * 不满足 sqlTemplate 则填充 - */ - private final String elseSql; - - public static DataScopeType findCode(String code) { - if (StringUtils.isBlank(code)) { - return null; - } - for (DataScopeType type : values()) { - if (type.getCode().equals(code)) { - return type; - } - } - return null; - } -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/InjectionMetaObjectHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/InjectionMetaObjectHandler.java deleted file mode 100644 index dfa24f0b..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/InjectionMetaObjectHandler.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.ruoyi.common.mybatis.handler; - -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.http.HttpStatus; -import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; -import lombok.extern.slf4j.Slf4j; -import org.apache.ibatis.reflection.MetaObject; -import org.ruoyi.common.core.domain.model.LoginUser; -import org.ruoyi.common.core.exception.ServiceException; -import org.ruoyi.common.mybatis.core.domain.BaseEntity; -import org.ruoyi.common.satoken.utils.LoginHelper; - -import java.util.Date; - -/** - * MP注入处理器 - * - * @author Lion Li - * @date 2021/4/25 - */ -@Slf4j -public class InjectionMetaObjectHandler implements MetaObjectHandler { - - @Override - public void insertFill(MetaObject metaObject) { - try { - if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { - Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime()) - ? 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()); - } - } - } catch (Exception e) { - throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); - } - } - - @Override - public void updateFill(MetaObject metaObject) { - try { - if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { - Date current = new Date(); - // 更新时间填充(不管为不为空) - baseEntity.setUpdateTime(current); - LoginUser loginUser = getLoginUser(); - // 当前已登录 更新人填充(不管为不为空) - if (ObjectUtil.isNotNull(loginUser)) { - baseEntity.setUpdateBy(loginUser.getUserId()); - } - } - } catch (Exception e) { - throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); - } - } - - /** - * 获取登录用户名 - */ - private LoginUser getLoginUser() { - LoginUser loginUser; - try { - loginUser = LoginHelper.getLoginUser(); - } catch (Exception e) { - log.warn("自动注入警告 => 用户未登录"); - return null; - } - return loginUser; - } - -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/PlusDataPermissionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/PlusDataPermissionHandler.java deleted file mode 100644 index f928f42a..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/PlusDataPermissionHandler.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.ruoyi.common.mybatis.handler; - -import cn.hutool.core.annotation.AnnotationUtil; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.ConcurrentHashSet; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ClassUtil; -import cn.hutool.core.util.ObjectUtil; -import lombok.extern.slf4j.Slf4j; -import net.sf.jsqlparser.JSQLParserException; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.Parenthesis; -import net.sf.jsqlparser.expression.operators.conditional.AndExpression; -import net.sf.jsqlparser.parser.CCJSqlParserUtil; -import org.ruoyi.common.core.domain.dto.RoleDTO; -import org.ruoyi.common.core.domain.model.LoginUser; -import org.ruoyi.common.core.exception.ServiceException; -import org.ruoyi.common.core.utils.SpringUtils; -import org.ruoyi.common.core.utils.StreamUtils; -import org.ruoyi.common.core.utils.StringUtils; -import org.ruoyi.common.mybatis.annotation.DataColumn; -import org.ruoyi.common.mybatis.annotation.DataPermission; -import org.ruoyi.common.mybatis.enums.DataScopeType; -import org.ruoyi.common.mybatis.helper.DataPermissionHelper; -import org.ruoyi.common.satoken.utils.LoginHelper; -import org.springframework.context.expression.BeanFactoryResolver; -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 java.lang.reflect.Method; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -/** - * 数据权限过滤 - * - * @author Lion Li - * @version 3.5.0 - */ -@Slf4j -public class PlusDataPermissionHandler { - - /** - * 方法或类(名称) 与 注解的映射关系缓存 - */ - private final Map dataPermissionCacheMap = new ConcurrentHashMap<>(); - - /** - * 无效注解方法缓存用于快速返回 - */ - private final Set invalidCacheSet = new ConcurrentHashSet<>(); - - /** - * spel 解析器 - */ - private final ExpressionParser parser = new SpelExpressionParser(); - private final ParserContext parserContext = new TemplateParserContext(); - /** - * bean解析器 用于处理 spel 表达式中对 bean 的调用 - */ - private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); - - - public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { - DataColumn[] dataColumns = findAnnotation(mappedStatementId); - if (ArrayUtil.isEmpty(dataColumns)) { - invalidCacheSet.add(mappedStatementId); - return where; - } - LoginUser currentUser = DataPermissionHelper.getVariable("user"); - if (ObjectUtil.isNull(currentUser)) { - currentUser = LoginHelper.getLoginUser(); - DataPermissionHelper.setVariable("user", currentUser); - } - // 如果是超级管理员或租户管理员,则不过滤数据 - if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { - return where; - } - String dataFilterSql = buildDataFilter(dataColumns, isSelect); - if (StringUtils.isBlank(dataFilterSql)) { - return where; - } - try { - Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql); - // 数据权限使用单独的括号 防止与其他条件冲突 - Parenthesis parenthesis = new Parenthesis(expression); - if (ObjectUtil.isNotNull(where)) { - return new AndExpression(where, parenthesis); - } else { - return parenthesis; - } - } catch (JSQLParserException e) { - throw new ServiceException("数据权限解析异常 => " + e.getMessage()); - } - } - - /** - * 构造数据过滤sql - */ - private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) { - // 更新或删除需满足所有条件 - String joinStr = isSelect ? " OR " : " AND "; - LoginUser user = DataPermissionHelper.getVariable("user"); - StandardEvaluationContext context = new StandardEvaluationContext(); - context.setBeanResolver(beanResolver); - DataPermissionHelper.getContext().forEach(context::setVariable); - Set conditions = new HashSet<>(); - for (RoleDTO role : user.getRoles()) { - user.setRoleId(role.getRoleId()); - // 获取角色权限泛型 - DataScopeType type = DataScopeType.findCode(role.getDataScope()); - if (ObjectUtil.isNull(type)) { - throw new ServiceException("角色数据范围异常 => " + role.getDataScope()); - } - // 全部数据权限直接返回 - if (type == DataScopeType.ALL) { - return ""; - } - boolean isSuccess = false; - for (DataColumn dataColumn : dataColumns) { - if (dataColumn.key().length != dataColumn.value().length) { - throw new ServiceException("角色数据范围异常 => key与value长度不匹配"); - } - // 不包含 key 变量 则不处理 - if (!StringUtils.containsAny(type.getSqlTemplate(), - Arrays.stream(dataColumn.key()).map(key -> "#" + key).toArray(String[]::new) - )) { - continue; - } - // 设置注解变量 key 为表达式变量 value 为变量值 - for (int i = 0; i < dataColumn.key().length; i++) { - context.setVariable(dataColumn.key()[i], dataColumn.value()[i]); - } - - // 解析sql模板并填充 - String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class); - conditions.add(joinStr + sql); - isSuccess = true; - } - // 未处理成功则填充兜底方案 - if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) { - conditions.add(joinStr + type.getElseSql()); - } - } - - if (CollUtil.isNotEmpty(conditions)) { - String sql = StreamUtils.join(conditions, Function.identity(), ""); - return sql.substring(joinStr.length()); - } - return ""; - } - - private 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 = ClassUtil.loadClass(clazzName); - List methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz)) - .filter(method -> method.getName().equals(methodName)).toList(); - DataPermission dataPermission; - // 获取方法注解 - for (Method method : methods) { - dataPermission = dataPermissionCacheMap.get(mappedStatementId); - if (ObjectUtil.isNotNull(dataPermission)) { - return dataPermission.value(); - } - 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(); - } - return null; - } - - /** - * 是否为无效方法 无数据权限 - */ - public boolean isInvalid(String mappedStatementId) { - return invalidCacheSet.contains(mappedStatementId); - } -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/helper/DataPermissionHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/helper/DataPermissionHelper.java deleted file mode 100644 index 8e3de7a3..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/helper/DataPermissionHelper.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.ruoyi.common.mybatis.helper; - -import cn.dev33.satoken.context.SaHolder; -import cn.dev33.satoken.context.model.SaStorage; -import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; -import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -/** - * 数据权限助手 - * - * @author Lion Li - * @version 3.5.0 - */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@SuppressWarnings("unchecked cast") -public class DataPermissionHelper { - - private static final String DATA_PERMISSION_KEY = "data:permission"; - - public static T getVariable(String key) { - Map context = getContext(); - return (T) context.get(key); - } - - - public static void setVariable(String key, Object value) { - Map context = getContext(); - context.put(key, value); - } - - public static Map getContext() { - SaStorage saStorage = SaHolder.getStorage(); - Object attribute = saStorage.get(DATA_PERMISSION_KEY); - if (ObjectUtil.isNull(attribute)) { - saStorage.set(DATA_PERMISSION_KEY, new HashMap<>()); - attribute = saStorage.get(DATA_PERMISSION_KEY); - } - if (attribute instanceof Map map) { - return map; - } - throw new NullPointerException("data permission context type exception"); - } - - /** - * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) - */ - public static void enableIgnore() { - InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); - } - - /** - * 关闭忽略数据权限 - */ - public static void disableIgnore() { - InterceptorIgnoreHelper.clearIgnoreStrategy(); - } - - /** - * 在忽略数据权限中执行 - * - * @param handle 处理执行方法 - */ - public static void ignore(Runnable handle) { - enableIgnore(); - try { - handle.run(); - } finally { - disableIgnore(); - } - } - - /** - * 在忽略数据权限中执行 - * - * @param handle 处理执行方法 - */ - public static T ignore(Supplier handle) { - enableIgnore(); - try { - return handle.get(); - } finally { - disableIgnore(); - } - } - -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/interceptor/PlusDataPermissionInterceptor.java deleted file mode 100644 index 98895d0a..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/interceptor/PlusDataPermissionInterceptor.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.ruoyi.common.mybatis.interceptor; - -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.inner.InnerInterceptor; -import net.sf.jsqlparser.expression.Expression; -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; -import org.apache.ibatis.executor.statement.StatementHandler; -import org.apache.ibatis.mapping.BoundSql; -import org.apache.ibatis.mapping.MappedStatement; -import org.apache.ibatis.mapping.SqlCommandType; -import org.apache.ibatis.session.ResultHandler; -import org.apache.ibatis.session.RowBounds; -import org.ruoyi.common.mybatis.handler.PlusDataPermissionHandler; - -import java.sql.Connection; -import java.sql.SQLException; -import java.util.List; - -/** - * 数据权限拦截器 - * - * @author Lion Li - * @version 3.5.0 - */ -public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { - - private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); - - @Override - public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { - // 检查忽略注解 - if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { - return; - } - // 检查是否无效 无数据权限注解 - if (dataPermissionHandler.isInvalid(ms.getId())) { - return; - } - // 解析 sql 分配对应方法 - PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); - mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); - } - - @Override - public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { - PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); - MappedStatement ms = mpSh.mappedStatement(); - SqlCommandType sct = ms.getSqlCommandType(); - if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { - if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { - return; - } - PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); - mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); - } - } - - @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 selectBodyList = setOperationList.getSelects(); - selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj)); - } - } - - @Override - protected void processUpdate(Update update, int index, String sql, Object obj) { - Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false); - if (null != sqlSegment) { - update.setWhere(sqlSegment); - } - } - - @Override - protected void processDelete(Delete delete, int index, String sql, Object obj) { - Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false); - if (null != sqlSegment) { - delete.setWhere(sqlSegment); - } - } - - /** - * 设置 where 条件 - * - * @param plainSelect 查询对象 - * @param mappedStatementId 执行方法id - */ - protected void setWhere(PlainSelect plainSelect, String mappedStatementId) { - Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true); - if (null != sqlSegment) { - plainSelect.setWhere(sqlSegment); - } - } - -} - diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/jakarta/DsJakartaHeaderProcessor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/jakarta/DsJakartaHeaderProcessor.java deleted file mode 100644 index fd67e07c..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/jakarta/DsJakartaHeaderProcessor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2018 organization baomidou - * - * 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.ruoyi.common.mybatis.jakarta; - -import com.baomidou.dynamic.datasource.processor.DsProcessor; -import jakarta.servlet.http.HttpServletRequest; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -/** - * @author TaoYu - * @since 3.6.0 - */ -public class DsJakartaHeaderProcessor extends DsProcessor { - - /** - * header prefix - */ - private static final String HEADER_PREFIX = "#header"; - - @Override - public boolean matches(String key) { - return key.startsWith(HEADER_PREFIX); - } - - @Override - public String doDetermineDatasource(MethodInvocation invocation, String key) { - HttpServletRequest request = (HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); - return request.getHeader(key.substring(8)); - } -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/jakarta/DsJakartaSessionProcessor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/jakarta/DsJakartaSessionProcessor.java deleted file mode 100644 index f055b463..00000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/jakarta/DsJakartaSessionProcessor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2018 organization baomidou - * - * 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.ruoyi.common.mybatis.jakarta; - -import com.baomidou.dynamic.datasource.processor.DsProcessor; -import jakarta.servlet.http.HttpServletRequest; -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - - -/** - * @author TaoYu - * @since 3.6.0 - */ -public class DsJakartaSessionProcessor extends DsProcessor { - - /** - * session开头 - */ - private static final String SESSION_PREFIX = "#session"; - - @Override - public boolean matches(String key) { - return key.startsWith(SESSION_PREFIX); - } - - @Override - public String doDetermineDatasource(MethodInvocation invocation, String key) { - HttpServletRequest request = (HttpServletRequest) ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); - return request.getSession().getAttribute(key.substring(9)).toString(); - } -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/config/MybatisPlusConfig.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/config/MybatisPlusConfig.java similarity index 66% rename from ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/config/MybatisPlusConfig.java rename to ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/config/MybatisPlusConfig.java index 7f28b1f6..f5942b2e 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/config/MybatisPlusConfig.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/config/MybatisPlusConfig.java @@ -1,17 +1,25 @@ -package org.ruoyi.common.mybatis.config; +package org.ruoyi.config; import cn.hutool.core.net.NetUtil; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import org.ruoyi.common.mybatis.handler.InjectionMetaObjectHandler; -import org.ruoyi.common.mybatis.interceptor.PlusDataPermissionInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import org.ruoyi.aspect.DataPermissionAspect; +import org.ruoyi.common.core.factory.YmlPropertySourceFactory; +import org.ruoyi.common.core.utils.SpringUtils; +import org.ruoyi.handler.InjectionMetaObjectHandler; +import org.ruoyi.handler.MybatisExceptionHandler; +import org.ruoyi.handler.PlusPostInitTableInfoHandler; +import org.ruoyi.interceptor.PlusDataPermissionInterceptor; import org.mybatis.spring.annotation.MapperScan; -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; /** @@ -20,13 +28,19 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; * @author Lion Li */ @EnableTransactionManagement(proxyTargetClass = true) -@AutoConfiguration @MapperScan("${mybatis-plus.mapperPackage}") +@PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class) public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 多租户插件 必须放到第一位 + try { + TenantLineInnerInterceptor tenant = SpringUtils.getBean(TenantLineInnerInterceptor.class); + interceptor.addInnerInterceptor(tenant); + } catch (BeansException ignore) { + } // 数据权限处理 interceptor.addInnerInterceptor(dataPermissionInterceptor()); // 分页插件 @@ -40,7 +54,15 @@ public class MybatisPlusConfig { * 数据权限拦截器 */ public PlusDataPermissionInterceptor dataPermissionInterceptor() { - return new PlusDataPermissionInterceptor(); + return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage")); + } + + /** + * 数据权限切面处理器 + */ + @Bean + public DataPermissionAspect dataPermissionAspect() { + return new DataPermissionAspect(); } /** @@ -48,8 +70,6 @@ public class MybatisPlusConfig { */ public PaginationInnerInterceptor paginationInnerInterceptor() { PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); - // 设置最大单页限制数量,默认 500 条,-1 不受限制 - paginationInnerInterceptor.setMaxLimit(-1L); // 分页合理化 paginationInnerInterceptor.setOverflow(true); return paginationInnerInterceptor; @@ -79,6 +99,22 @@ public class MybatisPlusConfig { return new DefaultIdentifierGenerator(NetUtil.getLocalhost()); } + /** + * 异常处理器 + */ + @Bean + public MybatisExceptionHandler mybatisExceptionHandler() { + return new MybatisExceptionHandler(); + } + + /** + * 初始化表对象处理器 + */ + @Bean + public PostInitTableInfoHandler postInitTableInfoHandler() { + return new PlusPostInitTableInfoHandler(); + } + /** * PaginationInnerInterceptor 分页插件,自动识别数据库类型 * https://baomidou.com/pages/97710a/ diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/domain/BaseEntity.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/domain/BaseEntity.java similarity index 96% rename from ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/domain/BaseEntity.java rename to ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/domain/BaseEntity.java index bfc43c54..5e362d60 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/domain/BaseEntity.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/domain/BaseEntity.java @@ -1,4 +1,4 @@ -package org.ruoyi.common.mybatis.core.domain; +package org.ruoyi.core.domain; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; @@ -17,7 +17,6 @@ import java.util.Map; * * @author Lion Li */ - @Data public class BaseEntity implements Serializable { diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/mapper/BaseMapperPlus.java new file mode 100644 index 00000000..097c7210 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/mapper/BaseMapperPlus.java @@ -0,0 +1,335 @@ +package org.ruoyi.core.mapper; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.toolkit.Db; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; +import org.ruoyi.common.core.utils.MapstructUtils; +import org.ruoyi.common.core.utils.StreamUtils; + + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * 自定义 Mapper 接口, 实现 自定义扩展 + * + * @param table 泛型 + * @param vo 泛型 + * @author Lion Li + * @since 2021-05-13 + */ +@SuppressWarnings("unchecked") +public interface BaseMapperPlus extends BaseMapper { + + Log log = LogFactory.getLog(BaseMapperPlus.class); + + /** + * 获取当前实例对象关联的泛型类型 V 的 Class 对象 + * + * @return 返回当前实例对象关联的泛型类型 V 的 Class 对象 + */ + default Class currentVoClass() { + return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1]; + } + + /** + * 获取当前实例对象关联的泛型类型 T 的 Class 对象 + * + * @return 返回当前实例对象关联的泛型类型 T 的 Class 对象 + */ + default Class currentModelClass() { + return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[0]; + } + + /** + * 使用默认的查询条件查询并返回结果列表 + * + * @return 返回查询结果的列表 + */ + default List selectList() { + return this.selectList(new QueryWrapper<>()); + } + + /** + * 批量插入实体对象集合 + * + * @param entityList 实体对象集合 + * @return 插入操作是否成功的布尔值 + */ + default boolean insertBatch(Collection entityList) { + return Db.saveBatch(entityList); + } + + /** + * 批量根据ID更新实体对象集合 + * + * @param entityList 实体对象集合 + * @return 更新操作是否成功的布尔值 + */ + default boolean updateBatchById(Collection entityList) { + return Db.updateBatchById(entityList); + } + + /** + * 批量插入或更新实体对象集合 + * + * @param entityList 实体对象集合 + * @return 插入或更新操作是否成功的布尔值 + */ + default boolean insertOrUpdateBatch(Collection entityList) { + return Db.saveOrUpdateBatch(entityList); + } + + /** + * 批量插入实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 插入操作是否成功的布尔值 + */ + default boolean insertBatch(Collection entityList, int batchSize) { + return Db.saveBatch(entityList, batchSize); + } + + /** + * 批量根据ID更新实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 更新操作是否成功的布尔值 + */ + default boolean updateBatchById(Collection entityList, int batchSize) { + return Db.updateBatchById(entityList, batchSize); + } + + /** + * 批量插入或更新实体对象集合并指定批处理大小 + * + * @param entityList 实体对象集合 + * @param batchSize 批处理大小 + * @return 插入或更新操作是否成功的布尔值 + */ + default boolean insertOrUpdateBatch(Collection entityList, int batchSize) { + return Db.saveOrUpdateBatch(entityList, batchSize); + } + + /** + * 根据ID查询单个VO对象 + * + * @param id 主键ID + * @return 查询到的单个VO对象 + */ + default V selectVoById(Serializable id) { + return selectVoById(id, this.currentVoClass()); + } + + /** + * 根据ID查询单个VO对象并将其转换为指定的VO类 + * + * @param id 主键ID + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 + */ + default C selectVoById(Serializable id, Class voClass) { + T obj = this.selectById(id); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + + /** + * 根据ID集合批量查询VO对象列表 + * + * @param idList 主键ID集合 + * @return 查询到的VO对象列表 + */ + default List selectVoByIds(Collection idList) { + return selectVoByIds(idList, this.currentVoClass()); + } + + /** + * 根据ID集合批量查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param idList 主键ID集合 + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoByIds(Collection idList, Class voClass) { + List list = this.selectByIds(idList); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据查询条件Map查询VO对象列表 + * + * @param map 查询条件Map + * @return 查询到的VO对象列表 + */ + default List selectVoByMap(Map map) { + return selectVoByMap(map, this.currentVoClass()); + } + + /** + * 根据查询条件Map查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param map 查询条件Map + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoByMap(Map map, Class voClass) { + List list = this.selectByMap(map); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据条件查询单个VO对象 + * + * @param wrapper 查询条件Wrapper + * @return 查询到的单个VO对象 + */ + default V selectVoOne(Wrapper wrapper) { + return selectVoOne(wrapper, this.currentVoClass()); + } + + /** + * 根据条件查询单个VO对象,并根据需要决定是否抛出异常 + * + * @param wrapper 查询条件Wrapper + * @param throwEx 是否抛出异常的标志 + * @return 查询到的单个VO对象 + */ + default V selectVoOne(Wrapper wrapper, boolean throwEx) { + return selectVoOne(wrapper, this.currentVoClass(), throwEx); + } + + /** + * 根据条件查询单个VO对象,并指定返回的VO对象的类型 + * + * @param wrapper 查询条件Wrapper + * @param voClass 返回的VO对象的Class对象 + * @param 返回的VO对象的类型 + * @return 查询到的单个VO对象,经过类型转换为指定的VO类后返回 + */ + default C selectVoOne(Wrapper wrapper, Class voClass) { + return selectVoOne(wrapper, voClass, true); + } + + /** + * 根据条件查询单个实体对象,并将其转换为指定的VO对象 + * + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param throwEx 是否抛出异常的标志 + * @param VO类的类型 + * @return 查询到的单个VO对象,经过转换为指定的VO类后返回 + */ + default C selectVoOne(Wrapper wrapper, Class voClass, boolean throwEx) { + T obj = this.selectOne(wrapper, throwEx); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + + /** + * 查询所有VO对象列表 + * + * @return 查询到的VO对象列表 + */ + default List selectVoList() { + return selectVoList(new QueryWrapper<>(), this.currentVoClass()); + } + + /** + * 根据条件查询VO对象列表 + * + * @param wrapper 查询条件Wrapper + * @return 查询到的VO对象列表 + */ + default List selectVoList(Wrapper wrapper) { + return selectVoList(wrapper, this.currentVoClass()); + } + + /** + * 根据条件查询实体对象列表,并将其转换为指定的VO对象列表 + * + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @return 查询到的VO对象列表,经过转换为指定的VO类后返回 + */ + default List selectVoList(Wrapper wrapper, Class voClass) { + List list = this.selectList(wrapper); + if (CollUtil.isEmpty(list)) { + return CollUtil.newArrayList(); + } + return MapstructUtils.convert(list, voClass); + } + + /** + * 根据条件分页查询VO对象列表 + * + * @param page 分页信息 + * @param wrapper 查询条件Wrapper + * @return 查询到的VO对象分页列表 + */ + default

    > P selectVoPage(IPage page, Wrapper wrapper) { + return selectVoPage(page, wrapper, this.currentVoClass()); + } + + /** + * 根据条件分页查询实体对象列表,并将其转换为指定的VO对象分页列表 + * + * @param page 分页信息 + * @param wrapper 查询条件Wrapper + * @param voClass 要转换的VO类的Class对象 + * @param VO类的类型 + * @param

    VO对象分页列表的类型 + * @return 查询到的VO对象分页列表,经过转换为指定的VO类后返回 + */ + default > P selectVoPage(IPage page, Wrapper wrapper, Class voClass) { + // 根据条件分页查询实体对象列表 + List list = this.selectList(page, wrapper); + // 创建一个新的VO对象分页列表,并设置分页信息 + IPage voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); + if (CollUtil.isEmpty(list)) { + return (P) voPage; + } + voPage.setRecords(MapstructUtils.convert(list, voClass)); + return (P) voPage; + } + + /** + * 根据条件查询符合条件的对象,并将其转换为指定类型的对象列表 + * + * @param wrapper 查询条件Wrapper + * @param mapper 转换函数,用于将查询到的对象转换为指定类型的对象 + * @param 要转换的对象的类型 + * @return 查询到的符合条件的对象列表,经过转换为指定类型的对象后返回 + */ + default List selectObjs(Wrapper wrapper, Function mapper) { + return StreamUtils.toList(this.selectObjs(wrapper), mapper); + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/page/PageQuery.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/page/PageQuery.java similarity index 90% rename from ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/page/PageQuery.java rename to ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/page/PageQuery.java index 33e0190d..a388cb4a 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/page/PageQuery.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/page/PageQuery.java @@ -1,9 +1,10 @@ -package org.ruoyi.common.mybatis.core.page; +package org.ruoyi.core.page; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import org.ruoyi.common.core.exception.ServiceException; import org.ruoyi.common.core.utils.StringUtils; @@ -19,7 +20,6 @@ import java.util.List; * * @author Lion Li */ - @Data public class PageQuery implements Serializable { @@ -56,6 +56,9 @@ public class PageQuery implements Serializable { */ public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + /** + * 构建分页对象 + */ public Page build() { Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); @@ -111,4 +114,14 @@ public class PageQuery implements Serializable { return list; } + @JsonIgnore + public Integer getFirstNum() { + return (pageNum - 1) * pageSize; + } + + public PageQuery(Integer pageSize, Integer pageNum) { + this.pageSize = pageSize; + this.pageNum = pageNum; + } + } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/page/TableDataInfo.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/page/TableDataInfo.java similarity index 85% rename from ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/page/TableDataInfo.java rename to ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/page/TableDataInfo.java index b4f7cd70..eb321ae9 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/core/page/TableDataInfo.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/core/page/TableDataInfo.java @@ -1,4 +1,4 @@ -package org.ruoyi.common.mybatis.core.page; +package org.ruoyi.core.page; import cn.hutool.http.HttpStatus; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -14,7 +14,6 @@ import java.util.List; * * @author Lion Li */ - @Data @NoArgsConstructor public class TableDataInfo implements Serializable { @@ -51,8 +50,13 @@ public class TableDataInfo implements Serializable { public TableDataInfo(List list, long total) { this.rows = list; this.total = total; + this.code = HttpStatus.HTTP_OK; + this.msg = "查询成功"; } + /** + * 根据分页对象构建表格分页数据对象 + */ public static TableDataInfo build(IPage page) { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); @@ -62,6 +66,9 @@ public class TableDataInfo implements Serializable { return rspData; } + /** + * 根据数据列表构建表格分页数据对象 + */ public static TableDataInfo build(List list) { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); @@ -71,6 +78,9 @@ public class TableDataInfo implements Serializable { return rspData; } + /** + * 构建表格分页数据对象 + */ public static TableDataInfo build() { TableDataInfo rspData = new TableDataInfo<>(); rspData.setCode(HttpStatus.HTTP_OK); diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/enums/DataBaseType.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/enums/DataBaseType.java similarity index 74% rename from ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/enums/DataBaseType.java rename to ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/enums/DataBaseType.java index fba59340..1b32cda4 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/enums/DataBaseType.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/enums/DataBaseType.java @@ -1,9 +1,10 @@ -package org.ruoyi.common.mybatis.enums; +package org.ruoyi.enums; import lombok.AllArgsConstructor; import lombok.Getter; import org.ruoyi.common.core.utils.StringUtils; + /** * 数据库类型 * @@ -33,8 +34,17 @@ public enum DataBaseType { */ SQL_SERVER("Microsoft SQL Server"); + /** + * 数据库类型 + */ private final String type; + /** + * 根据数据库产品名称查找对应的数据库类型 + * + * @param databaseProductName 数据库产品名称 + * @return 对应的数据库类型枚举值,如果未找到则返回 null + */ public static DataBaseType find(String databaseProductName) { if (StringUtils.isBlank(databaseProductName)) { return null; diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/enums/DataScopeType.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/enums/DataScopeType.java new file mode 100644 index 00000000..f478adbd --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/enums/DataScopeType.java @@ -0,0 +1,87 @@ +package org.ruoyi.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.ruoyi.common.core.domain.model.LoginUser; +import org.ruoyi.common.core.utils.StringUtils; +import org.ruoyi.helper.DataPermissionHelper; + +/** + * 数据权限类型枚举 + *

    + * 支持使用 SpEL 模板表达式定义 SQL 查询条件 + * 内置数据: + * - {@code user}: 当前登录用户信息,参考 {@link LoginUser} + * 内置服务: + * - {@code sdss}: 系统数据权限服务,参考 ISysDataScopeService + * 如需扩展数据,可以通过 {@link DataPermissionHelper} 进行操作 + * 如需扩展服务,可以通过 ISysDataScopeService 自行编写 + *

    + * + * @author Lion Li + * @version 3.5.0 + */ +@Getter +@AllArgsConstructor +public enum DataScopeType { + + /** + * 全部数据权限 + */ + ALL("1", "", ""), + + /** + * 自定数据权限 + */ + CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "), + + /** + * 部门数据权限 + */ + DEPT("3", " #{#deptName} = #{#user.deptId} ", " 1 = 0 "), + + /** + * 部门及以下数据权限 + */ + DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", " 1 = 0 "), + + /** + * 仅本人数据权限 + */ + SELF("5", " #{#userName} = #{#user.userId} ", " 1 = 0 "), + + /** + * 部门及以下或本人数据权限 + */ + DEPT_AND_CHILD_OR_SELF("6", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} ) OR #{#userName} = #{#user.userId} ", " 1 = 0 "); + + private final String code; + + /** + * SpEL 模板表达式,用于构建 SQL 查询条件 + */ + private final String sqlTemplate; + + /** + * 如果不满足 {@code sqlTemplate} 的条件,则使用此默认 SQL 表达式 + */ + private final String elseSql; + + /** + * 根据枚举代码查找对应的枚举值 + * + * @param code 枚举代码 + * @return 对应的枚举值,如果未找到则返回 null + */ + public static DataScopeType findCode(String code) { + if (StringUtils.isBlank(code)) { + return null; + } + for (DataScopeType type : values()) { + if (type.getCode().equals(code)) { + return type; + } + } + return null; + } +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/InjectionMetaObjectHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/InjectionMetaObjectHandler.java new file mode 100644 index 00000000..57843d35 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/InjectionMetaObjectHandler.java @@ -0,0 +1,103 @@ +package org.ruoyi.handler; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.ruoyi.common.core.domain.model.LoginUser; +import org.ruoyi.common.core.exception.ServiceException; +import org.ruoyi.common.core.utils.ObjectUtils; +import org.ruoyi.common.satoken.utils.LoginHelper; +import org.ruoyi.core.domain.BaseEntity; + + +import java.util.Date; + +/** + * MP注入处理器 + * + * @author Lion Li + * @date 2021/4/25 + */ +@Slf4j +public class InjectionMetaObjectHandler implements MetaObjectHandler { + + /** + * 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息 + * + * @param metaObject 元对象,用于获取原始对象并进行填充 + */ + @Override + public void insertFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + // 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间 + Date current = ObjectUtils.notNull(baseEntity.getCreateTime(), new Date()); + baseEntity.setCreateTime(current); + baseEntity.setUpdateTime(current); + + // 如果创建人为空,则填充当前登录用户的信息 + if (ObjectUtil.isNull(baseEntity.getCreateBy())) { + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNotNull(loginUser)) { + Long userId = loginUser.getUserId(); + // 填充创建人、更新人和创建部门信息 + baseEntity.setCreateBy(userId); + baseEntity.setUpdateBy(userId); + baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId())); + } + } + } else { + Date date = new Date(); + this.strictInsertFill(metaObject, "createTime", Date.class, date); + this.strictInsertFill(metaObject, "updateTime", Date.class, date); + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 更新填充方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息 + * + * @param metaObject 元对象,用于获取原始对象并进行填充 + */ + @Override + public void updateFill(MetaObject metaObject) { + try { + if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) { + // 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充 + Date current = new Date(); + baseEntity.setUpdateTime(current); + + // 获取当前登录用户的ID,并填充更新人信息 + Long userId = LoginHelper.getUserId(); + if (ObjectUtil.isNotNull(userId)) { + baseEntity.setUpdateBy(userId); + } + } else { + this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); + } + } catch (Exception e) { + throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + /** + * 获取当前登录用户信息 + * + * @return 当前登录用户的信息,如果用户未登录则返回 null + */ + private LoginUser getLoginUser() { + LoginUser loginUser; + try { + loginUser = LoginHelper.getLoginUser(); + } catch (Exception e) { + log.warn("自动注入警告 => 用户未登录"); + return null; + } + return loginUser; + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/MybatisExceptionHandler.java similarity index 90% rename from ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/MybatisExceptionHandler.java rename to ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/MybatisExceptionHandler.java index 281d172d..0cb338c2 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/handler/MybatisExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/MybatisExceptionHandler.java @@ -1,9 +1,11 @@ -package org.ruoyi.common.mybatis.handler; +package org.ruoyi.handler; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; -import org.ruoyi.common.core.domain.R; + import org.mybatis.spring.MyBatisSystemException; +import org.ruoyi.common.core.domain.R; +import org.ruoyi.common.core.utils.StringUtils; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -34,7 +36,7 @@ public class MybatisExceptionHandler { public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); String message = e.getMessage(); - if (message.contains("CannotFindDataSourceException")) { + if (StringUtils.contains("CannotFindDataSourceException", message)) { log.error("请求地址'{}', 未找到数据源", requestURI); return R.fail("未找到数据源,请联系管理员确认"); } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/PlusDataPermissionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/PlusDataPermissionHandler.java new file mode 100644 index 00000000..46e0b2e9 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/PlusDataPermissionHandler.java @@ -0,0 +1,359 @@ +package org.ruoyi.handler; + +import cn.hutool.core.annotation.AnnotationUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.apache.ibatis.io.Resources; + +import org.ruoyi.annotation.DataColumn; +import org.ruoyi.annotation.DataPermission; +import org.ruoyi.common.core.domain.dto.RoleDTO; +import org.ruoyi.common.core.domain.model.LoginUser; +import org.ruoyi.common.core.exception.ServiceException; +import org.ruoyi.common.core.utils.SpringUtils; +import org.ruoyi.common.core.utils.StreamUtils; +import org.ruoyi.common.core.utils.StringUtils; +import org.ruoyi.common.satoken.utils.LoginHelper; +import org.ruoyi.enums.DataScopeType; +import org.ruoyi.helper.DataPermissionHelper; + +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.*; +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.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + * 数据权限过滤 + * + * @author Lion Li + * @version 3.5.0 + */ +@Slf4j +public class PlusDataPermissionHandler { + + /** + * 类名称与注解的映射关系缓存(由于aop无法拦截mybatis接口类上的注解 只能通过启动预扫描的方式进行) + */ + private final Map dataPermissionCacheMap = new ConcurrentHashMap<>(); + + /** + * spel 解析器 + */ + private final ExpressionParser parser = new SpelExpressionParser(); + private final ParserContext parserContext = new TemplateParserContext(); + /** + * bean解析器 用于处理 spel 表达式中对 bean 的调用 + */ + private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); + + /** + * 构造方法,扫描指定包下的 Mapper 类并初始化缓存 + * + * @param mapperPackage Mapper 类所在的包路径 + */ + public PlusDataPermissionHandler(String mapperPackage) { + scanMapperClasses(mapperPackage); + } + + /** + * 获取数据过滤条件的 SQL 片段 + * + * @param where 原始的查询条件表达式 + * @param mappedStatementId Mapper 方法的 ID + * @param isSelect 是否为查询语句 + * @return 数据过滤条件的 SQL 片段 + */ + public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { + try { + // 获取数据权限配置 + DataPermission dataPermission = getDataPermission(mappedStatementId); + // 获取当前登录用户信息 + LoginUser currentUser = DataPermissionHelper.getVariable("user"); + if (ObjectUtil.isNull(currentUser)) { + currentUser = LoginHelper.getLoginUser(); + DataPermissionHelper.setVariable("user", currentUser); + } + // 如果是超级管理员或租户管理员,则不过滤数据 + if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { + return where; + } + // 构造数据过滤条件的 SQL 片段 + String dataFilterSql = buildDataFilter(dataPermission, isSelect); + if (StringUtils.isBlank(dataFilterSql)) { + return where; + } + Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql); + // 数据权限使用单独的括号 防止与其他条件冲突 + ParenthesedExpressionList parenthesis = new ParenthesedExpressionList<>(expression); + if (ObjectUtil.isNotNull(where)) { + return new AndExpression(where, parenthesis); + } else { + return parenthesis; + } + } catch (JSQLParserException e) { + throw new ServiceException("数据权限解析异常 => " + e.getMessage()); + } finally { + DataPermissionHelper.removePermission(); + } + } + + /** + * 构建数据过滤条件的 SQL 语句 + * + * @param dataPermission 数据权限注解 + * @param isSelect 标志当前操作是否为查询操作,查询操作和更新或删除操作在处理过滤条件时会有不同的处理方式 + * @return 构建的数据过滤条件的 SQL 语句 + * @throws ServiceException 如果角色的数据范围异常或者 key 与 value 的长度不匹配,则抛出 ServiceException 异常 + */ + private String buildDataFilter(DataPermission dataPermission, boolean isSelect) { + // 更新或删除需满足所有条件 + String joinStr = isSelect ? " OR " : " AND "; + if (StringUtils.isNotBlank(dataPermission.joinStr())) { + joinStr = " " + dataPermission.joinStr() + " "; + } + LoginUser user = DataPermissionHelper.getVariable("user"); + Object defaultValue = "-1"; + NullSafeStandardEvaluationContext context = new NullSafeStandardEvaluationContext(defaultValue); + context.addPropertyAccessor(new NullSafePropertyAccessor(context.getPropertyAccessors().get(0), defaultValue)); + context.setBeanResolver(beanResolver); + DataPermissionHelper.getContext().forEach(context::setVariable); + Set conditions = new HashSet<>(); + // 优先设置变量 + List keys = new ArrayList<>(); + Map ignoreMap = new HashMap<>(); + for (DataColumn dataColumn : dataPermission.value()) { + if (dataColumn.key().length != dataColumn.value().length) { + throw new ServiceException("角色数据范围异常 => key与value长度不匹配"); + } + // 包含权限标识符 这直接跳过 + if (StringUtils.isNotBlank(dataColumn.permission()) && + CollUtil.contains(user.getMenuPermission(), dataColumn.permission()) + ) { + ignoreMap.put(dataColumn, Boolean.TRUE); + continue; + } + // 设置注解变量 key 为表达式变量 value 为变量值 + for (int i = 0; i < dataColumn.key().length; i++) { + context.setVariable(dataColumn.key()[i], dataColumn.value()[i]); + } + keys.addAll(Arrays.stream(dataColumn.key()).map(key -> "#" + key).toList()); + } + + for (RoleDTO role : user.getRoles()) { + user.setRoleId(role.getRoleId()); + // 获取角色权限泛型 + DataScopeType type = DataScopeType.findCode(role.getDataScope()); + if (ObjectUtil.isNull(type)) { + throw new ServiceException("角色数据范围异常 => " + role.getDataScope()); + } + // 全部数据权限直接返回 + if (type == DataScopeType.ALL) { + return StringUtils.EMPTY; + } + boolean isSuccess = false; + for (DataColumn dataColumn : dataPermission.value()) { + // 包含权限标识符 这直接跳过 + if (ignoreMap.containsKey(dataColumn)) { + // 修复多角色与权限标识符共用问题 https://gitee.com/dromara/RuoYi-Vue-Plus/issues/IB4CS4 + conditions.add(joinStr + " 1 = 1 "); + isSuccess = true; + continue; + } + // 不包含 key 变量 则不处理 + if (!StringUtils.containsAny(type.getSqlTemplate(), keys.toArray(String[]::new))) { + continue; + } + // 当前注解不满足模板 不处理 + if (!StringUtils.containsAny(type.getSqlTemplate(), dataColumn.key())) { + continue; + } + // 忽略数据权限 防止spel表达式内有其他sql查询导致死循环调用 + String sql = DataPermissionHelper.ignore(() -> + parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class) + ); + // 解析sql模板并填充 + conditions.add(joinStr + sql); + isSuccess = true; + } + // 未处理成功则填充兜底方案 + if (!isSuccess && StringUtils.isNotBlank(type.getElseSql())) { + conditions.add(joinStr + type.getElseSql()); + } + } + + if (CollUtil.isNotEmpty(conditions)) { + String sql = StreamUtils.join(conditions, Function.identity(), ""); + return sql.substring(joinStr.length()); + } + return StringUtils.EMPTY; + } + + /** + * 扫描指定包下的 Mapper 类,并查找其中带有特定注解的方法或类 + * + * @param mapperPackage Mapper 类所在的包路径 + */ + private void scanMapperClasses(String mapperPackage) { + // 创建资源解析器和元数据读取工厂 + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + // 将 Mapper 包路径按分隔符拆分为数组 + String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; + try { + for (String packagePattern : packagePatternArray) { + // 将包路径转换为资源路径 + String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + // 获取指定路径下的所有 .class 文件资源 + 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) { + log.error("初始化数据安全缓存时出错:{}", e.getMessage()); + } + } + + /** + * 在指定的类中查找特定的注解 DataPermission,并将带有这个注解的方法或类存储到 dataPermissionCacheMap 中 + * + * @param clazz 要查找的类 + */ + private void findAnnotation(Class clazz) { + DataPermission dataPermission; + 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); + } + } + if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { + dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); + dataPermissionCacheMap.put(clazz.getName(), dataPermission); + } + } + + /** + * 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象 + * + * @param mapperId 映射语句 ID + * @return DataPermission 注解对象,如果不存在则返回 null + */ + public DataPermission getDataPermission(String mapperId) { + // 检查上下文中是否包含映射语句 ID 对应的 DataPermission 注解对象 + if (DataPermissionHelper.getPermission() != null) { + return DataPermissionHelper.getPermission(); + } + // 检查缓存中是否包含映射语句 ID 对应的 DataPermission 注解对象 + if (dataPermissionCacheMap.containsKey(mapperId)) { + return dataPermissionCacheMap.get(mapperId); + } + // 如果缓存中不包含映射语句 ID 对应的 DataPermission 注解对象,则尝试使用类名作为键查找 + String clazzName = mapperId.substring(0, mapperId.lastIndexOf(".")); + if (dataPermissionCacheMap.containsKey(clazzName)) { + return dataPermissionCacheMap.get(clazzName); + } + return null; + } + + /** + * 检查给定的映射语句 ID 是否有效,即是否能够找到对应的 DataPermission 注解对象 + * + * @param mapperId 映射语句 ID + * @return 如果找到对应的 DataPermission 注解对象,则返回 false;否则返回 true + */ + public boolean invalid(String mapperId) { + return getDataPermission(mapperId) == null; + } + + /** + * 对所有null变量找不到的变量返回默认值 + */ + @AllArgsConstructor + private static class NullSafeStandardEvaluationContext extends StandardEvaluationContext { + + private final Object defaultValue; + + @Override + public Object lookupVariable(String name) { + Object obj = super.lookupVariable(name); + // 如果读取到的值是 null,则返回默认值 + if (obj == null) { + return defaultValue; + } + return obj; + } + + } + + /** + * 对所有null变量找不到的变量返回默认值 委托模式 将不需要处理的方法委托给原处理器 + */ + @AllArgsConstructor + private static class NullSafePropertyAccessor implements PropertyAccessor { + + private final PropertyAccessor delegate; + private final Object defaultValue; + + @Override + public Class[] getSpecificTargetClasses() { + return delegate.getSpecificTargetClasses(); + } + + @Override + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return delegate.canRead(context, target, name); + } + + @Override + public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { + TypedValue value = delegate.read(context, target, name); + // 如果读取到的值是 null,则返回默认值 + if (value.getValue() == null) { + return new TypedValue(defaultValue); + } + return value; + } + + @Override + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + return delegate.canWrite(context, target, name); + } + + @Override + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { + delegate.write(context, target, name, newValue); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/PlusPostInitTableInfoHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/PlusPostInitTableInfoHandler.java new file mode 100644 index 00000000..aac6bb55 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/handler/PlusPostInitTableInfoHandler.java @@ -0,0 +1,28 @@ +package org.ruoyi.handler; + +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import org.apache.ibatis.session.Configuration; +import org.ruoyi.common.core.utils.SpringUtils; +import org.ruoyi.common.core.utils.reflect.ReflectUtils; + + +/** + * 修改表信息初始化方式 + * 目前用于全局修改是否使用逻辑删除 + * + * @author Lion Li + */ +public class PlusPostInitTableInfoHandler implements PostInitTableInfoHandler { + + @Override + public void postTableInfo(TableInfo tableInfo, Configuration configuration) { + String flag = SpringUtils.getProperty("mybatis-plus.enableLogicDelete", "true"); + // 只有关闭时 统一设置false 为true时mp自动判断不处理 + if (!Convert.toBool(flag)) { + ReflectUtils.setFieldValue(tableInfo, "withLogicDelete", false); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/helper/DataBaseHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/helper/DataBaseHelper.java similarity index 91% rename from ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/helper/DataBaseHelper.java rename to ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/helper/DataBaseHelper.java index f060e17a..16a16375 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/common/mybatis/helper/DataBaseHelper.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/helper/DataBaseHelper.java @@ -1,12 +1,13 @@ -package org.ruoyi.common.mybatis.helper; +package org.ruoyi.helper; import cn.hutool.core.convert.Convert; import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; import lombok.AccessLevel; import lombok.NoArgsConstructor; + import org.ruoyi.common.core.exception.ServiceException; import org.ruoyi.common.core.utils.SpringUtils; -import org.ruoyi.common.mybatis.enums.DataBaseType; +import org.ruoyi.enums.DataBaseType; import javax.sql.DataSource; import java.sql.Connection; @@ -62,8 +63,8 @@ public class DataBaseHelper { // charindex(',100,' , ',0,100,101,') <> 0 return "charindex(',%s,' , ','+%s+',') <> 0".formatted(var, var2); } else if (dataBasyType == DataBaseType.POSTGRE_SQL) { - // (select position(',100,' in ',0,100,101,')) <> 0 - return "(select position(',%s,' in ','||%s||',')) <> 0".formatted(var, var2); + // (select strpos(',0,100,101,' , ',100,')) <> 0 + return "(select strpos(','||%s||',' , ',%s,')) <> 0".formatted(var2, var); } else if (dataBasyType == DataBaseType.ORACLE) { // instr(',0,100,101,' , ',100,') <> 0 return "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var); @@ -71,6 +72,7 @@ public class DataBaseHelper { // find_in_set(100 , '0,100,101') return "find_in_set('%s' , %s) <> 0".formatted(var, var2); } + /** * 获取当前加载的数据库名 */ diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/helper/DataPermissionHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/helper/DataPermissionHelper.java new file mode 100644 index 00000000..3d8f99c8 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/helper/DataPermissionHelper.java @@ -0,0 +1,176 @@ +package org.ruoyi.helper; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaStorage; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.ruoyi.annotation.DataPermission; +import org.ruoyi.common.core.utils.reflect.ReflectUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; +import java.util.function.Supplier; + +/** + * 数据权限助手 + * + * @author Lion Li + * @version 3.5.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings("unchecked cast") +public class DataPermissionHelper { + + private static final String DATA_PERMISSION_KEY = "data:permission"; + + private static final ThreadLocal> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new); + + private static final ThreadLocal PERMISSION_CACHE = new ThreadLocal<>(); + + /** + * 获取当前执行mapper权限注解 + * + * @return 返回当前执行mapper权限注解 + */ + public static DataPermission getPermission() { + return PERMISSION_CACHE.get(); + } + + /** + * 设置当前执行mapper权限注解 + * + * @param dataPermission 数据权限注解 + */ + public static void setPermission(DataPermission dataPermission) { + PERMISSION_CACHE.set(dataPermission); + } + + /** + * 删除当前执行mapper权限注解 + */ + public static void removePermission() { + PERMISSION_CACHE.remove(); + } + + /** + * 从上下文中获取指定键的变量值,并将其转换为指定的类型 + * + * @param key 变量的键 + * @param 变量值的类型 + * @return 指定键的变量值,如果不存在则返回 null + */ + public static T getVariable(String key) { + Map context = getContext(); + return (T) context.get(key); + } + + /** + * 向上下文中设置指定键的变量值 + * + * @param key 要设置的变量的键 + * @param value 要设置的变量值 + */ + public static void setVariable(String key, Object value) { + Map context = getContext(); + context.put(key, value); + } + + /** + * 获取数据权限上下文 + * + * @return 存储在SaStorage中的Map对象,用于存储数据权限相关的上下文信息 + * @throws NullPointerException 如果数据权限上下文类型异常,则抛出NullPointerException + */ + public static Map getContext() { + SaStorage saStorage = SaHolder.getStorage(); + Object attribute = saStorage.get(DATA_PERMISSION_KEY); + if (ObjectUtil.isNull(attribute)) { + saStorage.set(DATA_PERMISSION_KEY, new HashMap<>()); + attribute = saStorage.get(DATA_PERMISSION_KEY); + } + if (attribute instanceof Map map) { + return map; + } + throw new NullPointerException("data permission context type exception"); + } + + private static IgnoreStrategy getIgnoreStrategy() { + Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL")); + if (ignoreStrategyLocal instanceof ThreadLocal IGNORE_STRATEGY_LOCAL) { + if (IGNORE_STRATEGY_LOCAL.get() instanceof IgnoreStrategy ignoreStrategy) { + return ignoreStrategy; + } + } + return null; + } + + /** + * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) + */ + public static void enableIgnore() { + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNull(ignoreStrategy)) { + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); + } else { + ignoreStrategy.setDataPermission(true); + } + Stack reentrantStack = REENTRANT_IGNORE.get(); + reentrantStack.push(reentrantStack.size() + 1); + } + + /** + * 关闭忽略数据权限 + */ + public static void disableIgnore() { + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNotNull(ignoreStrategy)) { + boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) + && !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack()) + && !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql()) + && !Boolean.TRUE.equals(ignoreStrategy.getTenantLine()) + && CollectionUtil.isEmpty(ignoreStrategy.getOthers()); + Stack reentrantStack = REENTRANT_IGNORE.get(); + boolean empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1; + if (noOtherIgnoreStrategy && empty) { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } else if (empty) { + ignoreStrategy.setDataPermission(false); + } + + } + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static void ignore(Runnable handle) { + enableIgnore(); + try { + handle.run(); + } finally { + disableIgnore(); + } + } + + /** + * 在忽略数据权限中执行 + * + * @param handle 处理执行方法 + */ + public static T ignore(Supplier handle) { + enableIgnore(); + try { + return handle.get(); + } finally { + disableIgnore(); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/interceptor/PlusDataPermissionInterceptor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/interceptor/PlusDataPermissionInterceptor.java new file mode 100644 index 00000000..10a58e5a --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/ruoyi/interceptor/PlusDataPermissionInterceptor.java @@ -0,0 +1,181 @@ +package org.ruoyi.interceptor; + +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; +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 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.SetOperationList; +import net.sf.jsqlparser.statement.update.Update; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.ruoyi.handler.PlusDataPermissionHandler; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +/** + * 数据权限拦截器 + * + * @author Lion Li + * @version 3.5.0 + */ +@Slf4j +public class PlusDataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor { + + private final PlusDataPermissionHandler dataPermissionHandler; + + /** + * 构造函数,初始化 PlusDataPermissionHandler 实例 + * + * @param mapperPackage 扫描的映射器包 + */ + public PlusDataPermissionInterceptor(String mapperPackage) { + this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage); + } + + /** + * 在执行查询之前,检查并处理数据权限相关逻辑 + * + * @param executor MyBatis 执行器对象 + * @param ms 映射语句对象 + * @param parameter 方法参数 + * @param rowBounds 分页对象 + * @param resultHandler 结果处理器 + * @param boundSql 绑定的 SQL 对象 + * @throws SQLException 如果发生 SQL 异常 + */ + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + // 检查是否需要忽略数据权限处理 + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + // 检查是否缺少有效的数据权限注解 + if (dataPermissionHandler.invalid(ms.getId())) { + return; + } + // 解析 sql 分配对应方法 + PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql); + mpBs.sql(parserSingle(mpBs.sql(), ms.getId())); + } + + /** + * 在准备 SQL 语句之前,检查并处理更新和删除操作的数据权限相关逻辑 + * + * @param sh MyBatis StatementHandler 对象 + * @param connection 数据库连接对象 + * @param transactionTimeout 事务超时时间 + */ + @Override + public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { + PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); + MappedStatement ms = mpSh.mappedStatement(); + // 获取 SQL 命令类型(增、删、改、查) + SqlCommandType sct = ms.getSqlCommandType(); + + // 只处理更新和删除操作的 SQL 语句 + if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { + if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) { + return; + } + // 检查是否缺少有效的数据权限注解 + if (dataPermissionHandler.invalid(ms.getId())) { + return; + } + PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); + mpBs.sql(parserMulti(mpBs.sql(), ms.getId())); + } + } + + /** + * 处理 SELECT 查询语句中的 WHERE 条件 + * + * @param select SELECT 查询对象 + * @param index 查询语句的索引 + * @param sql 查询语句 + * @param obj WHERE 条件参数 + */ + @Override + protected void processSelect(Select select, int index, String sql, Object obj) { + if (select instanceof PlainSelect) { + this.setWhere((PlainSelect) select, (String) obj); + } else if (select instanceof SetOperationList setOperationList) { + List - + select column_name, (case when (is_nullable = 'no' column_key != 'PRI') then '1' else null end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, @@ -19,7 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName}) order by ordinal_position - + select lower(temp.column_name) as column_name, (case when (temp.nullable = 'N' and temp.constraint_type != 'P') then '1' else null end) as is_required, (case when temp.constraint_type = 'P' then '1' else '0' end) as is_pk, @@ -39,7 +39,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" WHERE temp.row_flg = 1 ORDER BY temp.column_id - + SELECT column_name, is_required, is_pk, sort, column_comment, is_increment, column_type FROM ( SELECT c.relname AS table_name, @@ -73,7 +73,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" WHERE table_name = (#{tableName}) AND column_type ]]> '-' - + SELECT cast(A.NAME as nvarchar) as column_name, cast(B.NAME as nvarchar) + (case when B.NAME = 'numeric' then '(' + cast(A.prec as nvarchar) + ',' + cast(A.scale as nvarchar) + ')' else '' end) as column_type, 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 a0a7695c..6c994d82 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 @@ -15,7 +15,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"