Initial commit for jzo2o-framework
This commit is contained in:
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -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/
|
||||
50
jzo2o-canal-sync/pom.xml
Normal file
50
jzo2o-canal-sync/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<artifactId>jzo2o-parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../jzo2o-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>jzo2o-canal-sync</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<artifactId>jzo2o-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal.client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal.protocol</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<artifactId>jzo2o-rabbitmq</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<artifactId>jzo2o-redis</artifactId>
|
||||
</dependency>
|
||||
<!--单元测试-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.jzo2o.canal.constants;
|
||||
|
||||
/**
|
||||
* 字段常量
|
||||
*/
|
||||
public class FieldConstants {
|
||||
/**
|
||||
* 字段 - id
|
||||
*/
|
||||
public static final String ID = "id";
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.jzo2o.canal.constants;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
public class OperateType {
|
||||
/**
|
||||
* 新增操作
|
||||
*/
|
||||
public static final String INSERT = "INSERT";
|
||||
/**
|
||||
* 修改操作
|
||||
*/
|
||||
public static final String UPDATE = "UPDATE";
|
||||
/**
|
||||
* 删除操作
|
||||
*/
|
||||
public static final String DELETE = "DELETE";
|
||||
|
||||
public static boolean isSave(String operateType) {
|
||||
return !(!INSERT.equals(operateType) && !UPDATE.equals(operateType));
|
||||
}
|
||||
|
||||
public static boolean canHandle(String operateType) {
|
||||
return INSERT.equals(operateType) || UPDATE.equals(operateType) || DELETE.equals(operateType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.jzo2o.canal.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface CanalDataHandler<T> {
|
||||
|
||||
/**
|
||||
* 批量保存
|
||||
* @param data
|
||||
*/
|
||||
void batchSave(List<T> data);
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param ids
|
||||
*/
|
||||
void batchDelete(List<Long> ids);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.jzo2o.canal.listeners;
|
||||
|
||||
import com.jzo2o.canal.constants.FieldConstants;
|
||||
import com.jzo2o.canal.constants.OperateType;
|
||||
import com.jzo2o.canal.core.CanalDataHandler;
|
||||
import com.jzo2o.canal.model.CanalMqInfo;
|
||||
import com.jzo2o.canal.model.dto.CanalBaseDTO;
|
||||
import com.jzo2o.common.utils.BeanUtils;
|
||||
import com.jzo2o.common.utils.CollUtils;
|
||||
import com.jzo2o.common.utils.JsonUtils;
|
||||
import com.jzo2o.common.utils.NumberUtils;
|
||||
import org.springframework.amqp.core.Message;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class AbstractCanalRabbitMqMsgListener<T> implements CanalDataHandler<T> {
|
||||
|
||||
public void parseMsg(Message message) throws Exception {
|
||||
|
||||
try {
|
||||
// 1.数据格式转换
|
||||
CanalMqInfo canalMqInfo = JsonUtils.toBean(new String(message.getBody()), CanalMqInfo.class);
|
||||
// 2.过滤数据,没有数据或者非插入、修改、删除的操作均不处理
|
||||
if (CollUtils.isEmpty(canalMqInfo.getData()) || !(OperateType.canHandle(canalMqInfo.getType()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canalMqInfo.getData().size() > 1) {
|
||||
// 3.多条数据处理
|
||||
batchHandle(canalMqInfo);
|
||||
} else {
|
||||
// 4.单条数据处理
|
||||
singleHandle(canalMqInfo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//出现错误延迟1秒重试
|
||||
Thread.sleep(1000);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单条数据处理
|
||||
*
|
||||
* @param canalMqInfo
|
||||
*/
|
||||
private void singleHandle(CanalMqInfo canalMqInfo) {
|
||||
// 1.数据转换
|
||||
CanalBaseDTO canalBaseDTO = BeanUtils.toBean(canalMqInfo, CanalBaseDTO.class);
|
||||
Map<String, Object> fieldMap = CollUtils.getFirst(canalMqInfo.getData());
|
||||
canalBaseDTO.setId(parseId(fieldMap));
|
||||
canalBaseDTO.setFieldMap(fieldMap);
|
||||
canalBaseDTO.setIsSave(canalMqInfo.getIsSave());
|
||||
|
||||
Class<T> messageType = getMessageType();
|
||||
if (messageType == null) {
|
||||
return;
|
||||
}
|
||||
if (canalBaseDTO.getIsSave()) {
|
||||
T t1 = JsonUtils.toBean(JsonUtils.toJsonStr(canalBaseDTO.getFieldMap()), messageType);
|
||||
List<T> ts = Arrays.asList(t1);
|
||||
batchSave(ts);
|
||||
} else {
|
||||
Long id = canalBaseDTO.getId();
|
||||
List<Long> ids = Arrays.asList(id);
|
||||
batchDelete(ids);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void batchHandle(CanalMqInfo canalMqInfo) {
|
||||
Class<T> messageType = getMessageType();
|
||||
if (messageType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(canalMqInfo.getIsSave()){
|
||||
List<T> collect = canalMqInfo.getData().stream().map(fieldMap -> {
|
||||
CanalBaseDTO canalBaseDTO = CanalBaseDTO.builder()
|
||||
.id(parseId(fieldMap))
|
||||
.database(canalMqInfo.getDatabase())
|
||||
.table(canalMqInfo.getTable())
|
||||
.isSave(canalMqInfo.getIsSave())
|
||||
.fieldMap(fieldMap).build();
|
||||
return JsonUtils.toBean(JsonUtils.toJsonStr(canalBaseDTO.getFieldMap()), messageType);
|
||||
}).collect(Collectors.toList());
|
||||
batchSave(collect);
|
||||
}else{
|
||||
List<Long> ids = canalMqInfo.getData().stream().map(fieldMap -> {
|
||||
return parseId(fieldMap);
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
batchDelete(ids);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Long parseId(Map<String, Object> fieldMap) {
|
||||
Object objectId = fieldMap.get(FieldConstants.ID);
|
||||
return NumberUtils.parseLong(objectId.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
public abstract void batchSave(List<T> data);
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*
|
||||
* @param ids
|
||||
*/
|
||||
public abstract void batchDelete(List<Long> ids);
|
||||
|
||||
|
||||
//获取泛型参数
|
||||
public Class<T> getMessageType() {
|
||||
Type superClass = getClass().getGenericSuperclass();
|
||||
if (superClass instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) superClass;
|
||||
Type[] typeArgs = parameterizedType.getActualTypeArguments();
|
||||
if (typeArgs.length > 0 && typeArgs[0] instanceof Class) {
|
||||
return (Class<T>) typeArgs[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.jzo2o.canal.model;
|
||||
|
||||
import com.jzo2o.canal.constants.OperateType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@Data
|
||||
public class CanalMqInfo implements Serializable {
|
||||
|
||||
private String database;
|
||||
private String table;
|
||||
private Boolean isDd1;
|
||||
private String type;
|
||||
private Long es;
|
||||
private Long ts;
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
*/
|
||||
private List<Map<String, Object>> data;
|
||||
|
||||
public boolean getIsSave() {
|
||||
return !OperateType.DELETE.equals(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.jzo2o.canal.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* canal监听传递参数
|
||||
* @author itcast
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class CanalBaseDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 数据id
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 所属数据库
|
||||
*/
|
||||
private String database;
|
||||
/**
|
||||
* 所在表
|
||||
*/
|
||||
private String table;
|
||||
/**
|
||||
* 是否是数据保存,两种类型,一种是保存数据,另一种是删除数据
|
||||
*/
|
||||
private Boolean isSave;
|
||||
|
||||
/**
|
||||
* 数据变更字段
|
||||
*/
|
||||
private Map<String, Object> fieldMap;
|
||||
|
||||
private Long es;
|
||||
|
||||
}
|
||||
49
jzo2o-common/pom.xml
Normal file
49
jzo2o-common/pom.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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>jzo2o-parent</artifactId>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../jzo2o-parent</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jzo2o-common</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.jzo2o.common.constants;
|
||||
|
||||
public class CommonRedisConstants {
|
||||
|
||||
public static class RedisKey {
|
||||
/**
|
||||
* 手机短信验证码,
|
||||
*/
|
||||
public static final String VERIFY_CODE = "PHONE:CODE:VERIFY_CODE_%s_%s";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.jzo2o.common.constants;
|
||||
|
||||
public class CommonStatusConstants {
|
||||
/**
|
||||
* 用户冻结
|
||||
*/
|
||||
public static final int USER_STATUS_FREEZE = 1;
|
||||
|
||||
/**
|
||||
* 用户状态正常
|
||||
*/
|
||||
public static final int USER_STATUS_NORMAL = 0;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.jzo2o.common.constants;
|
||||
|
||||
/**
|
||||
* 通用异常信息,只放系统相关异常信息
|
||||
*
|
||||
* @author itcast
|
||||
*/
|
||||
public class ErrorInfo {
|
||||
public static class Msg {
|
||||
public static final String PROCESS_FAILD = "失败";
|
||||
/**
|
||||
* 请求异常默认异常信息
|
||||
*/
|
||||
public static final String REQUEST_PARAM_ILLEGAL = "请求参数不合法";
|
||||
/**
|
||||
* 频繁请求
|
||||
*/
|
||||
public static final String REQUEST_OPERATE_FREQUENTLY = "操作频繁,请稍后重试";
|
||||
/**
|
||||
* 请求超时
|
||||
*/
|
||||
public static final String REQUEST_TIME_OUT = "请求超时";
|
||||
|
||||
/**
|
||||
* 请求失败
|
||||
*/
|
||||
public static final String REQUEST_FAILD = "请求失败";
|
||||
|
||||
public static final String REQUEST_FORBIDDEN = "拒绝访问";
|
||||
|
||||
public static final String REQUEST_UNAUTHORIZED = "身份未识别";
|
||||
|
||||
public static final String NO_PERMISSIONS = "无访问权限";
|
||||
|
||||
public static final String FORBIDDEN_OPERATION = "禁止操作";
|
||||
}
|
||||
|
||||
public static class Code {
|
||||
|
||||
/**
|
||||
* 未登录
|
||||
*/
|
||||
public static final int NOT_LOGIN = 600;
|
||||
|
||||
/**
|
||||
* 登录过期
|
||||
*/
|
||||
public static final int LOGIN_TIMEOUT = 601;
|
||||
|
||||
/**
|
||||
* token不合法
|
||||
*/
|
||||
public static final int ILLEGAL_TOKEN = 602;
|
||||
|
||||
/**
|
||||
* 无权限访问
|
||||
*/
|
||||
public static final int NO_PERMISSIONS = 603;
|
||||
|
||||
/**
|
||||
* 禁止操作
|
||||
*/
|
||||
public static final int FORBIDDEN_OPERATION = 604;
|
||||
|
||||
/**
|
||||
* 账号冻结
|
||||
*/
|
||||
public static final int ACCOUNT_FREEZED = 605;
|
||||
|
||||
/**
|
||||
* 派单拒单
|
||||
*/
|
||||
public static final int DISPATCH_REJECT = 606;
|
||||
|
||||
/**
|
||||
* 机构/服务端取消订单失败
|
||||
*/
|
||||
public static final int ORDERS_CANCEL = 607;
|
||||
|
||||
/**
|
||||
* 抢单失败
|
||||
*/
|
||||
public static final int SEIZE_ORDERS_FAILD = 608;
|
||||
|
||||
/**
|
||||
* 交易失败
|
||||
*/
|
||||
public static final int TRADE_FAILED = 609;
|
||||
|
||||
/**
|
||||
* 对接评价系统http请求失败
|
||||
*/
|
||||
public static final int HTTP_EVALUATION_FAILED = 610;
|
||||
|
||||
/**
|
||||
* 抢券失败
|
||||
*/
|
||||
public static final int SEIZE_COUPON_FAILD = 611;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.jzo2o.common.constants;
|
||||
|
||||
/**
|
||||
* 请求/响应header常量
|
||||
*/
|
||||
public class HeaderConstants {
|
||||
/**
|
||||
* 当前用户信息
|
||||
*/
|
||||
public static final String USER_INFO = "USER-INFO";
|
||||
|
||||
/**
|
||||
* 当前用户类型
|
||||
*/
|
||||
public static final String USER_TYPE = "USER-TYPE";
|
||||
|
||||
/**
|
||||
* 异常捕获标识位
|
||||
*/
|
||||
public static final String EXCEPTION_CATCH_FLAG = "EXCEPTION-CATCH-FLAG";
|
||||
|
||||
/**
|
||||
* 异常标识 1 - 未见异常
|
||||
*/
|
||||
public static final String EXCEPTION_CATCH_FLAG_1 = "1";
|
||||
/**
|
||||
* 异常标识 2 - 异常
|
||||
*/
|
||||
public static final String EXCEPTION_CATCH_FLAG_2 = "2";
|
||||
|
||||
|
||||
/**
|
||||
* 请求id
|
||||
*/
|
||||
public static final String REQUEST_ID = "REQUEST-ID";
|
||||
|
||||
/**
|
||||
* 请求来源标识,1:外界访问,2:内部访问
|
||||
*/
|
||||
public static final String REQUEST_ORIGIN_FLAG = "REQUEST-ORIGIN-FLAG";
|
||||
|
||||
/**
|
||||
* 访问来源标识 1: 外界访问
|
||||
*/
|
||||
public static final String REQUEST_ORIGIN_FLAG_OUTSIDE = "1";
|
||||
|
||||
/**
|
||||
* 访问来源标识 2:内部访问
|
||||
*/
|
||||
public static final String REQUEST_ORIGIN_FLAG_INNER = "2";
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.jzo2o.common.constants;
|
||||
|
||||
/**
|
||||
* 静态变量
|
||||
*
|
||||
* @author zzj
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface MqConstants {
|
||||
/**
|
||||
* 默认延时时间为-1
|
||||
*/
|
||||
int DEFAULT_DELAY = -1;
|
||||
|
||||
/**
|
||||
* 低延迟时间:5秒
|
||||
*/
|
||||
int LOW_DELAY = 5000;
|
||||
|
||||
/**
|
||||
* 标准延迟时间:10秒
|
||||
*/
|
||||
int NORMAL_DELAY = 10000;
|
||||
|
||||
/**
|
||||
* 延迟交换机关键字
|
||||
*/
|
||||
String DELAYED_KEYWORD = "delayed";
|
||||
|
||||
/**
|
||||
* 表明是延迟队列
|
||||
*/
|
||||
String DELAYED = "true";
|
||||
|
||||
/**
|
||||
* 定义消息交换机,约定:1:类型都为topic,2:延迟队列命名由.delayed结尾
|
||||
*/
|
||||
interface Exchanges {
|
||||
|
||||
/**
|
||||
* 交易(支付)
|
||||
*/
|
||||
String TRADE = "jzo2o.exchange.topic.trade";
|
||||
|
||||
/**
|
||||
* 评分
|
||||
*/
|
||||
String EVALUATION_SCORE = "evaluation.score";
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义消息队列
|
||||
*/
|
||||
interface Queues {
|
||||
|
||||
/**
|
||||
* 订单微服务:更新支付状态
|
||||
*/
|
||||
String ORDERS_TRADE_UPDATE_STATUS = "jzo2o.queue.orders.trade.update.Status";
|
||||
|
||||
/**
|
||||
* 订单微服务:更新支付状态
|
||||
*/
|
||||
String SCORE_STATISTICS = "jzo2o.queue.customer.score.statistics";
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义路由key
|
||||
*/
|
||||
interface RoutingKeys {
|
||||
|
||||
/**
|
||||
* 更新支付状态
|
||||
*/
|
||||
String TRADE_UPDATE_STATUS = "UPDATE_STATUS";
|
||||
|
||||
/**
|
||||
* 分数统计
|
||||
*/
|
||||
String SCORE_STATISTICS = "evaluation.score.statistics";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.jzo2o.common.constants;
|
||||
|
||||
public class UserType {
|
||||
|
||||
/**
|
||||
* c端用户
|
||||
*/
|
||||
public static final int C_USER = 1;
|
||||
|
||||
/**
|
||||
* 服务人员
|
||||
*/
|
||||
public static final int WORKER = 2;
|
||||
|
||||
/**
|
||||
* 机构人员
|
||||
*/
|
||||
public static final int INSTITUTION = 3;
|
||||
/**
|
||||
* 运营人员
|
||||
*/
|
||||
public static final int OPERATION = 4;
|
||||
|
||||
/**
|
||||
* 系统
|
||||
*/
|
||||
public static final int SYSTEM = 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.jzo2o.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum EnableStatusEnum {
|
||||
UNKNOWAL(-1,"未知"),ENABLE(1,"启用"),DISABLE(0, "禁用");
|
||||
private int status;
|
||||
private String description;
|
||||
|
||||
public boolean equals(Integer status) {
|
||||
return this.status == status;
|
||||
}
|
||||
|
||||
public boolean equals(EnableStatusEnum enableStatusEnum) {
|
||||
return enableStatusEnum != null && enableStatusEnum.status == this.getStatus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.jzo2o.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum SmsBussinessTypeEnum {
|
||||
INSTITION_REGISTER(1), INSTITUTION_RESET_PASSWORD(2), SERVE_STAFF_LOGIN(3);
|
||||
|
||||
private int type;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import static com.jzo2o.common.constants.ErrorInfo.Msg.REQUEST_FAILD;
|
||||
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
||||
|
||||
/**
|
||||
* 请求异常,
|
||||
* 使用场景:请求参数不合法,频繁请求
|
||||
*
|
||||
* @author itcast
|
||||
*/
|
||||
public class BadRequestException extends CommonException {
|
||||
|
||||
public BadRequestException() {
|
||||
this(REQUEST_FAILD);
|
||||
}
|
||||
|
||||
public BadRequestException(String message) {
|
||||
super(HTTP_BAD_REQUEST, message);
|
||||
}
|
||||
|
||||
public BadRequestException(Throwable throwable, String message) {
|
||||
super(throwable, HTTP_BAD_REQUEST, message);
|
||||
}
|
||||
|
||||
public BadRequestException(Throwable throwable) {
|
||||
super(throwable, HTTP_BAD_REQUEST, REQUEST_FAILD);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.jzo2o.common.constants.ErrorInfo;
|
||||
import lombok.Data;
|
||||
|
||||
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@Data
|
||||
public class CommonException extends RuntimeException {
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
public CommonException() {
|
||||
this.code = HTTP_BAD_REQUEST;
|
||||
this.message = ErrorInfo.Msg.PROCESS_FAILD;
|
||||
}
|
||||
|
||||
public CommonException(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public CommonException(Throwable throwable, int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public CommonException(String message) {
|
||||
this(HttpStatus.HTTP_INTERNAL_ERROR, message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
|
||||
import static com.jzo2o.common.constants.ErrorInfo.Msg.PROCESS_FAILD;
|
||||
import static java.net.HttpURLConnection.HTTP_SERVER_ERROR;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
public class DBException extends CommonException {
|
||||
public DBException() {
|
||||
super(HTTP_SERVER_ERROR, PROCESS_FAILD);
|
||||
}
|
||||
|
||||
public DBException( String message) {
|
||||
super(HTTP_SERVER_ERROR, message);
|
||||
}
|
||||
|
||||
public DBException(Throwable throwable, String message) {
|
||||
super(throwable, HTTP_SERVER_ERROR, message);
|
||||
}
|
||||
|
||||
public DBException(Throwable throwable) {
|
||||
super(throwable, HTTP_SERVER_ERROR, PROCESS_FAILD);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
public class ElasticSearchException extends CommonException {
|
||||
public ElasticSearchException() {
|
||||
}
|
||||
|
||||
public ElasticSearchException(int code, String message) {
|
||||
super(code, message);
|
||||
}
|
||||
|
||||
public ElasticSearchException(Throwable throwable, int code, String message) {
|
||||
super(throwable, code, message);
|
||||
}
|
||||
|
||||
public ElasticSearchException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import com.jzo2o.common.constants.ErrorInfo;
|
||||
|
||||
/**
|
||||
* 禁止操作异常
|
||||
*
|
||||
* @author itheima
|
||||
*/
|
||||
public class ForbiddenOperationException extends CommonException {
|
||||
|
||||
public ForbiddenOperationException() {
|
||||
this(ErrorInfo.Msg.FORBIDDEN_OPERATION);
|
||||
}
|
||||
|
||||
public ForbiddenOperationException(String message) {
|
||||
super(ErrorInfo.Code.FORBIDDEN_OPERATION, message);
|
||||
}
|
||||
|
||||
public ForbiddenOperationException(Throwable throwable, String message) {
|
||||
super(throwable, ErrorInfo.Code.FORBIDDEN_OPERATION, message);
|
||||
}
|
||||
|
||||
public ForbiddenOperationException(Throwable throwable) {
|
||||
super(throwable, ErrorInfo.Code.FORBIDDEN_OPERATION, ErrorInfo.Msg.FORBIDDEN_OPERATION);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@Data
|
||||
public class MqException extends CommonException{
|
||||
|
||||
/**
|
||||
* mq失败消息
|
||||
*/
|
||||
private String msg;
|
||||
private Long mqId;
|
||||
|
||||
public MqException() {
|
||||
}
|
||||
|
||||
public MqException(int code, String message) {
|
||||
super(code, message);
|
||||
}
|
||||
|
||||
public MqException(Throwable throwable, int code, String message) {
|
||||
super(throwable, code, message);
|
||||
}
|
||||
|
||||
public MqException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import static com.jzo2o.common.constants.ErrorInfo.Msg.REQUEST_FORBIDDEN;
|
||||
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
|
||||
|
||||
/**
|
||||
* 权限校验被拒
|
||||
*
|
||||
* @author itheima
|
||||
*/
|
||||
public class RequestForbiddenException extends CommonException{
|
||||
|
||||
public RequestForbiddenException() {
|
||||
this(REQUEST_FORBIDDEN);
|
||||
}
|
||||
|
||||
public RequestForbiddenException(String message) {
|
||||
super(HTTP_FORBIDDEN, message);
|
||||
}
|
||||
|
||||
public RequestForbiddenException(Throwable throwable, String message) {
|
||||
super(throwable, HTTP_FORBIDDEN, message);
|
||||
}
|
||||
|
||||
public RequestForbiddenException(Throwable throwable) {
|
||||
super(throwable, HTTP_FORBIDDEN, REQUEST_FORBIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import static com.jzo2o.common.constants.ErrorInfo.Msg.REQUEST_TIME_OUT;
|
||||
import static java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT;
|
||||
|
||||
/**
|
||||
* 请求超时异常
|
||||
*
|
||||
* @author itheima
|
||||
*/
|
||||
public class RequestTimeoutException extends CommonException {
|
||||
|
||||
public RequestTimeoutException() {
|
||||
this(REQUEST_TIME_OUT);
|
||||
}
|
||||
|
||||
public RequestTimeoutException(String message) {
|
||||
super(HTTP_CLIENT_TIMEOUT, message);
|
||||
}
|
||||
|
||||
public RequestTimeoutException(Throwable throwable, String message) {
|
||||
super(throwable, HTTP_CLIENT_TIMEOUT, message);
|
||||
}
|
||||
|
||||
public RequestTimeoutException(Throwable throwable) {
|
||||
super(throwable, HTTP_CLIENT_TIMEOUT, REQUEST_TIME_OUT);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import static com.jzo2o.common.constants.ErrorInfo.Msg.REQUEST_UNAUTHORIZED;
|
||||
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
|
||||
|
||||
/**
|
||||
* 身份校验异常,错误码401
|
||||
* 使用场景:网关校验token,token不合法或token过期
|
||||
*
|
||||
* @author itheima
|
||||
*/
|
||||
public class RequestUnauthorizedException extends CommonException {
|
||||
|
||||
public RequestUnauthorizedException() {
|
||||
this(REQUEST_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
public RequestUnauthorizedException(String message) {
|
||||
super(HTTP_UNAUTHORIZED, message);
|
||||
}
|
||||
|
||||
public RequestUnauthorizedException(Throwable throwable, String message) {
|
||||
super(throwable, HTTP_UNAUTHORIZED, message);
|
||||
}
|
||||
public RequestUnauthorizedException(Throwable throwable) {
|
||||
super(throwable, HTTP_UNAUTHORIZED, REQUEST_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import static com.jzo2o.common.constants.ErrorInfo.Msg.PROCESS_FAILD;
|
||||
import static java.net.HttpURLConnection.HTTP_SERVER_ERROR;
|
||||
|
||||
/**
|
||||
* 服务器异常
|
||||
*
|
||||
* @author itcast
|
||||
*/
|
||||
public class ServerErrorException extends CommonException {
|
||||
|
||||
public ServerErrorException() {
|
||||
this(PROCESS_FAILD);
|
||||
}
|
||||
|
||||
public ServerErrorException(String message) {
|
||||
super(HTTP_SERVER_ERROR, message);
|
||||
}
|
||||
|
||||
public ServerErrorException(Throwable throwable, String message) {
|
||||
super(throwable, HTTP_SERVER_ERROR, message);
|
||||
}
|
||||
|
||||
public ServerErrorException(Throwable throwable) {
|
||||
super(throwable, HTTP_SERVER_ERROR, PROCESS_FAILD);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
import static com.jzo2o.common.constants.ErrorInfo.Msg.PROCESS_FAILD;
|
||||
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* 服务不可用,注册中心找不到对应服务
|
||||
*
|
||||
* @author itcast
|
||||
*/
|
||||
public class ServerUnavailableException extends CommonException {
|
||||
public ServerUnavailableException() {
|
||||
this(PROCESS_FAILD);
|
||||
}
|
||||
|
||||
public ServerUnavailableException(String message) {
|
||||
super(HTTP_UNAVAILABLE, message);
|
||||
}
|
||||
|
||||
public ServerUnavailableException(Throwable throwable, String message) {
|
||||
super(throwable, HTTP_UNAVAILABLE, message);
|
||||
}
|
||||
|
||||
public ServerUnavailableException(Throwable throwable) {
|
||||
super(throwable, HTTP_UNAVAILABLE, PROCESS_FAILD);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.jzo2o.common.expcetions;
|
||||
|
||||
/**
|
||||
* 应用启动异常
|
||||
* @author itcast
|
||||
*/
|
||||
public class StartException extends CommonException{
|
||||
|
||||
public StartException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.jzo2o.common.handler;
|
||||
|
||||
/**
|
||||
* 特殊类型转换器
|
||||
*/
|
||||
public interface ConvertHandler<O, T> {
|
||||
/**
|
||||
* 特殊对象类型转换
|
||||
*
|
||||
* @param originObject 源对象
|
||||
* @param targetObject 目标对象
|
||||
*/
|
||||
void map(O originObject, T targetObject);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jzo2o.common.handler;
|
||||
|
||||
/**
|
||||
* 请求id获取
|
||||
*/
|
||||
public interface RequestIdHandler {
|
||||
|
||||
String getRequestId();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.jzo2o.common.handler;
|
||||
|
||||
import com.jzo2o.common.model.CurrentUserInfo;
|
||||
|
||||
public interface UserInfoHandler {
|
||||
|
||||
CurrentUserInfo currentUserInfo();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.jzo2o.common.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 当前用户信息
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CurrentUserInfo implements Serializable {
|
||||
/**
|
||||
* 当前用户id
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 用户名/昵称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.jzo2o.common.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 经纬度
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Location {
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private Double lon;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private Double lat;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.jzo2o.common.model;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.jzo2o.common.expcetions.CommonException;
|
||||
import com.jzo2o.common.handler.ConvertHandler;
|
||||
import com.jzo2o.common.utils.BeanUtils;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ApiModel(value = "分页数据消息体", description = "分页数据统一对象")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PageResult<T> {
|
||||
/**
|
||||
* 总页数
|
||||
*/
|
||||
@ApiModelProperty(value = "总页数", required = true)
|
||||
private Long pages = 0L;
|
||||
|
||||
/**
|
||||
* 总条数
|
||||
*/
|
||||
@ApiModelProperty(value = "总条数", required = true)
|
||||
private Long total;
|
||||
|
||||
/**
|
||||
* 数据列表
|
||||
*/
|
||||
@ApiModelProperty(value = "数据列表", required = true)
|
||||
private List<T> list = Collections.EMPTY_LIST;
|
||||
|
||||
|
||||
/**
|
||||
* 返回一个分页对象实例
|
||||
*
|
||||
* @return 分页数据对象
|
||||
*/
|
||||
public static <T> PageResult<T> getInstance() {
|
||||
return PageResult.<T>builder().build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对items进行类型转换
|
||||
*
|
||||
* @param origin 源分页数据对象
|
||||
* @param clazz 指定items 属性的类型,不能为null
|
||||
* @return 目标分页数据对象
|
||||
*/
|
||||
public static <O, T> PageResult<T> of(PageResult<O> origin, Class<T> clazz) {
|
||||
return of(origin, clazz, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对items进行类型转换
|
||||
*
|
||||
* @param origin 源分页数据对象
|
||||
* @param clazz 指定items 属性的类型,不能为null
|
||||
* @param convertHandler 特殊对象类型转换器,可传null,即不进行特殊处理
|
||||
* @return 目标分页数据对象
|
||||
*/
|
||||
public static <O, T> PageResult<T> of(PageResult<O> origin, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
|
||||
//断言目标转换类型不为null
|
||||
if (null == clazz) {
|
||||
throw new CommonException("目标转换类型不能为null!");
|
||||
}
|
||||
|
||||
//复制除items外的属性
|
||||
PageResult<T> target = PageResult.getInstance();
|
||||
BeanUtils.copyProperties(origin, target, "items");
|
||||
|
||||
//items为空,直接返回
|
||||
if (CollUtil.isEmpty(origin.getList())) {
|
||||
return target;
|
||||
}
|
||||
|
||||
//对items进行类型转换
|
||||
List<T> targetList = BeanUtils.copyToList(origin.getList(), clazz, convertHandler);
|
||||
target.setList(targetList);
|
||||
|
||||
//封装分页数据
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* List{@link List}封装为分页数据对象
|
||||
*
|
||||
* @param list item数据
|
||||
* @param pages 页尺寸,可不传,数据不为空时默认为1
|
||||
* @param total 总条数
|
||||
* @return 目标分页数据对象
|
||||
*/
|
||||
public static <T> PageResult<T> of(List<T> list, Integer pageSize, Long pages, Long total) {
|
||||
PageResult pageResult = PageResult.<T>builder().pages(Optional.ofNullable(pages).orElse(0L))
|
||||
.total(Optional.ofNullable(total).orElse(0L)).build();
|
||||
|
||||
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
pageResult.setList(new ArrayList());
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
pageResult.setList(list);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.jzo2o.common.model;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Data
|
||||
public class Result<T> {
|
||||
|
||||
public static final int SUCCESS = 200;
|
||||
public static final String OK = "OK";
|
||||
public static final int FAILED = 1;
|
||||
|
||||
public static final byte[] OK_BYTES =
|
||||
String.format("{\"code\":%d,\"msg\":\"%s\",\"data\": {}}", SUCCESS, OK).getBytes(StandardCharsets.UTF_8);
|
||||
public static final byte[] OK_PREFIX =
|
||||
String.format("{\"code\":%d,\"msg\":\"%s\",\"data\": ", SUCCESS, OK).getBytes(StandardCharsets.UTF_8);
|
||||
public static final byte[] OK_SUFFIX = "}".getBytes(StandardCharsets.UTF_8);
|
||||
public static final byte[] OK_STR_PREFIX =
|
||||
String.format("{\"code\":%d,\"msg\":\"%s\",\"data\":", SUCCESS, OK).getBytes(StandardCharsets.UTF_8);
|
||||
public static final byte[] OK_STR_SUFFIX = "}".getBytes(StandardCharsets.UTF_8);
|
||||
public static final String REQUEST_OK = "OK";
|
||||
|
||||
@ApiModelProperty(value = "业务状态码,200-成功,其它-失败")
|
||||
private int code;
|
||||
@ApiModelProperty(value = "响应消息", example = "OK")
|
||||
private String msg;
|
||||
@ApiModelProperty(value = "响应数据")
|
||||
private T data;
|
||||
@ApiModelProperty(value = "请求id", example = "1af123c11412e")
|
||||
private String requestId;
|
||||
|
||||
public static Result<Void> ok() {
|
||||
return new Result<Void>(HttpStatus.HTTP_OK, REQUEST_OK, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> ok(T data) {
|
||||
return new Result<>(HttpStatus.HTTP_OK, REQUEST_OK, data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(String msg) {
|
||||
return new Result<>(HttpStatus.HTTP_BAD_REQUEST, msg, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error(int code, String msg) {
|
||||
return new Result<>(code, msg, null);
|
||||
}
|
||||
|
||||
public Result() {
|
||||
}
|
||||
|
||||
public Result(int code, String msg, T data) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public boolean success() {
|
||||
return code == HttpStatus.HTTP_OK;
|
||||
}
|
||||
|
||||
public static byte[] plainOk() {
|
||||
return OK_BYTES;
|
||||
}
|
||||
|
||||
public static byte[] plainOk(byte[] data) {
|
||||
if(data == null || data.length <= 0){
|
||||
return OK_BYTES;
|
||||
}
|
||||
byte b = data[0];
|
||||
if (b == 91 || b == 123) {
|
||||
return ArrayUtil.addAll(OK_PREFIX, data, OK_SUFFIX);
|
||||
}
|
||||
return ArrayUtil.addAll(OK_STR_PREFIX, data, OK_STR_SUFFIX);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.jzo2o.common.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
@Data
|
||||
@ApiModel("分页查询数据")
|
||||
public class PageQueryDTO {
|
||||
@ApiModelProperty("页码数")
|
||||
private Long pageNo=1L;
|
||||
@ApiModelProperty("每页条数")
|
||||
private Long pageSize=10L;
|
||||
@ApiModelProperty("排序字段1")
|
||||
private String orderBy1;
|
||||
@ApiModelProperty("排序字段1是否升序")
|
||||
private Boolean isAsc1 = false;
|
||||
|
||||
@ApiModelProperty("排序字段2,排序顺序排在排序字段1后边,如果排序字段1未设置,该字段也可以排序")
|
||||
private String orderBy2;
|
||||
@ApiModelProperty("排序字段2是否升序")
|
||||
private Boolean isAsc2 = false;
|
||||
|
||||
/**
|
||||
* 计算起始条数
|
||||
* @return
|
||||
*/
|
||||
public Long calFrom() {
|
||||
return pageNo.intValue() * pageSize.intValue() * 1L;
|
||||
}
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public static class OrderBy {
|
||||
private String orderBy;
|
||||
private Boolean isAsc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.jzo2o.common.model.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 文件上传响应值
|
||||
*
|
||||
* @author itcast
|
||||
* @create 2023/7/6 15:29
|
||||
**/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class StorageUploadResDTO {
|
||||
/**
|
||||
* 文件地址
|
||||
*/
|
||||
private String url;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.jzo2o.common.model.msg;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 交易状态消息
|
||||
*
|
||||
* @author itcast
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TradeStatusMsg {
|
||||
|
||||
/**
|
||||
* 交易单号
|
||||
*/
|
||||
private Long tradingOrderNo;
|
||||
|
||||
/**
|
||||
* 业务系统标识
|
||||
*/
|
||||
private String productAppId;
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private Long productOrderNo;
|
||||
|
||||
/**
|
||||
* 第三方支付的交易号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 支付渠道
|
||||
*/
|
||||
private String tradingChannel;
|
||||
|
||||
/**
|
||||
* 退款单号
|
||||
*/
|
||||
private Long refundNo;
|
||||
|
||||
/**
|
||||
* 支付/退款 状态名称
|
||||
*/
|
||||
private String statusName;
|
||||
|
||||
/**
|
||||
* 支付/退款 状态编码
|
||||
*/
|
||||
private Integer statusCode;
|
||||
|
||||
/**
|
||||
* 扩展信息
|
||||
*/
|
||||
private String info;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Long created;
|
||||
|
||||
public String toJson() {
|
||||
return JSONUtil.toJsonStr(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.jzo2o.common.handler.ConvertHandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 数组工具类
|
||||
* @ClassName ArrayUtils
|
||||
* @author itheima
|
||||
* @since 2022/7/10 12:02
|
||||
* @version 1.0.0
|
||||
**/
|
||||
public class ArrayUtils extends ArrayUtil {
|
||||
|
||||
/**
|
||||
* 将源数组转换成指定类型的列表
|
||||
*
|
||||
* @param originList 原始列表
|
||||
* @param targetClazz 转换后列表元素的类型
|
||||
* @param <R> 原始列表元素的类型
|
||||
* @param <T> 目标列表元素的类型
|
||||
* @return 目标类型的集合
|
||||
*/
|
||||
public static <R, T> List<T> convert(R[] originList, Class<T> targetClazz) {
|
||||
return convert(originList, targetClazz, null);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将源数组转换成指定类型的列表
|
||||
*
|
||||
* @param originList 原始列表
|
||||
* @param targetClazz 转换后列表元素的类型
|
||||
* @param convert 转换特殊字段接口
|
||||
* @param <R> 原始列表元素的类型
|
||||
* @param <T> 目标列表元素的类型
|
||||
* @return 目标类型的集合
|
||||
*/
|
||||
public static <R, T> List<T> convert(R[] originList, Class<T> targetClazz, ConvertHandler<R, T> convert) {
|
||||
if (isEmpty(originList)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Arrays.stream(originList)
|
||||
.map(origin -> BeanUtils.copyBean(origin, targetClazz, convert))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
public class AspectUtils {
|
||||
|
||||
/**
|
||||
* 获取被拦截方法对象
|
||||
* MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象
|
||||
* 所以应该使用反射获取当前对象的方法对象
|
||||
*/
|
||||
public static Method getMethod(ProceedingJoinPoint pjp) {
|
||||
//获取参数的类型
|
||||
Signature sig = pjp.getSignature();
|
||||
if (sig instanceof MethodSignature) {
|
||||
MethodSignature methodSignature = (MethodSignature) sig;
|
||||
return methodSignature.getMethod();
|
||||
} else {
|
||||
throw new IllegalArgumentException("It's not method");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在aop切面中SPEL表达式对formatter进行格式化,
|
||||
* 转换出指定的值
|
||||
*
|
||||
* @param formatter
|
||||
* @param method
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public static String parse(String formatter, Method method, Object[] args) {
|
||||
if(formatter == null || formatter.indexOf("{") < 0){
|
||||
return formatter;
|
||||
}
|
||||
LocalVariableTableParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
return SpelUtils.parse(formatter, nameDiscoverer.getParameterNames(method), args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在aop切面中批量SPEL表达式对formatter进行格式化,
|
||||
* @param formatters
|
||||
* @param method
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public static List<Object> parseList(String[] formatters, Method method, Object[] args) {
|
||||
if (ArrayUtils.isEmpty(formatters)) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.stream(formatters)
|
||||
.map(formatter -> AspectUtils.parse(formatter, method, args))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import com.jzo2o.common.constants.ErrorInfo;
|
||||
import com.jzo2o.common.expcetions.BadRequestException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 断言工具类,符合条件不会抛出异常,不符合条件则会抛出异常
|
||||
*
|
||||
* @author itheima
|
||||
*/
|
||||
public class AssertUtils {
|
||||
/**
|
||||
* 两个对象相同,不同则会抛出异常
|
||||
*
|
||||
* @param obj1 duixiang1
|
||||
* @param obj2 对象2
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void equals(Object obj1, Object obj2, String... message) {
|
||||
if (obj1 == null || obj2 == null) {
|
||||
handleException(message);
|
||||
return;
|
||||
}
|
||||
if (obj1 == obj2) {
|
||||
return;
|
||||
}
|
||||
if (!obj1.equals(obj2)) {
|
||||
handleException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断对象不为null,为null抛出异常
|
||||
*
|
||||
* @param obj 判断对象
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void isNotNull(Object obj, String... message) {
|
||||
if (obj == null) {
|
||||
handleException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串不为空,为空抛出异常
|
||||
*
|
||||
* @param str 判空字符串
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void isNotBlank(String str, String... message) {
|
||||
if (StringUtils.isBlank(str)) {
|
||||
handleException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断boolean对象是否为真,为真不抛出异常
|
||||
*
|
||||
* @param boo boolean对象
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void isTrue(Boolean boo, String... message) {
|
||||
if (!BooleanUtils.isTrue(boo)) {
|
||||
handleException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断集合是否不为空,为空抛出异常
|
||||
*
|
||||
* @param coll 判空集合对象
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void isNotEmpty(Iterable<?> coll, String... message) {
|
||||
if (CollUtils.isEmpty(coll)) {
|
||||
handleException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断map是否不为空,为空抛出异常
|
||||
*
|
||||
* @param map 判不为空map
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void isNotEmpty(Map<?, ?> map, String... message) {
|
||||
if (CollUtils.isEmpty(map)) {
|
||||
handleException(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断boolean对象是否为假,为假则不抛出异常
|
||||
*
|
||||
* @param boo boolean对象
|
||||
* @param message 异常信息
|
||||
*/
|
||||
public static void isFalse(Boolean boo, String... message) {
|
||||
if (!BooleanUtils.isFalse(boo)) {
|
||||
handleException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断一个值是否是指定值中的一个,没有匹配到抛出异常信息
|
||||
*
|
||||
* @param t 需要比较的数据
|
||||
* @param message 抛出异常信息
|
||||
* @param targets 比较集合
|
||||
* @param <T> 比较数据类型
|
||||
*/
|
||||
public static <T> void in(T t,String message, T ... targets) {
|
||||
for (T target : targets) {
|
||||
if(t.equals(target)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 没有匹配到抛出指定信息
|
||||
handleException(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断一个值是否是指定值不被包含在集合中,没有匹配到抛出异常信息
|
||||
*
|
||||
* @param t 需要比较的数据
|
||||
* @param message 抛出异常信息
|
||||
* @param targets 比较集合中
|
||||
* @param <T> 比较数据类型
|
||||
*/
|
||||
public static <T> void notIn(T t,String message, T ... targets) {
|
||||
for (T target : targets) {
|
||||
if(t.equals(target)) {
|
||||
// 信息被匹配到抛出异常信息
|
||||
handleException(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 异常信息处理
|
||||
*
|
||||
* @param message 异常信息
|
||||
*/
|
||||
private static void handleException(String... message) {
|
||||
String msg = ErrorInfo.Msg.REQUEST_PARAM_ILLEGAL;
|
||||
if (message != null && message.length > 0) {
|
||||
msg = message[0];
|
||||
}
|
||||
throw new BadRequestException(msg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
public class Base64Utils {
|
||||
|
||||
/**
|
||||
* 字符串编码
|
||||
*
|
||||
* @param content
|
||||
* @return
|
||||
*/
|
||||
public static byte[] encode(String content) {
|
||||
if (null == content) {
|
||||
return "".getBytes();
|
||||
}
|
||||
return Base64.getEncoder().encode(content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static String encodeStr(String content) {
|
||||
return new String(encode(content), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static String decodeStr(String content) {
|
||||
return new String(Base64.getDecoder().decode(content.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
140
jzo2o-common/src/main/java/com/jzo2o/common/utils/BeanUtils.java
Normal file
140
jzo2o-common/src/main/java/com/jzo2o/common/utils/BeanUtils.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.jzo2o.common.handler.ConvertHandler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 继承自 hutool 的BeanUtil,增加了bean转换时自定义转换器的功能
|
||||
*
|
||||
* @author itcast
|
||||
*/
|
||||
public class BeanUtils extends BeanUtil {
|
||||
|
||||
/**
|
||||
* 将原对象转换成目标对象,对于字段不匹配的字段可以使用转换器处理
|
||||
*
|
||||
* @param source 原对象
|
||||
* @param clazz 目标对象的class
|
||||
* @param convertHandler 转换器
|
||||
* @param <O> 原对象类型
|
||||
* @param <T> 目标对象类型
|
||||
* @return 目标对象
|
||||
*/
|
||||
public static <O, T> T copyBean(O source, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
|
||||
T target = copyBean(source, clazz);
|
||||
if (convertHandler != null) {
|
||||
convertHandler.map(source, target);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制set集合中的属性到目标对象中
|
||||
*
|
||||
* @param originSet 原始对象集合
|
||||
* @param targetType 目标对象类型
|
||||
* @param convertHandler 目标对象数据转换器
|
||||
* @return
|
||||
* @param <O> 原始对象类型
|
||||
* @param <T> 目标对象类型
|
||||
*/
|
||||
public static <O, T> List<T> copyToList(Set<O> originSet, Class<T> targetType, ConvertHandler<O, T> convertHandler) {
|
||||
if (CollUtils.isEmpty(originSet)) {
|
||||
return null;
|
||||
}
|
||||
return originSet.stream().map(o -> copyBean(o, targetType, convertHandler)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制set集合中的属性到目标对象中
|
||||
*
|
||||
* @param originSet 原始对象集合
|
||||
* @param targetType 目标对象类型
|
||||
* @return
|
||||
* @param <O> 原始对象类型
|
||||
* @param <T> 目标对象类型
|
||||
*/
|
||||
public static <O, T> List<T> copyToList(Set<O> originSet, Class<T> targetType) {
|
||||
return copyToList(originSet, targetType, (ConvertHandler<O, T>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制集合中的Bean属性
|
||||
*
|
||||
* @param originList 原Bean集合
|
||||
* @param targetType 目标Bean类型
|
||||
* @param convertHandler 特殊对象类型转换器,可传null,即不进行特殊处理
|
||||
* @return 复制后的List
|
||||
*/
|
||||
public static <O, T> List<T> copyToList(List<O> originList, Class<T> targetType, ConvertHandler<O, T> convertHandler) {
|
||||
List<T> targetList = cn.hutool.core.bean.BeanUtil.copyToList(originList, targetType);
|
||||
//特殊类型转换
|
||||
if (CollUtil.isNotEmpty(targetList) && ObjectUtil.isNotEmpty(convertHandler)) {
|
||||
for (int i = 0; i < originList.size(); i++) {
|
||||
convertHandler.map(originList.get(i), targetList.get(i));
|
||||
}
|
||||
}
|
||||
return targetList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将原对象转换成目标对象,对于字段不匹配的字段可以使用转换器处理
|
||||
*
|
||||
* @param source 原对象
|
||||
* @param clazz 目标对象的class
|
||||
* @param <R> 原对象类型
|
||||
* @param <T> 目标对象类型
|
||||
* @return 目标对象
|
||||
*/
|
||||
public static <R, T> T copyBean(R source, Class<T> clazz) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
return toBean(source, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表转换另一种类型的列表
|
||||
*
|
||||
* @param originList 原列表
|
||||
* @param targetClass 目标类型class
|
||||
* @param <R> 原列表元素类型
|
||||
* @param <T> 目标列表元素类型
|
||||
* @return 目标列表
|
||||
*/
|
||||
@Deprecated
|
||||
public static <R, T> List<T> copyList(List<R> originList, Class<T> targetClass) {
|
||||
if (CollUtils.isEmpty(originList)) {
|
||||
return CollUtils.emptyList();
|
||||
}
|
||||
return copyToList(originList, targetClass);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static <O, T> List<T> copyList(List<O> list, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
|
||||
if (list == null || list.size() == 0) {
|
||||
return CollUtils.emptyList();
|
||||
}
|
||||
return list.stream().map(r -> copyBean(r, clazz, convertHandler)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T> T copyIgnoreNull(T source,T target, Class<T> clazz){
|
||||
//1.源数据和目标数据都转为map
|
||||
Map<String, Object> oldData = BeanUtil.beanToMap(target,false,true);
|
||||
Map<String, Object> newData = BeanUtil.beanToMap(source,false,true);
|
||||
|
||||
//2.用新数据覆盖旧数据
|
||||
oldData.putAll(newData);
|
||||
|
||||
//3.map转为bean返回
|
||||
return BeanUtil.mapToBean(oldData, clazz, false, new CopyOptions());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
|
||||
public class BooleanUtils extends BooleanUtil {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.ByteUtil;
|
||||
|
||||
public class ByteUtils extends ByteUtil {
|
||||
|
||||
/**
|
||||
* 将byte[] 数组转换成字符串,如果为空返回 ""
|
||||
* @param content 字节内容
|
||||
* @return 字符串值
|
||||
*/
|
||||
public static String parse(byte[] content){
|
||||
if(content == null || content.length <= 0) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return new String(content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
public class ClassUtils extends ClassUtil {
|
||||
|
||||
}
|
||||
199
jzo2o-common/src/main/java/com/jzo2o/common/utils/CollUtils.java
Normal file
199
jzo2o-common/src/main/java/com/jzo2o/common/utils/CollUtils.java
Normal file
@@ -0,0 +1,199 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.IterUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 继承自 hutool 的集合工具类
|
||||
*/
|
||||
public class CollUtils extends CollectionUtil {
|
||||
|
||||
public static <T> List<T> emptyList() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static <T> Set<T> emptySet() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> emptyMap() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
public static <T> Set<T> singletonSet(T t) {
|
||||
return Collections.singleton(t);
|
||||
}
|
||||
|
||||
public static <T> List<T> singletonList(T t) {
|
||||
return Collections.singletonList(t);
|
||||
}
|
||||
|
||||
public static List<Integer> convertToInteger(List<String> originList) {
|
||||
return CollUtils.isNotEmpty(originList) ? originList.stream().map(NumberUtils::parseInt).collect(Collectors.toList()) : null;
|
||||
}
|
||||
|
||||
public static List<Long> convertToLong(List<String> originLIst) {
|
||||
return CollUtils.isNotEmpty(originLIst) ? originLIst.stream().map(NumberUtils::parseLong).collect(Collectors.toList()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以 conjunction 为分隔符将集合转换为字符串 如果集合元素为数组、Iterable或Iterator,则递归组合其为字符串
|
||||
*
|
||||
* @param collection 集合
|
||||
* @param conjunction 分隔符
|
||||
* @param <T> 集合元素类型
|
||||
* @return 连接后的字符串
|
||||
* See Also: IterUtil.join(Iterator, CharSequence)
|
||||
*/
|
||||
public static <T> String join(Collection<T> collection, CharSequence conjunction) {
|
||||
if (null == collection || collection.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return IterUtil.join(collection.iterator(), conjunction);
|
||||
}
|
||||
|
||||
public static <T> String joinIgnoreNull(Collection<T> collection, CharSequence conjunction) {
|
||||
if (null == collection || collection.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (T t : collection) {
|
||||
if (t == null) continue;
|
||||
sb.append(t).append(",");
|
||||
}
|
||||
if (sb.length() <= 0) {
|
||||
return null;
|
||||
}
|
||||
return sb.deleteCharAt(sb.length() - 1).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将元素加入到集合中,为null的过滤掉
|
||||
*
|
||||
* @param list 集合
|
||||
* @param data 要添加的数据
|
||||
* @param <T> 元素类型
|
||||
*/
|
||||
public static <T> void add(Collection<T> list, T... data) {
|
||||
if (list == null || ArrayUtils.isEmpty(data)) {
|
||||
return;
|
||||
}
|
||||
for (T t : data) {
|
||||
if (ObjectUtils.isNotEmpty(t)) {
|
||||
list.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//将两个集合出现次数相加
|
||||
public static Map<Long, Integer> union(Map<Long, Integer> map1, Map<Long, Integer> map2) {
|
||||
if (CollUtils.isEmpty(map1)) {
|
||||
return map2;
|
||||
} else if (CollUtils.isEmpty(map2)) {
|
||||
return map1;
|
||||
}
|
||||
for (Map.Entry<Long, Integer> entry : map1.entrySet()) {
|
||||
Integer num = map2.get(entry.getKey());
|
||||
map2.put(entry.getKey(), NumberUtils.null2Zero(num) + entry.getValue());
|
||||
}
|
||||
return map2;
|
||||
}
|
||||
|
||||
public static <T, R> R getFiledOfFirst(List<T> list, Function<T, R> function) {
|
||||
if (CollUtils.isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
return function.apply(list.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将枚举集合表转换成列表
|
||||
* @param enumeration 枚举集合
|
||||
* @return 列表
|
||||
* @param <T> 元素类型
|
||||
*/
|
||||
public static <T> List<T> toList(Enumeration<T> enumeration) {
|
||||
if (!enumeration.hasMoreElements()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<T> data = new ArrayList<>();
|
||||
T current = null;
|
||||
while ((current = enumeration.nextElement()) != null) {
|
||||
data.add(current);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数组为空的序号列表
|
||||
*
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
public static List<Integer> getIndexsOfNullData(List<?> list) {
|
||||
if (isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
// filter 中的过滤条件不能调换,和表达式的执行顺序有关系
|
||||
return list.stream().filter(x -> counter.incrementAndGet() >= 0 && x == null)
|
||||
.map(x -> Integer.valueOf(counter.intValue() - 1))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T> List<T> valueofIndexs(List<T> list, List<Integer> indexs) {
|
||||
if(CollUtils.isEmpty(indexs) || CollUtils.isEmpty(list)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return indexs.stream()
|
||||
.map(index -> list.get(index))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取集合中某一列的值集合
|
||||
*
|
||||
* @param list
|
||||
* @param function
|
||||
* @return
|
||||
* @param <T>
|
||||
* @param <R>
|
||||
*/
|
||||
public static <T,R> List<R> getFieldValues(List<T> list, Function<T,R> function) {
|
||||
if(isEmpty(list)){
|
||||
return null;
|
||||
}
|
||||
return list.stream().map(function)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将两个列表合并出第三个列表
|
||||
* @param list1
|
||||
* @param list2
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T> List<T> union(List<T> list1, List<T> list2) {
|
||||
if(isEmpty(list1)) {
|
||||
return list2;
|
||||
}
|
||||
if(isEmpty(list2)){
|
||||
return list1;
|
||||
}
|
||||
List<T> result = new ArrayList<>();
|
||||
result.addAll(list1);
|
||||
result.addAll(list2);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <K,V> Map<K,V> defaultIfEmpty(Map<K,V> originMap, Map<K,V> defaultMap) {
|
||||
return CollUtils.isEmpty(originMap) ? defaultMap : originMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 原始比较器无法处理空值问题,特写此工具类
|
||||
*/
|
||||
public class ComparatorUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 比较器,可以将空值元素移动到头部
|
||||
*
|
||||
* @param keyExtractor
|
||||
* @param <T>
|
||||
* @param <U>
|
||||
* @return
|
||||
*/
|
||||
public static <T, U extends Comparable<? super U>> Comparator<T> nullToFirstComparing(Function<? super T, ? extends U> keyExtractor) {
|
||||
return nullComparing(true, keyExtractor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较器,可以将空值元素移动到尾部
|
||||
*
|
||||
* @param keyExtractor
|
||||
* @param <T>
|
||||
* @param <U>
|
||||
* @return
|
||||
*/
|
||||
public static <T, U extends Comparable<? super U>> Comparator<T> nullToLastComparing(Function<? super T, ? extends U> keyExtractor) {
|
||||
return nullComparing(false, keyExtractor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较器,可以处理空值元素,将空值元素移动到头部或尾部
|
||||
*
|
||||
* @param nullFirst 空元素是否排到头部
|
||||
* @return 比较器
|
||||
*/
|
||||
public static <T, U extends Comparable<? super U>> Comparator<T> nullComparing(boolean nullFirst,
|
||||
Function<? super T, ? extends U> keyExtractor) {
|
||||
Objects.requireNonNull(keyExtractor);
|
||||
|
||||
return (Comparator<T> & Serializable)
|
||||
(c1, c2) -> {
|
||||
boolean c1NUll = ObjectUtils.isNull(c1) || ObjectUtils.isNull(keyExtractor.apply(c1));
|
||||
boolean c2NUll = ObjectUtils.isNull(c2) || ObjectUtils.isNull(keyExtractor.apply(c2));
|
||||
|
||||
if (c1NUll) {
|
||||
return c2NUll ? 0 : (nullFirst ? -1 : 1);
|
||||
} else if (c2NUll) {
|
||||
return nullFirst ? 1 : -1;
|
||||
}
|
||||
return keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断comparator是否反转过
|
||||
*
|
||||
* @param comparator
|
||||
* @return
|
||||
*/
|
||||
public static boolean isAsc(Comparator<?> comparator) {
|
||||
Objects.requireNonNull(comparator);
|
||||
return !comparator.getClass().getName().contains("ReverseComparator");
|
||||
}
|
||||
}
|
||||
226
jzo2o-common/src/main/java/com/jzo2o/common/utils/DateUtils.java
Normal file
226
jzo2o-common/src/main/java/com/jzo2o/common/utils/DateUtils.java
Normal file
@@ -0,0 +1,226 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 时间工具类,用于本地时间操作,包含LocalDateTimeUtil的所有方法和自定义的LocalDateTime的操作方法及常量
|
||||
*
|
||||
* @author itheima
|
||||
* @version 1.0.0 1.0
|
||||
* @see 1.0
|
||||
* @since 从哪个版本开始支持该类的功能
|
||||
*/
|
||||
public class DateUtils extends LocalDateTimeUtil {
|
||||
|
||||
public static final String DEFAULT_YEAR_FORMAT = "yyyy";
|
||||
public static final String DEFAULT_MONTH_FORMAT = "yyyy-MM";
|
||||
public static final String DEFAULT_MONTH_FORMAT_SLASH = "yyyy/MM";
|
||||
public static final String DEFAULT_MONTH_FORMAT_EN = "yyyy年MM月";
|
||||
public static final String DEFAULT_MONTH_FORMAT_COMPACT = "yyyyMM";
|
||||
public static final String DEFAULT_WEEK_FORMAT = "yyyy-ww";
|
||||
public static final String DEFAULT_WEEK_FORMAT_EN = "yyyy年ww周";
|
||||
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
|
||||
public static final String DEFAULT_DATE_FORMAT_EN = "yyyy年MM月dd日";
|
||||
public static final String DEFAULT_DATE_FORMAT_COMPACT = "yyyyMMdd";
|
||||
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final String DEFAULT_DATE_TIME_COMPACT = "yyyyMMddHHmmss";
|
||||
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
|
||||
public static final String DATE_HOUR_FORMAT = "ddHH";
|
||||
public static final String DAY = "DAY";
|
||||
public static final String MONTH = "MONTH";
|
||||
public static final String WEEK = "WEEK";
|
||||
public static final long MAX_MONTH_DAY = 30L;
|
||||
public static final long MAX_3_MONTH_DAY = 90L;
|
||||
public static final long MAX_YEAR_DAY = 365L;
|
||||
|
||||
|
||||
public static final DateTimeFormatter SIGN_DATE_SUFFIX_FORMATTER =
|
||||
DateTimeFormatter.ofPattern(":yyyyMM");
|
||||
public static final DateTimeFormatter POINTS_BOARD_SUFFIX_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyyMM");
|
||||
|
||||
public static final String TIME_ZONE_8 = "GMT+8";
|
||||
|
||||
/**
|
||||
* 获取utc时间
|
||||
*
|
||||
* @param localDateTime 转化时间
|
||||
* @return utc时间
|
||||
*/
|
||||
public static LocalDateTime getUTCTime(LocalDateTime localDateTime) {
|
||||
ZoneId australia = ZoneId.of("Asia/Shanghai");
|
||||
ZonedDateTime dateAndTimeInSydney = ZonedDateTime.of(localDateTime, australia);
|
||||
ZonedDateTime utcDate = dateAndTimeInSydney.withZoneSameInstant(ZoneOffset.UTC);
|
||||
return utcDate.toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Asia时间
|
||||
*
|
||||
* @param localDateTime 转化时间
|
||||
* @return Asia时间
|
||||
*/
|
||||
public static LocalDateTime getAsiaTime(LocalDateTime localDateTime) {
|
||||
ZoneId australia = ZoneId.of("Asia/Shanghai");
|
||||
ZonedDateTime dateAndTimeInSydney = ZonedDateTime.of(localDateTime, ZoneOffset.UTC);
|
||||
ZonedDateTime utcDate = dateAndTimeInSydney.withZoneSameInstant(australia);
|
||||
return utcDate.toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某一天的开始:0点0分
|
||||
*
|
||||
* @param localDateTime 指定日期
|
||||
* @return 转换后的时间
|
||||
*/
|
||||
public static LocalDateTime getDayStartTime(LocalDateTime localDateTime) {
|
||||
if (localDateTime == null) {
|
||||
return null;
|
||||
}
|
||||
return localDateTime.toLocalDate().atStartOfDay();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某一天的结束:23点 59分 59秒的时间
|
||||
*
|
||||
* @param localDateTime 指定日期
|
||||
* @return 转换后的时间
|
||||
*/
|
||||
public static LocalDateTime getDayEndTime(LocalDateTime localDateTime) {
|
||||
if (localDateTime == null) {
|
||||
return null;
|
||||
}
|
||||
return LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.MAX);
|
||||
}
|
||||
|
||||
public static Date addDays(int i) {
|
||||
Calendar c = Calendar.getInstance(TimeZone.getTimeZone(TIME_ZONE_8));
|
||||
c.add(Calendar.DAY_OF_MONTH, i);
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
|
||||
public static LocalDate getMonthBegin(LocalDate date) {
|
||||
return LocalDate.of(date.getYear(), date.getMonth(), 1);
|
||||
}
|
||||
|
||||
public static LocalDate getMonthEnd(LocalDate date) {
|
||||
return LocalDate.of(date.getYear(), date.getMonthValue() + 1, 1).minusDays(1);
|
||||
}
|
||||
|
||||
public static LocalDateTime getMonthBeginTime(LocalDate date) {
|
||||
return LocalDate.of(date.getYear(), date.getMonth(), 1).atStartOfDay();
|
||||
}
|
||||
|
||||
public static LocalDateTime getMonthEndTime(LocalDate date) {
|
||||
return LocalDate.of(date.getYear(), date.getMonthValue() + 1, 1)
|
||||
.minusDays(1).atTime(LocalTime.MAX);
|
||||
}
|
||||
|
||||
public static LocalDateTime getWeekBeginTime(LocalDate now) {
|
||||
return now.minusDays(now.getDayOfWeek().getValue() - 1).atStartOfDay();
|
||||
}
|
||||
|
||||
public static LocalDateTime getWeekEndTime(LocalDate now) {
|
||||
return LocalDateTime.of(now.plusDays(8 - now.getDayOfWeek().getValue()), LocalTime.MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近15天日期(不包含当天),格式MM.dd
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static List<String> last15Day() {
|
||||
// 1.定义日期列表
|
||||
List<String> days = new ArrayList<>();
|
||||
// 2.获取15天前的时间
|
||||
LocalDateTime time = now().minusDays(15);
|
||||
// 3.for循环遍历
|
||||
for (int count = 0; count < 15; count++) {
|
||||
// 3.1.格式化时间
|
||||
days.add(String.format("%s.%s",
|
||||
NumberUtils.repair0(time.getMonthValue(), 2), NumberUtils.repair0(time.getDayOfMonth(), 2)));
|
||||
// 3.2.日期加1天
|
||||
time = time.plusDays(1);
|
||||
}
|
||||
// 4.返回结果
|
||||
return days;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前时间s
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static int getCurrentTime() {
|
||||
return (int) (System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
||||
public static int getDay() {
|
||||
return getDay(null);
|
||||
}
|
||||
|
||||
public static int getDay(LocalDateTime localDateTime) {
|
||||
if (localDateTime == null) {
|
||||
localDateTime = now();
|
||||
}
|
||||
String format = format(localDateTime, DEFAULT_DATE_FORMAT_COMPACT);
|
||||
return NumberUtils.parseInt(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数字格式的日志
|
||||
*
|
||||
* @param localDateTime 日期
|
||||
* @param format 格式模板,只支持纯数字模板
|
||||
* @return
|
||||
*/
|
||||
public static Long getFormatDate(LocalDateTime localDateTime, String format) {
|
||||
String date = format(localDateTime, format);
|
||||
return date == null ? null : NumberUtils.parseLong(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数字格式的日志
|
||||
*
|
||||
* @param localDateTime 日期
|
||||
* @param format 格式模板,只支持纯数字模板
|
||||
* @return
|
||||
*/
|
||||
public static Integer getIntFormatDate(LocalDateTime localDateTime, String format) {
|
||||
String date = format(localDateTime, format);
|
||||
return date == null ? null : NumberUtils.parseInt(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最小的一个时间
|
||||
* @param localDateTimes
|
||||
* @return
|
||||
*/
|
||||
public static LocalDateTime getMin(LocalDateTime... localDateTimes) {
|
||||
if(localDateTimes == null || localDateTimes.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
if(localDateTimes.length == 1) {
|
||||
return localDateTimes[0];
|
||||
}
|
||||
List<LocalDateTime> localDateTimeList = Arrays.asList(localDateTimes);
|
||||
return localDateTimeList.stream().sorted().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static LocalDateTime getMax(LocalDateTime ... localDateTimes) {
|
||||
if(localDateTimes == null || localDateTimes.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
if(localDateTimes.length == 1) {
|
||||
return localDateTimes[0];
|
||||
}
|
||||
List<LocalDateTime> localDateTimeList = Arrays.asList(localDateTimes);
|
||||
return localDateTimeList.stream().sorted(Comparator.reverseOrder()).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
|
||||
public class EnumUtils extends EnumUtil {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
|
||||
public class HttpUtils extends HttpUtil {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
public class IdUtils extends IdUtil {
|
||||
public static final String ID = "id";
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
|
||||
public class IoUtils extends IoUtil {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
public class JsonUtils extends JSONUtil {
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.json.JSON;
|
||||
import cn.hutool.jwt.JWT;
|
||||
import com.jzo2o.common.model.CurrentUserInfo;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
|
||||
public class JwtTool {
|
||||
// 便于联调token有效期设置一个月 todo
|
||||
private static final Duration JWT_TOKEN_TTL = Duration.ofMinutes(24 * 60 * 30);
|
||||
private static final String PAYLOAD_USER_KEY = "user";
|
||||
private static final String UserType = "userType";
|
||||
|
||||
private byte[] key;
|
||||
|
||||
public JwtTool(String keyStr) {
|
||||
key = keyStr.getBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 jwttoken
|
||||
*
|
||||
* @param currentUserId 用户id
|
||||
* @param name 用户姓名/昵称
|
||||
* @param avatar 用户头像
|
||||
* @return jwt token
|
||||
*/
|
||||
public String createToken(Long currentUserId, String name, String avatar, int userType) {
|
||||
// 名称base64编码,防止token无法解析
|
||||
String encodeName = StringUtils.isEmpty(name) ? null : Base64Utils.encodeStr(name);
|
||||
// 1.生成jws
|
||||
return JWT.create()
|
||||
.setPayload(PAYLOAD_USER_KEY, new CurrentUserInfo(currentUserId, encodeName, avatar, userType))
|
||||
.setExpiresAt(new Date(System.currentTimeMillis() + JWT_TOKEN_TTL.toMillis()))
|
||||
.setCharset(Charset.forName("UTF-8"))
|
||||
.setKey(key)
|
||||
.sign();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从访问token中获取用户信息
|
||||
*
|
||||
* @param token 访问token
|
||||
* @return 用户信息
|
||||
*/
|
||||
public CurrentUserInfo parseToken(String token) {
|
||||
try {
|
||||
JWT jwt = JWT.of(token)
|
||||
.setKey(key);
|
||||
Object payload = jwt.getPayload(PAYLOAD_USER_KEY);
|
||||
return BeanUtils.isEmpty(payload) ? null : BeanUtils.copyBean(payload, CurrentUserInfo.class,((originObject, targetObject) -> {
|
||||
// base64解码
|
||||
if(StringUtils.isNotEmpty(targetObject.getName())) {
|
||||
targetObject.setName(Base64Utils.decodeStr(targetObject.getName()));
|
||||
}
|
||||
}));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从token中获取服务获取用户类型
|
||||
*
|
||||
* @param token 访问token
|
||||
* @return 用户类型
|
||||
*/
|
||||
public static Integer getUserType(String token) {
|
||||
// 1.解码
|
||||
byte[] decode = Base64.getMimeDecoder().decode(token.split("\\.")[1]);
|
||||
// token明文字符串
|
||||
String tokenPlainText = new String(decode);
|
||||
// token详情字符串
|
||||
String tokenInfo = tokenPlainText.substring(0, tokenPlainText.lastIndexOf('}') + 1);
|
||||
JSON json = JsonUtils.parse(tokenInfo);
|
||||
JSON user = json.getByPath(PAYLOAD_USER_KEY, JSON.class);
|
||||
return user.getByPath(UserType, Integer.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.lang.func.Func1;
|
||||
import cn.hutool.core.lang.func.LambdaUtil;
|
||||
|
||||
public class LambdaUtils extends LambdaUtil {
|
||||
|
||||
/**
|
||||
* 获取lambda表达式Getter或Setter函数(方法)对应的字段名称,并转换成下划线格式,规则如下:
|
||||
* getXxxx获取为xxxx,如getName得到name。
|
||||
* setXxxx获取为xxxx,如setName得到name。
|
||||
* isXxxx获取为xxxx,如isName得到name。
|
||||
* 其它不满足规则的方法名抛出IllegalArgumentException
|
||||
*/
|
||||
public static <T> String getUnderLineFieldName(Func1<T, ?> func) throws IllegalArgumentException {
|
||||
String fieldName = getFieldName(func);
|
||||
return StringUtils.isEmpty(fieldName) ? null : StringUtils.toUnderlineCase(fieldName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
/**
|
||||
* 坐标相关工具类,处理经纬度
|
||||
*/
|
||||
public class LocationUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 反转经纬度
|
||||
*
|
||||
* @param location
|
||||
* @return
|
||||
*/
|
||||
public static String reversLatLon(String location) {
|
||||
return reversLatLon(location, ",");
|
||||
}
|
||||
|
||||
/**
|
||||
* 反转经纬度
|
||||
*
|
||||
* @param location
|
||||
* @return
|
||||
*/
|
||||
public static String reversLatLon(String location, String symbol) {
|
||||
if (StringUtils.isEmpty(location) || StringUtils.isEmpty(symbol)) {
|
||||
return location;
|
||||
}
|
||||
String[] locationSplit = location.split(symbol);
|
||||
if (locationSplit.length != 2) {
|
||||
return location;
|
||||
}
|
||||
return locationSplit[1] + symbol + locationSplit[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字经纬度返回字符串经纬度
|
||||
*
|
||||
* @param lon
|
||||
* @param lat
|
||||
* @return
|
||||
*/
|
||||
public static String getLocation(Double lon, Double lat) {
|
||||
if (lon == null || lat == null) {
|
||||
return null;
|
||||
}
|
||||
return String.format("%s,%s", lon, lat);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NumberUtils extends NumberUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 如果number为空,将number转换为0,否则原数字返回
|
||||
*
|
||||
* @param number 原数值
|
||||
* @return 整型数字,0或原数字
|
||||
*/
|
||||
public static Integer null2Zero(Integer number){
|
||||
return number == null ? 0 : number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果number为空,将number转换为0,否则原数字返回
|
||||
*
|
||||
* @param number 原数值
|
||||
* @return 整型数字,0或原数字
|
||||
*/
|
||||
public static Double null2Zero(Double number){
|
||||
return number == null ? 0 : number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果是空值,返回默认数据;如果有值直接返回
|
||||
* @param number
|
||||
* @param defaultNumber
|
||||
* @return
|
||||
*/
|
||||
public static double null2Default(Double number, double defaultNumber) {
|
||||
return number == null ? defaultNumber : number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果number为空,将number转换为0L,否则原数字返回
|
||||
*
|
||||
* @param number 原数值
|
||||
* @return 长整型数字,0L或原数字
|
||||
*/
|
||||
public static Long null2Zero(Long number){
|
||||
return number == null ? 0L : number;
|
||||
}
|
||||
|
||||
|
||||
public static Double setScale(Double number) {
|
||||
return new BigDecimal(number)
|
||||
.setScale(2, BigDecimal.ROUND_HALF_UP)
|
||||
.doubleValue();
|
||||
}
|
||||
/**
|
||||
* 比较两个数字是否相同,
|
||||
* @param number1 数值1
|
||||
* @param number2 数值2
|
||||
* @return 是否一致
|
||||
*/
|
||||
public static boolean equals(Integer number1, Integer number2) {
|
||||
if(number1 == null || number2 == null){
|
||||
return false;
|
||||
}
|
||||
return number1.equals(number2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字除法保留指定小数位
|
||||
* @param num1 被除数
|
||||
* @param num2 除数
|
||||
* @param scale 小数点位数
|
||||
* @return 结果
|
||||
*/
|
||||
public static Double divToDouble(Integer num1, Integer num2, int scale){
|
||||
if(num2 == null || num2 ==0 || num1 == null || num1 == 0) {
|
||||
return 0d;
|
||||
}
|
||||
return div(num1, num2, scale).doubleValue();
|
||||
}
|
||||
|
||||
public static Double max(List<Double> data){
|
||||
if(CollUtils.isEmpty(data)){
|
||||
return null;
|
||||
}
|
||||
return data.stream()
|
||||
.max(Comparator.comparingDouble(num -> num))
|
||||
.orElse(0d);
|
||||
}
|
||||
public static Double min(List<Double> data){
|
||||
if(CollUtils.isEmpty(data)){
|
||||
return null;
|
||||
}
|
||||
return data.stream()
|
||||
.min(Comparator.comparingDouble(num -> num))
|
||||
.orElse(0d);
|
||||
}
|
||||
|
||||
public static Double average(List<Double> data){
|
||||
if(CollUtils.isEmpty(data)){
|
||||
return 0d;
|
||||
}
|
||||
return data.stream()
|
||||
.collect(Collectors.averagingDouble(Double::doubleValue));
|
||||
|
||||
}
|
||||
|
||||
public static Integer toInt(Object obj) {
|
||||
return obj == null ? null
|
||||
: obj instanceof Integer
|
||||
? (int) obj : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取绝对值,如果为null,返回0
|
||||
* @param number 数值
|
||||
* @return 绝对值
|
||||
*/
|
||||
public static int abs(Integer number) {
|
||||
return number == null
|
||||
? 0
|
||||
: Math.abs(number);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字格式化字符串,不足位数补0
|
||||
*
|
||||
* @param originNumber 原始数字
|
||||
* @param digit 数字位数
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String repair0(Integer originNumber, Integer digit){
|
||||
StringBuilder number = new StringBuilder(originNumber + "");
|
||||
while (number.length() < digit) {
|
||||
number.insert(0, "0");
|
||||
}
|
||||
return number.toString();
|
||||
}
|
||||
|
||||
public static Integer null2Default(Integer originNumber, int defaultNumber) {
|
||||
return originNumber == null ? defaultNumber : originNumber;
|
||||
}
|
||||
|
||||
public static Long null2Default(Long originNumber, long defaultNumber) {
|
||||
return originNumber == null ? defaultNumber : originNumber;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Object操作工具
|
||||
**/
|
||||
public class ObjectUtils extends ObjectUtil {
|
||||
|
||||
/**
|
||||
* 获取对象t的某个字段值
|
||||
*
|
||||
* @param t 获取对象t
|
||||
* @param function labda表达式,例如Orders::get
|
||||
* @return 对象t的某个字段值
|
||||
* @param <T> 对象t的类型
|
||||
* @param <R> 对象t对应字段的类型
|
||||
*/
|
||||
public static <T,R> R get(T t, Function<T,R> function) {
|
||||
if(t == null) {
|
||||
return null;
|
||||
}
|
||||
return function.apply(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成指定类型数据
|
||||
* @param t 转换前对象t
|
||||
* @param clazz 转换目标对象class
|
||||
* @return 转换后的对象或数据
|
||||
* @param <T> 转换前对象类型
|
||||
* @param <R> 转换后对象类型
|
||||
*/
|
||||
public static <T,R> R parse(T t, Class<R> clazz) {
|
||||
if(t == null) {
|
||||
return null;
|
||||
}
|
||||
if(!ClassUtils.equals(clazz, ClassUtils.getClassName(t, false), false)){
|
||||
throw new RuntimeException("数据转换异常,数据转换类型错误");
|
||||
}
|
||||
return (R)t;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果有值直接返回,无值返回默认值
|
||||
*
|
||||
* @param originObject 原始值
|
||||
* @param defaultObject 默认值
|
||||
* @return T类型的对象
|
||||
* @param <T> 返回值和入参的类型
|
||||
*/
|
||||
public static <T> T defaultIfEmpty(final T originObject, final T defaultObject) {
|
||||
return isNull(originObject) ? defaultObject : originObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
/**
|
||||
* 反射工具
|
||||
**/
|
||||
public class ReflectUtils extends ReflectUtil {
|
||||
|
||||
/**
|
||||
* 判断一个类中是否含有指定字段
|
||||
*
|
||||
* @param fieldName 指定字段名称
|
||||
* @param clazz 类class
|
||||
* @return 是否包含 true/false
|
||||
*/
|
||||
public static boolean containField(String fieldName, Class<?> clazz) {
|
||||
return getField(clazz, fieldName) != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SpelUtils {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("(\\#\\{([^\\}]*)\\})");
|
||||
/**
|
||||
* 将模板中的表达式替换成args参数中的值
|
||||
*
|
||||
* @param formatter 模板
|
||||
* @param paraNameArr 方法对应的参数名称
|
||||
* @param args 方法参数值value,用来进行退换对应的表达式
|
||||
* @return 模板替换后的字符串
|
||||
*
|
||||
* 例 format : counter:#{user.id}
|
||||
* paraNameAddr [user]
|
||||
* args [{"user":{"id":1}}]
|
||||
*
|
||||
* 转换后结果 -> counter:1
|
||||
*/
|
||||
public static String parse(String formatter, String[] paraNameArr, Object[] args) {
|
||||
if (StringUtils.isNotBlank(formatter) && formatter.indexOf("#") > -1) {
|
||||
|
||||
Matcher matcher = PATTERN.matcher(formatter);
|
||||
//将正则表达式中#{}的值取出放在keys中
|
||||
List<String> keys = new ArrayList<>();
|
||||
while (matcher.find()) {
|
||||
keys.add(matcher.group());
|
||||
}
|
||||
if (!CollUtils.isEmpty(keys)) {
|
||||
//SPEL表达式对象
|
||||
ExpressionParser parser = new SpelExpressionParser();
|
||||
StandardEvaluationContext context = new StandardEvaluationContext();
|
||||
//将名称和value一一对应
|
||||
for (int i = 0; i < paraNameArr.length; i++) {
|
||||
context.setVariable(paraNameArr[i], args[i]);
|
||||
}
|
||||
|
||||
for (String tmp : keys) {
|
||||
formatter = formatter.replace(tmp,
|
||||
//通过SPEL表达式获取对应的值,然后再替换掉原有值
|
||||
parser.parseExpression("#" + tmp.substring(2, tmp.length() - 1)).getValue(context, String.class));
|
||||
}
|
||||
return formatter;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.jzo2o.common.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 字符串工具类,继承了{@link StrUtil}
|
||||
**/
|
||||
public class StringUtils extends StrUtil {
|
||||
private static final byte[] TRUE = new byte[]{'T','R','U','E'};
|
||||
/**
|
||||
* 判断是否是字符串
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
public static boolean isStr(byte[] bytes) {
|
||||
if(TRUE.equals(bytes)) {
|
||||
return false;
|
||||
}
|
||||
for (byte word : bytes) {
|
||||
//判断是数字
|
||||
if(word >= 48 && word <= 57) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
53
jzo2o-es/pom.xml
Normal file
53
jzo2o-es/pom.xml
Normal file
@@ -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>jzo2o-parent</artifactId>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../jzo2o-parent/pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jzo2o-es</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<artifactId>jzo2o-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>co.elastic.clients</groupId>
|
||||
<artifactId>elasticsearch-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.json</groupId>
|
||||
<artifactId>jakarta.json-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.jzo2o.es.config;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import com.jzo2o.es.core.ElasticSearchTemplate;
|
||||
import com.jzo2o.es.core.impl.ElasticSearchTemplateImpl;
|
||||
import com.jzo2o.es.properties.EsProperties;
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
import co.elastic.clients.transport.rest_client.RestClientTransport;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(EsProperties.class)
|
||||
public class EsConfiguration {
|
||||
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
static {
|
||||
MAPPER.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
|
||||
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
|
||||
MAPPER.registerModule(javaTimeModule);
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ElasticsearchClient esClient(EsProperties esProperties) {
|
||||
RestClient restClient = RestClient.builder(new HttpHost(esProperties.getHost(), esProperties.getPort())).build();
|
||||
// Create the transport with a Jackson mapper
|
||||
// 使用自定义json序列化
|
||||
JacksonJsonpMapper jacksonJsonpMapper = new JacksonJsonpMapper(MAPPER);
|
||||
|
||||
ElasticsearchTransport transport = new RestClientTransport(restClient, jacksonJsonpMapper);
|
||||
// And create the API client
|
||||
return new ElasticsearchClient(transport);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ElasticSearchTemplate template(ElasticsearchClient elasticsearchClient) {
|
||||
return new ElasticSearchTemplateImpl(elasticsearchClient);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.jzo2o.es.constants;
|
||||
|
||||
public class FieldConstants {
|
||||
public static final String ID = "id";
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jzo2o.es.core;
|
||||
|
||||
import com.jzo2o.es.core.operations.DocumentOperations;
|
||||
import com.jzo2o.es.core.operations.IndexOperations;
|
||||
|
||||
public interface ElasticSearchTemplate {
|
||||
DocumentOperations opsForDoc();
|
||||
IndexOperations opsForIndex();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.jzo2o.es.core.impl;
|
||||
|
||||
import com.jzo2o.es.core.ElasticSearchTemplate;
|
||||
import com.jzo2o.es.core.operations.DefaultDocumentOperations;
|
||||
import com.jzo2o.es.core.operations.DefaultIndexOperations;
|
||||
import com.jzo2o.es.core.operations.DocumentOperations;
|
||||
import com.jzo2o.es.core.operations.IndexOperations;
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ElasticSearchTemplateImpl implements ElasticSearchTemplate {
|
||||
private final ElasticsearchClient elasticsearchClient;
|
||||
|
||||
public ElasticSearchTemplateImpl(ElasticsearchClient elasticsearchClient) {
|
||||
this.elasticsearchClient = elasticsearchClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentOperations opsForDoc() {
|
||||
return new DefaultDocumentOperations(elasticsearchClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexOperations opsForIndex() {
|
||||
return new DefaultIndexOperations(elasticsearchClient);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
package com.jzo2o.es.core.operations;
|
||||
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.Result;
|
||||
import com.jzo2o.common.expcetions.CommonException;
|
||||
import com.jzo2o.common.expcetions.ElasticSearchException;
|
||||
import com.jzo2o.common.model.PageResult;
|
||||
import com.jzo2o.common.model.dto.PageQueryDTO;
|
||||
import com.jzo2o.common.utils.*;
|
||||
import com.jzo2o.es.constants.FieldConstants;
|
||||
import com.jzo2o.es.utils.TermUtils;
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch._types.GeoDistanceType;
|
||||
import co.elastic.clients.elasticsearch._types.SortOrder;
|
||||
import co.elastic.clients.elasticsearch._types.WriteResponseBase;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.FieldAndFormat;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQuery;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.search.Hit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 后期优化分页功能 todo,临时使用
|
||||
*/
|
||||
@Slf4j
|
||||
public class DefaultDocumentOperations implements DocumentOperations {
|
||||
private final ElasticsearchClient elasticsearchClient;
|
||||
|
||||
public DefaultDocumentOperations(ElasticsearchClient elasticsearchClient) {
|
||||
this.elasticsearchClient = elasticsearchClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Boolean insert(String index, T document) {
|
||||
|
||||
try {
|
||||
CreateResponse createResponse = elasticsearchClient.create(builder -> builder.id(getId(document)).document(document).index(index));
|
||||
log.debug("create document response : {}", createResponse);
|
||||
boolean success = isSuccess(createResponse);
|
||||
return success;
|
||||
|
||||
} catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
// return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Boolean batchInsert(String index, List<T> documents) {
|
||||
BulkRequest.Builder br = new BulkRequest.Builder();
|
||||
for (T document : documents) {
|
||||
br.operations(op -> op.index(idx -> idx.index(index)
|
||||
.id(getId(document))
|
||||
.document(document)));
|
||||
}
|
||||
try {
|
||||
BulkResponse bulk = elasticsearchClient.bulk(br.build());
|
||||
Boolean success = isSuccess(bulk);
|
||||
return success;
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Boolean batchUpsert(String index, List<T> documents) {
|
||||
if (CollUtils.isEmpty(documents)) {
|
||||
return false;
|
||||
}
|
||||
List<String> ids = documents.stream().map(document -> getId(document)).collect(Collectors.toList());
|
||||
List<?> documentInEs = this.findByIds(index, ids, Arrays.asList(FieldConstants.ID), documents.get(0).getClass());
|
||||
List<String> idsInEs = CollUtils.isEmpty(documentInEs) ? new ArrayList<>() : documentInEs.stream().map(document -> getId(document)).collect(Collectors.toList());
|
||||
|
||||
BulkRequest.Builder builder = new BulkRequest.Builder();
|
||||
for (T document : documents) {
|
||||
String id = getId(document);
|
||||
boolean exists = idsInEs.contains(id);
|
||||
|
||||
builder.operations(op -> {
|
||||
if (exists) {
|
||||
op.update(u -> u.action(a -> a.doc(document)).index(index).id(id));
|
||||
} else {
|
||||
op.index(idx -> idx.index(index)
|
||||
.id(id)
|
||||
.document(document));
|
||||
}
|
||||
return op;
|
||||
});
|
||||
}
|
||||
try {
|
||||
BulkResponse bulk = elasticsearchClient.bulk(builder.build());
|
||||
Boolean success = isSuccess(bulk);
|
||||
return success;
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Boolean updateById(String index, T document) {
|
||||
Object id = ReflectUtils.getFieldValue(document, IdUtils.ID);
|
||||
if (id == null) {
|
||||
throw new ElasticSearchException("es更新失败,id为空");
|
||||
}
|
||||
try {
|
||||
// 2.数据更新
|
||||
UpdateResponse<?> response = elasticsearchClient.update(u -> u
|
||||
.index(index)
|
||||
.id(id.toString())
|
||||
.doc(document)
|
||||
, document.getClass());
|
||||
Boolean success = isSuccess(response);
|
||||
return success;
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ID> Boolean deleteById(String index, ID id) {
|
||||
|
||||
try {
|
||||
// 2.数据更新
|
||||
DeleteResponse response = elasticsearchClient.delete(builder -> builder.id(id.toString()).index(index));
|
||||
Boolean success = isSuccess(response);
|
||||
return success;
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ID> Boolean batchDelete(String index, List<ID> ids) {
|
||||
BulkRequest.Builder builder = new BulkRequest.Builder();
|
||||
|
||||
|
||||
ids.stream().forEach(id ->
|
||||
builder.operations(b -> b.delete(d -> d.index(index).id(id.toString())))
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
BulkResponse bulk = elasticsearchClient.bulk(builder.build());
|
||||
Boolean success = isSuccess(bulk);
|
||||
return success;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, ID> T findById(String index, ID id, Class<T> clazz) {
|
||||
try {
|
||||
GetResponse<T> response = elasticsearchClient.get(GetRequest.of(builder -> builder.id(id.toString()).index(index)), clazz);
|
||||
return response.source();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, ID> List<T> findByIds(String index, List<ID> ids, Class<T> clazz) {
|
||||
SearchRequest.Builder searchRequestBuild = new SearchRequest.Builder();
|
||||
TermsQuery termsQuery = TermsQuery.of(t -> t.field(FieldConstants.ID).terms(new TermsQueryField.Builder().value(TermUtils.parse(ids)).build()));
|
||||
|
||||
searchRequestBuild.index(index)
|
||||
.query(builder -> builder.terms(termsQuery));
|
||||
try {
|
||||
|
||||
SearchResponse<T> searchResponse = elasticsearchClient.search(searchRequestBuild.build(), clazz);
|
||||
return searchResponse.hits().hits()
|
||||
.stream()
|
||||
.map(tHit -> tHit.source())
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, ID> List<T> findByIds(String index, List<ID> ids, List<String> fields, Class<T> clazz) {
|
||||
SearchRequest.Builder searchRequestBuild = new SearchRequest.Builder();
|
||||
TermsQuery termsQuery = TermsQuery.of(t -> t.field(FieldConstants.ID).terms(new TermsQueryField.Builder().value(TermUtils.parse(ids)).build()));
|
||||
searchRequestBuild.index(index)
|
||||
.query(builder -> builder.terms(termsQuery));
|
||||
if (CollUtils.isNotEmpty(fields)) {
|
||||
List<FieldAndFormat> fieldAndFormats = fields.stream().map(field -> FieldAndFormat.of(builder -> builder.field(field))).collect(Collectors.toList());
|
||||
searchRequestBuild.fields(fieldAndFormats);
|
||||
}
|
||||
try {
|
||||
|
||||
SearchResponse<T> searchResponse = elasticsearchClient.search(searchRequestBuild.build(), clazz);
|
||||
return searchResponse.hits().hits()
|
||||
.stream()
|
||||
.map(tHit -> tHit.source())
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> PageResult<T> findForPage(PageQueryDTO pageQueryDTO, Class<T> targetClass) {
|
||||
|
||||
SearchRequest.Builder builder = new SearchRequest.Builder();
|
||||
builder.from(pageQueryDTO.calFrom().intValue());
|
||||
builder.size(pageQueryDTO.getPageSize().intValue());
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder().build();
|
||||
try {
|
||||
|
||||
SearchResponse<T> search = elasticsearchClient.search(searchRequest, targetClass);
|
||||
long total = search.hits().total().value();
|
||||
List<T> data = search.hits().hits().stream().map(Hit::source).collect(Collectors.toList());
|
||||
return PageResult.of(data,Integer.parseInt(pageQueryDTO.getPageSize()+""),pageQueryDTO.getPageNo(),total);
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> searchByWithGeo(String index, SearchRequest.Builder searchBuilder, String locationName, String location, double distance, String sortBy, Boolean isAsc, int size, Class<T> clazz) {
|
||||
// 坐标,距离
|
||||
searchBuilder.query(query ->
|
||||
query.bool(q ->
|
||||
q.filter(filter->
|
||||
filter.geoDistance(geo->{
|
||||
geo.distance(distance + "km");
|
||||
geo.field(locationName);
|
||||
geo.location(location1 -> location1.text(location));
|
||||
geo.distanceType(GeoDistanceType.Arc);
|
||||
return geo;}))
|
||||
)
|
||||
);
|
||||
// 自定排序字段和排序
|
||||
if(StringUtils.isNotEmpty(sortBy)){
|
||||
searchBuilder.sort(sortOptionsBuilder ->
|
||||
sortOptionsBuilder.field(fieldSortBuilder->{
|
||||
fieldSortBuilder.field(sortBy);
|
||||
fieldSortBuilder.order(BooleanUtils.isTrue(isAsc) ? SortOrder.Asc : SortOrder.Desc);
|
||||
return fieldSortBuilder;
|
||||
})
|
||||
);
|
||||
}
|
||||
searchBuilder.size(size);
|
||||
searchBuilder.index(index);
|
||||
try {
|
||||
SearchResponse<T> searchResponse = elasticsearchClient.search(searchBuilder.build(), clazz);
|
||||
|
||||
return searchResponse.hits().hits()
|
||||
.stream()
|
||||
.map(tHit -> tHit.source())
|
||||
.collect(Collectors.toList());
|
||||
}catch (IOException e){
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SearchResponse<T> search(SearchRequest searchRequest, Class<T> clazz) {
|
||||
try {
|
||||
return elasticsearchClient.search(searchRequest,clazz);
|
||||
}catch (IOException e){
|
||||
log.error(e.getMessage(),e);
|
||||
throw new CommonException(500,e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSuccess(WriteResponseBase writeResponseBase) {
|
||||
//todo
|
||||
return writeResponseBase != null &&
|
||||
(Result.Created.equals(writeResponseBase.result()) ||
|
||||
Result.Deleted.equals(writeResponseBase.result()) ||
|
||||
Result.Updated.equals(writeResponseBase.result()) ||
|
||||
Result.NoOp.equals(writeResponseBase.result()));
|
||||
}
|
||||
|
||||
private Boolean isSuccess(BulkResponse response) {
|
||||
//todo
|
||||
log.debug("bulk response : {}", JsonUtils.toJsonStr(response));
|
||||
if(response.errors()) {
|
||||
return false;
|
||||
}
|
||||
return response.items().stream()
|
||||
.filter(item -> item.status() != 200)
|
||||
.map(item -> false)
|
||||
.findFirst().orElse(true);
|
||||
}
|
||||
|
||||
public Boolean isSuccess(DeleteByQueryResponse deleteByQueryResponse) {
|
||||
return deleteByQueryResponse.deleted() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档id, 如果文档中设置了id,使用文档的id,如果未设置,使用雪花算法生成
|
||||
*
|
||||
* @param document
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
private <T> String getId(T document) {
|
||||
Object objectId = ReflectUtils.getFieldValue(document, IdUtils.ID);
|
||||
if (objectId == null) {
|
||||
objectId = IdUtils.objectId();
|
||||
}
|
||||
return objectId.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.jzo2o.es.core.operations;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@Slf4j
|
||||
public class DefaultIndexOperations implements IndexOperations{
|
||||
private final ElasticsearchClient elasticsearchClient;
|
||||
|
||||
public DefaultIndexOperations(ElasticsearchClient elasticsearchClient) {
|
||||
this.elasticsearchClient = elasticsearchClient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.jzo2o.es.core.operations;
|
||||
|
||||
import com.jzo2o.common.model.PageResult;
|
||||
import com.jzo2o.common.model.dto.PageQueryDTO;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
|
||||
import co.elastic.clients.elasticsearch.core.SearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.SearchResponse;
|
||||
import co.elastic.clients.util.ObjectBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
public interface DocumentOperations {
|
||||
|
||||
/**
|
||||
* 新增文档
|
||||
*
|
||||
* @param index 文档所属索引
|
||||
* @param document 文档
|
||||
* @param <T> document类型
|
||||
* @return 文档创建结果
|
||||
*/
|
||||
<T> Boolean insert(String index, T document);
|
||||
|
||||
<T> Boolean batchInsert(String index, List<T> documents);
|
||||
|
||||
<T> Boolean batchUpsert(String index, List<T> documents);
|
||||
|
||||
<T> Boolean updateById(String index, T document);
|
||||
|
||||
<ID> Boolean deleteById(String index, ID id);
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param index
|
||||
* @param ids
|
||||
* @return
|
||||
* @param <ID>
|
||||
*/
|
||||
<ID> Boolean batchDelete(String index, List<ID> ids);
|
||||
|
||||
<T, ID> T findById(String index, ID id, Class<T> clazz);
|
||||
|
||||
<T, ID> List<T> findByIds(String index, List<ID> ids, Class<T> clazz);
|
||||
|
||||
<T, ID> List<T> findByIds(String index, List<ID> ids, List<String> includes, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* 还未写完 todo
|
||||
* @param pageQueryDTO
|
||||
* @param targetClass
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
|
||||
<T> PageResult<T> findForPage(PageQueryDTO pageQueryDTO, Class<T> targetClass);
|
||||
|
||||
/**
|
||||
* 根据条件使用经纬度范围检索,先经过条件筛选,然后距离查询
|
||||
* @param index
|
||||
* @param searchBuilder
|
||||
* @param location
|
||||
* @param locationName
|
||||
* @param distance
|
||||
* @param sortBy
|
||||
* @param isAsc
|
||||
* @param size
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
<T> List<T> searchByWithGeo(String index,
|
||||
SearchRequest.Builder searchBuilder,
|
||||
String locationName,
|
||||
String location,
|
||||
double distance,
|
||||
String sortBy,
|
||||
Boolean isAsc,
|
||||
int size,
|
||||
Class<T> clazz);
|
||||
|
||||
<T> SearchResponse<T> search(SearchRequest searchRequest, Class<T> clazz);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.jzo2o.es.core.operations;
|
||||
|
||||
public interface IndexOperations {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jzo2o.es.model.query;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class EsPageQuery {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.jzo2o.es.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "jzo2o.es")
|
||||
@Data
|
||||
public class EsProperties {
|
||||
/**
|
||||
* es host
|
||||
*/
|
||||
private String host;
|
||||
/**
|
||||
* es 端口
|
||||
*/
|
||||
private Integer port;
|
||||
}
|
||||
38
jzo2o-es/src/main/java/com/jzo2o/es/utils/DocumentUtils.java
Normal file
38
jzo2o-es/src/main/java/com/jzo2o/es/utils/DocumentUtils.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package com.jzo2o.es.utils;
|
||||
|
||||
import com.jzo2o.common.utils.BeanUtils;
|
||||
import com.jzo2o.common.utils.CollUtils;
|
||||
import org.elasticsearch.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.xcontent.XContentFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class DocumentUtils {
|
||||
|
||||
/**
|
||||
* 不可用于集合
|
||||
* @param document
|
||||
* @return
|
||||
* @param <T>
|
||||
* @throws IOException
|
||||
*/
|
||||
public static <T> XContentBuilder convert(T document) {
|
||||
|
||||
Map<String, Object> map = BeanUtils.beanToMap(document);
|
||||
if (CollUtils.isEmpty(map)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
xContentBuilder.field(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return xContentBuilder.endObject();
|
||||
}catch (IOException e){
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.jzo2o.es.utils;
|
||||
|
||||
import com.jzo2o.common.utils.CollUtils;
|
||||
import com.jzo2o.common.utils.ObjectUtils;
|
||||
import co.elastic.clients.elasticsearch.core.SearchResponse;
|
||||
import co.elastic.clients.elasticsearch.core.search.Hit;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SearchResponseUtils {
|
||||
|
||||
public static boolean isSuccess(SearchResponse<?> searchResponse) {
|
||||
return searchResponse != null && ObjectUtils.isNotEmpty(searchResponse.hits()) && CollUtils.isNotEmpty(searchResponse.hits().hits());
|
||||
}
|
||||
|
||||
public static boolean isNotSuccess(SearchResponse<?> searchResponse) {
|
||||
return !isSuccess(searchResponse);
|
||||
}
|
||||
|
||||
public static <T> List<T> getResponse(SearchResponse<T> searchResponse) {
|
||||
return getResponse(searchResponse, null);
|
||||
}
|
||||
|
||||
|
||||
public static <T> List<T> getResponse(SearchResponse<T> searchResponse, Convert<T> convert) {
|
||||
if (!isSuccess(searchResponse)) {
|
||||
return null;
|
||||
}
|
||||
if (convert == null) {
|
||||
return searchResponse.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return searchResponse.hits().hits()
|
||||
.stream().map(hit -> {
|
||||
convert.convert(hit, hit.source());
|
||||
return hit.source();
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
public static interface Convert<T> {
|
||||
void convert(Hit<T> hit, T t);
|
||||
}
|
||||
}
|
||||
21
jzo2o-es/src/main/java/com/jzo2o/es/utils/TermUtils.java
Normal file
21
jzo2o-es/src/main/java/com/jzo2o/es/utils/TermUtils.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.jzo2o.es.utils;
|
||||
|
||||
import com.jzo2o.common.utils.CollUtils;
|
||||
import co.elastic.clients.elasticsearch._types.FieldValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TermUtils {
|
||||
|
||||
|
||||
|
||||
public static <T> List<FieldValue> parse(List<T> sources) {
|
||||
if (CollUtils.isEmpty(sources)) {
|
||||
return null;
|
||||
}
|
||||
List<FieldValue> values = new ArrayList<>();
|
||||
sources.stream().forEach(s -> values.add(FieldValue.of(s.toString())));
|
||||
return values;
|
||||
}
|
||||
}
|
||||
2
jzo2o-es/src/main/resources/META-INF/spring.factories
Normal file
2
jzo2o-es/src/main/resources/META-INF/spring.factories
Normal file
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.jzo2o.es.config.EsConfiguration
|
||||
4
jzo2o-es/src/test/resources/application.yml
Normal file
4
jzo2o-es/src/test/resources/application.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
jzo2o:
|
||||
es:
|
||||
host: 192.168.101.65
|
||||
port: 9200
|
||||
40
jzo2o-knife4j-web/pom.xml
Normal file
40
jzo2o-knife4j-web/pom.xml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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>jzo2o-parent</artifactId>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../jzo2o-parent</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jzo2o-knife4j-web</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<artifactId>jzo2o-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.jzo2o.knife4j.config;
|
||||
|
||||
import com.jzo2o.common.model.Result;
|
||||
import com.jzo2o.knife4j.filter.SwaggerFilter;
|
||||
import com.jzo2o.knife4j.properties.SwaggerProperties;
|
||||
import com.fasterxml.classmate.TypeResolver;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({SwaggerProperties.class})
|
||||
@Import(SwaggerFilter.class)
|
||||
@ConditionalOnProperty(prefix = "swagger", name = "enable", havingValue = "true")
|
||||
public class Knife4jConfiguration {
|
||||
|
||||
@Resource
|
||||
private SwaggerProperties swaggerProperties;
|
||||
|
||||
@Bean(value = "defaultApi2")
|
||||
public Docket defaultApi2(TypeResolver typeResolver) {
|
||||
// 1.初始化Docket
|
||||
Docket docket = new Docket(DocumentationType.SWAGGER_2);
|
||||
// 2.是否需要包装R
|
||||
docket.additionalModels(typeResolver.resolve(Result.class));
|
||||
return docket.apiInfo(new ApiInfoBuilder()
|
||||
.title(this.swaggerProperties.getTitle())
|
||||
.description(this.swaggerProperties.getDescription())
|
||||
.contact(new Contact(
|
||||
this.swaggerProperties.getContactName(),
|
||||
this.swaggerProperties.getContactUrl(),
|
||||
this.swaggerProperties.getContactEmail()))
|
||||
.version(this.swaggerProperties.getVersion())
|
||||
.build())
|
||||
.select()
|
||||
//这里指定Controller扫描包路径
|
||||
.apis(RequestHandlerSelectors.basePackage(this.swaggerProperties.getPackagePath()))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.jzo2o.knife4j.filter;
|
||||
|
||||
import com.jzo2o.common.utils.IoUtils;
|
||||
import com.jzo2o.knife4j.response.SwaggerTransformServletResponse;
|
||||
import com.jzo2o.knife4j.utils.ResponseWrapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class SwaggerFilter implements Filter {
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
if (((HttpServletRequest) servletRequest).getRequestURI().contains("/v2/api-docs")) {
|
||||
|
||||
ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) servletResponse);
|
||||
filterChain.doFilter(servletRequest, responseWrapper);
|
||||
responseWrapper.getResponseData();
|
||||
byte[] responseData = SwaggerTransformServletResponse.getBody(responseWrapper.getResponseData());
|
||||
IoUtils.write(servletResponse.getOutputStream(), false, responseData);
|
||||
} else {
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.jzo2o.knife4j.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* swagger配置属性
|
||||
*
|
||||
* @Author itheima
|
||||
* @Date 2023/04/06 17:25
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "swagger")
|
||||
public class SwaggerProperties implements Serializable {
|
||||
private Boolean enableResponseWrap = false;
|
||||
|
||||
/**
|
||||
* controller扫描路径
|
||||
*/
|
||||
public String packagePath;
|
||||
|
||||
/**
|
||||
* swagger文档标题
|
||||
*/
|
||||
public String title;
|
||||
|
||||
/**
|
||||
* 应用描述
|
||||
*/
|
||||
public String description;
|
||||
|
||||
/**
|
||||
* 联系人名称
|
||||
*/
|
||||
public String contactName;
|
||||
|
||||
/**
|
||||
* 联系人访问地址
|
||||
*/
|
||||
public String contactUrl;
|
||||
|
||||
/**
|
||||
* 联系人email
|
||||
*/
|
||||
public String contactEmail;
|
||||
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
public String version;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.jzo2o.knife4j.response;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SwaggerTransformServletResponse {
|
||||
|
||||
private static final String voidName = String.format("R«%s»", "Void");
|
||||
private static final String rName = "R";
|
||||
private static final String DEFINITIONS = "definitions";
|
||||
private static final String PATHS = "paths";
|
||||
private static final String RESPONSES = "responses";
|
||||
private static final String SCHEMA = "schema";
|
||||
private static final String REF = "$ref";
|
||||
private static final String TYPE = "type";
|
||||
private static final String TITLE = "title";
|
||||
private static final String ORIGINAL_REF = "originalRef";
|
||||
private static final String REF_PREFIX = "#/definitions/";
|
||||
|
||||
|
||||
public static byte[] getBody(byte[] bytes) throws IOException {
|
||||
|
||||
JSONObject strData = JSONUtil.createObj()
|
||||
.putOnce(TYPE, "string")
|
||||
.putOnce("example", "")
|
||||
.putOnce("description", "响应数据");
|
||||
JSONObject rDefinition = buildDefinition(rName, strData);
|
||||
JSONObject voidDefinition = buildDefinition(voidName, null);
|
||||
JSONObject voidSchema = buildSchema(voidName);
|
||||
JSONObject rSchema = buildSchema(rName);
|
||||
// 2.转为json
|
||||
String json = new String(bytes, StandardCharsets.UTF_8);
|
||||
JSONObject jsonObject = JSONUtil.parseObj(json);
|
||||
// 3.获取原始数据
|
||||
// 3.1.获取paths
|
||||
JSONObject paths = (JSONObject) jsonObject.getObj(PATHS);
|
||||
// 3.2.获取definitions
|
||||
JSONObject definitions = (JSONObject) jsonObject.getObj(DEFINITIONS);
|
||||
// 3.3.遍历路径
|
||||
for (Object pathObj : paths.values()) {
|
||||
JSONObject path = (JSONObject) pathObj;
|
||||
// 3.4.遍历请求方式对应的接口
|
||||
for (Object pathValueObj : path.values()) {
|
||||
JSONObject pathValue = (JSONObject) pathValueObj;
|
||||
// 3.5.获取200响应结果
|
||||
JSONObject responses = (JSONObject) pathValue.get(RESPONSES);
|
||||
JSONObject resp200 = (JSONObject) responses.get("200");
|
||||
// 3.6.获取旧的schema,基于schema做判断进行下一步处理
|
||||
Object schemaObj = resp200.get(SCHEMA);
|
||||
if (schemaObj != null) {
|
||||
// 有返回值
|
||||
JSONObject schema = (JSONObject) schemaObj;
|
||||
String type = schema.get(TYPE, String.class);
|
||||
if (type == null) {
|
||||
// 对象类型
|
||||
handleObjectSchema(resp200, definitions, schema);
|
||||
} else if (type.equals("array")) {
|
||||
// 数组类型
|
||||
handleArraySchema(resp200, definitions, schema);
|
||||
} else {
|
||||
// 基本类型
|
||||
handleBasicSchema(resp200, definitions, rDefinition,rSchema);
|
||||
}
|
||||
} else {
|
||||
// 返回值是void
|
||||
handleVoidSchema(resp200, definitions, voidDefinition, voidSchema);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return jsonObject.toString().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static void handleVoidSchema(JSONObject resp200, JSONObject definitions,JSONObject voidDefinition,JSONObject voidSchema) {
|
||||
// 1.写入definition
|
||||
definitions.putIfAbsent(voidName, voidDefinition);
|
||||
// 2.写入path
|
||||
resp200.putOpt(SCHEMA, voidSchema);
|
||||
}
|
||||
|
||||
private static void handleBasicSchema(JSONObject resp200, JSONObject definitions,JSONObject rDefinition,JSONObject rSchema) {
|
||||
// 1.写入definition
|
||||
definitions.putIfAbsent(rName, rDefinition);
|
||||
// 2.写入path
|
||||
resp200.putOpt(SCHEMA, rSchema);
|
||||
}
|
||||
|
||||
private static void handleArraySchema(JSONObject resp200, JSONObject definitions, JSONObject oldSchema) {
|
||||
// 1.写入definition
|
||||
String originalRef = oldSchema.getByPath("items." + ORIGINAL_REF, String.class);
|
||||
String name = String.format("R«List«%s»»", originalRef);
|
||||
definitions.putIfAbsent(name, buildArrayDefinition(originalRef, name));
|
||||
// 2.写入path
|
||||
resp200.putOpt(SCHEMA, buildSchema(name));
|
||||
}
|
||||
|
||||
private static void handleObjectSchema(JSONObject resp200, JSONObject definitions, JSONObject oldSchema) {
|
||||
// 1.写入definition
|
||||
String originalRef = oldSchema.get(ORIGINAL_REF, String.class);
|
||||
String name = String.format("R«%s»", originalRef);
|
||||
definitions.putIfAbsent(name, buildDefinition(name, buildSchema(originalRef)));
|
||||
// 2.写入path
|
||||
resp200.putOpt(SCHEMA, buildSchema(name));
|
||||
}
|
||||
|
||||
private static JSONObject buildArrayDefinition(String originalRef, String title) {
|
||||
JSONObject data = JSONUtil.createObj()
|
||||
.putOnce("description", "响应数据")
|
||||
.putOnce("type", "array")
|
||||
.putOnce("items",
|
||||
buildSchema(originalRef));
|
||||
return buildDefinition(title, data);
|
||||
}
|
||||
|
||||
private static JSONObject buildDefinition(String title, Object data) {
|
||||
JSONObject code = JSONUtil.createObj()
|
||||
.putOnce(TYPE, "integer")
|
||||
.putOnce("format", "int32")
|
||||
.putOnce("example", 200)
|
||||
.putOnce("description", "状态码,200-成功,其它-失败");
|
||||
JSONObject msg = JSONUtil.createObj()
|
||||
.putOnce(TYPE, "string")
|
||||
.putOnce("example", "OK")
|
||||
.putOnce("description", "响应消息");
|
||||
JSONObject properties = JSONUtil.createObj().putOnce("code", code).putOnce("msg", msg);
|
||||
if (data != null) {
|
||||
properties.putOnce("data", data);
|
||||
}
|
||||
return JSONUtil.createObj()
|
||||
.putOnce(TYPE, "object")
|
||||
.putOnce(TITLE, title)
|
||||
.putOnce("properties", properties)
|
||||
.putOnce("description", "通用响应结果");
|
||||
}
|
||||
|
||||
private static JSONObject buildSchema(String name) {
|
||||
return JSONUtil.createObj()
|
||||
.putOnce(REF, REF_PREFIX + name)
|
||||
.putOnce(ORIGINAL_REF, name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.jzo2o.knife4j.utils;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
/**
|
||||
* 重写response返回值
|
||||
*
|
||||
* @author itcast
|
||||
*/
|
||||
public class ResponseWrapper extends HttpServletResponseWrapper {
|
||||
private ByteArrayOutputStream buffer = null;
|
||||
private ServletOutputStream out = null;
|
||||
private PrintWriter writer = null;
|
||||
|
||||
public ResponseWrapper(HttpServletResponse resp) throws IOException {
|
||||
super(resp);
|
||||
buffer = new ByteArrayOutputStream();// 真正存储数据的流
|
||||
out = new WapperedOutputStream(buffer);
|
||||
writer = new PrintWriter(new OutputStreamWriter(buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() throws IOException {
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getWriter() throws UnsupportedEncodingException {
|
||||
return writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushBuffer() throws IOException {
|
||||
if (out != null) {
|
||||
out.flush();
|
||||
}
|
||||
if (writer != null) {
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
buffer.reset();
|
||||
}
|
||||
|
||||
public byte[] getResponseData() throws IOException {
|
||||
flushBuffer();
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
public String getContent() throws IOException{
|
||||
flushBuffer();
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private class WapperedOutputStream extends ServletOutputStream {
|
||||
private ByteArrayOutputStream bos = null;
|
||||
|
||||
public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException {
|
||||
bos = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
bos.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
bos.write(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
bos.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteListener(WriteListener writeListener) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"groups": [
|
||||
|
||||
],
|
||||
"properties": [
|
||||
|
||||
],
|
||||
"hints": []
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.jzo2o.knife4j.config.Knife4jConfiguration
|
||||
74
jzo2o-mvc/pom.xml
Normal file
74
jzo2o-mvc/pom.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?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>jzo2o-parent</artifactId>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../jzo2o-parent</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>jzo2o-mvc</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.jzo2o</groupId>
|
||||
<artifactId>jzo2o-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--undertow服务器-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.jzo2o.mvc.advice;
|
||||
|
||||
import com.jzo2o.common.constants.ErrorInfo;
|
||||
import com.jzo2o.common.expcetions.CommonException;
|
||||
import com.jzo2o.common.utils.*;
|
||||
import com.jzo2o.mvc.constants.HeaderConstants;
|
||||
import com.jzo2o.mvc.model.Result;
|
||||
import com.jzo2o.mvc.utils.RequestUtils;
|
||||
import com.jzo2o.mvc.utils.ResponseUtils;
|
||||
import feign.FeignException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import static com.jzo2o.mvc.constants.HeaderConstants.BODY_PROCESSED;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class CommonExceptionAdvice {
|
||||
|
||||
|
||||
/**
|
||||
* 捕获feign异常
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler({FeignException.class})
|
||||
public Result feignException(FeignException e) {
|
||||
ResponseUtils.setResponseHeader(BODY_PROCESSED, "1");
|
||||
Object headerValue = e.responseHeaders().get(HeaderConstants.INNER_ERROR);
|
||||
|
||||
if(RequestUtils.getRequest().getRequestURL().toString().contains("/inner/")) {
|
||||
// 内部接口调用内部接口,异常抛出
|
||||
if(ObjectUtils.isNull(headerValue)) {
|
||||
throw new CommonException(ErrorInfo.Msg.REQUEST_FAILD);
|
||||
}else {
|
||||
String encodeMsg = JsonUtils.parseArray(headerValue).getStr(0);
|
||||
String[] msgs = Base64Utils.decodeStr(encodeMsg).split("\\|");
|
||||
throw new CommonException(NumberUtils.parseInt(msgs[0]), msgs[1]);
|
||||
}
|
||||
}else {
|
||||
// 外部接口调用内部接口异常捕获
|
||||
if(ObjectUtils.isNull(headerValue)) {
|
||||
return Result.error(ErrorInfo.Msg.REQUEST_FAILD);
|
||||
}else {
|
||||
String encodeMsg = JsonUtils.parseArray(headerValue).getStr(0);
|
||||
String[] msgs = Base64Utils.decodeStr(encodeMsg).split("\\|");
|
||||
return Result.error(NumberUtils.parseInt(msgs[0]), msgs[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义异常处理
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler({CommonException.class})
|
||||
public Result customException(CommonException e) {
|
||||
log.error("请求异常,message:{},e", e.getMessage(),e);
|
||||
// 标识异常已被处理
|
||||
ResponseUtils.setResponseHeader(BODY_PROCESSED, "1");
|
||||
if(RequestUtils.getRequest().getRequestURL().toString().contains("/inner/")) {
|
||||
CommonException commonException = new CommonException(e.getCode(), e.getMessage());
|
||||
ResponseUtils.setResponseHeader(HeaderConstants.INNER_ERROR, Base64Utils.encodeStr(e.getCode() + "|" + e.getMessage()));
|
||||
throw commonException;
|
||||
}
|
||||
return Result.error(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 非自定义异常处理
|
||||
* @param e 异常
|
||||
* @return
|
||||
*/
|
||||
@ExceptionHandler({Exception.class})
|
||||
public Result noCustomException(Exception e) {
|
||||
log.error("请求异常,", e);
|
||||
// 标识异常已被处理
|
||||
ResponseUtils.setResponseHeader(BODY_PROCESSED, "1");
|
||||
if(RequestUtils.getRequest().getRequestURL().toString().contains("/inner/")) {
|
||||
CommonException commonException = new CommonException(ErrorInfo.Msg.REQUEST_FAILD);
|
||||
|
||||
ResponseUtils.setResponseHeader(HeaderConstants.INNER_ERROR, Base64Utils.encodeStr( "500|" + ErrorInfo.Msg.REQUEST_FAILD));
|
||||
throw commonException;
|
||||
}
|
||||
return Result.error(ErrorInfo.Msg.REQUEST_FAILD);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.jzo2o.mvc.config;
|
||||
|
||||
import com.jzo2o.mvc.handler.RequestIdHandlerImpl;
|
||||
import com.jzo2o.mvc.handler.UserInfoHandlerImpl;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Import({RequestIdHandlerImpl.class, UserInfoHandlerImpl.class})
|
||||
public class AutoConfiguration {
|
||||
}
|
||||
34
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/CrosConfig.java
Normal file
34
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/CrosConfig.java
Normal file
@@ -0,0 +1,34 @@
|
||||
//package com.jzo2o.mvc.config;
|
||||
//
|
||||
//import org.springframework.context.annotation.Bean;
|
||||
//import org.springframework.context.annotation.Configuration;
|
||||
//import org.springframework.web.cors.CorsConfiguration;
|
||||
//import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
//import org.springframework.web.filter.CorsFilter;
|
||||
//
|
||||
//import java.util.Collections;
|
||||
//
|
||||
///**
|
||||
// * 解决apifox拉取api报错问题
|
||||
// */
|
||||
//@Configuration
|
||||
//public class CrosConfig {
|
||||
//
|
||||
// @Bean
|
||||
// public CorsFilter corsFilter() {
|
||||
// // 跨域配置源
|
||||
// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
// //设置跨域的配置信息
|
||||
// CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
// //1,允许任何来源
|
||||
// corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
|
||||
// //2,允许任何请求头
|
||||
// corsConfiguration.addAllowedHeader(CorsConfiguration.ALL);
|
||||
// //3,允许任何方法
|
||||
// corsConfiguration.addAllowedMethod(CorsConfiguration.ALL);
|
||||
// //4,允许凭证
|
||||
// corsConfiguration.setAllowCredentials(true);
|
||||
// source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
// return new CorsFilter(source);
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.jzo2o.mvc.config;
|
||||
|
||||
import com.jzo2o.mvc.filter.PackResultFilter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@Configuration
|
||||
@Import(PackResultFilter.class)
|
||||
public class FilterConfiguration {
|
||||
}
|
||||
43
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/JsonConfig.java
Normal file
43
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/JsonConfig.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.jzo2o.mvc.config;
|
||||
|
||||
import com.jzo2o.common.utils.DateUtils;
|
||||
import com.jzo2o.mvc.serialize.BigDecimalSerializer;
|
||||
import com.jzo2o.mvc.serialize.LocalDateTimeSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import static com.jzo2o.common.utils.DateUtils.DEFAULT_DATE_TIME_FORMAT;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
public class JsonConfig {
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
|
||||
return jacksonObjectMapperBuilder -> {
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT);
|
||||
jacksonObjectMapperBuilder.timeZone(DateUtils.TIME_ZONE_8);
|
||||
jacksonObjectMapperBuilder.serializers(new com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer(dateTimeFormatter));
|
||||
jacksonObjectMapperBuilder.deserializers(new LocalDateTimeDeserializer(dateTimeFormatter));
|
||||
// long -> string
|
||||
jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
|
||||
jacksonObjectMapperBuilder.serializerByType(BigInteger.class, ToStringSerializer.instance);
|
||||
// bigDecimal保留两位小数
|
||||
jacksonObjectMapperBuilder.serializerByType(BigDecimal.class, BigDecimalSerializer.instance);
|
||||
// LocalDateTime 格式yyyy-MM-dd HH:mm:ss
|
||||
jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, LocalDateTimeSerializer.instance);
|
||||
};
|
||||
}
|
||||
}
|
||||
31
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/MvcConfig.java
Normal file
31
jzo2o-mvc/src/main/java/com/jzo2o/mvc/config/MvcConfig.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.jzo2o.mvc.config;
|
||||
|
||||
|
||||
import com.jzo2o.mvc.advice.CommonExceptionAdvice;
|
||||
import com.jzo2o.mvc.interceptor.UserContextInteceptor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class MvcConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* <h1>通用的ControllerAdvice异常处理器</h1>
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// token拦截器
|
||||
registry.addInterceptor(new UserContextInteceptor())
|
||||
.addPathPatterns("/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommonExceptionAdvice commonExceptionAdvice() {
|
||||
return new CommonExceptionAdvice();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//package com.jzo2o.mvc.config;
|
||||
//
|
||||
//import com.jzo2o.mvc.advice.CommonExceptionAdvice;
|
||||
//import com.jzo2o.mvc.interceptor.UserContextInteceptor;
|
||||
//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;
|
||||
//
|
||||
///**
|
||||
// * @author itcast
|
||||
// */
|
||||
//@Configuration
|
||||
//public class UserContextConfiguration implements WebMvcConfigurer {
|
||||
//
|
||||
// @Override
|
||||
// public void addInterceptors(InterceptorRegistry registry) {
|
||||
// // token拦截器
|
||||
// registry.addInterceptor(new UserContextInteceptor())
|
||||
// .addPathPatterns("/**");
|
||||
// }
|
||||
//
|
||||
// @Bean
|
||||
// public CommonExceptionAdvice commonExceptionAdvice() {
|
||||
// return new CommonExceptionAdvice();
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.jzo2o.mvc.constants;
|
||||
|
||||
public class HeaderConstants {
|
||||
/**
|
||||
* 判断响应参数是否已经处理,1:已经处理,其他未经处理
|
||||
*/
|
||||
public static final String BODY_PROCESSED = "Processed-Mark";
|
||||
|
||||
public static final String INNER_ERROR = "INNER-ERROR";
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.jzo2o.mvc.filter;
|
||||
|
||||
import com.jzo2o.common.model.Result;
|
||||
import com.jzo2o.common.utils.IoUtils;
|
||||
import com.jzo2o.mvc.wrapper.ResponseWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.jzo2o.mvc.constants.HeaderConstants.BODY_PROCESSED;
|
||||
|
||||
/**
|
||||
* 用于包装外网访问
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class PackResultFilter implements Filter {
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
// 1.无需包装,放过拦截
|
||||
String requestURI = ((HttpServletRequest) servletRequest).getRequestURI();
|
||||
if (requestURI.contains(".") ||
|
||||
requestURI.contains("/swagger") ||
|
||||
requestURI.contains("/api-docs") ||
|
||||
requestURI.contains("/inner")) {
|
||||
filterChain.doFilter(servletRequest, servletResponse);
|
||||
return;
|
||||
}
|
||||
// 2.包装响应值
|
||||
// 2.1.处理业务,获取响应值
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
ResponseWrapper responseWrapper = new ResponseWrapper(response);
|
||||
filterChain.doFilter(servletRequest, responseWrapper);
|
||||
|
||||
// 无需包装
|
||||
if (response.containsHeader(BODY_PROCESSED) && response.getHeader(BODY_PROCESSED).equals("1")) {
|
||||
IoUtils.write(response.getOutputStream(), false, responseWrapper.getResponseData());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.2.包装
|
||||
byte[] bytes = Result.plainOk(responseWrapper.getResponseData());
|
||||
log.debug("result : {}", new String(bytes));
|
||||
// 2.3.写入
|
||||
response.setContentType("applicaton/json;charset=UTF-8");
|
||||
IoUtils.write(response.getOutputStream(), false, bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.jzo2o.mvc.handler;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.jzo2o.common.constants.HeaderConstants;
|
||||
import com.jzo2o.common.handler.RequestIdHandler;
|
||||
import com.jzo2o.common.model.CurrentUserInfo;
|
||||
import com.jzo2o.common.utils.StringUtils;
|
||||
import com.jzo2o.mvc.utils.RequestUtils;
|
||||
import com.jzo2o.mvc.utils.UserContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@Component
|
||||
public class RequestIdHandlerImpl implements RequestIdHandler {
|
||||
@Override
|
||||
public String getRequestId() {
|
||||
// 从请求header头中获取请求id,获取不到id,生成新的请求id
|
||||
CurrentUserInfo currentUserInfo = UserContext.currentUser();
|
||||
if(currentUserInfo == null) {
|
||||
return null;
|
||||
}
|
||||
String requestId = RequestUtils.getValueFromHeader(HeaderConstants.REQUEST_ID);
|
||||
if (StringUtils.isEmpty(requestId)) {
|
||||
return IdUtil.getSnowflakeNextIdStr();
|
||||
} else {
|
||||
return requestId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.jzo2o.mvc.handler;
|
||||
|
||||
import com.jzo2o.common.handler.UserInfoHandler;
|
||||
import com.jzo2o.common.model.CurrentUserInfo;
|
||||
import com.jzo2o.mvc.utils.UserContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class UserInfoHandlerImpl implements UserInfoHandler {
|
||||
@Override
|
||||
public CurrentUserInfo currentUserInfo() {
|
||||
return UserContext.currentUser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.jzo2o.mvc.interceptor;
|
||||
|
||||
import com.jzo2o.common.constants.HeaderConstants;
|
||||
import com.jzo2o.common.model.CurrentUserInfo;
|
||||
import com.jzo2o.common.utils.Base64Utils;
|
||||
import com.jzo2o.common.utils.JsonUtils;
|
||||
import com.jzo2o.mvc.utils.UserContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author itcast
|
||||
*/
|
||||
@Slf4j
|
||||
public class UserContextInteceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 1.尝试获取头信息中的用户信息
|
||||
String userInfo = request.getHeader(HeaderConstants.USER_INFO);
|
||||
// 2.判断是否为空
|
||||
if (userInfo == null) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
// 3.base64解码用户信息
|
||||
String decodeUserInfo = Base64Utils.decodeStr(userInfo);
|
||||
CurrentUserInfo currentUserInfo = JsonUtils.toBean(decodeUserInfo, CurrentUserInfo.class);
|
||||
|
||||
// 4.转为用户id并保存
|
||||
UserContext.set(currentUserInfo);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("用户身份信息格式不正确,{}, 原因:{}", userInfo, e.getMessage());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||
// 清理用户信息
|
||||
UserContext.clear();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user