commit
23e906fed5
781 changed files with 74702 additions and 0 deletions
@ -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 |
@ -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: | |
|||
[在这里上传图片] |
|||
|
@ -0,0 +1,5 @@ |
|||
blank_issues_enabled: false |
|||
contact_links: |
|||
- name: RuoYi-Vue-Plus 文档中心 |
|||
url: https://plus-doc.dromara.org |
|||
about: 提供 RuoYi-Vue-Plus 搭建使用指南、平台基本开发使用方式、介绍、基础知识和常见问题解答 |
@ -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 |
@ -0,0 +1,7 @@ |
|||
### 更改目的 解决了什么问题(请提交到dev分支) |
|||
|
|||
|
|||
### 改动逻辑 这么写的思路(让作者更好的理解你的意图) |
|||
|
|||
|
|||
### 测试 都做了哪些测试(未经过测试不采纳) |
@ -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 |
@ -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.2.3" /> |
|||
<option name="buildOnly" value="true" /> |
|||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" /> |
|||
</settings> |
|||
</deployment> |
|||
<method v="2" /> |
|||
</configuration> |
|||
</component> |
@ -0,0 +1,12 @@ |
|||
<component name="ProjectRunConfigurationManager"> |
|||
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="演示机"> |
|||
<deployment type="dockerfile"> |
|||
<settings> |
|||
<option name="imageTag" value="ruoyi/ruoyi-server:5.2.3" /> |
|||
<option name="buildOnly" value="true" /> |
|||
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" /> |
|||
</settings> |
|||
</deployment> |
|||
<method v="2" /> |
|||
</configuration> |
|||
</component> |
@ -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.2.3" /> |
|||
<option name="buildOnly" value="true" /> |
|||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" /> |
|||
</settings> |
|||
</deployment> |
|||
<method v="2" /> |
|||
</configuration> |
|||
</component> |
@ -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. |
@ -0,0 +1,182 @@ |
|||
<img src="https://foruda.gitee.com/images/1679673773341074847/178e8451_1766278.png" width="50%" height="50%"> |
|||
<div style="height: 10px; clear: both;"></div> |
|||
|
|||
- - - |
|||
## 平台简介 |
|||
|
|||
[![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus) |
|||
[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus) |
|||
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE) |
|||
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus) |
|||
<br> |
|||
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.2.3-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) |
|||
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.2-blue.svg)]() |
|||
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() |
|||
[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() |
|||
|
|||
> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架) |
|||
|
|||
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br> |
|||
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 |
|||
|
|||
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system) |
|||
|
|||
> 前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui) |
|||
|
|||
> 文档地址: [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> |
|||
[如何成为赞助商 加群联系作者详谈](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) |
|||
|
|||
## 软件架构图 |
|||
|
|||
![Plus部署架构图](https://foruda.gitee.com/images/1678981882624240692/ae2a3f3e_1766278.png "Plus部署架构图.png") |
|||
|
|||
## 如何参与贡献 |
|||
|
|||
[参与贡献的方式 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" /> |
|||
|
|||
## 演示图例 |
|||
|
|||
| | | |
|||
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png "屏幕截图") | |
|||
| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") | |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,496 @@ |
|||
<?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>RuoYi-Vue-Plus多租户管理系统</description> |
|||
|
|||
<properties> |
|||
<revision>5.2.3</revision> |
|||
<spring-boot.version>3.2.11</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.6.0</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.39.0</satoken.version> |
|||
<mybatis-plus.version>3.5.8</mybatis-plus.version> |
|||
<p6spy.version>3.9.1</p6spy.version> |
|||
<hutool.version>5.8.31</hutool.version> |
|||
<spring-boot-admin.version>3.2.3</spring-boot-admin.version> |
|||
<redisson.version>3.37.0</redisson.version> |
|||
<lock4j.version>2.2.7</lock4j.version> |
|||
<dynamic-ds.version>4.3.1</dynamic-ds.version> |
|||
<snailjob.version>1.1.2</snailjob.version> |
|||
<mapstruct-plus.version>1.4.5</mapstruct-plus.version> |
|||
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version> |
|||
<lombok.version>1.18.34</lombok.version> |
|||
<bouncycastle.version>1.76</bouncycastle.version> |
|||
<justauth.version>1.16.6</justauth.version> |
|||
<!-- 离线IP地址定位库 --> |
|||
<ip2region.version>2.7.0</ip2region.version> |
|||
|
|||
<!-- OSS 配置 --> |
|||
<aws.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-20241022</anyline.version> |
|||
<!--工作流配置--> |
|||
<flowable.version>7.0.1</flowable.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.verison>3.11.0</maven-compiler-plugin.verison> |
|||
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version> |
|||
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version> |
|||
</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> |
|||
|
|||
<dependency> |
|||
<groupId>org.flowable</groupId> |
|||
<artifactId>flowable-bom</artifactId> |
|||
<version>${flowable.version}</version> |
|||
<type>pom</type> |
|||
<scope>import</scope> |
|||
</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-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.verison}</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> |
|||
|
|||
|
@ -0,0 +1,26 @@ |
|||
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ |
|||
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds |
|||
#FROM bellsoft/liberica-openjdk-debian:21.0.3-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 |
|||
|
|||
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 |
|||
|
@ -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,18 @@ |
|||
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,234 @@ |
|||
package org.dromara.web.controller; |
|||
|
|||
import cn.dev33.satoken.annotation.SaIgnore; |
|||
import cn.dev33.satoken.exception.NotLoginException; |
|||
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.UserConstants; |
|||
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 (!UserConstants.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); |
|||
} |
|||
|
|||
/** |
|||
* 第三方登录请求 |
|||
* |
|||
* @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); |
|||
} |
|||
|
|||
/** |
|||
* 第三方登录回调业务处理 绑定授权 |
|||
* |
|||
* @param loginBody 请求体 |
|||
* @return 结果 |
|||
*/ |
|||
@PostMapping("/social/callback") |
|||
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) { |
|||
// 获取第三方登录信息
|
|||
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(); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 取消授权 |
|||
* |
|||
* @param socialId socialId |
|||
*/ |
|||
@DeleteMapping(value = "/unlock/{socialId}") |
|||
public R<Void> unlockSocial(@PathVariable Long socialId) { |
|||
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.equals(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,246 @@ |
|||
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.TenantConstants; |
|||
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.enums.TenantStatus; |
|||
import org.dromara.common.core.exception.ServiceException; |
|||
import org.dromara.common.core.exception.user.UserException; |
|||
import org.dromara.common.core.utils.*; |
|||
import org.dromara.common.log.event.LogininforEvent; |
|||
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 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(); |
|||
loginUser.setTenantId(user.getTenantId()); |
|||
loginUser.setUserId(user.getUserId()); |
|||
loginUser.setDeptId(user.getDeptId()); |
|||
loginUser.setUsername(user.getUserName()); |
|||
loginUser.setNickname(user.getNickName()); |
|||
loginUser.setUserType(user.getUserType()); |
|||
loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); |
|||
loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); |
|||
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(user.getUserId()); |
|||
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.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 (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { |
|||
return; |
|||
} |
|||
if (StringUtils.isBlank(tenantId)) { |
|||
throw new TenantException("tenant.number.not.blank"); |
|||
} |
|||
SysTenantVo tenant = tenantService.queryByTenantId(tenantId); |
|||
if (ObjectUtil.isNull(tenant)) { |
|||
log.info("登录租户:{} 不存在.", tenantId); |
|||
throw new TenantException("tenant.not.exists"); |
|||
} else if (TenantStatus.DISABLE.getCode().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.REGISTER, MessageUtils.message("user.jcaptcha.expire")); |
|||
throw new CaptchaExpireException(); |
|||
} |
|||
if (!code.equalsIgnoreCase(captcha)) { |
|||
recordLogininfor(tenantId, username, Constants.REGISTER, 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.domain.model.EmailLoginBody; |
|||
import org.dromara.common.core.domain.model.LoginUser; |
|||
import org.dromara.common.core.enums.LoginType; |
|||
import org.dromara.common.core.enums.UserStatus; |
|||
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 (UserStatus.DISABLE.getCode().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.domain.model.LoginUser; |
|||
import org.dromara.common.core.domain.model.PasswordLoginBody; |
|||
import org.dromara.common.core.enums.LoginType; |
|||
import org.dromara.common.core.enums.UserStatus; |
|||
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 (UserStatus.DISABLE.getCode().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.domain.model.LoginUser; |
|||
import org.dromara.common.core.domain.model.SmsLoginBody; |
|||
import org.dromara.common.core.enums.LoginType; |
|||
import org.dromara.common.core.enums.UserStatus; |
|||
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 (UserStatus.DISABLE.getCode().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.domain.model.LoginUser; |
|||
import org.dromara.common.core.domain.model.SocialLoginBody; |
|||
import org.dromara.common.core.enums.UserStatus; |
|||
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 (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |
|||
log.info("登录用户:{} 已被停用.", ""); |
|||
throw new UserException("user.blocked", ""); |
|||
} |
|||
return user; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,92 @@ |
|||
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 org.dromara.common.core.domain.model.XcxLoginBody; |
|||
import org.dromara.common.core.domain.model.XcxLoginUser; |
|||
import org.dromara.common.core.enums.UserStatus; |
|||
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.SysClient; |
|||
import org.dromara.system.domain.vo.SysClientVo; |
|||
import org.dromara.system.domain.vo.SysUserVo; |
|||
import org.dromara.web.domain.vo.LoginVo; |
|||
import org.dromara.web.service.IAuthStrategy; |
|||
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(); |
|||
|
|||
// todo 以下自行实现
|
|||
// 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
|
|||
String openid = ""; |
|||
// 框架登录不限制从什么表查询 只要最终构建出 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 (UserStatus.DISABLE.getCode().equals(user.getStatus())) { |
|||
log.info("登录用户:{} 已被停用.", openid); |
|||
// todo 用户已被停用 业务逻辑自行实现
|
|||
} |
|||
return user; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,263 @@ |
|||
--- # 监控中心配置 |
|||
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/snail_job.sql `sj_group_config` 表 |
|||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" |
|||
server: |
|||
host: 127.0.0.1 |
|||
port: 17888 |
|||
# 详见 script/sql/snail_job.sql `sj_namespace` 表 |
|||
namespace: ${spring.profiles.active} |
|||
# 随主应用端口飘逸 |
|||
port: 2${server.port} |
|||
# 客户端ip指定 |
|||
host: |
|||
|
|||
--- # 数据源配置 |
|||
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://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: 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: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 |
@ -0,0 +1,265 @@ |
|||
--- # 临时文件存储位置 避免临时文件被系统清理报错 |
|||
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/snail_job.sql `sj_group_config` 表 |
|||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" |
|||
server: |
|||
host: 127.0.0.1 |
|||
port: 17888 |
|||
# 详见 script/sql/snail_job.sql `sj_namespace` 表 |
|||
namespace: ${spring.profiles.active} |
|||
# 随主应用端口飘逸 |
|||
port: 2${server.port} |
|||
# 客户端ip指定 |
|||
host: |
|||
|
|||
--- # 数据源配置 |
|||
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 |
@ -0,0 +1,294 @@ |
|||
# 项目相关配置 |
|||
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: 8080 |
|||
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 |
|||
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 |
|||
# swagger 文档配置 |
|||
- /*/api-docs |
|||
- /*/api-docs/** |
|||
|
|||
# 多租户配置 |
|||
tenant: |
|||
# 是否开启 |
|||
enable: true |
|||
# 排除表 |
|||
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: |
|||
# 多包名使用 例如 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 |
|||
|
|||
# 防止XSS攻击 |
|||
xss: |
|||
# 过滤开关 |
|||
enabled: true |
|||
# 排除链接(多个用逗号分隔) |
|||
excludeUrls: |
|||
- /system/notice |
|||
- /workflow/model/save |
|||
- /workflow/model/editModelXml |
|||
|
|||
# 全局线程池相关配置 |
|||
# 如使用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: true |
|||
path: /resource/sse |
|||
|
|||
--- # websocket |
|||
websocket: |
|||
# 如果关闭 需要和前端开关一起关闭 |
|||
enabled: false |
|||
# 路径 |
|||
path: /resource/websocket |
|||
# 设置访问源地址 |
|||
allowedOrigins: '*' |
|||
|
|||
--- #flowable配置 |
|||
flowable: |
|||
# 开关 用于启动/停用工作流 |
|||
enabled: true |
|||
process.enabled: ${flowable.enabled} |
|||
eventregistry.enabled: ${flowable.enabled} |
|||
async-executor-activate: false #关闭定时任务JOB |
|||
# 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。 |
|||
database-schema-update: true |
|||
activity-font-name: 宋体 |
|||
label-font-name: 宋体 |
|||
annotation-font-name: 宋体 |
|||
# 关闭各个模块生成表,目前只使用工作流基础表 |
|||
idm: |
|||
enabled: false |
|||
cmmn: |
|||
enabled: false |
|||
dmn: |
|||
enabled: false |
|||
app: |
|||
enabled: false |
@ -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=对不起,您的租户已过期,请联系管理员 |
Binary file not shown.
@ -0,0 +1,129 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<configuration> |
|||
<property name="log.path" value="./logs"/> |
|||
<property name="console.log.pattern" |
|||
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> |
|||
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/> |
|||
|
|||
<!-- 控制台输出 --> |
|||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> |
|||
<encoder> |
|||
<pattern>${console.log.pattern}</pattern> |
|||
<charset>utf-8</charset> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<!-- 控制台输出 --> |
|||
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${log.path}/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 =================="); |
|||
} |
|||
|
|||
|
|||
} |
@ -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> |
@ -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.2.3</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> |
@ -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,78 @@ |
|||
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.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.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() { |
|||
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core, |
|||
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).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,73 @@ |
|||
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"; |
|||
|
|||
/** |
|||
* OSS配置 |
|||
*/ |
|||
String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config"; |
|||
|
|||
/** |
|||
* 在线用户 |
|||
*/ |
|||
String ONLINE_TOKEN = "online_tokens"; |
|||
|
|||
} |
@ -0,0 +1,81 @@ |
|||
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; |
|||
|
|||
/** |
|||
* 令牌 |
|||
*/ |
|||
String TOKEN = "token"; |
|||
|
|||
/** |
|||
* 顶级部门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,45 @@ |
|||
package org.dromara.common.core.constant; |
|||
|
|||
/** |
|||
* 租户常量信息 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
public interface TenantConstants { |
|||
|
|||
/** |
|||
* 租户正常状态 |
|||
*/ |
|||
String NORMAL = "0"; |
|||
|
|||
/** |
|||
* 租户封禁状态 |
|||
*/ |
|||
String DISABLE = "1"; |
|||
|
|||
/** |
|||
* 超级管理员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,152 @@ |
|||
package org.dromara.common.core.constant; |
|||
|
|||
/** |
|||
* 用户常量信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public interface UserConstants { |
|||
|
|||
/** |
|||
* 平台内系统用户的唯一标志 |
|||
*/ |
|||
String SYS_USER = "SYS_USER"; |
|||
|
|||
/** |
|||
* 正常状态 |
|||
*/ |
|||
String NORMAL = "0"; |
|||
|
|||
/** |
|||
* 异常状态 |
|||
*/ |
|||
String EXCEPTION = "1"; |
|||
|
|||
/** |
|||
* 用户正常状态 |
|||
*/ |
|||
String USER_NORMAL = "0"; |
|||
|
|||
/** |
|||
* 用户封禁状态 |
|||
*/ |
|||
String USER_DISABLE = "1"; |
|||
|
|||
/** |
|||
* 角色正常状态 |
|||
*/ |
|||
String ROLE_NORMAL = "0"; |
|||
|
|||
/** |
|||
* 角色封禁状态 |
|||
*/ |
|||
String ROLE_DISABLE = "1"; |
|||
|
|||
/** |
|||
* 部门正常状态 |
|||
*/ |
|||
String DEPT_NORMAL = "0"; |
|||
|
|||
/** |
|||
* 部门停用状态 |
|||
*/ |
|||
String DEPT_DISABLE = "1"; |
|||
|
|||
/** |
|||
* 岗位正常状态 |
|||
*/ |
|||
String POST_NORMAL = "0"; |
|||
|
|||
/** |
|||
* 岗位停用状态 |
|||
*/ |
|||
String POST_DISABLE = "1"; |
|||
|
|||
/** |
|||
* 字典正常状态 |
|||
*/ |
|||
String DICT_NORMAL = "0"; |
|||
|
|||
/** |
|||
* 通用存在标志 |
|||
*/ |
|||
String DEL_FLAG_NORMAL = "0"; |
|||
|
|||
/** |
|||
* 通用删除标志 |
|||
*/ |
|||
String DEL_FLAG_REMOVED = "2"; |
|||
|
|||
/** |
|||
* 是否为系统默认(是) |
|||
*/ |
|||
String YES = "Y"; |
|||
|
|||
/** |
|||
* 是否菜单外链(是) |
|||
*/ |
|||
String YES_FRAME = "0"; |
|||
|
|||
/** |
|||
* 是否菜单外链(否) |
|||
*/ |
|||
String NO_FRAME = "1"; |
|||
|
|||
/** |
|||
* 菜单正常状态 |
|||
*/ |
|||
String MENU_NORMAL = "0"; |
|||
|
|||
/** |
|||
* 菜单停用状态 |
|||
*/ |
|||
String MENU_DISABLE = "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"; |
|||
|
|||
/** |
|||
* 用户名长度限制 |
|||
*/ |
|||
int USERNAME_MIN_LENGTH = 2; |
|||
int USERNAME_MAX_LENGTH = 20; |
|||
|
|||
/** |
|||
* 密码长度限制 |
|||
*/ |
|||
int PASSWORD_MIN_LENGTH = 5; |
|||
int PASSWORD_MAX_LENGTH = 20; |
|||
|
|||
/** |
|||
* 超级管理员ID |
|||
*/ |
|||
Long SUPER_ADMIN_ID = 1L; |
|||
|
|||
} |
@ -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,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,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,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,41 @@ |
|||
package org.dromara.common.core.domain.event; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serial; |
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* 总体流程监听 |
|||
* |
|||
* @author may |
|||
*/ |
|||
|
|||
@Data |
|||
public class ProcessEvent implements Serializable { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** |
|||
* 流程定义key |
|||
*/ |
|||
private String key; |
|||
|
|||
/** |
|||
* 业务id |
|||
*/ |
|||
private String businessKey; |
|||
|
|||
/** |
|||
* 状态 |
|||
*/ |
|||
private String status; |
|||
|
|||
/** |
|||
* 当为true时为申请人节点办理 |
|||
*/ |
|||
private boolean submit; |
|||
|
|||
|
|||
} |
@ -0,0 +1,40 @@ |
|||
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; |
|||
|
|||
/** |
|||
* 流程定义key |
|||
*/ |
|||
private String key; |
|||
|
|||
/** |
|||
* 审批节点key |
|||
*/ |
|||
private String taskDefinitionKey; |
|||
|
|||
/** |
|||
* 任务id |
|||
*/ |
|||
private String taskId; |
|||
|
|||
/** |
|||
* 业务id |
|||
*/ |
|||
private String businessKey; |
|||
|
|||
} |
@ -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,142 @@ |
|||
package org.dromara.common.core.domain.model; |
|||
|
|||
import org.dromara.common.core.domain.dto.RoleDTO; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
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; |
|||
|
|||
/** |
|||
* 数据权限 当前角色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,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; |
|||
|
|||
import static org.dromara.common.core.constant.UserConstants.*; |
|||
|
|||
/** |
|||
* 密码登录对象 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class PasswordLoginBody extends LoginBody { |
|||
|
|||
/** |
|||
* 用户名 |
|||
*/ |
|||
@NotBlank(message = "{user.username.not.blank}") |
|||
@Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}") |
|||
private String username; |
|||
|
|||
/** |
|||
* 用户密码 |
|||
*/ |
|||
@NotBlank(message = "{user.password.not.blank}") |
|||
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}") |
|||
private String password; |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package org.dromara.common.core.domain.model; |
|||
|
|||
import jakarta.validation.constraints.NotBlank; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.hibernate.validator.constraints.Length; |
|||
|
|||
import static org.dromara.common.core.constant.UserConstants.*; |
|||
|
|||
/** |
|||
* 用户注册对象 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class RegisterBody extends LoginBody { |
|||
|
|||
/** |
|||
* 用户名 |
|||
*/ |
|||
@NotBlank(message = "{user.username.not.blank}") |
|||
@Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}") |
|||
private String username; |
|||
|
|||
/** |
|||
* 用户密码 |
|||
*/ |
|||
@NotBlank(message = "{user.password.not.blank}") |
|||
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, 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,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,152 @@ |
|||
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; |
|||
|
|||
/** |
|||
* 业务状态枚举 |
|||
* |
|||
* @author may |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum BusinessStatusEnum { |
|||
/** |
|||
* 已撤销 |
|||
*/ |
|||
CANCEL("cancel", "已撤销"), |
|||
/** |
|||
* 草稿 |
|||
*/ |
|||
DRAFT("draft", "草稿"), |
|||
/** |
|||
* 待审核 |
|||
*/ |
|||
WAITING("waiting", "待审核"), |
|||
/** |
|||
* 已完成 |
|||
*/ |
|||
FINISH("finish", "已完成"), |
|||
/** |
|||
* 已作废 |
|||
*/ |
|||
INVALID("invalid", "已作废"), |
|||
/** |
|||
* 已退回 |
|||
*/ |
|||
BACK("back", "已退回"), |
|||
/** |
|||
* 已终止 |
|||
*/ |
|||
TERMINATION("termination", "已终止"); |
|||
|
|||
/** |
|||
* 状态 |
|||
*/ |
|||
private final String status; |
|||
|
|||
/** |
|||
* 描述 |
|||
*/ |
|||
private final String desc; |
|||
|
|||
/** |
|||
* 获取业务状态 |
|||
* |
|||
* @param status 状态 |
|||
*/ |
|||
public static String findByStatus(String status) { |
|||
if (StringUtils.isBlank(status)) { |
|||
return StrUtil.EMPTY; |
|||
} |
|||
return Arrays.stream(BusinessStatusEnum.values()) |
|||
.filter(statusEnum -> statusEnum.getStatus().equals(status)) |
|||
.findFirst() |
|||
.map(BusinessStatusEnum::getDesc) |
|||
.orElse(StrUtil.EMPTY); |
|||
} |
|||
|
|||
/** |
|||
* 启动流程校验 |
|||
* |
|||
* @param status 状态 |
|||
*/ |
|||
public static void checkStartStatus(String status) { |
|||
if (WAITING.getStatus().equals(status)) { |
|||
throw new ServiceException("该单据已提交过申请,正在审批中!"); |
|||
} 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,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 LionLi |
|||
*/ |
|||
@Getter |
|||
@AllArgsConstructor |
|||
public enum TenantStatus { |
|||
/** |
|||
* 正常 |
|||
*/ |
|||
OK("0", "正常"), |
|||
/** |
|||
* 停用 |
|||
*/ |
|||
DISABLE("1", "停用"), |
|||
/** |
|||
* 删除 |
|||
*/ |
|||
DELETED("2", "删除"); |
|||
|
|||
private final String code; |
|||
private final String info; |
|||
|
|||
} |
@ -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; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
package org.dromara.common.core.exception.file; |
|||
|
|||
import org.dromara.common.core.exception.base.BaseException; |
|||
|
|||
import java.io.Serial; |
|||
|
|||
/** |
|||
* 文件信息异常类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class FileException extends BaseException { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
public FileException(String code, Object[] args) { |
|||
super("file", code, args, null); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,18 @@ |
|||
package org.dromara.common.core.exception.file; |
|||
|
|||
import java.io.Serial; |
|||
|
|||
/** |
|||
* 文件名称超长限制异常类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class FileNameLengthLimitExceededException extends FileException { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
public FileNameLengthLimitExceededException(int defaultFileNameLength) { |
|||
super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}); |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
package org.dromara.common.core.exception.file; |
|||
|
|||
import java.io.Serial; |
|||
|
|||
/** |
|||
* 文件名大小限制异常类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class FileSizeLimitExceededException extends FileException { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
public FileSizeLimitExceededException(long defaultMaxSize) { |
|||
super("upload.exceed.maxSize", new Object[]{defaultMaxSize}); |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
package org.dromara.common.core.exception.user; |
|||
|
|||
import java.io.Serial; |
|||
|
|||
/** |
|||
* 验证码错误异常类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class CaptchaException extends UserException { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
public CaptchaException() { |
|||
super("user.jcaptcha.error"); |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
package org.dromara.common.core.exception.user; |
|||
|
|||
import java.io.Serial; |
|||
|
|||
/** |
|||
* 验证码失效异常类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class CaptchaExpireException extends UserException { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
public CaptchaExpireException() { |
|||
super("user.jcaptcha.expire"); |
|||
} |
|||
} |
@ -0,0 +1,20 @@ |
|||
package org.dromara.common.core.exception.user; |
|||
|
|||
import org.dromara.common.core.exception.base.BaseException; |
|||
|
|||
import java.io.Serial; |
|||
|
|||
/** |
|||
* 用户信息异常类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class UserException extends BaseException { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
public UserException(String code, Object... args) { |
|||
super("user", code, args, null); |
|||
} |
|||
} |
@ -0,0 +1,52 @@ |
|||
package org.dromara.common.core.factory; |
|||
|
|||
import cn.hutool.core.lang.PatternPool; |
|||
import org.dromara.common.core.constant.RegexConstants; |
|||
|
|||
import java.util.regex.Pattern; |
|||
|
|||
/** |
|||
* 正则表达式模式池工厂 |
|||
* <p>初始化的时候将正则表达式加入缓存池当中</p> |
|||
* <p>提高正则表达式的性能,避免重复编译相同的正则表达式</p> |
|||
* |
|||
* @author 21001 |
|||
*/ |
|||
public class RegexPatternPoolFactory extends PatternPool { |
|||
|
|||
/** |
|||
* 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) |
|||
*/ |
|||
public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE); |
|||
|
|||
/** |
|||
* 身份证号码(后6位) |
|||
*/ |
|||
public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6); |
|||
|
|||
/** |
|||
* QQ号码 |
|||
*/ |
|||
public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER); |
|||
|
|||
/** |
|||
* 邮政编码 |
|||
*/ |
|||
public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE); |
|||
|
|||
/** |
|||
* 注册账号 |
|||
*/ |
|||
public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT); |
|||
|
|||
/** |
|||
* 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 |
|||
*/ |
|||
public static final Pattern PASSWORD = get(RegexConstants.PASSWORD); |
|||
|
|||
/** |
|||
* 通用状态(0表示正常,1表示停用) |
|||
*/ |
|||
public static final Pattern STATUS = get(RegexConstants.STATUS); |
|||
|
|||
} |
@ -0,0 +1,31 @@ |
|||
package org.dromara.common.core.factory; |
|||
|
|||
import org.dromara.common.core.utils.StringUtils; |
|||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; |
|||
import org.springframework.core.env.PropertiesPropertySource; |
|||
import org.springframework.core.env.PropertySource; |
|||
import org.springframework.core.io.support.DefaultPropertySourceFactory; |
|||
import org.springframework.core.io.support.EncodedResource; |
|||
|
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* yml 配置源工厂 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
public class YmlPropertySourceFactory extends DefaultPropertySourceFactory { |
|||
|
|||
@Override |
|||
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { |
|||
String sourceName = resource.getResource().getFilename(); |
|||
if (StringUtils.isNotBlank(sourceName) && StringUtils.endsWithAny(sourceName, ".yml", ".yaml")) { |
|||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); |
|||
factory.setResources(resource.getResource()); |
|||
factory.afterPropertiesSet(); |
|||
return new PropertiesPropertySource(sourceName, factory.getObject()); |
|||
} |
|||
return super.createPropertySource(name, resource); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,18 @@ |
|||
package org.dromara.common.core.service; |
|||
|
|||
/** |
|||
* 通用 参数配置服务 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
public interface ConfigService { |
|||
|
|||
/** |
|||
* 根据参数 key 获取参数值 |
|||
* |
|||
* @param configKey 参数 key |
|||
* @return 参数值 |
|||
*/ |
|||
String getConfigValue(String configKey); |
|||
|
|||
} |
@ -0,0 +1,18 @@ |
|||
package org.dromara.common.core.service; |
|||
|
|||
/** |
|||
* 通用 部门服务 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
public interface DeptService { |
|||
|
|||
/** |
|||
* 通过部门ID查询部门名称 |
|||
* |
|||
* @param deptIds 部门ID串逗号分隔 |
|||
* @return 部门名称串逗号分隔 |
|||
*/ |
|||
String selectDeptNameByIds(String deptIds); |
|||
|
|||
} |
@ -0,0 +1,67 @@ |
|||
package org.dromara.common.core.service; |
|||
|
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 通用 字典服务 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
public interface DictService { |
|||
|
|||
/** |
|||
* 分隔符 |
|||
*/ |
|||
String SEPARATOR = ","; |
|||
|
|||
/** |
|||
* 根据字典类型和字典值获取字典标签 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @param dictValue 字典值 |
|||
* @return 字典标签 |
|||
*/ |
|||
default String getDictLabel(String dictType, String dictValue) { |
|||
return getDictLabel(dictType, dictValue, SEPARATOR); |
|||
} |
|||
|
|||
/** |
|||
* 根据字典类型和字典标签获取字典值 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @param dictLabel 字典标签 |
|||
* @return 字典值 |
|||
*/ |
|||
default String getDictValue(String dictType, String dictLabel) { |
|||
return getDictValue(dictType, dictLabel, SEPARATOR); |
|||
} |
|||
|
|||
/** |
|||
* 根据字典类型和字典值获取字典标签 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @param dictValue 字典值 |
|||
* @param separator 分隔符 |
|||
* @return 字典标签 |
|||
*/ |
|||
String getDictLabel(String dictType, String dictValue, String separator); |
|||
|
|||
/** |
|||
* 根据字典类型和字典标签获取字典值 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @param dictLabel 字典标签 |
|||
* @param separator 分隔符 |
|||
* @return 字典值 |
|||
*/ |
|||
String getDictValue(String dictType, String dictLabel, String separator); |
|||
|
|||
/** |
|||
* 获取字典下所有的字典值与标签 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @return dictValue为key,dictLabel为值组成的Map |
|||
*/ |
|||
Map<String, String> getAllDictByDictType(String dictType); |
|||
|
|||
} |
@ -0,0 +1,29 @@ |
|||
package org.dromara.common.core.service; |
|||
|
|||
import org.dromara.common.core.domain.dto.OssDTO; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 通用 OSS服务 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
public interface OssService { |
|||
|
|||
/** |
|||
* 通过ossId查询对应的url |
|||
* |
|||
* @param ossIds ossId串逗号分隔 |
|||
* @return url串逗号分隔 |
|||
*/ |
|||
String selectUrlByIds(String ossIds); |
|||
|
|||
/** |
|||
* 通过ossId查询列表 |
|||
* |
|||
* @param ossIds ossId串逗号分隔 |
|||
* @return 列表 |
|||
*/ |
|||
List<OssDTO> selectByIds(String ossIds); |
|||
} |
@ -0,0 +1,85 @@ |
|||
package org.dromara.common.core.service; |
|||
|
|||
import org.dromara.common.core.domain.dto.UserDTO; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 通用 用户服务 |
|||
* |
|||
* @author Lion Li |
|||
*/ |
|||
public interface UserService { |
|||
|
|||
/** |
|||
* 通过用户ID查询用户账户 |
|||
* |
|||
* @param userId 用户ID |
|||
* @return 用户账户 |
|||
*/ |
|||
String selectUserNameById(Long userId); |
|||
|
|||
/** |
|||
* 通过用户ID查询用户账户 |
|||
* |
|||
* @param userId 用户ID |
|||
* @return 用户名称 |
|||
*/ |
|||
String selectNicknameById(Long userId); |
|||
|
|||
/** |
|||
* 通过用户ID查询用户账户 |
|||
* |
|||
* @param userIds 用户ID 多个用逗号隔开 |
|||
* @return 用户名称 |
|||
*/ |
|||
String selectNicknameByIds(String userIds); |
|||
|
|||
/** |
|||
* 通过用户ID查询用户手机号 |
|||
* |
|||
* @param userId 用户id |
|||
* @return 用户手机号 |
|||
*/ |
|||
String selectPhonenumberById(Long userId); |
|||
|
|||
/** |
|||
* 通过用户ID查询用户邮箱 |
|||
* |
|||
* @param userId 用户id |
|||
* @return 用户邮箱 |
|||
*/ |
|||
String selectEmailById(Long userId); |
|||
|
|||
/** |
|||
* 通过用户ID查询用户列表 |
|||
* |
|||
* @param userIds 用户ids |
|||
* @return 用户列表 |
|||
*/ |
|||
List<UserDTO> selectListByIds(List<Long> userIds); |
|||
|
|||
/** |
|||
* 通过角色ID查询用户ID |
|||
* |
|||
* @param roleIds 角色ids |
|||
* @return 用户ids |
|||
*/ |
|||
List<Long> selectUserIdsByRoleIds(List<Long> roleIds); |
|||
|
|||
/** |
|||
* 通过角色ID查询用户 |
|||
* |
|||
* @param roleIds 角色ids |
|||
* @return 用户 |
|||
*/ |
|||
List<UserDTO> selectUsersByRoleIds(List<Long> roleIds); |
|||
|
|||
/** |
|||
* 通过部门ID查询用户 |
|||
* |
|||
* @param deptIds 部门ids |
|||
* @return 用户 |
|||
*/ |
|||
List<UserDTO> selectUsersByDeptIds(List<Long> deptIds); |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue