init
This commit is contained in:
		
							
								
								
									
										18
									
								
								RuoYi-Vue-Plus/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								RuoYi-Vue-Plus/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| # http://editorconfig.org | ||||
| root = true | ||||
|  | ||||
| # 空格替代Tab缩进在各种编辑工具下效果一致 | ||||
| [*] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
| charset = utf-8 | ||||
| end_of_line = lf | ||||
| trim_trailing_whitespace = true | ||||
| insert_final_newline = true | ||||
|  | ||||
| [*.{json,yml,yaml}] | ||||
| indent_size = 2 | ||||
|  | ||||
| [*.md] | ||||
| insert_final_newline = false | ||||
| trim_trailing_whitespace = false | ||||
							
								
								
									
										51
									
								
								RuoYi-Vue-Plus/.gitee/ISSUE_TEMPLATE/bug.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								RuoYi-Vue-Plus/.gitee/ISSUE_TEMPLATE/bug.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| name: Bug 反馈 | ||||
| description: 当你使用过程中发现了一个 Bug,导致应用崩溃或抛出异常,或者有一个组件存在问题,或者某些地方看起来不对劲,请在这里反馈。 | ||||
| title: "[Bug]: " | ||||
| labels: ["bug"] | ||||
| body: | ||||
|   - type: textarea | ||||
|     id: version | ||||
|     attributes: | ||||
|       label: 版本 | ||||
|       description: 你当前正在使用我们软件的哪个版本(pom文件内的版本号)? | ||||
|       value: | | ||||
|         注意: 未填写版本号不予处理直接关闭或删除 | ||||
|         jdk版本(带上尾号): | ||||
|         框架版本(项目启动时输出的版本号): | ||||
|         其他依赖版本(你觉得有必要的): | ||||
|     validations: | ||||
|       required: true | ||||
|   - type: checkboxes | ||||
|     attributes: | ||||
|       label: 功能不好用不会用是否已经看过项目文档? | ||||
|       options: | ||||
|         - label: https://plus-doc.dromara.org | ||||
|           required: true | ||||
|   - type: checkboxes | ||||
|     attributes: | ||||
|       label: 这个问题是否已经存在? | ||||
|       options: | ||||
|         - label: 我已经搜索过现有的问题 (https://gitee.com/dromara/RuoYi-Vue-Plus/issues) | ||||
|           required: true | ||||
|   - type: textarea | ||||
|     attributes: | ||||
|       label: 希望结果 | ||||
|       description: 想知道你觉得怎么样是正常或者合理的。 | ||||
|     validations: | ||||
|       required: true | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       label: 如何复现 | ||||
|       description: 请详细告诉我们如何复现你遇到的问题。 | ||||
|       value: | | ||||
|         如涉及代码,可提供一个最小代码示例,并使用```附上它,或者截图均可,越详细越好。<br> | ||||
|         大多数问题都是:代码编写错误问题,逻辑问题,或者用法错误等问题。 | ||||
|     validations: | ||||
|       required: true | ||||
|   - type: textarea | ||||
|     attributes: | ||||
|       label: 相关代码与报错信息(请勿发混乱格式) | ||||
|       description: 如果可以的话,上传任何关于 bug 的截图。 | ||||
|       value: | | ||||
|         [在这里上传图片] | ||||
|  | ||||
							
								
								
									
										5
									
								
								RuoYi-Vue-Plus/.gitee/ISSUE_TEMPLATE/config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								RuoYi-Vue-Plus/.gitee/ISSUE_TEMPLATE/config.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| blank_issues_enabled: false | ||||
| contact_links: | ||||
|   - name: RuoYi-Vue-Plus 文档中心 | ||||
|     url: https://plus-doc.dromara.org | ||||
|     about: 提供 RuoYi-Vue-Plus 搭建使用指南、平台基本开发使用方式、介绍、基础知识和常见问题解答 | ||||
							
								
								
									
										43
									
								
								RuoYi-Vue-Plus/.gitee/ISSUE_TEMPLATE/feature.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								RuoYi-Vue-Plus/.gitee/ISSUE_TEMPLATE/feature.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| name: 功能建议 | ||||
| description: 对本项目提出一个功能建议。 | ||||
| title: "[功能建议]: " | ||||
| labels: ["enhancement"] | ||||
| body: | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: | | ||||
|         感谢提出功能建议,我们将仔细考虑!请持续关注该issues,在加入计划后我们会有贡献者设置为负责人,同时状态成为进行中。 | ||||
|   - type: textarea | ||||
|     id: related-problem | ||||
|     attributes: | ||||
|       label: 你的功能建议是否和某个问题相关? | ||||
|       description: 清晰并简洁地描述问题是什么,例如,当我...时,我总是感到困扰。 | ||||
|     validations: | ||||
|       required: false | ||||
|   - type: textarea | ||||
|     id: desired-solution | ||||
|     attributes: | ||||
|       label: 你希望看到什么解决方案? | ||||
|       description: 清晰并简洁地描述你希望发生的事情。 | ||||
|     validations: | ||||
|       required: true | ||||
|   - type: textarea | ||||
|     id: alternatives | ||||
|     attributes: | ||||
|       label: 你考虑过哪些替代方案? | ||||
|       description: 清晰并简洁地描述你考虑过的任何替代解决方案或功能。 | ||||
|     validations: | ||||
|       required: false | ||||
|   - type: textarea | ||||
|     id: additional-context | ||||
|     attributes: | ||||
|       label: 你有其他上下文或截图吗? | ||||
|       description: 在此处添加有关功能请求的任何其他上下文或截图。 | ||||
|     validations: | ||||
|       required: false | ||||
|   - type: checkboxes | ||||
|     attributes: | ||||
|       label: 意向参与贡献 | ||||
|       options: | ||||
|         - label: 我有意向参与具体功能的开发实现并将代码贡献回到上游社区。 | ||||
|           required: false | ||||
							
								
								
									
										7
									
								
								RuoYi-Vue-Plus/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								RuoYi-Vue-Plus/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| ### 更改目的 解决了什么问题(请提交到dev分支) | ||||
|  | ||||
|  | ||||
| ### 改动逻辑 这么写的思路(让作者更好的理解你的意图) | ||||
|  | ||||
|  | ||||
| ### 测试 都做了哪些测试(未经过测试不采纳) | ||||
							
								
								
									
										48
									
								
								RuoYi-Vue-Plus/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								RuoYi-Vue-Plus/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| ###################################################################### | ||||
| # 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 | ||||
|  | ||||
| .flattened-pom.xml | ||||
							
								
								
									
										12
									
								
								RuoYi-Vue-Plus/.run/ruoyi-monitor-admin.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								RuoYi-Vue-Plus/.run/ruoyi-monitor-admin.run.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| <component name="ProjectRunConfigurationManager"> | ||||
|   <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> | ||||
|     <deployment type="dockerfile"> | ||||
|       <settings> | ||||
|         <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.3.0" /> | ||||
|         <option name="buildOnly" value="true" /> | ||||
|         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" /> | ||||
|       </settings> | ||||
|     </deployment> | ||||
|     <method v="2" /> | ||||
|   </configuration> | ||||
| </component> | ||||
							
								
								
									
										12
									
								
								RuoYi-Vue-Plus/.run/ruoyi-server.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								RuoYi-Vue-Plus/.run/ruoyi-server.run.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| <component name="ProjectRunConfigurationManager"> | ||||
|   <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> | ||||
|     <deployment type="dockerfile"> | ||||
|       <settings> | ||||
|         <option name="imageTag" value="ruoyi/ruoyi-server:5.3.0" /> | ||||
|         <option name="buildOnly" value="true" /> | ||||
|         <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" /> | ||||
|       </settings> | ||||
|     </deployment> | ||||
|     <method v="2" /> | ||||
|   </configuration> | ||||
| </component> | ||||
							
								
								
									
										12
									
								
								RuoYi-Vue-Plus/.run/ruoyi-snailjob-server.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								RuoYi-Vue-Plus/.run/ruoyi-snailjob-server.run.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| <component name="ProjectRunConfigurationManager"> | ||||
|   <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> | ||||
|     <deployment type="dockerfile"> | ||||
|       <settings> | ||||
|         <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.3.0" /> | ||||
|         <option name="buildOnly" value="true" /> | ||||
|         <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" /> | ||||
|       </settings> | ||||
|     </deployment> | ||||
|     <method v="2" /> | ||||
|   </configuration> | ||||
| </component> | ||||
							
								
								
									
										20
									
								
								RuoYi-Vue-Plus/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								RuoYi-Vue-Plus/LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2019 RuoYi-Vue-Plus | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | ||||
| this software and associated documentation files (the "Software"), to deal in | ||||
| the Software without restriction, including without limitation the rights to | ||||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||||
| the Software, and to permit persons to whom the Software is furnished to do so, | ||||
| subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||||
| FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||||
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||||
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
							
								
								
									
										185
									
								
								RuoYi-Vue-Plus/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								RuoYi-Vue-Plus/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | ||||
| <img src="https://foruda.gitee.com/images/1679673773341074847/178e8451_1766278.png" width="50%" height="50%"> | ||||
| <div style="height: 10px; clear: both;"></div> | ||||
|  | ||||
| - - - | ||||
| ## 平台简介 | ||||
|  | ||||
| [](https://gitee.com/dromara/RuoYi-Vue-Plus) | ||||
| [](https://github.com/dromara/RuoYi-Vue-Plus) | ||||
| [](https://gitcode.com/dromara/RuoYi-Vue-Plus) | ||||
| [](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) | ||||
| [](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) | ||||
| <br> | ||||
| [](https://gitee.com/dromara/RuoYi-Vue-Plus) | ||||
| []() | ||||
| []() | ||||
| []() | ||||
|  | ||||
| > Dromara RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架) | ||||
|  | ||||
| > 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br> | ||||
| 活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 | ||||
|  | ||||
| > 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) | ||||
|  | ||||
| > 官方前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)<br> | ||||
| > 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) | ||||
|  | ||||
| > 文档地址: [plus-doc](https://plus-doc.dromara.org) | ||||
|  | ||||
| ## 赞助商 | ||||
|  | ||||
| MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br> | ||||
| CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br> | ||||
| 数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br> | ||||
| 引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br> | ||||
| <font color="red">**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/** </font><br> | ||||
| [如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group) | ||||
|  | ||||
| # 本框架与RuoYi的功能差异 | ||||
|  | ||||
| | 功能          | 本框架                                                                                                               | RuoYi                                                                              | | ||||
| |-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| | ||||
| | 前端项目        | 采用 Vue3 + TS + ElementPlus 重写                                                                                     | 基于Vue2/Vue3 + JS                                                                   |  | ||||
| | 后端项目结构      | 采用插件化 + 扩展包形式 结构解耦 易于扩展                                                                                           | 模块相互注入耦合严重难以扩展                                                                     |  | ||||
| | 后端代码风格      | 严格遵守Alibaba规范与项目统一配置的代码格式化                                                                                        | 代码书写与常规结构不同阅读障碍大                                                                   | | ||||
| | Web容器       | 采用 Undertow 基于 XNIO 的高性能容器                                                                                        | 采用 Tomcat                                                                          | | ||||
| | 权限认证        | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展                                                                                  | Spring Security 配置繁琐扩展性极差                                                          | | ||||
| | 权限注解        | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式        | 只支持是否存在匹配                                                                          | | ||||
| | 三方鉴权        | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证                                                                               | 无                                                                                  | | ||||
| | 关系数据库支持     | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例)      | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换                                                    | | ||||
| | 缓存数据库       | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列                                                                             | Redis 简单 get set 支持                                                                | | ||||
| | Redis客户端    | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题              | | ||||
| | 缓存注解        | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存                                      | 需手动编写Redis代码逻辑                                                                     | | ||||
| | ORM框架       | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等                                             | 采用 Mybatis 基于XML需要手写SQL                                                            | | ||||
| | SQL监控       | 采用 p6spy 可输出完整SQL与执行时间监控                                                                                          | log输出 需手动拼接sql与参数无法快速查看调试问题                                                        | | ||||
| | 数据分页        | 采用 Mybatis-Plus 分页插件<br/>框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序                                                  | 采用 PageHelper 仅支持单查询分页 参数只能从param传 只能单排序 功能扩展性差 体验不好                               | | ||||
| | 数据权限        | 采用 Mybatis-Plus 插件 自行分析拼接SQL 无感式过滤<br/>只需为Mapper设置好注解条件 支持多种自定义 不限于部门角色                                           | 采用 注解+aop 实现 基于部门角色 生成的sql兼容性差 不支持其他业务扩展<br/>生成sql后需手动拼接到具体业务sql上 对于多个Mapper查询不起作用 | | ||||
| | 数据脱敏        | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件<br/>支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展                                        | 无                                                                                  | | ||||
| | 数据加解密       | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等                                              | 无                                                                                  | | ||||
| | 接口传输加密      | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性                                                                     | 无                                                                                  | | ||||
| | 数据翻译        | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现                   | 无                                                                                  | | ||||
| | 多数据源框架      | 采用 dynamic-datasource 支持市面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持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 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储<br/>支持权限管理 安全可靠 文件可加密存储                                                     | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应                                                    | | ||||
| | 云存储         | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家                                                                          | 不支持                                                                                | | ||||
| | 短信          | 采用 sms4j 短信融合包 支持数十种短信厂家 只需在yml配置好厂家密钥即可使用 可多厂家共用                                                                 | 不支持                                                                                | | ||||
| | 邮件          | 采用 mail-api 通用协议支持大部分邮件厂商                                                                                         | 不支持                                                                                | | ||||
| | 接口文档        | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了                                                     | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成                                                |  | ||||
| | 校验框架        | 采用 Validation 支持注解与工具类校验 注解支持国际化                                                                                  | 仅支持注解 且注解不支持国际化                                                                    | | ||||
| | Excel框架     | 采用 Alibaba EasyExcel 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等                                               | 基于 POI 手写实现 功能有限 复杂 扩展性差                                                           | | ||||
| | 工作流支持       | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能                                                                                   | 无                                                                                  | | ||||
| | 工具类框架       | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码                                                       | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等                                            |  | ||||
| | 监控框架        | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控                                    | 无                                                                                  |  | ||||
| | 链路追踪        | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗<br/>用了它即可实时查看请求经过的每一处每一个节点                                            | 无                                                                                  | | ||||
| | 代码生成器       | 只需设计好表结构 一键生成所有crud代码与页面<br/>降低80%的开发量 把精力都投入到业务设计上<br/>框架为其适配MP、SpringDoc规范化代码 同时支持动态多数据源代码生成                    | 代码生成原生结构 只支持单数据源生成                                                                 | | ||||
| | 部署方式        | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼                                                                           | 原生jar部署 其他环境需手动下载安装 自行搭建                                                           |  | ||||
| | 项目路径修改      | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的                                                                              | 需要做很多改造 文档说明有限                                                                     | | ||||
| | 国际化         | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化                                                                     | 只提供基础功能 其他需自行编写扩展                                                                  | | ||||
| | 代码单例测试      | 提供单例测试 使用方式编写方法与maven多环境单测插件                                                                                      | 只提供基础功能 其他需自行编写扩展                                                                  | | ||||
| | Demo案例      | 提供框架功能的实际使用案例 单独一个模块提供了很多很全                                                                                       | 无                                                                                  | | ||||
|  | ||||
|  | ||||
| ## 本框架与RuoYi的业务差异 | ||||
|  | ||||
| | 业务     | 功能说明                                                                 | 本框架 | RuoYi            | | ||||
| |--------|----------------------------------------------------------------------|-----|------------------| | ||||
| | 租户管理   | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等                                      | 支持  | 无                | | ||||
| | 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等                                          | 支持  | 无                | | ||||
| | 客户端管理  | 系统内对接的所有客户端管理 如: pc端、小程序端等<br>支持动态授权登录方式 如: 短信登录、密码登录等 支持动态控制token时效 | 支持  | 无                | | ||||
| | 用户管理   | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等                                       | 支持  | 支持               | | ||||
| | 部门管理   | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限                                       | 支持  | 支持               | | ||||
| | 岗位管理   | 配置系统用户所属担任职务                                                         | 支持  | 支持               | | ||||
| | 菜单管理   | 配置系统菜单、操作权限、按钮权限标识等                                                  | 支持  | 支持               | | ||||
| | 角色管理   | 角色菜单权限分配、设置角色按机构进行数据范围权限划分                                           | 支持  | 支持               | | ||||
| | 字典管理   | 对系统中经常使用的一些较为固定的数据进行维护                                               | 支持  | 支持               | | ||||
| | 参数管理   | 对系统动态配置常用参数                                                          | 支持  | 支持               | | ||||
| | 通知公告   | 系统通知公告信息发布维护                                                         | 支持  | 支持               | | ||||
| | 操作日志   | 系统正常操作日志记录和查询 系统异常信息日志记录和查询                                          | 支持  | 支持               | | ||||
| | 登录日志   | 系统登录日志记录查询包含登录异常                                                     | 支持  | 支持               | | ||||
| | 文件管理   | 系统文件展示、上传、下载、删除等管理                                                   | 支持  | 无                | | ||||
| | 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理                                       | 支持  | 无                | | ||||
| | 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作                                                | 支持  | 支持               | | ||||
| | 定时任务   | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等                                      | 支持  | 仅支持任务与日志管理       | | ||||
| | 代码生成   | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载                              | 支持  | 仅支持单数据源          | | ||||
| | 系统接口   | 根据业务代码自动生成相关的api接口文档                                                 | 支持  | 支持               | | ||||
| | 服务监控   | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等                                  | 支持  | 仅支持单机CPU、内存、磁盘监控 | | ||||
| | 缓存监控   | 对系统的缓存信息查询,命令统计等。                                                    | 支持  | 支持               | | ||||
| | 在线构建器  | 拖动表单元素生成相应的HTML代码。                                                   | 支持  | 支持               | | ||||
| | 使用案例   | 系统的一些功能案例                                                            | 支持  | 不支持              | | ||||
|  | ||||
| ## 参考文档 | ||||
|  | ||||
| 使用框架前请仔细阅读文档重点注意事项 | ||||
| <br> | ||||
| >[初始化项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init) | ||||
| >>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/init) | ||||
| > | ||||
| >[专栏与视频 入门必看](https://plus-doc.dromara.org/#/common/column) | ||||
| >>[https://plus-doc.dromara.org/#/common/column](https://plus-doc.dromara.org/#/common/column) | ||||
| > | ||||
| >[部署项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy) | ||||
| >>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy) | ||||
| > | ||||
| >[如何加群](https://plus-doc.dromara.org/#/common/add_group) | ||||
| >>[https://plus-doc.dromara.org/#/common/add_group](https://plus-doc.dromara.org/#/common/add_group) | ||||
| > | ||||
| >[参考文档 Wiki](https://plus-doc.dromara.org) | ||||
| >>[https://plus-doc.dromara.org](https://plus-doc.dromara.org) | ||||
|  | ||||
| ## 软件架构图 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## 如何参与贡献 | ||||
|  | ||||
| [参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution) | ||||
|  | ||||
| ## 捐献作者 | ||||
| 作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭   | ||||
| <img src="https://foruda.gitee.com/images/1678975784848381069/d8661ed9_1766278.png" width="300px" height="450px" /> | ||||
| <img src="https://foruda.gitee.com/images/1678975801230205215/6f96229d_1766278.png" width="300px" height="450px" /> | ||||
|  | ||||
| ## 演示图例 | ||||
|  | ||||
| |                                                                                            |                                                                                            | | ||||
| |--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
| |  |  | | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										506
									
								
								RuoYi-Vue-Plus/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										506
									
								
								RuoYi-Vue-Plus/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,506 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|  | ||||
|     <groupId>org.dromara</groupId> | ||||
|     <artifactId>ruoyi-vue-plus</artifactId> | ||||
|     <version>${revision}</version> | ||||
|  | ||||
|     <name>RuoYi-Vue-Plus</name> | ||||
|     <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url> | ||||
|     <description>Dromara RuoYi-Vue-Plus多租户管理系统</description> | ||||
|  | ||||
|     <properties> | ||||
|         <revision>5.3.0</revision> | ||||
|         <spring-boot.version>3.4.2</spring-boot.version> | ||||
|         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
|         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | ||||
|         <java.version>17</java.version> | ||||
|         <mybatis.version>3.5.16</mybatis.version> | ||||
|         <springdoc.version>2.8.4</springdoc.version> | ||||
|         <therapi-javadoc.version>0.15.0</therapi-javadoc.version> | ||||
|         <easyexcel.version>4.0.3</easyexcel.version> | ||||
|         <velocity.version>2.3</velocity.version> | ||||
|         <satoken.version>1.40.0</satoken.version> | ||||
|         <mybatis-plus.version>3.5.10</mybatis-plus.version> | ||||
|         <p6spy.version>3.9.1</p6spy.version> | ||||
|         <hutool.version>5.8.35</hutool.version> | ||||
|         <spring-boot-admin.version>3.4.1</spring-boot-admin.version> | ||||
|         <redisson.version>3.44.0</redisson.version> | ||||
|         <lock4j.version>2.2.7</lock4j.version> | ||||
|         <dynamic-ds.version>4.3.1</dynamic-ds.version> | ||||
|         <snailjob.version>1.3.0</snailjob.version> | ||||
|         <mapstruct-plus.version>1.4.6</mapstruct-plus.version> | ||||
|         <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version> | ||||
|         <lombok.version>1.18.36</lombok.version> | ||||
|         <bouncycastle.version>1.76</bouncycastle.version> | ||||
|         <justauth.version>1.16.7</justauth.version> | ||||
|         <!-- 离线IP地址定位库 --> | ||||
|         <ip2region.version>2.7.0</ip2region.version> | ||||
|  | ||||
|         <!-- OSS 配置 --> | ||||
|         <aws.sdk.version>2.28.22</aws.sdk.version> | ||||
|         <aws.crt.version>0.31.3</aws.crt.version> | ||||
|         <!-- SMS 配置 --> | ||||
|         <sms4j.version>3.3.3</sms4j.version> | ||||
|         <!-- 限制框架中的fastjson版本 --> | ||||
|         <fastjson.version>1.2.83</fastjson.version> | ||||
|         <!-- 面向运行时的D-ORM依赖 --> | ||||
|         <anyline.version>8.7.2-20250101</anyline.version> | ||||
|         <!--工作流配置--> | ||||
|         <warm-flow.version>1.6.6</warm-flow.version> | ||||
|  | ||||
|         <!-- 插件版本 --> | ||||
|         <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> | ||||
|         <maven-war-plugin.version>3.2.2</maven-war-plugin.version> | ||||
|         <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version> | ||||
|         <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> | ||||
|         <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version> | ||||
|     </properties> | ||||
|  | ||||
|     <profiles> | ||||
|         <profile> | ||||
|             <id>local</id> | ||||
|             <properties> | ||||
|                 <!-- 环境标识,需要与配置文件的名称相对应 --> | ||||
|                 <profiles.active>local</profiles.active> | ||||
|                 <logging.level>info</logging.level> | ||||
|                 <monitor.username>ruoyi</monitor.username> | ||||
|                 <monitor.password>123456</monitor.password> | ||||
|             </properties> | ||||
|         </profile> | ||||
|         <profile> | ||||
|             <id>dev</id> | ||||
|             <properties> | ||||
|                 <!-- 环境标识,需要与配置文件的名称相对应 --> | ||||
|                 <profiles.active>dev</profiles.active> | ||||
|                 <logging.level>info</logging.level> | ||||
|                 <monitor.username>ruoyi</monitor.username> | ||||
|                 <monitor.password>123456</monitor.password> | ||||
|             </properties> | ||||
|             <activation> | ||||
|                 <!-- 默认环境 --> | ||||
|                 <activeByDefault>true</activeByDefault> | ||||
|             </activation> | ||||
|         </profile> | ||||
|         <profile> | ||||
|             <id>prod</id> | ||||
|             <properties> | ||||
|                 <profiles.active>prod</profiles.active> | ||||
|                 <logging.level>warn</logging.level> | ||||
|                 <monitor.username>ruoyi</monitor.username> | ||||
|                 <monitor.password>123456</monitor.password> | ||||
|             </properties> | ||||
|         </profile> | ||||
|     </profiles> | ||||
|  | ||||
|     <!-- 依赖声明 --> | ||||
|     <dependencyManagement> | ||||
|         <dependencies> | ||||
|  | ||||
|             <!-- SpringBoot的依赖配置--> | ||||
|             <dependency> | ||||
|                 <groupId>org.springframework.boot</groupId> | ||||
|                 <artifactId>spring-boot-dependencies</artifactId> | ||||
|                 <version>${spring-boot.version}</version> | ||||
|                 <type>pom</type> | ||||
|                 <scope>import</scope> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- hutool 的依赖配置--> | ||||
|             <dependency> | ||||
|                 <groupId>cn.hutool</groupId> | ||||
|                 <artifactId>hutool-bom</artifactId> | ||||
|                 <version>${hutool.version}</version> | ||||
|                 <type>pom</type> | ||||
|                 <scope>import</scope> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- Warm-Flow国产工作流引擎, 在线文档:http://warm-flow.cn/ --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara.warm</groupId> | ||||
|                 <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId> | ||||
|                 <version>${warm-flow.version}</version> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara.warm</groupId> | ||||
|                 <artifactId>warm-flow-plugin-ui-sb-web</artifactId> | ||||
|                 <version>${warm-flow.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- JustAuth 的依赖配置--> | ||||
|             <dependency> | ||||
|                 <groupId>me.zhyd.oauth</groupId> | ||||
|                 <artifactId>JustAuth</artifactId> | ||||
|                 <version>${justauth.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- common 的依赖配置--> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-bom</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|                 <type>pom</type> | ||||
|                 <scope>import</scope> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>org.springdoc</groupId> | ||||
|                 <artifactId>springdoc-openapi-starter-webmvc-api</artifactId> | ||||
|                 <version>${springdoc.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>com.github.therapi</groupId> | ||||
|                 <artifactId>therapi-runtime-javadoc</artifactId> | ||||
|                 <version>${therapi-javadoc.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>org.projectlombok</groupId> | ||||
|                 <artifactId>lombok</artifactId> | ||||
|                 <version>${lombok.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>com.alibaba</groupId> | ||||
|                 <artifactId>easyexcel</artifactId> | ||||
|                 <version>${easyexcel.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- velocity代码生成使用模板 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.apache.velocity</groupId> | ||||
|                 <artifactId>velocity-engine-core</artifactId> | ||||
|                 <version>${velocity.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ --> | ||||
|             <dependency> | ||||
|                 <groupId>cn.dev33</groupId> | ||||
|                 <artifactId>sa-token-spring-boot3-starter</artifactId> | ||||
|                 <version>${satoken.version}</version> | ||||
|             </dependency> | ||||
|             <!-- Sa-Token 整合 jwt --> | ||||
|             <dependency> | ||||
|                 <groupId>cn.dev33</groupId> | ||||
|                 <artifactId>sa-token-jwt</artifactId> | ||||
|                 <version>${satoken.version}</version> | ||||
|                 <exclusions> | ||||
|                     <exclusion> | ||||
|                         <groupId>cn.hutool</groupId> | ||||
|                         <artifactId>hutool-all</artifactId> | ||||
|                     </exclusion> | ||||
|                 </exclusions> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>cn.dev33</groupId> | ||||
|                 <artifactId>sa-token-core</artifactId> | ||||
|                 <version>${satoken.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- dynamic-datasource 多数据源--> | ||||
|             <dependency> | ||||
|                 <groupId>com.baomidou</groupId> | ||||
|                 <artifactId>dynamic-datasource-spring-boot3-starter</artifactId> | ||||
|                 <version>${dynamic-ds.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>org.mybatis</groupId> | ||||
|                 <artifactId>mybatis</artifactId> | ||||
|                 <version>${mybatis.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>com.baomidou</groupId> | ||||
|                 <artifactId>mybatis-plus-spring-boot3-starter</artifactId> | ||||
|                 <version>${mybatis-plus.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>com.baomidou</groupId> | ||||
|                 <artifactId>mybatis-plus-jsqlparser</artifactId> | ||||
|                 <version>${mybatis-plus.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>com.baomidou</groupId> | ||||
|                 <artifactId>mybatis-plus-annotation</artifactId> | ||||
|                 <version>${mybatis-plus.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- sql性能分析插件 --> | ||||
|             <dependency> | ||||
|                 <groupId>p6spy</groupId> | ||||
|                 <artifactId>p6spy</artifactId> | ||||
|                 <version>${p6spy.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!--  AWS SDK for Java 2.x  --> | ||||
|             <dependency> | ||||
|                 <groupId>software.amazon.awssdk</groupId> | ||||
|                 <artifactId>s3</artifactId> | ||||
|                 <version>${aws.sdk.version}</version> | ||||
|             </dependency> | ||||
|             <!-- 使用AWS基于 CRT 的 S3 客户端 --> | ||||
|             <dependency> | ||||
|                 <groupId>software.amazon.awssdk.crt</groupId> | ||||
|                 <artifactId>aws-crt</artifactId> | ||||
|                 <version>${aws.crt.version}</version> | ||||
|             </dependency> | ||||
|             <!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 --> | ||||
|             <dependency> | ||||
|                 <groupId>software.amazon.awssdk</groupId> | ||||
|                 <artifactId>s3-transfer-manager</artifactId> | ||||
|                 <version>${aws.sdk.version}</version> | ||||
|             </dependency> | ||||
|             <!--短信sms4j--> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara.sms4j</groupId> | ||||
|                 <artifactId>sms4j-spring-boot-starter</artifactId> | ||||
|                 <version>${sms4j.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>de.codecentric</groupId> | ||||
|                 <artifactId>spring-boot-admin-starter-server</artifactId> | ||||
|                 <version>${spring-boot-admin.version}</version> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>de.codecentric</groupId> | ||||
|                 <artifactId>spring-boot-admin-starter-client</artifactId> | ||||
|                 <version>${spring-boot-admin.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!--redisson--> | ||||
|             <dependency> | ||||
|                 <groupId>org.redisson</groupId> | ||||
|                 <artifactId>redisson-spring-boot-starter</artifactId> | ||||
|                 <version>${redisson.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>com.baomidou</groupId> | ||||
|                 <artifactId>lock4j-redisson-spring-boot-starter</artifactId> | ||||
|                 <version>${lock4j.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- SnailJob Client --> | ||||
|             <dependency> | ||||
|                 <groupId>com.aizuda</groupId> | ||||
|                 <artifactId>snail-job-client-starter</artifactId> | ||||
|                 <version>${snailjob.version}</version> | ||||
|             </dependency> | ||||
|             <dependency> | ||||
|                 <groupId>com.aizuda</groupId> | ||||
|                 <artifactId>snail-job-client-job-core</artifactId> | ||||
|                 <version>${snailjob.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 加密包引入 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.bouncycastle</groupId> | ||||
|                 <artifactId>bcprov-jdk15to18</artifactId> | ||||
|                 <version>${bouncycastle.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>io.github.linpeilie</groupId> | ||||
|                 <artifactId>mapstruct-plus-spring-boot-starter</artifactId> | ||||
|                 <version>${mapstruct-plus.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 离线IP地址定位库 ip2region --> | ||||
|             <dependency> | ||||
|                 <groupId>org.lionsoul</groupId> | ||||
|                 <artifactId>ip2region</artifactId> | ||||
|                 <version>${ip2region.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>commons-io</groupId> | ||||
|                 <artifactId>commons-io</artifactId> | ||||
|                 <version>2.15.0</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>com.alibaba</groupId> | ||||
|                 <artifactId>fastjson</artifactId> | ||||
|                 <version>${fastjson.version}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-system</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-job</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-generator</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-demo</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!--  工作流模块  --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-workflow</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|         </dependencies> | ||||
|     </dependencyManagement> | ||||
|  | ||||
|     <modules> | ||||
|         <module>ruoyi-admin</module> | ||||
|         <module>ruoyi-common</module> | ||||
|         <module>ruoyi-extend</module> | ||||
|         <module>ruoyi-modules</module> | ||||
|     </modules> | ||||
|     <packaging>pom</packaging> | ||||
|  | ||||
|     <build> | ||||
|         <plugins> | ||||
|             <plugin> | ||||
|                 <groupId>org.apache.maven.plugins</groupId> | ||||
|                 <artifactId>maven-compiler-plugin</artifactId> | ||||
|                 <version>${maven-compiler-plugin.version}</version> | ||||
|                 <configuration> | ||||
|                     <source>${java.version}</source> | ||||
|                     <target>${java.version}</target> | ||||
|                     <encoding>${project.build.sourceEncoding}</encoding> | ||||
|                     <annotationProcessorPaths> | ||||
|                         <path> | ||||
|                             <groupId>com.github.therapi</groupId> | ||||
|                             <artifactId>therapi-runtime-javadoc-scribe</artifactId> | ||||
|                             <version>${therapi-javadoc.version}</version> | ||||
|                         </path> | ||||
|                         <path> | ||||
|                             <groupId>org.projectlombok</groupId> | ||||
|                             <artifactId>lombok</artifactId> | ||||
|                             <version>${lombok.version}</version> | ||||
|                         </path> | ||||
|                         <path> | ||||
|                             <groupId>org.springframework.boot</groupId> | ||||
|                             <artifactId>spring-boot-configuration-processor</artifactId> | ||||
|                             <version>${spring-boot.version}</version> | ||||
|                         </path> | ||||
|                         <path> | ||||
|                             <groupId>io.github.linpeilie</groupId> | ||||
|                             <artifactId>mapstruct-plus-processor</artifactId> | ||||
|                             <version>${mapstruct-plus.version}</version> | ||||
|                         </path> | ||||
|                         <path> | ||||
|                             <groupId>org.projectlombok</groupId> | ||||
|                             <artifactId>lombok-mapstruct-binding</artifactId> | ||||
|                             <version>${mapstruct-plus.lombok.version}</version> | ||||
|                         </path> | ||||
|                     </annotationProcessorPaths> | ||||
|                     <compilerArgs> | ||||
|                         <arg>-parameters</arg> | ||||
|                     </compilerArgs> | ||||
|                 </configuration> | ||||
|             </plugin> | ||||
|             <!-- 单元测试使用 --> | ||||
|             <plugin> | ||||
|                 <groupId>org.apache.maven.plugins</groupId> | ||||
|                 <artifactId>maven-surefire-plugin</artifactId> | ||||
|                 <version>${maven-surefire-plugin.version}</version> | ||||
|                 <configuration> | ||||
|                     <argLine>-Dfile.encoding=UTF-8</argLine> | ||||
|                     <!-- 根据打包环境执行对应的@Tag测试方法 --> | ||||
|                     <groups>${profiles.active}</groups> | ||||
|                     <!-- 排除标签 --> | ||||
|                     <excludedGroups>exclude</excludedGroups> | ||||
|                 </configuration> | ||||
|             </plugin> | ||||
|             <!-- 统一版本号管理 --> | ||||
|             <plugin> | ||||
|                 <groupId>org.codehaus.mojo</groupId> | ||||
|                 <artifactId>flatten-maven-plugin</artifactId> | ||||
|                 <version>${flatten-maven-plugin.version}</version> | ||||
|                 <configuration> | ||||
|                     <updatePomFile>true</updatePomFile> | ||||
|                     <flattenMode>resolveCiFriendliesOnly</flattenMode> | ||||
|                 </configuration> | ||||
|                 <executions> | ||||
|                     <execution> | ||||
|                         <id>flatten</id> | ||||
|                         <phase>process-resources</phase> | ||||
|                         <goals> | ||||
|                             <goal>flatten</goal> | ||||
|                         </goals> | ||||
|                     </execution> | ||||
|                     <execution> | ||||
|                         <id>flatten.clean</id> | ||||
|                         <phase>clean</phase> | ||||
|                         <goals> | ||||
|                             <goal>clean</goal> | ||||
|                         </goals> | ||||
|                     </execution> | ||||
|                 </executions> | ||||
|             </plugin> | ||||
|         </plugins> | ||||
|         <resources> | ||||
|             <resource> | ||||
|                 <directory>src/main/resources</directory> | ||||
|                 <!-- 关闭过滤 --> | ||||
|                 <filtering>false</filtering> | ||||
|             </resource> | ||||
|             <resource> | ||||
|                 <directory>src/main/resources</directory> | ||||
|                 <!-- 引入所有 匹配文件进行过滤 --> | ||||
|                 <includes> | ||||
|                     <include>application*</include> | ||||
|                     <include>bootstrap*</include> | ||||
|                     <include>banner*</include> | ||||
|                 </includes> | ||||
|                 <!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 --> | ||||
|                 <filtering>true</filtering> | ||||
|             </resource> | ||||
|         </resources> | ||||
|     </build> | ||||
|  | ||||
|     <repositories> | ||||
|         <repository> | ||||
|             <id>public</id> | ||||
|             <name>huawei nexus</name> | ||||
|             <url>https://mirrors.huaweicloud.com/repository/maven/</url> | ||||
|             <releases> | ||||
|                 <enabled>true</enabled> | ||||
|             </releases> | ||||
|         </repository> | ||||
|     </repositories> | ||||
|  | ||||
|     <pluginRepositories> | ||||
|         <pluginRepository> | ||||
|             <id>public</id> | ||||
|             <name>huawei nexus</name> | ||||
|             <url>https://mirrors.huaweicloud.com/repository/maven/</url> | ||||
|             <releases> | ||||
|                 <enabled>true</enabled> | ||||
|             </releases> | ||||
|             <snapshots> | ||||
|                 <enabled>false</enabled> | ||||
|             </snapshots> | ||||
|         </pluginRepository> | ||||
|     </pluginRepositories> | ||||
|  | ||||
| </project> | ||||
|  | ||||
|  | ||||
							
								
								
									
										30
									
								
								RuoYi-Vue-Plus/ruoyi-admin/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								RuoYi-Vue-Plus/ruoyi-admin/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ | ||||
| FROM bellsoft/liberica-openjdk-debian:17.0.11-cds | ||||
| #FROM bellsoft/liberica-openjdk-debian:21.0.5-cds | ||||
| #FROM findepi/graalvm:java17-native | ||||
|  | ||||
| LABEL maintainer="Lion Li" | ||||
|  | ||||
| RUN mkdir -p /ruoyi/server/logs \ | ||||
|     /ruoyi/server/temp \ | ||||
|     /ruoyi/skywalking/agent | ||||
|  | ||||
| WORKDIR /ruoyi/server | ||||
|  | ||||
| ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" | ||||
|  | ||||
| EXPOSE ${SERVER_PORT} | ||||
|  | ||||
| ADD ./target/ruoyi-admin.jar ./app.jar | ||||
| # 工作流字体文件 | ||||
| ADD ./zhFonts/ /usr/share/fonts/zhFonts/ | ||||
|  | ||||
| SHELL ["/bin/bash", "-c"] | ||||
|  | ||||
| ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ | ||||
|            # 应用名称 如果想区分集群节点监控 改成不同的名称即可 | ||||
|            #-Dskywalking.agent.service_name=ruoyi-server \ | ||||
|            #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \ | ||||
|            -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \ | ||||
|            -jar app.jar | ||||
|  | ||||
							
								
								
									
										153
									
								
								RuoYi-Vue-Plus/ruoyi-admin/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								RuoYi-Vue-Plus/ruoyi-admin/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,153 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <parent> | ||||
|         <artifactId>ruoyi-vue-plus</artifactId> | ||||
|         <groupId>org.dromara</groupId> | ||||
|         <version>${revision}</version> | ||||
|     </parent> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|     <packaging>jar</packaging> | ||||
|     <artifactId>ruoyi-admin</artifactId> | ||||
|  | ||||
|     <description> | ||||
|         web服务入口 | ||||
|     </description> | ||||
|  | ||||
|     <dependencies> | ||||
|  | ||||
|         <!-- Mysql驱动包 --> | ||||
|         <dependency> | ||||
|             <groupId>com.mysql</groupId> | ||||
|             <artifactId>mysql-connector-j</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
| <!--        <!– mp支持的数据库均支持 只需要增加对应的jdbc依赖即可 –>--> | ||||
| <!--        <!– Oracle –>--> | ||||
| <!--        <dependency>--> | ||||
| <!--            <groupId>com.oracle.database.jdbc</groupId>--> | ||||
| <!--            <artifactId>ojdbc8</artifactId>--> | ||||
| <!--        </dependency>--> | ||||
| <!--        <!– 兼容oracle低版本 –>--> | ||||
| <!--        <dependency>--> | ||||
| <!--            <groupId>com.oracle.database.nls</groupId>--> | ||||
| <!--            <artifactId>orai18n</artifactId>--> | ||||
| <!--        </dependency>--> | ||||
| <!--        <!– PostgreSql –>--> | ||||
| <!--        <dependency>--> | ||||
| <!--            <groupId>org.postgresql</groupId>--> | ||||
| <!--            <artifactId>postgresql</artifactId>--> | ||||
| <!--        </dependency>--> | ||||
| <!--        <!– SqlServer –>--> | ||||
| <!--        <dependency>--> | ||||
| <!--            <groupId>com.microsoft.sqlserver</groupId>--> | ||||
| <!--            <artifactId>mssql-jdbc</artifactId>--> | ||||
| <!--        </dependency>--> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-common-doc</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-common-social</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-common-ratelimiter</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-common-mail</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-system</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-job</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- 代码生成--> | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-generator</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!--  demo模块  --> | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-demo</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!--  工作流模块  --> | ||||
|         <dependency> | ||||
|             <groupId>org.dromara</groupId> | ||||
|             <artifactId>ruoyi-workflow</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>de.codecentric</groupId> | ||||
|             <artifactId>spring-boot-admin-starter-client</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-test</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- skywalking 整合 logback --> | ||||
| <!--        <dependency>--> | ||||
| <!--            <groupId>org.apache.skywalking</groupId>--> | ||||
| <!--            <artifactId>apm-toolkit-logback-1.x</artifactId>--> | ||||
| <!--            <version>${与你的agent探针版本保持一致}</version>--> | ||||
| <!--        </dependency>--> | ||||
| <!--        <dependency>--> | ||||
| <!--            <groupId>org.apache.skywalking</groupId>--> | ||||
| <!--            <artifactId>apm-toolkit-trace</artifactId>--> | ||||
| <!--            <version>${与你的agent探针版本保持一致}</version>--> | ||||
| <!--        </dependency>--> | ||||
|  | ||||
|     </dependencies> | ||||
|  | ||||
|     <build> | ||||
|         <finalName>${project.artifactId}</finalName> | ||||
|         <plugins> | ||||
|             <plugin> | ||||
|                 <groupId>org.springframework.boot</groupId> | ||||
|                 <artifactId>spring-boot-maven-plugin</artifactId> | ||||
|                 <version>${spring-boot.version}</version> | ||||
|                 <executions> | ||||
|                     <execution> | ||||
|                         <goals> | ||||
|                             <goal>repackage</goal> | ||||
|                         </goals> | ||||
|                     </execution> | ||||
|                 </executions> | ||||
|             </plugin> | ||||
|             <plugin> | ||||
|                 <groupId>org.apache.maven.plugins</groupId> | ||||
|                 <artifactId>maven-jar-plugin</artifactId> | ||||
|                 <version>${maven-jar-plugin.version}</version> | ||||
|             </plugin> | ||||
|             <plugin> | ||||
|                 <groupId>org.apache.maven.plugins</groupId> | ||||
|                 <artifactId>maven-war-plugin</artifactId> | ||||
|                 <version>${maven-war-plugin.version}</version> | ||||
|                 <configuration> | ||||
|                     <failOnMissingWebXml>false</failOnMissingWebXml> | ||||
|                     <warName>${project.artifactId}</warName> | ||||
|                 </configuration> | ||||
|             </plugin> | ||||
|         </plugins> | ||||
|     </build> | ||||
|  | ||||
| </project> | ||||
| @ -0,0 +1,23 @@ | ||||
| package org.dromara; | ||||
|  | ||||
| import org.springframework.boot.SpringApplication; | ||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
| import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; | ||||
|  | ||||
| /** | ||||
|  * 启动程序 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @SpringBootApplication | ||||
| public class DromaraApplication { | ||||
|  | ||||
|     public static void main(String[] args) { | ||||
|         SpringApplication application = new SpringApplication(DromaraApplication.class); | ||||
|         application.setApplicationStartup(new BufferingApplicationStartup(2048)); | ||||
|         application.run(args); | ||||
|         System.out.println("(♥◠‿◠)ノ゙  RuoYi-Vue-Plus启动成功   ლ(´ڡ`ლ)゙"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,17 @@ | ||||
| package org.dromara; | ||||
|  | ||||
| import org.springframework.boot.builder.SpringApplicationBuilder; | ||||
| import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; | ||||
|  | ||||
| /** | ||||
|  * web容器中进行部署 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| public class DromaraServletInitializer extends SpringBootServletInitializer { | ||||
|     @Override | ||||
|     protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { | ||||
|         return application.sources(DromaraApplication.class); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,239 @@ | ||||
| package org.dromara.web.controller; | ||||
|  | ||||
| import cn.dev33.satoken.annotation.SaIgnore; | ||||
| import cn.dev33.satoken.exception.NotLoginException; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.codec.Base64; | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import jakarta.servlet.http.HttpServletRequest; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import me.zhyd.oauth.model.AuthResponse; | ||||
| import me.zhyd.oauth.model.AuthUser; | ||||
| import me.zhyd.oauth.request.AuthRequest; | ||||
| import me.zhyd.oauth.utils.AuthStateUtils; | ||||
| import org.dromara.common.core.constant.SystemConstants; | ||||
| import org.dromara.common.core.domain.R; | ||||
| import org.dromara.common.core.domain.model.LoginBody; | ||||
| import org.dromara.common.core.domain.model.RegisterBody; | ||||
| import org.dromara.common.core.domain.model.SocialLoginBody; | ||||
| import org.dromara.common.core.utils.*; | ||||
| import org.dromara.common.encrypt.annotation.ApiEncrypt; | ||||
| import org.dromara.common.json.utils.JsonUtils; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.dromara.common.social.config.properties.SocialLoginConfigProperties; | ||||
| import org.dromara.common.social.config.properties.SocialProperties; | ||||
| import org.dromara.common.social.utils.SocialUtils; | ||||
| import org.dromara.common.sse.dto.SseMessageDto; | ||||
| import org.dromara.common.sse.utils.SseMessageUtils; | ||||
| import org.dromara.common.tenant.helper.TenantHelper; | ||||
| import org.dromara.system.domain.bo.SysTenantBo; | ||||
| import org.dromara.system.domain.vo.SysClientVo; | ||||
| import org.dromara.system.domain.vo.SysTenantVo; | ||||
| import org.dromara.system.service.ISysClientService; | ||||
| import org.dromara.system.service.ISysConfigService; | ||||
| import org.dromara.system.service.ISysSocialService; | ||||
| import org.dromara.system.service.ISysTenantService; | ||||
| import org.dromara.web.domain.vo.LoginTenantVo; | ||||
| import org.dromara.web.domain.vo.LoginVo; | ||||
| import org.dromara.web.domain.vo.TenantListVo; | ||||
| import org.dromara.web.service.IAuthStrategy; | ||||
| import org.dromara.web.service.SysLoginService; | ||||
| import org.dromara.web.service.SysRegisterService; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
| import org.springframework.web.bind.annotation.*; | ||||
|  | ||||
| import java.net.URL; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ScheduledExecutorService; | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| /** | ||||
|  * 认证 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Slf4j | ||||
| @SaIgnore | ||||
| @RequiredArgsConstructor | ||||
| @RestController | ||||
| @RequestMapping("/auth") | ||||
| public class AuthController { | ||||
|  | ||||
|     private final SocialProperties socialProperties; | ||||
|     private final SysLoginService loginService; | ||||
|     private final SysRegisterService registerService; | ||||
|     private final ISysConfigService configService; | ||||
|     private final ISysTenantService tenantService; | ||||
|     private final ISysSocialService socialUserService; | ||||
|     private final ISysClientService clientService; | ||||
|     private final ScheduledExecutorService scheduledExecutorService; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * 登录方法 | ||||
|      * | ||||
|      * @param body 登录信息 | ||||
|      * @return 结果 | ||||
|      */ | ||||
|     @ApiEncrypt | ||||
|     @PostMapping("/login") | ||||
|     public R<LoginVo> login(@RequestBody String body) { | ||||
|         LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class); | ||||
|         ValidatorUtils.validate(loginBody); | ||||
|         // 授权类型和客户端id | ||||
|         String clientId = loginBody.getClientId(); | ||||
|         String grantType = loginBody.getGrantType(); | ||||
|         SysClientVo client = clientService.queryByClientId(clientId); | ||||
|         // 查询不到 client 或 client 内不包含 grantType | ||||
|         if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { | ||||
|             log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); | ||||
|             return R.fail(MessageUtils.message("auth.grant.type.error")); | ||||
|         } else if (!SystemConstants.NORMAL.equals(client.getStatus())) { | ||||
|             return R.fail(MessageUtils.message("auth.grant.type.blocked")); | ||||
|         } | ||||
|         // 校验租户 | ||||
|         loginService.checkTenant(loginBody.getTenantId()); | ||||
|         // 登录 | ||||
|         LoginVo loginVo = IAuthStrategy.login(body, client, grantType); | ||||
|  | ||||
|         Long userId = LoginHelper.getUserId(); | ||||
|         scheduledExecutorService.schedule(() -> { | ||||
|             SseMessageDto dto = new SseMessageDto(); | ||||
|             dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统"); | ||||
|             dto.setUserIds(List.of(userId)); | ||||
|             SseMessageUtils.publishMessage(dto); | ||||
|         }, 5, TimeUnit.SECONDS); | ||||
|         return R.ok(loginVo); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取跳转URL | ||||
|      * | ||||
|      * @param source 登录来源 | ||||
|      * @return 结果 | ||||
|      */ | ||||
|     @GetMapping("/binding/{source}") | ||||
|     public R<String> authBinding(@PathVariable("source") String source, | ||||
|                                  @RequestParam String tenantId, @RequestParam String domain) { | ||||
|         SocialLoginConfigProperties obj = socialProperties.getType().get(source); | ||||
|         if (ObjectUtil.isNull(obj)) { | ||||
|             return R.fail(source + "平台账号暂不支持"); | ||||
|         } | ||||
|         AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties); | ||||
|         Map<String, String> map = new HashMap<>(); | ||||
|         map.put("tenantId", tenantId); | ||||
|         map.put("domain", domain); | ||||
|         map.put("state", AuthStateUtils.createState()); | ||||
|         String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8)); | ||||
|         return R.ok("操作成功", authorizeUrl); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 前端回调绑定授权(需要token) | ||||
|      * | ||||
|      * @param loginBody 请求体 | ||||
|      * @return 结果 | ||||
|      */ | ||||
|     @PostMapping("/social/callback") | ||||
|     public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) { | ||||
|         // 校验token | ||||
|         StpUtil.checkLogin(); | ||||
|         // 获取第三方登录信息 | ||||
|         AuthResponse<AuthUser> response = SocialUtils.loginAuth( | ||||
|                 loginBody.getSource(), loginBody.getSocialCode(), | ||||
|                 loginBody.getSocialState(), socialProperties); | ||||
|         AuthUser authUserData = response.getData(); | ||||
|         // 判断授权响应是否成功 | ||||
|         if (!response.ok()) { | ||||
|             return R.fail(response.getMsg()); | ||||
|         } | ||||
|         loginService.socialRegister(authUserData); | ||||
|         return R.ok(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * 取消授权(需要token) | ||||
|      * | ||||
|      * @param socialId socialId | ||||
|      */ | ||||
|     @DeleteMapping(value = "/unlock/{socialId}") | ||||
|     public R<Void> unlockSocial(@PathVariable Long socialId) { | ||||
|         // 校验token | ||||
|         StpUtil.checkLogin(); | ||||
|         Boolean rows = socialUserService.deleteWithValidById(socialId); | ||||
|         return rows ? R.ok() : R.fail("取消授权失败"); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * 退出登录 | ||||
|      */ | ||||
|     @PostMapping("/logout") | ||||
|     public R<Void> logout() { | ||||
|         loginService.logout(); | ||||
|         return R.ok("退出成功"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 用户注册 | ||||
|      */ | ||||
|     @ApiEncrypt | ||||
|     @PostMapping("/register") | ||||
|     public R<Void> register(@Validated @RequestBody RegisterBody user) { | ||||
|         if (!configService.selectRegisterEnabled(user.getTenantId())) { | ||||
|             return R.fail("当前系统没有开启注册功能!"); | ||||
|         } | ||||
|         registerService.register(user); | ||||
|         return R.ok(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 登录页面租户下拉框 | ||||
|      * | ||||
|      * @return 租户列表 | ||||
|      */ | ||||
|     @GetMapping("/tenant/list") | ||||
|     public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception { | ||||
|         // 返回对象 | ||||
|         LoginTenantVo result = new LoginTenantVo(); | ||||
|         boolean enable = TenantHelper.isEnable(); | ||||
|         result.setTenantEnabled(enable); | ||||
|         // 如果未开启租户这直接返回 | ||||
|         if (!enable) { | ||||
|             return R.ok(result); | ||||
|         } | ||||
|  | ||||
|         List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo()); | ||||
|         List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class); | ||||
|         try { | ||||
|             // 如果只超管返回所有租户 | ||||
|             if (LoginHelper.isSuperAdmin()) { | ||||
|                 result.setVoList(voList); | ||||
|                 return R.ok(result); | ||||
|             } | ||||
|         } catch (NotLoginException ignored) { | ||||
|         } | ||||
|  | ||||
|         // 获取域名 | ||||
|         String host; | ||||
|         String referer = request.getHeader("referer"); | ||||
|         if (StringUtils.isNotBlank(referer)) { | ||||
|             // 这里从referer中取值是为了本地使用hosts添加虚拟域名,方便本地环境调试 | ||||
|             host = referer.split("//")[1].split("/")[0]; | ||||
|         } else { | ||||
|             host = new URL(request.getRequestURL().toString()).getHost(); | ||||
|         } | ||||
|         // 根据域名进行筛选 | ||||
|         List<TenantListVo> list = StreamUtils.filter(voList, vo -> | ||||
|             StringUtils.equalsIgnoreCase(vo.getDomain(), host)); | ||||
|         result.setVoList(CollUtil.isNotEmpty(list) ? list : voList); | ||||
|         return R.ok(result); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,136 @@ | ||||
| package org.dromara.web.controller; | ||||
|  | ||||
| import cn.dev33.satoken.annotation.SaIgnore; | ||||
| import cn.hutool.captcha.AbstractCaptcha; | ||||
| import cn.hutool.captcha.generator.CodeGenerator; | ||||
| import cn.hutool.core.util.IdUtil; | ||||
| import cn.hutool.core.util.RandomUtil; | ||||
| import jakarta.validation.constraints.NotBlank; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.dromara.common.core.constant.Constants; | ||||
| import org.dromara.common.core.constant.GlobalConstants; | ||||
| import org.dromara.common.core.domain.R; | ||||
| import org.dromara.common.core.utils.SpringUtils; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import org.dromara.common.core.utils.reflect.ReflectUtils; | ||||
| import org.dromara.common.mail.config.properties.MailProperties; | ||||
| import org.dromara.common.mail.utils.MailUtils; | ||||
| import org.dromara.common.ratelimiter.annotation.RateLimiter; | ||||
| import org.dromara.common.ratelimiter.enums.LimitType; | ||||
| import org.dromara.common.redis.utils.RedisUtils; | ||||
| import org.dromara.common.web.config.properties.CaptchaProperties; | ||||
| import org.dromara.common.web.enums.CaptchaType; | ||||
| import org.dromara.sms4j.api.SmsBlend; | ||||
| import org.dromara.sms4j.api.entity.SmsResponse; | ||||
| import org.dromara.sms4j.core.factory.SmsFactory; | ||||
| import org.dromara.web.domain.vo.CaptchaVo; | ||||
| import org.springframework.expression.Expression; | ||||
| import org.springframework.expression.ExpressionParser; | ||||
| import org.springframework.expression.spel.standard.SpelExpressionParser; | ||||
| import org.springframework.validation.annotation.Validated; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
|  | ||||
| import java.time.Duration; | ||||
| import java.util.LinkedHashMap; | ||||
|  | ||||
| /** | ||||
|  * 验证码操作处理 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @SaIgnore | ||||
| @Slf4j | ||||
| @Validated | ||||
| @RequiredArgsConstructor | ||||
| @RestController | ||||
| public class CaptchaController { | ||||
|  | ||||
|     private final CaptchaProperties captchaProperties; | ||||
|     private final MailProperties mailProperties; | ||||
|  | ||||
|     /** | ||||
|      * 短信验证码 | ||||
|      * | ||||
|      * @param phonenumber 用户手机号 | ||||
|      */ | ||||
|     @RateLimiter(key = "#phonenumber", time = 60, count = 1) | ||||
|     @GetMapping("/resource/sms/code") | ||||
|     public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { | ||||
|         String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber; | ||||
|         String code = RandomUtil.randomNumbers(4); | ||||
|         RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); | ||||
|         // 验证码模板id 自行处理 (查数据库或写死均可) | ||||
|         String templateId = ""; | ||||
|         LinkedHashMap<String, String> map = new LinkedHashMap<>(1); | ||||
|         map.put("code", code); | ||||
|         SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); | ||||
|         SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map); | ||||
|         if (!smsResponse.isSuccess()) { | ||||
|             log.error("验证码短信发送异常 => {}", smsResponse); | ||||
|             return R.fail(smsResponse.getData().toString()); | ||||
|         } | ||||
|         return R.ok(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 邮箱验证码 | ||||
|      * | ||||
|      * @param email 邮箱 | ||||
|      */ | ||||
|     @RateLimiter(key = "#email", time = 60, count = 1) | ||||
|     @GetMapping("/resource/email/code") | ||||
|     public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { | ||||
|         if (!mailProperties.getEnabled()) { | ||||
|             return R.fail("当前系统没有开启邮箱功能!"); | ||||
|         } | ||||
|         String key = GlobalConstants.CAPTCHA_CODE_KEY + email; | ||||
|         String code = RandomUtil.randomNumbers(4); | ||||
|         RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); | ||||
|         try { | ||||
|             MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); | ||||
|         } catch (Exception e) { | ||||
|             log.error("验证码短信发送异常 => {}", e.getMessage()); | ||||
|             return R.fail(e.getMessage()); | ||||
|         } | ||||
|         return R.ok(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 生成验证码 | ||||
|      */ | ||||
|     @RateLimiter(time = 60, count = 10, limitType = LimitType.IP) | ||||
|     @GetMapping("/auth/code") | ||||
|     public R<CaptchaVo> getCode() { | ||||
|         CaptchaVo captchaVo = new CaptchaVo(); | ||||
|         boolean captchaEnabled = captchaProperties.getEnable(); | ||||
|         if (!captchaEnabled) { | ||||
|             captchaVo.setCaptchaEnabled(false); | ||||
|             return R.ok(captchaVo); | ||||
|         } | ||||
|         // 保存验证码信息 | ||||
|         String uuid = IdUtil.simpleUUID(); | ||||
|         String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid; | ||||
|         // 生成验证码 | ||||
|         CaptchaType captchaType = captchaProperties.getType(); | ||||
|         boolean isMath = CaptchaType.MATH == captchaType; | ||||
|         Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength(); | ||||
|         CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length); | ||||
|         AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); | ||||
|         captcha.setGenerator(codeGenerator); | ||||
|         captcha.createCode(); | ||||
|         // 如果是数学验证码,使用SpEL表达式处理验证码结果 | ||||
|         String code = captcha.getCode(); | ||||
|         if (isMath) { | ||||
|             ExpressionParser parser = new SpelExpressionParser(); | ||||
|             Expression exp = parser.parseExpression(StringUtils.remove(code, "=")); | ||||
|             code = exp.getValue(String.class); | ||||
|         } | ||||
|         RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); | ||||
|         captchaVo.setUuid(uuid); | ||||
|         captchaVo.setImg(captcha.getImageBase64()); | ||||
|         return R.ok(captchaVo); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,32 @@ | ||||
| package org.dromara.web.controller; | ||||
|  | ||||
| import cn.dev33.satoken.annotation.SaIgnore; | ||||
| import org.dromara.common.core.config.RuoYiConfig; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
|  | ||||
| /** | ||||
|  * 首页 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @SaIgnore | ||||
| @RequiredArgsConstructor | ||||
| @RestController | ||||
| public class IndexController { | ||||
|  | ||||
|     /** | ||||
|      * 系统基础配置 | ||||
|      */ | ||||
|     private final RuoYiConfig ruoyiConfig; | ||||
|  | ||||
|     /** | ||||
|      * 访问首页,提示语 | ||||
|      */ | ||||
|     @GetMapping("/") | ||||
|     public String index() { | ||||
|         return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| package org.dromara.web.domain.vo; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| /** | ||||
|  * 验证码信息 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| @Data | ||||
| public class CaptchaVo { | ||||
|  | ||||
|     /** | ||||
|      * 是否开启验证码 | ||||
|      */ | ||||
|     private Boolean captchaEnabled = true; | ||||
|  | ||||
|     private String uuid; | ||||
|  | ||||
|     /** | ||||
|      * 验证码图片 | ||||
|      */ | ||||
|     private String img; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,25 @@ | ||||
| package org.dromara.web.domain.vo; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * 登录租户对象 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| @Data | ||||
| public class LoginTenantVo { | ||||
|  | ||||
|     /** | ||||
|      * 租户开关 | ||||
|      */ | ||||
|     private Boolean tenantEnabled; | ||||
|  | ||||
|     /** | ||||
|      * 租户对象列表 | ||||
|      */ | ||||
|     private List<TenantListVo> voList; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,54 @@ | ||||
| package org.dromara.web.domain.vo; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
| import lombok.Data; | ||||
|  | ||||
| /** | ||||
|  * 登录验证信息 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| @Data | ||||
| public class LoginVo { | ||||
|  | ||||
|     /** | ||||
|      * 授权令牌 | ||||
|      */ | ||||
|     @JsonProperty("access_token") | ||||
|     private String accessToken; | ||||
|  | ||||
|     /** | ||||
|      * 刷新令牌 | ||||
|      */ | ||||
|     @JsonProperty("refresh_token") | ||||
|     private String refreshToken; | ||||
|  | ||||
|     /** | ||||
|      * 授权令牌 access_token 的有效期 | ||||
|      */ | ||||
|     @JsonProperty("expire_in") | ||||
|     private Long expireIn; | ||||
|  | ||||
|     /** | ||||
|      * 刷新令牌 refresh_token 的有效期 | ||||
|      */ | ||||
|     @JsonProperty("refresh_expire_in") | ||||
|     private Long refreshExpireIn; | ||||
|  | ||||
|     /** | ||||
|      * 应用id | ||||
|      */ | ||||
|     @JsonProperty("client_id") | ||||
|     private String clientId; | ||||
|  | ||||
|     /** | ||||
|      * 令牌权限 | ||||
|      */ | ||||
|     private String scope; | ||||
|  | ||||
|     /** | ||||
|      * 用户 openid | ||||
|      */ | ||||
|     private String openid; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| package org.dromara.web.domain.vo; | ||||
|  | ||||
| import org.dromara.system.domain.vo.SysTenantVo; | ||||
| import io.github.linpeilie.annotations.AutoMapper; | ||||
| import lombok.Data; | ||||
|  | ||||
| /** | ||||
|  * 租户列表 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| @AutoMapper(target = SysTenantVo.class) | ||||
| public class TenantListVo { | ||||
|  | ||||
|     /** | ||||
|      * 租户编号 | ||||
|      */ | ||||
|     private String tenantId; | ||||
|  | ||||
|     /** | ||||
|      * 企业名称 | ||||
|      */ | ||||
|     private String companyName; | ||||
|  | ||||
|     /** | ||||
|      * 域名 | ||||
|      */ | ||||
|     private String domain; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,165 @@ | ||||
| package org.dromara.web.listener; | ||||
|  | ||||
| import cn.dev33.satoken.config.SaTokenConfig; | ||||
| import cn.dev33.satoken.listener.SaTokenListener; | ||||
| import cn.dev33.satoken.stp.SaLoginModel; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.convert.Convert; | ||||
| import cn.hutool.http.useragent.UserAgent; | ||||
| import cn.hutool.http.useragent.UserAgentUtil; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.dromara.common.core.constant.CacheConstants; | ||||
| import org.dromara.common.core.constant.Constants; | ||||
| import org.dromara.common.core.domain.dto.UserOnlineDTO; | ||||
| import org.dromara.common.core.utils.MessageUtils; | ||||
| import org.dromara.common.core.utils.ServletUtils; | ||||
| import org.dromara.common.core.utils.SpringUtils; | ||||
| import org.dromara.common.core.utils.ip.AddressUtils; | ||||
| import org.dromara.common.log.event.LogininforEvent; | ||||
| import org.dromara.common.redis.utils.RedisUtils; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.dromara.common.tenant.helper.TenantHelper; | ||||
| import org.dromara.web.service.SysLoginService; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | ||||
| import java.time.Duration; | ||||
|  | ||||
| /** | ||||
|  * 用户行为 侦听器的实现 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @RequiredArgsConstructor | ||||
| @Component | ||||
| @Slf4j | ||||
| public class UserActionListener implements SaTokenListener { | ||||
|  | ||||
|     private final SaTokenConfig tokenConfig; | ||||
|     private final SysLoginService loginService; | ||||
|  | ||||
|     /** | ||||
|      * 每次登录时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { | ||||
|         UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); | ||||
|         String ip = ServletUtils.getClientIP(); | ||||
|         UserOnlineDTO dto = new UserOnlineDTO(); | ||||
|         dto.setIpaddr(ip); | ||||
|         dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); | ||||
|         dto.setBrowser(userAgent.getBrowser().getName()); | ||||
|         dto.setOs(userAgent.getOs().getName()); | ||||
|         dto.setLoginTime(System.currentTimeMillis()); | ||||
|         dto.setTokenId(tokenValue); | ||||
|         String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY); | ||||
|         String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY); | ||||
|         dto.setUserName(username); | ||||
|         dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY)); | ||||
|         dto.setDeviceType(loginModel.getDevice()); | ||||
|         dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY)); | ||||
|         TenantHelper.dynamic(tenantId, () -> { | ||||
|             if(tokenConfig.getTimeout() == -1) { | ||||
|                 RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); | ||||
|             } else { | ||||
|                 RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); | ||||
|             } | ||||
|         }); | ||||
|         // 记录登录日志 | ||||
|         LogininforEvent logininforEvent = new LogininforEvent(); | ||||
|         logininforEvent.setTenantId(tenantId); | ||||
|         logininforEvent.setUsername(username); | ||||
|         logininforEvent.setStatus(Constants.LOGIN_SUCCESS); | ||||
|         logininforEvent.setMessage(MessageUtils.message("user.login.success")); | ||||
|         logininforEvent.setRequest(ServletUtils.getRequest()); | ||||
|         SpringUtils.context().publishEvent(logininforEvent); | ||||
|         // 更新登录信息 | ||||
|         loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip); | ||||
|         log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次注销时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doLogout(String loginType, Object loginId, String tokenValue) { | ||||
|         String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); | ||||
|         TenantHelper.dynamic(tenantId, () -> { | ||||
|             RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); | ||||
|         }); | ||||
|         log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次被踢下线时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doKickout(String loginType, Object loginId, String tokenValue) { | ||||
|         String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); | ||||
|         TenantHelper.dynamic(tenantId, () -> { | ||||
|             RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); | ||||
|         }); | ||||
|         log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次被顶下线时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doReplaced(String loginType, Object loginId, String tokenValue) { | ||||
|         String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); | ||||
|         TenantHelper.dynamic(tenantId, () -> { | ||||
|             RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); | ||||
|         }); | ||||
|         log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次被封禁时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次被解封时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doUntieDisable(String loginType, Object loginId, String service) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次打开二级认证时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次创建Session时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doCloseSafe(String loginType, String tokenValue, String service) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次创建Session时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doCreateSession(String id) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次注销Session时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doLogoutSession(String id) { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 每次Token续期时触发 | ||||
|      */ | ||||
|     @Override | ||||
|     public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,46 @@ | ||||
| package org.dromara.web.service; | ||||
|  | ||||
|  | ||||
| import org.dromara.common.core.exception.ServiceException; | ||||
| import org.dromara.common.core.utils.SpringUtils; | ||||
| import org.dromara.system.domain.SysClient; | ||||
| import org.dromara.system.domain.vo.SysClientVo; | ||||
| import org.dromara.web.domain.vo.LoginVo; | ||||
|  | ||||
| /** | ||||
|  * 授权策略 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| public interface IAuthStrategy { | ||||
|  | ||||
|     String BASE_NAME = "AuthStrategy"; | ||||
|  | ||||
|     /** | ||||
|      * 登录 | ||||
|      * | ||||
|      * @param body      登录对象 | ||||
|      * @param client    授权管理视图对象 | ||||
|      * @param grantType 授权类型 | ||||
|      * @return 登录验证信息 | ||||
|      */ | ||||
|     static LoginVo login(String body, SysClientVo client, String grantType) { | ||||
|         // 授权类型和客户端id | ||||
|         String beanName = grantType + BASE_NAME; | ||||
|         if (!SpringUtils.containsBean(beanName)) { | ||||
|             throw new ServiceException("授权类型不正确!"); | ||||
|         } | ||||
|         IAuthStrategy instance = SpringUtils.getBean(beanName); | ||||
|         return instance.login(body, client); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 登录 | ||||
|      * | ||||
|      * @param body   登录对象 | ||||
|      * @param client 授权管理视图对象 | ||||
|      * @return 登录验证信息 | ||||
|      */ | ||||
|     LoginVo login(String body, SysClientVo client); | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,251 @@ | ||||
| package org.dromara.web.service; | ||||
|  | ||||
| import cn.dev33.satoken.exception.NotLoginException; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.bean.BeanUtil; | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.lang.Opt; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import com.baomidou.lock.annotation.Lock4j; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import me.zhyd.oauth.model.AuthUser; | ||||
| import org.dromara.common.core.constant.CacheConstants; | ||||
| import org.dromara.common.core.constant.Constants; | ||||
| import org.dromara.common.core.constant.SystemConstants; | ||||
| import org.dromara.common.core.constant.TenantConstants; | ||||
| import org.dromara.common.core.domain.dto.PostDTO; | ||||
| import org.dromara.common.core.domain.dto.RoleDTO; | ||||
| import org.dromara.common.core.domain.model.LoginUser; | ||||
| import org.dromara.common.core.enums.LoginType; | ||||
| import org.dromara.common.core.exception.ServiceException; | ||||
| import org.dromara.common.core.exception.user.UserException; | ||||
| import org.dromara.common.core.utils.*; | ||||
| import org.dromara.common.log.event.LogininforEvent; | ||||
| import org.dromara.common.mybatis.helper.DataPermissionHelper; | ||||
| import org.dromara.common.redis.utils.RedisUtils; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.dromara.common.tenant.exception.TenantException; | ||||
| import org.dromara.common.tenant.helper.TenantHelper; | ||||
| import org.dromara.system.domain.SysUser; | ||||
| import org.dromara.system.domain.bo.SysSocialBo; | ||||
| import org.dromara.system.domain.vo.*; | ||||
| import org.dromara.system.mapper.SysUserMapper; | ||||
| import org.dromara.system.service.*; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import java.time.Duration; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.function.Supplier; | ||||
|  | ||||
| /** | ||||
|  * 登录校验方法 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @RequiredArgsConstructor | ||||
| @Slf4j | ||||
| @Service | ||||
| public class SysLoginService { | ||||
|  | ||||
|     @Value("${user.password.maxRetryCount}") | ||||
|     private Integer maxRetryCount; | ||||
|  | ||||
|     @Value("${user.password.lockTime}") | ||||
|     private Integer lockTime; | ||||
|  | ||||
|     private final ISysTenantService tenantService; | ||||
|     private final ISysPermissionService permissionService; | ||||
|     private final ISysSocialService sysSocialService; | ||||
|     private final ISysRoleService roleService; | ||||
|     private final ISysDeptService deptService; | ||||
|     private final ISysPostService postService; | ||||
|     private final SysUserMapper userMapper; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * 绑定第三方用户 | ||||
|      * | ||||
|      * @param authUserData 授权响应实体 | ||||
|      */ | ||||
|     @Lock4j | ||||
|     public void socialRegister(AuthUser authUserData) { | ||||
|         String authId = authUserData.getSource() + authUserData.getUuid(); | ||||
|         // 第三方用户信息 | ||||
|         SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class); | ||||
|         BeanUtil.copyProperties(authUserData.getToken(), bo); | ||||
|         Long userId = LoginHelper.getUserId(); | ||||
|         bo.setUserId(userId); | ||||
|         bo.setAuthId(authId); | ||||
|         bo.setOpenId(authUserData.getUuid()); | ||||
|         bo.setUserName(authUserData.getUsername()); | ||||
|         bo.setNickName(authUserData.getNickname()); | ||||
|         List<SysSocialVo> checkList = sysSocialService.selectByAuthId(authId); | ||||
|         if (CollUtil.isNotEmpty(checkList)) { | ||||
|             throw new ServiceException("此三方账号已经被绑定!"); | ||||
|         } | ||||
|         // 查询是否已经绑定用户 | ||||
|         SysSocialBo params = new SysSocialBo(); | ||||
|         params.setUserId(userId); | ||||
|         params.setSource(bo.getSource()); | ||||
|         List<SysSocialVo> list = sysSocialService.queryList(params); | ||||
|         if (CollUtil.isEmpty(list)) { | ||||
|             // 没有绑定用户, 新增用户信息 | ||||
|             sysSocialService.insertByBo(bo); | ||||
|         } else { | ||||
|             // 更新用户信息 | ||||
|             bo.setId(list.get(0).getId()); | ||||
|             sysSocialService.updateByBo(bo); | ||||
|             // 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断 | ||||
|             // throw new ServiceException("此平台账号已经被绑定!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * 退出登录 | ||||
|      */ | ||||
|     public void logout() { | ||||
|         try { | ||||
|             LoginUser loginUser = LoginHelper.getLoginUser(); | ||||
|             if (ObjectUtil.isNull(loginUser)) { | ||||
|                 return; | ||||
|             } | ||||
|             if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { | ||||
|                 // 超级管理员 登出清除动态租户 | ||||
|                 TenantHelper.clearDynamic(); | ||||
|             } | ||||
|             recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); | ||||
|         } catch (NotLoginException ignored) { | ||||
|         } finally { | ||||
|             try { | ||||
|                 StpUtil.logout(); | ||||
|             } catch (NotLoginException ignored) { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 记录登录信息 | ||||
|      * | ||||
|      * @param tenantId 租户ID | ||||
|      * @param username 用户名 | ||||
|      * @param status   状态 | ||||
|      * @param message  消息内容 | ||||
|      */ | ||||
|     public void recordLogininfor(String tenantId, String username, String status, String message) { | ||||
|         LogininforEvent logininforEvent = new LogininforEvent(); | ||||
|         logininforEvent.setTenantId(tenantId); | ||||
|         logininforEvent.setUsername(username); | ||||
|         logininforEvent.setStatus(status); | ||||
|         logininforEvent.setMessage(message); | ||||
|         logininforEvent.setRequest(ServletUtils.getRequest()); | ||||
|         SpringUtils.context().publishEvent(logininforEvent); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 构建登录用户 | ||||
|      */ | ||||
|     public LoginUser buildLoginUser(SysUserVo user) { | ||||
|         LoginUser loginUser = new LoginUser(); | ||||
|         Long userId = user.getUserId(); | ||||
|         loginUser.setTenantId(user.getTenantId()); | ||||
|         loginUser.setUserId(userId); | ||||
|         loginUser.setDeptId(user.getDeptId()); | ||||
|         loginUser.setUsername(user.getUserName()); | ||||
|         loginUser.setNickname(user.getNickName()); | ||||
|         loginUser.setUserType(user.getUserType()); | ||||
|         loginUser.setMenuPermission(permissionService.getMenuPermission(userId)); | ||||
|         loginUser.setRolePermission(permissionService.getRolePermission(userId)); | ||||
|         if (ObjectUtil.isNotNull(user.getDeptId())) { | ||||
|             Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById); | ||||
|             loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY)); | ||||
|             loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY)); | ||||
|         } | ||||
|         List<SysRoleVo> roles = roleService.selectRolesByUserId(userId); | ||||
|         List<SysPostVo> posts = postService.selectPostsByUserId(userId); | ||||
|         loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); | ||||
|         loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class)); | ||||
|         return loginUser; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 记录登录信息 | ||||
|      * | ||||
|      * @param userId 用户ID | ||||
|      */ | ||||
|     public void recordLoginInfo(Long userId, String ip) { | ||||
|         SysUser sysUser = new SysUser(); | ||||
|         sysUser.setUserId(userId); | ||||
|         sysUser.setLoginIp(ip); | ||||
|         sysUser.setLoginDate(DateUtils.getNowDate()); | ||||
|         sysUser.setUpdateBy(userId); | ||||
|         DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 登录校验 | ||||
|      */ | ||||
|     public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) { | ||||
|         String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username; | ||||
|         String loginFail = Constants.LOGIN_FAIL; | ||||
|  | ||||
|         // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip) | ||||
|         int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0); | ||||
|         // 锁定时间内登录 则踢出 | ||||
|         if (errorNumber >= maxRetryCount) { | ||||
|             recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); | ||||
|             throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); | ||||
|         } | ||||
|  | ||||
|         if (supplier.get()) { | ||||
|             // 错误次数递增 | ||||
|             errorNumber++; | ||||
|             RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); | ||||
|             // 达到规定错误次数 则锁定登录 | ||||
|             if (errorNumber >= maxRetryCount) { | ||||
|                 recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); | ||||
|                 throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); | ||||
|             } else { | ||||
|                 // 未达到规定错误次数 | ||||
|                 recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); | ||||
|                 throw new UserException(loginType.getRetryLimitCount(), errorNumber); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 登录成功 清空错误次数 | ||||
|         RedisUtils.deleteObject(errorKey); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 校验租户 | ||||
|      * | ||||
|      * @param tenantId 租户ID | ||||
|      */ | ||||
|     public void checkTenant(String tenantId) { | ||||
|         if (!TenantHelper.isEnable()) { | ||||
|             return; | ||||
|         } | ||||
|         if (StringUtils.isBlank(tenantId)) { | ||||
|             throw new TenantException("tenant.number.not.blank"); | ||||
|         } | ||||
|         if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { | ||||
|             return; | ||||
|         } | ||||
|         SysTenantVo tenant = tenantService.queryByTenantId(tenantId); | ||||
|         if (ObjectUtil.isNull(tenant)) { | ||||
|             log.info("登录租户:{} 不存在.", tenantId); | ||||
|             throw new TenantException("tenant.not.exists"); | ||||
|         } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) { | ||||
|             log.info("登录租户:{} 已被停用.", tenantId); | ||||
|             throw new TenantException("tenant.blocked"); | ||||
|         } else if (ObjectUtil.isNotNull(tenant.getExpireTime()) | ||||
|             && new Date().after(tenant.getExpireTime())) { | ||||
|             log.info("登录租户:{} 已超过有效期.", tenantId); | ||||
|             throw new TenantException("tenant.expired"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,115 @@ | ||||
| package org.dromara.web.service; | ||||
|  | ||||
| import cn.dev33.satoken.secure.BCrypt; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.dromara.common.core.constant.Constants; | ||||
| import org.dromara.common.core.constant.GlobalConstants; | ||||
| import org.dromara.common.core.domain.model.RegisterBody; | ||||
| import org.dromara.common.core.enums.UserType; | ||||
| import org.dromara.common.core.exception.user.CaptchaException; | ||||
| import org.dromara.common.core.exception.user.CaptchaExpireException; | ||||
| import org.dromara.common.core.exception.user.UserException; | ||||
| import org.dromara.common.core.utils.MessageUtils; | ||||
| import org.dromara.common.core.utils.ServletUtils; | ||||
| import org.dromara.common.core.utils.SpringUtils; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import org.dromara.common.log.event.LogininforEvent; | ||||
| import org.dromara.common.redis.utils.RedisUtils; | ||||
| import org.dromara.common.tenant.helper.TenantHelper; | ||||
| import org.dromara.common.web.config.properties.CaptchaProperties; | ||||
| import org.dromara.system.domain.SysUser; | ||||
| import org.dromara.system.domain.bo.SysUserBo; | ||||
| import org.dromara.system.mapper.SysUserMapper; | ||||
| import org.dromara.system.service.ISysUserService; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * 注册校验方法 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @RequiredArgsConstructor | ||||
| @Service | ||||
| public class SysRegisterService { | ||||
|  | ||||
|     private final ISysUserService userService; | ||||
|     private final SysUserMapper userMapper; | ||||
|     private final CaptchaProperties captchaProperties; | ||||
|  | ||||
|     /** | ||||
|      * 注册 | ||||
|      */ | ||||
|     public void register(RegisterBody registerBody) { | ||||
|         String tenantId = registerBody.getTenantId(); | ||||
|         String username = registerBody.getUsername(); | ||||
|         String password = registerBody.getPassword(); | ||||
|         // 校验用户类型是否存在 | ||||
|         String userType = UserType.getUserType(registerBody.getUserType()).getUserType(); | ||||
|  | ||||
|         boolean captchaEnabled = captchaProperties.getEnable(); | ||||
|         // 验证码开关 | ||||
|         if (captchaEnabled) { | ||||
|             validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid()); | ||||
|         } | ||||
|         SysUserBo sysUser = new SysUserBo(); | ||||
|         sysUser.setUserName(username); | ||||
|         sysUser.setNickName(username); | ||||
|         sysUser.setPassword(BCrypt.hashpw(password)); | ||||
|         sysUser.setUserType(userType); | ||||
|  | ||||
|         boolean exist = TenantHelper.dynamic(tenantId, () -> { | ||||
|             return userMapper.exists(new LambdaQueryWrapper<SysUser>() | ||||
|                 .eq(SysUser::getUserName, sysUser.getUserName())); | ||||
|         }); | ||||
|         if (exist) { | ||||
|             throw new UserException("user.register.save.error", username); | ||||
|         } | ||||
|         boolean regFlag = userService.registerUser(sysUser, tenantId); | ||||
|         if (!regFlag) { | ||||
|             throw new UserException("user.register.error"); | ||||
|         } | ||||
|         recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success")); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 校验验证码 | ||||
|      * | ||||
|      * @param username 用户名 | ||||
|      * @param code     验证码 | ||||
|      * @param uuid     唯一标识 | ||||
|      */ | ||||
|     public void validateCaptcha(String tenantId, String username, String code, String uuid) { | ||||
|         String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); | ||||
|         String captcha = RedisUtils.getCacheObject(verifyKey); | ||||
|         RedisUtils.deleteObject(verifyKey); | ||||
|         if (captcha == null) { | ||||
|             recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); | ||||
|             throw new CaptchaExpireException(); | ||||
|         } | ||||
|         if (!code.equalsIgnoreCase(captcha)) { | ||||
|             recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); | ||||
|             throw new CaptchaException(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 记录登录信息 | ||||
|      * | ||||
|      * @param tenantId 租户ID | ||||
|      * @param username 用户名 | ||||
|      * @param status   状态 | ||||
|      * @param message  消息内容 | ||||
|      * @return | ||||
|      */ | ||||
|     private void recordLogininfor(String tenantId, String username, String status, String message) { | ||||
|         LogininforEvent logininforEvent = new LogininforEvent(); | ||||
|         logininforEvent.setTenantId(tenantId); | ||||
|         logininforEvent.setUsername(username); | ||||
|         logininforEvent.setStatus(status); | ||||
|         logininforEvent.setMessage(message); | ||||
|         logininforEvent.setRequest(ServletUtils.getRequest()); | ||||
|         SpringUtils.context().publishEvent(logininforEvent); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,102 @@ | ||||
| package org.dromara.web.service.impl; | ||||
|  | ||||
| import cn.dev33.satoken.stp.SaLoginModel; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.dromara.common.core.constant.Constants; | ||||
| import org.dromara.common.core.constant.GlobalConstants; | ||||
| import org.dromara.common.core.constant.SystemConstants; | ||||
| import org.dromara.common.core.domain.model.EmailLoginBody; | ||||
| import org.dromara.common.core.domain.model.LoginUser; | ||||
| import org.dromara.common.core.enums.LoginType; | ||||
| import org.dromara.common.core.exception.user.CaptchaExpireException; | ||||
| import org.dromara.common.core.exception.user.UserException; | ||||
| import org.dromara.common.core.utils.MessageUtils; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import org.dromara.common.core.utils.ValidatorUtils; | ||||
| import org.dromara.common.json.utils.JsonUtils; | ||||
| import org.dromara.common.redis.utils.RedisUtils; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.dromara.common.tenant.helper.TenantHelper; | ||||
| import org.dromara.system.domain.SysUser; | ||||
| import org.dromara.system.domain.vo.SysClientVo; | ||||
| import org.dromara.system.domain.vo.SysUserVo; | ||||
| import org.dromara.system.mapper.SysUserMapper; | ||||
| import org.dromara.web.domain.vo.LoginVo; | ||||
| import org.dromara.web.service.IAuthStrategy; | ||||
| import org.dromara.web.service.SysLoginService; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * 邮件认证策略 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| @Slf4j | ||||
| @Service("email" + IAuthStrategy.BASE_NAME) | ||||
| @RequiredArgsConstructor | ||||
| public class EmailAuthStrategy implements IAuthStrategy { | ||||
|  | ||||
|     private final SysLoginService loginService; | ||||
|     private final SysUserMapper userMapper; | ||||
|  | ||||
|     @Override | ||||
|     public LoginVo login(String body, SysClientVo client) { | ||||
|         EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class); | ||||
|         ValidatorUtils.validate(loginBody); | ||||
|         String tenantId = loginBody.getTenantId(); | ||||
|         String email = loginBody.getEmail(); | ||||
|         String emailCode = loginBody.getEmailCode(); | ||||
|         LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { | ||||
|             SysUserVo user = loadUserByEmail(email); | ||||
|             loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); | ||||
|             // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 | ||||
|             return loginService.buildLoginUser(user); | ||||
|         }); | ||||
|         loginUser.setClientKey(client.getClientKey()); | ||||
|         loginUser.setDeviceType(client.getDeviceType()); | ||||
|         SaLoginModel model = new SaLoginModel(); | ||||
|         model.setDevice(client.getDeviceType()); | ||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||
|         model.setTimeout(client.getTimeout()); | ||||
|         model.setActiveTimeout(client.getActiveTimeout()); | ||||
|         model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); | ||||
|         // 生成token | ||||
|         LoginHelper.login(loginUser, model); | ||||
|  | ||||
|         LoginVo loginVo = new LoginVo(); | ||||
|         loginVo.setAccessToken(StpUtil.getTokenValue()); | ||||
|         loginVo.setExpireIn(StpUtil.getTokenTimeout()); | ||||
|         loginVo.setClientId(client.getClientId()); | ||||
|         return loginVo; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 校验邮箱验证码 | ||||
|      */ | ||||
|     private boolean validateEmailCode(String tenantId, String email, String emailCode) { | ||||
|         String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email); | ||||
|         if (StringUtils.isBlank(code)) { | ||||
|             loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); | ||||
|             throw new CaptchaExpireException(); | ||||
|         } | ||||
|         return code.equals(emailCode); | ||||
|     } | ||||
|  | ||||
|     private SysUserVo loadUserByEmail(String email) { | ||||
|         SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email)); | ||||
|         if (ObjectUtil.isNull(user)) { | ||||
|             log.info("登录用户:{} 不存在.", email); | ||||
|             throw new UserException("user.not.exists", email); | ||||
|         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||
|             log.info("登录用户:{} 已被停用.", email); | ||||
|             throw new UserException("user.blocked", email); | ||||
|         } | ||||
|         return user; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,123 @@ | ||||
| package org.dromara.web.service.impl; | ||||
|  | ||||
| import cn.dev33.satoken.secure.BCrypt; | ||||
| import cn.dev33.satoken.stp.SaLoginModel; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.dromara.common.core.constant.Constants; | ||||
| import org.dromara.common.core.constant.GlobalConstants; | ||||
| import org.dromara.common.core.constant.SystemConstants; | ||||
| import org.dromara.common.core.domain.model.LoginUser; | ||||
| import org.dromara.common.core.domain.model.PasswordLoginBody; | ||||
| import org.dromara.common.core.enums.LoginType; | ||||
| import org.dromara.common.core.exception.user.CaptchaException; | ||||
| import org.dromara.common.core.exception.user.CaptchaExpireException; | ||||
| import org.dromara.common.core.exception.user.UserException; | ||||
| import org.dromara.common.core.utils.MessageUtils; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import org.dromara.common.core.utils.ValidatorUtils; | ||||
| import org.dromara.common.json.utils.JsonUtils; | ||||
| import org.dromara.common.redis.utils.RedisUtils; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.dromara.common.tenant.helper.TenantHelper; | ||||
| import org.dromara.common.web.config.properties.CaptchaProperties; | ||||
| import org.dromara.system.domain.SysUser; | ||||
| import org.dromara.system.domain.vo.SysClientVo; | ||||
| import org.dromara.system.domain.vo.SysUserVo; | ||||
| import org.dromara.system.mapper.SysUserMapper; | ||||
| import org.dromara.web.domain.vo.LoginVo; | ||||
| import org.dromara.web.service.IAuthStrategy; | ||||
| import org.dromara.web.service.SysLoginService; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * 密码认证策略 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| @Slf4j | ||||
| @Service("password" + IAuthStrategy.BASE_NAME) | ||||
| @RequiredArgsConstructor | ||||
| public class PasswordAuthStrategy implements IAuthStrategy { | ||||
|  | ||||
|     private final CaptchaProperties captchaProperties; | ||||
|     private final SysLoginService loginService; | ||||
|     private final SysUserMapper userMapper; | ||||
|  | ||||
|     @Override | ||||
|     public LoginVo login(String body, SysClientVo client) { | ||||
|         PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class); | ||||
|         ValidatorUtils.validate(loginBody); | ||||
|         String tenantId = loginBody.getTenantId(); | ||||
|         String username = loginBody.getUsername(); | ||||
|         String password = loginBody.getPassword(); | ||||
|         String code = loginBody.getCode(); | ||||
|         String uuid = loginBody.getUuid(); | ||||
|  | ||||
|         boolean captchaEnabled = captchaProperties.getEnable(); | ||||
|         // 验证码开关 | ||||
|         if (captchaEnabled) { | ||||
|             validateCaptcha(tenantId, username, code, uuid); | ||||
|         } | ||||
|         LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { | ||||
|             SysUserVo user = loadUserByUsername(username); | ||||
|             loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); | ||||
|             // 此处可根据登录用户的数据不同 自行创建 loginUser | ||||
|             return loginService.buildLoginUser(user); | ||||
|         }); | ||||
|         loginUser.setClientKey(client.getClientKey()); | ||||
|         loginUser.setDeviceType(client.getDeviceType()); | ||||
|         SaLoginModel model = new SaLoginModel(); | ||||
|         model.setDevice(client.getDeviceType()); | ||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||
|         model.setTimeout(client.getTimeout()); | ||||
|         model.setActiveTimeout(client.getActiveTimeout()); | ||||
|         model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); | ||||
|         // 生成token | ||||
|         LoginHelper.login(loginUser, model); | ||||
|  | ||||
|         LoginVo loginVo = new LoginVo(); | ||||
|         loginVo.setAccessToken(StpUtil.getTokenValue()); | ||||
|         loginVo.setExpireIn(StpUtil.getTokenTimeout()); | ||||
|         loginVo.setClientId(client.getClientId()); | ||||
|         return loginVo; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 校验验证码 | ||||
|      * | ||||
|      * @param username 用户名 | ||||
|      * @param code     验证码 | ||||
|      * @param uuid     唯一标识 | ||||
|      */ | ||||
|     private void validateCaptcha(String tenantId, String username, String code, String uuid) { | ||||
|         String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); | ||||
|         String captcha = RedisUtils.getCacheObject(verifyKey); | ||||
|         RedisUtils.deleteObject(verifyKey); | ||||
|         if (captcha == null) { | ||||
|             loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); | ||||
|             throw new CaptchaExpireException(); | ||||
|         } | ||||
|         if (!code.equalsIgnoreCase(captcha)) { | ||||
|             loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); | ||||
|             throw new CaptchaException(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private SysUserVo loadUserByUsername(String username) { | ||||
|         SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username)); | ||||
|         if (ObjectUtil.isNull(user)) { | ||||
|             log.info("登录用户:{} 不存在.", username); | ||||
|             throw new UserException("user.not.exists", username); | ||||
|         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||
|             log.info("登录用户:{} 已被停用.", username); | ||||
|             throw new UserException("user.blocked", username); | ||||
|         } | ||||
|         return user; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,102 @@ | ||||
| package org.dromara.web.service.impl; | ||||
|  | ||||
| import cn.dev33.satoken.stp.SaLoginModel; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.dromara.common.core.constant.Constants; | ||||
| import org.dromara.common.core.constant.GlobalConstants; | ||||
| import org.dromara.common.core.constant.SystemConstants; | ||||
| import org.dromara.common.core.domain.model.LoginUser; | ||||
| import org.dromara.common.core.domain.model.SmsLoginBody; | ||||
| import org.dromara.common.core.enums.LoginType; | ||||
| import org.dromara.common.core.exception.user.CaptchaExpireException; | ||||
| import org.dromara.common.core.exception.user.UserException; | ||||
| import org.dromara.common.core.utils.MessageUtils; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import org.dromara.common.core.utils.ValidatorUtils; | ||||
| import org.dromara.common.json.utils.JsonUtils; | ||||
| import org.dromara.common.redis.utils.RedisUtils; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.dromara.common.tenant.helper.TenantHelper; | ||||
| import org.dromara.system.domain.SysUser; | ||||
| import org.dromara.system.domain.vo.SysClientVo; | ||||
| import org.dromara.system.domain.vo.SysUserVo; | ||||
| import org.dromara.system.mapper.SysUserMapper; | ||||
| import org.dromara.web.domain.vo.LoginVo; | ||||
| import org.dromara.web.service.IAuthStrategy; | ||||
| import org.dromara.web.service.SysLoginService; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * 短信认证策略 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| @Slf4j | ||||
| @Service("sms" + IAuthStrategy.BASE_NAME) | ||||
| @RequiredArgsConstructor | ||||
| public class SmsAuthStrategy implements IAuthStrategy { | ||||
|  | ||||
|     private final SysLoginService loginService; | ||||
|     private final SysUserMapper userMapper; | ||||
|  | ||||
|     @Override | ||||
|     public LoginVo login(String body, SysClientVo client) { | ||||
|         SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class); | ||||
|         ValidatorUtils.validate(loginBody); | ||||
|         String tenantId = loginBody.getTenantId(); | ||||
|         String phonenumber = loginBody.getPhonenumber(); | ||||
|         String smsCode = loginBody.getSmsCode(); | ||||
|         LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { | ||||
|             SysUserVo user = loadUserByPhonenumber(phonenumber); | ||||
|             loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); | ||||
|             // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 | ||||
|             return loginService.buildLoginUser(user); | ||||
|         }); | ||||
|         loginUser.setClientKey(client.getClientKey()); | ||||
|         loginUser.setDeviceType(client.getDeviceType()); | ||||
|         SaLoginModel model = new SaLoginModel(); | ||||
|         model.setDevice(client.getDeviceType()); | ||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||
|         model.setTimeout(client.getTimeout()); | ||||
|         model.setActiveTimeout(client.getActiveTimeout()); | ||||
|         model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); | ||||
|         // 生成token | ||||
|         LoginHelper.login(loginUser, model); | ||||
|  | ||||
|         LoginVo loginVo = new LoginVo(); | ||||
|         loginVo.setAccessToken(StpUtil.getTokenValue()); | ||||
|         loginVo.setExpireIn(StpUtil.getTokenTimeout()); | ||||
|         loginVo.setClientId(client.getClientId()); | ||||
|         return loginVo; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 校验短信验证码 | ||||
|      */ | ||||
|     private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { | ||||
|         String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); | ||||
|         if (StringUtils.isBlank(code)) { | ||||
|             loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); | ||||
|             throw new CaptchaExpireException(); | ||||
|         } | ||||
|         return code.equals(smsCode); | ||||
|     } | ||||
|  | ||||
|     private SysUserVo loadUserByPhonenumber(String phonenumber) { | ||||
|         SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber)); | ||||
|         if (ObjectUtil.isNull(user)) { | ||||
|             log.info("登录用户:{} 不存在.", phonenumber); | ||||
|             throw new UserException("user.not.exists", phonenumber); | ||||
|         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||
|             log.info("登录用户:{} 已被停用.", phonenumber); | ||||
|             throw new UserException("user.blocked", phonenumber); | ||||
|         } | ||||
|         return user; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,131 @@ | ||||
| package org.dromara.web.service.impl; | ||||
|  | ||||
| import cn.dev33.satoken.stp.SaLoginModel; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.map.MapUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import cn.hutool.http.HttpUtil; | ||||
| import cn.hutool.http.Method; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import me.zhyd.oauth.model.AuthResponse; | ||||
| import me.zhyd.oauth.model.AuthUser; | ||||
| import org.dromara.common.core.constant.SystemConstants; | ||||
| import org.dromara.common.core.domain.model.LoginUser; | ||||
| import org.dromara.common.core.domain.model.SocialLoginBody; | ||||
| import org.dromara.common.core.exception.ServiceException; | ||||
| import org.dromara.common.core.exception.user.UserException; | ||||
| import org.dromara.common.core.utils.StreamUtils; | ||||
| import org.dromara.common.core.utils.ValidatorUtils; | ||||
| import org.dromara.common.json.utils.JsonUtils; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.dromara.common.social.config.properties.SocialProperties; | ||||
| import org.dromara.common.social.utils.SocialUtils; | ||||
| import org.dromara.common.tenant.helper.TenantHelper; | ||||
| import org.dromara.system.domain.vo.SysClientVo; | ||||
| import org.dromara.system.domain.vo.SysSocialVo; | ||||
| import org.dromara.system.domain.vo.SysUserVo; | ||||
| import org.dromara.system.mapper.SysUserMapper; | ||||
| import org.dromara.system.service.ISysSocialService; | ||||
| import org.dromara.web.domain.vo.LoginVo; | ||||
| import org.dromara.web.service.IAuthStrategy; | ||||
| import org.dromara.web.service.SysLoginService; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Optional; | ||||
|  | ||||
| /** | ||||
|  * 第三方授权策略 | ||||
|  * | ||||
|  * @author thiszhc is 三三 | ||||
|  */ | ||||
| @Slf4j | ||||
| @Service("social" + IAuthStrategy.BASE_NAME) | ||||
| @RequiredArgsConstructor | ||||
| public class SocialAuthStrategy implements IAuthStrategy { | ||||
|  | ||||
|     private final SocialProperties socialProperties; | ||||
|     private final ISysSocialService sysSocialService; | ||||
|     private final SysUserMapper userMapper; | ||||
|     private final SysLoginService loginService; | ||||
|  | ||||
|     /** | ||||
|      * 登录-第三方授权登录 | ||||
|      * | ||||
|      * @param body     登录信息 | ||||
|      * @param client   客户端信息 | ||||
|      */ | ||||
|     @Override | ||||
|     public LoginVo login(String body, SysClientVo client) { | ||||
|         SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class); | ||||
|         ValidatorUtils.validate(loginBody); | ||||
|         AuthResponse<AuthUser> response = SocialUtils.loginAuth( | ||||
|                 loginBody.getSource(), loginBody.getSocialCode(), | ||||
|                 loginBody.getSocialState(), socialProperties); | ||||
|         if (!response.ok()) { | ||||
|             throw new ServiceException(response.getMsg()); | ||||
|         } | ||||
|         AuthUser authUserData = response.getData(); | ||||
|         if ("GITEE".equals(authUserData.getSource())) { | ||||
|             // 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖 | ||||
|             HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus") | ||||
|                     .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken())) | ||||
|                     .executeAsync(); | ||||
|             HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus") | ||||
|                     .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken())) | ||||
|                     .executeAsync(); | ||||
|         } | ||||
|  | ||||
|         List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid()); | ||||
|         if (CollUtil.isEmpty(list)) { | ||||
|             throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!"); | ||||
|         } | ||||
|         SysSocialVo social; | ||||
|         if (TenantHelper.isEnable()) { | ||||
|             Optional<SysSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId())); | ||||
|             if (opt.isEmpty()) { | ||||
|                 throw new ServiceException("对不起,你没有权限登录当前租户!"); | ||||
|             } | ||||
|             social = opt.get(); | ||||
|         } else { | ||||
|             social = list.get(0); | ||||
|         } | ||||
|         LoginUser loginUser = TenantHelper.dynamic(social.getTenantId(), () -> { | ||||
|             SysUserVo user = loadUser(social.getUserId()); | ||||
|             // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 | ||||
|             return loginService.buildLoginUser(user); | ||||
|         }); | ||||
|         loginUser.setClientKey(client.getClientKey()); | ||||
|         loginUser.setDeviceType(client.getDeviceType()); | ||||
|         SaLoginModel model = new SaLoginModel(); | ||||
|         model.setDevice(client.getDeviceType()); | ||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||
|         model.setTimeout(client.getTimeout()); | ||||
|         model.setActiveTimeout(client.getActiveTimeout()); | ||||
|         model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); | ||||
|         // 生成token | ||||
|         LoginHelper.login(loginUser, model); | ||||
|  | ||||
|         LoginVo loginVo = new LoginVo(); | ||||
|         loginVo.setAccessToken(StpUtil.getTokenValue()); | ||||
|         loginVo.setExpireIn(StpUtil.getTokenTimeout()); | ||||
|         loginVo.setClientId(client.getClientId()); | ||||
|         return loginVo; | ||||
|     } | ||||
|  | ||||
|     private SysUserVo loadUser(Long userId) { | ||||
|         SysUserVo user = userMapper.selectVoById(userId); | ||||
|         if (ObjectUtil.isNull(user)) { | ||||
|             log.info("登录用户:{} 不存在.", ""); | ||||
|             throw new UserException("user.not.exists", ""); | ||||
|         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||
|             log.info("登录用户:{} 已被停用.", ""); | ||||
|             throw new UserException("user.blocked", ""); | ||||
|         } | ||||
|         return user; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,111 @@ | ||||
| package org.dromara.web.service.impl; | ||||
|  | ||||
| import cn.dev33.satoken.stp.SaLoginModel; | ||||
| import cn.dev33.satoken.stp.StpUtil; | ||||
| import cn.hutool.core.util.ObjectUtil; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import me.zhyd.oauth.config.AuthConfig; | ||||
| import me.zhyd.oauth.model.AuthCallback; | ||||
| import me.zhyd.oauth.model.AuthResponse; | ||||
| import me.zhyd.oauth.model.AuthToken; | ||||
| import me.zhyd.oauth.model.AuthUser; | ||||
| import me.zhyd.oauth.request.AuthRequest; | ||||
| import me.zhyd.oauth.request.AuthWechatMiniProgramRequest; | ||||
| import org.dromara.common.core.constant.SystemConstants; | ||||
| import org.dromara.common.core.domain.model.XcxLoginBody; | ||||
| import org.dromara.common.core.domain.model.XcxLoginUser; | ||||
| import org.dromara.common.core.exception.ServiceException; | ||||
| import org.dromara.common.core.utils.ValidatorUtils; | ||||
| import org.dromara.common.json.utils.JsonUtils; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| import org.dromara.system.domain.vo.SysClientVo; | ||||
| import org.dromara.system.domain.vo.SysUserVo; | ||||
| import org.dromara.web.domain.vo.LoginVo; | ||||
| import org.dromara.web.service.IAuthStrategy; | ||||
| import org.dromara.web.service.SysLoginService; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * 小程序认证策略 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| @Slf4j | ||||
| @Service("xcx" + IAuthStrategy.BASE_NAME) | ||||
| @RequiredArgsConstructor | ||||
| public class XcxAuthStrategy implements IAuthStrategy { | ||||
|  | ||||
|     private final SysLoginService loginService; | ||||
|  | ||||
|     @Override | ||||
|     public LoginVo login(String body, SysClientVo client) { | ||||
|         XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class); | ||||
|         ValidatorUtils.validate(loginBody); | ||||
|         // xcxCode 为 小程序调用 wx.login 授权后获取 | ||||
|         String xcxCode = loginBody.getXcxCode(); | ||||
|         // 多个小程序识别使用 | ||||
|         String appid = loginBody.getAppid(); | ||||
|  | ||||
|         // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid | ||||
|         AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder() | ||||
|             .clientId(appid).clientSecret("自行填写密钥 可根据不同appid填入不同密钥") | ||||
|             .ignoreCheckRedirectUri(true).ignoreCheckState(true).build()); | ||||
|         AuthCallback authCallback = new AuthCallback(); | ||||
|         authCallback.setCode(xcxCode); | ||||
|         AuthResponse<AuthUser> resp = authRequest.login(authCallback); | ||||
|         String openid, unionId; | ||||
|         if (resp.ok()) { | ||||
|             AuthToken token = resp.getData().getToken(); | ||||
|             openid = token.getOpenId(); | ||||
|             // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。 | ||||
|             unionId = token.getUnionId(); | ||||
|         } else { | ||||
|             throw new ServiceException(resp.getMsg()); | ||||
|         } | ||||
|         // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 | ||||
|         SysUserVo user = loadUserByOpenid(openid); | ||||
|         // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 | ||||
|         XcxLoginUser loginUser = new XcxLoginUser(); | ||||
|         loginUser.setTenantId(user.getTenantId()); | ||||
|         loginUser.setUserId(user.getUserId()); | ||||
|         loginUser.setUsername(user.getUserName()); | ||||
|         loginUser.setNickname(user.getNickName()); | ||||
|         loginUser.setUserType(user.getUserType()); | ||||
|         loginUser.setClientKey(client.getClientKey()); | ||||
|         loginUser.setDeviceType(client.getDeviceType()); | ||||
|         loginUser.setOpenid(openid); | ||||
|  | ||||
|         SaLoginModel model = new SaLoginModel(); | ||||
|         model.setDevice(client.getDeviceType()); | ||||
|         // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 | ||||
|         // 例如: 后台用户30分钟过期 app用户1天过期 | ||||
|         model.setTimeout(client.getTimeout()); | ||||
|         model.setActiveTimeout(client.getActiveTimeout()); | ||||
|         model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); | ||||
|         // 生成token | ||||
|         LoginHelper.login(loginUser, model); | ||||
|  | ||||
|         LoginVo loginVo = new LoginVo(); | ||||
|         loginVo.setAccessToken(StpUtil.getTokenValue()); | ||||
|         loginVo.setExpireIn(StpUtil.getTokenTimeout()); | ||||
|         loginVo.setClientId(client.getClientId()); | ||||
|         loginVo.setOpenid(openid); | ||||
|         return loginVo; | ||||
|     } | ||||
|  | ||||
|     private SysUserVo loadUserByOpenid(String openid) { | ||||
|         // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 | ||||
|         // todo 自行实现 userService.selectUserByOpenid(openid); | ||||
|         SysUserVo user = new SysUserVo(); | ||||
|         if (ObjectUtil.isNull(user)) { | ||||
|             log.info("登录用户:{} 不存在.", openid); | ||||
|             // todo 用户不存在 业务逻辑自行实现 | ||||
|         } else if (SystemConstants.DISABLE.equals(user.getStatus())) { | ||||
|             log.info("登录用户:{} 已被停用.", openid); | ||||
|             // todo 用户已被停用 业务逻辑自行实现 | ||||
|         } | ||||
|         return user; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,265 @@ | ||||
| --- # 监控中心配置 | ||||
| spring.boot.admin.client: | ||||
|   # 增加客户端开关 | ||||
|   enabled: false | ||||
|   url: http://localhost:9090/admin | ||||
|   instance: | ||||
|     service-host-type: IP | ||||
|     metadata: | ||||
|       username: ${spring.boot.admin.client.username} | ||||
|       userpassword: ${spring.boot.admin.client.password} | ||||
|   username: @monitor.username@ | ||||
|   password: @monitor.password@ | ||||
|  | ||||
| --- # snail-job 配置 | ||||
| snail-job: | ||||
|   enabled: false | ||||
|   # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 | ||||
|   group: "ruoyi_group" | ||||
|   # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表 | ||||
|   token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" | ||||
|   server: | ||||
|     host: 127.0.0.1 | ||||
|     port: 17888 | ||||
|   # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段 | ||||
|   namespace: ${spring.profiles.active} | ||||
|   # 随主应用端口漂移 | ||||
|   port: 2${server.port} | ||||
|   # 客户端ip指定 | ||||
|   host: | ||||
|   # RPC类型: netty, grpc | ||||
|   rpc-type: grpc | ||||
|  | ||||
| --- # 数据源配置 | ||||
| spring: | ||||
|   datasource: | ||||
|     type: com.zaxxer.hikari.HikariDataSource | ||||
|     # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content | ||||
|     dynamic: | ||||
|       # 性能分析插件(有性能损耗 不建议生产环境使用) | ||||
|       p6spy: true | ||||
|       # 设置默认的数据源或者数据源组,默认值即为 master | ||||
|       primary: master | ||||
|       # 严格模式 匹配不到数据源则报错 | ||||
|       strict: true | ||||
|       datasource: | ||||
|         # 主库数据源 | ||||
|         master: | ||||
|           type: ${spring.datasource.type} | ||||
|           driverClassName: com.mysql.cj.jdbc.Driver | ||||
|           # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 | ||||
|           # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) | ||||
|           url: jdbc:mysql://192.168.110.199:3306/energy?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 | ||||
|           username: energy | ||||
|           password: fikwNsk8BidXSKe4 | ||||
| #        # 从库数据源 | ||||
| #        slave: | ||||
| #          lazy: true | ||||
| #          type: ${spring.datasource.type} | ||||
| #          driverClassName: com.mysql.cj.jdbc.Driver | ||||
| #          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | ||||
| #          username: | ||||
| #          password: | ||||
| #        oracle: | ||||
| #          type: ${spring.datasource.type} | ||||
| #          driverClassName: oracle.jdbc.OracleDriver | ||||
| #          url: jdbc:oracle:thin:@//localhost:1521/XE | ||||
| #          username: ROOT | ||||
| #          password: root | ||||
| #        postgres: | ||||
| #          type: ${spring.datasource.type} | ||||
| #          driverClassName: org.postgresql.Driver | ||||
| #          url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true | ||||
| #          username: root | ||||
| #          password: root | ||||
| #        sqlserver: | ||||
| #          type: ${spring.datasource.type} | ||||
| #          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver | ||||
| #          url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true | ||||
| #          username: SA | ||||
| #          password: root | ||||
|       hikari: | ||||
|         # 最大连接池数量 | ||||
|         maxPoolSize: 20 | ||||
|         # 最小空闲线程数量 | ||||
|         minIdle: 10 | ||||
|         # 配置获取连接等待超时的时间 | ||||
|         connectionTimeout: 30000 | ||||
|         # 校验超时时间 | ||||
|         validationTimeout: 5000 | ||||
|         # 空闲连接存活最大时间,默认10分钟 | ||||
|         idleTimeout: 600000 | ||||
|         # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 | ||||
|         maxLifetime: 1800000 | ||||
|         # 多久检查一次连接的活性 | ||||
|         keepaliveTime: 30000 | ||||
|  | ||||
| --- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) | ||||
| spring.data: | ||||
|   redis: | ||||
|     # 地址 | ||||
|     host: 192.168.110.199 | ||||
|     # 端口,默认为6379 | ||||
|     port: 6379 | ||||
|     # 数据库索引 | ||||
|     database: 0 | ||||
|     # redis 密码必须配置 | ||||
|     password: 123456 | ||||
|     # 连接超时时间 | ||||
|     timeout: 10s | ||||
|     # 是否开启ssl | ||||
|     ssl.enabled: false | ||||
|  | ||||
| # redisson 配置 | ||||
| redisson: | ||||
|   # redis key前缀 | ||||
|   keyPrefix: | ||||
|   # 线程池数量 | ||||
|   threads: 4 | ||||
|   # Netty线程池数量 | ||||
|   nettyThreads: 8 | ||||
|   # 单节点配置 | ||||
|   singleServerConfig: | ||||
|     # 客户端名称 | ||||
|     clientName: ${ruoyi.name} | ||||
|     # 最小空闲连接数 | ||||
|     connectionMinimumIdleSize: 8 | ||||
|     # 连接池大小 | ||||
|     connectionPoolSize: 32 | ||||
|     # 连接空闲超时,单位:毫秒 | ||||
|     idleConnectionTimeout: 10000 | ||||
|     # 命令等待超时,单位:毫秒 | ||||
|     timeout: 3000 | ||||
|     # 发布和订阅连接池大小 | ||||
|     subscriptionConnectionPoolSize: 50 | ||||
|  | ||||
| --- # mail 邮件发送 | ||||
| mail: | ||||
|   enabled: false | ||||
|   host: smtp.163.com | ||||
|   port: 465 | ||||
|   # 是否需要用户名密码验证 | ||||
|   auth: true | ||||
|   # 发送方,遵循RFC-822标准 | ||||
|   from: xxx@163.com | ||||
|   # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) | ||||
|   user: xxx@163.com | ||||
|   # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) | ||||
|   pass: xxxxxxxxxx | ||||
|   # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 | ||||
|   starttlsEnable: true | ||||
|   # 使用SSL安全连接 | ||||
|   sslEnable: true | ||||
|   # SMTP超时时长,单位毫秒,缺省值不超时 | ||||
|   timeout: 0 | ||||
|   # Socket连接超时值,单位毫秒,缺省值不超时 | ||||
|   connectionTimeout: 0 | ||||
|  | ||||
| --- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 | ||||
| # https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 | ||||
| sms: | ||||
|   # 配置源类型用于标定配置来源(interface,yaml) | ||||
|   config-type: yaml | ||||
|   # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 | ||||
|   restricted: true | ||||
|   # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 | ||||
|   minute-max: 1 | ||||
|   # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 | ||||
|   account-max: 30 | ||||
|   # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中 | ||||
|   blends: | ||||
|     # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 | ||||
|     # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 | ||||
|     config1: | ||||
|       # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 | ||||
|       supplier: alibaba | ||||
|       # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 | ||||
|       access-key-id: 您的accessKey | ||||
|       # 称为accessSecret有些称之为apiSecret | ||||
|       access-key-secret: 您的accessKeySecret | ||||
|       signature: 您的短信签名 | ||||
|       sdk-app-id: 您的sdkAppId | ||||
|     config2: | ||||
|       # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 | ||||
|       supplier: tencent | ||||
|       access-key-id: 您的accessKey | ||||
|       access-key-secret: 您的accessKeySecret | ||||
|       signature: 您的短信签名 | ||||
|       sdk-app-id: 您的sdkAppId | ||||
|  | ||||
|  | ||||
| --- # 三方授权 | ||||
| justauth: | ||||
|   # 前端外网访问地址 | ||||
|   address: http://localhost:80 | ||||
|   type: | ||||
|     maxkey: | ||||
|       # maxkey 服务器地址 | ||||
|       # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据 | ||||
|       server-url: http://sso.maxkey.top | ||||
|       client-id: 876892492581044224 | ||||
|       client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=maxkey | ||||
|     topiam: | ||||
|       # topiam 服务器地址 | ||||
|       server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol | ||||
|       client-id: 449c4*********937************759 | ||||
|       client-secret: ac7***********1e0************28d | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=topiam | ||||
|       scopes: [openid, email, phone, profile] | ||||
|     qq: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=qq | ||||
|       union-id: false | ||||
|     weibo: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=weibo | ||||
|     gitee: | ||||
|       client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98 | ||||
|       client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=gitee | ||||
|     dingtalk: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=dingtalk | ||||
|     baidu: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=baidu | ||||
|     csdn: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=csdn | ||||
|     coding: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=coding | ||||
|       coding-group-name: xx | ||||
|     oschina: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=oschina | ||||
|     alipay_wallet: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet | ||||
|       alipay-public-key: MIIB**************DAQAB | ||||
|     wechat_open: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=wechat_open | ||||
|     wechat_mp: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=wechat_mp | ||||
|     wechat_enterprise: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise | ||||
|       agent-id: 1000002 | ||||
|     gitlab: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=gitlab | ||||
| @ -0,0 +1,267 @@ | ||||
| --- # 临时文件存储位置 避免临时文件被系统清理报错 | ||||
| spring.servlet.multipart.location: /ruoyi/server/temp | ||||
|  | ||||
| --- # 监控中心配置 | ||||
| spring.boot.admin.client: | ||||
|   # 增加客户端开关 | ||||
|   enabled: true | ||||
|   url: http://localhost:9090/admin | ||||
|   instance: | ||||
|     service-host-type: IP | ||||
|     metadata: | ||||
|       username: ${spring.boot.admin.client.username} | ||||
|       userpassword: ${spring.boot.admin.client.password} | ||||
|   username: @monitor.username@ | ||||
|   password: @monitor.password@ | ||||
|  | ||||
| --- # snail-job 配置 | ||||
| snail-job: | ||||
|   enabled: true | ||||
|   # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 | ||||
|   group: "ruoyi_group" | ||||
|   # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表 | ||||
|   token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" | ||||
|   server: | ||||
|     host: 127.0.0.1 | ||||
|     port: 17888 | ||||
|   # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段 | ||||
|   namespace: ${spring.profiles.active} | ||||
|   # 随主应用端口漂移 | ||||
|   port: 2${server.port} | ||||
|   # 客户端ip指定 | ||||
|   host: | ||||
|   # RPC类型: netty, grpc | ||||
|   rpc-type: grpc | ||||
|  | ||||
| --- # 数据源配置 | ||||
| spring: | ||||
|   datasource: | ||||
|     type: com.zaxxer.hikari.HikariDataSource | ||||
|     # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content | ||||
|     dynamic: | ||||
|       # 性能分析插件(有性能损耗 不建议生产环境使用) | ||||
|       p6spy: false | ||||
|       # 设置默认的数据源或者数据源组,默认值即为 master | ||||
|       primary: master | ||||
|       # 严格模式 匹配不到数据源则报错 | ||||
|       strict: true | ||||
|       datasource: | ||||
|         # 主库数据源 | ||||
|         master: | ||||
|           type: ${spring.datasource.type} | ||||
|           driverClassName: com.mysql.cj.jdbc.Driver | ||||
|           # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 | ||||
|           # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) | ||||
|           url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | ||||
|           username: root | ||||
|           password: root | ||||
| #        # 从库数据源 | ||||
| #        slave: | ||||
| #          lazy: true | ||||
| #          type: ${spring.datasource.type} | ||||
| #          driverClassName: com.mysql.cj.jdbc.Driver | ||||
| #          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | ||||
| #          username: | ||||
| #          password: | ||||
| #        oracle: | ||||
| #          type: ${spring.datasource.type} | ||||
| #          driverClassName: oracle.jdbc.OracleDriver | ||||
| #          url: jdbc:oracle:thin:@//localhost:1521/XE | ||||
| #          username: ROOT | ||||
| #          password: root | ||||
| #        postgres: | ||||
| #          type: ${spring.datasource.type} | ||||
| #          driverClassName: org.postgresql.Driver | ||||
| #          url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true | ||||
| #          username: root | ||||
| #          password: root | ||||
| #        sqlserver: | ||||
| #          type: ${spring.datasource.type} | ||||
| #          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver | ||||
| #          url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true | ||||
| #          username: SA | ||||
| #          password: root | ||||
|       hikari: | ||||
|         # 最大连接池数量 | ||||
|         maxPoolSize: 20 | ||||
|         # 最小空闲线程数量 | ||||
|         minIdle: 10 | ||||
|         # 配置获取连接等待超时的时间 | ||||
|         connectionTimeout: 30000 | ||||
|         # 校验超时时间 | ||||
|         validationTimeout: 5000 | ||||
|         # 空闲连接存活最大时间,默认10分钟 | ||||
|         idleTimeout: 600000 | ||||
|         # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟 | ||||
|         maxLifetime: 1800000 | ||||
|         # 多久检查一次连接的活性 | ||||
|         keepaliveTime: 30000 | ||||
|  | ||||
| --- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) | ||||
| spring.data: | ||||
|   redis: | ||||
|     # 地址 | ||||
|     host: localhost | ||||
|     # 端口,默认为6379 | ||||
|     port: 6379 | ||||
|     # 数据库索引 | ||||
|     database: 0 | ||||
|     # redis 密码必须配置 | ||||
|     password: ruoyi123 | ||||
|     # 连接超时时间 | ||||
|     timeout: 10s | ||||
|     # 是否开启ssl | ||||
|     ssl.enabled: false | ||||
|  | ||||
| # redisson 配置 | ||||
| redisson: | ||||
|   # redis key前缀 | ||||
|   keyPrefix: | ||||
|   # 线程池数量 | ||||
|   threads: 16 | ||||
|   # Netty线程池数量 | ||||
|   nettyThreads: 32 | ||||
|   # 单节点配置 | ||||
|   singleServerConfig: | ||||
|     # 客户端名称 | ||||
|     clientName: ${ruoyi.name} | ||||
|     # 最小空闲连接数 | ||||
|     connectionMinimumIdleSize: 32 | ||||
|     # 连接池大小 | ||||
|     connectionPoolSize: 64 | ||||
|     # 连接空闲超时,单位:毫秒 | ||||
|     idleConnectionTimeout: 10000 | ||||
|     # 命令等待超时,单位:毫秒 | ||||
|     timeout: 3000 | ||||
|     # 发布和订阅连接池大小 | ||||
|     subscriptionConnectionPoolSize: 50 | ||||
|  | ||||
| --- # mail 邮件发送 | ||||
| mail: | ||||
|   enabled: false | ||||
|   host: smtp.163.com | ||||
|   port: 465 | ||||
|   # 是否需要用户名密码验证 | ||||
|   auth: true | ||||
|   # 发送方,遵循RFC-822标准 | ||||
|   from: xxx@163.com | ||||
|   # 用户名(注意:如果使用foxmail邮箱,此处user为qq号) | ||||
|   user: xxx@163.com | ||||
|   # 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助) | ||||
|   pass: xxxxxxxxxx | ||||
|   # 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。 | ||||
|   starttlsEnable: true | ||||
|   # 使用SSL安全连接 | ||||
|   sslEnable: true | ||||
|   # SMTP超时时长,单位毫秒,缺省值不超时 | ||||
|   timeout: 0 | ||||
|   # Socket连接超时值,单位毫秒,缺省值不超时 | ||||
|   connectionTimeout: 0 | ||||
|  | ||||
| --- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 | ||||
| # https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 | ||||
| sms: | ||||
|   # 配置源类型用于标定配置来源(interface,yaml) | ||||
|   config-type: yaml | ||||
|   # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制 | ||||
|   restricted: true | ||||
|   # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 | ||||
|   minute-max: 1 | ||||
|   # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 | ||||
|   account-max: 30 | ||||
|   # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中 | ||||
|   blends: | ||||
|     # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 | ||||
|     # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 | ||||
|     config1: | ||||
|       # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 | ||||
|       supplier: alibaba | ||||
|       # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。 | ||||
|       access-key-id: 您的accessKey | ||||
|       # 称为accessSecret有些称之为apiSecret | ||||
|       access-key-secret: 您的accessKeySecret | ||||
|       signature: 您的短信签名 | ||||
|       sdk-app-id: 您的sdkAppId | ||||
|     config2: | ||||
|       # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 | ||||
|       supplier: tencent | ||||
|       access-key-id: 您的accessKey | ||||
|       access-key-secret: 您的accessKeySecret | ||||
|       signature: 您的短信签名 | ||||
|       sdk-app-id: 您的sdkAppId | ||||
|  | ||||
| --- # 三方授权 | ||||
| justauth: | ||||
|   # 前端外网访问地址 | ||||
|   address: http://localhost:80 | ||||
|   type: | ||||
|     maxkey: | ||||
|       # maxkey 服务器地址 | ||||
|       # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据 | ||||
|       server-url: http://sso.maxkey.top | ||||
|       client-id: 876892492581044224 | ||||
|       client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=maxkey | ||||
|     topiam: | ||||
|       # topiam 服务器地址 | ||||
|       server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol | ||||
|       client-id: 449c4*********937************759 | ||||
|       client-secret: ac7***********1e0************28d | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=topiam | ||||
|       scopes: [ openid, email, phone, profile ] | ||||
|     qq: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=qq | ||||
|       union-id: false | ||||
|     weibo: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=weibo | ||||
|     gitee: | ||||
|       client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98 | ||||
|       client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=gitee | ||||
|     dingtalk: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=dingtalk | ||||
|     baidu: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=baidu | ||||
|     csdn: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=csdn | ||||
|     coding: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=coding | ||||
|       coding-group-name: xx | ||||
|     oschina: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=oschina | ||||
|     alipay_wallet: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet | ||||
|       alipay-public-key: MIIB**************DAQAB | ||||
|     wechat_open: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=wechat_open | ||||
|     wechat_mp: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=wechat_mp | ||||
|     wechat_enterprise: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise | ||||
|       agent-id: 1000002 | ||||
|     gitlab: | ||||
|       client-id: 10**********6 | ||||
|       client-secret: 1f7d08**********5b7**********29e | ||||
|       redirect-uri: ${justauth.address}/social-callback?source=gitlab | ||||
							
								
								
									
										283
									
								
								RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/application.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,283 @@ | ||||
| # 项目相关配置 | ||||
| ruoyi: | ||||
|   # 名称 | ||||
|   name: RuoYi-Vue-Plus | ||||
|   # 版本 | ||||
|   version: ${revision} | ||||
|   # 版权年份 | ||||
|   copyrightYear: 2024 | ||||
|  | ||||
| captcha: | ||||
|   enable: true | ||||
|   # 页面 <参数设置> 可开启关闭 验证码校验 | ||||
|   # 验证码类型 math 数组计算 char 字符验证 | ||||
|   type: MATH | ||||
|   # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 | ||||
|   category: CIRCLE | ||||
|   # 数字验证码位数 | ||||
|   numberLength: 1 | ||||
|   # 字符验证码长度 | ||||
|   charLength: 4 | ||||
|  | ||||
| # 开发环境配置 | ||||
| server: | ||||
|   # 服务器的HTTP端口,默认为8080 | ||||
|   port: 8899 | ||||
|   servlet: | ||||
|     # 应用的访问路径 | ||||
|     context-path: / | ||||
|   # undertow 配置 | ||||
|   undertow: | ||||
|     # HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的 | ||||
|     max-http-post-size: -1 | ||||
|     # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 | ||||
|     # 每块buffer的空间大小,越小的空间被利用越充分 | ||||
|     buffer-size: 512 | ||||
|     # 是否分配的直接内存 | ||||
|     direct-buffers: true | ||||
|     threads: | ||||
|       # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 | ||||
|       io: 8 | ||||
|       # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 | ||||
|       worker: 256 | ||||
|  | ||||
| # 日志配置 | ||||
| logging: | ||||
|   level: | ||||
|     org.dromara: @logging.level@ | ||||
|     org.springframework: warn | ||||
|     org.mybatis.spring.mapper: error | ||||
|     org.apache.fury: warn | ||||
|   config: classpath:logback-plus.xml | ||||
|  | ||||
| # 用户配置 | ||||
| user: | ||||
|   password: | ||||
|     # 密码最大错误次数 | ||||
|     maxRetryCount: 5 | ||||
|     # 密码锁定时间(默认10分钟) | ||||
|     lockTime: 10 | ||||
|  | ||||
| # Spring配置 | ||||
| spring: | ||||
|   application: | ||||
|     name: ${ruoyi.name} | ||||
|   threads: | ||||
|     # 开启虚拟线程 仅jdk21可用 | ||||
|     virtual: | ||||
|       enabled: false | ||||
|   # 资源信息 | ||||
|   messages: | ||||
|     # 国际化资源文件路径 | ||||
|     basename: i18n/messages | ||||
|   profiles: | ||||
|     active: @profiles.active@ | ||||
|   # 文件上传 | ||||
|   servlet: | ||||
|     multipart: | ||||
|       # 单个文件大小 | ||||
|       max-file-size: 10MB | ||||
|       # 设置总上传的文件大小 | ||||
|       max-request-size: 20MB | ||||
|   mvc: | ||||
|     # 设置静态资源路径 防止所有请求都去查静态资源 | ||||
|     static-path-pattern: /static/** | ||||
|     format: | ||||
|       date-time: yyyy-MM-dd HH:mm:ss | ||||
|   jackson: | ||||
|     # 日期格式化 | ||||
|     date-format: yyyy-MM-dd HH:mm:ss | ||||
|     serialization: | ||||
|       # 格式化输出 | ||||
|       indent_output: false | ||||
|       # 忽略无法转换的对象 | ||||
|       fail_on_empty_beans: false | ||||
|     deserialization: | ||||
|       # 允许对象忽略json中不存在的属性 | ||||
|       fail_on_unknown_properties: false | ||||
|  | ||||
| # Sa-Token配置 | ||||
| sa-token: | ||||
|   # token名称 (同时也是cookie名称) | ||||
|   token-name: Authorization | ||||
|   # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) | ||||
|   is-concurrent: true | ||||
|   # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) | ||||
|   is-share: false | ||||
|   # jwt秘钥 | ||||
|   jwt-secret-key: abcdefghijklmnopqrstuvwxyz | ||||
|  | ||||
| # security配置 | ||||
| security: | ||||
|   # 排除路径 | ||||
|   excludes: | ||||
|     - /*.html | ||||
|     - /**/*.html | ||||
|     - /**/*.css | ||||
|     - /**/*.js | ||||
|     - /favicon.ico | ||||
|     - /error | ||||
|     - /*/api-docs | ||||
|     - /*/api-docs/** | ||||
|     - /warm-flow-ui/token-name | ||||
|  | ||||
| # 多租户配置 | ||||
| tenant: | ||||
|   # 是否开启 | ||||
|   enable: false | ||||
|   # 排除表 | ||||
|   excludes: | ||||
|     - sys_menu | ||||
|     - sys_tenant | ||||
|     - sys_tenant_package | ||||
|     - sys_role_dept | ||||
|     - sys_role_menu | ||||
|     - sys_user_post | ||||
|     - sys_user_role | ||||
|     - sys_client | ||||
|     - sys_oss_config | ||||
|  | ||||
| # MyBatisPlus配置 | ||||
| # https://baomidou.com/config/ | ||||
| mybatis-plus: | ||||
|   # 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效 | ||||
|   enableLogicDelete: true | ||||
|   # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper | ||||
|   mapperPackage: org.dromara.**.mapper | ||||
|   # 对应的 XML 文件位置 | ||||
|   mapperLocations: classpath*:mapper/**/*Mapper.xml | ||||
|   # 实体扫描,多个package用逗号或者分号分隔 | ||||
|   typeAliasesPackage: org.dromara.**.domain | ||||
|   global-config: | ||||
|     dbConfig: | ||||
|       # 主键类型 | ||||
|       # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID | ||||
|       # 如需改为自增 需要将数据库表全部设置为自增 | ||||
|       idType: ASSIGN_ID | ||||
|  | ||||
| # 数据加密 | ||||
| mybatis-encryptor: | ||||
|   # 是否开启加密 | ||||
|   enable: false | ||||
|   # 默认加密算法 | ||||
|   algorithm: BASE64 | ||||
|   # 编码方式 BASE64/HEX。默认BASE64 | ||||
|   encode: BASE64 | ||||
|   # 安全秘钥 对称算法的秘钥 如:AES,SM4 | ||||
|   password: | ||||
|   # 公私钥 非对称算法的公私钥 如:SM2,RSA | ||||
|   publicKey: | ||||
|   privateKey: | ||||
|  | ||||
| # api接口加密 | ||||
| api-decrypt: | ||||
|   # 是否开启全局接口加密 | ||||
|   enabled: true | ||||
|   # AES 加密头标识 | ||||
|   headerFlag: encrypt-key | ||||
|   # 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换 | ||||
|   # 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE= | ||||
|   publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ== | ||||
|   # 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换 | ||||
|   # 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== | ||||
|   privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y= | ||||
|  | ||||
| springdoc: | ||||
|   api-docs: | ||||
|     # 是否开启接口文档 | ||||
|     enabled: true | ||||
| #  swagger-ui: | ||||
| #    # 持久化认证数据 | ||||
| #    persistAuthorization: true | ||||
|   info: | ||||
|     # 标题 | ||||
|     title: '标题:${ruoyi.name}多租户管理系统_接口文档' | ||||
|     # 描述 | ||||
|     description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...' | ||||
|     # 版本 | ||||
|     version: '版本号: ${ruoyi.version}' | ||||
|     # 作者信息 | ||||
|     contact: | ||||
|       name: Lion Li | ||||
|       email: crazylionli@163.com | ||||
|       url: https://gitee.com/dromara/RuoYi-Vue-Plus | ||||
|   components: | ||||
|     # 鉴权方式配置 | ||||
|     security-schemes: | ||||
|       apiKey: | ||||
|         type: APIKEY | ||||
|         in: HEADER | ||||
|         name: ${sa-token.token-name} | ||||
|   #这里定义了两个分组,可定义多个,也可以不定义 | ||||
|   group-configs: | ||||
|     - group: 1.演示模块 | ||||
|       packages-to-scan: org.dromara.demo | ||||
|     - group: 2.通用模块 | ||||
|       packages-to-scan: org.dromara.web | ||||
|     - group: 3.系统模块 | ||||
|       packages-to-scan: org.dromara.system | ||||
|     - group: 4.代码生成模块 | ||||
|       packages-to-scan: org.dromara.generator | ||||
|     - group: 5.工作流模块 | ||||
|       packages-to-scan: org.dromara.workflow | ||||
|  | ||||
| # 防止XSS攻击 | ||||
| xss: | ||||
|   # 过滤开关 | ||||
|   enabled: true | ||||
|   # 排除链接(多个用逗号分隔) | ||||
|   excludeUrls: | ||||
|     - /system/notice | ||||
|     - /warm-flow/save-xml | ||||
|  | ||||
| # 全局线程池相关配置 | ||||
| # 如使用JDK21请直接使用虚拟线程 不要开启此配置 | ||||
| thread-pool: | ||||
|   # 是否开启线程池 | ||||
|   enabled: false | ||||
|   # 队列最大长度 | ||||
|   queueCapacity: 128 | ||||
|   # 线程池维护线程所允许的空闲时间 | ||||
|   keepAliveSeconds: 300 | ||||
|  | ||||
| --- # 分布式锁 lock4j 全局配置 | ||||
| lock4j: | ||||
|   # 获取分布式锁超时时间,默认为 3000 毫秒 | ||||
|   acquire-timeout: 3000 | ||||
|   # 分布式锁的超时时间,默认为 30 秒 | ||||
|   expire: 30000 | ||||
|  | ||||
| --- # Actuator 监控端点的配置项 | ||||
| management: | ||||
|   endpoints: | ||||
|     web: | ||||
|       exposure: | ||||
|         include: '*' | ||||
|   endpoint: | ||||
|     health: | ||||
|       show-details: ALWAYS | ||||
|     logfile: | ||||
|       external-file: ./logs/sys-console.log | ||||
|  | ||||
| --- # 默认/推荐使用sse推送 | ||||
| sse: | ||||
|   enabled: false | ||||
|   path: /resource/sse | ||||
|  | ||||
| --- # websocket | ||||
| websocket: | ||||
|   # 如果关闭 需要和前端开关一起关闭 | ||||
|   enabled: false | ||||
|   # 路径 | ||||
|   path: /resource/websocket | ||||
|   # 设置访问源地址 | ||||
|   allowedOrigins: '*' | ||||
|  | ||||
| --- # warm-flow工作流配置 | ||||
| warm-flow: | ||||
|   # 是否开启工作流,默认true | ||||
|   enabled: false | ||||
|   # 是否开启设计器ui | ||||
|   ui: true | ||||
|   # 默认Authorization,如果有多个token,用逗号分隔 | ||||
|   token-name: ${sa-token.token-name},clientid | ||||
							
								
								
									
										8
									
								
								RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/banner.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/banner.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| Application Version: ${revision} | ||||
| Spring Boot Version: ${spring-boot.version} | ||||
| __________            _____.___.__         ____   ____                     __________.__ | ||||
| \______   \__ __  ____\__  |   |__|        \   \ /   /_ __   ____          \______   \  |  __ __  ______ | ||||
|  |       _/  |  \/  _ \/   |   |  |  ______ \   Y   /  |  \_/ __ \   ______ |     ___/  | |  |  \/  ___/ | ||||
|  |    |   \  |  (  <_> )____   |  | /_____/  \     /|  |  /\  ___/  /_____/ |    |   |  |_|  |  /\___ \ | ||||
|  |____|_  /____/ \____// ______|__|           \___/ |____/  \___  >         |____|   |____/____//____  > | ||||
|         \/             \/                                       \/                                   \/ | ||||
| @ -0,0 +1,61 @@ | ||||
| #错误消息 | ||||
| not.null=* 必须填写 | ||||
| user.jcaptcha.error=验证码错误 | ||||
| user.jcaptcha.expire=验证码已失效 | ||||
| user.not.exists=对不起, 您的账号:{0} 不存在. | ||||
| user.password.not.match=用户不存在/密码错误 | ||||
| user.password.retry.limit.count=密码输入错误{0}次 | ||||
| user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 | ||||
| user.password.delete=对不起,您的账号:{0} 已被删除 | ||||
| user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 | ||||
| role.blocked=角色已封禁,请联系管理员 | ||||
| user.logout.success=退出成功 | ||||
| length.not.valid=长度必须在{min}到{max}个字符之间 | ||||
| user.username.not.blank=用户名不能为空 | ||||
| user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 | ||||
| user.username.length.valid=账户长度必须在{min}到{max}个字符之间 | ||||
| user.password.not.blank=用户密码不能为空 | ||||
| user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 | ||||
| user.password.not.valid=* 5-50个字符 | ||||
| user.email.not.valid=邮箱格式错误 | ||||
| user.email.not.blank=邮箱不能为空 | ||||
| user.phonenumber.not.blank=用户手机号不能为空 | ||||
| user.mobile.phone.number.not.valid=手机号格式错误 | ||||
| user.login.success=登录成功 | ||||
| user.register.success=注册成功 | ||||
| user.register.save.error=保存用户 {0} 失败,注册账号已存在 | ||||
| user.register.error=注册失败,请联系系统管理人员 | ||||
| user.notfound=请重新登录 | ||||
| user.forcelogout=管理员强制退出,请重新登录 | ||||
| user.unknown.error=未知错误,请重新登录 | ||||
| auth.grant.type.error=认证权限类型错误 | ||||
| auth.grant.type.blocked=认证权限类型已禁用 | ||||
| auth.grant.type.not.blank=认证权限类型不能为空 | ||||
| auth.clientid.not.blank=认证客户端id不能为空 | ||||
| ##文件上传消息 | ||||
| upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB! | ||||
| upload.filename.exceed.length=上传的文件名最长{0}个字符 | ||||
| ##权限 | ||||
| no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] | ||||
| repeat.submit.message=不允许重复提交,请稍候再试 | ||||
| rate.limiter.message=访问过于频繁,请稍候再试 | ||||
| sms.code.not.blank=短信验证码不能为空 | ||||
| sms.code.retry.limit.count=短信验证码输入错误{0}次 | ||||
| sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 | ||||
| email.code.not.blank=邮箱验证码不能为空 | ||||
| email.code.retry.limit.count=邮箱验证码输入错误{0}次 | ||||
| email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 | ||||
| xcx.code.not.blank=小程序[code]不能为空 | ||||
| social.source.not.blank=第三方登录平台[source]不能为空 | ||||
| social.code.not.blank=第三方登录平台[code]不能为空 | ||||
| social.state.not.blank=第三方登录平台[state]不能为空 | ||||
| ##租户 | ||||
| tenant.number.not.blank=租户编号不能为空 | ||||
| tenant.not.exists=对不起, 您的租户不存在,请联系管理员 | ||||
| tenant.blocked=对不起,您的租户已禁用,请联系管理员 | ||||
| tenant.expired=对不起,您的租户已过期,请联系管理员 | ||||
| @ -0,0 +1,61 @@ | ||||
| #错误消息 | ||||
| not.null=* Required fill in | ||||
| user.jcaptcha.error=Captcha error | ||||
| user.jcaptcha.expire=Captcha invalid | ||||
| user.not.exists=Sorry, your account: {0} does not exist | ||||
| user.password.not.match=User does not exist/Password error | ||||
| user.password.retry.limit.count=Password input error {0} times | ||||
| user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes | ||||
| user.password.delete=Sorry, your account:{0} has been deleted | ||||
| user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator | ||||
| role.blocked=Role disabled,please contact administrators | ||||
| user.logout.success=Exit successful | ||||
| length.not.valid=The length must be between {min} and {max} characters | ||||
| user.username.not.blank=Username cannot be blank | ||||
| user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number | ||||
| user.username.length.valid=Account length must be between {min} and {max} characters | ||||
| user.password.not.blank=Password cannot be empty | ||||
| user.password.length.valid=Password length must be between {min} and {max} characters | ||||
| user.password.not.valid=* 5-50 characters | ||||
| user.email.not.valid=Mailbox format error | ||||
| user.email.not.blank=Mailbox cannot be blank | ||||
| user.phonenumber.not.blank=Phone number cannot be blank | ||||
| user.mobile.phone.number.not.valid=Phone number format error | ||||
| user.login.success=Login successful | ||||
| user.register.success=Register successful | ||||
| user.register.save.error=Failed to save user {0}, The registered account already exists | ||||
| user.register.error=Register failed, please contact system administrator | ||||
| user.notfound=Please login again | ||||
| user.forcelogout=The administrator is forced to exit,please login again | ||||
| user.unknown.error=Unknown error, please login again | ||||
| auth.grant.type.error=Auth grant type error | ||||
| auth.grant.type.blocked=Auth grant type disabled | ||||
| auth.grant.type.not.blank=Auth grant type cannot be blank | ||||
| auth.clientid.not.blank=Auth clientid cannot be blank | ||||
| ##文件上传消息 | ||||
| upload.exceed.maxSize=The uploaded file size exceeds the limit file size!<br/>the maximum allowed file size is:{0}MB! | ||||
| upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters | ||||
| ##权限 | ||||
| no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}] | ||||
| no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}] | ||||
| no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}] | ||||
| no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}] | ||||
| no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}] | ||||
| no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}] | ||||
| repeat.submit.message=Repeat submit is not allowed, please try again later | ||||
| rate.limiter.message=Visit too frequently, please try again later | ||||
| sms.code.not.blank=Sms code cannot be blank | ||||
| sms.code.retry.limit.count=Sms code input error {0} times | ||||
| sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes | ||||
| email.code.not.blank=Email code cannot be blank | ||||
| email.code.retry.limit.count=Email code input error {0} times | ||||
| email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes | ||||
| xcx.code.not.blank=Mini program [code] cannot be blank | ||||
| social.source.not.blank=Social login platform [source] cannot be blank | ||||
| social.code.not.blank=Social login platform [code] cannot be blank | ||||
| social.state.not.blank=Social login platform [state] cannot be blank | ||||
| ##租户 | ||||
| tenant.number.not.blank=Tenant number cannot be blank | ||||
| tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator | ||||
| tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator | ||||
| tenant.expired=Sorry, your tenant has expired. Please contact the administrator. | ||||
| @ -0,0 +1,61 @@ | ||||
| #错误消息 | ||||
| not.null=* 必须填写 | ||||
| user.jcaptcha.error=验证码错误 | ||||
| user.jcaptcha.expire=验证码已失效 | ||||
| user.not.exists=对不起, 您的账号:{0} 不存在. | ||||
| user.password.not.match=用户不存在/密码错误 | ||||
| user.password.retry.limit.count=密码输入错误{0}次 | ||||
| user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 | ||||
| user.password.delete=对不起,您的账号:{0} 已被删除 | ||||
| user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员 | ||||
| role.blocked=角色已封禁,请联系管理员 | ||||
| user.logout.success=退出成功 | ||||
| length.not.valid=长度必须在{min}到{max}个字符之间 | ||||
| user.username.not.blank=用户名不能为空 | ||||
| user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 | ||||
| user.username.length.valid=账户长度必须在{min}到{max}个字符之间 | ||||
| user.password.not.blank=用户密码不能为空 | ||||
| user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间 | ||||
| user.password.not.valid=* 5-50个字符 | ||||
| user.email.not.valid=邮箱格式错误 | ||||
| user.email.not.blank=邮箱不能为空 | ||||
| user.phonenumber.not.blank=用户手机号不能为空 | ||||
| user.mobile.phone.number.not.valid=手机号格式错误 | ||||
| user.login.success=登录成功 | ||||
| user.register.success=注册成功 | ||||
| user.register.save.error=保存用户 {0} 失败,注册账号已存在 | ||||
| user.register.error=注册失败,请联系系统管理人员 | ||||
| user.notfound=请重新登录 | ||||
| user.forcelogout=管理员强制退出,请重新登录 | ||||
| user.unknown.error=未知错误,请重新登录 | ||||
| auth.grant.type.error=认证权限类型错误 | ||||
| auth.grant.type.blocked=认证权限类型已禁用 | ||||
| auth.grant.type.not.blank=认证权限类型不能为空 | ||||
| auth.clientid.not.blank=认证客户端id不能为空 | ||||
| ##文件上传消息 | ||||
| upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB! | ||||
| upload.filename.exceed.length=上传的文件名最长{0}个字符 | ||||
| ##权限 | ||||
| no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] | ||||
| no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] | ||||
| repeat.submit.message=不允许重复提交,请稍候再试 | ||||
| rate.limiter.message=访问过于频繁,请稍候再试 | ||||
| sms.code.not.blank=短信验证码不能为空 | ||||
| sms.code.retry.limit.count=短信验证码输入错误{0}次 | ||||
| sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟 | ||||
| email.code.not.blank=邮箱验证码不能为空 | ||||
| email.code.retry.limit.count=邮箱验证码输入错误{0}次 | ||||
| email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟 | ||||
| xcx.code.not.blank=小程序[code]不能为空 | ||||
| social.source.not.blank=第三方登录平台[source]不能为空 | ||||
| social.code.not.blank=第三方登录平台[code]不能为空 | ||||
| social.state.not.blank=第三方登录平台[state]不能为空 | ||||
| ##租户 | ||||
| tenant.number.not.blank=租户编号不能为空 | ||||
| tenant.not.exists=对不起, 您的租户不存在,请联系管理员 | ||||
| tenant.blocked=对不起,您的租户已禁用,请联系管理员 | ||||
| tenant.expired=对不起,您的租户已过期,请联系管理员 | ||||
							
								
								
									
										
											BIN
										
									
								
								RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/ip2region.xdb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/ip2region.xdb
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										129
									
								
								RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/logback-plus.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								RuoYi-Vue-Plus/ruoyi-admin/src/main/resources/logback-plus.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,129 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <configuration> | ||||
|     <property name="log.path" value="./logs"/> | ||||
|     <property name="console.log.pattern" | ||||
|               value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> | ||||
|     <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/> | ||||
|  | ||||
|     <!-- 控制台输出 --> | ||||
|     <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> | ||||
|         <encoder> | ||||
|             <pattern>${console.log.pattern}</pattern> | ||||
|             <charset>utf-8</charset> | ||||
|         </encoder> | ||||
|     </appender> | ||||
|  | ||||
|     <!-- 控制台输出 --> | ||||
|     <appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender"> | ||||
|         <file>${log.path}/sys-console.log</file> | ||||
|         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | ||||
|             <!-- 日志文件名格式 --> | ||||
|             <fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern> | ||||
|             <!-- 日志最大 1天 --> | ||||
|             <maxHistory>1</maxHistory> | ||||
|         </rollingPolicy> | ||||
|         <encoder> | ||||
|             <pattern>${log.pattern}</pattern> | ||||
|             <charset>utf-8</charset> | ||||
|         </encoder> | ||||
|         <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> | ||||
|             <!-- 过滤的级别 --> | ||||
|             <level>INFO</level> | ||||
|         </filter> | ||||
|     </appender> | ||||
|  | ||||
|     <!-- 系统日志输出 --> | ||||
|     <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> | ||||
|         <file>${log.path}/sys-info.log</file> | ||||
|         <!-- 循环政策:基于时间创建日志文件 --> | ||||
|         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | ||||
|             <!-- 日志文件名格式 --> | ||||
|             <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> | ||||
|             <!-- 日志最大的历史 60天 --> | ||||
|             <maxHistory>60</maxHistory> | ||||
|         </rollingPolicy> | ||||
|         <encoder> | ||||
|             <pattern>${log.pattern}</pattern> | ||||
|         </encoder> | ||||
|         <filter class="ch.qos.logback.classic.filter.LevelFilter"> | ||||
|             <!-- 过滤的级别 --> | ||||
|             <level>INFO</level> | ||||
|             <!-- 匹配时的操作:接收(记录) --> | ||||
|             <onMatch>ACCEPT</onMatch> | ||||
|             <!-- 不匹配时的操作:拒绝(不记录) --> | ||||
|             <onMismatch>DENY</onMismatch> | ||||
|         </filter> | ||||
|     </appender> | ||||
|  | ||||
|     <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> | ||||
|         <file>${log.path}/sys-error.log</file> | ||||
|         <!-- 循环政策:基于时间创建日志文件 --> | ||||
|         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | ||||
|             <!-- 日志文件名格式 --> | ||||
|             <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> | ||||
|             <!-- 日志最大的历史 60天 --> | ||||
|             <maxHistory>60</maxHistory> | ||||
|         </rollingPolicy> | ||||
|         <encoder> | ||||
|             <pattern>${log.pattern}</pattern> | ||||
|         </encoder> | ||||
|         <filter class="ch.qos.logback.classic.filter.LevelFilter"> | ||||
|             <!-- 过滤的级别 --> | ||||
|             <level>ERROR</level> | ||||
|             <!-- 匹配时的操作:接收(记录) --> | ||||
|             <onMatch>ACCEPT</onMatch> | ||||
|             <!-- 不匹配时的操作:拒绝(不记录) --> | ||||
|             <onMismatch>DENY</onMismatch> | ||||
|         </filter> | ||||
|     </appender> | ||||
|  | ||||
|     <!-- info异步输出 --> | ||||
|     <appender name="async_info" class="ch.qos.logback.classic.AsyncAppender"> | ||||
|         <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> | ||||
|         <discardingThreshold>0</discardingThreshold> | ||||
|         <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> | ||||
|         <queueSize>512</queueSize> | ||||
|         <!-- 添加附加的appender,最多只能添加一个 --> | ||||
|         <appender-ref ref="file_info"/> | ||||
|     </appender> | ||||
|  | ||||
|     <!-- error异步输出 --> | ||||
|     <appender name="async_error" class="ch.qos.logback.classic.AsyncAppender"> | ||||
|         <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> | ||||
|         <discardingThreshold>0</discardingThreshold> | ||||
|         <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> | ||||
|         <queueSize>512</queueSize> | ||||
|         <!-- 添加附加的appender,最多只能添加一个 --> | ||||
|         <appender-ref ref="file_error"/> | ||||
|     </appender> | ||||
|  | ||||
|     <!-- 整合 skywalking 控制台输出 tid --> | ||||
| <!--    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">--> | ||||
| <!--        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">--> | ||||
| <!--            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">--> | ||||
| <!--                <pattern>[%tid] ${console.log.pattern}</pattern>--> | ||||
| <!--            </layout>--> | ||||
| <!--            <charset>utf-8</charset>--> | ||||
| <!--        </encoder>--> | ||||
| <!--    </appender>--> | ||||
|  | ||||
|     <!-- 整合 skywalking 推送采集日志 --> | ||||
| <!--    <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">--> | ||||
| <!--        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">--> | ||||
| <!--            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">--> | ||||
| <!--                <pattern>[%tid] ${console.log.pattern}</pattern>--> | ||||
| <!--            </layout>--> | ||||
| <!--            <charset>utf-8</charset>--> | ||||
| <!--        </encoder>--> | ||||
| <!--    </appender>--> | ||||
|  | ||||
|     <!--系统操作日志--> | ||||
|     <root level="info"> | ||||
|         <appender-ref ref="console" /> | ||||
|         <appender-ref ref="async_info" /> | ||||
|         <appender-ref ref="async_error" /> | ||||
|         <appender-ref ref="file_console" /> | ||||
| <!--        <appender-ref ref="sky_log"/>--> | ||||
|     </root> | ||||
|  | ||||
| </configuration> | ||||
| @ -0,0 +1,45 @@ | ||||
| package org.dromara.test; | ||||
|  | ||||
| import org.junit.jupiter.api.Assertions; | ||||
| import org.junit.jupiter.api.DisplayName; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| /** | ||||
|  * 断言单元测试案例 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @DisplayName("断言单元测试案例") | ||||
| public class AssertUnitTest { | ||||
|  | ||||
|     @DisplayName("测试 assertEquals 方法") | ||||
|     @Test | ||||
|     public void testAssertEquals() { | ||||
|         Assertions.assertEquals("666", new String("666")); | ||||
|         Assertions.assertNotEquals("666", new String("666")); | ||||
|     } | ||||
|  | ||||
|     @DisplayName("测试 assertSame 方法") | ||||
|     @Test | ||||
|     public void testAssertSame() { | ||||
|         Object obj = new Object(); | ||||
|         Object obj1 = obj; | ||||
|         Assertions.assertSame(obj, obj1); | ||||
|         Assertions.assertNotSame(obj, obj1); | ||||
|     } | ||||
|  | ||||
|     @DisplayName("测试 assertTrue 方法") | ||||
|     @Test | ||||
|     public void testAssertTrue() { | ||||
|         Assertions.assertTrue(true); | ||||
|         Assertions.assertFalse(true); | ||||
|     } | ||||
|  | ||||
|     @DisplayName("测试 assertNull 方法") | ||||
|     @Test | ||||
|     public void testAssertNull() { | ||||
|         Assertions.assertNull(null); | ||||
|         Assertions.assertNotNull(null); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,70 @@ | ||||
| package org.dromara.test; | ||||
|  | ||||
| import org.dromara.common.core.config.RuoYiConfig; | ||||
| import org.junit.jupiter.api.*; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.boot.test.context.SpringBootTest; | ||||
|  | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| /** | ||||
|  * 单元测试案例 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件 | ||||
| @DisplayName("单元测试案例") | ||||
| public class DemoUnitTest { | ||||
|  | ||||
|     @Autowired | ||||
|     private RuoYiConfig ruoYiConfig; | ||||
|  | ||||
|     @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解") | ||||
|     @Test | ||||
|     public void testTest() { | ||||
|         System.out.println(ruoYiConfig); | ||||
|     } | ||||
|  | ||||
|     @Disabled | ||||
|     @DisplayName("测试 @Disabled 注解") | ||||
|     @Test | ||||
|     public void testDisabled() { | ||||
|         System.out.println(ruoYiConfig); | ||||
|     } | ||||
|  | ||||
|     @Timeout(value = 2L, unit = TimeUnit.SECONDS) | ||||
|     @DisplayName("测试 @Timeout 注解") | ||||
|     @Test | ||||
|     public void testTimeout() throws InterruptedException { | ||||
|         Thread.sleep(3000); | ||||
|         System.out.println(ruoYiConfig); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @DisplayName("测试 @RepeatedTest 注解") | ||||
|     @RepeatedTest(3) | ||||
|     public void testRepeatedTest() { | ||||
|         System.out.println(666); | ||||
|     } | ||||
|  | ||||
|     @BeforeAll | ||||
|     public static void testBeforeAll() { | ||||
|         System.out.println("@BeforeAll =================="); | ||||
|     } | ||||
|  | ||||
|     @BeforeEach | ||||
|     public void testBeforeEach() { | ||||
|         System.out.println("@BeforeEach =================="); | ||||
|     } | ||||
|  | ||||
|     @AfterEach | ||||
|     public void testAfterEach() { | ||||
|         System.out.println("@AfterEach =================="); | ||||
|     } | ||||
|  | ||||
|     @AfterAll | ||||
|     public static void testAfterAll() { | ||||
|         System.out.println("@AfterAll =================="); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,72 @@ | ||||
| package org.dromara.test; | ||||
|  | ||||
| import org.dromara.common.core.enums.UserType; | ||||
| import org.junit.jupiter.api.AfterEach; | ||||
| import org.junit.jupiter.api.BeforeEach; | ||||
| import org.junit.jupiter.api.DisplayName; | ||||
| import org.junit.jupiter.params.ParameterizedTest; | ||||
| import org.junit.jupiter.params.provider.EnumSource; | ||||
| import org.junit.jupiter.params.provider.MethodSource; | ||||
| import org.junit.jupiter.params.provider.NullSource; | ||||
| import org.junit.jupiter.params.provider.ValueSource; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| /** | ||||
|  * 带参数单元测试案例 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @DisplayName("带参数单元测试案例") | ||||
| public class ParamUnitTest { | ||||
|  | ||||
|     @DisplayName("测试 @ValueSource 注解") | ||||
|     @ParameterizedTest | ||||
|     @ValueSource(strings = {"t1", "t2", "t3"}) | ||||
|     public void testValueSource(String str) { | ||||
|         System.out.println(str); | ||||
|     } | ||||
|  | ||||
|     @DisplayName("测试 @NullSource 注解") | ||||
|     @ParameterizedTest | ||||
|     @NullSource | ||||
|     public void testNullSource(String str) { | ||||
|         System.out.println(str); | ||||
|     } | ||||
|  | ||||
|     @DisplayName("测试 @EnumSource 注解") | ||||
|     @ParameterizedTest | ||||
|     @EnumSource(UserType.class) | ||||
|     public void testEnumSource(UserType type) { | ||||
|         System.out.println(type.getUserType()); | ||||
|     } | ||||
|  | ||||
|     @DisplayName("测试 @MethodSource 注解") | ||||
|     @ParameterizedTest | ||||
|     @MethodSource("getParam") | ||||
|     public void testMethodSource(String str) { | ||||
|         System.out.println(str); | ||||
|     } | ||||
|  | ||||
|     public static Stream<String> getParam() { | ||||
|         List<String> list = new ArrayList<>(); | ||||
|         list.add("t1"); | ||||
|         list.add("t2"); | ||||
|         list.add("t3"); | ||||
|         return list.stream(); | ||||
|     } | ||||
|  | ||||
|     @BeforeEach | ||||
|     public void testBeforeEach() { | ||||
|         System.out.println("@BeforeEach =================="); | ||||
|     } | ||||
|  | ||||
|     @AfterEach | ||||
|     public void testAfterEach() { | ||||
|         System.out.println("@AfterEach =================="); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,54 @@ | ||||
| package org.dromara.test; | ||||
|  | ||||
| import org.junit.jupiter.api.*; | ||||
| import org.springframework.boot.test.context.SpringBootTest; | ||||
|  | ||||
| /** | ||||
|  * 标签单元测试案例 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @SpringBootTest | ||||
| @DisplayName("标签单元测试案例") | ||||
| public class TagUnitTest { | ||||
|  | ||||
|     @Tag("dev") | ||||
|     @DisplayName("测试 @Tag dev") | ||||
|     @Test | ||||
|     public void testTagDev() { | ||||
|         System.out.println("dev"); | ||||
|     } | ||||
|  | ||||
|     @Tag("prod") | ||||
|     @DisplayName("测试 @Tag prod") | ||||
|     @Test | ||||
|     public void testTagProd() { | ||||
|         System.out.println("prod"); | ||||
|     } | ||||
|  | ||||
|     @Tag("local") | ||||
|     @DisplayName("测试 @Tag local") | ||||
|     @Test | ||||
|     public void testTagLocal() { | ||||
|         System.out.println("local"); | ||||
|     } | ||||
|  | ||||
|     @Tag("exclude") | ||||
|     @DisplayName("测试 @Tag exclude") | ||||
|     @Test | ||||
|     public void testTagExclude() { | ||||
|         System.out.println("exclude"); | ||||
|     } | ||||
|  | ||||
|     @BeforeEach | ||||
|     public void testBeforeEach() { | ||||
|         System.out.println("@BeforeEach =================="); | ||||
|     } | ||||
|  | ||||
|     @AfterEach | ||||
|     public void testAfterEach() { | ||||
|         System.out.println("@AfterEach =================="); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										1
									
								
								RuoYi-Vue-Plus/ruoyi-admin/zhFonts/.uuid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								RuoYi-Vue-Plus/ruoyi-admin/zhFonts/.uuid
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| 3f2ee348-0303-40ca-bf03-03f48d2d2141 | ||||
							
								
								
									
										
											BIN
										
									
								
								RuoYi-Vue-Plus/ruoyi-admin/zhFonts/SIMSUN.TTC
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								RuoYi-Vue-Plus/ruoyi-admin/zhFonts/SIMSUN.TTC
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										4
									
								
								RuoYi-Vue-Plus/ruoyi-admin/zhFonts/fonts.dir
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								RuoYi-Vue-Plus/ruoyi-admin/zhFonts/fonts.dir
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| 3 | ||||
| SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1 | ||||
| SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1 | ||||
| SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r | ||||
							
								
								
									
										4
									
								
								RuoYi-Vue-Plus/ruoyi-admin/zhFonts/fonts.scale
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								RuoYi-Vue-Plus/ruoyi-admin/zhFonts/fonts.scale
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| 3 | ||||
| SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1 | ||||
| SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1 | ||||
| SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r | ||||
							
								
								
									
										46
									
								
								RuoYi-Vue-Plus/ruoyi-common/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								RuoYi-Vue-Plus/ruoyi-common/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <parent> | ||||
|         <artifactId>ruoyi-vue-plus</artifactId> | ||||
|         <groupId>org.dromara</groupId> | ||||
|         <version>${revision}</version> | ||||
|     </parent> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|  | ||||
|     <modules> | ||||
|         <module>ruoyi-common-bom</module> | ||||
|         <module>ruoyi-common-social</module> | ||||
|         <module>ruoyi-common-core</module> | ||||
|         <module>ruoyi-common-doc</module> | ||||
|         <module>ruoyi-common-excel</module> | ||||
|         <module>ruoyi-common-idempotent</module> | ||||
|         <module>ruoyi-common-job</module> | ||||
|         <module>ruoyi-common-log</module> | ||||
|         <module>ruoyi-common-mail</module> | ||||
|         <module>ruoyi-common-mybatis</module> | ||||
|         <module>ruoyi-common-oss</module> | ||||
|         <module>ruoyi-common-ratelimiter</module> | ||||
|         <module>ruoyi-common-redis</module> | ||||
|         <module>ruoyi-common-satoken</module> | ||||
|         <module>ruoyi-common-security</module> | ||||
|         <module>ruoyi-common-sms</module> | ||||
|         <module>ruoyi-common-web</module> | ||||
|         <module>ruoyi-common-translation</module> | ||||
|         <module>ruoyi-common-sensitive</module> | ||||
|         <module>ruoyi-common-json</module> | ||||
|         <module>ruoyi-common-encrypt</module> | ||||
|         <module>ruoyi-common-tenant</module> | ||||
|         <module>ruoyi-common-websocket</module> | ||||
|         <module>ruoyi-common-sse</module> | ||||
|     </modules> | ||||
|  | ||||
|     <artifactId>ruoyi-common</artifactId> | ||||
|     <packaging>pom</packaging> | ||||
|  | ||||
|     <description> | ||||
|         common 通用模块 | ||||
|     </description> | ||||
|  | ||||
| </project> | ||||
							
								
								
									
										185
									
								
								RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-bom/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-bom/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|  | ||||
|     <groupId>org.dromara</groupId> | ||||
|     <artifactId>ruoyi-common-bom</artifactId> | ||||
|     <version>${revision}</version> | ||||
|     <packaging>pom</packaging> | ||||
|  | ||||
|     <description> | ||||
|         ruoyi-common-bom common依赖项 | ||||
|     </description> | ||||
|  | ||||
|     <properties> | ||||
|         <revision>5.3.0</revision> | ||||
|     </properties> | ||||
|  | ||||
|     <dependencyManagement> | ||||
|         <dependencies> | ||||
|             <!-- 核心模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-core</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 接口模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-doc</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- excel --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-excel</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 幂等 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-idempotent</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 调度模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-job</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 日志记录 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-log</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 邮件服务 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-mail</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 数据库服务 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-mybatis</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- OSS --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-oss</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 限流 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-ratelimiter</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 缓存服务 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-redis</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- satoken --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-satoken</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 安全模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-security</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 短信模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-sms</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-social</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- web服务 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-web</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 翻译模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-translation</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 脱敏模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-sensitive</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 序列化模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-json</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 数据库加解密模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-encrypt</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- 租户模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-tenant</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- WebSocket模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-websocket</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|             <!-- SSE模块 --> | ||||
|             <dependency> | ||||
|                 <groupId>org.dromara</groupId> | ||||
|                 <artifactId>ruoyi-common-sse</artifactId> | ||||
|                 <version>${revision}</version> | ||||
|             </dependency> | ||||
|  | ||||
|         </dependencies> | ||||
|     </dependencyManagement> | ||||
|  | ||||
| </project> | ||||
							
								
								
									
										99
									
								
								RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								RuoYi-Vue-Plus/ruoyi-common/ruoyi-common-core/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <parent> | ||||
|         <groupId>org.dromara</groupId> | ||||
|         <artifactId>ruoyi-common</artifactId> | ||||
|         <version>${revision}</version> | ||||
|     </parent> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|  | ||||
|     <artifactId>ruoyi-common-core</artifactId> | ||||
|  | ||||
|     <description> | ||||
|         ruoyi-common-core 核心模块 | ||||
|     </description> | ||||
|  | ||||
|     <dependencies> | ||||
|         <!-- Spring框架基本的核心工具 --> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework</groupId> | ||||
|             <artifactId>spring-context-support</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- SpringWeb模块 --> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework</groupId> | ||||
|             <artifactId>spring-web</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- 自定义验证注解 --> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-validation</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-starter-aop</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!--常用工具类 --> | ||||
|         <dependency> | ||||
|             <groupId>org.apache.commons</groupId> | ||||
|             <artifactId>commons-lang3</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- servlet包 --> | ||||
|         <dependency> | ||||
|             <groupId>jakarta.servlet</groupId> | ||||
|             <artifactId>jakarta.servlet-api</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>cn.hutool</groupId> | ||||
|             <artifactId>hutool-core</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>cn.hutool</groupId> | ||||
|             <artifactId>hutool-http</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>cn.hutool</groupId> | ||||
|             <artifactId>hutool-extra</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.projectlombok</groupId> | ||||
|             <artifactId>lombok</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!--  自动生成YML配置关联JSON文件  --> | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-configuration-processor</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>org.springframework.boot</groupId> | ||||
|             <artifactId>spring-boot-properties-migrator</artifactId> | ||||
|             <scope>runtime</scope> | ||||
|         </dependency> | ||||
|  | ||||
|         <dependency> | ||||
|             <groupId>io.github.linpeilie</groupId> | ||||
|             <artifactId>mapstruct-plus-spring-boot-starter</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- 离线IP地址定位库 --> | ||||
|         <dependency> | ||||
|             <groupId>org.lionsoul</groupId> | ||||
|             <artifactId>ip2region</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|     </dependencies> | ||||
|  | ||||
| </project> | ||||
| @ -0,0 +1,17 @@ | ||||
| package org.dromara.common.core.config; | ||||
|  | ||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||
| import org.springframework.context.annotation.EnableAspectJAutoProxy; | ||||
| import org.springframework.scheduling.annotation.EnableAsync; | ||||
|  | ||||
| /** | ||||
|  * 程序注解配置 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @AutoConfiguration | ||||
| @EnableAspectJAutoProxy | ||||
| @EnableAsync(proxyTargetClass = true) | ||||
| public class ApplicationConfig { | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,52 @@ | ||||
| package org.dromara.common.core.config; | ||||
|  | ||||
| import cn.hutool.core.util.ArrayUtil; | ||||
| import org.dromara.common.core.exception.ServiceException; | ||||
| import org.dromara.common.core.utils.SpringUtils; | ||||
| import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; | ||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||
| import org.springframework.core.task.VirtualThreadTaskExecutor; | ||||
| import org.springframework.scheduling.annotation.AsyncConfigurer; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.concurrent.Executor; | ||||
|  | ||||
| /** | ||||
|  * 异步配置 | ||||
|  * <p> | ||||
|  * 如果未使用虚拟线程则生效 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @AutoConfiguration | ||||
| public class AsyncConfig implements AsyncConfigurer { | ||||
|  | ||||
|     /** | ||||
|      * 自定义 @Async 注解使用系统线程池 | ||||
|      */ | ||||
|     @Override | ||||
|     public Executor getAsyncExecutor() { | ||||
|         if(SpringUtils.isVirtual()) { | ||||
|             return new VirtualThreadTaskExecutor("async-"); | ||||
|         } | ||||
|         return SpringUtils.getBean("scheduledExecutorService"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 异步执行异常处理 | ||||
|      */ | ||||
|     @Override | ||||
|     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { | ||||
|         return (throwable, method, objects) -> { | ||||
|             throwable.printStackTrace(); | ||||
|             StringBuilder sb = new StringBuilder(); | ||||
|             sb.append("Exception message - ").append(throwable.getMessage()) | ||||
|                 .append(", Method name - ").append(method.getName()); | ||||
|             if (ArrayUtil.isNotEmpty(objects)) { | ||||
|                 sb.append(", Parameter value - ").append(Arrays.toString(objects)); | ||||
|             } | ||||
|             throw new ServiceException(sb.toString()); | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| package org.dromara.common.core.config; | ||||
|  | ||||
| import lombok.Data; | ||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | ||||
| /** | ||||
|  * 读取项目相关配置 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| @Component | ||||
| @ConfigurationProperties(prefix = "ruoyi") | ||||
| public class RuoYiConfig { | ||||
|  | ||||
|     /** | ||||
|      * 项目名称 | ||||
|      */ | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * 版本 | ||||
|      */ | ||||
|     private String version; | ||||
|  | ||||
|     /** | ||||
|      * 版权年份 | ||||
|      */ | ||||
|     private String copyrightYear; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,87 @@ | ||||
| package org.dromara.common.core.config; | ||||
|  | ||||
| import jakarta.annotation.PreDestroy; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.apache.commons.lang3.concurrent.BasicThreadFactory; | ||||
| import org.dromara.common.core.config.properties.ThreadPoolProperties; | ||||
| import org.dromara.common.core.utils.SpringUtils; | ||||
| import org.dromara.common.core.utils.Threads; | ||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.core.task.VirtualThreadTaskExecutor; | ||||
| import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||||
|  | ||||
| import java.util.concurrent.ScheduledExecutorService; | ||||
| import java.util.concurrent.ScheduledThreadPoolExecutor; | ||||
| import java.util.concurrent.ThreadPoolExecutor; | ||||
|  | ||||
| /** | ||||
|  * 线程池配置 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  **/ | ||||
| @Slf4j | ||||
| @AutoConfiguration | ||||
| @EnableConfigurationProperties(ThreadPoolProperties.class) | ||||
| public class ThreadPoolConfig { | ||||
|  | ||||
|     /** | ||||
|      * 核心线程数 = cpu 核心数 + 1 | ||||
|      */ | ||||
|     private final int core = Runtime.getRuntime().availableProcessors() + 1; | ||||
|  | ||||
|     private ScheduledExecutorService scheduledExecutorService; | ||||
|  | ||||
|     @Bean(name = "threadPoolTaskExecutor") | ||||
|     @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") | ||||
|     public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) { | ||||
|         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); | ||||
|         executor.setCorePoolSize(core); | ||||
|         executor.setMaxPoolSize(core * 2); | ||||
|         executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); | ||||
|         executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); | ||||
|         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); | ||||
|         return executor; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 执行周期性或定时任务 | ||||
|      */ | ||||
|     @Bean(name = "scheduledExecutorService") | ||||
|     protected ScheduledExecutorService scheduledExecutorService() { | ||||
|         // daemon 必须为 true | ||||
|         BasicThreadFactory.Builder builder = new BasicThreadFactory.Builder().daemon(true); | ||||
|         if (SpringUtils.isVirtual()) { | ||||
|             builder.namingPattern("virtual-schedule-pool-%d").wrappedFactory(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); | ||||
|         } else { | ||||
|             builder.namingPattern("schedule-pool-%d"); | ||||
|         } | ||||
|         ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core, | ||||
|             builder.build(), | ||||
|             new ThreadPoolExecutor.CallerRunsPolicy()) { | ||||
|             @Override | ||||
|             protected void afterExecute(Runnable r, Throwable t) { | ||||
|                 super.afterExecute(r, t); | ||||
|                 Threads.printException(r, t); | ||||
|             } | ||||
|         }; | ||||
|         this.scheduledExecutorService = scheduledThreadPoolExecutor; | ||||
|         return scheduledThreadPoolExecutor; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 销毁事件 | ||||
|      */ | ||||
|     @PreDestroy | ||||
|     public void destroy() { | ||||
|         try { | ||||
|             log.info("====关闭后台任务任务线程池===="); | ||||
|             Threads.shutdownAndAwaitTermination(scheduledExecutorService); | ||||
|         } catch (Exception e) { | ||||
|             log.error(e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,40 @@ | ||||
| package org.dromara.common.core.config; | ||||
|  | ||||
| import jakarta.validation.Validator; | ||||
| import org.hibernate.validator.HibernateValidator; | ||||
| import org.springframework.boot.autoconfigure.AutoConfiguration; | ||||
| import org.springframework.context.MessageSource; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; | ||||
|  | ||||
| import java.util.Properties; | ||||
|  | ||||
| /** | ||||
|  * 校验框架配置类 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @AutoConfiguration | ||||
| public class ValidatorConfig { | ||||
|  | ||||
|     /** | ||||
|      * 配置校验框架 快速返回模式 | ||||
|      */ | ||||
|     @Bean | ||||
|     public Validator validator(MessageSource messageSource) { | ||||
|         try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) { | ||||
|             // 国际化 | ||||
|             factoryBean.setValidationMessageSource(messageSource); | ||||
|             // 设置使用 HibernateValidator 校验器 | ||||
|             factoryBean.setProviderClass(HibernateValidator.class); | ||||
|             Properties properties = new Properties(); | ||||
|             // 设置 快速异常返回 | ||||
|             properties.setProperty("hibernate.validator.fail_fast", "true"); | ||||
|             factoryBean.setValidationProperties(properties); | ||||
|             // 加载配置 | ||||
|             factoryBean.afterPropertiesSet(); | ||||
|             return factoryBean.getValidator(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package org.dromara.common.core.config.properties; | ||||
|  | ||||
| import lombok.Data; | ||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
|  | ||||
| /** | ||||
|  * 线程池 配置属性 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| @ConfigurationProperties(prefix = "thread-pool") | ||||
| public class ThreadPoolProperties { | ||||
|  | ||||
|     /** | ||||
|      * 是否开启线程池 | ||||
|      */ | ||||
|     private boolean enabled; | ||||
|  | ||||
|     /** | ||||
|      * 队列最大长度 | ||||
|      */ | ||||
|     private int queueCapacity; | ||||
|  | ||||
|     /** | ||||
|      * 线程池维护线程所允许的空闲时间 | ||||
|      */ | ||||
|     private int keepAliveSeconds; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package org.dromara.common.core.constant; | ||||
|  | ||||
| /** | ||||
|  * 缓存的key 常量 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| public interface CacheConstants { | ||||
|  | ||||
|     /** | ||||
|      * 在线用户 redis key | ||||
|      */ | ||||
|     String ONLINE_TOKEN_KEY = "online_tokens:"; | ||||
|  | ||||
|     /** | ||||
|      * 参数管理 cache key | ||||
|      */ | ||||
|     String SYS_CONFIG_KEY = "sys_config:"; | ||||
|  | ||||
|     /** | ||||
|      * 字典管理 cache key | ||||
|      */ | ||||
|     String SYS_DICT_KEY = "sys_dict:"; | ||||
|  | ||||
|     /** | ||||
|      * 登录账户密码错误次数 redis key | ||||
|      */ | ||||
|     String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,83 @@ | ||||
| package org.dromara.common.core.constant; | ||||
|  | ||||
| /** | ||||
|  * 缓存组名称常量 | ||||
|  * <p> | ||||
|  * key 格式为 cacheNames#ttl#maxIdleTime#maxSize | ||||
|  * <p> | ||||
|  * ttl 过期时间 如果设置为0则不过期 默认为0 | ||||
|  * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 | ||||
|  * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 | ||||
|  * <p> | ||||
|  * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| public interface CacheNames { | ||||
|  | ||||
|     /** | ||||
|      * 演示案例 | ||||
|      */ | ||||
|     String DEMO_CACHE = "demo:cache#60s#10m#20"; | ||||
|  | ||||
|     /** | ||||
|      * 系统配置 | ||||
|      */ | ||||
|     String SYS_CONFIG = "sys_config"; | ||||
|  | ||||
|     /** | ||||
|      * 数据字典 | ||||
|      */ | ||||
|     String SYS_DICT = "sys_dict"; | ||||
|  | ||||
|     /** | ||||
|      * 租户 | ||||
|      */ | ||||
|     String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d"; | ||||
|  | ||||
|     /** | ||||
|      * 客户端 | ||||
|      */ | ||||
|     String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d"; | ||||
|  | ||||
|     /** | ||||
|      * 用户账户 | ||||
|      */ | ||||
|     String SYS_USER_NAME = "sys_user_name#30d"; | ||||
|  | ||||
|     /** | ||||
|      * 用户名称 | ||||
|      */ | ||||
|     String SYS_NICKNAME = "sys_nickname#30d"; | ||||
|  | ||||
|     /** | ||||
|      * 部门 | ||||
|      */ | ||||
|     String SYS_DEPT = "sys_dept#30d"; | ||||
|  | ||||
|     /** | ||||
|      * OSS内容 | ||||
|      */ | ||||
|     String SYS_OSS = "sys_oss#30d"; | ||||
|  | ||||
|     /** | ||||
|      * 角色自定义权限 | ||||
|      */ | ||||
|     String SYS_ROLE_CUSTOM = "sys_role_custom#30d"; | ||||
|  | ||||
|     /** | ||||
|      * 部门及以下权限 | ||||
|      */ | ||||
|     String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d"; | ||||
|  | ||||
|     /** | ||||
|      * OSS配置 | ||||
|      */ | ||||
|     String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config"; | ||||
|  | ||||
|     /** | ||||
|      * 在线用户 | ||||
|      */ | ||||
|     String ONLINE_TOKEN = "online_tokens"; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,76 @@ | ||||
| package org.dromara.common.core.constant; | ||||
|  | ||||
| /** | ||||
|  * 通用常量信息 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| public interface Constants { | ||||
|  | ||||
|     /** | ||||
|      * UTF-8 字符集 | ||||
|      */ | ||||
|     String UTF8 = "UTF-8"; | ||||
|  | ||||
|     /** | ||||
|      * GBK 字符集 | ||||
|      */ | ||||
|     String GBK = "GBK"; | ||||
|  | ||||
|     /** | ||||
|      * www主域 | ||||
|      */ | ||||
|     String WWW = "www."; | ||||
|  | ||||
|     /** | ||||
|      * http请求 | ||||
|      */ | ||||
|     String HTTP = "http://"; | ||||
|  | ||||
|     /** | ||||
|      * https请求 | ||||
|      */ | ||||
|     String HTTPS = "https://"; | ||||
|  | ||||
|     /** | ||||
|      * 通用成功标识 | ||||
|      */ | ||||
|     String SUCCESS = "0"; | ||||
|  | ||||
|     /** | ||||
|      * 通用失败标识 | ||||
|      */ | ||||
|     String FAIL = "1"; | ||||
|  | ||||
|     /** | ||||
|      * 登录成功 | ||||
|      */ | ||||
|     String LOGIN_SUCCESS = "Success"; | ||||
|  | ||||
|     /** | ||||
|      * 注销 | ||||
|      */ | ||||
|     String LOGOUT = "Logout"; | ||||
|  | ||||
|     /** | ||||
|      * 注册 | ||||
|      */ | ||||
|     String REGISTER = "Register"; | ||||
|  | ||||
|     /** | ||||
|      * 登录失败 | ||||
|      */ | ||||
|     String LOGIN_FAIL = "Error"; | ||||
|  | ||||
|     /** | ||||
|      * 验证码有效期(分钟) | ||||
|      */ | ||||
|     Integer CAPTCHA_EXPIRATION = 2; | ||||
|  | ||||
|     /** | ||||
|      * 顶级父级id | ||||
|      */ | ||||
|     Long TOP_PARENT_ID = 0L; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,34 @@ | ||||
| package org.dromara.common.core.constant; | ||||
|  | ||||
| /** | ||||
|  * 全局的key常量 (业务无关的key) | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| public interface GlobalConstants { | ||||
|  | ||||
|     /** | ||||
|      * 全局 redis key (业务无关的key) | ||||
|      */ | ||||
|     String GLOBAL_REDIS_KEY = "global:"; | ||||
|  | ||||
|     /** | ||||
|      * 验证码 redis key | ||||
|      */ | ||||
|     String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:"; | ||||
|  | ||||
|     /** | ||||
|      * 防重提交 redis key | ||||
|      */ | ||||
|     String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:"; | ||||
|  | ||||
|     /** | ||||
|      * 限流 redis key | ||||
|      */ | ||||
|     String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:"; | ||||
|  | ||||
|     /** | ||||
|      * 三方认证 redis key | ||||
|      */ | ||||
|     String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:"; | ||||
| } | ||||
| @ -0,0 +1,93 @@ | ||||
| package org.dromara.common.core.constant; | ||||
|  | ||||
| /** | ||||
|  * 返回状态码 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| public interface HttpStatus { | ||||
|     /** | ||||
|      * 操作成功 | ||||
|      */ | ||||
|     int SUCCESS = 200; | ||||
|  | ||||
|     /** | ||||
|      * 对象创建成功 | ||||
|      */ | ||||
|     int CREATED = 201; | ||||
|  | ||||
|     /** | ||||
|      * 请求已经被接受 | ||||
|      */ | ||||
|     int ACCEPTED = 202; | ||||
|  | ||||
|     /** | ||||
|      * 操作已经执行成功,但是没有返回数据 | ||||
|      */ | ||||
|     int NO_CONTENT = 204; | ||||
|  | ||||
|     /** | ||||
|      * 资源已被移除 | ||||
|      */ | ||||
|     int MOVED_PERM = 301; | ||||
|  | ||||
|     /** | ||||
|      * 重定向 | ||||
|      */ | ||||
|     int SEE_OTHER = 303; | ||||
|  | ||||
|     /** | ||||
|      * 资源没有被修改 | ||||
|      */ | ||||
|     int NOT_MODIFIED = 304; | ||||
|  | ||||
|     /** | ||||
|      * 参数列表错误(缺少,格式不匹配) | ||||
|      */ | ||||
|     int BAD_REQUEST = 400; | ||||
|  | ||||
|     /** | ||||
|      * 未授权 | ||||
|      */ | ||||
|     int UNAUTHORIZED = 401; | ||||
|  | ||||
|     /** | ||||
|      * 访问受限,授权过期 | ||||
|      */ | ||||
|     int FORBIDDEN = 403; | ||||
|  | ||||
|     /** | ||||
|      * 资源,服务未找到 | ||||
|      */ | ||||
|     int NOT_FOUND = 404; | ||||
|  | ||||
|     /** | ||||
|      * 不允许的http方法 | ||||
|      */ | ||||
|     int BAD_METHOD = 405; | ||||
|  | ||||
|     /** | ||||
|      * 资源冲突,或者资源被锁 | ||||
|      */ | ||||
|     int CONFLICT = 409; | ||||
|  | ||||
|     /** | ||||
|      * 不支持的数据,媒体类型 | ||||
|      */ | ||||
|     int UNSUPPORTED_TYPE = 415; | ||||
|  | ||||
|     /** | ||||
|      * 系统内部错误 | ||||
|      */ | ||||
|     int ERROR = 500; | ||||
|  | ||||
|     /** | ||||
|      * 接口未实现 | ||||
|      */ | ||||
|     int NOT_IMPLEMENTED = 501; | ||||
|  | ||||
|     /** | ||||
|      * 系统警告消息 | ||||
|      */ | ||||
|     int WARN = 601; | ||||
| } | ||||
| @ -0,0 +1,54 @@ | ||||
| package org.dromara.common.core.constant; | ||||
|  | ||||
| import cn.hutool.core.lang.RegexPool; | ||||
|  | ||||
| /** | ||||
|  * 常用正则表达式字符串 | ||||
|  * <p> | ||||
|  * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/ | ||||
|  * | ||||
|  * @author Feng | ||||
|  */ | ||||
| public interface RegexConstants extends RegexPool { | ||||
|  | ||||
|     /** | ||||
|      * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) | ||||
|      */ | ||||
|     String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; | ||||
|  | ||||
|     /** | ||||
|      * 权限标识必须符合 tool:build:list 格式,或者空字符串 | ||||
|      */ | ||||
|     String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$"; | ||||
|  | ||||
|     /** | ||||
|      * 身份证号码(后6位) | ||||
|      */ | ||||
|     String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"; | ||||
|  | ||||
|     /** | ||||
|      * QQ号码 | ||||
|      */ | ||||
|     String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$"; | ||||
|  | ||||
|     /** | ||||
|      * 邮政编码 | ||||
|      */ | ||||
|     String POSTAL_CODE = "^[1-9]\\d{5}$"; | ||||
|  | ||||
|     /** | ||||
|      * 注册账号 | ||||
|      */ | ||||
|     String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$"; | ||||
|  | ||||
|     /** | ||||
|      * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 | ||||
|      */ | ||||
|     String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"; | ||||
|  | ||||
|     /** | ||||
|      * 通用状态(0表示正常,1表示停用) | ||||
|      */ | ||||
|     String STATUS = "^[01]$"; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,75 @@ | ||||
| package org.dromara.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; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| package org.dromara.common.core.constant; | ||||
|  | ||||
| /** | ||||
|  * 租户常量信息 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| public interface TenantConstants { | ||||
|  | ||||
|     /** | ||||
|      * 超级管理员ID | ||||
|      */ | ||||
|     Long SUPER_ADMIN_ID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 超级管理员角色 roleKey | ||||
|      */ | ||||
|     String SUPER_ADMIN_ROLE_KEY = "superadmin"; | ||||
|  | ||||
|     /** | ||||
|      * 租户管理员角色 roleKey | ||||
|      */ | ||||
|     String TENANT_ADMIN_ROLE_KEY = "admin"; | ||||
|  | ||||
|     /** | ||||
|      * 租户管理员角色名称 | ||||
|      */ | ||||
|     String TENANT_ADMIN_ROLE_NAME = "管理员"; | ||||
|  | ||||
|     /** | ||||
|      * 默认租户ID | ||||
|      */ | ||||
|     String DEFAULT_TENANT_ID = "000000"; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,110 @@ | ||||
| package org.dromara.common.core.domain; | ||||
|  | ||||
| import org.dromara.common.core.constant.HttpStatus; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 响应信息主体 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class R<T> implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 成功 | ||||
|      */ | ||||
|     public static final int SUCCESS = 200; | ||||
|  | ||||
|     /** | ||||
|      * 失败 | ||||
|      */ | ||||
|     public static final int FAIL = 500; | ||||
|  | ||||
|     private int code; | ||||
|  | ||||
|     private String msg; | ||||
|  | ||||
|     private T data; | ||||
|  | ||||
|     public static <T> R<T> ok() { | ||||
|         return restResult(null, SUCCESS, "操作成功"); | ||||
|     } | ||||
|  | ||||
|     public static <T> R<T> ok(T data) { | ||||
|         return restResult(data, SUCCESS, "操作成功"); | ||||
|     } | ||||
|  | ||||
|     public static <T> R<T> ok(String msg) { | ||||
|         return restResult(null, SUCCESS, msg); | ||||
|     } | ||||
|  | ||||
|     public static <T> R<T> ok(String msg, T data) { | ||||
|         return restResult(data, SUCCESS, msg); | ||||
|     } | ||||
|  | ||||
|     public static <T> R<T> fail() { | ||||
|         return restResult(null, FAIL, "操作失败"); | ||||
|     } | ||||
|  | ||||
|     public static <T> R<T> fail(String msg) { | ||||
|         return restResult(null, FAIL, msg); | ||||
|     } | ||||
|  | ||||
|     public static <T> R<T> fail(T data) { | ||||
|         return restResult(data, FAIL, "操作失败"); | ||||
|     } | ||||
|  | ||||
|     public static <T> R<T> fail(String msg, T data) { | ||||
|         return restResult(data, FAIL, msg); | ||||
|     } | ||||
|  | ||||
|     public static <T> R<T> fail(int code, String msg) { | ||||
|         return restResult(null, code, msg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 返回警告消息 | ||||
|      * | ||||
|      * @param msg 返回内容 | ||||
|      * @return 警告消息 | ||||
|      */ | ||||
|     public static <T> R<T> warn(String msg) { | ||||
|         return restResult(null, HttpStatus.WARN, msg); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 返回警告消息 | ||||
|      * | ||||
|      * @param msg 返回内容 | ||||
|      * @param data 数据对象 | ||||
|      * @return 警告消息 | ||||
|      */ | ||||
|     public static <T> R<T> warn(String msg, T data) { | ||||
|         return restResult(data, HttpStatus.WARN, msg); | ||||
|     } | ||||
|  | ||||
|     private static <T> R<T> restResult(T data, int code, String msg) { | ||||
|         R<T> r = new R<>(); | ||||
|         r.setCode(code); | ||||
|         r.setData(data); | ||||
|         r.setMsg(msg); | ||||
|         return r; | ||||
|     } | ||||
|  | ||||
|     public static <T> Boolean isError(R<T> ret) { | ||||
|         return !isSuccess(ret); | ||||
|     } | ||||
|  | ||||
|     public static <T> Boolean isSuccess(R<T> ret) { | ||||
|         return R.SUCCESS == ret.getCode(); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,71 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
|  | ||||
| /** | ||||
|  * 办理任务请求对象 | ||||
|  * | ||||
|  * @author may | ||||
|  */ | ||||
| @Data | ||||
| public class CompleteTaskDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 任务id | ||||
|      */ | ||||
|     private Long taskId; | ||||
|  | ||||
|     /** | ||||
|      * 附件id | ||||
|      */ | ||||
|     private String fileId; | ||||
|  | ||||
|     /** | ||||
|      * 抄送人员 | ||||
|      */ | ||||
|     private List<FlowCopyDTO> flowCopyList; | ||||
|  | ||||
|     /** | ||||
|      * 消息类型 | ||||
|      */ | ||||
|     private List<String> messageType; | ||||
|  | ||||
|     /** | ||||
|      * 办理意见 | ||||
|      */ | ||||
|     private String message; | ||||
|  | ||||
|     /** | ||||
|      * 消息通知 | ||||
|      */ | ||||
|     private String notice; | ||||
|  | ||||
|     /** | ||||
|      * 流程变量 | ||||
|      */ | ||||
|     private Map<String, Object> variables; | ||||
|  | ||||
|     /** | ||||
|      * 扩展变量(此处为逗号分隔的ossId) | ||||
|      */ | ||||
|     private String ext; | ||||
|  | ||||
|     public Map<String, Object> getVariables() { | ||||
|         if (variables == null) { | ||||
|             return new HashMap<>(16); | ||||
|         } | ||||
|         variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); | ||||
|         return variables; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,37 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 部门 | ||||
|  * | ||||
|  * @author AprilWind | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class DeptDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 部门ID | ||||
|      */ | ||||
|     private Long deptId; | ||||
|  | ||||
|     /** | ||||
|      * 父部门ID | ||||
|      */ | ||||
|     private Long parentId; | ||||
|  | ||||
|     /** | ||||
|      * 部门名称 | ||||
|      */ | ||||
|     private String deptName; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * 抄送 | ||||
|  * | ||||
|  * @author may | ||||
|  */ | ||||
| @Data | ||||
| public class FlowCopyDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 用户id | ||||
|      */ | ||||
|     private Long userId; | ||||
|  | ||||
|     /** | ||||
|      * 用户名称 | ||||
|      */ | ||||
|     private String userName; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,46 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * OSS对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class OssDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 对象存储主键 | ||||
|      */ | ||||
|     private Long ossId; | ||||
|  | ||||
|     /** | ||||
|      * 文件名 | ||||
|      */ | ||||
|     private String fileName; | ||||
|  | ||||
|     /** | ||||
|      * 原名 | ||||
|      */ | ||||
|     private String originalName; | ||||
|  | ||||
|     /** | ||||
|      * 文件后缀名 | ||||
|      */ | ||||
|     private String fileSuffix; | ||||
|  | ||||
|     /** | ||||
|      * URL地址 | ||||
|      */ | ||||
|     private String url; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,46 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 岗位 | ||||
|  * | ||||
|  * @author AprilWind | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class PostDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 岗位ID | ||||
|      */ | ||||
|     private Long postId; | ||||
|  | ||||
|     /** | ||||
|      * 部门id | ||||
|      */ | ||||
|     private Long deptId; | ||||
|  | ||||
|     /** | ||||
|      * 岗位编码 | ||||
|      */ | ||||
|     private String postCode; | ||||
|  | ||||
|     /** | ||||
|      * 岗位名称 | ||||
|      */ | ||||
|     private String postName; | ||||
|  | ||||
|     /** | ||||
|      * 岗位类别编码 | ||||
|      */ | ||||
|     private String postCategory; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,42 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 角色 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class RoleDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 角色ID | ||||
|      */ | ||||
|     private Long roleId; | ||||
|  | ||||
|     /** | ||||
|      * 角色名称 | ||||
|      */ | ||||
|     private String roleName; | ||||
|  | ||||
|     /** | ||||
|      * 角色权限 | ||||
|      */ | ||||
|     private String roleKey; | ||||
|  | ||||
|     /** | ||||
|      * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) | ||||
|      */ | ||||
|     private String dataScope; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,45 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
|  | ||||
| /** | ||||
|  * 启动流程对象 | ||||
|  * | ||||
|  * @author may | ||||
|  */ | ||||
| @Data | ||||
| public class StartProcessDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 业务唯一值id | ||||
|      */ | ||||
|     private String businessId; | ||||
|  | ||||
|     /** | ||||
|      * 流程定义编码 | ||||
|      */ | ||||
|     private String flowCode; | ||||
|  | ||||
|     /** | ||||
|      * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} | ||||
|      */ | ||||
|     private Map<String, Object> variables; | ||||
|  | ||||
|     public Map<String, Object> getVariables() { | ||||
|         if (variables == null) { | ||||
|             return new HashMap<>(16); | ||||
|         } | ||||
|         variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); | ||||
|         return variables; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 启动流程返回对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| public class StartProcessReturnDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 流程实例id | ||||
|      */ | ||||
|     private Long processInstanceId; | ||||
|  | ||||
|     /** | ||||
|      * 任务id | ||||
|      */ | ||||
|     private Long taskId; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,101 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.function.Function; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| /** | ||||
|  * 任务受让人 | ||||
|  * | ||||
|  * @author AprilWind | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class TaskAssigneeDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 总大小 | ||||
|      */ | ||||
|     private Long total = 0L; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      */ | ||||
|     private List<TaskHandler> list; | ||||
|  | ||||
|     public TaskAssigneeDTO(Long total, List<TaskHandler> list) { | ||||
|         this.total = total; | ||||
|         this.list = list; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 将源列表转换为 TaskHandler 列表 | ||||
|      * | ||||
|      * @param <T>              通用类型 | ||||
|      * @param sourceList       待转换的源列表 | ||||
|      * @param storageId        提取 storageId 的函数 | ||||
|      * @param handlerCode      提取 handlerCode 的函数 | ||||
|      * @param handlerName      提取 handlerName 的函数 | ||||
|      * @param groupName        提取 groupName 的函数 | ||||
|      * @param createTimeMapper 提取 createTime 的函数 | ||||
|      * @return 转换后的 TaskHandler 列表 | ||||
|      */ | ||||
|     public static <T> List<TaskHandler> convertToHandlerList( | ||||
|         List<T> sourceList, | ||||
|         Function<T, Long> storageId, | ||||
|         Function<T, String> handlerCode, | ||||
|         Function<T, String> handlerName, | ||||
|         Function<T, Long> groupName, | ||||
|         Function<T, Date> createTimeMapper) { | ||||
|         return sourceList.stream() | ||||
|             .map(item -> new TaskHandler( | ||||
|                 String.valueOf(storageId.apply(item)), | ||||
|                 handlerCode.apply(item), | ||||
|                 handlerName.apply(item), | ||||
|                 groupName != null ? String.valueOf(groupName.apply(item)) : null, | ||||
|                 createTimeMapper.apply(item) | ||||
|             )).collect(Collectors.toList()); | ||||
|     } | ||||
|  | ||||
|     @Data | ||||
|     @NoArgsConstructor | ||||
|     @AllArgsConstructor | ||||
|     public static class TaskHandler { | ||||
|  | ||||
|         /** | ||||
|          * 主键 | ||||
|          */ | ||||
|         private String storageId; | ||||
|  | ||||
|         /** | ||||
|          * 权限编码 | ||||
|          */ | ||||
|         private String handlerCode; | ||||
|  | ||||
|         /** | ||||
|          * 权限名称 | ||||
|          */ | ||||
|         private String handlerName; | ||||
|  | ||||
|         /** | ||||
|          * 权限分组 | ||||
|          */ | ||||
|         private String groupName; | ||||
|  | ||||
|         /** | ||||
|          * 创建时间 | ||||
|          */ | ||||
|         private Date createTime; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,73 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.util.Date; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * 用户 | ||||
|  * | ||||
|  * @author Michelle.Chung | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class UserDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 用户ID | ||||
|      */ | ||||
|     private Long userId; | ||||
|  | ||||
|     /** | ||||
|      * 部门ID | ||||
|      */ | ||||
|     private Long deptId; | ||||
|  | ||||
|     /** | ||||
|      * 用户账号 | ||||
|      */ | ||||
|     private String userName; | ||||
|  | ||||
|     /** | ||||
|      * 用户昵称 | ||||
|      */ | ||||
|     private String nickName; | ||||
|  | ||||
|     /** | ||||
|      * 用户类型(sys_user系统用户) | ||||
|      */ | ||||
|     private String userType; | ||||
|  | ||||
|     /** | ||||
|      * 用户邮箱 | ||||
|      */ | ||||
|     private String email; | ||||
|  | ||||
|     /** | ||||
|      * 手机号码 | ||||
|      */ | ||||
|     private String phonenumber; | ||||
|  | ||||
|     /** | ||||
|      * 用户性别(0男 1女 2未知) | ||||
|      */ | ||||
|     private String sex; | ||||
|  | ||||
|     /** | ||||
|      * 帐号状态(0正常 1停用) | ||||
|      */ | ||||
|     private String status; | ||||
|  | ||||
|     /** | ||||
|      * 创建时间 | ||||
|      */ | ||||
|     private Date createTime; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,72 @@ | ||||
| package org.dromara.common.core.domain.dto; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 当前在线会话 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class UserOnlineDTO implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 会话编号 | ||||
|      */ | ||||
|     private String tokenId; | ||||
|  | ||||
|     /** | ||||
|      * 部门名称 | ||||
|      */ | ||||
|     private String deptName; | ||||
|  | ||||
|     /** | ||||
|      * 用户名称 | ||||
|      */ | ||||
|     private String userName; | ||||
|  | ||||
|     /** | ||||
|      * 客户端 | ||||
|      */ | ||||
|     private String clientKey; | ||||
|  | ||||
|     /** | ||||
|      * 设备类型 | ||||
|      */ | ||||
|     private String deviceType; | ||||
|  | ||||
|     /** | ||||
|      * 登录IP地址 | ||||
|      */ | ||||
|     private String ipaddr; | ||||
|  | ||||
|     /** | ||||
|      * 登录地址 | ||||
|      */ | ||||
|     private String loginLocation; | ||||
|  | ||||
|     /** | ||||
|      * 浏览器类型 | ||||
|      */ | ||||
|     private String browser; | ||||
|  | ||||
|     /** | ||||
|      * 操作系统 | ||||
|      */ | ||||
|     private String os; | ||||
|  | ||||
|     /** | ||||
|      * 登录时间 | ||||
|      */ | ||||
|     private Long loginTime; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,34 @@ | ||||
| package org.dromara.common.core.domain.event; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 删除流程监听 | ||||
|  * | ||||
|  * @author AprilWind | ||||
|  */ | ||||
| @Data | ||||
| public class ProcessDeleteEvent implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 租户ID | ||||
|      */ | ||||
|     private String tenantId; | ||||
|  | ||||
|     /** | ||||
|      * 流程定义编码 | ||||
|      */ | ||||
|     private String flowCode; | ||||
|  | ||||
|     /** | ||||
|      * 业务id | ||||
|      */ | ||||
|     private String businessId; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,50 @@ | ||||
| package org.dromara.common.core.domain.event; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.util.Map; | ||||
|  | ||||
| /** | ||||
|  * 总体流程监听 | ||||
|  * | ||||
|  * @author may | ||||
|  */ | ||||
| @Data | ||||
| public class ProcessEvent implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 租户ID | ||||
|      */ | ||||
|     private String tenantId; | ||||
|  | ||||
|     /** | ||||
|      * 流程定义编码 | ||||
|      */ | ||||
|     private String flowCode; | ||||
|  | ||||
|     /** | ||||
|      * 业务id | ||||
|      */ | ||||
|     private String businessId; | ||||
|  | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     private String status; | ||||
|  | ||||
|     /** | ||||
|      * 办理参数 | ||||
|      */ | ||||
|     private Map<String, Object> params; | ||||
|  | ||||
|     /** | ||||
|      * 当为true时为申请人节点办理 | ||||
|      */ | ||||
|     private boolean submit; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| package org.dromara.common.core.domain.event; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 流程办理监听 | ||||
|  * | ||||
|  * @author may | ||||
|  */ | ||||
| @Data | ||||
| public class ProcessTaskEvent implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 租户ID | ||||
|      */ | ||||
|     private String tenantId; | ||||
|  | ||||
|     /** | ||||
|      * 流程定义编码 | ||||
|      */ | ||||
|     private String flowCode; | ||||
|  | ||||
|     /** | ||||
|      * 审批节点编码 | ||||
|      */ | ||||
|     private String nodeCode; | ||||
|  | ||||
|     /** | ||||
|      * 任务id | ||||
|      */ | ||||
|     private Long taskId; | ||||
|  | ||||
|     /** | ||||
|      * 业务id | ||||
|      */ | ||||
|     private String businessId; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import jakarta.validation.constraints.Email; | ||||
| import jakarta.validation.constraints.NotBlank; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
|  | ||||
| /** | ||||
|  * 邮件登录对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| public class EmailLoginBody extends LoginBody { | ||||
|  | ||||
|     /** | ||||
|      * 邮箱 | ||||
|      */ | ||||
|     @NotBlank(message = "{user.email.not.blank}") | ||||
|     @Email(message = "{user.email.not.valid}") | ||||
|     private String email; | ||||
|  | ||||
|     /** | ||||
|      * 邮箱code | ||||
|      */ | ||||
|     @NotBlank(message = "{email.code.not.blank}") | ||||
|     private String emailCode; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import jakarta.validation.constraints.NotBlank; | ||||
| import lombok.Data; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 用户登录对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| public class LoginBody implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 客户端id | ||||
|      */ | ||||
|     @NotBlank(message = "{auth.clientid.not.blank}") | ||||
|     private String clientId; | ||||
|  | ||||
|     /** | ||||
|      * 授权类型 | ||||
|      */ | ||||
|     @NotBlank(message = "{auth.grant.type.not.blank}") | ||||
|     private String grantType; | ||||
|  | ||||
|     /** | ||||
|      * 租户ID | ||||
|      */ | ||||
|     private String tenantId; | ||||
|  | ||||
|     /** | ||||
|      * 验证码 | ||||
|      */ | ||||
|     private String code; | ||||
|  | ||||
|     /** | ||||
|      * 唯一标识 | ||||
|      */ | ||||
|     private String uuid; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,148 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.dromara.common.core.domain.dto.PostDTO; | ||||
| import org.dromara.common.core.domain.dto.RoleDTO; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| /** | ||||
|  * 登录用户身份权限 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class LoginUser implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 租户ID | ||||
|      */ | ||||
|     private String tenantId; | ||||
|  | ||||
|     /** | ||||
|      * 用户ID | ||||
|      */ | ||||
|     private Long userId; | ||||
|  | ||||
|     /** | ||||
|      * 部门ID | ||||
|      */ | ||||
|     private Long deptId; | ||||
|  | ||||
|     /** | ||||
|      * 部门类别编码 | ||||
|      */ | ||||
|     private String deptCategory; | ||||
|  | ||||
|     /** | ||||
|      * 部门名 | ||||
|      */ | ||||
|     private String deptName; | ||||
|  | ||||
|     /** | ||||
|      * 用户唯一标识 | ||||
|      */ | ||||
|     private String token; | ||||
|  | ||||
|     /** | ||||
|      * 用户类型 | ||||
|      */ | ||||
|     private String userType; | ||||
|  | ||||
|     /** | ||||
|      * 登录时间 | ||||
|      */ | ||||
|     private Long loginTime; | ||||
|  | ||||
|     /** | ||||
|      * 过期时间 | ||||
|      */ | ||||
|     private Long expireTime; | ||||
|  | ||||
|     /** | ||||
|      * 登录IP地址 | ||||
|      */ | ||||
|     private String ipaddr; | ||||
|  | ||||
|     /** | ||||
|      * 登录地点 | ||||
|      */ | ||||
|     private String loginLocation; | ||||
|  | ||||
|     /** | ||||
|      * 浏览器类型 | ||||
|      */ | ||||
|     private String browser; | ||||
|  | ||||
|     /** | ||||
|      * 操作系统 | ||||
|      */ | ||||
|     private String os; | ||||
|  | ||||
|     /** | ||||
|      * 菜单权限 | ||||
|      */ | ||||
|     private Set<String> menuPermission; | ||||
|  | ||||
|     /** | ||||
|      * 角色权限 | ||||
|      */ | ||||
|     private Set<String> rolePermission; | ||||
|  | ||||
|     /** | ||||
|      * 用户名 | ||||
|      */ | ||||
|     private String username; | ||||
|  | ||||
|     /** | ||||
|      * 用户昵称 | ||||
|      */ | ||||
|     private String nickname; | ||||
|  | ||||
|     /** | ||||
|      * 角色对象 | ||||
|      */ | ||||
|     private List<RoleDTO> roles; | ||||
|  | ||||
|     /** | ||||
|      * 岗位对象 | ||||
|      */ | ||||
|     private List<PostDTO> posts; | ||||
|  | ||||
|     /** | ||||
|      * 数据权限 当前角色ID | ||||
|      */ | ||||
|     private Long roleId; | ||||
|  | ||||
|     /** | ||||
|      * 客户端 | ||||
|      */ | ||||
|     private String clientKey; | ||||
|  | ||||
|     /** | ||||
|      * 设备类型 | ||||
|      */ | ||||
|     private String deviceType; | ||||
|  | ||||
|     /** | ||||
|      * 获取登录id | ||||
|      */ | ||||
|     public String getLoginId() { | ||||
|         if (userType == null) { | ||||
|             throw new IllegalArgumentException("用户类型不能为空"); | ||||
|         } | ||||
|         if (userId == null) { | ||||
|             throw new IllegalArgumentException("用户ID不能为空"); | ||||
|         } | ||||
|         return userType + ":" + userId; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import jakarta.validation.constraints.NotBlank; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import org.hibernate.validator.constraints.Length; | ||||
|  | ||||
| /** | ||||
|  * 密码登录对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| public class PasswordLoginBody extends LoginBody { | ||||
|  | ||||
|     /** | ||||
|      * 用户名 | ||||
|      */ | ||||
|     @NotBlank(message = "{user.username.not.blank}") | ||||
|     @Length(min = 2, max = 20, message = "{user.username.length.valid}") | ||||
|     private String username; | ||||
|  | ||||
|     /** | ||||
|      * 用户密码 | ||||
|      */ | ||||
|     @NotBlank(message = "{user.password.not.blank}") | ||||
|     @Length(min = 5, max = 20, message = "{user.password.length.valid}") | ||||
|     private String password; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import jakarta.validation.constraints.NotBlank; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import org.hibernate.validator.constraints.Length; | ||||
|  | ||||
| /** | ||||
|  * 用户注册对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| public class RegisterBody extends LoginBody { | ||||
|  | ||||
|     /** | ||||
|      * 用户名 | ||||
|      */ | ||||
|     @NotBlank(message = "{user.username.not.blank}") | ||||
|     @Length(min = 2, max = 20, message = "{user.username.length.valid}") | ||||
|     private String username; | ||||
|  | ||||
|     /** | ||||
|      * 用户密码 | ||||
|      */ | ||||
|     @NotBlank(message = "{user.password.not.blank}") | ||||
|     @Length(min = 5, max = 20, message = "{user.password.length.valid}") | ||||
|     private String password; | ||||
|  | ||||
|     private String userType; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,29 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import jakarta.validation.constraints.NotBlank; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
|  | ||||
| /** | ||||
|  * 短信登录对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| public class SmsLoginBody extends LoginBody { | ||||
|  | ||||
|     /** | ||||
|      * 手机号 | ||||
|      */ | ||||
|     @NotBlank(message = "{user.phonenumber.not.blank}") | ||||
|     private String phonenumber; | ||||
|  | ||||
|     /** | ||||
|      * 短信code | ||||
|      */ | ||||
|     @NotBlank(message = "{sms.code.not.blank}") | ||||
|     private String smsCode; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,35 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import jakarta.validation.constraints.NotBlank; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
|  | ||||
| /** | ||||
|  * 三方登录对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| public class SocialLoginBody extends LoginBody { | ||||
|  | ||||
|     /** | ||||
|      * 第三方登录平台 | ||||
|      */ | ||||
|     @NotBlank(message = "{social.source.not.blank}") | ||||
|     private String source; | ||||
|  | ||||
|     /** | ||||
|      * 第三方登录code | ||||
|      */ | ||||
|     @NotBlank(message = "{social.code.not.blank}") | ||||
|     private String socialCode; | ||||
|  | ||||
|     /** | ||||
|      * 第三方登录socialState | ||||
|      */ | ||||
|     @NotBlank(message = "{social.state.not.blank}") | ||||
|     private String socialState; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,56 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * 任务受让人 | ||||
|  * | ||||
|  * @author AprilWind | ||||
|  */ | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| public class TaskAssigneeBody implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 权限编码 | ||||
|      */ | ||||
|     private String handlerCode; | ||||
|  | ||||
|     /** | ||||
|      * 权限名称 | ||||
|      */ | ||||
|     private String handlerName; | ||||
|  | ||||
|     /** | ||||
|      * 权限分组 | ||||
|      */ | ||||
|     private String groupId; | ||||
|  | ||||
|     /** | ||||
|      * 开始时间 | ||||
|      */ | ||||
|     private String beginTime; | ||||
|  | ||||
|     /** | ||||
|      * 结束时间 | ||||
|      */ | ||||
|     private String endTime; | ||||
|  | ||||
|     /** | ||||
|      * 当前页 | ||||
|      */ | ||||
|     private Integer pageNum = 1; | ||||
|  | ||||
|     /** | ||||
|      * 每页显示条数 | ||||
|      */ | ||||
|     private Integer pageSize = 10; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,28 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import jakarta.validation.constraints.NotBlank; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
|  | ||||
| /** | ||||
|  * 三方登录对象 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
|  | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| public class XcxLoginBody extends LoginBody { | ||||
|  | ||||
|     /** | ||||
|      * 小程序id(多个小程序时使用) | ||||
|      */ | ||||
|     private String appid; | ||||
|  | ||||
|     /** | ||||
|      * 小程序code | ||||
|      */ | ||||
|     @NotBlank(message = "{xcx.code.not.blank}") | ||||
|     private String xcxCode; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| package org.dromara.common.core.domain.model; | ||||
|  | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| /** | ||||
|  * 小程序登录用户身份权限 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| public class XcxLoginUser extends LoginUser { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * openid | ||||
|      */ | ||||
|     private String openid; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,215 @@ | ||||
| package org.dromara.common.core.enums; | ||||
|  | ||||
| import cn.hutool.core.util.StrUtil; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Getter; | ||||
| import org.dromara.common.core.exception.ServiceException; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.function.Function; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| /** | ||||
|  * 业务状态枚举 | ||||
|  * | ||||
|  * @author may | ||||
|  */ | ||||
| @Getter | ||||
| @AllArgsConstructor | ||||
| public enum BusinessStatusEnum { | ||||
|  | ||||
|     /** | ||||
|      * 已撤销 | ||||
|      */ | ||||
|     CANCEL("cancel", "已撤销"), | ||||
|  | ||||
|     /** | ||||
|      * 草稿 | ||||
|      */ | ||||
|     DRAFT("draft", "草稿"), | ||||
|  | ||||
|     /** | ||||
|      * 待审核 | ||||
|      */ | ||||
|     WAITING("waiting", "待审核"), | ||||
|  | ||||
|     /** | ||||
|      * 已完成 | ||||
|      */ | ||||
|     FINISH("finish", "已完成"), | ||||
|  | ||||
|     /** | ||||
|      * 已作废 | ||||
|      */ | ||||
|     INVALID("invalid", "已作废"), | ||||
|  | ||||
|     /** | ||||
|      * 已退回 | ||||
|      */ | ||||
|     BACK("back", "已退回"), | ||||
|  | ||||
|     /** | ||||
|      * 已终止 | ||||
|      */ | ||||
|     TERMINATION("termination", "已终止"); | ||||
|  | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     private final String status; | ||||
|  | ||||
|     /** | ||||
|      * 描述 | ||||
|      */ | ||||
|     private final String desc; | ||||
|  | ||||
|     private static final Map<String, BusinessStatusEnum> STATUS_MAP = Arrays.stream(BusinessStatusEnum.values()) | ||||
|         .collect(Collectors.toConcurrentMap(BusinessStatusEnum::getStatus, Function.identity())); | ||||
|  | ||||
|     /** | ||||
|      * 根据状态获取对应的 BusinessStatusEnum 枚举 | ||||
|      * | ||||
|      * @param status 业务状态码 | ||||
|      * @return 对应的 BusinessStatusEnum 枚举,如果找不到则返回 null | ||||
|      */ | ||||
|     public static BusinessStatusEnum getByStatus(String status) { | ||||
|         // 使用 STATUS_MAP 获取对应的枚举,若找不到则返回 null | ||||
|         return STATUS_MAP.get(status); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 根据状态获取对应的业务状态描述信息 | ||||
|      * | ||||
|      * @param status 业务状态码 | ||||
|      * @return 返回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串 | ||||
|      */ | ||||
|     public static String findByStatus(String status) { | ||||
|         if (StringUtils.isBlank(status)) { | ||||
|             return StrUtil.EMPTY; | ||||
|         } | ||||
|         BusinessStatusEnum statusEnum = STATUS_MAP.get(status); | ||||
|         return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 判断是否为指定的状态之一:草稿、已撤销或已退回 | ||||
|      * | ||||
|      * @param status 要检查的状态 | ||||
|      * @return 如果状态为草稿、已撤销或已退回之一,则返回 true;否则返回 false | ||||
|      */ | ||||
|     public static boolean isDraftOrCancelOrBack(String status) { | ||||
|         return DRAFT.status.equals(status) || CANCEL.status.equals(status) || BACK.status.equals(status); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 判断是否为撤销,退回,作废,终止 | ||||
|      * | ||||
|      * @param status status | ||||
|      * @return 结果 | ||||
|      */ | ||||
|     public static boolean initialState(String status) { | ||||
|         return CANCEL.status.equals(status) || BACK.status.equals(status) || INVALID.status.equals(status) || TERMINATION.status.equals(status); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取运行中的实例状态列表 | ||||
|      * | ||||
|      * @return 包含运行中实例状态的不可变列表 | ||||
|      * (包含 DRAFT、WAITING、BACK 和 CANCEL 状态) | ||||
|      */ | ||||
|     public static List<String> runningStatus() { | ||||
|         return Arrays.asList(DRAFT.status, WAITING.status, BACK.status, CANCEL.status); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 获取结束实例的状态列表 | ||||
|      * | ||||
|      * @return 包含结束实例状态的不可变列表 | ||||
|      * (包含 FINISH、INVALID 和 TERMINATION 状态) | ||||
|      */ | ||||
|     public static List<String> finishStatus() { | ||||
|         return Arrays.asList(FINISH.status, INVALID.status, TERMINATION.status); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 启动流程校验 | ||||
|      * | ||||
|      * @param status 状态 | ||||
|      */ | ||||
|     public static void checkStartStatus(String status) { | ||||
|         if (WAITING.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已提交过申请,正在审批中!"); | ||||
|         } else if (FINISH.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已完成申请!"); | ||||
|         } else if (INVALID.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已作废!"); | ||||
|         } else if (TERMINATION.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已终止!"); | ||||
|         } else if (StringUtils.isBlank(status)) { | ||||
|             throw new ServiceException("流程状态为空!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 撤销流程校验 | ||||
|      * | ||||
|      * @param status 状态 | ||||
|      */ | ||||
|     public static void checkCancelStatus(String status) { | ||||
|         if (CANCEL.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已撤销!"); | ||||
|         } else if (FINISH.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已完成申请!"); | ||||
|         } else if (INVALID.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已作废!"); | ||||
|         } else if (TERMINATION.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已终止!"); | ||||
|         } else if (BACK.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已退回!"); | ||||
|         } else if (StringUtils.isBlank(status)) { | ||||
|             throw new ServiceException("流程状态为空!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 驳回流程校验 | ||||
|      * | ||||
|      * @param status 状态 | ||||
|      */ | ||||
|     public static void checkBackStatus(String status) { | ||||
|         if (BACK.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已退回!"); | ||||
|         } else if (FINISH.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已完成申请!"); | ||||
|         } else if (INVALID.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已作废!"); | ||||
|         } else if (TERMINATION.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已终止!"); | ||||
|         } else if (CANCEL.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已撤销!"); | ||||
|         } else if (StringUtils.isBlank(status)) { | ||||
|             throw new ServiceException("流程状态为空!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 作废,终止流程校验 | ||||
|      * | ||||
|      * @param status 状态 | ||||
|      */ | ||||
|     public static void checkInvalidStatus(String status) { | ||||
|         if (FINISH.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已完成申请!"); | ||||
|         } else if (INVALID.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已作废!"); | ||||
|         } else if (TERMINATION.getStatus().equals(status)) { | ||||
|             throw new ServiceException("该单据已终止!"); | ||||
|         } else if (StringUtils.isBlank(status)) { | ||||
|             throw new ServiceException("流程状态为空!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,37 @@ | ||||
| package org.dromara.common.core.enums; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Getter; | ||||
|  | ||||
| /** | ||||
|  * 设备类型 | ||||
|  * 针对一套 用户体系 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Getter | ||||
| @AllArgsConstructor | ||||
| public enum DeviceType { | ||||
|  | ||||
|     /** | ||||
|      * pc端 | ||||
|      */ | ||||
|     PC("pc"), | ||||
|  | ||||
|     /** | ||||
|      * app端 | ||||
|      */ | ||||
|     APP("app"), | ||||
|  | ||||
|     /** | ||||
|      * 小程序端 | ||||
|      */ | ||||
|     XCX("xcx"), | ||||
|  | ||||
|     /** | ||||
|      * social第三方端 | ||||
|      */ | ||||
|     SOCIAL("social"); | ||||
|  | ||||
|     private final String device; | ||||
| } | ||||
| @ -0,0 +1,146 @@ | ||||
| package org.dromara.common.core.enums; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Getter; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
|  | ||||
| /* | ||||
|  * 日期格式 | ||||
|  * "yyyy":4位数的年份,例如:2023年表示为"2023"。 | ||||
|  * "yy":2位数的年份,例如:2023年表示为"23"。 | ||||
|  * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。 | ||||
|  * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。 | ||||
|  * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。 | ||||
|  * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。 | ||||
|  * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。 | ||||
|  * "E":星期的缩写,例如:星期三表示为"Wed"。 | ||||
|  * "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。 | ||||
|  * 时间格式 | ||||
|  * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。 | ||||
|  * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。 | ||||
|  * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。 | ||||
|  * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。 | ||||
|  * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。 | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * 日期格式与时间格式枚举 | ||||
|  */ | ||||
| @Getter | ||||
| @AllArgsConstructor | ||||
| public enum FormatsType { | ||||
|  | ||||
|     /** | ||||
|      * 例如:2023年表示为"23" | ||||
|      */ | ||||
|     YY("yy"), | ||||
|  | ||||
|     /** | ||||
|      * 例如:2023年表示为"2023" | ||||
|      */ | ||||
|     YYYY("yyyy"), | ||||
|  | ||||
|     /** | ||||
|      * 例例如,2023年7月可以表示为 "2023-07" | ||||
|      */ | ||||
|     YYYY_MM("yyyy-MM"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22" | ||||
|      */ | ||||
|     YYYY_MM_DD("yyyy-MM-dd"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30" | ||||
|      */ | ||||
|     YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45" | ||||
|      */ | ||||
|     YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"), | ||||
|  | ||||
|     /** | ||||
|      * 例如:下午3点30分45秒,表示为 "15:30:45" | ||||
|      */ | ||||
|     HH_MM_SS("HH:mm:ss"), | ||||
|  | ||||
|     /** | ||||
|      * 例例如,2023年7月可以表示为 "2023/07" | ||||
|      */ | ||||
|     YYYY_MM_SLASH("yyyy/MM"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22" | ||||
|      */ | ||||
|     YYYY_MM_DD_SLASH("yyyy/MM/dd"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" | ||||
|      */ | ||||
|     YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45" | ||||
|      */ | ||||
|     YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"), | ||||
|  | ||||
|     /** | ||||
|      * 例例如,2023年7月可以表示为 "2023.07" | ||||
|      */ | ||||
|     YYYY_MM_DOT("yyyy.MM"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22" | ||||
|      */ | ||||
|     YYYY_MM_DD_DOT("yyyy.MM.dd"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30" | ||||
|      */ | ||||
|     YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45" | ||||
|      */ | ||||
|     YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,2023年7月可以表示为 "202307" | ||||
|      */ | ||||
|     YYYYMM("yyyyMM"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,2023年7月22日可以表示为 "20230722" | ||||
|      */ | ||||
|     YYYYMMDD("yyyyMMdd"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,2023年7月22日下午3点可以表示为 "2023072215" | ||||
|      */ | ||||
|     YYYYMMDDHH("yyyyMMddHH"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,2023年7月22日下午3点30分可以表示为 "202307221530" | ||||
|      */ | ||||
|     YYYYMMDDHHMM("yyyyMMddHHmm"), | ||||
|  | ||||
|     /** | ||||
|      * 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045" | ||||
|      */ | ||||
|     YYYYMMDDHHMMSS("yyyyMMddHHmmss"); | ||||
|  | ||||
|     /** | ||||
|      * 时间格式 | ||||
|      */ | ||||
|     private final String timeFormat; | ||||
|  | ||||
|     public static FormatsType getFormatsType(String str) { | ||||
|         for (FormatsType value : values()) { | ||||
|             if (StringUtils.contains(str, value.getTimeFormat())) { | ||||
|                 return value; | ||||
|             } | ||||
|         } | ||||
|         throw new RuntimeException("'FormatsType' not found By " + str); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| package org.dromara.common.core.enums; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Getter; | ||||
|  | ||||
| /** | ||||
|  * 登录类型 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Getter | ||||
| @AllArgsConstructor | ||||
| public enum LoginType { | ||||
|  | ||||
|     /** | ||||
|      * 密码登录 | ||||
|      */ | ||||
|     PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"), | ||||
|  | ||||
|     /** | ||||
|      * 短信登录 | ||||
|      */ | ||||
|     SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), | ||||
|  | ||||
|     /** | ||||
|      * 邮箱登录 | ||||
|      */ | ||||
|     EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"), | ||||
|  | ||||
|     /** | ||||
|      * 小程序登录 | ||||
|      */ | ||||
|     XCX("", ""); | ||||
|  | ||||
|     /** | ||||
|      * 登录重试超出限制提示 | ||||
|      */ | ||||
|     final String retryLimitExceed; | ||||
|  | ||||
|     /** | ||||
|      * 登录重试限制计数提示 | ||||
|      */ | ||||
|     final String retryLimitCount; | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| package org.dromara.common.core.enums; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Getter; | ||||
|  | ||||
| /** | ||||
|  * 用户状态 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Getter | ||||
| @AllArgsConstructor | ||||
| public enum UserStatus { | ||||
|     /** | ||||
|      * 正常 | ||||
|      */ | ||||
|     OK("0", "正常"), | ||||
|     /** | ||||
|      * 停用 | ||||
|      */ | ||||
|     DISABLE("1", "停用"), | ||||
|     /** | ||||
|      * 删除 | ||||
|      */ | ||||
|     DELETED("2", "删除"); | ||||
|  | ||||
|     private final String code; | ||||
|     private final String info; | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,37 @@ | ||||
| package org.dromara.common.core.enums; | ||||
|  | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Getter; | ||||
|  | ||||
| /** | ||||
|  * 设备类型 | ||||
|  * 针对多套 用户体系 | ||||
|  * | ||||
|  * @author Lion Li | ||||
|  */ | ||||
| @Getter | ||||
| @AllArgsConstructor | ||||
| public enum UserType { | ||||
|  | ||||
|     /** | ||||
|      * pc端 | ||||
|      */ | ||||
|     SYS_USER("sys_user"), | ||||
|  | ||||
|     /** | ||||
|      * app端 | ||||
|      */ | ||||
|     APP_USER("app_user"); | ||||
|  | ||||
|     private final String userType; | ||||
|  | ||||
|     public static UserType getUserType(String str) { | ||||
|         for (UserType value : values()) { | ||||
|             if (StringUtils.contains(str, value.getUserType())) { | ||||
|                 return value; | ||||
|             } | ||||
|         } | ||||
|         throw new RuntimeException("'UserType' not found By " + str); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,59 @@ | ||||
| package org.dromara.common.core.exception; | ||||
|  | ||||
| import lombok.*; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| /** | ||||
|  * 业务异常 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public final class ServiceException extends RuntimeException { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 错误码 | ||||
|      */ | ||||
|     private Integer code; | ||||
|  | ||||
|     /** | ||||
|      * 错误提示 | ||||
|      */ | ||||
|     private String message; | ||||
|  | ||||
|     /** | ||||
|      * 错误明细,内部调试错误 | ||||
|      */ | ||||
|     private String detailMessage; | ||||
|  | ||||
|     public ServiceException(String message) { | ||||
|         this.message = message; | ||||
|     } | ||||
|  | ||||
|     public ServiceException(String message, Integer code) { | ||||
|         this.message = message; | ||||
|         this.code = code; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getMessage() { | ||||
|         return message; | ||||
|     } | ||||
|  | ||||
|     public ServiceException setMessage(String message) { | ||||
|         this.message = message; | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public ServiceException setDetailMessage(String detailMessage) { | ||||
|         this.detailMessage = detailMessage; | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,62 @@ | ||||
| package org.dromara.common.core.exception; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| /** | ||||
|  * sse 特制异常 | ||||
|  * | ||||
|  * @author LionLi | ||||
|  */ | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public final class SseException extends RuntimeException { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 错误码 | ||||
|      */ | ||||
|     private Integer code; | ||||
|  | ||||
|     /** | ||||
|      * 错误提示 | ||||
|      */ | ||||
|     private String message; | ||||
|  | ||||
|     /** | ||||
|      * 错误明细,内部调试错误 | ||||
|      */ | ||||
|     private String detailMessage; | ||||
|  | ||||
|     public SseException(String message) { | ||||
|         this.message = message; | ||||
|     } | ||||
|  | ||||
|     public SseException(String message, Integer code) { | ||||
|         this.message = message; | ||||
|         this.code = code; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getMessage() { | ||||
|         return message; | ||||
|     } | ||||
|  | ||||
|     public SseException setMessage(String message) { | ||||
|         this.message = message; | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     public SseException setDetailMessage(String detailMessage) { | ||||
|         this.detailMessage = detailMessage; | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,74 @@ | ||||
| package org.dromara.common.core.exception.base; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import org.dromara.common.core.utils.MessageUtils; | ||||
| import org.dromara.common.core.utils.StringUtils; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| /** | ||||
|  * 基础异常 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class BaseException extends RuntimeException { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 所属模块 | ||||
|      */ | ||||
|     private String module; | ||||
|  | ||||
|     /** | ||||
|      * 错误码 | ||||
|      */ | ||||
|     private String code; | ||||
|  | ||||
|     /** | ||||
|      * 错误码对应的参数 | ||||
|      */ | ||||
|     private Object[] args; | ||||
|  | ||||
|     /** | ||||
|      * 错误消息 | ||||
|      */ | ||||
|     private String defaultMessage; | ||||
|  | ||||
|     public BaseException(String module, String code, Object[] args) { | ||||
|         this(module, code, args, null); | ||||
|     } | ||||
|  | ||||
|     public BaseException(String module, String defaultMessage) { | ||||
|         this(module, null, null, defaultMessage); | ||||
|     } | ||||
|  | ||||
|     public BaseException(String code, Object[] args) { | ||||
|         this(null, code, args, null); | ||||
|     } | ||||
|  | ||||
|     public BaseException(String defaultMessage) { | ||||
|         this(null, null, null, defaultMessage); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getMessage() { | ||||
|         String message = null; | ||||
|         if (!StringUtils.isEmpty(code)) { | ||||
|             message = MessageUtils.message(code, args); | ||||
|         } | ||||
|         if (message == null) { | ||||
|             message = defaultMessage; | ||||
|         } | ||||
|         return message; | ||||
|     } | ||||
|  | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user