初始提交
This commit is contained in:
148
common-base-starter/common-base-starter.iml
Normal file
148
common-base-starter/common-base-starter.iml
Normal file
@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Spring" name="Spring">
|
||||
<configuration />
|
||||
</facet>
|
||||
<facet type="web" name="Web">
|
||||
<configuration>
|
||||
<webroots />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.apache.velocity:velocity-engine-core:2.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger2:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.5.14" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.swagger:swagger-models:1.5.14" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.8.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.springfox:springfox-spi:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.springfox:springfox-core:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.springfox:springfox-schema:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger-common:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.springfox:springfox-spring-web:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.11" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.3.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-beans:4.0.9.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-context:4.0.9.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:1.2.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.2.0.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger-ui:2.8.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.caspar-chen:swagger-ui-layer:1.1.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.3.2.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.3.2.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.3.2.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.3.2.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.13.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.13.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
|
||||
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.8.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.8.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.26" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.3.2.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.3.2.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.37" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.37" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.8.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.8.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.8.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: cn.hutool:hutool-all:5.7.20" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.belerweb:pinyin4j:2.5.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.alibaba:fastjson:2.0.9" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.alibaba.fastjson2:fastjson2-extension:2.0.9" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.alibaba.fastjson2:fastjson2:2.0.9" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.guava:guava:29.0-jre" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.guava:failureaccess:1.0.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.checkerframework:checker-qual:2.11.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.errorprone:error_prone_annotations:2.3.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.j2objc:j2objc-annotations:1.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.8" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.alibaba:transmittable-thread-local:2.11.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.redisson:redisson-spring-boot-starter:3.13.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.3.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.3.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.3.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.5.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.12" level="project" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-data-redis:2.3.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-redis:2.3.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-keyvalue:2.3.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.data:spring-data-commons:2.3.0.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-tx:5.2.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-oxm:5.2.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.6.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.redisson:redisson:3.13.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.51.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.51.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.51.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.51.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.51.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-resolver-dns:4.1.51.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-codec-dns:4.1.51.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.51.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: javax.cache:cache-api:1.0.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.3.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.reactivex.rxjava2:rxjava:2.2.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jboss.marshalling:jboss-marshalling-river:2.0.9.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jboss.marshalling:jboss-marshalling:2.0.9.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.11.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jodd:jodd-bean:5.0.13" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jodd:jodd-core:5.0.13" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.redisson:redisson-spring-data-23:3.13.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.3.2.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.8.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.pagehelper:pagehelper:5.3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.jsqlparser:jsqlparser:4.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-boot-starter:3.4.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.baomidou:mybatis-plus:3.4.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.4.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.4.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.3.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-extension:3.4.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-core:3.4.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.baomidou:mybatis-plus-annotation:3.4.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml:4.1.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:4.1.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:3.1.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-compress:1.19" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.github.virtuald:curvesapi:1.06" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.poi:poi:4.1.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.13" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-collections4:4.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.zaxxer:SparseBitSet:1.2" level="project" />
|
||||
<orderEntry type="module" module-name="common-redis-starter" />
|
||||
</component>
|
||||
</module>
|
187
common-base-starter/pom.xml
Normal file
187
common-base-starter/pom.xml
Normal file
@ -0,0 +1,187 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>mosty-common</artifactId>
|
||||
<groupId>com.mosty</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-base-starter</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<redisson.version>3.13.3</redisson.version>
|
||||
<springfox-version>2.8.0</springfox-version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
|
||||
<!--velocity代码生成使用模板 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
<version>2.3</version>
|
||||
</dependency>
|
||||
|
||||
<!--swagger-->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${springfox-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${springfox-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.caspar-chen</groupId>
|
||||
<artifactId>swagger-ui-layer</artifactId>
|
||||
<version>1.1.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot-dependencies.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.7.20</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.belerweb</groupId>
|
||||
<artifactId>pinyin4j</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>29.0-jre</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
<version>2.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<version>${spring-boot-dependencies.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper</artifactId>
|
||||
<version>5.3.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>2.0.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.4.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
<version>3.4.3</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mosty</groupId>
|
||||
<artifactId>common-redis-starter</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<attach>true</attach>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,113 @@
|
||||
package com.mosty.common.base.autoconfig;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 脱敏类
|
||||
* @author kevin
|
||||
* @date 2022/3/8 11:10 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class Desensitization {
|
||||
|
||||
/**
|
||||
* 手机类型脱敏
|
||||
* @author kevin
|
||||
* @date 2022/3/8 11:13 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static class Phone extends JsonSerializer<String> {
|
||||
@Override
|
||||
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
jsonGenerator.writeObject(value);
|
||||
return;
|
||||
}
|
||||
String trim = value.trim();
|
||||
if (trim.length() == 11) {
|
||||
String phone = trim.replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
|
||||
jsonGenerator.writeObject(phone);
|
||||
return;
|
||||
}
|
||||
jsonGenerator.writeObject(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 银行卡号类型脱敏
|
||||
* @author kevin
|
||||
* @date 2022/3/8 11:13 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static class BankCard extends JsonSerializer<String> {
|
||||
|
||||
@Override
|
||||
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
jsonGenerator.writeObject(value);
|
||||
return;
|
||||
}
|
||||
String trim = value.trim();
|
||||
String phone = trim.replaceAll("(\\d{6})\\d*(\\d{4})", "$1****$2");
|
||||
jsonGenerator.writeObject(phone);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份证号类型脱敏
|
||||
* @author kevin
|
||||
* @date 2022/3/8 11:13 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static class IdentityCard extends JsonSerializer<String> {
|
||||
|
||||
@Override
|
||||
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
jsonGenerator.writeObject(value);
|
||||
return;
|
||||
}
|
||||
String trim = value.trim();
|
||||
String phone = trim.replaceAll("(\\d{6})\\d*(\\w{4})", "$1****$2");
|
||||
jsonGenerator.writeObject(phone);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 姓名类型脱敏
|
||||
* @author kevin
|
||||
* @date 2022/3/8 11:13 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static class FullName extends JsonSerializer<String> {
|
||||
|
||||
@Override
|
||||
public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
|
||||
if (StringUtils.isEmpty(value) || value.length() == 1 || value.trim().length() == 1) {
|
||||
jsonGenerator.writeObject(value);
|
||||
return;
|
||||
}
|
||||
String trim = value.trim();
|
||||
if (trim.length() == 2) {
|
||||
jsonGenerator.writeObject(value.charAt(0) + "*");
|
||||
return;
|
||||
}
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
if (i == 0 || i == value.length() - 1) {
|
||||
stringBuffer.append(value.charAt(i));
|
||||
} else {
|
||||
stringBuffer.append("*");
|
||||
}
|
||||
}
|
||||
jsonGenerator.writeObject(stringBuffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
//package com.mosty.common.base.autoconfig;
|
||||
//
|
||||
//import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||
//import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
//import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||
//import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||
//import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
//import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.apache.commons.lang3.StringUtils;
|
||||
//import org.springframework.beans.factory.annotation.Value;
|
||||
//import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//import org.springframework.core.convert.converter.Converter;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
//import java.time.LocalDate;
|
||||
//import java.time.LocalDateTime;
|
||||
//import java.time.LocalTime;
|
||||
//import java.time.format.DateTimeFormatter;
|
||||
//
|
||||
///**
|
||||
// * 全局: 序列化,反序列化,格式处理
|
||||
// * @author kevin
|
||||
// * @date 2022/3/28 2:37 PM
|
||||
// * @since 1.0.0
|
||||
// */
|
||||
//@Slf4j
|
||||
//@Configuration
|
||||
//public class JacksonCustomizerConfig {
|
||||
//
|
||||
// @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
|
||||
// private String localDateTimePattern;
|
||||
//
|
||||
// @Value("${spring.jackson.local-date-format:yyyy-MM-dd}")
|
||||
// private String localDatePattern;
|
||||
//
|
||||
// @Value("${spring.jackson.local-time-format:HH:mm:ss}")
|
||||
// private String localTimePattern;
|
||||
//
|
||||
// @Bean
|
||||
// public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
|
||||
// return builder -> {
|
||||
// builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(localDateTimePattern)));
|
||||
// builder.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(localDatePattern)));
|
||||
// builder.serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(localTimePattern)));
|
||||
// builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(localDateTimePattern)));
|
||||
// builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(localDatePattern)));
|
||||
// builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(localTimePattern)));
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 时间LocalDateTime转换
|
||||
// */
|
||||
// @Component
|
||||
// public static class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
|
||||
// @Override
|
||||
// public LocalDateTime convert(String source) {
|
||||
// if (StringUtils.isBlank(source)) {
|
||||
// return null;
|
||||
// }
|
||||
// if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
|
||||
// return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
// }
|
||||
// throw new IllegalArgumentException("Invalid value '" + source + "'");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 时间LocalDate转换
|
||||
// */
|
||||
// @Component
|
||||
// public static class LocalDateConverter implements Converter<String, LocalDate> {
|
||||
// @Override
|
||||
// public LocalDate convert(String source) {
|
||||
// if (StringUtils.isBlank(source)) {
|
||||
// return null;
|
||||
// }
|
||||
// if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) {
|
||||
// return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
||||
// }
|
||||
// throw new IllegalArgumentException("Invalid value '" + source + "'");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
@ -0,0 +1,87 @@
|
||||
package com.mosty.common.base.autoconfig.swagger;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.ParameterBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.schema.ModelRef;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.service.Parameter;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.mosty.common.base.constant.SystemConfigConstants.TOKEN_HEADER;
|
||||
|
||||
/**
|
||||
* swagger 配置、自动装配类类
|
||||
* {@link ComponentScan} 具体扫描地址可以更改为:
|
||||
* {@link ComponentScan({"com.mosty.common.config.controller","..."})}
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2022/1/22 5:47 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
@ComponentScan({"com.mosty.common"})
|
||||
@SuppressWarnings("unused")
|
||||
public class SwaggerConfig {
|
||||
|
||||
/** swagger基础扫描包 */
|
||||
private static final String SWAGGER_SCAN_BASE_PACKAGE = "com.mosty";
|
||||
|
||||
/** swagger默认关闭,则没有配置也保证不会启动报错 */
|
||||
@Value("${swagger.enable:true}")
|
||||
private Boolean swaggerEnable;
|
||||
@Value("${swagger.title:mosty}")
|
||||
private String title;
|
||||
@Value("${swagger.host:127.0.0.1}")
|
||||
private String host;
|
||||
@Value("${swagger.port:9999}")
|
||||
private String port;
|
||||
@Value("${swagger.version:1.0.0}")
|
||||
private String version;
|
||||
@Value("${swagger.name:基础服务}")
|
||||
private String name;
|
||||
@Value("${swagger.url:''}")
|
||||
private String url;
|
||||
@Value("${swagger.email:''}")
|
||||
private String email;
|
||||
@Value("${spring.application.name}")
|
||||
private String serviceName;
|
||||
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
if (swaggerEnable) {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
parameters.add(new ParameterBuilder()
|
||||
.name(TOKEN_HEADER)
|
||||
.description("令牌")
|
||||
.modelRef(new ModelRef("string"))
|
||||
.parameterType("header")
|
||||
.required(false)
|
||||
.build());
|
||||
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.host(host + ":" + port)
|
||||
.apiInfo(
|
||||
new ApiInfoBuilder().title(serviceName).description(serviceName)
|
||||
.version(version).contact(new Contact(name, url, email)).build())
|
||||
.globalOperationParameters(parameters)
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage(SWAGGER_SCAN_BASE_PACKAGE))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
return new Docket(DocumentationType.SWAGGER_2).enable(false);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package com.mosty.common.base.constant;
|
||||
|
||||
/**
|
||||
* 通用常量信息
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class Constants {
|
||||
/**
|
||||
* UTF-8 字符集
|
||||
*/
|
||||
public static final String UTF8 = "UTF-8";
|
||||
|
||||
/**
|
||||
* GBK 字符集
|
||||
*/
|
||||
public static final String GBK = "GBK";
|
||||
|
||||
/**
|
||||
* http请求
|
||||
*/
|
||||
public static final String HTTP = "http://";
|
||||
|
||||
/**
|
||||
* https请求
|
||||
*/
|
||||
public static final String HTTPS = "https://";
|
||||
|
||||
/**
|
||||
* 通用成功标识
|
||||
*/
|
||||
public static final String SUCCESS = "0";
|
||||
|
||||
/**
|
||||
* 通用失败标识
|
||||
*/
|
||||
public static final String FAIL = "1";
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
public static final String LOGIN_SUCCESS = "Success";
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
public static final String LOGOUT = "Logout";
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
public static final String REGISTER = "Register";
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
public static final String LOGIN_FAIL = "Error";
|
||||
|
||||
/**
|
||||
* 系统用户授权缓存
|
||||
*/
|
||||
public static final String SYS_AUTH_CACHE = "sys-authCache";
|
||||
|
||||
/**
|
||||
* 参数管理 cache name
|
||||
*/
|
||||
public static final String SYS_CONFIG_CACHE = "sys-config";
|
||||
|
||||
/**
|
||||
* 参数管理 cache key
|
||||
*/
|
||||
public static final String SYS_CONFIG_KEY = "sys_config:";
|
||||
|
||||
/**
|
||||
* 字典管理 cache name
|
||||
*/
|
||||
public static final String SYS_DICT_CACHE = "sys-dict";
|
||||
|
||||
/**
|
||||
* 字典管理 cache key
|
||||
*/
|
||||
public static final String SYS_DICT_KEY = "sys_dict:";
|
||||
|
||||
/**
|
||||
* 资源映射路径 前缀
|
||||
*/
|
||||
public static final String RESOURCE_PREFIX = "/profile";
|
||||
|
||||
/**
|
||||
* RMI 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_RMI = "rmi:";
|
||||
|
||||
/**
|
||||
* LDAP 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_LDAP = "ldap:";
|
||||
|
||||
/**
|
||||
* LDAPS 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_LDAPS = "ldaps:";
|
||||
|
||||
/**
|
||||
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
|
||||
*/
|
||||
public static final String[] JOB_WHITELIST_STR = {"com.ruoyi"};
|
||||
|
||||
/**
|
||||
* 定时任务违规的字符
|
||||
*/
|
||||
public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
|
||||
"org.springframework", "org.apache", "com.ruoyi.common.utils.file"};
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
package com.mosty.common.base.constant;
|
||||
|
||||
/**
|
||||
* 代码生成通用常量
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class GenConstants {
|
||||
/**
|
||||
* 单表(增删改查)
|
||||
*/
|
||||
public static final String TPL_CRUD = "crud";
|
||||
|
||||
/**
|
||||
* 树表(增删改查)
|
||||
*/
|
||||
public static final String TPL_TREE = "tree";
|
||||
|
||||
/**
|
||||
* 主子表(增删改查)
|
||||
*/
|
||||
public static final String TPL_SUB = "sub";
|
||||
|
||||
/**
|
||||
* 树编码字段
|
||||
*/
|
||||
public static final String TREE_CODE = "treeCode";
|
||||
|
||||
/**
|
||||
* 树父编码字段
|
||||
*/
|
||||
public static final String TREE_PARENT_CODE = "treeParentCode";
|
||||
|
||||
/**
|
||||
* 树名称字段
|
||||
*/
|
||||
public static final String TREE_NAME = "treeName";
|
||||
|
||||
/**
|
||||
* 上级菜单ID字段
|
||||
*/
|
||||
public static final String PARENT_MENU_ID = "parentMenuId";
|
||||
|
||||
/**
|
||||
* 上级菜单名称字段
|
||||
*/
|
||||
public static final String PARENT_MENU_NAME = "parentMenuName";
|
||||
|
||||
/**
|
||||
* 数据库字符串类型
|
||||
*/
|
||||
public static final String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
|
||||
|
||||
/**
|
||||
* 数据库文本类型
|
||||
*/
|
||||
public static final String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
|
||||
|
||||
/**
|
||||
* 数据库时间类型
|
||||
*/
|
||||
public static final String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
|
||||
|
||||
/**
|
||||
* 数据库数字类型
|
||||
*/
|
||||
public static final String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
|
||||
"bit", "bigint", "float", "double", "decimal"};
|
||||
|
||||
/**
|
||||
* 页面不需要编辑字段
|
||||
*/
|
||||
public static final String[] COLUMNNAME_NOT_EDIT = {"id", "create_by", "create_time", "del_flag"};
|
||||
|
||||
/**
|
||||
* 页面不需要显示的列表字段
|
||||
*/
|
||||
public static final String[] COLUMNNAME_NOT_LIST = {"id", "create_by", "create_time", "del_flag", "update_by",
|
||||
"update_time"};
|
||||
|
||||
/**
|
||||
* 页面不需要查询字段
|
||||
*/
|
||||
public static final String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
|
||||
"update_time", "remark"};
|
||||
|
||||
/**
|
||||
* Entity基类字段
|
||||
*/
|
||||
public static final String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime", "remark"};
|
||||
|
||||
/**
|
||||
* Tree基类字段
|
||||
*/
|
||||
public static final String[] TREE_ENTITY = {"parentName", "parentId", "orderNum", "ancestors"};
|
||||
|
||||
/**
|
||||
* 文本框
|
||||
*/
|
||||
public static final String HTML_INPUT = "input";
|
||||
|
||||
/**
|
||||
* 文本域
|
||||
*/
|
||||
public static final String HTML_TEXTAREA = "textarea";
|
||||
|
||||
/**
|
||||
* 下拉框
|
||||
*/
|
||||
public static final String HTML_SELECT = "select";
|
||||
|
||||
/**
|
||||
* 单选框
|
||||
*/
|
||||
public static final String HTML_RADIO = "radio";
|
||||
|
||||
/**
|
||||
* 复选框
|
||||
*/
|
||||
public static final String HTML_CHECKBOX = "checkbox";
|
||||
|
||||
/**
|
||||
* 日期控件
|
||||
*/
|
||||
public static final String HTML_DATETIME = "datetime";
|
||||
|
||||
/**
|
||||
* 上传控件
|
||||
*/
|
||||
public static final String HTML_UPLOAD = "upload";
|
||||
|
||||
/**
|
||||
* 富文本控件
|
||||
*/
|
||||
public static final String HTML_SUMMERNOTE = "summernote";
|
||||
|
||||
/**
|
||||
* 字符串类型
|
||||
*/
|
||||
public static final String TYPE_STRING = "String";
|
||||
|
||||
/**
|
||||
* 整型
|
||||
*/
|
||||
public static final String TYPE_INTEGER = "Integer";
|
||||
|
||||
/**
|
||||
* 长整型
|
||||
*/
|
||||
public static final String TYPE_LONG = "Long";
|
||||
|
||||
/**
|
||||
* 浮点型
|
||||
*/
|
||||
public static final String TYPE_DOUBLE = "Double";
|
||||
|
||||
/**
|
||||
* 高精度计算类型
|
||||
*/
|
||||
public static final String TYPE_BIGDECIMAL = "BigDecimal";
|
||||
|
||||
/**
|
||||
* 时间类型
|
||||
*/
|
||||
public static final String TYPE_DATE = "Date";
|
||||
|
||||
/**
|
||||
* 模糊查询
|
||||
*/
|
||||
public static final String QUERY_LIKE = "LIKE";
|
||||
|
||||
/**
|
||||
* 需要
|
||||
*/
|
||||
public static final String REQUIRE = "1";
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.mosty.common.base.constant;
|
||||
|
||||
/**
|
||||
* 用户会话
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public enum OnlineStatus {
|
||||
/**
|
||||
* 用户状态
|
||||
*/
|
||||
on_line("在线"), off_line("离线");
|
||||
|
||||
private final String info;
|
||||
|
||||
private OnlineStatus(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.mosty.common.base.constant;
|
||||
|
||||
/**
|
||||
* 权限通用常量
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class PermissionConstants {
|
||||
/**
|
||||
* 新增权限
|
||||
*/
|
||||
public static final String ADD_PERMISSION = "add";
|
||||
|
||||
/**
|
||||
* 修改权限
|
||||
*/
|
||||
public static final String EDIT_PERMISSION = "edit";
|
||||
|
||||
/**
|
||||
* 删除权限
|
||||
*/
|
||||
public static final String REMOVE_PERMISSION = "remove";
|
||||
|
||||
/**
|
||||
* 导出权限
|
||||
*/
|
||||
public static final String EXPORT_PERMISSION = "export";
|
||||
|
||||
/**
|
||||
* 显示权限
|
||||
*/
|
||||
public static final String VIEW_PERMISSION = "view";
|
||||
|
||||
/**
|
||||
* 查询权限
|
||||
*/
|
||||
public static final String LIST_PERMISSION = "list";
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.mosty.common.base.constant;
|
||||
|
||||
/**
|
||||
* 任务调度通用常量
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ScheduleConstants {
|
||||
public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
|
||||
|
||||
/**
|
||||
* 执行目标key
|
||||
*/
|
||||
public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
|
||||
|
||||
/**
|
||||
* 默认
|
||||
*/
|
||||
public static final String MISFIRE_DEFAULT = "0";
|
||||
|
||||
/**
|
||||
* 立即触发执行
|
||||
*/
|
||||
public static final String MISFIRE_IGNORE_MISFIRES = "1";
|
||||
|
||||
/**
|
||||
* 触发一次执行
|
||||
*/
|
||||
public static final String MISFIRE_FIRE_AND_PROCEED = "2";
|
||||
|
||||
/**
|
||||
* 不触发立即执行
|
||||
*/
|
||||
public static final String MISFIRE_DO_NOTHING = "3";
|
||||
|
||||
public enum Status {
|
||||
/**
|
||||
* 正常
|
||||
*/
|
||||
NORMAL("0"),
|
||||
/**
|
||||
* 暂停
|
||||
*/
|
||||
PAUSE("1");
|
||||
|
||||
private String value;
|
||||
|
||||
private Status(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.mosty.common.base.constant;
|
||||
|
||||
/**
|
||||
* Shiro通用常量
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ShiroConstants {
|
||||
/**
|
||||
* 当前登录的用户
|
||||
*/
|
||||
public static final String CURRENT_USER = "currentUser";
|
||||
|
||||
/**
|
||||
* 用户名字段
|
||||
*/
|
||||
public static final String CURRENT_USERNAME = "username";
|
||||
|
||||
/**
|
||||
* 锁定屏幕字段
|
||||
*/
|
||||
public static final String LOCK_SCREEN = "lockscreen";
|
||||
|
||||
/**
|
||||
* 消息key
|
||||
*/
|
||||
public static final String MESSAGE = "message";
|
||||
|
||||
/**
|
||||
* 错误key
|
||||
*/
|
||||
public static final String ERROR = "errorMsg";
|
||||
|
||||
/**
|
||||
* 编码格式
|
||||
*/
|
||||
public static final String ENCODING = "UTF-8";
|
||||
|
||||
/**
|
||||
* 当前在线会话
|
||||
*/
|
||||
public static final String ONLINE_SESSION = "online_session";
|
||||
|
||||
/**
|
||||
* 验证码key
|
||||
*/
|
||||
public static final String CURRENT_CAPTCHA = "captcha";
|
||||
|
||||
/**
|
||||
* 验证码开关
|
||||
*/
|
||||
public static final String CURRENT_ENABLED = "captchaEnabled";
|
||||
|
||||
/**
|
||||
* 验证码类型
|
||||
*/
|
||||
public static final String CURRENT_TYPE = "captchaType";
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
public static final String CURRENT_VALIDATECODE = "validateCode";
|
||||
|
||||
/**
|
||||
* 验证码错误
|
||||
*/
|
||||
public static final String CAPTCHA_ERROR = "captchaError";
|
||||
|
||||
/**
|
||||
* 登录记录缓存
|
||||
*/
|
||||
public static final String LOGINRECORDCACHE = "loginRecordCache";
|
||||
|
||||
/**
|
||||
* 系统活跃用户缓存
|
||||
*/
|
||||
public static final String SYS_USERCACHE = "sys-userCache";
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.mosty.common.base.constant;
|
||||
|
||||
/**
|
||||
* 系统配置相关
|
||||
* @author kevin
|
||||
* @date 2022/3/21 10:46 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class SystemConfigConstants {
|
||||
|
||||
// token请求的 Header头 key
|
||||
public static final String TOKEN_HEADER = "Authorization";
|
||||
|
||||
// token加密key
|
||||
public static final String TOKEN_KEY = "RVvjAORWxgmDyvGP8RkD7HuaYEuekN7rXyaDaLn2jbk6OJvFupTXzicR883pFUmI";
|
||||
|
||||
// token 主题
|
||||
public static final String TOKEN_ISSUER = "mosty-admin";
|
||||
|
||||
// token redis的key
|
||||
public static final String TOKEN_PREFIX = "token:";
|
||||
|
||||
// redis中缓存的所有用户的登录信息
|
||||
// public static final String USER_INFO_LIST = "user_info_list:";
|
||||
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
package com.mosty.common.base.constant;
|
||||
|
||||
/**
|
||||
* 用户常量信息
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserConstants {
|
||||
|
||||
/**
|
||||
* 平台内系统用户的唯一标志
|
||||
*/
|
||||
public static final String SYS_USER = "SYS_USER";
|
||||
|
||||
/**
|
||||
* 正常状态
|
||||
*/
|
||||
public static final String NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 异常状态
|
||||
*/
|
||||
public static final String EXCEPTION = "1";
|
||||
|
||||
/**
|
||||
* 用户封禁状态
|
||||
*/
|
||||
public static final String USER_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 角色封禁状态
|
||||
*/
|
||||
public static final String ROLE_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 部门正常状态
|
||||
*/
|
||||
public static final String DEPT_NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 部门停用状态
|
||||
*/
|
||||
public static final String DEPT_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 字典正常状态
|
||||
*/
|
||||
public static final String DICT_NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 是否为系统默认(是)
|
||||
*/
|
||||
public static final String YES = "Y";
|
||||
|
||||
/**
|
||||
* 用户名长度限制
|
||||
*/
|
||||
public static final int USERNAME_MIN_LENGTH = 2;
|
||||
public static final int USERNAME_MAX_LENGTH = 20;
|
||||
|
||||
/**
|
||||
* 登录名称是否唯一的返回结果码
|
||||
*/
|
||||
public final static String USER_NAME_UNIQUE = "0";
|
||||
public final static String USER_NAME_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 手机号码是否唯一的返回结果
|
||||
*/
|
||||
public final static String USER_PHONE_UNIQUE = "0";
|
||||
public final static String USER_PHONE_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* e-mail 是否唯一的返回结果
|
||||
*/
|
||||
public final static String USER_EMAIL_UNIQUE = "0";
|
||||
public final static String USER_EMAIL_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 部门名称是否唯一的返回结果码
|
||||
*/
|
||||
public final static String DEPT_NAME_UNIQUE = "0";
|
||||
public final static String DEPT_NAME_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 角色名称是否唯一的返回结果码
|
||||
*/
|
||||
public final static String ROLE_NAME_UNIQUE = "0";
|
||||
public final static String ROLE_NAME_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 岗位名称是否唯一的返回结果码
|
||||
*/
|
||||
public final static String POST_NAME_UNIQUE = "0";
|
||||
public final static String POST_NAME_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 角色权限是否唯一的返回结果码
|
||||
*/
|
||||
public final static String ROLE_KEY_UNIQUE = "0";
|
||||
public final static String ROLE_KEY_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 岗位编码是否唯一的返回结果码
|
||||
*/
|
||||
public final static String POST_CODE_UNIQUE = "0";
|
||||
public final static String POST_CODE_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 菜单名称是否唯一的返回结果码
|
||||
*/
|
||||
public final static String MENU_NAME_UNIQUE = "0";
|
||||
public final static String MENU_NAME_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 字典类型是否唯一的返回结果码
|
||||
*/
|
||||
public final static String DICT_TYPE_UNIQUE = "0";
|
||||
public final static String DICT_TYPE_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 参数键名是否唯一的返回结果码
|
||||
*/
|
||||
public final static String CONFIG_KEY_UNIQUE = "0";
|
||||
public final static String CONFIG_KEY_NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 密码长度限制
|
||||
*/
|
||||
public static final int PASSWORD_MIN_LENGTH = 5;
|
||||
public static final int PASSWORD_MAX_LENGTH = 20;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
public static final String SYSTEM_USER_TYPE = "00";
|
||||
public static final String REGISTER_USER_TYPE = "01";
|
||||
|
||||
/**
|
||||
* 手机号码格式限制
|
||||
*/
|
||||
public static final String MOBILE_PHONE_NUMBER_PATTERN = "^0{0,1}(13[0-9]|15[0-9]|14[0-9]|18[0-9])[0-9]{8}$";
|
||||
|
||||
/**
|
||||
* 邮箱格式限制
|
||||
*/
|
||||
public static final String EMAIL_PATTERN = "^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?";
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.mosty.common.base.constant.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 系统配置key
|
||||
* @date 2022/2/16 11:06 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum ConfigKeyEnum {
|
||||
/** 密码等级 */
|
||||
PASSWORD_LEVEL("password.level", "密码等级");
|
||||
|
||||
|
||||
/** key */
|
||||
public final String code;
|
||||
|
||||
/** 描述 */
|
||||
public final String desc;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.mosty.common.base.constant.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 数据是否删除标识
|
||||
* @author kevin
|
||||
* @date 2022/2/16 11:06 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum DeletedEnum {
|
||||
|
||||
/** 正常状态 */
|
||||
NATURE(0, "正常状态"),
|
||||
|
||||
/** 注销状态 */
|
||||
DELETED(1, "注销状态");
|
||||
|
||||
/** 编码 */
|
||||
public final Integer code;
|
||||
|
||||
/** 描述 */
|
||||
public final String desc;
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.mosty.common.base.constant.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 字典类型枚举
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2022/2/20 3:52 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum DictTypeEnum {
|
||||
|
||||
/** 正常字典 */
|
||||
NATURE(1, "正常字典【列表】"),
|
||||
|
||||
/** 树型字典 */
|
||||
TREE(2, "树型结构");
|
||||
|
||||
/** 编码 */
|
||||
public final Integer code;
|
||||
|
||||
/** 描述 */
|
||||
public final String desc;
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.mosty.common.base.constant.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 菜单类型枚举
|
||||
* @author kevin
|
||||
* @date 2022/2/22 12:53 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum MenuTypeEnum {
|
||||
|
||||
MENU_GROUP(1, "菜单组"),
|
||||
MENU(2, "菜单"),
|
||||
PAGE(3, "页面"),
|
||||
RESOURCE(4, "资源"),
|
||||
;
|
||||
|
||||
/** 编码 */
|
||||
public final Integer code;
|
||||
|
||||
/** 描述 */
|
||||
public final String desc;
|
||||
|
||||
/**
|
||||
* 根据编码匹配菜单类型
|
||||
* @param code 编码
|
||||
* @return 菜单类型枚举
|
||||
*/
|
||||
public static MenuTypeEnum valueOf(Integer code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
for (MenuTypeEnum menuTypeEnum : MenuTypeEnum.values()) {
|
||||
if (menuTypeEnum.code.equals(code)) {
|
||||
return menuTypeEnum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.mosty.common.base.constant.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* 密码等级和对应规则
|
||||
* @date 2022/2/16 11:06 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum PasswordLevelEnum {
|
||||
|
||||
/** 密码等级 */
|
||||
PASSWORD_LEVEL_ONE(1, "");
|
||||
|
||||
|
||||
/** 等级 */
|
||||
public final Integer code;
|
||||
|
||||
/** 规则 */
|
||||
public final String rule;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.mosty.common.base.constant.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum PermissionLevelEnum {
|
||||
|
||||
|
||||
/**
|
||||
* 全部
|
||||
*/
|
||||
ALL("1", "全部"),
|
||||
|
||||
/**
|
||||
* 部门及以下
|
||||
*/
|
||||
DEPT_AND_CHILD("2", "部门及以下"),
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
DEPT("3", "仅部门权限"),
|
||||
/**
|
||||
* 部门及以下
|
||||
*/
|
||||
ONESELF("4", "仅查看本人");
|
||||
|
||||
|
||||
/**
|
||||
* 等级
|
||||
*/
|
||||
public String level;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
public String desc;
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.mosty.common.base.data.scope;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 数据权限过滤注解
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface DataScope {
|
||||
|
||||
/**
|
||||
* 部门表的别名
|
||||
*/
|
||||
String deptAlias() default "";
|
||||
|
||||
/**
|
||||
* 用户表的别名
|
||||
*/
|
||||
String userAlias() default "";
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
package com.mosty.common.base.domain;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.mosty.common.base.domain.page.PageUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* web层通用数据处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Slf4j
|
||||
public class BaseController {
|
||||
|
||||
/**
|
||||
* 将前台传递过来的日期格式的字符串,自动转化为Date类型
|
||||
*/
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder binder) {
|
||||
// Date 类型转换
|
||||
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
|
||||
@Override
|
||||
public void setAsText(String text) {
|
||||
try {
|
||||
setValue(DateUtils.parseDate(text));
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求分页数据
|
||||
*/
|
||||
protected void startPage() {
|
||||
PageUtils.startPage();
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 设置请求排序数据
|
||||
// */
|
||||
// protected void startOrderBy()
|
||||
// {
|
||||
// PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
// if (StringUtils.isNotEmpty(pageDomain.getOrderBy()))
|
||||
// {
|
||||
// String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
|
||||
// PageHelper.orderBy(orderBy);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取request
|
||||
// */
|
||||
// public HttpServletRequest getRequest()
|
||||
// {
|
||||
// return ServletUtils.getRequest();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取response
|
||||
// */
|
||||
// public HttpServletResponse getResponse()
|
||||
// {
|
||||
// return ServletUtils.getResponse();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取session
|
||||
// */
|
||||
// public HttpSession getSession()
|
||||
// {
|
||||
// return getRequest().getSession();
|
||||
// }
|
||||
//
|
||||
|
||||
/**
|
||||
* 响应请求分页数据
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected TableDataInfo getDataTable(List<?> list) {
|
||||
TableDataInfo rspData = new TableDataInfo();
|
||||
rspData.setCode(0);
|
||||
rspData.setRows(list);
|
||||
rspData.setTotal(new PageInfo(list).getTotal());
|
||||
return rspData;
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 响应返回结果
|
||||
// *
|
||||
// * @param rows 影响行数
|
||||
// * @return 操作结果
|
||||
// */
|
||||
// protected AjaxResult toAjax(int rows)
|
||||
// {
|
||||
// return rows > 0 ? success() : error();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 响应返回结果
|
||||
// *
|
||||
// * @param result 结果
|
||||
// * @return 操作结果
|
||||
// */
|
||||
// protected AjaxResult toAjax(boolean result)
|
||||
// {
|
||||
// return result ? success() : error();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 返回成功
|
||||
// */
|
||||
// public AjaxResult success()
|
||||
// {
|
||||
// return AjaxResult.success();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 返回失败消息
|
||||
// */
|
||||
// public AjaxResult error()
|
||||
// {
|
||||
// return AjaxResult.error();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 返回成功消息
|
||||
// */
|
||||
// public AjaxResult success(String message)
|
||||
// {
|
||||
// return AjaxResult.success(message);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 返回成功数据
|
||||
// */
|
||||
// public static AjaxResult success(Object data)
|
||||
// {
|
||||
// return AjaxResult.success("操作成功", data);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 返回失败消息
|
||||
// */
|
||||
// public AjaxResult error(String message)
|
||||
// {
|
||||
// return AjaxResult.error(message);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 返回错误码消息
|
||||
// */
|
||||
// public AjaxResult error(Type type, String message)
|
||||
// {
|
||||
// return new AjaxResult(type, message);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 页面跳转
|
||||
// */
|
||||
// public String redirect(String url)
|
||||
// {
|
||||
// return StringUtils.format("redirect:{}", url);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取用户缓存信息
|
||||
// */
|
||||
// public SysUser getSysUser()
|
||||
// {
|
||||
// return ShiroUtils.getSysUser();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 设置用户缓存信息
|
||||
// */
|
||||
// public void setSysUser(SysUser user)
|
||||
// {
|
||||
// ShiroUtils.setSysUser(user);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取登录用户id
|
||||
// */
|
||||
// public Long getUserId()
|
||||
// {
|
||||
// return getSysUser().getUserId();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取登录用户名
|
||||
// */
|
||||
// public String getLoginName()
|
||||
// {
|
||||
// return getSysUser().getLoginName();
|
||||
// }
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.mosty.common.base.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CxSelect树结构实体类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class CxSelect implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 数据值字段名称
|
||||
*/
|
||||
private String v;
|
||||
|
||||
/**
|
||||
* 数据标题字段名称
|
||||
*/
|
||||
private String n;
|
||||
|
||||
/**
|
||||
* 子集数据字段名称
|
||||
*/
|
||||
private List<CxSelect> s;
|
||||
|
||||
public CxSelect() {
|
||||
}
|
||||
|
||||
public CxSelect(String v, String n) {
|
||||
this.v = v;
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
public List<CxSelect> getS() {
|
||||
return s;
|
||||
}
|
||||
|
||||
public void setN(String n) {
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
public String getN() {
|
||||
return n;
|
||||
}
|
||||
|
||||
public void setS(List<CxSelect> s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public String getV() {
|
||||
return v;
|
||||
}
|
||||
|
||||
public void setV(String v) {
|
||||
this.v = v;
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package com.mosty.common.base.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 基础请求返回类
|
||||
* @author kevin
|
||||
* @date 2022/1/22 8:29 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
@SuppressWarnings("unused")
|
||||
public class ResponseResult<T> implements Serializable {
|
||||
|
||||
/** 调用成功编码 */
|
||||
private static final int SUCCESS_CODE = 10000;
|
||||
|
||||
/** 调用失败编码 */
|
||||
private static final int FAIL_CODE = -1;
|
||||
|
||||
/** 编号 */
|
||||
private int code = SUCCESS_CODE;
|
||||
|
||||
/** 消息 */
|
||||
private String message;
|
||||
|
||||
/** 数据 */
|
||||
private T data;
|
||||
|
||||
public ResponseResult() {
|
||||
}
|
||||
|
||||
public ResponseResult(T data) {
|
||||
this.message = "OK";
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public ResponseResult(int code, String msg) {
|
||||
this.code = code;
|
||||
this.message = msg;
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
public ResponseResult(int code, String msg, T data) {
|
||||
this.code = code;
|
||||
this.message = msg;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static <T> ResponseResult<T> success() {
|
||||
return new ResponseResult(SUCCESS_CODE, "OK", null);
|
||||
}
|
||||
|
||||
public static <T> ResponseResult<T> success(int code, String message, T data) {
|
||||
return new ResponseResult(code, message, data);
|
||||
}
|
||||
|
||||
public static <T> ResponseResult<T> success(String message, T data) {
|
||||
return new ResponseResult(SUCCESS_CODE, message, data);
|
||||
}
|
||||
|
||||
public static <T> ResponseResult<T> success(T data) {
|
||||
return new ResponseResult(SUCCESS_CODE, "OK", data);
|
||||
}
|
||||
|
||||
public static <T> ResponseResult<T> fail(String message) {
|
||||
return new ResponseResult(FAIL_CODE, message, (Object)null);
|
||||
}
|
||||
|
||||
public static <T> ResponseResult<T> fail(int code, String message) {
|
||||
return new ResponseResult(code, message, (Object)null);
|
||||
}
|
||||
|
||||
public static <T> ResponseResult<T> fail(int code, String message, T t) {
|
||||
return new ResponseResult(code, message, t);
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return SUCCESS_CODE == code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ResponseResult<?> that = (ResponseResult<?>) o;
|
||||
return Objects.equals(code, that.code) && Objects.equals(message, that.message) && Objects.equals(data, that.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(code, message, data);
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.mosty.common.base.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表格分页数据对象
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class TableDataInfo implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
private long total;
|
||||
|
||||
/**
|
||||
* 列表数据
|
||||
*/
|
||||
private List<?> rows;
|
||||
|
||||
/**
|
||||
* 消息状态码
|
||||
*/
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 表格数据对象
|
||||
*/
|
||||
public TableDataInfo() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*
|
||||
* @param list 列表数据
|
||||
* @param total 总记录数
|
||||
*/
|
||||
public TableDataInfo(List<?> list, int total) {
|
||||
this.rows = list;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public List<?> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void setRows(List<?> rows) {
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.mosty.common.base.domain.page;
|
||||
|
||||
|
||||
import com.mosty.common.base.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 分页数据
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class PageDomain {
|
||||
/**
|
||||
* 当前记录起始索引
|
||||
*/
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
private String orderByColumn;
|
||||
|
||||
/**
|
||||
* 排序的方向desc或者asc
|
||||
*/
|
||||
private String isAsc = "asc";
|
||||
|
||||
/**
|
||||
* 分页参数合理化
|
||||
*/
|
||||
private Boolean reasonable = true;
|
||||
|
||||
public String getOrderBy() {
|
||||
if (StringUtils.isEmpty(orderByColumn)) {
|
||||
return "";
|
||||
}
|
||||
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
|
||||
}
|
||||
|
||||
public Integer getPageNum() {
|
||||
return pageNum;
|
||||
}
|
||||
|
||||
public void setPageNum(Integer pageNum) {
|
||||
this.pageNum = pageNum;
|
||||
}
|
||||
|
||||
public Integer getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(Integer pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public String getOrderByColumn() {
|
||||
return orderByColumn;
|
||||
}
|
||||
|
||||
public void setOrderByColumn(String orderByColumn) {
|
||||
this.orderByColumn = orderByColumn;
|
||||
}
|
||||
|
||||
public String getIsAsc() {
|
||||
return isAsc;
|
||||
}
|
||||
|
||||
public void setIsAsc(String isAsc) {
|
||||
this.isAsc = isAsc;
|
||||
}
|
||||
|
||||
public Boolean getReasonable() {
|
||||
if (StringUtils.isNull(reasonable)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
return reasonable;
|
||||
}
|
||||
|
||||
public void setReasonable(Boolean reasonable) {
|
||||
this.reasonable = reasonable;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.mosty.common.base.domain.page;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 分页工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class PageUtils extends PageHelper {
|
||||
|
||||
/**
|
||||
* 设置请求分页数据
|
||||
*/
|
||||
public static void startPage() {
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
Integer pageNum = pageDomain.getPageNum();
|
||||
Integer pageSize = pageDomain.getPageSize();
|
||||
if (!Objects.isNull(pageNum) && !Objects.isNull(pageSize)) {
|
||||
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
|
||||
Boolean reasonable = pageDomain.getReasonable();
|
||||
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
package com.mosty.common.base.domain.page;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 客户端工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ServletUtils {
|
||||
/**
|
||||
* 定义移动端请求的所有可能类型
|
||||
*/
|
||||
private final static String[] agent = {"Android", "iPhone", "iPod", "iPad", "Windows Phone", "MQQBrowser"};
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name) {
|
||||
return getRequest().getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name, String defaultValue) {
|
||||
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name) {
|
||||
return Convert.toInt(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name, Integer defaultValue) {
|
||||
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name) {
|
||||
return Convert.toBool(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name, Boolean defaultValue) {
|
||||
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取request
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
return getRequestAttributes().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取response
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
return getRequestAttributes().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
public static ServletRequestAttributes getRequestAttributes() {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes) attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串渲染到客户端
|
||||
*
|
||||
* @param response 渲染对象
|
||||
* @param string 待渲染的字符串
|
||||
* @return null
|
||||
*/
|
||||
public static String renderString(HttpServletResponse response, String string) {
|
||||
try {
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.getWriter().print(string);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是Ajax异步请求
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
// public static boolean isAjaxRequest(HttpServletRequest request) {
|
||||
// String accept = request.getHeader("accept");
|
||||
// if (accept != null && accept.indexOf("application/json") != -1) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// String xRequestedWith = request.getHeader("X-Requested-With");
|
||||
// if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// String uri = request.getRequestURI();
|
||||
// if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// String ajax = request.getParameter("__ajax");
|
||||
// if (StringUtils.inStringIgnoreCase(ajax, "json", "xml")) {
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 判断User-Agent 是不是来自于手机
|
||||
*/
|
||||
public static boolean checkAgentIsMobile(String ua) {
|
||||
boolean flag = false;
|
||||
if (!ua.contains("Windows NT") || (ua.contains("Windows NT") && ua.contains("compatible; MSIE 9.0;"))) {
|
||||
// 排除 苹果桌面系统
|
||||
if (!ua.contains("Windows NT") && !ua.contains("Macintosh")) {
|
||||
for (String item : agent) {
|
||||
if (ua.contains(item)) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.mosty.common.base.domain.page;
|
||||
|
||||
import com.mosty.common.base.exception.BusinessException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* sql操作工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SqlUtil {
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
|
||||
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
*/
|
||||
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
|
||||
|
||||
/**
|
||||
* 检查字符,防止注入绕过
|
||||
*/
|
||||
public static String escapeOrderBySql(String value) {
|
||||
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
|
||||
throw new BusinessException("参数不符合规范,不能进行查询");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 order by 语法是否符合规范
|
||||
*/
|
||||
public static boolean isValidOrderBySql(String value) {
|
||||
return value.matches(SQL_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL关键字检查
|
||||
*/
|
||||
public static void filterKeyword(String value) {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||
for (int i = 0; i < sqlKeywords.length; i++) {
|
||||
if (StringUtils.indexOfIgnoreCase(value, sqlKeywords[i]) > -1) {
|
||||
throw new BusinessException("参数存在SQL注入风险");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.mosty.common.base.domain.page;
|
||||
|
||||
|
||||
/**
|
||||
* 表格数据处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class TableSupport {
|
||||
/**
|
||||
* 当前记录起始索引
|
||||
*/
|
||||
public static final String PAGE_NUM = "pageNum";
|
||||
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
public static final String PAGE_SIZE = "pageSize";
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
public static final String ORDER_BY_COLUMN = "orderByColumn";
|
||||
|
||||
/**
|
||||
* 排序的方向 "desc" 或者 "asc".
|
||||
*/
|
||||
public static final String IS_ASC = "isAsc";
|
||||
|
||||
/**
|
||||
* 分页参数合理化
|
||||
*/
|
||||
public static final String REASONABLE = "reasonable";
|
||||
|
||||
/**
|
||||
* 封装分页对象
|
||||
*/
|
||||
public static PageDomain getPageDomain() {
|
||||
PageDomain pageDomain = new PageDomain();
|
||||
pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM));
|
||||
pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE));
|
||||
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
|
||||
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
|
||||
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
|
||||
return pageDomain;
|
||||
}
|
||||
|
||||
public static PageDomain buildPageRequest() {
|
||||
return getPageDomain();
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Entity基类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
private String searchValue;
|
||||
|
||||
/**
|
||||
* 创建者id
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 创建者名称
|
||||
*/
|
||||
private String createName;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新者id
|
||||
*/
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
* 更新者名称
|
||||
*/
|
||||
private String updateName;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
private Map<String, Object> params;
|
||||
|
||||
public String getSearchValue() {
|
||||
return searchValue;
|
||||
}
|
||||
|
||||
public void setSearchValue(String searchValue) {
|
||||
this.searchValue = searchValue;
|
||||
}
|
||||
|
||||
public Long getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(Long createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public String getCreateName() {
|
||||
return createName;
|
||||
}
|
||||
|
||||
public void setCreateName(String createName) {
|
||||
this.createName = createName;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(LocalDateTime createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public Long getUpdateBy() {
|
||||
return updateBy;
|
||||
}
|
||||
|
||||
public void setUpdateBy(Long updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
}
|
||||
|
||||
public String getUpdateName() {
|
||||
return updateName;
|
||||
}
|
||||
|
||||
public void setUpdateName(String updateName) {
|
||||
this.updateName = updateName;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdateTime() {
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
public void setUpdateTime(LocalDateTime updateTime) {
|
||||
this.updateTime = updateTime;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Map<String, Object> getParams() {
|
||||
if (params == null) {
|
||||
params = new HashMap<>();
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(Map<String, Object> params) {
|
||||
this.params = params;
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
/**
|
||||
* 业务操作类型
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public enum BusinessType {
|
||||
// 其它
|
||||
OTHER,
|
||||
|
||||
// 新增
|
||||
INSERT,
|
||||
|
||||
// 修改
|
||||
UPDATE,
|
||||
|
||||
// 删除
|
||||
DELETE,
|
||||
|
||||
// 授权
|
||||
GRANT,
|
||||
|
||||
// 导出
|
||||
EXPORT,
|
||||
|
||||
// 导入
|
||||
IMPORT,
|
||||
|
||||
// 强退
|
||||
FORCE,
|
||||
|
||||
// 生成代码
|
||||
GENCODE,
|
||||
|
||||
// 清空
|
||||
CLEAN,
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 字符集工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class CharsetKit
|
||||
{
|
||||
/** ISO-8859-1 */
|
||||
public static final String ISO_8859_1 = "ISO-8859-1";
|
||||
/** UTF-8 */
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
/** GBK */
|
||||
public static final String GBK = "GBK";
|
||||
|
||||
/** ISO-8859-1 */
|
||||
public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
|
||||
/** UTF-8 */
|
||||
public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
|
||||
/** GBK */
|
||||
public static final Charset CHARSET_GBK = Charset.forName(GBK);
|
||||
|
||||
/**
|
||||
* 转换为Charset对象
|
||||
*
|
||||
* @param charset 字符集,为空则返回默认字符集
|
||||
* @return Charset
|
||||
*/
|
||||
public static Charset charset(String charset)
|
||||
{
|
||||
return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换字符串的字符集编码
|
||||
*
|
||||
* @param source 字符串
|
||||
* @param srcCharset 源字符集,默认ISO-8859-1
|
||||
* @param destCharset 目标字符集,默认UTF-8
|
||||
* @return 转换后的字符集
|
||||
*/
|
||||
public static String convert(String source, String srcCharset, String destCharset)
|
||||
{
|
||||
return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换字符串的字符集编码
|
||||
*
|
||||
* @param source 字符串
|
||||
* @param srcCharset 源字符集,默认ISO-8859-1
|
||||
* @param destCharset 目标字符集,默认UTF-8
|
||||
* @return 转换后的字符集
|
||||
*/
|
||||
public static String convert(String source, Charset srcCharset, Charset destCharset)
|
||||
{
|
||||
if (null == srcCharset)
|
||||
{
|
||||
srcCharset = StandardCharsets.ISO_8859_1;
|
||||
}
|
||||
|
||||
if (null == destCharset)
|
||||
{
|
||||
destCharset = StandardCharsets.UTF_8;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
|
||||
{
|
||||
return source;
|
||||
}
|
||||
return new String(source.getBytes(srcCharset), destCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 系统字符集编码
|
||||
*/
|
||||
public static String systemCharset()
|
||||
{
|
||||
return Charset.defaultCharset().name();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,188 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 自定义导出Excel数据注解
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Excel {
|
||||
/**
|
||||
* 导出时在excel中排序
|
||||
*/
|
||||
public int sort() default Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* 导出到Excel中的名字.
|
||||
*/
|
||||
public String name() default "";
|
||||
|
||||
/**
|
||||
* 日期格式, 如: yyyy-MM-dd
|
||||
*/
|
||||
public String dateFormat() default "";
|
||||
|
||||
/**
|
||||
* 读取内容转表达式 (如: 0=男,1=女,2=未知)
|
||||
*/
|
||||
public String readConverterExp() default "";
|
||||
|
||||
/**
|
||||
* 分隔符,读取字符串组内容
|
||||
*/
|
||||
public String separator() default ",";
|
||||
|
||||
/**
|
||||
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
|
||||
*/
|
||||
public int scale() default -1;
|
||||
|
||||
/**
|
||||
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
|
||||
*/
|
||||
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
|
||||
|
||||
/**
|
||||
* 导出时在excel中每个列的高度
|
||||
*/
|
||||
public double height() default 14;
|
||||
|
||||
/**
|
||||
* 导出时在excel中每个列的宽度
|
||||
*/
|
||||
public double width() default 16;
|
||||
|
||||
/**
|
||||
* 文字后缀,如% 90 变成90%
|
||||
*/
|
||||
public String suffix() default "";
|
||||
|
||||
/**
|
||||
* 当值为空时,字段的默认值
|
||||
*/
|
||||
public String defaultValue() default "";
|
||||
|
||||
/**
|
||||
* 提示信息
|
||||
*/
|
||||
public String prompt() default "";
|
||||
|
||||
/**
|
||||
* 设置只能选择不能输入的列内容.
|
||||
*/
|
||||
public String[] combo() default {};
|
||||
|
||||
/**
|
||||
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
|
||||
*/
|
||||
public boolean needMerge() default false;
|
||||
|
||||
/**
|
||||
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
|
||||
*/
|
||||
public boolean isExport() default true;
|
||||
|
||||
/**
|
||||
* 另一个类中的属性名称,支持多级获取,以小数点隔开
|
||||
*/
|
||||
public String targetAttr() default "";
|
||||
|
||||
/**
|
||||
* 是否自动统计数据,在最后追加一行统计数据总和
|
||||
*/
|
||||
public boolean isStatistics() default false;
|
||||
|
||||
|
||||
/**
|
||||
* 如果是字典类型,请设置字典的type值
|
||||
* >>>>>>>特别提醒,如若配置该字段,则“combo”和“readConverterExp”不启用
|
||||
*/
|
||||
public String dictType() default "";
|
||||
|
||||
/**
|
||||
* 导出类型(0数字 1字符串)
|
||||
*/
|
||||
public ColumnType cellType() default ColumnType.STRING;
|
||||
|
||||
/**
|
||||
* 导出列头背景颜色
|
||||
*/
|
||||
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
|
||||
|
||||
/**
|
||||
* 导出列头字体颜色
|
||||
*/
|
||||
public IndexedColors headerColor() default IndexedColors.WHITE;
|
||||
|
||||
/**
|
||||
* 导出单元格背景颜色
|
||||
*/
|
||||
public IndexedColors backgroundColor() default IndexedColors.WHITE;
|
||||
|
||||
/**
|
||||
* 导出单元格字体颜色
|
||||
*/
|
||||
public IndexedColors color() default IndexedColors.BLACK;
|
||||
|
||||
/**
|
||||
* 导出字段对齐方式
|
||||
*/
|
||||
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
|
||||
|
||||
/**
|
||||
* 自定义数据处理器
|
||||
*/
|
||||
public Class<?> handler() default ExcelHandlerAdapter.class;
|
||||
|
||||
/**
|
||||
* 自定义数据处理器参数
|
||||
*/
|
||||
public String[] args() default {};
|
||||
|
||||
/**
|
||||
* 字段类型(0:导出导入;1:仅导出;2:仅导入)
|
||||
*/
|
||||
Type type() default Type.ALL;
|
||||
|
||||
public enum Type
|
||||
{
|
||||
ALL(0), EXPORT(1), IMPORT(2);
|
||||
private final int value;
|
||||
|
||||
Type(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value()
|
||||
{
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ColumnType
|
||||
{
|
||||
NUMERIC(0), STRING(1), IMAGE(2);
|
||||
private final int value;
|
||||
|
||||
ColumnType(int value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value()
|
||||
{
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
/**
|
||||
* Excel数据格式处理适配器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface ExcelHandlerAdapter {
|
||||
/**
|
||||
* 格式化
|
||||
*
|
||||
* @param value 单元格数据值
|
||||
* @param args excel注解args参数组
|
||||
* @return 处理后的值
|
||||
*/
|
||||
Object format(Object value, String[] args);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Excel注解集
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Excels {
|
||||
|
||||
Excel[] value();
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
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 kevin
|
||||
* @date 2022/1/25 9:10 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target({ ElementType.PARAMETER, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Log {
|
||||
|
||||
// 模块
|
||||
String title() default "";
|
||||
|
||||
// 功能
|
||||
BusinessType businessType() default BusinessType.OTHER;
|
||||
|
||||
// 操作人类别
|
||||
OperatorType operatorType() default OperatorType.MANAGE;
|
||||
|
||||
// 是否保存请求的参数
|
||||
boolean isSaveRequestData() default true;
|
||||
|
||||
// 是否保存响应的参数
|
||||
boolean isSaveResponseData() default true;
|
||||
|
||||
// 是否上传数据
|
||||
boolean isScSj() default false;
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
/**
|
||||
* 操作日志静态变量
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2022/1/25 9:36 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class LogConst {
|
||||
|
||||
// 操作日志线程池名称
|
||||
public static final String LOG_THREAD_POOL = "OPERATION_LOG";
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
/**
|
||||
* 操作人类别
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2022/1/25 9:10 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum OperatorType {
|
||||
/**
|
||||
* 其它
|
||||
*/
|
||||
OTHER,
|
||||
|
||||
/**
|
||||
* 后台用户
|
||||
*/
|
||||
MANAGE,
|
||||
|
||||
/**
|
||||
* 手机端用户
|
||||
*/
|
||||
MOBILE
|
||||
}
|
@ -0,0 +1,410 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Date;
|
||||
|
||||
import com.mosty.common.base.util.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ReflectUtils
|
||||
{
|
||||
private static final String SETTER_PREFIX = "set";
|
||||
|
||||
private static final String GETTER_PREFIX = "get";
|
||||
|
||||
private static final String CGLIB_CLASS_SEPARATOR = "$$";
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
|
||||
|
||||
/**
|
||||
* 调用Getter方法.
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeGetter(Object obj, String propertyName)
|
||||
{
|
||||
Object object = obj;
|
||||
for (String name : StringUtils.split(propertyName, "."))
|
||||
{
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
|
||||
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
|
||||
}
|
||||
return (E) object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用Setter方法, 仅匹配方法名。
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
public static <E> void invokeSetter(Object obj, String propertyName, E value)
|
||||
{
|
||||
Object object = obj;
|
||||
String[] names = StringUtils.split(propertyName, ".");
|
||||
for (int i = 0; i < names.length; i++)
|
||||
{
|
||||
if (i < names.length - 1)
|
||||
{
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
|
||||
}
|
||||
else
|
||||
{
|
||||
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
invokeMethodByName(object, setterMethodName, new Object[] { value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E getFieldValue(final Object obj, final String fieldName)
|
||||
{
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null)
|
||||
{
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return null;
|
||||
}
|
||||
E result = null;
|
||||
try
|
||||
{
|
||||
result = (E) field.get(obj);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
logger.error("不可能抛出的异常{}", e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
|
||||
*/
|
||||
public static <E> void setFieldValue(final Object obj, final String fieldName, final E value)
|
||||
{
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null)
|
||||
{
|
||||
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
field.set(obj, value);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
logger.error("不可能抛出的异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符.
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
|
||||
* 同时匹配方法名+参数类型,
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
|
||||
final Object[] args)
|
||||
{
|
||||
if (obj == null || methodName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
|
||||
if (method == null)
|
||||
{
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return (E) method.invoke(obj, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符,
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
|
||||
* 只匹配函数名,如果有多个同名函数调用第一个。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args)
|
||||
{
|
||||
Method method = getAccessibleMethodByName(obj, methodName, args.length);
|
||||
if (method == null)
|
||||
{
|
||||
// 如果为空不报错,直接返回空。
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
// 类型转换(将参数数据类型转换为目标方法参数类型)
|
||||
Class<?>[] cs = method.getParameterTypes();
|
||||
for (int i = 0; i < cs.length; i++)
|
||||
{
|
||||
if (args[i] != null && !args[i].getClass().equals(cs[i]))
|
||||
{
|
||||
if (cs[i] == String.class)
|
||||
{
|
||||
args[i] = Convert.toStr(args[i]);
|
||||
if (StringUtils.endsWith((String) args[i], ".0"))
|
||||
{
|
||||
args[i] = StringUtils.substringBefore((String) args[i], ".0");
|
||||
}
|
||||
}
|
||||
else if (cs[i] == Integer.class)
|
||||
{
|
||||
args[i] = Convert.toInt(args[i]);
|
||||
}
|
||||
else if (cs[i] == Long.class)
|
||||
{
|
||||
args[i] = Convert.toLong(args[i]);
|
||||
}
|
||||
else if (cs[i] == Double.class)
|
||||
{
|
||||
args[i] = Convert.toDouble(args[i]);
|
||||
}
|
||||
else if (cs[i] == Float.class)
|
||||
{
|
||||
args[i] = Convert.toFloat(args[i]);
|
||||
}
|
||||
else if (cs[i] == Date.class)
|
||||
{
|
||||
if (args[i] instanceof String)
|
||||
{
|
||||
args[i] = DateUtils.parseDate(args[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = DateUtil.getJavaDate((Double) args[i]);
|
||||
}
|
||||
}
|
||||
else if (cs[i] == boolean.class || cs[i] == Boolean.class)
|
||||
{
|
||||
args[i] = Convert.toBool(args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (E) method.invoke(obj, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
*/
|
||||
public static Field getAccessibleField(final Object obj, final String fieldName)
|
||||
{
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(fieldName, "fieldName can't be blank");
|
||||
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
|
||||
{
|
||||
try
|
||||
{
|
||||
Field field = superClass.getDeclaredField(fieldName);
|
||||
makeAccessible(field);
|
||||
return field;
|
||||
}
|
||||
catch (NoSuchFieldException e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 匹配函数名+参数类型。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethod(final Object obj, final String methodName,
|
||||
final Class<?>... parameterTypes)
|
||||
{
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
|
||||
{
|
||||
try
|
||||
{
|
||||
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 只匹配函数名。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum)
|
||||
{
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
|
||||
{
|
||||
Method[] methods = searchType.getDeclaredMethods();
|
||||
for (Method method : methods)
|
||||
{
|
||||
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum)
|
||||
{
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible(Method method)
|
||||
{
|
||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible())
|
||||
{
|
||||
method.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible(Field field)
|
||||
{
|
||||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|
||||
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
|
||||
{
|
||||
field.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
|
||||
* 如无法找到, 返回Object.class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> getClassGenricType(final Class clazz)
|
||||
{
|
||||
return getClassGenricType(clazz, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
|
||||
* 如无法找到, 返回Object.class.
|
||||
*/
|
||||
public static Class getClassGenricType(final Class clazz, final int index)
|
||||
{
|
||||
Type genType = clazz.getGenericSuperclass();
|
||||
|
||||
if (!(genType instanceof ParameterizedType))
|
||||
{
|
||||
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
|
||||
|
||||
if (index >= params.length || index < 0)
|
||||
{
|
||||
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
|
||||
+ params.length);
|
||||
return Object.class;
|
||||
}
|
||||
if (!(params[index] instanceof Class))
|
||||
{
|
||||
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
return (Class) params[index];
|
||||
}
|
||||
|
||||
public static Class<?> getUserClass(Object instance)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
throw new RuntimeException("Instance must not be null");
|
||||
}
|
||||
Class clazz = instance.getClass();
|
||||
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR))
|
||||
{
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass != null && !Object.class.equals(superClass))
|
||||
{
|
||||
return superClass;
|
||||
}
|
||||
}
|
||||
return clazz;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将反射时的checked exception转换为unchecked exception.
|
||||
*/
|
||||
public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e)
|
||||
{
|
||||
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|
||||
|| e instanceof NoSuchMethodException)
|
||||
{
|
||||
return new IllegalArgumentException(msg, e);
|
||||
}
|
||||
else if (e instanceof InvocationTargetException)
|
||||
{
|
||||
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
|
||||
}
|
||||
return new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* spring工具类 方便在非spring管理环境中获取bean
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public final class SpringUtils implements BeanFactoryPostProcessor
|
||||
{
|
||||
/** Spring应用上下文环境 */
|
||||
private static ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
|
||||
{
|
||||
SpringUtils.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param name
|
||||
* @return Object 一个以所给名字注册的bean的实例
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String name) throws BeansException
|
||||
{
|
||||
return (T) beanFactory.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型为requiredType的对象
|
||||
*
|
||||
* @param clz
|
||||
* @return
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clz) throws BeansException
|
||||
{
|
||||
T result = (T) beanFactory.getBean(clz);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean containsBean(String name)
|
||||
{
|
||||
return beanFactory.containsBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||
*
|
||||
* @param name
|
||||
* @return boolean
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.isSingleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return Class 注册对象的类型
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.getType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*
|
||||
*/
|
||||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
|
||||
{
|
||||
return beanFactory.getAliases(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取aop代理对象
|
||||
*
|
||||
* @param invoker
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy(T invoker)
|
||||
{
|
||||
return (T) AopContext.currentProxy();
|
||||
}
|
||||
}
|
@ -0,0 +1,562 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.hutool.core.text.StrFormatter;
|
||||
import com.mosty.common.base.constant.Constants;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
{
|
||||
/** 空字符串 */
|
||||
private static final String NULLSTR = "";
|
||||
|
||||
/** 下划线 */
|
||||
private static final char SEPARATOR = '_';
|
||||
|
||||
/**
|
||||
* 获取参数不为空值
|
||||
*
|
||||
* @param value defaultValue 要判断的value
|
||||
* @return value 返回值
|
||||
*/
|
||||
public static <T> T nvl(T value, T defaultValue)
|
||||
{
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否为空, 包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Collection<?> coll)
|
||||
{
|
||||
return isNull(coll) || coll.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否非空,包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Collection<?> coll)
|
||||
{
|
||||
return !isEmpty(coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否为空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
** @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Object[] objects)
|
||||
{
|
||||
return isNull(objects) || (objects.length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否非空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Object[] objects)
|
||||
{
|
||||
return !isEmpty(objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Map<?, ?> map)
|
||||
{
|
||||
return isNull(map) || map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Map<?, ?> map)
|
||||
{
|
||||
return !isEmpty(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(String str)
|
||||
{
|
||||
return isNull(str) || NULLSTR.equals(str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为非空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:非空串 false:空串
|
||||
*/
|
||||
public static boolean isNotEmpty(String str)
|
||||
{
|
||||
return !isEmpty(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否为空
|
||||
*
|
||||
* @param object Object
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isNull(Object object)
|
||||
{
|
||||
return object == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否非空
|
||||
*
|
||||
* @param object Object
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotNull(Object object)
|
||||
{
|
||||
return !isNull(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否是数组类型(Java基本型别的数组)
|
||||
*
|
||||
* @param object 对象
|
||||
* @return true:是数组 false:不是数组
|
||||
*/
|
||||
public static boolean isArray(Object object)
|
||||
{
|
||||
return isNotNull(object) && object.getClass().isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 去空格
|
||||
*/
|
||||
public static String trim(String str)
|
||||
{
|
||||
return (str == null ? "" : str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0)
|
||||
{
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (start < 0)
|
||||
{
|
||||
start = 0;
|
||||
}
|
||||
if (start > str.length())
|
||||
{
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
return str.substring(start);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @param end 结束
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start, int end)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (end < 0)
|
||||
{
|
||||
end = str.length() + end;
|
||||
}
|
||||
if (start < 0)
|
||||
{
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (end > str.length())
|
||||
{
|
||||
end = str.length();
|
||||
}
|
||||
|
||||
if (start > end)
|
||||
{
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0)
|
||||
{
|
||||
start = 0;
|
||||
}
|
||||
if (end < 0)
|
||||
{
|
||||
end = 0;
|
||||
}
|
||||
|
||||
return str.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为空,并且不是空白字符
|
||||
*
|
||||
* @param str 要判断的value
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean hasText(String str)
|
||||
{
|
||||
return (str != null && !str.isEmpty() && containsText(str));
|
||||
}
|
||||
|
||||
private static boolean containsText(CharSequence str)
|
||||
{
|
||||
int strLen = str.length();
|
||||
for (int i = 0; i < strLen; i++)
|
||||
{
|
||||
if (!Character.isWhitespace(str.charAt(i)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文本, {} 表示占位符<br>
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||
* 例:<br>
|
||||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||
*
|
||||
* @param template 文本模板,被替换的部分用 {} 表示
|
||||
* @param params 参数值
|
||||
* @return 格式化后的文本
|
||||
*/
|
||||
public static String format(String template, Object... params)
|
||||
{
|
||||
if (isEmpty(params) || isEmpty(template))
|
||||
{
|
||||
return template;
|
||||
}
|
||||
return StrFormatter.format(template, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为http(s)://开头
|
||||
*
|
||||
* @param link 链接
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean ishttp(String link)
|
||||
{
|
||||
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
|
||||
*
|
||||
* @param collection 给定的集合
|
||||
* @param array 给定的数组
|
||||
* @return boolean 结果
|
||||
*/
|
||||
public static boolean containsAny(Collection<String> collection, String... array)
|
||||
{
|
||||
if (isEmpty(collection) || isEmpty(array))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (String str : array)
|
||||
{
|
||||
if (collection.contains(str))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线命名
|
||||
*/
|
||||
public static String toUnderScoreCase(String str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 前置字符是否大写
|
||||
boolean preCharIsUpperCase = true;
|
||||
// 当前字符是否大写
|
||||
boolean curreCharIsUpperCase = true;
|
||||
// 下一字符是否大写
|
||||
boolean nexteCharIsUpperCase = true;
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
{
|
||||
char c = str.charAt(i);
|
||||
if (i > 0)
|
||||
{
|
||||
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
preCharIsUpperCase = false;
|
||||
}
|
||||
|
||||
curreCharIsUpperCase = Character.isUpperCase(c);
|
||||
|
||||
if (i < (str.length() - 1))
|
||||
{
|
||||
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
|
||||
}
|
||||
|
||||
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
|
||||
{
|
||||
sb.append(SEPARATOR);
|
||||
}
|
||||
else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
|
||||
{
|
||||
sb.append(SEPARATOR);
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含字符串
|
||||
*
|
||||
* @param str 验证字符串
|
||||
* @param strs 字符串组
|
||||
* @return 包含返回true
|
||||
*/
|
||||
public static boolean inStringIgnoreCase(String str, String... strs)
|
||||
{
|
||||
if (str != null && strs != null)
|
||||
{
|
||||
for (String s : strs)
|
||||
{
|
||||
if (str.equalsIgnoreCase(trim(s)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
|
||||
*
|
||||
* @param name 转换前的下划线大写方式命名的字符串
|
||||
* @return 转换后的驼峰式命名的字符串
|
||||
*/
|
||||
public static String convertToCamelCase(String name)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
// 快速检查
|
||||
if (name == null || name.isEmpty())
|
||||
{
|
||||
// 没必要转换
|
||||
return "";
|
||||
}
|
||||
else if (!name.contains("_"))
|
||||
{
|
||||
// 不含下划线,仅将首字母大写
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
// 用下划线将原始字符串分割
|
||||
String[] camels = name.split("_");
|
||||
for (String camel : camels)
|
||||
{
|
||||
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
||||
if (camel.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// 首字母大写
|
||||
result.append(camel.substring(0, 1).toUpperCase());
|
||||
result.append(camel.substring(1).toLowerCase());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰式命名法
|
||||
* 例如:user_name->userName
|
||||
*/
|
||||
public static String toCamelCase(String s)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (s.indexOf(SEPARATOR) == -1)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
s = s.toLowerCase();
|
||||
StringBuilder sb = new StringBuilder(s.length());
|
||||
boolean upperCase = false;
|
||||
for (int i = 0; i < s.length(); i++)
|
||||
{
|
||||
char c = s.charAt(i);
|
||||
|
||||
if (c == SEPARATOR)
|
||||
{
|
||||
upperCase = true;
|
||||
}
|
||||
else if (upperCase)
|
||||
{
|
||||
sb.append(Character.toUpperCase(c));
|
||||
upperCase = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
|
||||
*
|
||||
* @param str 指定字符串
|
||||
* @param strs 需要检查的字符串数组
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean matches(String str, List<String> strs)
|
||||
{
|
||||
if (isEmpty(str) || isEmpty(strs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (String pattern : strs)
|
||||
{
|
||||
if (isMatch(pattern, str))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断url是否与规则配置:
|
||||
* ? 表示单个字符;
|
||||
* * 表示一层路径内的任意字符串,不可跨层级;
|
||||
* ** 表示任意层路径;
|
||||
*
|
||||
* @param pattern 匹配规则
|
||||
* @param url 需要匹配的url
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String url)
|
||||
{
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
return matcher.match(pattern, url);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T cast(Object obj)
|
||||
{
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||
*
|
||||
* @param num 数字对象
|
||||
* @param size 字符串指定长度
|
||||
* @return 返回数字的字符串格式,该字符串为指定长度。
|
||||
*/
|
||||
public static final String padl(final Number num, final int size)
|
||||
{
|
||||
return padl(num.toString(), size, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
||||
*
|
||||
* @param s 原始字符串
|
||||
* @param size 字符串指定长度
|
||||
* @param c 用于补齐的字符
|
||||
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
|
||||
*/
|
||||
public static final String padl(final String s, final int size, final char c)
|
||||
{
|
||||
final StringBuilder sb = new StringBuilder(size);
|
||||
if (s != null)
|
||||
{
|
||||
final int len = s.length();
|
||||
if (s.length() <= size)
|
||||
{
|
||||
for (int i = size - len; i > 0; i--)
|
||||
{
|
||||
sb.append(c);
|
||||
}
|
||||
sb.append(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
return s.substring(len - size, len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = size; i > 0; i--)
|
||||
{
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package com.mosty.common.base.entity.log;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
@ApiModel(value = "巡防力量表", description = "")
|
||||
@TableName("sys_oper_log")
|
||||
@Validated
|
||||
public class SysOperLog implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2222248015038776527L;
|
||||
|
||||
@Excel(name = "操作序号", cellType = Excel.ColumnType.NUMERIC)
|
||||
private Long operId;
|
||||
|
||||
@Excel(name = "模块名称")
|
||||
@ApiModelProperty(value = "模块名称")
|
||||
private String mkmc;
|
||||
|
||||
@Excel(name = "操作模块")
|
||||
@ApiModelProperty(value = "操作模块")
|
||||
private String title;
|
||||
|
||||
@Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
|
||||
@ApiModelProperty(value = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据")
|
||||
private Integer businessType;
|
||||
|
||||
@Excel(name = "请求方法")
|
||||
@ApiModelProperty(value = "请求方法")
|
||||
private String method;
|
||||
|
||||
@Excel(name = "请求方式")
|
||||
private String requestMethod;
|
||||
|
||||
@Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户")
|
||||
private Integer operatorType;
|
||||
|
||||
@Excel(name = "操作人员")
|
||||
@ApiModelProperty(value = "操作人员")
|
||||
private String operName;
|
||||
|
||||
@Excel(name = "操作人员ID")
|
||||
@ApiModelProperty(value = "操作人员ID")
|
||||
private String operUserId;
|
||||
|
||||
@Excel(name = "操作人员身份证号")
|
||||
@ApiModelProperty(value = "操作人员身份证号")
|
||||
private String operSfzh;
|
||||
|
||||
@Excel(name = "部门ID")
|
||||
private String ssbmid;
|
||||
|
||||
@Excel(name = "部门代码")
|
||||
private String ssbmdm;
|
||||
|
||||
@Excel(name = "部门名称")
|
||||
private String ssbm;
|
||||
|
||||
@Excel(name = "请求地址")
|
||||
private String operUrl;
|
||||
|
||||
@Excel(name = "操作地址")
|
||||
private String operIp;
|
||||
|
||||
@Excel(name = "请求参数")
|
||||
private String operParam;
|
||||
|
||||
@Excel(name = "返回参数")
|
||||
private String jsonResult;
|
||||
|
||||
@Excel(name = "状态", readConverterExp = "0=正常,1=异常")
|
||||
private Integer status;
|
||||
|
||||
@Excel(name = "错误消息")
|
||||
private String errorMsg;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date operTime = new Date();
|
||||
|
||||
private int sfsc;
|
||||
|
||||
//返回内容
|
||||
private String fhnr;
|
||||
}
|
@ -0,0 +1,380 @@
|
||||
package com.mosty.common.base.entity.realm;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.mosty.common.base.entity.log.BaseEntity;
|
||||
import com.mosty.common.base.entity.log.Excel;
|
||||
import com.mosty.common.base.entity.log.Excels;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 用户对象 sys_user
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SysUser extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 用户ID */
|
||||
@Excel(name = "用户序号", cellType = Excel.ColumnType.NUMERIC, prompt = "用户编号")
|
||||
private Long userId;
|
||||
|
||||
/** 部门ID */
|
||||
@Excel(name = "部门编号", type = Excel.Type.IMPORT)
|
||||
private Long deptId;
|
||||
|
||||
/** 部门父ID */
|
||||
private Long parentId;
|
||||
|
||||
/** 角色ID */
|
||||
private Long roleId;
|
||||
|
||||
/** 登录名称 */
|
||||
@Excel(name = "登录名称")
|
||||
private String loginName;
|
||||
|
||||
/** 用户名称 */
|
||||
@Excel(name = "用户名称")
|
||||
private String userName;
|
||||
|
||||
/** 用户类型 */
|
||||
private String userType;
|
||||
|
||||
/** 用户邮箱 */
|
||||
@Excel(name = "用户邮箱")
|
||||
private String email;
|
||||
|
||||
/** 手机号码 */
|
||||
@Excel(name = "手机号码")
|
||||
private String phonenumber;
|
||||
|
||||
/** 用户性别 */
|
||||
@Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
|
||||
private String sex;
|
||||
|
||||
/** 用户头像 */
|
||||
private String avatar;
|
||||
|
||||
/** 密码 */
|
||||
private String password;
|
||||
|
||||
/** 盐加密 */
|
||||
private String salt;
|
||||
|
||||
/** 帐号状态(0正常 1停用) */
|
||||
@Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
|
||||
private String status;
|
||||
|
||||
/** 删除标志(0代表存在 2代表删除) */
|
||||
private String delFlag;
|
||||
|
||||
/** 最后登录IP */
|
||||
@Excel(name = "最后登录IP", type = Excel.Type.EXPORT)
|
||||
private String loginIp;
|
||||
|
||||
/** 最后登录时间 */
|
||||
@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT)
|
||||
private Date loginDate;
|
||||
|
||||
/** 密码最后更新时间 */
|
||||
private Date pwdUpdateDate;
|
||||
|
||||
/** 部门对象 */
|
||||
@Excels({
|
||||
@Excel(name = "部门名称", targetAttr = "deptName", type = Excel.Type.EXPORT),
|
||||
@Excel(name = "部门负责人", targetAttr = "leader", type = Excel.Type.EXPORT)
|
||||
})
|
||||
// private SysDept dept;
|
||||
//
|
||||
// private List<SysRole> roles;
|
||||
|
||||
/** 角色组 */
|
||||
private Long[] roleIds;
|
||||
|
||||
/** 岗位组 */
|
||||
private Long[] postIds;
|
||||
|
||||
public SysUser()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SysUser(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getUserId()
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public boolean isAdmin()
|
||||
{
|
||||
return isAdmin(this.userId);
|
||||
}
|
||||
|
||||
public static boolean isAdmin(Long userId)
|
||||
{
|
||||
return userId != null && 1L == userId;
|
||||
}
|
||||
|
||||
public Long getDeptId()
|
||||
{
|
||||
return deptId;
|
||||
}
|
||||
|
||||
public void setDeptId(Long deptId)
|
||||
{
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
public Long getParentId()
|
||||
{
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId)
|
||||
{
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public Long getRoleId()
|
||||
{
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Long roleId)
|
||||
{
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
// @Xss(message = "登录账号不能包含脚本字符")
|
||||
// @NotBlank(message = "登录账号不能为空")
|
||||
// @Size(min = 0, max = 30, message = "登录账号长度不能超过30个字符")
|
||||
public String getLoginName()
|
||||
{
|
||||
return loginName;
|
||||
}
|
||||
|
||||
public void setLoginName(String loginName)
|
||||
{
|
||||
this.loginName = loginName;
|
||||
}
|
||||
|
||||
// @Xss(message = "用户昵称不能包含脚本字符")
|
||||
// @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
|
||||
public String getUserName()
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName)
|
||||
{
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getUserType()
|
||||
{
|
||||
return userType;
|
||||
}
|
||||
|
||||
public void setUserType(String userType)
|
||||
{
|
||||
this.userType = userType;
|
||||
}
|
||||
|
||||
// @Email(message = "邮箱格式不正确")
|
||||
// @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
|
||||
public String getEmail()
|
||||
{
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email)
|
||||
{
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
// @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
|
||||
public String getPhonenumber()
|
||||
{
|
||||
return phonenumber;
|
||||
}
|
||||
|
||||
public void setPhonenumber(String phonenumber)
|
||||
{
|
||||
this.phonenumber = phonenumber;
|
||||
}
|
||||
|
||||
public String getSex()
|
||||
{
|
||||
return sex;
|
||||
}
|
||||
|
||||
public void setSex(String sex)
|
||||
{
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
public String getAvatar()
|
||||
{
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar)
|
||||
{
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getSalt()
|
||||
{
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(String salt)
|
||||
{
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getDelFlag()
|
||||
{
|
||||
return delFlag;
|
||||
}
|
||||
|
||||
public void setDelFlag(String delFlag)
|
||||
{
|
||||
this.delFlag = delFlag;
|
||||
}
|
||||
|
||||
public String getLoginIp()
|
||||
{
|
||||
return loginIp;
|
||||
}
|
||||
|
||||
public void setLoginIp(String loginIp)
|
||||
{
|
||||
this.loginIp = loginIp;
|
||||
}
|
||||
|
||||
public Date getLoginDate()
|
||||
{
|
||||
return loginDate;
|
||||
}
|
||||
|
||||
public void setLoginDate(Date loginDate)
|
||||
{
|
||||
this.loginDate = loginDate;
|
||||
}
|
||||
|
||||
public Date getPwdUpdateDate()
|
||||
{
|
||||
return pwdUpdateDate;
|
||||
}
|
||||
|
||||
public void setPwdUpdateDate(Date pwdUpdateDate)
|
||||
{
|
||||
this.pwdUpdateDate = pwdUpdateDate;
|
||||
}
|
||||
|
||||
// public SysDept getDept()
|
||||
// {
|
||||
// if (dept == null)
|
||||
// {
|
||||
// dept = new SysDept();
|
||||
// }
|
||||
// return dept;
|
||||
// }
|
||||
//
|
||||
// public void setDept(SysDept dept)
|
||||
// {
|
||||
// this.dept = dept;
|
||||
// }
|
||||
//
|
||||
// public List<SysRole> getRoles()
|
||||
// {
|
||||
// return roles;
|
||||
// }
|
||||
//
|
||||
// public void setRoles(List<SysRole> roles)
|
||||
// {
|
||||
// this.roles = roles;
|
||||
// }
|
||||
|
||||
public Long[] getRoleIds()
|
||||
{
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
public void setRoleIds(Long[] roleIds)
|
||||
{
|
||||
this.roleIds = roleIds;
|
||||
}
|
||||
|
||||
public Long[] getPostIds()
|
||||
{
|
||||
return postIds;
|
||||
}
|
||||
|
||||
public void setPostIds(Long[] postIds)
|
||||
{
|
||||
this.postIds = postIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("userId", getUserId())
|
||||
.append("deptId", getDeptId())
|
||||
.append("loginName", getLoginName())
|
||||
.append("userName", getUserName())
|
||||
.append("userType", getUserType())
|
||||
.append("email", getEmail())
|
||||
.append("phonenumber", getPhonenumber())
|
||||
.append("sex", getSex())
|
||||
.append("avatar", getAvatar())
|
||||
.append("password", getPassword())
|
||||
.append("salt", getSalt())
|
||||
.append("status", getStatus())
|
||||
.append("delFlag", getDelFlag())
|
||||
.append("loginIp", getLoginIp())
|
||||
.append("loginDate", getLoginDate())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
// .append("dept", getDept())
|
||||
// .append("roles", getRoles())
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package com.mosty.common.base.exception;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* 断言,快速业务异常
|
||||
* @author kevin
|
||||
* @date 2022/4/28 1:49 下午
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class Asserts {
|
||||
|
||||
private Asserts() {
|
||||
}
|
||||
|
||||
|
||||
public static void directThrow(RuntimeException exception) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言 抛出 {@link BusinessExceptionEnum} 对应的业务异常
|
||||
* @param expression 表达式
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void checkAuth(boolean expression, String message) {
|
||||
if (expression) {
|
||||
throw new BusinessException(HttpStatus.UNAUTHORIZED.value(), message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言 抛出 {@link BusinessExceptionEnum} 对应的业务异常
|
||||
* @param expression 表达式
|
||||
* @param message 异常信息
|
||||
* @param args 参数信息
|
||||
*/
|
||||
public static void checkAuth(boolean expression, String message, Object... args) {
|
||||
if (expression) {
|
||||
throw new BusinessException(HttpStatus.UNAUTHORIZED.value(), String.format(message, args));
|
||||
}
|
||||
}
|
||||
|
||||
public static void check(boolean expression, String message) {
|
||||
if (expression) {
|
||||
throw new BusinessException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言 抛出 {@link BusinessExceptionEnum} 对应的业务异常
|
||||
* @param expression 表达式
|
||||
* @param exceptionEnum 业务异常
|
||||
*/
|
||||
public static void check(boolean expression, BusinessExceptionEnum exceptionEnum) {
|
||||
check(expression, exceptionEnum.message);
|
||||
}
|
||||
|
||||
public static void check(boolean expression, RuntimeException e) {
|
||||
if (expression) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static void check(boolean expression, String message, Object... args) {
|
||||
if (expression) {
|
||||
throw new BusinessException(String.format(message, args));
|
||||
}
|
||||
}
|
||||
|
||||
public static void check(boolean expression, String message, Object arg) {
|
||||
if (expression) {
|
||||
throw new BusinessException(String.format(message, arg));
|
||||
}
|
||||
}
|
||||
|
||||
public static void notNull(Object object, String name) {
|
||||
if (object == null) {
|
||||
throw new BusinessException(name + " is null");
|
||||
}
|
||||
}
|
||||
|
||||
public static void notEmpty(CharSequence s, String name) {
|
||||
if (StringUtils.isEmpty(s)) {
|
||||
throw new BusinessException(name + " is empty");
|
||||
}
|
||||
}
|
||||
|
||||
public static void notBlank(CharSequence s, String name) {
|
||||
if (StringUtils.isBlank(s)) {
|
||||
throw new BusinessException(name + " is blank");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.mosty.common.base.exception;
|
||||
|
||||
import io.swagger.models.auth.In;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 业务异常处理器
|
||||
* @author kevin
|
||||
* @date 2022/1/22 4:38 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
@SuppressWarnings("unused")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
/** 异常编码 */
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
private Object data;
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BusinessException(String message, Object data) {
|
||||
super(message);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public BusinessException(Integer code, String message, Object data) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public BusinessException(Integer code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
public BusinessException(String message, Throwable arg) {
|
||||
super(message, arg);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.mosty.common.base.exception;
|
||||
|
||||
|
||||
/**
|
||||
* 业务异常枚举, code 为{@code 0} 表示默认值
|
||||
*
|
||||
* @author hongmin.li
|
||||
* @date 2021/8/17 10:55
|
||||
* @since 1.6.5
|
||||
*/
|
||||
public enum BusinessExceptionEnum {
|
||||
|
||||
/** 门店功能配置获取异常 */
|
||||
STORE_FUNCTION_EXCEPTION(0, "获取换货比例远程服务异常"),
|
||||
;
|
||||
|
||||
public final int code;
|
||||
|
||||
public final String message;
|
||||
|
||||
BusinessExceptionEnum(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.mosty.common.base.exception;
|
||||
|
||||
/**
|
||||
* 操作状态
|
||||
*
|
||||
* @author ruoyi
|
||||
*
|
||||
*/
|
||||
public enum BusinessStatus {
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS,
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
FAIL,
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.mosty.common.base.exception;
|
||||
|
||||
import com.mosty.common.base.domain.ResponseResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
* @author kevin
|
||||
* @date 2022/2/22 1:50 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SuppressWarnings("unused")
|
||||
@ControllerAdvice(basePackages = "com.mosty")
|
||||
public class MostyExceptionHandler {
|
||||
|
||||
/**
|
||||
* 业务异常捕获
|
||||
* 需要使用spring mvc的全局异常拦截器处理
|
||||
* @param throwable 全局异常
|
||||
* @return 信息体
|
||||
*/
|
||||
@ResponseBody
|
||||
@ExceptionHandler(Throwable.class)
|
||||
public ResponseResult<String> throwableHandler(Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
log.warn("throwable异常:message={}", throwable.getMessage());
|
||||
log.warn("throwable异常:stack={}", throwable.getStackTrace());
|
||||
return ResponseResult.fail("系统异常,请稍后重试");
|
||||
}
|
||||
|
||||
/**
|
||||
* spring valid框架验证器异常捕获
|
||||
* 需要使用spring mvc的全局异常拦截器处理
|
||||
* @param validException 验证异常
|
||||
* @return 信息体
|
||||
*/
|
||||
@ResponseBody
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseResult<String> validHandler(MethodArgumentNotValidException validException) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
List<ObjectError> allErrors = validException.getBindingResult().getAllErrors();
|
||||
String message = allErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(";"));
|
||||
log.warn("调用接口验证异常:message={}", message);
|
||||
return ResponseResult.fail(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务异常捕获
|
||||
* 需要使用spring mvc的全局异常拦截器处理
|
||||
* @param businessException 业务异常
|
||||
* @return 信息体
|
||||
*/
|
||||
@ResponseBody
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseResult<String> businessHandler(BusinessException businessException) {
|
||||
log.warn("业务异常:message={}", businessException.getMessage());
|
||||
if (businessException.getCode() != null) {
|
||||
return ResponseResult.fail(businessException.getCode(), businessException.getMessage());
|
||||
}
|
||||
return ResponseResult.fail(businessException.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.mosty.common.base.exception;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public final class ServiceException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
* <p>
|
||||
*/
|
||||
private String detailMessage;
|
||||
|
||||
/**
|
||||
* 空构造方法,避免反序列化问题
|
||||
*/
|
||||
public ServiceException() {
|
||||
}
|
||||
|
||||
public ServiceException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getDetailMessage() {
|
||||
return detailMessage;
|
||||
}
|
||||
|
||||
public ServiceException setDetailMessage(String detailMessage) {
|
||||
this.detailMessage = detailMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public ServiceException setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.mosty.common.base.exception;
|
||||
|
||||
/**
|
||||
* 工具类异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UtilException extends RuntimeException
|
||||
{
|
||||
private static final long serialVersionUID = 8247610319171014183L;
|
||||
|
||||
public UtilException(Throwable e)
|
||||
{
|
||||
super(e.getMessage(), e);
|
||||
}
|
||||
|
||||
public UtilException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UtilException(String message, Throwable throwable)
|
||||
{
|
||||
super(message, throwable);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.mosty.common.base.lock.optimistic;
|
||||
|
||||
import org.springframework.aop.framework.AdvisedSupport;
|
||||
import org.springframework.aop.framework.AopProxy;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class AopTargetUtils {
|
||||
|
||||
/**
|
||||
* 递归获取代理的 目标对象
|
||||
* @param proxy 代理对象
|
||||
* @return 目标对象
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Object getTarget(Object proxy) throws Exception {
|
||||
if (!AopUtils.isAopProxy(proxy)) {
|
||||
return proxy;
|
||||
}
|
||||
if (AopUtils.isJdkDynamicProxy(proxy)) {
|
||||
proxy = getJdkDynamicProxyTargetObject(proxy);
|
||||
} else {
|
||||
proxy = getCglibProxyTargetObject(proxy);
|
||||
}
|
||||
return getTarget(proxy);
|
||||
}
|
||||
|
||||
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
|
||||
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
|
||||
h.setAccessible(true);
|
||||
Object dynamicAdvisedInterceptor = h.get(proxy);
|
||||
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
|
||||
advised.setAccessible(true);
|
||||
Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
|
||||
return target;
|
||||
}
|
||||
|
||||
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
|
||||
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
|
||||
h.setAccessible(true);
|
||||
AopProxy aopProxy = (AopProxy) h.get(proxy);
|
||||
Field advised = aopProxy.getClass().getDeclaredField("advised");
|
||||
advised.setAccessible(true);
|
||||
Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
|
||||
return target;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.mosty.common.base.lock.optimistic;
|
||||
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
|
||||
/**
|
||||
* 允许启动乐观锁自定义插件
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2020/8/11 0:03
|
||||
* @since 1.0.0
|
||||
* @see AbstractApplicationContext#refresh()
|
||||
* @see AbstractApplicationContext {@code invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) }
|
||||
* @see org.springframework.context.annotation
|
||||
* {@code ConfigurationClassParser#processImports(ConfigurationClass, ConfigurationClassParser.SourceClass, Collection, boolean)}
|
||||
*
|
||||
* @see OptimisticLockException 需要关注跑出的检查异常,后面根据业务给予处理
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Configuration
|
||||
//@Import(OptimisticLockConfiguration.class)
|
||||
@Deprecated
|
||||
public @interface EnableOptimisticLock {
|
||||
|
||||
}
|
@ -0,0 +1,300 @@
|
||||
//package com.mosty.common.base.lock.optimistic;
|
||||
//
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.apache.ibatis.binding.MapperMethod;
|
||||
//import org.apache.ibatis.executor.Executor;
|
||||
//import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
//import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
//import org.apache.ibatis.mapping.BoundSql;
|
||||
//import org.apache.ibatis.mapping.MappedStatement;
|
||||
//import org.apache.ibatis.mapping.ParameterMapping;
|
||||
//import org.apache.ibatis.mapping.SqlCommandType;
|
||||
//import org.apache.ibatis.plugin.*;
|
||||
//import org.apache.ibatis.reflection.MetaObject;
|
||||
//import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
//import org.apache.ibatis.session.Configuration;
|
||||
//import org.apache.ibatis.session.SqlSessionFactory;
|
||||
//import org.apache.ibatis.type.JdbcType;
|
||||
//import org.apache.ibatis.type.TypeException;
|
||||
//import org.apache.ibatis.type.TypeHandler;
|
||||
//
|
||||
//import java.lang.reflect.Proxy;
|
||||
//import java.sql.Connection;
|
||||
//import java.sql.PreparedStatement;
|
||||
//import java.sql.SQLException;
|
||||
//import java.util.Objects;
|
||||
//import java.util.Properties;
|
||||
//import java.util.regex.Pattern;
|
||||
//
|
||||
//
|
||||
///**
|
||||
// * Mybatis 自定义插件,{@link OptimisticLock} 就是一个{@link Plugin}
|
||||
// *
|
||||
// * 使用:1、如果某些 Mapper 不想使用乐观锁,可以在Mapper.xml 中不添加 乐观锁字段即可,如:
|
||||
// * <update id="update" parameterType="com.quanyou.qup.config.service.entity.ConfOrderApprove">
|
||||
// * UPDATE CONF_ORDER_APPROVE
|
||||
// * <set>
|
||||
// * <if test="orderTypeCode != null">ORDER_TYPE_CODE = #{orderTypeCode},</if>
|
||||
// * <if test="receiveOrgCode != null">RECEIVE_ORG_CODE = #{receiveOrgCode},</if>
|
||||
// * <!-- 不添加该乐观锁字段即可 -->
|
||||
// * <if test="objectVersionNumber != null">OBJECT_VERSION_NUMBER = #{objectVersionNumber},</if>
|
||||
// * </set>
|
||||
// * WHERE ID = #{id}
|
||||
// * </update>
|
||||
// * 2、业务端自行根据update的条数进行处理(给用户相应的提示等)或不处理(可能update本身就可能不会修改任何数据)
|
||||
// * 3、乐观锁的字段类型必须为{@link Long} ,可以对乐观锁的数据库的java pojo字段进行定义,【注意: OptimisticLock只能放在SqlInterceptor和ResultInterceptor之后】如下:
|
||||
// * <plugins>
|
||||
// * <plugin interceptor="com.quanyou.qup.query.interceptor.SqlInterceptor"/>
|
||||
// * <plugin interceptor="com.quanyou.qup.query.interceptor.ResultInterceptor"/>
|
||||
// * <plugin interceptor="com.quanyou.qup.middle.common.lock.optimistic.OptimisticLock">
|
||||
// * <!-- 定义数据库字段名称 -->
|
||||
// * <property name="VERSION_COLUMN" value="OBJECT_VERSION_NUMBER"/>
|
||||
// * <!-- 定义ORM实体的字段名称 -->
|
||||
// * <property name="VERSION_POJO" value="objectVersionNumber"/>
|
||||
// * </plugin>
|
||||
// * </plugins>
|
||||
// *
|
||||
// * 每一个拦截器可以拦截下面的四种,当前只拦截了三种
|
||||
// * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
|
||||
// * ParameterHandler (getParameterObject, setParameters)
|
||||
// * ResultSetHandler (handleResultSets, handleOutputParameters)
|
||||
// * StatementHandler (prepare, parameterize, batch, update, query)
|
||||
// *
|
||||
// * @author kevin
|
||||
// * @date 2020/8/10 22:14
|
||||
// * @since 1.0.0
|
||||
// * @see org.apache.ibatis.session.Configuration#interceptorChain
|
||||
// * @see Plugin#signatureMap 其中:Map<Class<?>, Set<Method>> Class 为所有需要实现乐观锁的Mapper,Set<Method>集合为需要乐观锁的方法集合【原则上是所有写的方法】
|
||||
// * @see Invocation#proceed() 直接执行被代理对象的方法
|
||||
// * @see MappedStatement 对应每个mapper中的<select>、<delete> 象等封装对
|
||||
// */
|
||||
//@SuppressWarnings("ALL")
|
||||
//@Slf4j
|
||||
//@Intercepts({
|
||||
// @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
|
||||
// @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}),
|
||||
// @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
|
||||
//})
|
||||
//public class OptimisticLock implements Interceptor {
|
||||
//
|
||||
// /**
|
||||
// * 乐观锁对应的数据库字段
|
||||
// */
|
||||
// private String VERSION_COLUMN = "OBJECT_VERSION_NUMBER";
|
||||
//
|
||||
// /**
|
||||
// * 乐观锁的java pojo 字段
|
||||
// */
|
||||
// private String VERSION_POJO = "objectVersionNumber";
|
||||
//
|
||||
// /** {@link Executor} 拦截的方法 */
|
||||
// private static final String EXECUTOR_MATHOD = "update";
|
||||
//
|
||||
// /** @link ParameterHandler} 拦截的方法 */
|
||||
// private static final String PARAMETER_HANDLER_METHOD = "setParameters";
|
||||
//
|
||||
// /** {@link StatementHandler} 拦截的方法 */
|
||||
// private static final String STATEMENT_HANDLER_METHOD = "prepare";
|
||||
//
|
||||
// /**
|
||||
// * 允许传入 Properties初始化参数
|
||||
// * @param properties 默认不需要传入
|
||||
// */
|
||||
// @Override
|
||||
// public void setProperties(Properties properties) {
|
||||
// String versionColumn = properties.getProperty("VERSION_COLUMN");
|
||||
// String versionPojo = properties.getProperty("VERSION_POJO");
|
||||
// if (versionColumn != null && versionColumn.trim().length() > 0) {
|
||||
// log.info("set OptimisticLock VERSION_COLUMN = {}", versionColumn);
|
||||
// VERSION_COLUMN = versionColumn;
|
||||
// }
|
||||
// if (versionPojo != null && versionPojo.trim().length() > 0) {
|
||||
// log.info("set OptimisticLock VERSION_POJO = {}", versionPojo);
|
||||
// VERSION_POJO = versionPojo;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Object intercept(Invocation invocation) throws Throwable {
|
||||
// String methodName = invocation.getMethod().getName();
|
||||
//
|
||||
// /*if (!flag) {
|
||||
// synchronized (OptimisticLock.class) {
|
||||
// super.initFilterSkipListMap();
|
||||
// flag = true;
|
||||
// }
|
||||
// }
|
||||
// // 过滤器不为空才来判断是否过滤,提高效率
|
||||
// if (filterNotEmpty) {
|
||||
// final Object target = invocation.getTarget();
|
||||
// if (target instanceof DefaultParameterHandler) {
|
||||
// DefaultParameterHandler parameterHandler = (DefaultParameterHandler) target;
|
||||
// final Field field = parameterHandler.getClass().getDeclaredField("mappedStatement");
|
||||
// field.setAccessible(true);
|
||||
// final MappedStatement mappedStatement = (MappedStatement) field.get(parameterHandler);
|
||||
// final String id = mappedStatement.getId();
|
||||
// String currentClazz = id.substring(0, id.lastIndexOf("."));
|
||||
// if (super.shouldSkip(currentClazz)) {
|
||||
// return target;
|
||||
// }
|
||||
// }
|
||||
// final Object[] args1 = invocation.getArgs();
|
||||
// if (args1 != null) {
|
||||
// for (Object arg : args1) {
|
||||
// if (arg instanceof MappedStatement) {
|
||||
// MappedStatement mappedStatement = (MappedStatement) arg;
|
||||
// final String id = mappedStatement.getId();
|
||||
// String currentClazz = id.substring(0, id.lastIndexOf("."));
|
||||
// if (super.shouldSkip(currentClazz)) {
|
||||
// return target;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }*/
|
||||
//
|
||||
// if (EXECUTOR_MATHOD.equals(methodName)) {
|
||||
// Object[] args = invocation.getArgs();
|
||||
// // 查看乐观锁执行结果是否正确
|
||||
// if (args != null && args[0] instanceof MappedStatement) {
|
||||
// MappedStatement mappedStatement = (MappedStatement) args[0];
|
||||
// if (mappedStatement.getSqlCommandType() == SqlCommandType.UPDATE) {
|
||||
// return invocation.proceed();
|
||||
// /*Object param = invocation.getArgs()[1];
|
||||
// BoundSql boundSql = mappedStatement.getBoundSql(param);
|
||||
// String sql = boundSql.getSql();
|
||||
// String paramJson = JSON.toJSONString(param);
|
||||
// if (result == 0) {
|
||||
// throw new OptimisticLockException("[触发乐观锁,更新失败], 失败SQL: " + sql + ", 参数: " + paramJson);
|
||||
// }
|
||||
//
|
||||
// return result;*/
|
||||
// }
|
||||
// }
|
||||
// } else if (PARAMETER_HANDLER_METHOD.equals(methodName)) {
|
||||
// // 设置乐观锁入参
|
||||
// setParamters(invocation);
|
||||
// } else if (STATEMENT_HANDLER_METHOD.equals(methodName)) {
|
||||
// // 为sql拼乐观锁字段
|
||||
// updateSqlAppendVersion(invocation);
|
||||
// }
|
||||
//
|
||||
// // 最后执行jdk被代理方法的调用
|
||||
// return invocation.proceed();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 拼接 乐观锁的sql
|
||||
// * @param invocation 代理信息
|
||||
// */
|
||||
// private void updateSqlAppendVersion(Invocation invocation) {
|
||||
// MetaObject metaObject;StatementHandler routingHandler = (StatementHandler)processTarget(invocation.getTarget());
|
||||
// metaObject = SystemMetaObject.forObject(routingHandler);
|
||||
// MetaObject hm = metaObject.metaObjectForProperty("delegate");
|
||||
// String originalSql = (String)hm.getValue("boundSql.sql");
|
||||
// boolean locker = resolve(hm, this.VERSION_COLUMN);
|
||||
// if (!locker) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// String builder = new StringBuilder().append(originalSql).append(" AND ").append(this.VERSION_COLUMN).append(" = ?").toString();
|
||||
// hm.setValue("boundSql.sql", builder);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 设置乐观锁入参值
|
||||
// *
|
||||
// * @param invocation 代理信息
|
||||
// */
|
||||
// private void setParamters(Invocation invocation) {
|
||||
// MetaObject metaObject;
|
||||
// ParameterHandler handler = (ParameterHandler)processTarget(invocation.getTarget());
|
||||
// metaObject = SystemMetaObject.forObject(handler);
|
||||
// boolean locker = resolve(metaObject, this.VERSION_COLUMN);
|
||||
// if (!locker) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// BoundSql boundSql = (BoundSql)metaObject.getValue("boundSql");
|
||||
// Object parameterObject = boundSql.getParameterObject();
|
||||
// if (parameterObject instanceof MapperMethod.ParamMap) {
|
||||
// MapperMethod.ParamMap<?> paramMap = (MapperMethod.ParamMap)parameterObject;
|
||||
// if (!paramMap.containsKey(this.VERSION_POJO)) {
|
||||
// throw new TypeException("All the primitive type parameters must add MyBatis's @Param Annotaion");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Configuration configuration = ((MappedStatement)metaObject.getValue("mappedStatement")).getConfiguration();
|
||||
// MetaObject pm = configuration.newMetaObject(parameterObject);
|
||||
// Object value = pm.getValue(this.VERSION_POJO);
|
||||
// ParameterMapping versionMapping = (new ParameterMapping.Builder(configuration, this.VERSION_POJO, Object.class)).build();
|
||||
// TypeHandler typeHandler = versionMapping.getTypeHandler();
|
||||
// JdbcType jdbcType = versionMapping.getJdbcType();
|
||||
// if (value == null && jdbcType == null) {
|
||||
// jdbcType = configuration.getJdbcTypeForNull();
|
||||
// }
|
||||
//
|
||||
// int versionLocation = boundSql.getParameterMappings().size() + 1;
|
||||
//
|
||||
// try {
|
||||
// PreparedStatement ps = (PreparedStatement)invocation.getArgs()[0];
|
||||
// typeHandler.setParameter(ps, versionLocation, value, jdbcType);
|
||||
// } catch (SQLException | TypeException var16) {
|
||||
// throw new TypeException("set parameter 'version' faild, Cause: " + var16, var16);
|
||||
// }
|
||||
//
|
||||
// if (!Objects.equals(value.getClass(), Long.class) && Objects.equals(value.getClass(), Long.TYPE) && log.isDebugEnabled()) {
|
||||
// log.error("[[[[[[[[[[[-> [Optimistic Loker] <-]]]]]]]]]]]property type error, the type of version property must be Long or long.");
|
||||
// }
|
||||
//
|
||||
// pm.setValue(this.VERSION_POJO, (Long)value + 1L);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 在 {@link SqlSessionFactory#openSession(boolean)} 时, 对 {@link Executor} 的包装【模式】后的包装类型,
|
||||
// * 再使用责任链{@link InterceptorChain} 进行层层动态代理,返回代理对象
|
||||
// * @param obj 当前obj为{@link Executor} 或者 动态代理过的对象
|
||||
// * @return 当前代理后的对象
|
||||
// */
|
||||
// @Override
|
||||
// public Object plugin(Object target) {
|
||||
// if (target instanceof Executor || target instanceof StatementHandler || target instanceof ParameterHandler) {
|
||||
// // 执行jdk动态代理
|
||||
// return Plugin.wrap(target, this);
|
||||
// }
|
||||
// return target;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 递归: 获取真正被代理的对象
|
||||
// * @param target 代理对象
|
||||
// * @return 最底层的被代理对象
|
||||
// */
|
||||
// public static Object processTarget(Object target) {
|
||||
// if (Proxy.isProxyClass(target.getClass())) {
|
||||
// MetaObject mo = SystemMetaObject.forObject(target);
|
||||
// return processTarget(mo.getValue("h.target"));
|
||||
// } else if (!(target instanceof StatementHandler) && !(target instanceof ParameterHandler)) {
|
||||
// if (log.isDebugEnabled()) {
|
||||
// log.error("Optimistic Lock plugin plugin init faild.");
|
||||
// }
|
||||
// throw new RuntimeException("Optimistic Lock plugin init faild.");
|
||||
// } else {
|
||||
// return target;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 我们的数据库字段都有乐观锁字段,不用验证
|
||||
// *
|
||||
// * 判断当前是否存现乐观锁字段
|
||||
// * @param mo 调用前的对象
|
||||
// * @param versionColumn 乐观锁列
|
||||
// * @return 是否包含乐观锁字段
|
||||
// */
|
||||
// private static boolean resolve(MetaObject mo, String versionColumn) {
|
||||
// String originalSql = (String)mo.getValue("boundSql.sql");
|
||||
// MappedStatement ms = (MappedStatement)mo.getValue("mappedStatement");
|
||||
// return Objects.equals(ms.getSqlCommandType(), SqlCommandType.UPDATE) ? Pattern.matches("[\\s\\S]*?" + versionColumn + "[\\s\\S]*?=[\\s\\S]*?\\?[\\s\\S]*?", originalSql.toUpperCase()) : false;
|
||||
// }
|
||||
//}
|
@ -0,0 +1,32 @@
|
||||
//package com.mosty.common.base.lock.optimistic;
|
||||
//
|
||||
//import org.springframework.beans.factory.config.BeanDefinition;
|
||||
//import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Role;
|
||||
//
|
||||
//
|
||||
///**
|
||||
// * 乐观锁配置
|
||||
// *
|
||||
// * @author kevin
|
||||
// * @date 2020/8/11 0:06
|
||||
// * @since 1.0.0
|
||||
// */
|
||||
////@Configuration
|
||||
//@AutoConfigureOrder(value = Integer.MIN_VALUE)
|
||||
//public class OptimisticLockConfiguration {
|
||||
//
|
||||
// /**
|
||||
// * 依赖注入 乐观锁插件的Bean
|
||||
// * @return 乐观锁插件
|
||||
// */
|
||||
// @Bean(name = "optimisticLock")
|
||||
// @ConditionalOnMissingBean(OptimisticLock.class)
|
||||
// @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
// public OptimisticLock initOptimisticLock() {
|
||||
// return new OptimisticLock();
|
||||
// }
|
||||
//
|
||||
//}
|
@ -0,0 +1,20 @@
|
||||
package com.mosty.common.base.lock.optimistic;
|
||||
|
||||
/**
|
||||
* 乐观锁更新异常
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2020/8/11 9:26
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class OptimisticLockException extends Exception {
|
||||
|
||||
/**
|
||||
* 异常信息【sql】
|
||||
*/
|
||||
private String message;
|
||||
|
||||
public OptimisticLockException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.mosty.common.base.lock.redisson;
|
||||
|
||||
/**
|
||||
* 分布式锁回调任务
|
||||
* @author kevin
|
||||
* @date 2022/1/24 11:18 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CallBack<V> {
|
||||
|
||||
/**
|
||||
* 需要回调执行的方法体
|
||||
* @return 执行结果
|
||||
*/
|
||||
V callBack();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.mosty.common.base.lock.redisson;
|
||||
|
||||
|
||||
/**
|
||||
* Redisson 分布式锁获取失败异常
|
||||
* @author kevin
|
||||
* @date 2020/11/20 10:18
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class RedisConcurrentException extends RuntimeException {
|
||||
|
||||
private static final String ERROR_MESSAGE = "分布式锁并发获取超时,调用者自行捕获处理";
|
||||
|
||||
public RedisConcurrentException() {
|
||||
super(ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.mosty.common.base.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class MybatisConfig {
|
||||
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 分页插件
|
||||
interceptor.addInnerInterceptor(paginationInterceptor());
|
||||
// 乐观锁插件
|
||||
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册mybatis plus的分页插件
|
||||
* 分页插件,自动识别数据库类型
|
||||
*/
|
||||
public PaginationInnerInterceptor paginationInterceptor() {
|
||||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
|
||||
// 设置最大单页限制数量,默认 500 条,-1 不受限制
|
||||
paginationInnerInterceptor.setMaxLimit(-1L);
|
||||
// 分页合理化
|
||||
paginationInnerInterceptor.setOverflow(true);
|
||||
return paginationInnerInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 乐观锁插件
|
||||
*/
|
||||
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
|
||||
return new OptimisticLockerInnerInterceptor();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.mosty.common.base.system.config;
|
||||
|
||||
import com.mosty.common.base.timeconsume.TimeConsume;
|
||||
import com.mosty.common.base.timeconsume.TimeConsumeActionConfig;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 系统配置启动类
|
||||
* @author kevin
|
||||
* @date 2022/2/4 11:10 AM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(TimeConsumeActionConfig.class)
|
||||
public @interface EnableSystemConfig {
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
package com.mosty.common.base.threadpool;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 线程池工具类
|
||||
* @author kevin
|
||||
* @date 2022/1/25 8:48 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SuppressWarnings("ALL")
|
||||
public class SimpleThreadPool extends ThreadPoolInit implements EnvironmentAware {
|
||||
|
||||
/**
|
||||
* 默认最大的超时时间
|
||||
*/
|
||||
private static int DEFAULT_TIMEOUT = 50000;
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
// 执行父类中的环境设置
|
||||
super.setEnvironment(environment);
|
||||
|
||||
String[] activeProfiles = environment.getActiveProfiles();
|
||||
if (activeProfiles == null) {
|
||||
return;
|
||||
}
|
||||
String pro = "prod";
|
||||
for (String activeProfile : activeProfiles) {
|
||||
if (pro.equals(activeProfile)) {
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("active spring profile prod");
|
||||
}
|
||||
DEFAULT_TIMEOUT = 2000;
|
||||
}
|
||||
}
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("SimpleThreadPool DEFAULT_TIMEOUT = {} ms", DEFAULT_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行没有返回值的任务
|
||||
* @param key 线程池枚举
|
||||
* @param runnable 任务
|
||||
*/
|
||||
public static void execute(String key, Runnable runnable) {
|
||||
if (!THREAD_POOL_EXECUTOR_MAP.containsKey(key)) {
|
||||
throw new IllegalArgumentException("未找到线程池:" + key);
|
||||
}
|
||||
// 执行任务
|
||||
THREAD_POOL_EXECUTOR_MAP.get(key).execute(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行有返回值任务
|
||||
*
|
||||
* @param key 线程池名称
|
||||
* @param callable 需要执行的任务可变数组
|
||||
* @param <T> 任务类型
|
||||
* @return 任务结果
|
||||
*/
|
||||
public static <T> List<Future<T>> execute(String key, Callable<T>... callable) {
|
||||
if (callable == null || callable.length == 0) {
|
||||
throw new IllegalArgumentException("任务不能为空!");
|
||||
}
|
||||
List<Callable<T>> taskList = Arrays.stream(callable).collect(Collectors.toList());
|
||||
return executeAll(key, taskList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量执行并行任务, 使用默认的最大超时时间,单位毫秒
|
||||
* @param key 线程池名称
|
||||
* @param callableList 任务列表
|
||||
* @return 结果
|
||||
*/
|
||||
public static <T> List<Future<T>> executeAll(String key, List<Callable<T>> callableList) {
|
||||
return executeAll(key, DEFAULT_TIMEOUT, callableList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量执行并行任务, 使用默认的最大超时时间,单位毫秒
|
||||
* @param key 线程池名称
|
||||
* @param callableList 任务列表
|
||||
* @return 结果
|
||||
*/
|
||||
public static <T> List<Future<T>> executeAll(ExecutorService service, List<Callable<T>> callableList) {
|
||||
return executeAll(service, DEFAULT_TIMEOUT, callableList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量执行并行任务
|
||||
* @param key 线程池名称
|
||||
* @param maxTimeout 最大超时时间,没有取默认值,单位2000毫秒
|
||||
* @param callableList 任务列表
|
||||
* @return 结果
|
||||
*/
|
||||
public static <T> List<Future<T>> executeAll(ExecutorService service, int maxTimeout, List<Callable<T>> callableList) {
|
||||
try {
|
||||
return service.invokeAll(callableList, maxTimeout, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("线程池批量执行任务异常失败", e);
|
||||
} catch (Exception e) {
|
||||
log.error("线程池批量执行任务异常失败", e);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量执行并行任务
|
||||
* @param key 线程池名称
|
||||
* @param callableList 任务列表
|
||||
* @return 结果
|
||||
*/
|
||||
public static <T> List<Future<T>> executeAllUntil(String key, List<Callable<T>> callableList) {
|
||||
if (!THREAD_POOL_EXECUTOR_MAP.containsKey(key)) {
|
||||
throw new IllegalArgumentException("未配置线程池" + key);
|
||||
}
|
||||
try {
|
||||
return THREAD_POOL_EXECUTOR_MAP.get(key).invokeAll(callableList);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("线程池批量执行任务异常失败", e);
|
||||
} catch (Exception e) {
|
||||
log.error("线程池批量执行任务异常失败", e);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量执行并行任务
|
||||
* @param key 线程池名称
|
||||
* @param maxTimeout 最大超时时间,没有取默认值,单位2000毫秒
|
||||
* @param callableList 任务列表
|
||||
* @return 结果
|
||||
*/
|
||||
public static <T> List<Future<T>> executeAll(String key, int maxTimeout, List<Callable<T>> callableList) {
|
||||
if (!THREAD_POOL_EXECUTOR_MAP.containsKey(key)) {
|
||||
throw new IllegalArgumentException("未配置线程池" + key);
|
||||
}
|
||||
try {
|
||||
return THREAD_POOL_EXECUTOR_MAP.get(key).invokeAll(callableList, maxTimeout, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("线程池批量执行任务异常失败", e);
|
||||
} catch (Exception e) {
|
||||
log.error("线程池批量执行任务异常失败", e);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key 线程池名称
|
||||
* @param callableList 任务列表
|
||||
* @return 结果
|
||||
*/
|
||||
public static void executeRunnable(String key, Runnable... r) {
|
||||
if (!THREAD_POOL_EXECUTOR_MAP.containsKey(key)) {
|
||||
throw new IllegalArgumentException("未配置线程池" + key);
|
||||
}
|
||||
ThreadPoolExecutor executor = THREAD_POOL_EXECUTOR_MAP.get(key);
|
||||
for (int i = 0; i < r.length; i++) {
|
||||
executor.submit(r[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.mosty.common.base.threadpool;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 线程池定义
|
||||
* @author kevin
|
||||
* @date 2020/8/23 13:43
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ThreadPool {
|
||||
|
||||
/**
|
||||
* 添加线程池
|
||||
* @return 线程池定义对象
|
||||
*/
|
||||
List<ThreadPoolEntity> appendThreadPool();
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.mosty.common.base.threadpool;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 线程池定义类
|
||||
* @author kevin
|
||||
* @date 2020/8/23 13:53
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public final class ThreadPoolEntity {
|
||||
|
||||
/**
|
||||
* 先池名称
|
||||
*/
|
||||
public final String taskName;
|
||||
|
||||
/**
|
||||
* 线程池的最大超时时间
|
||||
*/
|
||||
// public final int maxTimeout = 2000;
|
||||
|
||||
/**
|
||||
* 线程池任务类型
|
||||
*/
|
||||
public final SimpleThreadPool.PoolModel poolModel;
|
||||
|
||||
/**
|
||||
* 是否允许核心线程超时
|
||||
*/
|
||||
public final Boolean allowsCoreThreadTimeOut;
|
||||
|
||||
/**
|
||||
* 是否预热核心线程池
|
||||
*/
|
||||
public final Boolean preStartAllCoreThreads;
|
||||
|
||||
/**
|
||||
* 线程池说明
|
||||
*/
|
||||
public final String detail;
|
||||
|
||||
/**
|
||||
* 核心线程数
|
||||
*/
|
||||
public final int corePoolNum;
|
||||
|
||||
/**
|
||||
* 最大线程数
|
||||
*/
|
||||
public final int maxPoolNum;
|
||||
|
||||
/**
|
||||
* 超时时间
|
||||
*/
|
||||
public final int deleteThreadNum;
|
||||
|
||||
/**
|
||||
* 超时单位
|
||||
*/
|
||||
public final TimeUnit deleteTreadUnit;
|
||||
|
||||
/**
|
||||
* 任务队列,没有特殊理由不能使用无界队列
|
||||
*/
|
||||
public final BlockingQueue<Runnable> blockingQueue;
|
||||
|
||||
/**
|
||||
* 拒绝策略
|
||||
*/
|
||||
public final RejectedExecutionHandler rejectedExecutionHandler;
|
||||
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
package com.mosty.common.base.threadpool;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.tomcat.util.threads.TaskQueue;
|
||||
import org.apache.tomcat.util.threads.TaskThreadFactory;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 线程池定义和初始化和订单打印线程状态数据
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2020/8/23 14:23
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class ThreadPoolInit implements EnvironmentAware {
|
||||
|
||||
/**
|
||||
* 线程池容器
|
||||
*/
|
||||
public static final Map<String, ThreadPoolExecutor> THREAD_POOL_EXECUTOR_MAP = new ConcurrentHashMap<>(16);
|
||||
|
||||
/**
|
||||
* 是否打印,后续可以做成 Zookeeper Watch, Nacos 配置回调等开关,所以当前不使用{@code final} 关键字
|
||||
*/
|
||||
private static Boolean printThreadPoolInfoInterval = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 定时执行任务的单线程, 所有任务公用
|
||||
* 当前使用的有: 打印线程信息,装运点等定时刷新任务
|
||||
*/
|
||||
public static final ScheduledExecutorService SINGLE_POOL = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
static {
|
||||
// 初始化线程池
|
||||
ServiceLoader<ThreadPool> load = ServiceLoader.load(ThreadPool.class);
|
||||
load.forEach(threadPool -> threadPool.appendThreadPool().forEach(SimpleThreadPool::putThreadPool));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
String[] activeProfiles = environment.getActiveProfiles();
|
||||
String pro = "dev";
|
||||
for (String activeProfile : activeProfiles) {
|
||||
if (pro.equals(activeProfile)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 初始化线程池状态信息订单打印
|
||||
if (printThreadPoolInfoInterval) {
|
||||
printScheduledThreadStats();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 线程池类型, 只是作为标识当前任务是CPU型还是IO型为主
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public enum PoolModel {
|
||||
/** io型 */
|
||||
IO,
|
||||
/** CPU型 */
|
||||
CPU,
|
||||
/** io型,Tomcat扩展的 juc线程池 */
|
||||
FAST_IO
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 线程工厂
|
||||
*/
|
||||
static class DefaultThreadFactory implements ThreadFactory {
|
||||
|
||||
/**
|
||||
* 定义线程组
|
||||
*/
|
||||
static ThreadGroup threadGroup;
|
||||
|
||||
/**
|
||||
* 定义每个线程池中每个线程的名称后缀数字
|
||||
*/
|
||||
static final AtomicInteger THREAD_NUMBER = new AtomicInteger(1);
|
||||
|
||||
/**
|
||||
* 定义每个线程词的名称前缀
|
||||
*/
|
||||
static String namePrefix;
|
||||
|
||||
public DefaultThreadFactory(String name) {
|
||||
final SecurityManager securityManager = System.getSecurityManager();
|
||||
threadGroup = (securityManager != null) ? securityManager.getThreadGroup() : Thread.currentThread().getThreadGroup();
|
||||
namePrefix = name + "-thread-";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable runnable) {
|
||||
Thread thread = new Thread(threadGroup, runnable, namePrefix + THREAD_NUMBER.getAndIncrement(), 0);
|
||||
if (thread.isDaemon()) {
|
||||
thread.setDaemon(false);
|
||||
}
|
||||
if(thread.getPriority() != Thread.NORM_PRIORITY){
|
||||
thread.setPriority(Thread.NORM_PRIORITY);
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动打印线程
|
||||
*/
|
||||
private static void printScheduledThreadStats() {
|
||||
SINGLE_POOL.scheduleAtFixedRate(() -> THREAD_POOL_EXECUTOR_MAP.forEach((name, threadPool) -> {
|
||||
log.info("{} Pool Size: {} , Active Threads: {}, Number of Tasks Completed:{} , Number of Tasks in Queue: {}", name, threadPool.getPoolSize(),
|
||||
threadPool.getActiveCount(), threadPool.getCompletedTaskCount(), threadPool.getQueue().size());
|
||||
}), 0, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册打印日志信息
|
||||
* @param name 线程池名称
|
||||
* @param start 启始打印时间
|
||||
* @param interval 间隔打印时间
|
||||
*/
|
||||
public static void printScheduledThreadStats(String name, long start, long interval) {
|
||||
SINGLE_POOL.scheduleAtFixedRate(() -> {
|
||||
ThreadPoolExecutor threadPool = THREAD_POOL_EXECUTOR_MAP.get(name);
|
||||
log.info("{} Pool Size: {} , Active Threads: {}, Number of Tasks Completed:{} , Number of Tasks in Queue: {}", name, threadPool.getPoolSize(),
|
||||
threadPool.getActiveCount(), threadPool.getCompletedTaskCount(), threadPool.getQueue().size());
|
||||
}, start, interval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 往线程池容器中放入线程池池
|
||||
* @param threadPoolEntity 线程池定义对象
|
||||
*/
|
||||
public static void putThreadPool(ThreadPoolEntity threadPoolEntity) {
|
||||
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor(threadPoolEntity);
|
||||
log.info("name: {}, threadPoolExecutor = {}", threadPoolEntity.taskName, threadPoolExecutor);
|
||||
THREAD_POOL_EXECUTOR_MAP.put(threadPoolEntity.taskName, threadPoolExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据枚举获取线程池
|
||||
* @param entity 线程池枚举
|
||||
*/
|
||||
private static ThreadPoolExecutor getThreadPoolExecutor(ThreadPoolEntity entity) {
|
||||
ThreadPoolExecutor executor;
|
||||
if (entity.poolModel == PoolModel.FAST_IO) {
|
||||
if (!(entity.blockingQueue instanceof TaskQueue)) {
|
||||
throw new RuntimeException("PoolModel.FAST_IO 类型的线程池,只能创建 " + TaskQueue.class.getName() + " 类型的队列!");
|
||||
}
|
||||
TaskQueue taskQueue = (TaskQueue)entity.blockingQueue;
|
||||
org.apache.tomcat.util.threads.ThreadPoolExecutor threadPoolExecutor = new org.apache.tomcat.util.threads.ThreadPoolExecutor(entity.corePoolNum, entity.maxPoolNum, entity.deleteThreadNum,
|
||||
entity.deleteTreadUnit, taskQueue, new TaskThreadFactory(entity.taskName, false, 5), entity.rejectedExecutionHandler);
|
||||
// 设置父类,用于判断线程的对列表是否真的满了
|
||||
taskQueue.setParent(threadPoolExecutor);
|
||||
executor = threadPoolExecutor;
|
||||
|
||||
log.info("init ThreadPoolExecutorImpl and TaskQueue!");
|
||||
} else {
|
||||
executor = new ThreadPoolExecutor(entity.corePoolNum, entity.maxPoolNum, entity.deleteThreadNum, entity.deleteTreadUnit,
|
||||
entity.blockingQueue, new DefaultThreadFactory(entity.taskName), entity.rejectedExecutionHandler);
|
||||
// 是否预热核心线程
|
||||
if (entity.preStartAllCoreThreads) {
|
||||
executor.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
log.info("init ThreadPoolExecutor and {}!", entity.blockingQueue.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
try {
|
||||
// 是否允许核心线程超时
|
||||
if (entity.allowsCoreThreadTimeOut) {
|
||||
executor.allowsCoreThreadTimeOut();
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
log.error("初始化线程池错误:" + e);
|
||||
}
|
||||
return executor;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.mosty.common.base.threadpool.log;
|
||||
|
||||
import com.mosty.common.base.threadpool.SimpleThreadPool;
|
||||
import com.mosty.common.base.threadpool.ThreadPoolEntity;
|
||||
import com.mosty.common.base.threadpool.ThreadPoolInit;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static com.mosty.common.base.entity.log.LogConst.LOG_THREAD_POOL;
|
||||
|
||||
/**
|
||||
* 操作日志线程池 单体服务、基础微服务、普通微服务都需要
|
||||
* @author kevin
|
||||
* @date 2022/1/25 8:40 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
@AutoConfigureAfter(SimpleThreadPool.class)
|
||||
public class OperationLogThreadPool implements InitializingBean {
|
||||
|
||||
/**
|
||||
* 操作日志核心和最大线程数,可以根据并发量调整生效,默认单线程
|
||||
*/
|
||||
@Value("${operation-log.thread-pool.thread-mum:1}")
|
||||
private int threadNum;
|
||||
|
||||
/**
|
||||
* 操作日志最大队列数,可以根据并发量调整生效, 默认1000
|
||||
*/
|
||||
@Value("${operation-log.thread-pool.log-queue-size:1000}")
|
||||
private int logQueueSize;
|
||||
|
||||
private OperationLogThreadPool() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
SimpleThreadPool.putThreadPool(new ThreadPoolEntity(LOG_THREAD_POOL, ThreadPoolInit.PoolModel.IO,
|
||||
false, true, "操作日志线程池",
|
||||
threadNum, threadNum, 0, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(logQueueSize), new ThreadPoolExecutor.DiscardOldestPolicy()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.mosty.common.base.timeconsume;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 开启方法耗时, 默认打印的监控名称{@link TimeConsume#taskName()} 为: 方法所在类名#方法名称
|
||||
* @author kevin
|
||||
* @date 2020/8/23 10:14
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(TimeConsumeActionConfig.class)
|
||||
public @interface EnableTimeConsume {
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.mosty.common.base.timeconsume;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 耗时打印标记
|
||||
*
|
||||
* 如果使用总开关{@link EnableTimeConsume} 的关闭不起作用需要:
|
||||
*
|
||||
* @ComponentScan(value = "com.quanyou.qup",
|
||||
* excludeFilters = {@ComponentScan.Filter(type= FilterType.ASSIGNABLE_TYPE,
|
||||
* value = {TimeConsumeActionConfig.class})})
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2020/8/23 10:24
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TimeConsume {
|
||||
|
||||
/**
|
||||
* 任务名称,用于记录时间
|
||||
*
|
||||
* @return taskName 任务名称
|
||||
*/
|
||||
String taskName() default "";
|
||||
|
||||
/**
|
||||
* 打印时间消耗, 一般在执行流程的最后一步打印
|
||||
* 为防止流氓扫描注入,开关失效
|
||||
*
|
||||
* @return 是否打印日志
|
||||
*/
|
||||
boolean print() default true;
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package com.mosty.common.base.timeconsume;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.After;
|
||||
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.springframework.util.StringUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Stack;
|
||||
|
||||
|
||||
/**
|
||||
* 统计方法执行时间,可以支持注解的方法嵌套,自己保证会被动态代理切中
|
||||
* @author kevin
|
||||
* @date 2020/8/23 10:25
|
||||
* @since 1.0.0
|
||||
* @see org.springframework.context.annotation.EnableAspectJAutoProxy
|
||||
*/
|
||||
@Aspect
|
||||
@Slf4j
|
||||
public class TimeConsumeAction {
|
||||
|
||||
/**
|
||||
* 名称分割符
|
||||
*/
|
||||
private static final String SEPARATOR_NAME = "#";
|
||||
|
||||
/**
|
||||
* 每个耗时的间隔符
|
||||
*/
|
||||
private static final String SEPARATOR = " ; ";
|
||||
|
||||
/**
|
||||
* 方法调用计时器
|
||||
*/
|
||||
private static final ThreadLocal<Stack<TimeConsumeStopWatch>> MONITOR_THREAD_LOCAL = TransmittableThreadLocal.withInitial(Stack::new);
|
||||
|
||||
/**
|
||||
* 耗时日志叠加
|
||||
*/
|
||||
private static final ThreadLocal<StringBuilder> LOG_THREAD_LOCAL = TransmittableThreadLocal.withInitial(() -> new StringBuilder(128));
|
||||
|
||||
/**
|
||||
* 只切面 TimeConsume 注解标注的方法
|
||||
*/
|
||||
@Pointcut("@annotation(com.mosty.common.base.timeconsume.TimeConsume)")
|
||||
private void timeConsumeAspect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Aop环绕
|
||||
* @param pjp 切入点信息
|
||||
* @return 代理对象
|
||||
* @throws Throwable 执行异常
|
||||
*/
|
||||
@Around("timeConsumeAspect()")
|
||||
public Object around(ProceedingJoinPoint pjp) throws Throwable {
|
||||
TimeConsume timeConsume = getTimeConsume(pjp);
|
||||
if (timeConsume.print()) {
|
||||
String taskName = StringUtils.hasLength(timeConsume.taskName()) ? getDefaultName(pjp) : timeConsume.taskName();
|
||||
TimeConsumeStopWatch stopWatch = new TimeConsumeStopWatch(taskName);
|
||||
stopWatch.start(taskName);
|
||||
MONITOR_THREAD_LOCAL.get().push(stopWatch);
|
||||
}
|
||||
return pjp.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Aop后置方法
|
||||
* @param pjp 切入点信息
|
||||
*/
|
||||
@After("timeConsumeAspect()")
|
||||
public void after(JoinPoint pjp) {
|
||||
TimeConsume timeConsume = getTimeConsume(pjp);
|
||||
if (timeConsume.print()) {
|
||||
TimeConsumeStopWatch stopWatch = MONITOR_THREAD_LOCAL.get().pop();
|
||||
stopWatch.stop();
|
||||
LOG_THREAD_LOCAL.get().append(stopWatch.shortSummary()).append(SEPARATOR);
|
||||
|
||||
// 最外层(可能多个)出时,调用remove方法进行gc防止内存溢出
|
||||
if (MONITOR_THREAD_LOCAL.get().empty()) {
|
||||
log.info(LOG_THREAD_LOCAL.get().toString());
|
||||
MONITOR_THREAD_LOCAL.remove();
|
||||
LOG_THREAD_LOCAL.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认task名称
|
||||
* @param pjp 切入点信息
|
||||
* @return 默认task名称
|
||||
*/
|
||||
private String getDefaultName(JoinPoint pjp) {
|
||||
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
|
||||
return methodSignature.getDeclaringType().getSimpleName() + SEPARATOR_NAME + methodSignature.getMethod().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解的参数信息
|
||||
* @param pjp 切入点信息
|
||||
* @return 注解信息
|
||||
*/
|
||||
private TimeConsume getTimeConsume(JoinPoint pjp) {
|
||||
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
|
||||
Method method = methodSignature.getMethod();
|
||||
return method.getAnnotation(TimeConsume.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.mosty.common.base.timeconsume;
|
||||
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* 根据策略启动是否加载
|
||||
* @author kevin
|
||||
* @date 2022/1/19 9:39 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TimeConsumeActionConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = "time.consume", name = "enable", havingValue = "true")
|
||||
public TimeConsumeAction timeConsumeAction() {
|
||||
return new TimeConsumeAction();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.mosty.common.base.timeconsume;
|
||||
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
|
||||
/**
|
||||
* 方法调用耗时的计时器
|
||||
* @author kevin
|
||||
* @date 2020/8/23 10:14
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TimeConsumeStopWatch extends StopWatch {
|
||||
|
||||
/**
|
||||
* 无参数构造,让父类处理
|
||||
*/
|
||||
public TimeConsumeStopWatch() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* 有参数构造,让父类处理
|
||||
* @param id 计时器名称
|
||||
*/
|
||||
public TimeConsumeStopWatch(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shortSummary() {
|
||||
return "'" + this.getId() + "': " + this.getTotalTimeMillis() /*/ 1000000*/ + "ms";
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.mosty.common.base.token;
|
||||
|
||||
/**
|
||||
* 当前登陆用户信息上下文
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2022/2/6 3:05 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class MostyContext {
|
||||
|
||||
/**
|
||||
* 登陆用户的上下文信息
|
||||
*/
|
||||
private static final ThreadLocal<MostyUserInfo> USER_INFO_CONTEXT = ThreadLocal.withInitial(() -> null);
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.mosty.common.base.token;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 当前用户登陆信息
|
||||
* @author kevin
|
||||
* @date 2022/2/6 3:06 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class MostyUserInfo {
|
||||
|
||||
/**
|
||||
* 所属公司
|
||||
*/
|
||||
private Long companyId;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户名称
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户所属部门(用户有多个部门时,是当前用户选择切换的部门)
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
|
||||
|
||||
// private List<UserRole>
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
public class Base64Util {
|
||||
/**
|
||||
* BASE64解密
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static byte[] decryptBASE64(byte[] key) throws Exception {
|
||||
return Base64Utils.decode(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* BASE64加密
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String encryptBASE64(byte[] key) throws Exception {
|
||||
return new String(Base64Utils.encode(key));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validator;
|
||||
|
||||
/**
|
||||
* bean对象属性验证
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class BeanValidators
|
||||
{
|
||||
public static void validateWithException(Validator validator, Object object, Class<?>... groups)
|
||||
throws ConstraintViolationException
|
||||
{
|
||||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||
if (!constraintViolations.isEmpty())
|
||||
{
|
||||
throw new ConstraintViolationException(constraintViolations);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
|
||||
import net.sourceforge.pinyin4j.PinyinHelper;
|
||||
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
|
||||
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 获取首字母工具
|
||||
*
|
||||
* @author
|
||||
* @Date
|
||||
*/
|
||||
public class ChineseCharacterUtil {
|
||||
|
||||
/**
|
||||
* 获取汉字首字母或全拼大写字母
|
||||
*
|
||||
* @param chinese 汉字
|
||||
* @param isFull 是否全拼 true:表示全拼 false表示:首字母
|
||||
*
|
||||
* @return 全拼或者首字母大写字符窜
|
||||
*/
|
||||
public static String getUpperCase(String chinese,boolean isFull){
|
||||
return convertHanzi2Pinyin(chinese,isFull).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取汉字首字母或全拼小写字母
|
||||
*
|
||||
* @param chinese 汉字
|
||||
* @param isFull 是否全拼 true:表示全拼 false表示:首字母
|
||||
*
|
||||
* @return 全拼或者首字母小写字符窜
|
||||
*/
|
||||
public static String getLowerCase(String chinese,boolean isFull){
|
||||
return convertHanzi2Pinyin(chinese,isFull).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将汉字转成拼音
|
||||
* <P>
|
||||
* 取首字母或全拼
|
||||
*
|
||||
* @param hanzi 汉字字符串
|
||||
* @param isFull 是否全拼 true:表示全拼 false表示:首字母
|
||||
*
|
||||
* @return 拼音
|
||||
*/
|
||||
private static String convertHanzi2Pinyin(String hanzi,boolean isFull){
|
||||
/***
|
||||
* ^[\u2E80-\u9FFF]+$ 匹配所有东亚区的语言
|
||||
* ^[\u4E00-\u9FFF]+$ 匹配简体和繁体
|
||||
* ^[\u4E00-\u9FA5]+$ 匹配简体
|
||||
*/
|
||||
String regExp="^[\u4E00-\u9FFF]+$";
|
||||
StringBuffer sb=new StringBuffer();
|
||||
if(hanzi==null||"".equals(hanzi.trim())){
|
||||
return "";
|
||||
}
|
||||
String pinyin="";
|
||||
for(int i=0;i<hanzi.length();i++){
|
||||
char unit=hanzi.charAt(i);
|
||||
//是汉字,则转拼音
|
||||
if(match(String.valueOf(unit),regExp)){
|
||||
pinyin=convertSingleHanzi2Pinyin(unit);
|
||||
if(isFull){
|
||||
sb.append(pinyin);
|
||||
}
|
||||
else{
|
||||
sb.append(pinyin.charAt(0));
|
||||
}
|
||||
}else{
|
||||
sb.append(unit);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将单个汉字转成拼音
|
||||
*
|
||||
* @param hanzi 汉字字符
|
||||
*
|
||||
* @return 拼音
|
||||
*/
|
||||
private static String convertSingleHanzi2Pinyin(char hanzi){
|
||||
HanyuPinyinOutputFormat outputFormat = new HanyuPinyinOutputFormat();
|
||||
outputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
|
||||
String[] res;
|
||||
StringBuffer sb=new StringBuffer();
|
||||
try {
|
||||
res = PinyinHelper.toHanyuPinyinStringArray(hanzi,outputFormat);
|
||||
sb.append(res[0]);//对于多音字,只用第一个拼音
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/***
|
||||
* 匹配
|
||||
* <P>
|
||||
* 根据字符和正则表达式进行匹配
|
||||
*
|
||||
* @param str 源字符串
|
||||
* @param regex 正则表达式
|
||||
*
|
||||
* @return true:匹配成功 false:匹配失败
|
||||
*/
|
||||
private static boolean match(String str,String regex){
|
||||
Pattern pattern= Pattern.compile(regex);
|
||||
Matcher matcher=pattern.matcher(str);
|
||||
return matcher.find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试方法
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
System.out.println(convertHanzi2Pinyin("刘二牛",false).toLowerCase());
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
||||
public static String YYYY = "yyyy";
|
||||
|
||||
public static String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static String MM_DD = "MM-dd";
|
||||
public static String YY_MM = "yy-MM";
|
||||
|
||||
|
||||
private static String[] parsePatterns = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
|
||||
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
|
||||
|
||||
/**
|
||||
* 获取当前Date型日期
|
||||
*
|
||||
* @return Date() 当前日期
|
||||
*/
|
||||
public static Date getNowDate() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前Date型日期
|
||||
*
|
||||
* @return Date() 当前日期
|
||||
*/
|
||||
public static Date LocalDateTimeToDate(LocalDateTime localDateTime) {
|
||||
Date date = Date.from(localDateTime.atZone(ZoneOffset.ofHours(8)).toInstant());
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期, 默认格式为yyyy-MM-dd
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String getDate() {
|
||||
return dateTimeNow(YYYY_MM_DD);
|
||||
}
|
||||
|
||||
public static final String getTime() {
|
||||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow() {
|
||||
return dateTimeNow(YYYYMMDDHHMMSS);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow(final String format) {
|
||||
return parseDateToStr(format, new Date());
|
||||
}
|
||||
|
||||
public static final String dateTime(final Date date) {
|
||||
return parseDateToStr(YYYY_MM_DD, date);
|
||||
}
|
||||
|
||||
public static final String parseDateToStr(final String format, final Date date) {
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
public static final Date dateTime(final String format, final String ts) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(ts);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如2018/08/08
|
||||
*/
|
||||
public static final String datePath() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyy/MM/dd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如2018/08/08
|
||||
*/
|
||||
public static final String datePathString() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, YYYY_MM_DD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如20180808
|
||||
*/
|
||||
public static final String dateTime() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyyMMdd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期型字符串转化为日期 格式
|
||||
*/
|
||||
public static Date parseDate(Object str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return parseDate(str.toString(), parsePatterns);
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器启动时间
|
||||
*/
|
||||
public static Date getServerStartDate() {
|
||||
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
|
||||
return new Date(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算相差天数
|
||||
*/
|
||||
public static int differentDaysByMillisecond(Date date1, Date date2) {
|
||||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间差
|
||||
*/
|
||||
public static String getDatePoor(Date endDate, Date nowDate) {
|
||||
long nd = 1000 * 24 * 60 * 60;
|
||||
long nh = 1000 * 60 * 60;
|
||||
long nm = 1000 * 60;
|
||||
// long ns = 1000;
|
||||
// 获得两个时间的毫秒时间差异
|
||||
long diff = endDate.getTime() - nowDate.getTime();
|
||||
// 计算差多少天
|
||||
long day = diff / nd;
|
||||
// 计算差多少小时
|
||||
long hour = diff % nd / nh;
|
||||
// 计算差多少分钟
|
||||
long min = diff % nd % nh / nm;
|
||||
// 计算差多少秒//输出结果
|
||||
// long sec = diff % nd % nh % nm / ns;
|
||||
return day + "天" + hour + "小时" + min + "分钟";
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得指定的下一个日期<br>
|
||||
* <p>
|
||||
* 注意:按月运算时,如果运算后当月没有该日,则为当月的最后一天
|
||||
* </p>
|
||||
*
|
||||
* @param startDate 开始日期
|
||||
* @param nextType 运算类型(Y=年数;M=月数;D=天数;H=小时数;m=分钟数;S=秒数)
|
||||
* @param nextStep 运算长度(为负数指取以前的日期)
|
||||
* @return java.util.Date
|
||||
*/
|
||||
public static Date getNextDate(Date startDate, String nextType,
|
||||
int nextStep) {
|
||||
Date endDate = startDate;
|
||||
if (startDate != null) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(startDate);
|
||||
if ("Y".equals(nextType)) {
|
||||
calendar.add(Calendar.YEAR, nextStep);
|
||||
} else if ("M".equals(nextType)) {
|
||||
calendar.add(Calendar.MONTH, nextStep);
|
||||
} else if ("D".equals(nextType)) {
|
||||
calendar.add(Calendar.DATE, nextStep);
|
||||
} else if ("H".equals(nextType)) {
|
||||
calendar.add(Calendar.HOUR, nextStep);
|
||||
} else if ("m".equals(nextType)) {
|
||||
calendar.add(Calendar.MINUTE, nextStep);
|
||||
} else if ("S".equals(nextType)) {
|
||||
calendar.add(Calendar.SECOND, nextStep);
|
||||
}
|
||||
endDate = calendar.getTime();
|
||||
}
|
||||
return endDate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 增加 LocalDateTime ==> Date
|
||||
*/
|
||||
public static Date toDate(LocalDateTime temporalAccessor)
|
||||
{
|
||||
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zdt.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 LocalDate ==> Date
|
||||
*/
|
||||
public static Date toDate(LocalDate temporalAccessor)
|
||||
{
|
||||
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
|
||||
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zdt.toInstant());
|
||||
}
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import com.mosty.common.base.lock.redisson.CallBack;
|
||||
import com.mosty.common.base.lock.redisson.RedisConcurrentException;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 分布式锁(设置超时时间,就一直进行获取)
|
||||
*
|
||||
* <p>
|
||||
* 1、可以在{@link CacheConfig} 中添加自己的枚举信息,调用:
|
||||
* {@link DistributedLockUtil#invokeInLock(CallBack, CacheConfig, String...)} 或者 {@link DistributedLockUtil#getInstance()#invokeInLock(CallBack, String...)}
|
||||
*
|
||||
* 2、也可以不使用枚举:
|
||||
* {@link DistributedLockUtil#getInstance()#setPrefix(String)#invokeInLock(CallBack, String...)} 调用前可以设置超时 {@link #setTimeout(long, long)}
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2020/9/1 20:05
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Slf4j
|
||||
public class DistributedLockUtil {
|
||||
|
||||
/**
|
||||
* redisson 客户端
|
||||
*/
|
||||
private static volatile RedissonClient redissonClient;
|
||||
|
||||
/**
|
||||
* 前缀参数
|
||||
*/
|
||||
private static final ThreadLocal<Parameter> PREFIX_PARAMETER = TransmittableThreadLocal.withInitial(() -> null);
|
||||
|
||||
private DistributedLockUtil() {
|
||||
}
|
||||
|
||||
private static class Singleton {
|
||||
private static final DistributedLockUtil INSTANCE = new DistributedLockUtil();
|
||||
}
|
||||
public static DistributedLockUtil getInstance() {
|
||||
return Singleton.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 redisson 客户端
|
||||
* @return redisson
|
||||
*/
|
||||
private static RedissonClient getRedissonClient() {
|
||||
if (redissonClient == null) {
|
||||
synchronized (DistributedLockUtil.class) {
|
||||
if (redissonClient == null) {
|
||||
redissonClient = SpringIocContext.getBean(RedissonClient.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
return redissonClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加前缀信息
|
||||
* @param prefix 前缀
|
||||
* @return 当前单利对象
|
||||
*/
|
||||
public DistributedLockUtil setPrefix(String prefix) {
|
||||
Parameter parameter = PREFIX_PARAMETER.get();
|
||||
if (parameter == null) {
|
||||
PREFIX_PARAMETER.set(new Parameter(prefix));
|
||||
return this;
|
||||
}
|
||||
parameter.setPrefix(prefix);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置获取的超时时间
|
||||
* @param waitTime 最大等待获取锁时间
|
||||
* @param leaseTime 任务执行时间
|
||||
* @return 当前单利对象
|
||||
*/
|
||||
public DistributedLockUtil setTimeout(long waitTime, long leaseTime) {
|
||||
Parameter parameter = PREFIX_PARAMETER.get();
|
||||
if (parameter == null) {
|
||||
PREFIX_PARAMETER.set(new Parameter(waitTime, leaseTime));
|
||||
return this;
|
||||
}
|
||||
parameter.setWaitTime(waitTime);
|
||||
parameter.setLeaseTime(leaseTime);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置获取的超时时间
|
||||
* @param cacheConfig 缓存配置
|
||||
* @return 当前单利对象
|
||||
*/
|
||||
public DistributedLockUtil setCacheConfig(CacheConfig cacheConfig) {
|
||||
Parameter parameter = PREFIX_PARAMETER.get();
|
||||
if (parameter == null) {
|
||||
PREFIX_PARAMETER.set(new Parameter(cacheConfig));
|
||||
return this;
|
||||
}
|
||||
parameter.setCacheConfig(cacheConfig);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用单利对象调用该方法 在排序的分布式锁中,执行操作
|
||||
*
|
||||
* 调用前需要调用{@link #setPrefix(String)} 或者 {@link #setCacheConfig(CacheConfig)} 也可以调用{@link #setTimeout(long, long)}
|
||||
*
|
||||
* @param method 需要回调执行的方法体
|
||||
* @param lockKey 缓存key列表
|
||||
* @param <V> 回调返回值
|
||||
* @return 回调返回信息
|
||||
*/
|
||||
public <V> V invokeInLock(CallBack<V> method, String... lockKey) {
|
||||
Parameter parameter = PREFIX_PARAMETER.get();
|
||||
if (parameter == null || (parameter.getCacheConfig() == null && StringUtils.hasLength(parameter.getPrefix()))) {
|
||||
throw new RuntimeException("请先调用setPrefix或setCacheConfig方法!");
|
||||
}
|
||||
try {
|
||||
return invokeInLock(method, null, lockKey);
|
||||
} finally {
|
||||
PREFIX_PARAMETER.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在排序的分布式锁中,执行操作
|
||||
* @param method 需要回调执行的方法体
|
||||
* @param cacheConfig 可以为null, 则自己带前置传入
|
||||
* @param lockKey 缓存key列表
|
||||
* @param <V> 回调返回值
|
||||
* @return 回调返回信息
|
||||
*/
|
||||
public static <V> V invokeInLock(CallBack<V> method, @Nullable CacheConfig cacheConfig, String... lockKey) throws RedisConcurrentException {
|
||||
Parameter parameter = PREFIX_PARAMETER.get();
|
||||
if (lockKey == null || (cacheConfig == null && parameter == null)) {
|
||||
throw new RuntimeException("请求参数不能为空!");
|
||||
}
|
||||
boolean hasCacheConfig = cacheConfig == null;
|
||||
final String prefix = hasCacheConfig ? parameter.getPrefix() : cacheConfig.cachePrefix;
|
||||
long waitTime = hasCacheConfig ? parameter.getWaitTime() : cacheConfig.waitTime;
|
||||
long leaseTime = hasCacheConfig ? parameter.getLeaseTime() : cacheConfig.leaseTime;
|
||||
|
||||
// 获取批量的分布式锁
|
||||
RLock[] rLocks = Arrays.stream(lockKey)
|
||||
.distinct()
|
||||
// 进行排序,防止获取分布式锁时死锁
|
||||
.sorted()
|
||||
// 组装获取锁
|
||||
.map(key -> getRedissonClient().getLock(prefix + key))
|
||||
.toArray(RLock[]::new);
|
||||
RLock lock = redissonClient.getMultiLock(rLocks);
|
||||
|
||||
// 加锁
|
||||
boolean isLock = false;
|
||||
StopWatch stopWatch = new StopWatch(prefix);
|
||||
try {
|
||||
isLock = waitTime == 0 ? lock.tryLock() : lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
|
||||
stopWatch.start();
|
||||
} catch (InterruptedException e) {
|
||||
log.error("获取分布式锁失败!");
|
||||
}
|
||||
V result = null;
|
||||
if (isLock) {
|
||||
try {
|
||||
// 在加锁的情况下执行回调任务
|
||||
result = method.callBack();
|
||||
} finally {
|
||||
// 最终进行解锁, stopWatch.stop放前面一定保证调用
|
||||
try {
|
||||
stopWatch.stop();
|
||||
lock.unlock();
|
||||
} catch (Exception e) {
|
||||
log.error("分布式锁超时【续命锁也超时】,已经自动解锁!");
|
||||
} finally {
|
||||
log.info("分布式锁日志:锁前缀 {} 设置的等待锁时间为 {} MILLISECONDS, 执行时间为 {} MILLISECONDS, 执行时间时间信息: {}!", prefix, waitTime, leaseTime, stopWatch.shortSummary());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new RedisConcurrentException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存前缀
|
||||
* @author kevin
|
||||
* @date 2020/9/2 10:04
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum CacheConfig {
|
||||
|
||||
/** 预销售分布式锁前缀 获取等待时间5秒 执行最大超时 5分钟*/
|
||||
PRE_SELL("PRE_SELL_LOCK_", 50000, 300000);
|
||||
|
||||
/** 缓存前缀 */
|
||||
public String cachePrefix;
|
||||
|
||||
/** 最大等待获取锁时间(毫秒) */
|
||||
public long waitTime;
|
||||
|
||||
/** 任务执行时间(毫秒) */
|
||||
public long leaseTime;
|
||||
|
||||
CacheConfig(String cachePrefix, long waitTime, long leaseTime) {
|
||||
this.cachePrefix = cachePrefix;
|
||||
this.waitTime = waitTime;
|
||||
this.leaseTime = leaseTime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置配置参数
|
||||
*/
|
||||
@Data
|
||||
private static class Parameter {
|
||||
/** 前缀 */
|
||||
private String prefix;
|
||||
|
||||
/** 缓存枚举 */
|
||||
private CacheConfig cacheConfig;
|
||||
|
||||
/** 最大等待获取锁时间(毫秒) */
|
||||
private long waitTime;
|
||||
|
||||
/** 任务执行时间(毫秒) */
|
||||
private long leaseTime;
|
||||
|
||||
public Parameter(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public Parameter(CacheConfig cacheConfig) {
|
||||
this.cacheConfig = cacheConfig;
|
||||
}
|
||||
|
||||
public Parameter(long waitTime, long leaseTime) {
|
||||
this.waitTime = waitTime;
|
||||
this.leaseTime = leaseTime;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.velocity.shaded.commons.io.FilenameUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
/**
|
||||
* 文件类型工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FileTypeUtils
|
||||
{
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: ruoyi.txt, 返回: txt
|
||||
*
|
||||
* @param file 文件名
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType(File file)
|
||||
{
|
||||
if (null == file)
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return getFileType(file.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: ruoyi.txt, 返回: txt
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType(String fileName)
|
||||
{
|
||||
int separatorIndex = fileName.lastIndexOf(".");
|
||||
if (separatorIndex < 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return fileName.substring(separatorIndex + 1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名的后缀
|
||||
*
|
||||
* @param file 表单文件
|
||||
* @return 后缀名
|
||||
*/
|
||||
public static final String getExtension(MultipartFile file)
|
||||
{
|
||||
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
|
||||
if (StringUtils.isEmpty(extension))
|
||||
{
|
||||
extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
*
|
||||
* @param photoByte 文件字节码
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileExtendName(byte[] photoByte)
|
||||
{
|
||||
String strFileExtendName = "JPG";
|
||||
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
|
||||
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97))
|
||||
{
|
||||
strFileExtendName = "GIF";
|
||||
}
|
||||
else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70))
|
||||
{
|
||||
strFileExtendName = "JPG";
|
||||
}
|
||||
else if ((photoByte[0] == 66) && (photoByte[1] == 77))
|
||||
{
|
||||
strFileExtendName = "BMP";
|
||||
}
|
||||
else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71))
|
||||
{
|
||||
strFileExtendName = "PNG";
|
||||
}
|
||||
return strFileExtendName;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Arrays;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 图片处理工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ImageUtils
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
|
||||
|
||||
public static byte[] getImage(String imagePath)
|
||||
{
|
||||
InputStream is = getFile(imagePath);
|
||||
try
|
||||
{
|
||||
return IOUtils.toByteArray(is);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("图片加载异常 {}", e);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream getFile(String imagePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] result = readFile(imagePath);
|
||||
result = Arrays.copyOf(result, result.length);
|
||||
return new ByteArrayInputStream(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("获取图片异常 {}", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件为字节数据
|
||||
*
|
||||
* @param url 地址
|
||||
* @return 字节数据
|
||||
*/
|
||||
public static byte[] readFile(String url)
|
||||
{
|
||||
InputStream in = null;
|
||||
try
|
||||
{
|
||||
// 网络地址
|
||||
URL urlObj = new URL(url);
|
||||
URLConnection urlConnection = urlObj.openConnection();
|
||||
urlConnection.setConnectTimeout(30 * 1000);
|
||||
urlConnection.setReadTimeout(60 * 1000);
|
||||
urlConnection.setDoInput(true);
|
||||
in = urlConnection.getInputStream();
|
||||
return IOUtils.toByteArray(in);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("访问文件异常 {}", e);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* 获取IP工具类
|
||||
*
|
||||
* @author kevin
|
||||
* @date 2022/2/16 11:15 PM
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class IpUtil {
|
||||
|
||||
/**
|
||||
* 获取ip地址
|
||||
* @return ip地址
|
||||
*/
|
||||
public static String getIpAddress() {
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttributes == null) {
|
||||
return "";
|
||||
}
|
||||
HttpServletRequest request = requestAttributes.getRequest();
|
||||
return getIpAddress(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ip地址
|
||||
* @param request http请求
|
||||
* @return IP地址
|
||||
*/
|
||||
public static String getIpAddress(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return "unknown";
|
||||
}
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
System.out.println("Proxy-Client-IP ip: " + ip);
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
System.out.println("WL-Proxy-Client-IP ip: " + ip);
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||
System.out.println("HTTP_CLIENT_IP ip: " + ip);
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
System.out.println("HTTP_X_FORWARDED_FOR ip: " + ip);
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
System.out.println("X-Real-IP ip: " + ip);
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
System.out.println("getRemoteAddr ip: " + ip);
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
public static boolean internalIp(String ip) {
|
||||
byte[] addr = textToNumericFormatV4(ip);
|
||||
return internalIp(addr) || "127.0.0.1".equals(ip);
|
||||
}
|
||||
|
||||
private static boolean internalIp(byte[] addr) {
|
||||
if (StringUtils.isNull(addr) || addr.length < 2) {
|
||||
return true;
|
||||
}
|
||||
final byte b0 = addr[0];
|
||||
final byte b1 = addr[1];
|
||||
// 10.x.x.x/8
|
||||
final byte SECTION_1 = 0x0A;
|
||||
// 172.16.x.x/12
|
||||
final byte SECTION_2 = (byte) 0xAC;
|
||||
final byte SECTION_3 = (byte) 0x10;
|
||||
final byte SECTION_4 = (byte) 0x1F;
|
||||
// 192.168.x.x/16
|
||||
final byte SECTION_5 = (byte) 0xC0;
|
||||
final byte SECTION_6 = (byte) 0xA8;
|
||||
switch (b0) {
|
||||
case SECTION_1:
|
||||
return true;
|
||||
case SECTION_2:
|
||||
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
|
||||
return true;
|
||||
}
|
||||
case SECTION_5:
|
||||
switch (b1) {
|
||||
case SECTION_6:
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将IPv4地址转换成字节
|
||||
*
|
||||
* @param text IPv4地址
|
||||
* @return byte 字节
|
||||
*/
|
||||
public static byte[] textToNumericFormatV4(String text) {
|
||||
if (text.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[4];
|
||||
String[] elements = text.split("\\.", -1);
|
||||
try {
|
||||
long l;
|
||||
int i;
|
||||
switch (elements.length) {
|
||||
case 1:
|
||||
l = Long.parseLong(elements[0]);
|
||||
if ((l < 0L) || (l > 4294967295L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
|
||||
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 2:
|
||||
l = Integer.parseInt(elements[0]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l & 0xFF);
|
||||
l = Integer.parseInt(elements[1]);
|
||||
if ((l < 0L) || (l > 16777215L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 3:
|
||||
for (i = 0; i < 2; ++i) {
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
l = Integer.parseInt(elements[2]);
|
||||
if ((l < 0L) || (l > 65535L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 4:
|
||||
for (i = 0; i < 4; ++i) {
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static String getHostIp() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
}
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
public static String getHostName() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
|
||||
public class LocalDateTimeUtils {
|
||||
/**
|
||||
* 获取当天最早时间
|
||||
* @return
|
||||
*/
|
||||
public static LocalDateTime todayStart(){
|
||||
LocalDateTime today_start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
|
||||
return today_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当天最晚时间
|
||||
* @return
|
||||
*/
|
||||
public static LocalDateTime todayEnd(){
|
||||
LocalDateTime today_end = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
|
||||
return today_end;
|
||||
}
|
||||
|
||||
/**
|
||||
* 往前几天
|
||||
* @return
|
||||
*/
|
||||
public static LocalDateTime daysStart(Integer day){
|
||||
LocalDateTime now = LocalDateTime.now().minusDays(day);
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* 往后几天
|
||||
* @return
|
||||
*/
|
||||
public static LocalDateTime daysEnd(Integer day){
|
||||
LocalDateTime now = LocalDateTime.now().plusDays(day);
|
||||
return now;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* Md5加密方法
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Slf4j
|
||||
public class Md5Utils {
|
||||
|
||||
private static byte[] md5(String s) {
|
||||
MessageDigest algorithm;
|
||||
try {
|
||||
algorithm = MessageDigest.getInstance("MD5");
|
||||
algorithm.reset();
|
||||
algorithm.update(s.getBytes("UTF-8"));
|
||||
byte[] messageDigest = algorithm.digest();
|
||||
return messageDigest;
|
||||
} catch (Exception e) {
|
||||
log.error("MD5 Error...", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final String toHex(byte hash[]) {
|
||||
if (hash == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuffer buf = new StringBuffer(hash.length * 2);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hash.length; i++) {
|
||||
if ((hash[i] & 0xff) < 0x10) {
|
||||
buf.append("0");
|
||||
}
|
||||
buf.append(Long.toString(hash[i] & 0xff, 16));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String hash(String s) {
|
||||
try {
|
||||
return new String(toHex(md5(s)).getBytes("UTF-8"), "UTF-8");
|
||||
} catch (Exception e) {
|
||||
log.error("not supported charset...{}", e);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
/**
|
||||
* 媒体类型工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class MimeTypeUtils
|
||||
{
|
||||
public static final String IMAGE_PNG = "image/png";
|
||||
|
||||
public static final String IMAGE_JPG = "image/jpg";
|
||||
|
||||
public static final String IMAGE_JPEG = "image/jpeg";
|
||||
|
||||
public static final String IMAGE_BMP = "image/bmp";
|
||||
|
||||
public static final String IMAGE_GIF = "image/gif";
|
||||
|
||||
public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" };
|
||||
|
||||
public static final String[] FLASH_EXTENSION = { "swf", "flv" };
|
||||
|
||||
public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
|
||||
"asf", "rm", "rmvb" };
|
||||
|
||||
public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
|
||||
|
||||
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
|
||||
// 图片
|
||||
"bmp", "gif", "jpg", "jpeg", "png",
|
||||
// word excel powerpoint
|
||||
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
|
||||
// 压缩文件
|
||||
"rar", "zip", "gz", "bz2",
|
||||
// 视频格式
|
||||
"mp4", "avi", "rmvb",
|
||||
// pdf
|
||||
"pdf" };
|
||||
|
||||
public static String getExtension(String prefix)
|
||||
{
|
||||
switch (prefix)
|
||||
{
|
||||
case IMAGE_PNG:
|
||||
return "png";
|
||||
case IMAGE_JPG:
|
||||
return "jpg";
|
||||
case IMAGE_JPEG:
|
||||
return "jpeg";
|
||||
case IMAGE_BMP:
|
||||
return "bmp";
|
||||
case IMAGE_GIF:
|
||||
return "gif";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* @author:xingquanxiang createTime:2020/1/10 18:47
|
||||
* description: 加密加盐工具类, 带加密 和 校验
|
||||
*/
|
||||
public class PasswordUtil {
|
||||
/**
|
||||
* md5加密处理
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
private static String md5(String s) {
|
||||
try {
|
||||
//MessageDigest是封装md5算法的工具对象还支持SHA算法
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
//通过digest拿到的任意字符串,得到的bates都是等长的
|
||||
byte[] bytes = md.digest(s.getBytes("utf-8"));
|
||||
return toHex(bytes);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static String toHex(byte[] bytes) {
|
||||
//toHex的字符串把二进制转换成十六进制
|
||||
final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
|
||||
StringBuilder ret = new StringBuilder(bytes.length * 2);
|
||||
//循环判断是为了补位操作
|
||||
for (int i=0; i<bytes.length; i++) {
|
||||
ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
|
||||
ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对密码进行加密加盐操作
|
||||
* @param password
|
||||
* @return
|
||||
*/
|
||||
public static String encode(String password,String salt){
|
||||
//加密
|
||||
String hexPwd = md5(password);
|
||||
//加盐操作
|
||||
StringBuilder builder = new StringBuilder(hexPwd);
|
||||
builder.insert(18, salt);
|
||||
//返回加密加盐后的字符串
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 校对密码是否匹配,匹配则返回true
|
||||
* @param password
|
||||
* @param dbPassword
|
||||
* @return
|
||||
*/
|
||||
public static boolean match(String password, String dbPassword){
|
||||
StringBuilder builder = new StringBuilder(dbPassword);
|
||||
//去盐操作,生成md5加密后原始字符
|
||||
builder.replace(18, 26, "");
|
||||
//加密新密码,生成md5加密字符
|
||||
password = md5(password);
|
||||
//校对加密字符与原始字符是否匹配
|
||||
return password.equals(builder.toString());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
boolean match = match("123456", "E10ADC3949BA59ABBEntcqRM9U56E057F20F883E");
|
||||
System.out.println(match);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomUtil {
|
||||
public static final String ALLCHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
public static final String LETTERCHAR = "abcdefghijkllmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机字符串(只包含大小写字母、数字)
|
||||
*
|
||||
* @param length
|
||||
* 随机字符串长度
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String generateString(int length) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机纯字母字符串(只包含大小写字母)
|
||||
*
|
||||
* @param length
|
||||
* 随机字符串长度
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String generateMixString(int length) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(ALLCHAR.charAt(random.nextInt(LETTERCHAR.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机纯大写字母字符串(只包含大小写字母)
|
||||
*
|
||||
* @param length
|
||||
* 随机字符串长度
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String generateLowerString(int length) {
|
||||
return generateMixString(length).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个定长的随机纯小写字母字符串(只包含大小写字母)
|
||||
*
|
||||
* @param length
|
||||
* 随机字符串长度
|
||||
* @return 随机字符串
|
||||
*/
|
||||
public static String generateUpperString(int length) {
|
||||
return generateMixString(length).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个定长的纯0字符串
|
||||
*
|
||||
* @param length
|
||||
* 字符串长度
|
||||
* @return 纯0字符串
|
||||
*/
|
||||
public static String generateZeroString(int length) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append('0');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数字生成一个定长的字符串,长度不够前面补0
|
||||
*
|
||||
* @param num
|
||||
* 数字
|
||||
* @param fixdlenth
|
||||
* 字符串长度
|
||||
* @return 定长的字符串
|
||||
*/
|
||||
public static String toFixdLengthString(long num, int fixdlenth) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String strNum = String.valueOf(num);
|
||||
if (fixdlenth - strNum.length() >= 0) {
|
||||
sb.append(generateZeroString(fixdlenth - strNum.length()));
|
||||
} else {
|
||||
throw new RuntimeException("将数字" + num + "转化为长度为" + fixdlenth
|
||||
+ "的字符串发生异常!");
|
||||
}
|
||||
sb.append(strNum);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次生成的len位数都不相同
|
||||
*
|
||||
* @param param
|
||||
* @return 定长的数字
|
||||
*/
|
||||
public static int getNotSimple(int[] param, int len) {
|
||||
Random rand = new Random();
|
||||
for (int i = param.length; i > 1; i--) {
|
||||
int index = rand.nextInt(i);
|
||||
int tmp = param[index];
|
||||
param[index] = param[i - 1];
|
||||
param[i - 1] = tmp;
|
||||
}
|
||||
int result = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
result = result * 10 + param[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("返回一个定长的随机字符串(只包含大小写字母、数字):" + generateString(10));
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 客户端工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class ServletUtils {
|
||||
/**
|
||||
* 定义移动端请求的所有可能类型
|
||||
*/
|
||||
private final static String[] agent = { "Android", "iPhone", "iPod", "iPad", "Windows Phone", "MQQBrowser" };
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name)
|
||||
{
|
||||
return getRequest().getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter(String name, String defaultValue)
|
||||
{
|
||||
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name)
|
||||
{
|
||||
return Convert.toInt(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt(String name, Integer defaultValue)
|
||||
{
|
||||
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name)
|
||||
{
|
||||
return Convert.toBool(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name, Boolean defaultValue)
|
||||
{
|
||||
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取request
|
||||
*/
|
||||
public static HttpServletRequest getRequest()
|
||||
{
|
||||
return getRequestAttributes().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取response
|
||||
*/
|
||||
public static HttpServletResponse getResponse()
|
||||
{
|
||||
return getRequestAttributes().getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session
|
||||
*/
|
||||
public static HttpSession getSession()
|
||||
{
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
public static ServletRequestAttributes getRequestAttributes()
|
||||
{
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes) attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串渲染到客户端
|
||||
*
|
||||
* @param response 渲染对象
|
||||
* @param string 待渲染的字符串
|
||||
* @return null
|
||||
*/
|
||||
public static String renderString(HttpServletResponse response, String string)
|
||||
{
|
||||
try
|
||||
{
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.getWriter().print(string);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是Ajax异步请求
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public static boolean isAjaxRequest(HttpServletRequest request)
|
||||
{
|
||||
String accept = request.getHeader("accept");
|
||||
if (accept != null && accept.indexOf("application/json") != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String ajax = request.getParameter("__ajax");
|
||||
if (StringUtils.inStringIgnoreCase(ajax, "json", "xml"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断User-Agent 是不是来自于手机
|
||||
*/
|
||||
public static boolean checkAgentIsMobile(String ua)
|
||||
{
|
||||
boolean flag = false;
|
||||
if (!ua.contains("Windows NT") || (ua.contains("Windows NT") && ua.contains("compatible; MSIE 9.0;")))
|
||||
{
|
||||
// 排除 苹果桌面系统
|
||||
if (!ua.contains("Windows NT") && !ua.contains("Macintosh"))
|
||||
{
|
||||
for (String item : agent)
|
||||
{
|
||||
if (ua.contains(item))
|
||||
{
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean ioc 静态工具类
|
||||
* @author kevin
|
||||
* @date 2020/9/17 13:21
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
@SuppressWarnings("unused")
|
||||
public class SpringIocContext implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* spring IOC容器
|
||||
* @see SpringApplication #createApplicationContext()
|
||||
*/
|
||||
public static ApplicationContext applicationContext;
|
||||
|
||||
public SpringIocContext() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
|
||||
SpringIocContext.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取带有 注解的类
|
||||
* @param annotationClass 注解类
|
||||
* @return 标注了注解的Bean
|
||||
*/
|
||||
public static Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationClass) {
|
||||
try {
|
||||
return applicationContext.getBeansWithAnnotation(annotationClass);
|
||||
} catch (Exception var2) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ioc容器获取bean
|
||||
* @param beanClass bean {@link Class}
|
||||
* @param <T> 泛型
|
||||
* @return bean对象
|
||||
*/
|
||||
public static <T> T getBean(Class<T> beanClass) {
|
||||
return applicationContext.getBean(beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从ioc容器获取bean
|
||||
* @param beanName bean名称
|
||||
* @return bean对象
|
||||
*/
|
||||
public static Object getBean(String beanName) {
|
||||
return applicationContext.getBean(beanName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取aop代理对象
|
||||
*
|
||||
* @param invoker
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy(T invoker)
|
||||
{
|
||||
return (T) AopContext.currentProxy();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,478 @@
|
||||
package com.mosty.common.base.util;
|
||||
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/**
|
||||
* 空字符串
|
||||
*/
|
||||
private static final String NULLSTR = "";
|
||||
|
||||
/**
|
||||
* 下划线
|
||||
*/
|
||||
private static final char SEPARATOR = '_';
|
||||
|
||||
/**
|
||||
* 获取参数不为空值
|
||||
*
|
||||
* @param value defaultValue 要判断的value
|
||||
* @return value 返回值
|
||||
*/
|
||||
public static <T> T nvl(T value, T defaultValue) {
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否为空, 包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Collection<?> coll) {
|
||||
return isNull(coll) || coll.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否非空,包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Collection<?> coll) {
|
||||
return !isEmpty(coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否为空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
* * @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Object[] objects) {
|
||||
return isNull(objects) || (objects.length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否非空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Object[] objects) {
|
||||
return !isEmpty(objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(Map<?, ?> map) {
|
||||
return isNull(map) || map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty(Map<?, ?> map) {
|
||||
return !isEmpty(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(String str) {
|
||||
return isNull(str) || NULLSTR.equals(str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为非空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:非空串 false:空串
|
||||
*/
|
||||
public static boolean isNotEmpty(String str) {
|
||||
return !isEmpty(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否为空
|
||||
*
|
||||
* @param object Object
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isNull(Object object) {
|
||||
return object == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否非空
|
||||
*
|
||||
* @param object Object
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotNull(Object object) {
|
||||
return !isNull(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否是数组类型(Java基本型别的数组)
|
||||
*
|
||||
* @param object 对象
|
||||
* @return true:是数组 false:不是数组
|
||||
*/
|
||||
public static boolean isArray(Object object) {
|
||||
return isNotNull(object) && object.getClass().isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 去空格
|
||||
*/
|
||||
public static String trim(String str) {
|
||||
return (str == null ? "" : str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start) {
|
||||
if (str == null) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (start > str.length()) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
return str.substring(start);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @param end 结束
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start, int end) {
|
||||
if (str == null) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (end < 0) {
|
||||
end = str.length() + end;
|
||||
}
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (end > str.length()) {
|
||||
end = str.length();
|
||||
}
|
||||
|
||||
if (start > end) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = 0;
|
||||
}
|
||||
|
||||
return str.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文本, {} 表示占位符<br>
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||
* 例:<br>
|
||||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||
*
|
||||
* @param template 文本模板,被替换的部分用 {} 表示
|
||||
* @param params 参数值
|
||||
* @return 格式化后的文本
|
||||
*/
|
||||
// public static String format(String template, Object... params)
|
||||
// {
|
||||
// if (isEmpty(params) || isEmpty(template))
|
||||
// {
|
||||
// return template;
|
||||
// }
|
||||
// return StrFormatter.format(template, params);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 是否为http(s)://开头
|
||||
*
|
||||
* @param link 链接
|
||||
* @return 结果
|
||||
*/
|
||||
// public static boolean ishttp(String link)
|
||||
// {
|
||||
// return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 字符串转set
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 分隔符
|
||||
* @return set集合
|
||||
*/
|
||||
public static final Set<String> str2Set(String str, String sep) {
|
||||
return new HashSet<String>(str2List(str, sep, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转list
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 分隔符
|
||||
* @param filterBlank 过滤纯空白
|
||||
* @param trim 去掉首尾空白
|
||||
* @return list集合
|
||||
*/
|
||||
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// 过滤空白字符串
|
||||
if (filterBlank && StringUtils.isBlank(str)) {
|
||||
return list;
|
||||
}
|
||||
String[] split = str.split(sep);
|
||||
for (String string : split) {
|
||||
if (filterBlank && StringUtils.isBlank(string)) {
|
||||
continue;
|
||||
}
|
||||
if (trim) {
|
||||
string = string.trim();
|
||||
}
|
||||
list.add(string);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
|
||||
*
|
||||
* @param cs 指定字符串
|
||||
* @param searchCharSequences 需要检查的字符串数组
|
||||
* @return 是否包含任意一个字符串
|
||||
*/
|
||||
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
|
||||
if (isEmpty(cs) || isEmpty(searchCharSequences)) {
|
||||
return false;
|
||||
}
|
||||
for (CharSequence testStr : searchCharSequences) {
|
||||
if (containsIgnoreCase(cs, testStr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线命名
|
||||
*/
|
||||
public static String toUnderScoreCase(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 前置字符是否大写
|
||||
boolean preCharIsUpperCase = true;
|
||||
// 当前字符是否大写
|
||||
boolean curreCharIsUpperCase = true;
|
||||
// 下一字符是否大写
|
||||
boolean nexteCharIsUpperCase = true;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char c = str.charAt(i);
|
||||
if (i > 0) {
|
||||
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
|
||||
} else {
|
||||
preCharIsUpperCase = false;
|
||||
}
|
||||
|
||||
curreCharIsUpperCase = Character.isUpperCase(c);
|
||||
|
||||
if (i < (str.length() - 1)) {
|
||||
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
|
||||
}
|
||||
|
||||
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
|
||||
sb.append(SEPARATOR);
|
||||
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
|
||||
sb.append(SEPARATOR);
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含字符串
|
||||
*
|
||||
* @param str 验证字符串
|
||||
* @param strs 字符串组
|
||||
* @return 包含返回true
|
||||
*/
|
||||
public static boolean inStringIgnoreCase(String str, String... strs) {
|
||||
if (str != null && strs != null) {
|
||||
for (String s : strs) {
|
||||
if (str.equalsIgnoreCase(trim(s))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除最后一个字符串
|
||||
*
|
||||
* @param str 输入字符串
|
||||
* @param spit 以什么类型结尾的
|
||||
* @return 截取后的字符串
|
||||
*/
|
||||
public static String lastStringDel(String str, String spit) {
|
||||
if (!StringUtils.isEmpty(str) && str.endsWith(spit)) {
|
||||
return str.subSequence(0, str.length() - 1).toString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
|
||||
*
|
||||
* @param name 转换前的下划线大写方式命名的字符串
|
||||
* @return 转换后的驼峰式命名的字符串
|
||||
*/
|
||||
public static String convertToCamelCase(String name) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
// 快速检查
|
||||
if (name == null || name.isEmpty()) {
|
||||
// 没必要转换
|
||||
return "";
|
||||
} else if (!name.contains("_")) {
|
||||
// 不含下划线,仅将首字母大写
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
// 用下划线将原始字符串分割
|
||||
String[] camels = name.split("_");
|
||||
for (String camel : camels) {
|
||||
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
||||
if (camel.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// 首字母大写
|
||||
result.append(camel.substring(0, 1).toUpperCase());
|
||||
result.append(camel.substring(1).toLowerCase());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰式命名法
|
||||
* 例如:user_name->userName
|
||||
*/
|
||||
public static String toCamelCase(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
if (s.indexOf(SEPARATOR) == -1) {
|
||||
return s;
|
||||
}
|
||||
s = s.toLowerCase();
|
||||
StringBuilder sb = new StringBuilder(s.length());
|
||||
boolean upperCase = false;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
|
||||
if (c == SEPARATOR) {
|
||||
upperCase = true;
|
||||
} else if (upperCase) {
|
||||
sb.append(Character.toUpperCase(c));
|
||||
upperCase = false;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
|
||||
*
|
||||
* @param str 指定字符串
|
||||
* @param strs 需要检查的字符串数组
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean matches(String str, List<String> strs) {
|
||||
if (isEmpty(str) || isEmpty(strs)) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : strs) {
|
||||
if (isMatch(pattern, str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断url是否与规则配置:
|
||||
* ? 表示单个字符;
|
||||
* * 表示一层路径内的任意字符串,不可跨层级;
|
||||
* ** 表示任意层路径;
|
||||
*
|
||||
* @param pattern 匹配规则
|
||||
* @param url 需要匹配的url
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String url) {
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
return matcher.match(pattern, url);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T cast(Object obj) {
|
||||
return (T) obj;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.mosty.common.base.util.SpringIocContext,\
|
||||
com.mosty.common.base.autoconfig.swagger.SwaggerConfig,\
|
||||
com.mosty.common.base.threadpool.SimpleThreadPool,\
|
||||
com.mosty.common.base.threadpool.log.OperationLogThreadPool,\
|
||||
com.mosty.common.base.mybatis.MybatisConfig,\
|
||||
com.mosty.common.base.exception.MostyExceptionHandler
|
||||
# ,\
|
||||
#com.mosty.common.base.autoconfig.JacksonCustomizerConfig
|
Reference in New Issue
Block a user