Commit ddb7c473 by Shadow

im-common项目迁移

parent 1a3c22be
package com.wecloud.im.biz.config;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.RequestHandler;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.Annotations;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.schema.ApiModelProperties;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.wecloud.im.core.common.exception.SpringBootPlusConfigException;
import com.wecloud.im.swagger.SwaggerProperties;
import com.wecloud.netty.dispatch.extend.ActionRequest;
import com.wecloud.netty.dispatch.extend.ArgumentBox;
/**
* Swagger2全局配置
* 分组教程 https://sns.bladex.vip/q-342.html
*
* @author geekidea
* @date 2020/3/21
*/
@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
@ConditionalOnProperty(value = {"knife4j.enable"}, matchIfMissing = true)
public class Swagger2Config {
/**
* 扫描多包时,包路径的拆分符,分号
*/
private static final String SPLIT_COMMA = ",";
/**
* 扫描多包时,包路径的拆分符,逗号
*/
private static final String SPLIT_SEMICOLON = ";";
/**
* Swagger忽略的参数类型
*/
private final Class<?>[] ignoredParameterTypes = new Class[]{
ServletRequest.class,
ServletResponse.class,
HttpServletRequest.class,
HttpServletResponse.class,
HttpSession.class,
ActionRequest.class,
ArgumentBox.class,
ApiIgnore.class
};
@Autowired
private SwaggerProperties swaggerProperties;
public static Predicate<RequestHandler> basePackage(final String[] basePackages) {
return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true);
}
private static Function<Class<?>, Boolean> handlerPackage(final String[] basePackages) {
return input -> {
// 循环判断匹配
for (String strPackage : basePackages) {
boolean isMatch = input.getPackage().getName().startsWith(strPackage);
if (isMatch) {
return true;
}
}
return false;
};
}
@SuppressWarnings("deprecation")
private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
return Optional.fromNullable(input.declaringClass());
}
@Bean
public Docket restAppApi() {
// 获取需要扫描的包
String[] basePackages = {"com.wecloud"};
ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("app")
.select();
// 如果扫描的包为空,则默认扫描类上有@Api注解的类
if (ArrayUtils.isEmpty(basePackages)) {
apiSelectorBuilder.apis(RequestHandlerSelectors.withClassAnnotation(Api.class));
} else {
// 扫描指定的包
apiSelectorBuilder.apis(basePackage(basePackages));
}
Docket docket = apiSelectorBuilder.paths(PathSelectors.any())
.build()
.enable(swaggerProperties.isEnable())
.ignoredParameterTypes(ignoredParameterTypes)
.globalOperationParameters(getParameters());
return docket;
}
/**
* 获取apiInfo
*
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(swaggerProperties.getTitle())
.description(swaggerProperties.getDescription())
.termsOfServiceUrl(swaggerProperties.getUrl())
.contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
.version(swaggerProperties.getVersion())
.build();
}
/**
* 获取扫描的包
*
* @return
*/
public String[] getBasePackages() {
log.info("swaggerProperties = " + swaggerProperties);
String basePackage = swaggerProperties.getBasePackage();
if (StringUtils.isBlank(basePackage)) {
throw new SpringBootPlusConfigException("Swagger basePackage不能为空");
}
String[] basePackages = null;
if (basePackage.contains(SPLIT_COMMA)) {
basePackages = basePackage.split(SPLIT_COMMA);
} else if (basePackage.contains(SPLIT_SEMICOLON)) {
basePackages = basePackage.split(SPLIT_SEMICOLON);
}
log.info("swagger scan basePackages:" + Arrays.toString(basePackages));
return basePackages;
}
/**
* 添加额外参数
*
* @return
*/
private List<Parameter> getParameters() {
// 获取自定义参数配置
List<SwaggerProperties.ParameterConfig> parameterConfig = swaggerProperties.getParameterConfig();
if (CollectionUtils.isEmpty(parameterConfig)) {
return null;
}
List<Parameter> parameters = new ArrayList<>();
parameterConfig.forEach(parameter -> {
// 设置自定义参数
parameters.add(new ParameterBuilder()
.name(parameter.getName())
.description(parameter.getDescription())
.modelRef(new ModelRef(parameter.getDataType()))
.parameterType(parameter.getType())
.required(parameter.isRequired())
.defaultValue(parameter.getDefaultValue())
.build());
});
return parameters;
}
/**
* 按照类中字段顺序显示
*/
@Component
public static class ApiModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {
@Override
public void apply(ModelPropertyContext context) {
try {
Optional<BeanPropertyDefinition> beanPropertyDefinitionOptional = context.getBeanPropertyDefinition();
Optional<ApiModelProperty> annotation = Optional.absent();
if (context.getAnnotatedElement().isPresent()) {
annotation = annotation.or(ApiModelProperties.findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
}
if (context.getBeanPropertyDefinition().isPresent()) {
annotation = annotation.or(Annotations.findPropertyAnnotation(context.getBeanPropertyDefinition().get(), ApiModelProperty.class));
}
if (beanPropertyDefinitionOptional.isPresent()) {
BeanPropertyDefinition beanPropertyDefinition = beanPropertyDefinitionOptional.get();
if (annotation.isPresent() && annotation.get().position() != 0) {
return;
}
AnnotatedField annotatedField = beanPropertyDefinition.getField();
if (annotatedField == null) {
return;
}
Class<?> clazz = annotatedField.getDeclaringClass();
Field[] fields = clazz.getDeclaredFields();
// 获取当前字段对象
Field field = clazz.getDeclaredField(annotatedField.getName());
boolean required = false;
// 获取字段注解
NotNull notNull = field.getDeclaredAnnotation(NotNull.class);
NotBlank notBlank = field.getDeclaredAnnotation(NotBlank.class);
if (notNull != null || notBlank != null) {
required = true;
}
int position = ArrayUtils.indexOf(fields, field);
if (position != -1) {
context.getBuilder().position(position).required(required);
}
}
} catch (Exception exception) {
log.error("Swagger ApiModelProperty预处理异常", exception);
}
}
@Override
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}
}
......@@ -28,16 +28,16 @@ spring:
# Redis配置
redis:
# database: 0
# host: 127.0.0.1
# password:
# port: 6379
database: 0
host: 127.0.0.1
password:
host: 121.37.22.224
password: temple123456
port: 6379
# database: 0
# host: 121.37.22.224
# password: temple123456
# port: 6379
dubbo:
protocol:
port: 20882
......@@ -57,7 +57,7 @@ load-blance:
# NameServer地址 用;作为地址的分隔符
rocketmq:
namesrvAddr: 127.0.0.1:9876
namesrvAddr: 121.37.22.224:9876
# 生产者的组名
producerId: im-server
......
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wecloud</groupId>
<artifactId>im-common-bom</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<lombok.version>1.18.12</lombok.version>
<mapstruct.version>1.3.1.Final</mapstruct.version>
<utf8>UTF-8</utf8>
<dubbo.version>2.7.14</dubbo.version>
<im.version>2.0</im.version>
<project.build.sourceEncoding>${utf8}</project.build.sourceEncoding>
<project.reporting.outputEncoding>${utf8}</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-boot.version>2.2.5.RELEASE</spring-boot.version>
<spring-boot-admin.version>2.2.2</spring-boot-admin.version>
<mybatis-plus-boot-starter.version>3.3.1</mybatis-plus-boot-starter.version>
<velocity.version>2.2</velocity.version>
<commons-lang3.version>3.9</commons-lang3.version>
<commons-collections4.version>4.4</commons-collections4.version>
<commons-io.version>2.6</commons-io.version>
<commons-codec.version>1.14</commons-codec.version>
<commons-net.version>3.6</commons-net.version>
<commons-pool2.version>2.8.0</commons-pool2.version>
<commons-text.version>1.8</commons-text.version>
<fastjson.version>1.2.67</fastjson.version>
<mysql.version>5.1.47</mysql.version>
<reflections.version>0.9.9</reflections.version>
<jansi.version>1.18</jansi.version>
<hutool.version>5.7.22</hutool.version>
<junit.version>4.12</junit.version>
<ini4j.version>0.5.4</ini4j.version>
<mapstruct.version>1.3.1.Final</mapstruct.version>
<shiro.version>1.5.1</shiro.version>
<jwt.version>3.10.1</jwt.version>
<guava.version>28.2-jre</guava.version>
<snakeyaml.version>1.26</snakeyaml.version>
<swagger2.version>2.9.2</swagger2.version>
<knife4j.version>2.0.2</knife4j.version>
<pagehelper.version>1.4.2</pagehelper.version>
<mybatis-spring-boot-starter.versiong>2.0.1</mybatis-spring-boot-starter.versiong>
<druid.version>1.1.10</druid.version>
<rocket.version>4.3.2</rocket.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring-boot start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<!-- spring-boot end -->
<!-- mybatis-plus begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<!-- mybatis-plus end -->
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- swagger start -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.springfox</groupId>-->
<!-- <artifactId>springfox-swagger-ui</artifactId>-->
<!-- <version>${swagger2.version}</version>-->
<!-- </dependency>-->
<!-- swagger end -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- apache commons start -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons-net.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${commons-pool2.version}</version>
</dependency>
<!-- apache commons end -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${reflections.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>${jansi.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- ini格式处理 -->
<dependency>
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
<version>${ini4j.version}</version>
</dependency>
<!-- 对象属性复制 https://mapstruct.org/ -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<!-- XSS -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>${commons-text.version}</version>
</dependency>
<!-- 项目模块start -->
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>config</artifactId>
<version>${im.version}</version>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-swagger</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wecloud.imserver</groupId>
<artifactId>client</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-log</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-datasource</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-redis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-security</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-server</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-web</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.versiong}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 项目模块end -->
<!-- RocketMq start -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>${rocket.version}</version>
</dependency>
<!-- RocketMq start -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
<?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">
<groupId>com.wecloud</groupId>
<artifactId>im-common-core</artifactId>
<version>1.0-SNAPSHOT</version>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<description>im server 核心模块</description>
<dependencies>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- spring-boot start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring-boot end -->
<!-- mybatis-plus begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- mybatis-plus end -->
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- swagger start -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<!-- swagger end -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
<!-- Shiro+JWT start -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
</dependency>
<!-- Shiro+JWT end -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.ini4j</groupId>
<artifactId>ini4j</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<!-- RocketMq start -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
</dependency>
<!-- RocketMq start -->
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-bom</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
package com.wecloud.im.core.bean;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* Filter请求详情信息
*
* @author geekidea
* @date 2020/3/26
**/
@Data
@Accessors(chain = true)
public class RequestDetail implements Serializable {
private static final long serialVersionUID = 2543641512850125440L;
/**
* 请求ip地址
*/
private String ip;
/**
* 请求路径
*/
private String path;
}
package com.wecloud.im.core.common.api;
/**
* <p>
* REST API 响应码
* </p>
*
* @author geekidea
* @since 2018-11-08
*/
public enum ApiCode {
/**
* 操作成功
**/
SUCCESS(200, "api.response.code.SUCCESS"),
/**
* 非法访问
**/
UNAUTHORIZED(401, "api.response.code.UNAUTHORIZED"),
/**
* 没有权限
**/
NOT_PERMISSION(403, "api.response.code.NOT_PERMISSION"),
/**
* 你请求的资源不存在
**/
NOT_FOUND(404, "api.response.code.NOT_FOUND"),
/**
* 操作失败
**/
FAIL(500, "api.response.code.FAIL"),
/**
* 登录失败
**/
LOGIN_EXCEPTION(4000, "api.response.code.LOGIN_EXCEPTION"),
/**
* 系统异常
**/
SYSTEM_EXCEPTION(5000, "api.response.code.SYSTEM_EXCEPTION"),
/**
* 请求参数校验异常
**/
PARAMETER_EXCEPTION(5001, "api.response.code.PARAMETER_EXCEPTION"),
/**
* 请求参数解析异常
**/
PARAMETER_PARSE_EXCEPTION(5002, "api.response.code.PARAMETER_PARSE_EXCEPTION"),
/**
* HTTP内容类型异常
**/
HTTP_MEDIA_TYPE_EXCEPTION(5003, "api.response.code.HTTP_MEDIA_TYPE_EXCEPTION"),
/**
* 系统处理异常
**/
SPRING_BOOT_PLUS_EXCEPTION(5100, "api.response.code.SPRING_BOOT_PLUS_EXCEPTION"),
/**
* 业务处理异常
**/
BUSINESS_EXCEPTION(5101, "api.response.code.BUSINESS_EXCEPTION"),
/**
* 数据库处理异常
**/
DAO_EXCEPTION(5102, "api.response.code.DAO_EXCEPTION"),
/**
* 验证码校验异常
**/
VERIFICATION_CODE_EXCEPTION(5103, "api.response.code.VERIFICATION_CODE_EXCEPTION"),
/**
* 登录授权异常
**/
AUTHENTICATION_EXCEPTION(5104, "api.response.code.AUTHENTICATION_EXCEPTION"),
/**
* 没有访问权限
**/
UNAUTHENTICATED_EXCEPTION(5105, "api.response.code.UNAUTHENTICATED_EXCEPTION"),
/**
* 没有访问权限
**/
UNAUTHORIZED_EXCEPTION(5106, "api.response.code.UNAUTHORIZED_EXCEPTION"),
/**
* JWT Token解析异常
**/
JWTDECODE_EXCEPTION(5107, "api.response.code.JWTDECODE_EXCEPTION"),
/**
* 默认的异常处理
*/
HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION(5108, "api.response.code.HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION"),
/**
* 已有会话,不能重复创建会话
*/
REPETITION_CONVERSATION(6010, "api.response.code.REPETITION_CONVERSATION"),
/**
* 成员不存在,不能创建会话
*/
CLIENT_NOT_FOUNT(6011, "api.response.code.CLIENT_NOT_FOUNT"),
/**
* 被对方拉黑
*/
IS_BE_BLACK(6012, "api.response.code.IS_BE_BLACK"),
/**
* 你把对方拉黑
*/
IS_TO_BLACK(6013, "api.response.code.IS_TO_BLACK"),
/**
* 已被踢出会话
*/
IS_BE_KICK_OUT(6014, "api.response.code.IS_BE_KICK_OUT"),
/**
* 已被禁言
*/
IS_BE_MUTED(6015, "api.response.code.IS_BE_MUTED"),
/**
* 群聊已解散
*/
IS_BE_DISBAND(6016, "api.response.code.IS_BE_DISBAND"),
/**
* 群已禁止发链接
*/
IS_BE_FORBID_SEND_LINK(6017, "api.response.code.IS_BE_FORBID_SEND_LINK"),
/**
* 群已禁止发图片
*/
IS_BE_FORBID_SEND_PIC(6018, "api.response.code.IS_BE_FORBID_SEND_PIC"),
/**
* 消息超出数量限制
*/
MSG_EXCEED_QUANTITY_LIMIT(6019, "api.response.code.IS_BE_FORBID_SEND_PIC"),
/**
* 消息超出VIP数量限制
*/
EXCEED_VIP_QUANTITY_LIMIT(6020, "api.response.code.IS_BE_FORBID_SEND_PIC"),
;
private final int code;
private final String message;
ApiCode(final int code, final String message) {
this.code = code;
this.message = message;
}
public static ApiCode getApiCode(int code) {
ApiCode[] ecs = ApiCode.values();
for (ApiCode ec : ecs) {
if (ec.getCode() == code) {
return ec;
}
}
return SUCCESS;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
package com.wecloud.im.core.common.api;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import com.wecloud.im.core.config.il8n.I18nMessageUtil;
import com.wecloud.im.core.config.il8n.LanguageEnum;
/**
* <p>
* REST API 返回结果
* 国际化message封装
* </p>
*
* @author geekidea
* @since 2018-11-08
*/
@Data
@Accessors(chain = true)
@Builder
@AllArgsConstructor
public class ApiResult<T> implements Serializable {
private static final long serialVersionUID = 8004487252556526569L;
/**
* 响应码
*/
private int code;
/**
* 响应消息
*/
private String message;
/**
* 响应数据
*/
private T data;
public ApiResult() {
}
public static ApiResult<Boolean> result(boolean flag) {
if (flag) {
return ok();
}
return fail();
}
public static ApiResult<Boolean> result(ApiCode apiCode) {
return result(apiCode, null);
}
public static <T> ApiResult<T> result(ApiCode apiCode, T data) {
return result(apiCode, null, data);
}
public static <T> ApiResult<T> result(ApiCode apiCode, T data, String language) {
return result(apiCode, null, data);
}
public static <T> ApiResult<T> result(ApiCode apiCode, String message, T data) {
// boolean success = false;
// if (apiCode.getCode() == ApiCode.SUCCESS.getCode()) {
// success = true;
// }
// 多语言国际化,根据http上下文, 取得heard中的language语言属性,实现不用在业务代码中传递语言字段
// HttpServletRequest request = HttpServletRequestUtil.getRequest();
// String language = request.getHeader("language");
String success = "SUCCESS";
try {
message = I18nMessageUtil.getMessage(LanguageEnum.getLanguageType(null), apiCode.getMessage(), success);
} catch (IOException e) {
message = success;
}
return (ApiResult<T>) ApiResult.builder()
.code(apiCode.getCode())
.message(message)
.data(data)
// .success(success)
// .time(new Date())
.build();
}
public static ApiResult<Boolean> ok() {
return ok(true);
}
public static <T> ApiResult<T> ok(T data) {
return result(ApiCode.SUCCESS, data);
}
// public static <T> ApiResult<T> ok(T data, String message) {
// return result(ApiCode.SUCCESS, message, data);
// }
// public static ApiResult<Map<String, Object>> okMap(String key, Object value) {
// Map<String, Object> map = new HashMap<>(1);
// map.put(key, value);
// return ok(map);
// }
public static ApiResult<Boolean> fail(ApiCode apiCode) {
return result(apiCode, null);
}
public static <T> ApiResult<T> fail(ApiCode apiCode, String language) {
return result(apiCode, null);
}
public static <T> ApiResult<T> fail(ApiCode apiCode, T data) {
if (ApiCode.SUCCESS == apiCode) {
throw new RuntimeException("失败结果状态码不能为" + ApiCode.SUCCESS.getCode());
}
return result(apiCode, data);
}
public static ApiResult<String> fail(Integer errorCode, String message) {
return new ApiResult<String>()
// .setSuccess(false)
.setCode(errorCode)
.setMessage(message);
}
public static ApiResult<Map<String, Object>> fail(String key, Object value) {
Map<String, Object> map = new HashMap<>(1);
map.put(key, value);
return result(ApiCode.FAIL, map);
}
public static ApiResult<Boolean> fail() {
return fail(ApiCode.FAIL);
}
public static ApiResult<Boolean> fail(String language) {
return fail(ApiCode.FAIL, language);
}
}
package com.wecloud.im.core.common.bean;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
* 用户客户端信息对象
* </p>
*
* @author geekidea
* @date 2019-05-23
**/
@Data
public class ClientInfo implements Serializable {
private static final long serialVersionUID = -5549531244606897514L;
/**
* ip
*/
private String ip;
/**
* ip对应的地址
*/
private String addree;
/**
* 浏览器名称
*/
private String browserName;
/**
* 浏览器版本
*/
private String browserversion;
/**
* 浏览器引擎名称
*/
private String engineName;
/**
* 浏览器引擎版本
*/
private String engineVersion;
/**
* 系统名称
*/
private String osName;
/**
* 平台名称
*/
private String platformName;
/**
* 是否是手机
*/
private boolean mobile;
/**
* 移动端设备型号
*/
private String deviceName;
/**
* 移动端设备型号
*/
private String deviceModel;
}
package com.wecloud.im.core.common.bean;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
* 设备信息
* </p>
*
* @author geekidea
* @date 2019-05-24
**/
@Data
public class DeviceInfo implements Serializable {
private static final long serialVersionUID = -5912785220335057555L;
/**
* 设备名称
*/
private String name;
/**
* 设备型号
*/
private String model;
}
package com.wecloud.im.core.common.controller;
import lombok.extern.slf4j.Slf4j;
/**
* Controller父类
*
* @author geekidea
* @date 2018-11-08
*/
@Slf4j
public abstract class BaseController {
}
package com.wecloud.im.core.common.controller;
import springfox.documentation.annotations.ApiIgnore;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.wecloud.im.core.log.annotation.OperationLogIgnore;
import com.wecloud.im.core.util.UUIDUtil;
/**
* CSRF 供swagger调用
*
* @author geekidea
* @date 2019/12/10
**/
@ApiIgnore
@OperationLogIgnore
@RestController
public class CsrfController {
@RequestMapping(value = "/csrf", method = {RequestMethod.GET, RequestMethod.POST})
public String csrf() {
return UUIDUtil.getUuid();
}
}
package com.wecloud.im.core.common.controller;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
import org.apache.commons.collections4.CollectionUtils;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wecloud.im.core.common.api.ApiResult;
import com.wecloud.im.core.common.enums.BaseEnum;
import com.wecloud.im.core.common.vo.EnumVo;
import com.wecloud.im.core.log.annotation.OperationLogIgnore;
import com.wecloud.im.core.util.BaseEnumUtil;
/**
* <p>
* 展示实现BaseEnum接口的所有枚举值
* </p>
*
* @author geekidea
* @date 2018/11/02
*/
@RestController
@Slf4j
@OperationLogIgnore
@Api(value = "枚举字典", tags = {"枚举字典"})
public class EnumController {
private static final List<String> FRAMEWORK_ENUM_PACKAGES = Arrays.asList(
"io.geekidea.springbootplus.framework.common.enums",
"io.geekidea.springbootplus.system.enums");
/**
* 枚举包路径
*/
@Value("${spring-boot-plus.enum-packages}")
private List<String> enumPackages;
@GetMapping("/enum")
public ApiResult<Map<String, Map<Integer, EnumVo<? extends BaseEnum>>>> enumList() {
log.info("enumList...");
return ApiResult.ok(BaseEnumUtil.getEnumMap());
}
@PostConstruct
public void init() {
try {
if (enumPackages == null) {
enumPackages = new ArrayList<>();
}
enumPackages.addAll(FRAMEWORK_ENUM_PACKAGES);
// 获取BaseEnum接口的所有实现
log.info("enumPackages:" + enumPackages);
Reflections reflections = new Reflections(enumPackages);
Set<Class<? extends BaseEnum>> set = reflections.getSubTypesOf(BaseEnum.class);
if (CollectionUtils.isEmpty(set)) {
return;
}
// 循环获取BaseEnum枚举
for (Class<? extends BaseEnum> clazz : set) {
BaseEnum[] enumConstants = clazz.getEnumConstants();
Map<Integer, EnumVo<? extends BaseEnum>> enumVoMap = new ConcurrentHashMap<>(enumConstants.length);
for (BaseEnum baseEnum : enumConstants) {
Integer code = baseEnum.getCode();
String desc = baseEnum.getDesc();
EnumVo<BaseEnum> enumVo = new EnumVo<BaseEnum>()
.setCode(code)
.setDesc(desc)
.setBaseEnum(baseEnum);
enumVoMap.put(code, enumVo);
}
// 设置map
BaseEnumUtil.getEnumMap().put(clazz.getName(), enumVoMap);
}
log.info("enumMap:{}", BaseEnumUtil.getEnumMap());
} catch (Exception e) {
log.error("获取BaseEnum枚举map异常", e);
}
}
}
package com.wecloud.im.core.common.controller;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import com.wecloud.im.core.log.annotation.OperationLogIgnore;
/**
* <p>
* 项目根路径提示信息
* </p>
*
* @author geekidea
* @date 2018/11/12
*/
@Slf4j
@Controller
@OperationLogIgnore
@Api(value = "Index API", tags = {"Index"})
public class IndexController {
@GetMapping("/")
public String home() {
return "redirect:/index.html";
}
/**
* SwaggerUI
*/
@GetMapping("/docs")
public String docs() {
return "redirect:/swagger-ui.html";
}
// /**
// * SwaggerUI
// */
// @GetMapping("/websocketDocs")
// public String websocket() {
// return "redirect:/Wecloud-IM-Websocket-Docs.html";
// }
}
package com.wecloud.im.core.common.entity;
import io.swagger.annotations.ApiModel;
import java.io.Serializable;
/**
* 实体父类
*
* @author geekidea
* @date 2018-11-08
*/
@ApiModel("BaseEntity")
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = -7176390653391227433L;
}
package com.wecloud.im.core.common.enums;
/**
* 枚举类型父接口
*
* @author geekidea
* @date 2018-11-08
*/
public interface BaseEnum {
/**
* 通过枚举类型和code值获取对应的枚举类型
*
* @param enumType
* @param code
* @param <T>
* @return
*/
static <T extends BaseEnum> T valueOf(Class<? extends BaseEnum> enumType, Integer code) {
if (enumType == null || code == null) {
return null;
}
T[] enumConstants = (T[]) enumType.getEnumConstants();
if (enumConstants == null) {
return null;
}
for (T enumConstant : enumConstants) {
int enumCode = enumConstant.getCode();
if (code.equals(enumCode)) {
return enumConstant;
}
}
return null;
}
/**
* 获取枚举标识
*
* @return
*/
Integer getCode();
/**
* 获取枚举描述
*
* @return
*/
String getDesc();
}
package com.wecloud.im.core.common.exception;
import com.wecloud.im.core.common.api.ApiCode;
/**
* 业务异常
*
* @author geekidea
* @date 2018-11-08
*/
public class BusinessException extends SpringBootPlusException {
private static final long serialVersionUID = -2303357122330162359L;
public BusinessException(String message) {
super(message);
}
public BusinessException(Integer errorCode, String message) {
super(errorCode, message);
}
public BusinessException(ApiCode apiCode) {
super(apiCode);
}
}
package com.wecloud.im.core.common.exception;
import com.wecloud.im.core.common.api.ApiCode;
/**
* DAO异常
*
* @author geekidea
* @date 2018-11-08
*/
public class DaoException extends SpringBootPlusException {
private static final long serialVersionUID = -6912618737345878854L;
public DaoException(String message) {
super(message);
}
public DaoException(Integer errorCode, String message) {
super(errorCode, message);
}
public DaoException(ApiCode apiCode) {
super(apiCode);
}
}
package com.wecloud.im.core.common.exception;
import lombok.extern.slf4j.Slf4j;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wecloud.im.core.common.api.ApiCode;
import com.wecloud.im.core.common.api.ApiResult;
/**
* 全局Error/404处理
*
* @author geekidea
* @date 2018-11-08
*/
@ApiIgnore
@RestController
@Slf4j
public class GlobalErrorController implements ErrorController {
private static final String ERROR_PATH = "/error";
@RequestMapping(ERROR_PATH)
public ApiResult<?> handleError(HttpServletRequest request, HttpServletResponse response) {
int status = response.getStatus();
switch (status) {
case HttpServletResponse.SC_UNAUTHORIZED:
return ApiResult.fail(ApiCode.UNAUTHORIZED);
case HttpServletResponse.SC_FORBIDDEN:
return ApiResult.fail(ApiCode.NOT_PERMISSION);
case HttpServletResponse.SC_NOT_FOUND:
return ApiResult.fail(ApiCode.NOT_FOUND);
default:
break;
}
return ApiResult.fail(ApiCode.FAIL);
}
@Override
public String getErrorPath() {
log.error("errorPath....");
return ERROR_PATH;
}
}
package com.wecloud.im.core.common.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.wecloud.im.core.common.api.ApiCode;
/**
* spring-boot-plus配置异常
*
* @author geekidea
* @date 2020/3/21
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SpringBootPlusConfigException extends SpringBootPlusException {
private static final long serialVersionUID = 8952028631871769425L;
private Integer errorCode;
private String message;
public SpringBootPlusConfigException() {
super();
}
public SpringBootPlusConfigException(String message) {
super(message);
this.message = message;
}
public SpringBootPlusConfigException(Integer errorCode, String message) {
super(message);
this.errorCode = errorCode;
this.message = message;
}
public SpringBootPlusConfigException(ApiCode apiCode) {
super(apiCode.getMessage());
this.errorCode = apiCode.getCode();
this.message = apiCode.getMessage();
}
public SpringBootPlusConfigException(String message, Throwable cause) {
super(message, cause);
}
public SpringBootPlusConfigException(Throwable cause) {
super(cause);
}
}
package com.wecloud.im.core.common.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.wecloud.im.core.common.api.ApiCode;
/**
* 自定义异常
*
* @author geekidea
* @date 2018-11-08
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SpringBootPlusException extends RuntimeException {
private static final long serialVersionUID = -2470461654663264392L;
private Integer errorCode;
private String message;
public SpringBootPlusException() {
super();
}
public SpringBootPlusException(String message) {
super(message);
this.message = message;
}
public SpringBootPlusException(Integer errorCode, String message) {
super(message);
this.errorCode = errorCode;
this.message = message;
}
public SpringBootPlusException(ApiCode apiCode) {
super(apiCode.getMessage());
this.errorCode = apiCode.getCode();
this.message = apiCode.getMessage();
}
public SpringBootPlusException(String message, Throwable cause) {
super(message, cause);
}
public SpringBootPlusException(Throwable cause) {
super(cause);
}
}
package com.wecloud.im.core.common.param;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
/**
* ID参数
*
* @author geekidea
* @date 2018-11-08
*/
@Data
@ApiModel("ID参数")
public class IdParam implements Serializable {
private static final long serialVersionUID = -5353973980674510450L;
@NotNull(message = "ID不能为空")
private Long id;
}
package com.wecloud.im.core.common.service;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* 公共Service接口
*
* @author geekidea
* @date 2018-11-08
*/
public interface BaseService<T> extends IService<T> {
}
package com.wecloud.im.core.common.service.impl;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wecloud.im.core.common.service.BaseService;
import com.wecloud.im.core.util.LambdaColumn;
/**
* 公共Service父类
*
* @author geekidea
* @date 2018-11-08
*/
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
/**
* 实体类型
*/
private Class<?> entityClass;
{
Class<?> clazz = this.getClass();
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
Type[] p = ((ParameterizedType) type).getActualTypeArguments();
this.entityClass = (Class<T>) p[1];
}
}
/**
* 获取对应字段的数据表列名称
*
* @param func
* @return
*/
public String getLambdaColumn(SFunction<T, ?> func) {
return new LambdaColumn<T>().get(func);
}
}
package com.wecloud.im.core.common.vo;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 枚举类型VO
*
* @author geekidea
* @date 2019-11-02
**/
@Data
@Accessors(chain = true)
public class EnumVo<T> {
/**
* 枚举code
*/
private Integer code;
/**
* 枚举描述
*/
private String desc;
/**
* 枚举类型
*/
private T baseEnum;
}
package com.wecloud.im.core.config.converter;
import java.util.Date;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
/**
* 转换器配置
*
* @author geekidea
* @date 2018-11-08
*/
@Configuration
public class ConverterConfig {
@Bean
public Converter<String, Date> stringToDateConverter() {
return new StringToDateConverter();
}
@Bean
public Converter<String, Integer> stringToIntegerConverter() {
return new StringToIntegerConverter();
}
@Bean
public Converter<String, Double> stringToDoubleConverter() {
return new StringToDoubleConverter();
}
}
package com.wecloud.im.core.config.converter;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
/**
* <code>
* 日期转换器,将请求参数的日期字符串转换成java.util.Date类型
* </code>
*
* @author geekidea
* @date 2018-11-08
*/
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
return StringToDateUtil.convert(source);
}
}
package com.wecloud.im.core.config.converter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
/**
* <code>
* <pre>
* 日期转换器,将请求参数的日期字符串转换成java.util.Date类型
* 日期格式顺序:
* 1.yyyy-MM-dd HH:mm:ss:S
* 2.yyyy-MM-dd HH:mm:ss
* 3.yyyy-MM-dd HH:mm
* 4.yyyy-MM-dd HH
* 5.yyyy-MM-dd
* </pre>
* </code>
*
* @author geekidea
* @date 2018-11-08
*/
public class StringToDateUtil {
/**
* 时间戳字符长度,不包含毫秒
*/
private static final Integer TIMESTAMP_LENGTH = 10;
/**
* 日期格式化数组
*/
private static DateFormat[] dateFormats = {
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:S"),
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),
new SimpleDateFormat("yyyy-MM-dd HH:mm"),
new SimpleDateFormat("yyyy-MM-dd HH"),
new SimpleDateFormat("yyyy-MM-dd"),
new SimpleDateFormat("yyyy-MM")
};
/**
* <code>
* <pre>
* 1.如果日期字符串为空,则直接返回空
* 2.使用格式化组进行格式化,如果解析成功,则直接返回
* 4.否则,抛出非法参数异常
* @param source 请求的日期参数
* @return 解析后的日期类型:java.util.Date
* @exception IllegalArgumentException 非法参数异常
* </pre>
* </code>
*/
public static Date convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
source = source.trim();
try {
int timeLength = source.length();
Long time = Long.parseLong(source);
if (timeLength == TIMESTAMP_LENGTH) {
time = time * 1000;
}
Date date = new Date(time);
return date;
} catch (Exception e) {
}
Date date = null;
boolean flag = false;
for (DateFormat dateFormat : dateFormats) {
try {
date = dateFormat.parse(source);
flag = true;
break;
} catch (ParseException e) {
}
}
if (flag) {
return date;
} else {
throw new IllegalArgumentException("不能解析日期:" + source);
}
}
}
package com.wecloud.im.core.config.converter;
import org.springframework.core.convert.converter.Converter;
/**
* <code>
*
* </code>
*
* @author geekidea
* @date 2018-11-08
*/
public class StringToDoubleConverter implements Converter<String, Double> {
@Override
public Double convert(String source) {
return StringToDoubleUtil.convert(source);
}
}
package com.wecloud.im.core.config.converter;
import org.apache.commons.lang3.StringUtils;
/**
* <code>
* <pre>
* 空字符串("")转换成Double的null
*
* </pre>
* </code>
*
* @author geekidea
* @date 2018-11-08
*/
public class StringToDoubleUtil {
public static Double convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
Double d = Double.parseDouble(source);
return d;
}
}
package com.wecloud.im.core.config.converter;
import org.springframework.core.convert.converter.Converter;
/**
* <code>
*
* </code>
*
* @author geekidea
* @date 2018-11-08
*/
public class StringToIntegerConverter implements Converter<String, Integer> {
@Override
public Integer convert(String source) {
return StringToIntegerUtil.convert(source);
}
}
package com.wecloud.im.core.config.converter;
import org.apache.commons.lang3.StringUtils;
/**
* <code>
* <pre>
* 空字符串("")转换成Integer的null
*
* </pre>
* </code>
*
* @author geekidea
* @date 2018-11-08
*/
public class StringToIntegerUtil {
public static Integer convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
Integer i = Integer.parseInt(source);
return i;
}
}
package com.wecloud.im.core.config.il8n;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;
/**
* 多语言国际化消息工具类
*/
public class I18nMessageUtil {
// 根目录
private static final String PATH_PARENT = "classpath:static/i18n/messages_";
// 后缀
private static final String SUFFIX = ".properties";
// 分解器
private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
// 存取器
private static MessageSourceAccessor accessor;
private I18nMessageUtil() {
}
/**
* 初始化资源文件的存储器
* 加载指定语言配置文件
*
* @param language 语言类型(文件名即为语言类型,eg: en_us 表明使用 美式英文 语言配置)
*/
private static void initMessageSourceAccessor(String language) throws IOException {
/*
* 获取配置文件名
*/
Resource resource = RESOURCE_PATTERN_RESOLVER.getResource(PATH_PARENT + language + SUFFIX);
String fileName = resource.getURL().toString();
int lastIndex = fileName.lastIndexOf(".");
String baseName = fileName.substring(0, lastIndex);
/*
* 读取配置文件
*/
ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
reloadableResourceBundleMessageSource.setBasename(baseName);
reloadableResourceBundleMessageSource.setCacheSeconds(5);
reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8");
accessor = new MessageSourceAccessor(reloadableResourceBundleMessageSource);
}
/**
* 获取一条语言配置信息
*
* @param language 语言类型,zh_CN: 简体中文, en_US: 英文
* @param message 配置信息属性名,eg: api.response.code.user.signUp
* @param defaultMessage 默认信息,当无法从配置文件中读取到对应的配置信息时返回该信息
* @return
* @throws IOException
*/
public static String getMessage(String language, String message, String defaultMessage) throws IOException {
initMessageSourceAccessor(language);
return accessor.getMessage(message, defaultMessage, LocaleContextHolder.getLocale());
}
}
package com.wecloud.im.core.config.il8n;
import lombok.Getter;
import lombok.ToString;
import org.springframework.util.StringUtils;
/**
* 语言枚举类
*/
@Getter
@ToString
public enum LanguageEnum {
/**
* 美式英文
*/
LANGUAGE_EN_US("en_US"),
// /**
// * 柬埔寨 高棉语
// */
// LANGUAGE_KH("kh"),
/**
* 简体中文
*/
LANGUAGE_ZH_CN("zh_CN");
private final String language;
LanguageEnum(String language) {
this.language = language;
}
/**
* 获取指定语言类型(如果没有对应的语言类型,则返回中文)
*
* @param language 语言类型
* @return
*/
public static String getLanguageType(String language) {
// 设置默认为中文
if (StringUtils.isEmpty(language)) {
return LANGUAGE_ZH_CN.language;
}
for (LanguageEnum languageEnum : LanguageEnum.values()) {
if (languageEnum.language.equalsIgnoreCase(language)) {
return languageEnum.language;
}
}
return LANGUAGE_ZH_CN.language;
}
}
package com.wecloud.im.core.config.jackson.deserializer;
import java.io.IOException;
import java.util.Date;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.wecloud.im.core.config.converter.StringToDateUtil;
/**
* <p>
* Jackson Date序列化器
* </p>
*
* @author geekidea
* @date 2018-11-08
*/
public class JacksonDateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
String date = jp.getText();
return StringToDateUtil.convert(date);
}
}
package com.wecloud.im.core.config.jackson.deserializer;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.wecloud.im.core.config.converter.StringToDoubleUtil;
/**
* @author geekidea
* @date 2018-11-08
*/
public class JacksonDoubleDeserializer extends JsonDeserializer<Double> {
@Override
public Double deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String string = jsonParser.getText();
return StringToDoubleUtil.convert(string);
}
}
package com.wecloud.im.core.config.jackson.deserializer;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.wecloud.im.core.config.converter.StringToIntegerUtil;
/**
* @author geekidea
* @date 2018-11-08
*/
public class JacksonIntegerDeserializer extends JsonDeserializer<Integer> {
@Override
public Integer deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String string = jsonParser.getText();
return StringToIntegerUtil.convert(string);
}
}
package com.wecloud.im.core.config.jackson.deserializer;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.wecloud.im.core.constant.DatePattern;
/**
* <p>
* Jackson LocaDateTime反序列化器
* </p>
*
* @author geekidea
* @date 2018-11-08
*/
public class JacksonLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
String string = jp.getText();
if (StringUtils.isBlank(string)) {
return null;
}
return LocalDateTime.parse(string, DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD_HH_MM_SS));
}
}
package com.wecloud.im.core.config.jackson.deserializer;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.wecloud.im.core.config.converter.StringToDoubleUtil;
/**
* @author geekidea
* @date 2018-11-08
*/
public class JacksonLongDeserializer extends JsonDeserializer<Double> {
@Override
public Double deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String string = jsonParser.getText();
return StringToDoubleUtil.convert(string);
}
}
package com.wecloud.im.core.config.jackson.serializer;
import java.io.IOException;
import java.util.Date;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.wecloud.im.core.util.DateUtil;
/**
* <p>
* Jackson Date反序列化器
* </p>
*
* @author geekidea
* @date 2018-11-08
*/
public class JacksonDateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
String string = null;
if (date != null) {
string = DateUtil.getDateTimeString(date);
}
jsonGenerator.writeString(string);
}
}
package com.wecloud.im.core.config.jackson.serializer;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.wecloud.im.core.constant.DatePattern;
/**
* <p>
* Jackson LocalDateTime 自定义序列化器
* </p>
*
* @author geekidea
* @date 2018/11/8
*/
public class JacksonLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
String string = null;
if (localDateTime != null) {
string = localDateTime.format(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD_HH_MM_SS));
}
jsonGenerator.writeString(string);
}
}
package com.wecloud.im.core.config.jackson.serializer;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
/**
* <p>
* Jackson Long反序列化器
* </p>
*
* @author geekidea
* @date 2018-11-08
*/
public class JacksonLongSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
String string = null;
if (aLong != null) {
string = aLong.toString();
}
jsonGenerator.writeString(string);
}
}
package com.wecloud.im.core.constant;
/**
* <p>
* redis key 常量
* </p>
*
* @author geekidea
* @date 2019-05-23
**/
public interface ClientLoginRedisKey {
/**
* 登录用户信息key
* login:user:client
*/
String LOGIN_USER = "login:user:%s";
/**
* 登录用户username token
* login:user:token:client:token
*/
String LOGIN_USER_TOKEN = "login:user:token:%s:%s";
/**
* 登录用户下的所有token, 查询使用
* login:user:token:client:*
*/
String LOGIN_USER_ALL_TOKEN = "login:user:token:%s:*";
}
package com.wecloud.im.core.constant;
/**
* 公共常量
*
* @author geekidea
* @date 2018-11-08
*/
public interface CommonConstant {
/**
* 默认页码为1
*/
Long DEFAULT_PAGE_INDEX = 1L;
/**
* 默认页大小为10
*/
Long DEFAULT_PAGE_SIZE = 10L;
/**
* 数量最大为100
*/
Long MAX_PAGE_SIZE = 1000L;
/**
* 分页总行数名称
*/
String PAGE_TOTAL_NAME = "total";
/**
* 分页数据列表名称
*/
String PAGE_RECORDS_NAME = "records";
/**
* 分页当前页码名称
*/
String PAGE_INDEX_NAME = "pageIndex";
/**
* 分页当前页大小名称
*/
String PAGE_SIZE_NAME = "pageSize";
/**
* 登录token
*/
String JWT_DEFAULT_TOKEN_NAME = "token";
/**
* JWT-id
*/
String CLIENT_ID = "clientId";
String APP_KEY = "appKey";
String PLATFORM = "platform";
/**
* JWT刷新新token响应状态码
*/
int JWT_REFRESH_TOKEN_CODE = 460;
/**
* JWT刷新新token响应状态码,
* Redis中不存在,但jwt未过期,不生成新的token,返回361状态码
*/
int JWT_INVALID_TOKEN_CODE = 461;
/**
* JWT Token默认密钥
*/
String JWT_DEFAULT_SECRET = "666666";
/**
* JWT 默认过期时间,5184000L = 60天,单位秒
*/
Long JWT_DEFAULT_EXPIRE_SECOND = 5184000L;
/**
* ..
*/
String SPOT_SPOT = "..";
/**
* ../
*/
String SPOT_SPOT_BACKSLASH = "../";
/**
* 用户浏览器代理
*/
String USER_AGENT = "User-Agent";
/**
* 本机地址IP
*/
String LOCALHOST_IP = "127.0.0.1";
/**
* 本机地址名称
*/
String LOCALHOST_IP_NAME = "本机地址";
/**
* 局域网IP
*/
String LAN_IP = "192.168";
/**
* 局域网名称
*/
String LAN_IP_NAME = "局域网";
}
package com.wecloud.im.core.constant;
/**
* <p>
* redis key 常量
* </p>
*
* @author geekidea
* @date 2019-05-23
**/
public interface CommonRedisKey {
/**
* 登录用户token信息key
* login:token:tokenMd5
*/
String LOGIN_TOKEN = "login:token:%s";
/**
* 登录用户盐值信息key
* login:salt:clientId
*/
String LOGIN_SALT = "login:salt:%s";
}
package com.wecloud.im.core.constant;
/**
* <p>
* 日期格式常量
* </p>
*
* @author geekidea
* @date 2018-11-08
*/
public interface DatePattern {
/**
* 年-月-日
*/
String YYYY_MM_DD = "yyyy-MM-dd";
/**
* 年-月-日 时:分
*/
String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
/**
* 年-月-日 时:分:秒
*/
String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
/**
* 年-月-日 时:分:秒:毫秒
*/
String YYYY_MM_DD_HH_MM_SS_S = "yyyy-MM-dd HH:mm:ss.S";
/**
* 时:分
*/
String HH_MM = "HH:mm";
/**
* 时:分:秒
*/
String HH_MM_SS = "HH:mm:ss";
/**
* 时:分:秒:毫秒
*/
String HH_MM_SS_S = "HH:mm:ss:S";
}
package com.wecloud.im.core.exception;
import com.wecloud.im.core.common.api.ApiCode;
import com.wecloud.im.core.common.exception.SpringBootPlusException;
/**
* 系统登录异常
*
* @author geekidea
* @date 2019-08-04
*/
public class SysLoginException extends SpringBootPlusException {
private static final long serialVersionUID = -3157438982569715170L;
public SysLoginException(String message) {
super(message);
}
public SysLoginException(Integer errorCode, String message) {
super(errorCode, message);
}
public SysLoginException(ApiCode apiCode) {
super(apiCode);
}
}
package com.wecloud.im.core.exception;
import com.wecloud.im.core.common.api.ApiCode;
import com.wecloud.im.core.common.exception.SpringBootPlusException;
/**
* 验证码校验异常
*
* @author geekidea
* @date 2018-11-08
*/
public class VerificationCodeException extends SpringBootPlusException {
private static final long serialVersionUID = -2640690119865434398L;
public VerificationCodeException(String message) {
super(message);
}
public VerificationCodeException(Integer errorCode, String message) {
super(errorCode, message);
}
public VerificationCodeException(ApiCode apiCode) {
super(apiCode);
}
}
package com.wecloud.im.core.ip.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.wecloud.im.core.common.entity.BaseEntity;
/**
* IP地址
*
* @author geekidea
* @since 2020-03-25
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "IpAddress对象")
public class IpAddress extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String ipStart;
private String ipEnd;
@ApiModelProperty("区域")
private String area;
@ApiModelProperty("运营商")
private String operator;
private Long ipStartNum;
private Long ipEndNum;
}
package com.wecloud.im.core.ip.mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wecloud.im.core.ip.entity.IpAddress;
/**
* IP地址 Mapper 接口
*
* @author geekidea
* @since 2020-03-25
*/
@Repository
public interface IpAddressMapper extends BaseMapper<IpAddress> {
/**
* 通过ip地址获取IP对象
*
* @param ip
* @return
*/
IpAddress getByIp(@Param("ip") String ip);
}
package com.wecloud.im.core.ip.service;
import com.wecloud.im.core.common.service.BaseService;
import com.wecloud.im.core.ip.entity.IpAddress;
/**
* IP地址 服务类
*
* @author geekidea
* @since 2020-03-25
*/
public interface IpAddressService extends BaseService<IpAddress> {
/**
* 通过ip地址获取IP对象
*
* @param ip
* @return
*/
IpAddress getByIp(String ip);
/**
* 通过ip地址获取区域
*
* @param ip
* @return
*/
String getAreaByIp(String ip);
/**
* 通过ip地址获取运营商
*
* @param ip
* @return
*/
String getOperatorByIp(String ip);
}
package com.wecloud.im.core.ip.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.wecloud.im.core.common.service.impl.BaseServiceImpl;
import com.wecloud.im.core.constant.CommonConstant;
import com.wecloud.im.core.ip.entity.IpAddress;
import com.wecloud.im.core.ip.mapper.IpAddressMapper;
import com.wecloud.im.core.ip.service.IpAddressService;
/**
* IP地址 服务实现类
*
* @author geekidea
* @since 2020-03-25
*/
@Slf4j
@Service
public class IpAddressServiceImpl extends BaseServiceImpl<IpAddressMapper, IpAddress> implements IpAddressService {
@Autowired
private IpAddressMapper ipAddressMapper;
@Override
public IpAddress getByIp(String ip) {
if (StringUtils.isBlank(ip)) {
return null;
}
if (CommonConstant.LOCALHOST_IP.equals(ip)) {
return new IpAddress().setArea(CommonConstant.LOCALHOST_IP_NAME);
}
if (CommonConstant.LAN_IP.equals(ip)) {
return new IpAddress().setArea(CommonConstant.LAN_IP_NAME);
}
return ipAddressMapper.getByIp(ip);
}
@Override
public String getAreaByIp(String ip) {
IpAddress ipAddress = getByIp(ip);
if (ipAddress != null) {
return ipAddress.getArea();
}
return null;
}
@Override
public String getOperatorByIp(String ip) {
IpAddress ipAddress = getByIp(ip);
if (ipAddress != null) {
return ipAddress.getOperator();
}
return null;
}
}
package com.wecloud.im.core.log.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* 模块名称注解
*
* @author geekidea
* @date 2020/3/19
**/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Module {
/**
* 模块名称
*
* @return
*/
String name() default "";
/**
* 模块名称
*
* @return
*/
@AliasFor("name")
String value() default "";
}
package com.wecloud.im.core.log.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import com.wecloud.im.core.log.enums.OperationLogType;
/**
* 操作日志注解
* 记录:日志名称,日志类型,日志备注
*
* @author geekidea
* @date 2020/3/19
**/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
/**
* 日志名称
*
* @return
*/
String name() default "";
/**
* 日志名称
*
* @return
*/
@AliasFor("name")
String value() default "";
/**
* 日志类型
*
* @return
*/
OperationLogType type() default OperationLogType.OTHER;
/**
* 日志备注
*
* @return
*/
String remark() default "";
}
package com.wecloud.im.core.log.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 忽略操作日志记录注解
* 在controller上标注该方法后,将不会记录操作日志
* 可以标注在类和方法上,如果标记在类上,则会忽略controller中的所有方法
*
* @author geekidea
* @date 2020/3/19
**/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLogIgnore {
}
package com.wecloud.im.core.log.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import com.wecloud.im.core.common.enums.BaseEnum;
/**
* 操作日志类型枚举
*
* @author geekidea
* @date 2020/3/19
**/
@Getter
@AllArgsConstructor
public enum OperationLogType implements BaseEnum {
/**
* 其它
**/
OTHER(0, "其它"),
/**
* 添加
**/
ADD(1, "添加"),
/**
* 修改
**/
UPDATE(2, "修改"),
/**
* 删除
**/
DELETE(3, "删除"),
/**
* 查询
**/
query(4, "详情查询"),
/**
* 详情查询
**/
INFO(5, "详情查询"),
/**
* 列表查询
**/
LIST(6, "列表查询"),
/**
* 分页列表
**/
PAGE(7, "分页列表"),
/**
* 其它查询
**/
OTHER_QUERY(8, "其它查询"),
/**
* 文件上传
**/
UPLOAD(9, "文件上传"),
/**
* 文件下载
**/
download(10, "文件下载"),
/**
* Excel导入
**/
excel_import(11, "Excel导入"),
/**
* Excel导出
**/
EXCEL_EXPORT(12, "Excel导出");
private Integer code;
private String desc;
}
package com.wecloud.im.core.pagination;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
/**
* 可排序查询参数对象
*
* @author geekidea
* @since 2019-08-04
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("可排序查询参数对象")
public abstract class BasePageOrderParam extends BasePageParam {
private static final long serialVersionUID = 57714391204790143L;
@ApiModelProperty("排序")
private List<OrderItem> pageSorts;
public void defaultPageSort(OrderItem orderItem) {
this.defaultPageSorts(Arrays.asList(orderItem));
}
public void defaultPageSorts(List<OrderItem> pageSorts) {
if (CollectionUtils.isEmpty(pageSorts)) {
return;
}
this.pageSorts = pageSorts;
}
}
package com.wecloud.im.core.pagination;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import com.wecloud.im.core.constant.CommonConstant;
/**
* 查询参数
*
* @author geekidea
* @since 2018-11-08
*/
@Data
@ApiModel("查询参数对象")
public abstract class BasePageParam implements Serializable {
private static final long serialVersionUID = -3263921252635611410L;
@ApiModelProperty(value = "页码,默认为1", example = "1")
private Long pageIndex = CommonConstant.DEFAULT_PAGE_INDEX;
@ApiModelProperty(value = "页大小,默认为10", example = "10")
private Long pageSize = CommonConstant.DEFAULT_PAGE_SIZE;
@ApiModelProperty(value = "搜索字符串", example = "")
private String keyword;
public void setPageIndex(Long pageIndex) {
if (pageIndex == null || pageIndex <= 0) {
this.pageIndex = CommonConstant.DEFAULT_PAGE_INDEX;
} else {
this.pageIndex = pageIndex;
}
}
public void setPageSize(Long pageSize) {
if (pageSize == null || pageSize <= 0) {
this.pageSize = CommonConstant.DEFAULT_PAGE_SIZE;
} else if (pageSize > CommonConstant.MAX_PAGE_SIZE) {
// 每页不能超过100
this.pageSize = CommonConstant.MAX_PAGE_SIZE;
} else {
this.pageSize = pageSize;
}
}
}
package com.wecloud.im.core.pagination;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.google.common.base.CaseFormat;
import com.wecloud.im.core.util.PropertyColumnUtil;
/**
* 排序列映射
*
* @author geekidea
* @date 2020/3/14
**/
@Data
@Accessors(chain = true)
public class OrderMapping {
private boolean underLineMode;
private Map<String, String> map = new ConcurrentHashMap<>();
public OrderMapping() {
}
public OrderMapping(boolean underLineMode) {
this.underLineMode = underLineMode;
}
public OrderMapping mapping(String property, String column) {
map.put(property, column);
return this;
}
public OrderMapping mapping(String property, String tablePrefix, String column) {
if (StringUtils.isNotBlank(tablePrefix)) {
column = tablePrefix + "." + column;
}
map.put(property, column);
return this;
}
public OrderMapping mapping(String property, Class<?> clazz) {
String column = PropertyColumnUtil.getColumn(clazz, property);
map.put(property, column);
return this;
}
public OrderMapping mapping(String property, String tablePrefix, Class<?> clazz) {
String column = PropertyColumnUtil.getColumn(clazz, property);
mapping(property, tablePrefix, column);
return this;
}
public String getMappingColumn(String property) {
if (StringUtils.isBlank(property)) {
return null;
}
return map.get(property);
}
public void filterOrderItems(List<OrderItem> orderItems) {
if (CollectionUtils.isEmpty(orderItems)) {
return;
}
// 如果集合不为空,则按照PropertyColumnUtil映射
if (MapUtils.isNotEmpty(map)) {
orderItems.forEach(item -> {
item.setColumn(this.getMappingColumn(item.getColumn()));
});
} else if (underLineMode) {
// 如果开启下划线模式,自动转换成下划线
orderItems.forEach(item -> {
String column = item.getColumn();
if (StringUtils.isNotBlank(column)) {
// 驼峰转换成下划线
item.setColumn(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, column));
}
});
}
}
}
package com.wecloud.im.core.pagination;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.collections4.CollectionUtils;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
/**
* 自定义分页参数
*
* @author geekidea
* @date 2020/3/27
**/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class PageInfo<T> extends Page<T> {
private static final long serialVersionUID = -2211095086394170578L;
/**
* 分页参数
*/
private BasePageParam pageParam;
/**
* 默认排序字段信息
*/
private OrderItem defaultOrderItem;
/**
* 排序字段映射
*/
private OrderMapping orderMapping;
public PageInfo() {
}
/**
* 传入分页参数
*
* @param pageParam
*/
public PageInfo(BasePageParam pageParam) {
this(pageParam, null, null);
}
/**
* 传入分页参数,默认排序
*
* @param basePageParam 分页相关参数
* @param defaultOrderItem 默认排序字段
*/
public PageInfo(BasePageParam basePageParam, OrderItem defaultOrderItem) {
this(basePageParam, defaultOrderItem, null);
}
/**
* 传入分页参数,排序字段映射
*
* @param pageParam
* @param orderMapping
*/
public PageInfo(BasePageParam pageParam, OrderMapping orderMapping) {
this(pageParam, null, orderMapping);
}
/**
* 传入分页参数,默认排序,排序字段映射
*
* @param pageParam
* @param defaultOrderItem
* @param orderMapping
*/
public PageInfo(BasePageParam pageParam, OrderItem defaultOrderItem, OrderMapping orderMapping) {
this.pageParam = pageParam;
this.defaultOrderItem = defaultOrderItem;
this.orderMapping = orderMapping;
this.handle();
}
/**
* 分页构造函数
*
* @param current 当前页
* @param size 每页显示条数
*/
public PageInfo(long current, long size) {
super(current, size, 0);
}
public PageInfo(long current, long size, long total) {
super(current, size, total, true);
}
public PageInfo(long current, long size, boolean isSearchCount) {
super(current, size, isSearchCount);
}
public PageInfo(long current, long size, long total, boolean isSearchCount) {
super(current, size, total, isSearchCount);
}
/**
* 如果是pageParam是OrderPageParam,并且不为空,则使用前端排序
* 否则使用默认排序
*/
private void handle() {
if (pageParam == null) {
return;
}
// 设置pageIndex/pageSize
super.setCurrent(pageParam.getPageIndex());
super.setSize(pageParam.getPageSize());
// 排序字段处理
BasePageOrderParam basePageOrderParam = (BasePageOrderParam) pageParam;
List<OrderItem> orderItems = basePageOrderParam.getPageSorts();
if (CollectionUtils.isEmpty(orderItems)) {
setDefaultOrder(defaultOrderItem);
return;
}
if (orderMapping == null) {
orderMapping = new OrderMapping(true);
}
orderMapping.filterOrderItems(orderItems);
super.setOrders(orderItems);
}
/**
* 设置默认排序
*
* @param defaultOrderItem
* @return
*/
public PageInfo<T> setDefaultOrder(OrderItem defaultOrderItem) {
if (defaultOrderItem != null) {
this.defaultOrderItem = defaultOrderItem;
super.setOrders(Arrays.asList(defaultOrderItem));
}
return this;
}
}
package com.wecloud.im.core.pagination;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.wecloud.im.core.constant.CommonConstant;
/**
* 分页结果对象
*
* @author geekidea
* @date 2018-11-08
*/
@Slf4j
@Data
@ApiModel("分页结果对象")
public class Paging<T> implements Serializable {
private static final long serialVersionUID = 4784961132604516495L;
@ApiModelProperty("总行数")
@JSONField(name = CommonConstant.PAGE_TOTAL_NAME)
@JsonProperty(CommonConstant.PAGE_TOTAL_NAME)
private long total = 0;
@ApiModelProperty("数据列表")
@JSONField(name = CommonConstant.PAGE_RECORDS_NAME)
@JsonProperty(CommonConstant.PAGE_RECORDS_NAME)
private List<T> records = Collections.emptyList();
@ApiModelProperty(value = "页码")
@JSONField(name = CommonConstant.PAGE_INDEX_NAME)
@JsonProperty(CommonConstant.PAGE_INDEX_NAME)
private Long pageIndex;
@ApiModelProperty(value = "页大小")
@JSONField(name = CommonConstant.PAGE_SIZE_NAME)
@JsonProperty(CommonConstant.PAGE_SIZE_NAME)
private Long pageSize;
public Paging() {
}
public Paging(IPage<T> page) {
this.total = page.getTotal();
this.records = page.getRecords();
this.pageIndex = page.getCurrent();
this.pageSize = page.getSize();
}
}
package com.wecloud.im.core.util;
import com.wecloud.im.core.common.exception.BusinessException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
* @Author wenzhida
* @Date 2022/2/21 22:26
* @Description 加解密工具类
*/
public class AesUtil {
private static final String S_KEY = "weeKeejjLL123.VB";
/**
* 加密
* @param sSrc
* @return
*/
public static String encrypt(String sSrc) {
try {
byte[] raw = S_KEY.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// "算法/模式/补码方式"
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
// 此处使用BASE64做转码功能,同时能起到2次加密的作用。
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(encrypted);
} catch (Exception e) {
throw new BusinessException("系统异常,稍后重试");
}
}
/**
* 解密
* @param sSrc
* @return
* @throws Exception
*/
public static String decrypt(String sSrc) {
try {
byte[] raw = S_KEY.getBytes("utf-8");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
Base64.Decoder decoder = Base64.getDecoder();
byte[] encrypted1 = decoder.decode(sSrc);//先用base64解密
try {
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "utf-8");
return originalString;
} catch (Exception e) {
System.out.println(e.toString());
return null;
}
} catch (Exception ex) {
System.out.println(ex.toString());
return null;
}
}
public static void main(String[] args) throws Exception {
// 需要加密的字串
String cSrc = "123";
System.out.println(cSrc);
// 加密
String enString = AesUtil.encrypt(cSrc);
System.out.println("加密后的字串是:" + enString);
// 解密
String DeString = AesUtil.decrypt(enString);
System.out.println("解密后的字串是:" + DeString);
}
}
package com.wecloud.im.core.util;
import lombok.extern.slf4j.Slf4j;
import org.fusesource.jansi.Ansi;
import org.springframework.core.env.Environment;
/**
* @author geekidea
* @date 2018-11-08
*/
@Slf4j
public class AnsiUtil {
private static final boolean ENABLE_ANSI;
static {
Boolean value = false;
try {
Environment environment = SpringContextUtil.getBean(Environment.class);
value = environment.getProperty("spring-boot-plus.enable-ansi", boolean.class);
value = value == null ? false : value;
} catch (Exception e) {
e.printStackTrace();
}
ENABLE_ANSI = value;
}
public static String getAnsi(Ansi.Color color, String text) {
if (ENABLE_ANSI) {
return Ansi.ansi().eraseScreen().fg(color).a(text).reset().toString();
}
return text;
}
public static String getAnsi(Ansi.Color color, String text, boolean flag) {
if (flag) {
return Ansi.ansi().eraseScreen().fg(color).a(text).reset().toString();
}
return text;
}
}
package com.wecloud.im.core.util;
import java.util.Base64;
/**
* @Author wenzhida
* @Date 2022/2/21 22:37
* @Description BASE64加密/解密
*/
public class Base64Util {
/***
* BASE64解密
* @param key
* @return
* @throws Exception
*/
public static byte[] decryBASE64(String key) throws Exception {
Base64.Decoder decoder = Base64.getDecoder();
return decoder.decode(key);
}
/***
* BASE64加密
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(byte[] key) throws Exception {
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(key);
}
}
package com.wecloud.im.core.util;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections4.MapUtils;
import com.wecloud.im.core.common.enums.BaseEnum;
import com.wecloud.im.core.common.vo.EnumVo;
/**
* BaseEnum枚举工具类
*
* @author geekidea
* @date 2018-11-08
*/
public class BaseEnumUtil {
private static final Map<String, Map<Integer, EnumVo<? extends BaseEnum>>> ENUM_MAP = new LinkedHashMap<>();
/**
* 通过code获取描述
*
* @param baseEnumType
* @param code
* @return
*/
public static BaseEnum getEnum(Class<? extends BaseEnum> baseEnumType, Integer code) {
EnumVo<? extends BaseEnum> enumVo = getEnumVo(baseEnumType, code);
if (enumVo == null) {
return null;
}
return enumVo.getBaseEnum();
}
/**
* 通过code获取描述
*
* @param baseEnumType
* @param code
* @return
*/
public static EnumVo<? extends BaseEnum> getEnumVo(Class<? extends BaseEnum> baseEnumType, Integer code) {
Map<Integer, EnumVo<? extends BaseEnum>> map = getMap(baseEnumType);
if (MapUtils.isEmpty(map)) {
return null;
}
return map.get(code);
}
/**
* 判断code在枚举中是否存在
*
* @param baseEnumType
* @param code
* @return
*/
public static boolean exists(Class<? extends BaseEnum> baseEnumType, Integer code) {
EnumVo<? extends BaseEnum> enumVo = getEnumVo(baseEnumType, code);
if (enumVo == null) {
return false;
}
return true;
}
/**
* 判断code在枚举中是否不存在
*
* @param baseEnumType
* @param code
* @return
*/
public static boolean notExists(Class<? extends BaseEnum> baseEnumType, Integer code) {
return !exists(baseEnumType, code);
}
/**
* 通过code获取描述
*
* @param baseEnumType
* @param code
* @return
*/
public static String getDesc(Class<? extends BaseEnum> baseEnumType, Integer code) {
EnumVo<? extends BaseEnum> enumVo = getEnumVo(baseEnumType, code);
if (enumVo == null) {
return null;
}
return enumVo.getDesc();
}
/**
* 通过类型获取枚举Map
*
* @param baseEnumType
* @return
*/
public static Map<Integer, EnumVo<? extends BaseEnum>> getMap(Class<? extends BaseEnum> baseEnumType) {
return ENUM_MAP.get(baseEnumType.getName());
}
/**
* 通过类型获取枚举code集合
*
* @param baseEnumType
* @return
*/
public static Set<Integer> getCodeSet(Class<? extends BaseEnum> baseEnumType) {
Map<Integer, EnumVo<? extends BaseEnum>> map = getMap(baseEnumType);
if (MapUtils.isEmpty(map)) {
return null;
}
return map.keySet();
}
/**
* 通过类型获取枚举desc集合
*
* @param baseEnumType
* @return
*/
public static Collection<EnumVo<? extends BaseEnum>> getDescList(Class<? extends BaseEnum> baseEnumType) {
Map<Integer, EnumVo<? extends BaseEnum>> map = getMap(baseEnumType);
if (MapUtils.isEmpty(map)) {
return null;
}
return map.values();
}
public static Map<String, Map<Integer, EnumVo<? extends BaseEnum>>> getEnumMap() {
return ENUM_MAP;
}
}
package com.wecloud.im.core.util;
import javax.servlet.http.HttpServletRequest;
/**
* <code>
* 浏览器工具类<br/>
* 1.获取当前浏览器名称
* 2.判断当前用户的浏览器
* </code>
*
* @author geekidea
* @since 2018-11-08
*/
public final class BrowserUtil {
public static final String IE = "msie";
public static final String FIREFOX = "firefox";
public static final String CHROME = "chrome";
private BrowserUtil() {
throw new AssertionError();
}
/**
* 获取当前浏览器名称
*
* @param request
* @return 返回浏览器名称
*/
public static String getCurrent(HttpServletRequest request) {
String userAgent = request.getHeader("USER-AGENT").toLowerCase();
if (userAgent != null && !("".equals(userAgent.trim()))) {
if (userAgent.indexOf(CHROME) >= 0) {
return CHROME;
} else if (userAgent.indexOf(FIREFOX) >= 0) {
return FIREFOX;
} else if (userAgent.indexOf(IE) >= 0) {
return IE;
}
}
return null;
}
/**
* 是否是IE浏览器
*
* @param request
* @return
*/
public static boolean isIe(HttpServletRequest request) {
return IE.equals(getCurrent(request));
}
/**
* 是否是Firefox浏览器
*
* @param request
* @return
*/
public static boolean isFirefox(HttpServletRequest request) {
return FIREFOX.equals(getCurrent(request));
}
/**
* 是否是Chrome浏览器
*
* @param request
* @return
*/
public static boolean isChrome(HttpServletRequest request) {
return CHROME.equals(getCurrent(request));
}
}
package com.wecloud.im.core.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.wecloud.im.core.common.bean.ClientInfo;
import com.wecloud.im.core.common.bean.DeviceInfo;
import com.wecloud.im.core.constant.CommonConstant;
/**
* <p>
* 用户客户端信息工具类
* </p>
*
* @author geekidea
* @date 2019-05-24
**/
public class ClientInfoUtil {
private static final Pattern DEVICE_INFO_PATTERN = Pattern.compile(";\\s?(\\S*?\\s?\\S*?)\\s?Build/(\\S*?)[;)]");
private static final Pattern DEVICE_INFO_PATTERN_1 = Pattern.compile(";\\s?(\\S*?\\s?\\S*?)\\s?\\)");
/**
* 获取用户客户端信息
*
* @param request
* @return
*/
public static ClientInfo get(HttpServletRequest request) {
String userAgent = request.getHeader(CommonConstant.USER_AGENT);
return get(userAgent);
}
/**
* 获取用户客户端信息
*
* @param userAgentString
* @return
*/
public static ClientInfo get(String userAgentString) {
ClientInfo clientInfo = new ClientInfo();
UserAgent userAgent = UserAgentUtil.parse(userAgentString);
// 浏览器名称
clientInfo.setBrowserName(userAgent.getBrowser().getName());
// 浏览器版本
clientInfo.setBrowserversion(userAgent.getVersion());
// 浏览器引擎名称
clientInfo.setEngineName(userAgent.getEngine().getName());
// 浏览器引擎版本
clientInfo.setEngineVersion(userAgent.getEngineVersion());
// 用户操作系统名称
clientInfo.setOsName(userAgent.getOs().getName());
// 用户操作平台名称
clientInfo.setPlatformName(userAgent.getPlatform().getName());
// 是否是手机
clientInfo.setMobile(userAgent.isMobile());
// 获取移动设备名称和机型
DeviceInfo deviceInfo = getDeviceInfo(userAgentString);
// 设置移动设备名称和机型
clientInfo.setDeviceName(deviceInfo.getName());
clientInfo.setDeviceModel(deviceInfo.getModel());
// ip
clientInfo.setIp(IpUtil.getRequestIp());
return clientInfo;
}
/**
* 获取移动端用户设备的名称和机型
*
* @param userAgentString
* @return
*/
public static DeviceInfo getDeviceInfo(String userAgentString) {
DeviceInfo deviceInfo = new DeviceInfo();
try {
Matcher matcher = DEVICE_INFO_PATTERN.matcher(userAgentString);
String model = null;
String name = null;
if (matcher.find()) {
model = matcher.group(1);
name = matcher.group(2);
}
if (model == null && name == null) {
matcher = DEVICE_INFO_PATTERN_1.matcher(userAgentString);
if (matcher.find()) {
model = matcher.group(1);
}
}
deviceInfo.setName(name);
deviceInfo.setModel(model);
} catch (Exception e) {
e.printStackTrace();
}
return deviceInfo;
}
}
package com.wecloud.im.core.util;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
/**
* 获取文件的内容类型
* mime-type参考:https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?revision=1752884&view=co
*
* @author geekidea
* @date 2019/08/20
* @since
*/
@Slf4j
public final class ContentTypeUtil {
private static final String MIME_TYPE_CONFIG_FILE = "config/mime-type.properties";
private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
private static Properties properties;
static {
try {
properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(MIME_TYPE_CONFIG_FILE));
} catch (IOException e) {
log.error("读取配置文件" + MIME_TYPE_CONFIG_FILE + "异常", e);
}
log.info(MIME_TYPE_CONFIG_FILE + " = " + properties);
}
/**
* 获取文件内容类型
*
* @param file
* @return
*/
public static String getContentType(File file) {
if (file == null) {
return null;
}
Path path = Paths.get(file.toURI());
if (path == null) {
return null;
}
String contentType = null;
try {
contentType = Files.probeContentType(path);
} catch (IOException e) {
log.error("获取文件ContentType异常", e);
}
if (contentType == null) {
// 读取拓展的自定义配置
contentType = getContentTypeByExtension(file);
}
// 设置默认的内容类型
if (contentType == null) {
contentType = DEFAULT_MIME_TYPE;
}
return contentType;
}
/**
* 根据文件后缀获取自定义配置的文件mime-type
*
* @param file
* @return
*/
private static String getContentTypeByExtension(File file) {
if (properties == null) {
return null;
}
String extension = FilenameUtils.getExtension(file.getName());
if (StringUtils.isBlank(extension)) {
return null;
}
String contentType = properties.getProperty(extension);
return contentType;
}
}
package com.wecloud.im.core.util;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.wecloud.im.core.constant.DatePattern;
/**
* @author geekidea
* @date 2018-11-08
*/
public class DateUtil {
public static String getDateString(Date date) {
if (date == null) {
return null;
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatePattern.YYYY_MM_DD);
String dateString = simpleDateFormat.format(date);
return dateString;
}
public static String getDateTimeString(Date date) {
if (date == null) {
return null;
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatePattern.YYYY_MM_DD_HH_MM_SS);
String dateString = simpleDateFormat.format(date);
return dateString;
}
}
package com.wecloud.im.core.util;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Base64Utils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileCopyUtils;
import com.wecloud.im.core.constant.CommonConstant;
/**
* 文件下载工具类
*
* @author geekidea
* @date 2019/8/21
* @since 1.2.1-RELEASE
*/
@Slf4j
public final class DownloadUtil {
/**
* 下载文件,使用默认下载处理器
*
* @param downloadDir
* @param downloadFileName
* @param allowFileExtensions
* @param response
* @throws Exception
*/
public static void download(String downloadDir, String downloadFileName, List<String> allowFileExtensions, HttpServletResponse response) throws Exception {
download(downloadDir, downloadFileName, allowFileExtensions, response, new DefaultDownloadHandler());
}
/**
* 下载文件,使用自定义下载处理器
*
* @param downloadDir 文件目录
* @param downloadFileName 文件名称
* @throws Exception
*/
public static void download(String downloadDir, String downloadFileName, List<String> allowFileExtensions, HttpServletResponse response, DownloadHandler downloadHandler) throws Exception {
log.info("downloadDir:{}", downloadDir);
log.info("downloadFileName:{}", downloadFileName);
if (StringUtils.isBlank(downloadDir)) {
throw new IOException("文件目录不能为空");
}
if (StringUtils.isBlank(downloadFileName)) {
throw new IOException("文件名称不能为空");
}
// 安全判断,防止../情况,防止出现类似非法文件名称:../../hello/123.txt
if (downloadFileName.contains(CommonConstant.SPOT_SPOT) || downloadFileName.contains(CommonConstant.SPOT_SPOT_BACKSLASH)) {
throw new IOException("非法的文件名称");
}
// 允许下载的文件后缀判断
if (CollectionUtils.isEmpty(allowFileExtensions)) {
throw new IllegalArgumentException("请设置允许下载的文件后缀");
}
// 获取文件名称
String fileExtension = FilenameUtils.getExtension(downloadFileName);
// 从服务器读取文件,然后输出
File downloadFile = new File(downloadDir, downloadFileName);
if (!downloadFile.exists()) {
throw new IOException("文件不存在");
}
// 判断文件类型,输出对应ContentType,如果没有对应的内容类型,可在config/mime-type.properties配置
String contentType = ContentTypeUtil.getContentType(downloadFile);
log.info("contentType:{}", contentType);
// 文件大小
long length = downloadFile.length();
log.info("length:{}", length);
// 下载回调处理
if (downloadHandler == null) {
// 使用默认下载处理器
downloadHandler = new DefaultDownloadHandler();
}
boolean flag = downloadHandler.handle(downloadDir, downloadFileName, downloadFile, fileExtension, contentType, length);
if (!flag) {
log.info("下载自定义校验失败,取消下载");
return;
}
// 下载文件名称编码,Firefox中文乱码处理
String encodeDownFileName;
HttpServletRequest request = HttpServletRequestUtil.getRequest();
String browser = BrowserUtil.getCurrent(request);
if (BrowserUtil.FIREFOX.equals(browser)) {
encodeDownFileName = "=?UTF-8?B?" + (Base64Utils.encodeToString(downloadFileName.getBytes(StandardCharsets.UTF_8))) + "?=";
} else {
encodeDownFileName = URLEncoder.encode(downloadFileName, "utf-8").replaceAll("\\+", "%20");
}
log.info("encodeDownFileName:{}", encodeDownFileName);
log.info("下载文件:" + downloadFile.getAbsolutePath());
response.reset();
// 设置Content-Disposition响应头
response.setHeader("Content-Disposition", "attachment;fileName=\"" + encodeDownFileName + "\"");
// 设置响应Content-Type
response.setContentType(contentType);
// 设置响应文件大小
response.setContentLengthLong(length);
// 文件下载
InputStream in = new BufferedInputStream(new FileInputStream(downloadFile));
FileCopyUtils.copy(in, response.getOutputStream());
}
public interface DownloadHandler {
/**
* 下载自定义处理
*
* @param dir
* @param fileName
* @param file
* @param fileExtension
* @param contentType
* @param length
* @return
* @throws Exception
*/
boolean handle(String dir, String fileName, File file, String fileExtension, String contentType, long length) throws Exception;
}
public static class DefaultDownloadHandler implements DownloadHandler {
@Override
public boolean handle(String dir, String fileName, File file, String fileExtension, String contentType, long length) throws Exception {
return false;
}
}
}
package com.wecloud.im.core.util;
import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
* 获取公网ip
*/
@Component
@Slf4j
public class GetIpUtils {
private static final String LOCAL = "local";
private static final String AWS = "aws";
private static final String HUAWEI_CLOUD = "huawei";
/**
* 内网ip
*/
private static String lAN_IP = null;
/**
* 公网ip
*/
private static String PUBLIC_IP = null;
/**
* 服务器运营商local,aws,huawei
*/
@Value("${load-blance.server-type}")
private String serverType;
/**
* 排除无效的mac地址
*/
private final static byte[][] INVALID_MACS = {
{0x00, 0x05, 0x69}, // VMWare
{0x00, 0x1C, 0x14}, // VMWare
{0x00, 0x0C, 0x29}, // VMWare
{0x00, 0x50, 0x56}, // VMWare
{0x08, 0x00, 0x27}, // Virtualbox
{0x0A, 0x00, 0x27}, // Virtualbox
{0x00, 0x03, (byte) 0xFF}, // Virtual-PC
{0x00, 0x15, 0x5D} // Hyper-V
};
/**
* 判断是否为虚拟mac地址
*
* @param mac
* @return
*/
private static boolean isVmMac(byte[] mac) {
if (null == mac) {
return false;
}
for (byte[] invalid : INVALID_MACS) {
if (invalid[0] == mac[0] && invalid[1] == mac[1] && invalid[2] == mac[2]) {
return true;
}
}
return false;
}
/**
* 获取本机地址
*/
private static String getLocalIpAddress() {
try {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface ni = networkInterfaces.nextElement();
/*
排除docker虚拟网卡
*/
String docker0 = "docker0";
if (ni.getName().equals(docker0)) {
continue;
}
if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) {
continue;
}
if (isVmMac(ni.getHardwareAddress())) {
continue;
}
Enumeration<InetAddress> inetAddresses = ni.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if (inetAddress.isLinkLocalAddress()) {
continue;
}
return inetAddress.getHostAddress();
}
}
} catch (SocketException e) {
log.info("获取本机IP地址失败。" + e);
}
return StringUtils.EMPTY;
}
/**
* 内网ip
*
* @return
*/
public static String getlanIp() {
if (lAN_IP == null) {
synchronized (GetIpUtils.class) {
if(lAN_IP == null) {
lAN_IP = getLocalIpAddress();
}
}
}
return lAN_IP;
}
/**
* 公网ip
*/
public String getPublicIp() {
if (PUBLIC_IP == null) {
switch (serverType) {
case LOCAL:
PUBLIC_IP = getlanIp();
break;
case AWS:
PUBLIC_IP = HttpUtil.get("http://instance-data/latest/meta-data/public-ipv4", 30);
break;
case HUAWEI_CLOUD:
PUBLIC_IP = HttpUtil.get("http://169.254.169.254/latest/meta-data/public-ipv4", 30);
break;
default:
}
}
return PUBLIC_IP;
}
}
package com.wecloud.im.core.util;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 获取当前请求的HttpServletRequest对象
* http上下文对象
*
* @author geekidea
* @date 2018-11-08
*/
public class HttpServletRequestUtil {
public static HttpServletRequest getRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}
package com.wecloud.im.core.util;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
/**
* @author geekidea
* @date 2018-11-08
*/
public final class HttpServletResponseUtil {
private static final String UTF8 = "UTF-8";
private static final String CONTENT_TYPE = "application/json";
private HttpServletResponseUtil() {
throw new AssertionError();
}
public static void printJson(HttpServletResponse response, Object object) throws Exception {
response.setCharacterEncoding(UTF8);
response.setContentType(CONTENT_TYPE);
PrintWriter printWriter = response.getWriter();
printWriter.write(JSON.toJSONString(object));
printWriter.flush();
printWriter.close();
}
}
package com.wecloud.im.core.util;
import java.io.IOException;
import java.io.StringReader;
import java.util.Map;
import org.ini4j.Config;
import org.ini4j.Ini;
import org.ini4j.Profile;
/**
* @author geekidea
* @date 2019-09-29
* @since 1.3.0.RELEASE
**/
public class IniUtil {
public static Map<String, String> parseIni(String string) {
Config config = new Config();
config.setGlobalSection(true);
config.setGlobalSectionName("");
Ini ini = new Ini();
ini.setConfig(config);
try {
ini.load(new StringReader(string));
Profile.Section section = ini.get("");
return section;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Map<String, String> parseIni(String sectionName, String string) {
Ini ini = new Ini();
try {
ini.load(new StringReader(string));
Profile.Section section = ini.get(sectionName);
return section;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
package com.wecloud.im.core.util;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 获取IP地址工具类
*
* @author geekidea
* @date 2018-11-08
*/
public final class IpUtil {
private static final String UNKNOWN = "unknown";
private static final String IPV6_LOCAL = "0:0:0:0:0:0:0:1";
private IpUtil() {
throw new AssertionError();
}
/**
* 获取请求用户的IP地址
*
* @return
*/
public static String getRequestIp() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
return getRequestIp(request);
}
/**
* 获取请求用户的IP地址
*
* @param request
* @return
*/
public static String getRequestIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (IPV6_LOCAL.equals(ip)) {
ip = getLocalhostIp();
}
return ip;
}
public static String getLocalhostIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
}
return null;
}
}
package com.wecloud.im.core.util;
import java.util.TimeZone;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* Jackson序列化工具类
*
* @author geekidea
* @date 2019-11-01
**/
public class Jackson {
/**
* 时区
*/
private static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT");
/**
* 键按自然顺序输出
*
* @param object
* @return
*/
public static String toJsonString(Object object) {
return toJsonString(object, false);
}
/**
* 键按自然顺序格式化输出
*
* @param object
* @param prettyFormat
* @return
*/
public static String toJsonString(Object object, boolean prettyFormat) {
if (object == null) {
return null;
}
ObjectMapper objectMapper = new ObjectMapper();
try {
// 格式化输出
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, prettyFormat);
// 键按自然顺序输出
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
// 设置时区
objectMapper.setTimeZone(TIME_ZONE);
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 键按自然顺序格式化输出
*
* @param object
* @return
*/
public static String toJsonStringNonNull(Object object) {
return toJsonStringNonNull(object, false);
}
/**
* 键按自然顺序格式化输出
*
* @param object
* @param prettyFormat
* @return
*/
public static String toJsonStringNonNull(Object object, boolean prettyFormat) {
if (object == null) {
return null;
}
ObjectMapper objectMapper = new ObjectMapper();
try {
// 格式化输出
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, prettyFormat);
// 键按自然顺序输出
objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
// 为空的序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 设置时区
objectMapper.setTimeZone(TIME_ZONE);
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
package com.wecloud.im.core.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cglib.beans.BeanMap;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* fasterxml 的json工具类
*
* @author lixiaozhong
*/
@Slf4j
public class JsonUtils {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
static {
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
* Json格式的字符串向JavaBean转换,传入空串将返回null
*
* @param strJsonBody Json格式的字符串
* @param c 目标JavaBean类型
* @return JavaBean对象, 如果解析失败返回 null
*/
public static <T> T decodeJson(String strJsonBody, Class<T> c) {
if (StringUtils.isEmpty(strJsonBody)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(strJsonBody, c);
} catch (IOException e) {
log.warn("解析json字符串失败,原字符串: {} class {}", strJsonBody, c, e);
return null;
}
}
/**
* Json格式的字符串向JavaBean转换,传入空串将返回null (显式抛出异常)
*
* @param strJsonBody Json格式的字符串
* @param c 目标JavaBean类型
* @return JavaBean对象
* @throws IOException
*/
public static <T> T json2Object(String strJsonBody, Class<T> c) throws IOException {
if (StringUtils.isEmpty(strJsonBody)) {
return null;
}
return OBJECT_MAPPER.readValue(strJsonBody, c);
}
/**
* Json格式的字符串向HashMap转换,传入空串将返回空map (显式抛出异常)
*
* @param strJsonBody Json格式的字符串
* @return HashMap对象
* @throws IOException
*/
public static HashMap<String, Object> json2Map(String strJsonBody) throws IOException {
if (StringUtils.isEmpty(strJsonBody)) {
return new HashMap<String, Object>();
}
return OBJECT_MAPPER.readValue(strJsonBody, HashMap.class);
}
/**
* Json格式的字符串向HashMap转换,传入空串将返回空map
*
* @param strJsonBody Json格式的字符串
* @return HashMap对象
* @throws IOException
*/
public static HashMap<String, Object> decodeJson2Map(String strJsonBody) {
if (StringUtils.isEmpty(strJsonBody)) {
return new HashMap<String, Object>();
}
try {
return OBJECT_MAPPER.readValue(strJsonBody, HashMap.class);
} catch (IOException e) {
log.warn("解析json字符串失败,原字符串: {}", strJsonBody);
return null;
}
}
/**
*将json转换成Object对象
* @param strJsonBody
* @return
* @throws IOException
*/
public static Object json2Object(String strJsonBody) throws IOException {
if (StringUtils.isEmpty(strJsonBody)) {
return null;
}
// 每个属性的实际类型是string
return OBJECT_MAPPER.readValue(strJsonBody, Object.class);
}
/**
* Json格式的字符串向JavaBean List集合转换,传入空串将返回空list (显式抛出异常)
*
* @param strJsonBody
* @param c
* @return
* @throws IOException
*/
@SuppressWarnings("unchecked")
public static <T> List<T> json2List(String strJsonBody, Class<T> c) throws IOException {
if (StringUtils.isEmpty(strJsonBody)) {
return Collections.emptyList();
}
JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(ArrayList.class, c);
return OBJECT_MAPPER.readValue(strJsonBody, javaType);
}
/**
* Json格式的字符串向JavaBean List集合转换,传入空串将返回空list
*
* @param strJsonBody
* @param c
* @return 对象列表,解析失败返回 null
*/
@SuppressWarnings("unchecked")
public static <T> List<T> decodeJsonToList(String strJsonBody, Class<T> c) {
if (StringUtils.isEmpty(strJsonBody)) {
return Collections.emptyList();
}
JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(ArrayList.class, c);
try {
return OBJECT_MAPPER.readValue(strJsonBody, javaType);
} catch (IOException e) {
log.warn("解析json字符串失败,原字符串: {}", strJsonBody);
return null;
}
}
/**
* Json格式的字符串向List<String>集合转换,传入空串将返回null
*
* @param strJsonBody
* @return
* @throws IOException
*/
public static List<String> json2List(String strJsonBody) throws IOException {
return json2List(strJsonBody, String.class);
}
/**
* Object转为Json格式字符串的方法(显式抛出异常)
*
* @param o
* @return
* @throws JsonProcessingException
*/
public static String object2Json(Object o) throws JsonProcessingException {
return OBJECT_MAPPER.writeValueAsString(o);
}
/**
* Object转为Json格式字符串的方法
*
* @param o
* @return 对象的json字符串,如果处理过程中出错,返回null
*/
public static String encodeJson(Object o) {
try {
return OBJECT_MAPPER.writeValueAsString(o);
} catch (JsonProcessingException e) {
log.warn("对象转换成json失败");
return null;
}
}
/**
* 判断字符也许是JSON
*
* @param string
* @return
*/
public static boolean maybeJson(String string) {
return maybeJsonArray(string) || maybeJsonObject(string);
}
/**
* 判断字符也许是JSONArray
*
* @param string
* @return
*/
public static boolean maybeJsonArray(String string) {
string = (null == string) ? string : string.trim();
return string != null && ("null".equals(string) || (string.startsWith("[") && string.endsWith("]")));
}
/**
* 判断字符也许是JSONObject
*
* @param string
* @return
*/
public static boolean maybeJsonObject(String string) {
string = (null == string) ? string : string.trim();
return string != null && ("null".equals(string) || (string.startsWith("{") && string.endsWith("}")));
}
/**
* 将map装换为javabean对象,不支持深度转换,支持深度请看 mapToBeanDeep 方法
*
* @param map
* @param clazz
* @return
*/
public static <T> T mapToBean(Map<String, Object> map, Class<T> clazz) {
T obj = null;
try {
obj = clazz.newInstance();
} catch (Exception e) {
log.error("mapToBean转换,创建实例对象失败", e);
return null;
}
BeanMap beanMap = BeanMap.create(obj);
beanMap.putAll(map);
return obj;
}
/**
* 将一个 Map/javaBean 对象转化为一个 javaBean 深度
* @param srcObject 要转化的对象,可以是map,bean
* @param destClass 要转化的类型
* @return 转化出来的 JavaBean 对象
*/
public static <T>T beanCopyDeep(Object srcObject, Class<T> destClass) {
String s = encodeJson(srcObject);
return decodeJson(s, destClass);
}
private static boolean isFinalType(Class clazz) {
if (clazz == String.class ) {
return true;
}
if (clazz == Boolean.class) {
return true;
}
if (clazz == Long.class) {
return true;
}
if (clazz == Integer.class) {
return true;
}
if (clazz == Double.class) {
return true;
}
if (clazz == Float.class) {
return true;
}
if (clazz == Short.class) {
return true;
}
if (clazz == BigDecimal.class) {
return true;
}
if (clazz == Byte.class) {
return true;
}
if (clazz == int.class) {
return true;
}
if (clazz == long.class) {
return true;
}
if (clazz == float.class) {
return true;
}
if (clazz == double.class) {
return true;
}
if (clazz == byte.class) {
return true;
}
if (clazz == char.class) {
return true;
}
if (clazz == short.class) {
return true;
}
if (clazz == boolean.class) {
return true;
}
return false;
}
/**
* 将javabean对象转换为map
*/
public static <T> Map<String, Object> beanToMap(T bean) {
Map<String, Object> map = new HashMap();
if (bean != null) {
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
Object value = beanMap.get(key);
map.put(key + "", value);
}
}
return map;
}
}
package com.wecloud.im.core.util;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
/**
* 根据lambda表达式获取数据库列名
*
* @author geekidea
* @date 2019/12/22
**/
public class LambdaColumn<T> {
private static Map<String, Map<String, String>> ENTITY_COLUMN_MAP = new ConcurrentHashMap<>();
public static <T> LambdaColumn<T> lambda() {
return new LambdaColumn<>();
}
public String get(SFunction<T, ?> func) {
SerializedLambda lambda = LambdaUtils.resolve(func);
String methodName = lambda.getImplMethodName();
String methodPropertyName = null;
if (methodName.startsWith("get")) {
methodPropertyName = methodName.substring(3);
methodPropertyName = methodPropertyName.substring(0, 1).toLowerCase() + methodPropertyName.substring(1);
}
Class<?> cls = lambda.getImplClass();
String className = lambda.getImplClassName();
if (ENTITY_COLUMN_MAP.containsKey(className)) {
return ENTITY_COLUMN_MAP.get(className).get(methodPropertyName);
}
Map<String, String> map = getPropertyColumnMap(cls);
if (MapUtils.isNotEmpty(map)) {
ENTITY_COLUMN_MAP.put(className, map);
}
return ENTITY_COLUMN_MAP.get(className).get(methodPropertyName);
}
/**
* 从mybatisplus的TableInfo类中获取列名map信息
*
* @param cls
* @return
*/
private Map<String, String> getPropertyColumnMap(Class<?> cls) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
List<TableFieldInfo> tableFieldInfos = tableInfo.getFieldList();
if (CollectionUtils.isEmpty(tableFieldInfos)) {
return null;
}
Map<String, String> map = new ConcurrentHashMap<>();
String keyProperty = tableInfo.getKeyProperty();
String keyColumn = tableInfo.getKeyColumn();
map.put(keyProperty, keyColumn);
for (TableFieldInfo tableFieldInfo : tableFieldInfos) {
String column = tableFieldInfo.getColumn();
String property = tableFieldInfo.getProperty();
map.put(property, column);
}
return map;
}
}
package com.wecloud.im.core.util;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* Map构建工具类
* </p>
*
* @author geekidea
* @date 2019-05-23
**/
public class MapUtil {
private final Map<String, Object> map;
private MapUtil() {
map = new HashMap<>();
}
public static MapUtil builder() {
return new MapUtil();
}
public MapUtil put(String key, Object value) {
this.map.put(key, value);
return this;
}
public Map<String, Object> build() {
return this.map;
}
}
package com.wecloud.im.core.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
/**
* 密码加密工具类
*
* @author geekidea
* @date 2018-11-08
*/
@Slf4j
public class PasswordUtil {
/**
* 密码加盐,再加密
*
* @param pwd
* @param salt
* @return
*/
public static String encrypt(String pwd, String salt) {
if (StringUtils.isBlank(pwd)) {
throw new IllegalArgumentException("密码不能为空");
}
if (StringUtils.isBlank(salt)) {
throw new IllegalArgumentException("盐值不能为空");
}
return DigestUtils.sha256Hex(pwd + salt);
}
}
package com.wecloud.im.core.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
/**
* 手机号码工具类
*
* @author geekidea
* @date 2020/2/26
**/
@Slf4j
public class PhoneUtil {
/**
* 手机号码长度
*/
private static final int PHONE_LENGTH = 11;
/**
* 脱敏*号
*/
private static final String ASTERISK = "****";
/**
* 手机号码脱敏
* 截取手机号码前三位,后4为,中间4位使用*号代替
* 18812345678
* 188****5678
*
* @param phone
* @return
*/
public static String desensitize(String phone) {
// 校验手机号码
if (StringUtils.isBlank(phone)) {
return null;
}
if (phone.length() != PHONE_LENGTH) {
log.error("手机号码不合法:" + phone);
return phone;
}
String before = phone.substring(0, 3);
String after = phone.substring(7, 11);
String desensitizePhone = before + "****" + after;
return desensitizePhone;
}
public static void main(String[] args) {
String phone = desensitize("1881234567");
System.out.println("phone = " + phone);
}
}
package com.wecloud.im.core.util;
import lombok.extern.slf4j.Slf4j;
import org.fusesource.jansi.Ansi;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* <p>
* 打印项目信息
* </p>
*
* @author geekidea
* @date 2019-05-08
**/
@Slf4j
public class PrintApplicationInfo {
/**
* 执行之前,打印前置条件提示
*/
public static void printTip(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
// 项目profile
String profileActive = environment.getProperty("spring.profiles.active");
StringBuffer tip = new StringBuffer();
tip.append("===========================================================================================\n");
tip.append(" \n");
tip.append(" !!!准备工作!!! \n");
// tip.append(" 1.导入SQL初始化脚本:docs/db,根据不同数据库导入对应SQL脚本并修改链接等信息配置\n");
// tip.append(" 2.启动Redis服务,必要条件\n");
// tip.append(" 3.启动SpringBootAdmin Server,可选操作,admin模块中,启动SpringBootPlusAdminApplication\n");
// tip.append(" 4.根据项目需要,修改项目配置,请先查看官网配置文档:https://springboot.plus/config/\n");
// tip.append(" 5.项目模块说明:\n");
// tip.append(" admin: SpringBootAdmin Server启动模块\n");
tip.append(" bootstrap: 项目启动模块\n");
tip.append(" config: 项目配置模块\n");
// tip.append(" distribution:项目打包模块,打包时,请先选中Maven Profiles中的release和对应环境\n");
// tip.append(" example: 业务自定义模块,自己的业务代码可在example下进行,也可以再创建模块\n");
tip.append(" framework: 项目核心框架模块\n");
tip.append(" generator: 代码生成模块,启动类:SpringBootPlusGenerator,请根据实际情况进行配置\n");
// tip.append(" scheduled: 任务调度模块\n");
// tip.append(" system: 系统管理模块\n");
// tip.append(" 6.FAQ:https://springboot.plus/faq\n");
// tip.append(" 7.如开发中遇到bug及问题,欢迎提交ISSUES:https://github.com/geekidea/spring-boot-plus/issues\n");
// tip.append(" 8.QQ:625301326,进群答案:springboot.plus\n");
tip.append(" \n");
tip.append("===========================================================================================\n");
if ("dev".equals(profileActive)) {
log.info("\n{}", Ansi.ansi().eraseScreen().fg(Ansi.Color.YELLOW).a(tip.toString()).reset().toString());
}
}
/**
* 启动成功之后,打印项目信息
*/
public static void print(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
// 项目名称
String projectFinalName = environment.getProperty("info.project-finalName");
// 项目版本
String projectVersion = environment.getProperty("info.project-version");
// 项目profile
String profileActive = environment.getProperty("spring.profiles.active");
// 项目路径
String contextPath = environment.getProperty("server.servlet.context-path");
// 项目IP或域名地址
String serverIp = environment.getProperty("spring-boot-plus.server-ip");
// 项目端口
String port = environment.getProperty("server.port");
// Spring Boot Admin Server地址,请先在admin模块中启动 SpringBootPlusAdminApplication
String springBootAdminServerUrl = environment.getProperty("spring.boot.admin.client.url");
log.info("projectFinalName : {}", projectFinalName);
log.info("projectVersion : {}", projectVersion);
log.info("profileActive : {}", profileActive);
log.info("contextPath : {}", contextPath);
log.info("serverIp : {}", serverIp);
log.info("port : {}", port);
String startSuccess = " ____ __ __ ____ \n" +
"/\\ _`\\ /\\ \\__ /\\ \\__ /\\ _`\\ \n" +
"\\ \\,\\L\\_\\ \\ ,_\\ __ _ __\\ \\ ,_\\ \\ \\,\\L\\_\\ __ __ ___ ___ __ ____ ____ \n" +
" \\/_\\__ \\\\ \\ \\/ /'__`\\ /\\`'__\\ \\ \\/ \\/_\\__ \\ /\\ \\/\\ \\ /'___\\ /'___\\ /'__`\\ /',__\\ /',__\\ \n" +
" /\\ \\L\\ \\ \\ \\_/\\ \\L\\.\\_\\ \\ \\/ \\ \\ \\_ /\\ \\L\\ \\ \\ \\_\\ \\/\\ \\__//\\ \\__//\\ __//\\__, `\\/\\__, `\\\n" +
" \\ `\\____\\ \\__\\ \\__/.\\_\\\\ \\_\\ \\ \\__\\ \\ `\\____\\ \\____/\\ \\____\\ \\____\\ \\____\\/\\____/\\/\\____/\n" +
" \\/_____/\\/__/\\/__/\\/_/ \\/_/ \\/__/ \\/_____/\\/___/ \\/____/\\/____/\\/____/\\/___/ \\/___/ \n" +
" \n" +
" ";
String homeUrl = "http://" + serverIp + ":" + port + contextPath;
String swaggerUrl = "http://" + serverIp + ":" + port + contextPath + "/swagger-ui.html";
String knife4jUrl = "http://" + serverIp + ":" + port + contextPath + "/doc.html";
// log.info("Admin: {}", springBootAdminServerUrl);
log.info("Home: {}", homeUrl);
log.info("Knife4j: {}", knife4jUrl);
// log.info("Swagger: {}", swaggerUrl);
log.info("spring-boot-plus project start success...........");
// if ("dev".equals(profileActive)) {
// log.info("\n{}", AnsiUtil.getAnsi(Ansi.Color.BLUE, startSuccess));
// } else {
// log.info("\n{}", startSuccess);
// }
}
}
package com.wecloud.im.core.util;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
/**
* @author geekidea
* @date 2020/3/2
**/
public class PropertyColumnUtil {
private static Map<Class<?>, Map<String, String>> cacheMap = new ConcurrentHashMap<>();
public static Map<Class<?>, Map<String, String>> getMap() {
return cacheMap;
}
/**
* 根据实体class,从mybatisplus中获取对应Table的属性列名Map
*
* @param clazz
* @return
*/
private static Map<String, String> getTableFieldMap(Class<?> clazz) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);
if (tableInfo == null) {
return null;
}
List<TableFieldInfo> tableFieldInfos = tableInfo.getFieldList();
if (CollectionUtils.isEmpty(tableFieldInfos)) {
return null;
}
Map<String, String> cacheMap = tableFieldInfos.stream().collect(Collectors.toMap(TableFieldInfo::getProperty, TableFieldInfo::getColumn));
return cacheMap;
}
/**
* 从本地缓存中获取属性列名map
*
* @param clazz
* @return
*/
public static Map<String, String> getPropertyColumnMap(Class<?> clazz) {
Map<String, String> propertyColumnMap = cacheMap.get(clazz);
if (MapUtils.isEmpty(propertyColumnMap)) {
// 从TableInfo中获取,并缓存到内存map中
Map<String, String> fieldMap = getTableFieldMap(clazz);
if (MapUtils.isEmpty(fieldMap)) {
return null;
} else {
cacheMap.put(clazz, fieldMap);
return fieldMap;
}
} else {
return propertyColumnMap;
}
}
/**
* 通过实体class类型和属性名称,从缓存中获取对应的列名
*
* @param clazz
* @param property
* @return
*/
public static String getColumn(Class<?> clazz, String property) {
Map<String, String> propertyColumnMap = getPropertyColumnMap(clazz);
if (MapUtils.isEmpty(propertyColumnMap)) {
throw new IllegalArgumentException("没有找到对应的实体映射对象");
}
String column = propertyColumnMap.get(property);
if (StringUtils.isEmpty(column)) {
throw new IllegalArgumentException("没有找到对应的列");
}
return column;
}
}
package com.wecloud.im.core.util;
/**
* @Author wenzhida
* @Date 2022/2/23 14:57
* @Description 随机工具类
*/
public class RandomUtil {
/**
* 生成短信验证码
* @return
*/
public static String generateVerifyCode() {
Integer verifyCode = (int) ((Math.random() * 9 + 1) * 100000);
return verifyCode.toString();
}
/**
* 生成随机字符串
* @param length 字符串长度
* @return
*/
public static String generateRandomStr(int length) {
String src = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char[] m = src.toCharArray();
StringBuilder strSb = new StringBuilder();
for (int j = 0; j < length; j++) {
char c = m[(int) (Math.random() * 36)];
strSb.append(c);
}
return strSb.toString();
}
}
package com.wecloud.im.core.util;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
* @author geekidea
* @date 2018-11-08
*/
@Component
@Slf4j
public class RedisCacheUtil {
private static RedisCacheUtil redisCacheUtil;
@Autowired
private RedisTemplate redisTemplate;
/**
* 将当前对象赋值给静态对象,调用spring组件: redisCacheUtil.redisTemplate.xxx()
*/
@PostConstruct
public void init() {
redisCacheUtil = this;
}
}
package com.wecloud.im.core.util;
import com.wecloud.im.core.bean.RequestDetail;
/**
* 记录请求详情信息到当前线程中,可在任何地方获取
*
* @author geekidea
* @date 2020/3/26
**/
public class RequestDetailThreadLocal {
private static final ThreadLocal<RequestDetail> THREAD_LOCAL = new ThreadLocal<>();
/**
* 从当前线程中获取请求信息
*/
public static RequestDetail getRequestDetail() {
return THREAD_LOCAL.get();
}
/**
* 设置请求信息到当前线程中
*
* @param requestDetail
*/
public static void setRequestDetail(RequestDetail requestDetail) {
THREAD_LOCAL.set(requestDetail);
}
/**
* 销毁
*/
public static void remove() {
THREAD_LOCAL.remove();
}
}
package com.wecloud.im.core.util;
import cn.hutool.core.lang.Snowflake;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.net.Inet4Address;
import java.net.UnknownHostException;
/**
* 雪花算法 获取id工具类
*
* @author
*/
@Component
public class SnowflakeUtil {
/**
* workerId, dataCenterId动态获取
* 12位序列号部分,支持同一毫秒内同一个节点可以生成4096个ID, 在目前一段不用做成动态获取服务器ID
*/
private static volatile Snowflake SNOWFLAKE = null;
/**
* 多线程中加synchronized 保证不会获取重复id
*
* @return
*/
public static Long getId() {
if (SNOWFLAKE == null) {
synchronized (SnowflakeUtil.class) {
if (SNOWFLAKE == null) {
SNOWFLAKE = new Snowflake(SnowflakeUtil.getWorkId(), 1L);
}
}
}
return SNOWFLAKE.nextId();
}
/**
* workId通过本机ip计算获得
* @return
*/
private static Long getWorkId(){
try {
String hostAddress = Inet4Address.getLocalHost().getHostAddress();
int[] ints = StringUtils.toCodePoints(hostAddress);
int sums = 0;
for(int b : ints){
sums += b;
}
return (long)(sums % 32);
} catch (UnknownHostException e) {
// 如果获取失败,则使用随机数备用
return RandomUtils.nextLong(0,31);
}
}
}
package com.wecloud.im.core.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Spring工具类,获取Spring上下文对象等
*
* @author geekidea
* @date 2018-11-08
*/
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringContextUtil.applicationContext == null) {
SpringContextUtil.applicationContext = applicationContext;
}
}
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
package com.wecloud.im.core.util;
/**
* @author geekidea
* @date 2018-11-08
*/
public class TokenUtil {
/**
* @return
*/
public static String generateFirstLoginRestPwdToken() {
String token = "first-login-rest-pwd-token:" + UUIDUtil.getUuid();
return token;
}
/**
* 生成验证码token
*
* @return
*/
public static String generateVerificationCodeToken() {
String token = "verification-code-token:" + UUIDUtil.getUuid();
return token;
}
}
package com.wecloud.im.core.util;
import java.util.UUID;
/**
* @author geekidea
* @date 2018-11-08
*/
public class UUIDUtil {
public static String getUuid() {
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
return uuid;
}
}
package com.wecloud.im.core.util;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* 文件上传工具类
*
* @author geekidea
* @date 2019/8/21
* @since 1.2.1-RELEASE
*/
@Slf4j
public final class UploadUtil {
/**
* 上传文件,默认文件名格式,yyyyMMddHHmmssS
*
* @param uploadPath
* @param multipartFile
* @return
* @throws Exception
*/
public static String upload(String uploadPath, MultipartFile multipartFile) throws Exception {
return upload(uploadPath, multipartFile, new DefaultUploadFileNameHandleImpl());
}
/**
* 上传文件
*
* @param uploadPath 上传目录
* @param multipartFile 上传文件
* @param uploadFileNameHandle 回调
* @return
* @throws Exception
*/
public static String upload(String uploadPath, MultipartFile multipartFile, UploadFileNameHandle uploadFileNameHandle) throws Exception {
// 获取输入流
InputStream inputStream = multipartFile.getInputStream();
// 文件保存目录
File saveDir = new File(uploadPath);
// 判断目录是否存在,不存在,则创建,如创建失败,则抛出异常
if (!saveDir.exists()) {
boolean flag = saveDir.mkdirs();
if (!flag) {
throw new RuntimeException("创建" + saveDir + "目录失败!");
}
}
String originalFilename = multipartFile.getOriginalFilename();
String saveFileName;
if (uploadFileNameHandle == null) {
saveFileName = new DefaultUploadFileNameHandleImpl().handle(originalFilename);
} else {
saveFileName = uploadFileNameHandle.handle(originalFilename);
}
log.info("saveFileName = " + saveFileName);
File saveFile = new File(saveDir, saveFileName);
// 保存文件到服务器指定路径
FileUtils.copyToFile(inputStream, saveFile);
return saveFileName;
}
/**
* 删除删除的文件
*
* @param uploadPath
* @param saveFileName
*/
public static void deleteQuietly(String uploadPath, String saveFileName) {
File saveDir = new File(uploadPath);
File saveFile = new File(saveDir, saveFileName);
log.info("删除文件:" + saveFile);
FileUtils.deleteQuietly(saveFile);
}
public interface UploadFileNameHandle {
/**
* 回调处理接口
*
* @param originalFilename
* @return
*/
String handle(String originalFilename);
}
public static class DefaultUploadFileNameHandleImpl implements UploadFileNameHandle {
@Override
public String handle(String originalFilename) {
// 文件后缀
String fileExtension = FilenameUtils.getExtension(originalFilename);
// 这里可自定义文件名称,比如按照业务类型/文件格式/日期
// 此处按照文件日期存储
String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssS"));
String fileName = dateString + "." + fileExtension;
return fileName;
}
}
}
package com.wecloud.im.core.util;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
* @author geekidea
* @date 2018-11-08
*/
public class VerificationCode {
/**
* 255
**/
private static final int TWO_FIVE_FIVE = 255;
/**
* 验证码图片的长
**/
private final int weight = 110;
/**
* 验证码图片的高
*/
private final int height = 38;
/**
* 获取随机数对象
**/
private final Random r = new Random();
/**
* 字体数组
**/
private final String[] fontNames = {"宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312"};
/**
* 验证码数组
**/
private final String codes = "23456789acdefghjkmnopqrstuvwxyzACDEFGHJKMNPQRSTUVWXYZ";
/**
* 生成的验证码的个数
**/
private final int codeNum = 4;
/**
* 用来保存验证码的文本内容
**/
private String text;
/**
* 获取随机的颜色
*/
private Color randomColor() {
//这里为什么是150,因为当r,g,b都为255时,即为白色,为了好辨认,需要颜色深一点。
int r = this.r.nextInt(150);
int g = this.r.nextInt(150);
int b = this.r.nextInt(150);
//返回一个随机颜色
return new Color(r, g, b);
}
/**
* 获取随机字体
*/
private Font randomFont() {
//获取随机的字体
int index = r.nextInt(fontNames.length);
String fontName = fontNames[index];
//随机获取字体的样式,0是无样式,1是加粗,2是斜体,3是加粗加斜体
int style = r.nextInt(4);
//随机获取字体的大小
int size = r.nextInt(5) + 24;
//返回一个随机的字体
return new Font(fontName, style, size);
}
/**
* 获取随机字符
*/
private char randomChar() {
int index = r.nextInt(codes.length());
return codes.charAt(index);
}
/**
* 画干扰线,验证码干扰线用来防止计算机解析图片
*/
private void drawLine(BufferedImage image) {
int num = 155;
//定义干扰线的数量
Graphics2D g = (Graphics2D) image.getGraphics();
for (int i = 0; i < num; i++) {
int x = r.nextInt(weight);
int y = r.nextInt(height);
int xl = r.nextInt(weight);
int yl = r.nextInt(height);
g.setColor(getRandColor(160, 200));
g.drawLine(x, y, x + xl, y + yl);
}
}
/**
* 创建图片的方法
*/
private BufferedImage createImage() {
//创建图片缓冲区
BufferedImage image = new BufferedImage(weight, height, BufferedImage.TYPE_INT_RGB);
//获取画笔
Graphics2D g = (Graphics2D) image.getGraphics();
// 设定图像背景色(因为是做背景,所以偏淡)
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, weight, height);
//返回一个图片
return image;
}
/**
* 获取验证码图片的方法
*/
public BufferedImage getImage() {
BufferedImage image = createImage();
//获取画笔
Graphics2D g = (Graphics2D) image.getGraphics();
StringBuilder sb = new StringBuilder();
drawLine(image);
//画四个字符即可
for (int i = 0; i < codeNum; i++) {
//随机生成字符,因为只有画字符串的方法,没有画字符的方法,所以需要将字符变成字符串再画
String s = randomChar() + "";
//添加到StringBuilder里面
sb.append(s);
//定义字符的x坐标
float x = i * 1.0F * weight / 4;
//设置字体,随机
g.setFont(randomFont());
//设置颜色,随机
g.setColor(randomColor());
g.drawString(s, x, height - 5);
}
this.text = sb.toString();
return image;
}
/**
* 给定范围获得随机颜色
*/
Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > TWO_FIVE_FIVE) {
fc = TWO_FIVE_FIVE;
}
if (bc > TWO_FIVE_FIVE) {
bc = TWO_FIVE_FIVE;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
/**
* 获取验证码文本的方法
*/
public String getText() {
return text;
}
}
/**
* 公共工具包
*
* @author geekidea
* @date 2018-11-08
*/
package com.wecloud.im.core.util;
package com.wecloud.im.core.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.wecloud.im.core.common.enums.BaseEnum;
import com.wecloud.im.core.common.exception.BusinessException;
import com.wecloud.im.core.util.BaseEnumUtil;
import com.wecloud.im.core.validator.constraints.EnumType;
/**
* 自定义系统内的枚举验证注解实现类
*
* @author geekidea
* @date 2018-11-08
*/
public class EnumTypeValidator implements ConstraintValidator<EnumType, Integer> {
private Class<? extends BaseEnum> baseEnum;
@Override
public void initialize(EnumType parameters) {
baseEnum = parameters.type();
if (baseEnum == null) {
throw new BusinessException("请传入枚举类型类");
}
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null) {
return true;
}
return BaseEnumUtil.exists(baseEnum, value);
}
}
package com.wecloud.im.core.validator;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.wecloud.im.core.validator.constraints.IdCard;
/**
* 自定义身份证号码验证注解实现类
*
* @author geekidea
* @date 2018-11-08
*/
public class IdCardValidator implements ConstraintValidator<IdCard, String> {
private static final String REG_EX = "(^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{2}[0-9Xx]$)|(^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}[0-9Xx]$)";
private static final Pattern PATTERN = Pattern.compile(REG_EX);
@Override
public void initialize(IdCard parameters) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null) {
return true;
}
return PATTERN.matcher(value).matches();
}
}
package com.wecloud.im.core.validator;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.wecloud.im.core.validator.constraints.Phone;
/**
* 自定义手机号码验证注解实现类
*
* @author geekidea
* @date 2018-11-08
*/
public class PhoneValidator implements ConstraintValidator<Phone, String> {
private static final String REG_EX = "^1[3,4,5,6,7,8,9]\\d{9}$";
private static final Pattern PATTERN = Pattern.compile(REG_EX);
@Override
public void initialize(Phone parameters) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null) {
return true;
}
return PATTERN.matcher(value).matches();
}
}
package com.wecloud.im.core.validator.constraints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import com.wecloud.im.core.common.enums.BaseEnum;
import com.wecloud.im.core.validator.EnumTypeValidator;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 枚举类型注解
*
* @author geekidea
* @date 2018-11-08
*/
@Documented
@Constraint(validatedBy = {EnumTypeValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
public @interface EnumType {
String message() default "请输入正确的类型值";
Class<? extends BaseEnum> type();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
package com.wecloud.im.core.validator.constraints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import com.wecloud.im.core.validator.IdCardValidator;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 自定义身份证号码正则验证注解
*
* @author geekidea
* @date 2018-11-08
*/
@Documented
@Constraint(validatedBy = {IdCardValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
public @interface IdCard {
String message() default "请输入有效的身份证号码";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
package com.wecloud.im.core.validator.constraints;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import com.wecloud.im.core.validator.PhoneValidator;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 自定义手机号码正则验证注解
*
* @author geekidea
* @date 2018-11-08
*/
@Documented
@Constraint(validatedBy = {PhoneValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
public @interface Phone {
String message() default "请输入有效的手机号码";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
package com.wecloud.im.core.validator.groups;
import javax.validation.groups.Default;
/**
* Validator分组验证:添加
*
* @author geekidea
* @date 2020/3/8
**/
public interface Add extends Default {
}
package com.wecloud.im.core.validator.groups;
import javax.validation.groups.Default;
/**
* Validator分组验证:修改
*
* @author geekidea
* @date 2020/3/8
**/
public interface Update extends Default {
}
package com.wecloud.im.core.xss;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* Xss过滤器
*
* @author geekidea
* @date 2019-10-10
* @since 1.3.1.RELEASE
**/
@Slf4j
public class XssFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("XssFilter init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper(request);
filterChain.doFilter(xssHttpServletRequestWrapper, servletResponse);
}
@Override
public void destroy() {
log.info("XssFilter destroy");
}
}
package com.wecloud.im.core.xss;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.text.StringEscapeUtils;
/**
* XSS 跨站脚本攻击(Cross Site Scripting) 处理
*
* @author geekidea
* @date 2019-10-10
* @since 1.3.1.RELEASE
**/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getQueryString() {
return StringEscapeUtils.escapeHtml4(super.getQueryString());
}
@Override
public String getParameter(String name) {
return StringEscapeUtils.escapeHtml4(super.getParameter(name));
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (ArrayUtils.isEmpty(values)) {
return values;
}
int length = values.length;
String[] escapeValues = new String[length];
for (int i = 0; i < length; i++) {
escapeValues[i] = StringEscapeUtils.escapeHtml4(values[i]);
}
return escapeValues;
}
}
package com.wecloud.im.core.xss;
import java.io.IOException;
import org.apache.commons.text.StringEscapeUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
/**
* Jackson请求参数字符串转义处理
*
* @author geekidea
* @date 2019-10-10
* @since 1.3.1.RELEASE
**/
public class XssJacksonDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return StringEscapeUtils.escapeHtml4(jsonParser.getText());
}
}
package com.wecloud.im.core.xss;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import org.apache.commons.text.StringEscapeUtils;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
/**
* Jackson响应参数字符串转义处理
*
* @author geekidea
* @date 2019-10-10
* @since 1.3.1.RELEASE
**/
@Slf4j
public class XssJacksonSerializer extends JsonSerializer<String> {
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(StringEscapeUtils.escapeHtml4(s));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wecloud.im.core.ip.mapper.IpAddressMapper">
<select id="getByIp" resultType="com.wecloud.im.core.ip.entity.IpAddress">
select area, operator
from ip_address
where INET_ATON(#{ip}) > ip_start_num
and INET_ATON(#{ip}) <![CDATA[ < ]]> ip_end_num
</select>
</mapper>
<?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>im-common</artifactId>
<groupId>com.wecloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>im-common-log</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-security</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-bom</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
package com.wecloud.im.log.aop;
import lombok.extern.slf4j.Slf4j;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.fusesource.jansi.Ansi;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.wecloud.im.core.common.api.ApiCode;
import com.wecloud.im.core.common.api.ApiResult;
import com.wecloud.im.core.common.bean.ClientInfo;
import com.wecloud.im.core.common.exception.SpringBootPlusException;
import com.wecloud.im.core.constant.CommonConstant;
import com.wecloud.im.core.ip.entity.IpAddress;
import com.wecloud.im.core.ip.service.IpAddressService;
import com.wecloud.im.core.log.annotation.Module;
import com.wecloud.im.core.log.annotation.OperationLog;
import com.wecloud.im.core.log.annotation.OperationLogIgnore;
import com.wecloud.im.core.util.AnsiUtil;
import com.wecloud.im.core.util.ClientInfoUtil;
import com.wecloud.im.core.util.DateUtil;
import com.wecloud.im.core.util.IpUtil;
import com.wecloud.im.core.util.Jackson;
import com.wecloud.im.core.util.UUIDUtil;
import com.wecloud.im.log.bean.OperationLogInfo;
import com.wecloud.im.log.bean.RequestInfo;
import com.wecloud.im.log.entity.SysLoginLog;
import com.wecloud.im.log.entity.SysOperationLog;
import com.wecloud.im.log.properties.SpringBootPlusAopProperties;
import com.wecloud.im.log.service.SysLoginLogService;
import com.wecloud.im.log.service.SysOperationLogService;
import com.wecloud.im.security.service.LoginToken;
import com.wecloud.im.security.service.LoginUsername;
import com.wecloud.im.security.util.JwtTokenUtil;
import com.wecloud.im.security.util.JwtUtil;
/**
* <p>
* Controller Aop 抽象类
* 获取响应结果信息
* <p>
* 日志输出类型:print-type
* 1. 请求和响应分开,按照执行顺序打印
* 2. ThreadLocal线程绑定,方法执行结束时,连续打印请求和响应日志
* 3. ThreadLocal线程绑定,方法执行结束时,同时打印请求和响应日志
* </p>
*
* @author geekidea
* @date 2018-11-08
*/
@Slf4j
public abstract class BaseLogAop {
/**
* 请求ID
*/
private static final String REQUEST_ID = "requestId";
/**
* 零
*/
private static final int ZERO = 0;
/**
* 截取字符串的最多长度
*/
private static final int MAX_LENGTH = 300;
/**
* 登录日志:登录类型
*/
private static final int LOGIN_TYPE = 1;
/**
* 登录日志:登出类型
*/
private static final int LOGOUT_TYPE = 2;
/**
* 本地线程变量,保存请求参数信息到当前线程中
*/
protected static ThreadLocal<String> threadLocal = new ThreadLocal<>();
protected static ThreadLocal<RequestInfo> requestInfoThreadLocal = new ThreadLocal<>();
protected static ThreadLocal<OperationLogInfo> operationLogThreadLocal = new ThreadLocal<>();
@Autowired
protected SysOperationLogService sysOperationLogService;
/**
* Aop日志配置
*/
protected SpringBootPlusAopProperties.LogAopConfig logAopConfig;
/**
* 日志打印类型
*/
protected SpringBootPlusAopProperties.LogPrintType logPrintType;
/**
* 是否启用请求ID
*/
protected boolean enableRequestId;
/**
* requestId生成类型
*/
protected SpringBootPlusAopProperties.RequestIdType requestIdType;
/**
* Aop操作日志配置
*/
protected SpringBootPlusAopProperties.OperationLogConfig operationLogConfig;
/**
* Aop登录日志配置
*/
protected SpringBootPlusAopProperties.LoginLogConfig loginLogConfig;
@Autowired
private IpAddressService ipAddressService;
@Autowired
private SysLoginLogService sysLoginLogService;
/**
* 项目上下文路径
*/
@Value("${server.servlet.context-path}")
private String contextPath;
@Autowired
public void setSpringBootPlusAopProperties(SpringBootPlusAopProperties springBootPlusAopProperties) {
logAopConfig = springBootPlusAopProperties.getLog();
logPrintType = logAopConfig.getLogPrintType();
enableRequestId = logAopConfig.isEnableRequestId();
requestIdType = logAopConfig.getRequestIdType();
operationLogConfig = springBootPlusAopProperties.getOperationLog();
loginLogConfig = springBootPlusAopProperties.getLoginLog();
log.info("logAopConfig = " + logAopConfig);
log.info("logPrintType = " + logPrintType);
log.info("enableRequestId = " + enableRequestId);
log.info("requestIdType = " + requestIdType);
log.info("operationLogConfig = " + operationLogConfig);
log.info("loginLogConfig = " + loginLogConfig);
log.info("contextPath = " + contextPath);
}
/**
* 环绕通知
* 方法执行前打印请求参数信息
* 方法执行后打印响应结果信息
*
* @param joinPoint
* @return
* @throws Throwable
*/
public abstract Object doAround(ProceedingJoinPoint joinPoint) throws Throwable;
/**
* 异常通知方法
*
* @param joinPoint
* @param exception
*/
public abstract void afterThrowing(JoinPoint joinPoint, Exception exception);
/**
* 设置请求ID
*
* @param requestInfo
*/
protected abstract void setRequestId(RequestInfo requestInfo);
/**
* 获取请求信息对象
*
* @param requestInfo
*/
protected abstract void getRequestInfo(RequestInfo requestInfo);
/**
* 获取响应结果对象
*
* @param
*/
protected abstract void getResponseResult(Object result);
/**
* 请求响应处理完成之后的回调方法
*
* @param requestInfo
* @param operationLogInfo
* @param result
* @param exception
*/
protected abstract void finish(RequestInfo requestInfo, OperationLogInfo operationLogInfo, Object result, Exception exception);
/**
* 处理
*
* @param joinPoint
* @return
* @throws Throwable
*/
public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取请求相关信息
try {
// 获取当前的HttpServletRequest对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// HTTP请求信息对象
RequestInfo requestInfo = new RequestInfo();
// 请求路径 /api/foobar/add
String path = request.getRequestURI();
requestInfo.setPath(path);
// 获取实际路径 /foobar/add
String realPath = getRealPath(path);
requestInfo.setRealPath(realPath);
// 排除路径
Set<String> excludePaths = logAopConfig.getExcludePaths();
// 请求路径
if (handleExcludePaths(excludePaths, realPath)) {
return joinPoint.proceed();
}
// 获取请求类名和方法名称
Signature signature = joinPoint.getSignature();
// 获取真实的方法对象
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
// 处理操作日志信息
handleOperationLogInfo(method);
// IP地址
String ip = IpUtil.getRequestIp();
requestInfo.setIp(ip);
// 获取请求方式
String requestMethod = request.getMethod();
requestInfo.setRequestMethod(requestMethod);
// 获取请求内容类型
String contentType = request.getContentType();
requestInfo.setContentType(contentType);
// 判断控制器方法参数中是否有RequestBody注解
Annotation[][] annotations = method.getParameterAnnotations();
boolean isRequestBody = isRequestBody(annotations);
requestInfo.setRequestBody(isRequestBody);
AnnotatedType[] annotatedTypes = method.getAnnotatedParameterTypes();
// 获取Shiro注解值,并记录到map中
handleShiroAnnotationValue(requestInfo, method);
// 设置请求参数
Object requestParamObject = getRequestParamObject(joinPoint, request, requestMethod, contentType, isRequestBody);
requestInfo.setParam(requestParamObject);
requestInfo.setTime(DateUtil.getDateTimeString(new Date()));
// 获取请求头token
String token = request.getHeader(JwtTokenUtil.getTokenName());
requestInfo.setToken(token);
if (StringUtils.isNotBlank(token)) {
requestInfo.setTokenMd5(DigestUtils.md5Hex(token));
}
// 用户浏览器代理字符串
requestInfo.setUserAgent(request.getHeader(CommonConstant.USER_AGENT));
// 记录请求ID
setRequestId(requestInfo);
// 调用子类重写方法,控制请求信息日志处理
getRequestInfo(requestInfo);
} catch (Exception e) {
log.error("请求日志AOP处理异常", e);
}
// 执行目标方法,获得返回值
// 方法异常时,会调用子类的@AfterThrowing注解的方法,不会调用下面的代码,异常单独处理
Object result = joinPoint.proceed();
try {
// 调用子类重写方法,控制响应结果日志处理
getResponseResult(result);
} catch (Exception e) {
log.error("处理响应结果异常", e);
} finally {
handleAfterReturn(result, null);
}
return result;
}
/**
* 正常调用返回或者异常结束后调用此方法
*
* @param result
* @param exception
*/
protected void handleAfterReturn(Object result, Exception exception) {
// 获取RequestInfo
RequestInfo requestInfo = requestInfoThreadLocal.get();
// 获取OperationLogInfo
OperationLogInfo operationLogInfo = operationLogThreadLocal.get();
// 调用抽象方法,是否保存日志操作,需要子类重写该方法,手动调用saveSysOperationLog
finish(requestInfo, operationLogInfo, result, null);
// 释放资源
remove();
}
/**
* 处理异常
*
* @param exception
*/
public void handleAfterThrowing(Exception exception) {
// 获取RequestInfo
RequestInfo requestInfo = requestInfoThreadLocal.get();
// 获取OperationLogInfo
OperationLogInfo operationLogInfo = operationLogThreadLocal.get();
// 调用抽象方法,是否保存日志操作,需要子类重写该方法,手动调用saveSysOperationLog
finish(requestInfo, operationLogInfo, null, exception);
// 释放资源
remove();
}
private void handleOperationLogInfo(Method method) {
// 设置控制器类名称和方法名称
OperationLogInfo operationLogInfo = new OperationLogInfo()
.setControllerClassName(method.getDeclaringClass().getName())
.setControllerMethodName(method.getName());
// 获取Module类注解
Class<?> controllerClass = method.getDeclaringClass();
Module module = controllerClass.getAnnotation(Module.class);
if (module != null) {
String moduleName = module.name();
String moduleValue = module.value();
if (StringUtils.isNotBlank(moduleValue)) {
operationLogInfo.setModule(moduleValue);
}
if (StringUtils.isNotBlank(moduleName)) {
operationLogInfo.setModule(moduleName);
}
}
// 获取OperationLogIgnore注解
OperationLogIgnore classOperationLogIgnore = controllerClass.getAnnotation(OperationLogIgnore.class);
if (classOperationLogIgnore != null) {
// 不记录日志
operationLogInfo.setIgnore(true);
}
// 判断方法是否要过滤
OperationLogIgnore operationLogIgnore = method.getAnnotation(OperationLogIgnore.class);
if (operationLogIgnore != null) {
operationLogInfo.setIgnore(true);
}
// 从方法上获取OperationLog注解
OperationLog operationLog = method.getAnnotation(OperationLog.class);
if (operationLog != null) {
String operationLogName = operationLog.name();
String operationLogValue = operationLog.value();
if (StringUtils.isNotBlank(operationLogValue)) {
operationLogInfo.setName(operationLogValue);
}
if (StringUtils.isNotBlank(operationLogName)) {
operationLogInfo.setName(operationLogName);
}
operationLogInfo.setType(operationLog.type().getCode()).setRemark(operationLog.remark());
}
operationLogThreadLocal.set(operationLogInfo);
}
/**
* 获取Shiro注解值,并记录到map中
*
* @param
* @param method
*/
protected void handleShiroAnnotationValue(RequestInfo requestInfo, Method method) {
RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);
if (requiresRoles != null) {
String[] requiresRolesValues = requiresRoles.value();
if (ArrayUtils.isNotEmpty(requiresRolesValues)) {
String requiresRolesString = Arrays.toString(requiresRolesValues);
requestInfo.setRequiresRoles(requiresRolesString);
}
}
RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
if (requiresPermissions != null) {
String[] requiresPermissionsValues = requiresPermissions.value();
if (ArrayUtils.isNotEmpty(requiresPermissionsValues)) {
String requiresPermissionsString = Arrays.toString(requiresPermissionsValues);
requestInfo.setRequiresPermissions(requiresPermissionsString);
}
}
RequiresAuthentication requiresAuthentication = method.getAnnotation(RequiresAuthentication.class);
if (requiresAuthentication != null) {
requestInfo.setRequiresAuthentication(true);
}
RequiresUser requiresUser = method.getAnnotation(RequiresUser.class);
if (requiresUser != null) {
requestInfo.setRequiresUser(true);
}
RequiresGuest requiresGuest = method.getAnnotation(RequiresGuest.class);
if (requiresGuest != null) {
requestInfo.setRequiresGuest(true);
}
}
/**
* 处理请求ID
*
* @param requestInfo
*/
protected void handleRequestId(RequestInfo requestInfo) {
if (!enableRequestId) {
return;
}
String requestId = null;
if (SpringBootPlusAopProperties.RequestIdType.IDWORK == requestIdType) {
requestId = IdWorker.getIdStr();
} else if (SpringBootPlusAopProperties.RequestIdType.UUID == requestIdType) {
requestId = UUIDUtil.getUuid();
}
// 设置请求ID
MDC.put(REQUEST_ID, requestId);
requestInfo.setRequestId(requestId);
}
/**
* 处理请求参数
*
* @param requestInfo
*/
protected void handleRequestInfo(RequestInfo requestInfo) {
requestInfoThreadLocal.set(requestInfo);
if (SpringBootPlusAopProperties.LogPrintType.NONE == logPrintType) {
return;
}
// 获取请求信息
String requestInfoString = formatRequestInfo(requestInfo);
// 如果打印方式为顺序打印,则直接打印,否则,保存的threadLocal中
if (SpringBootPlusAopProperties.LogPrintType.ORDER == logPrintType) {
printRequestInfoString(requestInfoString);
} else {
threadLocal.set(requestInfoString);
}
}
/**
* 处理响应结果
*
* @param result
*/
protected void handleResponseResult(Object result) {
if (SpringBootPlusAopProperties.LogPrintType.NONE == logPrintType) {
return;
}
if (result != null && result instanceof ApiResult) {
ApiResult<?> apiResult = (ApiResult<?>) result;
int code = apiResult.getCode();
// 获取格式化后的响应结果
String responseResultString = formatResponseResult(apiResult);
if (SpringBootPlusAopProperties.LogPrintType.ORDER == logPrintType) {
printResponseResult(code, responseResultString);
} else {
// 从threadLocal中获取线程请求信息
String requestInfoString = threadLocal.get();
// 如果是连续打印,则先打印请求参数,再打印响应结果
if (SpringBootPlusAopProperties.LogPrintType.LINE == logPrintType) {
printRequestInfoString(requestInfoString);
printResponseResult(code, responseResultString);
} else if (SpringBootPlusAopProperties.LogPrintType.MERGE == logPrintType) {
printRequestResponseString(code, requestInfoString, responseResultString);
}
}
}
}
/**
* 同时打印请求和响应信息
*
* @param code
* @param requestInfoString
* @param responseResultString
*/
protected void printRequestResponseString(int code, String requestInfoString, String responseResultString) {
if (code == ApiCode.SUCCESS.getCode()) {
log.info(requestInfoString + "\n" + responseResultString);
} else {
log.error(requestInfoString + "\n" + responseResultString);
}
}
/**
* 格式化请求信息
*
* @param requestInfo
* @return
*/
protected String formatRequestInfo(RequestInfo requestInfo) {
String requestInfoString = null;
try {
if (logAopConfig.isRequestLogFormat()) {
requestInfoString = "\n" + Jackson.toJsonStringNonNull(requestInfo, true);
} else {
requestInfoString = Jackson.toJsonStringNonNull(requestInfo);
}
} catch (Exception e) {
log.error("格式化请求信息日志异常", e);
}
return AnsiUtil.getAnsi(Ansi.Color.GREEN, "requestInfo:" + requestInfoString);
}
/**
* 打印请求信息
*
* @param requestInfoString
*/
protected void printRequestInfoString(String requestInfoString) {
log.info(requestInfoString);
}
/**
* 格式化响应信息
*
* @param apiResult
* @return
*/
protected String formatResponseResult(ApiResult<?> apiResult) {
String responseResultString = "responseResult:";
try {
if (logAopConfig.isResponseLogFormat()) {
responseResultString += "\n" + Jackson.toJsonString(apiResult, true);
} else {
responseResultString += Jackson.toJsonString(apiResult);
}
int code = apiResult.getCode();
if (code == ApiCode.SUCCESS.getCode()) {
return AnsiUtil.getAnsi(Ansi.Color.BLUE, responseResultString);
} else {
return AnsiUtil.getAnsi(Ansi.Color.RED, responseResultString);
}
} catch (Exception e) {
log.error("格式化响应日志异常", e);
}
return responseResultString;
}
/**
* 打印响应信息
*
* @param code
* @param responseResultString
*/
protected void printResponseResult(int code, String responseResultString) {
if (code == ApiCode.SUCCESS.getCode()) {
log.info(responseResultString);
} else {
log.error(responseResultString);
}
}
/**
* 获取请求参数JSON字符串
*
* @param joinPoint
* @param request
* @param requestMethod
* @param contentType
* @param isRequestBody
*/
protected Object getRequestParamObject(ProceedingJoinPoint joinPoint, HttpServletRequest request, String requestMethod, String contentType, boolean isRequestBody) {
Object paramObject = null;
if (isRequestBody) {
// POST,application/json,RequestBody的类型,简单判断,然后序列化成JSON字符串
Object[] args = joinPoint.getArgs();
paramObject = getArgsObject(args);
} else {
// 获取getParameterMap中所有的值,处理后序列化成JSON字符串
Map<String, String[]> paramsMap = request.getParameterMap();
paramObject = getParamJSONObject(paramsMap);
}
return paramObject;
}
/**
* 判断控制器方法参数中是否有RequestBody注解
*
* @param annotations
* @return
*/
protected boolean isRequestBody(Annotation[][] annotations) {
boolean isRequestBody = false;
for (Annotation[] annotationArray : annotations) {
for (Annotation annotation : annotationArray) {
if (annotation instanceof RequestBody) {
isRequestBody = true;
}
}
}
return isRequestBody;
}
/**
* 请求参数拼装
*
* @param args
* @return
*/
protected Object getArgsObject(Object[] args) {
if (args == null) {
return null;
}
// 去掉HttpServletRequest和HttpServletResponse
List<Object> realArgs = new ArrayList<>();
for (Object arg : args) {
if (arg instanceof HttpServletRequest) {
continue;
}
if (arg instanceof HttpServletResponse) {
continue;
}
if (arg instanceof MultipartFile) {
continue;
}
if (arg instanceof ModelAndView) {
continue;
}
realArgs.add(arg);
}
if (realArgs.size() == 1) {
return realArgs.get(0);
} else {
return realArgs;
}
}
/**
* 获取参数Map的JSON字符串
*
* @param paramsMap
* @return
*/
protected JSONObject getParamJSONObject(Map<String, String[]> paramsMap) {
if (MapUtils.isEmpty(paramsMap)) {
return null;
}
JSONObject jsonObject = new JSONObject();
for (Map.Entry<String, String[]> kv : paramsMap.entrySet()) {
String key = kv.getKey();
String[] values = kv.getValue();
// 没有值
if (values == null) {
jsonObject.put(key, null);
} else if (values.length == 1) {
// 一个值
jsonObject.put(key, values[0]);
} else {
// 多个值
jsonObject.put(key, values);
}
}
return jsonObject;
}
/**
* 处理排除路径,匹配返回true,否则返回false
*
* @param excludePaths 排除路径
* @param realPath 请求实际路径
* @return
*/
protected boolean handleExcludePaths(Set<String> excludePaths, String realPath) {
if (CollectionUtils.isEmpty(excludePaths) || StringUtils.isBlank(realPath)) {
return false;
}
// 如果是排除路径,则跳过
return excludePaths.contains(realPath);
}
/**
* 获取实际路径
*
* @param requestPath
* @return
*/
private String getRealPath(String requestPath) {
// 如果项目路径不为空,则去掉项目路径,获取实际访问路径
if (StringUtils.isNotBlank(contextPath)) {
return requestPath.substring(contextPath.length());
}
return requestPath;
}
/**
* 异步保存系统操作日志
*
* @param requestInfo
* @param operationLogInfo
* @param result
* @param exception
*/
@Async
protected void saveSysOperationLog(RequestInfo requestInfo, OperationLogInfo operationLogInfo, Object result, Exception exception) {
try {
// 如果不记录操作日志,则跳过
if (!operationLogConfig.isEnable()) {
return;
}
// 排除路径
Set<String> excludePaths = operationLogConfig.getExcludePaths();
// 请求路径
if (handleExcludePaths(excludePaths, requestInfo.getRealPath())) {
return;
}
// 操作日志
SysOperationLog sysOperationLog = new SysOperationLog();
// 设置操作日志信息
if (operationLogInfo != null) {
// 如果类或方法上标注有OperationLogIgnore,则跳过
if (operationLogInfo.isIgnore()) {
return;
}
sysOperationLog.setModule(operationLogInfo.getModule())
.setName(operationLogInfo.getName())
.setType(operationLogInfo.getType())
.setRemark(operationLogInfo.getRemark())
.setClassName(operationLogInfo.getControllerClassName())
.setMethodName(operationLogInfo.getControllerMethodName());
}
// 设置请求参数信息
if (requestInfo != null) {
sysOperationLog.setIp(requestInfo.getIp())
.setPath(requestInfo.getPath())
.setRequestId(requestInfo.getRequestId())
.setRequestMethod(requestInfo.getRequestMethod())
.setContentType(requestInfo.getContentType())
.setRequestBody(requestInfo.getRequestBody())
.setToken(requestInfo.getTokenMd5());
// 设置参数字符串
sysOperationLog.setParam(Jackson.toJsonStringNonNull(requestInfo.getParam()));
// User-Agent
ClientInfo clientInfo = ClientInfoUtil.get(requestInfo.getUserAgent());
if (clientInfo != null) {
sysOperationLog.setBrowserName(clientInfo.getBrowserName())
.setBrowserVersion(clientInfo.getBrowserversion())
.setEngineName(clientInfo.getEngineName())
.setEngineVersion(clientInfo.getEngineVersion())
.setOsName(clientInfo.getOsName())
.setPlatformName(clientInfo.getPlatformName())
.setMobile(clientInfo.isMobile())
.setDeviceName(clientInfo.getDeviceName())
.setDeviceModel(clientInfo.getDeviceModel());
}
// 设置IP区域
IpAddress ipAddress = ipAddressService.getByIp(requestInfo.getIp());
if (ipAddress != null) {
requestInfo.setIpAddress(ipAddress);
sysOperationLog.setArea(ipAddress.getArea()).setOperator(ipAddress.getOperator());
}
}
// 设置响应结果
if (result != null && result instanceof ApiResult) {
ApiResult<?> apiResult = (ApiResult<?>) result;
apiResult.getCode();
sysOperationLog
// .setSuccess(apiResult.isSuccess()
// )
.setCode(apiResult.getCode())
.setMessage(apiResult.getMessage());
}
// 设置当前登录信息
// sysOperationLog.setUserId(LoginUtil.getUserId()).setUserName(LoginUtil.getUsername());
// 设置异常信息
if (exception != null) {
Integer errorCode = null;
String exceptionMessage = exception.getMessage();
if (StringUtils.isNotBlank(exceptionMessage)) {
exceptionMessage = StringUtils.substring(exceptionMessage, ZERO, MAX_LENGTH);
}
if (exception instanceof SpringBootPlusException) {
SpringBootPlusException springBootPlusException = (SpringBootPlusException) exception;
errorCode = springBootPlusException.getErrorCode();
}
// 异常字符串长度截取
sysOperationLog.setSuccess(false)
.setCode(errorCode)
.setExceptionMessage(exceptionMessage)
.setExceptionName(exception.getClass().getName());
}
// 保存日志到数据库
sysOperationLogService.saveSysOperationLog(sysOperationLog);
} catch (Exception e) {
if (e instanceof JWTDecodeException) {
JWTDecodeException jwtDecodeException = (JWTDecodeException) e;
throw jwtDecodeException;
}
log.error("保存系统操作日志失败", e);
}
}
/**
* 异步保存系统登录日志
*
* @param requestInfo
* @param operationLogInfo
* @param result
* @param exception
*/
@Async
protected void saveSysLoginLog(RequestInfo requestInfo, OperationLogInfo operationLogInfo, Object result, Exception exception) {
try {
// 如果不记录登录日志,则跳过
if (!loginLogConfig.isEnable()) {
return;
}
String realPath = requestInfo.getRealPath();
if (StringUtils.isBlank(realPath)) {
return;
}
boolean flag = false;
Integer type = null;
// 判断是否是登录路径
if (realPath.equals(loginLogConfig.getLoginPath())) {
flag = true;
type = LOGIN_TYPE;
} else if (realPath.equals(loginLogConfig.getLogoutPath())) {
flag = true;
type = LOGOUT_TYPE;
}
// 保存登录登出日志
if (flag) {
SysLoginLog sysLoginLog = new SysLoginLog();
sysLoginLog.setType(type);
// 设置异常信息
if (exception != null) {
Integer errorCode = null;
String exceptionMessage = exception.getMessage();
if (StringUtils.isNotBlank(exceptionMessage)) {
exceptionMessage = StringUtils.substring(exceptionMessage, ZERO, MAX_LENGTH);
}
if (exception instanceof SpringBootPlusException) {
SpringBootPlusException springBootPlusException = (SpringBootPlusException) exception;
errorCode = springBootPlusException.getErrorCode();
}
// 异常字符串长度截取
sysLoginLog.setCode(errorCode).setExceptionMessage(exceptionMessage);
}
// 判断登录登出结果
if (result != null && result instanceof ApiResult) {
ApiResult<?> apiResult = (ApiResult<?>) result;
sysLoginLog
// .setSuccess(
// apiResult.isSuccess())
.setCode(apiResult.getCode());
// if (apiResult.isSuccess()) {
if (LOGIN_TYPE == type) {
Object object = apiResult.getData();
if (object != null && object instanceof LoginToken) {
LoginToken loginToken = (LoginToken) object;
String token = loginToken.getToken();
if (StringUtils.isNotBlank(token)) {
// 设置登录token
String tokenMd5 = DigestUtils.md5Hex(token);
sysLoginLog.setToken(tokenMd5);
}
}
// }
} else {
sysLoginLog.setExceptionMessage(apiResult.getMessage());
}
}
// 设置请求参数信息
if (requestInfo != null) {
sysLoginLog.setIp(requestInfo.getIp()).setRequestId(requestInfo.getRequestId());
// 设置登录用户名
if (LOGIN_TYPE == type) {
Object paramObject = requestInfo.getParam();
if (paramObject != null && paramObject instanceof LoginUsername) {
LoginUsername loginUsername = (LoginUsername) paramObject;
String username = loginUsername.getUsername();
sysLoginLog.setUsername(username);
}
} else if (LOGOUT_TYPE == type) {
String username = JwtUtil.getClientId(requestInfo.getToken());
sysLoginLog.setUsername(username);
// 设置登出token
sysLoginLog.setToken(requestInfo.getTokenMd5());
}
// User-Agent
String userAgent = requestInfo.getUserAgent();
if (StringUtils.isNotBlank(userAgent)) {
sysLoginLog.setUserAgent(StringUtils.substring(userAgent, ZERO, MAX_LENGTH));
}
ClientInfo clientInfo = ClientInfoUtil.get(userAgent);
if (clientInfo != null) {
sysLoginLog.setBrowserName(clientInfo.getBrowserName())
.setBrowserVersion(clientInfo.getBrowserversion())
.setEngineName(clientInfo.getEngineName())
.setEngineVersion(clientInfo.getEngineVersion())
.setOsName(clientInfo.getOsName())
.setPlatformName(clientInfo.getPlatformName())
.setMobile(clientInfo.isMobile())
.setDeviceName(clientInfo.getDeviceName())
.setDeviceModel(clientInfo.getDeviceModel());
}
IpAddress ipAddress = requestInfo.getIpAddress();
if (ipAddress == null) {
ipAddress = ipAddressService.getByIp(requestInfo.getIp());
}
if (ipAddress != null) {
sysLoginLog.setArea(ipAddress.getArea()).setOperator(ipAddress.getOperator());
}
// 保存登录日志
sysLoginLogService.saveSysLoginLog(sysLoginLog);
}
}
} catch (Exception e) {
log.error("保存系统登录日志失败", e);
}
}
/**
* 释放资源
*/
protected void remove() {
threadLocal.remove();
requestInfoThreadLocal.remove();
operationLogThreadLocal.remove();
MDC.clear();
}
}
package com.wecloud.im.log.bean;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 操作日志信息
*
* @author geekidea
* @date 2020/3/19
**/
@Data
@Accessors(chain = true)
public class OperationLogInfo {
/**
* 是否忽略
*/
private boolean ignore;
/**
* 模块名称
*/
private String module;
/**
* 日志名称
*/
private String name;
/**
* 日志类型
*/
private Integer type;
/**
* 日志备注
*/
private String remark;
/**
* controller类名称
*/
private String controllerClassName;
/**
* controller目标方法名称
*/
private String controllerMethodName;
}
package com.wecloud.im.log.bean;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.wecloud.im.core.ip.entity.IpAddress;
/**
* HTTP请求信息对象
*
* @author geekidea
* @date 2020/3/18
**/
@Data
@Accessors(chain = true)
public class RequestInfo implements Serializable {
private static final long serialVersionUID = 1421424612944015973L;
/**
* 请求路径
* /api/foobar/add
*/
private String path;
/**
* 请求ID
*/
@JsonIgnore
@JSONField(serialize = false)
private String requestId;
/**
* 请求实际路径
* /foobar/add
*/
@JsonIgnore
@JSONField(serialize = false)
private String realPath;
/**
* 请求IP地址
*/
private String ip;
/**
* 请求IP对象
*/
@JsonIgnore
@JSONField(serialize = false)
private IpAddress ipAddress;
/**
* 请求方式,GET/POST
*/
private String requestMethod;
/**
* 请求内容类型
*/
private String contentType;
/**
* 判断控制器方法参数中是否有RequestBody注解
*/
private Boolean requestBody;
/**
* 请求参数对象
*/
private Object param;
/**
* 请求时间字符串
*/
private String time;
/**
* 请求token
*/
private String token;
/**
* 请求token MD5值
*/
@JsonIgnore
@JSONField(serialize = false)
private String tokenMd5;
/**
* 用户代理字符串
*/
@JsonIgnore
@JSONField(serialize = false)
private String userAgent;
/**
* requiresRoles值
*/
private String requiresRoles;
/**
* requiresPermissions值
*/
private String requiresPermissions;
/**
* requiresAuthentication
*/
private Boolean requiresAuthentication;
/**
* requiresUser
*/
private Boolean requiresUser;
/**
* requiresGuest
*/
private Boolean requiresGuest;
}
package com.wecloud.im.log.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wecloud.im.core.common.api.ApiResult;
import com.wecloud.im.core.common.controller.BaseController;
import com.wecloud.im.core.log.annotation.Module;
import com.wecloud.im.core.log.annotation.OperationLog;
import com.wecloud.im.core.log.enums.OperationLogType;
import com.wecloud.im.core.pagination.Paging;
import com.wecloud.im.log.entity.SysLoginLog;
import com.wecloud.im.log.param.SysLoginLogPageParam;
import com.wecloud.im.log.service.SysLoginLogService;
/**
* 系统登录日志 控制器
*
* @author geekidea
* @since 2020-03-24
*/
@Slf4j
@RestController
@RequestMapping("/sys/sysLoginLog")
@Module("log")
@Api(value = "系统登录日志API", tags = {"系统登录日志"})
public class SysLoginLogController extends BaseController {
@Autowired
private SysLoginLogService sysLoginLogService;
/**
* 系统登录日志分页列表
*/
@PostMapping("/getPageList")
@RequiresPermissions("sys:login:log:page")
@OperationLog(name = "系统登录日志分页列表", type = OperationLogType.PAGE)
@ApiOperation(value = "系统登录日志分页列表", response = SysLoginLog.class)
public ApiResult<Paging<SysLoginLog>> getSysLoginLogPageList(@Validated @RequestBody SysLoginLogPageParam sysLoginLogPageParam) throws Exception {
Paging<SysLoginLog> paging = sysLoginLogService.getSysLoginLogPageList(sysLoginLogPageParam);
return ApiResult.ok(paging);
}
}
package com.wecloud.im.log.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wecloud.im.core.common.api.ApiResult;
import com.wecloud.im.core.common.controller.BaseController;
import com.wecloud.im.core.pagination.Paging;
import com.wecloud.im.log.entity.SysOperationLog;
import com.wecloud.im.log.param.SysOperationLogPageParam;
import com.wecloud.im.log.service.SysOperationLogService;
/**
* 系统操作日志 控制器
*
* @author geekidea
* @since 2020-03-19
*/
@Slf4j
@RestController
@RequestMapping("/sys/sysOperationLog")
@Api(value = "系统操作日志API", tags = {"系统操作日志"})
public class SysOperationLogController extends BaseController {
@Autowired
private SysOperationLogService sysOperationLogService;
/**
* 系统操作日志分页列表
*/
@PostMapping("/getPageList")
@RequiresPermissions("sys:operation:log:page")
@ApiOperation(value = "系统操作日志分页列表", response = SysOperationLog.class)
public ApiResult<Paging<SysOperationLog>> getSysOperationLogPageList(@Validated @RequestBody SysOperationLogPageParam sysOperationLogPageParam) throws Exception {
Paging<SysOperationLog> paging = sysOperationLogService.getSysOperationLogPageList(sysOperationLogPageParam);
return ApiResult.ok(paging);
}
}
package com.wecloud.im.log.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
import javax.validation.constraints.NotNull;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.wecloud.im.core.common.entity.BaseEntity;
import com.wecloud.im.core.validator.groups.Update;
/**
* 系统登录日志
*
* @author geekidea
* @since 2020-03-24
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "SysLoginLog对象")
public class SysLoginLog extends BaseEntity {
private static final long serialVersionUID = 1L;
@NotNull(message = "id不能为空", groups = {Update.class})
@ApiModelProperty("主键")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty("请求ID")
private String requestId;
@ApiModelProperty("用户名称")
private String username;
@ApiModelProperty("IP")
private String ip;
@ApiModelProperty("区域")
private String area;
@ApiModelProperty("运营商")
private String operator;
@ApiModelProperty("tokenMd5值")
private String token;
@ApiModelProperty("1:登录,2:登出")
private Integer type;
@ApiModelProperty("是否成功 true:成功/false:失败")
private Boolean success;
@ApiModelProperty("响应码")
private Integer code;
@ApiModelProperty("失败消息记录")
private String exceptionMessage;
@ApiModelProperty("浏览器名称")
private String userAgent;
@ApiModelProperty("浏览器名称")
private String browserName;
@ApiModelProperty("浏览器版本")
private String browserVersion;
@ApiModelProperty("浏览器引擎名称")
private String engineName;
@ApiModelProperty("浏览器引擎版本")
private String engineVersion;
@ApiModelProperty("系统名称")
private String osName;
@ApiModelProperty("平台名称")
private String platformName;
@ApiModelProperty("是否是手机,0:否,1:是")
private Boolean mobile;
@ApiModelProperty("移动端设备名称")
private String deviceName;
@ApiModelProperty("移动端设备型号")
private String deviceModel;
@ApiModelProperty("备注")
private String remark;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("修改时间")
private Date updateTime;
}
package com.wecloud.im.log.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
import javax.validation.constraints.NotNull;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.wecloud.im.core.common.entity.BaseEntity;
import com.wecloud.im.core.validator.groups.Update;
/**
* 系统操作日志
*
* @author geekidea
* @since 2020-03-19
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "SysOperationLog对象")
public class SysOperationLog extends BaseEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主键")
@TableId(value = "id", type = IdType.AUTO)
@NotNull(message = "id不能为空", groups = {Update.class})
private Long id;
@ApiModelProperty("请求ID")
private String requestId;
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("用户名称")
private String userName;
@ApiModelProperty("日志名称")
private String name;
@ApiModelProperty("IP")
private String ip;
@ApiModelProperty("区域")
private String area;
@ApiModelProperty("运营商")
private String operator;
@ApiModelProperty("全路径")
private String path;
@ApiModelProperty("模块名称")
private String module;
@ApiModelProperty("类名")
private String className;
@ApiModelProperty("方法名称")
private String methodName;
@ApiModelProperty("请求方式,GET/POST")
private String requestMethod;
@ApiModelProperty("内容类型")
private String contentType;
@ApiModelProperty("是否是JSON请求映射参数")
private Boolean requestBody;
@ApiModelProperty("请求参数")
private String param;
@ApiModelProperty("tokenMd5值")
private String token;
@ApiModelProperty("0:其它,1:新增,2:修改,3:删除,4:详情查询,5:所有列表,6:分页列表,7:其它查询,8:上传文件")
private Integer type;
@ApiModelProperty("0:失败,1:成功")
private Boolean success;
@ApiModelProperty("响应结果状态码")
private Integer code;
@ApiModelProperty("响应结果消息")
private String message;
@ApiModelProperty("异常类名称")
private String exceptionName;
@ApiModelProperty("异常信息")
private String exceptionMessage;
@ApiModelProperty("浏览器名称")
private String browserName;
@ApiModelProperty("浏览器版本")
private String browserVersion;
@ApiModelProperty("浏览器引擎名称")
private String engineName;
@ApiModelProperty("浏览器引擎版本")
private String engineVersion;
@ApiModelProperty("系统名称")
private String osName;
@ApiModelProperty("平台名称")
private String platformName;
@ApiModelProperty("是否是手机,0:否,1:是")
private Boolean mobile;
@ApiModelProperty("移动端设备名称")
private String deviceName;
@ApiModelProperty("移动端设备型号")
private String deviceModel;
@ApiModelProperty("备注")
private String remark;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("修改时间")
private Date updateTime;
}
package com.wecloud.im.log.mapper;
import org.springframework.stereotype.Repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wecloud.im.log.entity.SysLoginLog;
/**
* 系统登录日志 Mapper 接口
*
* @author geekidea
* @since 2020-03-24
*/
@Repository
public interface SysLoginLogMapper extends BaseMapper<SysLoginLog> {
}
package com.wecloud.im.log.mapper;
import org.springframework.stereotype.Repository;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wecloud.im.log.entity.SysOperationLog;
/**
* 系统操作日志 Mapper 接口
*
* @author geekidea
* @since 2020-03-19
*/
@Repository
public interface SysOperationLogMapper extends BaseMapper<SysOperationLog> {
}
package com.wecloud.im.log.param;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.wecloud.im.core.pagination.BasePageOrderParam;
/**
* <pre>
* 系统登录日志 分页参数对象
* </pre>
*
* @author geekidea
* @date 2020-03-24
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "系统登录日志分页参数")
public class SysLoginLogPageParam extends BasePageOrderParam {
private static final long serialVersionUID = 1L;
}
package com.wecloud.im.log.param;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.wecloud.im.core.pagination.BasePageOrderParam;
/**
* <pre>
* 系统操作日志 分页参数对象
* </pre>
*
* @author geekidea
* @date 2020-03-19
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "系统操作日志分页参数")
public class SysOperationLogPageParam extends BasePageOrderParam {
private static final long serialVersionUID = 1L;
}
package com.wecloud.im.log.properties;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Set;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
/**
* AOP配置属性
*
* @author geekidea
* @date 2019-09-29
**/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-plus.aop")
public class SpringBootPlusAopProperties {
/**
* 请求路径Filter配置
*/
@NestedConfigurationProperty
private LogAopConfig log = new LogAopConfig();
/**
* 操作日志配置
*/
@NestedConfigurationProperty
private OperationLogConfig operationLog;
/**
* 登录日志配置
*/
private LoginLogConfig loginLog;
/**
* 日志打印类型
*
* @author geekidea
* @date 2020/3/19
**/
public enum LogPrintType {
/**
* 不打印日志
*/
NONE,
/**
* 请求和响应日志,按照执行顺序分开打印
*/
ORDER,
/**
* 方法执行结束时,连续分开打印请求和响应日志
*/
LINE,
/**
* 方法执行结束时,合并请求和响应日志,同时打印
*/
MERGE
}
/**
* 请求ID生成类型
*
* @author geekidea
* @date 2020/3/25
**/
public enum RequestIdType {
/**
* 生成UUID无中横线
*/
UUID,
/**
* 生成数字
*/
IDWORK
}
@Data
public static class AopConfig {
/**
* 是否启用
*/
private boolean enable;
}
@Data
@EqualsAndHashCode(callSuper = true)
public static class LogAopConfig extends AopConfig {
/**
* 是否启用requestId
*/
private boolean enableRequestId = true;
/**
* 日志输出类型:print-type
*/
private LogPrintType logPrintType = LogPrintType.ORDER;
/**
* 请求ID生成类型
*/
private RequestIdType requestIdType = RequestIdType.IDWORK;
/**
* 请求日志在控制台是否格式化输出,local环境建议开启,服务器环境设置为false
*/
private boolean requestLogFormat = true;
/**
* 响应日志在控制台是否格式化输出,local环境建议开启,服务器环境设置为false
*/
private boolean responseLogFormat = true;
/**
* 排除路径
*/
private Set<String> excludePaths;
}
/**
* 操作日志配置
*/
@Data
public static class OperationLogConfig {
/**
* 是否启用
*/
private boolean enable = true;
/**
* 排除路径
*/
private Set<String> excludePaths;
}
/**
* 登录日志配置
*/
@Data
public static class LoginLogConfig {
/**
* 是否启用
*/
private boolean enable = true;
/**
* 登录路径
*/
private String loginPath = "/login";
/**
* 登出路径
*/
private String logoutPath = "/logout";
}
}
package com.wecloud.im.log.service;
import com.wecloud.im.core.common.service.BaseService;
import com.wecloud.im.core.pagination.Paging;
import com.wecloud.im.log.entity.SysLoginLog;
import com.wecloud.im.log.param.SysLoginLogPageParam;
/**
* 系统登录日志 服务类
*
* @author geekidea
* @since 2020-03-24
*/
public interface SysLoginLogService extends BaseService<SysLoginLog> {
/**
* 保存
*
* @param sysLoginLog
* @return
* @throws Exception
*/
boolean saveSysLoginLog(SysLoginLog sysLoginLog) throws Exception;
/**
* 修改
*
* @param sysLoginLog
* @return
* @throws Exception
*/
boolean updateSysLoginLog(SysLoginLog sysLoginLog) throws Exception;
/**
* 删除
*
* @param id
* @return
* @throws Exception
*/
boolean deleteSysLoginLog(Long id) throws Exception;
/**
* 获取分页对象
*
* @param sysLoginLogQueryParam
* @return
* @throws Exception
*/
Paging<SysLoginLog> getSysLoginLogPageList(SysLoginLogPageParam sysLoginLogPageParam) throws Exception;
}
package com.wecloud.im.log.service;
import com.wecloud.im.core.common.service.BaseService;
import com.wecloud.im.core.pagination.Paging;
import com.wecloud.im.log.entity.SysOperationLog;
import com.wecloud.im.log.param.SysOperationLogPageParam;
/**
* 系统操作日志 服务类
*
* @author geekidea
* @since 2020-03-19
*/
public interface SysOperationLogService extends BaseService<SysOperationLog> {
/**
* 保存
*
* @param sysOperationLog
* @return
* @throws Exception
*/
boolean saveSysOperationLog(SysOperationLog sysOperationLog) throws Exception;
/**
* 修改
*
* @param sysOperationLog
* @return
* @throws Exception
*/
boolean updateSysOperationLog(SysOperationLog sysOperationLog) throws Exception;
/**
* 删除
*
* @param id
* @return
* @throws Exception
*/
boolean deleteSysOperationLog(Long id) throws Exception;
/**
* 获取分页对象
*
* @param sysOperationLogQueryParam
* @return
* @throws Exception
*/
Paging<SysOperationLog> getSysOperationLogPageList(SysOperationLogPageParam sysOperationLogPageParam) throws Exception;
}
package com.wecloud.im.log.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wecloud.im.core.common.service.impl.BaseServiceImpl;
import com.wecloud.im.core.pagination.PageInfo;
import com.wecloud.im.core.pagination.Paging;
import com.wecloud.im.log.entity.SysLoginLog;
import com.wecloud.im.log.mapper.SysLoginLogMapper;
import com.wecloud.im.log.param.SysLoginLogPageParam;
import com.wecloud.im.log.service.SysLoginLogService;
/**
* 系统登录日志 服务实现类
*
* @author geekidea
* @since 2020-03-24
*/
@Slf4j
@Service
public class SysLoginLogServiceImpl extends BaseServiceImpl<SysLoginLogMapper, SysLoginLog> implements SysLoginLogService {
@Autowired
private SysLoginLogMapper sysLoginLogMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveSysLoginLog(SysLoginLog sysLoginLog) throws Exception {
return super.save(sysLoginLog);
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean updateSysLoginLog(SysLoginLog sysLoginLog) throws Exception {
return super.updateById(sysLoginLog);
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean deleteSysLoginLog(Long id) throws Exception {
return super.removeById(id);
}
@Override
public Paging<SysLoginLog> getSysLoginLogPageList(SysLoginLogPageParam sysLoginLogPageParam) throws Exception {
Page<SysLoginLog> page = new PageInfo<>(sysLoginLogPageParam, OrderItem.desc(getLambdaColumn(SysLoginLog::getCreateTime)));
LambdaQueryWrapper<SysLoginLog> wrapper = new LambdaQueryWrapper<>();
IPage<SysLoginLog> iPage = sysLoginLogMapper.selectPage(page, wrapper);
return new Paging<SysLoginLog>(iPage);
}
}
package com.wecloud.im.log.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wecloud.im.core.common.service.impl.BaseServiceImpl;
import com.wecloud.im.core.pagination.PageInfo;
import com.wecloud.im.core.pagination.Paging;
import com.wecloud.im.log.entity.SysOperationLog;
import com.wecloud.im.log.mapper.SysOperationLogMapper;
import com.wecloud.im.log.param.SysOperationLogPageParam;
import com.wecloud.im.log.service.SysOperationLogService;
/**
* 系统操作日志 服务实现类
*
* @author geekidea
* @since 2020-03-19
*/
@Slf4j
@Service
public class SysOperationLogServiceImpl extends BaseServiceImpl<SysOperationLogMapper, SysOperationLog> implements SysOperationLogService {
@Autowired
private SysOperationLogMapper sysOperationLogMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveSysOperationLog(SysOperationLog sysOperationLog) throws Exception {
return super.save(sysOperationLog);
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean updateSysOperationLog(SysOperationLog sysOperationLog) throws Exception {
return super.updateById(sysOperationLog);
}
@Override
public Paging<SysOperationLog> getSysOperationLogPageList(SysOperationLogPageParam sysOperationLogPageParam) throws Exception {
Page<SysOperationLog> page = new PageInfo<>(sysOperationLogPageParam, OrderItem.desc(getLambdaColumn(SysOperationLog::getCreateTime)));
LambdaQueryWrapper<SysOperationLog> wrapper = new LambdaQueryWrapper<>();
IPage<SysOperationLog> iPage = sysOperationLogMapper.selectPage(page, wrapper);
return new Paging<SysOperationLog>(iPage);
}
@Transactional(rollbackFor = Exception.class)
@Override
public boolean deleteSysOperationLog(Long id) throws Exception {
return super.removeById(id);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wecloud.im.log.mapper.SysLoginLogMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wecloud.im.log.mapper.SysOperationLogMapper">
</mapper>
<?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>im-common</artifactId>
<groupId>com.wecloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>im-common-swagger</artifactId>
<dependencies>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.20</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>2.9.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-spring-web</artifactId>
<version>2.9.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-common</artifactId>
<version>2.9.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-autoconfigure</artifactId>
<version>2.0.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.wecloud</groupId>
<artifactId>im-common-bom</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
package com.wecloud.im.swagger;
import lombok.Data;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
/**
* Swagger配置属性
*
* @author geekidea
* @date 2020/3/21
**/
@Data
@Component
@ConfigurationProperties(prefix = "spring-boot-plus.swagger")
public class SwaggerProperties {
/**
* 是否启用Swagger
*/
private boolean enable;
/**
* 扫描的基本包
*/
@Value("${spring-boot-plus.swagger.base.package}")
private String basePackage;
/**
* 联系人邮箱
*/
@Value("${spring-boot-plus.swagger.contact.email}")
private String contactEmail;
/**
* 联系人名称
*/
@Value("${spring-boot-plus.swagger.contact.name}")
private String contactName;
/**
* 联系人网址
*/
@Value("${spring-boot-plus.swagger.contact.url}")
private String contactUrl;
/**
* 描述
*/
private String description;
/**
* 标题
*/
private String title;
/**
* 网址
*/
private String url;
/**
* 版本
*/
private String version;
/**
* 自定义参数配置
*/
@NestedConfigurationProperty
private List<ParameterConfig> parameterConfig;
/**
* 自定义参数配置
*/
@Data
public static class ParameterConfig {
/**
* 名称
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 参数类型
* header, cookie, body, query
*/
private String type = "head";
/**
* 数据类型
*/
private String dataType = "String";
/**
* 是否必填
*/
private boolean required;
/**
* 默认值
*/
private String defaultValue;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment