3.7.0版本合并springboot3分支

本次提交未升级【minidao、仪表盘、online的依赖】
pull/6344/head
JEECG 2024-06-22 17:03:43 +08:00
commit 816eeb9225
232 changed files with 7152 additions and 56677 deletions

View File

@ -1,21 +1,13 @@
##### 版本号:
##### 前端版本vue3版还是 vue2版
##### 问题描述:
##### 截图&代码:
##### 错误截图:
#### 友情提示为了提高issue处理效率
- 未按格式要求发帖,会被直接删掉;
- 描述过于简单或模糊,导致无法处理的,会被直接删掉;
- 请自己初判问题描述是否清楚,是否方便我们调查处理;
- 针对问题请说明是Online在线功能(需说明用的主题模板),还是生成的代码功能;
#### 友情提示:
- 未按格式要求发帖、描述过于简抽象的,会被直接删掉;
- 请确保问题描述清楚,方便我们理解并一次性调查解决问题;
- 如果使用的不是master请说明你使用的那个分支

View File

@ -7,13 +7,13 @@
JEECG BOOT Low Code Development Platform
===============
当前最新版本: 3.6.3发布日期2024-03-11
当前最新版本: 3.7.0发布日期2024-06-17
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-guojusoft-orange.svg)](http://www.jeecg.com)
[![](https://img.shields.io/badge/Blog-blog-blue.svg)](https://jeecg.blog.csdn.net)
[![](https://img.shields.io/badge/version-3.6.3-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-3.7.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
@ -37,19 +37,18 @@ JEECG Business process: Using workflow to implement and extend the task interfac
Technical support
-----------------------------------
Problems or bugs in use can be found in [Making on the Issues](https://github.com/jeecgboot/jeecg-boot/issues/new)
Problems or bugs in use can be found in [Making on the Issues](https://github.com/jeecgboot/JeecgBoot/issues/new)
Official Support: http://jeecg.com/doc/help
Download the source code
-----------------------------------
项目源码
-----------------------------------
| Source |Front-end source (Vue3 version) | The background source |
|-|-|-|
| Github | [jeecgboot-vue3](https://github.com/jeecgboot/jeecgboot-vue3) | [jeecg-boot](https://github.com/jeecgboot/jeecg-boot) |
| Gitee | [jeecgboot-vue3](https://gitee.com/jeecg/jeecgboot-vue3) | [jeecg-boot](https://gitee.com/jeecg/jeecg-boot) |
- UI(Vue3) SourceCodehttps://github.com/jeecgboot/jeecgboot-vue3
- APP SourceCodehttps://github.com/jeecgboot/jeecg-uniapp
##### Project description
@ -58,7 +57,6 @@ Download the source code
| `jeecg-boot` | SpringBoot background source code (support microservices) |
| `jeecgboot-vue3` | Vue3+TS new front-end source code|
| `jeecg-uniapp` | [APP development framework, a code multi terminal adaptation, and support APP, small program, H5](https://github.com/jeecgboot/jeecg-uniapp) |
| `SpringBoot3+JDK17` | [BranchSourceCode](https://github.com/jeecgboot/jeecg-boot/tree/springboot3) [UpgradeBlog](https://blog.csdn.net/zhangdaiscott/article/details/134805602) |
| `More` | [Download more source code](http://jeecg.com/download) |
@ -74,7 +72,7 @@ Docker starts the project
-----------------------------------
- [Docker starts the monomer background](https://help.jeecg.com/java/setup/docker/up.html)
- [Docker starts the Vue3 front-end](http://help.jeecg.com/publish/docker.html)
- [Docker starts the front-end](http://help.jeecg.com/publish/docker.html)
- [Docker starts the micro-service background](https://help.jeecg.com/java/springcloud/docker.html)
- [ChatGPT AI Config](https://help.jeecg.com/java/chatgpt.html)
@ -87,18 +85,11 @@ Technical documentation
- Doc [http://help.jeecg.com](http://help.jeecg.com)
- Newbie guide [Quick start](http://www.jeecg.com/doc/quickstart) | [video](https://space.bilibili.com/454617261/channel/series) | [Q&A ](http://www.jeecg.com/doc/qa) | [help](http://jeecg.com/doc/help) | [1 minute experience](https://my.oschina.net/jeecg/blog/3083313)
- Microservice Development [Monomer upgrade to microservice](https://help.jeecg.com/java/springcloud/switchcloud/monomer.html)
- QQ group ⑧825232878、⑦791696430、⑥730954414(full)、683903138(full)、⑤860162132(full)、④774126647(full)、③816531124(full)、②769925425(full)、①284271917(full)
- Demo [Vue3](http://boot3.jeecg.com) | [Vue2](http://boot.jeecg.com) | [APP](http://jeecg.com/appIndex)
- QQ group ⑨808791225、⑧825232878、⑦791696430、⑥730954414(full)、683903138(full)、⑤860162132(full)、④774126647(full)、③816531124(full)、②769925425(full)、①284271917(full)
- Demo [OnlineDemo](http://boot3.jeecg.com) | [APP](http://jeecg.com/appIndex)
> [please click obtain account password to obtain](http://jeecg.com/doc/demo)
Thinking
-----------------------------------
> We are pursuing the goal of implementing complex business systems without writing code! That has been done so far
- https://www.qiaoqiaoyun.com
Star charts
-----------------------------------
@ -161,7 +152,7 @@ Why JeecgBoot?
* Support SAAS service model and provide SaaS multi-tenant architecture solution.
* Distributed file service, integration of minio, Ali OSS and other excellent third parties, to provide convenient file upload and management, but also support local storage.
* Mainstream database compatibility, a set of code is fully compatible with Mysql, Postgresql, Oracle, Sqlserver, MariaDB, dream and other mainstream databases.
* Integrate workflow activiti and realize only the configuration of flow direction in the page, which can greatly simplify the development of bpm workflow; Using bpm's process designer to draw the flow direction, a workflow is basically complete with a small amount of java code;
* Integrate workflow flowable and realize only the configuration of flow direction in the page, which can greatly simplify the development of bpm workflow; Using bpm's process designer to draw the flow direction, a workflow is basically complete with a small amount of java code;
* Low code ability: online process design, using open source Activiti process engine, to achieve online drawing process, custom form, form attachment, business flow
* Multi-data source: its simple way of use, online configuration of data source configuration, convenient to grab data from other data;
* Provide single sign-on CAS integration solution, and complete docking code has been provided in the project
@ -228,8 +219,7 @@ Technical Architecture:
#### The front end
- Vue2 version`Vue2.6+@vue/cli+AntDesignVue+Viser-vue+Vuex` [detail](https://github.com/jeecgboot/ant-design-vue-jeecg)
- Vue3 version`Vue3.0+TypeScript+Vite+AntDesignVue+pinia+echarts` [detail](https://github.com/jeecgboot/jeecgboot-vue3)
- TechnologyStack`Vue3.0+TypeScript+Vite+AntDesignVue+pinia+echarts`
#### Support library

246
README.md
View File

@ -1,19 +1,14 @@
![JEECG](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/logov3.png "JeecgBoot低代码开发平台")
JEECG BOOT 低代码开发平台
JeecgBoot 低代码开发平台
===============
当前最新版本: 3.6.3发布日期2024-03-11
当前最新版本: 3.7.0发布日期2024-06-17
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://jeecg.com/aboutusIndex)
[![](https://img.shields.io/badge/Blog-官方博客-blue.svg)](https://jeecg.blog.csdn.net)
[![](https://img.shields.io/badge/version-3.6.3-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-3.7.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
@ -24,122 +19,77 @@ JEECG BOOT 低代码开发平台
<h3 align="center">Java Low Code Platform for Enterprise web applications</h3>
JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端分离架构 SpringBoot2.xSpringCloudAnt Design&VueMybatis-plusShiroJWT支持微服务。强大的代码生成器让前后端代码一键生成实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE) 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性!
JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端分离架构 SpringBoot2.x和3.xSpringCloudAnt Design&VueMybatis-plusShiroJWT支持微服务。强大的代码生成器让前后端代码一键生成实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE) 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性!
JeecgBoot 提供了一系列`低代码模块`,实现在线开发`真正的零代码`Online表单开发、Online报表、报表配置能力、在线图表设计、大屏设计、移动配置能力、表单设计器、在线设计流程、流程自动化配置、插件能力可插拔等等
JeecgBoot 提供了一系列`低代码模块`,实现在线开发`真正的零代码`Online表单开发、Online报表、报表配置能力、在线图表设计、仪表盘设计、大屏设计、移动配置能力、表单设计器、在线设计流程、流程自动化配置、插件能力(可插拔)等等!
`JEECG宗旨是:` 简单功能由OnlineCoding配置实现做到`零代码开发`复杂功能由代码生成器生成进行手工Merge 实现`低代码开发`,既保证了`智能`又兼顾`灵活`;实现了低代码开发的同时又支持灵活编码,解决了当前低代码产品普遍不灵活的弊端!
`JEECG业务流程:` 采用工作流来实现、扩展出任务接口,供开发编写业务逻辑,表单提供多种解决方案: 表单设计器、online配置表单、编码表单。同时实现了流程与表单的分离设计松耦合、并支持任务节点灵活配置既保证了公司流程的保密性又减少了开发人员的工作量。
遇到技术问题,[请在这里反馈BUG](https://github.com/jeecgboot/jeecg-boot/issues/new)
适用项目
-----------------------------------
Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中尤其适合SAAS项目、企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中支持信创国产化(默认适配达梦和人大金仓)。尤其适合SAAS项目、企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
项目源码
源码下载
-----------------------------------
| 仓库 |前端源码 Vue3版 | 后端JAVA源码 |
|-|-|-|
| Github | [jeecgboot-vue3](https://github.com/jeecgboot/jeecgboot-vue3) | [jeecg-boot](https://github.com/jeecgboot/jeecg-boot) |
| 码云 | [jeecgboot-vue3](https://gitee.com/jeecg/jeecgboot-vue3) | [jeecg-boot](https://gitee.com/jeecg/jeecg-boot) |
- 前端源码地址https://github.com/jeecgboot/jeecgboot-vue3
- APP源码地址https://github.com/jeecgboot/jeecg-uniapp
> 官方已推出 `SpringBoot3+JDK17版本` [分支源码下载](https://github.com/jeecgboot/jeecg-boot/tree/springboot3) | [升级SpringBoot3博客](https://blog.csdn.net/zhangdaiscott/article/details/134805602)
#### 项目说明
| 项目名 | 说明 |
|--------------------|------------------------|
| `jeecgboot-vue3` | 前端源码 (Vue3版本) |
| `jeecg-boot` | 后端JAVA源码支持微服务 |
| `jeecg-uniapp` | [APP开发框架一份代码多终端适配同时支持APP、小程序、H5](https://github.com/jeecgboot/jeecg-uniapp) |
| `更多开源项目` | [更多底层源码下载](http://jeecg.com/download) |
| `jeecg-boot` | 后端源码JAVASpringBoot微服务架构 |
| `jeecgboot-vue3` | 前端源码VUE3vue3+vite5+ts最新技术栈 |
| `jeecg-uniapp` | APP框架,一份代码多终端适配支持APP、小程序、H5 |
快速搭建开发环境
技术支持
-----------------------------------
- [通过IDEA导入项目](https://help.jeecg.com/java/setup/idea.html)
关闭gitee的issue通道使用中遇到问题或者BUG可以在 [Github上提Issues](https://github.com/jeecgboot/JeecgBoot/issues/new)
快速启动项目
-----------------------------------
- [前端项目快速启动](http://help.jeecg.com/setup/startup.html)
- [通过IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup.html)
- [Vue3前端项目快速启动](http://help.jeecg.com/setup/startup.html)
- [单体快速切换为微服务版](https://help.jeecg.com/java/springcloud/switchcloud/monomer.html)
- [ChatGPT AI助手配置文档](https://help.jeecg.com/java/chatgpt.html)
Docker快速启动项目
Docker启动项目
-----------------------------------
- [Docker启动单体后台](https://help.jeecg.com/java/setup/docker/up.html)
- [Docker启动Vue3前端](http://help.jeecg.com/publish/docker.html)
- [Docker启动前端](http://help.jeecg.com/publish/docker.html)
- [Docker启动后台](https://help.jeecg.com/java/setup/docker/up.html)
微服务方式启动
-----------------------------------
- [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer.html)
- [Docker启动微服务后台](https://help.jeecg.com/java/springcloud/docker.html)
技术文档
-----------------------------------
- 项目官网: [http://www.jeecg.com](http://www.jeecg.com)
- 产品官网: [http://www.jeecg.com](http://www.jeecg.com)
- 开发文档: [https://help.jeecg.com](https://help.jeecg.com)
- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [常见问题 ](http://www.jeecg.com/doc/qa) | [视频教程](https://space.bilibili.com/454617261/channel/series) | [1分钟低代码体验](https://my.oschina.net/jeecg/blog/3083313)
- AI助手配置: https://help.jeecg.com/java/chatgpt.html
- 在线演示 [Vue3演示](http://boot3.jeecg.com) | [APP演示](http://jeecg.com/appIndex) | [敲敲云零代码](https://qiaoqiaoyun.com)
- 在线演示 [在线演示](http://boot3.jeecg.com) | [APP演示](http://jeecg.com/appIndex)
> 演示系统的登录账号密码,请点击 [获取账号密码](http://jeecg.com/doc/demo) 获取
>
- QQ交流群 ⑧825232878、⑦791696430(满)、⑥730954414(满)、683903138(满)、⑤860162132(满)、④774126647(满)、③816531124(满)、②769925425(满)、①284271917(满)
> ` 提醒【QQ群是自助服务群建议给帮助您解决问题的同学发送指定红包表示感谢】 `
大龄码农的思考
-----------------------------------
> 作为码农年纪大了写不动代码了怎么办??哎!!
所以我们团队在追求不写代码也可实现复杂业务系统!目前已经做到了,不信你到敲敲云零代码试试(通过流程串联修改业务数据)
- https://www.qiaoqiaoyun.com
技术支持
-----------------------------------
关闭gitee的issue通道使用中遇到问题或者BUG可以在 [Github上提Issues](https://github.com/jeecgboot/jeecg-boot/issues/new)
官方支持: [http://jeecg.com/doc/help](http://jeecg.com/doc/help)
VUE2版本专题介绍
-----------------------------------
#### 项目介绍
- 项目名称ant-design-vue-jeecg
- 说明JeecgBoot前端提供两套解决方案一套VUE2和一套VUE3版本目前vue2版本最新代码只支持到jeecgboot 3.4.3版本,一定注意。
#### 源码下载
| 源码 | 源码地址 |
|--------------------|------------------------|
| 后端JAVA源码 `Vue2版` |https://gitee.com/jeecg/jeecg-boot/tree/v3.4.3last |
| 前端vue2源码 `Vue2版` |https://gitee.com/jeecg/ant-design-vue-jeecg |
#### Vue2与Vue3版本区别
> - VUE3版本彻底抛弃IE兼容不兼容IE和低版本浏览器只适配高版本谷歌和Edge
政府、事业类单位项目需要谨慎选择——国产化迁移是一个漫长的过程万一过程中要求IE兼容这个不可逆
> - 所以如果对浏览器有要求的项目请选择VUE2版本。
> - VUE3版是全新的技术栈紧跟主流前端重写各个功能都做了优化拥有更好的体验效果
#### 技术文档
- 在线演示:[Vue2版演示](http://boot.jeecg.com)
- 开发文档:| [开发文档](http://doc.jeecg.com) | [Vue2前端快速启动](http://doc.jeecg.com/2678320) | [Vue2前端采用Docker启动](http://doc.jeecg.com/3043612)
Star走势图
-----------------------------------
[![Star History Chart](https://api.star-history.com/svg?repos=jeecgboot/jeecg-boot&type=Date)](https://star-history.com/#jeecgboot/jeecg-boot)
- QQ交流群 ⑨808791225、⑧825232878、⑦791696430(满)、⑥730954414(满)、683903138(满)、⑤860162132(满)、④774126647(满)、③816531124(满)、②769925425(满)、①284271917(满)
@ -196,8 +146,8 @@ Star走势图
* 17.支持SAAS服务模式提供SaaS多租户架构方案。
* 18.分布式文件服务集成minio、阿里OSS等优秀的第三方提供便捷的文件上传与管理同时也支持本地存储。
* 19.主流数据库兼容一套代码完全兼容Mysql、Postgresql、Oracle、Sqlserver、MariaDB、达梦等主流数据库。
* 20.集成工作流flowable、activiti并实现了只需在页面配置流程转向可极大的简化bpm工作流的开发用bpm的流程设计器画出了流程走向一个工作流基本就完成了只需写很少量的java代码
* 21.低代码能力在线流程设计采用开源flowable、activiti流程引擎,实现在线画流程,自定义表单,表单挂靠,业务流转
* 20.集成工作流flowable并实现了只需在页面配置流程转向可极大的简化bpm工作流的开发用bpm的流程设计器画出了流程走向一个工作流基本就完成了只需写很少量的java代码
* 21.低代码能力在线流程设计采用开源flowable流程引擎实现在线画流程,自定义表单,表单挂靠,业务流转
* 22.多数据源:及其简易的使用方式,在线配置数据源配置,便捷的从其他数据抓取数据;
* 23.提供单点登录CAS集成方案项目中已经提供完善的对接代码
* 24.低代码能力表单设计器支持用户自定义表单布局支持单表一对多表单、支持select、radio、checkbox、textarea、date、popup、列表、宏等控件
@ -236,7 +186,7 @@ Star走势图
- 缓存Redis
- 数据库脚本MySQL5.7+ & Oracle 11g & Sqlserver2017(其他数据库,[需要自己转](https://my.oschina.net/jeecg/blog/4905722)
- 数据库脚本MySQL5.7+ (其他数据库,[需要自己转](https://my.oschina.net/jeecg/blog/4905722)
#### 后端
@ -262,8 +212,7 @@ Star走势图
#### 前端
- Vue2版本`Vue2.6+@vue/cli+AntDesignVue+Viser-vue+Vuex等` [详细查看](https://github.com/jeecgboot/ant-design-vue-jeecg)
- Vue3版本`Vue3.0+TypeScript+Vite+AntDesignVue+pinia+echarts等新方案` [详细查看](https://github.com/jeecgboot/jeecgboot-vue3)
- 技术栈:`Vue3.0+TypeScript+Vite+AntDesignVue+pinia+echarts等最新技术栈`
#### 支持库
@ -467,21 +416,10 @@ Star走势图
```
### 流程引擎推荐
JeecgBoot企业版本默认集成了activiti和flowable两套方案大家在使用本开源项目时如果想进一步集成流程引擎推荐结合贺波老师的书 [《深入Activiti流程引擎核心原理与高阶实战》](https://item.m.jd.com/product/13928958.html?gx=RnAomTM2bmCImZxDqYAkVCoIHuIYVqc)
<img src="https://jeecgos.oss-cn-beijing.aliyuncs.com/files/tuijian20231220161656.png" width="25%" height="auto">
### 系统效果
##### ChatGPT AI交互
> 进入JeecgBoot后台首页点击首页右侧中间“AI助手”弹出AI助手对话界面。
![](https://oscimg.oschina.net/oscnet/up-7c6405641a40f56638999d52da0cb5b4343.png)
##### PC端
![](https://oscimg.oschina.net/oscnet/up-000530d95df337b43089ac77e562494f454.png)
@ -500,23 +438,9 @@ JeecgBoot企业版本默认集成了activiti和flowable两套方案大家在
![](https://oscimg.oschina.net/oscnet/up-16c07e000278329b69b228ae3189814b8e9.png)
##### AI助手
![](https://oscimg.oschina.net/oscnet/up-7c6405641a40f56638999d52da0cb5b4343.png)
##### 流程设计
![](https://oscimg.oschina.net/oscnet/up-981ce174e4fbb48c8a2ce4ccfd7372e2994.png)
![输入图片说明](https://static.oschina.net/uploads/img/201907/05165142_yyQ7.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160917_9Ftz.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160633_u59G.png "在这里输入图片标题")
##### 简版流程设计
![](https://oscimg.oschina.net/oscnet/up-1dc0d052149ec675f3e4fad632b82b48add.png)
![](https://oscimg.oschina.net/oscnet/up-de31bc2f9d9b8332c554b0954cc73d79593.png)
![](https://oscimg.oschina.net/oscnet/up-7f83b25159663686d67ed080eb16068c3b4.png)
##### 仪表盘设计器
![](https://oscimg.oschina.net/oscnet/up-9c9d41288c31398d76b390bdd400f13a582.png)
@ -532,38 +456,6 @@ JeecgBoot企业版本默认集成了activiti和flowable两套方案大家在
![](https://oscimg.oschina.net/oscnet/up-f7e9cb2e3740f2d19ff63b40ec2dd554f96.png)
##### 表单设计器
![](https://oscimg.oschina.net/oscnet/up-5f8cb657615714b02190b355e59f60c5937.png)
![](https://oscimg.oschina.net/oscnet/up-d9659b2f324e33218476ec98c9b400e6508.png)
![](https://oscimg.oschina.net/oscnet/up-4868615395272d3206dbb960ade02dbc291.png)
##### 大屏设计器
![](https://oscimg.oschina.net/oscnet/up-402a6034124474bfef8dfc5b4b2bac1ce5c.png)
![](https://oscimg.oschina.net/oscnet/up-6f7ba2e2ebbeea0d203db8d69fd87644c9f.png)
![](https://oscimg.oschina.net/oscnet/up-ee8d34f318da466b8a6070a6e3111d12ce7.png)
![](https://oscimg.oschina.net/oscnet/up-6b81781b43086819049c4421206810667c5.png)
##### UNIAPP效果
![](https://oscimg.oschina.net/oscnet/up-aac943fbd26561879c57a41f7a406edf274.png)
![](https://oscimg.oschina.net/oscnet/up-9a44ba2e82b09c750629d12fafd7f60f553.png)
##### 零代码应用
![](https://oscimg.oschina.net/oscnet/up-4be29ae761b2615c8c54b3f668cd8432d9b.png)
![](https://oscimg.oschina.net/oscnet/up-787e76bc24b38ecc7ed19f338808d128255.png)
![](https://oscimg.oschina.net/oscnet/up-99d24a236c483362868523ad0d90f611487.png)
![](https://oscimg.oschina.net/oscnet/up-339a0f29d10449abc7724e3bcda802761c1.png)
![](https://oscimg.oschina.net/oscnet/up-b356670cdc14c609958c7619a537397c4b9.png)
##### 手机端
![](https://oscimg.oschina.net/oscnet/da543c5d0d57baab0cecaa4670c8b68c521.jpg)
@ -586,8 +478,62 @@ JeecgBoot企业版本默认集成了activiti和flowable两套方案大家在
##### 在线接口文档
![输入图片说明](https://static.oschina.net/uploads/img/201908/27095258_M2Xq.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160957_hN3X.png "在这里输入图片标题")
##### UNIAPP效果
![](https://oscimg.oschina.net/oscnet/up-aac943fbd26561879c57a41f7a406edf274.png)
![](https://oscimg.oschina.net/oscnet/up-9a44ba2e82b09c750629d12fafd7f60f553.png)
##### 大屏设计器
![](https://oscimg.oschina.net/oscnet/up-402a6034124474bfef8dfc5b4b2bac1ce5c.png)
![](https://oscimg.oschina.net/oscnet/up-6f7ba2e2ebbeea0d203db8d69fd87644c9f.png)
![](https://oscimg.oschina.net/oscnet/up-ee8d34f318da466b8a6070a6e3111d12ce7.png)
![](https://oscimg.oschina.net/oscnet/up-6b81781b43086819049c4421206810667c5.png)
##### 流程设计
![](https://oscimg.oschina.net/oscnet/up-981ce174e4fbb48c8a2ce4ccfd7372e2994.png)
![](https://oscimg.oschina.net/oscnet/up-1dc0d052149ec675f3e4fad632b82b48add.png)
![](https://oscimg.oschina.net/oscnet/up-de31bc2f9d9b8332c554b0954cc73d79593.png)
![输入图片说明](https://static.oschina.net/uploads/img/201907/05165142_yyQ7.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160917_9Ftz.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160633_u59G.png "在这里输入图片标题")
##### 表单设计器
![](https://oscimg.oschina.net/oscnet/up-5f8cb657615714b02190b355e59f60c5937.png)
![](https://oscimg.oschina.net/oscnet/up-d9659b2f324e33218476ec98c9b400e6508.png)
![](https://oscimg.oschina.net/oscnet/up-4868615395272d3206dbb960ade02dbc291.png)
## 捐赠
如果觉得还不错,请作者喝杯咖啡吧 ☺
![](https://static.oschina.net/uploads/img/201903/08155608_0EFX.png)
![](https://static.oschina.net/uploads/img/201903/08155608_0EFX.png)
### 流程引擎推荐
大家在使用本开源项目时,如果想进一步集成流程引擎,推荐结合贺波老师的书 [《深入Activiti流程引擎核心原理与高阶实战》](https://item.m.jd.com/product/13928958.html?gx=RnAomTM2bmCImZxDqYAkVCoIHuIYVqc)
<img src="https://jeecgos.oss-cn-beijing.aliyuncs.com/files/tuijian20231220161656.png" width="25%" height="auto">

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeecg-boot-base-core</artifactId>
@ -159,6 +159,25 @@
<version>${postgresql.version}</version>
<scope>runtime</scope>
</dependency>
<!--人大金仓驱动 版本号V008R006C005B0013 -->
<dependency>
<groupId>org.jeecgframework</groupId>
<artifactId>kingbase8</artifactId>
<version>${kingbase8.version}</version>
<scope>runtime</scope>
</dependency>
<!--达梦数据库驱动 版本号1-3-26-2023.07.26-197096-20046-ENT -->
<dependency>
<groupId>com.dameng</groupId>
<artifactId>Dm8JdbcDriver18</artifactId>
<version>${dm8.version}</version>
</dependency>
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmDialect-for-hibernate5.0</artifactId>
<version>${dm8.version}</version>
</dependency>
<!-- Quartz定时任务 -->
<dependency>
@ -343,6 +362,11 @@
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
</dependencies>
<!-- chatgpt -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-starter3-chatgpt</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -18,6 +18,13 @@ public interface CommonAPI {
* @return
*/
Set<String> queryUserRoles(String username);
/**
* 1
* @param userId
* @return
*/
Set<String> queryUserRolesById(String userId);
/**
@ -49,6 +56,13 @@ public interface CommonAPI {
* @return
*/
public LoginUser getUserByName(String username);
/**
* 5Id
* @param username
* @return
*/
public String getUserIdByName(String username);
/**

View File

@ -1,5 +1,6 @@
package org.jeecg.common.api.dto;
import lombok.Data;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecg.common.system.vo.LoginUser;
import java.io.Serializable;
import java.util.Date;
@ -55,6 +56,11 @@ public class LogDTO implements Serializable {
*/
private Integer tenantId;
/**
* pc: app: h5:
*/
private String clientType;
public LogDTO(){
}

View File

@ -52,7 +52,9 @@ public class DictAspect {
/**
* Pointcut
*/
@Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..)) || @annotation(org.jeecg.common.aspect.annotation.AutoDict)")
@Pointcut("(@within(org.springframework.web.bind.annotation.RestController) || " +
"@within(org.springframework.stereotype.Controller) || @annotation(org.jeecg.common.aspect.annotation.AutoDict)) " +
"&& execution(public org.jeecg.common.api.vo.Result org.jeecg..*.*(..))")
public void excudeService() {
}
@ -92,7 +94,8 @@ public class DictAspect {
* @param result
*/
private Object parseDictText(Object result) {
if (result instanceof Result) {
//if (result instanceof Result) {
if (true) {
if (((Result) result).getResult() instanceof IPage) {
List<JSONObject> items = new ArrayList<>();

View File

@ -36,6 +36,16 @@ public interface CommonConstant {
*/
int LOG_TYPE_2 = 2;
/**
*
*/
int LOG_TYPE_3 = 3;
/**
*
*/
int LOG_TYPE_4 = 4;
/**
*
*/
@ -286,6 +296,10 @@ public interface CommonConstant {
* 线
*/
String IM_PREFIX_USER_FRIEND_CACHE = "sys:cache:im:im_prefix_user_friend_";
/**
* id
*/
String SYS_USER_ID_MAPPING_CACHE = "sys:cache:user:id_mapping";
/**
* 1 2
@ -577,4 +591,30 @@ public interface CommonConstant {
public static final String SAAS_MODE_TENANT = "tenant";
//update-end---author:scott ---date::2023-09-10 for积木报表常量----
//update-begin---author:wangshuai---date:2024-04-07---for:修改手机号常量---
/**
* redis-key
*/
String CHANGE_PHONE_REDIS_KEY_PRE = "sys:cache:phone:change_phone_msg:";
/**
* KEY
*/
String CACHE_KEY_USER_LAST_ANNOUNT_TIME_1HOUR = "sys:cache:userinfo:user_last_annount_time::%s";
/**
*
*/
String VERIFY_ORIGINAL_PHONE = "verifyOriginalPhone";
/**
*
*/
String UPDATE_PHONE = "updatePhone";
//update-end---author:wangshuai---date:2024-04-07---for:修改手机号常量---
/**
*
*/
Integer PHONE_SMS_FAIL_CODE = 40002;
}

View File

@ -58,6 +58,22 @@ public interface DataBaseConstant {
* -
*/
public static final String SYS_MULTI_ORG_CODE_TABLE = "sys_multi_org_code";
/**
* -ID
*/
public static final String SYS_ORG_ID = "sysOrgId";
/**
* -ID
*/
public static final String SYS_ORG_ID_TABLE = "sys_org_id";
/**
* -code
*/
public static final String SYS_ROLE_CODE = "sysRoleCode";
/**
* -code
*/
public static final String SYS_ROLE_CODE_TABLE = "sys_role_code";
/**
* -
*/
@ -66,7 +82,14 @@ public interface DataBaseConstant {
* -
*/
public static final String SYS_USER_CODE_TABLE = "sys_user_code";
/**
* ID
*/
public static final String SYS_USER_ID = "sysUserId";
/**
* ID
*/
public static final String SYS_USER_ID_TABLE = "sys_user_id";
/**
*
*/

View File

@ -34,17 +34,22 @@ public interface ServiceNameConstants {
*/
String SERVICE_DEMO = "jeecg-demo";
/**
* online线
* joa
*/
String SERVICE_ONLINE = "jeecg-online";
/**
* OA
*/
String SERVICE_EOA = "jeecg-eoa";
/**
*
*/
String SERVICE_FORM = "jeecg-desform";
String SERVICE_JOA = "jeecg-joa";
// /**
// * 微服务名online在线模块
// */
// String SERVICE_ONLINE = "jeecg-online";
// /**
// * 微服务名OA模块
// */
// String SERVICE_EOA = "jeecg-eoa";
// /**
// * 微服务名:表单设计模块
// */
// String SERVICE_FORM = "jeecg-desform";
/**
* gatewayheader basePath

View File

@ -23,7 +23,7 @@ public enum CgformEnum {
/**
* jvxe
* */
JVXE_TABLE(2, "jvxe", "/jeecg/code-template-online", "jvxe.onetomany", "JVXE风格" ,new String[]{"vue3","vue","vue3Native"}),
JVXE_TABLE(2, "jvxe", "/jeecg/code-template-online", "jvxe.onetomany", "默认风格" ,new String[]{"vue3","vue","vue3Native"}),
/**
* (erp)

View File

@ -0,0 +1,23 @@
package org.jeecg.common.constant.enums;
/**
*
*/
public enum ClientTerminalTypeEnum {
PC("pc", "电脑终端"),
H5("h5", "移动网页端"),
APP("app", "手机app端");
private String key;
private String text;
ClientTerminalTypeEnum(String value, String text) {
this.key = value;
this.text = text;
}
public String getKey() {
return this.key;
}
}

View File

@ -0,0 +1,27 @@
package org.jeecg.common.constant.enums;
/**
*
*/
public enum DateRangeEnum {
// 今天
TODAY,
// 昨天
YESTERDAY,
// 明天
TOMORROW,
// 本周
THIS_WEEK,
// 上周
LAST_WEEK,
// 下周
NEXT_WEEK,
// 过去七天
LAST_7_DAYS,
// 本月
THIS_MONTH,
// 上月
LAST_MONTH,
// 下月
NEXT_MONTH,
}

View File

@ -12,6 +12,8 @@ public enum DySmsEnum {
LOGIN_TEMPLATE_CODE("SMS_175435174","敲敲云","code"),
/**忘记密码短信模板编码*/
FORGET_PASSWORD_TEMPLATE_CODE("SMS_175435174","敲敲云","code"),
/**修改密码短信模板编码*/
CHANGE_PASSWORD_TEMPLATE_CODE("SMS_465391221","敲敲云","code"),
/**注册账号短信模板编码*/
REGISTER_TEMPLATE_CODE("SMS_175430166","敲敲云","code"),
/**会议通知*/

View File

@ -0,0 +1,87 @@
package org.jeecg.common.desensitization;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.desensitization.annotation.Sensitive;
import org.jeecg.common.desensitization.enums.SensitiveEnum;
import org.jeecg.common.desensitization.util.SensitiveInfoUtil;
import org.jeecg.common.util.encryption.AesEncryptUtil;
import java.io.IOException;
import java.util.Objects;
/**
* @author eightmonth@qq.com
* @date 2024/6/19 10:43
*/
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class SensitiveSerialize extends JsonSerializer<String> implements ContextualSerializer {
private SensitiveEnum type;
@Override
public void serialize(String data, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
switch (type){
case ENCODE:
try {
jsonGenerator.writeString(AesEncryptUtil.encrypt(data));
} catch (Exception exception) {
log.error("数据加密错误", exception.getMessage());
jsonGenerator.writeString(data);
}
break;
case CHINESE_NAME:
jsonGenerator.writeString(SensitiveInfoUtil.chineseName(data));
break;
case ID_CARD:
jsonGenerator.writeString(SensitiveInfoUtil.idCardNum(data));
break;
case FIXED_PHONE:
jsonGenerator.writeString(SensitiveInfoUtil.fixedPhone(data));
break;
case MOBILE_PHONE:
jsonGenerator.writeString(SensitiveInfoUtil.mobilePhone(data));
break;
case ADDRESS:
jsonGenerator.writeString(SensitiveInfoUtil.address(data, 3));
break;
case EMAIL:
jsonGenerator.writeString(SensitiveInfoUtil.email(data));
break;
case BANK_CARD:
jsonGenerator.writeString(SensitiveInfoUtil.bankCard(data));
break;
case CNAPS_CODE:
jsonGenerator.writeString(SensitiveInfoUtil.cnapsCode(data));
break;
default:
jsonGenerator.writeString(data);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if (beanProperty != null) {
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);
if (sensitive == null) {
sensitive = beanProperty.getContextAnnotation(Sensitive.class);
}
if (sensitive != null) {
return new SensitiveSerialize(sensitive.type());
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
}

View File

@ -0,0 +1,26 @@
package org.jeecg.common.desensitization.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.jeecg.common.desensitization.SensitiveSerialize;
import org.jeecg.common.desensitization.enums.SensitiveEnum;
import java.lang.annotation.*;
/**
*
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerialize.class)
public @interface Sensitive {
/**
*
* @return
*/
SensitiveEnum type() default SensitiveEnum.ENCODE;
}

View File

@ -198,7 +198,7 @@ public class SensitiveInfoUtil {
* @param fullName
* @return <**>
*/
private static String chineseName(String fullName) {
public static String chineseName(String fullName) {
if (oConvertUtils.isEmpty(fullName)) {
return "";
}
@ -211,7 +211,7 @@ public class SensitiveInfoUtil {
* @param firstName
* @return <**>
*/
private static String chineseName(String familyName, String firstName) {
public static String chineseName(String familyName, String firstName) {
if (oConvertUtils.isEmpty(familyName) || oConvertUtils.isEmpty(firstName)) {
return "";
}
@ -223,7 +223,7 @@ public class SensitiveInfoUtil {
* @param id
* @return <*************5762>
*/
private static String idCardNum(String id) {
public static String idCardNum(String id) {
if (oConvertUtils.isEmpty(id)) {
return "";
}
@ -236,7 +236,7 @@ public class SensitiveInfoUtil {
* @param num
* @return <****1234>
*/
private static String fixedPhone(String num) {
public static String fixedPhone(String num) {
if (oConvertUtils.isEmpty(num)) {
return "";
}
@ -248,7 +248,7 @@ public class SensitiveInfoUtil {
* @param num
* @return <:138******1234>
*/
private static String mobilePhone(String num) {
public static String mobilePhone(String num) {
if (oConvertUtils.isEmpty(num)) {
return "";
}
@ -265,7 +265,7 @@ public class SensitiveInfoUtil {
* @param sensitiveSize
* @return <****>
*/
private static String address(String address, int sensitiveSize) {
public static String address(String address, int sensitiveSize) {
if (oConvertUtils.isEmpty(address)) {
return "";
}
@ -281,7 +281,7 @@ public class SensitiveInfoUtil {
* @param email
* @return <:g**@163.com>
*/
private static String email(String email) {
public static String email(String email) {
if (oConvertUtils.isEmpty(email)) {
return "";
}
@ -300,7 +300,7 @@ public class SensitiveInfoUtil {
* @param cardNum
* @return <:6222600**********1234>
*/
private static String bankCard(String cardNum) {
public static String bankCard(String cardNum) {
if (oConvertUtils.isEmpty(cardNum)) {
return "";
}
@ -312,7 +312,7 @@ public class SensitiveInfoUtil {
* @param code
* @return <:12********>
*/
private static String cnapsCode(String code) {
public static String cnapsCode(String code) {
if (oConvertUtils.isEmpty(code)) {
return "";
}
@ -326,7 +326,7 @@ public class SensitiveInfoUtil {
* @param reservedLength
* @return
*/
private static String formatRight(String str, int reservedLength){
public static String formatRight(String str, int reservedLength){
String name = str.substring(0, reservedLength);
String stars = String.join("", Collections.nCopies(str.length()-reservedLength, "*"));
return name + stars;
@ -338,7 +338,7 @@ public class SensitiveInfoUtil {
* @param reservedLength
* @return
*/
private static String formatLeft(String str, int reservedLength){
public static String formatLeft(String str, int reservedLength){
int len = str.length();
String show = str.substring(len-reservedLength);
String stars = String.join("", Collections.nCopies(len-reservedLength, "*"));
@ -352,7 +352,7 @@ public class SensitiveInfoUtil {
* @param endLen
* @return
*/
private static String formatBetween(String str, int beginLen, int endLen){
public static String formatBetween(String str, int beginLen, int endLen){
int len = str.length();
String begin = str.substring(0, beginLen);
String end = str.substring(len-endLen);

View File

@ -0,0 +1,40 @@
package org.jeecg.common.exception;
import org.jeecg.common.constant.CommonConstant;
/**
* @Description: ()
* @date: 2024-04-26
* @author: scott
*/
public class JeecgBootBizTipException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* code
*/
private int errCode = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
public JeecgBootBizTipException(String message){
super(message);
}
public JeecgBootBizTipException(String message, int errCode){
super(message);
this.errCode = errCode;
}
public int getErrCode() {
return errCode;
}
public JeecgBootBizTipException(Throwable cause)
{
super(cause);
}
public JeecgBootBizTipException(String message, Throwable cause)
{
super(message,cause);
}
}

View File

@ -1,15 +1,30 @@
package org.jeecg.common.exception;
import cn.hutool.core.util.ObjectUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.jeecg.common.api.dto.LogDTO;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.ClientTerminalTypeEnum;
import org.jeecg.common.enums.SentinelErrorInfoEnum;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.BrowserUtils;
import org.jeecg.common.util.IpUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.base.service.BaseCommonService;
import org.springframework.beans.BeansException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.connection.PoolException;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
@ -17,6 +32,8 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;
import java.util.Map;
/**
*
*
@ -27,12 +44,25 @@ import org.springframework.web.servlet.NoHandlerFoundException;
@Slf4j
public class JeecgBootExceptionHandler {
@Resource
BaseCommonService baseCommonService;
/**
*
*/
@ExceptionHandler(JeecgBootException.class)
public Result<?> handleJeecgBootException(JeecgBootException e){
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error(e.getErrCode(), e.getMessage());
}
/**
*
*/
@ExceptionHandler(JeecgBootBizTipException.class)
public Result<?> handleJeecgBootBizTipException(JeecgBootBizTipException e){
log.error(e.getMessage());
return Result.error(e.getErrCode(), e.getMessage());
}
@ -42,6 +72,7 @@ public class JeecgBootExceptionHandler {
@ExceptionHandler(JeecgCloudException.class)
public Result<?> handleJeecgCloudException(JeecgCloudException e){
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error(e.getMessage());
}
@ -52,25 +83,28 @@ public class JeecgBootExceptionHandler {
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public Result<?> handleJeecgBoot401Exception(JeecgBoot401Exception e){
log.error(e.getMessage(), e);
addSysLog(e);
return new Result(401,e.getMessage());
}
@ExceptionHandler(NoHandlerFoundException.class)
public Result<?> handlerNoFoundException(Exception e) {
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error(404, "路径不存在,请检查路径是否正确");
}
@ExceptionHandler(DuplicateKeyException.class)
public Result<?> handleDuplicateKeyException(DuplicateKeyException e){
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error("数据库中已存在该记录");
}
@ExceptionHandler({UnauthorizedException.class, AuthorizationException.class})
public Result<?> handleAuthorizationException(AuthorizationException e){
log.error(e.getMessage(), e);
return Result.noauth("没有权限,请联系管理员授权");
return Result.noauth("没有权限,请联系管理员授权,后刷新缓存!");
}
@ExceptionHandler(Exception.class)
@ -83,6 +117,7 @@ public class JeecgBootExceptionHandler {
return Result.error(errorInfoEnum.getError());
}
//update-end---author:zyf ---date:20220411 for处理Sentinel限流自定义异常
addSysLog(e);
return Result.error("操作失败,"+e.getMessage());
}
@ -107,6 +142,7 @@ public class JeecgBootExceptionHandler {
}
log.error(sb.toString(), e);
//return Result.error("没有权限,请联系管理员授权");
addSysLog(e);
return Result.error(405,sb.toString());
}
@ -116,12 +152,14 @@ public class JeecgBootExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
}
@ExceptionHandler(DataIntegrityViolationException.class)
public Result<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
log.error(e.getMessage(), e);
addSysLog(e);
//【issues/3624】数据库执行异常handleDataIntegrityViolationException提示有误 #3624
return Result.error("执行数据库异常,违反了完整性例如:违反惟一约束、违反非空限制、字段内容超出长度等");
}
@ -129,6 +167,7 @@ public class JeecgBootExceptionHandler {
@ExceptionHandler(PoolException.class)
public Result<?> handlePoolException(PoolException e) {
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error("Redis 连接异常!");
}
@ -149,7 +188,57 @@ public class JeecgBootExceptionHandler {
log.error("校验失败存在SQL注入风险{}", msg);
return Result.error("校验失败存在SQL注入风险");
}
addSysLog(exception);
return Result.error("校验失败存在SQL注入风险" + msg);
}
//update-begin---author:chenrui ---date:20240423 for[QQYUN-8732]把错误的日志都抓取了 方便后续处理,单独弄个日志类型------------
/**
*
* @param e
* @author chenrui
* @date 2024/4/22 17:16
*/
private void addSysLog(Throwable e) {
LogDTO log = new LogDTO();
log.setLogType(CommonConstant.LOG_TYPE_4);
log.setLogContent(e.getClass().getName()+":"+e.getMessage());
log.setRequestParam(ExceptionUtils.getStackTrace(e));
//获取request
HttpServletRequest request = null;
try {
request = SpringContextUtils.getHttpServletRequest();
} catch (NullPointerException | BeansException ignored) {
}
if (null != request) {
//请求的参数
Map<String, String[]> parameterMap = request.getParameterMap();
if(!CollectionUtils.isEmpty(parameterMap)){
log.setMethod(oConvertUtils.mapToString(request.getParameterMap()));
}
// 请求地址
log.setRequestUrl(request.getRequestURI());
//设置IP地址
log.setIp(IpUtils.getIpAddr(request));
//设置客户端
if(BrowserUtils.isDesktop(request)){
log.setClientType(ClientTerminalTypeEnum.PC.getKey());
}else{
log.setClientType(ClientTerminalTypeEnum.APP.getKey());
}
}
//获取登录用户信息
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
if(sysUser!=null){
log.setUserid(sysUser.getUsername());
log.setUsername(sysUser.getRealname());
}
baseCommonService.addLog(log);
}
//update-end---author:chenrui ---date:20240423 for[QQYUN-8732]把错误的日志都抓取了 方便后续处理,单独弄个日志类型------------
}

View File

@ -0,0 +1,19 @@
package org.jeecg.common.system.enhance;
import java.util.List;
/**
*
*/
public interface UserFilterEnhance {
/**
* id
* @param loginUserId id
*
* @return List<String> id
*/
default List<String> getUserIds(String loginUserId) {
return null;
}
}

View File

@ -2,7 +2,6 @@ package org.jeecg.common.system.query;
import java.beans.PropertyDescriptor;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.text.ParseException;
@ -15,7 +14,6 @@ import java.util.stream.Collectors;
import org.apache.commons.beanutils.PropertyUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JeecgDataAutorUtils;
import org.jeecg.common.system.util.JwtUtil;
@ -25,7 +23,6 @@ import org.jeecg.common.util.*;
import org.springframework.util.NumberUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
@ -94,10 +91,27 @@ public class QueryGenerator {
public static <T> QueryWrapper<T> initQueryWrapper(T searchObj,Map<String, String[]> parameterMap){
long start = System.currentTimeMillis();
QueryWrapper<T> queryWrapper = new QueryWrapper<T>();
installMplus(queryWrapper, searchObj, parameterMap);
installMplus(queryWrapper, searchObj, parameterMap, null);
log.debug("---查询条件构造器初始化完成,耗时:"+(System.currentTimeMillis()-start)+"毫秒----");
return queryWrapper;
}
//update-begin---author:chenrui ---date:20240527 for[TV360X-378]增加自定义字段查询规则功能------------
/**
* QueryWrapper
* @param searchObj
* @param parameterMap request.getParameterMap()
* @param customRuleMap {field:QueryRuleEnum}
* @return QueryWrapper
*/
public static <T> QueryWrapper<T> initQueryWrapper(T searchObj,Map<String, String[]> parameterMap, Map<String, QueryRuleEnum> customRuleMap){
long start = System.currentTimeMillis();
QueryWrapper<T> queryWrapper = new QueryWrapper<T>();
installMplus(queryWrapper, searchObj, parameterMap, customRuleMap);
log.debug("---查询条件构造器初始化完成,耗时:"+(System.currentTimeMillis()-start)+"毫秒----");
return queryWrapper;
}
//update-end---author:chenrui ---date:20240527 for[TV360X-378]增加自定义字段查询规则功能------------
/**
* Mybatis Plus
@ -108,8 +122,7 @@ public class QueryGenerator {
* <br>:QueryWrapper<JeecgDemo> queryWrapper = new QueryWrapper<JeecgDemo>();
* <br>3.使 {@link #initQueryWrapper}
*/
private static void installMplus(QueryWrapper<?> queryWrapper,Object searchObj,Map<String, String[]> parameterMap) {
private static void installMplus(QueryWrapper<?> queryWrapper, Object searchObj, Map<String, String[]> parameterMap, Map<String, QueryRuleEnum> customRuleMap) {
/*
* : orgCode #{sys_org_code}
SQLorgCode in #{sys_org_code}
@ -174,8 +187,16 @@ public class QueryGenerator {
queryWrapper.and(j -> j.like(field,vals[0]));
}
}else {
//根据参数值带什么关键字符串判断走什么类型的查询
QueryRuleEnum rule = convert2Rule(value);
//update-begin---author:chenrui ---date:20240527 for[TV360X-378]增加自定义字段查询规则功能------------
QueryRuleEnum rule;
if(null != customRuleMap && customRuleMap.containsKey(name)) {
// 有自定义规则,使用自定义规则.
rule = customRuleMap.get(name);
}else {
//根据参数值带什么关键字符串判断走什么类型的查询
rule = convert2Rule(value);
}
//update-end---author:chenrui ---date:20240527 for[TV360X-378]增加自定义字段查询规则功能------------
value = replaceValue(rule,value);
// add -begin 添加判断为字符串时设为全模糊查询
//if( (rule==null || QueryRuleEnum.EQ.equals(rule)) && "class java.lang.String".equals(type)) {
@ -274,7 +295,7 @@ public class QueryGenerator {
//update-end-author:scott date:2022-10-10 for:【jeecg-boot/issues/I5FJU6】doMultiFieldsOrder() 多字段排序方法存在问题
//SQL注入check
SqlInjectionUtil.filterContent(column);
SqlInjectionUtil.filterContentMulti(column);
//update-begin--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
// 排序规则修改
@ -678,9 +699,40 @@ public class QueryGenerator {
case LEFT_LIKE:
queryWrapper.likeLeft(name, value);
break;
case NOT_LEFT_LIKE:
queryWrapper.notLikeLeft(name, value);
break;
case RIGHT_LIKE:
queryWrapper.likeRight(name, value);
break;
case NOT_RIGHT_LIKE:
queryWrapper.notLikeRight(name, value);
break;
//update-begin---author:chenrui ---date:20240527 for[TV360X-378]下拉多框根据条件查询不出来:增加自定义字段查询规则功能------------
case LIKE_WITH_OR:
final String nameFinal = name;
Object[] vals;
if (value instanceof String) {
vals = value.toString().split(COMMA);
} else if (value instanceof String[]) {
vals = (Object[]) value;
}
//update-begin-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
else if (value.getClass().isArray()) {
vals = (Object[]) value;
} else {
vals = new Object[]{value};
}
queryWrapper.and(j -> {
log.info("---查询过滤器Query规则---field:{}, rule:{}, value:{}", nameFinal, "like", vals[0]);
j = j.like(nameFinal, vals[0]);
for (int k = 1; k < vals.length; k++) {
j = j.or().like(nameFinal, vals[k]);
log.info("---查询过滤器Query规则 .or()---field:{}, rule:{}, value:{}", nameFinal, "like", vals[k]);
}
});
break;
//update-end---author:chenrui ---date:20240527 for[TV360X-378]下拉多框根据条件查询不出来:增加自定义字段查询规则功能------------
default:
log.info("--查询规则未匹配到---");
break;
@ -856,7 +908,9 @@ public class QueryGenerator {
Class propType = origDescriptors[i].getPropertyType();
boolean isString = propType.equals(String.class);
Object value;
if(isString) {
//update-begin---author:chenrui ---date:20240527 for[TV360X-539]数据权限,配置日期等于条件时后端报转换错误------------
if(isString || Date.class.equals(propType)) {
//update-end---author:chenrui ---date:20240527 for[TV360X-539]数据权限,配置日期等于条件时后端报转换错误------------
value = converRuleValue(dataRule.getRuleValue());
}else {
value = NumberUtils.parseNumber(dataRule.getRuleValue(),propType);

View File

@ -33,12 +33,21 @@ public enum QueryRuleEnum {
RIGHT_LIKE("RIGHT_LIKE","right_like","右模糊"),
/**查询规则 带加号等于*/
EQ_WITH_ADD("EQWITHADD","eq_with_add","带加号等于"),
/**查询规则 多词模糊匹配*/
/**查询规则 多词模糊匹配(and)*/
LIKE_WITH_AND("LIKEWITHAND","like_with_and","多词模糊匹配————暂时未用上"),
/**查询规则 多词模糊匹配(or)*/
LIKE_WITH_OR("LIKEWITHOR","like_with_or","多词模糊匹配(or)"),
/**查询规则 自定义SQL片段*/
SQL_RULES("USE_SQL_RULES","ext","自定义SQL片段"),
/** 查询工作表 */
LINKAGE("LINKAGE","linkage","查询工作表"),
// ------- 当前表单设计器内专用 -------
/**查询规则 不以…结尾*/
NOT_LEFT_LIKE("NOT_LEFT_LIKE","not_left_like","不以…结尾"),
/**查询规则 不以…开头*/
NOT_RIGHT_LIKE("NOT_RIGHT_LIKE","not_right_like","不以…开头"),
/** 值为空 */
EMPTY("EMPTY","empty","值为空"),
/** 值不为空 */
@ -49,7 +58,10 @@ public enum QueryRuleEnum {
ELE_MATCH("ELE_MATCH","elemMatch","多词匹配"),
/**查询规则 范围查询*/
RANGE("RANGE","range","范围查询"),
NOT_RANGE("NOT_RANGE","not_range","不在范围查询");
/**查询规则 不在范围内查询*/
NOT_RANGE("NOT_RANGE","not_range","不在范围查询"),
/** 自定义mongodb查询语句 */
CUSTOM_MONGODB("CUSTOM_MONGODB","custom_mongodb","自定义mongodb查询语句");
// ------- 当前表单设计器内专用 -------
private String value;

View File

@ -221,6 +221,16 @@ public class JwtUtil {
returnValue = user.getSysUserCode();
}
}
// 替换为系统登录用户ID
else if (key.equals(DataBaseConstant.SYS_USER_ID) || key.equalsIgnoreCase(DataBaseConstant.SYS_USER_ID_TABLE)) {
if(user==null) {
returnValue = sysUser.getId();
}else {
returnValue = user.getSysUserId();
}
}
//替换为系统登录用户真实名字
else if (key.equals(DataBaseConstant.SYS_USER_NAME)|| key.toLowerCase().equals(DataBaseConstant.SYS_USER_NAME_TABLE)) {
if(user==null) {
@ -238,6 +248,16 @@ public class JwtUtil {
returnValue = user.getSysOrgCode();
}
}
// 替换为系统用户登录所使用的机构ID
else if (key.equals(DataBaseConstant.SYS_ORG_ID) || key.equalsIgnoreCase(DataBaseConstant.SYS_ORG_ID_TABLE)) {
if (user == null) {
returnValue = sysUser.getOrgId();
} else {
returnValue = user.getSysOrgId();
}
}
//替换为系统用户所拥有的所有机构编码
else if (key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_MULTI_ORG_CODE_TABLE)) {
if(user==null){
@ -251,6 +271,16 @@ public class JwtUtil {
}
}
}
// 替换为当前登录用户的角色code多个逗号分割
else if (key.equals(DataBaseConstant.SYS_ROLE_CODE) || key.equalsIgnoreCase(DataBaseConstant.SYS_ROLE_CODE_TABLE)) {
if (user == null) {
returnValue = sysUser.getRoleCode();
} else {
returnValue = user.getSysRoleCode();
}
}
//update-begin-author:taoyan date:20210330 for:多租户ID作为系统变量
else if (key.equals(TenantConstant.TENANT_ID) || key.toLowerCase().equals(TenantConstant.TENANT_ID_TABLE)){
try {

View File

@ -3,7 +3,9 @@ package org.jeecg.common.system.util;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.annotation.EnumDict;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
@ -114,4 +116,21 @@ public class ResourceUtil {
return map;
}
/**
*
*
* @param classPath
*/
public static Object getImplementationClass(String classPath){
try {
Class<?> aClass = Class.forName(classPath);
return SpringContextUtils.getBean(aClass);
} catch (ClassNotFoundException e) {
log.error("类没有找到",e);
return null;
} catch (NoSuchBeanDefinitionException e){
log.error(classPath + "没有实现",e);
return null;
}
}
}

View File

@ -51,6 +51,17 @@ public class LoginUser {
*/
@SensitiveField
private String orgCode;
/**
* id
*/
@SensitiveField
private String orgId;
/**
* code
*/
@SensitiveField
private String roleCode;
/**
*
*/

View File

@ -9,17 +9,29 @@ import org.jeecg.common.util.DateUtils;
* @author: jeecg-boot
*/
public class SysUserCacheInfo {
private String sysUserId;
private String sysUserCode;
private String sysUserName;
private String sysOrgCode;
/**
* ID
*/
private String sysOrgId;
private List<String> sysMultiOrgCode;
private boolean oneDepart;
/**
* code
*/
private String sysRoleCode;
public boolean isOneDepart() {
return oneDepart;
}
@ -68,4 +80,27 @@ public class SysUserCacheInfo {
this.sysMultiOrgCode = sysMultiOrgCode;
}
public String getSysUserId() {
return sysUserId;
}
public void setSysUserId(String sysUserId) {
this.sysUserId = sysUserId;
}
public String getSysOrgId() {
return sysOrgId;
}
public void setSysOrgId(String sysOrgId) {
this.sysOrgId = sysOrgId;
}
public String getSysRoleCode() {
return sysRoleCode;
}
public void setSysRoleCode(String sysRoleCode) {
this.sysRoleCode = sysRoleCode;
}
}

View File

@ -0,0 +1,242 @@
package org.jeecg.common.util;
import cn.hutool.core.date.DateUtil;
import org.jeecg.common.constant.enums.DateRangeEnum;
import java.util.Calendar;
import java.util.Date;
/**
*
*
* @author scott
* @date 20230801
*/
public class DateRangeUtils {
/**
*
*
* @param rangeEnum
* @return Date[]
*/
public static Date[] getDateRangeByEnum(DateRangeEnum rangeEnum) {
if (rangeEnum == null) {
return null;
}
Date[] ranges = new Date[2];
switch (rangeEnum) {
case TODAY:
ranges[0] = getTodayStartTime();
ranges[1] = getTodayEndTime();
break;
case YESTERDAY:
ranges[0] = getYesterdayStartTime();
ranges[1] = getYesterdayEndTime();
break;
case TOMORROW:
ranges[0] = getTomorrowStartTime();
ranges[1] = getTomorrowEndTime();
break;
case THIS_WEEK:
ranges[0] = getThisWeekStartDay();
ranges[1] = getThisWeekEndDay();
break;
case LAST_WEEK:
ranges[0] = getLastWeekStartDay();
ranges[1] = getLastWeekEndDay();
break;
case NEXT_WEEK:
ranges[0] = getNextWeekStartDay();
ranges[1] = getNextWeekEndDay();
break;
case LAST_7_DAYS:
ranges[0] = getLast7DaysStartTime();
ranges[1] = getLast7DaysEndTime();
break;
case THIS_MONTH:
ranges[0] = getThisMonthStartDay();
ranges[1] = getThisMonthEndDay();
break;
case LAST_MONTH:
ranges[0] = getLastMonthStartDay();
ranges[1] = getLastMonthEndDay();
break;
case NEXT_MONTH:
ranges[0] = getNextMonthStartDay();
ranges[1] = getNextMonthEndDay();
break;
default:
return null;
}
return ranges;
}
/**
* 00:00:00
*/
public static Date getNextMonthStartDay() {
return DateUtil.beginOfMonth(DateUtil.nextMonth());
}
/**
* 23:59:59
*/
public static Date getNextMonthEndDay() {
return DateUtil.endOfMonth(DateUtil.nextMonth());
}
/**
* 00:00:00
*/
public static Date getThisMonthStartDay() {
return DateUtil.beginOfMonth(DateUtil.date());
}
/**
* 23:59:59
*/
public static Date getThisMonthEndDay() {
return DateUtil.endOfMonth(DateUtil.date());
}
/**
* 00:00:00
*/
public static Date getLastMonthStartDay() {
return DateUtil.beginOfMonth(DateUtil.lastMonth());
}
/**
* 23:59:59
*/
public static Date getLastMonthEndDay() {
return DateUtil.endOfMonth(DateUtil.lastMonth());
}
/**
* 00:00:00
*/
public static Date getLastWeekStartDay() {
return DateUtil.beginOfWeek(DateUtil.lastWeek());
}
/**
* 23:59:59
*/
public static Date getLastWeekEndDay() {
return DateUtil.endOfWeek(DateUtil.lastWeek());
}
/**
* 00:00:00
*/
public static Date getThisWeekStartDay() {
Date today = new Date();
return DateUtil.beginOfWeek(today);
}
/**
* 23:59:59
*/
public static Date getThisWeekEndDay() {
Date today = new Date();
return DateUtil.endOfWeek(today);
}
/**
* 00:00:00
*/
public static Date getNextWeekStartDay() {
return DateUtil.beginOfWeek(DateUtil.nextWeek());
}
/**
* 23:59:59
*/
public static Date getNextWeekEndDay() {
return DateUtil.endOfWeek(DateUtil.nextWeek());
}
/**
*
*
* @return
*/
public static Date getLast7DaysStartTime() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.DATE, -7);
return DateUtil.beginOfDay(calendar.getTime());
}
/**
*
*
* @return
*/
public static Date getLast7DaysEndTime() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(getLast7DaysStartTime());
calendar.add(Calendar.DATE, 6);
return DateUtil.endOfDay(calendar.getTime());
}
/**
*
*
* @return
*/
public static Date getYesterdayStartTime() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.DATE, -1);
return DateUtil.beginOfDay(calendar.getTime());
}
/**
*
*
* @return
*/
public static Date getYesterdayEndTime() {
return DateUtil.endOfDay(getYesterdayStartTime());
}
/**
*
*
* @return
*/
public static Date getTomorrowStartTime() {
return DateUtil.beginOfDay(DateUtil.tomorrow());
}
/**
*
*
* @return
*/
public static Date getTomorrowEndTime() {
return DateUtil.endOfDay(DateUtil.tomorrow());
}
/**
*
*
* @return
*/
public static Date getTodayStartTime() {
return DateUtil.beginOfDay(new Date());
}
/**
*
*
* @return
*/
public static Date getTodayEndTime() {
return DateUtil.endOfDay(new Date());
}
}

View File

@ -8,6 +8,11 @@ import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@ -116,6 +121,17 @@ public class DateUtils extends PropertyEditorSupport {
public static Date getDate() {
return new Date();
}
/**
*
*
* @return
*/
public static LocalDate getLocalDate() {
LocalDate today = LocalDate.now();
return today;
}
/**
*
@ -704,6 +720,44 @@ public class DateUtils extends PropertyEditorSupport {
return isSameMonth && calendar1.get(Calendar.DAY_OF_MONTH) == calendar2.get(Calendar.DAY_OF_MONTH);
}
/**
*
*
* @param targetDate
* @return
*/
public static long calculateTimeDifference(Date targetDate) {
// 获取当前时间
LocalDateTime currentTime = LocalDateTime.now();
// 将java.util.Date转换为java.time.LocalDateTime
LocalDateTime convertedTargetDate = targetDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
// 计算时间差
Duration duration = Duration.between(currentTime, convertedTargetDate);
// 获取时间差的毫秒数
long timeDifferenceInMillis = duration.toMillis();
return timeDifferenceInMillis;
}
/**
*
*
* @param targetDate
* @return
*/
public static long calculateDaysDifference(Date targetDate) {
// 获取当前日期
LocalDate currentDate = LocalDate.now();
// 将java.util.Date转换为java.time.LocalDate
LocalDate convertedTargetDate = targetDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
// 计算日期差
long daysDifference = ChronoUnit.DAYS.between(currentDate, convertedTargetDate);
return daysDifference;
}
/**
*
*

View File

@ -0,0 +1,96 @@
package org.jeecg.common.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ConcurrentHashMap;
/**
* SMS_175430166
*
* 1IP15IP
* 2IP120使
*
* 3
*
* /sys/sms
* /desform/api/sendVerifyCode
* /sys/sendChangePwdSms
*/
@Slf4j
public class DySmsLimit {
// 1分钟内最大发短信数量单一IP
private static final int MAX_MESSAGE_PER_MINUTE = 5;
// 1分钟
private static final int MILLIS_PER_MINUTE = 60000;
// 一分钟内报警线最大短信数量超了进黑名单单一IP
private static final int MAX_TOTAL_MESSAGE_PER_MINUTE = 20;
private static ConcurrentHashMap<String, Long> ipLastRequestTime = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, Integer> ipRequestCount = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, Boolean> ipBlacklist = new ConcurrentHashMap<>();
/**
* @param ip IP
* @return
*/
public static boolean canSendSms(String ip) {
long currentTime = System.currentTimeMillis();
long lastRequestTime = ipLastRequestTime.getOrDefault(ip, 0L);
int requestCount = ipRequestCount.getOrDefault(ip, 0);
log.info("IP{}, Msg requestCount{} ", ip, requestCount);
if (ipBlacklist.getOrDefault(ip, false)) {
// 如果IP在黑名单中则禁止发送短信
log.error("IP{}, 进入黑名单,禁止发送请求短信!", ip);
return false;
}
if (currentTime - lastRequestTime >= MILLIS_PER_MINUTE) {
// 如果距离上次请求已经超过一分钟,则重置计数
ipRequestCount.put(ip, 1);
ipLastRequestTime.put(ip, currentTime);
return true;
} else {
// 如果距离上次请求不到一分钟
ipRequestCount.put(ip, requestCount + 1);
if (requestCount < MAX_MESSAGE_PER_MINUTE) {
// 如果请求次数小于5次允许发送短信
return true;
} else if (requestCount >= MAX_TOTAL_MESSAGE_PER_MINUTE) {
// 如果请求次数超过报警线短信数量将IP加入黑名单
ipBlacklist.put(ip, true);
return false;
} else {
log.error("IP{}, 1分钟内请求短信超过5次请稍后重试", ip);
return false;
}
}
}
/**
*
*
* @param ip IP
*/
public static void clearSendSmsCount(String ip) {
long currentTime = System.currentTimeMillis();
ipRequestCount.put(ip, 0);
ipLastRequestTime.put(ip, currentTime);
}
// public static void main(String[] args) {
// String ip = "192.168.1.1";
// for (int i = 1; i < 50; i++) {
// if (canSendSms(ip)) {
// System.out.println("Send SMS successfully");
// } else {
// //System.out.println("Exceed SMS limit for IP " + ip);
// }
// }
//
// System.out.println(ipLastRequestTime);
// System.out.println(ipRequestCount);
// System.out.println(ipBlacklist);
// }
}

View File

@ -7,6 +7,11 @@ import org.jeecg.common.constant.CommonConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* IP
*
@ -45,15 +50,52 @@ public class IpUtils {
} catch (Exception e) {
logger.error("IPUtils ERROR ", e);
}
// //使用代理则获取第一个IP地址
// if(StringUtils.isEmpty(ip) && ip.length() > 15) {
// if(ip.indexOf(",") > 0) {
// ip = ip.substring(0, ip.indexOf(","));
// }
// }
//logger.info("获取客户端 ip{} ", ip);
// 使用代理则获取第一个IP地址
if (StringUtils.isNotEmpty(ip) && ip.length() > 15) {
if (ip.indexOf(",") > 0) {
//ip = ip.substring(0, ip.indexOf(","));
String[] ipAddresses = ip.split(",");
for (String ipAddress : ipAddresses) {
ipAddress = ipAddress.trim();
if (isValidIpAddress(ipAddress)) {
return ipAddress;
}
}
}
}
return ip;
}
/**
* IP
* @param ipAddress
* @return
*/
public static boolean isValidIpAddress(String ipAddress) {
String ipPattern = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
Pattern pattern = Pattern.compile(ipPattern);
Matcher matcher = pattern.matcher(ipAddress);
return matcher.matches();
}
/**
* ip
* @return
*/
public static String getServerIp(){
InetAddress inetAddress = null;
try {
inetAddress = InetAddress.getLocalHost();
String ipAddress = inetAddress.getHostAddress();
//System.out.println("IP地址: " + ipAddress);
return ipAddress;
} catch (UnknownHostException e) {
logger.error("获取ip地址失败", e);
}
return "";
}
}

View File

@ -75,7 +75,7 @@ public class SqlInjectionUtil {
* sql
* @param values
*/
public static void filterContent(String... values) {
public static void filterContentMulti(String... values) {
filterContent(values, null);
}
@ -291,7 +291,15 @@ public class SqlInjectionUtil {
if(oConvertUtils.isEmpty(table)){
return table;
}
//update-begin---author:scott ---date:2024-05-28 for表单设计器列表翻译存在表名带条件导致翻译出问题----
int index = table.toLowerCase().indexOf(" where ");
if (index != -1) {
table = table.substring(0, index);
log.info("截掉where之后的新表名" + table);
}
//update-end---author:scott ---date::2024-05-28 for表单设计器列表翻译存在表名带条件导致翻译出问题----
table = table.trim();
/**
*
@ -308,7 +316,7 @@ public class SqlInjectionUtil {
}
//进一步验证是否存在SQL注入风险
filterContent(table);
filterContentMulti(table);
return table;
}
@ -345,7 +353,7 @@ public class SqlInjectionUtil {
}
//进一步验证是否存在SQL注入风险
filterContent(field);
filterContentMulti(field);
return field;
}

View File

@ -28,6 +28,10 @@ public class TokenUtils {
* @return
*/
public static String getTokenByRequest(HttpServletRequest request) {
if (request == null) {
return null;
}
String token = request.getParameter("token");
if (token == null) {
token = request.getHeader("X-Access-Token");

View File

@ -38,6 +38,11 @@ public class DynamicDBUtil {
String driverClassName = dbSource.getDbDriver();
String url = dbSource.getDbUrl();
// url配置成 “123” 会触发Druid死循环一直去重复尝试连接
if (oConvertUtils.isEmpty(url) || !url.toLowerCase().startsWith("jdbc:")) {
throw new JeecgBootException("数据源URL配置格式不正确");
}
String dbUser = dbSource.getDbUsername();
String dbPassword = dbSource.getDbPassword();
dataSource.setDriverClassName(driverClassName);
@ -47,6 +52,8 @@ public class DynamicDBUtil {
dataSource.setTestOnBorrow(false);
dataSource.setTestOnReturn(false);
dataSource.setBreakAfterAcquireFailure(true);
//设置超时时间60秒
dataSource.setLoginTimeout(60);
dataSource.setConnectionErrorRetryAttempts(0);
dataSource.setUsername(dbUser);
dataSource.setMaxWait(30000);

View File

@ -2,7 +2,9 @@ package org.jeecg.common.util;
import com.alibaba.fastjson.JSONArray;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.springframework.beans.BeanUtils;
@ -14,10 +16,7 @@ import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.net.*;
import java.sql.Date;
import java.util.*;
import java.util.regex.Matcher;
@ -50,6 +49,27 @@ public class oConvertUtils {
return (false);
}
/**
* decode
*
* @param inStr
* @return
*/
public static String decodeString(String inStr) {
if (oConvertUtils.isEmpty(inStr)) {
return null;
}
try {
inStr = URLDecoder.decode(inStr, "UTF-8");
} catch (Exception e) {
// 解决URLDecoder: Illegal hex characters in escape (%) pattern - For input string: "自动"
//e.printStackTrace();
}
return inStr;
}
public static String decode(String strIn, String sourceCode, String targetCode) {
String temp = code2code(strIn, sourceCode, targetCode);
return temp;
@ -238,6 +258,20 @@ public class oConvertUtils {
return (String.valueOf(i));
}
/**
*
*
* @param input
* @return
*/
public static String getNormalString(String input) {
if (oConvertUtils.isEmpty(input)) {
return null;
}
String result = input.replaceAll("[^0-9a-zA-Z\\u4e00-\\u9fa5]", "");
return result;
}
public static String getString(String s, String defval) {
if (isEmpty(s)) {
return (defval);
@ -287,6 +321,22 @@ public class oConvertUtils {
return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive());
}
/**
* base64
*
* @param base64Str base64
* @return
*/
public static String decodeBase64Str(String base64Str) {
byte[] byteContent = Base64.decodeBase64(base64Str);
if (byteContent == null) {
return null;
}
String decodedString = new String(byteContent);
return decodedString;
}
/**
* @param request
* IP
@ -750,6 +800,16 @@ public class oConvertUtils {
}
return obj.getClass().isArray();
}
/**
*
*
* @param collection
* @return
*/
public static int getCollectionSize(Collection<?> collection) {
return collection != null ? collection.size() : 0;
}
/**
*
@ -941,5 +1001,32 @@ public class oConvertUtils {
}
return count;
}
/**
* mapstr
*
* @param map
* @return
*/
public static String mapToString(Map<String, String[]> map) {
if (map == null || map.size() == 0) {
return null;
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String[]> entry : map.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
sb.append(key).append("=");
sb.append(values != null ? StringUtils.join(values, ",") : "");
sb.append("&");
}
String result = sb.toString();
if (result.endsWith("&")) {
result = result.substring(0, sb.length() - 1);
}
return result;
}
}

View File

@ -166,22 +166,22 @@ public class ConstAnalyzer implements ExpressionVisitor, ItemsListVisitor {
expr.getBetweenExpressionEnd().accept(this);
}
// /**
// * 用于处理 OverlapsCondition 类型的表达式
// * @param overlapsCondition
// */
// @Override
// public void visit(OverlapsCondition overlapsCondition) {
// constFlag.set(false);
// }
// /**
// * 用于处理 SafeCastExpression 类型的表达式。
// * @param safeCastExpression
// */
// @Override
// public void visit(SafeCastExpression safeCastExpression) {
// constFlag.set(false);
// }
/**
* OverlapsCondition
* @param overlapsCondition
*/
@Override
public void visit(OverlapsCondition overlapsCondition) {
constFlag.set(false);
}
/**
* SafeCastExpression
* @param safeCastExpression
*/
@Override
public void visit(SafeCastExpression safeCastExpression) {
constFlag.set(false);
}
@Override
public void visit(EqualsTo expr) {

View File

@ -13,6 +13,9 @@ import java.util.Map;
/**
* @author eightmonth@qq.com
* DruidWallConfig
* SELECTWHERE
* @author eightmonth
* @date 2024/4/8 11:37
*/
public class DruidWallConfigRegister implements SpringApplicationRunListener {

View File

@ -10,20 +10,21 @@ import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@ -39,6 +40,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
/**
* Spring Boot 2.0
@ -57,6 +59,11 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired(required = false)
private PrometheusMeterRegistry prometheusMeterRegistry;
@Autowired
private ObjectProvider<Jackson2ObjectMapperBuilder> builderProvider;
@Autowired
private JacksonProperties jacksonProperties;
/**
* - 使 Html
*/
@ -109,6 +116,10 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
@Primary
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 继承spring jackson 默认机制
if (Objects.nonNull(builderProvider.getIfAvailable())) {
objectMapper = builderProvider.getIfAvailable().createXmlMapper(false).build();
}
//处理bigDecimal
objectMapper.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
@ -117,8 +128,10 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, false);
//默认的处理日期时间格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//默认的处理日期时间格式,接受通过spring.jackson.date-format配置格式化模式
if (Objects.isNull(jacksonProperties.getDateFormat())) {
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
@ -130,17 +143,17 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
return objectMapper;
}
/**
* SpringBootAdminHttptrace
* https://blog.csdn.net/u013810234/article/details/110097201
*/
@Bean
public InMemoryHttpExchangeRepository getInMemoryHttpTrace(){
InMemoryHttpExchangeRepository repository = new InMemoryHttpExchangeRepository();
// 默认保存1000条http请求记录
repository.setCapacity(1000);
return repository;
}
// /**
// * SpringBootAdmin的Httptrace不见了
// * https://blog.csdn.net/u013810234/article/details/110097201
// */
// @Bean
// public InMemoryHttpExchangeRepository getInMemoryHttpTrace(){
// InMemoryHttpExchangeRepository repository = new InMemoryHttpExchangeRepository();
// // 默认保存1000条http请求记录
// repository.setCapacity(1000);
// return repository;
// }
/**

View File

@ -71,7 +71,7 @@ public class LowCodeModeInterceptor implements HandlerInterceptor {
if (loginUser == null) {
loginUser = commonAPI.getUserByName(JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest()));
//当前登录人拥有的角色
hasRoles = commonAPI.queryUserRoles(loginUser.getUsername());
hasRoles = commonAPI.queryUserRolesById(loginUser.getId());
}
log.info("get loginUser info: {}", loginUser);

View File

@ -10,6 +10,8 @@ import org.apache.shiro.SecurityUtils;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.TenantConstant;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.stereotype.Component;
@ -94,7 +96,24 @@ public class MybatisInterceptor implements Interceptor {
field.setAccessible(false);
if (localTenantId == null) {
field.setAccessible(true);
field.set(parameter, oConvertUtils.getInt(TenantContext.getTenant(),0));
String tenantId = TenantContext.getTenant();
//如果通过线程获取租户ID为空则通过当前请求的request获取租户shiro排除拦截器的请求会获取不到租户ID
if(oConvertUtils.isEmpty(tenantId) && MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
try {
tenantId = TokenUtils.getTenantIdByRequest(SpringContextUtils.getHttpServletRequest());
} catch (Exception e) {
//e.printStackTrace();
}
}
if (field.getType().equals(String.class)) {
// 字段类型为String
field.set(parameter, tenantId);
} else {
// 字段类型不是String
field.set(parameter, oConvertUtils.getInt(tenantId, 0));
}
field.setAccessible(false);
}
}

View File

@ -30,7 +30,7 @@ import net.sf.jsqlparser.expression.LongValue;
*
*/
@Configuration
@MapperScan(value={"org.jeecg.modules.**.mapper*"})
@MapperScan(value={"org.jeecg.**.mapper*"})
public class MybatisPlusSaasConfig {
/**

View File

@ -6,8 +6,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* spring MVC@RequestMapping
* @author eightmonth@qq.com
* Token
*
* spring MVC@RequestMapping
* @author eightmonth
* @date 2024/2/28 9:58
*/
@Target(ElementType.METHOD)

View File

@ -19,13 +19,13 @@ import org.jeecg.config.shiro.filters.JwtFilter;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
@ -47,8 +47,6 @@ import java.util.*;
@Slf4j
@Configuration
// 免认证注解 @IgnoreAuth 注解生效范围配置
@ComponentScan(basePackages = {"org.jeecg"})
public class ShiroConfig {
@Resource
@ -59,6 +57,7 @@ public class ShiroConfig {
private JeecgBaseConfig jeecgBaseConfig;
@Autowired(required = false)
private RedisProperties redisProperties;
/**
* Filter Chain
*
@ -88,6 +87,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/sys/cas/client/validateLogin", "anon"); //cas验证登录
filterChainDefinitionMap.put("/sys/randomImage/**", "anon"); //登录验证码接口排除
filterChainDefinitionMap.put("/sys/checkCaptcha", "anon"); //登录验证码接口排除
filterChainDefinitionMap.put("/sys/smsCheckCaptcha", "anon"); //短信次数发送太多验证码排除
filterChainDefinitionMap.put("/sys/login", "anon"); //登录接口排除
filterChainDefinitionMap.put("/sys/mLogin", "anon"); //登录接口排除
filterChainDefinitionMap.put("/sys/logout", "anon"); //登出接口排除
@ -153,7 +153,7 @@ public class ShiroConfig {
//大屏模板例子
filterChainDefinitionMap.put("/test/bigScreen/**", "anon");
filterChainDefinitionMap.put("/bigscreen/template1/**", "anon");
filterChainDefinitionMap.put("/bigscreen/template1/**", "anon");
filterChainDefinitionMap.put("/bigscreen/template2/**", "anon");
//filterChainDefinitionMap.put("/test/jeecgDemo/rabbitMqClientTest/**", "anon"); //MQ测试
//filterChainDefinitionMap.put("/test/jeecgDemo/html", "anon"); //模板页面
//filterChainDefinitionMap.put("/test/jeecgDemo/redis/**", "anon"); //redis测试
@ -173,16 +173,6 @@ public class ShiroConfig {
// 企业微信证书排除
filterChainDefinitionMap.put("/WW_verify*", "anon");
// 通过注解免登录url
List<String> ignoreAuthUrlList = collectIgnoreAuthUrl();
if (!CollectionUtils.isEmpty(ignoreAuthUrlList)) {
for (String url : ignoreAuthUrlList) {
filterChainDefinitionMap.put(url, "anon");
}
}
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
@ -281,6 +271,9 @@ public class ShiroConfig {
}
/**
* RedisConfigstarter
* jeecg-boot-starter-github\jeecg-boot-common\src\main\java\org\jeecg\common\modules\redis\config\RedisConfig.java
*
* shiro redisManager
* 使shiro-redis
*
@ -335,62 +328,6 @@ public class ShiroConfig {
return manager;
}
@SneakyThrows
public List<String> collectIgnoreAuthUrl() {
List<String> ignoreAuthUrls = new ArrayList<>();
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(RestController.class));
// 获取当前类的扫描注解的配置
Set<BeanDefinition> components = new HashSet<>();
for (String basePackage : AnnotationUtils.getAnnotation(ShiroConfig.class, ComponentScan.class).basePackages()) {
components.addAll(provider.findCandidateComponents(basePackage));
}
// 逐个匹配获取免认证路径
for (BeanDefinition component : components) {
String beanClassName = component.getBeanClassName();
Class<?> clazz = Class.forName(beanClassName);
RequestMapping base = clazz.getAnnotation(RequestMapping.class);
String[] baseUrl = {};
if (Objects.nonNull(base)) {
baseUrl = base.value();
}
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(GetMapping.class)) {
GetMapping requestMapping = method.getAnnotation(GetMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PostMapping.class)) {
PostMapping requestMapping = method.getAnnotation(PostMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PutMapping.class)) {
PutMapping requestMapping = method.getAnnotation(PutMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(DeleteMapping.class)) {
DeleteMapping requestMapping = method.getAnnotation(DeleteMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PatchMapping.class)) {
PatchMapping requestMapping = method.getAnnotation(PatchMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
}
}
}
return ignoreAuthUrls;
}
private List<String> rebuildUrl(String[] bases, String[] uris) {
List<String> urls = new ArrayList<>();
for (String base : bases) {

View File

@ -71,7 +71,7 @@ public class ShiroRealm extends AuthorizingRealm {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 设置用户拥有的角色集合比如“admin,test”
Set<String> roleSet = commonApi.queryUserRoles(username);
Set<String> roleSet = commonApi.queryUserRolesById(userId);
//System.out.println(roleSet.toString());
info.setRoles(roleSet);
@ -226,6 +226,8 @@ public class ShiroRealm extends AuthorizingRealm {
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
//update-begin---author:scott ---date::2024-06-18 for【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
super.clearCachedAuthorizationInfo(principals);
//update-end---author:scott ---date::2024-06-18 for【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
}
}

View File

@ -8,6 +8,7 @@ import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.shiro.JwtToken;
import org.jeecg.config.shiro.ignore.InMemoryIgnoreAuth;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
@ -47,6 +48,11 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
// 判断当前路径是不是注解了@IngoreAuth路径如果是则放开验证
if (InMemoryIgnoreAuth.contains(((HttpServletRequest) request).getServletPath())) {
return true;
}
executeLogin(request, response);
return true;
} catch (Exception e) {

View File

@ -0,0 +1,113 @@
package org.jeecg.config.shiro.ignore;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.config.shiro.IgnoreAuth;
import org.springframework.aop.framework.Advised;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import java.lang.reflect.Method;
import java.util.*;
/**
* spring boot@RestControllerspringbean
* @author eightmonth
* @date 2024/4/18 11:35
*/
@Slf4j
@Component
@AllArgsConstructor
public class IgnoreAuthPostProcessor implements ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext applicationContext;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
long startTime = System.currentTimeMillis();
List<String> ignoreAuthUrls = new ArrayList<>();
if (event.getApplicationContext().getParent() == null) {
// 只处理根应用上下文的事件,避免在子上下文中重复处理
Map<String, Object> restControllers = applicationContext.getBeansWithAnnotation(RestController.class);
for (Object restController : restControllers.values()) {
// 如 online系统的controller并不是spring 默认生成
if (restController instanceof Advised) {
ignoreAuthUrls.addAll(postProcessRestController(restController));
}
}
}
log.info("Init Token ignoreAuthUrls Config [ 集合 ] {}", ignoreAuthUrls);
if (!CollectionUtils.isEmpty(ignoreAuthUrls)) {
InMemoryIgnoreAuth.set(ignoreAuthUrls);
}
// 计算方法的耗时
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
log.info("Init Token ignoreAuthUrls Config [ 耗时 ] " + elapsedTime + "毫秒");
}
private List<String> postProcessRestController(Object restController) {
List<String> ignoreAuthUrls = new ArrayList<>();
Class<?> clazz = ((Advised) restController).getTargetClass();
RequestMapping base = clazz.getAnnotation(RequestMapping.class);
String[] baseUrl = Objects.nonNull(base) ? base.value() : new String[]{};
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(GetMapping.class)) {
GetMapping requestMapping = method.getAnnotation(GetMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PostMapping.class)) {
PostMapping requestMapping = method.getAnnotation(PostMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PutMapping.class)) {
PutMapping requestMapping = method.getAnnotation(PutMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(DeleteMapping.class)) {
DeleteMapping requestMapping = method.getAnnotation(DeleteMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PatchMapping.class)) {
PatchMapping requestMapping = method.getAnnotation(PatchMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
}
}
return ignoreAuthUrls;
}
private List<String> rebuildUrl(String[] bases, String[] uris) {
List<String> urls = new ArrayList<>();
if (bases.length > 0) {
for (String base : bases) {
for (String uri : uris) {
urls.add(prefix(base) + prefix(uri));
}
}
} else {
Arrays.stream(uris).forEach(uri -> {
urls.add(prefix(uri));
});
}
return urls;
}
private String prefix(String seg) {
return seg.startsWith("/") ? seg : "/"+seg;
}
}

View File

@ -0,0 +1,38 @@
package org.jeecg.config.shiro.ignore;
import java.util.ArrayList;
import java.util.List;
/**
* 使@IgnoreAuthurlJwtFilter
* PS使ThreadLocalThreadLocalJwtFilterThreadLocal
* @author eightmonth
* @date 2024/4/18 15:02
*/
public class InMemoryIgnoreAuth {
private static final List<String> IGNORE_AUTH_LIST = new ArrayList<>();
public InMemoryIgnoreAuth() {}
public static void set(List<String> list) {
IGNORE_AUTH_LIST.addAll(list);
}
public static List<String> get() {
return IGNORE_AUTH_LIST;
}
public static void clear() {
IGNORE_AUTH_LIST.clear();
}
public static boolean contains(String url) {
for (String ignoreAuth : IGNORE_AUTH_LIST) {
if (url.endsWith(ignoreAuth)) {
return true;
}
}
return false;
}
}

View File

@ -59,7 +59,8 @@ public class HttpUtils {
// 获取URL上的参数
Map<String, String> urlParams = getUrlParams(request);
for (Map.Entry entry : urlParams.entrySet()) {
result.put((String)entry.getKey(), (String)entry.getValue());
//不能直接转成String,否则会有类型转换错误
result.put((String)entry.getKey(), String.valueOf(entry.getValue()));
}
Map<String, String> allRequestParam = new HashMap<>(16);
// get请求不需要拿body参数
@ -69,7 +70,8 @@ public class HttpUtils {
// 将URL的参数和body参数进行合并
if (allRequestParam != null) {
for (Map.Entry entry : allRequestParam.entrySet()) {
result.put((String)entry.getKey(), (String)entry.getValue());
//不能直接转成String,否则会有类型转换错误
result.put((String)entry.getKey(), String.valueOf(entry.getValue()));
}
}
return result;

View File

@ -4,7 +4,7 @@
<!-- 保存日志11 -->
<insert id="saveLog" parameterType="Object">
insert into sys_log (id, log_type, log_content, method, operate_type, request_url, request_type, request_param, ip, userid, username, cost_time, create_time,create_by, tenant_id)
insert into sys_log (id, log_type, log_content, method, operate_type, request_url, request_type, request_param, ip, userid, username, cost_time, create_time,create_by, tenant_id, client_type)
values(
#{dto.id,jdbcType=VARCHAR},
#{dto.logType,jdbcType=INTEGER},
@ -20,7 +20,8 @@
#{dto.costTime,jdbcType=BIGINT},
#{dto.createTime,jdbcType=TIMESTAMP},
#{dto.createBy,jdbcType=VARCHAR},
#{dto.tenantId,jdbcType=INTEGER}
#{dto.tenantId,jdbcType=INTEGER},
#{dto.clientType,jdbcType=VARCHAR}
)
</insert>

View File

@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.dto.LogDTO;
import org.jeecg.common.constant.enums.ClientTerminalTypeEnum;
import org.jeecg.common.util.BrowserUtils;
import org.jeecg.modules.base.mapper.BaseCommonMapper;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.common.system.vo.LoginUser;
@ -33,7 +35,7 @@ public class BaseCommonServiceImpl implements BaseCommonService {
logDTO.setId(String.valueOf(IdWorker.getId()));
}
//保存日志异常捕获处理防止数据太大存储失败导致业务失败JT-238
try {
try {
logDTO.setCreateTime(new Date());
baseCommonMapper.saveLog(logDTO);
} catch (Exception e) {
@ -55,6 +57,17 @@ public class BaseCommonServiceImpl implements BaseCommonService {
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
//设置IP地址
sysLog.setIp(IpUtils.getIpAddr(request));
try {
//设置客户端
if(BrowserUtils.isDesktop(request)){
sysLog.setClientType(ClientTerminalTypeEnum.PC.getKey());
}else{
sysLog.setClientType(ClientTerminalTypeEnum.APP.getKey());
}
} catch (Exception e) {
//e.printStackTrace();
}
} catch (Exception e) {
sysLog.setIp("127.0.0.1");
}

View File

@ -0,0 +1,41 @@
package org.jeecg.test.sqlparse;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.IpUtils;
import org.jeecg.common.util.oConvertUtils;
import org.junit.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author: scott
* @date: 20240429 16:48
*/
public class TestIpUtil {
public static void main(String[] args) {
Map<String, String[]> map = new HashMap<>();
map.put("key1", new String[]{"value1", "value2", "value3"});
map.put("key4", null);
map.put("key2", new String[]{"value4", "value5"});
map.put("key3", new String[]{"value6"});
System.out.println(oConvertUtils.mapToString(map));
}
@Test
public void test() {
String ip = "2408:8207:1851:10e0:50bd:1a50:60c8:b030, 115.231.101.180";
String[] ipAddresses = ip.split(",");
for (String ipAddress : ipAddresses) {
System.out.println(ipAddress);
ipAddress = ipAddress.trim();
if (IpUtils.isValidIpAddress(ipAddress)) {
System.out.println("ipAddress= " + ipAddress);
}
}
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-boot-parent</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -16,12 +16,6 @@
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base-core</artifactId>
</dependency>
<!-- chatgpt -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-starter-chatgpt</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -16,7 +16,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
* @Date: 2024/1/9 16:30
*/
@Controller
@RequestMapping("/ai/chat")
@RequestMapping("/test/ai/chat")
public class ChatController {
@Autowired

View File

@ -22,6 +22,7 @@ import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.UUIDGenerator;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.demo.test.entity.JeecgDemo;
import org.jeecg.modules.demo.test.service.IJeecgDemoService;
import org.springframework.beans.factory.annotation.Autowired;
@ -229,6 +230,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
* @param modelAndView
* @return
*/
@IgnoreAuth
@RequestMapping("/html")
public ModelAndView ftl(ModelAndView modelAndView) {
modelAndView.setViewName("demo3");

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-system-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -85,6 +85,14 @@ public interface ISysBaseAPI extends CommonAPI {
*/
@GetMapping("/sys/api/getRolesByUsername")
List<String> getRolesByUsername(@RequestParam("username") String username);
/**
* 7
* @param userId
* @return
*/
@GetMapping("/sys/api/getRolesByUserId")
List<String> getRolesByUserId(@RequestParam("userId") String userId);
/**
* 8
@ -93,6 +101,14 @@ public interface ISysBaseAPI extends CommonAPI {
*/
@GetMapping("/sys/api/getDepartIdsByUsername")
List<String> getDepartIdsByUsername(@RequestParam("username") String username);
/**
* 8
* @param userId
* @return id
*/
@GetMapping("/sys/api/getDepartIdsByUserId")
List<String> getDepartIdsByUserId(@RequestParam("userId") String userId);
/**
* 8.2 ID
@ -304,6 +320,14 @@ public interface ISysBaseAPI extends CommonAPI {
*/
@GetMapping("/sys/api/getUserRoleSet")
Set<String> getUserRoleSet(@RequestParam("username")String username);
/**
* 30
* @param userId
* @return
*/
@GetMapping("/sys/api/getUserRoleSetById")
Set<String> getUserRoleSetById(@RequestParam("userId")String userId);
/**
* 31
@ -348,6 +372,15 @@ public interface ISysBaseAPI extends CommonAPI {
@Override
@GetMapping("/sys/api/queryUserRoles")
Set<String> queryUserRoles(@RequestParam("username")String username);
/**
* 35
* @param userId
* @return
*/
@Override
@GetMapping("/sys/api/queryUserRolesById")
Set<String> queryUserRolesById(@RequestParam("userId")String userId);
/**
* 36
@ -387,6 +420,15 @@ public interface ISysBaseAPI extends CommonAPI {
@SensitiveDecode
@GetMapping("/sys/api/getUserByName")
LoginUser getUserByName(@RequestParam("username") String username);
/**
* 39ID CommonAPI
* @param username
* @return ID
*/
@Override
@GetMapping("/sys/api/getUserIdByName")
String getUserIdByName(@RequestParam("username") String username);
/**
* 40
@ -609,7 +651,7 @@ public interface ISysBaseAPI extends CommonAPI {
* @param dataLogDto
*/
@PostMapping("/sys/api/saveDataLog")
void saveDataLog(DataLogDTO dataLogDto);
void saveDataLog(@RequestBody DataLogDTO dataLogDto);
/**
*

View File

@ -60,11 +60,21 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public List<String> getRolesByUserId(String userId) {
return null;
}
@Override
public List<String> getDepartIdsByUsername(String username) {
return null;
}
@Override
public List<String> getDepartIdsByUserId(String userId) {
return null;
}
@Override
public Set<String> getDepartParentIdsByUsername(String username) {
return null;
@ -193,6 +203,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public Set<String> getUserRoleSetById(String userId) {
return null;
}
@Override
public Set<String> getUserPermissionSet(String userId) {
return null;
@ -218,6 +233,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public Set<String> queryUserRolesById(String userId) {
return null;
}
@Override
public Set<String> queryUserAuths(String userId) {
return null;
@ -239,6 +259,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null;
}
@Override
public String getUserIdByName(String username) {
return null;
}
@Override
public String translateDictFromTable(String table, String text, String code, String key) {
return null;

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-system-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -81,6 +81,13 @@ public interface ISysBaseAPI extends CommonAPI {
* @return
*/
List<String> getRolesByUsername(String username);
/**
* 7
* @param userId
* @return
*/
List<String> getRolesByUserId(String userId);
/**
* 8
@ -88,6 +95,12 @@ public interface ISysBaseAPI extends CommonAPI {
* @return id
*/
List<String> getDepartIdsByUsername(String username);
/**
* 8
* @param userId
* @return id
*/
List<String> getDepartIdsByUserId(String userId);
/**
* 8.2 ID
@ -299,7 +312,13 @@ public interface ISysBaseAPI extends CommonAPI {
* @return
*/
Set<String> getUserRoleSet(String username);
/**
* 31
* @param useId
* @return
*/
Set<String> getUserRoleSetById(String useId);
/**
* 32
* @param userId

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-module-system</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-module-system</artifactId>
<version>3.6.3</version>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -39,7 +39,7 @@
<artifactId>jimureport-drag</artifactId>
<version>2.0.2</version>
</dependency>
<!-- 积木报表 mongo redis 支持包
<!-- 积木报表 mongo redis 支持包
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-nosql-starter</artifactId>

View File

@ -15,6 +15,10 @@ import org.springframework.context.annotation.Configuration;
* ${spring.datasource.dynamic.datasource.master.url:}
* @author: scott
* @date: 20210218 16:30
*
*
* org/jeecg/interceptor/OnlineRepairCodeGenerateDbConfig.java
* @ConditionalOnMissingClass("org.jeecg.config.init.CodeGenerateDbConfig")
*/
@Slf4j
@Configuration

View File

@ -0,0 +1,102 @@
//package org.jeecg.config.jimureport;
//
//import lombok.extern.slf4j.Slf4j;
//import org.jeecg.common.api.dto.LogDTO;
//import org.jeecg.common.system.api.ISysBaseAPI;
//import org.jeecg.common.system.vo.DictModel;
//import org.jeecg.common.util.oConvertUtils;
//import org.jeecg.modules.base.service.BaseCommonService;
//import org.jeecg.modules.drag.service.IOnlDragExternalService;
//import org.jeecg.modules.drag.vo.DragDictModel;
//import org.jeecg.modules.drag.vo.DragLogDTO;
//import org.springframework.beans.BeanUtils;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Lazy;
//import org.springframework.stereotype.Service;
//
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//
///**
// * @Description: 字典处理
// * @Author: lsq
// * @Date:2023-01-09
// * @Version:V1.0
// */
//@Slf4j
//@Service("onlDragExternalServiceImpl")
//public class JimuDragExternalServiceImpl implements IOnlDragExternalService {
//
// @Autowired
// @Lazy
// private BaseCommonService baseCommonService;
//
// @Autowired
// @Lazy
// private ISysBaseAPI sysBaseApi;
// /**
// * 根据多个字典code查询多个字典项
// * @param codeList
// * @return key = dictCode value=对应的字典项
// */
// @Override
// public Map<String, List<DragDictModel>> getManyDictItems(List<String> codeList) {
// Map<String, List<DragDictModel>> manyDragDictItems = new HashMap<>();
// Map<String, List<DictModel>> dictItemsMap = sysBaseApi.getManyDictItems(codeList);
// dictItemsMap.forEach((k,v)->{
// List<DragDictModel> dictItems = new ArrayList<>();
// v.forEach(dictItem->{
// DragDictModel dictModel = new DragDictModel();
// BeanUtils.copyProperties(dictItem,dictModel);
// dictItems.add(dictModel);
// });
// manyDragDictItems.put(k,dictItems);
// });
// return manyDragDictItems;
// }
//
// /**
// *
// * @param dictCode
// * @return
// */
// @Override
// public List<DragDictModel> getDictItems(String dictCode) {
// List<DragDictModel> dictItems = new ArrayList<>();
// if(oConvertUtils.isNotEmpty(dictCode)){
// List<DictModel> dictItemsList = sysBaseApi.getDictItems(dictCode);
// dictItemsList.forEach(dictItem->{
// DragDictModel dictModel = new DragDictModel();
// BeanUtils.copyProperties(dictItem,dictModel);
// dictItems.add(dictModel);
// });
// }
// return dictItems;
// }
//
// /**
// * 添加日志
// * @param dragLogDTO
// */
// @Override
// public void addLog(DragLogDTO dragLogDTO) {
// if(oConvertUtils.isNotEmpty(dragLogDTO)){
// LogDTO dto = new LogDTO();
// BeanUtils.copyProperties(dragLogDTO,dto);
// baseCommonService.addLog(dto);
// }
// }
//
// /**
// * 保存日志
// * @param logMsg
// * @param logType
// * @param operateType
// */
// @Override
// public void addLog(String logMsg, int logType, int operateType) {
// baseCommonService.addLog(logMsg,logType,operateType);
// }
//}

View File

@ -8,6 +8,7 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.api.dto.LogDTO;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.SysTenantPack;
@ -52,7 +53,7 @@ public class TenantPackUserLogAspect {
for(Object obj: args){
if(obj instanceof SysTenantPack){
// logType=3 租户操作日志
logType = 3;
logType = CommonConstant.LOG_TYPE_3;
SysTenantPack pack = (SysTenantPack)obj;
if(opType==2){
content = "创建了角色权限 "+ pack.getPackName();
@ -60,7 +61,7 @@ public class TenantPackUserLogAspect {
tenantId = pack.getTenantId();
break;
}else if(obj instanceof SysTenantPackUser){
logType = 3;
logType = CommonConstant.LOG_TYPE_3;
SysTenantPackUser packUser = (SysTenantPackUser)obj;
if(opType==2){
content = "将 "+packUser.getRealname()+" 添加到角色 "+ packUser.getPackName();

View File

@ -102,6 +102,17 @@ public class SystemApiController {
}
return loginUser;
}
/**
* ID
* @param username
* @return
*/
@GetMapping("/getUserIdByName")
public String getUserIdByName(@RequestParam("username") String username){
String userId = sysBaseApi.getUserIdByName(username);
return userId;
}
/**
* id
@ -129,6 +140,16 @@ public class SystemApiController {
List<String> getRolesByUsername(@RequestParam("username") String username){
return sysBaseApi.getRolesByUsername(username);
}
/**
*
* @param userId
* @return
*/
@GetMapping("/getRolesByUserId")
List<String> getRolesByUserId(@RequestParam("userId") String userId){
return sysBaseApi.getRolesByUserId(userId);
}
/**
*
@ -139,6 +160,16 @@ public class SystemApiController {
List<String> getDepartIdsByUsername(@RequestParam("username") String username){
return sysBaseApi.getDepartIdsByUsername(username);
}
/**
*
* @param userId
* @return id
*/
@GetMapping("/getDepartIdsByUserId")
List<String> getDepartIdsByUserId(@RequestParam("userId") String userId){
return sysBaseApi.getDepartIdsByUserId(userId);
}
/**
* ID
@ -383,6 +414,16 @@ public class SystemApiController {
public Set<String> getUserRoleSet(@RequestParam("username")String username){
return sysBaseApi.getUserRoleSet(username);
}
/**
*
* @param userId
* @return
*/
@GetMapping("/getUserRoleSetById")
public Set<String> getUserRoleSetById(@RequestParam("userId")String userId){
return sysBaseApi.getUserRoleSetById(userId);
}
/**
*
@ -415,6 +456,16 @@ public class SystemApiController {
public Set<String> queryUserRoles(@RequestParam("username") String username){
return sysUserService.getUserRolesSet(username);
}
/**
*
* @param userId
* @return
*/
@GetMapping("/queryUserRolesById")
public Set<String> queryUserRolesById(@RequestParam("userId") String userId){
return sysUserService.getUserRoleSetById(userId);
}
/**
@ -893,7 +944,7 @@ public class SystemApiController {
* @return
*/
@GetMapping("/getUserAccountsByDepCode")
public List<String> getUserAccountsByDepCode(String orgCode){
public List<String> getUserAccountsByDepCode(@RequestParam("orgCode") String orgCode){
return sysBaseApi.getUserAccountsByDepCode(orgCode);
}

View File

@ -173,7 +173,7 @@ public class SysMessageTemplateController extends JeecgController<SysMessageTemp
sysBaseApi.sendTemplateMessage(md);
return result.success("消息发送成功!");
} catch (Exception e) {
log.error("发送消息出错", e.getMessage());
log.error("发送消息出错" + e.getMessage(), e);
return result.error500("发送消息出错!");
}
}

View File

@ -79,7 +79,6 @@ public enum RangeDateEnum {
//本周
calendar1.set(Calendar.DAY_OF_WEEK, 2);
calendar2.set(Calendar.DAY_OF_WEEK,2);
calendar2.add(Calendar.WEEK_OF_MONTH,1);
calendar2.add(Calendar.DAY_OF_WEEK,-1);
} else if(SZ.key.equals(key)){

View File

@ -31,8 +31,13 @@ public class WebSocket {
* Redis
*/
public static final String REDIS_TOPIC_NAME = "socketHandler";
//避免初次调用出现空指针的情况
private static JeecgRedisClient jeecgRedisClient;
@Autowired
private JeecgRedisClient jeecgRedisClient;
private void setJeecgRedisClient(JeecgRedisClient jeecgRedisClient){
WebSocket.jeecgRedisClient = jeecgRedisClient;
}
//==========【websocket接受、推送消息等方法 —— 具体服务节点推送ws消息】========================================================================================
@ -109,6 +114,9 @@ public class WebSocket {
log.debug("【系统 WebSocket】收到客户端消息:" + message);
}else{
log.debug("【系统 WebSocket】收到客户端消息:" + message);
//update-begin---author:wangshuai---date:2024-05-07---for:【issues/1161】前端websocket因心跳导致监听不起作用---
this.sendMessage(userId, "ping");
//update-end---author:wangshuai---date:2024-05-07---for:【issues/1161】前端websocket因心跳导致监听不起作用---
}
// //------------------------------------------------------------------------------

View File

@ -0,0 +1,38 @@
package org.jeecg.modules.monitor.actuator;
import org.jeecg.modules.monitor.actuator.httptrace.CustomInMemoryHttpTraceRepository;
import org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesProperties;
import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
*
* @Author: chenrui
* @Date: 2024/5/13 17:20
*/
@Configuration
@EnableConfigurationProperties(HttpExchangesProperties.class)
@AutoConfigureBefore(HttpExchangesAutoConfiguration.class)
public class CustomActuatorConfig {
/**
*
* @return
* @author chenrui
* @date 2024/5/14 14:52
*/
@Bean
@ConditionalOnProperty(prefix = "management.trace.http", name = "enabled", matchIfMissing = true)
@ConditionalOnMissingBean(HttpExchangeRepository.class)
public CustomInMemoryHttpTraceRepository traceRepository() {
return new CustomInMemoryHttpTraceRepository();
}
}

View File

@ -0,0 +1,44 @@
package org.jeecg.modules.monitor.actuator.httptrace;
import lombok.Getter;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.web.exchanges.HttpExchange;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.util.List;
import static org.springframework.boot.actuate.endpoint.annotation.Selector.Match.ALL_REMAINING;
/**
* @Description: ENDPOINT: (),responseCode
* @Author: chenrui
* @Date: 2024/5/13 17:02
*/
@Component
@Endpoint(id = "httptrace-new")
public class CustomHttpTraceEndpoint{
private final CustomInMemoryHttpTraceRepository repository;
public CustomHttpTraceEndpoint(CustomInMemoryHttpTraceRepository repository) {
Assert.notNull(repository, "Repository must not be null");
this.repository = repository;
}
@ReadOperation
public HttpTraceDescriptor traces(@Selector(match = ALL_REMAINING) String query) {
return new HttpTraceDescriptor(this.repository.findAll(query));
}
@Getter
public static final class HttpTraceDescriptor {
private final List<HttpExchange> traces;
private HttpTraceDescriptor(List<HttpExchange> traces) {
this.traces = traces;
}
}
}

View File

@ -0,0 +1,94 @@
package org.jeecg.modules.monitor.actuator.httptrace;
import org.springframework.boot.actuate.web.exchanges.HttpExchange;
import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Description:
* @Author: chenrui
* @Date: 2024/5/13 17:02
*/
public class CustomInMemoryHttpTraceRepository extends InMemoryHttpExchangeRepository {
@Override
public List<HttpExchange> findAll() {
return super.findAll();
}
public List<HttpExchange> findAll(String query) {
List<HttpExchange> allTrace = super.findAll();
if (null != allTrace && !allTrace.isEmpty()) {
Stream<HttpExchange> stream = allTrace.stream();
String[] params = query.split(",");
stream = filter(params, stream);
stream = sort(params, stream);
allTrace = stream.collect(Collectors.toList());
}
return allTrace;
}
private Stream<HttpExchange> sort(String[] params, Stream<HttpExchange> stream) {
if (params.length < 2) {
return stream;
}
String sortBy = params[1];
String order;
if (params.length > 2) {
order = params[2];
} else {
order = "desc";
}
return stream.sorted((o1, o2) -> {
int i = 0;
if("timeTaken".equalsIgnoreCase(sortBy)) {
i = o1.getTimeTaken().compareTo(o2.getTimeTaken());
}else if("timestamp".equalsIgnoreCase(sortBy)){
i = o1.getTimestamp().compareTo(o2.getTimestamp());
}
if("desc".equalsIgnoreCase(order)){
i *=-1;
}
return i;
});
}
private static Stream<HttpExchange> filter(String[] params, Stream<HttpExchange> stream) {
if (params.length == 0) {
return stream;
}
String statusQuery = params[0];
if (null != statusQuery && !statusQuery.isEmpty()) {
statusQuery = statusQuery.toLowerCase().trim();
switch (statusQuery) {
case "error":
stream = stream.filter(httpTrace -> {
int status = httpTrace.getResponse().getStatus();
return status >= 404 && status < 501;
});
break;
case "warn":
stream = stream.filter(httpTrace -> {
int status = httpTrace.getResponse().getStatus();
return status >= 201 && status < 404;
});
break;
case "success":
stream = stream.filter(httpTrace -> {
int status = httpTrace.getResponse().getStatus();
return status == 200;
});
break;
case "all":
default:
break;
}
return stream;
}
return stream;
}
}

View File

@ -0,0 +1,52 @@
package org.jeecg.modules.monitor.controller;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.util.HashMap;
import java.util.Map;
/**
* @Description:
* @author: chenrui
*/
@Slf4j
@RestController
@RequestMapping("/sys/actuator/memory")
public class ActuatorMemoryController {
/**
*
* @return
* @throws Exception
*/
@GetMapping("/info")
public Result<?> getRedisInfo() throws Exception {
OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
JSONObject operatingSystemJson = JSONObject.parseObject(JSONObject.toJSONString(operatingSystemMXBean));
long totalPhysicalMemory = operatingSystemJson.getLongValue("totalPhysicalMemorySize");
long freePhysicalMemory = operatingSystemJson.getLongValue("freePhysicalMemorySize");
long usedPhysicalMemory = totalPhysicalMemory - freePhysicalMemory;
Runtime runtime = Runtime.getRuntime();
Map<String,Number> result = new HashMap<>();
result.put("memory.physical.total", totalPhysicalMemory);
result.put("memory.physical.used", freePhysicalMemory);
result.put("memory.physical.free", usedPhysicalMemory);
result.put("memory.physical.usage", NumberUtil.div(usedPhysicalMemory, totalPhysicalMemory));
result.put("memory.runtime.total", runtime.totalMemory());
result.put("memory.runtime.used", runtime.freeMemory());
result.put("memory.runtime.max", runtime.totalMemory() - runtime.freeMemory());
result.put("memory.runtime.free", runtime.maxMemory() - runtime.totalMemory() + runtime.freeMemory());
result.put("memory.runtime.usage", NumberUtil.div(runtime.totalMemory() - runtime.freeMemory(), runtime.totalMemory()));
return Result.ok(result);
}
}

View File

@ -43,6 +43,21 @@ public class ActuatorRedisController {
return Result.ok(infoList);
}
//update-begin---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
/**
* Redis()
* @return
* @throws Exception
* @author chenrui
* @date 2024/5/14 14:56
*/
@GetMapping(value = "/metrics/history")
public Result<?> getMetricsHistory() throws Exception {
Map<String,List<Map<String,Object>>> metricsHistory = this.redisService.getMetricsHistory();
return Result.OK(metricsHistory);
}
//update-end---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
@GetMapping("/keysSize")
public Map<String, Object> getKeysSize() throws Exception {
return redisService.getKeysSize();

View File

@ -44,4 +44,12 @@ public interface RedisService {
* @throws RedisConnectException
*/
Map<String, JSONArray> getMapForReport(String type) throws RedisConnectException ;
/**
*
* @return
* @author chenrui
* @date 2024/5/14 14:57
*/
Map<String, List<Map<String, Object>>> getMetricsHistory();
}

View File

@ -1,10 +1,6 @@
package org.jeecg.modules.monitor.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.*;
import jakarta.annotation.Resource;
@ -12,13 +8,13 @@ import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.monitor.domain.RedisInfo;
import org.jeecg.modules.monitor.exception.RedisConnectException;
import org.jeecg.modules.monitor.service.RedisService;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@ -40,6 +36,11 @@ public class RedisServiceImpl implements RedisService {
*/
private static final String REDIS_MESSAGE = "3";
/**
* redis
*/
private static final Map<String,List<Map<String, Object>>> REDIS_METRICS = new HashMap<>(2);
/**
* Redis
*/
@ -126,4 +127,48 @@ public class RedisServiceImpl implements RedisService {
mapJson.put("data",json);
return mapJson;
}
//update-begin---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
/**
*
* @return
* @author chenrui
* @date 2024/5/14 14:57
*/
@Override
public Map<String, List<Map<String, Object>>> getMetricsHistory() {
return REDIS_METRICS;
}
/**
* redis <br/>
* 60s,,keysize
* @throws RedisConnectException
* @author chenrui
* @date 2024/5/14 14:09
*/
@Scheduled(fixedRate = 60000)
public void recordCustomMetric() throws RedisConnectException {
List<Map<String, Object>> list= new ArrayList<>();
if(REDIS_METRICS.containsKey("dbSize")){
list = REDIS_METRICS.get("dbSize");
}else{
REDIS_METRICS.put("dbSize",list);
}
if(list.size()>60){
list.remove(0);
}
list.add(getKeysSize());
list= new ArrayList<>();
if(REDIS_METRICS.containsKey("memory")){
list = REDIS_METRICS.get("memory");
}else{
REDIS_METRICS.put("memory",list);
}
if(list.size()>60){
list.remove(0);
}
list.add(getMemoryInfo());
}
//update-end---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
}

View File

@ -79,7 +79,7 @@ public class QuartzJobController {
* @param quartzJob
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:add")
@RequestMapping(value = "/add", method = RequestMethod.POST)
public Result<?> add(@RequestBody QuartzJob quartzJob) {
@ -93,7 +93,7 @@ public class QuartzJobController {
* @param quartzJob
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:edit")
@RequestMapping(value = "/edit", method ={RequestMethod.PUT, RequestMethod.POST})
public Result<?> eidt(@RequestBody QuartzJob quartzJob) {
@ -112,7 +112,7 @@ public class QuartzJobController {
* @param id
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:delete")
@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
@ -131,7 +131,7 @@ public class QuartzJobController {
* @param ids
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:deleteBatch")
@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
@ -151,7 +151,7 @@ public class QuartzJobController {
* @param id
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:pause")
@GetMapping(value = "/pause")
@Operation(summary = "停止定时任务")
@ -170,7 +170,7 @@ public class QuartzJobController {
* @param id
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:resume")
@GetMapping(value = "/resume")
@Operation(summary = "启动定时任务")
@ -271,7 +271,7 @@ public class QuartzJobController {
* @param id
* @return
*/
@RequiresRoles("admin")
//@RequiresRoles("admin")
@RequiresPermissions("system:quartzJob:execute")
@GetMapping("/execute")
public Result<?> execute(@RequestParam(name = "id", required = true) String id) {

View File

@ -0,0 +1,69 @@
package org.jeecg.modules.system.cache;
import jakarta.annotation.PostConstruct;
import me.zhyd.oauth.cache.AuthCacheConfig;
import me.zhyd.oauth.cache.AuthStateCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.util.concurrent.TimeUnit;
public class AuthStateRedisCache implements AuthStateCache {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private ValueOperations<String, String> valueOperations;
@PostConstruct
public void init() {
valueOperations = redisTemplate.opsForValue();
}
/**
* 3
*
* @param key key
* @param value
*/
@Override
public void cache(String key, String value) {
valueOperations.set(key, value, AuthCacheConfig.timeout, TimeUnit.MILLISECONDS);
}
/**
*
*
* @param key key
* @param value
* @param timeout
*/
@Override
public void cache(String key, String value, long timeout) {
valueOperations.set(key, value, timeout, TimeUnit.MILLISECONDS);
}
/**
*
*
* @param key key
* @return
*/
@Override
public String get(String key) {
return valueOperations.get(key);
}
/**
* keykeyvaluefalse
*
* @param key key
* @return truekeyvaluefalsekey
*/
@Override
public boolean containsKey(String key) {
return redisTemplate.hasKey(key);
}
}

View File

@ -0,0 +1,15 @@
package org.jeecg.modules.system.config;
import me.zhyd.oauth.cache.AuthStateCache;
import org.jeecg.modules.system.cache.AuthStateRedisCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AuthStateConfiguration {
@Bean
public AuthStateCache authStateCache() {
return new AuthStateRedisCache();
}
}

View File

@ -10,6 +10,7 @@ import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
@ -50,7 +51,7 @@ public class CommonController {
*/
@GetMapping("/403")
public Result<?> noauth() {
return Result.error("没有权限,请联系管理员授权");
return Result.error("没有权限,请联系管理员授权,后刷新缓存!");
}
/**

View File

@ -243,7 +243,7 @@ public class LoginController {
* 访
* @return
*/
@GetMapping("visitInfo")
@GetMapping("/visitInfo")
public Result<List<Map<String,Object>>> visitInfo() {
Result<List<Map<String,Object>>> result = new Result<List<Map<String,Object>>>();
Calendar calendar = new GregorianCalendar();
@ -295,12 +295,14 @@ public class LoginController {
* @return
*/
@PostMapping(value = "/sms")
public Result<String> sms(@RequestBody JSONObject jsonObject) {
public Result<String> sms(@RequestBody JSONObject jsonObject,HttpServletRequest request) {
Result<String> result = new Result<String>();
String clientIp = IpUtils.getIpAddr(request);
String mobile = jsonObject.get("mobile").toString();
//手机号模式 登录模式: "2" 注册模式: "1"
String smsmode=jsonObject.get("smsmode").toString();
log.info(mobile);
log.info("-------- IP:{}, 手机号:{},获取绑定验证码", clientIp, mobile);
if(oConvertUtils.isEmpty(mobile)){
result.setMessage("手机号不允许为空!");
result.setSuccess(false);
@ -318,6 +320,17 @@ public class LoginController {
return result;
}
//-------------------------------------------------------------------------------------
//增加 check防止恶意刷短信接口
if(!DySmsLimit.canSendSms(clientIp)){
log.warn("--------[警告] IP地址:{}, 短信接口请求太多-------", clientIp);
result.setMessage("短信接口请求太多,请稍后再试!");
result.setCode(CommonConstant.PHONE_SMS_FAIL_CODE);
result.setSuccess(false);
return result;
}
//-------------------------------------------------------------------------------------
//随机数
String captcha = RandomUtil.randomNumbers(6);
JSONObject obj = new JSONObject();
@ -734,4 +747,82 @@ public class LoginController {
redisUtil.set(key, ++val, 600);
}
/**
* ()
*
* @param jsonObject
* @return
*/
@PostMapping(value = "/sendChangePwdSms")
public Result<String> sendSms(@RequestBody JSONObject jsonObject) {
Result<String> result = new Result<>();
String mobile = jsonObject.get("mobile").toString();
if (oConvertUtils.isEmpty(mobile)) {
result.setMessage("手机号不允许为空!");
result.setSuccess(false);
return result;
}
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String username = sysUser.getUsername();
LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<>();
query.eq(SysUser::getUsername, username).eq(SysUser::getPhone, mobile);
SysUser user = sysUserService.getOne(query);
if (null == user) {
return Result.error("当前登录用户和绑定的手机号不匹配,无法修改密码!");
}
String redisKey = CommonConstant.PHONE_REDIS_KEY_PRE + mobile;
Object object = redisUtil.get(redisKey);
if (object != null) {
result.setMessage("验证码10分钟内仍然有效");
result.setSuccess(false);
return result;
}
//随机数
String captcha = RandomUtil.randomNumbers(6);
JSONObject obj = new JSONObject();
obj.put("code", captcha);
try {
boolean b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.CHANGE_PASSWORD_TEMPLATE_CODE);
if (!b) {
result.setMessage("短信验证码发送失败,请稍后重试");
result.setSuccess(false);
return result;
}
//验证码5分钟内有效
redisUtil.set(redisKey, captcha, 300);
result.setSuccess(true);
} catch (ClientException e) {
e.printStackTrace();
result.error500(" 短信接口未配置,请联系管理员!");
return result;
}
return result;
}
/**
*
* @param sysLoginModel
* @return
*/
@RequestMapping(value = "/smsCheckCaptcha", method = RequestMethod.POST)
public Result<?> smsCheckCaptcha(@RequestBody SysLoginModel sysLoginModel, HttpServletRequest request){
String captcha = sysLoginModel.getCaptcha();
String checkKey = sysLoginModel.getCheckKey();
if(captcha==null){
return Result.error("验证码无效");
}
String lowerCaseCaptcha = captcha.toLowerCase();
String realKey = Md5Util.md5Encode(lowerCaseCaptcha+checkKey+jeecgBaseConfig.getSignatureSecret(), "utf-8");
Object checkCode = redisUtil.get(realKey);
if(checkCode==null || !checkCode.equals(lowerCaseCaptcha)) {
return Result.error("验证码错误");
}
String clientIp = IpUtils.getIpAddr(request);
//清空短信记录数量
DySmsLimit.clearSendSmsCount(clientIp);
redisUtil.removeAll(realKey);
return Result.ok();
}
}

View File

@ -1,6 +1,5 @@
package org.jeecg.modules.system.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -18,10 +17,7 @@ import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.*;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.message.enums.RangeDateEnum;
import org.jeecg.modules.message.websocket.WebSocket;
@ -40,6 +36,7 @@ import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
@ -51,7 +48,10 @@ import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
@ -86,7 +86,9 @@ public class SysAnnouncementController {
@Autowired
@Lazy
private RedisUtil redisUtil;
@Autowired
public RedisTemplate redisTemplate;
/**
* QQYUN-5072线
*/
@ -332,12 +334,19 @@ public class SysAnnouncementController {
* @return
*/
@RequestMapping(value = "/listByUser", method = RequestMethod.GET)
public Result<Map<String, Object>> listByUser(@RequestParam(required = false, defaultValue = "5") Integer pageSize) {
public Result<Map<String, Object>> listByUser(@RequestParam(required = false, defaultValue = "5") Integer pageSize, HttpServletRequest request) {
long start = System.currentTimeMillis();
Result<Map<String,Object>> result = new Result<Map<String,Object>>();
Map<String,Object> sysMsgMap = new HashMap(5);
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
//update-begin---author:scott ---date:2024-05-11 for【性能优化】优化系统通知只查近2个月的通知---
// 获取上个月的第一天(只查近两个月的通知)
Date lastMonthStartDay = DateRangeUtils.getLastMonthStartDay();
log.info("-----查询近两个月收到的未读通知-----近2月的第一天{}", lastMonthStartDay);
//update-end---author:scott ---date::2024-05-11 for【性能优化】优化系统通知只查近2个月的通知---
// //补推送数据(用户和通知的关系表)
// completeNoteThreadPool.execute(()->{
@ -347,19 +356,19 @@ public class SysAnnouncementController {
// 2.查询用户未读的系统消息
Page<SysAnnouncement> anntMsgList = new Page<SysAnnouncement>(0, pageSize);
//通知公告消息
anntMsgList = sysAnnouncementService.querySysCementPageByUserId(anntMsgList,userId,"1");
anntMsgList = sysAnnouncementService.querySysCementPageByUserId(anntMsgList,userId,"1",null, lastMonthStartDay);
sysMsgMap.put("anntMsgList", anntMsgList.getRecords());
sysMsgMap.put("anntMsgTotal", anntMsgList.getTotal());
log.info("begin 获取用户系统公告 (通知)" + (System.currentTimeMillis() - start) + "毫秒");
log.info("begin 获取用户近2个月的系统公告 (通知)" + (System.currentTimeMillis() - start) + "毫秒");
//系统消息
Page<SysAnnouncement> sysMsgList = new Page<SysAnnouncement>(0, pageSize);
sysMsgList = sysAnnouncementService.querySysCementPageByUserId(sysMsgList,userId,"2");
sysMsgList = sysAnnouncementService.querySysCementPageByUserId(sysMsgList,userId,"2",null, lastMonthStartDay);
sysMsgMap.put("sysMsgList", sysMsgList.getRecords());
sysMsgMap.put("sysMsgTotal", sysMsgList.getTotal());
log.info("end 获取用户系统公告 (系统消息)" + (System.currentTimeMillis() - start) + "毫秒");
log.info("end 获取用户2个月的系统公告 (系统消息)" + (System.currentTimeMillis() - start) + "毫秒");
result.setSuccess(true);
result.setResult(sysMsgMap);
@ -367,6 +376,24 @@ public class SysAnnouncementController {
}
/**
*
*
* @return
*/
@RequestMapping(value = "/getUnreadMessageCount", method = RequestMethod.GET)
public Result<Integer> getUnreadMessageCount(@RequestParam(required = false, defaultValue = "5") Integer pageSize, HttpServletRequest request) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
// 获取上个月的第一天(只查近两个月的通知)
Date lastMonthStartDay = DateRangeUtils.getLastMonthStartDay();
log.info(" ------查询近两个月收到的未读通知消息数量------近2月的第一天{}", lastMonthStartDay);
Integer unreadMessageCount = sysAnnouncementService.getUnreadMessageCountByUserId(userId, lastMonthStartDay);
return Result.ok(unreadMessageCount);
}
/**
* excel
*
@ -570,15 +597,37 @@ public class SysAnnouncementController {
*/
@GetMapping("/getLastAnnountTime")
public Result<Page<SysAnnouncementSend>> getLastAnnountTime(@RequestParam(name = "userId") String userId){
Result<Page<SysAnnouncementSend>> result = new Result<>();
Page<SysAnnouncementSend> page = new Page<>(1,1);
Result<Page<SysAnnouncementSend>> result = new Result<>();
//----------------------------------------------------------------------------------------
// step.1 此接口过慢,可以采用缓存一小时方案
String keyString = String.format(CommonConstant.CACHE_KEY_USER_LAST_ANNOUNT_TIME_1HOUR, userId);
if (redisTemplate.hasKey(keyString)) {
log.info("[SysAnnouncementSend Redis] 通过Redis缓存查询用户最后一次收到系统通知时间userId={}", userId);
Page<SysAnnouncementSend> pageList = (Page<SysAnnouncementSend>) redisTemplate.opsForValue().get(keyString);
result.setSuccess(true);
result.setResult(pageList);
return result;
}
//----------------------------------------------------------------------------------------
Page<SysAnnouncementSend> page = new Page<>(1,1);
LambdaQueryWrapper<SysAnnouncementSend> query = new LambdaQueryWrapper<>();
query.eq(SysAnnouncementSend::getUserId,userId);
query.select(SysAnnouncementSend::getCreateTime);
//只查询上个月和本月,的通知的数据
query.ne(SysAnnouncementSend::getCreateTime, DateRangeUtils.getLastMonthStartDay());
query.select(SysAnnouncementSend::getCreateTime); // 提高查询效率
query.orderByDesc(SysAnnouncementSend::getCreateTime);
Page<SysAnnouncementSend> pageList = sysAnnouncementSendService.page(page, query);
result.setSuccess(true);
result.setResult(pageList);
//----------------------------------------------------------------------------------------
if (pageList != null && pageList.getSize() > 0) {
// step.3 保留1小时redis缓存
redisTemplate.opsForValue().set(keyString, pageList, 3600, TimeUnit.SECONDS);
}
//----------------------------------------------------------------------------------------
result.setSuccess(true);
result.setResult(pageList);
return result;
}

View File

@ -365,7 +365,8 @@ public class SysDepartController {
//}
//});
//step.2 组装导出数据
List<SysDepartExportVo> sysDepartExportVos = sysDepartService.getExportDepart(sysDepart.getTenantId());
Integer tenantId = sysDepart == null ? null : sysDepart.getTenantId();
List<SysDepartExportVo> sysDepartExportVos = sysDepartService.getExportDepart(tenantId);
//导出文件名称
mv.addObject(NormalExcelConstants.FILE_NAME, "部门列表");
mv.addObject(NormalExcelConstants.CLASS, SysDepartExportVo.class);

View File

@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CacheConstant;
@ -18,8 +19,12 @@ import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.system.vo.DictQuery;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.*;
import org.jeecg.common.util.ImportExcelUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.config.shiro.ShiroRealm;
import org.jeecg.modules.system.entity.SysDict;
import org.jeecg.modules.system.entity.SysDictItem;
import org.jeecg.modules.system.model.SysDictTree;
@ -69,6 +74,8 @@ public class SysDictController {
public RedisTemplate<String, Object> redisTemplate;
@Autowired
private RedisUtil redisUtil;
@Autowired
private ShiroRealm shiroRealm;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public Result<IPage<SysDict>> queryPageList(SysDict sysDict,@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@ -326,6 +333,13 @@ public class SysDictController {
@RequestParam(name="condition") String condition,
@RequestParam(value = "sign",required = false) String sign,HttpServletRequest request) {
Result<List<TreeSelectModel>> result = new Result<List<TreeSelectModel>>();
// 【QQYUN-9207】防止参数为空导致报错
if (oConvertUtils.isEmpty(tableName) || oConvertUtils.isEmpty(text) || oConvertUtils.isEmpty(code)) {
result.error500("字典Code格式不正确");
return result;
}
// 1.获取查询条件参数
Map<String, String> query = null;
if(oConvertUtils.isNotEmpty(condition)) {
@ -475,6 +489,14 @@ public class SysDictController {
redisUtil.removeAll("jmreport:cache:dict");
redisUtil.removeAll("jmreport:cache:dictTable");
//update-end-author:liusq date:20230404 for: [issue/4358]springCache中的清除缓存的操作使用了“keys”
//update-begin---author:scott ---date:2024-06-18 for【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
// 清除当前用户的授权缓存信息
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isAuthenticated()) {
shiroRealm.clearCache(currentUser.getPrincipals());
}
//update-end---author:scott ---date::2024-06-18 for【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
return result;
}
@ -496,6 +518,10 @@ public class SysDictController {
QueryWrapper<SysDict> queryWrapper = QueryGenerator.initQueryWrapper(sysDict, request.getParameterMap());
//Step.2 AutoPoi 导出Excel
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
String selections = request.getParameter("selections");
if(!oConvertUtils.isEmpty(selections)){
queryWrapper.in("id",selections.split(","));
}
List<SysDictPage> pageList = new ArrayList<SysDictPage>();
List<SysDict> sysDictList = sysDictService.list(queryWrapper);

View File

@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
@ -15,6 +16,7 @@ import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.Md5Util;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.config.shiro.ShiroRealm;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.constant.DefIndexConst;
import org.jeecg.modules.system.entity.*;
@ -65,6 +67,9 @@ public class SysPermissionController {
@Autowired
private ISysRoleIndexService sysRoleIndexService;
@Autowired
private ShiroRealm shiroRealm;
/**
*
@ -597,6 +602,16 @@ public class SysPermissionController {
//update-end---author:wangshuai ---date:20220316 for[VUEN-234]用户管理角色授权添加敏感日志------------
result.success("保存成功!");
log.info("======角色授权成功=====耗时:" + (System.currentTimeMillis() - start) + "毫秒");
//update-begin---author:scott ---date:2024-06-18 for【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
// 获取当前用户的Subject对象
Subject currentUser = SecurityUtils.getSubject();
// 清除当前用户的授权缓存信息
if (currentUser.isAuthenticated()) {
shiroRealm.clearCache(currentUser.getPrincipals());
}
//update-end---author:scott ---date::2024-06-18 for【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
} catch (Exception e) {
result.error500("授权失败!");
log.error(e.getMessage(), e);

View File

@ -17,9 +17,11 @@ import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.modules.redis.client.JeecgRedisClient;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
@ -83,6 +85,8 @@ public class SysRoleController {
private ISysUserRoleService sysUserRoleService;
@Autowired
private BaseCommonService baseCommonService;
@Autowired
private JeecgRedisClient jeecgRedisClient;
/**
*
@ -124,7 +128,9 @@ public class SysRoleController {
HttpServletRequest req) {
Result<IPage<SysRole>> result = new Result<IPage<SysRole>>();
//此接口必须通过租户来隔离查询
role.setTenantId(oConvertUtils.getInt(!"0".equals(TenantContext.getTenant()) ? TenantContext.getTenant() : "", -1));
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
role.setTenantId(oConvertUtils.getInt(!"0".equals(TenantContext.getTenant()) ? TenantContext.getTenant() : "", -1));
}
QueryWrapper<SysRole> queryWrapper = QueryGenerator.initQueryWrapper(role, req.getParameterMap());
Page<SysRole> page = new Page<SysRole>(pageNo, pageSize);
@ -145,7 +151,9 @@ public class SysRoleController {
Result<SysRole> result = new Result<SysRole>();
try {
//开启多租户隔离,角色id自动生成10位
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
//update-begin---author:wangshuai---date:2024-05-23---for:【TV360X-42】角色新增时设置的编码保存后不一致---
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL && oConvertUtils.isEmpty(role.getRoleCode())){
//update-end---author:wangshuai---date:2024-05-23---for:【TV360X-42】角色新增时设置的编码保存后不一致---
role.setRoleCode(RandomUtil.randomString(10));
}
role.setCreateTime(new Date());
@ -212,7 +220,7 @@ public class SysRoleController {
String username = "admin";
if(getRoleCount == 0 && !username.equals(sysUser.getUsername())){
baseCommonService.addLog("未经授权删除非本租户下的角色ID" + id + ",操作人:" + sysUser.getUsername(), CommonConstant.LOG_TYPE_2, CommonConstant.OPERATE_TYPE_4);
return Result.error("删除角色失败,当前角色不在此租户中。");
return Result.error("删除角色失败,删除角色不属于登录租户!");
}
}
@ -222,6 +230,7 @@ public class SysRoleController {
//update-end---author:wangshuai---date:2024-01-16---for:【QQYUN-7974】禁止删除 admin 角色---
sysRoleService.deleteRole(id);
return Result.ok("删除角色成功");
}
@ -570,4 +579,5 @@ public class SysRoleController {
result.setResult(sysRoleCountPage);
return result;
}
}

View File

@ -1,6 +1,7 @@
package org.jeecg.modules.system.controller;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -351,10 +352,10 @@ public class SysTenantController {
* @param ids
* @return
*/
@DeleteMapping("/deletePackPermissions")
@DeleteMapping("/deleteTenantPack")
@RequiresPermissions("system:tenant:delete:pack")
public Result<String> deletePackPermissions(@RequestParam(value = "ids") String ids) {
sysTenantPackService.deletePackPermissions(ids);
public Result<String> deleteTenantPack(@RequestParam(value = "ids") String ids) {
sysTenantPackService.deleteTenantPack(ids);
return Result.ok("删除租户产品包成功");
}
@ -500,7 +501,7 @@ public class SysTenantController {
Integer tenantId = sysTenantService.joinTenantByHouseNumber(sysTenant, sysUser.getId());
Result<Integer> result = new Result<>();
if(tenantId != 0){
result.setMessage("申请租户成功");
result.setMessage("申请加入组织成功");
result.setSuccess(true);
result.setResult(tenantId);
return result;
@ -935,4 +936,39 @@ public class SysTenantController {
return Result.error("类型不匹配,禁止修改数据");
}
/**
* 使
*
*
*/
@DeleteMapping("/deleteUserByPassword")
public Result<String> deleteUserByPassword(@RequestBody SysUser sysUser,HttpServletRequest request){
Integer tenantId = oConvertUtils.getInteger(TokenUtils.getTenantIdByRequest(request), null);
sysTenantService.deleteUserByPassword(sysUser, tenantId);
return Result.ok("删除用户成功");
}
/**
*
* @return
*/
@RequestMapping(value = "/getCurrentUserTenantForFile", method = RequestMethod.GET)
public Result<Map<String,Object>> getCurrentUserTenantForFile() {
Result<Map<String,Object>> result = new Result<Map<String,Object>>();
try {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
List<SysTenant> tenantList = sysTenantService.getTenantListByUserId(sysUser.getId());
Map<String,Object> map = new HashMap<>(5);
//在开启saas租户隔离的时候并且租户数据不为空则返回租户信息
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL && CollectionUtil.isNotEmpty(tenantList)) {
map.put("list", tenantList);
}
result.setSuccess(true);
result.setResult(map);
}catch(Exception e) {
log.error(e.getMessage(), e);
result.error500("查询失败!");
}
return result;
}
}

View File

@ -7,7 +7,6 @@ import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
@ -17,15 +16,17 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.PermissionData;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.common.modules.redis.client.JeecgRedisClient;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.*;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.model.DepartIdModel;
import org.jeecg.modules.system.model.SysUserSysDepartModel;
@ -101,6 +102,9 @@ public class SysUserController {
@Autowired
private ISysUserTenantService userTenantService;
@Autowired
private JeecgRedisClient jeecgRedisClient;
/**
*
* @param user
@ -1129,7 +1133,7 @@ public class SysUserController {
}
sysUser = this.sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername,username).eq(SysUser::getPhone,phone));
if (sysUser == null) {
result.setMessage("未找到用户");
result.setMessage("当前登录用户和绑定的手机号不匹配,无法修改密码");
result.setSuccess(false);
return result;
} else {
@ -1143,6 +1147,8 @@ public class SysUserController {
//update-end---author:wangshuai ---date:20220316 for[VUEN-234]密码重置添加敏感日志------------
result.setSuccess(true);
result.setMessage("密码重置完成!");
//修改完密码后清空redis
redisUtil.removeAll(redisKey);
return result;
}
}
@ -1551,15 +1557,15 @@ public class SysUserController {
@RequestParam(name = "departId", required = false) String departId,
@RequestParam(name = "roleId", required = false) String roleId,
@RequestParam(name="keyword",required=false) String keyword,
@RequestParam(name="excludeUserIdList",required = false) String excludeUserIdList) {
@RequestParam(name="excludeUserIdList",required = false) String excludeUserIdList,
HttpServletRequest req) {
//------------------------------------------------------------------------------------------------
Integer tenantId = null;
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
String tenantStr = TenantContext.getTenant();
if(oConvertUtils.isNotEmpty(tenantStr)){
tenantId = Integer.parseInt(tenantStr);
}
tenantId = oConvertUtils.getInteger(tenantStr, oConvertUtils.getInt(TokenUtils.getTenantIdByRequest(req), -1));
log.info("---------简流中选择用户接口通过租户筛选租户ID={}", tenantId);
}
//------------------------------------------------------------------------------------------------
IPage<SysUser> pageList = sysUserDepartService.getUserInformation(tenantId, departId,roleId, keyword, pageSize, pageNo,excludeUserIdList);

View File

@ -61,8 +61,13 @@ public class ThirdAppController {
@GetMapping("/getEnabledType")
public Result getEnabledType() {
Map<String, Boolean> enabledMap = new HashMap(5);
//update-begin---author:wangshuai ---date:20230224 for[QQYUN-3440]通过租户模式隔离 ------------
int tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
int tenantId;
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), -1);
} else {
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
}
//查询当前租户下的第三方配置
List<SysThirdAppConfig> list = appConfigService.getThirdConfigListByThirdType(tenantId);
//钉钉是否已配置

View File

@ -74,6 +74,8 @@ public class SysDepart implements Serializable {
private String delFlag;
/**对接企业微信的ID*/
private String qywxIdentifier;
/**对接钉钉的部门ID*/
private String dingIdentifier;
/**创建人*/
private String createBy;
/**创建日期*/

View File

@ -108,6 +108,12 @@ public class SysLog implements Serializable {
*/
@Dict(dicCode = "operate_type")
private Integer operateType;
/**
* pc: app: h5:
*/
@Dict(dicCode = "client_type")
private String clientType;
/**
* ID

View File

@ -26,7 +26,16 @@ public interface SysAnnouncementMapper extends BaseMapper<SysAnnouncement> {
* @param msgCategory
* @return
*/
List<SysAnnouncement> querySysCementListByUserId(Page<SysAnnouncement> page, @Param("userId")String userId,@Param("msgCategory")String msgCategory);
List<SysAnnouncement> querySysCementListByUserId(Page<SysAnnouncement> page, @Param("userId")String userId,@Param("msgCategory")String msgCategory,
@Param("tenantId")Integer tenantId, @Param("beginDate")Date beginDate);
/**
*
*
* @param userId id
* @return
*/
Integer getUnreadMessageCountByUserId(@Param("userId") String userId, @Param("beginDate") Date beginDate);
/**
*

View File

@ -40,6 +40,14 @@ public interface SysDepartMapper extends BaseMapper<SysDepart> {
* @return
*/
public List<SysDepart> queryDepartsByUsername(@Param("username") String username);
/**
*
*
* @param userId
* @return
*/
public List<String> queryDepartsByUserId(@Param("userId") String userId);
/**
* id

View File

@ -20,4 +20,12 @@ public interface SysPackPermissionMapper extends BaseMapper<SysPackPermission> {
* @return
*/
List<String> getPermissionsByPackId(@Param("packId") String packId);
/**
*
*
* @param tenantIdList
*/
void deletePackPermByTenantIds(@Param("tenantIdList") List<Integer> tenantIdList);
}

View File

@ -37,7 +37,16 @@ public interface SysRoleMapper extends BaseMapper<SysRole> {
*/
@InterceptorIgnore(tenantLine = "true")
SysRole getRoleNoTenant(@Param("roleCode") String roleCode);
/**
* idCode
*
* @param userId
* @param tenantId
* @return
*/
List<SysRole> getRoleCodeListByUserId(@Param("userId") String userId, @Param("tenantId") Integer tenantId);
/**
*
* @Author scott

View File

@ -127,4 +127,11 @@ public interface SysTenantMapper extends BaseMapper<SysTenant> {
*/
@Select("select count(1) from sys_tenant where id = #{tenantId} and del_flag = 0")
Long tenantIzExist(@Param("tenantId") Integer tenantId);
/**
* id
* @param userId
* @return
*/
List<SysTenant> getTenantListByUserId(@Param("userId") String userId);
}

View File

@ -14,4 +14,10 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface SysTenantPackMapper extends BaseMapper<SysTenantPack> {
/**
*
*
* @param tenantIdList
*/
void deletePackByTenantIds(@Param("tenantIdList") List<Integer> tenantIdList);
}

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