前端代码和后端代码合并到一个仓库中

This commit is contained in:
JEECG
2024-06-23 10:09:17 +08:00
parent 56da1a23c2
commit bb918b742e
1667 changed files with 41 additions and 26 deletions

15
jeecg-boot/.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
## ide
**/.idea
*.iml
rebel.xml
## backend
**/target
**/logs
## front
**/*.lock
os_del.cmd
os_del_doc.cmd
.svn
derby.log

13
jeecg-boot/db/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM mysql:8.0.19
MAINTAINER jeecgos@163.com
ENV TZ=Asia/Shanghai
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY ./tables_nacos.sql /docker-entrypoint-initdb.d
COPY ./jeecgboot-mysql-5.7.sql /docker-entrypoint-initdb.d
COPY ./tables_xxl_job.sql /docker-entrypoint-initdb.d

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,119 @@
#
# XXL-JOB v2.2.0
# Copyright (c) 2015-present, xuxueli.
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_general_ci;
use `xxl_job`;
SET NAMES utf8mb4;
CREATE TABLE `xxl_job_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_group` int(11) NOT NULL COMMENT '执行器主键ID',
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CRON',
`job_desc` varchar(255) NOT NULL,
`add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`author` varchar(64) DEFAULT NULL COMMENT '作者',
`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
`executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
`executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略',
`executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间单位秒',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',
`glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext COMMENT 'GLUE源代码',
`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',
`glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间',
`child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID多个逗号分隔',
`trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态0-停止1-运行',
`trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间',
`trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`job_group` int(11) NOT NULL COMMENT '执行器主键ID',
`job_id` int(11) NOT NULL COMMENT '任务主键ID',
`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址本次执行的地址',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
`executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数格式如 1/2',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',
`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',
`trigger_code` int(11) NOT NULL COMMENT '调度-结果',
`trigger_msg` text COMMENT '调度-日志',
`handle_time` datetime DEFAULT NULL COMMENT '执行-时间',
`handle_code` int(11) NOT NULL COMMENT '执行-状态',
`handle_msg` text COMMENT '执行-日志',
`alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态0-默认1-无需告警2-告警成功3-告警失败',
PRIMARY KEY (`id`),
KEY `I_trigger_time` (`trigger_time`),
KEY `I_handle_code` (`handle_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_log_report` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`trigger_day` datetime DEFAULT NULL COMMENT '调度-时间',
`running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量',
`suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量',
`fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量',
PRIMARY KEY (`id`),
UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_logglue` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_id` int(11) NOT NULL COMMENT '任务主键ID',
`glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext COMMENT 'GLUE源代码',
`glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注',
`add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_registry` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`registry_group` varchar(50) NOT NULL,
`registry_key` varchar(255) NOT NULL,
`registry_value` varchar(255) NOT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_group` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`app_name` varchar(64) NOT NULL COMMENT '执行器AppName',
`title` varchar(12) NOT NULL COMMENT '执行器名称',
`address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型0=自动注册1=手动录入',
`address_list` varchar(512) DEFAULT NULL COMMENT '执行器地址列表多地址逗号分隔',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '账号',
`password` varchar(50) NOT NULL COMMENT '密码',
`role` tinyint(4) NOT NULL COMMENT '角色0-普通用户1-管理员',
`permission` varchar(255) DEFAULT NULL COMMENT '权限执行器ID列表多个逗号分割',
PRIMARY KEY (`id`),
UNIQUE KEY `i_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `xxl_job_lock` (
`lock_name` varchar(50) NOT NULL COMMENT '锁名称',
PRIMARY KEY (`lock_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`) VALUES (1, 'xxl-job-executor-sample', '示例执行器', 0, NULL);
INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_cron`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '0 0 0 * * ? *', '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', '');
INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL);
INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock');
commit;

View File

@ -0,0 +1,15 @@
# 版本升级方法
> JeecgBoot属于平台级产品每次升级改动较大目前做不到平滑升级。
### 增量升级方案
#### 1.代码合并
本地通过svn或git做好主干在分支上做业务开发jeecg每次版本发布可以手工覆盖主干的代码对比合并代码
#### 2.数据库升级
- 从3.6.2+版本增加flyway自动升级数据库机制支持 mysql5.7、mysql8;
- 其他库请手工执行SQL, 目录: `jeecg-module-system\jeecg-system-start\src\main\resources\flyway\sql\mysql`
> 注意: 升级sql只提供mysql版本如果有权限升级, 还需要手工角色授权,退出重新登录才好使。
#### 3.兼容问题
每次发版,会针对不兼容地方重点说明。

View File

@ -0,0 +1,52 @@
version: '2'
services:
jeecg-boot-mysql:
build:
context: ./db
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: '%'
TZ: Asia/Shanghai
restart: always
container_name: jeecg-boot-mysql
image: jeecg-boot-mysql
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
--max_allowed_packet=128M
--default-authentication-plugin=caching_sha2_password
ports:
- 3306:3306
networks:
- jeecg-boot
jeecg-boot-redis:
image: redis:5.0
ports:
- 6379:6379
restart: always
hostname: jeecg-boot-redis
container_name: jeecg-boot-redis
networks:
- jeecg-boot
jeecg-boot-system:
build:
context: ./jeecg-module-system/jeecg-system-start
restart: on-failure
depends_on:
- jeecg-boot-mysql
- jeecg-boot-redis
container_name: jeecg-boot-system
image: jeecg-boot-system
hostname: jeecg-boot-system
ports:
- 8080:8080
networks:
- jeecg-boot
networks:
jeecg-boot:
name: jeecg_boot

View File

@ -0,0 +1,298 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId>
<version>3.7.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeecg-boot-base-core</artifactId>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jeecg</id>
<name>jeecg Repository</name>
<url>https://maven.jeecg.org/nexus/content/repositories/jeecg</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>jeecg-snapshots</id>
<name>jeecg-snapshots Repository</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<!--jeecg-tools-->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-common</artifactId>
</dependency>
<!--集成springmvc框架并实现自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--springboot2.3+ 需引入validation对应的包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--springboot2.6+解决metrics端点不显示jvm信息的问题-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<!-- commons -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons.version}</version>
</dependency>
<!-- freemarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 动态数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-datasource-spring-boot-starter.version}</version>
</dependency>
<!-- 数据库驱动 -->
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
<scope>runtime</scope>
</dependency>
<!-- sqlserver-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<version>${sqljdbc4.version}</version>
<scope>runtime</scope>
</dependency>
<!-- oracle驱动 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>${ojdbc6.version}</version>
<scope>runtime</scope>
</dependency>
<!-- postgresql驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<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>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${java-jwt.version}</version>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- shiro-redis -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>${shiro-redis.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
<exclusion>
<artifactId>checkstyle</artifactId>
<groupId>com.puppycrawl.tools</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- knife4j -->
<!-- <dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>${knife4j-spring-boot-starter.version}</version>
</dependency>
<!-- 代码生成器 -->
<!-- 如下载失败,请参考此文档 https://help.jeecg.com/java/setup/maven.html -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>codegenerate</artifactId>
<version>${codegenerate.version}</version>
</dependency>
<!-- AutoPoi Excel工具类-->
<dependency>
<groupId>org.jeecgframework</groupId>
<artifactId>autopoi-web</artifactId>
<version>${autopoi-web.version}</version>
<exclusions>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
<exclusion>
<artifactId>xercesImpl</artifactId>
<groupId>xerces</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
<optional>true</optional>
</dependency>
<!-- mini文件存储服务 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
<!-- 阿里云短信 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>${aliyun-java-sdk-dysmsapi.version}</version>
</dependency>
<!-- aliyun oss -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun.oss.version}</version>
</dependency>
<!-- 第三方登录 -->
<dependency>
<groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<!-- 解决okhttp引用了kotlin,应用启动有警告日志问题 -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<!--加载hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
<!-- chatgpt -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-starter-chatgpt</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,147 @@
package org.jeecg.common.api;
import org.jeecg.common.system.vo.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 通用api
* @author: jeecg-boot
*/
public interface CommonAPI {
/**
* 1查询用户角色信息
* @param username
* @return
*/
Set<String> queryUserRoles(String username);
/**
* 1查询用户角色信息
* @param userId
* @return
*/
Set<String> queryUserRolesById(String userId);
/**
* 2查询用户权限信息
* @param userId
* @return
*/
Set<String> queryUserAuths(String userId);
/**
* 3根据 id 查询数据库中存储的 DynamicDataSourceModel
*
* @param dbSourceId
* @return
*/
DynamicDataSourceModel getDynamicDbSourceById(String dbSourceId);
/**
* 4根据 code 查询数据库中存储的 DynamicDataSourceModel
*
* @param dbSourceCode
* @return
*/
DynamicDataSourceModel getDynamicDbSourceByCode(String dbSourceCode);
/**
* 5根据用户账号查询用户信息
* @param username
* @return
*/
public LoginUser getUserByName(String username);
/**
* 5根据用户账号查询用户Id
* @param username
* @return
*/
public String getUserIdByName(String username);
/**
* 6字典表的 翻译
* @param table
* @param text
* @param code
* @param key
* @return
*/
String translateDictFromTable(String table, String text, String code, String key);
/**
* 7普通字典的翻译
* @param code
* @param key
* @return
*/
String translateDict(String code, String key);
/**
* 8查询数据权限
* @param component 组件
* @param username 用户名
* @param requestPath 前段请求地址
* @return
*/
List<SysPermissionDataRuleModel> queryPermissionDataRule(String component, String requestPath, String username);
/**
* 9查询用户信息
* @param username
* @return
*/
SysUserCacheInfo getCacheUser(String username);
/**
* 10获取数据字典
* @param code
* @return
*/
public List<DictModel> queryDictItemsByCode(String code);
/**
* 获取有效的数据字典项
* @param code
* @return
*/
public List<DictModel> queryEnableDictItemsByCode(String code);
/**
* 13获取表数据字典
* @param tableFilterSql
* @param text
* @param code
* @return
*/
List<DictModel> queryTableDictItemsByCode(String tableFilterSql, String text, String code);
/**
* 14 普通字典的翻译根据多个dictCode和多条数据多个以逗号分割
* @param dictCodes 例如user_status,sex
* @param keys 例如1,2,0
* @return
*/
Map<String, List<DictModel>> translateManyDict(String dictCodes, String keys);
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
/**
* 15 字典表的 翻译,可批量
* @param table
* @param text
* @param code
* @param keys 多个用逗号分割
* @param dataSource 数据源
* @return
*/
List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys, String dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
}

View File

@ -0,0 +1,38 @@
package org.jeecg.common.api.dto;
import lombok.Data;
/**
* @Author taoYan
* @Date 2022/7/26 14:44
**/
@Data
public class DataLogDTO {
private String tableName;
private String dataId;
private String content;
private String type;
private String createName;
public DataLogDTO(){
}
public DataLogDTO(String tableName, String dataId, String content, String type) {
this.tableName = tableName;
this.dataId = dataId;
this.content = content;
this.type = type;
}
public DataLogDTO(String tableName, String dataId, String type) {
this.tableName = tableName;
this.dataId = dataId;
this.type = type;
}
}

View File

@ -0,0 +1,31 @@
package org.jeecg.common.api.dto;
import lombok.Data;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
/**
* 文件下载
* cloud api 用到的接口传输对象
* @author: jeecg-boot
*/
@Data
public class FileDownDTO implements Serializable {
private static final long serialVersionUID = 6749126258686446019L;
private String filePath;
private String uploadpath;
private String uploadType;
private HttpServletResponse response;
public FileDownDTO(){}
public FileDownDTO(String filePath, String uploadpath, String uploadType,HttpServletResponse response){
this.filePath = filePath;
this.uploadpath = uploadpath;
this.uploadType = uploadType;
this.response = response;
}
}

View File

@ -0,0 +1,56 @@
package org.jeecg.common.api.dto;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import java.io.Serializable;
/**
* 文件上传
* cloud api 用到的接口传输对象
* @author: jeecg-boot
*/
@Data
public class FileUploadDTO implements Serializable {
private static final long serialVersionUID = -4111953058578954386L;
private MultipartFile file;
private String bizPath;
private String uploadType;
private String customBucket;
public FileUploadDTO(){
}
/**
* 简单上传 构造器1
* @param file
* @param bizPath
* @param uploadType
*/
public FileUploadDTO(MultipartFile file,String bizPath,String uploadType){
this.file = file;
this.bizPath = bizPath;
this.uploadType = uploadType;
}
/**
* 申明桶 文件上传 构造器2
* @param file
* @param bizPath
* @param uploadType
* @param customBucket
*/
public FileUploadDTO(MultipartFile file,String bizPath,String uploadType,String customBucket){
this.file = file;
this.bizPath = bizPath;
this.uploadType = uploadType;
this.customBucket = customBucket;
}
}

View File

@ -0,0 +1,80 @@
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;
/**
* 日志对象
* cloud api 用到的接口传输对象
* @author: jeecg-boot
*/
@Data
public class LogDTO implements Serializable {
private static final long serialVersionUID = 8482720462943906924L;
/**内容*/
private String logContent;
/**日志类型(0:操作日志;1:登录日志;2:定时任务) */
private Integer logType;
/**操作类型(1:添加;2:修改;3:删除;) */
private Integer operateType;
/**登录用户 */
private LoginUser loginUser;
private String id;
private String createBy;
private Date createTime;
private Long costTime;
private String ip;
/**请求参数 */
private String requestParam;
/**请求类型*/
private String requestType;
/**请求路径*/
private String requestUrl;
/**请求方法 */
private String method;
/**操作人用户名称*/
private String username;
/**操作人用户账户*/
private String userid;
/**
* 租户ID
*/
private Integer tenantId;
/**
* 客户终端类型 pc:电脑端 app:手机端 h5:移动网页端
*/
private String clientType;
public LogDTO(){
}
public LogDTO(String logContent, Integer logType, Integer operatetype){
this.logContent = logContent;
this.logType = logType;
this.operateType = operatetype;
}
public LogDTO(String logContent, Integer logType, Integer operatetype, LoginUser loginUser){
this.logContent = logContent;
this.logType = logType;
this.operateType = operatetype;
this.loginUser = loginUser;
}
}

View File

@ -0,0 +1,49 @@
package org.jeecg.common.api.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* online 拦截器权限判断
* cloud api 用到的接口传输对象
* @author: jeecg-boot
*/
@Data
public class OnlineAuthDTO implements Serializable {
private static final long serialVersionUID = 1771827545416418203L;
/**
* 用户名
*/
private String username;
/**
* 可能的请求地址
*/
private List<String> possibleUrl;
/**
* online开发的菜单地址
*/
private String onlineFormUrl;
//update-begin---author:chenrui ---date:20240123 for[QQYUN-7992]【online】工单申请下的online表单未配置online表单开发菜单操作报错无权限------------
/**
* online工单的地址
*/
private String onlineWorkOrderUrl;
//update-end---author:chenrui ---date:20240123 for[QQYUN-7992]【online】工单申请下的online表单未配置online表单开发菜单操作报错无权限------------
public OnlineAuthDTO(){
}
public OnlineAuthDTO(String username, List<String> possibleUrl, String onlineFormUrl){
this.username = username;
this.possibleUrl = possibleUrl;
this.onlineFormUrl = onlineFormUrl;
}
}

View File

@ -0,0 +1,46 @@
package org.jeecg.common.api.dto.message;
import lombok.Data;
import java.io.Serializable;
/**
* 带业务参数的消息
*
* @author: taoyan
* @date: 2022/8/17
*/
@Data
public class BusMessageDTO extends MessageDTO implements Serializable {
private static final long serialVersionUID = 9104793287983367669L;
/**
* 业务类型
*/
private String busType;
/**
* 业务id
*/
private String busId;
public BusMessageDTO(){
}
/**
* 构造 带业务参数的消息
* @param fromUser
* @param toUser
* @param title
* @param msgContent
* @param msgCategory
* @param busType
* @param busId
*/
public BusMessageDTO(String fromUser, String toUser, String title, String msgContent, String msgCategory, String busType, String busId){
super(fromUser, toUser, title, msgContent, msgCategory);
this.busId = busId;
this.busType = busType;
}
}

View File

@ -0,0 +1,46 @@
package org.jeecg.common.api.dto.message;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* 带业务参数的模板消息
* @author: jeecg-boot
*/
@Data
public class BusTemplateMessageDTO extends TemplateMessageDTO implements Serializable {
private static final long serialVersionUID = -4277810906346929459L;
/**
* 业务类型
*/
private String busType;
/**
* 业务id
*/
private String busId;
public BusTemplateMessageDTO(){
}
/**
* 构造 带业务参数的模板消息
* @param fromUser
* @param toUser
* @param title
* @param templateParam
* @param templateCode
* @param busType
* @param busId
*/
public BusTemplateMessageDTO(String fromUser, String toUser, String title, Map<String, String> templateParam, String templateCode, String busType, String busId){
super(fromUser, toUser, title, templateParam, templateCode);
this.busId = busId;
this.busType = busType;
}
}

View File

@ -0,0 +1,122 @@
package org.jeecg.common.api.dto.message;
import lombok.Data;
import org.jeecg.common.constant.CommonConstant;
import java.io.Serializable;
import java.util.*;
/**
* 普通消息
* @author: jeecg-boot
*/
@Data
public class MessageDTO implements Serializable {
private static final long serialVersionUID = -5690444483968058442L;
/**
* 发送人(用户登录账户)
*/
protected String fromUser;
/**
* 发送给(用户登录账户)
*/
protected String toUser;
/**
* 发送给所有人
*/
protected Boolean toAll;
/**
* 消息主题
*/
protected String title;
/**
* 消息内容
*/
protected String content;
/**
* 消息类型 1:消息 2:系统消息
*/
protected String category;
/**
* 消息类型org.jeecg.common.constant.enums.MessageTypeEnum
* XT("system", "系统消息")
* YJ("email", "邮件消息")
* DD("dingtalk", "钉钉消息")
* QYWX("wechat_enterprise", "企业微信")
*/
protected String type;
//---【推送模板相关参数】-------------------------------------------------------------
/**
* 是否发送Markdown格式的消息
*/
protected boolean isMarkdown;
/**
* 模板消息对应的模板编码
*/
protected String templateCode;
/**
* 解析模板内容 对应的数据
*/
protected Map<String, Object> data;
//---【推送模板相关参数】-------------------------------------------------------------
//---【邮件相关参数】-------------------------------------------------------------
/**
* 邮件抄送人
*/
private String copyToUser;
/**
* 邮件推送地址
*/
protected Set<String> toEmailList;
/**
* 邮件抄送地址
*/
protected Set<String> ccEmailList;
//---【邮件相关参数】-------------------------------------------------------------
public MessageDTO(){
}
/**
* 构造器1 系统消息
*/
public MessageDTO(String fromUser,String toUser,String title, String content){
this.fromUser = fromUser;
this.toUser = toUser;
this.title = title;
this.content = content;
//默认 都是2系统消息
this.category = CommonConstant.MSG_CATEGORY_2;
}
/**
* 构造器2 支持设置category 1:消息 2:系统消息
*/
public MessageDTO(String fromUser,String toUser,String title, String content, String category){
this.fromUser = fromUser;
this.toUser = toUser;
this.title = title;
this.content = content;
this.category = category;
}
public boolean isMarkdown() {
return this.isMarkdown;
}
public void setIsMarkdown(boolean isMarkdown) {
this.isMarkdown = isMarkdown;
}
}

View File

@ -0,0 +1,38 @@
package org.jeecg.common.api.dto.message;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* 消息模板dto
* @author: jeecg-boot
*/
@Data
public class TemplateDTO implements Serializable {
private static final long serialVersionUID = 5848247133907528650L;
/**
* 模板编码
*/
protected String templateCode;
/**
* 模板参数
*/
protected Map<String, String> templateParam;
/**
* 构造器 通过设置模板参数和模板编码 作为参数获取消息内容
*/
public TemplateDTO(String templateCode, Map<String, String> templateParam){
this.templateCode = templateCode;
this.templateParam = templateParam;
}
public TemplateDTO(){
}
}

View File

@ -0,0 +1,49 @@
package org.jeecg.common.api.dto.message;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* 模板消息
* @author: jeecg-boot
*/
@Data
public class TemplateMessageDTO extends TemplateDTO implements Serializable {
private static final long serialVersionUID = 411137565170647585L;
/**
* 发送人(用户登录账户)
*/
protected String fromUser;
/**
* 发送给(用户登录账户)
*/
protected String toUser;
/**
* 消息主题
*/
protected String title;
public TemplateMessageDTO(){
}
/**
* 构造器1 发模板消息用
*/
public TemplateMessageDTO(String fromUser, String toUser,String title, Map<String, String> templateParam, String templateCode){
super(templateCode, templateParam);
this.fromUser = fromUser;
this.toUser = toUser;
this.title = title;
}
}

View File

@ -0,0 +1,177 @@
package org.jeecg.common.api.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.jeecg.common.constant.CommonConstant;
import java.io.Serializable;
/**
* 接口返回数据格式
* @author scott
* @email jeecgos@163.com
* @date 2019年1月19日
*/
@Data
@ApiModel(value="接口返回对象", description="接口返回对象")
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 成功标志
*/
@ApiModelProperty(value = "成功标志")
private boolean success = true;
/**
* 返回处理消息
*/
@ApiModelProperty(value = "返回处理消息")
private String message = "";
/**
* 返回代码
*/
@ApiModelProperty(value = "返回代码")
private Integer code = 0;
/**
* 返回数据对象 data
*/
@ApiModelProperty(value = "返回数据对象")
private T result;
/**
* 时间戳
*/
@ApiModelProperty(value = "时间戳")
private long timestamp = System.currentTimeMillis();
public Result() {
}
/**
* 兼容VUE3版token失效不跳转登录页面
* @param code
* @param message
*/
public Result(Integer code, String message) {
this.code = code;
this.message = message;
}
public Result<T> success(String message) {
this.message = message;
this.code = CommonConstant.SC_OK_200;
this.success = true;
return this;
}
public static<T> Result<T> ok() {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
return r;
}
public static<T> Result<T> ok(String msg) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
//Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
r.setResult((T) msg);
r.setMessage(msg);
return r;
}
public static<T> Result<T> ok(T data) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(data);
return r;
}
public static<T> Result<T> OK() {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
return r;
}
/**
* 此方法是为了兼容升级所创建
*
* @param msg
* @param <T>
* @return
*/
public static<T> Result<T> OK(String msg) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage(msg);
//Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
r.setResult((T) msg);
return r;
}
public static<T> Result<T> OK(T data) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(data);
return r;
}
public static<T> Result<T> OK(String msg, T data) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static<T> Result<T> error(String msg, T data) {
Result<T> r = new Result<T>();
r.setSuccess(false);
r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static<T> Result<T> error(String msg) {
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
}
public static<T> Result<T> error(int code, String msg) {
Result<T> r = new Result<T>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}
public Result<T> error500(String message) {
this.message = message;
this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
this.success = false;
return this;
}
/**
* 无权限访问返回结果
*/
public static<T> Result<T> noauth(String msg) {
return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);
}
@JsonIgnore
private String onlTable;
}

View File

@ -0,0 +1,259 @@
package org.jeecg.common.aspect;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
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.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.ModuleType;
import org.jeecg.common.constant.enums.OperateTypeEnum;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.IpUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 系统日志,切面处理类
*
* @Author scott
* @email jeecgos@163.com
* @Date 2018年1月14日
*/
@Aspect
@Component
public class AutoLogAspect {
@Resource
private BaseCommonService baseCommonService;
@Pointcut("@annotation(org.jeecg.common.aspect.annotation.AutoLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveSysLog(point, time, result);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogDTO dto = new LogDTO();
AutoLog syslog = method.getAnnotation(AutoLog.class);
if(syslog != null){
//update-begin-author:taoyan date:
String content = syslog.value();
if(syslog.module()== ModuleType.ONLINE){
content = getOnlineLogContent(obj, content);
}
//注解上的描述,操作日志内容
dto.setLogType(syslog.logType());
dto.setLogContent(content);
}
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
dto.setMethod(className + "." + methodName + "()");
//设置操作类型
if (CommonConstant.LOG_TYPE_2 == dto.getLogType()) {
dto.setOperateType(getOperateType(methodName, syslog.operateType()));
}
//获取request
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
//请求的参数
dto.setRequestParam(getReqestParams(request,joinPoint));
//设置IP地址
dto.setIp(IpUtils.getIpAddr(request));
//获取登录用户信息
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
if(sysUser!=null){
dto.setUserid(sysUser.getUsername());
dto.setUsername(sysUser.getRealname());
}
//耗时
dto.setCostTime(time);
dto.setCreateTime(new Date());
//保存系统日志
baseCommonService.addLog(dto);
}
/**
* 获取操作类型
*/
private int getOperateType(String methodName,int operateType) {
if (operateType > 0) {
return operateType;
}
//update-begin---author:wangshuai ---date:20220331 for阿里云代码扫描规范(不允许任何魔法值出现在代码中)------------
return OperateTypeEnum.getTypeByMethodName(methodName);
//update-end---author:wangshuai ---date:20220331 for阿里云代码扫描规范(不允许任何魔法值出现在代码中)------------
}
/**
* @Description: 获取请求参数
* @author: scott
* @date: 2020/4/16 0:10
* @param request: request
* @param joinPoint: joinPoint
* @Return: java.lang.String
*/
private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) {
String httpMethod = request.getMethod();
String params = "";
if (CommonConstant.HTTP_POST.equals(httpMethod) || CommonConstant.HTTP_PUT.equals(httpMethod) || CommonConstant.HTTP_PATCH.equals(httpMethod)) {
Object[] paramsArray = joinPoint.getArgs();
// java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
// https://my.oschina.net/mengzhang6/blog/2395893
Object[] arguments = new Object[paramsArray.length];
for (int i = 0; i < paramsArray.length; i++) {
if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
//ServletRequest不能序列化从入参里排除否则报异常java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除否则报异常java.lang.IllegalStateException: getOutputStream() has already been called for this response
continue;
}
arguments[i] = paramsArray[i];
}
//update-begin-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
PropertyFilter profilter = new PropertyFilter() {
@Override
public boolean apply(Object o, String name, Object value) {
int length = 500;
if(value!=null && value.toString().length()>length){
return false;
}
if(value instanceof MultipartFile){
return false;
}
return true;
}
};
params = JSONObject.toJSONString(arguments, profilter);
//update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
} else {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 请求的方法参数值
Object[] args = joinPoint.getArgs();
// 请求的方法参数名称
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
if (args != null && paramNames != null) {
for (int i = 0; i < args.length; i++) {
params += " " + paramNames[i] + ": " + args[i];
}
}
}
return params;
}
/**
* online日志内容拼接
* @param obj
* @param content
* @return
*/
private String getOnlineLogContent(Object obj, String content){
if (Result.class.isInstance(obj)){
Result res = (Result)obj;
String msg = res.getMessage();
String tableName = res.getOnlTable();
if(oConvertUtils.isNotEmpty(tableName)){
content+=",表名:"+tableName;
}
if(res.isSuccess()){
content+= ","+(oConvertUtils.isEmpty(msg)?"操作成功":msg);
}else{
content+= ","+(oConvertUtils.isEmpty(msg)?"操作失败":msg);
}
}
return content;
}
/* private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SysLog sysLog = new SysLog();
AutoLog syslog = method.getAnnotation(AutoLog.class);
if(syslog != null){
//update-begin-author:taoyan date:
String content = syslog.value();
if(syslog.module()== ModuleType.ONLINE){
content = getOnlineLogContent(obj, content);
}
//注解上的描述,操作日志内容
sysLog.setLogContent(content);
sysLog.setLogType(syslog.logType());
}
//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
//设置操作类型
if (sysLog.getLogType() == CommonConstant.LOG_TYPE_2) {
sysLog.setOperateType(getOperateType(methodName, syslog.operateType()));
}
//获取request
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
//请求的参数
sysLog.setRequestParam(getReqestParams(request,joinPoint));
//设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
//获取登录用户信息
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
if(sysUser!=null){
sysLog.setUserid(sysUser.getUsername());
sysLog.setUsername(sysUser.getRealname());
}
//耗时
sysLog.setCostTime(time);
sysLog.setCreateTime(new Date());
//保存系统日志
sysLogService.save(sysLog);
}*/
}

View File

@ -0,0 +1,457 @@
package org.jeecg.common.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @Description: 字典aop类
* @Author: dangzhenghui
* @Date: 2019-3-17 21:50
* @Version: 1.0
*/
@Aspect
@Component
@Slf4j
public class DictAspect {
@Lazy
@Autowired
private CommonAPI commonApi;
@Autowired
public RedisTemplate redisTemplate;
@Autowired
private ObjectMapper objectMapper;
private static final String JAVA_UTIL_DATE = "java.util.Date";
/**
* 定义切点Pointcut
*/
@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() {
}
@Around("excudeService()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
long time1=System.currentTimeMillis();
Object result = pjp.proceed();
long time2=System.currentTimeMillis();
log.debug("获取JSON数据 耗时:"+(time2-time1)+"ms");
long start=System.currentTimeMillis();
result=this.parseDictText(result);
long end=System.currentTimeMillis();
log.debug("注入字典到JSON数据 耗时"+(end-start)+"ms");
return result;
}
/**
* 本方法针对返回对象为Result 的IPage的分页列表数据进行动态字典注入
* 字典注入实现 通过对实体类添加注解@dict 来标识需要的字典内容,字典分为单字典code即可 table字典 code table text配合使用与原来jeecg的用法相同
* 示例为SysUser 字段为sex 添加了注解@Dict(dicCode = "sex") 会在字典服务立马查出来对应的text 然后在请求list的时候将这个字典text已字段名称加_dictText形式返回到前端
* 例输入当前返回值的就会多出一个sex_dictText字段
* {
* sex:1,
* sex_dictText:"男"
* }
* 前端直接取值sext_dictText在table里面无需再进行前端的字典转换了
* customRender:function (text) {
* if(text==1){
* return "男";
* }else if(text==2){
* return "女";
* }else{
* return text;
* }
* }
* 目前vue是这么进行字典渲染到table上的多了就很麻烦了 这个直接在服务端渲染完成前端可以直接用
* @param result
*/
private Object parseDictText(Object result) {
//if (result instanceof Result) {
if (true) {
if (((Result) result).getResult() instanceof IPage) {
List<JSONObject> items = new ArrayList<>();
//step.1 筛选出加了 Dict 注解的字段列表
List<Field> dictFieldList = new ArrayList<>();
// 字典数据列表, key = 字典codevalue=数据列表
Map<String, List<String>> dataListMap = new HashMap<>(5);
//取出结果集
List<Object> records=((IPage) ((Result) result).getResult()).getRecords();
//update-begin--Author:zyf -- Date:20220606 ----for【VUEN-1230】 判断是否含有字典注解,没有注解返回-----
Boolean hasDict= checkHasDict(records);
if(!hasDict){
return result;
}
log.debug(" __ 进入字典翻译切面 DictAspect —— " );
//update-end--Author:zyf -- Date:20220606 ----for【VUEN-1230】 判断是否含有字典注解,没有注解返回-----
for (Object record : records) {
String json="{}";
try {
//update-begin--Author:zyf -- Date:20220531 ----for【issues/#3629】 DictAspect Jackson序列化报错-----
//解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
json = objectMapper.writeValueAsString(record);
//update-end--Author:zyf -- Date:20220531 ----for【issues/#3629】 DictAspect Jackson序列化报错-----
} catch (JsonProcessingException e) {
log.error("json解析失败"+e.getMessage(),e);
}
//update-begin--Author:scott -- Date:20211223 ----for【issues/3303】restcontroller返回json数据后key顺序错乱 -----
JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);
//update-end--Author:scott -- Date:20211223 ----for【issues/3303】restcontroller返回json数据后key顺序错乱 -----
//update-begin--Author:scott -- Date:20190603 ----for解决继承实体字段无法翻译问题------
//for (Field field : record.getClass().getDeclaredFields()) {
// 遍历所有字段把字典Code取出来放到 map 里
for (Field field : oConvertUtils.getAllFields(record)) {
String value = item.getString(field.getName());
if (oConvertUtils.isEmpty(value)) {
continue;
}
//update-end--Author:scott -- Date:20190603 ----for解决继承实体字段无法翻译问题------
if (field.getAnnotation(Dict.class) != null) {
if (!dictFieldList.contains(field)) {
dictFieldList.add(field);
}
String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable();
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
String dataSource = field.getAnnotation(Dict.class).ds();
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
List<String> dataList;
String dictCode = code;
if (!StringUtils.isEmpty(table)) {
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
dictCode = String.format("%s,%s,%s,%s", table, text, code, dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
}
dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>());
this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
}
//date类型默认转换string格式化日期
//update-begin--Author:zyf -- Date:20220531 ----for【issues/#3629】 DictAspect Jackson序列化报错-----
//if (JAVA_UTIL_DATE.equals(field.getType().getName())&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){
//SimpleDateFormat aDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));
//}
//update-end--Author:zyf -- Date:20220531 ----for【issues/#3629】 DictAspect Jackson序列化报错-----
}
items.add(item);
}
//step.2 调用翻译方法,一次性翻译
Map<String, List<DictModel>> translText = this.translateAllDict(dataListMap);
//step.3 将翻译结果填充到返回结果里
for (JSONObject record : items) {
for (Field field : dictFieldList) {
String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable();
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
// 自定义的字典表数据源
String dataSource = field.getAnnotation(Dict.class).ds();
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
String fieldDictCode = code;
if (!StringUtils.isEmpty(table)) {
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
fieldDictCode = String.format("%s,%s,%s,%s", table, text, code, dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
}
String value = record.getString(field.getName());
if (oConvertUtils.isNotEmpty(value)) {
List<DictModel> dictModels = translText.get(fieldDictCode);
if(dictModels==null || dictModels.size()==0){
continue;
}
String textValue = this.translDictText(dictModels, value);
log.debug(" 字典Val : " + textValue);
log.debug(" __翻译字典字段__ " + field.getName() + CommonConstant.DICT_TEXT_SUFFIX + " " + textValue);
// TODO-sun 测试输出,待删
log.debug(" ---- dictCode: " + fieldDictCode);
log.debug(" ---- value: " + value);
log.debug(" ----- text: " + textValue);
log.debug(" ---- dictModels: " + JSON.toJSONString(dictModels));
record.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue);
}
}
}
((IPage) ((Result) result).getResult()).setRecords(items);
}
}
return result;
}
/**
* list 去重添加
*/
private void listAddAllDeduplicate(List<String> dataList, List<String> addList) {
// 筛选出dataList中没有的数据
List<String> filterList = addList.stream().filter(i -> !dataList.contains(i)).collect(Collectors.toList());
dataList.addAll(filterList);
}
/**
* 一次性把所有的字典都翻译了
* 1. 所有的普通数据字典的所有数据只执行一次SQL
* 2. 表字典相同的所有数据只执行一次SQL
* @param dataListMap
* @return
*/
private Map<String, List<DictModel>> translateAllDict(Map<String, List<String>> dataListMap) {
// 翻译后的字典文本key=dictCode
Map<String, List<DictModel>> translText = new HashMap<>(5);
// 需要翻译的数据有些可以从redis缓存中获取就不走数据库查询
List<String> needTranslData = new ArrayList<>();
//step.1 先通过redis中获取缓存字典数据
for (String dictCode : dataListMap.keySet()) {
List<String> dataList = dataListMap.get(dictCode);
if (dataList.size() == 0) {
continue;
}
// 表字典需要翻译的数据
List<String> needTranslDataTable = new ArrayList<>();
for (String s : dataList) {
String data = s.trim();
if (data.length() == 0) {
continue; //跳过循环
}
if (dictCode.contains(",")) {
String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, data);
if (redisTemplate.hasKey(keyString)) {
try {
String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
list.add(new DictModel(data, text));
} catch (Exception e) {
log.warn(e.getMessage());
}
} else if (!needTranslDataTable.contains(data)) {
// 去重添加
needTranslDataTable.add(data);
}
} else {
String keyString = String.format("sys:cache:dict::%s:%s", dictCode, data);
if (redisTemplate.hasKey(keyString)) {
try {
String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
list.add(new DictModel(data, text));
} catch (Exception e) {
log.warn(e.getMessage());
}
} else if (!needTranslData.contains(data)) {
// 去重添加
needTranslData.add(data);
}
}
}
//step.2 调用数据库翻译表字典
if (needTranslDataTable.size() > 0) {
String[] arr = dictCode.split(",");
String table = arr[0], text = arr[1], code = arr[2];
String values = String.join(",", needTranslDataTable);
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
// 自定义的数据源
String dataSource = null;
if (arr.length > 3) {
dataSource = arr[3];
}
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
log.debug("translateDictFromTableByKeys.dictCode:" + dictCode);
log.debug("translateDictFromTableByKeys.values:" + values);
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
//update-begin---author:wangshuai---date:2024-01-09---for:微服务下为空报错没有参数需要传递空字符串---
if(null == dataSource){
dataSource = "";
}
//update-end---author:wangshuai---date:2024-01-09---for:微服务下为空报错没有参数需要传递空字符串---
List<DictModel> texts = commonApi.translateDictFromTableByKeys(table, text, code, values, dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
log.debug("translateDictFromTableByKeys.result:" + texts);
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
list.addAll(texts);
// 做 redis 缓存
for (DictModel dict : texts) {
String redisKey = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, dict.getValue());
try {
// update-begin-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061
// 保留5分钟
redisTemplate.opsForValue().set(redisKey, dict.getText(), 300, TimeUnit.SECONDS);
// update-end-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061
} catch (Exception e) {
log.warn(e.getMessage(), e);
}
}
}
}
//step.3 调用数据库进行翻译普通字典
if (needTranslData.size() > 0) {
List<String> dictCodeList = Arrays.asList(dataListMap.keySet().toArray(new String[]{}));
// 将不包含逗号的字典code筛选出来因为带逗号的是表字典而不是普通的数据字典
List<String> filterDictCodes = dictCodeList.stream().filter(key -> !key.contains(",")).collect(Collectors.toList());
String dictCodes = String.join(",", filterDictCodes);
String values = String.join(",", needTranslData);
log.debug("translateManyDict.dictCodes:" + dictCodes);
log.debug("translateManyDict.values:" + values);
Map<String, List<DictModel>> manyDict = commonApi.translateManyDict(dictCodes, values);
log.debug("translateManyDict.result:" + manyDict);
for (String dictCode : manyDict.keySet()) {
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
List<DictModel> newList = manyDict.get(dictCode);
list.addAll(newList);
// 做 redis 缓存
for (DictModel dict : newList) {
String redisKey = String.format("sys:cache:dict::%s:%s", dictCode, dict.getValue());
try {
redisTemplate.opsForValue().set(redisKey, dict.getText());
} catch (Exception e) {
log.warn(e.getMessage(), e);
}
}
}
}
return translText;
}
/**
* 字典值替换文本
*
* @param dictModels
* @param values
* @return
*/
private String translDictText(List<DictModel> dictModels, String values) {
List<String> result = new ArrayList<>();
// 允许多个逗号分隔,允许传数组对象
String[] splitVal = values.split(",");
for (String val : splitVal) {
String dictText = val;
for (DictModel dict : dictModels) {
if (val.equals(dict.getValue())) {
dictText = dict.getText();
break;
}
}
result.add(dictText);
}
return String.join(",", result);
}
/**
* 翻译字典文本
* @param code
* @param text
* @param table
* @param key
* @return
*/
@Deprecated
private String translateDictValue(String code, String text, String table, String key) {
if(oConvertUtils.isEmpty(key)) {
return null;
}
StringBuffer textValue=new StringBuffer();
String[] keys = key.split(",");
for (String k : keys) {
String tmpValue = null;
log.debug(" 字典 key : "+ k);
if (k.trim().length() == 0) {
continue; //跳过循环
}
//update-begin--Author:scott -- Date:20210531 ----for !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
if (!StringUtils.isEmpty(table)){
log.debug("--DictAspect------dicTable="+ table+" ,dicText= "+text+" ,dicCode="+code);
String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s,%s,%s]",table,text,code,k.trim());
if (redisTemplate.hasKey(keyString)){
try {
tmpValue = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
} catch (Exception e) {
log.warn(e.getMessage());
}
}else {
tmpValue= commonApi.translateDictFromTable(table,text,code,k.trim());
}
}else {
String keyString = String.format("sys:cache:dict::%s:%s",code,k.trim());
if (redisTemplate.hasKey(keyString)){
try {
tmpValue = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
} catch (Exception e) {
log.warn(e.getMessage());
}
}else {
tmpValue = commonApi.translateDict(code, k.trim());
}
}
//update-end--Author:scott -- Date:20210531 ----for !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
if (tmpValue != null) {
if (!"".equals(textValue.toString())) {
textValue.append(",");
}
textValue.append(tmpValue);
}
}
return textValue.toString();
}
/**
* 检测返回结果集中是否包含Dict注解
* @param records
* @return
*/
private Boolean checkHasDict(List<Object> records){
if(oConvertUtils.isNotEmpty(records) && records.size()>0){
for (Field field : oConvertUtils.getAllFields(records.get(0))) {
if (oConvertUtils.isNotEmpty(field.getAnnotation(Dict.class))) {
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,139 @@
package org.jeecg.common.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.aspect.annotation.PermissionData;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.system.util.JeecgDataAutorUtils;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
import org.jeecg.common.system.vo.SysUserCacheInfo;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.List;
/**
* 数据权限切面处理类
* 当被请求的方法有注解PermissionData时,会在往当前request中写入数据权限信息
* @Date 2019年4月10日
* @Version: 1.0
* @author: jeecg-boot
*/
@Aspect
@Component
@Slf4j
public class PermissionDataAspect {
@Lazy
@Autowired
private CommonAPI commonApi;
private static final String SPOT_DO = ".do";
@Pointcut("@annotation(org.jeecg.common.aspect.annotation.PermissionData)")
public void pointCut() {
}
@Around("pointCut()")
public Object arround(ProceedingJoinPoint point) throws Throwable{
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
PermissionData pd = method.getAnnotation(PermissionData.class);
String component = pd.pageComponent();
String requestMethod = request.getMethod();
String requestPath = request.getRequestURI().substring(request.getContextPath().length());
requestPath = filterUrl(requestPath);
//update-begin-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效
//先判断是否online报表请求
if(requestPath.indexOf(UrlMatchEnum.CGREPORT_DATA.getMatchUrl())>=0 || requestPath.indexOf(UrlMatchEnum.CGREPORT_ONLY_DATA.getMatchUrl())>=0){
// 获取地址栏参数
String urlParamString = request.getParameter(CommonConstant.ONL_REP_URL_PARAM_STR);
if(oConvertUtils.isNotEmpty(urlParamString)){
requestPath+="?"+urlParamString;
}
}
//update-end-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效
log.debug("拦截请求 >> {} ; 请求类型 >> {} . ", requestPath, requestMethod);
String username = JwtUtil.getUserNameByToken(request);
//查询数据权限信息
//TODO 微服务情况下也得支持缓存机制
List<SysPermissionDataRuleModel> dataRules = commonApi.queryPermissionDataRule(component, requestPath, username);
if(dataRules!=null && dataRules.size()>0) {
//临时存储
JeecgDataAutorUtils.installDataSearchConditon(request, dataRules);
//TODO 微服务情况下也得支持缓存机制
SysUserCacheInfo userinfo = commonApi.getCacheUser(username);
JeecgDataAutorUtils.installUserInfo(request, userinfo);
}
return point.proceed();
}
private String filterUrl(String requestPath){
String url = "";
if(oConvertUtils.isNotEmpty(requestPath)){
url = requestPath.replace("\\", "/");
url = url.replace("//", "/");
if(url.indexOf(SymbolConstant.DOUBLE_SLASH)>=0){
url = filterUrl(url);
}
/*if(url.startsWith("/")){
url=url.substring(1);
}*/
}
return url;
}
/**
* 获取请求地址
* @param request
* @return
*/
@Deprecated
private String getJgAuthRequsetPath(HttpServletRequest request) {
String queryString = request.getQueryString();
String requestPath = request.getRequestURI();
if(oConvertUtils.isNotEmpty(queryString)){
requestPath += "?" + queryString;
}
// 去掉其他参数(保留一个参数) 例如loginController.do?login
if (requestPath.indexOf(SymbolConstant.AND) > -1) {
requestPath = requestPath.substring(0, requestPath.indexOf("&"));
}
if(requestPath.indexOf(QueryRuleEnum.EQ.getValue())!=-1){
if(requestPath.indexOf(SPOT_DO)!=-1){
requestPath = requestPath.substring(0,requestPath.indexOf(".do")+3);
}else{
requestPath = requestPath.substring(0,requestPath.indexOf("?"));
}
}
// 去掉项目路径
requestPath = requestPath.substring(request.getContextPath().length() + 1);
return filterUrl(requestPath);
}
@Deprecated
private boolean moHuContain(List<String> list,String key){
for(String str : list){
if(key.contains(str)){
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,68 @@
package org.jeecg.common.aspect;
/**
* @Author scott
* @Date 2020/1/14 13:36
* @Description: 请求URL与菜单路由URL转换规则方便于采用菜单路由URL来配置数据权限规则
*/
public enum UrlMatchEnum {
/**求URL与菜单路由URL转换规则 /online/cgform/api/getData/ */
CGFORM_DATA("/online/cgform/api/getData/", "/online/cgformList/"),
/**求URL与菜单路由URL转换规则 /online/cgform/api/exportXls/ */
CGFORM_EXCEL_DATA("/online/cgform/api/exportXls/", "/online/cgformList/"),
/**求URL与菜单路由URL转换规则 /online/cgform/api/getTreeData/ */
CGFORM_TREE_DATA("/online/cgform/api/getTreeData/", "/online/cgformList/"),
/**求URL与菜单路由URL转换规则 /online/cgreport/api/getColumnsAndData/ */
CGREPORT_DATA("/online/cgreport/api/getColumnsAndData/", "/online/cgreport/"),
/** 求URL与菜单路由URL转换规则/online/cgreport/api/getData/ 【vue3报表数据请求地址】 */
CGREPORT_ONLY_DATA("/online/cgreport/api/getData/", "/online/cgreport/"),
/**求URL与菜单路由URL转换规则 /online/cgreport/api/exportXls/ */
CGREPORT_EXCEL_DATA("/online/cgreport/api/exportXls/", "/online/cgreport/"),
/**求URL与菜单路由URL转换规则 /online/cgreport/api/exportManySheetXls/ */
CGREPORT_EXCEL_DATA2("/online/cgreport/api/exportManySheetXls/", "/online/cgreport/");
UrlMatchEnum(String url, String matchUrl) {
this.url = url;
this.matchUrl = matchUrl;
}
/**
* Request 请求 URL前缀
*/
private String url;
/**
* 菜单路由 URL前缀 (对应菜单路径)
*/
private String matchUrl;
/**
* 根据req url 获取到菜单配置路径前端页面路由URL
*
* @param url
* @return
*/
public static String getMatchResultByUrl(String url) {
//获取到枚举
UrlMatchEnum[] values = UrlMatchEnum.values();
//加强for循环进行遍历操作
for (UrlMatchEnum lr : values) {
//如果遍历获取的type和参数type一致
if (url.indexOf(lr.url) != -1) {
//返回type对象的desc
return url.replace(lr.url, lr.matchUrl);
}
}
return null;
}
public String getMatchUrl() {
return matchUrl;
}
// public static void main(String[] args) {
// /**
// * 比如request真实请求URL: /online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a
// * 转换匹配路由URL后对应配置的菜单路径:/online/cgformList/81fcf7d8922d45069b0d5ba983612d3a
// */
// System.out.println(UrlMatchEnum.getMatchResultByUrl("/online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a"));
// }
}

View File

@ -0,0 +1,23 @@
package org.jeecg.common.aspect.annotation;
import java.lang.annotation.*;
/**
* 通过此注解声明的接口,自动实现字典翻译
*
* @Author scott
* @email jeecgos@163.com
* @Date 2022年01月05日
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoDict {
/**
* 暂时无用
* @return
*/
String value() default "";
}

View File

@ -0,0 +1,46 @@
package org.jeecg.common.aspect.annotation;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.ModuleType;
import java.lang.annotation.*;
/**
* 系统日志注解
*
* @Author scott
* @email jeecgos@163.com
* @Date 2019年1月14日
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoLog {
/**
* 日志内容
*
* @return
*/
String value() default "";
/**
* 日志类型
*
* @return 0:操作日志;1:登录日志;2:定时任务;
*/
int logType() default CommonConstant.LOG_TYPE_2;
/**
* 操作日志类型
*
* @return 1查询2添加3修改4删除
*/
int operateType() default 0;
/**
* 模块类型 默认为common
* @return
*/
ModuleType module() default ModuleType.COMMON;
}

View File

@ -0,0 +1,54 @@
package org.jeecg.common.aspect.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 字典注解
* @author: dangzhenghui
* @date: 2019年03月17日-下午9:37:16
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
/**
* 方法描述: 数据code
* 作 者: dangzhenghui
* 日 期: 2019年03月17日-下午9:37:16
*
* @return 返回类型: String
*/
String dicCode();
/**
* 方法描述: 数据Text
* 作 者: dangzhenghui
* 日 期: 2019年03月17日-下午9:37:16
*
* @return 返回类型: String
*/
String dicText() default "";
/**
* 方法描述: 数据字典表
* 作 者: dangzhenghui
* 日 期: 2019年03月17日-下午9:37:16
*
* @return 返回类型: String
*/
String dictTable() default "";
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
/**
* 方法描述: 数据字典表所在数据源名称
* 作 者: chenrui
* 日 期: 2023年12月20日-下午4:58
*
* @return 返回类型: String
*/
String ds() default "";
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
}

View File

@ -0,0 +1,20 @@
package org.jeecg.common.aspect.annotation;
import java.lang.annotation.*;
/**
* 动态table切换
*
* @author :zyf
* @date:2020-04-25
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DynamicTable {
/**
* 需要动态解析的表名
* @return
*/
String value();
}

View File

@ -0,0 +1,19 @@
package org.jeecg.common.aspect.annotation;
import java.lang.annotation.*;
/**
* online请求拦截专用注解
* @author: jeecg-boot
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface OnlineAuth {
/**
* 请求关键字在xxx/code之前的字符串
* @return
*/
String value();
}

View File

@ -0,0 +1,29 @@
package org.jeecg.common.aspect.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据权限注解
* @Author taoyan
* @Date 2019年4月11日
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
public @interface PermissionData {
/**
* 暂时没用
* @return
*/
String value() default "";
/**
* 配置菜单的组件路径,用于数据权限
*/
String pageComponent() default "";
}

View File

@ -0,0 +1,620 @@
package org.jeecg.common.constant;
/**
* @Description: 通用常量
* @author: jeecg-boot
*/
public interface CommonConstant {
/**
* 正常状态
*/
Integer STATUS_NORMAL = 0;
/**
* 禁用状态
*/
Integer STATUS_DISABLE = -1;
/**
* 删除标志
*/
Integer DEL_FLAG_1 = 1;
/**
* 未删除
*/
Integer DEL_FLAG_0 = 0;
/**
* 系统日志类型: 登录
*/
int LOG_TYPE_1 = 1;
/**
* 系统日志类型: 操作
*/
int LOG_TYPE_2 = 2;
/**
* 系统日志类型: 租户操作日志
*/
int LOG_TYPE_3 = 3;
/**
* 系统日志类型: 异常
*/
int LOG_TYPE_4 = 4;
/**
* 操作日志类型: 查询
*/
int OPERATE_TYPE_1 = 1;
/**
* 操作日志类型: 添加
*/
int OPERATE_TYPE_2 = 2;
/**
* 操作日志类型: 更新
*/
int OPERATE_TYPE_3 = 3;
/**
* 操作日志类型: 删除
*/
int OPERATE_TYPE_4 = 4;
/**
* 操作日志类型: 倒入
*/
int OPERATE_TYPE_5 = 5;
/**
* 操作日志类型: 导出
*/
int OPERATE_TYPE_6 = 6;
/** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */
Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
/** {@code 404 Not Found} (HTTP/1.0 - RFC 1945) */
Integer SC_INTERNAL_NOT_FOUND_404 = 404;
/** {@code 200 OK} (HTTP/1.0 - RFC 1945) */
Integer SC_OK_200 = 200;
/**访问权限认证未通过 510*/
Integer SC_JEECG_NO_AUTHZ=510;
/** 登录用户Shiro权限缓存KEY前缀 */
public static String PREFIX_USER_SHIRO_CACHE = "shiro:cache:org.jeecg.config.shiro.ShiroRealm.authorizationCache:";
/** 登录用户Token令牌缓存KEY前缀 */
String PREFIX_USER_TOKEN = "prefix_user_token:";
// /** Token缓存时间3600秒即一小时 */
// int TOKEN_EXPIRE_TIME = 3600;
/** 登录二维码 */
String LOGIN_QRCODE_PRE = "QRCODELOGIN:";
String LOGIN_QRCODE = "LQ:";
/** 登录二维码token */
String LOGIN_QRCODE_TOKEN = "LQT:";
/**
* 0一级菜单
*/
Integer MENU_TYPE_0 = 0;
/**
* 1子菜单
*/
Integer MENU_TYPE_1 = 1;
/**
* 2按钮权限
*/
Integer MENU_TYPE_2 = 2;
/**通告对象类型USER:指定用户ALL:全体用户)*/
String MSG_TYPE_UESR = "USER";
String MSG_TYPE_ALL = "ALL";
/**发布状态0未发布1已发布2已撤销*/
String NO_SEND = "0";
String HAS_SEND = "1";
String HAS_CANCLE = "2";
/**阅读状态0未读1已读*/
Integer HAS_READ_FLAG = 1;
Integer NO_READ_FLAG = 0;
/**优先级L低M中H高*/
String PRIORITY_L = "L";
String PRIORITY_M = "M";
String PRIORITY_H = "H";
/**
* 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板
*/
String SMS_TPL_TYPE_0 = "0";
String SMS_TPL_TYPE_1 = "1";
String SMS_TPL_TYPE_2 = "2";
/**
* 状态(0无效1有效)
*/
String STATUS_0 = "0";
String STATUS_1 = "1";
/**
* 同步工作流引擎1同步0不同步
*/
Integer ACT_SYNC_1 = 1;
Integer ACT_SYNC_0 = 0;
/**
* 消息类型1:通知公告2:系统消息
*/
String MSG_CATEGORY_1 = "1";
String MSG_CATEGORY_2 = "2";
/**
* 是否配置菜单的数据权限 1是0否
*/
Integer RULE_FLAG_0 = 0;
Integer RULE_FLAG_1 = 1;
/**
* 是否用户已被冻结 1正常(解冻) 2冻结 3离职
*/
Integer USER_UNFREEZE = 1;
Integer USER_FREEZE = 2;
Integer USER_QUIT = 3;
/**字典翻译文本后缀*/
String DICT_TEXT_SUFFIX = "_dictText";
/**字典翻译颜色后缀*/
String DICT_COLOR_SUFFIX = "_dictColor";
/**
* 表单设计器主表类型
*/
Integer DESIGN_FORM_TYPE_MAIN = 1;
/**
* 表单设计器子表表类型
*/
Integer DESIGN_FORM_TYPE_SUB = 2;
/**
* 表单设计器URL授权通过
*/
Integer DESIGN_FORM_URL_STATUS_PASSED = 1;
/**
* 表单设计器URL授权未通过
*/
Integer DESIGN_FORM_URL_STATUS_NOT_PASSED = 2;
/**
* 表单设计器新增 Flag
*/
String DESIGN_FORM_URL_TYPE_ADD = "add";
/**
* 表单设计器修改 Flag
*/
String DESIGN_FORM_URL_TYPE_EDIT = "edit";
/**
* 表单设计器详情 Flag
*/
String DESIGN_FORM_URL_TYPE_DETAIL = "detail";
/**
* 表单设计器复用数据 Flag
*/
String DESIGN_FORM_URL_TYPE_REUSE = "reuse";
/**
* 表单设计器编辑 Flag (已弃用)
*/
String DESIGN_FORM_URL_TYPE_VIEW = "view";
/**
* online参数值设置Y, 否N
*/
String ONLINE_PARAM_VAL_IS_TURE = "Y";
String ONLINE_PARAM_VAL_IS_FALSE = "N";
/**
* 文件上传类型本地localMiniominio阿里云alioss
*/
String UPLOAD_TYPE_LOCAL = "local";
String UPLOAD_TYPE_MINIO = "minio";
String UPLOAD_TYPE_OSS = "alioss";
/**
* 文档上传自定义桶名称
*/
String UPLOAD_CUSTOM_BUCKET = "eoafile";
/**
* 文档上传自定义路径
*/
String UPLOAD_CUSTOM_PATH = "eoafile";
/**
* 文件外链接有效天数
*/
Integer UPLOAD_EFFECTIVE_DAYS = 1;
/**
* 员工身份 1:普通员工 2:上级)
*/
Integer USER_IDENTITY_1 = 1;
Integer USER_IDENTITY_2 = 2;
/** sys_user 表 username 唯一键索引 */
String SQL_INDEX_UNIQ_SYS_USER_USERNAME = "uniq_sys_user_username";
/** sys_user 表 work_no 唯一键索引 */
String SQL_INDEX_UNIQ_SYS_USER_WORK_NO = "uniq_sys_user_work_no";
/** sys_user 表 phone 唯一键索引 */
String SQL_INDEX_UNIQ_SYS_USER_PHONE = "uniq_sys_user_phone";
/** 达梦数据库升提示。违反表[SYS_USER]唯一性约束 */
String SQL_INDEX_UNIQ_SYS_USER = "唯一性约束";
/** sys_user 表 email 唯一键索引 */
String SQL_INDEX_UNIQ_SYS_USER_EMAIL = "uniq_sys_user_email";
/** sys_quartz_job 表 job_class_name 唯一键索引 */
String SQL_INDEX_UNIQ_JOB_CLASS_NAME = "uniq_job_class_name";
/** sys_position 表 code 唯一键索引 */
String SQL_INDEX_UNIQ_CODE = "uniq_code";
/** sys_role 表 code 唯一键索引 */
String SQL_INDEX_UNIQ_SYS_ROLE_CODE = "uniq_sys_role_role_code";
/** sys_depart 表 code 唯一键索引 */
String SQL_INDEX_UNIQ_DEPART_ORG_CODE = "uniq_depart_org_code";
/** sys_category 表 code 唯一键索引 */
String SQL_INDEX_UNIQ_CATEGORY_CODE = "idx_sc_code";
/**
* 在线聊天 是否为默认分组
*/
String IM_DEFAULT_GROUP = "1";
/**
* 在线聊天 图片文件保存路径
*/
String IM_UPLOAD_CUSTOM_PATH = "biz/user_imgs";
/**
* 在线聊天 用户状态
*/
String IM_STATUS_ONLINE = "online";
/**
* 在线聊天 SOCKET消息类型
*/
String IM_SOCKET_TYPE = "chatMessage";
/**
* 在线聊天 是否开启默认添加好友 1是 0否
*/
String IM_DEFAULT_ADD_FRIEND = "1";
/**
* 在线聊天 用户好友缓存前缀
*/
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不同意
*/
String SIGN_PATCH_BIZ_STATUS_1 = "1";
String SIGN_PATCH_BIZ_STATUS_2 = "2";
/**
* 公文文档上传自定义路径
*/
String UPLOAD_CUSTOM_PATH_OFFICIAL = "officialdoc";
/**
* 公文文档下载自定义路径
*/
String DOWNLOAD_CUSTOM_PATH_OFFICIAL = "officaldown";
/**
* WPS存储值类别(1 code文号 2 textWPS模板还是公文发文模板)
*/
String WPS_TYPE_1="1";
String WPS_TYPE_2="2";
/**===============================================================================================*/
/**
* ::非常重要::
* 注意:这四个常量值如果修改,需要与 jeecg-boot-starter/jeecg-boot-common/org.jeecg.config.FeignConfig 类中的值保持一致。
*/
String X_ACCESS_TOKEN = "X-Access-Token";
String X_SIGN = "X-Sign";
String X_TIMESTAMP = "X-TIMESTAMP";
/** 租户请求头 更名为X-Tenant-Id */
String TENANT_ID = "X-Tenant-Id";
/** 简流接口请求头,用于排除不支持的控件字段 */
String X_MiniFlowExclusionFieldMode = "X-Miniflowexclusionfieldmode";
/**===============================================================================================*/
String TOKEN_IS_INVALID_MSG = "Token失效请重新登录!";
String X_FORWARDED_SCHEME = "X-Forwarded-Scheme";
/**
* 微服务读取配置文件属性 服务地址
*/
String CLOUD_SERVER_KEY = "spring.cloud.nacos.discovery.server-addr";
/**
* 第三方登录 验证密码/创建用户 都需要设置一个操作码 防止被恶意调用
*/
String THIRD_LOGIN_CODE = "third_login_code";
/**
* 第三方APP同步方向本地 --> 第三方APP
*/
String THIRD_SYNC_TO_APP = "SYNC_TO_APP";
/**
* 第三方APP同步方向第三方APP --> 本地
*/
String THIRD_SYNC_TO_LOCAL = "SYNC_TO_LOCAL";
/** 系统通告消息状态0=未发布 */
String ANNOUNCEMENT_SEND_STATUS_0 = "0";
/** 系统通告消息状态1=已发布 */
String ANNOUNCEMENT_SEND_STATUS_1 = "1";
/** 系统通告消息状态2=已撤销 */
String ANNOUNCEMENT_SEND_STATUS_2 = "2";
/**ONLINE 报表权限用 从request中获取地址栏后的参数*/
String ONL_REP_URL_PARAM_STR="onlRepUrlParamStr";
/**POST请求*/
String HTTP_POST = "POST";
/**PUT请求*/
String HTTP_PUT = "PUT";
/**PATCH请求*/
String HTTP_PATCH = "PATCH";
/**未知的*/
String UNKNOWN = "unknown";
/**字符串http*/
String STR_HTTP = "http";
/**String 类型的空值*/
String STRING_NULL = "null";
/**前端vue3版本Header参数名*/
String VERSION="X-Version";
String VERSION_V3 = "v3";
/**存储在线程变量里的动态表名*/
String DYNAMIC_TABLE_NAME="DYNAMIC_TABLE_NAME";
/**
* http:// http协议
*/
String HTTP_PROTOCOL = "http://";
/**
* https:// https协议
*/
String HTTPS_PROTOCOL = "https://";
/** 部门表唯一keyid */
String DEPART_KEY_ID = "id";
/** 部门表唯一keyorgCode */
String DEPART_KEY_ORG_CODE = "orgCode";
/**======【消息推送相关】==============================================================================*/
/**
* 发消息 会传递一些信息到map
*/
String NOTICE_MSG_SUMMARY = "NOTICE_MSG_SUMMARY";
/**
* 发消息 会传递一个业务ID到map
*/
String NOTICE_MSG_BUS_ID = "NOTICE_MSG_BUS_ID";
/**
* 发消息 消息业务类型
*/
String NOTICE_MSG_BUS_TYPE = "NOTICE_MSG_BUS_TYPE";
/**
* 邮箱消息中地址登录时地址后携带的token,需要替换成真实的token值
*/
String LOGIN_TOKEN = "{LOGIN_TOKEN}";
/**
* 模板消息中 跳转地址的对应的key
*/
String MSG_HREF_URL = "url";
/**
* sys_data_log表的类型 用于区别评论区域的日志数据
*/
String DATA_LOG_TYPE_COMMENT = "comment";
/**
* sys_data_log表的类型 老的数据比较 类型都设置为json
*/
String DATA_LOG_TYPE_JSON = "json";
/** 消息模板markdown */
String MSG_TEMPLATE_TYPE_MD = "5";
/**========【消息推送相关】==========================================================================*/
/**
* 短信验证码redis-key的前缀
*/
String PHONE_REDIS_KEY_PRE = "phone_msg";
/**
* 是文件夹
*/
String IT_IS_FOLDER = "1";
/**
* 文件拥有者
*/
String FILE_OWNER = "owner";
/**
* 文件管理员
*/
String FILE_ADMIN = "admin";
/**
* 只允许编辑
*/
String FILE_EDITABLE = "editable";
/**
* 登录失败用于记录失败次数的key
*/
String LOGIN_FAIL = "LOGIN_FAIL_";
/**
* 入职事件
*/
Integer BPM_USER_EVENT_ADD = 1;
/**
* 离职事件
*/
Integer BPM_USER_EVENT_LEVEL = 2;
/**
* 用户租户状态(正常/已通过审核的)
*/
String USER_TENANT_NORMAL = "1";
/**
* 用户租户状态(离职)
*/
String USER_TENANT_QUIT = "2";
/**
* 用户租户状态(审核中)
*/
String USER_TENANT_UNDER_REVIEW = "3";
/**
* 用户租户状态(拒绝)
*/
String USER_TENANT_REFUSE = "4";
/**
* 用户租户状态(邀请)
*/
String USER_TENANT_INVITE = "5";
/**
* 不是叶子节点
*/
Integer NOT_LEAF = 0;
/**
* 是叶子节点
*/
Integer IS_LEAF = 1;
/**
* 钉钉
*/
String DINGTALK = "DINGTALK";
/**
* 企业微信
*/
String WECHAT_ENTERPRISE = "WECHAT_ENTERPRISE";
/**
* 系统默认租户id 0
*/
Integer TENANT_ID_DEFAULT_VALUE = 0;
/**
* 【low-app用】 应用级别的复制
*/
String COPY_LEVEL_APP = "app";
/**
* 【low-app用】 菜单级别的复制
*/
String COPY_LEVEL_MENU = "menu";
/**
* 【low-app用】 应用备份
*/
String COPY_LEVEL_BAK = "backup";
/**
* 【low-app用】 从备份还原
*/
String COPY_LEVEL_COVER = "cover";
/** 【QQYUN-6034】关联字段变更历史值缓存半个小时 */
String CACHE_REL_FIELD_OLD_VAL = "sys:cache:desform:relFieldOldVal:";
/**
* 排序类型:升序
*/
String ORDER_TYPE_ASC = "ASC";
/**
* 排序类型:降序
*/
String ORDER_TYPE_DESC = "DESC";
//update-begin---author:scott ---date:2023-09-10 for积木报表常量----
/**
* 报表允许设计开发的角色
*/
public static String[] allowDevRoles = new String[]{"lowdeveloper", "admin"};
/**
* 【对应积木报表的常量】
* 数据隔离模式: 按照创建人隔离
*/
public static final String SAAS_MODE_CREATED = "created";
/**
* 【对应积木报表的常量】
* 数据隔离模式: 按照租户隔离
*/
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

@ -0,0 +1,48 @@
package org.jeecg.common.constant;
/**
* 系统通告 - 发布状态
* @Author LeeShaoQing
*
*/
public interface CommonSendStatus {
/**
* 未发布
*/
public static final String UNPUBLISHED_STATUS_0 = "0";
/**
* 已发布
*/
public static final String PUBLISHED_STATUS_1 = "1";
/**
* 撤销
*/
public static final String REVOKE_STATUS_2 = "2";
/**
* app端推送会话标识后缀
*/
public static final String APP_SESSION_SUFFIX = "_app";
/**-----【流程相关通知模板code】------------------------------------------------------------*/
/**流程催办——系统通知消息模板*/
public static final String TZMB_BPM_CUIBAN = "bpm_cuiban";
/**流程抄送——系统通知消息模板*/
public static final String TZMB_BPM_CC = "bpm_cc";
/**流程催办——邮件通知消息模板*/
public static final String TZMB_BPM_CUIBAN_EMAIL = "bpm_cuiban_email";
/**标准模板—系统消息通知*/
public static final String TZMB_SYS_TS_NOTE = "sys_ts_note";
/**流程超时提醒——系统通知消息模板*/
public static final String TZMB_BPM_CHAOSHI_TIP = "bpm_chaoshi_tip";
/**-----【流程相关通知模板code】-----------------------------------------------------------*/
/**
* 系统通知拓展参数(比如:用于流程抄送和催办通知,这里额外传递流程跳转页面所需要的路由参数)
*/
public static final String MSG_ABSTRACT_JSON = "msg_abstract";
}

View File

@ -0,0 +1,182 @@
package org.jeecg.common.constant;
/**
* 数据库上下文常量
* @author: jeecg-boot
*/
public interface DataBaseConstant {
//*********数据库类型****************************************
/**MYSQL数据库*/
public static final String DB_TYPE_MYSQL = "MYSQL";
/** ORACLE*/
public static final String DB_TYPE_ORACLE = "ORACLE";
/**达梦数据库*/
public static final String DB_TYPE_DM = "DM";
/**postgreSQL达梦数据库*/
public static final String DB_TYPE_POSTGRESQL = "POSTGRESQL";
/**人大金仓数据库*/
public static final String DB_TYPE_KINGBASEES = "KINGBASEES";
/**sqlserver数据库*/
public static final String DB_TYPE_SQLSERVER = "SQLSERVER";
/**mariadb 数据库*/
public static final String DB_TYPE_MARIADB = "MARIADB";
/**DB2 数据库*/
public static final String DB_TYPE_DB2 = "DB2";
/**HSQL 数据库*/
public static final String DB_TYPE_HSQL = "HSQL";
// // 数据库类型,对应 database_type 字典
// public static final String DB_TYPE_MYSQL_NUM = "1";
// public static final String DB_TYPE_MYSQL7_NUM = "6";
// public static final String DB_TYPE_ORACLE_NUM = "2";
// public static final String DB_TYPE_SQLSERVER_NUM = "3";
// public static final String DB_TYPE_POSTGRESQL_NUM = "4";
// public static final String DB_TYPE_MARIADB_NUM = "5";
//*********系统上下文变量****************************************
/**
* 数据-所属机构编码
*/
public static final String SYS_ORG_CODE = "sysOrgCode";
/**
* 数据-所属机构编码
*/
public static final String SYS_ORG_CODE_TABLE = "sys_org_code";
/**
* 数据-所属机构编码
*/
public static final String SYS_MULTI_ORG_CODE = "sysMultiOrgCode";
/**
* 数据-所属机构编码
*/
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";
/**
* 数据-系统用户编码(对应登录用户账号)
*/
public static final String SYS_USER_CODE = "sysUserCode";
/**
* 数据-系统用户编码(对应登录用户账号)
*/
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";
/**
* 登录用户真实姓名
*/
public static final String SYS_USER_NAME = "sysUserName";
/**
* 登录用户真实姓名
*/
public static final String SYS_USER_NAME_TABLE = "sys_user_name";
/**
* 系统日期"yyyy-MM-dd"
*/
public static final String SYS_DATE = "sysDate";
/**
* 系统日期"yyyy-MM-dd"
*/
public static final String SYS_DATE_TABLE = "sys_date";
/**
* 系统时间"yyyy-MM-dd HH:mm"
*/
public static final String SYS_TIME = "sysTime";
/**
* 系统时间"yyyy-MM-dd HH:mm"
*/
public static final String SYS_TIME_TABLE = "sys_time";
/**
* 数据-所属机构编码
*/
public static final String SYS_BASE_PATH = "sys_base_path";
//*********系统上下文变量****************************************
//*********系统建表标准字段****************************************
/**
* 创建者登录名称
*/
public static final String CREATE_BY_TABLE = "create_by";
/**
* 创建者登录名称
*/
public static final String CREATE_BY = "createBy";
/**
* 创建日期时间
*/
public static final String CREATE_TIME_TABLE = "create_time";
/**
* 创建日期时间
*/
public static final String CREATE_TIME = "createTime";
/**
* 更新用户登录名称
*/
public static final String UPDATE_BY_TABLE = "update_by";
/**
* 更新用户登录名称
*/
public static final String UPDATE_BY = "updateBy";
/**
* 更新日期时间
*/
public static final String UPDATE_TIME = "updateTime";
/**
* 更新日期时间
*/
public static final String UPDATE_TIME_TABLE = "update_time";
/**
* 业务流程状态
*/
public static final String BPM_STATUS = "bpmStatus";
/**
* 业务流程状态
*/
public static final String BPM_STATUS_TABLE = "bpm_status";
//*********系统建表标准字段****************************************
/**
* sql语句 where
*/
String SQL_WHERE = "where";
/**
* sql语句 asc
*/
String SQL_ASC = "asc";
/**
* sqlserver数据库,中间有空格
*/
String DB_TYPE_SQL_SERVER_BLANK = "sql server";
}

View File

@ -0,0 +1,16 @@
package org.jeecg.common.constant;
/**
* 动态切换表配置常量
*
* @author: scott
* @date: 2022年04月25日 22:30
*/
public class DynamicTableConstant {
/**
* 角色首页配置表
* vue2表名: sys_role_index
* vue3表名: sys_role_index_vue3
*/
public static final String SYS_ROLE_INDEX = "sys_role_index";
}

View File

@ -0,0 +1,25 @@
package org.jeecg.common.constant;
/**
* 规则值生成 编码常量类
* @author: taoyan
* @date: 2020年04月02日
*/
public class FillRuleConstant {
/**
* 公文发文编码
*/
public static final String DOC_SEND = "doc_send_code";
/**
* 部门编码
*/
public static final String DEPART = "org_num_role";
/**
* 分类字典编码
*/
public static final String CATEGORY = "category_code_rule";
}

View File

@ -0,0 +1,218 @@
package org.jeecg.common.constant;
import com.alibaba.fastjson.JSONObject;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.Set;
import java.util.List;
/**
* @Description: 省市区
* @author: jeecg-boot
*/
@Component("pca")
public class ProvinceCityArea {
List<Area> areaList;
public String getText(String code){
this.initAreaList();
if(this.areaList!=null || this.areaList.size()>0){
List<String> ls = new ArrayList<String>();
getAreaByCode(code,ls);
return String.join("/",ls);
}
return "";
}
public String getCode(String text){
this.initAreaList();
if(areaList!=null && areaList.size()>0){
for(int i=areaList.size()-1;i>=0;i--){
//update-begin-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
String areaText = areaList.get(i).getText();
String cityText = areaList.get(i).getAheadText();
if(text.indexOf(areaText)>=0 && (cityText!=null && text.indexOf(cityText)>=0)){
return areaList.get(i).getId();
}
//update-end-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
}
}
return null;
}
// update-begin-author:sunjianlei date:20220121 for:【JTC-704】数据导入错误 省市区组件,文件中为北京市,导入后,导为了山西省
/**
* 获取省市区code精准匹配
* @param texts 文本数组,省,市,区
* @return 返回 省市区的code
*/
public String[] getCode(String[] texts) {
if (texts == null || texts.length == 0) {
return null;
}
this.initAreaList();
if (areaList == null || areaList.size() == 0) {
return null;
}
String[] codes = new String[texts.length];
String code = null;
for (int i = 0; i < texts.length; i++) {
String text = texts[i];
Area area;
if (code == null) {
area = getAreaByText(text);
} else {
area = getAreaByPidAndText(code, text);
}
if (area != null) {
code = area.id;
codes[i] = code;
} else {
return null;
}
}
return codes;
}
/**
* 根据text获取area
* @param text
* @return
*/
public Area getAreaByText(String text) {
for (Area area : areaList) {
if (text.equals(area.getText())) {
return area;
}
}
return null;
}
/**
* 通过pid获取 area 对象
* @param pCode 父级编码
* @param text
* @return
*/
public Area getAreaByPidAndText(String pCode, String text) {
this.initAreaList();
if (this.areaList != null && this.areaList.size() > 0) {
for (Area area : this.areaList) {
if (area.getPid().equals(pCode) && area.getText().equals(text)) {
return area;
}
}
}
return null;
}
// update-end-author:sunjianlei date:20220121 for:【JTC-704】数据导入错误 省市区组件,文件中为北京市,导入后,导为了山西省
public void getAreaByCode(String code,List<String> ls){
for(Area area: areaList){
if(area.getId().equals(code)){
String pid = area.getPid();
ls.add(0,area.getText());
getAreaByCode(pid,ls);
}
}
}
private void initAreaList(){
//System.out.println("=====================");
if(this.areaList==null || this.areaList.size()==0){
this.areaList = new ArrayList<Area>();
try {
String jsonData = oConvertUtils.readStatic("classpath:static/pca.json");
JSONObject baseJson = JSONObject.parseObject(jsonData);
//第一层 省
JSONObject provinceJson = baseJson.getJSONObject("86");
for(String provinceKey: provinceJson.keySet()){
//System.out.println("===="+provinceKey);
Area province = new Area(provinceKey,provinceJson.getString(provinceKey),"86");
this.areaList.add(province);
//第二层 市
JSONObject cityJson = baseJson.getJSONObject(provinceKey);
for(String cityKey:cityJson.keySet()){
//System.out.println("-----"+cityKey);
Area city = new Area(cityKey,cityJson.getString(cityKey),provinceKey);
this.areaList.add(city);
//第三层 区
JSONObject areaJson = baseJson.getJSONObject(cityKey);
if(areaJson!=null){
for(String areaKey:areaJson.keySet()){
//System.out.println("········"+areaKey);
Area area = new Area(areaKey,areaJson.getString(areaKey),cityKey);
//update-begin-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
area.setAheadText(cityJson.getString(cityKey));
//update-end-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
this.areaList.add(area);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private String jsonRead(File file){
Scanner scanner = null;
StringBuilder buffer = new StringBuilder();
try {
scanner = new Scanner(file, "utf-8");
while (scanner.hasNextLine()) {
buffer.append(scanner.nextLine());
}
} catch (Exception e) {
} finally {
if (scanner != null) {
scanner.close();
}
}
return buffer.toString();
}
class Area{
String id;
String text;
String pid;
// 用于存储上级文本数据,区的上级文本 是市的数据
String aheadText;
public Area(String id,String text,String pid){
this.id = id;
this.text = text;
this.pid = pid;
}
public String getId() {
return id;
}
public String getText() {
return text;
}
public String getPid() {
return pid;
}
public String getAheadText() {
return aheadText;
}
public void setAheadText(String aheadText) {
this.aheadText = aheadText;
}
}
}

View File

@ -0,0 +1,59 @@
/*
*
* * Copyright (c) 2019-2020, 冷冷 (wangiegie@gmail.com).
* * <p>
* * Licensed under the GNU Lesser General Public License 3.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* * <p>
* * https://www.gnu.org/licenses/lgpl.html
* * <p>
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package org.jeecg.common.constant;
/**
* @author scott
* @date 2019年05月18日
* 服务名称
*/
public interface ServiceNameConstants {
/**
* 微服务名:系统管理模块
*/
String SERVICE_SYSTEM = "jeecg-system";
/**
* 微服务名: demo模块
*/
String SERVICE_DEMO = "jeecg-demo";
/**
* 微服务名joa模块
*/
String SERVICE_JOA = "jeecg-joa";
// /**
// * 微服务名online在线模块
// */
// String SERVICE_ONLINE = "jeecg-online";
// /**
// * 微服务名OA模块
// */
// String SERVICE_EOA = "jeecg-eoa";
// /**
// * 微服务名:表单设计模块
// */
// String SERVICE_FORM = "jeecg-desform";
/**
* gateway通过header传递根路径 basePath
*/
String X_GATEWAY_BASE_PATH = "X_GATEWAY_BASE_PATH";
}

View File

@ -0,0 +1,123 @@
package org.jeecg.common.constant;
/**
* @Description: 符号和特殊符号常用类
* @author: wangshuai
* @date: 2022年03月30日 17:44
*/
public class SymbolConstant {
/**
* 符号:点
*/
public static final String SPOT = ".";
/**
* 符号:双斜杠
*/
public static final String DOUBLE_BACKSLASH = "\\";
/**
* 符号:冒号
*/
public static final String COLON = ":";
/**
* 符号:逗号
*/
public static final String COMMA = ",";
/**
* 符号:左花括号 }
*/
public static final String LEFT_CURLY_BRACKET = "{";
/**
* 符号:右花括号 }
*/
public static final String RIGHT_CURLY_BRACKET = "}";
/**
* 符号:井号 #
*/
public static final String WELL_NUMBER = "#";
/**
* 符号:单斜杠
*/
public static final String SINGLE_SLASH = "/";
/**
* 符号:双斜杠
*/
public static final String DOUBLE_SLASH = "//";
/**
* 符号:感叹号
*/
public static final String EXCLAMATORY_MARK = "!";
/**
* 符号:下划线
*/
public static final String UNDERLINE = "_";
/**
* 符号:单引号
*/
public static final String SINGLE_QUOTATION_MARK = "'";
/**
* 符号:星号
*/
public static final String ASTERISK = "*";
/**
* 符号:百分号
*/
public static final String PERCENT_SIGN = "%";
/**
* 符号:美元 $
*/
public static final String DOLLAR = "$";
/**
* 符号:和 &
*/
public static final String AND = "&";
/**
* 符号:../
*/
public static final String SPOT_SINGLE_SLASH = "../";
/**
* 符号:..\\
*/
public static final String SPOT_DOUBLE_BACKSLASH = "..\\";
/**
* 系统变量前缀 #{
*/
public static final String SYS_VAR_PREFIX = "#{";
/**
* 符号 {{
*/
public static final String DOUBLE_LEFT_CURLY_BRACKET = "{{";
/**
* 符号:[
*/
public static final String SQUARE_BRACKETS_LEFT = "[";
/**
* 符号:]
*/
public static final String SQUARE_BRACKETS_RIGHT = "]";
/**
* 拼接字符串符号 分号 ;
*/
public static final String SEMICOLON = ";";
}

View File

@ -0,0 +1,50 @@
package org.jeecg.common.constant;
/**
* @Description: TenantConstant
* @author: scott
* @date: 2022年08月29日 15:29
*/
public interface TenantConstant {
/*------【低代码应用参数】----------------------------------------------*/
/**
* header的lowAppId标识
*/
String X_LOW_APP_ID = "X-Low-App-ID";
/**
* 应用ID——实体字段
*/
String FIELD_LOW_APP_ID = "lowAppId";
/**
* 应用ID——表字段
*/
String DB_FIELD_LOW_APP_ID = "low_app_id";
/*------【低代码应用参数】---------------------------------------------*/
/*--------【租户参数】-----------------------------------------------*/
/**
* 租户ID实体字段名 和 url参数名
*/
String TENANT_ID = "tenantId";
/**
* 租户ID 数据库字段名
*/
String TENANT_ID_TABLE = "tenant_id";
/*-------【租户参数】-----------------------------------------------*/
/**
* 超级管理员
*/
String SUPER_ADMIN = "superAdmin";
/**
* 组织账户管理员
*/
String ACCOUNT_ADMIN = "accountAdmin";
/**
* 组织应用管理员
*/
String APP_ADMIN = "appAdmin";
}

View File

@ -0,0 +1,31 @@
package org.jeecg.common.constant;
/**
* VXESocket 常量
* @author: jeecg-boot
*/
public class VxeSocketConst {
/**
* 消息类型
*/
public static final String TYPE = "type";
/**
* 消息数据
*/
public static final String DATA = "data";
/**
* 消息类型:心跳检测
*/
public static final String TYPE_HB = "heart_beat";
/**
* 消息类型:通用数据传递
*/
public static final String TYPE_CSD = "common_send_date";
/**
* 消息类型更新vxe table数据
*/
public static final String TYPE_UVT = "update_vxe_table";
}

View File

@ -0,0 +1,66 @@
package org.jeecg.common.constant;
/**
* @Description: Websocket常量类
* @author: taoyan
* @date: 2020年03月23日
*/
public class WebsocketConst {
/**
* 消息json key:cmd
*/
public static final String MSG_CMD = "cmd";
/**
* 消息json key:msgId
*/
public static final String MSG_ID = "msgId";
/**
* 消息json key:msgTxt
*/
public static final String MSG_TXT = "msgTxt";
/**
* 消息json key:userId
*/
public static final String MSG_USER_ID = "userId";
/**
* 消息json key:chat
*/
public static final String MSG_CHAT = "chat";
/**
* 消息类型 heartcheck
*/
public static final String CMD_CHECK = "heartcheck";
/**
* 消息类型 user 用户消息
*/
public static final String CMD_USER = "user";
/**
* 消息类型 topic 系统通知
*/
public static final String CMD_TOPIC = "topic";
/**
* 消息类型 email
*/
public static final String CMD_EMAIL = "email";
/**
* 消息类型 meetingsign 会议签到
*/
public static final String CMD_SIGN = "sign";
/**
* 消息类型 新闻发布/取消
*/
public static final String NEWS_PUBLISH = "publish";
}

View File

@ -0,0 +1,167 @@
package org.jeecg.common.constant.enums;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* online表单枚举 代码生成器用到
* @author: jeecg-boot
*/
public enum CgformEnum {
/**
* 单表
*/
ONE(1, "one", "/jeecg/code-template-online", "default.one", "经典风格", new String[]{"vue3","vue","vue3Native"}),
/**
* 多表
*/
MANY(2, "many", "/jeecg/code-template-online", "default.onetomany", "经典风格" ,new String[]{"vue"}),
/**
* 多表jvxe风格
* */
JVXE_TABLE(2, "jvxe", "/jeecg/code-template-online", "jvxe.onetomany", "默认风格" ,new String[]{"vue3","vue","vue3Native"}),
/**
* 多表 (erp风格)
*/
ERP(2, "erp", "/jeecg/code-template-online", "erp.onetomany", "ERP风格" ,new String[]{"vue3","vue","vue3Native"}),
/**
* 多表(内嵌子表风格)
*/
INNER_TABLE(2, "innerTable", "/jeecg/code-template-online", "inner-table.onetomany", "内嵌子表风格" ,new String[]{"vue3","vue"}),
/**
* 多表tab风格
* */
TAB(2, "tab", "/jeecg/code-template-online", "tab.onetomany", "Tab风格" ,new String[]{"vue3","vue"}),
/**
* 树形列表
*/
TREE(3, "tree", "/jeecg/code-template-online", "default.tree", "树形列表" ,new String[]{"vue3","vue","vue3Native"});
/**
* 类型 1/单表 2/一对多 3/树
*/
int type;
/**
* 编码标识
*/
String code;
/**
* 代码生成器模板路径
*/
String templatePath;
/**
* 代码生成器模板路径
*/
String stylePath;
/**
* 模板风格名称
*/
String note;
/**
* 支持代码风格 vue3:vue3包装代码 vue3Native:vue3原生代码 vue:vue2代码
*/
String[] vueStyle;
/**
* 构造器
*
* @param type 类型 1/单表 2/一对多 3/树
* @param code 模板编码
* @param templatePath 模板路径
* @param stylePath 模板子路径
* @param note
* @param vueStyle 支持代码风格
*/
CgformEnum(int type, String code, String templatePath, String stylePath, String note, String[] vueStyle) {
this.type = type;
this.code = code;
this.templatePath = templatePath;
this.stylePath = stylePath;
this.note = note;
this.vueStyle = vueStyle;
}
/**
* 根据code获取模板路径
*
* @param code
* @return
*/
public static String getTemplatePathByConfig(String code) {
return getCgformEnumByConfig(code).templatePath;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getTemplatePath() {
return templatePath;
}
public void setTemplatePath(String templatePath) {
this.templatePath = templatePath;
}
public String getStylePath() {
return stylePath;
}
public void setStylePath(String stylePath) {
this.stylePath = stylePath;
}
public String[] getVueStyle() {
return vueStyle;
}
public void setVueStyle(String[] vueStyle) {
this.vueStyle = vueStyle;
}
/**
* 根据code找枚举
*
* @param code
* @return
*/
public static CgformEnum getCgformEnumByConfig(String code) {
for (CgformEnum e : CgformEnum.values()) {
if (e.code.equals(code)) {
return e;
}
}
return null;
}
/**
* 根据类型找所有
*
* @param type
* @return
*/
public static List<Map<String, Object>> getJspModelList(int type) {
List<Map<String, Object>> ls = new ArrayList<Map<String, Object>>();
for (CgformEnum e : CgformEnum.values()) {
if (e.type == type) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", e.code);
map.put("note", e.note);
ls.add(map);
}
}
return ls;
}
}

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

@ -0,0 +1,83 @@
package org.jeecg.common.constant.enums;
import org.apache.commons.lang3.StringUtils;
/**
* @Description: 短信枚举类
* @author: jeecg-boot
*/
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"),
/**会议通知*/
MEET_NOTICE_TEMPLATE_CODE("SMS_201480469","JEECG","username,title,minute,time"),
/**我的计划通知*/
PLAN_NOTICE_TEMPLATE_CODE("SMS_201470515","JEECG","username,title,time"),
/**支付成功短信通知*/
PAY_SUCCESS_NOTICE_CODE("SMS_461735163","敲敲云","realname,money,endTime"),
/**会员到期通知提醒*/
VIP_EXPIRE_NOTICE_CODE("SMS_461885023","敲敲云","realname,endTime");
/**
* 短信模板编码
*/
private String templateCode;
/**
* 签名
*/
private String signName;
/**
* 短信模板必需的数据名称多个key以逗号分隔此处配置作为校验
*/
private String keys;
private DySmsEnum(String templateCode,String signName,String keys) {
this.templateCode = templateCode;
this.signName = signName;
this.keys = keys;
}
public String getTemplateCode() {
return templateCode;
}
public void setTemplateCode(String templateCode) {
this.templateCode = templateCode;
}
public String getSignName() {
return signName;
}
public void setSignName(String signName) {
this.signName = signName;
}
public String getKeys() {
return keys;
}
public void setKeys(String keys) {
this.keys = keys;
}
public static DySmsEnum toEnum(String templateCode) {
if(StringUtils.isEmpty(templateCode)){
return null;
}
for(DySmsEnum item : DySmsEnum.values()) {
if(item.getTemplateCode().equals(templateCode)) {
return item;
}
}
return null;
}
}

View File

@ -0,0 +1,66 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
* 邮件html模板配置地址美剧
*
* @author: liusq
* @Date: 2023-10-13
*/
public enum EmailTemplateEnum {
/**
* 流程催办
*/
BPM_CUIBAN_EMAIL("bpm_cuiban_email", "/templates/email/bpm_cuiban_email.ftl"),
/**
* 流程新任务
*/
BPM_NEW_TASK_EMAIL("bpm_new_task_email", "/templates/email/bpm_new_task_email.ftl"),
/**
* 表单新增记录
*/
DESFORM_NEW_DATA_EMAIL("desform_new_data_email", "/templates/email/desform_new_data_email.ftl");
/**
* 模板名称
*/
private String name;
/**
* 模板地址
*/
private String url;
EmailTemplateEnum(String name, String url) {
this.name = name;
this.url = url;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public static EmailTemplateEnum getByName(String name) {
if (oConvertUtils.isEmpty(name)) {
return null;
}
for (EmailTemplateEnum val : values()) {
if (val.getName().equals(name)) {
return val;
}
}
return null;
}
}

View File

@ -0,0 +1,76 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
* 文件类型
*/
public enum FileTypeEnum {
// 文档类型folder:文件夹 excel:excel doc:word pp:ppt image:图片 archive:其他文档 video:视频 voice:语音)
// FOLDER
xls(".xls","excel","excel"),
xlsx(".xlsx","excel","excel"),
doc(".doc","doc","word"),
docx(".docx","doc","word"),
ppt(".ppt","pp","ppt"),
pptx(".pptx","pp","ppt"),
gif(".gif","image","图片"),
jpg(".jpg","image","图片"),
jpeg(".jpeg","image","图片"),
png(".png","image","图片"),
txt(".txt","text","文本"),
avi(".avi","video","视频"),
mov(".mov","video","视频"),
rmvb(".rmvb","video","视频"),
rm(".rm","video","视频"),
flv(".flv","video","视频"),
mp4(".mp4","video","视频"),
zip(".zip","zip","压缩包"),
pdf(".pdf","pdf","pdf"),
mp3(".mp3","mp3","语音");
private String type;
private String value;
private String text;
private FileTypeEnum(String type,String value,String text){
this.type = type;
this.value = value;
this.text = text;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public static FileTypeEnum getByType(String type){
if (oConvertUtils.isEmpty(type)) {
return null;
}
for (FileTypeEnum val : values()) {
if (val.getType().equals(type)) {
return val;
}
}
return null;
}
}

View File

@ -0,0 +1,88 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.system.annotation.EnumDict;
import org.jeecg.common.system.vo.DictModel;
import java.util.ArrayList;
import java.util.List;
/**
* 消息类型
* @author: jeecg-boot
*/
@EnumDict("messageType")
public enum MessageTypeEnum {
/** 系统消息 */
XT("system", "系统消息"),
/** 邮件消息 */
YJ("email", "邮件消息"),
/** 钉钉消息 */
DD("dingtalk", "钉钉消息"),
/** 企业微信 */
QYWX("wechat_enterprise", "企业微信");
MessageTypeEnum(String type, String note){
this.type = type;
this.note = note;
}
/**
* 消息类型
*/
String type;
/**
* 类型说明
*/
String note;
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
/**
* 获取字典数据
* @return
*/
public static List<DictModel> getDictList(){
List<DictModel> list = new ArrayList<>();
DictModel dictModel = null;
for(MessageTypeEnum e: MessageTypeEnum.values()){
dictModel = new DictModel();
dictModel.setValue(e.getType());
dictModel.setText(e.getNote());
list.add(dictModel);
}
return list;
}
/**
* 根据type获取枚举
*
* @param type
* @return
*/
public static MessageTypeEnum valueOfType(String type) {
for (MessageTypeEnum e : MessageTypeEnum.values()) {
if (e.getType().equals(type)) {
return e;
}
}
return null;
}
}

View File

@ -0,0 +1,18 @@
package org.jeecg.common.constant.enums;
/**
* 日志按模块分类
* @author: jeecg-boot
*/
public enum ModuleType {
/**
* 普通
*/
COMMON,
/**
* online
*/
ONLINE;
}

View File

@ -0,0 +1,95 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.constant.CommonConstant;
/**
* @Description: 操作类型
* @author: jeecg-boot
* @date: 2022/3/31 10:05
*/
public enum OperateTypeEnum {
/**
* 列表
*/
LIST(CommonConstant.OPERATE_TYPE_1, "list"),
/**
* 新增
*/
ADD(CommonConstant.OPERATE_TYPE_2, "add"),
/**
* 编辑
*/
EDIT(CommonConstant.OPERATE_TYPE_3, "edit"),
/**
* 删除
*/
DELETE(CommonConstant.OPERATE_TYPE_4, "delete"),
/**
* 导入
*/
IMPORT(CommonConstant.OPERATE_TYPE_5, "import"),
/**
* 导出
*/
EXPORT(CommonConstant.OPERATE_TYPE_6, "export");
/**
* 类型 1列表,2新增,3编辑,4删除,5导入,6导出
*/
int type;
/**
* 编码(请求方式)
*/
String code;
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
/**
* 构造器
*
* @param type 类型
* @param code 编码(请求方式)
*/
OperateTypeEnum(int type, String code) {
this.type = type;
this.code = code;
}
/**
* 根据请求名称匹配
*
* @param methodName 请求名称
* @return Integer 类型
*/
public static Integer getTypeByMethodName(String methodName) {
for (OperateTypeEnum e : OperateTypeEnum.values()) {
if (methodName.startsWith(e.getCode())) {
return e.getType();
}
}
return CommonConstant.OPERATE_TYPE_1;
}
}

View File

@ -0,0 +1,97 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
import java.util.List;
/**
* 首页自定义
* 通过角色编码与首页组件路径配置
* 枚举的顺序有权限高低权重作用(也就是配置多个角色,在前面的角色首页,会优先生效)
* @author: jeecg-boot
*/
public enum RoleIndexConfigEnum {
/**首页自定义 admin*/
// ADMIN("admin", "dashboard/Analysis"),
//TEST("test", "dashboard/IndexChart"),
/**首页自定义 hr*/
// HR("hr", "dashboard/IndexBdc");
//DM("dm", "dashboard/IndexTask"),
// 注:此值仅为防止报错,无任何实际意义
ROLE_INDEX_CONFIG_ENUM("RoleIndexConfigEnumDefault", "dashboard/Analysis");
/**
* 角色编码
*/
String roleCode;
/**
* 路由index
*/
String componentUrl;
/**
* 构造器
*
* @param roleCode 角色编码
* @param componentUrl 首页组件路径(规则跟菜单配置一样)
*/
RoleIndexConfigEnum(String roleCode, String componentUrl) {
this.roleCode = roleCode;
this.componentUrl = componentUrl;
}
/**
* 根据code找枚举
* @param roleCode 角色编码
* @return
*/
private static RoleIndexConfigEnum getEnumByCode(String roleCode) {
for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
if (e.roleCode.equals(roleCode)) {
return e;
}
}
return null;
}
/**
* 根据code找index
* @param roleCode 角色编码
* @return
*/
private static String getIndexByCode(String roleCode) {
for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
if (e.roleCode.equals(roleCode)) {
return e.componentUrl;
}
}
return null;
}
public static String getIndexByRoles(List<String> roles) {
String[] rolesArray = roles.toArray(new String[roles.size()]);
for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
if (oConvertUtils.isIn(e.roleCode,rolesArray)){
return e.componentUrl;
}
}
return null;
}
public String getRoleCode() {
return roleCode;
}
public void setRoleCode(String roleCode) {
this.roleCode = roleCode;
}
public String getComponentUrl() {
return componentUrl;
}
public void setComponentUrl(String componentUrl) {
this.componentUrl = componentUrl;
}
}

View File

@ -0,0 +1,82 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
* 系统公告自定义跳转方式
* @author: jeecg-boot
*/
public enum SysAnnmentTypeEnum {
/**
* 邮件跳转组件
*/
EMAIL("email", "component", "modules/eoa/email/modals/EoaEmailInForm"),
/**
* 流程跳转到我的任务
*/
BPM("bpm", "url", "/bpm/task/MyTaskList"),
/**
* 流程抄送任务
*/
BPM_VIEW("bpm_cc", "url", "/bpm/task/MyTaskList"),
/**
* 邀请用户跳转到个人设置
*/
TENANT_INVITE("tenant_invite", "url", "/system/usersetting");
/**
* 业务类型(email:邮件 bpm:流程)
*/
private String type;
/**
* 打开方式 组件component 路由url
*/
private String openType;
/**
* 组件/路由 地址
*/
private String openPage;
SysAnnmentTypeEnum(String type, String openType, String openPage) {
this.type = type;
this.openType = openType;
this.openPage = openPage;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getOpenType() {
return openType;
}
public void setOpenType(String openType) {
this.openType = openType;
}
public String getOpenPage() {
return openPage;
}
public void setOpenPage(String openPage) {
this.openPage = openPage;
}
public static SysAnnmentTypeEnum getByType(String type) {
if (oConvertUtils.isEmpty(type)) {
return null;
}
for (SysAnnmentTypeEnum val : values()) {
if (val.getType().equals(type)) {
return val;
}
}
return null;
}
}

View File

@ -0,0 +1,75 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.system.annotation.EnumDict;
import org.jeecg.common.system.vo.DictModel;
import java.util.ArrayList;
import java.util.List;
/**
* 消息跳转【vue3】
* @Author taoYan
* @Date 2022/8/19 20:41
**/
@EnumDict("messageHref")
public enum Vue3MessageHrefEnum {
/**
* 流程催办
*/
BPM("bpm", "/task/myHandleTaskInfo"),
/**
* 系统消息通知
*/
BPM_SYSTEM_MSG("bpm_msg_node", ""),
/**
* 流程抄送任务
*/
BPM_VIEW("bpm_cc", "/task/myHandleTaskInfo"),
/**
* 节点通知
*/
BPM_TASK("bpm_task", "/task/myHandleTaskInfo"),
/**
* 邮件消息
*/
EMAIL("email", "/eoa/email");
String busType;
String path;
Vue3MessageHrefEnum(String busType, String path) {
this.busType = busType;
this.path = path;
}
public String getBusType() {
return busType;
}
public String getPath() {
return path;
}
/**
* 获取字典数据
* @return
*/
public static List<DictModel> getDictList(){
List<DictModel> list = new ArrayList<>();
DictModel dictModel = null;
for(Vue3MessageHrefEnum e: Vue3MessageHrefEnum.values()){
dictModel = new DictModel();
dictModel.setValue(e.getBusType());
dictModel.setText(e.getPath());
list.add(dictModel);
}
return list;
}
}

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

@ -0,0 +1,20 @@
package org.jeecg.common.desensitization.annotation;
import java.lang.annotation.*;
/**
* 解密注解
*
* 在方法上定义 将方法返回对象中的敏感字段 解密,需要注意的是,如果没有加密过,解密会出问题,返回原字符串
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SensitiveDecode {
/**
* 指明需要脱敏的实体类class
* @return
*/
Class entity() default Object.class;
}

View File

@ -0,0 +1,20 @@
package org.jeecg.common.desensitization.annotation;
import java.lang.annotation.*;
/**
* 加密注解
*
* 在方法上声明 将方法返回对象中的敏感字段 加密/格式化
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SensitiveEncode {
/**
* 指明需要脱敏的实体类class
* @return
*/
Class entity() default Object.class;
}

View File

@ -0,0 +1,21 @@
package org.jeecg.common.desensitization.annotation;
import org.jeecg.common.desensitization.enums.SensitiveEnum;
import java.lang.annotation.*;
/**
* 在字段上定义 标识字段存储的信息是敏感的
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SensitiveField {
/**
* 不同类型处理不同
* @return
*/
SensitiveEnum type() default SensitiveEnum.ENCODE;
}

View File

@ -0,0 +1,81 @@
package org.jeecg.common.desensitization.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.desensitization.annotation.SensitiveDecode;
import org.jeecg.common.desensitization.annotation.SensitiveEncode;
import org.jeecg.common.desensitization.util.SensitiveInfoUtil;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.List;
/**
* 敏感数据切面处理类
* @Author taoYan
* @Date 2022/4/20 17:45
**/
@Slf4j
@Aspect
@Component
public class SensitiveDataAspect {
/**
* 定义切点Pointcut
*/
@Pointcut("@annotation(org.jeecg.common.desensitization.annotation.SensitiveEncode) || @annotation(org.jeecg.common.desensitization.annotation.SensitiveDecode)")
public void sensitivePointCut() {
}
@Around("sensitivePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 处理结果
Object result = point.proceed();
if(result == null){
return result;
}
Class resultClass = result.getClass();
log.debug(" resultClass = {}" , resultClass);
if(resultClass.isPrimitive()){
//是基本类型 直接返回 不需要处理
return result;
}
// 获取方法注解信息:是哪个实体、是加密还是解密
boolean isEncode = true;
Class entity = null;
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Method method = methodSignature.getMethod();
SensitiveEncode encode = method.getAnnotation(SensitiveEncode.class);
if(encode==null){
SensitiveDecode decode = method.getAnnotation(SensitiveDecode.class);
if(decode!=null){
entity = decode.entity();
isEncode = false;
}
}else{
entity = encode.entity();
}
long startTime=System.currentTimeMillis();
if(resultClass.equals(entity) || entity.equals(Object.class)){
// 方法返回实体和注解的entity一样如果注解没有申明entity属性则认为是(方法返回实体和注解的entity一样)
SensitiveInfoUtil.handlerObject(result, isEncode);
} else if(result instanceof List){
// 方法返回List<实体>
SensitiveInfoUtil.handleList(result, entity, isEncode);
}else{
// 方法返回一个对象
SensitiveInfoUtil.handleNestedObject(result, entity, isEncode);
}
long endTime=System.currentTimeMillis();
log.info((isEncode ? "加密操作," : "解密操作,") + "Aspect程序耗时" + (endTime - startTime) + "ms");
return result;
}
}

View File

@ -0,0 +1,55 @@
package org.jeecg.common.desensitization.enums;
/**
* 敏感字段信息类型
*/
public enum SensitiveEnum {
/**
* 加密
*/
ENCODE,
/**
* 中文名
*/
CHINESE_NAME,
/**
* 身份证号
*/
ID_CARD,
/**
* 座机号
*/
FIXED_PHONE,
/**
* 手机号
*/
MOBILE_PHONE,
/**
* 地址
*/
ADDRESS,
/**
* 电子邮件
*/
EMAIL,
/**
* 银行卡
*/
BANK_CARD,
/**
* 公司开户银行联号
*/
CNAPS_CODE;
}

View File

@ -0,0 +1,363 @@
package org.jeecg.common.desensitization.util;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.desensitization.annotation.SensitiveField;
import org.jeecg.common.desensitization.enums.SensitiveEnum;
import org.jeecg.common.util.encryption.AesEncryptUtil;
import org.jeecg.common.util.oConvertUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Collections;
import java.util.List;
/**
* 敏感信息处理工具类
* @author taoYan
* @date 2022/4/20 18:01
**/
@Slf4j
public class SensitiveInfoUtil {
/**
* 处理嵌套对象
* @param obj 方法返回值
* @param entity 实体class
* @param isEncode 是否加密true: 加密操作 / false:解密操作)
* @throws IllegalAccessException
*/
public static void handleNestedObject(Object obj, Class entity, boolean isEncode) throws IllegalAccessException {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
if(field.getType().isPrimitive()){
continue;
}
if(field.getType().equals(entity)){
// 对象里面是实体
field.setAccessible(true);
Object nestedObject = field.get(obj);
handlerObject(nestedObject, isEncode);
break;
}else{
// 对象里面是List<实体>
if(field.getGenericType() instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType)field.getGenericType();
if(pt.getRawType().equals(List.class)){
if(pt.getActualTypeArguments()[0].equals(entity)){
field.setAccessible(true);
Object nestedObject = field.get(obj);
handleList(nestedObject, entity, isEncode);
break;
}
}
}
}
}
}
/**
* 处理Object
* @param obj 方法返回值
* @param isEncode 是否加密true: 加密操作 / false:解密操作)
* @return
* @throws IllegalAccessException
*/
public static Object handlerObject(Object obj, boolean isEncode) throws IllegalAccessException {
if (oConvertUtils.isEmpty(obj)) {
return obj;
}
long startTime=System.currentTimeMillis();
log.debug(" obj --> "+ obj.toString());
// 判断是不是一个对象
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
boolean isSensitiveField = field.isAnnotationPresent(SensitiveField.class);
if(isSensitiveField){
// 必须有SensitiveField注解 才作处理
if(field.getType().isAssignableFrom(String.class)){
//必须是字符串类型 才作处理
field.setAccessible(true);
String realValue = (String) field.get(obj);
if(realValue==null || "".equals(realValue)){
continue;
}
SensitiveField sf = field.getAnnotation(SensitiveField.class);
if(isEncode==true){
//加密
String value = SensitiveInfoUtil.getEncodeData(realValue, sf.type());
field.set(obj, value);
}else{
//解密只处理 encode类型的
if(sf.type().equals(SensitiveEnum.ENCODE)){
String value = SensitiveInfoUtil.getDecodeData(realValue);
field.set(obj, value);
}
}
}
}
}
//long endTime=System.currentTimeMillis();
//log.info((isEncode ? "加密操作," : "解密操作,") + "当前程序耗时:" + (endTime - startTime) + "ms");
return obj;
}
/**
* 处理 List<实体>
* @param obj
* @param entity
* @param isEncodetrue: 加密操作 / false:解密操作)
*/
public static void handleList(Object obj, Class entity, boolean isEncode){
List list = (List)obj;
if(list.size()>0){
Object first = list.get(0);
if(first.getClass().equals(entity)){
for(int i=0; i<list.size(); i++){
Object temp = list.get(i);
try {
handlerObject(temp, isEncode);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 处理数据 获取解密后的数据
* @param data
* @return
*/
public static String getDecodeData(String data){
String result = null;
try {
result = AesEncryptUtil.desEncrypt(data);
} catch (Exception exception) {
log.debug("数据解密错误,原数据:"+data);
}
//解决debug模式下加解密失效导致中文被解密变成空的问题
if(oConvertUtils.isEmpty(result) && oConvertUtils.isNotEmpty(data)){
result = data;
}
return result;
}
/**
* 处理数据 获取加密后的数据 或是格式化后的数据
* @param data 字符串
* @param sensitiveEnum 类型
* @return 处理后的字符串
*/
public static String getEncodeData(String data, SensitiveEnum sensitiveEnum){
String result;
switch (sensitiveEnum){
case ENCODE:
try {
result = AesEncryptUtil.encrypt(data);
} catch (Exception exception) {
log.error("数据加密错误", exception.getMessage());
result = data;
}
break;
case CHINESE_NAME:
result = chineseName(data);
break;
case ID_CARD:
result = idCardNum(data);
break;
case FIXED_PHONE:
result = fixedPhone(data);
break;
case MOBILE_PHONE:
result = mobilePhone(data);
break;
case ADDRESS:
result = address(data, 3);
break;
case EMAIL:
result = email(data);
break;
case BANK_CARD:
result = bankCard(data);
break;
case CNAPS_CODE:
result = cnapsCode(data);
break;
default:
result = data;
}
return result;
}
/**
* [中文姓名] 只显示第一个汉字其他隐藏为2个星号
* @param fullName 全名
* @return <例子:李**>
*/
public static String chineseName(String fullName) {
if (oConvertUtils.isEmpty(fullName)) {
return "";
}
return formatRight(fullName, 1);
}
/**
* [中文姓名] 只显示第一个汉字其他隐藏为2个星号
* @param familyName 姓
* @param firstName 名
* @return <例子:李**>
*/
public static String chineseName(String familyName, String firstName) {
if (oConvertUtils.isEmpty(familyName) || oConvertUtils.isEmpty(firstName)) {
return "";
}
return chineseName(familyName + firstName);
}
/**
* [身份证号] 显示最后四位其他隐藏。共计18位或者15位。
* @param id 身份证号
* @return <例子:*************5762>
*/
public static String idCardNum(String id) {
if (oConvertUtils.isEmpty(id)) {
return "";
}
return formatLeft(id, 4);
}
/**
* [固定电话] 后四位,其他隐藏
* @param num 固定电话
* @return <例子:****1234>
*/
public static String fixedPhone(String num) {
if (oConvertUtils.isEmpty(num)) {
return "";
}
return formatLeft(num, 4);
}
/**
* [手机号码] 前三位,后四位,其他隐藏
* @param num 手机号码
* @return <例子:138******1234>
*/
public static String mobilePhone(String num) {
if (oConvertUtils.isEmpty(num)) {
return "";
}
int len = num.length();
if(len<11){
return num;
}
return formatBetween(num, 3, 4);
}
/**
* [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护
* @param address 地址
* @param sensitiveSize 敏感信息长度
* @return <例子:北京市海淀区****>
*/
public static String address(String address, int sensitiveSize) {
if (oConvertUtils.isEmpty(address)) {
return "";
}
int len = address.length();
if(len<sensitiveSize){
return address;
}
return formatRight(address, sensitiveSize);
}
/**
* [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示
* @param email 电子邮箱
* @return <例子:g**@163.com>
*/
public static String email(String email) {
if (oConvertUtils.isEmpty(email)) {
return "";
}
int index = email.indexOf("@");
if (index <= 1){
return email;
}
String begin = email.substring(0, 1);
String end = email.substring(index);
String stars = "**";
return begin + stars + end;
}
/**
* [银行卡号] 前六位后四位其他用星号隐藏每位1个星号
* @param cardNum 银行卡号
* @return <例子:6222600**********1234>
*/
public static String bankCard(String cardNum) {
if (oConvertUtils.isEmpty(cardNum)) {
return "";
}
return formatBetween(cardNum, 6, 4);
}
/**
* [公司开户银行联号] 公司开户银行联行号,显示前两位其他用星号隐藏每位1个星号
* @param code 公司开户银行联号
* @return <例子:12********>
*/
public static String cnapsCode(String code) {
if (oConvertUtils.isEmpty(code)) {
return "";
}
return formatRight(code, 2);
}
/**
* 将右边的格式化成*
* @param str 字符串
* @param reservedLength 保留长度
* @return 格式化后的字符串
*/
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;
}
/**
* 将左边的格式化成*
* @param str 字符串
* @param reservedLength 保留长度
* @return 格式化后的字符串
*/
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, "*"));
return stars + show;
}
/**
* 将中间的格式化成*
* @param str 字符串
* @param beginLen 开始保留长度
* @param endLen 结尾保留长度
* @return 格式化后的字符串
*/
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);
String stars = String.join("", Collections.nCopies(len-beginLen-endLen, "*"));
return begin + stars + end;
}
}

View File

@ -0,0 +1,546 @@
package org.jeecg.common.es;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.util.RestUtil;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* 关于 ElasticSearch 的一些方法(创建索引、添加数据、查询等)
*
* @author sunjianlei
*/
@Slf4j
@Component
@ConditionalOnProperty(prefix = "jeecg.elasticsearch", name = "cluster-nodes")
public class JeecgElasticsearchTemplate {
/** es服务地址 */
private String baseUrl;
private final String FORMAT_JSON = "format=json";
/** Elasticsearch 的版本号 */
private String version = null;
/**ElasticSearch 最大可返回条目数*/
public static final int ES_MAX_SIZE = 10000;
/**es7*/
public static final String IE_SEVEN = "7";
/**url not found 404*/
public static final String URL_NOT_FOUND = "404 Not Found";
public JeecgElasticsearchTemplate(@Value("${jeecg.elasticsearch.cluster-nodes}") String baseUrl, @Value("${jeecg.elasticsearch.check-enabled}") boolean checkEnabled) {
log.debug("JeecgElasticsearchTemplate BaseURL" + baseUrl);
if (StringUtils.isNotEmpty(baseUrl)) {
this.baseUrl = baseUrl;
// 验证配置的ES地址是否有效
if (checkEnabled) {
try {
this.getElasticsearchVersion();
log.info("ElasticSearch 服务连接成功");
log.info("ElasticSearch version: " + this.version);
} catch (Exception e) {
this.version = "";
log.warn("ElasticSearch 服务连接失败原因配置未通过。可能是BaseURL未配置或配置有误也可能是Elasticsearch服务未启动。接下来将会拒绝执行任何方法");
}
}
}
}
/**
* 获取 Elasticsearch 的版本号信息失败返回null
*/
private void getElasticsearchVersion() {
if (this.version == null) {
String url = this.getBaseUrl().toString();
JSONObject result = RestUtil.get(url);
if (result != null) {
JSONObject v = result.getJSONObject("version");
this.version = v.getString("number");
}
}
}
public StringBuilder getBaseUrl(String indexName, String typeName) {
typeName = typeName.trim().toLowerCase();
return this.getBaseUrl(indexName).append("/").append(typeName);
}
public StringBuilder getBaseUrl(String indexName) {
indexName = indexName.trim().toLowerCase();
return this.getBaseUrl().append("/").append(indexName);
}
public StringBuilder getBaseUrl() {
return new StringBuilder("http://").append(this.baseUrl);
}
/**
* cat 查询ElasticSearch系统数据返回json
*/
private <T> ResponseEntity<T> cat(String urlAfter, Class<T> responseType) {
String url = this.getBaseUrl().append("/_cat").append(urlAfter).append("?").append(FORMAT_JSON).toString();
return RestUtil.request(url, HttpMethod.GET, null, null, null, responseType);
}
/**
* 查询所有索引
* <p>
* 查询地址GET http://{baseUrl}/_cat/indices
*/
public JSONArray getIndices() {
return getIndices(null);
}
/**
* 查询单个索引
* <p>
* 查询地址GET http://{baseUrl}/_cat/indices/{indexName}
*/
public JSONArray getIndices(String indexName) {
StringBuilder urlAfter = new StringBuilder("/indices");
if (!StringUtils.isEmpty(indexName)) {
urlAfter.append("/").append(indexName.trim().toLowerCase());
}
return cat(urlAfter.toString(), JSONArray.class).getBody();
}
/**
* 索引是否存在
*/
public boolean indexExists(String indexName) {
try {
JSONArray array = getIndices(indexName);
return array != null;
} catch (org.springframework.web.client.HttpClientErrorException ex) {
if (HttpStatus.NOT_FOUND == ex.getStatusCode()) {
return false;
} else {
throw ex;
}
}
}
/**
* 根据ID获取索引数据未查询到返回null
* <p>
* 查询地址GET http://{baseUrl}/{indexName}/{typeName}/{dataId}
*
* @param indexName 索引名称
* @param typeName type一个任意字符串用于分类
* @param dataId 数据id
* @return
*/
public JSONObject getDataById(String indexName, String typeName, String dataId) {
String url = this.getBaseUrl(indexName, typeName).append("/").append(dataId).toString();
log.info("url:" + url);
JSONObject result = RestUtil.get(url);
boolean found = result.getBoolean("found");
if (found) {
return result.getJSONObject("_source");
} else {
return null;
}
}
/**
* 创建索引
* <p>
* 查询地址PUT http://{baseUrl}/{indexName}
*/
public boolean createIndex(String indexName) {
String url = this.getBaseUrl(indexName).toString();
/* 返回结果 (仅供参考)
"createIndex": {
"shards_acknowledged": true,
"acknowledged": true,
"index": "hello_world"
}
*/
try {
return RestUtil.put(url).getBoolean("acknowledged");
} catch (org.springframework.web.client.HttpClientErrorException ex) {
if (HttpStatus.BAD_REQUEST == ex.getStatusCode()) {
log.warn("索引创建失败:" + indexName + " 已存在,无需再创建");
} else {
ex.printStackTrace();
}
}
return false;
}
/**
* 删除索引
* <p>
* 查询地址DELETE http://{baseUrl}/{indexName}
*/
public boolean removeIndex(String indexName) {
String url = this.getBaseUrl(indexName).toString();
try {
return RestUtil.delete(url).getBoolean("acknowledged");
} catch (org.springframework.web.client.HttpClientErrorException ex) {
if (HttpStatus.NOT_FOUND == ex.getStatusCode()) {
log.warn("索引删除失败:" + indexName + " 不存在,无需删除");
} else {
ex.printStackTrace();
}
}
return false;
}
/**
* 获取索引字段映射(可获取字段类型)
* <p>
*
* @param indexName 索引名称
* @param typeName 分类名称
* @return
*/
public JSONObject getIndexMapping(String indexName, String typeName) {
String url = this.getBaseUrl(indexName, typeName).append("/_mapping?").append(FORMAT_JSON).toString();
// 针对 es 7.x 版本做兼容
this.getElasticsearchVersion();
if (oConvertUtils.isNotEmpty(this.version) && this.version.startsWith(IE_SEVEN)) {
url += "&include_type_name=true";
}
log.info("getIndexMapping-url:" + url);
/*
* 参考返回JSON结构
*
*{
* // 索引名称
* "[indexName]": {
* "mappings": {
* // 分类名称
* "[typeName]": {
* "properties": {
* // 字段名
* "input_number": {
* // 字段类型
* "type": "long"
* },
* "input_string": {
* "type": "text",
* "fields": {
* "keyword": {
* "type": "keyword",
* "ignore_above": 256
* }
* }
* }
* }
* }
* }
* }
* }
*/
try {
return RestUtil.get(url);
} catch (org.springframework.web.client.HttpClientErrorException e) {
String message = e.getMessage();
if (message != null && message.contains(URL_NOT_FOUND)) {
return null;
}
throw e;
}
}
/**
* 获取索引字段映射返回Java实体类
*
* @param indexName
* @param typeName
* @return
*/
public <T> Map<String, T> getIndexMappingFormat(String indexName, String typeName, Class<T> clazz) {
JSONObject mapping = this.getIndexMapping(indexName, typeName);
Map<String, T> map = new HashMap<>(5);
if (mapping == null) {
return map;
}
// 获取字段属性
JSONObject properties = mapping.getJSONObject(indexName)
.getJSONObject("mappings")
.getJSONObject(typeName)
.getJSONObject("properties");
// 封装成 java类型
for (String key : properties.keySet()) {
T entity = properties.getJSONObject(key).toJavaObject(clazz);
map.put(key, entity);
}
return map;
}
/**
* 保存数据详见saveOrUpdate
*/
public boolean save(String indexName, String typeName, String dataId, JSONObject data) {
return this.saveOrUpdate(indexName, typeName, dataId, data);
}
/**
* 更新数据详见saveOrUpdate
*/
public boolean update(String indexName, String typeName, String dataId, JSONObject data) {
return this.saveOrUpdate(indexName, typeName, dataId, data);
}
/**
* 保存或修改索引数据
* <p>
* 查询地址PUT http://{baseUrl}/{indexName}/{typeName}/{dataId}
*
* @param indexName 索引名称
* @param typeName type一个任意字符串用于分类
* @param dataId 数据id
* @param data 要存储的数据
* @return
*/
public boolean saveOrUpdate(String indexName, String typeName, String dataId, JSONObject data) {
String url = this.getBaseUrl(indexName, typeName).append("/").append(dataId).append("?refresh=wait_for").toString();
/* 返回结果(仅供参考)
"createIndexA2": {
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_index": "test_index_1",
"_type": "test_type_1",
"_id": "a2",
"_version": 1,
"_primary_term": 1
}
*/
try {
// 去掉 data 中为空的值
Set<String> keys = data.keySet();
List<String> emptyKeys = new ArrayList<>(keys.size());
for (String key : keys) {
String value = data.getString(key);
//1、剔除空值
if (oConvertUtils.isEmpty(value) || "[]".equals(value)) {
emptyKeys.add(key);
}
//2、剔除上传控件值(会导致ES同步失败报异常failed to parse field [ge_pic] of type [text] )
if (oConvertUtils.isNotEmpty(value) && value.indexOf("[{")!=-1) {
emptyKeys.add(key);
log.info("-------剔除上传控件字段------------key: "+ key);
}
}
for (String key : emptyKeys) {
data.remove(key);
}
} catch (Exception e) {
e.printStackTrace();
}
try {
String result = RestUtil.put(url, data).getString("result");
return "created".equals(result) || "updated".equals(result);
} catch (Exception e) {
log.error(e.getMessage() + "\n-- url: " + url + "\n-- data: " + data.toJSONString());
//TODO 打印接口返回异常json
return false;
}
}
/**
* 批量保存数据
*
* @param indexName 索引名称
* @param typeName type一个任意字符串用于分类
* @param dataList 要存储的数据数组每行数据必须包含id
* @return
*/
public boolean saveBatch(String indexName, String typeName, JSONArray dataList) {
String url = this.getBaseUrl().append("/_bulk").append("?refresh=wait_for").toString();
StringBuilder bodySb = new StringBuilder();
for (int i = 0; i < dataList.size(); i++) {
JSONObject data = dataList.getJSONObject(i);
String id = data.getString("id");
// 该行的操作
// {"create": {"_id":"${id}", "_index": "${indexName}", "_type": "${typeName}"}}
JSONObject action = new JSONObject();
JSONObject actionInfo = new JSONObject();
actionInfo.put("_id", id);
actionInfo.put("_index", indexName);
actionInfo.put("_type", typeName);
action.put("create", actionInfo);
bodySb.append(action.toJSONString()).append("\n");
// 该行的数据
data.remove("id");
bodySb.append(data.toJSONString()).append("\n");
}
System.out.println("+-+-+-: bodySb.toString(): " + bodySb.toString());
HttpHeaders headers = RestUtil.getHeaderApplicationJson();
RestUtil.request(url, HttpMethod.PUT, headers, null, bodySb, JSONObject.class);
return true;
}
/**
* 删除索引数据
* <p>
* 请求地址DELETE http://{baseUrl}/{indexName}/{typeName}/{dataId}
*/
public boolean delete(String indexName, String typeName, String dataId) {
String url = this.getBaseUrl(indexName, typeName).append("/").append(dataId).toString();
/* 返回结果(仅供参考)
{
"_index": "es_demo",
"_type": "docs",
"_id": "001",
"_version": 3,
"result": "deleted",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 28,
"_primary_term": 18
}
*/
try {
return "deleted".equals(RestUtil.delete(url).getString("result"));
} catch (org.springframework.web.client.HttpClientErrorException ex) {
if (HttpStatus.NOT_FOUND == ex.getStatusCode()) {
return false;
} else {
throw ex;
}
}
}
/* = = = 以下关于查询和查询条件的方法 = = =*/
/**
* 查询数据
* <p>
* 请求地址POST http://{baseUrl}/{indexName}/{typeName}/_search
*/
public JSONObject search(String indexName, String typeName, JSONObject queryObject) {
String url = this.getBaseUrl(indexName, typeName).append("/_search").toString();
log.info("url:" + url + " ,search: " + queryObject.toJSONString());
JSONObject res = RestUtil.post(url, queryObject);
log.info("url:" + url + " ,return res: \n" + res.toJSONString());
return res;
}
/**
* @param source 源滤波器指定返回的字段传null返回所有字段
* @param query
* @param from 从第几条数据开始
* @param size 返回条目数
* @return { "query": query }
*/
public JSONObject buildQuery(List<String> source, JSONObject query, int from, int size) {
JSONObject json = new JSONObject();
if (source != null) {
json.put("_source", source);
}
json.put("query", query);
json.put("from", from);
json.put("size", size);
return json;
}
/**
* @return { "bool" : { "must": must, "must_not": mustNot, "should": should } }
*/
public JSONObject buildBoolQuery(JSONArray must, JSONArray mustNot, JSONArray should) {
JSONObject bool = new JSONObject();
if (must != null) {
bool.put("must", must);
}
if (mustNot != null) {
bool.put("must_not", mustNot);
}
if (should != null) {
bool.put("should", should);
}
JSONObject json = new JSONObject();
json.put("bool", bool);
return json;
}
/**
* @param field 要查询的字段
* @param args 查询参数,参考: *哈哈* OR *哒* NOT *呵* OR *啊*
* @return
*/
public JSONObject buildQueryString(String field, String... args) {
if (field == null) {
return null;
}
StringBuilder sb = new StringBuilder(field).append(":(");
if (args != null) {
for (String arg : args) {
sb.append(arg).append(" ");
}
}
sb.append(")");
return this.buildQueryString(sb.toString());
}
/**
* @return { "query_string": { "query": query } }
*/
public JSONObject buildQueryString(String query) {
JSONObject queryString = new JSONObject();
queryString.put("query", query);
JSONObject json = new JSONObject();
json.put("query_string", queryString);
return json;
}
/**
* @param field 查询字段
* @param min 最小值
* @param max 最大值
* @param containMin 范围内是否包含最小值
* @param containMax 范围内是否包含最大值
* @return { "range" : { field : { 『 "gt『e』?containMin" : min 』?min!=null , 『 "lt『e』?containMax" : max 』}} }
*/
public JSONObject buildRangeQuery(String field, Object min, Object max, boolean containMin, boolean containMax) {
JSONObject inner = new JSONObject();
if (min != null) {
if (containMin) {
inner.put("gte", min);
} else {
inner.put("gt", min);
}
}
if (max != null) {
if (containMax) {
inner.put("lte", max);
} else {
inner.put("lt", max);
}
}
JSONObject range = new JSONObject();
range.put(field, inner);
JSONObject json = new JSONObject();
json.put("range", range);
return json;
}
}

View File

@ -0,0 +1,98 @@
package org.jeecg.common.es;
/**
* 用于创建 ElasticSearch 的 queryString
*
* @author sunjianlei
*/
public class QueryStringBuilder {
StringBuilder builder;
public QueryStringBuilder(String field, String str, boolean not, boolean addQuot) {
builder = this.createBuilder(field, str, not, addQuot);
}
public QueryStringBuilder(String field, String str, boolean not) {
builder = this.createBuilder(field, str, not, true);
}
/**
* 创建 StringBuilder
*
* @param field
* @param str
* @param not 是否是不匹配
* @param addQuot 是否添加双引号
* @return
*/
public StringBuilder createBuilder(String field, String str, boolean not, boolean addQuot) {
StringBuilder sb = new StringBuilder(field).append(":(");
if (not) {
sb.append(" NOT ");
}
this.addQuotEffect(sb, str, addQuot);
return sb;
}
public QueryStringBuilder and(String str) {
return this.and(str, true);
}
public QueryStringBuilder and(String str, boolean addQuot) {
builder.append(" AND ");
this.addQuot(str, addQuot);
return this;
}
public QueryStringBuilder or(String str) {
return this.or(str, true);
}
public QueryStringBuilder or(String str, boolean addQuot) {
builder.append(" OR ");
this.addQuot(str, addQuot);
return this;
}
public QueryStringBuilder not(String str) {
return this.not(str, true);
}
public QueryStringBuilder not(String str, boolean addQuot) {
builder.append(" NOT ");
this.addQuot(str, addQuot);
return this;
}
/**
* 添加双引号(模糊查询,不能加双引号)
*/
private QueryStringBuilder addQuot(String str, boolean addQuot) {
return this.addQuotEffect(this.builder, str, addQuot);
}
/**
* 是否在两边加上双引号
* @param builder
* @param str
* @param addQuot
* @return
*/
private QueryStringBuilder addQuotEffect(StringBuilder builder, String str, boolean addQuot) {
if (addQuot) {
builder.append('"');
}
builder.append(str);
if (addQuot) {
builder.append('"');
}
return this;
}
@Override
public String toString() {
return builder.append(")").toString();
}
}

View File

@ -0,0 +1,23 @@
package org.jeecg.common.exception;
/**
* @Description: jeecg-boot自定义401异常
* @author: jeecg-boot
*/
public class JeecgBoot401Exception extends RuntimeException {
private static final long serialVersionUID = 1L;
public JeecgBoot401Exception(String message){
super(message);
}
public JeecgBoot401Exception(Throwable cause)
{
super(cause);
}
public JeecgBoot401Exception(String message, Throwable cause)
{
super(message,cause);
}
}

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

@ -0,0 +1,39 @@
package org.jeecg.common.exception;
import org.jeecg.common.constant.CommonConstant;
/**
* @Description: jeecg-boot自定义异常
* @author: jeecg-boot
*/
public class JeecgBootException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 返回给前端的错误code
*/
private int errCode = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
public JeecgBootException(String message){
super(message);
}
public JeecgBootException(String message, int errCode){
super(message);
this.errCode = errCode;
}
public int getErrCode() {
return errCode;
}
public JeecgBootException(Throwable cause)
{
super(cause);
}
public JeecgBootException(String message,Throwable cause)
{
super(message,cause);
}
}

View File

@ -0,0 +1,244 @@
package org.jeecg.common.exception;
import cn.hutool.core.util.ObjectUtil;
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;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 异常处理器
*
* @Author scott
* @Date 2019
*/
@RestControllerAdvice
@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());
}
/**
* 处理自定义微服务异常
*/
@ExceptionHandler(JeecgCloudException.class)
public Result<?> handleJeecgCloudException(JeecgCloudException e){
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error(e.getMessage());
}
/**
* 处理自定义异常
*/
@ExceptionHandler(JeecgBoot401Exception.class)
@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("没有权限,请联系管理员授权,后刷新缓存!");
}
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e){
log.error(e.getMessage(), e);
//update-begin---author:zyf ---date:20220411 for处理Sentinel限流自定义异常
Throwable throwable = e.getCause();
SentinelErrorInfoEnum errorInfoEnum = SentinelErrorInfoEnum.getErrorByException(throwable);
if (ObjectUtil.isNotEmpty(errorInfoEnum)) {
return Result.error(errorInfoEnum.getError());
}
//update-end---author:zyf ---date:20220411 for处理Sentinel限流自定义异常
addSysLog(e);
return Result.error("操作失败,"+e.getMessage());
}
/**
* @Author 政辉
* @param e
* @return
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result<?> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e){
StringBuffer sb = new StringBuffer();
sb.append("不支持");
sb.append(e.getMethod());
sb.append("请求方法,");
sb.append("支持以下");
String [] methods = e.getSupportedMethods();
if(methods!=null){
for(String str:methods){
sb.append(str);
sb.append("");
}
}
log.error(sb.toString(), e);
//return Result.error("没有权限,请联系管理员授权");
addSysLog(e);
return Result.error(405,sb.toString());
}
/**
* spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException
*/
@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("执行数据库异常,违反了完整性例如:违反惟一约束、违反非空限制、字段内容超出长度等");
}
@ExceptionHandler(PoolException.class)
public Result<?> handlePoolException(PoolException e) {
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error("Redis 连接异常!");
}
/**
* SQL注入风险全局异常处理
*
* @param exception
* @return
*/
@ExceptionHandler(JeecgSqlInjectionException.class)
public Result<?> handleSQLException(Exception exception) {
String msg = exception.getMessage().toLowerCase();
final String extractvalue = "extractvalue";
final String updatexml = "updatexml";
boolean hasSensitiveInformation = msg.indexOf(extractvalue) >= 0 || msg.indexOf(updatexml) >= 0;
if (msg != null && hasSensitiveInformation) {
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,23 @@
package org.jeecg.common.exception;
/**
* @Description: jeecg-boot自定义SQL注入异常
* @author: jeecg-boot
*/
public class JeecgSqlInjectionException extends RuntimeException {
private static final long serialVersionUID = 1L;
public JeecgSqlInjectionException(String message){
super(message);
}
public JeecgSqlInjectionException(Throwable cause)
{
super(cause);
}
public JeecgSqlInjectionException(String message, Throwable cause)
{
super(message,cause);
}
}

View File

@ -0,0 +1,22 @@
package org.jeecg.common.handler;
import com.alibaba.fastjson.JSONObject;
/**
* 填值规则接口
*
* @author Yan_东
* 如需使用填值规则功能,规则实现类必须实现此接口
*/
public interface IFillRuleHandler {
/**
* 填值规则
* @param params 页面配置固定参数
* @param formData 动态表单参数
* @return
*/
public Object execute(JSONObject params, JSONObject formData);
}

View File

@ -0,0 +1,19 @@
package org.jeecg.common.system.annotation;
import java.lang.annotation.*;
/**
* 将枚举类转化成字典数据
* @Author taoYan
* @Date 2022/7/8 10:34
**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnumDict {
/**
* 作为字典数据的唯一编码
*/
String value() default "";
}

View File

@ -0,0 +1,203 @@
package org.jeecg.common.system.base.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
* @Description: Controller基类
* @Author: dangzhenghui@163.com
* @Date: 2019-4-21 8:13
* @Version: 1.0
*/
@Slf4j
public class JeecgController<T, S extends IService<T>> {
/**issues/2933 JeecgController注入service时改用protected修饰能避免重复引用service*/
@Autowired
protected S service;
@Resource
private JeecgBaseConfig jeecgBaseConfig;
/**
* 导出excel
*
* @param request
*/
protected ModelAndView exportXls(HttpServletRequest request, T object, Class<T> clazz, String title) {
// Step.1 组装查询条件
QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// 过滤选中数据
String selections = request.getParameter("selections");
if (oConvertUtils.isNotEmpty(selections)) {
List<String> selectionList = Arrays.asList(selections.split(","));
queryWrapper.in("id",selectionList);
}
// Step.2 获取导出数据
List<T> exportList = service.list(queryWrapper);
// Step.3 AutoPoi 导出Excel
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
//此处设置的filename无效 ,前端会重更新设置一下
mv.addObject(NormalExcelConstants.FILE_NAME, title);
mv.addObject(NormalExcelConstants.CLASS, clazz);
//update-begin--Author:liusq Date:20210126 for图片导出报错ImageBasePath未设置--------------------
ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title);
exportParams.setImageBasePath(jeecgBaseConfig.getPath().getUpload());
//update-end--Author:liusq Date:20210126 for图片导出报错ImageBasePath未设置----------------------
mv.addObject(NormalExcelConstants.PARAMS,exportParams);
mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
return mv;
}
/**
* 根据每页sheet数量导出多sheet
*
* @param request
* @param object 实体类
* @param clazz 实体类class
* @param title 标题
* @param exportFields 导出字段自定义
* @param pageNum 每个sheet的数据条数
* @param request
*/
protected ModelAndView exportXlsSheet(HttpServletRequest request, T object, Class<T> clazz, String title,String exportFields,Integer pageNum) {
// Step.1 组装查询条件
QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// Step.2 计算分页sheet数据
double total = service.count();
int count = (int)Math.ceil(total/pageNum);
//update-begin-author:liusq---date:20220629--for: 多sheet导出根据选择导出写法调整 ---
// Step.3 过滤选中数据
String selections = request.getParameter("selections");
if (oConvertUtils.isNotEmpty(selections)) {
List<String> selectionList = Arrays.asList(selections.split(","));
queryWrapper.in("id",selectionList);
}
//update-end-author:liusq---date:20220629--for: 多sheet导出根据选择导出写法调整 ---
// Step.4 多sheet处理
List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
for (int i = 1; i <=count ; i++) {
Page<T> page = new Page<T>(i, pageNum);
IPage<T> pageList = service.page(page, queryWrapper);
List<T> exportList = pageList.getRecords();
Map<String, Object> map = new HashMap<>(5);
ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title+i,jeecgBaseConfig.getPath().getUpload());
exportParams.setType(ExcelType.XSSF);
//map.put("title",exportParams);
//表格Title
map.put(NormalExcelConstants.PARAMS,exportParams);
//表格对应实体
map.put(NormalExcelConstants.CLASS,clazz);
//数据集合
map.put(NormalExcelConstants.DATA_LIST, exportList);
listMap.add(map);
}
// Step.4 AutoPoi 导出Excel
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
//此处设置的filename无效 ,前端会重更新设置一下
mv.addObject(NormalExcelConstants.FILE_NAME, title);
mv.addObject(NormalExcelConstants.MAP_LIST, listMap);
return mv;
}
/**
* 根据权限导出excel传入导出字段参数
*
* @param request
*/
protected ModelAndView exportXls(HttpServletRequest request, T object, Class<T> clazz, String title,String exportFields) {
ModelAndView mv = this.exportXls(request,object,clazz,title);
mv.addObject(NormalExcelConstants.EXPORT_FIELDS,exportFields);
return mv;
}
/**
* 获取对象ID
*
* @return
*/
private String getId(T item) {
try {
return PropertyUtils.getProperty(item, "id").toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
protected Result<?> importExcel(HttpServletRequest request, HttpServletResponse response, Class<T> clazz) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
// 获取上传文件对象
MultipartFile file = entity.getValue();
ImportParams params = new ImportParams();
params.setTitleRows(2);
params.setHeadRows(1);
params.setNeedSave(true);
try {
List<T> list = ExcelImportUtil.importExcel(file.getInputStream(), clazz, params);
//update-begin-author:taoyan date:20190528 for:批量插入数据
long start = System.currentTimeMillis();
service.saveBatch(list);
//400条 saveBatch消耗时间1592毫秒 循环插入消耗时间1947毫秒
//1200条 saveBatch消耗时间3687毫秒 循环插入消耗时间5212毫秒
log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
//update-end-author:taoyan date:20190528 for:批量插入数据
return Result.ok("文件导入成功!数据行数:" + list.size());
} catch (Exception e) {
//update-begin-author:taoyan date:20211124 for: 导入数据重复增加提示
String msg = e.getMessage();
log.error(msg, e);
if(msg!=null && msg.indexOf("Duplicate entry")>=0){
return Result.error("文件导入失败:有重复数据!");
}else{
return Result.error("文件导入失败:" + e.getMessage());
}
//update-end-author:taoyan date:20211124 for: 导入数据重复增加提示
} finally {
try {
file.getInputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return Result.error("文件导入失败!");
}
}

View File

@ -0,0 +1,68 @@
package org.jeecg.common.system.base.entity;
import java.io.Serializable;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: Entity基类
* @Author: dangzhenghui@163.com
* @Date: 2019-4-28
* @Version: 1.1
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class JeecgEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "ID")
private java.lang.String id;
/**
* 创建人
*/
@ApiModelProperty(value = "创建人")
@Excel(name = "创建人", width = 15)
private java.lang.String createBy;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
@Excel(name = "创建时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private java.util.Date createTime;
/**
* 更新人
*/
@ApiModelProperty(value = "更新人")
@Excel(name = "更新人", width = 15)
private java.lang.String updateBy;
/**
* 更新时间
*/
@ApiModelProperty(value = "更新时间")
@Excel(name = "更新时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private java.util.Date updateTime;
}

View File

@ -0,0 +1,12 @@
package org.jeecg.common.system.base.service;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @Description: Service基类
* @Author: dangzhenghui@163.com
* @Date: 2019-4-21 8:13
* @Version: 1.0
*/
public interface JeecgService<T> extends IService<T> {
}

View File

@ -0,0 +1,19 @@
package org.jeecg.common.system.base.service.impl;
import org.jeecg.common.system.base.entity.JeecgEntity;
import org.jeecg.common.system.base.service.JeecgService;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
/**
* @Description: ServiceImpl基类
* @Author: dangzhenghui@163.com
* @Date: 2019-4-21 8:13
* @Version: 1.0
*/
@Slf4j
public class JeecgServiceImpl<M extends BaseMapper<T>, T extends JeecgEntity> extends ServiceImpl<M, T> implements JeecgService<T> {
}

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

@ -0,0 +1,45 @@
package org.jeecg.common.system.query;
import org.jeecg.common.util.oConvertUtils;
/**
* 查询链接规则
*
* @Author Sunjianlei
*/
public enum MatchTypeEnum {
/**查询链接规则 AND*/
AND("AND"),
/**查询链接规则 OR*/
OR("OR");
private String value;
MatchTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static MatchTypeEnum getByValue(Object value) {
if (oConvertUtils.isEmpty(value)) {
return null;
}
return getByValue(value.toString());
}
public static MatchTypeEnum getByValue(String value) {
if (oConvertUtils.isEmpty(value)) {
return null;
}
for (MatchTypeEnum val : values()) {
if (val.getValue().toLowerCase().equals(value.toLowerCase())) {
return val;
}
}
return null;
}
}

View File

@ -0,0 +1,81 @@
package org.jeecg.common.system.query;
import java.io.Serializable;
/**
* @Description: QueryCondition
* @author: jeecg-boot
*/
public class QueryCondition implements Serializable {
private static final long serialVersionUID = 4740166316629191651L;
private String field;
/** 组件的类型例如input、select、radio */
private String type;
/**
* 对应的数据库字段的类型
* 支持int、bigDecimal、short、long、float、double、boolean
*/
private String dbType;
private String rule;
private String val;
public QueryCondition(String field, String type, String dbType, String rule, String val) {
this.field = field;
this.type = type;
this.dbType = dbType;
this.rule = rule;
this.val = val;
}
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDbType() {
return dbType;
}
public void setDbType(String dbType) {
this.dbType = dbType;
}
public String getRule() {
return rule;
}
public void setRule(String rule) {
this.rule = rule;
}
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
@Override
public String toString(){
StringBuffer sb =new StringBuffer();
if(field == null || "".equals(field)){
return "";
}
sb.append(this.field).append(" ").append(this.rule).append(" ").append(this.type).append(" ").append(this.dbType).append(" ").append(this.val);
return sb.toString();
}
}

View File

@ -0,0 +1,990 @@
package org.jeecg.common.system.query;
import java.beans.PropertyDescriptor;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.exception.JeecgBootException;
import org.jeecg.common.system.util.JeecgDataAutorUtils;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.util.SqlConcatUtil;
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
import org.jeecg.common.util.*;
import org.springframework.util.NumberUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
/**
* @Description: 查询生成器
* @author: jeecg-boot
*/
@Slf4j
public class QueryGenerator {
public static final String SQL_RULES_COLUMN = "SQL_RULES_COLUMN";
private static final String BEGIN = "_begin";
private static final String END = "_end";
/**
* 数字类型字段,拼接此后缀 接受多值参数
*/
private static final String MULTI = "_MultiString";
private static final String STAR = "*";
private static final String COMMA = ",";
/**
* 查询 逗号转义符 相当于一个逗号【作废】
*/
public static final String QUERY_COMMA_ESCAPE = "++";
private static final String NOT_EQUAL = "!";
/**页面带有规则值查询,空格作为分隔符*/
private static final String QUERY_SEPARATE_KEYWORD = " ";
/**高级查询前端传来的参数名*/
private static final String SUPER_QUERY_PARAMS = "superQueryParams";
/** 高级查询前端传来的拼接方式参数名 */
private static final String SUPER_QUERY_MATCH_TYPE = "superQueryMatchType";
/** 单引号 */
public static final String SQL_SQ = "'";
/**排序列*/
private static final String ORDER_COLUMN = "column";
/**排序方式*/
private static final String ORDER_TYPE = "order";
private static final String ORDER_TYPE_ASC = "ASC";
/**mysql 模糊查询之特殊字符下划线 _、\*/
public static final String LIKE_MYSQL_SPECIAL_STRS = "_,%";
/**日期格式化yyyy-MM-dd*/
public static final String YYYY_MM_DD = "yyyy-MM-dd";
/**to_date*/
public static final String TO_DATE = "to_date";
/**时间格式化 */
private static final ThreadLocal<SimpleDateFormat> LOCAL = new ThreadLocal<SimpleDateFormat>();
private static SimpleDateFormat getTime(){
SimpleDateFormat time = LOCAL.get();
if(time == null){
time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
LOCAL.set(time);
}
return time;
}
/**
* 获取查询条件构造器QueryWrapper实例 通用查询条件已被封装完成
* @param searchObj 查询实体
* @param parameterMap request.getParameterMap()
* @return QueryWrapper实例
*/
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, 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 查询条件
* <p>使用此方法 需要有如下几点注意:
* <br>1.使用QueryWrapper 而非LambdaQueryWrapper;
* <br>2.实例化QueryWrapper时不可将实体传入参数
* <br>错误示例:如QueryWrapper<JeecgDemo> queryWrapper = new QueryWrapper<JeecgDemo>(jeecgDemo);
* <br>正确示例:QueryWrapper<JeecgDemo> queryWrapper = new QueryWrapper<JeecgDemo>();
* <br>3.也可以不使用这个方法直接调用 {@link #initQueryWrapper}直接获取实例
*/
private static void installMplus(QueryWrapper<?> queryWrapper, Object searchObj, Map<String, String[]> parameterMap, Map<String, QueryRuleEnum> customRuleMap) {
/*
* 注意:权限查询由前端配置数据规则 当一个人有多个所属部门时候 可以在规则配置包含条件 orgCode 包含 #{sys_org_code}
但是不支持在自定义SQL中写orgCode in #{sys_org_code}
当一个人只有一个部门 就直接配置等于条件: orgCode 等于 #{sys_org_code} 或者配置自定义SQL: orgCode = '#{sys_org_code}'
*/
//区间条件组装 模糊查询 高级查询组装 简单排序 权限查询
PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(searchObj);
Map<String,SysPermissionDataRuleModel> ruleMap = getRuleMap();
//权限规则自定义SQL表达式
for (String c : ruleMap.keySet()) {
if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue())));
}
}
String name, type, column;
// update-begin--Author:taoyan Date:20200923 forissues/1671 如果字段加注解了@TableField(exist = false),不走DB查询-------
//定义实体字段和数据库字段名称的映射 高级查询中 只能获取实体字段 如果设置TableField注解 那么查询条件会出问题
Map<String,String> fieldColumnMap = new HashMap<>(5);
for (int i = 0; i < origDescriptors.length; i++) {
//aliasName = origDescriptors[i].getName(); mybatis 不存在实体属性 不用处理别名的情况
name = origDescriptors[i].getName();
type = origDescriptors[i].getPropertyType().toString();
try {
if (judgedIsUselessField(name)|| !PropertyUtils.isReadable(searchObj, name)) {
continue;
}
Object value = PropertyUtils.getSimpleProperty(searchObj, name);
column = ReflectHelper.getTableFieldName(searchObj.getClass(), name);
if(column==null){
//column为null只有一种情况 那就是 添加了注解@TableField(exist = false) 后续都不用处理了
continue;
}
fieldColumnMap.put(name,column);
//数据权限查询
if(ruleMap.containsKey(name)) {
addRuleToQueryWrapper(ruleMap.get(name), column, origDescriptors[i].getPropertyType(), queryWrapper);
}
//区间查询
doIntervalQuery(queryWrapper, parameterMap, type, name, column);
//判断单值 参数带不同标识字符串 走不同的查询
//TODO 这种前后带逗号的支持分割后模糊查询(多选字段查询生效) 示例:,1,3,
if (null != value && value.toString().startsWith(COMMA) && value.toString().endsWith(COMMA)) {
String multiLikeval = value.toString().replace(",,", COMMA);
String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(COMMA);
final String field = oConvertUtils.camelToUnderline(column);
if(vals.length>1) {
queryWrapper.and(j -> {
log.info("---查询过滤器Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]);
j = j.like(field,vals[0]);
for (int k=1;k<vals.length;k++) {
j = j.or().like(field,vals[k]);
log.info("---查询过滤器Query规则 .or()---field:{}, rule:{}, value:{}", field, "like", vals[k]);
}
//return j;
});
}else {
log.info("---查询过滤器Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]);
queryWrapper.and(j -> j.like(field,vals[0]));
}
}else {
//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)) {
// 可以设置左右模糊或全模糊,因人而异
//rule = QueryRuleEnum.LIKE;
//}
// add -end 添加判断为字符串时设为全模糊查询
addEasyQuery(queryWrapper, column, rule, value);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
// 排序逻辑 处理
doMultiFieldsOrder(queryWrapper, parameterMap, fieldColumnMap);
//高级查询
doSuperQuery(queryWrapper, parameterMap, fieldColumnMap);
// update-end--Author:taoyan Date:20200923 forissues/1671 如果字段加注解了@TableField(exist = false),不走DB查询-------
}
/**
* 区间查询
* @param queryWrapper query对象
* @param parameterMap 参数map
* @param type 字段类型
* @param filedName 字段名称
* @param columnName 列名称
*/
private static void doIntervalQuery(QueryWrapper<?> queryWrapper, Map<String, String[]> parameterMap, String type, String filedName, String columnName) throws ParseException {
// 添加 判断是否有区间值
String endValue = null,beginValue = null;
if (parameterMap != null && parameterMap.containsKey(filedName + BEGIN)) {
beginValue = parameterMap.get(filedName + BEGIN)[0].trim();
addQueryByRule(queryWrapper, columnName, type, beginValue, QueryRuleEnum.GE);
}
if (parameterMap != null && parameterMap.containsKey(filedName + END)) {
endValue = parameterMap.get(filedName + END)[0].trim();
addQueryByRule(queryWrapper, columnName, type, endValue, QueryRuleEnum.LE);
}
//多值查询
if (parameterMap != null && parameterMap.containsKey(filedName + MULTI)) {
endValue = parameterMap.get(filedName + MULTI)[0].trim();
addQueryByRule(queryWrapper, columnName.replace(MULTI,""), type, endValue, QueryRuleEnum.IN);
}
}
private static void doMultiFieldsOrder(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap, Map<String,String> fieldColumnMap) {
Set<String> allFields = fieldColumnMap.keySet();
String column=null,order=null;
if(parameterMap!=null&& parameterMap.containsKey(ORDER_COLUMN)) {
column = parameterMap.get(ORDER_COLUMN)[0];
}
if(parameterMap!=null&& parameterMap.containsKey(ORDER_TYPE)) {
order = parameterMap.get(ORDER_TYPE)[0];
}
log.debug("排序规则>>列:" + column + ",排序方式:" + order);
//update-begin-author:scott date:2022-11-07 for:避免用户自定义表无默认字段{创建时间},导致排序报错
//TODO 避免用户自定义表无默认字段创建时间,导致排序报错
if(DataBaseConstant.CREATE_TIME.equals(column) && !fieldColumnMap.containsKey(DataBaseConstant.CREATE_TIME)){
column = "id";
log.warn("检测到实体里没有字段createTime改成采用ID排序");
}
//update-end-author:scott date:2022-11-07 for:避免用户自定义表无默认字段{创建时间},导致排序报错
if (oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
//字典字段,去掉字典翻译文本后缀
if(column.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) {
column = column.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX));
}
//update-begin-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时使用SQL注入生效
//判断column是不是当前实体的
log.debug("当前字段有:"+ allFields);
if (!allColumnExist(column, allFields)) {
throw new JeecgBootException("请注意,将要排序的列字段不存在:" + column);
}
//update-end-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时使用SQL注入生效
//update-begin-author:scott date:2022-10-10 for:【jeecg-boot/issues/I5FJU6】doMultiFieldsOrder() 多字段排序方法存在问题
//多字段排序方法没有读取 MybatisPlus 注解 @TableField 里 value 的值
if (column.contains(",")) {
List<String> columnList = Arrays.asList(column.split(","));
String columnStrNew = columnList.stream().map(c -> fieldColumnMap.get(c)).collect(Collectors.joining(","));
if (oConvertUtils.isNotEmpty(columnStrNew)) {
column = columnStrNew;
}
}else{
column = fieldColumnMap.get(column);
}
//update-end-author:scott date:2022-10-10 for:【jeecg-boot/issues/I5FJU6】doMultiFieldsOrder() 多字段排序方法存在问题
//SQL注入check
SqlInjectionUtil.filterContentMulti(column);
//update-begin--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
// 排序规则修改
// 将现有排序 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1,column2 desc"
// 修改为 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1 desc,column2 desc"
if (order.toUpperCase().indexOf(ORDER_TYPE_ASC)>=0) {
queryWrapper.orderByAsc(SqlInjectionUtil.getSqlInjectSortFields(column.split(",")));
} else {
queryWrapper.orderByDesc(SqlInjectionUtil.getSqlInjectSortFields(column.split(",")));
}
//update-end--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
}
}
//update-begin-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时使用SQL注入生效
/**
* 多字段排序 判断所传字段是否存在
* @return
*/
private static boolean allColumnExist(String columnStr, Set<String> allFields){
boolean exist = true;
if(columnStr.indexOf(COMMA)>=0){
String[] arr = columnStr.split(COMMA);
for(String column: arr){
if(!allFields.contains(column)){
exist = false;
break;
}
}
}else{
exist = allFields.contains(columnStr);
}
return exist;
}
//update-end-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时使用SQL注入生效
/**
* 高级查询
* @param queryWrapper 查询对象
* @param parameterMap 参数对象
* @param fieldColumnMap 实体字段和数据库列对应的map
*/
private static void doSuperQuery(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap, Map<String,String> fieldColumnMap) {
if(parameterMap!=null&& parameterMap.containsKey(SUPER_QUERY_PARAMS)){
String superQueryParams = parameterMap.get(SUPER_QUERY_PARAMS)[0];
String superQueryMatchType = parameterMap.get(SUPER_QUERY_MATCH_TYPE) != null ? parameterMap.get(SUPER_QUERY_MATCH_TYPE)[0] : MatchTypeEnum.AND.getValue();
MatchTypeEnum matchType = MatchTypeEnum.getByValue(superQueryMatchType);
// update-begin--Author:sunjianlei Date:20200325 for高级查询的条件要用括号括起来防止和用户的其他条件冲突 -------
try {
superQueryParams = URLDecoder.decode(superQueryParams, "UTF-8");
List<QueryCondition> conditions = JSON.parseArray(superQueryParams, QueryCondition.class);
if (conditions == null || conditions.size() == 0) {
return;
}
// update-begin-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
List<QueryCondition> filterConditions = conditions.stream().filter(
rule -> oConvertUtils.isNotEmpty(rule.getField())
&& oConvertUtils.isNotEmpty(rule.getRule())
&& oConvertUtils.isNotEmpty(rule.getVal())
).collect(Collectors.toList());
if (filterConditions.size() == 0) {
return;
}
// update-end-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
log.debug("---高级查询参数-->" + filterConditions);
queryWrapper.and(andWrapper -> {
for (int i = 0; i < filterConditions.size(); i++) {
QueryCondition rule = filterConditions.get(i);
if (oConvertUtils.isNotEmpty(rule.getField())
&& oConvertUtils.isNotEmpty(rule.getRule())
&& oConvertUtils.isNotEmpty(rule.getVal())) {
log.debug("SuperQuery ==> " + rule.toString());
//update-begin-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错
Object queryValue = rule.getVal();
if("date".equals(rule.getType())){
queryValue = DateUtils.str2Date(rule.getVal(),DateUtils.date_sdf.get());
}else if("datetime".equals(rule.getType())){
queryValue = DateUtils.str2Date(rule.getVal(), DateUtils.datetimeFormat.get());
}
// update-begin--author:sunjianlei date:20210702 for【/issues/I3VR8E】高级查询没有类型转换查询参数都是字符串类型 ----
String dbType = rule.getDbType();
if (oConvertUtils.isNotEmpty(dbType)) {
try {
String valueStr = String.valueOf(queryValue);
switch (dbType.toLowerCase().trim()) {
case "int":
queryValue = Integer.parseInt(valueStr);
break;
case "bigdecimal":
queryValue = new BigDecimal(valueStr);
break;
case "short":
queryValue = Short.parseShort(valueStr);
break;
case "long":
queryValue = Long.parseLong(valueStr);
break;
case "float":
queryValue = Float.parseFloat(valueStr);
break;
case "double":
queryValue = Double.parseDouble(valueStr);
break;
case "boolean":
queryValue = Boolean.parseBoolean(valueStr);
break;
default:
}
} catch (Exception e) {
log.error("高级查询值转换失败:", e);
}
}
// update-begin--author:sunjianlei date:20210702 for【/issues/I3VR8E】高级查询没有类型转换查询参数都是字符串类型 ----
addEasyQuery(andWrapper, fieldColumnMap.get(rule.getField()), QueryRuleEnum.getByValue(rule.getRule()), queryValue);
//update-end-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错
// 如果拼接方式是OR就拼接OR
if (MatchTypeEnum.OR == matchType && i < (filterConditions.size() - 1)) {
andWrapper.or();
}
}
}
//return andWrapper;
});
} catch (UnsupportedEncodingException e) {
log.error("--高级查询参数转码失败:" + superQueryParams, e);
} catch (Exception e) {
log.error("--高级查询拼接失败:" + e.getMessage());
e.printStackTrace();
}
// update-end--Author:sunjianlei Date:20200325 for高级查询的条件要用括号括起来防止和用户的其他条件冲突 -------
}
//log.info(" superQuery getCustomSqlSegment: "+ queryWrapper.getCustomSqlSegment());
}
/**
* 根据所传的值 转化成对应的比较方式
* 支持><= like in !
* @param value
* @return
*/
public static QueryRuleEnum convert2Rule(Object value) {
// 避免空数据
// update-begin-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
if (value == null) {
return QueryRuleEnum.EQ;
}
String val = (value + "").toString().trim();
if (val.length() == 0) {
return QueryRuleEnum.EQ;
}
// update-end-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
QueryRuleEnum rule =null;
//update-begin--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284-------------------
//TODO 此处规则,只适用于 le lt ge gt
// step 2 .>= =<
int length2 = 2;
int length3 = 3;
if (rule == null && val.length() >= length3) {
if(QUERY_SEPARATE_KEYWORD.equals(val.substring(length2, length3))){
rule = QueryRuleEnum.getByValue(val.substring(0, 2));
}
}
// step 1 .> <
if (rule == null && val.length() >= length2) {
if(QUERY_SEPARATE_KEYWORD.equals(val.substring(1, length2))){
rule = QueryRuleEnum.getByValue(val.substring(0, 1));
}
}
//update-end--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284---------------------
// step 3 like
//update-begin-author:taoyan for: /issues/3382 默认带*就走模糊,但是如果只有一个*,那么走等于查询
if(rule == null && val.equals(STAR)){
rule = QueryRuleEnum.EQ;
}
//update-end-author:taoyan for: /issues/3382 默认带*就走模糊,但是如果只有一个*,那么走等于查询
if (rule == null && val.contains(STAR)) {
if (val.startsWith(STAR) && val.endsWith(STAR)) {
rule = QueryRuleEnum.LIKE;
} else if (val.startsWith(STAR)) {
rule = QueryRuleEnum.LEFT_LIKE;
} else if(val.endsWith(STAR)){
rule = QueryRuleEnum.RIGHT_LIKE;
}
}
// step 4 in
if (rule == null && val.contains(COMMA)) {
//TODO in 查询这里应该有个bug 如果一字段本身就是多选 此时用in查询 未必能查询出来
rule = QueryRuleEnum.IN;
}
// step 5 !=
if(rule == null && val.startsWith(NOT_EQUAL)){
rule = QueryRuleEnum.NE;
}
// step 6 xx+xx+xx 这种情况适用于如果想要用逗号作精确查询 但是系统默认逗号走in 所以可以用++替换【此逻辑作废】
if(rule == null && val.indexOf(QUERY_COMMA_ESCAPE)>0){
rule = QueryRuleEnum.EQ_WITH_ADD;
}
//update-begin--Author:taoyan Date:20201229 forinitQueryWrapper组装sql查询条件错误 #284---------------------
//特殊处理Oracle的表达式to_date('xxx','yyyy-MM-dd')含有逗号会被识别为in查询转为等于查询
if(rule == QueryRuleEnum.IN && val.indexOf(YYYY_MM_DD)>=0 && val.indexOf(TO_DATE)>=0){
rule = QueryRuleEnum.EQ;
}
//update-end--Author:taoyan Date:20201229 forinitQueryWrapper组装sql查询条件错误 #284---------------------
return rule != null ? rule : QueryRuleEnum.EQ;
}
/**
* 替换掉关键字字符
*
* @param rule
* @param value
* @return
*/
private static Object replaceValue(QueryRuleEnum rule, Object value) {
if (rule == null) {
return null;
}
if (! (value instanceof String)){
return value;
}
String val = (value + "").toString().trim();
//update-begin-author:taoyan date:20220302 for: 查询条件的值为等号(=bug #3443
if(QueryRuleEnum.EQ.getValue().equals(val)){
return val;
}
//update-end-author:taoyan date:20220302 for: 查询条件的值为等号(=bug #3443
if (rule == QueryRuleEnum.LIKE) {
value = val.substring(1, val.length() - 1);
//mysql 模糊查询之特殊字符下划线 _、\
value = specialStrConvert(value.toString());
} else if (rule == QueryRuleEnum.LEFT_LIKE || rule == QueryRuleEnum.NE) {
value = val.substring(1);
//mysql 模糊查询之特殊字符下划线 _、\
value = specialStrConvert(value.toString());
} else if (rule == QueryRuleEnum.RIGHT_LIKE) {
value = val.substring(0, val.length() - 1);
//mysql 模糊查询之特殊字符下划线 _、\
value = specialStrConvert(value.toString());
} else if (rule == QueryRuleEnum.IN) {
value = val.split(",");
} else if (rule == QueryRuleEnum.EQ_WITH_ADD) {
value = val.replaceAll("\\+\\+", COMMA);
}else {
//update-begin--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284-------------------
if(val.startsWith(rule.getValue())){
//TODO 此处逻辑应该注释掉-> 如果查询内容中带有查询匹配规则符号,就会被截取的(比如:>=您好)
value = val.replaceFirst(rule.getValue(),"");
}else if(val.startsWith(rule.getCondition()+QUERY_SEPARATE_KEYWORD)){
value = val.replaceFirst(rule.getCondition()+QUERY_SEPARATE_KEYWORD,"").trim();
}
//update-end--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284-------------------
}
return value;
}
private static void addQueryByRule(QueryWrapper<?> queryWrapper,String name,String type,String value,QueryRuleEnum rule) throws ParseException {
if(oConvertUtils.isNotEmpty(value)) {
//update-begin--Author:sunjianlei Date:20220104 for【JTC-409】修复逗号分割情况下没有转换类型导致类型严格的数据库查询报错 -------------------
// 针对数字类型字段,多值查询
if(value.contains(COMMA)){
Object[] temp = Arrays.stream(value.split(COMMA)).map(v -> {
try {
return QueryGenerator.parseByType(v, type, rule);
} catch (ParseException e) {
e.printStackTrace();
return v;
}
}).toArray();
addEasyQuery(queryWrapper, name, rule, temp);
return;
}
Object temp = QueryGenerator.parseByType(value, type, rule);
addEasyQuery(queryWrapper, name, rule, temp);
//update-end--Author:sunjianlei Date:20220104 for【JTC-409】修复逗号分割情况下没有转换类型导致类型严格的数据库查询报错 -------------------
}
}
/**
* 根据类型转换给定的值
* @param value
* @param type
* @param rule
* @return
* @throws ParseException
*/
private static Object parseByType(String value, String type, QueryRuleEnum rule) throws ParseException {
Object temp;
switch (type) {
case "class java.lang.Integer":
temp = Integer.parseInt(value);
break;
case "class java.math.BigDecimal":
temp = new BigDecimal(value);
break;
case "class java.lang.Short":
temp = Short.parseShort(value);
break;
case "class java.lang.Long":
temp = Long.parseLong(value);
break;
case "class java.lang.Float":
temp = Float.parseFloat(value);
break;
case "class java.lang.Double":
temp = Double.parseDouble(value);
break;
case "class java.util.Date":
temp = getDateQueryByRule(value, rule);
break;
default:
temp = value;
break;
}
return temp;
}
/**
* 获取日期类型的值
* @param value
* @param rule
* @return
* @throws ParseException
*/
private static Date getDateQueryByRule(String value,QueryRuleEnum rule) throws ParseException {
Date date = null;
int length = 10;
if(value.length()==length) {
if(rule==QueryRuleEnum.GE) {
//比较大于
date = getTime().parse(value + " 00:00:00");
}else if(rule==QueryRuleEnum.LE) {
//比较小于
date = getTime().parse(value + " 23:59:59");
}
//TODO 日期类型比较特殊 可能oracle下不一定好使
}
if(date==null) {
date = getTime().parse(value);
}
return date;
}
/**
* 根据规则走不同的查询
* @param queryWrapper QueryWrapper
* @param name 字段名字
* @param rule 查询规则
* @param value 查询条件值
*/
public static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
if (name==null || value == null || rule == null || oConvertUtils.isEmpty(value)) {
return;
}
name = oConvertUtils.camelToUnderline(name);
log.debug("---高级查询 Query规则---field:{} , rule:{} , value:{}",name,rule.getValue(),value);
switch (rule) {
case GT:
queryWrapper.gt(name, value);
break;
case GE:
queryWrapper.ge(name, value);
break;
case LT:
queryWrapper.lt(name, value);
break;
case LE:
queryWrapper.le(name, value);
break;
case EQ:
case EQ_WITH_ADD:
queryWrapper.eq(name, value);
break;
case NE:
queryWrapper.ne(name, value);
break;
case IN:
if(value instanceof String) {
queryWrapper.in(name, (Object[])value.toString().split(COMMA));
}else if(value instanceof String[]) {
queryWrapper.in(name, (Object[]) value);
}
//update-begin-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
else if(value.getClass().isArray()) {
queryWrapper.in(name, (Object[])value);
}else {
queryWrapper.in(name, value);
}
//update-end-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
break;
case LIKE:
queryWrapper.like(name, value);
break;
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;
}
}
/**
*
* @param name
* @return
*/
private static boolean judgedIsUselessField(String name) {
return "class".equals(name) || "ids".equals(name)
|| "page".equals(name) || "rows".equals(name)
|| "sort".equals(name) || "order".equals(name);
}
/**
* 获取请求对应的数据权限规则 TODO 相同列权限多个 有问题
* @return
*/
public static Map<String, SysPermissionDataRuleModel> getRuleMap() {
Map<String, SysPermissionDataRuleModel> ruleMap = new HashMap<>(5);
List<SysPermissionDataRuleModel> list = null;
//update-begin-author:taoyan date:2023-6-1 for:QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
try {
list = JeecgDataAutorUtils.loadDataSearchConditon();
}catch (Exception e){
log.error("根据request对象获取权限数据失败可能是定时任务中执行的。", e);
}
//update-end-author:taoyan date:2023-6-1 for:QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
if(list != null&&list.size()>0){
if(list.get(0)==null){
return ruleMap;
}
for (SysPermissionDataRuleModel rule : list) {
String column = rule.getRuleColumn();
if(QueryRuleEnum.SQL_RULES.getValue().equals(rule.getRuleConditions())) {
column = SQL_RULES_COLUMN+rule.getId();
}
ruleMap.put(column, rule);
}
}
return ruleMap;
}
private static void addRuleToQueryWrapper(SysPermissionDataRuleModel dataRule, String name, Class propertyType, QueryWrapper<?> queryWrapper) {
QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
if(rule.equals(QueryRuleEnum.IN) && ! propertyType.equals(String.class)) {
String[] values = dataRule.getRuleValue().split(",");
Object[] objs = new Object[values.length];
for (int i = 0; i < values.length; i++) {
objs[i] = NumberUtils.parseNumber(values[i], propertyType);
}
addEasyQuery(queryWrapper, name, rule, objs);
}else {
if (propertyType.equals(String.class)) {
addEasyQuery(queryWrapper, name, rule, converRuleValue(dataRule.getRuleValue()));
}else if (propertyType.equals(Date.class)) {
String dateStr =converRuleValue(dataRule.getRuleValue());
int length = 10;
if(dateStr.length()==length){
addEasyQuery(queryWrapper, name, rule, DateUtils.str2Date(dateStr,DateUtils.date_sdf.get()));
}else{
addEasyQuery(queryWrapper, name, rule, DateUtils.str2Date(dateStr,DateUtils.datetimeFormat.get()));
}
}else {
addEasyQuery(queryWrapper, name, rule, NumberUtils.parseNumber(dataRule.getRuleValue(), propertyType));
}
}
}
public static String converRuleValue(String ruleValue) {
String value = JwtUtil.getUserSystemData(ruleValue,null);
return value!= null ? value : ruleValue;
}
/**
* @author: scott
* @Description: 去掉值前后单引号
* @date: 2020/3/19 21:26
* @param ruleValue:
* @Return: java.lang.String
*/
public static String trimSingleQuote(String ruleValue) {
if (oConvertUtils.isEmpty(ruleValue)) {
return "";
}
if (ruleValue.startsWith(QueryGenerator.SQL_SQ)) {
ruleValue = ruleValue.substring(1);
}
if (ruleValue.endsWith(QueryGenerator.SQL_SQ)) {
ruleValue = ruleValue.substring(0, ruleValue.length() - 1);
}
return ruleValue;
}
public static String getSqlRuleValue(String sqlRule){
try {
Set<String> varParams = getSqlRuleParams(sqlRule);
for(String var:varParams){
String tempValue = converRuleValue(var);
sqlRule = sqlRule.replace("#{"+var+"}",tempValue);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return sqlRule;
}
/**
* 获取sql中的#{key} 这个key组成的set
*/
public static Set<String> getSqlRuleParams(String sql) {
if(oConvertUtils.isEmpty(sql)){
return null;
}
Set<String> varParams = new HashSet<String>();
String regex = "\\#\\{\\w+\\}";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(sql);
while(m.find()){
String var = m.group();
varParams.add(var.substring(var.indexOf("{")+1,var.indexOf("}")));
}
return varParams;
}
/**
* 获取查询条件
* @param field
* @param alias
* @param value
* @param isString
* @return
*/
public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString) {
return SqlConcatUtil.getSingleQueryConditionSql(field, alias, value, isString,null);
}
/**
* 根据权限相关配置生成相关的SQL 语句
* @param clazz
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static String installAuthJdbc(Class<?> clazz) {
StringBuffer sb = new StringBuffer();
//权限查询
Map<String,SysPermissionDataRuleModel> ruleMap = getRuleMap();
PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(clazz);
String sqlAnd = " and ";
for (String c : ruleMap.keySet()) {
if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
sb.append(sqlAnd+getSqlRuleValue(ruleMap.get(c).getRuleValue()));
}
}
String name, column;
for (int i = 0; i < origDescriptors.length; i++) {
name = origDescriptors[i].getName();
if (judgedIsUselessField(name)) {
continue;
}
if(ruleMap.containsKey(name)) {
column = ReflectHelper.getTableFieldName(clazz, name);
if(column==null){
continue;
}
SysPermissionDataRuleModel dataRule = ruleMap.get(name);
QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
Class propType = origDescriptors[i].getPropertyType();
boolean isString = propType.equals(String.class);
Object value;
//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);
}
String filedSql = SqlConcatUtil.getSingleSqlByRule(rule, oConvertUtils.camelToUnderline(column), value,isString);
sb.append(sqlAnd+filedSql);
}
}
log.info("query auth sql is:"+sb.toString());
return sb.toString();
}
/**
* 根据权限相关配置 组装mp需要的权限
* @param queryWrapper
* @param clazz
* @return
*/
public static void installAuthMplus(QueryWrapper<?> queryWrapper,Class<?> clazz) {
//权限查询
Map<String,SysPermissionDataRuleModel> ruleMap = getRuleMap();
PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(clazz);
for (String c : ruleMap.keySet()) {
if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue())));
}
}
String name, column;
for (int i = 0; i < origDescriptors.length; i++) {
name = origDescriptors[i].getName();
if (judgedIsUselessField(name)) {
continue;
}
column = ReflectHelper.getTableFieldName(clazz, name);
if(column==null){
continue;
}
if(ruleMap.containsKey(name)) {
addRuleToQueryWrapper(ruleMap.get(name), column, origDescriptors[i].getPropertyType(), queryWrapper);
}
}
}
/**
* 转换sql中的系统变量
* @param sql
* @return
*/
public static String convertSystemVariables(String sql){
return getSqlRuleValue(sql);
}
/**
* 获取系统数据库类型
*/
private static String getDbType(){
return CommonUtils.getDatabaseType();
}
/**
* mysql 模糊查询之特殊字符下划线 _、\
*
* @param value:
* @Return: java.lang.String
*/
private static String specialStrConvert(String value) {
if (DataBaseConstant.DB_TYPE_MYSQL.equals(getDbType()) || DataBaseConstant.DB_TYPE_MARIADB.equals(getDbType())) {
String[] specialStr = QueryGenerator.LIKE_MYSQL_SPECIAL_STRS.split(",");
for (String str : specialStr) {
if (value.indexOf(str) !=-1) {
value = value.replace(str, "\\" + str);
}
}
}
return value;
}
}

View File

@ -0,0 +1,114 @@
package org.jeecg.common.system.query;
import org.jeecg.common.util.oConvertUtils;
/**
* Query 规则 常量
* @Author Scott
* @Date 2019年02月14日
*/
public enum QueryRuleEnum {
/**查询规则 大于*/
GT(">","gt","大于"),
/**查询规则 大于等于*/
GE(">=","ge","大于等于"),
/**查询规则 小于*/
LT("<","lt","小于"),
/**查询规则 小于等于*/
LE("<=","le","小于等于"),
/**查询规则 等于*/
EQ("=","eq","等于"),
/**查询规则 不等于*/
NE("!=","ne","不等于"),
/**查询规则 包含*/
IN("IN","in","包含"),
/**查询规则 全模糊*/
LIKE("LIKE","like","全模糊"),
/**查询规则 不模糊包含*/
NOT_LIKE("NOT_LIKE","not_like","不模糊包含"),
/**查询规则 左模糊*/
LEFT_LIKE("LEFT_LIKE","left_like","左模糊"),
/**查询规则 右模糊*/
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","值为空"),
/** 值不为空 */
NOT_EMPTY("NOT_EMPTY","not_empty","值不为空"),
/**查询规则 不包含*/
NOT_IN("NOT_IN","not_in","不包含"),
/**查询规则 多词匹配*/
ELE_MATCH("ELE_MATCH","elemMatch","多词匹配"),
/**查询规则 范围查询*/
RANGE("RANGE","range","范围查询"),
/**查询规则 不在范围内查询*/
NOT_RANGE("NOT_RANGE","not_range","不在范围查询"),
/** 自定义mongodb查询语句 */
CUSTOM_MONGODB("CUSTOM_MONGODB","custom_mongodb","自定义mongodb查询语句");
// ------- 当前表单设计器内专用 -------
private String value;
private String condition;
private String msg;
QueryRuleEnum(String value, String condition, String msg){
this.value = value;
this.condition = condition;
this.msg = msg;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getCondition() {
return condition;
}
public void setCondition(String condition) {
this.condition = condition;
}
public static QueryRuleEnum getByValue(String value){
if(oConvertUtils.isEmpty(value)) {
return null;
}
for(QueryRuleEnum val :values()){
if (val.getValue().equals(value) || val.getCondition().equalsIgnoreCase(value)){
return val;
}
}
return null;
}
}

View File

@ -0,0 +1,106 @@
package org.jeecg.common.system.util;
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
import org.jeecg.common.system.vo.SysUserCacheInfo;
import org.jeecg.common.util.SpringContextUtils;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName: JeecgDataAutorUtils
* @Description: 数据权限查询规则容器工具类
* @Author: 张代浩
* @Date: 2012-12-15 下午11:27:39
*
*/
public class JeecgDataAutorUtils {
public static final String MENU_DATA_AUTHOR_RULES = "MENU_DATA_AUTHOR_RULES";
public static final String MENU_DATA_AUTHOR_RULE_SQL = "MENU_DATA_AUTHOR_RULE_SQL";
public static final String SYS_USER_INFO = "SYS_USER_INFO";
/**
* 往链接请求里面,传入数据查询条件
*
* @param request
* @param dataRules
*/
public static synchronized void installDataSearchConditon(HttpServletRequest request, List<SysPermissionDataRuleModel> dataRules) {
@SuppressWarnings("unchecked")
// 1.先从request获取MENU_DATA_AUTHOR_RULES如果存则获取到LIST
List<SysPermissionDataRuleModel> list = (List<SysPermissionDataRuleModel>)loadDataSearchConditon();
if (list==null) {
// 2.如果不存在则new一个list
list = new ArrayList<SysPermissionDataRuleModel>();
}
for (SysPermissionDataRuleModel tsDataRule : dataRules) {
list.add(tsDataRule);
}
// 3.往list里面增量存指
request.setAttribute(MENU_DATA_AUTHOR_RULES, list);
}
/**
* 获取请求对应的数据权限规则
*
* @return
*/
@SuppressWarnings("unchecked")
public static synchronized List<SysPermissionDataRuleModel> loadDataSearchConditon() {
return (List<SysPermissionDataRuleModel>) SpringContextUtils.getHttpServletRequest().getAttribute(MENU_DATA_AUTHOR_RULES);
}
/**
* 获取请求对应的数据权限SQL
*
* @return
*/
public static synchronized String loadDataSearchConditonSqlString() {
return (String) SpringContextUtils.getHttpServletRequest().getAttribute(MENU_DATA_AUTHOR_RULE_SQL);
}
/**
* 往链接请求里面,传入数据查询条件
*
* @param request
* @param sql
*/
public static synchronized void installDataSearchConditon(HttpServletRequest request, String sql) {
String ruleSql = (String) loadDataSearchConditonSqlString();
if (!StringUtils.hasText(ruleSql)) {
request.setAttribute(MENU_DATA_AUTHOR_RULE_SQL,sql);
}
}
/**
* 将用户信息存到request
* @param request
* @param userinfo
*/
public static synchronized void installUserInfo(HttpServletRequest request, SysUserCacheInfo userinfo) {
request.setAttribute(SYS_USER_INFO, userinfo);
}
/**
* 将用户信息存到request
* @param userinfo
*/
public static synchronized void installUserInfo(SysUserCacheInfo userinfo) {
SpringContextUtils.getHttpServletRequest().setAttribute(SYS_USER_INFO, userinfo);
}
/**
* 从request获取用户信息
* @return
*/
public static synchronized SysUserCacheInfo loadUserInfo() {
return (SysUserCacheInfo) SpringContextUtils.getHttpServletRequest().getAttribute(SYS_USER_INFO);
}
}

View File

@ -0,0 +1,301 @@
package org.jeecg.common.system.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.constant.TenantConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.vo.SysUserCacheInfo;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
/**
* @Author Scott
* @Date 2018-07-12 14:23
* @Desc JWT工具类
**/
@Slf4j
public class JwtUtil {
/**Token有效期为7天Token在reids中缓存时间为两倍*/
public static final long EXPIRE_TIME = (7 * 12) * 60 * 60 * 1000;
static final String WELL_NUMBER = SymbolConstant.WELL_NUMBER + SymbolConstant.LEFT_CURLY_BRACKET;
/**
*
* @param response
* @param code
* @param errorMsg
*/
public static void responseError(ServletResponse response, Integer code, String errorMsg) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// issues/I4YH95浏览器显示乱码问题
httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
Result jsonResult = new Result(code, errorMsg);
jsonResult.setSuccess(false);
OutputStream os = null;
try {
os = httpServletResponse.getOutputStream();
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setStatus(code);
os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 校验token是否正确
*
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
try {
// 根据密码生成JWT效验器
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
// 效验TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息无需secret解密也能获得
*
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名,5min后过期
*
* @param username 用户名
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(String username, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
}
/**
* 根据request中的token获取用户账号
*
* @param request
* @return
* @throws JeecgBootException
*/
public static String getUserNameByToken(HttpServletRequest request) throws JeecgBootException {
String accessToken = request.getHeader("X-Access-Token");
String username = getUsername(accessToken);
if (oConvertUtils.isEmpty(username)) {
throw new JeecgBootException("未获取到用户");
}
return username;
}
/**
* 从session中获取变量
* @param key
* @return
*/
public static String getSessionData(String key) {
//${myVar}%
//得到${} 后面的值
String moshi = "";
String wellNumber = WELL_NUMBER;
if(key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET)!=-1){
moshi = key.substring(key.indexOf("}")+1);
}
String returnValue = null;
if (key.contains(wellNumber)) {
key = key.substring(2,key.indexOf("}"));
}
if (oConvertUtils.isNotEmpty(key)) {
HttpSession session = SpringContextUtils.getHttpServletRequest().getSession();
returnValue = (String) session.getAttribute(key);
}
//结果加上${} 后面的值
if(returnValue!=null){returnValue = returnValue + moshi;}
return returnValue;
}
/**
* 从当前用户中获取变量
* @param key
* @param user
* @return
*/
public static String getUserSystemData(String key, SysUserCacheInfo user) {
//1.优先获取 SysUserCacheInfo
if(user==null) {
try {
user = JeecgDataAutorUtils.loadUserInfo();
} catch (Exception e) {
log.warn("获取用户信息异常:" + e.getMessage());
}
}
//2.通过shiro获取登录用户信息
LoginUser sysUser = null;
try {
sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
} catch (Exception e) {
log.warn("SecurityUtils.getSubject() 获取用户信息异常:" + e.getMessage());
}
//#{sys_user_code}%
String moshi = "";
String wellNumber = WELL_NUMBER;
if(key.indexOf(SymbolConstant.RIGHT_CURLY_BRACKET)!=-1){
moshi = key.substring(key.indexOf("}")+1);
}
String returnValue = null;
//针对特殊标示处理#{sysOrgCode},判断替换
if (key.contains(wellNumber)) {
key = key.substring(2,key.indexOf("}"));
} else {
key = key;
}
//替换为当前系统时间(年月日)
if (key.equals(DataBaseConstant.SYS_DATE)|| key.toLowerCase().equals(DataBaseConstant.SYS_DATE_TABLE)) {
returnValue = DateUtils.formatDate();
}
//替换为当前系统时间(年月日时分秒)
else if (key.equals(DataBaseConstant.SYS_TIME)|| key.toLowerCase().equals(DataBaseConstant.SYS_TIME_TABLE)) {
returnValue = DateUtils.now();
}
//流程状态默认值(默认未发起)
else if (key.equals(DataBaseConstant.BPM_STATUS)|| key.toLowerCase().equals(DataBaseConstant.BPM_STATUS_TABLE)) {
returnValue = "1";
}
//后台任务获取用户信息异常,导致程序中断
if(sysUser==null && user==null){
return null;
}
//替换为系统登录用户帐号
if (key.equals(DataBaseConstant.SYS_USER_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_USER_CODE_TABLE)) {
if(user==null) {
returnValue = sysUser.getUsername();
}else {
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) {
returnValue = sysUser.getRealname();
}else {
returnValue = user.getSysUserName();
}
}
//替换为系统用户登录所使用的机构编码
else if (key.equals(DataBaseConstant.SYS_ORG_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_ORG_CODE_TABLE)) {
if(user==null) {
returnValue = sysUser.getOrgCode();
}else {
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){
//TODO 暂时使用用户登录部门,存在逻辑缺陷,不是用户所拥有的部门
returnValue = sysUser.getOrgCode();
}else{
if(user.isOneDepart()) {
returnValue = user.getSysMultiOrgCode().get(0);
}else {
returnValue = Joiner.on(",").join(user.getSysMultiOrgCode());
}
}
}
// 替换为当前登录用户的角色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 {
returnValue = SpringContextUtils.getHttpServletRequest().getHeader(CommonConstant.TENANT_ID);
} catch (Exception e) {
log.warn("获取系统租户异常:" + e.getMessage());
}
}
//update-end-author:taoyan date:20210330 for:多租户ID作为系统变量
if(returnValue!=null){returnValue = returnValue + moshi;}
return returnValue;
}
// public static void main(String[] args) {
// String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjUzMzY1MTMsInVzZXJuYW1lIjoiYWRtaW4ifQ.xjhud_tWCNYBOg_aRlMgOdlZoWFFKB_givNElHNw3X0";
// System.out.println(JwtUtil.getUsername(token));
// }
}

View File

@ -0,0 +1,136 @@
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;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 资源加载工具类
* @Author taoYan
* @Date 2022/7/8 10:40
**/
@Slf4j
public class ResourceUtil {
/**
* 枚举字典数据
*/
private final static Map<String, List<DictModel>> enumDictData = new HashMap<>(5);
/**
* 所有java类
*/
private final static String CLASS_PATTERN="/**/*.class";
/**
* 所有枚举java类
*/
private final static String CLASS_ENUM_PATTERN="/**/*Enum.class";
/**
* 包路径 org.jeecg
*/
private final static String BASE_PACKAGE = "org.jeecg";
/**
* 枚举类中获取字典数据的方法名
*/
private final static String METHOD_NAME = "getDictList";
/**
* 获取枚举类对应的字典数据 SysDictServiceImpl#queryAllDictItems()
* @return
*/
public static Map<String, List<DictModel>> getEnumDictData(){
if(enumDictData.keySet().size()>0){
return enumDictData;
}
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + CLASS_ENUM_PATTERN;
try {
Resource[] resources = resourcePatternResolver.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
MetadataReader reader = readerFactory.getMetadataReader(resource);
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
if (enumDict != null) {
EnumDict annotation = clazz.getAnnotation(EnumDict.class);
String key = annotation.value();
if(oConvertUtils.isNotEmpty(key)){
List<DictModel> list = (List<DictModel>) clazz.getDeclaredMethod(METHOD_NAME).invoke(null);
enumDictData.put(key, list);
}
}
}
}catch (Exception e){
log.error("获取枚举类字典数据异常", e.getMessage());
// e.printStackTrace();
}
return enumDictData;
}
/**
* 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List)
* @param dictCodeList
* @param keys
* @return
*/
public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys){
if(enumDictData.keySet().size()==0){
getEnumDictData();
}
Map<String, List<DictModel>> map = new HashMap<>();
for (String code : enumDictData.keySet()) {
if(dictCodeList.indexOf(code)>=0){
List<DictModel> dictItemList = enumDictData.get(code);
for(DictModel dm: dictItemList){
String value = dm.getValue();
if(keys.indexOf(value)>=0){
List<DictModel> list = new ArrayList<>();
list.add(new DictModel(value, dm.getText()));
map.put(code,list);
break;
}
}
}
}
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

@ -0,0 +1,243 @@
package org.jeecg.common.system.util;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.oConvertUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 查询过滤器SQL拼接写法拆成独立工具类
* @author:qinfeng
* @date 20230904
*/
@Slf4j
public class SqlConcatUtil {
/**
* 获取单个查询条件的值
* @param rule
* @param field
* @param value
* @param isString
* @return
*/
public static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString) {
return getSingleSqlByRule(rule, field, value, isString, null);
}
/**
* 报表获取查询条件 支持多数据源
* @param field
* @param alias
* @param value
* @param isString
* @param dataBaseType
* @return
*/
public static String getSingleQueryConditionSql(String field,String alias,Object value,boolean isString, String dataBaseType) {
if (value == null) {
return "";
}
field = alias+oConvertUtils.camelToUnderline(field);
QueryRuleEnum rule = QueryGenerator.convert2Rule(value);
return getSingleSqlByRule(rule, field, value, isString, dataBaseType);
}
/**
* 获取单个查询条件的值
* @param rule
* @param field
* @param value
* @param isString
* @param dataBaseType
* @return
*/
private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString, String dataBaseType) {
String res = "";
switch (rule) {
case GT:
res =field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case GE:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case LT:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case LE:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case EQ:
res = field+rule.getValue()+getFieldConditionValue(value, isString, dataBaseType);
break;
case EQ_WITH_ADD:
res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
break;
case NE:
res = field+" <> "+getFieldConditionValue(value, isString, dataBaseType);
break;
case IN:
res = field + " in "+getInConditionValue(value, isString);
break;
case LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LIKE);
break;
case LEFT_LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.LEFT_LIKE);
break;
case RIGHT_LIKE:
res = field + " like "+getLikeConditionValue(value, QueryRuleEnum.RIGHT_LIKE);
break;
default:
res = field+" = "+getFieldConditionValue(value, isString, dataBaseType);
break;
}
return res;
}
/**
* 获取查询条件的值
* @param value
* @param isString
* @param dataBaseType
* @return
*/
private static String getFieldConditionValue(Object value,boolean isString, String dataBaseType) {
String str = value.toString().trim();
if(str.startsWith(SymbolConstant.EXCLAMATORY_MARK)) {
str = str.substring(1);
}else if(str.startsWith(QueryRuleEnum.GE.getValue())) {
str = str.substring(2);
}else if(str.startsWith(QueryRuleEnum.LE.getValue())) {
str = str.substring(2);
}else if(str.startsWith(QueryRuleEnum.GT.getValue())) {
str = str.substring(1);
}else if(str.startsWith(QueryRuleEnum.LT.getValue())) {
str = str.substring(1);
}else if(str.indexOf(QueryGenerator.QUERY_COMMA_ESCAPE)>0) {
str = str.replaceAll("\\+\\+", SymbolConstant.COMMA);
}
if(dataBaseType==null){
dataBaseType = getDbType();
}
if(isString) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType)){
return " N'"+str+"' ";
}else{
return " '"+str+"' ";
}
}else {
// 如果不是字符串 有一种特殊情况 popup调用都走这个逻辑 参数传递的可能是“admin”这种格式的
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(dataBaseType) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return " N"+str;
}
return value.toString();
}
}
private static String getInConditionValue(Object value,boolean isString) {
//update-begin-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
String[] temp = value.toString().split(",");
if(temp.length==0){
return "('')";
}
if(isString) {
List<String> res = new ArrayList<>();
for (String string : temp) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
res.add("N'"+string+"'");
}else{
res.add("'"+string+"'");
}
}
return "("+String.join("," ,res)+")";
}else {
return "("+value.toString()+")";
}
//update-end-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
}
/**
* 先根据值判断 走左模糊还是右模糊
* 最后如果值不带任何标识(*或者%)则再根据ruleEnum判断
* @param value
* @param ruleEnum
* @return
*/
private static String getLikeConditionValue(Object value, QueryRuleEnum ruleEnum) {
String str = value.toString().trim();
if(str.startsWith(SymbolConstant.ASTERISK) && str.endsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'%"+str.substring(1,str.length()-1)+"%'";
}else{
return "'%"+str.substring(1,str.length()-1)+"%'";
}
}else if(str.startsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'%"+str.substring(1)+"'";
}else{
return "'%"+str.substring(1)+"'";
}
}else if(str.endsWith(SymbolConstant.ASTERISK)) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
return "N'"+str.substring(0,str.length()-1)+"%'";
}else{
return "'"+str.substring(0,str.length()-1)+"%'";
}
}else {
if(str.indexOf(SymbolConstant.PERCENT_SIGN)>=0) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return "N"+str;
}else{
return "N"+"'"+str+"'";
}
}else{
if(str.startsWith(SymbolConstant.SINGLE_QUOTATION_MARK) && str.endsWith(SymbolConstant.SINGLE_QUOTATION_MARK)){
return str;
}else{
return "'"+str+"'";
}
}
}else {
//update-begin-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
// 走到这里说明 value不带有任何模糊查询的标识(*或者%)
if (ruleEnum == QueryRuleEnum.LEFT_LIKE) {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'%" + str + "'";
} else {
return "'%" + str + "'";
}
} else if (ruleEnum == QueryRuleEnum.RIGHT_LIKE) {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'" + str + "%'";
} else {
return "'" + str + "%'";
}
} else {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
return "N'%" + str + "%'";
} else {
return "'%" + str + "%'";
}
}
//update-end-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
}
}
}
/**
* 获取系统数据库类型
*/
private static String getDbType() {
return CommonUtils.getDatabaseType();
}
}

View File

@ -0,0 +1,40 @@
package org.jeecg.common.system.vo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @Description: 文档管理
* @author: jeecg-boot
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ComboModel implements Serializable {
private String id;
private String title;
/**文档管理 表单table默认选中*/
private boolean checked;
/**文档管理 表单table 用户账号*/
private String username;
/**文档管理 表单table 用户邮箱*/
private String email;
/**文档管理 表单table 角色编码*/
private String roleCode;
public ComboModel(){
};
public ComboModel(String id,String title,boolean checked,String username){
this.id = id;
this.title = title;
this.checked = false;
this.username = username;
};
}

View File

@ -0,0 +1,71 @@
package org.jeecg.common.system.vo;
import java.io.Serializable;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 字典类
* @author: jeecg-boot
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DictModel implements Serializable{
private static final long serialVersionUID = 1L;
public DictModel() {
}
public DictModel(String value, String text) {
this.value = value;
this.text = text;
}
public DictModel(String value, String text, String color) {
this.value = value;
this.text = text;
this.color = color;
}
/**
* 字典value
*/
private String value;
/**
* 字典文本
*/
private String text;
/**
* 字典颜色
*/
private String color;
/**
* 特殊用途: JgEditableTable
* @return
*/
public String getTitle() {
return this.text;
}
/**
* 特殊用途: vue3 Select组件
*/
public String getLabel() {
return this.text;
}
/**
* 用于表单设计器 关联记录表数据存储
* QQYUN-5595【表单设计器】他表字段 导入没有翻译
*/
private JSONObject jsonObject;
}

View File

@ -0,0 +1,19 @@
package org.jeecg.common.system.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 查询多个字典时用到
* @author: jeecg-boot
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class DictModelMany extends DictModel {
/**
* 字典code根据多个字段code查询时才用到用于区分不同的字典选项
*/
private String dictCode;
}

View File

@ -0,0 +1,35 @@
package org.jeecg.common.system.vo;
import lombok.Data;
/**
* 字典查询参数实体
* @author: jeecg-boot
*/
@Data
public class DictQuery {
/**
* 表名
*/
private String table;
/**
* 存储列
*/
private String code;
/**
* 显示列
*/
private String text;
/**
* 关键字查询
*/
private String keyword;
/**
* 存储列的值 用于回显查询
*/
private String codeValue;
}

View File

@ -0,0 +1,58 @@
package org.jeecg.common.system.vo;
import lombok.Data;
import org.springframework.beans.BeanUtils;
/**
* @Description: 数据源
* @author: jeecg-boot
*/
@Data
public class DynamicDataSourceModel {
public DynamicDataSourceModel() {
}
public DynamicDataSourceModel(Object dbSource) {
if (dbSource != null) {
BeanUtils.copyProperties(dbSource, this);
}
}
/**
* id
*/
private java.lang.String id;
/**
* 数据源编码
*/
private java.lang.String code;
/**
* 数据库类型
*/
private java.lang.String dbType;
/**
* 驱动类
*/
private java.lang.String dbDriver;
/**
* 数据源地址
*/
private java.lang.String dbUrl;
// /**
// * 数据库名称
// */
// private java.lang.String dbName;
/**
* 用户名
*/
private java.lang.String dbUsername;
/**
* 密码
*/
private java.lang.String dbPassword;
}

View File

@ -0,0 +1,141 @@
package org.jeecg.common.system.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecg.common.desensitization.annotation.SensitiveField;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* <p>
* 在线用户信息
* </p>
*
* @Author scott
* @since 2018-12-20
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class LoginUser {
/**
* 登录人id
*/
@SensitiveField
private String id;
/**
* 登录人账号
*/
@SensitiveField
private String username;
/**
* 登录人名字
*/
@SensitiveField
private String realname;
/**
* 登录人密码
*/
@SensitiveField
private String password;
/**
* 当前登录部门code
*/
@SensitiveField
private String orgCode;
/**
* 当前登录部门id
*/
@SensitiveField
private String orgId;
/**
* 当前登录角色code多个逗号分割
*/
@SensitiveField
private String roleCode;
/**
* 头像
*/
@SensitiveField
private String avatar;
/**
* 生日
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
/**
* 性别1男 2
*/
private Integer sex;
/**
* 电子邮件
*/
@SensitiveField
private String email;
/**
* 电话
*/
@SensitiveField
private String phone;
/**
* 状态(1正常 2冻结
*/
private Integer status;
private Integer delFlag;
/**
* 同步工作流引擎1同步0不同步
*/
private Integer activitiSync;
/**
* 创建时间
*/
private Date createTime;
/**
* 身份1 普通员工 2 上级)
*/
private Integer userIdentity;
/**
* 管理部门ids
*/
@SensitiveField
private String departIds;
/**
* 职务,关联职务表
*/
@SensitiveField
private String post;
/**
* 座机号
*/
@SensitiveField
private String telephone;
/** 多租户ids临时用不持久化数据库(数据库字段不存在) */
@SensitiveField
private String relTenantIds;
/**设备id uniapp推送用*/
private String clientId;
}

View File

@ -0,0 +1,32 @@
package org.jeecg.common.system.vo;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 下拉树 model
*
* @author jeecg-boot
*/
@Data
public class SelectTreeModel implements Serializable {
private String key;
private String title;
private String value;
/**
* 父Id
*/
private String parentId;
/**
* 是否是叶节点
*/
private boolean isLeaf;
/**
* 子节点
*/
private List<SelectTreeModel> children;
}

View File

@ -0,0 +1,52 @@
package org.jeecg.common.system.vo;
import org.jeecgframework.poi.excel.annotation.Excel;
/**
* @Author qinfeng
* @Date 2020/2/19 12:01
* @Description:
* @Version 1.0
*/
public class SysCategoryModel {
/**主键*/
private java.lang.String id;
/**父级节点*/
private java.lang.String pid;
/**类型名称*/
private java.lang.String name;
/**类型编码*/
private java.lang.String code;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}

View File

@ -0,0 +1,148 @@
package org.jeecg.common.system.vo;
/**
* 部门机构model
* @author: lvdandan
*/
public class SysDepartModel {
/**ID*/
private String id;
/**父机构ID*/
private String parentId;
/**机构/部门名称*/
private String departName;
/**英文名*/
private String departNameEn;
/**缩写*/
private String departNameAbbr;
/**排序*/
private Integer departOrder;
/**描述*/
private String description;
/**机构类别 1组织机构2岗位*/
private String orgCategory;
/**机构类型*/
private String orgType;
/**机构编码*/
private String orgCode;
/**手机号*/
private String mobile;
/**传真*/
private String fax;
/**地址*/
private String address;
/**备注*/
private String memo;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
public String getDepartName() {
return departName;
}
public void setDepartName(String departName) {
this.departName = departName;
}
public String getDepartNameEn() {
return departNameEn;
}
public void setDepartNameEn(String departNameEn) {
this.departNameEn = departNameEn;
}
public String getDepartNameAbbr() {
return departNameAbbr;
}
public void setDepartNameAbbr(String departNameAbbr) {
this.departNameAbbr = departNameAbbr;
}
public Integer getDepartOrder() {
return departOrder;
}
public void setDepartOrder(Integer departOrder) {
this.departOrder = departOrder;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getOrgCategory() {
return orgCategory;
}
public void setOrgCategory(String orgCategory) {
this.orgCategory = orgCategory;
}
public String getOrgType() {
return orgType;
}
public void setOrgType(String orgType) {
this.orgType = orgType;
}
public String getOrgCode() {
return orgCode;
}
public void setOrgCode(String orgCode) {
this.orgCode = orgCode;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getFax() {
return fax;
}
public void setFax(String fax) {
this.fax = fax;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
}

View File

@ -0,0 +1,80 @@
package org.jeecg.common.system.vo;
/**
* @Description: 系统文件实体类
* @author: wangshuai
* @date: 2022年08月11日 9:48
*/
public class SysFilesModel {
/**主键id*/
private String id;
/**文件名称*/
private String fileName;
/**文件地址*/
private String url;
/**文档类型folder:文件夹 excel:excel doc:word pp:ppt image:图片 archive:其他文档 video:视频)*/
private String fileType;
/**文件上传类型(temp/本地上传(临时文件) manage/知识库)*/
private String storeType;
/**文件大小kb*/
private Double fileSize;
/**租户id*/
private String tenantId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public String getStoreType() {
return storeType;
}
public void setStoreType(String storeType) {
this.storeType = storeType;
}
public Double getFileSize() {
return fileSize;
}
public void setFileSize(Double fileSize) {
this.fileSize = fileSize;
}
public String getTenantId() {
return tenantId;
}
public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
}

View File

@ -0,0 +1,151 @@
package org.jeecg.common.system.vo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 菜单权限规则表
* </p>
*
* @Author huangzhilin
* @since 2019-03-29
*/
public class SysPermissionDataRuleModel {
/**
* id
*/
private String id;
/**
* 对应的菜单id
*/
private String permissionId;
/**
* 规则名称
*/
private String ruleName;
/**
* 字段
*/
private String ruleColumn;
/**
* 条件
*/
private String ruleConditions;
/**
* 规则值
*/
private String ruleValue;
/**
* 创建时间
*/
private Date createTime;
/**
* 创建人
*/
private String createBy;
/**
* 修改时间
*/
private Date updateTime;
/**
* 修改人
*/
private String updateBy;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPermissionId() {
return permissionId;
}
public void setPermissionId(String permissionId) {
this.permissionId = permissionId;
}
public String getRuleName() {
return ruleName;
}
public void setRuleName(String ruleName) {
this.ruleName = ruleName;
}
public String getRuleColumn() {
return ruleColumn;
}
public void setRuleColumn(String ruleColumn) {
this.ruleColumn = ruleColumn;
}
public String getRuleConditions() {
return ruleConditions;
}
public void setRuleConditions(String ruleConditions) {
this.ruleConditions = ruleConditions;
}
public String getRuleValue() {
return ruleValue;
}
public void setRuleValue(String ruleValue) {
this.ruleValue = ruleValue;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
}

View File

@ -0,0 +1,106 @@
package org.jeecg.common.system.vo;
import java.util.List;
import org.jeecg.common.util.DateUtils;
/**
* @Description: 用户缓存信息
* @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;
}
public void setOneDepart(boolean oneDepart) {
this.oneDepart = oneDepart;
}
public String getSysDate() {
return DateUtils.formatDate();
}
public String getSysTime() {
return DateUtils.now();
}
public String getSysUserCode() {
return sysUserCode;
}
public void setSysUserCode(String sysUserCode) {
this.sysUserCode = sysUserCode;
}
public String getSysUserName() {
return sysUserName;
}
public void setSysUserName(String sysUserName) {
this.sysUserName = sysUserName;
}
public String getSysOrgCode() {
return sysOrgCode;
}
public void setSysOrgCode(String sysOrgCode) {
this.sysOrgCode = sysOrgCode;
}
public List<String> getSysMultiOrgCode() {
return sysMultiOrgCode;
}
public void setSysMultiOrgCode(List<String> sysMultiOrgCode) {
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,61 @@
package org.jeecg.common.system.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecg.common.desensitization.annotation.SensitiveField;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* <p>
* 在线用户信息
* </p>
*
* @Author scott
* @since 2023-08-16
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class UserAccountInfo {
/**
* 登录人id
*/
private String id;
/**
* 登录人账号
*/
private String username;
/**
* 登录人名字
*/
private String realname;
/**
* 电子邮件
*/
private String email;
/**
* 头像
*/
@SensitiveField
private String avatar;
/**
* 同步工作流引擎1同步0不同步
*/
private Integer activitiSync;
/**
* 电话
*/
@SensitiveField
private String phone;
}

View File

@ -0,0 +1,13 @@
package org.jeecg.common.util;
/**
*
* @Author 张代浩
*
*/
public enum BrowserType {
/**
* 浏览类型 IE11,IE10,IE9,IE8,IE7,IE6,Firefox,Safari,Chrome,Opera,Camino,Gecko
*/
IE11,IE10,IE9,IE8,IE7,IE6,Firefox,Safari,Chrome,Opera,Camino,Gecko
}

View File

@ -0,0 +1,212 @@
package org.jeecg.common.util;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
/**
*
* @Author 张代浩
*
*/
public class BrowserUtils {
/**
* 判断是否是IE
* @param request
* @return
*/
public static boolean isIe(HttpServletRequest request) {
return (request.getHeader("USER-AGENT").toLowerCase().indexOf("msie") > 0 || request
.getHeader("USER-AGENT").toLowerCase().indexOf("rv:11.0") > 0) ? true
: false;
}
/**
* 获取IE版本
*
* @param request
* @return
*/
public static Double getIeVersion(HttpServletRequest request) {
Double version = 0.0;
if (getBrowserType(request, IE11)) {
version = 11.0;
} else if (getBrowserType(request, IE10)) {
version = 10.0;
} else if (getBrowserType(request, IE9)) {
version = 9.0;
} else if (getBrowserType(request, IE8)) {
version = 8.0;
} else if (getBrowserType(request, IE7)) {
version = 7.0;
} else if (getBrowserType(request, IE6)) {
version = 6.0;
}
return version;
}
/**
* 获取浏览器类型
*
* @param request
* @return
*/
public static BrowserType getBrowserType(HttpServletRequest request) {
BrowserType browserType = null;
if (getBrowserType(request, IE11)) {
browserType = BrowserType.IE11;
}
if (getBrowserType(request, IE10)) {
browserType = BrowserType.IE10;
}
if (getBrowserType(request, IE9)) {
browserType = BrowserType.IE9;
}
if (getBrowserType(request, IE8)) {
browserType = BrowserType.IE8;
}
if (getBrowserType(request, IE7)) {
browserType = BrowserType.IE7;
}
if (getBrowserType(request, IE6)) {
browserType = BrowserType.IE6;
}
if (getBrowserType(request, FIREFOX)) {
browserType = BrowserType.Firefox;
}
if (getBrowserType(request, SAFARI)) {
browserType = BrowserType.Safari;
}
if (getBrowserType(request, CHROME)) {
browserType = BrowserType.Chrome;
}
if (getBrowserType(request, OPERA)) {
browserType = BrowserType.Opera;
}
if (getBrowserType(request, CAMINO)) {
browserType = BrowserType.Camino;
}
return browserType;
}
private static boolean getBrowserType(HttpServletRequest request,
String brosertype) {
return request.getHeader("USER-AGENT").toLowerCase()
.indexOf(brosertype) > 0 ? true : false;
}
private final static String IE11 = "rv:11.0";
private final static String IE10 = "MSIE 10.0";
private final static String IE9 = "MSIE 9.0";
private final static String IE8 = "MSIE 8.0";
private final static String IE7 = "MSIE 7.0";
private final static String IE6 = "MSIE 6.0";
private final static String MAXTHON = "Maxthon";
private final static String QQ = "QQBrowser";
private final static String GREEN = "GreenBrowser";
private final static String SE360 = "360SE";
private final static String FIREFOX = "Firefox";
private final static String OPERA = "Opera";
private final static String CHROME = "Chrome";
private final static String SAFARI = "Safari";
private final static String OTHER = "其它";
private final static String CAMINO = "Camino";
public static String checkBrowse(HttpServletRequest request) {
String userAgent = request.getHeader("USER-AGENT");
if (regex(OPERA, userAgent)) {
return OPERA;
}
if (regex(CHROME, userAgent)) {
return CHROME;
}
if (regex(FIREFOX, userAgent)) {
return FIREFOX;
}
if (regex(SAFARI, userAgent)) {
return SAFARI;
}
if (regex(SE360, userAgent)) {
return SE360;
}
if (regex(GREEN, userAgent)) {
return GREEN;
}
if (regex(QQ, userAgent)) {
return QQ;
}
if (regex(MAXTHON, userAgent)) {
return MAXTHON;
}
if (regex(IE11, userAgent)) {
return IE11;
}
if (regex(IE10, userAgent)) {
return IE10;
}
if (regex(IE9, userAgent)) {
return IE9;
}
if (regex(IE8, userAgent)) {
return IE8;
}
if (regex(IE7, userAgent)) {
return IE7;
}
if (regex(IE6, userAgent)) {
return IE6;
}
return OTHER;
}
public static boolean regex(String regex, String str) {
Pattern p = Pattern.compile(regex, Pattern.MULTILINE);
Matcher m = p.matcher(str);
return m.find();
}
private static Map<String, String> langMap = new HashMap<String, String>();
private final static String ZH = "zh";
private final static String ZH_CN = "zh-cn";
private final static String EN = "en";
private final static String EN_US = "en";
static
{
langMap.put(ZH, ZH_CN);
langMap.put(EN, EN_US);
}
public static String getBrowserLanguage(HttpServletRequest request) {
String browserLang = request.getLocale().getLanguage();
String browserLangCode = (String)langMap.get(browserLang);
if(browserLangCode == null)
{
browserLangCode = EN_US;
}
return browserLangCode;
}
/** 判断请求是否来自电脑端 */
public static boolean isDesktop(HttpServletRequest request) {
return !isMobile(request);
}
/** 判断请求是否来自移动端 */
public static boolean isMobile(HttpServletRequest request) {
String ua = request.getHeader("User-Agent").toLowerCase();
String type = "(phone|pad|pod|iphone|ipod|ios|ipad|android|mobile|blackberry|iemobile|mqqbrowser|juc|fennec|wosbrowser|browserng|webos|symbian|windows phone)";
Pattern pattern = Pattern.compile(type);
return pattern.matcher(ua).find();
}
}

View File

@ -0,0 +1,490 @@
package org.jeecg.common.util;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.constant.ServiceNameConstants;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.oss.OssBootUtil;
import org.jeecgframework.poi.util.PoiPublicUtil;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Description: 通用工具
* @author: jeecg-boot
*/
@Slf4j
public class CommonUtils {
/**
* 中文正则
*/
private static Pattern ZHONGWEN_PATTERN = Pattern.compile("[\u4e00-\u9fa5]");
/**
* 文件名 正则字符串
* 文件名支持的字符串:字母数字中文.-_() 除此之外的字符将被删除
*/
private static String FILE_NAME_REGEX = "[^A-Za-z\\.\\(\\)\\-\\_0-9\\u4e00-\\u9fa5]";
public static String uploadOnlineImage(byte[] data,String basePath,String bizPath,String uploadType){
String dbPath = null;
String fileName = "image" + Math.round(Math.random() * 100000000000L);
fileName += "." + PoiPublicUtil.getFileExtendName(data);
try {
if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
File file = new File(basePath + File.separator + bizPath + File.separator );
if (!file.exists()) {
file.mkdirs();// 创建文件根目录
}
String savePath = file.getPath() + File.separator + fileName;
File savefile = new File(savePath);
FileCopyUtils.copy(data, savefile);
dbPath = bizPath + File.separator + fileName;
}else {
InputStream in = new ByteArrayInputStream(data);
String relativePath = bizPath+"/"+fileName;
if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){
dbPath = MinioUtil.upload(in,relativePath);
}else if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){
dbPath = OssBootUtil.upload(in,relativePath);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return dbPath;
}
/**
* 判断文件名是否带盘符,重新处理
* @param fileName
* @return
*/
public static String getFileName(String fileName){
//判断是否带有盘符信息
// Check for Unix-style path
int unixSep = fileName.lastIndexOf('/');
// Check for Windows-style path
int winSep = fileName.lastIndexOf('\\');
// Cut off at latest possible point
int pos = (winSep > unixSep ? winSep : unixSep);
if (pos != -1) {
// Any sort of path separator found...
fileName = fileName.substring(pos + 1);
}
//替换上传文件名字的特殊字符
fileName = fileName.replace("=","").replace(",","").replace("&","")
.replace("#", "").replace("", "").replace("", "");
//替换上传文件名字中的空格
fileName=fileName.replaceAll("\\s","");
//update-beign-author:taoyan date:20220302 for: /issues/3381 online 在线表单 使用文件组件时,上传文件名中含%,下载异常
fileName = fileName.replaceAll(FILE_NAME_REGEX, "");
//update-end-author:taoyan date:20220302 for: /issues/3381 online 在线表单 使用文件组件时,上传文件名中含%,下载异常
return fileName;
}
/**
* java 判断字符串里是否包含中文字符
* @param str
* @return
*/
public static boolean ifContainChinese(String str) {
if(str.getBytes().length == str.length()){
return false;
}else{
Matcher m = ZHONGWEN_PATTERN.matcher(str);
if (m.find()) {
return true;
}
return false;
}
}
/**
* 统一全局上传
* @Return: java.lang.String
*/
public static String upload(MultipartFile file, String bizPath, String uploadType) {
String url = "";
try {
if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
url = MinioUtil.upload(file, bizPath);
} else {
url = OssBootUtil.upload(file, bizPath);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new JeecgBootException(e.getMessage());
}
return url;
}
/**
* 本地文件上传
* @param mf 文件
* @param bizPath 自定义路径
* @return
*/
public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){
try {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
SsrfFileTypeFilter.checkUploadFileType(mf);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String fileName = null;
File file = new File(uploadpath + File.separator + bizPath + File.separator );
if (!file.exists()) {
// 创建文件根目录
file.mkdirs();
}
// 获取文件名
String orgName = mf.getOriginalFilename();
orgName = CommonUtils.getFileName(orgName);
if(orgName.indexOf(SymbolConstant.SPOT)!=-1){
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
}else{
fileName = orgName+ "_" + System.currentTimeMillis();
}
String savePath = file.getPath() + File.separator + fileName;
File savefile = new File(savePath);
FileCopyUtils.copy(mf.getBytes(), savefile);
String dbpath = null;
if(oConvertUtils.isNotEmpty(bizPath)){
dbpath = bizPath + File.separator + fileName;
}else{
dbpath = fileName;
}
if (dbpath.contains(SymbolConstant.DOUBLE_BACKSLASH)) {
dbpath = dbpath.replace("\\", "/");
}
return dbpath;
} catch (IOException e) {
log.error(e.getMessage(), e);
}catch (Exception e) {
log.error(e.getMessage(), e);
}
return "";
}
/**
* 统一全局上传 带桶
* @Return: java.lang.String
*/
public static String upload(MultipartFile file, String bizPath, String uploadType, String customBucket) {
String url = "";
try {
if (CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)) {
url = MinioUtil.upload(file, bizPath, customBucket);
} else {
url = OssBootUtil.upload(file, bizPath, customBucket);
}
} catch (Exception e) {
log.error(e.getMessage(),e);
}
return url;
}
/** 当前系统数据库类型 */
private static String DB_TYPE = "";
private static DbType dbTypeEnum = null;
/**
* 全局获取平台数据库类型(作废了)
* @return
*/
@Deprecated
public static String getDatabaseType() {
if(oConvertUtils.isNotEmpty(DB_TYPE)){
return DB_TYPE;
}
DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
try {
return getDatabaseTypeByDataSource(dataSource);
} catch (SQLException e) {
//e.printStackTrace();
log.warn(e.getMessage(),e);
return "";
}
}
/**
* 全局获取平台数据库类型对应mybaisPlus枚举
* @return
*/
public static DbType getDatabaseTypeEnum() {
if (oConvertUtils.isNotEmpty(dbTypeEnum)) {
return dbTypeEnum;
}
try {
DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
dbTypeEnum = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
return dbTypeEnum;
} catch (SQLException e) {
log.warn(e.getMessage(), e);
return null;
}
}
/**
* 根据数据源key获取DataSourceProperty
* @param sourceKey
* @return
*/
public static DataSourceProperty getDataSourceProperty(String sourceKey){
DynamicDataSourceProperties prop = SpringContextUtils.getApplicationContext().getBean(DynamicDataSourceProperties.class);
Map<String, DataSourceProperty> map = prop.getDatasource();
DataSourceProperty db = (DataSourceProperty)map.get(sourceKey);
return db;
}
/**
* 根据sourceKey 获取数据源连接
* @param sourceKey
* @return
* @throws SQLException
*/
public static Connection getDataSourceConnect(String sourceKey) throws SQLException {
if (oConvertUtils.isEmpty(sourceKey)) {
sourceKey = "master";
}
DynamicDataSourceProperties prop = SpringContextUtils.getApplicationContext().getBean(DynamicDataSourceProperties.class);
Map<String, DataSourceProperty> map = prop.getDatasource();
DataSourceProperty db = (DataSourceProperty)map.get(sourceKey);
if(db==null){
return null;
}
DriverManagerDataSource ds = new DriverManagerDataSource ();
ds.setDriverClassName(db.getDriverClassName());
ds.setUrl(db.getUrl());
ds.setUsername(db.getUsername());
ds.setPassword(db.getPassword());
return ds.getConnection();
}
/**
* 获取数据库类型
* @param dataSource
* @return
* @throws SQLException
*/
private static String getDatabaseTypeByDataSource(DataSource dataSource) throws SQLException{
if("".equals(DB_TYPE)) {
Connection connection = dataSource.getConnection();
try {
DatabaseMetaData md = connection.getMetaData();
String dbType = md.getDatabaseProductName().toUpperCase();
String sqlserver= "SQL SERVER";
if(dbType.indexOf(DataBaseConstant.DB_TYPE_MYSQL)>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_MYSQL;
}else if(dbType.indexOf(DataBaseConstant.DB_TYPE_ORACLE)>=0 ||dbType.indexOf(DataBaseConstant.DB_TYPE_DM)>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_ORACLE;
}else if(dbType.indexOf(DataBaseConstant.DB_TYPE_SQLSERVER)>=0||dbType.indexOf(sqlserver)>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_SQLSERVER;
}else if(dbType.indexOf(DataBaseConstant.DB_TYPE_POSTGRESQL)>=0 || dbType.indexOf(DataBaseConstant.DB_TYPE_KINGBASEES)>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_POSTGRESQL;
}else if(dbType.indexOf(DataBaseConstant.DB_TYPE_MARIADB)>=0) {
DB_TYPE = DataBaseConstant.DB_TYPE_MARIADB;
}else {
log.error("数据库类型:[" + dbType + "]不识别!");
//throw new JeecgBootException("数据库类型:["+dbType+"]不识别!");
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}finally {
connection.close();
}
}
return DB_TYPE;
}
/**
* 获取服务器地址
*
* @param request
* @return
*/
public static String getBaseUrl(HttpServletRequest request) {
//1.【兼容】兼容微服务下的 base path-------
String xGatewayBasePath = request.getHeader(ServiceNameConstants.X_GATEWAY_BASE_PATH);
if(oConvertUtils.isNotEmpty(xGatewayBasePath)){
log.info("x_gateway_base_path = "+ xGatewayBasePath);
return xGatewayBasePath;
}
//2.【兼容】SSL认证之后request.getScheme()获取不到https的问题
// https://blog.csdn.net/weixin_34376986/article/details/89767950
String scheme = request.getHeader(CommonConstant.X_FORWARDED_SCHEME);
if(oConvertUtils.isEmpty(scheme)){
scheme = request.getScheme();
}
//3.常规操作
String serverName = request.getServerName();
int serverPort = request.getServerPort();
String contextPath = request.getContextPath();
//返回 host domain
String baseDomainPath = null;
//update-begin---author:wangshuai---date:2024-03-15---for:【QQYUN-8561】企业微信登陆请求接口设置上下文不一致导致接口404---
int httpPort = 80;
int httpsPort = 443;
if(httpPort == serverPort || httpsPort == serverPort){
//update-end---author:wangshuai---date:2024-03-15---for:【QQYUN-8561】企业微信登陆请求接口设置上下文不一致导致接口404---~
baseDomainPath = scheme + "://" + serverName + contextPath ;
}else{
baseDomainPath = scheme + "://" + serverName + ":" + serverPort + contextPath ;
}
log.debug("-----Common getBaseUrl----- : " + baseDomainPath);
return baseDomainPath;
}
/**
* 递归合并 fastJSON 对象
*
* @param target 目标对象
* @param sources 来源对象,允许多个,优先级从左到右,最右侧的优先级最高
*/
public static JSONObject mergeJSON(JSONObject target, JSONObject... sources) {
for (JSONObject source : sources) {
CommonUtils.mergeJSON(target, source);
}
return target;
}
/**
* 递归合并 fastJSON 对象
*
* @param target 目标对象
* @param source 来源对象
*/
public static JSONObject mergeJSON(JSONObject target, JSONObject source) {
for (String key : source.keySet()) {
Object sourceItem = source.get(key);
// 是否是 JSONObject
if (sourceItem instanceof Map) {
// target中存在此key
if (target.containsKey(key)) {
// 两个都是 JSONObject继续合并
if (target.get(key) instanceof Map) {
CommonUtils.mergeJSON(target.getJSONObject(key), source.getJSONObject(key));
continue;
}
}
}
// target不存在此key或不是 JSONObject则覆盖
target.put(key, sourceItem);
}
return target;
}
/**
* 将list集合以分割符的方式进行分割
* @param list String类型的集合文本
* @param separator 分隔符
* @return
*/
public static String getSplitText(List<String> list, String separator) {
if (null != list && list.size() > 0) {
return StringUtils.join(list, separator);
}
return "";
}
/**
* 通过table的条件SQL
*
* @param tableSql sys_user where name = '1212'
* @return name = '1212'
*/
public static String getFilterSqlByTableSql(String tableSql) {
if(oConvertUtils.isEmpty(tableSql)){
return null;
}
if (tableSql.toLowerCase().indexOf(DataBaseConstant.SQL_WHERE) > 0) {
String[] arr = tableSql.split(" (?i)where ");
if (arr != null && oConvertUtils.isNotEmpty(arr[1])) {
return arr[1];
}
}
return "";
}
/**
* 通过table获取表名
*
* @param tableSql sys_user where name = '1212'
* @return sys_user
*/
public static String getTableNameByTableSql(String tableSql) {
if(oConvertUtils.isEmpty(tableSql)){
return null;
}
if (tableSql.toLowerCase().indexOf(DataBaseConstant.SQL_WHERE) > 0) {
String[] arr = tableSql.split(" (?i)where ");
return arr[0].trim();
} else {
return tableSql;
}
}
/**
* 判断两个数组是否存在交集
* @param set1
* @param arr2
* @return
*/
public static boolean hasIntersection(Set<String> set1, String[] arr2) {
if (set1 == null) {
return false;
}
if(set1.size()>0){
for (String str : arr2) {
if (set1.contains(str)) {
return true;
}
}
}
return false;
}
/**
* 输出info日志会捕获异常防止因为日志问题导致程序异常
*
* @param msg
* @param objects
*/
public static void logInfo(String msg, Object... objects) {
try {
log.info(msg, objects);
} catch (Exception e) {
log.warn("{} —— {}", msg, e.getMessage());
}
}
}

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());
}
}

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