!166 ♥️发布 2.2.0-BETA 公测版本

Merge pull request !166 from 疯狂的狮子Li/dev
2.X
疯狂的狮子Li 2024-06-06 02:48:32 +00:00 committed by Gitee
commit 27da4e7d20
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
592 changed files with 24065 additions and 6121 deletions

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-auth" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-auth:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-auth:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-auth/Dockerfile" />
</settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-gateway" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-gateway:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-gateway:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-gateway/Dockerfile" />
</settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-gen" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-gen:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-gen:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-gen/Dockerfile" />
</settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-job" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-job:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-job:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-job/Dockerfile" />
</settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-monitor" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-monitor:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-monitor:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-monitor/Dockerfile" />
</settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-nacos" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-nacos:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-nacos:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-nacos/Dockerfile" />
</settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-resource" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-resource:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-resource:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-resource/Dockerfile" />
</settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-seata-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-seata-server:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-seata-server:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-seata-server/Dockerfile" />
</settings>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-sentinel-dashboard" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-sentinel-dashboard:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-sentinel-dashboard:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-sentinel-dashboard/Dockerfile" />
</settings>

View File

@ -1,10 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ruoyi-powerjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-powerjob-server:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-powerjob-server/Dockerfile" />
<option name="sourceFilePath" value="ruoyi-visual/ruoyi-snailjob-server/Dockerfile" />
</settings>
</deployment>
<method v="2" />

View File

@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-system" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-system:2.1.2" />
<option name="imageTag" value="ruoyi/ruoyi-system:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-system/Dockerfile" />
</settings>

View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ruoyi-workflow" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-workflow:2.2.0" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-modules/ruoyi-workflow/Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>

View File

@ -9,7 +9,7 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Cloud-Plus/blob/master/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Cloud-Plus)
<br>
[![RuoYi-Cloud-Plus](https://img.shields.io/badge/RuoYi_Cloud_Plus-2.1.2-success.svg)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
[![RuoYi-Cloud-Plus](https://img.shields.io/badge/RuoYi_Cloud_Plus-2.2.0-success.svg)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.1-blue.svg)]()
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
[![JDK-19](https://img.shields.io/badge/JDK-21-green.svg)]()
@ -27,8 +27,10 @@
## 赞助商
MaxKey - https://gitee.com/dromara/MaxKey <br>
CCFlow - https://gitee.com/opencc/RuoYi-JFlow <br>
MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
数舵科技 软件定制开发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的功能差异
@ -66,10 +68,10 @@ CCFlow - https://gitee.com/opencc/RuoYi-JFlow <br>
| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物 | 无 |
| 序列化 | 采用 Jackson Spring官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 |
| 分布式幂等 | 参考美团GTIS防重系统简化实现(细节可看文档) | 手动编写注解基于aop实现 |
| 分布式任务调度 | 采用 PowerJob 天生支持分布式 统一的管理中心 底层基于JPA实现 支持多种数据库 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 |
| 分布式任务调度 | 采用 SnailJob 天生支持分布式 统一的管理中心 支持多种数据库 支持分片重试DAG任务流等 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 |
| 分布式日志中心 | 采用 ELK 业界成熟解决方案 实时收集所有服务的运行日志 快速发现定位问题 | 无 |
| 分布式搜索引擎 | 采用 ElasticSearch、Easy-Es 以 Mybatis-Plus 方式操作 ElasticSearch | 无 |
| 分布式消息队列 | 采用 SpringCloud-Stream 支持 Kafka、RocketMQ、RabbitMQ | 无 |
| 分布式消息队列 | 采用 支持 Kafka、RocketMQ、RabbitMQ 各种 延迟消息 事务消息 流消息 | 无 |
| 分库分表功能 | 采用 Apache Sharding-Proxy 代理服务无入侵支持分库分表 只需编写分库分表规则即可 | 无 |
| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储<br/>支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 |
| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家 | 不支持 |

BIN
bpmn/模型.zip Normal file

Binary file not shown.

View File

@ -30,9 +30,6 @@ dubbo:
consumer:
# 超时时间
timeout: 3000
scan:
# 接口实现类扫描
base-packages: org.dromara.**.dubbo
# 自定义配置
custom:
# 全局请求log
@ -41,6 +38,10 @@ dubbo:
log-level: info
spring:
threads:
# 开启虚拟线程 仅jdk21可用
virtual:
enabled: false
# 资源信息
messages:
# 国际化资源文件路径
@ -52,6 +53,8 @@ spring:
# 上传单个文件大小限制
max-file-size: 10MB
mvc:
# 设置静态资源路径 防止所有请求都去查静态资源
static-path-pattern: /static/**
format:
date-time: yyyy-MM-dd HH:mm:ss
#jackson配置
@ -141,7 +144,10 @@ logging:
org.springframework: warn
org.apache.dubbo: warn
com.alibaba.nacos: warn
tech.powerjob.worker.background: warn
org.mybatis.spring.mapper: error
org.apache.dubbo.config: error
# 临时处理 spring 调整日志级别导致启动警告问题 不影响使用等待 alibaba 适配
org.springframework.context.support.PostProcessorRegistrationDelegate: error
config: classpath:logback-plus.xml
# Sa-Token配置
@ -160,8 +166,7 @@ sa-token:
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
# 例如 com.**.**.mapper
# 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper
mapperPackage: org.dromara.**.mapper
# 对应的 XML 文件位置
mapperLocations: classpath*:mapper/**/*Mapper.xml

View File

@ -13,6 +13,10 @@ datasource:
url: jdbc:mysql://localhost:3306/ry-job?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root
password: password
workflow:
url: jdbc:mysql://localhost:3306/ry-workflow?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: password
# system-oracle:
# url: jdbc:oracle:thin:@//localhost:1521/XE
# username: ROOT

View File

@ -21,9 +21,8 @@ user:
# 密码锁定时间默认10分钟
lockTime: 10
--- # 三方授权
# 三方授权
justauth:
enabled: true
# 前端外网访问地址
address: http://localhost:80
type:
@ -34,6 +33,13 @@ justauth:
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

View File

@ -5,6 +5,7 @@ security:
enabled: true
excludeUrls:
- /system/notice
- /workflow/model/editModelXml
# 不校验白名单
ignore:
whites:
@ -59,6 +60,11 @@ spring:
- Path=/resource/**
filters:
- StripPrefix=1
# workflow服务
- id: ruoyi-workflow
uri: lb://ruoyi-workflow
predicates:
- Path=/workflow/**
# 演示服务
- id: ruoyi-demo
uri: lb://ruoyi-demo
@ -67,10 +73,10 @@ spring:
filters:
- StripPrefix=1
# MQ演示服务
- id: ruoyi-stream-mq
uri: lb://ruoyi-stream-mq
- id: ruoyi-test-mq
uri: lb://ruoyi-test-mq
predicates:
- Path=/stream-mq/**
- Path=/test-mq/**
filters:
- StripPrefix=1

View File

@ -13,21 +13,17 @@ spring:
username: ${datasource.job.username}
password: ${datasource.job.password}
# powerjob 配置
powerjob:
worker:
# 如何开启调度中心请查看文档教程
enabled: true
# 需要先在 powerjob 登录页执行应用注册后才能使用
app-name: ruoyi-worker
allow-lazy-connect-server: false
max-appended-wf-context-length: 4096
max-result-length: 4096
# 29203 端口 随着主应用端口飘逸 避免集群冲突
port: 2${server.port}
protocol: http
# 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。
# server-address: 127.0.0.1:7700
# 调度中心应用名 通过服务名连接调度中心(启用 server-name 会导致 server-address 不生效)
server-name: ruoyi-powerjob-server
store-strategy: disk
snail-job:
enabled: true
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "ruoyi_group"
# SnailJob 接入验证令牌
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
server:
# 从 nacos 获取服务
server-name: ruoyi-snailjob-server
# 服务名优先 ip垫底
host: 127.0.0.1
port: 1788
# 详见 script/sql/ry_job.sql `sj_namespace` 表
namespace: ${spring.profiles.active}

View File

@ -1,47 +0,0 @@
oms.env=${spring.profiles.active}
####### Database properties(Configure according to the the environment) #######
spring.datasource.core.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.core.jdbc-url=${datasource.job.url}
spring.datasource.core.username=${datasource.job.username}
spring.datasource.core.password=${datasource.job.password}
spring.datasource.core.maximum-pool-size=20
spring.datasource.core.minimum-idle=5
####### MongoDB properties(Non-core configuration properties) #######
####### delete mongodb config to disable mongodb #######
oms.mongodb.enable=false
#spring.data.mongodb.uri=mongodb+srv://zqq:No1Bug2Please3!@cluster0.wie54.gcp.mongodb.net/powerjob_daily?retryWrites=true&w=majority
####### Email properties(Non-core configuration properties) #######
####### Delete the following code to disable the mail #######
#spring.mail.host=smtp.163.com
#spring.mail.username=zqq@163.com
#spring.mail.password=GOFZPNARMVKCGONV
#spring.mail.properties.mail.smtp.auth=true
#spring.mail.properties.mail.smtp.starttls.enable=true
#spring.mail.properties.mail.smtp.starttls.required=true
####### DingTalk properties(Non-core configuration properties) #######
####### Delete the following code to disable the DingTalk #######
#oms.alarm.ding.app-key=dingauqwkvxxnqskknfv
#oms.alarm.ding.app-secret=XWrEPdAZMPgJeFtHuL0LH73LRj-74umF2_0BFcoXMfvnX0pCQvt0rpb1JOJU_HLl
#oms.alarm.ding.agent-id=847044348
####### Resource cleaning properties #######
oms.instanceinfo.retention=1
oms.container.retention.local=1
oms.container.retention.remote=-1
####### Cache properties #######
oms.instance.metadata.cache.size=1024
# 生产配置
#oms.instanceinfo.retention=7
#oms.container.retention.local=7
#oms.container.retention.remote=-1
#oms.instance.metadata.cache.size=2048
####### Threshold in precise fetching server(0~100). 100 means full detection of server, in which #######
####### split-brain could be avoided while performance overhead would increase. #######
oms.accurate.select.server.percentage = 50

View File

@ -54,28 +54,33 @@ mail:
connectionTimeout: 0
# sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
# https://wind.kim/doc/start 文档地址 各个厂商可同时使用
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
sms:
# 阿里云 dysmsapi.aliyuncs.com
alibaba:
#请求地址 默认为 dysmsapi.aliyuncs.com 如无特殊改变可以不用设置
requestUrl: dysmsapi.aliyuncs.com
#阿里云的accessKey
accessKeyId: xxxxxxx
#阿里云的accessKeySecret
accessKeySecret: xxxxxxx
#短信签名
signature: 测试
tencent:
#请求地址默认为 sms.tencentcloudapi.com 如无特殊改变可不用设置
requestUrl: sms.tencentcloudapi.com
#腾讯云的accessKey
accessKeyId: xxxxxxx
#腾讯云的accessKeySecret
accessKeySecret: xxxxxxx
#短信签名
signature: 测试
#短信sdkAppId
sdkAppId: appid
#地域信息默认为 ap-guangzhou 如无特殊改变可不用设置
territory: ap-guangzhou
# 配置源类型用于标定配置来源(interface,yaml)
config-type: yaml
# 用于标定yml中的配置是否开启短信拦截接口配置不受此限制
restricted: true
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
minute-max: 1
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
account-max: 30
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
blends:
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
config1:
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba
# 有些称为accessKey有些称之为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

View File

@ -0,0 +1,39 @@
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${datasource.job.url}
username: ${datasource.job.username}
password: ${datasource.job.password}
hikari:
connection-timeout: 30000
validation-timeout: 5000
minimum-idle: 10
maximum-pool-size: 20
idle-timeout: 600000
max-lifetime: 900000
keepaliveTime: 30000
# snail-job 服务端配置
snail-job:
# 拉取重试数据的每批次的大小
retry-pull-page-size: 1000
# 拉取重试数据的每批次的大小
job-pull-page-size: 1000
# 服务端 netty 端口
netty-port: 1788
# 重试和死信表的分区总数
total-partition: 2
# 一个客户端每秒最多接收的重试数量指令
limiter: 1000
# 号段模式下步长配置
step: 100
# 日志保存时间(单位: day)
log-storage: 90
# 回调配置
callback:
#回调最大执行次数
max-count: 288
#间隔时间
trigger-interval: 900
retry-max-pull-count: 10

View File

@ -0,0 +1,44 @@
spring:
datasource:
dynamic:
# 设置默认的数据源或者数据源组,默认值即为 master
primary: master
datasource:
# 主库数据源
master:
type: ${spring.datasource.type}
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${datasource.workflow.url}
username: ${datasource.workflow.username}
password: ${datasource.workflow.password}
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
# url: ${datasource.system-oracle.url}
# username: ${datasource.system-oracle.username}
# password: ${datasource.system-oracle.password}
# postgres:
# type: ${spring.datasource.type}
# driverClassName: org.postgresql.Driver
# url: ${datasource.system-postgres.url}
# username: ${datasource.system-postgres.username}
# password: ${datasource.system-postgres.password}
# flowable配置
flowable:
# 关闭定时任务JOB
async-executor-activate: false
# 将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

View File

@ -1,6 +1,7 @@
service.vgroupMapping.ruoyi-auth-group=default
service.vgroupMapping.ruoyi-system-group=default
service.vgroupMapping.ruoyi-resource-group=default
service.vgroupMapping.ruoyi-workflow-group=default
service.enableDegrade=false
service.disableGlobalTransaction=false
@ -10,7 +11,7 @@ store.mode=db
store.lock.mode=db
store.session.mode=db
#Used for password encryption
store.publicKey=
#store.publicKey=
#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
store.db.datasource=hikari

View File

@ -29,7 +29,7 @@ services:
network_mode: "host"
nacos:
image: ruoyi/ruoyi-nacos:2.1.2
image: ruoyi/ruoyi-nacos:2.2.0
container_name: nacos
ports:
- "8848:8848"
@ -96,7 +96,7 @@ services:
network_mode: "host"
seata-server:
image: ruoyi/ruoyi-seata-server:2.1.2
image: ruoyi/ruoyi-seata-server:2.2.0
container_name: seata-server
ports:
- "7091:7091"
@ -135,7 +135,7 @@ services:
network_mode: "host"
sentinel:
image: ruoyi/ruoyi-sentinel-dashboard:2.1.2
image: ruoyi/ruoyi-sentinel-dashboard:2.2.0
container_name: sentinel
environment:
TZ: Asia/Shanghai
@ -150,7 +150,7 @@ services:
network_mode: "host"
ruoyi-monitor:
image: ruoyi/ruoyi-monitor:2.1.2
image: ruoyi/ruoyi-monitor:2.2.0
container_name: ruoyi-monitor
environment:
# 时区上海
@ -165,23 +165,22 @@ services:
privileged: true
network_mode: "host"
ruoyi-powerjob-server:
image: ruoyi/ruoyi-powerjob-server:2.1.2
container_name: ruoyi-powerjob-server
ruoyi-snailjob-server:
image: ruoyi/ruoyi-snailjob-server:2.2.0
container_name: ruoyi-snailjob-server
environment:
# 时区上海
TZ: Asia/Shanghai
ports:
- "7700:7700"
- "10086:10086"
- "10010:10010"
- "8800:8800"
- "1788:1788"
volumes:
- /docker/powerjob/logs/:/ruoyi/powerjob/logs
- /docker/snailjob/logs/:/ruoyi/snailjob/logs
privileged: true
network_mode: "host"
ruoyi-gateway:
image: ruoyi/ruoyi-gateway:2.1.2
image: ruoyi/ruoyi-gateway:2.2.0
container_name: ruoyi-gateway
environment:
# 时区上海
@ -197,7 +196,7 @@ services:
network_mode: "host"
ruoyi-auth:
image: ruoyi/ruoyi-auth:2.1.2
image: ruoyi/ruoyi-auth:2.2.0
container_name: ruoyi-auth
environment:
# 时区上海
@ -213,7 +212,7 @@ services:
network_mode: "host"
ruoyi-system:
image: ruoyi/ruoyi-system:2.1.2
image: ruoyi/ruoyi-system:2.2.0
container_name: ruoyi-system
environment:
# 时区上海
@ -229,7 +228,7 @@ services:
network_mode: "host"
ruoyi-gen:
image: ruoyi/ruoyi-gen:2.1.2
image: ruoyi/ruoyi-gen:2.2.0
container_name: ruoyi-gen
environment:
# 时区上海
@ -245,7 +244,7 @@ services:
network_mode: "host"
ruoyi-job:
image: ruoyi/ruoyi-job:2.1.2
image: ruoyi/ruoyi-job:2.2.0
container_name: ruoyi-job
environment:
# 时区上海
@ -261,7 +260,7 @@ services:
network_mode: "host"
ruoyi-resource:
image: ruoyi/ruoyi-resource:2.1.2
image: ruoyi/ruoyi-resource:2.2.0
container_name: ruoyi-resource
environment:
# 时区上海
@ -276,6 +275,22 @@ services:
privileged: true
network_mode: "host"
ruoyi-workflow:
image: ruoyi/ruoyi-workflow:2.2.0
container_name: ruoyi-workflow
environment:
# 时区上海
TZ: Asia/Shanghai
ports:
- "9205:9205"
volumes:
# 配置文件
- /docker/ruoyi-workflow/logs/:/ruoyi/workflow/logs
# skywalking 探针
- /docker/skywalking/agent/:/ruoyi/skywalking/agent
privileged: true
network_mode: "host"
#################################################################################################
#################################### 以下为扩展根据需求搭建 #########################################
@ -328,9 +343,9 @@ services:
- elasticsearch
network_mode: "host"
mqnamesrv:
image: apache/rocketmq:4.9.4
container_name: mqnamesrv
rmqnamesrv:
image: apache/rocketmq:5.2.0
container_name: rmqnamesrv
ports:
- "9876:9876"
environment:
@ -340,25 +355,25 @@ services:
- /docker/rocketmq/namesrv/logs:/home/rocketmq/logs/rocketmqlogs
network_mode: "host"
mqbroker1:
image: apache/rocketmq:4.9.4
container_name: mqbroker1
rmqbroker1:
image: apache/rocketmq:5.2.0
container_name: rmqbroker1
ports:
- "10911:10911"
- "10909:10909"
- "10912:10912"
environment:
JAVA_OPT_EXT: -server -Xms512M -Xmx512M -Xmn256m
command: sh mqbroker -c /home/rocketmq/rocketmq-4.9.4/conf/broker.conf
command: sh mqbroker --enable-proxy -c /home/rocketmq/rocketmq-5.2.0/conf/broker.conf
depends_on:
- mqnamesrv
volumes:
- /docker/rocketmq/broker1/conf/broker.conf:/home/rocketmq/rocketmq-4.9.4/conf/broker.conf
- /docker/rocketmq/broker1/conf/broker.conf:/home/rocketmq/rocketmq-5.2.0/conf/broker.conf
- /docker/rocketmq/broker1/logs:/home/rocketmq/logs/rocketmqlogs
- /docker/rocketmq/broker1/store:/home/rocketmq/store
network_mode: "host"
mqconsole:
rmqconsole:
image: styletang/rocketmq-console-ng
container_name: mqconsole
ports:
@ -366,11 +381,11 @@ services:
environment:
JAVA_OPTS: -Dserver.port=19876 -Drocketmq.namesrv.addr=127.0.0.1:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false
depends_on:
- mqnamesrv
- rmqnamesrv
network_mode: "host"
rabbitmq:
image: rabbitmq:3.10.6
image: rabbitmq:3.13.3
container_name: rabbitmq
build:
context: ./rabbitmq
@ -402,7 +417,7 @@ services:
network_mode: "host"
kafka:
image: 'bitnami/kafka:3.2.0'
image: 'bitnami/kafka:3.6.2'
container_name: kafka
ports:
- "9092:9092"

View File

@ -78,11 +78,6 @@ http {
proxy_pass http://server/;
}
# 解决 powerjob 代理之后静态文件无法访问的问题 请勿修改乱动
location ~ ^/(js|css|jpg|png|svg|woff|ttf|ico|img)/ {
proxy_pass http://powerjob-server;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;

View File

@ -1,8 +1,8 @@
FROM rabbitmq:3.10.6-management
FROM rabbitmq:3.13.3-management
MAINTAINER Lion Li
ADD ./rabbitmq_delayed_message_exchange-3.10.2.ez /plugins
ADD ./rabbitmq_delayed_message_exchange-3.13.0.ez /plugins
RUN rabbitmq-plugins enable rabbitmq_delayed_message_exchange

107
pom.xml
View File

@ -13,37 +13,36 @@
<description>RuoYi-Cloud-Plus微服务系统</description>
<properties>
<revision>2.1.2</revision>
<revision>2.2.0-BETA</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<spring-boot.version>3.1.7</spring-boot.version>
<spring-cloud.version>2022.0.4</spring-cloud.version>
<spring-boot-admin.version>3.1.8</spring-boot-admin.version>
<spring-boot.mybatis>3.0.3</spring-boot.mybatis>
<mybatis.version>3.5.13</mybatis.version>
<mybatis-plus.version>3.5.4</mybatis-plus.version>
<spring-boot.version>3.2.6</spring-boot.version>
<spring-cloud.version>2023.0.2</spring-cloud.version>
<spring-boot-admin.version>3.2.3</spring-boot-admin.version>
<mybatis.version>3.5.16</mybatis.version>
<mybatis-plus.version>3.5.6</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version>
<dynamic-ds.version>4.2.0</dynamic-ds.version>
<dynamic-ds.version>4.3.0</dynamic-ds.version>
<velocity.version>2.3</velocity.version>
<swagger.core.version>2.2.15</swagger.core.version>
<springdoc.version>2.2.0</springdoc.version>
<swagger.core.version>2.2.21</swagger.core.version>
<springdoc.version>2.5.0</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<poi.version>5.2.3</poi.version>
<easyexcel.version>3.3.3</easyexcel.version>
<hutool.version>5.8.22</hutool.version>
<redisson.version>3.24.3</redisson.version>
<lock4j.version>2.2.5</lock4j.version>
<powerjob.version>4.3.6</powerjob.version>
<satoken.version>1.37.0</satoken.version>
<lombok.version>1.18.30</lombok.version>
<easyexcel.version>3.3.4</easyexcel.version>
<hutool.version>5.8.27</hutool.version>
<redisson.version>3.29.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version>
<snailjob.version>1.0.0-beta3</snailjob.version>
<satoken.version>1.38.0</satoken.version>
<lombok.version>1.18.32</lombok.version>
<logstash.version>7.4</logstash.version>
<easy-es.version>2.0.0-beta4</easy-es.version>
<easy-es.version>2.0.0</easy-es.version>
<elasticsearch.version>7.14.0</elasticsearch.version>
<skywalking-toolkit.version>8.16.0</skywalking-toolkit.version>
<bouncycastle.version>1.76</bouncycastle.version>
<alibaba-ttl.version>2.14.4</alibaba-ttl.version>
<mapstruct-plus.version>1.3.5</mapstruct-plus.version>
<mapstruct-plus.version>1.3.6</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<justauth.version>1.16.6</justauth.version>
<!-- 离线IP地址定位库 -->
@ -52,11 +51,16 @@
<fastjson.version>1.2.83</fastjson.version>
<!-- OSS 配置 -->
<aws-java-sdk-s3.version>1.12.600</aws-java-sdk-s3.version>
<aws.sdk.version>2.25.15</aws.sdk.version>
<aws.crt.version>0.29.13</aws.crt.version>
<okhttp.version>4.10.0</okhttp.version>
<!-- SMS 配置 -->
<sms4j.version>2.2.0</sms4j.version>
<sms4j.version>3.2.1</sms4j.version>
<!-- 工作流配置 -->
<flowable.version>7.0.0</flowable.version>
<!-- mq配置 -->
<rocketmq.version>2.3.0</rocketmq.version>
<!-- 插件版本 -->
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
@ -73,6 +77,8 @@
<nacos.server>127.0.0.1:8848</nacos.server>
<nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group>
<nacos.config.group>DEFAULT_GROUP</nacos.config.group>
<nacos.username>nacos</nacos.username>
<nacos.password>nacos</nacos.password>
<logstash.address>127.0.0.1:4560</logstash.address>
</properties>
<activation>
@ -87,6 +93,8 @@
<nacos.server>127.0.0.1:8848</nacos.server>
<nacos.discovery.group>DEFAULT_GROUP</nacos.discovery.group>
<nacos.config.group>DEFAULT_GROUP</nacos.config.group>
<nacos.username>nacos</nacos.username>
<nacos.password>nacos</nacos.password>
<logstash.address>127.0.0.1:4560</logstash.address>
</properties>
</profile>
@ -157,6 +165,14 @@
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-bom</artifactId>
<version>${flowable.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
@ -169,13 +185,6 @@
<version>${satoken.version}</version>
</dependency>
<!-- Mybatis 依赖配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${spring-boot.mybatis}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
@ -184,7 +193,7 @@
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
@ -266,16 +275,16 @@
<version>${lock4j.version}</version>
</dependency>
<!-- PowerJob -->
<!-- SnailJob Client -->
<dependency>
<groupId>tech.powerjob</groupId>
<artifactId>powerjob-worker</artifactId>
<version>${powerjob.version}</version>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-client-starter</artifactId>
<version>${snailjob.version}</version>
</dependency>
<dependency>
<groupId>tech.powerjob</groupId>
<artifactId>powerjob-official-processors</artifactId>
<version>${powerjob.version}</version>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-client-job-core</artifactId>
<version>${snailjob.version}</version>
</dependency>
<!-- 加密包引入 -->
@ -326,10 +335,23 @@
<version>${okhttp.version}</version>
</dependency>
<!-- AWS SDK for Java 2.x -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>${aws-java-sdk-s3.version}</version>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!-- 使用AWS基于 CRT 的 S3 客户端 -->
<dependency>
<groupId>software.amazon.awssdk.crt</groupId>
<artifactId>aws-crt</artifactId>
<version>${aws.crt.version}</version>
</dependency>
<!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3-transfer-manager</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!--短信sms4j-->
@ -364,6 +386,13 @@
<version>${mapstruct-plus.version}</version>
</dependency>
<!--消息队列-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -12,6 +12,7 @@
<module>ruoyi-api-bom</module>
<module>ruoyi-api-system</module>
<module>ruoyi-api-resource</module>
<module>ruoyi-api-workflow</module>
</modules>
<artifactId>ruoyi-api</artifactId>

View File

@ -15,7 +15,7 @@
</description>
<properties>
<revision>2.1.2</revision>
<revision>2.2.0-BETA</revision>
</properties>
<dependencyManagement>
@ -34,6 +34,13 @@
<version>${revision}</version>
</dependency>
<!-- workflow接口 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-api-workflow</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@ -3,6 +3,8 @@ package org.dromara.resource.api;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.resource.api.domain.RemoteFile;
import java.util.List;
/**
*
*
@ -26,4 +28,11 @@ public interface RemoteFileService {
*/
String selectUrlByIds(String ossIds);
/**
* ossId
*
* @param ossIds ossId
* @return
*/
List<RemoteFile> selectByIds(String ossIds);
}

View File

@ -4,6 +4,8 @@ import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.resource.api.domain.RemoteFile;
import java.util.List;
/**
* ()
*
@ -34,4 +36,10 @@ public class RemoteFileServiceMock implements RemoteFileService {
return StringUtils.EMPTY;
}
@Override
public List<RemoteFile> selectByIds(String ossIds) {
log.warn("服务调用异常 -> 降级处理");
return List.of();
}
}

View File

@ -13,7 +13,7 @@ public interface RemoteMessageService {
* @param sessionKey session id
* @param message
*/
void sendMessage(Long sessionKey, String message);
void publishMessage(Long sessionKey, String message);
void publishAll(String message);
}

View File

@ -20,9 +20,9 @@ public class RemoteMessageServiceStub implements RemoteMessageService {
* @param sessionKey session id
* @param message
*/
public void sendMessage(Long sessionKey, String message) {
public void publishMessage(Long sessionKey, String message) {
try {
remoteMessageService.sendMessage(sessionKey, message);
remoteMessageService.publishMessage(sessionKey, message);
} catch (Exception e) {
log.warn("websocket 功能未开启或服务未找到");
}

View File

@ -1,24 +1,145 @@
package org.dromara.resource.api;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.resource.api.domain.RemoteSms;
import java.util.LinkedHashMap;
import java.util.List;
/**
*
*
* @author Lion Li
* @author Feng
*/
public interface RemoteSmsService {
/**
*
*
*
* @param phones ()
* @param templateId id
* @param param
* @param phone
* @param message
* @return RemoteSms
*/
RemoteSms send(String phones, String templateId, LinkedHashMap<String, String> param) throws ServiceException;
RemoteSms sendMessage(String phone, String message);
/**
*
*
* @param phone
* @param messages 使 LinkedHashMap
* @return RemoteSms
*/
RemoteSms sendMessage(String phone, LinkedHashMap<String, String> messages);
/**
* 使
*
* @param phone
* @param templateId ID
* @param messages 使 LinkedHashMap
* @return RemoteSms
*/
RemoteSms sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages);
/**
*
*
* @param phones 1~1000
* @param message
* @return RemoteSms
*/
RemoteSms messageTexting(List<String> phones, String message);
/**
* 使
*
* @param phones 1~10001~1000
* @param templateId ID
* @param messages 使 LinkedHashMap
* @return RemoteSms
*/
RemoteSms messageTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages);
/**
*
*
* @param phone
* @param message
*/
void sendMessageAsync(String phone, String message);
/**
* 使
*
* @param phone
* @param templateId ID
* @param messages 使 LinkedHashMap
*/
void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages);
/**
*
*
* @param phone
* @param message
* @param delayedTime
*/
void delayMessage(String phone, String message, Long delayedTime);
/**
* 使
*
* @param phone
* @param templateId ID
* @param messages 使 LinkedHashMap
* @param delayedTime
*/
void delayMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
/**
*
*
* @param phones 1~1000
* @param message
* @param delayedTime
*/
void delayMessageTexting(List<String> phones, String message, Long delayedTime);
/**
* 使
*
* @param phones 1~1000
* @param templateId ID
* @param messages 使 LinkedHashMap
* @param delayedTime
*/
void delayMessageTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
/**
*
*
* @param phone
*/
void addBlacklist(String phone);
/**
*
*
* @param phones
*/
void addBlacklist(List<String> phones);
/**
*
*
* @param phone
*/
void removeBlacklist(String phone);
/**
*
*
* @param phones
*/
void removeBlacklist(List<String> phones);
}

View File

@ -31,4 +31,14 @@ public class RemoteFile implements Serializable {
*/
private String url;
/**
*
*/
private String originalName;
/**
*
*/
private String fileSuffix;
}

View File

@ -19,15 +19,15 @@ public class RemoteSms implements Serializable {
/**
*
*/
private Boolean isSuccess;
private Boolean success;
/**
*
* Alibaba
*/
private String message;
private String configId;
/**
*
*
* <p>
* SDK SendSmsResponse
*/

View File

@ -9,11 +9,17 @@ public interface RemoteDataScopeService {
/**
*
*
* @param roleId ID
* @return null
*/
String getRoleCustom(Long roleId);
/**
*
*
* @param deptId ID
* @return null
*/
String getDeptAndChild(Long deptId);

View File

@ -18,4 +18,5 @@ public interface RemoteDictService {
* @return
*/
List<RemoteDictDataVo> selectDictDataByType(String dictType);
}

View File

@ -23,4 +23,5 @@ public interface RemoteLogService {
* @param sysLogininfor 访
*/
void saveLogininfor(RemoteLogininforBo sysLogininfor);
}

View File

@ -13,22 +13,39 @@ import java.util.List;
public interface RemoteSocialService {
/**
* authId
* authId
*
* @param authId id
* @return
*/
List<RemoteSocialVo> selectByAuthId(String authId);
/**
*
*
* @param bo
*/
List<RemoteSocialVo> queryList(RemoteSocialBo bo);
/**
*
*
* @param bo
*/
void insertByBo(RemoteSocialBo bo);
/**
*
*
* @param bo
*/
void updateByBo(RemoteSocialBo bo);
/**
*
*
* @param socialId ID
* @return
*/
Boolean deleteWithValidById(Long socialId);

View File

@ -6,6 +6,8 @@ import org.dromara.system.api.domain.vo.RemoteTenantVo;
import java.util.List;
/**
*
*
* @author zhujie
*/
public interface RemoteTenantService {

View File

@ -3,9 +3,12 @@ package org.dromara.system.api;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.system.api.domain.bo.RemoteUserBo;
import org.dromara.system.api.domain.vo.RemoteUserVo;
import org.dromara.system.api.model.LoginUser;
import org.dromara.system.api.model.XcxLoginUser;
import java.util.List;
/**
*
*
@ -81,6 +84,30 @@ public interface RemoteUserService {
*/
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);
/**
*
*
@ -89,4 +116,19 @@ public interface RemoteUserService {
*/
void recordLoginInfo(Long userId, String ip);
/**
* ID
*
* @param userIds ids
* @return
*/
List<RemoteUserVo> selectListByIds(List<Long> userIds);
/**
* IDID
*
* @param roleIds ids
* @return ids
*/
List<Long> selectUserIdsByRoleIds(List<Long> roleIds);
}

View File

@ -11,7 +11,6 @@ import java.io.Serializable;
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class SysUserOnline implements Serializable {

View File

@ -14,7 +14,6 @@ import java.util.Map;
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class RemoteLogininforBo implements Serializable {

View File

@ -14,7 +14,6 @@ import java.util.Map;
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class RemoteOperLogBo implements Serializable {

View File

@ -19,7 +19,6 @@ import java.util.Date;
*
* @author Michelle.Chung
*/
@Data
@NoArgsConstructor
public class RemoteUserBo implements Serializable {

View File

@ -67,5 +67,4 @@ public class RemoteClientVo implements Serializable {
*/
private String status;
}

View File

@ -32,7 +32,7 @@ public class RemoteSocialVo implements Serializable {
private String tenantId;
/**
* ID
* ID
*/
private String authId;

View File

@ -0,0 +1,73 @@
package org.dromara.system.api.domain.vo;
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 RemoteUserVo 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;
}

View File

@ -35,6 +35,11 @@ public class LoginUser implements Serializable {
*/
private Long deptId;
/**
*
*/
private String deptCategory;
/**
*
*/

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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-api</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-api-workflow</artifactId>
<description>
ruoyi-api-workflow 工作流接口模块
</description>
<dependencies>
<!-- RuoYi Common Core-->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,78 @@
package org.dromara.workflow.api.domain;
import java.util.List;
import java.util.Map;
/**
*
*
* @Author ZETA
* @Date 2024/6/3
*/
public interface RemoteWorkflowService {
/**
*
*
* @param businessKeys id
* @return
*/
boolean deleteRunAndHisInstance(List<String> businessKeys);
/**
*
*
* @param taskId id
*/
String getBusinessStatusByTaskId(String taskId);
/**
*
*
* @param businessKey id
*/
String getBusinessStatus(String businessKey);
/**
* ()
*
* @param taskId id
* @param variableName
* @param value
*/
void setVariable(String taskId, String variableName, Object value);
/**
* ()
*
* @param taskId id
* @param variables
*/
void setVariables(String taskId, Map<String, Object> variables);
/**
* (,)
*
* @param taskId id
* @param variableName
* @param value
*/
void setVariableLocal(String taskId, String variableName, Object value);
/**
* (,)
*
* @param taskId id
* @param variables
*/
void setVariablesLocal(String taskId, Map<String, Object> variables);
/**
* idid
*
* @param businessKey id
* @return
*/
String getInstanceIdByBusinessKey(String businessKey);
}

View File

@ -0,0 +1,40 @@
package org.dromara.workflow.api.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;
}

View File

@ -0,0 +1,35 @@
package org.dromara.workflow.api.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(key_)
*/
private String keyNode;
/**
* id
*/
private String taskId;
/**
* id
*/
private String businessKey;
}

View File

@ -18,6 +18,6 @@ ADD ./target/ruoyi-auth.jar ./app.jar
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
#-Dskywalking.agent.service_name=ruoyi-auth \
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
-jar app.jar \
-XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*,:time,tags,level -XX:+UseZGC ${JAVA_OPTS}
-XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
-jar app.jar

View File

@ -16,16 +16,9 @@
<dependencies>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-nacos</artifactId>
</dependency>
<dependency>
@ -87,12 +80,6 @@
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-tenant</artifactId>
<exclusions>
<exclusion>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.dromara</groupId>

View File

@ -20,7 +20,7 @@ public class CaptchaConfig {
private static final int WIDTH = 160;
private static final int HEIGHT = 60;
private static final Color BACKGROUND = Color.PINK;
private static final Color BACKGROUND = Color.LIGHT_GRAY;
private static final Font FONT = new Font("Arial", Font.BOLD, 48);
/**

View File

@ -64,6 +64,7 @@ public class CaptchaController {
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator);
captcha.createCode();
// 如果是数学验证码使用SpEL表达式处理验证码结果
String code = captcha.getCode();
if (isMath) {
ExpressionParser parser = new SpelExpressionParser();

View File

@ -97,10 +97,7 @@ public class TokenController {
Long userId = LoginHelper.getUserId();
scheduledExecutorService.schedule(() -> {
try {
remoteMessageService.sendMessage(userId, "欢迎登录RuoYi-Cloud-Plus微服务管理系统");
} catch (Exception ignored) {
}
remoteMessageService.publishMessage(userId, "欢迎登录RuoYi-Cloud-Plus微服务管理系统");
}, 3, TimeUnit.SECONDS);
return R.ok(loginVo);
}

View File

@ -10,10 +10,19 @@ import lombok.Data;
@Data
public class TenantListVo {
/**
*
*/
private String tenantId;
/**
*
*/
private String companyName;
/**
*
*/
private String domain;
}

View File

@ -11,7 +11,6 @@ import org.dromara.common.core.domain.model.LoginBody;
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class EmailLoginBody extends LoginBody {

View File

@ -31,6 +31,9 @@ public class RegisterBody extends LoginBody {
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
private String password;
/**
*
*/
private String userType;
}

View File

@ -10,7 +10,6 @@ import org.dromara.common.core.domain.model.LoginBody;
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SmsLoginBody extends LoginBody {

View File

@ -10,7 +10,6 @@ import org.dromara.common.core.domain.model.LoginBody;
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SocialLoginBody extends LoginBody {

View File

@ -10,7 +10,6 @@ import org.dromara.common.core.domain.model.LoginBody;
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class XcxLoginBody extends LoginBody {

View File

@ -3,7 +3,6 @@ package org.dromara.auth.listener;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import lombok.RequiredArgsConstructor;
@ -18,14 +17,13 @@ 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.resource.api.RemoteMessageService;
import org.dromara.system.api.RemoteUserService;
import org.dromara.system.api.domain.SysUserOnline;
import org.dromara.system.api.model.LoginUser;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.concurrent.ScheduledExecutorService;
/**
*
@ -38,7 +36,6 @@ import java.util.concurrent.ScheduledExecutorService;
public class UserActionListener implements SaTokenListener {
private final SaTokenConfig tokenConfig;
private final ScheduledExecutorService scheduledExecutorService;
@DubboReference
private RemoteUserService remoteUserService;
@DubboReference
@ -51,7 +48,6 @@ public class UserActionListener implements SaTokenListener {
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtils.getClientIP();
LoginUser user = LoginHelper.getLoginUser();
SysUserOnline userOnline = new SysUserOnline();
userOnline.setIpaddr(ip);
userOnline.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
@ -59,27 +55,28 @@ public class UserActionListener implements SaTokenListener {
userOnline.setOs(userAgent.getOs().getName());
userOnline.setLoginTime(System.currentTimeMillis());
userOnline.setTokenId(tokenValue);
userOnline.setUserName(user.getUsername());
userOnline.setClientKey(user.getClientKey());
userOnline.setDeviceType(user.getDeviceType());
if (ObjectUtil.isNotNull(user.getDeptName())) {
userOnline.setDeptName(user.getDeptName());
}
if (tokenConfig.getTimeout() == -1) {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline);
} else {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline, Duration.ofSeconds(tokenConfig.getTimeout()));
}
String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
userOnline.setUserName(username);
userOnline.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
userOnline.setDeviceType(loginModel.getDevice());
userOnline.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
TenantHelper.dynamic(tenantId, () -> {
if (tokenConfig.getTimeout() == -1) {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline);
} else {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, userOnline, Duration.ofSeconds(tokenConfig.getTimeout()));
}
});
// 记录登录日志
LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(user.getTenantId());
logininforEvent.setUsername(user.getUsername());
logininforEvent.setTenantId(tenantId);
logininforEvent.setUsername(username);
logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
logininforEvent.setMessage(MessageUtils.message("user.login.success"));
logininforEvent.setRequest(ServletUtils.getRequest());
SpringUtils.context().publishEvent(logininforEvent);
// 更新登录信息
remoteUserService.recordLoginInfo(user.getUserId(), ServletUtils.getClientIP());
remoteUserService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
log.info("user doLogin, useId:{}, token:{}", loginId, tokenValue);
}

View File

@ -17,6 +17,7 @@ import org.springframework.context.annotation.Configuration;
@RefreshScope
@ConfigurationProperties(prefix = "security.captcha")
public class CaptchaProperties {
/**
*
*/

View File

@ -16,6 +16,11 @@ public interface IAuthStrategy {
/**
*
*
* @param body
* @param client
* @param grantType
* @return
*/
static LoginVo login(String body, RemoteClientVo client, String grantType) {
// 授权类型和客户端id
@ -29,6 +34,10 @@ public interface IAuthStrategy {
/**
*
*
* @param body
* @param client
* @return
*/
LoginVo login(String body, RemoteClientVo client);

View File

@ -6,6 +6,7 @@ import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.lock.annotation.Lock4j;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthUser;
@ -19,11 +20,11 @@ import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.enums.TenantStatus;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.core.exception.ServiceException;
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;
@ -74,18 +75,27 @@ public class SysLoginService {
*
* @param authUserData
*/
@Lock4j
public void socialRegister(AuthUser authUserData) {
String authId = authUserData.getSource() + authUserData.getUuid();
// 第三方用户信息
RemoteSocialBo bo = BeanUtil.toBean(authUserData, RemoteSocialBo.class);
BeanUtil.copyProperties(authUserData.getToken(), bo);
bo.setUserId(LoginHelper.getUserId());
Long userId = LoginHelper.getUserId();
bo.setUserId(userId);
bo.setAuthId(authId);
bo.setOpenId(authUserData.getUuid());
bo.setUserName(authUserData.getUsername());
bo.setNickName(authUserData.getNickname());
List<RemoteSocialVo> checkList = remoteSocialService.selectByAuthId(authId);
if (CollUtil.isNotEmpty(checkList)) {
throw new ServiceException("此三方账号已经被绑定!");
}
// 查询是否已经绑定用户
List<RemoteSocialVo> list = remoteSocialService.selectByAuthId(authId);
RemoteSocialBo params = new RemoteSocialBo();
params.setUserId(userId);
params.setSource(bo.getSource());
List<RemoteSocialVo> list = remoteSocialService.queryList(params);
if (CollUtil.isEmpty(list)) {
// 没有绑定用户, 新增用户信息
remoteSocialService.insertByBo(bo);
@ -93,6 +103,8 @@ public class SysLoginService {
// 更新用户信息
bo.setId(list.get(0).getId());
remoteSocialService.updateByBo(bo);
// 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断
// throw new ServiceException("此平台账号已经被绑定!");
}
}
@ -158,7 +170,7 @@ public class SysLoginService {
* @param uuid
*/
public void validateCaptcha(String tenantId, String username, String code, String uuid) {
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
String captcha = RedisUtils.getCacheObject(verifyKey);
RedisUtils.deleteObject(verifyKey);
if (captcha == null) {
@ -186,7 +198,6 @@ public class SysLoginService {
logininforEvent.setUsername(username);
logininforEvent.setStatus(status);
logininforEvent.setMessage(message);
logininforEvent.setRequest(ServletUtils.getRequest());
SpringUtils.context().publishEvent(logininforEvent);
}

View File

@ -88,7 +88,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
* @param uuid
*/
private void validateCaptcha(String tenantId, String username, String code, String uuid) {
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
String captcha = RedisUtils.getCacheObject(verifyKey);
RedisUtils.deleteObject(verifyKey);
if (captcha == null) {

View File

@ -21,6 +21,7 @@ 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.api.RemoteSocialService;
import org.dromara.system.api.RemoteUserService;
import org.dromara.system.api.domain.vo.RemoteClientVo;
@ -80,11 +81,16 @@ public class SocialAuthStrategy implements IAuthStrategy {
if (CollUtil.isEmpty(list)) {
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
}
Optional<RemoteSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny();
if (opt.isEmpty()) {
throw new ServiceException("对不起,你没有权限登录当前租户!");
RemoteSocialVo socialVo;
if (TenantHelper.isEnable()) {
Optional<RemoteSocialVo> opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny();
if (opt.isEmpty()) {
throw new ServiceException("对不起,你没有权限登录当前租户!");
}
socialVo = opt.get();
} else {
socialVo = list.get(0);
}
RemoteSocialVo socialVo = opt.get();
LoginUser loginUser = remoteUserService.getUserInfo(socialVo.getUserId(), socialVo.getTenantId());
loginUser.setClientKey(client.getClientKey());

View File

@ -17,6 +17,8 @@ spring:
nacos:
# nacos 服务地址
server-addr: @nacos.server@
username: @nacos.username@
password: @nacos.password@
discovery:
# 注册组
group: @nacos.discovery.group@

View File

@ -42,6 +42,7 @@
<module>ruoyi-common-tenant</module>
<module>ruoyi-common-websocket</module>
<module>ruoyi-common-social</module>
<module>ruoyi-common-nacos</module>
</modules>
<artifactId>ruoyi-common</artifactId>

View File

@ -14,12 +14,12 @@
</description>
<properties>
<revision>2.1.2</revision>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
<sentinel.version>1.8.6</sentinel.version>
<revision>2.2.0-BETA</revision>
<spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
<sentinel.version>1.8.8</sentinel.version>
<seata.version>1.7.1</seata.version>
<nacos.client.version>2.2.1</nacos.client.version>
<dubbo.version>3.2.7</dubbo.version>
<nacos.client.version>2.3.2</nacos.client.version>
<dubbo.version>3.2.11</dubbo.version>
<spring.context.support.version>1.0.11</spring.context.support.version>
</properties>
<dependencyManagement>

View File

@ -14,7 +14,7 @@
</description>
<properties>
<revision>2.1.2</revision>
<revision>2.2.0-BETA</revision>
</properties>
<dependencyManagement>
@ -218,6 +218,12 @@
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-nacos</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@ -99,6 +99,11 @@
<artifactId>ip2region</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -2,6 +2,7 @@ package org.dromara.common.core.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
/**
*
@ -11,6 +12,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
@AutoConfiguration
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableAsync(proxyTargetClass = true)
public class ApplicationConfig {
}

View File

@ -1,70 +1,34 @@
package org.dromara.common.core.config;
import cn.hutool.core.util.ArrayUtil;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.Threads;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.VirtualThreadTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.Arrays;
import java.util.concurrent.*;
import java.util.concurrent.Executor;
/**
*
* <p>
* 使线
*
* @author Lion Li
*/
@Slf4j
@EnableAsync(proxyTargetClass = true)
@AutoConfiguration
public class AsyncConfig implements AsyncConfigurer {
private final int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
private ScheduledExecutorService scheduledExecutorService;
/**
*
*/
@Bean(name = "scheduledExecutorService")
public ScheduledExecutorService scheduledExecutorService() {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(corePoolSize,
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);
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);
}
}
/**
* @Async 使线
*/
@Override
public Executor getAsyncExecutor() {
if(SpringUtils.isVirtual()) {
return new VirtualThreadTaskExecutor("async-");
}
return SpringUtils.getBean("scheduledExecutorService");
}
@ -85,27 +49,4 @@ public class AsyncConfig implements AsyncConfigurer {
};
}
/**
* 线
*/
public void printException(Runnable r, Throwable t) {
if (t == null && r instanceof Future<?>) {
try {
Future<?> future = (Future<?>) r;
if (future.isDone()) {
future.get();
}
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (t != null) {
log.error(t.getMessage(), t);
}
}
}

View File

@ -0,0 +1,61 @@
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.utils.Threads;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线
*
* @author Lion Li
**/
@Slf4j
@AutoConfiguration
public class ThreadPoolConfig {
/**
* 线 = cpu + 1
*/
private final int core = Runtime.getRuntime().availableProcessors() + 1;
private ScheduledExecutorService scheduledExecutorService;
/**
*
*/
@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);
}
}
}

View File

@ -35,6 +35,11 @@ public interface CacheNames {
*/
String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
/**
*
*/
String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
/**
*
*/

View File

@ -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,}$";
/**
* 01
*/
String STATUS = "^[01]$";
}

View File

@ -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("流程状态为空!");
}
}
}

View File

@ -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);
/**
* 01
*/
public static final Pattern STATUS = get(RegexConstants.STATUS);
}

View File

@ -1,148 +0,0 @@
package org.dromara.common.core.utils;
import cn.hutool.core.convert.Convert;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ReUtil {
public final static Pattern GROUP_VAR = Pattern.compile("\\$(\\d+)");
/**
*
*/
public final static Set<Character> RE_KEYS = new HashSet<>(
Arrays.asList('$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|'));
;
/**
* <br>
* replacementTemplate$11
*
* <p>
* 12341234(1234)
*
* <pre>
* ReUtil.replaceAll("中文1234", "(\\d+)", "($1)"))
*
* (1234)
* </pre>
*
* @param content
* @param regex
* @param replacementTemplate 使$1
* @return
*/
public static String replaceAll(CharSequence content, String regex, String replacementTemplate) {
final Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
return replaceAll(content, pattern, replacementTemplate);
}
/**
* <br>
* replacementTemplate$11
*
* @param content
* @param pattern {@link Pattern}
* @param replacementTemplate 使$1
* @return
* @since 3.0.4
*/
public static String replaceAll(CharSequence content, Pattern pattern, String replacementTemplate) {
if (StringUtils.isEmpty(content)) {
return StringUtils.EMPTY;
}
final Matcher matcher = pattern.matcher(content);
boolean result = matcher.find();
if (result) {
final Set<String> varNums = findAll(GROUP_VAR, replacementTemplate, 1, new HashSet<>());
final StringBuffer sb = new StringBuffer();
do {
String replacement = replacementTemplate;
for (String var : varNums) {
int group = Integer.parseInt(var);
replacement = replacement.replace("$" + var, matcher.group(group));
}
matcher.appendReplacement(sb, escape(replacement));
result = matcher.find();
}
while (result);
matcher.appendTail(sb);
return sb.toString();
}
return Convert.toStr(content);
}
/**
*
*
* @param <T>
* @param pattern
* @param content
* @param group
* @param collection
* @return
*/
public static <T extends Collection<String>> T findAll(Pattern pattern, CharSequence content, int group,
T collection) {
if (null == pattern || null == content) {
return null;
}
if (null == collection) {
throw new NullPointerException("Null collection param provided!");
}
final Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
collection.add(matcher.group(group));
}
return collection;
}
/**
*
*
* @param c
* @return
*/
public static String escape(char c) {
final StringBuilder builder = new StringBuilder();
if (RE_KEYS.contains(c)) {
builder.append('\\');
}
builder.append(c);
return builder.toString();
}
/**
*
*
* @param content
* @return
*/
public static String escape(CharSequence content) {
if (StringUtils.isBlank(content)) {
return StringUtils.EMPTY;
}
final StringBuilder builder = new StringBuilder();
int len = content.length();
char current;
for (int i = 0; i < len; i++) {
current = content.charAt(i);
if (RE_KEYS.contains(current)) {
builder.append('\\');
}
builder.append(current);
}
return builder.toString();
}
}

View File

@ -3,13 +3,17 @@ package org.dromara.common.core.utils;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.thread.Threading;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* spring
*
* @author Lion Li
*/
@Component
public final class SpringUtils extends SpringUtil {
/**
@ -57,4 +61,8 @@ public final class SpringUtils extends SpringUtil {
return getApplicationContext();
}
public static boolean isVirtual() {
return Threading.VIRTUAL.isActive(getBean(Environment.class));
}
}

View File

@ -22,6 +22,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
public static final String SEPARATOR = ",";
public static final String SLASH = "/";
/**
*
*

View File

@ -0,0 +1,30 @@
package org.dromara.common.core.utils.regex;
import cn.hutool.core.util.ReUtil;
import org.dromara.common.core.constant.RegexConstants;
/**
*
*
* @author Feng
*/
public final class RegexUtils extends ReUtil {
/**
*
*
* @param input
* @param regex 使 {@link RegexConstants}
* @param defaultInput
* @return
*/
public static String extractFromString(String input, String regex, String defaultInput) {
try {
return ReUtil.get(regex, input, 1);
} catch (Exception e) {
return defaultInput;
}
}
}

View File

@ -0,0 +1,105 @@
package org.dromara.common.core.utils.regex;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.core.lang.Validator;
import org.dromara.common.core.factory.RegexPatternPoolFactory;
import java.util.regex.Pattern;
/**
*
*
*
* @author Feng
*/
public class RegexValidator extends Validator {
/**
* 线
*/
public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE;
/**
* 6
*/
public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6;
/**
* QQ
*/
public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER;
/**
*
*/
public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE;
/**
*
*/
public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT;
/**
* 8
*/
public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD;
/**
* 01
*/
public static final Pattern STATUS = RegexPatternPoolFactory.STATUS;
/**
*
*
* @param value
* @return true false
*/
public static boolean isAccount(CharSequence value) {
return isMatchRegex(ACCOUNT, value);
}
/**
* ValidateException
*
* @param value
* @param errorMsg
* @param <T> CharSequence
* @return
* @throws ValidateException
*/
public static <T extends CharSequence> T validateAccount(T value, String errorMsg) throws ValidateException {
if (!isAccount(value)) {
throw new ValidateException(errorMsg);
}
return value;
}
/**
*
*
* @param value
* @return true false
*/
public static boolean isStatus(CharSequence value) {
return isMatchRegex(STATUS, value);
}
/**
* ValidateException
*
* @param value
* @param errorMsg
* @param <T> CharSequence
* @return
* @throws ValidateException
*/
public static <T extends CharSequence> T validateStatus(T value, String errorMsg) throws ValidateException {
if (!isStatus(value)) {
throw new ValidateException(errorMsg);
}
return value;
}
}

View File

@ -1,4 +1,5 @@
org.dromara.common.core.utils.SpringUtils
org.dromara.common.core.config.ApplicationConfig
org.dromara.common.core.config.ValidatorConfig
org.dromara.common.core.config.ThreadPoolConfig
org.dromara.common.core.config.AsyncConfig

View File

@ -33,10 +33,6 @@
<artifactId>dubbo-spring-boot-starter</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,14 +1,14 @@
package org.dromara.common.dict.service.impl;
import cn.dev33.satoken.context.SaHolder;
import cn.hutool.core.util.ObjectUtil;
import com.github.benmanes.caffeine.cache.Cache;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.system.api.RemoteDictService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.system.api.domain.vo.RemoteDictDataVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@ -24,6 +24,9 @@ import java.util.stream.Collectors;
@Service
public class DictServiceImpl implements DictService {
@Autowired
private Cache<Object, Object> ceffeine;
@DubboReference
private RemoteDictService remoteDictService;
@ -35,16 +38,13 @@ public class DictServiceImpl implements DictService {
* @param separator
* @return
*/
@SuppressWarnings("unchecked cast")
@SuppressWarnings("unchecked")
@Override
public String getDictLabel(String dictType, String dictValue, String separator) {
// 优先从本地缓存获取
List<RemoteDictDataVo> datas = (List<RemoteDictDataVo>) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType);
if (ObjectUtil.isNull(datas)) {
datas = remoteDictService.selectDictDataByType(dictType);
SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas);
}
List<RemoteDictDataVo> datas = (List<RemoteDictDataVo>) ceffeine.get(CacheConstants.SYS_DICT_KEY + "remote:" + dictType, k -> {
return remoteDictService.selectDictDataByType(dictType);
});
Map<String, String> map = StreamUtils.toMap(datas, RemoteDictDataVo::getDictValue, RemoteDictDataVo::getDictLabel);
if (StringUtils.containsAny(dictValue, separator)) {
return Arrays.stream(dictValue.split(separator))
@ -63,16 +63,13 @@ public class DictServiceImpl implements DictService {
* @param separator
* @return
*/
@SuppressWarnings("unchecked cast")
@SuppressWarnings("unchecked")
@Override
public String getDictValue(String dictType, String dictLabel, String separator) {
// 优先从本地缓存获取
List<RemoteDictDataVo> datas = (List<RemoteDictDataVo>) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType);
if (ObjectUtil.isNull(datas)) {
datas = remoteDictService.selectDictDataByType(dictType);
SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas);
}
List<RemoteDictDataVo> datas = (List<RemoteDictDataVo>) ceffeine.get(CacheConstants.SYS_DICT_KEY + "remote:" + dictType, k -> {
return remoteDictService.selectDictDataByType(dictType);
});
Map<String, String> map = StreamUtils.toMap(datas, RemoteDictDataVo::getDictLabel, RemoteDictDataVo::getDictValue);
if (StringUtils.containsAny(dictLabel, separator)) {
return Arrays.stream(dictLabel.split(separator))

View File

@ -41,11 +41,6 @@
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
</dependency>
<!-- Sa-Token 整合 Dubbo -->
<dependency>
<groupId>cn.dev33</groupId>
@ -59,5 +54,10 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,42 @@
package org.dromara.common.dubbo.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.core.Ordered;
import java.net.Inet6Address;
import java.net.InetAddress;
/**
* dubboIP(IP)
*
* @author Lion Li
*/
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
InetUtils inetUtils = beanFactory.getBean(InetUtils.class);
String ip = "127.0.0.1";
InetAddress address = inetUtils.findFirstNonLoopbackAddress();
if (address != null) {
if (address instanceof Inet6Address) {
String ipv6AddressString = address.getHostAddress();
if (ipv6AddressString.contains("%")) {
ipv6AddressString = ipv6AddressString.substring(0, ipv6AddressString.indexOf("%"));
}
ip = ipv6AddressString;
} else {
ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
}
}
System.setProperty("DUBBO_IP_TO_REGISTRY", ip);
}
}

View File

@ -2,8 +2,10 @@ package org.dromara.common.dubbo.config;
import org.dromara.common.core.factory.YmlPropertySourceFactory;
import org.dromara.common.dubbo.properties.DubboCustomProperties;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
/**
@ -14,4 +16,8 @@ import org.springframework.context.annotation.PropertySource;
@PropertySource(value = "classpath:common-dubbo.yml", factory = YmlPropertySourceFactory.class)
public class DubboConfiguration {
@Bean
public BeanFactoryPostProcessor customBeanFactoryPostProcessor() {
return new CustomBeanFactoryPostProcessor();
}
}

View File

@ -15,6 +15,8 @@ dubbo:
registry:
address: nacos://${spring.cloud.nacos.server-addr}
group: DUBBO_GROUP
username: ${spring.cloud.nacos.username}
password: ${spring.cloud.nacos.password}
parameters:
namespace: ${spring.profiles.active}
# 消费者相关配置

View File

@ -22,11 +22,6 @@
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
@ -42,6 +37,18 @@
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

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