@@ -0,0 +1,33 @@ | |||
HELP.md | |||
target/ | |||
!.mvn/wrapper/maven-wrapper.jar | |||
!**/src/main/**/target/ | |||
!**/src/test/**/target/ | |||
### STS ### | |||
.apt_generated | |||
.classpath | |||
.factorypath | |||
.project | |||
.settings | |||
.springBeans | |||
.sts4-cache | |||
### IntelliJ IDEA ### | |||
.idea | |||
*.iws | |||
*.iml | |||
*.ipr | |||
### NetBeans ### | |||
/nbproject/private/ | |||
/nbbuild/ | |||
/dist/ | |||
/nbdist/ | |||
/.nb-gradle/ | |||
build/ | |||
!**/src/main/**/build/ | |||
!**/src/test/**/build/ | |||
### VS Code ### | |||
.vscode/ |
@@ -0,0 +1,3 @@ | |||
# tuoheng_freeway | |||
拓恒高速公路巡检管理平台 |
@@ -0,0 +1,108 @@ | |||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<packaging>pom</packaging> | |||
<modules> | |||
<module>tuoheng-common</module> | |||
<module>tuoheng-service</module> | |||
<module>tuoheng-feign</module> | |||
</modules> | |||
<parent> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-parent</artifactId> | |||
<version>2.7.1</version> | |||
<relativePath/> <!-- lookup parent from repository --> | |||
</parent> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng_freeway</artifactId> | |||
<version>1.0.0</version> | |||
<name>tuoheng_freeway</name> | |||
<description>tuoheng_freeway</description> | |||
<properties> | |||
<java.version>11</java.version> | |||
<spring-cloud.version>2021.0.3</spring-cloud.version> | |||
<tuoheng.version>1.0.0</tuoheng.version> | |||
<fastjson.version>1.2.76</fastjson.version> | |||
<poi.version>3.17</poi.version> | |||
<commons.io.version>2.5</commons.io.version> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-test</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<dependencyManagement> | |||
<dependencies> | |||
<!-- 核心模块依赖 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-common-core</artifactId> | |||
<version>${tuoheng.version}</version> | |||
</dependency> | |||
<!-- 安全认证依赖 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-common-security</artifactId> | |||
<version>${tuoheng.version}</version> | |||
</dependency> | |||
<!-- 系统服务API接口层模块依赖 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-system-feign</artifactId> | |||
<version>${tuoheng.version}</version> | |||
</dependency> | |||
<!-- freeway api fegin 接口模块依赖 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-api-feign</artifactId> | |||
<version>${tuoheng.version}</version> | |||
</dependency> | |||
<!-- SpringCloud依赖 --> | |||
<dependency> | |||
<groupId>org.springframework.cloud</groupId> | |||
<artifactId>spring-cloud-dependencies</artifactId> | |||
<version>${spring-cloud.version}</version> | |||
<type>pom</type> | |||
<scope>import</scope> | |||
</dependency> | |||
<!-- Excel工具 --> | |||
<dependency> | |||
<groupId>org.apache.poi</groupId> | |||
<artifactId>poi-ooxml</artifactId> | |||
<version>${poi.version}</version> | |||
</dependency> | |||
<!-- JSON 解析器和生成器 --> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>fastjson</artifactId> | |||
<version>${fastjson.version}</version> | |||
</dependency> | |||
<!-- IO常用工具类 --> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
<version>${commons.io.version}</version> | |||
</dependency> | |||
</dependencies> | |||
</dependencyManagement> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
<configuration> | |||
<source>${java.version}</source> | |||
<target>${java.version}</target> | |||
<encoding>${project.build.sourceEncoding}</encoding> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -0,0 +1,20 @@ | |||
<?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>tuoheng_freeway</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>1.0.0</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-common</artifactId> | |||
<packaging>pom</packaging> | |||
<version>1.0.0</version> | |||
<modules> | |||
<module>tuoheng-common-core</module> | |||
<module>tuoheng-common-security</module> | |||
</modules> | |||
</project> |
@@ -0,0 +1,133 @@ | |||
<?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>tuoheng-common</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>1.0.0</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-common-core</artifactId> | |||
<!-- 依赖库管理 --> | |||
<dependencies> | |||
<!-- <dependency>--> | |||
<!-- <groupId>org.redisson</groupId>--> | |||
<!-- <artifactId>redisson</artifactId>--> | |||
<!-- <version>3.17.5</version>--> | |||
<!-- </dependency>--> | |||
<dependency> | |||
<groupId>cn.hutool</groupId> | |||
<artifactId>hutool-log</artifactId> | |||
<version>5.8.3</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>cn.hutool</groupId> | |||
<artifactId>hutool-core</artifactId> | |||
<version>5.8.3</version> | |||
</dependency> | |||
<!--任务调度--> | |||
<dependency> | |||
<groupId>com.xuxueli</groupId> | |||
<artifactId>xxl-job-core</artifactId> | |||
<version>2.3.0</version> | |||
</dependency> | |||
<!--引入kafka依赖--> | |||
<dependency> | |||
<groupId>org.springframework.kafka</groupId> | |||
<artifactId>spring-kafka</artifactId> | |||
<version>2.8.3</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.cloud</groupId> | |||
<artifactId>spring-cloud-starter-openfeign</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>fastjson</artifactId> | |||
</dependency> | |||
<!--mybatis-plus 起始依赖 --> | |||
<dependency> | |||
<groupId>com.baomidou</groupId> | |||
<artifactId>mybatis-plus-boot-starter</artifactId> | |||
<version>3.2.0</version> | |||
</dependency> | |||
<!-- 引入阿里数据库连接池 --> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>druid-spring-boot-starter</artifactId> | |||
<version>1.2.11</version> | |||
</dependency> | |||
<!-- JSON工具类 --> | |||
<dependency> | |||
<groupId>com.fasterxml.jackson.core</groupId> | |||
<artifactId>jackson-databind</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-lang3</artifactId> | |||
<version>3.9</version> | |||
</dependency> | |||
<!-- Apache Commons Pool2 --> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-pool2</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
</dependency> | |||
<!-- Excel依赖 --> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>easyexcel</artifactId> | |||
<version>3.1.0</version> | |||
</dependency> | |||
<!-- Java Servlet --> | |||
<dependency> | |||
<groupId>javax.servlet</groupId> | |||
<artifactId>javax.servlet-api</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>jakarta.validation</groupId> | |||
<artifactId>jakarta.validation-api</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.hibernate</groupId> | |||
<artifactId>hibernate-validator</artifactId> | |||
<version>6.1.0.Final</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>javax.el</groupId> | |||
<artifactId>javax.el-api</artifactId> | |||
<version>3.0.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.glassfish.web</groupId> | |||
<artifactId>javax.el</artifactId> | |||
<version>2.2.4</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.httpcomponents</groupId> | |||
<artifactId>httpclient</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.data</groupId> | |||
<artifactId>spring-data-redis</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.security</groupId> | |||
<artifactId>spring-security-core</artifactId> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -0,0 +1,123 @@ | |||
package com.tuoheng.common.core.advice; | |||
import com.tuoheng.common.core.enums.SysExceptionEnum; | |||
import com.tuoheng.common.core.exception.ServiceException; | |||
import com.tuoheng.common.core.utils.JsonResult; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.dao.DataAccessException; | |||
import org.springframework.util.CollectionUtils; | |||
import org.springframework.validation.BindException; | |||
import org.springframework.validation.BindingResult; | |||
import org.springframework.validation.FieldError; | |||
import org.springframework.validation.ObjectError; | |||
import org.springframework.web.bind.MethodArgumentNotValidException; | |||
import org.springframework.web.bind.annotation.ExceptionHandler; | |||
import org.springframework.web.bind.annotation.ResponseBody; | |||
import org.springframework.web.bind.annotation.RestControllerAdvice; | |||
import javax.validation.ConstraintViolation; | |||
import javax.validation.ConstraintViolationException; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
/** | |||
* 自定义异常处理器 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
@RestControllerAdvice | |||
public class CustomExceptionHandler { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(CustomExceptionHandler.class); | |||
/** | |||
* 密码校验异常 | |||
*/ | |||
@ExceptionHandler(BindException.class) | |||
@ResponseBody | |||
public JsonResult<Object> handleAuthenticationException(BindException e) { | |||
BindingResult bindingResult = e.getBindingResult(); | |||
String errorMessage = ""; | |||
for (FieldError fieldError : bindingResult.getFieldErrors()) { | |||
errorMessage += fieldError.getDefaultMessage() + "!"; | |||
} | |||
return JsonResult.error(SysExceptionEnum.PARAMETER_EMPTY_EXCEPTION.getCode(), errorMessage); | |||
} | |||
/** | |||
* 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是ConstraintViolationException | |||
*/ | |||
@ExceptionHandler(ConstraintViolationException.class) | |||
@ResponseBody | |||
public JsonResult<Object> handleConstraintViolationException(ConstraintViolationException e) { | |||
String message = e.getConstraintViolations() | |||
.stream() | |||
.map(constraintViolation -> { | |||
return constraintViolation.getPropertyPath().toString() + ": " + constraintViolation.getMessage(); | |||
}) | |||
.collect(Collectors.joining(",")); | |||
LOGGER.error("参数格式错误:{}", message); | |||
return JsonResult.error(SysExceptionEnum.PARAMETER_EMPTY_EXCEPTION.getCode(), SysExceptionEnum.PARAMETER_EMPTY_EXCEPTION.getMsg()); | |||
} | |||
/** | |||
* 校验异常处理 | |||
* 处理请求参数格式错误 @RequestBody上使用@Valid 实体上使用@NotNull等 | |||
*/ | |||
@ExceptionHandler(MethodArgumentNotValidException.class) | |||
@ResponseBody | |||
public JsonResult<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { | |||
LOGGER.warn("系统参数校验异常,异常信息:{}", e.getMessage()); | |||
JsonResult<Object> error = JsonResult.error(SysExceptionEnum.PARAMETER_EMPTY_EXCEPTION.getCode(), | |||
SysExceptionEnum.PARAMETER_EMPTY_EXCEPTION.getMsg()); | |||
BindingResult result = e.getBindingResult(); | |||
List<FieldError> fieldErrors = result.getFieldErrors(); | |||
if (!CollectionUtils.isEmpty(fieldErrors)) { | |||
error.setMsg(fieldErrors.stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(","))); | |||
} else { | |||
if (!CollectionUtils.isEmpty(result.getAllErrors())) { | |||
ObjectError currError = result.getAllErrors().get(0); | |||
error.setMsg(currError.getObjectName() + currError.getDefaultMessage()); | |||
} | |||
} | |||
return error; | |||
} | |||
/** | |||
* 系统业务异常处理 | |||
*/ | |||
@ExceptionHandler(ServiceException.class) | |||
@ResponseBody | |||
public JsonResult<Object> handleServiceException(ServiceException e) { | |||
// 打印业务异常日志 | |||
LOGGER.warn("系统业务逻辑异常,异常状态码 {},异常信息:{}", e.code, e.getMessage()); | |||
return JsonResult.error(e.code, e.getMessage()); | |||
} | |||
/** | |||
* 系统数据访问异常处理 | |||
*/ | |||
@ExceptionHandler(DataAccessException.class) | |||
@ResponseBody | |||
public JsonResult<Object> handleDataAccessException(DataAccessException e) { | |||
LOGGER.error("系统数据访问异常,异常信息:{}", e); | |||
return JsonResult.error(SysExceptionEnum.DATAACCESS_EXCEPTION.getCode(), SysExceptionEnum.DATAACCESS_EXCEPTION.getMsg()); | |||
} | |||
/** | |||
* 系统异常处理 | |||
*/ | |||
@ExceptionHandler(Exception.class) | |||
@ResponseBody | |||
public JsonResult<Object> handleException(Exception e) { | |||
System.out.println(e.getSuppressed()); | |||
System.out.println(e.getCause().getMessage()); | |||
System.out.println(e.getCause().getCause()); | |||
LOGGER.error("系统异常,异常信息:{}", e); | |||
return JsonResult.error(SysExceptionEnum.SYS_EXCEPTION.getCode(), SysExceptionEnum.SYS_EXCEPTION.getMsg()); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
//package com.tuoheng.common.core.annotation; | |||
// | |||
//import com.tuoheng.common.core.enums.LockType; | |||
//import org.springframework.core.annotation.AliasFor; | |||
// | |||
//import java.lang.annotation.*; | |||
//import java.util.concurrent.TimeUnit; | |||
// | |||
///** | |||
// * @author chenyukun | |||
// */ | |||
//@Target({ElementType.METHOD}) | |||
//@Retention(RetentionPolicy.RUNTIME) | |||
//@Inherited | |||
//public @interface LockAction { | |||
// | |||
// /** 锁的资源,key。支持spring El表达式*/ | |||
// @AliasFor("key") | |||
// String value() default "'default'"; | |||
// | |||
// @AliasFor("value") | |||
// String key() default "'default'"; | |||
// | |||
// String prefix() default "lock-"; | |||
// | |||
// /** 锁类型*/ | |||
// LockType lockType() default LockType.REENTRANT_LOCK; | |||
// | |||
// | |||
// /** 获取锁等待时间,默认3秒*/ | |||
// long waitTime() default 3000L; | |||
// | |||
// /** 锁自动释放时间,默认30秒*/ | |||
// long leaseTime() default 30000L; | |||
// | |||
// /** 时间单位(获取锁等待时间和持锁时间都用此单位)*/ | |||
// TimeUnit unit() default TimeUnit.MILLISECONDS; | |||
//} |
@@ -0,0 +1,36 @@ | |||
package com.tuoheng.common.core.annotation; | |||
import com.tuoheng.common.core.enums.LogType; | |||
import com.tuoheng.common.core.enums.OperType; | |||
import java.lang.annotation.*; | |||
/** | |||
* 自定义操作日志注解 | |||
*/ | |||
@Target({ElementType.PARAMETER, ElementType.METHOD}) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
public @interface Log { | |||
/** | |||
* 模块 | |||
*/ | |||
String title() default ""; | |||
/** | |||
* 日志类型 | |||
*/ | |||
LogType logType() default LogType.OTHER; | |||
/** | |||
* 操作人类别 | |||
*/ | |||
OperType operType() default OperType.MANAGE; | |||
/** | |||
* 是否保存请求的参数 | |||
*/ | |||
boolean isSaveRequestData() default true; | |||
} |
@@ -0,0 +1,8 @@ | |||
package com.tuoheng.common.core.common; | |||
/** | |||
* 基类控制器 | |||
*/ | |||
public class BaseController { | |||
} |
@@ -0,0 +1,58 @@ | |||
package com.tuoheng.common.core.common; | |||
import com.baomidou.mybatisplus.annotation.IdType; | |||
import com.baomidou.mybatisplus.annotation.TableId; | |||
import com.fasterxml.jackson.annotation.JsonFormat; | |||
import lombok.Data; | |||
import org.springframework.format.annotation.DateTimeFormat; | |||
import java.io.Serializable; | |||
import java.util.Date; | |||
/** | |||
* 基类实体对象 | |||
* | |||
* @author qiujinyang | |||
* @date 2022/7/1 | |||
*/ | |||
@Data | |||
public class BaseEntity implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* 主键ID | |||
*/ | |||
@TableId(value = "id", type = IdType.UUID) | |||
private String id; | |||
/** | |||
* 添加人 | |||
*/ | |||
private String createUser; | |||
/** | |||
* 创建时间 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
private Date createTime; | |||
/** | |||
* 更新人 | |||
*/ | |||
private String updateUser; | |||
/** | |||
* 更新时间 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
private Date updateTime; | |||
/** | |||
* 有效标识 | |||
*/ | |||
private Integer mark; | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.tuoheng.common.core.common; | |||
import lombok.Data; | |||
/** | |||
* 查询对象基类 | |||
*/ | |||
@Data | |||
public class BaseQuery { | |||
/** | |||
* 页码(默认1) | |||
*/ | |||
private Integer page = 1; | |||
/** | |||
* 每页数(默认:20) | |||
*/ | |||
private Integer limit = 20; | |||
} |
@@ -0,0 +1,210 @@ | |||
package com.tuoheng.common.core.common; | |||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; | |||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||
import com.tuoheng.common.core.utils.DateUtils; | |||
import com.tuoheng.common.core.utils.JsonResult; | |||
import com.tuoheng.common.core.utils.StringUtils; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements IBaseService<T> { | |||
/** | |||
* 根据查询条件获取数据列表 | |||
* | |||
* @param page 分页 | |||
* @param query 查询条件 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult getList(Page<T> page, BaseQuery query) { | |||
return null; | |||
} | |||
/** | |||
* 获取数据列表 | |||
* | |||
* @param query 查询条件 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult getList(BaseQuery query) { | |||
return null; | |||
} | |||
/** | |||
* 根据实体ID获取实体信息 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult info(String id) { | |||
if (StringUtils.isEmpty(id)) { | |||
return JsonResult.error("记录ID不能为空"); | |||
} | |||
Object result = this.getInfo(id); | |||
return JsonResult.success(result, "操作成功"); | |||
} | |||
/** | |||
* 根据ID获取记录信息 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
@Override | |||
public Object getInfo(Serializable id) { | |||
T entity = this.getById(id); | |||
return entity; | |||
} | |||
/** | |||
* 传入实体对象添加记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult add(T entity) { | |||
// entity.setCreateUser(SecurityUtils.getUserId()); | |||
entity.setCreateTime(DateUtils.now()); | |||
entity.setMark(1); | |||
boolean result = this.save(entity); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success(); | |||
} | |||
/** | |||
* 传入实体对象更新记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult update(T entity) { | |||
// entity.setUpdateUser(SecurityUtils.getUserId()); | |||
entity.setUpdateTime(DateUtils.now()); | |||
boolean result = this.updateById(entity); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success(); | |||
} | |||
/** | |||
* 根据实体对象添加、编辑记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult edit(T entity) { | |||
if (entity == null) { | |||
return JsonResult.error("实体对象不存在"); | |||
} | |||
if (entity.getId() != null && StringUtils.isNotEmpty(entity.getId())) { | |||
// 修改记录 | |||
return this.update(entity); | |||
} else { | |||
// 新增记录 | |||
return this.add(entity); | |||
} | |||
} | |||
/** | |||
* 删除记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult delete(T entity) { | |||
// entity.setUpdateUser(SecurityUtils.getUserId()); | |||
entity.setUpdateTime(DateUtils.now()); | |||
entity.setMark(0); | |||
boolean result = this.updateById(entity); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success("删除成功"); | |||
} | |||
/** | |||
* 根据ID删除记录 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult deleteById(String id) { | |||
// return JsonResult.error("禁止删除,请联系管理员"); | |||
if (id == null || StringUtils.isEmpty(id)) { | |||
return JsonResult.error("记录ID不能为空"); | |||
} | |||
// 设置Mark=0 | |||
UpdateWrapper updateWrapper = new UpdateWrapper(); | |||
updateWrapper.set("mark", 0); | |||
updateWrapper.eq("id", id); | |||
boolean result = update(updateWrapper); | |||
if (!result) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success("删除成功"); | |||
} | |||
/** | |||
* 根据ID删除记录 | |||
* | |||
* @param ids 记录ID | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult deleteByIds(String[] ids) { | |||
if (StringUtils.isNull(ids)) { | |||
return JsonResult.error("记录ID不能为空"); | |||
} | |||
// 设置Mark=0 | |||
Integer totalNum = 0; | |||
for (String id : ids) { | |||
UpdateWrapper updateWrapper = new UpdateWrapper(); | |||
updateWrapper.set("mark", 0); | |||
updateWrapper.eq("id", id); | |||
boolean result = update(updateWrapper); | |||
if (result) { | |||
totalNum++; | |||
} | |||
} | |||
if (totalNum != ids.length) { | |||
return JsonResult.error(); | |||
} | |||
return JsonResult.success("删除成功"); | |||
} | |||
/** | |||
* 设置状态 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
@Override | |||
public JsonResult setStatus(T entity) { | |||
return this.update(entity); | |||
} | |||
/** | |||
* 导出Excel | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public List<T> exportExcel() { | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.tuoheng.common.core.common; | |||
/** | |||
* 枚举类 封装 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
public interface ExceptionInterface { | |||
/** | |||
* 获取错误码 | |||
* | |||
* @return | |||
*/ | |||
int getCode(); | |||
/** | |||
* 获取异常信息 | |||
* | |||
* @return | |||
*/ | |||
String getMessage(); | |||
} |
@@ -0,0 +1,109 @@ | |||
package com.tuoheng.common.core.common; | |||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |||
import com.baomidou.mybatisplus.extension.service.IService; | |||
import com.tuoheng.common.core.utils.JsonResult; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
public interface IBaseService<T> extends IService<T> { | |||
/** | |||
* 根据查询条件获取数据列表 | |||
* | |||
* @param page 分页 | |||
* @param query 查询条件 | |||
* @return | |||
*/ | |||
JsonResult getList(Page<T> page, BaseQuery query); | |||
/** | |||
* 获取数据列表 | |||
* | |||
* @param query 查询条件 | |||
* @return | |||
*/ | |||
JsonResult getList(BaseQuery query); | |||
/** | |||
* 根据ID获取记录信息 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
JsonResult info(String id); | |||
/** | |||
* 根据ID获取记录信息 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
Object getInfo(Serializable id); | |||
/** | |||
* 根据实体对象添加记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult add(T entity); | |||
/** | |||
* 根据实体对象更新记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult update(T entity); | |||
/** | |||
* 根据实体对象添加、编辑记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult edit(T entity); | |||
/** | |||
* 删除记录 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult delete(T entity); | |||
/** | |||
* 根据ID删除记录 | |||
* | |||
* @param id 记录ID | |||
* @return | |||
*/ | |||
JsonResult deleteById(String id); | |||
/** | |||
* 根据ID删除记录 | |||
* | |||
* @param ids 记录ID | |||
* @return | |||
*/ | |||
JsonResult deleteByIds(String[] ids); | |||
/** | |||
* 设置状态 | |||
* | |||
* @param entity 实体对象 | |||
* @return | |||
*/ | |||
JsonResult setStatus(T entity); | |||
/** | |||
* 导出Excel | |||
* | |||
* @return | |||
*/ | |||
List<T> exportExcel(); | |||
} |
@@ -0,0 +1,27 @@ | |||
package com.tuoheng.common.core.config; | |||
import org.hibernate.validator.HibernateValidator; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import javax.validation.Validation; | |||
import javax.validation.Validator; | |||
import javax.validation.ValidatorFactory; | |||
/** | |||
* 验证配置类 | |||
*/ | |||
@Configuration | |||
public class ValidatorConfig { | |||
@Bean | |||
public ValidatorFactory validatorFactory() { | |||
return Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory(); | |||
} | |||
@Bean | |||
public Validator validator(ValidatorFactory validatorFactory) { | |||
return validatorFactory.getValidator(); | |||
} | |||
} |
@@ -0,0 +1,85 @@ | |||
package com.tuoheng.common.core.config.common; | |||
import lombok.Data; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Configuration; | |||
@Configuration | |||
@Data | |||
public class CommonConfig { | |||
/** | |||
* 图片域名 | |||
*/ | |||
public static String imageURL; | |||
/** | |||
* OSS域名 | |||
*/ | |||
public static String ossURL; | |||
/** | |||
* 视频域名 | |||
*/ | |||
public static String videoURL; | |||
/** | |||
* 高德KEY | |||
*/ | |||
public static String gaodeKey; | |||
/** | |||
* 通道地址 | |||
*/ | |||
public static String channelUrl; | |||
/** | |||
* 图片域名赋值 | |||
* | |||
* @param url 域名地址 | |||
*/ | |||
@Value("${tuoheng.image-url}") | |||
public void setImageURL(String url) { | |||
imageURL = url; | |||
} | |||
/** | |||
* 阿里云OSS域名 | |||
* | |||
* @param url 图片地址 | |||
*/ | |||
@Value("${tuoheng.oss-url:}") | |||
public void setOssURL(String url) { | |||
ossURL = url; | |||
} | |||
/** | |||
* 视频域名赋值 | |||
* | |||
* @param url 域名地址 | |||
*/ | |||
@Value("${tuoheng.video-url:}") | |||
public void setVideoURL(String url) { | |||
videoURL = url; | |||
} | |||
/** | |||
* 高德KEY赋值 | |||
* | |||
* @param key 高德KEY | |||
*/ | |||
@Value("${tuoheng.gaodeKey:}") | |||
public void setGaodeKey(String key) { | |||
gaodeKey = key; | |||
} | |||
/** | |||
* 通道域名赋值 | |||
* | |||
* @param url 通道地址 | |||
*/ | |||
@Value("${tuoheng.live-channel-domain-url:}") | |||
public void setChannelUrl(String url) { | |||
channelUrl = url; | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
package com.tuoheng.common.core.config.http; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.http.HttpRequest; | |||
import org.springframework.http.client.ClientHttpRequestExecution; | |||
import org.springframework.http.client.ClientHttpRequestInterceptor; | |||
import org.springframework.http.client.ClientHttpResponse; | |||
import java.io.IOException; | |||
import java.io.UnsupportedEncodingException; | |||
/** | |||
* 默认拦截器 | |||
*/ | |||
public class DefaultClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClientHttpRequestInterceptor.class); | |||
@Override | |||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { | |||
trackRequest(request,body); | |||
ClientHttpResponse httpResponse = execution.execute(request, body); | |||
trackResponse(httpResponse); | |||
return httpResponse; | |||
} | |||
private void trackResponse(ClientHttpResponse httpResponse)throws IOException { | |||
LOGGER.info("============================response begin=========================================="); | |||
LOGGER.info("Status code : {}", httpResponse.getStatusCode()); | |||
LOGGER.info("Status text : {}", httpResponse.getStatusText()); | |||
LOGGER.info("Headers : {}", httpResponse.getHeaders()); | |||
LOGGER.info("=======================response end================================================="); | |||
} | |||
private void trackRequest(HttpRequest request, byte[] body)throws UnsupportedEncodingException { | |||
LOGGER.info("======= request begin ========"); | |||
LOGGER.info("uri : {}", request.getURI()); | |||
LOGGER.info("method : {}", request.getMethod()); | |||
LOGGER.info("headers : {}", request.getHeaders()); | |||
LOGGER.info("request body : {}", new String(body, "UTF-8")); | |||
LOGGER.info("======= request end ========"); | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
package com.tuoheng.common.core.config.http; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.http.HttpHeaders; | |||
import org.springframework.http.HttpRequest; | |||
import org.springframework.http.client.ClientHttpRequestExecution; | |||
import org.springframework.http.client.ClientHttpRequestInterceptor; | |||
import org.springframework.http.client.ClientHttpResponse; | |||
import java.io.IOException; | |||
/** | |||
* 默认拦截器 | |||
*/ | |||
public class HeadClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(HeadClientHttpRequestInterceptor.class); | |||
@Override | |||
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { | |||
LOGGER.info("#####head handle########"); | |||
HttpHeaders headers = httpRequest.getHeaders(); | |||
headers.add("Accept", "application/json"); | |||
headers.add("Accept-Encoding", "gzip"); | |||
headers.add("Content-Encoding", "UTF-8"); | |||
headers.add("Content-Type", "application/json; charset=UTF-8"); | |||
ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes); | |||
HttpHeaders headersResponse = response.getHeaders(); | |||
headersResponse.add("Accept", "application/json"); | |||
return response; | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
package com.tuoheng.common.core.config.http; | |||
import lombok.Getter; | |||
import lombok.Setter; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Configuration; | |||
@Configuration | |||
@Getter | |||
@Setter | |||
public class RestProperties { | |||
public final static String HTTP = "http"; | |||
public final static String HTTPS = "https"; | |||
/** | |||
* 连接超时, 默认10秒 | |||
*/ | |||
@Value("${spring.http.restTemplate.connect.timeout: 10000}") | |||
private Integer connectTimeout; | |||
/** | |||
* 响应超时, 默认2分钟 | |||
*/ | |||
@Value("${spring.http.restTemplate.read.timeout: 120000}") | |||
private Integer readTimeout; | |||
/** | |||
* 请求超时时间, 默认10秒 | |||
*/ | |||
@Value("${spring.http.restTemplate.connection.request.timeout: 10000}") | |||
private Integer connectionRequestTimeout; | |||
/** | |||
* 请求失败重试次数, 默认重试3次 | |||
*/ | |||
@Value("${spring.http.restTemplate.retryCount: 3}") | |||
private Integer retryCount; | |||
/** | |||
* 请求失败重试开关,默认开启 | |||
*/ | |||
@Value("${spring.http.restTemplate.requestSentRetryEnabled: true}") | |||
private Boolean requestSentRetryEnabled; | |||
/** | |||
* 线程池最大连接数,默认1000 | |||
*/ | |||
@Value("${spring.http.restTemplate.pool.maxTotal: 1000}") | |||
private Integer maxTotal; | |||
/** | |||
* 线程池主机最大并发数,默认100 | |||
*/ | |||
@Value("${spring.http.restTemplate.pool.maxPerRoute: 100}") | |||
private Integer maxPerRoute; | |||
/** | |||
* 线程池空闲连接过期时间,默认60秒 | |||
*/ | |||
@Value("${spring.http.restTemplate.pool.validateAfterInactivity: 60000}") | |||
private Integer validateAfterInactivity; | |||
} |
@@ -0,0 +1,177 @@ | |||
package com.tuoheng.common.core.config.http; | |||
import org.apache.http.HttpEntityEnclosingRequest; | |||
import org.apache.http.HttpRequest; | |||
import org.apache.http.NoHttpResponseException; | |||
import org.apache.http.client.HttpRequestRetryHandler; | |||
import org.apache.http.client.protocol.HttpClientContext; | |||
import org.apache.http.config.Registry; | |||
import org.apache.http.config.RegistryBuilder; | |||
import org.apache.http.conn.ConnectTimeoutException; | |||
import org.apache.http.conn.HttpClientConnectionManager; | |||
import org.apache.http.conn.socket.ConnectionSocketFactory; | |||
import org.apache.http.conn.socket.PlainConnectionSocketFactory; | |||
import org.apache.http.conn.ssl.NoopHostnameVerifier; | |||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||
import org.apache.http.impl.client.*; | |||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; | |||
import org.apache.http.protocol.HttpContext; | |||
import org.apache.http.ssl.SSLContextBuilder; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.http.client.ClientHttpRequestFactory; | |||
import org.springframework.http.client.ClientHttpRequestInterceptor; | |||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; | |||
import org.springframework.http.converter.HttpMessageConverter; | |||
import org.springframework.http.converter.StringHttpMessageConverter; | |||
import org.springframework.web.client.RestTemplate; | |||
import javax.net.ssl.SSLContext; | |||
import javax.net.ssl.SSLException; | |||
import javax.net.ssl.SSLHandshakeException; | |||
import java.io.IOException; | |||
import java.io.InterruptedIOException; | |||
import java.net.UnknownHostException; | |||
import java.nio.charset.StandardCharsets; | |||
import java.security.GeneralSecurityException; | |||
import java.security.cert.X509Certificate; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
@Configuration | |||
public class RestTemplateConfig { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateConfig.class); | |||
@Bean | |||
public RestTemplate restTemplate(RestProperties restProperties) { | |||
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory(restProperties)); | |||
// 获取restTemplate中的转换器集合 | |||
List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters(); | |||
// 遍历转换器集合,找到对应的StringHttpMessageConverter转换器 | |||
for (HttpMessageConverter<?> converter : converterList) { | |||
if(converter.getClass() == StringHttpMessageConverter.class){ | |||
converterList.remove(converter); | |||
break; | |||
} | |||
} | |||
// 添加新的StringHttpMessageConverter转换器,并设置字符集为UTF-8 | |||
converterList.add(1, new StringHttpMessageConverter(StandardCharsets.UTF_8)); | |||
// 添加拦截器 | |||
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); | |||
// interceptors.add(new DefaultClientHttpRequestInterceptor()); | |||
interceptors.add(new HeadClientHttpRequestInterceptor()); | |||
restTemplate.setInterceptors(interceptors); | |||
return restTemplate; | |||
} | |||
private ClientHttpRequestFactory clientHttpRequestFactory(RestProperties restProperties) { | |||
// httpClientBuilder配置构架器 | |||
HttpClientBuilder httpClientBuilder = HttpClients.custom(); | |||
// 设置重试次数,此处注意,如果使用无参构造,重试次数为3。this(3, false); | |||
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(restProperties.getRetryCount(), restProperties.getRequestSentRetryEnabled())); | |||
// 设置保持长连接 | |||
httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()); | |||
// 使用连接池 | |||
httpClientBuilder.setConnectionManager(poolingConnectionManager(restProperties)); | |||
// 获取httpClient | |||
CloseableHttpClient httpClient = httpClientBuilder.build(); | |||
// 配置HttpClient的对应工厂HttpComponentsClientHttpRequestFactory | |||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); | |||
factory.setConnectTimeout(restProperties.getConnectTimeout()); | |||
factory.setReadTimeout(restProperties.getReadTimeout()); | |||
factory.setConnectionRequestTimeout(restProperties.getConnectionRequestTimeout()); | |||
return factory; | |||
} | |||
private HttpClientConnectionManager poolingConnectionManager(RestProperties restProperties) { | |||
// 注册http和https请求 | |||
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() | |||
.register(RestProperties.HTTP, PlainConnectionSocketFactory.getSocketFactory()) | |||
.register(RestProperties.HTTPS, createSSLConn()) | |||
.build(); | |||
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(registry); | |||
// 最大连接数 | |||
poolingConnectionManager.setMaxTotal(restProperties.getMaxTotal()); | |||
// 每个主机的并发 | |||
poolingConnectionManager.setDefaultMaxPerRoute(restProperties.getMaxPerRoute()); | |||
// 空闲连接过期时间 | |||
poolingConnectionManager.setValidateAfterInactivity(restProperties.getValidateAfterInactivity()); | |||
return poolingConnectionManager; | |||
} | |||
private SSLConnectionSocketFactory createSSLConn() { | |||
SSLConnectionSocketFactory sslsf = null; | |||
try | |||
{ | |||
SSLContext sslContext = new SSLContextBuilder() | |||
// 不检查证书 | |||
.loadTrustMaterial(null, (X509Certificate[] chain, String authType) -> true) | |||
.build(); | |||
// 不检查hostname | |||
sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); | |||
} catch (GeneralSecurityException e){ | |||
LOGGER.error("restTemplate开启SSL校验失败", e); | |||
} | |||
return sslsf; | |||
} | |||
/** | |||
* 自定义的重试策略,需要的时候使用 | |||
*/ | |||
private void customHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled){ | |||
// 请求失败时,进行请求重试 | |||
HttpRequestRetryHandler handler = new HttpRequestRetryHandler() { | |||
@Override | |||
public boolean retryRequest(IOException e, int currentRetryCount, HttpContext httpContext) { | |||
if(!requestSentRetryEnabled){ | |||
return false; | |||
} | |||
if (currentRetryCount > retryCount){ | |||
// 重试超过3次,放弃请求 | |||
LOGGER.error("retry has more than 3 time, give up request"); | |||
return false; | |||
} | |||
if (e instanceof NoHttpResponseException){ | |||
// 服务器没有响应,可能是服务器断开了连接,应该重试 | |||
LOGGER.error("receive no response from server, retry"); | |||
return true; | |||
} | |||
if (e instanceof SSLHandshakeException){ | |||
// SSL握手异常 | |||
LOGGER.error("SSL hand shake exception"); | |||
return false; | |||
} | |||
if (e instanceof InterruptedIOException){ | |||
// 超时 | |||
LOGGER.error("InterruptedIOException"); | |||
return false; | |||
} | |||
if (e instanceof UnknownHostException){ | |||
// 服务器不可达 | |||
LOGGER.error("server host unknown"); | |||
return false; | |||
} | |||
if (e instanceof ConnectTimeoutException){ | |||
// 连接超时 | |||
LOGGER.error("Connection Time out"); | |||
return false; | |||
} | |||
if (e instanceof SSLException){ | |||
LOGGER.error("SSLException"); | |||
return false; | |||
} | |||
HttpClientContext context = HttpClientContext.adapt(httpContext); | |||
HttpRequest request = context.getRequest(); | |||
if (!(request instanceof HttpEntityEnclosingRequest)){ | |||
// 如果请求不是关闭连接的请求 | |||
return true; | |||
} | |||
return false; | |||
} | |||
}; | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
//package com.tuoheng.common.core.config.redisson; | |||
// | |||
//import com.tuoheng.common.core.annotation.LockAction; | |||
//import org.aspectj.lang.ProceedingJoinPoint; | |||
//import org.aspectj.lang.annotation.Around; | |||
//import org.aspectj.lang.annotation.Aspect; | |||
//import org.aspectj.lang.annotation.Pointcut; | |||
//import org.aspectj.lang.reflect.MethodSignature; | |||
//import org.redisson.api.RLock; | |||
//import org.redisson.api.RedissonClient; | |||
//import org.slf4j.Logger; | |||
//import org.slf4j.LoggerFactory; | |||
//import org.springframework.beans.factory.annotation.Autowired; | |||
//import org.springframework.beans.factory.annotation.Qualifier; | |||
//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |||
//import org.springframework.context.annotation.Configuration; | |||
//import org.springframework.core.LocalVariableTableParameterNameDiscoverer; | |||
//import org.springframework.expression.EvaluationContext; | |||
//import org.springframework.expression.ExpressionParser; | |||
//import org.springframework.expression.spel.standard.SpelExpressionParser; | |||
//import org.springframework.expression.spel.support.StandardEvaluationContext; | |||
//import org.springframework.util.StringUtils; | |||
// | |||
//import java.lang.reflect.Method; | |||
//import java.util.Locale; | |||
// | |||
///** | |||
// * @author chenyukun | |||
// */ | |||
//@Aspect | |||
//@Configuration | |||
//@ConditionalOnProperty(name = "spring.redis.consumer.enable", havingValue = "true") | |||
//public class RedissonDistributedLockAspectConfiguration { | |||
// | |||
// private final Logger logger = LoggerFactory.getLogger(RedissonDistributedLockAspectConfiguration.class); | |||
// | |||
// @Autowired | |||
// @Qualifier("redissonClient") | |||
// private RedissonClient redissonClient; | |||
// | |||
// private final ExpressionParser parser = new SpelExpressionParser(); | |||
// | |||
// private final LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); | |||
// | |||
// @Pointcut("@annotation(com.tuoheng.common.core.annotation.LockAction)") | |||
// private void lockPoint(){ | |||
// } | |||
// | |||
// @Around("lockPoint()") | |||
// public Object around(ProceedingJoinPoint pjp) throws Throwable{ | |||
// Method method = ((MethodSignature) pjp.getSignature()).getMethod(); | |||
// LockAction lockAction = method.getAnnotation(LockAction.class); | |||
// String key = lockAction.value(); | |||
// String prefix = lockAction.prefix(); | |||
// Object[] args = pjp.getArgs(); | |||
// key = parse(key, method, args); | |||
// if(!StringUtils.hasLength(key) || !StringUtils.hasLength(prefix)){ | |||
// logger.error("get key or prefix failed. key is null or prefix is null"); | |||
// return null; | |||
// } | |||
// RLock lock = getLock(String.format(Locale.ENGLISH,"%s%s", prefix, key), lockAction); | |||
// if(!lock.tryLock(lockAction.waitTime(), lockAction.leaseTime(), lockAction.unit())) { | |||
// logger.debug("get lock failed [{}]", key); | |||
// return null; | |||
// } | |||
// | |||
// //得到锁,执行方法,释放锁 | |||
// logger.debug("get lock success [{}]", key); | |||
// try { | |||
// return pjp.proceed(); | |||
// } finally { | |||
// lock.unlock(); | |||
// logger.debug("release lock [{}]", key); | |||
// } | |||
// } | |||
// | |||
// /** | |||
// * 解析spring EL表达式 | |||
// * @param key 表达式 | |||
// * @param method 方法 | |||
// * @param args 方法参数 | |||
// * @return string | |||
// */ | |||
// private String parse(String key, Method method, Object[] args) { | |||
// String[] params = discoverer.getParameterNames(method); | |||
// if(params == null || params.length == 0){ | |||
// return null; | |||
// } | |||
// EvaluationContext context = new StandardEvaluationContext(); | |||
// for (int i = 0; i < params.length; i ++) { | |||
// context.setVariable(params[i], args[i]); | |||
// } | |||
// return parser.parseExpression(key).getValue(context, String.class); | |||
// } | |||
// | |||
// private RLock getLock(String key, LockAction lockAction) { | |||
// switch (lockAction.lockType()) { | |||
// case REENTRANT_LOCK: | |||
// return redissonClient.getLock(key); | |||
// | |||
// case FAIR_LOCK: | |||
// return redissonClient.getFairLock(key); | |||
// | |||
// case READ_LOCK: | |||
// return redissonClient.getReadWriteLock(key).readLock(); | |||
// | |||
// case WRITE_LOCK: | |||
// return redissonClient.getReadWriteLock(key).writeLock(); | |||
// default: | |||
// throw new RuntimeException("do not support lock type:" + lockAction.lockType().name()); | |||
// } | |||
// } | |||
//} |
@@ -0,0 +1,86 @@ | |||
package com.tuoheng.common.core.config.xxl; | |||
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
/** | |||
* xxl-job config | |||
* | |||
* @author xuxueli 2017-04-28 | |||
*/ | |||
@Configuration | |||
@ConditionalOnProperty(name = XxlJobConfig.XXL_ENABLE, havingValue = XxlJobConfig.TRUE) | |||
public class XxlJobConfig { | |||
public static final String XXL_ENABLE = "xxl.enable"; | |||
public static final String TRUE = "true"; | |||
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); | |||
@Value("${xxl.job.admin.addresses}") | |||
private String adminAddresses; | |||
@Value("${xxl.job.accessToken}") | |||
private String accessToken; | |||
@Value("${xxl.job.executor.appname}") | |||
private String appname; | |||
@Value("${xxl.job.executor.address}") | |||
private String address; | |||
@Value("${xxl.job.executor.ip}") | |||
private String ip; | |||
@Value("${xxl.job.executor.port}") | |||
private int port; | |||
@Value("${xxl.job.executor.logpath}") | |||
private String logPath; | |||
@Value("${xxl.job.executor.logretentiondays}") | |||
private int logRetentionDays; | |||
@Bean | |||
public XxlJobSpringExecutor xxlJobExecutor() throws InterruptedException { | |||
logger.info(">>>>>>>>>>> xxl-job config init."); | |||
Thread.sleep(5000); | |||
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); | |||
xxlJobSpringExecutor.setAdminAddresses(adminAddresses); | |||
xxlJobSpringExecutor.setAppname(appname); | |||
xxlJobSpringExecutor.setAddress(address); | |||
xxlJobSpringExecutor.setIp(ip); | |||
xxlJobSpringExecutor.setPort(port); | |||
xxlJobSpringExecutor.setAccessToken(accessToken); | |||
xxlJobSpringExecutor.setLogPath(logPath); | |||
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); | |||
return xxlJobSpringExecutor; | |||
} | |||
/** | |||
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP; | |||
* | |||
* 1、引入依赖: | |||
* <dependency> | |||
* <groupId>org.springframework.cloud</groupId> | |||
* <artifactId>spring-cloud-commons</artifactId> | |||
* <version>${version}</version> | |||
* </dependency> | |||
* | |||
* 2、配置文件,或者容器启动变量 | |||
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' | |||
* | |||
* 3、获取IP | |||
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); | |||
*/ | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.tuoheng.common.core.constant; | |||
/** | |||
* @Author: 吴彬 | |||
* @CreateTime: 2022-09-19 08:57 | |||
* @Description: 服务间请求头身份信息配置常量 | |||
* @Version: 1.0 | |||
*/ | |||
public class SecurityHeadConstant { | |||
/** | |||
* 用户信息加密串 | |||
*/ | |||
public static final String JSON_TOKEN = "json-token"; | |||
} |
@@ -0,0 +1,183 @@ | |||
package com.tuoheng.common.core.entity; | |||
import com.baomidou.mybatisplus.annotation.IdType; | |||
import com.baomidou.mybatisplus.annotation.TableField; | |||
import com.baomidou.mybatisplus.annotation.TableId; | |||
import com.baomidou.mybatisplus.annotation.TableName; | |||
import com.fasterxml.jackson.annotation.JsonFormat; | |||
import com.tuoheng.common.core.common.BaseEntity; | |||
import lombok.Data; | |||
import org.hibernate.validator.constraints.Length; | |||
import org.springframework.format.annotation.DateTimeFormat; | |||
import javax.validation.constraints.NotNull; | |||
import java.util.Date; | |||
/** | |||
* <p> | |||
* 后台用户管理表 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2022-06-21 | |||
*/ | |||
@Data | |||
@TableName("th_oauth_user") | |||
public class UserEntity extends BaseEntity { | |||
/** | |||
* 用户编号 | |||
*/ | |||
private String code; | |||
/** | |||
* 真实姓名 | |||
*/ | |||
private String realname; | |||
/** | |||
* 昵称 | |||
*/ | |||
private String nickname; | |||
/** | |||
* 性别:1男 2女 3保密 | |||
*/ | |||
private Integer gender; | |||
/** | |||
* 头像 | |||
*/ | |||
private String avatar; | |||
/** | |||
* 手机号码 | |||
*/ | |||
private String mobile; | |||
/** | |||
* 邮箱地址 | |||
*/ | |||
private String email; | |||
/** | |||
* 出生日期 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd") | |||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") | |||
private Date birthday; | |||
/** | |||
* 部门ID | |||
*/ | |||
private String deptId; | |||
/** | |||
* 省份编码 | |||
*/ | |||
private String provinceCode; | |||
/** | |||
* 城市编码 | |||
*/ | |||
private String cityCode; | |||
/** | |||
* 区县编码 | |||
*/ | |||
private String districtCode; | |||
/** | |||
* 街道编码 | |||
*/ | |||
private String streetCode; | |||
/** | |||
* 详细地址 | |||
*/ | |||
private String address; | |||
/** | |||
* 所属城市 | |||
*/ | |||
private String cityName; | |||
/** | |||
* 登录用户名 | |||
*/ | |||
private String username; | |||
/** | |||
* 登录密码 | |||
*/ | |||
private String password; | |||
/** | |||
* 用户类型:1管理员 | |||
*/ | |||
private Integer type; | |||
/** | |||
* 驾照类型:1飞行执照 2飞行许可证 | |||
*/ | |||
private Integer driverType; | |||
/** | |||
* 驾照编号 | |||
*/ | |||
private String driverCode; | |||
/** | |||
* 盐加密 | |||
*/ | |||
private String salt; | |||
/** | |||
* 个人简介 | |||
*/ | |||
private String intro; | |||
/** | |||
* 状态:1正常 2禁用 | |||
*/ | |||
private Integer status; | |||
/** | |||
* 备注 | |||
*/ | |||
private String note; | |||
/** | |||
* 显示顺序 | |||
*/ | |||
private Integer sort; | |||
/** | |||
* 登录次数 | |||
*/ | |||
private Integer loginNum; | |||
/** | |||
* 最近登录IP | |||
*/ | |||
private String loginIp; | |||
/** | |||
* 最近登录时间 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
private Date loginTime; | |||
/** | |||
* 角色ID | |||
*/ | |||
@TableField(exist = false) | |||
private String[] roleIds; | |||
/** | |||
* 城市集合 | |||
*/ | |||
@TableField(exist = false) | |||
private String[] city; | |||
} |
@@ -0,0 +1,20 @@ | |||
package com.tuoheng.common.core.enums; | |||
/** | |||
* @author chenyukun | |||
*/ | |||
public enum LockType { | |||
/** 可重入锁*/ | |||
REENTRANT_LOCK, | |||
/** 公平锁*/ | |||
FAIR_LOCK, | |||
/** 读锁*/ | |||
READ_LOCK, | |||
/** 写锁*/ | |||
WRITE_LOCK; | |||
} |
@@ -0,0 +1,68 @@ | |||
package com.tuoheng.common.core.enums; | |||
/** | |||
* 日志类型 | |||
*/ | |||
public enum LogType { | |||
/** | |||
* 其它 | |||
*/ | |||
OTHER, | |||
/** | |||
* 新增 | |||
*/ | |||
INSERT, | |||
/** | |||
* 修改 | |||
*/ | |||
UPDATE, | |||
/** | |||
* 删除 | |||
*/ | |||
DELETE, | |||
/** | |||
* 授权 | |||
*/ | |||
GRANT, | |||
/** | |||
* 导出 | |||
*/ | |||
EXPORT, | |||
/** | |||
* 导入 | |||
*/ | |||
IMPORT, | |||
/** | |||
* 强退 | |||
*/ | |||
FORCE, | |||
/** | |||
* 生成代码 | |||
*/ | |||
GENCODE, | |||
/** | |||
* 清空数据 | |||
*/ | |||
CLEAN, | |||
/** | |||
* 状态 | |||
*/ | |||
STATUS, | |||
/** | |||
* 重置密码 | |||
*/ | |||
RESETPWD, | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.tuoheng.common.core.enums; | |||
/** | |||
* 操作类型 | |||
*/ | |||
public enum OperType { | |||
/** | |||
* 其它 | |||
*/ | |||
OTHER, | |||
/** | |||
* 后台用户 | |||
*/ | |||
MANAGE, | |||
/** | |||
* 手机端用户 | |||
*/ | |||
MOBILE | |||
} |
@@ -0,0 +1,27 @@ | |||
package com.tuoheng.common.core.enums; | |||
import lombok.Getter; | |||
/** | |||
* 摄影方式枚举类 | |||
* | |||
* @Author xiaoying | |||
* @Date 2022/10/11 17:37 | |||
*/ | |||
public enum PhotographyEnum { | |||
INSPECTION(1, "普通巡检"), | |||
ORTHOIMAGE(2, "正射影像"), | |||
INCLINEDIMAGE(3, "倾斜摄影"); | |||
PhotographyEnum(int code, String msg) { | |||
this.code = code; | |||
this.msg = msg; | |||
} | |||
@Getter | |||
private final int code; | |||
@Getter | |||
private final String msg; | |||
} |
@@ -0,0 +1,110 @@ | |||
package com.tuoheng.common.core.enums; | |||
import com.tuoheng.common.core.exception.ExceptionInterface; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
/** | |||
* 业务异常 枚举 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
@AllArgsConstructor | |||
public enum ServiceExceptionEnum implements ExceptionInterface { | |||
/** | |||
* 未查询到数据 | |||
*/ | |||
GET_NO_DATA(10001, "未查到该记录!"), | |||
/** | |||
* 参数为空 | |||
*/ | |||
PARAMETER_IS_NULL(10002, "参数为空!"), | |||
/** | |||
* 获取用户ID失败 | |||
*/ | |||
GET_NO_USER_ID(10003, "获取用户ID失败!"), | |||
/** | |||
* websocket连接异常! | |||
*/ | |||
WEB_SOCKET_CONNECTION_ERROR(10004, "websocket连接异常!"), | |||
/** | |||
* 文件不能为空! | |||
*/ | |||
FILE_IS_EMPTY(10005, "上传文件不能为空!"), | |||
/** | |||
* 上传文件格式错误! | |||
*/ | |||
FILE_FORMAT_IS_ERROR(10006, "上传文件格式错误!"), | |||
/** | |||
* 获取租户信息失败! | |||
*/ | |||
GET_NO_TENANT(10007, "获取租户信息失败!"), | |||
/** | |||
* 上传文件数据错误,请检查文件! | |||
*/ | |||
FILE_DATA_IS_ERROR(10101, "上传文件数据错误,请检查文件!"), | |||
/** | |||
* 上传文件数据错误,请检查文件! | |||
*/ | |||
FILE_DATA_TIME_IS_ERROR(10102, "上传文件数据错误,请检查文件!"), | |||
/** | |||
* 上传文件数据错误,请检查文件! | |||
*/ | |||
FILE_DATA_LNG_IS_ERROR(10103, "上传文件数据错误,请检查文件!"), | |||
/** | |||
* 上传文件数据错误,请检查文件! | |||
*/ | |||
FILE_DATA_LAT_IS_ERROR(10104, "上传文件数据错误,请检查文件!"), | |||
/** | |||
* 读取图片经纬度失败,请手动填写! | |||
*/ | |||
IMAGE_LOCATION_IS_ERROR(11001, "读取图片经纬度失败,请手动填写!"), | |||
/** | |||
* 任务不存在! | |||
*/ | |||
TASK_DOES_NOT_EXIST(11111, "巡检任务不存在,请先创建巡检任务!"), | |||
/** | |||
* 视频分析中,请稍后再试 | |||
*/ | |||
VIDEO_ANALYSIS_IN_PROGRESS(11112, "视频分析中,请稍后再试!"), | |||
/** | |||
* 只有飞行完成状态可以操作 | |||
*/ | |||
TASK_NOT_OPERATION(11113, "只有飞行完成状态可以操作!"), | |||
/** | |||
* 没有这个飞行状态 | |||
*/ | |||
TASK_NOT_STATUS(11114, "飞行状态出错!"), | |||
/** | |||
* 巡检时间已过期 | |||
*/ | |||
INSPECTION_TIME_EXPIRE(11115, "巡检时间已过期,无法审核通过!"), | |||
; | |||
@Getter | |||
private final int code; | |||
@Getter | |||
private final String msg; | |||
} |
@@ -0,0 +1,97 @@ | |||
package com.tuoheng.common.core.enums; | |||
import com.tuoheng.common.core.exception.ExceptionInterface; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Getter; | |||
/** | |||
* 系统异常 枚举 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
@AllArgsConstructor | |||
public enum SysExceptionEnum implements ExceptionInterface { | |||
/* ===========================User====================================== */ | |||
/** | |||
* 数据校验异常 | |||
*/ | |||
PARAMETER_EMPTY_EXCEPTION(10000, "非法参数!"), | |||
/** | |||
* 登录用户名不能为空 | |||
*/ | |||
ACCOUNT_IS_EMPTY(10001, "登录用户名不能为空!"), | |||
/** | |||
* 用户名不存在 | |||
*/ | |||
PASSWORD_IS_EMPTY(10002, "登录密码不能为空!"), | |||
/** | |||
* 用户信息不存在 | |||
*/ | |||
ACCOUNT_NOT_EXIST(10003, "用户信息不存在!"), | |||
/** | |||
* 用户名或密码错误 | |||
*/ | |||
ACCOUNT_OR_PASSWORD_ERROR(10004, "用户名或密码错误,请重新输入!"), | |||
/** | |||
* token为空,鉴权失败 | |||
*/ | |||
TOKEN_IS_EMPTY(10005, "token不能为空!"), | |||
/** | |||
* token已失效 | |||
*/ | |||
TOKEN_EXPIRED_EXCEPTION(10006,"token 已失效!"), | |||
/** | |||
* token 验证异常 | |||
*/ | |||
JWT_VERIFICATION_EXCEPTION(10007,"token 验证异常!"), | |||
/** | |||
* 无权访问 | |||
*/ | |||
PERMISSION_NOT(10008, "无访问权限"), | |||
/** | |||
* 系统发生异常 | |||
*/ | |||
SYS_EXCEPTION(10009, "系统发生异常!"), | |||
/** | |||
* 系统数据访问异常 | |||
*/ | |||
DATAACCESS_EXCEPTION(100010, "系统数据访问异常!"), | |||
/** | |||
* 创建线程失败异常 | |||
*/ | |||
INCREMENT_LESS_THAN_ZERO(10011, "递增因子小于0!"), | |||
/** | |||
* 文件导出关闭流异常 | |||
*/ | |||
EXPORT_EXCEPTION(10012, "文件导出关闭流异常!"), | |||
/** | |||
* 文件读取异常 | |||
*/ | |||
EXPORT_READ_EXCEPTION(10013, "文件读取异常!"), | |||
/** | |||
* 账号已被禁用 | |||
*/ | |||
ACCOUNT_IS_DISABLE(123,"您的账号已被禁用,请联系管理员"), | |||
; | |||
@Getter | |||
private final int code; | |||
@Getter | |||
private final String msg; | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.tuoheng.common.core.exception; | |||
/** | |||
* 枚举类 封装 | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
public interface ExceptionInterface { | |||
/** | |||
* 获取错误码 | |||
* | |||
* @return | |||
*/ | |||
int getCode(); | |||
/** | |||
* 获取异常信息 | |||
* | |||
* @return | |||
*/ | |||
String getMsg(); | |||
} |
@@ -0,0 +1,59 @@ | |||
package com.tuoheng.common.core.exception; | |||
import com.tuoheng.common.core.utils.JsonResult; | |||
/** | |||
* 业务异常类(业务处理时手动抛出异常) | |||
* | |||
* @author zhu_zishuang | |||
* @date 2021-03-12 | |||
*/ | |||
public class ServiceException extends RuntimeException { | |||
/** | |||
* 异常码 | |||
*/ | |||
public int code; | |||
/** | |||
* 异常描述,兼容JsonResult | |||
*/ | |||
private String msg; | |||
/** | |||
* 构造器 | |||
* | |||
* @param exceptionInfo | |||
*/ | |||
public ServiceException(ExceptionInterface exceptionInfo) { | |||
super(exceptionInfo.getMsg()); | |||
this.msg = exceptionInfo.getMsg(); | |||
this.code = exceptionInfo.getCode(); | |||
} | |||
/** | |||
* 构造器 | |||
* | |||
* @param code | |||
* @param msg | |||
*/ | |||
public ServiceException(int code, String msg) { | |||
super(msg); | |||
this.msg = msg; | |||
this.code = code; | |||
} | |||
/** | |||
* 构造器 | |||
* | |||
* @param msg | |||
*/ | |||
public ServiceException(String msg) { | |||
super(msg); | |||
this.msg = msg; | |||
this.code = JsonResult.ERROR; | |||
} | |||
} | |||
@@ -0,0 +1,26 @@ | |||
//package com.tuoheng.common.core.interceptor; | |||
// | |||
//import com.tuoheng.common.core.constant.SecurityHeadConstant; | |||
//import feign.RequestInterceptor; | |||
//import feign.RequestTemplate; | |||
//import org.springframework.context.annotation.Configuration; | |||
//import org.springframework.web.context.request.RequestContextHolder; | |||
//import org.springframework.web.context.request.ServletRequestAttributes; | |||
// | |||
//import javax.servlet.http.HttpServletRequest; | |||
// | |||
///** | |||
// * @Author: 吴彬 | |||
// * @CreateTime: 2022-09-19 08:57 | |||
// * @Description: gateway feign请求全局添加用户加密json-token | |||
// * @Version: 1.0 | |||
// */ | |||
//@Configuration | |||
//public class FeignRequestInterceptor implements RequestInterceptor { | |||
// | |||
// @Override | |||
// public void apply(RequestTemplate requestTemplate) { | |||
// HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); | |||
// requestTemplate.header(SecurityHeadConstant.JSON_TOKEN, request.getHeader(SecurityHeadConstant.JSON_TOKEN)); | |||
// } | |||
//} |
@@ -0,0 +1,21 @@ | |||
package com.tuoheng.common.core.model.dto; | |||
import lombok.Data; | |||
import lombok.experimental.Accessors; | |||
/** | |||
* @author chenjiandong | |||
* @description: TODO | |||
* @date 2022/10/27 11:34 | |||
*/ | |||
@Data | |||
@Accessors(chain = true) | |||
public class LoginUser { | |||
private String username; | |||
private Long userId; | |||
private String thToken; | |||
} |
@@ -0,0 +1,322 @@ | |||
package com.tuoheng.common.core.utils; | |||
import cn.hutool.core.convert.Convert; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.tuoheng.common.core.config.common.CommonConfig; | |||
import org.springframework.util.CollectionUtils; | |||
import java.lang.reflect.Field; | |||
import java.security.MessageDigest; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.function.Consumer; | |||
import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
import java.util.stream.Collectors; | |||
/** | |||
* 公共函数类 | |||
*/ | |||
public class CommonUtils { | |||
private static final Pattern VOD_URL_PATTERN = Pattern.compile("^((https|http|ftp|rtsp|mms|rtmp|artc)://)" | |||
+ "(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" | |||
+ "(([0-9]{1,3}\\.){3}[0-9]{1,3}" | |||
+ "|" | |||
+ "([0-9a-z_!~*'()-]+\\.)*" | |||
+ "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\." | |||
+ "[a-z]{2,6})" | |||
+ "(:[0-9]{1,5})?" | |||
+ "((/?)|" | |||
+ "(/[0-9a-z_!~*'(){}.;?:@&=+$,%#-]+)+/?)$"); | |||
/** | |||
* 获取到图片域名的地址 | |||
* | |||
* @param imageUrl | |||
* @return | |||
*/ | |||
public static String getImageURL(String imageUrl) { | |||
return CommonConfig.imageURL + imageUrl; | |||
} | |||
/** | |||
* 正则匹配富文本图片 | |||
* | |||
* @param htmlStr 富文本内容 | |||
* @return | |||
*/ | |||
public static List<String> getImgStr(String htmlStr) { | |||
Pattern p_image = Pattern.compile("<img.*src\\s*=\\s*(.*?)[^>]*?>", Pattern.CASE_INSENSITIVE); | |||
Pattern r_image = Pattern.compile("src\\s*=\\s*\"?(.*?)(\"|>|\\s+)"); | |||
List<String> list = new ArrayList<>(); | |||
Matcher m_image = p_image.matcher(htmlStr); | |||
while (m_image.find()) { | |||
// 得到<img />数据 | |||
String img = m_image.group(); | |||
System.out.println(img); | |||
// 匹配<img>中的src数据 | |||
Matcher m = r_image.matcher(img); | |||
while (m.find()) { | |||
list.add(m.group(1)); | |||
} | |||
} | |||
return list; | |||
} | |||
/** | |||
* 验证邮箱是否正确 | |||
* | |||
* @param email | |||
* @return | |||
*/ | |||
public static boolean isEmail(String email) { | |||
boolean flag = false; | |||
try { | |||
String check = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; | |||
Pattern regex = Pattern.compile(check); | |||
Matcher matcher = regex.matcher(email); | |||
flag = matcher.matches(); | |||
} catch (Exception e) { | |||
flag = false; | |||
} | |||
return flag; | |||
} | |||
/** | |||
* 验证手机号是否正确 | |||
* | |||
* @param mobile | |||
* @return | |||
*/ | |||
public static boolean isMobile(String mobile) { | |||
boolean flag = false; | |||
try { | |||
Pattern p = Pattern.compile("^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$"); | |||
Matcher m = p.matcher(mobile); | |||
flag = m.matches(); | |||
} catch (Exception e) { | |||
flag = false; | |||
} | |||
return flag; | |||
} | |||
/** | |||
* 生成指定位数的随机字符串 | |||
* | |||
* @param isNum 是否是纯数字 | |||
* @param length 长度 | |||
* @return | |||
*/ | |||
public static String getRandomStr(boolean isNum, int length) { | |||
String resultStr = ""; | |||
String str = isNum ? "1234567890" : "1234567890abcdefghijkmnpqrstuvwxyz"; | |||
int len = str.length(); | |||
boolean isStop = true; | |||
do { | |||
resultStr = ""; | |||
int count = 0; | |||
for (int i = 0; i < length; i++) { | |||
double dblR = Math.random() * len; | |||
int intR = (int) Math.floor(dblR); | |||
char c = str.charAt(intR); | |||
if (('0' <= c) && (c <= '9')) { | |||
count++; | |||
} | |||
resultStr += str.charAt(intR); | |||
} | |||
if (count >= 2) { | |||
isStop = false; | |||
} | |||
} while (isStop); | |||
return resultStr; | |||
} | |||
/** | |||
* 判断是否在数组中 | |||
* | |||
* @param s | |||
* @param array | |||
* @return | |||
*/ | |||
public static boolean inArray(final String s, final String[] array) { | |||
for (String item : array) { | |||
if (item != null && item.equals(s)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* 从html中提取纯文本 | |||
* | |||
* @param strHtml | |||
* @return | |||
*/ | |||
public static String stripHtml(String strHtml) { | |||
String content = strHtml.replaceAll("</?[^>]+>", ""); //剔出<html>的标签 | |||
content = content.replaceAll("\\s*|\t|\r|\n", "");//去除字符串中的空格,回车,换行符,制表符 | |||
return content; | |||
} | |||
/** | |||
* 去除字符串中的空格、回车、换行符、制表符等 | |||
* | |||
* @param str 原始字符串 | |||
* @return | |||
*/ | |||
public static String replaceSpecialStr(String str) { | |||
String repl = ""; | |||
if (str != null) { | |||
Pattern p = Pattern.compile("\\s*|\t|\r|\n"); | |||
Matcher m = p.matcher(str); | |||
repl = m.replaceAll(""); | |||
} | |||
return repl; | |||
} | |||
/** | |||
* 判断某个元素是否在数组中 | |||
* | |||
* @param key 元素 | |||
* @param map 数组 | |||
* @return | |||
*/ | |||
public static boolean inArray(String key, Map<String, String> map) { | |||
boolean flag = false; | |||
for (String k : map.keySet()) { | |||
if (k.equals(key)) { | |||
flag = true; | |||
} | |||
} | |||
return flag; | |||
} | |||
/** | |||
* 对象转Map | |||
* | |||
* @param obj 对象 | |||
* @return | |||
* @throws IllegalAccessException | |||
*/ | |||
public static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException { | |||
Map<String, Object> map = new HashMap<>(); | |||
Class<?> clazz = obj.getClass(); | |||
for (Field field : clazz.getDeclaredFields()) { | |||
field.setAccessible(true); | |||
String fieldName = field.getName(); | |||
Object value = field.get(obj); | |||
map.put(fieldName, value); | |||
} | |||
return map; | |||
} | |||
/** | |||
* 判断是否是JSON格式 | |||
* | |||
* @param str JSON字符串 | |||
* @return | |||
*/ | |||
private boolean isJson(String str) { | |||
try { | |||
JSONObject jsonStr = JSONObject.parseObject(str); | |||
return true; | |||
} catch (Exception e) { | |||
return false; | |||
} | |||
} | |||
/** | |||
* MD5方法 | |||
* | |||
* @param source | |||
* @return | |||
*/ | |||
public static String md5(byte[] source) { | |||
try { | |||
MessageDigest md = MessageDigest.getInstance("MD5"); | |||
md.update(source); | |||
StringBuffer buf = new StringBuffer(); | |||
for (byte b : md.digest()) { | |||
buf.append(String.format("%02x", b & 0xff)); | |||
} | |||
return buf.toString(); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
return null; | |||
} | |||
} | |||
/** | |||
* 密码加密 | |||
* | |||
* @param password 密码 | |||
* @return | |||
*/ | |||
public static String password(String password) { | |||
String md51 = md5(password.getBytes()); | |||
String pwd = md5((md51 + "IgtUdEQJyVevaCxQnY").getBytes()); | |||
return pwd; | |||
} | |||
/** | |||
* 对数组进行分组 | |||
* | |||
* @param list 数据源 | |||
* @param size 每组几个 | |||
* @param <T> | |||
* @return | |||
*/ | |||
public static <T> List<List<T>> split(List<T> list, Integer size) { | |||
if (CollectionUtils.isEmpty(list)) { | |||
return new ArrayList<>(); | |||
} | |||
Integer count = list.size() / size; | |||
List<List<T>> arrayList = new ArrayList<>(); | |||
for (int i = 0; i < count; i++) { | |||
List<T> temp = list.subList(i * size, (i + 1) * size); | |||
arrayList.add(temp); | |||
} | |||
Integer extra = list.size() % size; | |||
if (extra != 0) { | |||
List<T> temp = list.subList(count * size, count * size + extra); | |||
arrayList.add(temp); | |||
} | |||
return arrayList; | |||
} | |||
/** | |||
* 批量处理方法 | |||
* | |||
* @param operater 回调方法,有入参无返回值 | |||
* @param sourceList 批量处理list对象 | |||
* @param threshold 阀值,比如有5000个对象,阀值设置1000,就是1000执行一次 | |||
* @param <T> 对象类型 | |||
*/ | |||
public static <T> void batchOperate(Consumer<List<T>> operater, List<T> sourceList, Integer threshold) { | |||
int size = sourceList.size(); | |||
int fromIndex = 0; | |||
List<T> list = null; | |||
while (size > fromIndex){ | |||
list = sourceList.stream().skip(fromIndex).limit(threshold).collect(Collectors.toList()); | |||
operater.accept(list); | |||
fromIndex += threshold; | |||
} | |||
} | |||
/** | |||
* 验证URL是否合法 | |||
* | |||
* @param str 字符串url | |||
* @return 布尔值 | |||
*/ | |||
public static boolean isURL(String str){ | |||
str = str.toLowerCase(); | |||
Matcher matcher = VOD_URL_PATTERN.matcher(str); | |||
return matcher.find(); | |||
} | |||
} |
@@ -0,0 +1,237 @@ | |||
package com.tuoheng.common.core.utils; | |||
import org.apache.commons.lang3.time.DateFormatUtils; | |||
import java.lang.management.ManagementFactory; | |||
import java.text.ParseException; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
/** | |||
* 时间工具类 | |||
*/ | |||
public final class DateUtils extends org.apache.commons.lang3.time.DateUtils { | |||
public static String YYYY = "yyyy"; | |||
public static String YYYY_MM = "yyyy-MM"; | |||
public static String YYYY_MM_DD = "yyyy-MM-dd"; | |||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; | |||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; | |||
private static String[] parsePatterns = { | |||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", | |||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", | |||
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; | |||
/** | |||
* 获取当前时间 | |||
* | |||
* @return | |||
*/ | |||
public static Date now() { | |||
return new Date(); | |||
} | |||
/** | |||
* 获取当前日期, 默认格式为yyyy-MM-dd | |||
* | |||
* @return String | |||
*/ | |||
public static String getDate() { | |||
return dateTimeNow(YYYY_MM_DD); | |||
} | |||
public static final String getTime() { | |||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS); | |||
} | |||
public static final String dateTimeNow() { | |||
return dateTimeNow(YYYYMMDDHHMMSS); | |||
} | |||
public static final String dateTimeNow(final String format) { | |||
return parseDateToStr(format, new Date()); | |||
} | |||
public static final String dateTime(final Date date) { | |||
return parseDateToStr(YYYY_MM_DD, date); | |||
} | |||
public static final String parseDateToStr(final String format, final Date date) { | |||
return new SimpleDateFormat(format).format(date); | |||
} | |||
public static final Date dateTime(final String format, final String ts) { | |||
try { | |||
return new SimpleDateFormat(format).parse(ts); | |||
} catch (ParseException e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
/** | |||
* 检查字符串是否使时间格式 | |||
* @param format 格式 | |||
* @param ts 时间字符串 | |||
* @return 布尔值 | |||
*/ | |||
public static boolean checkStrIsDate(final String format, final String ts) { | |||
try { | |||
new SimpleDateFormat(format).parse(ts); | |||
return true; | |||
} catch (ParseException e) { | |||
return false; | |||
} | |||
} | |||
/** | |||
* 日期路径 即年/月/日 如2018/08/08 | |||
*/ | |||
public static final String datePath() { | |||
Date now = new Date(); | |||
return DateFormatUtils.format(now, "yyyy/MM/dd"); | |||
} | |||
/** | |||
* 日期路径 即年/月/日 如20180808 | |||
*/ | |||
public static final String dateTime() { | |||
Date now = new Date(); | |||
return DateFormatUtils.format(now, "yyyyMMdd"); | |||
} | |||
/** | |||
* 日期型字符串转化为日期 格式 | |||
*/ | |||
public static Date parseDate(Object str) { | |||
if (str == null) { | |||
return null; | |||
} | |||
try { | |||
return parseDate(str.toString(), parsePatterns); | |||
} catch (ParseException e) { | |||
return null; | |||
} | |||
} | |||
/** | |||
* 获取服务器启动时间 | |||
*/ | |||
public static Date getServerStartDate() { | |||
long time = ManagementFactory.getRuntimeMXBean().getStartTime(); | |||
return new Date(time); | |||
} | |||
/** | |||
* 计算两个时间差 | |||
*/ | |||
public static String getDatePoor(Date endDate, Date nowDate) { | |||
long nd = 1000 * 24 * 60 * 60; | |||
long nh = 1000 * 60 * 60; | |||
long nm = 1000 * 60; | |||
// long ns = 1000; | |||
// 获得两个时间的毫秒时间差异 | |||
long diff = endDate.getTime() - nowDate.getTime(); | |||
// 计算差多少天 | |||
long day = diff / nd; | |||
// 计算差多少小时 | |||
long hour = diff % nd / nh; | |||
// 计算差多少分钟 | |||
long min = diff % nd % nh / nm; | |||
// 计算差多少秒//输出结果 | |||
// long sec = diff % nd % nh % nm / ns; | |||
return day + "天" + hour + "小时" + min + "分钟"; | |||
} | |||
/** | |||
* 根据时间生成code | |||
*/ | |||
public static String generateCode(String prefix){ | |||
// 根据当前时间,生成任务编码 | |||
Date date = new Date(); | |||
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss"); | |||
return prefix + formatter.format(date); | |||
} | |||
/** | |||
* 生成图片编号,P+年月日时分秒+随机三位数 | |||
* @return | |||
*/ | |||
public static String generateFileCode(){ | |||
String Date = DateUtils.dateTimeNow(DateUtils.YYYYMMDDHHMMSS); | |||
int randomNum = (int)(Math.random() * 900) + 100; | |||
String fileCode = "P" + Date + randomNum; | |||
return fileCode; | |||
} | |||
/** | |||
* 返回字符串 | |||
* | |||
* 给原本的时间originDate加上自定义的时间 | |||
* @param originDate 原本的时间 | |||
* @param day 要加的天数 | |||
* @param hour 要加的小时数 | |||
* @param minute 要加的分钟数 | |||
* @param second 要加的秒数 | |||
* @return 返回加完时间后的时间str_goalDate | |||
*/ | |||
public static String addDateTimeToStr(Date originDate, int day, int hour, int minute,int second) { | |||
SimpleDateFormat dateFormate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | |||
Calendar cal = Calendar.getInstance(); | |||
cal.setTime(originDate); | |||
cal.add(Calendar.DATE, day);// 24小时制,加天 | |||
cal.add(Calendar.HOUR, hour);// 24小时制 ,加小时 | |||
cal.add(Calendar.MINUTE, minute);// 24小时制,加分钟 | |||
cal.add(Calendar.SECOND, second);// 24小时制,加秒 | |||
String str_goalDate = dateFormate.format(cal.getTime()); | |||
return str_goalDate; | |||
} | |||
/** | |||
* 返回java.util.Date | |||
* | |||
* 给原本的时间originDate加上自定义的时间 | |||
* @param originDate 原本的时间 | |||
* @param day 要加的天数 | |||
* @param hour 要加的小时数 | |||
* @param minute 要加的分钟数 | |||
* @param second 要加的秒数 | |||
* @return 返回加完时间后的时间goalDate | |||
*/ | |||
public static Date addDateTimeToDate(Date originDate, int day, int hour, int minute,int second) { | |||
Calendar cal = Calendar.getInstance(); | |||
cal.setTime(originDate); | |||
cal.add(Calendar.DATE, day);// 24小时制,加天 | |||
cal.add(Calendar.HOUR, hour);// 24小时制 ,加小时 | |||
cal.add(Calendar.MINUTE, minute);// 24小时制,加分钟 | |||
cal.add(Calendar.SECOND, second);// 24小时制,加秒 | |||
Date goalDate = cal.getTime(); | |||
return goalDate; | |||
} | |||
public static void main(String[] args) { | |||
// 当前时间 | |||
Date date = new Date(System.currentTimeMillis()); | |||
System.out.println("当前时间 = " + date); | |||
// 想加2小时5分10秒,且返回字符串 | |||
String strDate = addDateTimeToStr(date, 0, 2, 5, 10); | |||
System.out.println("转换后的时间 = " + strDate); | |||
System.out.println("-------------"); | |||
System.out.println("当前时间=" + date); | |||
// 想加3小时2分5秒,且返回Date | |||
Date goalDate = addDateTimeToDate(date, 0, 3, 2, 5); | |||
System.out.println("转换后的时间 = " + goalDate); | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
package com.tuoheng.common.core.utils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import java.io.UnsupportedEncodingException; | |||
import java.net.URLDecoder; | |||
import java.net.URLEncoder; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Base64; | |||
/** | |||
* 加密工具类 | |||
* @Author: rosh | |||
*/ | |||
public final class EncryptUtil { | |||
private EncryptUtil() { | |||
} | |||
private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class); | |||
public static String encodeBase64(byte[] bytes) { | |||
return Base64.getEncoder().encodeToString(bytes); | |||
} | |||
public static byte[] decodeBase64(String str) { | |||
return Base64.getDecoder().decode(str); | |||
} | |||
public static String encodeUTF8StringBase64(String str) { | |||
return Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8)); | |||
} | |||
public static String decodeUTF8StringBase64(String str) { | |||
byte[] bytes = Base64.getDecoder().decode(str); | |||
return new String(bytes, StandardCharsets.UTF_8); | |||
} | |||
public static String encodeURL(String url) { | |||
String encoded = null; | |||
try { | |||
encoded = URLEncoder.encode(url, String.valueOf(StandardCharsets.UTF_8)); | |||
} catch (UnsupportedEncodingException e) { | |||
logger.warn("URLEncode失败", e); | |||
} | |||
return encoded; | |||
} | |||
public static String decodeURL(String url) { | |||
String decoded = null; | |||
try { | |||
decoded = URLDecoder.decode(url, String.valueOf(StandardCharsets.UTF_8)); | |||
} catch (UnsupportedEncodingException e) { | |||
logger.warn("URLDecode失败", e); | |||
} | |||
return decoded; | |||
} | |||
} |
@@ -0,0 +1,163 @@ | |||
package com.tuoheng.common.core.utils; | |||
import cn.hutool.core.util.ObjectUtil; | |||
import com.tuoheng.common.core.exception.ExceptionInterface; | |||
import com.tuoheng.common.core.exception.ServiceException; | |||
import java.util.Optional; | |||
/** | |||
* 异常工具 | |||
* @author chenyukun | |||
*/ | |||
public class ExceptionUtil { | |||
private ExceptionUtil(){} | |||
/** | |||
* 抛服务异常 | |||
* | |||
* @param code 异常编码 | |||
* @param exceptionMsg 异常信息 | |||
*/ | |||
public static void throwServiceExcetion(Integer code, String exceptionMsg){ | |||
throw new ServiceException(code, exceptionMsg); | |||
} | |||
/** | |||
* 抛服务异常 | |||
* | |||
* @param code 异常编码 | |||
* @param exceptionMsg 异常信息 | |||
* @param logMsg 日志消息 | |||
* @param logObj 日志消息占位对象 | |||
*/ | |||
public static void throwServiceExcetion(Integer code, String exceptionMsg, String logMsg, String ...logObj){ | |||
if(StringUtils.isNotEmpty(logObj)){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x, logObj)); | |||
} | |||
if(StringUtils.isEmpty(logObj)){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x)); | |||
} | |||
throw new ServiceException(code, exceptionMsg); | |||
} | |||
/** | |||
* 抛服务异常 | |||
* | |||
* @param code 异常编码 | |||
* @param exceptionMsg 异常信息 | |||
* @param exception 异常 | |||
* @param logMsg 日志消息 | |||
* @param logObj 日志消息占位对象 | |||
*/ | |||
public static void throwServiceExcetion(Integer code, String exceptionMsg, Exception exception, String logMsg, | |||
String ...logObj){ | |||
if(StringUtils.isNotEmpty(logObj) && exception != null){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x, logObj, exception)); | |||
} | |||
if(StringUtils.isNotEmpty(logObj) && exception == null){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x, logObj)); | |||
} | |||
if(StringUtils.isEmpty(logObj) && exception != null){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x, exception)); | |||
} | |||
if(StringUtils.isEmpty(logObj) && exception == null){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x)); | |||
} | |||
throw new ServiceException(code, exceptionMsg); | |||
} | |||
/** | |||
* 抛服务异常 | |||
* | |||
* @param exceptionInterface 异常接口 | |||
*/ | |||
public static void throwServiceExcetion(ExceptionInterface exceptionInterface){ | |||
throw new ServiceException(exceptionInterface); | |||
} | |||
/** | |||
* 抛服务异常 | |||
* | |||
* @param exceptionInterface 异常接口 | |||
* @param logMsg 日志消息 | |||
* @param logObj 日志对象 | |||
*/ | |||
public static void throwServiceExcetion(ExceptionInterface exceptionInterface, String logMsg, String ...logObj){ | |||
if(StringUtils.isNotEmpty(logObj)){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x, logObj)); | |||
} | |||
if(StringUtils.isEmpty(logObj)){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x)); | |||
} | |||
throw new ServiceException(exceptionInterface); | |||
} | |||
/** | |||
* 抛服务异常 | |||
* | |||
* @param exceptionInterface 异常接口 | |||
* @param exception 异常 | |||
* @param logMsg 日志消息 | |||
* @param logObj 日志对象 | |||
*/ | |||
public static void throwServiceExcetion(ExceptionInterface exceptionInterface, Exception exception, String logMsg, | |||
String ...logObj){ | |||
if(StringUtils.isNotEmpty(logObj) && exception != null){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x, logObj, exception)); | |||
} | |||
if(StringUtils.isNotEmpty(logObj) && exception == null){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x, logObj)); | |||
} | |||
if(StringUtils.isEmpty(logObj) && exception != null){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x, exception)); | |||
} | |||
if(StringUtils.isEmpty(logObj) && exception == null){ | |||
Optional.ofNullable(logMsg).ifPresent(x -> LogUtil.LOGGER.error(x)); | |||
} | |||
throw new ServiceException(exceptionInterface); | |||
} | |||
/** | |||
* 校验参数是否为空,为空抛异常 | |||
* | |||
* @param obj 参数对象 | |||
* @param exceptionInterface 异常接口 | |||
*/ | |||
public static void paramIsNull(Object obj, ExceptionInterface exceptionInterface){ | |||
if(ObjectUtil.isEmpty(obj)){ | |||
throwServiceExcetion(exceptionInterface); | |||
} | |||
} | |||
/** | |||
* 校验参数是否为空,为空抛异常 | |||
* | |||
* @param obj 参数对象 | |||
* @param exceptionInterface 异常接口 | |||
* @param logMsg 日志消息 | |||
* @param logObj 日志对象 | |||
*/ | |||
public static void paramIsNull(Object obj, ExceptionInterface exceptionInterface, String logMsg, String ...logObj){ | |||
if(ObjectUtil.isEmpty(obj)){ | |||
throwServiceExcetion(exceptionInterface, logMsg, logObj); | |||
} | |||
} | |||
/** | |||
* 校验参数是否为空,为空抛异常 | |||
* | |||
* @param obj 参数对象 | |||
* @param exceptionInterface 异常接口 | |||
* @param exception 异常 | |||
* @param logMsg 日志消息 | |||
* @param logObj 日志对象 | |||
*/ | |||
public static void paramIsNull(Object obj, ExceptionInterface exceptionInterface, Exception exception, | |||
String logMsg, String ...logObj){ | |||
if(ObjectUtil.isEmpty(obj)){ | |||
throwServiceExcetion(exceptionInterface, exception, logMsg, logObj); | |||
} | |||
} | |||
} |
@@ -0,0 +1,180 @@ | |||
package com.tuoheng.common.core.utils; | |||
import com.alibaba.fastjson.JSONObject; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import javax.net.ssl.*; | |||
import java.io.*; | |||
import java.net.*; | |||
import java.nio.charset.StandardCharsets; | |||
import java.security.cert.X509Certificate; | |||
import java.util.Map; | |||
/** | |||
* 通用http发送方法 | |||
*/ | |||
public class HttpUtils { | |||
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); | |||
/** | |||
* 向指定 URL 发送GET方法的请求 | |||
* | |||
* @param url 发送请求的 URL | |||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 | |||
* @return 所代表远程资源的响应结果 | |||
*/ | |||
public static String sendGet(String url, String param) { | |||
StringBuilder result = new StringBuilder(); | |||
BufferedReader in = null; | |||
try { | |||
String urlNameString = url + "?" + param; | |||
log.info("sendGet - {}", urlNameString); | |||
URL realUrl = new URL(urlNameString); | |||
URLConnection connection = realUrl.openConnection(); | |||
connection.setRequestProperty("accept", "*/*"); | |||
connection.setRequestProperty("connection", "Keep-Alive"); | |||
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); | |||
connection.connect(); | |||
in = new BufferedReader(new InputStreamReader(connection.getInputStream())); | |||
String line; | |||
while ((line = in.readLine()) != null) { | |||
result.append(line); | |||
} | |||
log.info("recv - {}", result); | |||
} catch (ConnectException e) { | |||
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); | |||
} catch (SocketTimeoutException e) { | |||
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); | |||
} catch (IOException e) { | |||
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); | |||
} catch (Exception e) { | |||
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); | |||
} finally { | |||
try { | |||
if (in != null) { | |||
in.close(); | |||
} | |||
} catch (Exception ex) { | |||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); | |||
} | |||
} | |||
return result.toString(); | |||
} | |||
/** | |||
* 处理post请求 | |||
* @param urlStr 请求url | |||
* @param data 请求体数据 | |||
* @param properties 请求头参数 | |||
* @return | |||
*/ | |||
public static String doSend(String urlStr, JSONObject data, Map<String, String> properties, String method){ | |||
try { | |||
log.info("请求url={}", urlStr); | |||
log.info("请求体数据data={}", data.toJSONString()); | |||
log.info("请求头参数properties={}", properties); | |||
URL url = new URL(urlStr); | |||
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); | |||
connection.setDoInput(true); // 设置可输入 | |||
connection.setDoOutput(true); // 设置该连接是可以输出的 | |||
//设置连接超时时间和读取超时时间 | |||
connection.setConnectTimeout(15000); | |||
connection.setReadTimeout(60000); | |||
//设置请求方式 | |||
connection.setRequestMethod(method); | |||
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); | |||
if(null != properties && !properties.isEmpty()){ | |||
for (String key:properties.keySet()){ | |||
connection.setRequestProperty(key, properties.get(key)); | |||
} | |||
} | |||
PrintWriter pw = new PrintWriter(new BufferedOutputStream(connection.getOutputStream())); | |||
pw.write(data.toJSONString()); | |||
pw.flush(); | |||
pw.close(); | |||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)); | |||
String line = null; | |||
StringBuilder result = new StringBuilder(); | |||
while ((line = br.readLine()) != null) { // 读取数据 | |||
result.append(line).append("\n"); | |||
} | |||
connection.disconnect(); | |||
log.info(result.toString()); | |||
return result.toString(); | |||
} catch (Exception e) { | |||
log.error("post请求失败,{}",e.getMessage()); | |||
return "post请求失败!"; | |||
} | |||
} | |||
public static String sendSSLPost(String url, String param) { | |||
StringBuilder result = new StringBuilder(); | |||
String urlNameString = url + "?" + param; | |||
try { | |||
log.info("sendSSLPost - {}", urlNameString); | |||
SSLContext sc = SSLContext.getInstance("SSL"); | |||
sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom()); | |||
URL console = new URL(urlNameString); | |||
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); | |||
conn.setRequestProperty("accept", "*/*"); | |||
conn.setRequestProperty("connection", "Keep-Alive"); | |||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); | |||
conn.setRequestProperty("Accept-Charset", "utf-8"); | |||
conn.setRequestProperty("contentType", "utf-8"); | |||
conn.setDoOutput(true); | |||
conn.setDoInput(true); | |||
conn.setSSLSocketFactory(sc.getSocketFactory()); | |||
conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); | |||
conn.connect(); | |||
InputStream is = conn.getInputStream(); | |||
BufferedReader br = new BufferedReader(new InputStreamReader(is)); | |||
String ret = ""; | |||
while ((ret = br.readLine()) != null) { | |||
if (ret != null && !ret.trim().equals("")) { | |||
result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8")); | |||
} | |||
} | |||
log.info("recv - {}", result); | |||
conn.disconnect(); | |||
br.close(); | |||
} catch (ConnectException e) { | |||
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); | |||
} catch (SocketTimeoutException e) { | |||
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); | |||
} catch (IOException e) { | |||
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); | |||
} catch (Exception e) { | |||
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); | |||
} | |||
return result.toString(); | |||
} | |||
private static class TrustAnyTrustManager implements X509TrustManager { | |||
@Override | |||
public void checkClientTrusted(X509Certificate[] chain, String authType) { | |||
} | |||
@Override | |||
public void checkServerTrusted(X509Certificate[] chain, String authType) { | |||
} | |||
@Override | |||
public X509Certificate[] getAcceptedIssuers() { | |||
return new X509Certificate[]{}; | |||
} | |||
} | |||
private static class TrustAnyHostnameVerifier implements HostnameVerifier { | |||
@Override | |||
public boolean verify(String hostname, SSLSession session) { | |||
return true; | |||
} | |||
} | |||
} |
@@ -0,0 +1,296 @@ | |||
package com.tuoheng.common.core.utils; | |||
import com.alibaba.fastjson.JSONObject; | |||
import lombok.extern.slf4j.Slf4j; | |||
import javax.servlet.http.HttpServletRequest; | |||
import java.net.InetAddress; | |||
import java.net.UnknownHostException; | |||
/** | |||
* 获取IP方法 | |||
* | |||
* @author ruoyi | |||
*/ | |||
@Slf4j | |||
public class IpUtils | |||
{ | |||
/** | |||
* 获取客户端IP | |||
* | |||
* @param request 请求对象 | |||
* @return IP地址 | |||
*/ | |||
public static String getIpAddr(HttpServletRequest request) | |||
{ | |||
if (request == null) | |||
{ | |||
return "unknown"; | |||
} | |||
String ip = request.getHeader("x-forwarded-for"); | |||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) | |||
{ | |||
ip = request.getHeader("Proxy-Client-IP"); | |||
} | |||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) | |||
{ | |||
ip = request.getHeader("X-Forwarded-For"); | |||
} | |||
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.getHeader("X-Real-IP"); | |||
} | |||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) | |||
{ | |||
ip = request.getRemoteAddr(); | |||
} | |||
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); | |||
} | |||
/** | |||
* 检查是否为内部IP地址 | |||
* | |||
* @param ip IP地址 | |||
* @return 结果 | |||
*/ | |||
public static boolean internalIp(String ip) | |||
{ | |||
byte[] addr = textToNumericFormatV4(ip); | |||
return internalIp(addr) || "127.0.0.1".equals(ip); | |||
} | |||
/** | |||
* 检查是否为内部IP地址 | |||
* | |||
* @param addr byte地址 | |||
* @return 结果 | |||
*/ | |||
private static boolean internalIp(byte[] addr) | |||
{ | |||
if (StringUtils.isNull(addr) || addr.length < 2) | |||
{ | |||
return true; | |||
} | |||
final byte b0 = addr[0]; | |||
final byte b1 = addr[1]; | |||
// 10.x.x.x/8 | |||
final byte SECTION_1 = 0x0A; | |||
// 172.16.x.x/12 | |||
final byte SECTION_2 = (byte) 0xAC; | |||
final byte SECTION_3 = (byte) 0x10; | |||
final byte SECTION_4 = (byte) 0x1F; | |||
// 192.168.x.x/16 | |||
final byte SECTION_5 = (byte) 0xC0; | |||
final byte SECTION_6 = (byte) 0xA8; | |||
switch (b0) | |||
{ | |||
case SECTION_1: | |||
return true; | |||
case SECTION_2: | |||
if (b1 >= SECTION_3 && b1 <= SECTION_4) | |||
{ | |||
return true; | |||
} | |||
case SECTION_5: | |||
switch (b1) | |||
{ | |||
case SECTION_6: | |||
return true; | |||
} | |||
default: | |||
return false; | |||
} | |||
} | |||
/** | |||
* 将IPv4地址转换成字节 | |||
* | |||
* @param text IPv4地址 | |||
* @return byte 字节 | |||
*/ | |||
public static byte[] textToNumericFormatV4(String text) | |||
{ | |||
if (text.length() == 0) | |||
{ | |||
return null; | |||
} | |||
byte[] bytes = new byte[4]; | |||
String[] elements = text.split("\\.", -1); | |||
try | |||
{ | |||
long l; | |||
int i; | |||
switch (elements.length) | |||
{ | |||
case 1: | |||
l = Long.parseLong(elements[0]); | |||
if ((l < 0L) || (l > 4294967295L)) | |||
{ | |||
return null; | |||
} | |||
bytes[0] = (byte) (int) (l >> 24 & 0xFF); | |||
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); | |||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); | |||
bytes[3] = (byte) (int) (l & 0xFF); | |||
break; | |||
case 2: | |||
l = Integer.parseInt(elements[0]); | |||
if ((l < 0L) || (l > 255L)) | |||
{ | |||
return null; | |||
} | |||
bytes[0] = (byte) (int) (l & 0xFF); | |||
l = Integer.parseInt(elements[1]); | |||
if ((l < 0L) || (l > 16777215L)) | |||
{ | |||
return null; | |||
} | |||
bytes[1] = (byte) (int) (l >> 16 & 0xFF); | |||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); | |||
bytes[3] = (byte) (int) (l & 0xFF); | |||
break; | |||
case 3: | |||
for (i = 0; i < 2; ++i) | |||
{ | |||
l = Integer.parseInt(elements[i]); | |||
if ((l < 0L) || (l > 255L)) | |||
{ | |||
return null; | |||
} | |||
bytes[i] = (byte) (int) (l & 0xFF); | |||
} | |||
l = Integer.parseInt(elements[2]); | |||
if ((l < 0L) || (l > 65535L)) | |||
{ | |||
return null; | |||
} | |||
bytes[2] = (byte) (int) (l >> 8 & 0xFF); | |||
bytes[3] = (byte) (int) (l & 0xFF); | |||
break; | |||
case 4: | |||
for (i = 0; i < 4; ++i) | |||
{ | |||
l = Integer.parseInt(elements[i]); | |||
if ((l < 0L) || (l > 255L)) | |||
{ | |||
return null; | |||
} | |||
bytes[i] = (byte) (int) (l & 0xFF); | |||
} | |||
break; | |||
default: | |||
return null; | |||
} | |||
} | |||
catch (NumberFormatException e) | |||
{ | |||
return null; | |||
} | |||
return bytes; | |||
} | |||
/** | |||
* 获取IP地址 | |||
* | |||
* @return 本地IP地址 | |||
*/ | |||
public static String getHostIp() | |||
{ | |||
try | |||
{ | |||
return InetAddress.getLocalHost().getHostAddress(); | |||
} | |||
catch (UnknownHostException e) | |||
{ | |||
} | |||
return "127.0.0.1"; | |||
} | |||
/** | |||
* 获取主机名 | |||
* | |||
* @return 本地主机名 | |||
*/ | |||
public static String getHostName() | |||
{ | |||
try | |||
{ | |||
return InetAddress.getLocalHost().getHostName(); | |||
} | |||
catch (UnknownHostException e) | |||
{ | |||
} | |||
return "未知"; | |||
} | |||
/** | |||
* 从多级反向代理中获得第一个非unknown IP地址 | |||
* | |||
* @param ip 获得的IP地址 | |||
* @return 第一个非unknown IP地址 | |||
*/ | |||
public static String getMultistageReverseProxyIp(String ip) | |||
{ | |||
// 多级反向代理检测 | |||
if (ip != null && ip.indexOf(",") > 0) | |||
{ | |||
final String[] ips = ip.trim().split(","); | |||
for (String subIp : ips) | |||
{ | |||
if (false == isUnknown(subIp)) | |||
{ | |||
ip = subIp; | |||
break; | |||
} | |||
} | |||
} | |||
return ip; | |||
} | |||
/** | |||
* 检测给定字符串是否为未知,多用于检测HTTP请求相关 | |||
* | |||
* @param checkString 被检测的字符串 | |||
* @return 是否未知 | |||
*/ | |||
public static boolean isUnknown(String checkString) | |||
{ | |||
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); | |||
} | |||
/** | |||
* 根据IP查询地区 | |||
* | |||
* @param ip IP地址 | |||
* @return | |||
*/ | |||
public static String getRealAddressByIP(String ip) { | |||
String address = "XX XX"; | |||
// 内网不查询 | |||
if (IpUtils.internalIp(ip)) { | |||
return "内网IP"; | |||
} | |||
try { | |||
String rspStr = HttpUtils.doSend("http://ip.taobao.com/service/getIpInfo.php?ip=" + ip, new JSONObject(), null, "GET"); | |||
if (org.springframework.util.StringUtils.isEmpty(rspStr)) { | |||
log.error("获取地理位置异常 {}", ip); | |||
return address; | |||
} | |||
JSONObject obj = JSONObject.parseObject(rspStr); | |||
JSONObject data = obj.getObject("data", JSONObject.class); | |||
String region = data.getString("region"); | |||
String city = data.getString("city"); | |||
address = region + " " + city; | |||
}catch (Exception e){ | |||
log.info("获取ip地址信息失败。{}",ip); | |||
} | |||
return address; | |||
} | |||
} |
@@ -0,0 +1,132 @@ | |||
package com.tuoheng.common.core.utils; | |||
import com.fasterxml.jackson.annotation.JsonInclude; | |||
import com.fasterxml.jackson.databind.DeserializationFeature; | |||
import com.fasterxml.jackson.databind.JsonNode; | |||
import com.fasterxml.jackson.databind.ObjectMapper; | |||
import com.fasterxml.jackson.databind.SerializationFeature; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.util.StringUtils; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Map; | |||
public class JacksonUtil { | |||
private final static Logger LOGGER = LoggerFactory.getLogger(JacksonUtil.class); | |||
private final static ObjectMapper objectMapper = new ObjectMapper(); | |||
private final static SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | |||
private JacksonUtil(){} | |||
static { | |||
//序列化的时候序列对象的所有属性 | |||
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); | |||
//反序列化的时候如果多了其他属性,不抛出异常 | |||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); | |||
//如果是空对象的时候,不抛异常 | |||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); | |||
//取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式 | |||
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); | |||
objectMapper.setDateFormat(dateformat); | |||
} | |||
/** | |||
* 转换为 JSON 字符串 | |||
* | |||
* @param obj | |||
* @return | |||
*/ | |||
public static <T> String obj2json(T obj) { | |||
if(obj != null){ | |||
try { | |||
return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj); | |||
} catch (Exception e) { | |||
LOGGER.error("对象转字符串异常: {}", e); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 漂亮的转换为 JSON 字符串 | |||
* | |||
* @param obj | |||
* @return | |||
*/ | |||
public static <T> String obj2StringPretty(T obj) { | |||
if(obj != null){ | |||
try { | |||
return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); | |||
} catch (Exception e) { | |||
LOGGER.error("对象转字符串异常: {}", e); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 转换为 JavaBean | |||
* | |||
* @param str | |||
* @param clazz | |||
* @return | |||
* @throws Exception | |||
*/ | |||
public static <T> T json2pojo(String str, Class<T> clazz) { | |||
if(StringUtils.hasLength(str) && clazz != null){ | |||
try { | |||
return clazz.equals(String.class)? (T)str : objectMapper.readValue(str,clazz); | |||
} catch (Exception e) { | |||
LOGGER.error("字符串转对象异常: {}", e); | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* 将 Map 转换为 JavaBean | |||
* | |||
* @param map | |||
* @param clazz | |||
* @return | |||
*/ | |||
public static <T> T map2pojo(Map map, Class<T> clazz) { | |||
return objectMapper.convertValue(map, clazz); | |||
} | |||
/** | |||
* 将 JSON 对象转换为 JavaBean | |||
* | |||
* @param obj | |||
* @param clazz | |||
* @return | |||
*/ | |||
public static <T> T obj2pojo(Object obj, Class<T> clazz) { | |||
return objectMapper.convertValue(obj, clazz); | |||
} | |||
/** | |||
* 将指定节点的 JSON 数据转换为 JavaBean | |||
* | |||
* @param str | |||
* @param treeNode | |||
* @param clazz | |||
* @return | |||
*/ | |||
public static <T> T json2pojoByTree(String str, String treeNode, Class<T> clazz) { | |||
if(StringUtils.hasLength(str) && StringUtils.hasLength(str) && clazz == null){ | |||
try { | |||
JsonNode jsonNode = objectMapper.readTree(str); | |||
JsonNode data = jsonNode.findPath(treeNode); | |||
return json2pojo(data.toString(), clazz); | |||
} catch (Exception e) { | |||
LOGGER.error("字符串按节点转对象异常: {}", e); | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,99 @@ | |||
package com.tuoheng.common.core.utils; | |||
import java.io.Serializable; | |||
/** | |||
* JSON回应类 | |||
* | |||
* @author 牧羊人 | |||
* @date 2019/11/28 | |||
*/ | |||
public class JsonResult<T> implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* 成功 | |||
*/ | |||
public static final int SUCCESS = 0; | |||
/** | |||
* 失败 | |||
*/ | |||
public static final int ERROR = -1; | |||
private int code; | |||
private String msg; | |||
private T data; | |||
public static <T> JsonResult<T> success() { | |||
return jsonResult(null, SUCCESS, "操作成功"); | |||
} | |||
public static <T> JsonResult<T> success(String msg) { | |||
return jsonResult(null, SUCCESS, msg); | |||
} | |||
public static <T> JsonResult<T> success(T data) { | |||
return jsonResult(data, SUCCESS, "操作成功"); | |||
} | |||
public static <T> JsonResult<T> success(T data, String msg) { | |||
return jsonResult(data, SUCCESS, msg); | |||
} | |||
public static <T> JsonResult<T> error() { | |||
return jsonResult(null, ERROR, "操作失败"); | |||
} | |||
public static <T> JsonResult<T> error(String msg) { | |||
return jsonResult(null, ERROR, msg); | |||
} | |||
public static <T> JsonResult<T> error(T data) { | |||
return jsonResult(data, ERROR, "操作失败"); | |||
} | |||
public static <T> JsonResult<T> error(T data, String msg) { | |||
return jsonResult(data, ERROR, msg); | |||
} | |||
public static <T> JsonResult<T> error(int code, String msg) { | |||
return jsonResult(null, code, msg); | |||
} | |||
private static <T> JsonResult<T> jsonResult(T data, int code, String msg) { | |||
JsonResult<T> result = new JsonResult<>(); | |||
result.setCode(code); | |||
result.setData(data); | |||
result.setMsg(msg); | |||
return result; | |||
} | |||
public int getCode() { | |||
return code; | |||
} | |||
public void setCode(int code) { | |||
this.code = code; | |||
} | |||
public String getMsg() { | |||
return msg; | |||
} | |||
public void setMsg(String msg) { | |||
this.msg = msg; | |||
} | |||
public T getData() { | |||
return data; | |||
} | |||
public void setData(T data) { | |||
this.data = data; | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
package com.tuoheng.common.core.utils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
/** | |||
* 日志工具类 | |||
* @author chenyukun | |||
*/ | |||
public class LogUtil { | |||
private LogUtil(){} | |||
public static final Logger LOGGER = LoggerFactory.getLogger(LogUtil.class); | |||
} |
@@ -0,0 +1,70 @@ | |||
package com.tuoheng.common.core.utils; | |||
import com.alibaba.fastjson.JSON; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.tuoheng.common.core.entity.UserEntity; | |||
import com.tuoheng.common.core.exception.ServiceException; | |||
import com.tuoheng.common.core.model.dto.LoginUser; | |||
import org.springframework.http.HttpStatus; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.context.SecurityContextHolder; | |||
public class SecurityUserUtils { | |||
/** | |||
* 获取认证信息 | |||
* | |||
* @return | |||
*/ | |||
public static Authentication getAuthentication() { | |||
return SecurityContextHolder.getContext().getAuthentication(); | |||
} | |||
/** | |||
* 获取用户登录信息 | |||
* | |||
* @return | |||
*/ | |||
public static LoginUser getLoginUser() { | |||
// header中获取用户token | |||
String token = ServletUtils.getRequest().getHeader("th-token"); | |||
String oUserJson = ServletUtils.getRequest().getHeader("o-user-json"); | |||
String json = EncryptUtil.decodeUTF8StringBase64(oUserJson); | |||
JSONObject jsonObject = JSON.parseObject(json); | |||
String username = jsonObject.getString("username"); | |||
Long oidcUserId = jsonObject.getLong("oUserId"); | |||
// 这里可以自定义封装自己的用户信息 | |||
//UserPo userPo = clientUserMapper.getUserByUserName(username); | |||
LoginUser user = new LoginUser() | |||
.setUsername(username) | |||
.setUserId(oidcUserId) | |||
.setThToken(token); | |||
return user; | |||
} | |||
public static String token() { | |||
// header中获取用户token | |||
String token = ServletUtils.getRequest().getHeader("th-token"); | |||
if (StringUtils.isEmpty(token)) { | |||
throw new ServiceException(HttpStatus.BAD_REQUEST.value(), "token不能为空"); | |||
} | |||
return token; | |||
} | |||
/** | |||
* 获取username | |||
* | |||
* @return | |||
*/ | |||
public static String username() { | |||
// header中获取用户token | |||
String oUserJson = ServletUtils.getRequest().getHeader("o-user-json"); | |||
if (StringUtils.isEmpty(oUserJson)) { | |||
throw new ServiceException(HttpStatus.BAD_REQUEST.value(), "oUserJson不能为空"); | |||
} | |||
String json = EncryptUtil.decodeUTF8StringBase64(oUserJson); | |||
JSONObject jsonObject = JSON.parseObject(json); | |||
String username = jsonObject.getString("username"); | |||
return username; | |||
} | |||
} |
@@ -0,0 +1,188 @@ | |||
package com.tuoheng.common.core.utils; | |||
import cn.hutool.core.convert.Convert; | |||
import org.springframework.util.LinkedCaseInsensitiveMap; | |||
import org.springframework.web.context.request.RequestAttributes; | |||
import org.springframework.web.context.request.RequestContextHolder; | |||
import org.springframework.web.context.request.ServletRequestAttributes; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import javax.servlet.http.HttpSession; | |||
import java.io.IOException; | |||
import java.util.Enumeration; | |||
import java.util.Map; | |||
/** | |||
* 客户端工具类 | |||
* | |||
* @author ruoyi | |||
*/ | |||
public class ServletUtils | |||
{ | |||
/** | |||
* 获取String参数 | |||
*/ | |||
public static String getParameter(String name) | |||
{ | |||
return getRequest().getParameter(name); | |||
} | |||
/** | |||
* 获取String参数 | |||
*/ | |||
public static String getParameter(String name, String defaultValue) | |||
{ | |||
return Convert.toStr(getRequest().getParameter(name), defaultValue); | |||
} | |||
/** | |||
* 获取Integer参数 | |||
*/ | |||
public static Integer getParameterToInt(String name) | |||
{ | |||
return Convert.toInt(getRequest().getParameter(name)); | |||
} | |||
/** | |||
* 获取Integer参数 | |||
*/ | |||
public static Integer getParameterToInt(String name, Integer defaultValue) | |||
{ | |||
return Convert.toInt(getRequest().getParameter(name), defaultValue); | |||
} | |||
/** | |||
* 获取Boolean参数 | |||
*/ | |||
public static Boolean getParameterToBool(String name) | |||
{ | |||
return Convert.toBool(getRequest().getParameter(name)); | |||
} | |||
/** | |||
* 获取Boolean参数 | |||
*/ | |||
public static Boolean getParameterToBool(String name, Boolean defaultValue) | |||
{ | |||
return Convert.toBool(getRequest().getParameter(name), defaultValue); | |||
} | |||
/** | |||
* 获取request | |||
*/ | |||
public static HttpServletRequest getRequest() | |||
{ | |||
try | |||
{ | |||
return getRequestAttributes().getRequest(); | |||
} | |||
catch (Exception e) | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* 获取response | |||
*/ | |||
public static HttpServletResponse getResponse() | |||
{ | |||
try | |||
{ | |||
return getRequestAttributes().getResponse(); | |||
} | |||
catch (Exception e) | |||
{ | |||
return null; | |||
} | |||
} | |||
/** | |||
* 获取session | |||
*/ | |||
public static HttpSession getSession() | |||
{ | |||
return getRequest().getSession(); | |||
} | |||
public static ServletRequestAttributes getRequestAttributes() | |||
{ | |||
try | |||
{ | |||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); | |||
return (ServletRequestAttributes) attributes; | |||
} | |||
catch (Exception e) | |||
{ | |||
return null; | |||
} | |||
} | |||
public static Map<String, String> getHeaders(HttpServletRequest request) | |||
{ | |||
Map<String, String> map = new LinkedCaseInsensitiveMap<>(); | |||
Enumeration<String> enumeration = request.getHeaderNames(); | |||
if (enumeration != null) | |||
{ | |||
while (enumeration.hasMoreElements()) | |||
{ | |||
String key = enumeration.nextElement(); | |||
String value = request.getHeader(key); | |||
map.put(key, value); | |||
} | |||
} | |||
return map; | |||
} | |||
/** | |||
* 将字符串渲染到客户端 | |||
* | |||
* @param response 渲染对象 | |||
* @param string 待渲染的字符串 | |||
*/ | |||
public static void renderString(HttpServletResponse response, String string) | |||
{ | |||
try | |||
{ | |||
response.setStatus(200); | |||
response.setContentType("application/json"); | |||
response.setCharacterEncoding("utf-8"); | |||
response.getWriter().print(string); | |||
} | |||
catch (IOException e) | |||
{ | |||
e.printStackTrace(); | |||
} | |||
} | |||
/** | |||
* 是否是Ajax异步请求 | |||
* | |||
* @param request | |||
*/ | |||
public static boolean isAjaxRequest(HttpServletRequest request) | |||
{ | |||
String accept = request.getHeader("accept"); | |||
if (accept != null && accept.contains("application/json")) | |||
{ | |||
return true; | |||
} | |||
String xRequestedWith = request.getHeader("X-Requested-With"); | |||
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) | |||
{ | |||
return true; | |||
} | |||
String uri = request.getRequestURI(); | |||
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) | |||
{ | |||
return true; | |||
} | |||
String ajax = request.getParameter("__ajax"); | |||
return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); | |||
} | |||
} |
@@ -0,0 +1,386 @@ | |||
package com.tuoheng.common.core.utils; | |||
import java.util.*; | |||
/** | |||
* 字符串工具类 | |||
*/ | |||
public class StringUtils extends org.apache.commons.lang3.StringUtils { | |||
/** | |||
* 空字符串 | |||
*/ | |||
private static final String NULLSTR = ""; | |||
/** | |||
* 下划线 | |||
*/ | |||
private static final char SEPARATOR = '_'; | |||
/** | |||
* 获取参数不为空值 | |||
* | |||
* @param value defaultValue 要判断的value | |||
* @return value 返回值 | |||
*/ | |||
public static <T> T nvl(T value, T defaultValue) { | |||
return value != null ? value : defaultValue; | |||
} | |||
/** | |||
* * 判断一个Collection是否为空, 包含List,Set,Queue | |||
* | |||
* @param coll 要判断的Collection | |||
* @return true:为空 false:非空 | |||
*/ | |||
public static boolean isEmpty(Collection<?> coll) { | |||
return isNull(coll) || coll.isEmpty(); | |||
} | |||
/** | |||
* * 判断一个Collection是否非空,包含List,Set,Queue | |||
* | |||
* @param coll 要判断的Collection | |||
* @return true:非空 false:空 | |||
*/ | |||
public static boolean isNotEmpty(Collection<?> coll) { | |||
return !isEmpty(coll); | |||
} | |||
/** | |||
* * 判断一个对象数组是否为空 | |||
* | |||
* @param objects 要判断的对象数组 | |||
* * @return true:为空 false:非空 | |||
*/ | |||
public static boolean isEmpty(Object[] objects) { | |||
return isNull(objects) || (objects.length == 0); | |||
} | |||
/** | |||
* * 判断一个对象数组是否非空 | |||
* | |||
* @param objects 要判断的对象数组 | |||
* @return true:非空 false:空 | |||
*/ | |||
public static boolean isNotEmpty(Object[] objects) { | |||
return !isEmpty(objects); | |||
} | |||
/** | |||
* * 判断一个Map是否为空 | |||
* | |||
* @param map 要判断的Map | |||
* @return true:为空 false:非空 | |||
*/ | |||
public static boolean isEmpty(Map<?, ?> map) { | |||
return isNull(map) || map.isEmpty(); | |||
} | |||
/** | |||
* * 判断一个Map是否为空 | |||
* | |||
* @param map 要判断的Map | |||
* @return true:非空 false:空 | |||
*/ | |||
public static boolean isNotEmpty(Map<?, ?> map) { | |||
return !isEmpty(map); | |||
} | |||
/** | |||
* * 判断一个字符串是否为空串 | |||
* | |||
* @param str String | |||
* @return true:为空 false:非空 | |||
*/ | |||
public static boolean isEmpty(String str) { | |||
return isNull(str) || NULLSTR.equals(str.trim()); | |||
} | |||
/** | |||
* * 判断一个字符串是否为非空串 | |||
* | |||
* @param str String | |||
* @return true:非空串 false:空串 | |||
*/ | |||
public static boolean isNotEmpty(String str) { | |||
return !isEmpty(str); | |||
} | |||
/** | |||
* * 判断一个对象是否为空 | |||
* | |||
* @param object Object | |||
* @return true:为空 false:非空 | |||
*/ | |||
public static boolean isNull(Object object) { | |||
return object == null; | |||
} | |||
/** | |||
* * 判断一个对象是否非空 | |||
* | |||
* @param object Object | |||
* @return true:非空 false:空 | |||
*/ | |||
public static boolean isNotNull(Object object) { | |||
return !isNull(object); | |||
} | |||
/** | |||
* * 判断一个对象是否是数组类型(Java基本型别的数组) | |||
* | |||
* @param object 对象 | |||
* @return true:是数组 false:不是数组 | |||
*/ | |||
public static boolean isArray(Object object) { | |||
return isNotNull(object) && object.getClass().isArray(); | |||
} | |||
/** | |||
* 去空格 | |||
*/ | |||
public static String trim(String str) { | |||
return (str == null ? "" : str.trim()); | |||
} | |||
/** | |||
* 截取字符串 | |||
* | |||
* @param str 字符串 | |||
* @param start 开始 | |||
* @return 结果 | |||
*/ | |||
public static String substring(final String str, int start) { | |||
if (str == null) { | |||
return NULLSTR; | |||
} | |||
if (start < 0) { | |||
start = str.length() + start; | |||
} | |||
if (start < 0) { | |||
start = 0; | |||
} | |||
if (start > str.length()) { | |||
return NULLSTR; | |||
} | |||
return str.substring(start); | |||
} | |||
/** | |||
* 截取字符串 | |||
* | |||
* @param str 字符串 | |||
* @param start 开始 | |||
* @param end 结束 | |||
* @return 结果 | |||
*/ | |||
public static String substring(final String str, int start, int end) { | |||
if (str == null) { | |||
return NULLSTR; | |||
} | |||
if (end < 0) { | |||
end = str.length() + end; | |||
} | |||
if (start < 0) { | |||
start = str.length() + start; | |||
} | |||
if (end > str.length()) { | |||
end = str.length(); | |||
} | |||
if (start > end) { | |||
return NULLSTR; | |||
} | |||
if (start < 0) { | |||
start = 0; | |||
} | |||
if (end < 0) { | |||
end = 0; | |||
} | |||
return str.substring(start, end); | |||
} | |||
/** | |||
* 字符串转set | |||
* | |||
* @param str 字符串 | |||
* @param sep 分隔符 | |||
* @return set集合 | |||
*/ | |||
public static final Set<String> str2Set(String str, String sep) { | |||
return new HashSet<String>(str2List(str, sep, true, false)); | |||
} | |||
/** | |||
* 字符串转list | |||
* | |||
* @param str 字符串 | |||
* @param sep 分隔符 | |||
* @param filterBlank 过滤纯空白 | |||
* @param trim 去掉首尾空白 | |||
* @return list集合 | |||
*/ | |||
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) { | |||
List<String> list = new ArrayList<String>(); | |||
if (StringUtils.isEmpty(str)) { | |||
return list; | |||
} | |||
// 过滤空白字符串 | |||
if (filterBlank && StringUtils.isBlank(str)) { | |||
return list; | |||
} | |||
String[] split = str.split(sep); | |||
for (String string : split) { | |||
if (filterBlank && StringUtils.isBlank(string)) { | |||
continue; | |||
} | |||
if (trim) { | |||
string = string.trim(); | |||
} | |||
list.add(string); | |||
} | |||
return list; | |||
} | |||
/** | |||
* 下划线转驼峰命名 | |||
*/ | |||
public static String toUnderScoreCase(String str) { | |||
if (str == null) { | |||
return null; | |||
} | |||
StringBuilder sb = new StringBuilder(); | |||
// 前置字符是否大写 | |||
boolean preCharIsUpperCase = true; | |||
// 当前字符是否大写 | |||
boolean curreCharIsUpperCase = true; | |||
// 下一字符是否大写 | |||
boolean nexteCharIsUpperCase = true; | |||
for (int i = 0; i < str.length(); i++) { | |||
char c = str.charAt(i); | |||
if (i > 0) { | |||
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); | |||
} else { | |||
preCharIsUpperCase = false; | |||
} | |||
curreCharIsUpperCase = Character.isUpperCase(c); | |||
if (i < (str.length() - 1)) { | |||
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); | |||
} | |||
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { | |||
sb.append(SEPARATOR); | |||
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { | |||
sb.append(SEPARATOR); | |||
} | |||
sb.append(Character.toLowerCase(c)); | |||
} | |||
return sb.toString(); | |||
} | |||
/** | |||
* 是否包含字符串 | |||
* | |||
* @param str 验证字符串 | |||
* @param strs 字符串组 | |||
* @return 包含返回true | |||
*/ | |||
public static boolean inStringIgnoreCase(String str, String... strs) { | |||
if (str != null && strs != null) { | |||
for (String s : strs) { | |||
if (str.equalsIgnoreCase(trim(s))) { | |||
return true; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld | |||
* | |||
* @param name 转换前的下划线大写方式命名的字符串 | |||
* @return 转换后的驼峰式命名的字符串 | |||
*/ | |||
public static String convertToCamelCase(String name) { | |||
StringBuilder result = new StringBuilder(); | |||
// 快速检查 | |||
if (name == null || name.isEmpty()) { | |||
// 没必要转换 | |||
return ""; | |||
} else if (!name.contains("_")) { | |||
// 不含下划线,仅将首字母大写 | |||
return name.substring(0, 1).toUpperCase() + name.substring(1); | |||
} | |||
// 用下划线将原始字符串分割 | |||
String[] camels = name.split("_"); | |||
for (String camel : camels) { | |||
// 跳过原始字符串中开头、结尾的下换线或双重下划线 | |||
if (camel.isEmpty()) { | |||
continue; | |||
} | |||
// 首字母大写 | |||
result.append(camel.substring(0, 1).toUpperCase()); | |||
result.append(camel.substring(1).toLowerCase()); | |||
} | |||
return result.toString(); | |||
} | |||
/** | |||
* 驼峰式命名法 例如:user_name->userName | |||
*/ | |||
public static String toCamelCase(String s) { | |||
if (s == null) { | |||
return null; | |||
} | |||
s = s.toLowerCase(); | |||
StringBuilder sb = new StringBuilder(s.length()); | |||
boolean upperCase = false; | |||
for (int i = 0; i < s.length(); i++) { | |||
char c = s.charAt(i); | |||
if (c == SEPARATOR) { | |||
upperCase = true; | |||
} else if (upperCase) { | |||
sb.append(Character.toUpperCase(c)); | |||
upperCase = false; | |||
} else { | |||
sb.append(c); | |||
} | |||
} | |||
return sb.toString(); | |||
} | |||
/** | |||
* 替换字符串中的域名 | |||
*/ | |||
public static String removeHost(String str, String host) { | |||
if (!StringUtils.isEmpty(str) && str.contains(host)) { | |||
return str.replaceAll(host, ""); | |||
} | |||
return str; | |||
} | |||
/** | |||
* 添加字符串中的域名 | |||
*/ | |||
public static String addHost(String str, String host) { | |||
if (!StringUtils.isEmpty(str) && !str.contains(host)) { | |||
return host + str; | |||
} | |||
return str; | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
package com.tuoheng.common.core.utils; | |||
/** | |||
* @Author xiaoying | |||
* @Date 2022/10/31 11:01 | |||
*/ | |||
public class ThreadLocalUtil { | |||
private final static ThreadLocal<Object> USER_THREAD_LOCAL = new ThreadLocal<>(); | |||
// 存放 | |||
public static void set(Object user) { | |||
USER_THREAD_LOCAL.set(user); | |||
} | |||
// 获取 | |||
public static Object get() { | |||
return USER_THREAD_LOCAL.get(); | |||
} | |||
// 移除 | |||
public static void remove() { | |||
USER_THREAD_LOCAL.remove(); | |||
} | |||
} | |||
@@ -0,0 +1,74 @@ | |||
package com.tuoheng.common.core.utils; | |||
import com.tuoheng.common.core.exception.ServiceException; | |||
import org.hibernate.validator.HibernateValidator; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.http.HttpStatus; | |||
import org.springframework.util.CollectionUtils; | |||
import javax.validation.ConstraintViolation; | |||
import javax.validation.Validation; | |||
import javax.validation.Validator; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import java.util.Set; | |||
/** | |||
* @author chenyukun | |||
*/ | |||
public class ValidatorUtils { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(ValidatorUtils.class); | |||
private static Validator validatorFast = Validation.byProvider(HibernateValidator.class) | |||
.configure() | |||
.failFast(true) | |||
.buildValidatorFactory() | |||
.getValidator(); | |||
private static Validator validatorAll = Validation.byProvider(HibernateValidator.class) | |||
.configure() | |||
.failFast(false) | |||
.buildValidatorFactory() | |||
.getValidator(); | |||
/** | |||
* 校验遇到第一个不合法的字段直接返回不合法字段,后续字段不再校验 | |||
* @param t t | |||
*/ | |||
public static <T> void validateFast(T t) { | |||
Set<ConstraintViolation<T>> validateResult = validatorFast.validate(t); | |||
buildResult(validateResult); | |||
} | |||
/** | |||
* 校验所有字段并返回不合法字段 | |||
* @param t t | |||
*/ | |||
public static <T> void validateAll(T t) { | |||
Set<ConstraintViolation<T>> validateResult = validatorAll.validate(t); | |||
buildResult(validateResult); | |||
} | |||
/** | |||
* 封装验证结果 | |||
* @param validateResult 验证结果 | |||
* @param <T> | |||
*/ | |||
private static <T> void buildResult(Set<ConstraintViolation<T>> validateResult){ | |||
Map<String, String> result = new HashMap<>(validateResult.size()); | |||
if(!CollectionUtils.isEmpty(validateResult)) { | |||
Iterator<ConstraintViolation<T>> it = validateResult.iterator(); | |||
while(it.hasNext()) { | |||
ConstraintViolation<T> cv = it.next(); | |||
result.put(cv.getPropertyPath().toString(), cv.getMessage()); | |||
} | |||
LOGGER.error("参数验证未通过,校验结果:{}", JacksonUtil.obj2json(result)); | |||
throw new ServiceException(HttpStatus.BAD_REQUEST.value(), JacksonUtil.obj2json(result)); | |||
} | |||
LOGGER.info("参数验证通过,校验异常数量:{}", validateResult.size()); | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
<?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>tuoheng-common</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>1.0.0</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-common-security</artifactId> | |||
<!-- 依赖库管理 --> | |||
<dependencies> | |||
<!-- Spring Security Oauth2 依赖 --> | |||
<dependency> | |||
<groupId>org.springframework.cloud</groupId> | |||
<artifactId>spring-cloud-starter-oauth2</artifactId> | |||
<!-- 此处需要加版本号 --> | |||
<version>2.2.5.RELEASE</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.cloud</groupId> | |||
<artifactId>spring-cloud-starter-security</artifactId> | |||
<!-- 此处需要加版本号 --> | |||
<version>2.1.4.RELEASE</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-data-redis</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.cloud</groupId> | |||
<artifactId>spring-cloud-starter-openfeign</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-web</artifactId> | |||
</dependency> | |||
<!--mybatis-plus 起始依赖 --> | |||
<dependency> | |||
<groupId>com.baomidou</groupId> | |||
<artifactId>mybatis-plus-boot-starter</artifactId> | |||
<version>3.2.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>fastjson</artifactId> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -0,0 +1,25 @@ | |||
package com.tuoheng.common.security.annotation; | |||
import com.tuoheng.common.security.config.OAuth2FeignConfig; | |||
import com.tuoheng.common.security.security.SecurityImportBeanDefinitionRegistrar; | |||
import org.mybatis.spring.annotation.MapperScan; | |||
import org.springframework.context.annotation.EnableAspectJAutoProxy; | |||
import org.springframework.context.annotation.Import; | |||
import org.springframework.scheduling.annotation.EnableAsync; | |||
import java.lang.annotation.*; | |||
@Target(ElementType.TYPE) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
@Inherited | |||
// 表示通过aop框架暴露该代理对象,AopContext能够访问 | |||
@EnableAspectJAutoProxy(exposeProxy = true) | |||
// 指定要扫描的Mapper类的包的路径 | |||
@MapperScan("com.tuoheng.**.**.mapper") | |||
// 开启线程异步执行 | |||
@EnableAsync | |||
// 自动加载类 | |||
@Import({SecurityImportBeanDefinitionRegistrar.class, OAuth2FeignConfig.class}) | |||
public @interface EnableCustomConfig { | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.tuoheng.common.security.annotation; | |||
import org.springframework.cloud.openfeign.EnableFeignClients; | |||
import java.lang.annotation.*; | |||
@Target(ElementType.TYPE) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@Documented | |||
@EnableFeignClients | |||
public @interface EnableTHFeignClients { | |||
String[] value() default {}; | |||
String[] basePackages() default {"com.tuoheng"}; | |||
Class<?>[] basePackageClasses() default {}; | |||
Class<?>[] defaultConfiguration() default {}; | |||
Class<?>[] clients() default {}; | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.tuoheng.common.security.config; | |||
import com.tuoheng.common.security.interceptor.OAuth2FeignRequestInterceptor; | |||
import feign.RequestInterceptor; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
/** | |||
* 拦截器配置 | |||
*/ | |||
@Configuration | |||
public class OAuth2FeignConfig { | |||
@Bean | |||
public RequestInterceptor requestInterceptor() { | |||
return new OAuth2FeignRequestInterceptor(); | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
package com.tuoheng.common.security.config; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; | |||
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; | |||
import org.springframework.cloud.client.loadbalancer.LoadBalanced; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.core.annotation.Order; | |||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; | |||
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; | |||
import org.springframework.web.client.DefaultResponseErrorHandler; | |||
import org.springframework.web.client.RestTemplate; | |||
@Configuration | |||
@EnableResourceServer | |||
//@EnableGlobalMethodSecurity(prePostEnabled = true) | |||
@Order(3) | |||
public class ResourceServerConfig extends ResourceServerConfigurerAdapter { | |||
@Autowired | |||
private ResourceServerProperties resourceServerProperties; | |||
@Autowired | |||
private OAuth2ClientProperties oAuth2ClientProperties; | |||
/** | |||
* 重写安全配置类 | |||
* | |||
* @param http | |||
* @throws Exception | |||
*/ | |||
@Override | |||
public void configure(HttpSecurity http) throws Exception { | |||
// super.configure(http); | |||
http.csrf().disable(); | |||
http | |||
.authorizeRequests() | |||
// 设置不登录可以访问 | |||
.antMatchers("/oauth/**").permitAll() | |||
// 其他请求认证后放行 | |||
.anyRequest().authenticated(); | |||
} | |||
@Bean | |||
@LoadBalanced | |||
public RestTemplate restTemplate() { | |||
RestTemplate restTemplate = new RestTemplate(); | |||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler()); | |||
return restTemplate; | |||
} | |||
// @Bean | |||
// @Primary | |||
// public ResourceServerTokenServices tokenServices() { | |||
// RemoteTokenServices remoteTokenServices = new RemoteTokenServices(); | |||
// DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); | |||
// UserAuthenticationConverter userTokenConverter = new CustomUserAuthenticationConverter(); | |||
// accessTokenConverter.setUserTokenConverter(userTokenConverter); | |||
// remoteTokenServices.setCheckTokenEndpointUrl(resourceServerProperties.getTokenInfoUri()); | |||
// remoteTokenServices.setClientId(oAuth2ClientProperties.getClientId()); | |||
// remoteTokenServices.setClientSecret(oAuth2ClientProperties.getClientSecret()); | |||
// remoteTokenServices.setRestTemplate(restTemplate()); | |||
// remoteTokenServices.setAccessTokenConverter(accessTokenConverter); | |||
// return remoteTokenServices; | |||
// } | |||
// @Override | |||
// public void configure(ResourceServerSecurityConfigurer resources) throws Exception { | |||
// resources.tokenServices(tokenServices()) | |||
// // 处理未认证 | |||
// .authenticationEntryPoint(new OAuthAuthExceptionEntryPoint()) | |||
// // 处理未授权 | |||
// .accessDeniedHandler(new OAuthAccessDeniedHandler()); | |||
// } | |||
} |
@@ -0,0 +1,33 @@ | |||
package com.tuoheng.common.security.constant; | |||
/** | |||
* 安全配置常量 | |||
*/ | |||
public class SecurityConstant { | |||
/** | |||
* 令牌类型 | |||
*/ | |||
public static final String BEARER_TOKEN_TYPE = "Bearer"; | |||
/** | |||
* 授权token url | |||
*/ | |||
public static final String AUTH_TOKEN = "/oauth/token"; | |||
/** | |||
* 注销token url | |||
*/ | |||
public static final String TOKEN_LOGOUT = "/token/logout"; | |||
/** | |||
* 用户ID字段 | |||
*/ | |||
public static final String DETAILS_USER_ID = "id"; | |||
/** | |||
* 用户名字段 | |||
*/ | |||
public static final String DETAILS_USERNAME = "username"; | |||
} |
@@ -0,0 +1,27 @@ | |||
package com.tuoheng.common.security.entity; | |||
import org.springframework.security.core.GrantedAuthority; | |||
import org.springframework.security.core.userdetails.User; | |||
import java.util.Collection; | |||
public class SecurityUser extends User { | |||
/** | |||
* 登录用户ID | |||
*/ | |||
private Integer userId; | |||
public SecurityUser(Integer userId, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) { | |||
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); | |||
this.userId = userId; | |||
} | |||
public Integer getUserId() { | |||
return this.userId; | |||
} | |||
public void setUserId(Integer userId) { | |||
this.userId = userId; | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
package com.tuoheng.common.security.interceptor; | |||
import com.tuoheng.common.security.constant.SecurityConstant; | |||
import feign.RequestInterceptor; | |||
import feign.RequestTemplate; | |||
import org.springframework.http.HttpHeaders; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.context.SecurityContext; | |||
import org.springframework.security.core.context.SecurityContextHolder; | |||
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; | |||
import org.springframework.stereotype.Component; | |||
@Component | |||
public class OAuth2FeignRequestInterceptor implements RequestInterceptor { | |||
@Override | |||
public void apply(RequestTemplate requestTemplate) { | |||
SecurityContext securityContext = SecurityContextHolder.getContext(); | |||
Authentication authentication = securityContext.getAuthentication(); | |||
if (authentication != null && authentication.getDetails() instanceof OAuth2AuthenticationDetails) { | |||
OAuth2AuthenticationDetails dateils = (OAuth2AuthenticationDetails) authentication.getDetails(); | |||
requestTemplate.header(HttpHeaders.AUTHORIZATION, | |||
String.format("%s %s", SecurityConstant.BEARER_TOKEN_TYPE, dateils.getTokenValue())); | |||
} | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
package com.tuoheng.common.security.security; | |||
import com.tuoheng.common.security.constant.SecurityConstant; | |||
import com.tuoheng.common.security.entity.SecurityUser; | |||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.GrantedAuthority; | |||
import org.springframework.security.core.authority.AuthorityUtils; | |||
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter; | |||
import org.springframework.util.StringUtils; | |||
import java.util.Collection; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
public class CustomUserAuthenticationConverter implements UserAuthenticationConverter { | |||
private static final String N_A = "N/A"; | |||
@Override | |||
public Map<String, ?> convertUserAuthentication(Authentication authentication) { | |||
Map<String, Object> authMap = new LinkedHashMap<>(); | |||
authMap.put(USERNAME, authentication.getName()); | |||
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) { | |||
authMap.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities())); | |||
} | |||
return authMap; | |||
} | |||
@Override | |||
public Authentication extractAuthentication(Map<String, ?> map) { | |||
if (map.containsKey(USERNAME)) { | |||
Collection<? extends GrantedAuthority> authorities = getAuthorities(map); | |||
// 自定义对象,这里可以根据实际需要进行设置 | |||
Integer userId = Integer.valueOf(map.get(SecurityConstant.DETAILS_USER_ID).toString()); | |||
String username = map.get(SecurityConstant.DETAILS_USERNAME).toString(); | |||
SecurityUser user = new SecurityUser(userId, username, N_A, true, true, true, true, authorities); | |||
return new UsernamePasswordAuthenticationToken(user, N_A, authorities); | |||
} | |||
return null; | |||
} | |||
/** | |||
* 获取权限资源信息 | |||
* | |||
* @param map | |||
* @return | |||
*/ | |||
private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) { | |||
Object authorities = map.get(AUTHORITIES); | |||
if (authorities instanceof String) { | |||
return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities); | |||
} | |||
if (authorities instanceof Collection) { | |||
return AuthorityUtils.commaSeparatedStringToAuthorityList( | |||
StringUtils.collectionToCommaDelimitedString((Collection<?>) authorities)); | |||
} | |||
throw new IllegalArgumentException("Authorities must be either a String or a Collection"); | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
package com.tuoheng.common.security.security; | |||
import org.codehaus.jackson.map.ObjectMapper; | |||
import org.springframework.security.access.AccessDeniedException; | |||
import org.springframework.security.web.access.AccessDeniedHandler; | |||
import org.springframework.stereotype.Component; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import java.io.IOException; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
@Component | |||
public class OAuthAccessDeniedHandler implements AccessDeniedHandler { | |||
@Override | |||
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { | |||
response.setContentType("application/json;charset=UTF-8"); | |||
Map<String, Object> map = new HashMap<String, Object>(); | |||
map.put("code", 401); | |||
map.put("msg", "权限不足"); | |||
map.put("data", accessDeniedException.getMessage()); | |||
map.put("success", false); | |||
map.put("path", request.getServletPath()); | |||
map.put("timestamp", String.valueOf(new Date().getTime())); | |||
ObjectMapper mapper = new ObjectMapper(); | |||
response.setContentType("application/json"); | |||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | |||
response.getWriter().write(mapper.writeValueAsString(map)); | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
package com.tuoheng.common.security.security; | |||
import com.alibaba.fastjson.JSONObject; | |||
import com.alibaba.fastjson.serializer.SerializerFeature; | |||
import com.tuoheng.common.security.utils.JsonResult; | |||
import org.springframework.http.HttpStatus; | |||
import org.springframework.security.core.AuthenticationException; | |||
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; | |||
import org.springframework.security.web.AuthenticationEntryPoint; | |||
import org.springframework.stereotype.Component; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import java.io.IOException; | |||
@Component | |||
public class OAuthAuthExceptionEntryPoint implements AuthenticationEntryPoint { | |||
@Override | |||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { | |||
response.setStatus(HttpStatus.OK.value()); | |||
response.setHeader("Content-Type", "application/json;charset=UTF-8"); | |||
// httpServletResponse.setContentType("application/json; charset=utf-8"); | |||
// httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | |||
Throwable cause = authException.getCause(); | |||
try { | |||
if (cause instanceof InvalidTokenException) { | |||
// 无效的token | |||
JsonResult jsonResult = new JsonResult(); | |||
response.getWriter().write(JSONObject.toJSONString(jsonResult.error(401, "请先登录"), SerializerFeature.WriteMapNullValue)); | |||
} else { | |||
// 访问此资源需要完全的身份验证 | |||
JsonResult jsonResult = new JsonResult(); | |||
response.getWriter().write(JSONObject.toJSONString(jsonResult.error(401, "访问此资源需要完全的身份验证"), SerializerFeature.WriteMapNullValue)); | |||
} | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
package com.tuoheng.common.security.security; | |||
import com.tuoheng.common.security.config.ResourceServerConfig; | |||
import org.springframework.beans.factory.support.BeanDefinitionBuilder; | |||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; | |||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; | |||
import org.springframework.core.type.AnnotationMetadata; | |||
import org.springframework.util.StringUtils; | |||
/** | |||
* 注册自动加载类 | |||
*/ | |||
public class SecurityImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { | |||
@Override | |||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { | |||
// ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry); | |||
Class<ResourceServerConfig> aClass = ResourceServerConfig.class; | |||
String beanName = StringUtils.uncapitalize(aClass.getSimpleName()); | |||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ResourceServerConfig.class); | |||
registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition()); | |||
} | |||
} |
@@ -0,0 +1,99 @@ | |||
package com.tuoheng.common.security.utils; | |||
import java.io.Serializable; | |||
/** | |||
* JSON回应类 | |||
* | |||
* @author 牧羊人 | |||
* @date 2019/11/28 | |||
*/ | |||
public class JsonResult<T> implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* 成功 | |||
*/ | |||
public static final int SUCCESS = 0; | |||
/** | |||
* 失败 | |||
*/ | |||
public static final int error = -1; | |||
private int code; | |||
private String msg; | |||
private T data; | |||
public static <T> JsonResult<T> success() { | |||
return jsonResult(null, SUCCESS, "操作成功"); | |||
} | |||
public static <T> JsonResult<T> success(String msg) { | |||
return jsonResult(null, SUCCESS, msg); | |||
} | |||
public static <T> JsonResult<T> success(T data) { | |||
return jsonResult(data, SUCCESS, "操作成功"); | |||
} | |||
public static <T> JsonResult<T> success(T data, String msg) { | |||
return jsonResult(data, SUCCESS, msg); | |||
} | |||
public static <T> JsonResult<T> error() { | |||
return jsonResult(null, error, "操作失败"); | |||
} | |||
public static <T> JsonResult<T> error(String msg) { | |||
return jsonResult(null, error, msg); | |||
} | |||
public static <T> JsonResult<T> error(T data) { | |||
return jsonResult(data, error, "操作失败"); | |||
} | |||
public static <T> JsonResult<T> error(T data, String msg) { | |||
return jsonResult(data, error, msg); | |||
} | |||
public static <T> JsonResult<T> error(int code, String msg) { | |||
return jsonResult(null, code, msg); | |||
} | |||
private static <T> JsonResult<T> jsonResult(T data, int code, String msg) { | |||
JsonResult<T> result = new JsonResult<>(); | |||
result.setCode(code); | |||
result.setData(data); | |||
result.setMsg(msg); | |||
return result; | |||
} | |||
public int getCode() { | |||
return code; | |||
} | |||
public void setCode(int code) { | |||
this.code = code; | |||
} | |||
public String getMsg() { | |||
return msg; | |||
} | |||
public void setMsg(String msg) { | |||
this.msg = msg; | |||
} | |||
public T getData() { | |||
return data; | |||
} | |||
public void setData(T data) { | |||
this.data = data; | |||
} | |||
} |
@@ -0,0 +1,78 @@ | |||
package com.tuoheng.common.security.utils; | |||
import com.tuoheng.common.security.entity.SecurityUser; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.context.SecurityContextHolder; | |||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | |||
public class SecurityUtils { | |||
/** | |||
* 获取用户ID | |||
* | |||
* @return | |||
*/ | |||
public static Integer getUserId() { | |||
return getSecurityUser().getUserId(); | |||
} | |||
/** | |||
* 获取用户认证信息 | |||
* | |||
* @return | |||
*/ | |||
public static SecurityUser getSecurityUser() { | |||
Authentication authentication = getAuthentication(); | |||
if (authentication == null) { | |||
return null; | |||
} | |||
return getLoginUser(authentication); | |||
} | |||
/** | |||
* 获取认证信息 | |||
* | |||
* @return | |||
*/ | |||
public static Authentication getAuthentication() { | |||
return SecurityContextHolder.getContext().getAuthentication(); | |||
} | |||
/** | |||
* 获取用户登录信息 | |||
* | |||
* @param authentication | |||
* @return | |||
*/ | |||
public static SecurityUser getLoginUser(Authentication authentication) { | |||
Object principal = authentication.getPrincipal(); | |||
if (principal instanceof SecurityUser) { | |||
return (SecurityUser) principal; | |||
} | |||
return null; | |||
} | |||
/** | |||
* 生成加密密码 | |||
* | |||
* @param password 密码 | |||
* @return 加密字符串 | |||
*/ | |||
public static String encryptPassword(String password) { | |||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); | |||
return passwordEncoder.encode(password); | |||
} | |||
/** | |||
* 比对密码是否一直 | |||
* | |||
* @param password 原始密码 | |||
* @param encodedPassword 加密后的密码 | |||
* @return | |||
*/ | |||
public static boolean matchesPassword(String password, String encodedPassword) { | |||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); | |||
return passwordEncoder.matches(password, encodedPassword); | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
<?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>tuoheng_freeway</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>1.0.0</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-feign</artifactId> | |||
<packaging>pom</packaging> | |||
<modules> | |||
<module>tuoheng-api-feign</module> | |||
</modules> | |||
</project> |
@@ -0,0 +1,4 @@ | |||
# tuoheng_freeway | |||
## tuoheng-freeway-feign | |||
freeway内部通过feign相互调用 |
@@ -0,0 +1,43 @@ | |||
<?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>tuoheng-feign</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>1.0.0</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-api-feign</artifactId> | |||
<dependencies> | |||
<!-- 核心基类依赖 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-common-core</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.cloud</groupId> | |||
<artifactId>spring-cloud-starter-openfeign</artifactId> | |||
</dependency> | |||
<!--mybatis-plus 起始依赖 --> | |||
<dependency> | |||
<groupId>com.baomidou</groupId> | |||
<artifactId>mybatis-plus-boot-starter</artifactId> | |||
<version>3.2.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<!-- JSON工具类 --> | |||
<dependency> | |||
<groupId>com.fasterxml.jackson.core</groupId> | |||
<artifactId>jackson-databind</artifactId> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -0,0 +1,15 @@ | |||
package com.tuoheng.api.feign.feign; | |||
import com.tuoheng.common.core.utils.JsonResult; | |||
import org.springframework.cloud.openfeign.FeignClient; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
@FeignClient(value = "tuoheng-freeway-admin") | |||
public interface ApiClient { | |||
/** | |||
* | |||
* @return | |||
*/ | |||
@GetMapping("/test") | |||
JsonResult test(); | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.tuoheng.api.feign.feign.fallback; | |||
import com.tuoheng.api.feign.feign.ApiClient; | |||
import com.tuoheng.common.core.utils.JsonResult; | |||
import org.springframework.stereotype.Component; | |||
/** | |||
* @Author xiaoying | |||
* @Date 2022/10/14 14:58 | |||
*/ | |||
@Component | |||
public class ApiClientFallback implements ApiClient { | |||
@Override | |||
public JsonResult test() { | |||
return JsonResult.success(); | |||
} | |||
} | |||
@@ -0,0 +1,3 @@ | |||
#org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | |||
# com.tuoheng.api.feign.feign.fallback.ApiClientFallback | |||
@@ -0,0 +1,20 @@ | |||
<?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>tuoheng_freeway</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>1.0.0</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-service</artifactId> | |||
<packaging>pom</packaging> | |||
<modules> | |||
<module>tuoheng-admin</module> | |||
<module>tuoheng-miniprogram</module> | |||
<module>tuoheng-api</module> | |||
</modules> | |||
</project> |
@@ -0,0 +1,213 @@ | |||
<?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>tuoheng-service</artifactId> | |||
<groupId>com.tuoheng</groupId> | |||
<version>1.0.0</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>tuoheng-admin</artifactId> | |||
<!-- 依赖声明管理 --> | |||
<dependencies> | |||
<!-- 核心基类模块 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-common-core</artifactId> | |||
</dependency> | |||
<!-- Feign调用层系统API接口 --> | |||
<dependency> | |||
<groupId>com.tuoheng</groupId> | |||
<artifactId>tuoheng-system-feign</artifactId> | |||
</dependency> | |||
<!-- Consul注册中心起始依赖 --> | |||
<dependency> | |||
<groupId>org.springframework.cloud</groupId> | |||
<artifactId>spring-cloud-starter-consul-discovery</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-web</artifactId> | |||
</dependency> | |||
<!-- MySql驱动 --> | |||
<dependency> | |||
<groupId>mysql</groupId> | |||
<artifactId>mysql-connector-java</artifactId> | |||
<scope>runtime</scope> | |||
</dependency> | |||
<!-- 引入阿里数据库连接池 --> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>druid-spring-boot-starter</artifactId> | |||
<version>1.1.10</version> | |||
</dependency> | |||
<!--mybatis-plus 代码自动生成 --> | |||
<dependency> | |||
<groupId>com.baomidou</groupId> | |||
<artifactId>mybatis-plus-generator</artifactId> | |||
<version>3.2.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>vod20170321</artifactId> | |||
<version>2.16.8</version> | |||
</dependency> | |||
<!--阿里云视频点播--> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>aliyun-java-sdk-core</artifactId> | |||
<version>4.5.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.aliyun.oss</groupId> | |||
<artifactId>aliyun-sdk-oss</artifactId> | |||
<version>3.10.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>aliyun-java-sdk-vod</artifactId> | |||
<version>2.15.11</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba</groupId> | |||
<artifactId>fastjson</artifactId> | |||
<version>1.2.28</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.json</groupId> | |||
<artifactId>json</artifactId> | |||
<version>20170516</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.code.gson</groupId> | |||
<artifactId>gson</artifactId> | |||
<version>2.8.2</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.aliyun</groupId> | |||
<artifactId>aliyun-java-sdk-kms</artifactId> | |||
<version>2.10.1</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.aliyun.vod</groupId> | |||
<artifactId>upload</artifactId> | |||
<version>1.4.14</version> | |||
<scope>system</scope> | |||
<systemPath>${project.basedir}/src/main/resources/lib/aliyun-java-vod-upload-1.4.14.jar</systemPath> | |||
</dependency> | |||
</dependencies> | |||
<!-- 环境变量配置 --> | |||
<profiles> | |||
<!-- 本地开发环境 --> | |||
<profile> | |||
<id>local</id> | |||
<properties> | |||
<package.environment>local</package.environment> | |||
</properties> | |||
<activation> | |||
<activeByDefault>true</activeByDefault> | |||
</activation> | |||
</profile> | |||
<!-- 开发环境 --> | |||
<profile> | |||
<id>dev</id> | |||
<properties> | |||
<package.environment>dev</package.environment> | |||
</properties> | |||
</profile> | |||
<!-- 测试环境 --> | |||
<profile> | |||
<id>test</id> | |||
<properties> | |||
<package.environment>test</package.environment> | |||
</properties> | |||
</profile> | |||
<!-- 生产环境 --> | |||
<profile> | |||
<id>prod</id> | |||
<properties> | |||
<package.environment>prod</package.environment> | |||
</properties> | |||
</profile> | |||
</profiles> | |||
<!-- 环境变量构建 --> | |||
<build> | |||
<finalName>tuoheng_freeway_admin</finalName> | |||
<resources> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>true</filtering> | |||
</resource> | |||
<resource> | |||
<directory>src/main/java</directory> | |||
<includes> | |||
<include>**/*.*</include> | |||
</includes> | |||
<excludes> | |||
<exclude>**/*.java</exclude> | |||
</excludes> | |||
</resource> | |||
<resource> | |||
<directory>src/main/resources</directory> | |||
<filtering>true</filtering> | |||
<targetPath>WEB-INF/classes</targetPath> | |||
<includes> | |||
<include>application-${package.environment}.yml</include> | |||
</includes> | |||
</resource> | |||
</resources> | |||
<pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-maven-plugin</artifactId> | |||
<version>2.1.11.RELEASE</version> | |||
<configuration> | |||
<finalName>${project.build.finalName}</finalName> | |||
</configuration> | |||
<executions> | |||
<execution> | |||
<goals> | |||
<goal>repackage</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
</plugins> | |||
</pluginManagement> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-resources-plugin</artifactId> | |||
<version>3.1.0</version> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-maven-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<goals> | |||
<goal>repackage</goal> | |||
</goals> | |||
</execution> | |||
</executions> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -0,0 +1,22 @@ | |||
package com.tuoheng.admin; | |||
import org.mybatis.spring.annotation.MapperScan; | |||
import org.springframework.boot.SpringApplication; | |||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; | |||
import org.springframework.cloud.openfeign.EnableFeignClients; | |||
import org.springframework.scheduling.annotation.EnableAsync; | |||
@SpringBootApplication | |||
@EnableDiscoveryClient | |||
@EnableFeignClients(basePackages = "com.tuoheng") | |||
@MapperScan("com.tuoheng.**.**.mapper") | |||
@EnableAsync | |||
public class THAdminApplication { | |||
public static void main(String[] args) { | |||
SpringApplication.run(THAdminApplication.class, args); | |||
System.out.println("(♥◠‿◠)ノ゙ 微服务【业务服务】启动成功 ლ(´ڡ`ლ)゙"); | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
package com.tuoheng.admin.config; | |||
import com.tuoheng.admin.interceptor.UserInterceptor; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | |||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | |||
import java.nio.charset.StandardCharsets; | |||
/** | |||
* @Author xiaoying | |||
* @Date 2022/10/31 14:06 | |||
*/ | |||
//web相关配置 | |||
@Configuration | |||
public class AdminMvcConfig implements WebMvcConfigurer { | |||
@Bean | |||
public UserInterceptor setBean(){ | |||
//System.out.println("注入了handler"); | |||
return new UserInterceptor(); | |||
} | |||
@Override | |||
public void addInterceptors(InterceptorRegistry registry) { | |||
//添加拦截器 | |||
registry.addInterceptor(setBean()) | |||
//设置拦截的请求 | |||
.addPathPatterns("/**") | |||
//排除拦截的请求 | |||
.excludePathPatterns( | |||
"/user/login", //登录 | |||
"/user/loginout", //退出 | |||
"/error" //springBoot异常处理的默认请求 | |||
); | |||
} | |||
} |
@@ -0,0 +1,80 @@ | |||
package com.tuoheng.admin.config; | |||
import com.aliyun.teaopenapi.models.Config; | |||
import com.aliyun.vod20170321.Client; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.context.annotation.Lazy; | |||
/** | |||
* 阿里云点播服务配置类 | |||
* | |||
* @author WangHaoran | |||
* @since 2022-03-11 | |||
*/ | |||
@Configuration | |||
@Slf4j | |||
public class AliyuncsVodConfig { | |||
/** | |||
* 阿里云endpoint | |||
*/ | |||
public final static String ENDPOINT = "vod.cn-shanghai.aliyuncs.com"; | |||
/** | |||
* 账号 | |||
*/ | |||
public static String accessKeyId; | |||
/** | |||
* 密码 | |||
*/ | |||
public static String accessKeySecret; | |||
/** | |||
* 角色ARN | |||
*/ | |||
public static String roleArn; | |||
/** | |||
* Bucket名称 | |||
*/ | |||
public static String bucketName; | |||
@Value("${aliyuncsVod.accessKeyId}") | |||
public void setAccessKeyId(String accessKeyId) { | |||
AliyuncsVodConfig.accessKeyId = accessKeyId; | |||
} | |||
@Value("${aliyuncsVod.accessKeySecret}") | |||
public void setAccessKeySecret(String accessKeySecret) { | |||
AliyuncsVodConfig.accessKeySecret = accessKeySecret; | |||
} | |||
@Value("${aliyuncsVod.roleArn}") | |||
public void setRoleArn(String roleArn) { | |||
AliyuncsVodConfig.roleArn = roleArn; | |||
} | |||
@Value("${aliyuncsVod.bucketName}") | |||
public void setBucketName(String bucketName) { | |||
AliyuncsVodConfig.bucketName = bucketName; | |||
} | |||
@Bean | |||
@Lazy | |||
public Client vodClient() { | |||
try { | |||
Config config = new Config() | |||
.setAccessKeyId(accessKeyId) | |||
.setAccessKeySecret(accessKeySecret) | |||
.setEndpoint(ENDPOINT); | |||
return new Client(config); | |||
} catch (Exception e) { | |||
log.error("获取vodClient客户端失败", e); | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,70 @@ | |||
package com.tuoheng.admin.config; | |||
import lombok.Data; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.context.annotation.Configuration; | |||
@Configuration | |||
@Data | |||
public class CommonConfig { | |||
/** | |||
* 图片域名 | |||
*/ | |||
public static String imageURL; | |||
/** | |||
* OSS域名 | |||
*/ | |||
public static String ossURL; | |||
/** | |||
* 视频域名 | |||
*/ | |||
public static String videoURL; | |||
/** | |||
* oidc地址 | |||
*/ | |||
public static String oidcUrl; | |||
/** | |||
* 图片域名赋值 | |||
* | |||
* @param url 域名地址 | |||
*/ | |||
@Value("${tuoheng.image-url}") | |||
public void setImageURL(String url) { | |||
imageURL = url; | |||
} | |||
/** | |||
* 阿里云OSS域名 | |||
* | |||
* @param url 图片地址 | |||
*/ | |||
@Value("${tuoheng.oss-url}") | |||
public void setOssURL(String url) { | |||
ossURL = url; | |||
} | |||
/** | |||
* 视频域名赋值 | |||
* | |||
* @param url 域名地址 | |||
*/ | |||
@Value("${tuoheng.video-url}") | |||
public void setVideoURL(String url) { | |||
videoURL = url; | |||
} | |||
/** | |||
* oidc地址赋值 | |||
* | |||
* @param url 通道地址 | |||
*/ | |||
@Value("${tuoheng.oidc-url}") | |||
public void setOidcUrl(String url) { | |||
oidcUrl = url; | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
package com.tuoheng.admin.config; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.http.client.ClientHttpRequestFactory; | |||
import org.springframework.http.client.SimpleClientHttpRequestFactory; | |||
import org.springframework.web.client.RestTemplate; | |||
/** | |||
* 第三方配置 | |||
* @Author: xiaoying | |||
* @Date: 2022/10/18 9:02 | |||
*/ | |||
@Configuration | |||
public class RestTemplateConfig { | |||
@Bean | |||
public RestTemplate restTemplate(ClientHttpRequestFactory factory){ | |||
return new RestTemplate(factory); | |||
} | |||
@Bean | |||
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){ | |||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); | |||
factory.setConnectTimeout(30000); | |||
factory.setReadTimeout(30000); | |||
return factory; | |||
} | |||
} |
@@ -0,0 +1,86 @@ | |||
package com.tuoheng.admin.config; | |||
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Value; | |||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
/** | |||
* xxl-job config | |||
* | |||
* @author xuxueli 2017-04-28 | |||
*/ | |||
@Configuration | |||
@ConditionalOnProperty(name = XxlJobConfig.XXL_ENABLE, havingValue = XxlJobConfig.TRUE) | |||
public class XxlJobConfig { | |||
public static final String XXL_ENABLE = "xxl.enable"; | |||
public static final String TRUE = "true"; | |||
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); | |||
@Value("${xxl.job.admin.addresses}") | |||
private String adminAddresses; | |||
@Value("${xxl.job.accessToken}") | |||
private String accessToken; | |||
@Value("${xxl.job.executor.appname}") | |||
private String appname; | |||
@Value("${xxl.job.executor.address}") | |||
private String address; | |||
@Value("${xxl.job.executor.ip}") | |||
private String ip; | |||
@Value("${xxl.job.executor.port}") | |||
private int port; | |||
@Value("${xxl.job.executor.logpath}") | |||
private String logPath; | |||
@Value("${xxl.job.executor.logretentiondays}") | |||
private int logRetentionDays; | |||
@Bean | |||
public XxlJobSpringExecutor xxlJobExecutor() throws InterruptedException { | |||
logger.info(">>>>>>>>>>> xxl-job config init."); | |||
Thread.sleep(5000); | |||
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); | |||
xxlJobSpringExecutor.setAdminAddresses(adminAddresses); | |||
xxlJobSpringExecutor.setAppname(appname); | |||
xxlJobSpringExecutor.setAddress(address); | |||
xxlJobSpringExecutor.setIp(ip); | |||
xxlJobSpringExecutor.setPort(port); | |||
xxlJobSpringExecutor.setAccessToken(accessToken); | |||
xxlJobSpringExecutor.setLogPath(logPath); | |||
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); | |||
return xxlJobSpringExecutor; | |||
} | |||
/** | |||
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP; | |||
* | |||
* 1、引入依赖: | |||
* <dependency> | |||
* <groupId>org.springframework.cloud</groupId> | |||
* <artifactId>spring-cloud-commons</artifactId> | |||
* <version>${version}</version> | |||
* </dependency> | |||
* | |||
* 2、配置文件,或者容器启动变量 | |||
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' | |||
* | |||
* 3、获取IP | |||
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); | |||
*/ | |||
} |
@@ -0,0 +1,223 @@ | |||
package com.tuoheng.admin.config.sql; | |||
import com.tuoheng.common.core.utils.LogUtil; | |||
import com.tuoheng.common.core.utils.StringUtils; | |||
import java.beans.IntrospectionException; | |||
import java.beans.PropertyDescriptor; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import java.util.Set; | |||
/** | |||
* 包含like的SQL语句转义模板 | |||
* @author chenyukun | |||
*/ | |||
public abstract class AbstractLikeSqlConverter<T> { | |||
/** | |||
* SQL语句like使用关键字% | |||
*/ | |||
private final static String LIKE_SQL_KEY = "%"; | |||
/** | |||
* SQL语句需要转义的关键字 | |||
*/ | |||
private final static String[] ESCAPE_CHAR = new String[]{LIKE_SQL_KEY, "_", "\\"}; | |||
/** | |||
* mybatis-plus中like的SQL语句样式 | |||
*/ | |||
private final static String MYBATIS_PLUS_LIKE_SQL = " like ?"; | |||
/** | |||
* mybatis-plus中参数前缀 | |||
*/ | |||
private final static String MYBATIS_PLUS_WRAPPER_PREFIX = "ew.paramNameValuePairs."; | |||
/** | |||
* mybatis-plus中参数键 | |||
*/ | |||
final static String MYBATIS_PLUS_WRAPPER_KEY = "ew"; | |||
/** | |||
* mybatis-plus中参数分隔符 | |||
*/ | |||
final static String MYBATIS_PLUS_WRAPPER_SEPARATOR = "."; | |||
/** | |||
* mybatis-plus中参数分隔符替换器 | |||
*/ | |||
final static String MYBATIS_PLUS_WRAPPER_SEPARATOR_REGEX = "\\."; | |||
/** | |||
* 已经替换过的标记 | |||
*/ | |||
final static String REPLACED_LIKE_KEYWORD_MARK = "replaced.keyword"; | |||
/** | |||
* 转义特殊字符 | |||
* | |||
* @param sql SQL语句 | |||
* @param fields 字段列表 | |||
* @param parameter 参数对象 | |||
*/ | |||
public void convert(String sql, Set<String> fields, T parameter) { | |||
for (String field : fields) { | |||
if (this.hasMybatisPlusLikeSql(sql)) { | |||
if (this.hasWrapper(field)) { | |||
// 第一种情况:在业务层进行条件构造产生的模糊查询关键字,使用QueryWrapper,LambdaQueryWrapper | |||
this.transferWrapper(field, parameter); | |||
} else { | |||
// 第二种情况:未使用条件构造器,但是在service层进行了查询关键字与模糊查询符`%`手动拼接 | |||
this.transferSelf(field, parameter); | |||
} | |||
} else { | |||
// 第三种情况:在Mapper类的注解SQL中进行了模糊查询的拼接 | |||
this.transferSplice(field, parameter); | |||
} | |||
} | |||
} | |||
/** | |||
* 转义条件构造的特殊字符 | |||
* 在业务层进行条件构造产生的模糊查询关键字,使用QueryWrapper,LambdaQueryWrapper | |||
* | |||
* @param field 字段名称 | |||
* @param parameter 参数对象 | |||
*/ | |||
public abstract void transferWrapper(String field, T parameter); | |||
/** | |||
* 转义自定义条件拼接的特殊字符 | |||
* 未使用条件构造器,但是在service层进行了查询关键字与模糊查询符`%`手动拼接 | |||
* | |||
* @param field 字段名称 | |||
* @param parameter 参数对象 | |||
*/ | |||
public abstract void transferSelf(String field, T parameter); | |||
/** | |||
* 转义自定义条件拼接的特殊字符 | |||
* 在Mapper类的注解SQL中进行了模糊查询的拼接 | |||
* | |||
* @param field 字段名称 | |||
* @param parameter 参数对象 | |||
*/ | |||
public abstract void transferSplice(String field, T parameter); | |||
/** | |||
* 转义通配符 | |||
* | |||
* @param before 待转义字符串 | |||
* @return 转义后字符串 | |||
*/ | |||
String escapeChar(String before) { | |||
if (StringUtils.isNotBlank(before)) { | |||
before = before.replaceAll("\\\\", "\\\\\\\\"); | |||
before = before.replaceAll("_", "\\\\_"); | |||
before = before.replaceAll("%", "\\\\%"); | |||
} | |||
return before; | |||
} | |||
/** | |||
* 是否包含需要转义的字符 | |||
* | |||
* @param obj 待判断的对象 | |||
* @return true/false | |||
*/ | |||
boolean hasEscapeChar(Object obj) { | |||
if (!(obj instanceof String)) { | |||
return false; | |||
} | |||
return this.hasEscapeChar((String) obj); | |||
} | |||
/** | |||
* 处理对象like问题 | |||
* | |||
* @param field 对象字段 | |||
* @param parameter 对象 | |||
*/ | |||
void resolveObj(String field, Object parameter) { | |||
if (parameter == null || StringUtils.isBlank(field)) { | |||
return; | |||
} | |||
try { | |||
PropertyDescriptor descriptor = new PropertyDescriptor(field, parameter.getClass()); | |||
Method readMethod = descriptor.getReadMethod(); | |||
Object param = readMethod.invoke(parameter); | |||
if (this.hasEscapeChar(param)) { | |||
Method setMethod = descriptor.getWriteMethod(); | |||
setMethod.invoke(parameter, this.escapeChar(param.toString())); | |||
} else if (this.cascade(field)) { | |||
int index = field.indexOf(MYBATIS_PLUS_WRAPPER_SEPARATOR) + 1; | |||
this.resolveObj(field.substring(index), param); | |||
} | |||
} catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) { | |||
LogUtil.LOGGER.error("反射 {} 的 {} get/set方法出现异常", parameter, field, e); | |||
} | |||
} | |||
/** | |||
* 判断是否是级联属性 | |||
* | |||
* @param field 字段名 | |||
* @return true/false | |||
*/ | |||
boolean cascade(String field) { | |||
if (StringUtils.isBlank(field)) { | |||
return false; | |||
} | |||
return field.contains(MYBATIS_PLUS_WRAPPER_SEPARATOR) && !this.hasWrapper(field); | |||
} | |||
/** | |||
* 是否包含mybatis-plus的包含like的SQL语句格式 | |||
* | |||
* @param sql 完整SQL语句 | |||
* @return true/false | |||
*/ | |||
private boolean hasMybatisPlusLikeSql(String sql) { | |||
if (StringUtils.isBlank(sql)) { | |||
return false; | |||
} | |||
return sql.toLowerCase().contains(MYBATIS_PLUS_LIKE_SQL); | |||
} | |||
/** | |||
* 判断是否使用mybatis-plus条件构造器 | |||
* | |||
* @param field 字段 | |||
* @return true/false | |||
*/ | |||
private boolean hasWrapper(String field) { | |||
if (StringUtils.isBlank(field)) { | |||
return false; | |||
} | |||
return field.contains(MYBATIS_PLUS_WRAPPER_PREFIX); | |||
} | |||
/** | |||
* 判断字符串是否含有需要转义的字符 | |||
* | |||
* @param str 待判断的字符串 | |||
* @return true/false | |||
*/ | |||
private boolean hasEscapeChar(String str) { | |||
if (StringUtils.isBlank(str)) { | |||
return false; | |||
} | |||
for (String s : ESCAPE_CHAR) { | |||
if (str.contains(s)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
//package com.tuoheng.admin.config.sql; | |||
// | |||
//import com.alibaba.druid.pool.DruidDataSource; | |||
//import com.alibaba.druid.support.http.StatViewServlet; | |||
//import com.alibaba.druid.support.http.WebStatFilter; | |||
//import org.springframework.boot.context.properties.ConfigurationProperties; | |||
//import org.springframework.boot.web.servlet.FilterRegistrationBean; | |||
//import org.springframework.boot.web.servlet.ServletRegistrationBean; | |||
//import org.springframework.context.annotation.Bean; | |||
//import org.springframework.context.annotation.Configuration; | |||
// | |||
//import javax.servlet.Filter; | |||
//import javax.servlet.Servlet; | |||
//import javax.sql.DataSource; | |||
//import java.util.Arrays; | |||
//import java.util.HashMap; | |||
//import java.util.Map; | |||
// | |||
///** | |||
// * Druid连接池配置类 | |||
// * @author chenyukun | |||
// */ | |||
//@Configuration | |||
//public class DruidConfig { | |||
// | |||
// @ConfigurationProperties(prefix = "spring.datasource") | |||
// @Bean | |||
// public DataSource druid() { | |||
// return new DruidDataSource(); | |||
// } | |||
// | |||
// /** | |||
// * 配置Druid的监控 配置一个管理后台的Servlet | |||
// * @return | |||
// */ | |||
// @Bean | |||
// public ServletRegistrationBean statViewServlet() { | |||
// ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); | |||
// Map<String, String> initParams = new HashMap<>(4); | |||
// initParams.put("loginUsername", "admin"); | |||
// initParams.put("loginPassword", "123456"); | |||
// initParams.put("allow", ""); | |||
// initParams.put("deny", "192.168.15.21"); | |||
// bean.setInitParameters(initParams); | |||
// return bean; | |||
// } | |||
// | |||
// /** | |||
// * 配置一个web监管的filter | |||
// */ | |||
// @Bean | |||
// public FilterRegistrationBean webStatFilter() { | |||
// final FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(); | |||
// bean.setFilter(new WebStatFilter()); | |||
// Map<String, String> initParams = new HashMap<>(); | |||
// bean.setInitParameters(initParams); | |||
// bean.setUrlPatterns(Arrays.asList("/*")); | |||
// return bean; | |||
// } | |||
// | |||
//} |
@@ -0,0 +1,75 @@ | |||
package com.tuoheng.admin.config.sql; | |||
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; | |||
import java.util.Map; | |||
import java.util.Objects; | |||
/** | |||
* @author chenyukun | |||
*/ | |||
public class MapLikeSqlConverter extends AbstractLikeSqlConverter<Map> { | |||
@Override | |||
public void transferWrapper(String field, Map parameter) { | |||
AbstractWrapper wrapper = (AbstractWrapper) parameter.get(MYBATIS_PLUS_WRAPPER_KEY); | |||
parameter = wrapper.getParamNameValuePairs(); | |||
String[] keys = field.split(MYBATIS_PLUS_WRAPPER_SEPARATOR_REGEX); | |||
// ew.paramNameValuePairs.param1,截取字符串之后,获取第三个,即为参数名 | |||
String paramName = keys[2]; | |||
String mapKey = String.format("%s.%s", REPLACED_LIKE_KEYWORD_MARK, paramName); | |||
if (parameter.containsKey(mapKey) && Objects.equals(parameter.get(mapKey), true)) { | |||
return; | |||
} | |||
if (this.cascade(field)) { | |||
this.resolveCascadeObj(field, parameter); | |||
} else { | |||
Object param = parameter.get(paramName); | |||
if (this.hasEscapeChar(param)) { | |||
String paramStr = param.toString(); | |||
parameter.put(keys[2], String.format("%%%s%%", this.escapeChar(paramStr.substring(1, paramStr.length() - 1)))); | |||
} | |||
} | |||
parameter.put(mapKey, true); | |||
} | |||
@Override | |||
public void transferSelf(String field, Map parameter) { | |||
if (this.cascade(field)) { | |||
this.resolveCascadeObj(field, parameter); | |||
return; | |||
} | |||
Object param = parameter.get(field); | |||
if (this.hasEscapeChar(param)) { | |||
String paramStr = param.toString(); | |||
parameter.put(field, String.format("%%%s%%", this.escapeChar(paramStr.substring(1, paramStr.length() - 1)))); | |||
} | |||
} | |||
@Override | |||
public void transferSplice(String field, Map parameter) { | |||
if (this.cascade(field)) { | |||
this.resolveCascadeObj(field, parameter); | |||
return; | |||
} | |||
Object param = parameter.get(field); | |||
if (this.hasEscapeChar(param)) { | |||
parameter.put(field, this.escapeChar(param.toString())); | |||
} | |||
} | |||
/** | |||
* 处理级联属性 | |||
* | |||
* @param field 级联字段名 | |||
* @param parameter 参数Map对象 | |||
*/ | |||
private void resolveCascadeObj(String field, Map parameter) { | |||
int index = field.indexOf(MYBATIS_PLUS_WRAPPER_SEPARATOR); | |||
Object param = parameter.get(field.substring(0, index)); | |||
if (param == null) { | |||
return; | |||
} | |||
this.resolveObj(field.substring(index + 1), param); | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
package com.tuoheng.admin.config.sql; | |||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; | |||
import com.tuoheng.admin.config.sql.MybatisPlusInterceptor; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.transaction.annotation.EnableTransactionManagement; | |||
/** | |||
* 分页插件配置类 | |||
*/ | |||
@EnableTransactionManagement(proxyTargetClass = true) | |||
@Configuration | |||
public class MybatisPlusConfig { | |||
/** | |||
* 分页插件 | |||
* | |||
* @return | |||
*/ | |||
@Bean | |||
public PaginationInterceptor paginationInterceptor() { | |||
return new PaginationInterceptor(); | |||
} | |||
/** | |||
* 自定义拦截器 | |||
*/ | |||
@Bean | |||
public MybatisPlusInterceptor mybatisPlusInterceptor() { | |||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); | |||
return interceptor; | |||
} | |||
} |
@@ -0,0 +1,151 @@ | |||
package com.tuoheng.admin.config.sql; | |||
import com.tuoheng.common.core.utils.StringUtils; | |||
import org.apache.ibatis.executor.Executor; | |||
import org.apache.ibatis.mapping.BoundSql; | |||
import org.apache.ibatis.mapping.MappedStatement; | |||
import org.apache.ibatis.plugin.*; | |||
import org.apache.ibatis.session.ResultHandler; | |||
import org.apache.ibatis.session.RowBounds; | |||
import java.util.*; | |||
/** | |||
* like字符转义 | |||
* @author chenyukun | |||
*/ | |||
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, | |||
RowBounds.class, ResultHandler.class})}) | |||
public class MybatisPlusInterceptor implements Interceptor { | |||
/** | |||
* SQL语句like | |||
*/ | |||
private final static String SQL_LIKE = " like "; | |||
/** | |||
* SQL语句占位符 | |||
*/ | |||
private final static String SQL_PLACEHOLDER = "?"; | |||
/** | |||
* SQL语句占位符分隔 | |||
*/ | |||
private final static String SQL_PLACEHOLDER_REGEX = "\\?"; | |||
/** | |||
* 所有的转义器 | |||
*/ | |||
private static Map<Class, AbstractLikeSqlConverter> converterMap = new HashMap<>(4); | |||
static { | |||
converterMap.put(Map.class, new MapLikeSqlConverter()); | |||
// converterMap.put(Object.class, new ObjectLikeSqlConverter()); | |||
} | |||
@Override | |||
public Object intercept(Invocation invocation) throws Throwable { | |||
// 获取sql及参数信息 | |||
Object[] args = invocation.getArgs(); | |||
MappedStatement statement = (MappedStatement) args[0]; | |||
Object parameterObject = args[1]; | |||
BoundSql boundSql = statement.getBoundSql(parameterObject); | |||
String sql = boundSql.getSql(); | |||
this.transferLikeSql(sql, parameterObject, boundSql); | |||
return invocation.proceed(); | |||
} | |||
@Override | |||
public Object plugin(Object target) { | |||
return Plugin.wrap(target, this); | |||
} | |||
@Override | |||
public void setProperties(Properties arg0) { | |||
} | |||
/** | |||
* 修改包含like的SQL语句 | |||
* | |||
* @param sql SQL语句 | |||
* @param parameterObject 参数对象 | |||
* @param boundSql 绑定SQL对象 | |||
*/ | |||
private void transferLikeSql(String sql, Object parameterObject, BoundSql boundSql) { | |||
// 判断是否有like关键字 | |||
if (!isEscape(sql)) { | |||
return; | |||
} | |||
sql = sql.replaceAll(" {2}", " "); | |||
// 获取关键字的个数(去重) | |||
Set<String> fields = this.getKeyFields(sql, boundSql); | |||
if (fields == null) { | |||
return; | |||
} | |||
// 此处可以增强,不止是支持Map对象,Map对象仅用于传入的条件为Map或者使用@Param传入的对象被Mybatis转为的Map | |||
AbstractLikeSqlConverter converter; | |||
// 对关键字进行特殊字符“清洗”,如果有特殊字符的,在特殊字符前添加转义字符(\) | |||
if (parameterObject instanceof Map) { | |||
converter = converterMap.get(Map.class); | |||
} else { | |||
converter = converterMap.get(Object.class); | |||
} | |||
converter.convert(sql, fields, parameterObject); | |||
} | |||
/** | |||
* 是否需要转义 | |||
* | |||
* @param sql SQL语句 | |||
* @return true/false | |||
*/ | |||
private boolean isEscape(String sql) { | |||
return this.hasLike(sql) && this.hasPlaceholder(sql); | |||
} | |||
/** | |||
* 判断SQL语句中是否含有like关键字 | |||
* | |||
* @param str SQL语句 | |||
* @return true/false | |||
*/ | |||
private boolean hasLike(String str) { | |||
if (StringUtils.isBlank(str)) { | |||
return false; | |||
} | |||
return str.toLowerCase().contains(SQL_LIKE); | |||
} | |||
/** | |||
* 判断SQL语句中是否包含SQL占位符 | |||
* | |||
* @param str SQL语句 | |||
* @return true/false | |||
*/ | |||
private boolean hasPlaceholder(String str) { | |||
if (StringUtils.isBlank(str)) { | |||
return false; | |||
} | |||
return str.toLowerCase().contains(SQL_PLACEHOLDER); | |||
} | |||
/** | |||
* 获取需要替换的所有字段集合 | |||
* | |||
* @param sql 完整SQL语句 | |||
* @param boundSql 绑定的SQL对象 | |||
* @return 字段集合列表 | |||
*/ | |||
private Set<String> getKeyFields(String sql, BoundSql boundSql) { | |||
String[] params = sql.split(SQL_PLACEHOLDER_REGEX); | |||
Set<String> fields = new HashSet<>(); | |||
for (int i = 0; i < params.length; i++) { | |||
if (this.hasLike(params[i])) { | |||
String field = boundSql.getParameterMappings().get(i).getProperty(); | |||
fields.add(field); | |||
} | |||
} | |||
return fields; | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
package com.tuoheng.admin.config.sql; | |||
import com.tuoheng.admin.config.sql.AbstractLikeSqlConverter; | |||
/** | |||
* 通用参数的转换器 | |||
* | |||
* @author chenyukun | |||
*/ | |||
public class ObjectLikeSqlConverter extends AbstractLikeSqlConverter<Object> { | |||
@Override | |||
public void transferWrapper(String field, Object parameter) { | |||
// 尚未发现这种情况 | |||
} | |||
@Override | |||
public void transferSelf(String field, Object parameter) { | |||
// 尚未发现这种情况 | |||
} | |||
@Override | |||
public void transferSplice(String field, Object parameter) { | |||
this.resolveObj(field, parameter); | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
package com.tuoheng.admin.constant; | |||
/** | |||
* oidc认证平台常量url | |||
* @Author xiaoying | |||
* @Date 2022/10/18 10:52 | |||
*/ | |||
public class OidcUrlConstant { | |||
/** | |||
* 判断username是否已存在 | |||
*/ | |||
public static String USER_JUDGE = "/oidc/admin/user/judge/create/{username}"; | |||
/** | |||
* 判断username是否已存在 | |||
*/ | |||
public static String USER_CREATE = "/oidc/admin/user/create"; | |||
/** | |||
* 判断username是否已存在 | |||
*/ | |||
public static String USER_UPDATEPASS = "/oidc/admin/user/updatePass"; | |||
/** | |||
* 修改用户角色 | |||
*/ | |||
public static String USER_UPDATEROLE = "/oidc/admin/user/updateRole"; | |||
} |
@@ -0,0 +1,35 @@ | |||
package com.tuoheng.admin.constant; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
/** | |||
* <p> | |||
* 用户管理 模块常量 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-04-20 | |||
*/ | |||
public class UserConstant { | |||
/** | |||
* 性别 | |||
*/ | |||
public static Map<Integer, String> USER_GENDER_LIST = new HashMap<Integer, String>() { | |||
{ | |||
put(1, "男"); | |||
put(2, "女"); | |||
put(3, "保密"); | |||
} | |||
}; | |||
/** | |||
* 状态 | |||
*/ | |||
public static Map<Integer, String> USER_STATUS_LIST = new HashMap<Integer, String>() { | |||
{ | |||
put(1, "正常"); | |||
put(2, "禁用"); | |||
} | |||
}; | |||
} |
@@ -0,0 +1,63 @@ | |||
package com.tuoheng.admin.controller; | |||
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse; | |||
import com.tuoheng.admin.service.IAliyunOssService; | |||
import com.tuoheng.admin.service.impl.AliyunOssServiceImpl; | |||
import com.tuoheng.common.core.utils.JsonResult; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import java.net.URL; | |||
import java.util.List; | |||
/** | |||
* 阿里云对象存储OSS 前端控制器 | |||
* | |||
* @author WangHaoran | |||
* @since 2022-03-15 | |||
*/ | |||
@RestController | |||
@RequestMapping("/aliyunOss") | |||
public class AliyunOssController { | |||
@Autowired | |||
IAliyunOssService aliyunOssService; | |||
/** | |||
* 生成单个以GET方法访问的签名URL | |||
* | |||
* @param objectName 填写Object完整路径,例如exampleobject.txt。Object完整路径中不能包含Bucket名称。 | |||
* @return | |||
* @throws Throwable | |||
*/ | |||
@GetMapping("/generatePresignedUrl") | |||
public URL generatePresignedUrl(String objectName) throws Throwable { | |||
return aliyunOssService.generatePresignedUrl(objectName); | |||
} | |||
/** | |||
* 生成多个以GET方法访问的签名URL | |||
* | |||
* @param objectNameList 填写Object完整路径,例如exampleobject.txt。Object完整路径中不能包含Bucket名称。 | |||
* 此处请填写多个Object完整路径,用于一次性获取多个Object的签名URL。 | |||
* @return | |||
* @throws Throwable | |||
*/ | |||
@GetMapping("/generatePresignedUrls") | |||
public List<URL> generatePresignedUrls(String[] objectNameList) throws Throwable { | |||
return aliyunOssService.generatePresignedUrls(objectNameList); | |||
} | |||
/** | |||
* 获取securityToken | |||
* | |||
* @return | |||
*/ | |||
@GetMapping("/getSecurityToken") | |||
public JsonResult getSecurityToken() { | |||
AssumeRoleResponse.Credentials credentials = AliyunOssServiceImpl.getSecurityToken("SessionTest"); | |||
return JsonResult.success(credentials); | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
package com.tuoheng.admin.controller; | |||
import com.tuoheng.admin.service.IAliyuncsVodService; | |||
import com.tuoheng.common.core.utils.JsonResult; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RestController; | |||
/** | |||
* 阿里云点播服务 前端控制器 | |||
* | |||
* @author WangHaoran | |||
* @since 2022-03-10 | |||
*/ | |||
@RestController | |||
@RequestMapping("/aliyuncsVod") | |||
public class AliyuncsVodController { | |||
@Autowired | |||
IAliyuncsVodService aliyuncsVodService; | |||
/** | |||
* 获取音视频上传地址和凭证 | |||
* | |||
* @param title | |||
* @param fileName | |||
* @return | |||
*/ | |||
@GetMapping("/createUploadVideo") | |||
public JsonResult createUploadVideo(String title, String fileName) { | |||
return JsonResult.success(aliyuncsVodService.createUploadVideo(title, fileName)); | |||
} | |||
/** | |||
* 刷新音/视频上传凭证 | |||
* | |||
* @param videoId 音频或视频ID | |||
* @return | |||
*/ | |||
@GetMapping("/refreshUploadVideo") | |||
public JsonResult refreshUploadVideo(String videoId) { | |||
return JsonResult.success(aliyuncsVodService.refreshUploadVideo(videoId)); | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
package com.tuoheng.admin.dto; | |||
import lombok.Data; | |||
@Data | |||
public class ClientRoleDto { | |||
private String clientId; | |||
private Integer roleId; | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.tuoheng.admin.dto; | |||
import lombok.Data; | |||
/** | |||
* 重置密码 | |||
*/ | |||
@Data | |||
public class ResetPwdDto { | |||
/** | |||
* 用户ID | |||
*/ | |||
private String id; | |||
} |
@@ -0,0 +1,21 @@ | |||
package com.tuoheng.admin.dto; | |||
import lombok.Data; | |||
/** | |||
* 修改密码Dto | |||
*/ | |||
@Data | |||
public class UpdatePwdDto { | |||
/** | |||
* 旧密码 | |||
*/ | |||
private String oldPassword; | |||
/** | |||
* 新密码 | |||
*/ | |||
private String newPassword; | |||
} |
@@ -0,0 +1,46 @@ | |||
package com.tuoheng.admin.dto; | |||
import lombok.Data; | |||
/** | |||
* 更新个人资料 | |||
*/ | |||
@Data | |||
public class UpdateUserInfoDto { | |||
/** | |||
* 个人头像 | |||
*/ | |||
private String avatar; | |||
/** | |||
* 真实姓名 | |||
*/ | |||
private String realname; | |||
/** | |||
* 昵称 | |||
*/ | |||
private String nickname; | |||
/** | |||
* 性别 | |||
*/ | |||
private String gender; | |||
/** | |||
* 手机号码 | |||
*/ | |||
private String mobile; | |||
/** | |||
* 个人简介 | |||
*/ | |||
private String intro; | |||
/** | |||
* 个性签名 | |||
*/ | |||
private String signature; | |||
} |
@@ -0,0 +1,51 @@ | |||
package com.tuoheng.admin.entity; | |||
import com.baomidou.mybatisplus.annotation.TableName; | |||
import com.tuoheng.common.core.common.BaseEntity; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import lombok.experimental.Accessors; | |||
/** | |||
* <p> | |||
* 系统角色表 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-10-31 | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = true) | |||
@Accessors(chain = true) | |||
@TableName("th_role") | |||
public class Role extends BaseEntity { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* 角色名称 | |||
*/ | |||
private String name; | |||
/** | |||
* 角色标签 | |||
*/ | |||
private String code; | |||
/** | |||
* 状态:1正常 2禁用 | |||
*/ | |||
private Integer status; | |||
/** | |||
* 备注 | |||
*/ | |||
private String note; | |||
/** | |||
* 排序 | |||
*/ | |||
private Integer sort; | |||
} |
@@ -0,0 +1,218 @@ | |||
package com.tuoheng.admin.entity; | |||
import com.baomidou.mybatisplus.annotation.*; | |||
import com.fasterxml.jackson.annotation.JsonFormat; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import lombok.experimental.Accessors; | |||
import org.springframework.format.annotation.DateTimeFormat; | |||
import java.io.Serializable; | |||
import java.util.Date; | |||
/** | |||
* <p> | |||
* 后台用户管理表 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-10-30 | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = false) | |||
@Accessors(chain = true) | |||
@TableName("th_oauth_user") | |||
public class User implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* 用户ID | |||
*/ | |||
@TableId(value = "id", type = IdType.UUID) | |||
private String id; | |||
/** | |||
* 用户编号 | |||
*/ | |||
private String code; | |||
/** | |||
* 真实姓名 | |||
*/ | |||
private String realname; | |||
/** | |||
* 昵称 | |||
*/ | |||
private String nickname; | |||
/** | |||
* 性别:1男 2女 3保密 | |||
*/ | |||
private Integer gender; | |||
/** | |||
* 头像 | |||
*/ | |||
private String avatar; | |||
/** | |||
* 手机号码 | |||
*/ | |||
private String mobile; | |||
/** | |||
* 邮箱地址 | |||
*/ | |||
private String email; | |||
/** | |||
* 出生日期 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd") | |||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") | |||
private Date birthday; | |||
/** | |||
* 部门ID | |||
*/ | |||
private String deptId; | |||
/** | |||
* 省份编码 | |||
*/ | |||
private String provinceCode; | |||
/** | |||
* 城市编码 | |||
*/ | |||
private String cityCode; | |||
/** | |||
* 区县编码 | |||
*/ | |||
private String districtCode; | |||
/** | |||
* 街道编码 | |||
*/ | |||
private String streetCode; | |||
/** | |||
* 详细地址 | |||
*/ | |||
private String address; | |||
/** | |||
* 所属城市 | |||
*/ | |||
private String cityName; | |||
/** | |||
* 登录用户名 | |||
*/ | |||
private String username; | |||
/** | |||
* 登录密码 | |||
*/ | |||
private String password; | |||
/** | |||
* 用户类型:1管理员 | |||
*/ | |||
private Integer type; | |||
/** | |||
* 驾照类型:1飞行执照 2飞行许可证 | |||
*/ | |||
private Integer driverType; | |||
/** | |||
* 驾照编号 | |||
*/ | |||
private String driverCode; | |||
/** | |||
* 盐加密 | |||
*/ | |||
private String salt; | |||
/** | |||
* 个人简介 | |||
*/ | |||
private String intro; | |||
/** | |||
* 状态:1正常 2禁用 | |||
*/ | |||
private Integer status; | |||
/** | |||
* 备注 | |||
*/ | |||
private String note; | |||
/** | |||
* 显示顺序 | |||
*/ | |||
private Integer sort; | |||
/** | |||
* 登录次数 | |||
*/ | |||
private Integer loginNum; | |||
/** | |||
* 最近登录IP | |||
*/ | |||
private String loginIp; | |||
/** | |||
* 最近登录时间 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
private Date loginTime; | |||
/** | |||
* 添加人 | |||
*/ | |||
private String createUser; | |||
/** | |||
* 创建时间 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
private Date createTime; | |||
/** | |||
* 更新人 | |||
*/ | |||
private String updateUser; | |||
/** | |||
* 更新时间 | |||
*/ | |||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") | |||
private Date updateTime; | |||
/** | |||
* 有效标识 | |||
*/ | |||
private Integer mark; | |||
/** | |||
* 角色ID | |||
*/ | |||
@TableField(exist = false) | |||
private String[] roleIds; | |||
/** | |||
* 城市集合 | |||
*/ | |||
@TableField(exist = false) | |||
private String[] city; | |||
} |
@@ -0,0 +1,37 @@ | |||
package com.tuoheng.admin.entity; | |||
import com.baomidou.mybatisplus.annotation.TableName; | |||
import com.tuoheng.common.core.common.BaseEntity; | |||
import lombok.Data; | |||
import lombok.EqualsAndHashCode; | |||
import lombok.experimental.Accessors; | |||
/** | |||
* <p> | |||
* 人员角色表 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-10-30 | |||
*/ | |||
@Data | |||
@EqualsAndHashCode(callSuper = true) | |||
@Accessors(chain = true) | |||
@TableName("th_user_role") | |||
public class UserRole extends BaseEntity { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* 人员ID | |||
*/ | |||
private String userId; | |||
/** | |||
* 角色ID | |||
*/ | |||
private String roleId; | |||
} |
@@ -0,0 +1,65 @@ | |||
package com.tuoheng.admin.interceptor; | |||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||
import com.tuoheng.admin.entity.User; | |||
import com.tuoheng.admin.service.IUserService; | |||
import com.tuoheng.common.core.exception.ServiceException; | |||
import com.tuoheng.common.core.utils.SecurityUserUtils; | |||
import com.tuoheng.common.core.utils.StringUtils; | |||
import com.tuoheng.common.core.utils.ThreadLocalUtil; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.beans.factory.BeanFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.http.HttpStatus; | |||
import org.springframework.stereotype.Component; | |||
import org.springframework.web.context.support.WebApplicationContextUtils; | |||
import org.springframework.web.servlet.HandlerInterceptor; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
@Component | |||
@Slf4j | |||
public class UserInterceptor implements HandlerInterceptor { | |||
@Autowired | |||
private IUserService userService; | |||
@Override | |||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { | |||
log.info("开始执行过滤器"); | |||
String username = SecurityUserUtils.username(); | |||
if (StringUtils.isEmpty(username)) { | |||
throw new ServiceException(HttpStatus.BAD_REQUEST.value(),"username不能为空"); | |||
} | |||
if(userService==null){ | |||
log.info("userService is null!!!"); | |||
BeanFactory factory = WebApplicationContextUtils | |||
.getRequiredWebApplicationContext(request.getServletContext()); | |||
userService = (IUserService) factory | |||
.getBean("UserServiceImpl"); | |||
} | |||
User user = userService.getOne(Wrappers.<User>lambdaQuery() | |||
.eq(User::getMark, 1) | |||
.eq(User::getStatus, 1) | |||
.eq(StringUtils.isNotEmpty(username), User::getUsername, username)); | |||
if (StringUtils.isNull(user)) { | |||
log.error("用户信息不存在:{}", user); | |||
return false; | |||
} | |||
ThreadLocalUtil.set(user); | |||
log.info("执行完毕"); | |||
return true; | |||
} | |||
@Override | |||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { | |||
//结束方法清理内存 | |||
ThreadLocalUtil.remove(); | |||
log.info("清理ThreadLocal内存完毕"); | |||
} | |||
} | |||
@@ -0,0 +1,16 @@ | |||
package com.tuoheng.admin.mapper; | |||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||
import com.tuoheng.admin.entity.User; | |||
/** | |||
* <p> | |||
* 后台用户管理表 Mapper 接口 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-10-30 | |||
*/ | |||
public interface UserMapper extends BaseMapper<User> { | |||
} |
@@ -0,0 +1,37 @@ | |||
package com.tuoheng.admin.mapper; | |||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||
import com.tuoheng.admin.entity.Role; | |||
import com.tuoheng.admin.entity.UserRole; | |||
import org.apache.ibatis.annotations.Param; | |||
import java.util.List; | |||
/** | |||
* <p> | |||
* 人员角色表 Mapper 接口 | |||
* </p> | |||
* | |||
* @author 拓恒 | |||
* @since 2020-10-30 | |||
*/ | |||
public interface UserRoleMapper extends BaseMapper<UserRole> { | |||
/** | |||
* 根据用户ID获取角色 | |||
* | |||
* @param userId 用户ID | |||
* @return | |||
*/ | |||
List<Role> getRolesByUserId(String userId); | |||
/** | |||
* 根据角色获取用户ID | |||
* | |||
* | |||
* @param roleId 角色ID | |||
* @return | |||
*/ | |||
List<String> getUsersByURoleId(@Param("roleId") String roleId); | |||
} |
@@ -0,0 +1,47 @@ | |||
package com.tuoheng.admin.query; | |||
import com.tuoheng.common.core.common.BaseQuery; | |||
import lombok.Data; | |||
/** | |||
* 用户查询条件 | |||
*/ | |||
@Data | |||
public class UserQuery extends BaseQuery { | |||
/** | |||
* 用户账号 | |||
*/ | |||
private String username; | |||
/** | |||
* 真实姓名 | |||
*/ | |||
private String realname; | |||
/** | |||
* 性别:1男 2女 3保密 | |||
*/ | |||
private Integer gender; | |||
/** | |||
* 终端 1 web端 2 小程序端 | |||
*/ | |||
private Integer terminalType; | |||
/** | |||
* 用户类型 | |||
*/ | |||
private Integer type; | |||
/** | |||
* 用户状态:1在用 2停用 | |||
*/ | |||
private Integer status; | |||
/** | |||
* 角色ID | |||
*/ | |||
private String roleId; | |||
} |
@@ -0,0 +1,16 @@ | |||
package com.tuoheng.admin.request; | |||
import com.tuoheng.admin.dto.ClientRoleDto; | |||
import lombok.Data; | |||
import java.util.List; | |||
@Data | |||
public class OidcCreateUserRequest { | |||
private String username; | |||
private String password; | |||
private List<ClientRoleDto> clientRoleDtoList; | |||
} |
@@ -0,0 +1,24 @@ | |||
package com.tuoheng.admin.request; | |||
import com.tuoheng.admin.dto.ClientRoleDto; | |||
import lombok.Data; | |||
import java.util.List; | |||
/** | |||
* @Author xiaoying | |||
* @Date 2022/10/31 10:17 | |||
*/ | |||
@Data | |||
public class OidcUpdateUserRequest { | |||
/** | |||
* 用户名 | |||
*/ | |||
private String username; | |||
/** | |||
* 客户端以及对应的权限 | |||
*/ | |||
private List<ClientRoleDto> clientRoleDtoList; | |||
} |