diff --git a/.run/ruoyi-auth.run.xml b/.run/ruoyi-auth.run.xml index 87d912cc..0b69031f 100644 --- a/.run/ruoyi-auth.run.xml +++ b/.run/ruoyi-auth.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-gateway.run.xml b/.run/ruoyi-gateway.run.xml index 953a838f..d399a727 100644 --- a/.run/ruoyi-gateway.run.xml +++ b/.run/ruoyi-gateway.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-gen.run.xml b/.run/ruoyi-gen.run.xml index ce6d4651..d0dc0e67 100644 --- a/.run/ruoyi-gen.run.xml +++ b/.run/ruoyi-gen.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-job.run.xml b/.run/ruoyi-job.run.xml index e9377d8a..4fa17309 100644 --- a/.run/ruoyi-job.run.xml +++ b/.run/ruoyi-job.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-monitor.run.xml b/.run/ruoyi-monitor.run.xml index d340665b..05292ec7 100644 --- a/.run/ruoyi-monitor.run.xml +++ b/.run/ruoyi-monitor.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-nacos.run.xml b/.run/ruoyi-nacos.run.xml index 148f9e50..3d8030ca 100644 --- a/.run/ruoyi-nacos.run.xml +++ b/.run/ruoyi-nacos.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-resource.run.xml b/.run/ruoyi-resource.run.xml index 2254b74f..5092e31a 100644 --- a/.run/ruoyi-resource.run.xml +++ b/.run/ruoyi-resource.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-seata-server.run.xml b/.run/ruoyi-seata-server.run.xml index f8618d23..9765f397 100644 --- a/.run/ruoyi-seata-server.run.xml +++ b/.run/ruoyi-seata-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-sentinel-dashboard.run.xml b/.run/ruoyi-sentinel-dashboard.run.xml index eda30c01..fd0ca162 100644 --- a/.run/ruoyi-sentinel-dashboard.run.xml +++ b/.run/ruoyi-sentinel-dashboard.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-powerjob-server.run.xml b/.run/ruoyi-snailjob-server.run.xml similarity index 68% rename from .run/ruoyi-powerjob-server.run.xml rename to .run/ruoyi-snailjob-server.run.xml index 3517aea3..e2d7e440 100644 --- a/.run/ruoyi-powerjob-server.run.xml +++ b/.run/ruoyi-snailjob-server.run.xml @@ -1,10 +1,10 @@ - + - diff --git a/.run/ruoyi-system.run.xml b/.run/ruoyi-system.run.xml index 1d91da35..a657faef 100644 --- a/.run/ruoyi-system.run.xml +++ b/.run/ruoyi-system.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-workflow.run.xml b/.run/ruoyi-workflow.run.xml new file mode 100644 index 00000000..4d1d25c5 --- /dev/null +++ b/.run/ruoyi-workflow.run.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/README.md b/README.md index d42eeb87..cf01bd66 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Cloud-Plus/blob/master/LICENSE) [![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Cloud-Plus)
-[![RuoYi-Cloud-Plus](https://img.shields.io/badge/RuoYi_Cloud_Plus-2.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
-CCFlow - https://gitee.com/opencc/RuoYi-JFlow
+MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey
+CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
+数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/
+引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group) # 本框架与RuoYi的功能差异 @@ -66,10 +68,10 @@ CCFlow - https://gitee.com/opencc/RuoYi-JFlow
| 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 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储
支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 | | 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家 | 不支持 | diff --git a/bpmn/模型.zip b/bpmn/模型.zip new file mode 100644 index 00000000..6f30952a Binary files /dev/null and b/bpmn/模型.zip differ diff --git a/config/nacos/application-common.yml b/config/nacos/application-common.yml index 346b18d2..29cd9b3f 100644 --- a/config/nacos/application-common.yml +++ b/config/nacos/application-common.yml @@ -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 diff --git a/config/nacos/datasource.yml b/config/nacos/datasource.yml index ee003009..bcf6b984 100644 --- a/config/nacos/datasource.yml +++ b/config/nacos/datasource.yml @@ -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 diff --git a/config/nacos/ruoyi-auth.yml b/config/nacos/ruoyi-auth.yml index 1dc58539..4d375882 100644 --- a/config/nacos/ruoyi-auth.yml +++ b/config/nacos/ruoyi-auth.yml @@ -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 diff --git a/config/nacos/ruoyi-gateway.yml b/config/nacos/ruoyi-gateway.yml index f818254f..b70b6d58 100644 --- a/config/nacos/ruoyi-gateway.yml +++ b/config/nacos/ruoyi-gateway.yml @@ -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 diff --git a/config/nacos/ruoyi-job.yml b/config/nacos/ruoyi-job.yml index 7041f72b..7fdfc501 100644 --- a/config/nacos/ruoyi-job.yml +++ b/config/nacos/ruoyi-job.yml @@ -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} diff --git a/config/nacos/ruoyi-powerjob-server.properties b/config/nacos/ruoyi-powerjob-server.properties deleted file mode 100644 index 2229942e..00000000 --- a/config/nacos/ruoyi-powerjob-server.properties +++ /dev/null @@ -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 diff --git a/config/nacos/ruoyi-resource.yml b/config/nacos/ruoyi-resource.yml index a49753c5..9c539725 100644 --- a/config/nacos/ruoyi-resource.yml +++ b/config/nacos/ruoyi-resource.yml @@ -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 diff --git a/config/nacos/ruoyi-snailjob-server.yml b/config/nacos/ruoyi-snailjob-server.yml new file mode 100644 index 00000000..d8dda8e2 --- /dev/null +++ b/config/nacos/ruoyi-snailjob-server.yml @@ -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 diff --git a/config/nacos/ruoyi-workflow.yml b/config/nacos/ruoyi-workflow.yml new file mode 100644 index 00000000..51cf2a6a --- /dev/null +++ b/config/nacos/ruoyi-workflow.yml @@ -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 diff --git a/config/nacos/seata-server.properties b/config/nacos/seata-server.properties index 9168baff..8ad7dade 100644 --- a/config/nacos/seata-server.properties +++ b/config/nacos/seata-server.properties @@ -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 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 386bd5e8..7a5eff86 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -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" diff --git a/docker/nginx/conf/nginx.conf b/docker/nginx/conf/nginx.conf index 12aaa65b..176ff729 100644 --- a/docker/nginx/conf/nginx.conf +++ b/docker/nginx/conf/nginx.conf @@ -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; diff --git a/docker/rabbitmq/Dockerfile b/docker/rabbitmq/Dockerfile index aa2d84b4..2fd2d79d 100644 --- a/docker/rabbitmq/Dockerfile +++ b/docker/rabbitmq/Dockerfile @@ -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 diff --git a/docker/rabbitmq/rabbitmq_delayed_message_exchange-3.10.2.ez b/docker/rabbitmq/rabbitmq_delayed_message_exchange-3.10.2.ez deleted file mode 100644 index fd3f6fad..00000000 Binary files a/docker/rabbitmq/rabbitmq_delayed_message_exchange-3.10.2.ez and /dev/null differ diff --git a/docker/rabbitmq/rabbitmq_delayed_message_exchange-3.13.0.ez b/docker/rabbitmq/rabbitmq_delayed_message_exchange-3.13.0.ez new file mode 100644 index 00000000..70819318 Binary files /dev/null and b/docker/rabbitmq/rabbitmq_delayed_message_exchange-3.13.0.ez differ diff --git a/pom.xml b/pom.xml index d9277ad4..2c6c29c6 100644 --- a/pom.xml +++ b/pom.xml @@ -13,37 +13,36 @@ RuoYi-Cloud-Plus微服务系统 - 2.1.2 + 2.2.0-BETA UTF-8 UTF-8 17 - 3.1.7 - 2022.0.4 - 3.1.8 - 3.0.3 - 3.5.13 - 3.5.4 + 3.2.6 + 2023.0.2 + 3.2.3 + 3.5.16 + 3.5.6 3.9.1 - 4.2.0 + 4.3.0 2.3 - 2.2.15 - 2.2.0 + 2.2.21 + 2.5.0 0.15.0 5.2.3 - 3.3.3 - 5.8.22 - 3.24.3 - 2.2.5 - 4.3.6 - 1.37.0 - 1.18.30 + 3.3.4 + 5.8.27 + 3.29.0 + 2.2.7 + 1.0.0-beta3 + 1.38.0 + 1.18.32 7.4 - 2.0.0-beta4 + 2.0.0 7.14.0 8.16.0 1.76 2.14.4 - 1.3.5 + 1.3.6 0.2.0 1.16.6 @@ -52,11 +51,16 @@ 1.2.83 - 1.12.600 + 2.25.15 + 0.29.13 4.10.0 - 2.2.0 + 3.2.1 + + 7.0.0 + + 2.3.0 3.11.0 @@ -73,6 +77,8 @@ 127.0.0.1:8848 DEFAULT_GROUP DEFAULT_GROUP + nacos + nacos 127.0.0.1:4560 @@ -87,6 +93,8 @@ 127.0.0.1:8848 DEFAULT_GROUP DEFAULT_GROUP + nacos + nacos 127.0.0.1:4560 @@ -157,6 +165,14 @@ import + + org.flowable + flowable-bom + ${flowable.version} + pom + import + + cn.dev33 sa-token-core @@ -169,13 +185,6 @@ ${satoken.version} - - - org.mybatis.spring.boot - mybatis-spring-boot-starter - ${spring-boot.mybatis} - - org.mybatis mybatis @@ -184,7 +193,7 @@ com.baomidou - mybatis-plus-boot-starter + mybatis-plus-spring-boot3-starter ${mybatis-plus.version} @@ -266,16 +275,16 @@ ${lock4j.version} - + - tech.powerjob - powerjob-worker - ${powerjob.version} + com.aizuda + snail-job-client-starter + ${snailjob.version} - tech.powerjob - powerjob-official-processors - ${powerjob.version} + com.aizuda + snail-job-client-job-core + ${snailjob.version} @@ -326,10 +335,23 @@ ${okhttp.version} + - com.amazonaws - aws-java-sdk-s3 - ${aws-java-sdk-s3.version} + software.amazon.awssdk + s3 + ${aws.sdk.version} + + + + software.amazon.awssdk.crt + aws-crt + ${aws.crt.version} + + + + software.amazon.awssdk + s3-transfer-manager + ${aws.sdk.version} @@ -364,6 +386,13 @@ ${mapstruct-plus.version} + + + org.apache.rocketmq + rocketmq-spring-boot-starter + ${rocketmq.version} + + diff --git a/ruoyi-api/pom.xml b/ruoyi-api/pom.xml index 2701751b..8b5f0b9e 100644 --- a/ruoyi-api/pom.xml +++ b/ruoyi-api/pom.xml @@ -12,6 +12,7 @@ ruoyi-api-bom ruoyi-api-system ruoyi-api-resource + ruoyi-api-workflow ruoyi-api diff --git a/ruoyi-api/ruoyi-api-bom/pom.xml b/ruoyi-api/ruoyi-api-bom/pom.xml index 46b61345..d8a6ad02 100644 --- a/ruoyi-api/ruoyi-api-bom/pom.xml +++ b/ruoyi-api/ruoyi-api-bom/pom.xml @@ -15,7 +15,7 @@ - 2.1.2 + 2.2.0-BETA @@ -34,6 +34,13 @@ ${revision} + + + org.dromara + ruoyi-api-workflow + ${revision} + + diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java index 6ab788e4..2204de62 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java @@ -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 selectByIds(String ossIds); } diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java index c689f2d1..2b79e22f 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java @@ -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 selectByIds(String ossIds) { + log.warn("服务调用异常 -> 降级处理"); + return List.of(); + } + } diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageService.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageService.java index 6b2ecb53..c6ea3f51 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageService.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageService.java @@ -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); } diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageServiceStub.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageServiceStub.java index 813fba63..3c9ec8bf 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageServiceStub.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteMessageServiceStub.java @@ -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 功能未开启或服务未找到"); } diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteSmsService.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteSmsService.java index ad4cbea2..1a34eba1 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteSmsService.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteSmsService.java @@ -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 param) throws ServiceException; + RemoteSms sendMessage(String phone, String message); + + /** + * 同步方法:发送固定消息模板多模板参数短信 + * + * @param phone 目标手机号 + * @param messages 短信模板参数,使用 LinkedHashMap 以保持参数顺序 + * @return 封装了短信发送结果的 RemoteSms 对象 + */ + RemoteSms sendMessage(String phone, LinkedHashMap messages); + + /** + * 同步方法:使用自定义模板发送短信 + * + * @param phone 目标手机号 + * @param templateId 短信模板ID + * @param messages 短信模板参数,使用 LinkedHashMap 以保持参数顺序 + * @return 封装了短信发送结果的 RemoteSms 对象 + */ + RemoteSms sendMessage(String phone, String templateId, LinkedHashMap messages); + + /** + * 同步方法:群发固定模板短信 + * + * @param phones 目标手机号列表(1~1000) + * @param message 短信内容 + * @return 封装了短信发送结果的 RemoteSms 对象 + */ + RemoteSms messageTexting(List phones, String message); + + /** + * 同步方法:使用自定义模板群发短信 + * + * @param phones 目标手机号列表(1~1000)(1~1000) + * @param templateId 短信模板ID + * @param messages 短信模板参数,使用 LinkedHashMap 以保持参数顺序 + * @return 封装了短信发送结果的 RemoteSms 对象 + */ + RemoteSms messageTexting(List phones, String templateId, LinkedHashMap 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 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 messages, Long delayedTime); + + /** + * 延迟群发:群发延迟短信 + * + * @param phones 目标手机号列表(1~1000) + * @param message 短信内容 + * @param delayedTime 延迟发送时间(毫秒) + */ + void delayMessageTexting(List phones, String message, Long delayedTime); + + /** + * 延迟群发:使用自定义模板发送群体延迟短信 + * + * @param phones 目标手机号列表(1~1000) + * @param templateId 短信模板ID + * @param messages 短信模板参数,使用 LinkedHashMap 以保持参数顺序 + * @param delayedTime 延迟发送时间(毫秒) + */ + void delayMessageTexting(List phones, String templateId, LinkedHashMap messages, Long delayedTime); + + /** + * 加入黑名单 + * + * @param phone 手机号 + */ + void addBlacklist(String phone); + + /** + * 加入黑名单 + * + * @param phones 手机号列表 + */ + void addBlacklist(List phones); + + /** + * 移除黑名单 + * + * @param phone 手机号 + */ + void removeBlacklist(String phone); + + /** + * 移除黑名单 + * + * @param phones 手机号 + */ + void removeBlacklist(List phones); } diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java index ecc4b16a..7140fe63 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java @@ -31,4 +31,14 @@ public class RemoteFile implements Serializable { */ private String url; + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + } diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteSms.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteSms.java index 430eb5bb..3e35d5ba 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteSms.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteSms.java @@ -19,15 +19,15 @@ public class RemoteSms implements Serializable { /** * 是否成功 */ - private Boolean isSuccess; + private Boolean success; /** - * 响应消息 + * 配置标识名 如未配置取对应渠道名例如 Alibaba */ - private String message; + private String configId; /** - * 实际响应体 + * 厂商原返回体 *

* 可自行转换为 SDK 对应的 SendSmsResponse */ diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDataScopeService.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDataScopeService.java index ceac940b..d6a40056 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDataScopeService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDataScopeService.java @@ -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); diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDictService.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDictService.java index 142dc45f..69fc30c8 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDictService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteDictService.java @@ -18,4 +18,5 @@ public interface RemoteDictService { * @return 字典数据集合信息 */ List selectDictDataByType(String dictType); + } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteLogService.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteLogService.java index af084a29..6b35118d 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteLogService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteLogService.java @@ -23,4 +23,5 @@ public interface RemoteLogService { * @param sysLogininfor 访问实体 */ void saveLogininfor(RemoteLogininforBo sysLogininfor); + } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteSocialService.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteSocialService.java index ff61c12b..0a54df56 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteSocialService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteSocialService.java @@ -13,22 +13,39 @@ import java.util.List; public interface RemoteSocialService { /** - * 根据 authId 查询用户信息 + * 根据 authId 查询用户授权信息 + * + * @param authId 认证id + * @return 授权信息 */ List selectByAuthId(String authId); + /** + * 查询列表 + * + * @param bo 社会化关系业务对象 + */ + List queryList(RemoteSocialBo bo); + /** * 保存社会化关系 + * + * @param bo 社会化关系业务对象 */ void insertByBo(RemoteSocialBo bo); /** * 更新社会化关系 + * + * @param bo 社会化关系业务对象 */ void updateByBo(RemoteSocialBo bo); /** * 删除社会化关系 + * + * @param socialId 社会化关系ID + * @return 结果 */ Boolean deleteWithValidById(Long socialId); diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteTenantService.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteTenantService.java index 5cfcc056..2626abed 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteTenantService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteTenantService.java @@ -6,6 +6,8 @@ import org.dromara.system.api.domain.vo.RemoteTenantVo; import java.util.List; /** + * 租户服务 + * * @author zhujie */ public interface RemoteTenantService { diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java index 8401e610..b71eaa85 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java @@ -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 selectListByIds(List userIds); + + /** + * 通过角色ID查询用户ID + * + * @param roleIds 角色ids + * @return 用户ids + */ + List selectUserIdsByRoleIds(List roleIds); } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/SysUserOnline.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/SysUserOnline.java index 4ad3c06c..25c985ad 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/SysUserOnline.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/SysUserOnline.java @@ -11,7 +11,6 @@ import java.io.Serializable; * * @author Lion Li */ - @Data @NoArgsConstructor public class SysUserOnline implements Serializable { diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteLogininforBo.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteLogininforBo.java index ac2c4115..d8c48a92 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteLogininforBo.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteLogininforBo.java @@ -14,7 +14,6 @@ import java.util.Map; * * @author Lion Li */ - @Data @NoArgsConstructor public class RemoteLogininforBo implements Serializable { diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteOperLogBo.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteOperLogBo.java index aad49f65..5ed441b8 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteOperLogBo.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteOperLogBo.java @@ -14,7 +14,6 @@ import java.util.Map; * * @author Lion Li */ - @Data @NoArgsConstructor public class RemoteOperLogBo implements Serializable { diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteUserBo.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteUserBo.java index 5d21b09a..0306b224 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteUserBo.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/bo/RemoteUserBo.java @@ -19,7 +19,6 @@ import java.util.Date; * * @author Michelle.Chung */ - @Data @NoArgsConstructor public class RemoteUserBo implements Serializable { diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteClientVo.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteClientVo.java index 400f56b7..e141f2b6 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteClientVo.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteClientVo.java @@ -67,5 +67,4 @@ public class RemoteClientVo implements Serializable { */ private String status; - } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteSocialVo.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteSocialVo.java index 71e8d58a..b3272f56 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteSocialVo.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteSocialVo.java @@ -32,7 +32,7 @@ public class RemoteSocialVo implements Serializable { private String tenantId; /** - * 的唯一ID + * 认证唯一ID */ private String authId; diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteUserVo.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteUserVo.java new file mode 100644 index 00000000..fb7f589c --- /dev/null +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/vo/RemoteUserVo.java @@ -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; + +} diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/LoginUser.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/LoginUser.java index 2463bd0a..405a87f0 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/LoginUser.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/model/LoginUser.java @@ -35,6 +35,11 @@ public class LoginUser implements Serializable { */ private Long deptId; + /** + * 部门类别编码 + */ + private String deptCategory; + /** * 部门名 */ diff --git a/ruoyi-api/ruoyi-api-workflow/pom.xml b/ruoyi-api/ruoyi-api-workflow/pom.xml new file mode 100644 index 00000000..5e9df772 --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/pom.xml @@ -0,0 +1,28 @@ + + + + org.dromara + ruoyi-api + ${revision} + + 4.0.0 + + ruoyi-api-workflow + + + ruoyi-api-workflow 工作流接口模块 + + + + + + + org.dromara + ruoyi-common-core + + + + + diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteWorkflowService.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteWorkflowService.java new file mode 100644 index 00000000..140255eb --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteWorkflowService.java @@ -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 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 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 variables); + + /** + * 按照业务id查询流程实例id + * + * @param businessKey 业务id + * @return 结果 + */ + String getInstanceIdByBusinessKey(String businessKey); + +} diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessEvent.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessEvent.java new file mode 100644 index 00000000..5b7b121f --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessEvent.java @@ -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; + +} diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessTaskEvent.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessTaskEvent.java new file mode 100644 index 00000000..8ba2caef --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessTaskEvent.java @@ -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; + +} diff --git a/ruoyi-auth/Dockerfile b/ruoyi-auth/Dockerfile index f6721d21..406bb65d 100644 --- a/ruoyi-auth/Dockerfile +++ b/ruoyi-auth/Dockerfile @@ -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 diff --git a/ruoyi-auth/pom.xml b/ruoyi-auth/pom.xml index a09df551..980d27e5 100644 --- a/ruoyi-auth/pom.xml +++ b/ruoyi-auth/pom.xml @@ -16,16 +16,9 @@ - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-config + org.dromara + ruoyi-common-nacos @@ -87,12 +80,6 @@ org.dromara ruoyi-common-tenant - - - org.dromara - ruoyi-common-mybatis - - org.dromara diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/config/CaptchaConfig.java b/ruoyi-auth/src/main/java/org/dromara/auth/config/CaptchaConfig.java index 58800167..e9804a07 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/config/CaptchaConfig.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/config/CaptchaConfig.java @@ -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); /** diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/controller/CaptchaController.java b/ruoyi-auth/src/main/java/org/dromara/auth/controller/CaptchaController.java index 63d002cf..1537cb5f 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/controller/CaptchaController.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/controller/CaptchaController.java @@ -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(); diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/controller/TokenController.java b/ruoyi-auth/src/main/java/org/dromara/auth/controller/TokenController.java index 6424a6a0..f4ae3941 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/controller/TokenController.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/controller/TokenController.java @@ -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); } diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/TenantListVo.java b/ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/TenantListVo.java index b993b37a..523e0f8e 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/TenantListVo.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/domain/vo/TenantListVo.java @@ -10,10 +10,19 @@ import lombok.Data; @Data public class TenantListVo { + /** + * 租户编号 + */ private String tenantId; + /** + * 企业名称 + */ private String companyName; + /** + * 域名 + */ private String domain; } diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/form/EmailLoginBody.java b/ruoyi-auth/src/main/java/org/dromara/auth/form/EmailLoginBody.java index 931e2364..5c4fc724 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/form/EmailLoginBody.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/form/EmailLoginBody.java @@ -11,7 +11,6 @@ import org.dromara.common.core.domain.model.LoginBody; * * @author Lion Li */ - @Data @EqualsAndHashCode(callSuper = true) public class EmailLoginBody extends LoginBody { diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/form/RegisterBody.java b/ruoyi-auth/src/main/java/org/dromara/auth/form/RegisterBody.java index 386c0fc7..dc620766 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/form/RegisterBody.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/form/RegisterBody.java @@ -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; } diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/form/SmsLoginBody.java b/ruoyi-auth/src/main/java/org/dromara/auth/form/SmsLoginBody.java index 48e262f3..7271b638 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/form/SmsLoginBody.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/form/SmsLoginBody.java @@ -10,7 +10,6 @@ import org.dromara.common.core.domain.model.LoginBody; * * @author Lion Li */ - @Data @EqualsAndHashCode(callSuper = true) public class SmsLoginBody extends LoginBody { diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/form/SocialLoginBody.java b/ruoyi-auth/src/main/java/org/dromara/auth/form/SocialLoginBody.java index cbd61c93..faf8aa6d 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/form/SocialLoginBody.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/form/SocialLoginBody.java @@ -10,7 +10,6 @@ import org.dromara.common.core.domain.model.LoginBody; * * @author Lion Li */ - @Data @EqualsAndHashCode(callSuper = true) public class SocialLoginBody extends LoginBody { diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/form/XcxLoginBody.java b/ruoyi-auth/src/main/java/org/dromara/auth/form/XcxLoginBody.java index c68306c1..3c8a7f53 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/form/XcxLoginBody.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/form/XcxLoginBody.java @@ -10,7 +10,6 @@ import org.dromara.common.core.domain.model.LoginBody; * * @author Lion Li */ - @Data @EqualsAndHashCode(callSuper = true) public class XcxLoginBody extends LoginBody { diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java b/ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java index 86df42c7..c4a1a0e1 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/listener/UserActionListener.java @@ -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); } diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/properties/CaptchaProperties.java b/ruoyi-auth/src/main/java/org/dromara/auth/properties/CaptchaProperties.java index 1cd70982..3f05d9ba 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/properties/CaptchaProperties.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/properties/CaptchaProperties.java @@ -17,6 +17,7 @@ import org.springframework.context.annotation.Configuration; @RefreshScope @ConfigurationProperties(prefix = "security.captcha") public class CaptchaProperties { + /** * 验证码类型 */ diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/service/IAuthStrategy.java b/ruoyi-auth/src/main/java/org/dromara/auth/service/IAuthStrategy.java index 0bc36577..28307c38 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/service/IAuthStrategy.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/service/IAuthStrategy.java @@ -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); diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/service/SysLoginService.java b/ruoyi-auth/src/main/java/org/dromara/auth/service/SysLoginService.java index eb2d7e64..eacdc01b 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/service/SysLoginService.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/service/SysLoginService.java @@ -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 checkList = remoteSocialService.selectByAuthId(authId); + if (CollUtil.isNotEmpty(checkList)) { + throw new ServiceException("此三方账号已经被绑定!"); + } // 查询是否已经绑定用户 - List list = remoteSocialService.selectByAuthId(authId); + RemoteSocialBo params = new RemoteSocialBo(); + params.setUserId(userId); + params.setSource(bo.getSource()); + List 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); } diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/service/impl/PasswordAuthStrategy.java b/ruoyi-auth/src/main/java/org/dromara/auth/service/impl/PasswordAuthStrategy.java index 9550485c..db4ea945 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/service/impl/PasswordAuthStrategy.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/service/impl/PasswordAuthStrategy.java @@ -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) { diff --git a/ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SocialAuthStrategy.java b/ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SocialAuthStrategy.java index ea1bde11..1b652297 100644 --- a/ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SocialAuthStrategy.java +++ b/ruoyi-auth/src/main/java/org/dromara/auth/service/impl/SocialAuthStrategy.java @@ -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 opt = list.stream().filter(x -> x.getTenantId().equals(loginBody.getTenantId())).findAny(); - if (opt.isEmpty()) { - throw new ServiceException("对不起,你没有权限登录当前租户!"); + RemoteSocialVo socialVo; + if (TenantHelper.isEnable()) { + Optional 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()); diff --git a/ruoyi-auth/src/main/resources/application.yml b/ruoyi-auth/src/main/resources/application.yml index 24f58103..90c05356 100644 --- a/ruoyi-auth/src/main/resources/application.yml +++ b/ruoyi-auth/src/main/resources/application.yml @@ -17,6 +17,8 @@ spring: nacos: # nacos 服务地址 server-addr: @nacos.server@ + username: @nacos.username@ + password: @nacos.password@ discovery: # 注册组 group: @nacos.discovery.group@ diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index ff3ea10f..204db8dd 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -42,6 +42,7 @@ ruoyi-common-tenant ruoyi-common-websocket ruoyi-common-social + ruoyi-common-nacos ruoyi-common diff --git a/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml b/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml index 4a7cb385..47dce94d 100644 --- a/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml @@ -14,12 +14,12 @@ - 2.1.2 - 2022.0.0.0 - 1.8.6 + 2.2.0-BETA + 2023.0.1.0 + 1.8.8 1.7.1 - 2.2.1 - 3.2.7 + 2.3.2 + 3.2.11 1.0.11 diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 043824eb..6ac85cd7 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -14,7 +14,7 @@ - 2.1.2 + 2.2.0-BETA @@ -218,6 +218,12 @@ ${revision} + + org.dromara + ruoyi-common-nacos + ${revision} + + diff --git a/ruoyi-common/ruoyi-common-core/pom.xml b/ruoyi-common/ruoyi-common-core/pom.xml index 01d876e7..84ab453a 100644 --- a/ruoyi-common/ruoyi-common-core/pom.xml +++ b/ruoyi-common/ruoyi-common-core/pom.xml @@ -99,6 +99,11 @@ ip2region + + com.alibaba + transmittable-thread-local + + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java index 07500ba1..d1200870 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ApplicationConfig.java @@ -2,6 +2,7 @@ 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 { } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java index 5a8714e8..cd01e33d 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/AsyncConfig.java @@ -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; /** * 异步配置 + *

+ * 如果未使用虚拟线程则生效 * * @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); - } - } - } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java new file mode 100644 index 00000000..d96d77b0 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ThreadPoolConfig.java @@ -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); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java index e59277aa..28ba1773 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/CacheNames.java @@ -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"; + /** * 用户账户 */ diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java new file mode 100644 index 00000000..77eed8c0 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/constant/RegexConstants.java @@ -0,0 +1,54 @@ +package org.dromara.common.core.constant; + +import cn.hutool.core.lang.RegexPool; + +/** + * 常用正则表达式字符串 + *

+ * 常用正则表达式集合,更多正则见: 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]$"; + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/BusinessStatusEnum.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/BusinessStatusEnum.java new file mode 100644 index 00000000..0af943a7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/BusinessStatusEnum.java @@ -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("流程状态为空!"); + } + } +} + diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java new file mode 100644 index 00000000..fd907d2c --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/factory/RegexPatternPoolFactory.java @@ -0,0 +1,52 @@ +package org.dromara.common.core.factory; + +import cn.hutool.core.lang.PatternPool; +import org.dromara.common.core.constant.RegexConstants; + +import java.util.regex.Pattern; + +/** + * 正则表达式模式池工厂 + *

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

+ *

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

+ * + * @author 21001 + */ +public class RegexPatternPoolFactory extends PatternPool { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE); + + /** + * 身份证号码(后6位) + */ + public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6); + + /** + * QQ号码 + */ + public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER); + + /** + * 邮政编码 + */ + public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE); + + /** + * 注册账号 + */ + public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT); + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + public static final Pattern PASSWORD = get(RegexConstants.PASSWORD); + + /** + * 通用状态(0表示正常,1表示停用) + */ + public static final Pattern STATUS = get(RegexConstants.STATUS); + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ReUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ReUtil.java deleted file mode 100644 index 2de7f4f8..00000000 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ReUtil.java +++ /dev/null @@ -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 RE_KEYS = new HashSet<>( - Arrays.asList('$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|')); - ; - - /** - * 正则替换指定值
- * 通过正则查找到字符串,然后把匹配到的字符串加入到replacementTemplate中,$1表示分组1的字符串 - * - *

- * 例如:原字符串是:中文1234,我想把1234换成(1234),则可以: - * - *

-     * ReUtil.replaceAll("中文1234", "(\\d+)", "($1)"))
-     *
-     * 结果:中文(1234)
-     * 
- * - * @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); - } - - /** - * 正则替换指定值
- * 通过正则查找到字符串,然后把匹配到的字符串加入到replacementTemplate中,$1表示分组1的字符串 - * - * @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 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 集合类型 - * @param pattern 编译后的正则模式 - * @param content 被查找的内容 - * @param group 正则的分组 - * @param collection 返回的集合类型 - * @return 结果集 - */ - public static > 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(); - } - -} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java index 0af4d51d..e58c394a 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java @@ -3,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)); + } + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java index 5e4db50e..dd6ebb11 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/StringUtils.java @@ -22,6 +22,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils { public static final String SEPARATOR = ","; + public static final String SLASH = "/"; + /** * 获取参数不为空值 * diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java new file mode 100644 index 00000000..b8b12d43 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexUtils.java @@ -0,0 +1,30 @@ +package org.dromara.common.core.utils.regex; + + +import cn.hutool.core.util.ReUtil; +import org.dromara.common.core.constant.RegexConstants; + +/** + * 正则相关工具类 + * + * @author Feng + */ +public final class RegexUtils extends ReUtil { + + /** + * 从输入字符串中提取匹配的部分,如果没有匹配则返回默认值 + * + * @param input 要提取的输入字符串 + * @param regex 用于匹配的正则表达式,可以使用 {@link RegexConstants} 中定义的常量 + * @param defaultInput 如果没有匹配时返回的默认值 + * @return 如果找到匹配的部分,则返回匹配的部分,否则返回默认值 + */ + public static String extractFromString(String input, String regex, String defaultInput) { + try { + return ReUtil.get(regex, input, 1); + } catch (Exception e) { + return defaultInput; + } + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java new file mode 100644 index 00000000..c0dda202 --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/regex/RegexValidator.java @@ -0,0 +1,105 @@ +package org.dromara.common.core.utils.regex; + +import cn.hutool.core.exceptions.ValidateException; +import cn.hutool.core.lang.Validator; +import org.dromara.common.core.factory.RegexPatternPoolFactory; + +import java.util.regex.Pattern; + +/** + * 正则字段校验器 + * 主要验证字段非空、是否为满足指定格式等 + * + * @author Feng + */ +public class RegexValidator extends Validator { + + /** + * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) + */ + public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE; + + /** + * 身份证号码(后6位) + */ + public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6; + + /** + * QQ号码 + */ + public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER; + + /** + * 邮政编码 + */ + public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE; + + /** + * 注册账号 + */ + public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT; + + /** + * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符 + */ + public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD; + + /** + * 通用状态(0表示正常,1表示停用) + */ + public static final Pattern STATUS = RegexPatternPoolFactory.STATUS; + + + /** + * 检查输入的账号是否匹配预定义的规则 + * + * @param value 要验证的账号 + * @return 如果账号符合规则,返回 true;否则,返回 false。 + */ + public static boolean isAccount(CharSequence value) { + return isMatchRegex(ACCOUNT, value); + } + + /** + * 验证输入的账号是否符合规则,如果不符合,则抛出 ValidateException 异常 + * + * @param value 要验证的账号 + * @param errorMsg 验证失败时抛出的异常消息 + * @param CharSequence 的子类型 + * @return 如果验证通过,返回输入的账号 + * @throws ValidateException 如果验证失败 + */ + public static 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 CharSequence 的子类型 + * @return 如果验证通过,返回输入的状态 + * @throws ValidateException 如果验证失败 + */ + public static T validateStatus(T value, String errorMsg) throws ValidateException { + if (!isStatus(value)) { + throw new ValidateException(errorMsg); + } + return value; + } + +} diff --git a/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index ddd30254..b82846e2 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/ruoyi-common/ruoyi-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -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 diff --git a/ruoyi-common/ruoyi-common-core/src/main/resources/ip2region.xdb b/ruoyi-common/ruoyi-common-core/src/main/resources/ip2region.xdb index 31f96a1f..7052c057 100644 Binary files a/ruoyi-common/ruoyi-common-core/src/main/resources/ip2region.xdb and b/ruoyi-common/ruoyi-common-core/src/main/resources/ip2region.xdb differ diff --git a/ruoyi-common/ruoyi-common-dict/pom.xml b/ruoyi-common/ruoyi-common-dict/pom.xml index 6cf4efce..e7dcf05f 100644 --- a/ruoyi-common/ruoyi-common-dict/pom.xml +++ b/ruoyi-common/ruoyi-common-dict/pom.xml @@ -33,10 +33,6 @@ dubbo-spring-boot-starter provided - - cn.dev33 - sa-token-core - diff --git a/ruoyi-common/ruoyi-common-dict/src/main/java/org/dromara/common/dict/service/impl/DictServiceImpl.java b/ruoyi-common/ruoyi-common-dict/src/main/java/org/dromara/common/dict/service/impl/DictServiceImpl.java index 2de6c554..62a5d45b 100644 --- a/ruoyi-common/ruoyi-common-dict/src/main/java/org/dromara/common/dict/service/impl/DictServiceImpl.java +++ b/ruoyi-common/ruoyi-common-dict/src/main/java/org/dromara/common/dict/service/impl/DictServiceImpl.java @@ -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 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 datas = (List) 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 datas = (List) ceffeine.get(CacheConstants.SYS_DICT_KEY + "remote:" + dictType, k -> { + return remoteDictService.selectDictDataByType(dictType); + }); Map 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 datas = (List) 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 datas = (List) ceffeine.get(CacheConstants.SYS_DICT_KEY + "remote:" + dictType, k -> { + return remoteDictService.selectDictDataByType(dictType); + }); Map map = StreamUtils.toMap(datas, RemoteDictDataVo::getDictLabel, RemoteDictDataVo::getDictValue); if (StringUtils.containsAny(dictLabel, separator)) { return Arrays.stream(dictLabel.split(separator)) diff --git a/ruoyi-common/ruoyi-common-dubbo/pom.xml b/ruoyi-common/ruoyi-common-dubbo/pom.xml index ce5bb3e5..4ff44a9d 100644 --- a/ruoyi-common/ruoyi-common-dubbo/pom.xml +++ b/ruoyi-common/ruoyi-common-dubbo/pom.xml @@ -41,11 +41,6 @@ lombok - - cn.dev33 - sa-token-spring-boot3-starter - - cn.dev33 @@ -59,5 +54,10 @@ + + org.springframework.cloud + spring-cloud-commons + + diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/dromara/common/dubbo/config/CustomBeanFactoryPostProcessor.java b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/dromara/common/dubbo/config/CustomBeanFactoryPostProcessor.java new file mode 100644 index 00000000..0384cd7e --- /dev/null +++ b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/dromara/common/dubbo/config/CustomBeanFactoryPostProcessor.java @@ -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; + +/** + * dubbo自定义IP注入(避免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); + } +} diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/dromara/common/dubbo/config/DubboConfiguration.java b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/dromara/common/dubbo/config/DubboConfiguration.java index 6fc622ef..9d50dd82 100644 --- a/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/dromara/common/dubbo/config/DubboConfiguration.java +++ b/ruoyi-common/ruoyi-common-dubbo/src/main/java/org/dromara/common/dubbo/config/DubboConfiguration.java @@ -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(); + } } diff --git a/ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml b/ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml index fdc21f6a..45324774 100644 --- a/ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml +++ b/ruoyi-common/ruoyi-common-dubbo/src/main/resources/common-dubbo.yml @@ -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} # 消费者相关配置 diff --git a/ruoyi-common/ruoyi-common-encrypt/pom.xml b/ruoyi-common/ruoyi-common-encrypt/pom.xml index df3222be..ed4910ef 100644 --- a/ruoyi-common/ruoyi-common-encrypt/pom.xml +++ b/ruoyi-common/ruoyi-common-encrypt/pom.xml @@ -22,11 +22,6 @@ ruoyi-common-core - - org.mybatis.spring.boot - mybatis-spring-boot-starter - - org.bouncycastle bcprov-jdk15to18 @@ -42,6 +37,18 @@ spring-webmvc + + com.baomidou + mybatis-plus-spring-boot3-starter + true + + + org.mybatis + mybatis-spring + + + + diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java index e988a3a2..17543792 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java @@ -1,11 +1,14 @@ package org.dromara.common.encrypt.config; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties; import org.dromara.common.encrypt.core.EncryptorManager; import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor; import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor; import org.dromara.common.encrypt.properties.EncryptorProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -16,8 +19,9 @@ import org.springframework.context.annotation.Bean; * @author 老马 * @version 4.6.0 */ -@AutoConfiguration -@EnableConfigurationProperties(EncryptorProperties.class) +@AutoConfiguration(after = MybatisPlusAutoConfiguration.class) +@EnableConfigurationProperties({EncryptorProperties.class, MybatisPlusProperties.class}) +@ConditionalOnClass(MybatisPlusAutoConfiguration.class) @ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true") public class EncryptorAutoConfiguration { @@ -25,8 +29,8 @@ public class EncryptorAutoConfiguration { private EncryptorProperties properties; @Bean - public EncryptorManager encryptorManager() { - return new EncryptorManager(); + public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) { + return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage()); } @Bean diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java index 782995d8..a6d3cf9c 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java @@ -1,14 +1,23 @@ package org.dromara.common.encrypt.core; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; -import org.dromara.common.encrypt.annotation.EncryptField; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.io.Resources; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.annotation.EncryptField; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.util.ClassUtils; import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -19,6 +28,7 @@ import java.util.stream.Collectors; * @version 4.6.0 */ @Slf4j +@NoArgsConstructor public class EncryptorManager { /** @@ -31,25 +41,24 @@ public class EncryptorManager { */ Map, Set> fieldCache = new ConcurrentHashMap<>(); + /** + * 构造方法传入类加密字段缓存 + * + * @param typeAliasesPackage 实体类包 + */ + public EncryptorManager(String typeAliasesPackage) { + scanEncryptClasses(typeAliasesPackage); + } + + /** * 获取类加密字段缓存 */ public Set getFieldCache(Class sourceClazz) { - return fieldCache.computeIfAbsent(sourceClazz, clazz -> { - Set fieldSet = new HashSet<>(); - while (clazz != null) { - Field[] fields = clazz.getDeclaredFields(); - fieldSet.addAll(Arrays.asList(fields)); - clazz = clazz.getSuperclass(); - } - fieldSet = fieldSet.stream().filter(field -> - field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class) - .collect(Collectors.toSet()); - for (Field field : fieldSet) { - field.setAccessible(true); - } - return fieldSet; - }); + if (ObjectUtil.isNotNull(fieldCache)) { + return fieldCache.get(sourceClazz); + } + return null; } /** @@ -97,4 +106,53 @@ public class EncryptorManager { return encryptor.decrypt(value); } + /** + * 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体 + */ + private void scanEncryptClasses(String typeAliasesPackage) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; + try { + for (String packagePattern : packagePatternArray) { + String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + Resource[] resources = resolver.getResources(classpath + path + "/*.class"); + for (Resource resource : resources) { + ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); + Class clazz = Resources.classForName(classMetadata.getClassName()); + Set encryptFieldSet = getEncryptFieldSetFromClazz(clazz); + if (CollUtil.isNotEmpty(encryptFieldSet)) { + fieldCache.put(clazz, encryptFieldSet); + } + } + } + } catch (Exception e) { + log.error("初始化数据安全缓存时出错:{}", e.getMessage()); + } + } + + /** + * 获得一个类的加密字段集合 + */ + private Set getEncryptFieldSetFromClazz(Class clazz) { + Set fieldSet = new HashSet<>(); + // 判断clazz如果是接口,内部类,匿名类就直接返回 + if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) { + return fieldSet; + } + while (clazz != null) { + Field[] fields = clazz.getDeclaredFields(); + fieldSet.addAll(Arrays.asList(fields)); + clazz = clazz.getSuperclass(); + } + fieldSet = fieldSet.stream().filter(field -> + field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class) + .collect(Collectors.toSet()); + for (Field field : fieldSet) { + field.setAccessible(true); + } + return fieldSet; + } + } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java index 8d898c04..98351321 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java @@ -11,7 +11,6 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.ApiEncrypt; import org.dromara.common.encrypt.properties.ApiDecryptProperties; import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExecutionChain; @@ -36,42 +35,38 @@ public class CryptoFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest servletRequest = (HttpServletRequest) request; HttpServletResponse servletResponse = (HttpServletResponse) response; - - boolean responseFlag = false; + // 获取加密注解 + ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest); + boolean responseFlag = apiEncrypt != null && apiEncrypt.response(); ServletRequest requestWrapper = null; ServletResponse responseWrapper = null; EncryptResponseBodyWrapper responseBodyWrapper = null; - // 是否为 json 请求 - if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { - // 是否为 put 或者 post 请求 - if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) { - // 是否存在加密标头 - String headerValue = servletRequest.getHeader(properties.getHeaderFlag()); - // 获取加密注解 - ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest); - responseFlag = apiEncrypt != null && apiEncrypt.response(); - if (StringUtils.isNotBlank(headerValue)) { - // 请求解密 - requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag()); - } else { - // 是否有注解,有就报错,没有放行 - if (ObjectUtil.isNotNull(apiEncrypt)) { - HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class); - exceptionResolver.resolveException( - servletRequest, servletResponse, null, - new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN)); - return; - } - } - // 判断是否响应加密 - if (responseFlag) { - responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse); - responseWrapper = responseBodyWrapper; + // 是否为 put 或者 post 请求 + if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) { + // 是否存在加密标头 + String headerValue = servletRequest.getHeader(properties.getHeaderFlag()); + if (StringUtils.isNotBlank(headerValue)) { + // 请求解密 + requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag()); + } else { + // 是否有注解,有就报错,没有放行 + if (ObjectUtil.isNotNull(apiEncrypt)) { + HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class); + exceptionResolver.resolveException( + servletRequest, servletResponse, null, + new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN)); + return; } } } + // 判断是否响应加密 + if (responseFlag) { + responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse); + responseWrapper = responseBodyWrapper; + } + chain.doFilter( ObjectUtil.defaultIfNull(requestWrapper, request), ObjectUtil.defaultIfNull(responseWrapper, response)); diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java index 5eb34a71..6b3b563f 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java @@ -79,6 +79,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey); // 设置响应头 + servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag); servletResponse.setHeader(headerFlag, encryptPassword); servletResponse.setHeader("Access-Control-Allow-Origin", "*"); servletResponse.setHeader("Access-Control-Allow-Methods", "*"); diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java index 7c2508f8..460aa360 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java @@ -73,7 +73,11 @@ public class MybatisDecryptInterceptor implements Interceptor { list.forEach(this::decryptHandler); return; } + // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if(ObjectUtil.isNull(fields)){ + return; + } try { for (Field field : fields) { field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field)); diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java index 152f7db4..bcc2f4c9 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java @@ -82,7 +82,11 @@ public class MybatisEncryptInterceptor implements Interceptor { list.forEach(this::encryptHandler); return; } + // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if(ObjectUtil.isNull(fields)){ + return; + } try { for (Field field : fields) { field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field)); diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java index bbdaaa14..6b9211b5 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/annotation/CellMerge.java @@ -21,4 +21,9 @@ public @interface CellMerge { */ int index() default -1; + /** + * 合并需要依赖的其他字段名称 + */ + String[] mergeBy() default {}; + } diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java index bcb5be76..7c0a48b9 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeStrategy.java @@ -1,8 +1,12 @@ package org.dromara.common.excel.core; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; import com.alibaba.excel.write.merge.AbstractMergeStrategy; import lombok.AllArgsConstructor; import lombok.Data; @@ -15,10 +19,7 @@ import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.excel.annotation.CellMerge; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 列值重复合并策略 @@ -26,7 +27,7 @@ import java.util.Map; * @author Lion Li */ @Slf4j -public class CellMergeStrategy extends AbstractMergeStrategy { +public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler { private final List cellList; private final boolean hasTitle; @@ -41,17 +42,28 @@ public class CellMergeStrategy extends AbstractMergeStrategy { @Override protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { - // judge the list is not null - if (CollUtil.isNotEmpty(cellList)) { - // the judge is necessary - if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == 0) { - for (CellRangeAddress item : cellList) { - sheet.addMergedRegion(item); + //单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空 + final int rowIndex = cell.getRowIndex(); + if (CollUtil.isNotEmpty(cellList)){ + for (CellRangeAddress cellAddresses : cellList) { + final int firstRow = cellAddresses.getFirstRow(); + if (cellAddresses.isInRange(cell) && rowIndex != firstRow){ + cell.setBlank(); } } } } + @Override + public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) { + //当前表格写完后,统一写入 + if (CollUtil.isNotEmpty(cellList)){ + for (CellRangeAddress item : cellList) { + context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item); + } + } + } + @SneakyThrows private List handle(List list, boolean hasTitle) { List cellList = new ArrayList<>(); @@ -93,35 +105,15 @@ public class CellMergeStrategy extends AbstractMergeStrategy { // 空值跳过不合并 continue; } + if (!cellValue.equals(val)) { - if (i - repeatCell.getCurrent() > 1) { + if ((i - repeatCell.getCurrent() > 1) && isMerge(list, i, field)) { cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); } map.put(field, new RepeatCell(val, i)); - } else if (j == 0) { - if (i == list.size() - 1) { - if (i > repeatCell.getCurrent()) { - cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); - } - } - } else { - // 判断前面的是否合并了 - RepeatCell firstCell = map.get(mergeFields.get(0)); - if (repeatCell.getCurrent() != firstCell.getCurrent()) { - if (i == list.size() - 1) { - if (i > repeatCell.getCurrent()) { - cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); - } - } else if (repeatCell.getCurrent() < firstCell.getCurrent()) { - if (i - repeatCell.getCurrent() > 1) { - cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum)); - } - map.put(field, new RepeatCell(val, i)); - } - } else if (i == list.size() - 1) { - if (i > repeatCell.getCurrent()) { - cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); - } + } else if (i == list.size() - 1) { + if (i > repeatCell.getCurrent() && isMerge(list, i, field)) { + cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex, colNum, colNum)); } } } @@ -130,6 +122,24 @@ public class CellMergeStrategy extends AbstractMergeStrategy { return cellList; } + private boolean isMerge(List list, int i, Field field) { + boolean isMerge = true; + CellMerge cm = field.getAnnotation(CellMerge.class); + final String[] mergeBy = cm.mergeBy(); + if (StrUtil.isAllNotBlank(mergeBy)) { + //比对当前list(i)和list(i - 1)的各个属性值一一比对 如果全为真 则为真 + for (String fieldName : mergeBy) { + final Object valCurrent = ReflectUtil.getFieldValue(list.get(i), fieldName); + final Object valPre = ReflectUtil.getFieldValue(list.get(i - 1), fieldName); + if (!Objects.equals(valPre, valCurrent)) { + //依赖字段如有任一不等值,则标记为不可合并 + isMerge = false; + } + } + } + return isMerge; + } + @Data @AllArgsConstructor static class RepeatCell { diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index 3b791ea5..b3f68ed1 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -20,6 +20,7 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.DictService; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.annotation.ExcelEnumFormat; @@ -99,15 +100,16 @@ public class ExcelDownHandler implements SheetWriteHandler { ExcelDictFormat format = field.getDeclaredAnnotation(ExcelDictFormat.class); String dictType = format.dictType(); String converterExp = format.readConverterExp(); - if (StrUtil.isNotBlank(dictType)) { + if (StringUtils.isNotBlank(dictType)) { // 如果传递了字典名,则依据字典建立下拉 Collection values = Optional.ofNullable(dictService.getAllDictByDictType(dictType)) .orElseThrow(() -> new ServiceException(String.format("字典 %s 不存在", dictType))) .values(); options = new ArrayList<>(values); - } else if (StrUtil.isNotBlank(converterExp)) { + } else if (StringUtils.isNotBlank(converterExp)) { // 如果指定了确切的值,则直接解析确切的值 - options = StrUtil.split(converterExp, format.separator(), true, true); + List strList = StringUtils.splitList(converterExp, format.separator()); + options = StreamUtils.toList(strList, s -> StringUtils.split(s, "=")[1]); } } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) { // 否则如果指定了@ExcelEnumFormat,则使用枚举的逻辑 diff --git a/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java index fc53b1b0..2afc85af 100644 --- a/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java +++ b/ruoyi-common/ruoyi-common-idempotent/src/main/java/org/dromara/common/idempotent/aspectj/RepeatSubmitAspect.java @@ -4,6 +4,13 @@ import cn.dev33.satoken.SaManager; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.crypto.SecureUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.domain.R; import org.dromara.common.core.exception.ServiceException; @@ -13,13 +20,6 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.redis.utils.RedisUtils; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.AfterReturning; -import org.aspectj.lang.annotation.AfterThrowing; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; import org.springframework.validation.BindingResult; import org.springframework.web.multipart.MultipartFile; @@ -127,7 +127,7 @@ public class RepeatSubmitAspect { public boolean isFilterObject(final Object o) { Class clazz = o.getClass(); if (clazz.isArray()) { - return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); } else if (Collection.class.isAssignableFrom(clazz)) { Collection collection = (Collection) o; for (Object value : collection) { diff --git a/ruoyi-common/ruoyi-common-job/pom.xml b/ruoyi-common/ruoyi-common-job/pom.xml index 0df7fee4..2a08c7da 100644 --- a/ruoyi-common/ruoyi-common-job/pom.xml +++ b/ruoyi-common/ruoyi-common-job/pom.xml @@ -28,20 +28,16 @@ spring-cloud-commons - + - tech.powerjob - powerjob-worker - - - powerjob-remote-impl-akka - tech.powerjob - - + com.aizuda + snail-job-client-starter + ${snailjob.version} - tech.powerjob - powerjob-official-processors + com.aizuda + snail-job-client-job-core + ${snailjob.version} diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java deleted file mode 100644 index 35612f3b..00000000 --- a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/PowerJobConfig.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.dromara.common.job.config; - -import cn.hutool.core.collection.CollUtil; -import org.dromara.common.core.utils.StreamUtils; -import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.job.config.properties.PowerJobProperties; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.context.annotation.Bean; -import tech.powerjob.common.utils.CommonUtils; -import tech.powerjob.common.utils.NetUtils; -import tech.powerjob.worker.PowerJobSpringWorker; -import tech.powerjob.worker.common.PowerJobWorkerConfig; - -import java.util.Arrays; -import java.util.List; - -/** - * Autoconfiguration class for PowerJob-worker. - * - * @author songyinyin - * @since 2020/7/26 16:37 - */ -@AutoConfiguration -@EnableConfigurationProperties(PowerJobProperties.class) -@ConditionalOnProperty(prefix = "powerjob.worker", name = "enabled", havingValue = "true", matchIfMissing = true) -public class PowerJobConfig{ - - @Bean - public PowerJobSpringWorker initPowerJob(PowerJobProperties properties, DiscoveryClient discoveryClient) { - - PowerJobProperties.Worker worker = properties.getWorker(); - - /* - * Address of PowerJob-server node(s). Do not mistake for ActorSystem port. Do not add - * any prefix, i.e. http://. - */ - List serverAddress; - if (StringUtils.isNotBlank(worker.getServerName())) { - List instances = discoveryClient.getInstances(worker.getServerName()); - if (CollUtil.isEmpty(instances)) { - throw new RuntimeException("调度中心不存在!"); - } - serverAddress = StreamUtils.toList(instances, instance -> - String.format("%s:%s", instance.getHost(), instance.getPort())); - } else { - CommonUtils.requireNonNull(worker.getServerAddress(), "serverAddress can't be empty! " + - "if you don't want to enable powerjob, please config program arguments: powerjob.worker.enabled=false"); - serverAddress = Arrays.asList(worker.getServerAddress().split(",")); - } - /* - * Create OhMyConfig object for setting properties. - */ - PowerJobWorkerConfig config = new PowerJobWorkerConfig(); - /* - * Configuration of worker port. Random port is enabled when port is set with non-positive number. - */ - if (worker.getPort() != null) { - config.setPort(worker.getPort()); - } else { - int port = worker.getAkkaPort(); - if (port <= 0) { - port = NetUtils.getRandomPort(); - } - config.setPort(port); - } - /* - * appName, name of the application. Applications should be registered in advance to prevent - * error. This property should be the same with what you entered for appName when getting - * registered. - */ - config.setAppName(worker.getAppName()); - config.setServerAddress(serverAddress); - config.setProtocol(worker.getProtocol()); - /* - * For non-Map/MapReduce tasks, {@code memory} is recommended for speeding up calculation. - * Map/MapReduce tasks may produce batches of subtasks, which could lead to OutOfMemory - * exception or error, {@code disk} should be applied. - */ - config.setStoreStrategy(worker.getStoreStrategy()); - /* - * When enabledTestMode is set as true, PowerJob-worker no longer connects to PowerJob-server - * or validate appName. - */ - config.setAllowLazyConnectServer(worker.isAllowLazyConnectServer()); - /* - * Max length of appended workflow context . Appended workflow context value that is longer than the value will be ignored. - */ - config.setMaxAppendedWfContextLength(worker.getMaxAppendedWfContextLength()); - - config.setTag(worker.getTag()); - - config.setMaxHeavyweightTaskNum(worker.getMaxHeavyweightTaskNum()); - - config.setMaxLightweightTaskNum(worker.getMaxLightweightTaskNum()); - - config.setHealthReportInterval(worker.getHealthReportInterval()); - /* - * Create PowerJobSpringWorker object and set properties. - */ - return new PowerJobSpringWorker(config); - } - -} diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java new file mode 100644 index 00000000..163e43f7 --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/SnailJobConfig.java @@ -0,0 +1,78 @@ +package org.dromara.common.job.config; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import cn.hutool.core.collection.CollUtil; +import com.aizuda.snailjob.client.common.appender.SnailLogbackAppender; +import com.aizuda.snailjob.client.common.event.SnailChannelReconnectEvent; +import com.aizuda.snailjob.client.common.event.SnailClientStartingEvent; +import com.aizuda.snailjob.client.starter.EnableSnailJob; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.job.config.properties.SnailJobServerProperties; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.util.List; + +/** + * 启动定时任务 + * + * @author dhb52 + * @since 2024/3/12 + */ +@AutoConfiguration +@EnableConfigurationProperties(SnailJobServerProperties.class) +@ConditionalOnProperty(prefix = "snail-job", name = "enabled", havingValue = "true") +@EnableScheduling +@EnableSnailJob +public class SnailJobConfig { + + @Autowired + private SnailJobServerProperties properties; + @Autowired + private DiscoveryClient discoveryClient; + + @EventListener(SnailClientStartingEvent.class) + public void onStarting(SnailClientStartingEvent event) { + // 从 nacos 获取 server 服务连接 + registerServer(); + // 注册 日志监控配置 + registerLogging(); + } + + @EventListener(SnailChannelReconnectEvent.class) + public void onReconnect(SnailChannelReconnectEvent event) { + // 连接中断 重新从 nacos 获取存活的服务连接(高可用配置) + registerServer(); + } + + private void registerServer() { + String serverName = properties.getServerName(); + if (StringUtils.isNotBlank(serverName)) { + List instances = discoveryClient.getInstances(serverName); + if (CollUtil.isNotEmpty(instances)) { + ServiceInstance instance = instances.get(0); + System.setProperty("snail-job.server.host", instance.getHost()); + System.setProperty("snail-job.server.port", properties.getPort()); + } + } + } + + private void registerLogging() { + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + SnailLogbackAppender ca = new SnailLogbackAppender<>(); + ca.setName("snail_log_appender"); + ca.start(); + Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.addAppender(ca); + } + +} diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/properties/PowerJobProperties.java b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/properties/PowerJobProperties.java deleted file mode 100644 index df5f2853..00000000 --- a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/properties/PowerJobProperties.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.dromara.common.job.config.properties; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import tech.powerjob.common.RemoteConstant; -import tech.powerjob.common.enums.Protocol; -import tech.powerjob.worker.common.constants.StoreStrategy; -import tech.powerjob.worker.core.processor.ProcessResult; -import tech.powerjob.worker.core.processor.WorkflowContext; - -/** - * PowerJob properties configuration class. - * - * @author songyinyin - * @since 2020/7/26 16:37 - */ -@ConfigurationProperties(prefix = "powerjob") -public class PowerJobProperties { - - private final Worker worker = new Worker(); - - public Worker getWorker() { - return worker; - } - - /** - * Powerjob worker configuration properties. - */ - @Setter - @Getter - public static class Worker { - - /** - * Whether to enable PowerJob Worker - */ - private boolean enabled = true; - - /** - * Name of application, String type. Total length of this property should be no more than 255 - * characters. This is one of the required properties when registering a new application. This - * property should be assigned with the same value as what you entered for the appName. - */ - private String appName; - /** - * Akka port of Powerjob-worker, optional value. Default value of this property is 27777. - * If multiple PowerJob-worker nodes were deployed, different, unique ports should be assigned. - * Deprecated, please use 'port' - */ - @Deprecated - private int akkaPort = RemoteConstant.DEFAULT_WORKER_PORT; - /** - * port - */ - private Integer port; - /** - * Address(es) of Powerjob-server node(s). Ip:port or domain. - * Example of single Powerjob-server node: - *

- * 127.0.0.1:7700 - *

- * Example of Powerjob-server cluster: - *

- * 192.168.0.10:7700,192.168.0.11:7700,192.168.0.12:7700 - *

- */ - private String serverAddress; - - private String serverName; - /** - * Protocol for communication between WORKER and server - */ - private Protocol protocol = Protocol.AKKA; - /** - * Local store strategy for H2 database. {@code disk} or {@code memory}. - */ - private StoreStrategy storeStrategy = StoreStrategy.DISK; - /** - * Max length of response result. Result that is longer than the value will be truncated. - * {@link ProcessResult} max length for #msg - */ - private int maxResultLength = 8192; - /** - * If allowLazyConnectServer is set as true, PowerJob worker allows launching without a direct connection to the server. - * allowLazyConnectServer is used for conditions that your have no powerjob-server in your develop env so you can't startup the application - */ - private boolean allowLazyConnectServer = false; - /** - * Max length of appended workflow context value length. Appended workflow context value that is longer than the value will be ignored. - * {@link WorkflowContext} max length for #appendedContextData - */ - private int maxAppendedWfContextLength = 8192; - - private String tag; - /** - * Max numbers of LightTaskTacker - */ - private Integer maxLightweightTaskNum = 1024; - /** - * Max numbers of HeavyTaskTacker - */ - private Integer maxHeavyweightTaskNum = 64; - /** - * Interval(s) of worker health report - */ - private Integer healthReportInterval = 10; - - } -} diff --git a/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/properties/SnailJobServerProperties.java b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/properties/SnailJobServerProperties.java new file mode 100644 index 00000000..8b3fe000 --- /dev/null +++ b/ruoyi-common/ruoyi-common-job/src/main/java/org/dromara/common/job/config/properties/SnailJobServerProperties.java @@ -0,0 +1,14 @@ +package org.dromara.common.job.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties(prefix = "snail-job.server") +public class SnailJobServerProperties { + + private String serverName; + + private String port; + +} diff --git a/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 222bcc15..3aa18813 100644 --- a/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/ruoyi-common/ruoyi-common-job/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1 @@ -org.dromara.common.job.config.PowerJobConfig +org.dromara.common.job.config.SnailJobConfig diff --git a/ruoyi-common/ruoyi-common-log/pom.xml b/ruoyi-common/ruoyi-common-log/pom.xml index cbda66f8..2614957f 100644 --- a/ruoyi-common/ruoyi-common-log/pom.xml +++ b/ruoyi-common/ruoyi-common-log/pom.xml @@ -27,11 +27,6 @@ ruoyi-common-json
- - com.alibaba - transmittable-thread-local - - org.apache.dubbo dubbo-spring-boot-starter diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java index 06beca6a..7fe20bc4 100644 --- a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java +++ b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java @@ -4,7 +4,6 @@ import cn.hutool.core.lang.Dict; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.ttl.TransmittableThreadLocal; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -49,9 +48,9 @@ public class LogAspect { /** - * 计算操作消耗时间 + * 计时 key */ - private static final ThreadLocal TIME_THREADLOCAL = new TransmittableThreadLocal<>(); + private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); /** * 处理请求前执行 @@ -59,7 +58,7 @@ public class LogAspect { @Before(value = "@annotation(controllerLog)") public void boBefore(JoinPoint joinPoint, Log controllerLog) { StopWatch stopWatch = new StopWatch(); - TIME_THREADLOCAL.set(stopWatch); + KEY_CACHE.set(stopWatch); stopWatch.start(); } @@ -112,7 +111,7 @@ public class LogAspect { // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); // 设置消耗时间 - StopWatch stopWatch = TIME_THREADLOCAL.get(); + StopWatch stopWatch = KEY_CACHE.get(); stopWatch.stop(); operLog.setCostTime(stopWatch.getTime()); // 发布事件保存数据库 @@ -122,7 +121,7 @@ public class LogAspect { log.error("异常信息:{}", exp.getMessage()); exp.printStackTrace(); } finally { - TIME_THREADLOCAL.remove(); + KEY_CACHE.remove(); } } @@ -161,7 +160,7 @@ public class LogAspect { Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); String requestMethod = operLog.getRequestMethod(); if (MapUtil.isEmpty(paramsMap) - && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { + && HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) { String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); operLog.setOperParam(StringUtils.substring(params, 0, 2000)); } else { @@ -204,7 +203,7 @@ public class LogAspect { public boolean isFilterObject(final Object o) { Class clazz = o.getClass(); if (clazz.isArray()) { - return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); } else if (Collection.class.isAssignableFrom(clazz)) { Collection collection = (Collection) o; for (Object value : collection) { diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogEventListener.java b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogEventListener.java index 3697e585..3584e0c6 100644 --- a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogEventListener.java +++ b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogEventListener.java @@ -18,7 +18,6 @@ import org.dromara.system.api.domain.bo.RemoteLogininforBo; import org.dromara.system.api.domain.bo.RemoteOperLogBo; import org.dromara.system.api.domain.vo.RemoteClientVo; import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** @@ -38,24 +37,25 @@ public class LogEventListener { /** * 保存系统日志记录 */ - @Async @EventListener public void saveLog(OperLogEvent operLogEvent) { RemoteOperLogBo sysOperLog = BeanUtil.toBean(operLogEvent, RemoteOperLogBo.class); remoteLogService.saveLog(sysOperLog); } - @Async + /** + * 保存系统访问记录 + */ @EventListener public void saveLogininfor(LogininforEvent logininforEvent) { - HttpServletRequest request = logininforEvent.getRequest(); + HttpServletRequest request = ServletUtils.getRequest(); final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); final String ip = ServletUtils.getClientIP(request); // 客户端信息 - String clientid = request.getHeader(LoginHelper.CLIENT_KEY); + String clientId = request.getHeader(LoginHelper.CLIENT_KEY); RemoteClientVo clientVo = null; - if (StringUtils.isNotBlank(clientid)) { - clientVo = remoteClientService.queryByClientId(clientid); + if (StringUtils.isNotBlank(clientId)) { + clientVo = remoteClientService.queryByClientId(clientId); } String address = AddressUtils.getRealAddressByIP(ip); diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogininforEvent.java b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogininforEvent.java index 938eaadd..212bf18b 100644 --- a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogininforEvent.java +++ b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/event/LogininforEvent.java @@ -2,8 +2,6 @@ package org.dromara.common.log.event; import lombok.Data; -import jakarta.servlet.http.HttpServletRequest; - import java.io.Serial; import java.io.Serializable; @@ -39,11 +37,6 @@ public class LogininforEvent implements Serializable { */ private String message; - /** - * 请求体 - */ - private HttpServletRequest request; - /** * 其他参数 */ diff --git a/ruoyi-common/ruoyi-common-mybatis/pom.xml b/ruoyi-common/ruoyi-common-mybatis/pom.xml index 64876fb2..d25f4f4d 100644 --- a/ruoyi-common/ruoyi-common-mybatis/pom.xml +++ b/ruoyi-common/ruoyi-common-mybatis/pom.xml @@ -27,20 +27,9 @@ true - - org.mybatis.spring.boot - mybatis-spring-boot-starter - - com.baomidou - mybatis-plus-boot-starter - - - org.mybatis - mybatis-spring - - + mybatis-plus-spring-boot3-starter diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfiguration.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfiguration.java index e0ec34b4..065a6fad 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfiguration.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfiguration.java @@ -1,34 +1,33 @@ package org.dromara.common.mybatis.config; import cn.hutool.core.net.NetUtil; -import com.baomidou.mybatisplus.autoconfigure.DdlApplicationRunner; -import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator; import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; -import com.baomidou.mybatisplus.extension.ddl.IDdl; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import org.dromara.common.core.factory.YmlPropertySourceFactory; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler; +import org.dromara.common.mybatis.handler.MybatisExceptionHandler; import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor; +import org.dromara.common.mybatis.service.SysDataScopeService; import org.mybatis.spring.annotation.MapperScan; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.BeansException; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import org.springframework.transaction.annotation.EnableTransactionManagement; -import java.util.List; - /** * mybatis-plus配置类(下方注释有插件介绍) * * @author Lion Li */ +@AutoConfiguration @EnableTransactionManagement(proxyTargetClass = true) -@AutoConfiguration(before = MybatisPlusAutoConfiguration.class) @MapperScan("${mybatis-plus.mapperPackage}") @PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class) public class MybatisPlusConfiguration { @@ -36,6 +35,12 @@ public class MybatisPlusConfiguration { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 多租户插件 必须放到第一位 + try { + TenantLineInnerInterceptor tenant = SpringUtils.getBean(TenantLineInnerInterceptor.class); + interceptor.addInnerInterceptor(tenant); + } catch (BeansException ignore) { + } // 数据权限处理 interceptor.addInnerInterceptor(dataPermissionInterceptor()); // 分页插件 @@ -49,7 +54,7 @@ public class MybatisPlusConfiguration { * 数据权限拦截器 */ public PlusDataPermissionInterceptor dataPermissionInterceptor() { - return new PlusDataPermissionInterceptor(); + return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage")); } /** @@ -57,8 +62,6 @@ public class MybatisPlusConfiguration { */ public PaginationInnerInterceptor paginationInnerInterceptor() { PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); - // 设置最大单页限制数量,默认 500 条,-1 不受限制 - paginationInnerInterceptor.setMaxLimit(-1L); // 分页合理化 paginationInnerInterceptor.setOverflow(true); return paginationInnerInterceptor; @@ -88,6 +91,22 @@ public class MybatisPlusConfiguration { return new DefaultIdentifierGenerator(NetUtil.getLocalhost()); } + /** + * 异常处理器 + */ + @Bean + public MybatisExceptionHandler mybatisExceptionHandler() { + return new MybatisExceptionHandler(); + } + + /** + * 数据权限处理实现 + */ + @Bean("sdss") + public SysDataScopeService sysDataScopeService() { + return new SysDataScopeService(); + } + /** * PaginationInnerInterceptor 分页插件,自动识别数据库类型 * https://baomidou.com/pages/97710a/ @@ -108,9 +127,4 @@ public class MybatisPlusConfiguration { * https://baomidou.com/pages/2a45ff/ */ - @Bean - public DdlApplicationRunner ddlApplicationRunner(@Autowired(required = false) List ddlList) { - return new DdlApplicationRunner(ddlList); - } - } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java index b14e2d25..00411984 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java @@ -144,6 +144,10 @@ public interface BaseMapperPlus extends BaseMapper { return selectVoOne(wrapper, this.currentVoClass()); } + default V selectVoOne(Wrapper wrapper, boolean throwEx) { + return selectVoOne(wrapper, this.currentVoClass(), throwEx); + } + /** * 根据 entity 条件,查询一条记录 */ @@ -155,6 +159,17 @@ public interface BaseMapperPlus extends BaseMapper { return MapstructUtils.convert(obj, voClass); } + /** + * 根据 entity 条件,查询一条记录 + */ + default C selectVoOne(Wrapper wrapper, Class voClass, boolean throwEx) { + T obj = this.selectOne(wrapper, throwEx); + if (ObjectUtil.isNull(obj)) { + return null; + } + return MapstructUtils.convert(obj, voClass); + } + default List selectVoList() { return selectVoList(new QueryWrapper<>(), this.currentVoClass()); } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java index 8ef4a578..f66fd165 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java @@ -4,10 +4,11 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.Data; +import lombok.NoArgsConstructor; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.sql.SqlUtil; -import lombok.Data; import java.io.Serial; import java.io.Serializable; @@ -21,6 +22,7 @@ import java.util.List; */ @Data +@NoArgsConstructor public class PageQuery implements Serializable { @Serial @@ -111,4 +113,8 @@ public class PageQuery implements Serializable { return list; } + public Integer getFirstNum() { + return (pageNum - 1) * pageSize; + } + } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java index c4ff3bf3..ce754733 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/InjectionMetaObjectHandler.java @@ -29,16 +29,17 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler { ? baseEntity.getCreateTime() : new Date(); baseEntity.setCreateTime(current); baseEntity.setUpdateTime(current); - LoginUser loginUser = getLoginUser(); - if (ObjectUtil.isNotNull(loginUser)) { - Long userId = ObjectUtil.isNotNull(baseEntity.getCreateBy()) - ? baseEntity.getCreateBy() : loginUser.getUserId(); - // 当前已登录 且 创建人为空 则填充 - baseEntity.setCreateBy(userId); - // 当前已登录 且 更新人为空 则填充 - baseEntity.setUpdateBy(userId); - baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept()) - ? baseEntity.getCreateDept() : loginUser.getDeptId()); + if (ObjectUtil.isNull(baseEntity.getCreateBy())) { + LoginUser loginUser = getLoginUser(); + if (ObjectUtil.isNotNull(loginUser)) { + Long userId = loginUser.getUserId(); + // 当前已登录 且 创建人为空 则填充 + baseEntity.setCreateBy(userId); + // 当前已登录 且 更新人为空 则填充 + baseEntity.setUpdateBy(userId); + baseEntity.setCreateDept(ObjectUtil.isNotNull(baseEntity.getCreateDept()) + ? baseEntity.getCreateDept() : loginUser.getDeptId()); + } } } } catch (Exception e) { @@ -54,10 +55,10 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler { Date current = new Date(); // 更新时间填充(不管为不为空) baseEntity.setUpdateTime(current); - LoginUser loginUser = getLoginUser(); // 当前已登录 更新人填充(不管为不为空) - if (ObjectUtil.isNotNull(loginUser)) { - baseEntity.setUpdateBy(loginUser.getUserId()); + Long userId = LoginHelper.getUserId(); + if (ObjectUtil.isNotNull(userId)) { + baseEntity.setUpdateBy(userId); } } } catch (Exception e) { diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java index c517fa72..ec3ee0df 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/MybatisExceptionHandler.java @@ -2,6 +2,7 @@ package org.dromara.common.mybatis.handler; import org.dromara.common.core.domain.R; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.StringUtils; import org.mybatis.spring.MyBatisSystemException; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -35,7 +36,7 @@ public class MybatisExceptionHandler { public R handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); String message = e.getMessage(); - if ("CannotFindDataSourceException".contains(message)) { + if (StringUtils.contains("CannotFindDataSourceException", message)) { log.error("请求地址'{}', 未找到数据源", requestURI); return R.fail("未找到数据源,请联系管理员确认"); } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java index dc4f9cb7..fd7dd34d 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java @@ -2,7 +2,6 @@ package org.dromara.common.mybatis.handler; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ObjectUtil; import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.JSQLParserException; @@ -10,6 +9,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Parenthesis; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.apache.ibatis.io.Resources; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; @@ -21,16 +21,26 @@ import org.dromara.common.mybatis.helper.DataPermissionHelper; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.api.model.LoginUser; import org.dromara.system.api.model.RoleDTO; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.expression.BeanResolver; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.ClassUtils; import java.lang.reflect.Method; -import java.util.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -58,9 +68,13 @@ public class PlusDataPermissionHandler { */ private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); + public PlusDataPermissionHandler(String mapperPackage) { + scanMapperClasses(mapperPackage); + } + public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { - DataColumn[] dataColumns = findAnnotation(mappedStatementId); + DataPermission dataPermission = getDataPermission(mappedStatementId); LoginUser currentUser = DataPermissionHelper.getVariable("user"); if (ObjectUtil.isNull(currentUser)) { currentUser = LoginHelper.getLoginUser(); @@ -70,7 +84,7 @@ public class PlusDataPermissionHandler { if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { return where; } - String dataFilterSql = buildDataFilter(dataColumns, isSelect); + String dataFilterSql = buildDataFilter(dataPermission.value(), isSelect); if (StringUtils.isBlank(dataFilterSql)) { return where; } @@ -144,43 +158,64 @@ public class PlusDataPermissionHandler { return ""; } - public DataColumn[] findAnnotation(String mappedStatementId) { - StringBuilder sb = new StringBuilder(mappedStatementId); - int index = sb.lastIndexOf("."); - String clazzName = sb.substring(0, index); - String methodName = sb.substring(index + 1, sb.length()); - Class clazz; + /** + * 通过 mapperPackage 设置的扫描包 扫描缓存有注解的方法与类 + */ + private void scanMapperClasses(String mapperPackage) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); + String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); + String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; try { - clazz = ClassUtil.loadClass(clazzName); + for (String packagePattern : packagePatternArray) { + String path = ClassUtils.convertClassNameToResourcePath(packagePattern); + Resource[] resources = resolver.getResources(classpath + path + "/*.class"); + for (Resource resource : resources) { + ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); + Class clazz = Resources.classForName(classMetadata.getClassName()); + findAnnotation(clazz); + } + } } catch (Exception e) { - return null; + log.error("初始化数据安全缓存时出错:{}", e.getMessage()); } - List methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz)) - .filter(method -> method.getName().equals(methodName)).toList(); + } + + private void findAnnotation(Class clazz) { DataPermission dataPermission; // 获取方法注解 - for (Method method : methods) { - dataPermission = dataPermissionCacheMap.get(mappedStatementId); - if (ObjectUtil.isNotNull(dataPermission)) { - return dataPermission.value(); + for (Method method : clazz.getMethods()) { + if (method.isDefault() || method.isVarArgs()) { + continue; } + String mappedStatementId = clazz.getName() + "." + method.getName(); if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) { dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class); dataPermissionCacheMap.put(mappedStatementId, dataPermission); - return dataPermission.value(); } } - dataPermission = dataPermissionCacheMap.get(clazz.getName()); - if (ObjectUtil.isNotNull(dataPermission)) { - return dataPermission.value(); - } // 获取类注解 if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); dataPermissionCacheMap.put(clazz.getName(), dataPermission); - return dataPermission.value(); + } + } + + public DataPermission getDataPermission(String mapperId) { + if (dataPermissionCacheMap.containsKey(mapperId)) { + return dataPermissionCacheMap.get(mapperId); + } + String clazzName = mapperId.substring(0, mapperId.lastIndexOf(".")); + if (dataPermissionCacheMap.containsKey(clazzName)) { + return dataPermissionCacheMap.get(clazzName); } return null; } + /** + * 是否无效 + */ + public boolean invalid(String mapperId) { + return getDataPermission(mapperId) == null; + } } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java index 0ab0c113..6eed8f77 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java @@ -1,18 +1,16 @@ package org.dromara.common.mybatis.interceptor; -import cn.hutool.core.collection.ConcurrentHashSet; -import cn.hutool.core.util.ArrayUtil; import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import com.baomidou.mybatisplus.core.toolkit.PluginUtils; -import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport; +import com.baomidou.mybatisplus.extension.plugins.handler.MultiDataPermissionHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.BaseMultiTableInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; -import org.dromara.common.mybatis.annotation.DataColumn; -import org.dromara.common.mybatis.handler.PlusDataPermissionHandler; +import lombok.extern.slf4j.Slf4j; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; -import net.sf.jsqlparser.statement.select.SelectBody; import net.sf.jsqlparser.statement.select.SetOperationList; import net.sf.jsqlparser.statement.update.Update; import org.apache.ibatis.executor.Executor; @@ -22,11 +20,11 @@ import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; +import org.dromara.common.mybatis.handler.PlusDataPermissionHandler; import java.sql.Connection; import java.sql.SQLException; import java.util.List; -import java.util.Set; /** * 数据权限拦截器 @@ -34,13 +32,14 @@ import java.util.Set; * @author Lion Li * @version 3.5.0 */ -public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { +@Slf4j +public class PlusDataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor { - private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); - /** - * 无效注解方法缓存用于快速返回 - */ - private final Set invalidCacheSet = new ConcurrentHashSet<>(); + private final PlusDataPermissionHandler dataPermissionHandler; + + public PlusDataPermissionInterceptor(String mapperPackage) { + this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage); + } @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { @@ -49,12 +48,7 @@ public class PlusDataPermissionInterceptor extends JsqlParserSupport implements return; } // 检查是否无效 无数据权限注解 - if (invalidCacheSet.contains(ms.getId())) { - return; - } - DataColumn[] dataColumns = dataPermissionHandler.findAnnotation(ms.getId()); - if (ArrayUtil.isEmpty(dataColumns)) { - invalidCacheSet.add(ms.getId()); + if (dataPermissionHandler.invalid(ms.getId())) { return; } // 解析 sql 分配对应方法 @@ -72,12 +66,7 @@ public class PlusDataPermissionInterceptor extends JsqlParserSupport implements return; } // 检查是否无效 无数据权限注解 - if (invalidCacheSet.contains(ms.getId())) { - return; - } - DataColumn[] dataColumns = dataPermissionHandler.findAnnotation(ms.getId()); - if (ArrayUtil.isEmpty(dataColumns)) { - invalidCacheSet.add(ms.getId()); + if (dataPermissionHandler.invalid(ms.getId())) { return; } PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); @@ -87,11 +76,10 @@ public class PlusDataPermissionInterceptor extends JsqlParserSupport implements @Override protected void processSelect(Select select, int index, String sql, Object obj) { - SelectBody selectBody = select.getSelectBody(); - if (selectBody instanceof PlainSelect plainSelect) { - this.setWhere(plainSelect, (String) obj); - } else if (selectBody instanceof SetOperationList setOperationList) { - List selectBodyList = setOperationList.getSelects(); + if (select instanceof PlainSelect) { + this.setWhere((PlainSelect) select, (String) obj); + } else if (select instanceof SetOperationList setOperationList) { + List select table_name, table_comment, create_time, update_time from information_schema.tables - where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database()) + where table_schema = (select database()) + and table_name NOT LIKE 'sj_%' and table_name NOT LIKE 'gen_%' + and table_name NOT LIKE 'act_%' AND table_name NOT LIKE 'flw_%' and table_name in #{name} @@ -101,7 +106,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where dt.table_name = dtc.table_name and dt.table_name = uo.object_name and uo.object_type = 'TABLE' - AND dt.table_name NOT LIKE 'XXL_JOB_%' AND dt.table_name NOT LIKE 'GEN_%' + AND dt.table_name NOT LIKE 'sj_%' AND dt.table_name NOT LIKE 'GEN_%' + AND dt.table_name NOT LIKE 'ACT_%' AND dt.table_name NOT LIKE 'FLW_%' and lower(dt.table_name) in #{name} @@ -121,7 +127,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" AND n.nspname = 'public'::name AND n.nspname ]]> ''::name ) list_table - where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%' + where table_name NOT LIKE 'sj_%' and table_name NOT LIKE 'gen_%' + and table_name NOT LIKE 'act_%' and table_name NOT LIKE 'flw_%' and table_name in #{name} @@ -132,7 +139,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/spy.properties b/ruoyi-modules/ruoyi-gen/src/main/resources/spy.properties deleted file mode 100644 index abbd8931..00000000 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/spy.properties +++ /dev/null @@ -1,28 +0,0 @@ -# p6spy 性能分析插件配置文件 -modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory -# 自定义日志打印 -logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger -#日志输出到控制台 -appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger -# 使用日志系统记录 sql -#appender=com.p6spy.engine.spy.appender.Slf4JLogger -# 设置 p6spy driver 代理 -#deregisterdrivers=true -# 取消JDBC URL前缀 -useprefix=true -# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. -excludecategories=info,debug,result,commit,resultset -# 日期格式 -dateformat=yyyy-MM-dd HH:mm:ss -# SQL语句打印时间格式 -databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss -# 实际驱动可多个 -#driverlist=org.h2.Driver -# 是否开启慢SQL记录 -outagedetection=true -# 慢SQL记录标准 2 秒 -outagedetectioninterval=2 -# 是否过滤 Log -filter=true -# 过滤 Log 时所排除的 sql 关键字,以逗号分隔 -exclude=SELECT 1 diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm index a8425371..0a6495fc 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm @@ -20,7 +20,7 @@ import org.dromara.common.excel.utils.ExcelUtil; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; import ${packageName}.service.I${ClassName}Service; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; #elseif($table.tree) #end @@ -45,7 +45,7 @@ public class ${ClassName}Controller extends BaseController { */ @SaCheckPermission("${permissionPrefix}:list") @GetMapping("/list") -#if($table.crud || $table.sub) +#if($table.crud) public TableDataInfo<${ClassName}Vo> list(${ClassName}Bo bo, PageQuery pageQuery) { return ${className}Service.queryPageList(bo, pageQuery); } diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm index d596a0e9..f1fa3d99 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm @@ -3,7 +3,7 @@ package ${packageName}.service; import ${packageName}.domain.${ClassName}; import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; #end @@ -24,7 +24,7 @@ public interface I${ClassName}Service { */ ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}); -#if($table.crud || $table.sub) +#if($table.crud) /** * 查询${functionName}列表 */ diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm index 75a9b83a..d603de12 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm @@ -2,7 +2,7 @@ package ${packageName}.service.impl; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; -#if($table.crud || $table.sub) +#if($table.crud) import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -41,7 +41,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { return baseMapper.selectVoById(${pkColumn.javaField}); } -#if($table.crud || $table.sub) +#if($table.crud) /** * 查询${functionName}列表 */ diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm index f876f55c..d13ef2f2 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm @@ -1,8 +1,9 @@ #foreach($column in $columns) #set($javaField=$column.javaField) @@ -225,9 +222,9 @@ v-for="dict in ${dictType}" :key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long") - :label="parseInt(dict.value)" + :value="parseInt(dict.value)" #else - :label="dict.value" + :value="dict.value" #end >{{dict.label}} @@ -235,7 +232,7 @@ #elseif($column.htmlType == "radio" && $dictType) - 请选择字典生成 + 请选择字典生成 #elseif($column.htmlType == "datetime") @@ -322,9 +319,9 @@ const data = reactive>({ queryParams: { #foreach ($column in $columns) #if($column.query) - #if($column.htmlType != "datetime" || $column.queryType != "BETWEEN") +#if($column.htmlType != "datetime" || $column.queryType != "BETWEEN") $column.javaField: undefined, - #end +#end #end #end params: { @@ -421,17 +418,15 @@ const resetQuery = () => { /** 新增按钮操作 */ const handleAdd = (row?: ${BusinessName}VO) => { + reset(); + getTreeselect(); + if (row != null && row.${treeCode}) { + form.value.${treeParentCode} = row.${treeCode}; + } else { + form.value.${treeParentCode} = 0; + } dialog.visible = true; dialog.title = "添加${functionName}"; - nextTick(() => { - reset(); - getTreeselect(); - if (row != null && row.${treeCode}) { - form.value.${treeParentCode} = row.${treeParentCode}; - } else { - form.value.${treeParentCode} = 0; - } - }); } /** 展开/折叠操作 */ @@ -449,25 +444,21 @@ const toggleExpandAll = (data: ${BusinessName}VO[], status: boolean) => { } /** 修改按钮操作 */ -const handleUpdate = (row: ${BusinessName}VO) => { - loading.value = true; +const handleUpdate = async (row: ${BusinessName}VO) => { + reset(); + await getTreeselect(); + if (row != null) { + form.value.${treeParentCode} = row.${treeParentCode}; + } + const res = await get${BusinessName}(row.${pkColumn.javaField}); + Object.assign(form.value, res.data); +#foreach ($column in $columns) + #if($column.htmlType == "checkbox") + form.value.$column.javaField = form.value.${column.javaField}.split(","); + #end +#end dialog.visible = true; dialog.title = "修改${functionName}"; - nextTick(async () => { - reset(); - await getTreeselect(); - if (row != null) { - form.value.${treeParentCode} = row.${treeCode}; - } - const res = await get${BusinessName}(row.${pkColumn.javaField}); - loading.value = false; - Object.assign(form.value, res.data); -#foreach ($column in $columns) -#if($column.htmlType == "checkbox") - form.value.$column.javaField = form.value.${column.javaField}.split(","); -#end -#end - }); } /** 提交按钮 */ diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm index 6ba61fcb..886f4ab7 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm @@ -1,8 +1,9 @@