Initial commit for jzo2o-framework

This commit is contained in:
2026-07-01 00:24:23 +08:00
commit 33a0ed47dc
205 changed files with 9846 additions and 0 deletions

33
.gitignore vendored Normal file
View 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
View 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>

View File

@@ -0,0 +1,11 @@
package com.jzo2o.canal.constants;
/**
* 字段常量
*/
public class FieldConstants {
/**
* 字段 - id
*/
public static final String ID = "id";
}

View File

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

View File

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

View File

@@ -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;
}
}

View File

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

View File

@@ -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
View 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>

View File

@@ -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";
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -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类型都为topic2延迟队列命名由.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";
}
}

View File

@@ -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;
}

View File

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

View File

@@ -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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
* 使用场景网关校验tokentoken不合法或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);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
package com.jzo2o.common.expcetions;
/**
* 应用启动异常
* @author itcast
*/
public class StartException extends CommonException{
public StartException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,14 @@
package com.jzo2o.common.handler;
/**
* 特殊类型转换器
*/
public interface ConvertHandler<O, T> {
/**
* 特殊对象类型转换
*
* @param originObject 源对象
* @param targetObject 目标对象
*/
void map(O originObject, T targetObject);
}

View File

@@ -0,0 +1,9 @@
package com.jzo2o.common.handler;
/**
* 请求id获取
*/
public interface RequestIdHandler {
String getRequestId();
}

View File

@@ -0,0 +1,8 @@
package com.jzo2o.common.handler;
import com.jzo2o.common.model.CurrentUserInfo;
public interface UserInfoHandler {
CurrentUserInfo currentUserInfo();
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

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

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -0,0 +1,6 @@
package com.jzo2o.common.utils;
import cn.hutool.core.util.BooleanUtil;
public class BooleanUtils extends BooleanUtil {
}

View File

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

View File

@@ -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 {
}

View 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;
}
}

View File

@@ -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");
}
}

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

View File

@@ -0,0 +1,6 @@
package com.jzo2o.common.utils;
import cn.hutool.core.util.EnumUtil;
public class EnumUtils extends EnumUtil {
}

View File

@@ -0,0 +1,6 @@
package com.jzo2o.common.utils;
import cn.hutool.http.HttpUtil;
public class HttpUtils extends HttpUtil {
}

View File

@@ -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";
}

View File

@@ -0,0 +1,6 @@
package com.jzo2o.common.utils;
import cn.hutool.core.io.IoUtil;
public class IoUtils extends IoUtil {
}

View File

@@ -0,0 +1,6 @@
package com.jzo2o.common.utils;
import cn.hutool.json.JSONUtil;
public class JsonUtils extends JSONUtil {
}

View File

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

View File

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

View File

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

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
View 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>

View File

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

View File

@@ -0,0 +1,5 @@
package com.jzo2o.es.constants;
public class FieldConstants {
public static final String ID = "id";
}

View File

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

View File

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

View File

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

View File

@@ -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;
}
}

View File

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

View File

@@ -0,0 +1,4 @@
package com.jzo2o.es.core.operations;
public interface IndexOperations {
}

View File

@@ -0,0 +1,9 @@
package com.jzo2o.es.model.query;
import lombok.Data;
@Data
public class EsPageQuery {
}

View File

@@ -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;
}

View 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;
}
}
}

View File

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

View 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;
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jzo2o.es.config.EsConfiguration

View File

@@ -0,0 +1,4 @@
jzo2o:
es:
host: 192.168.101.65
port: 9200

40
jzo2o-knife4j-web/pom.xml Normal file
View 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>

View File

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

View File

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

View File

@@ -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;
}

View File

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

View File

@@ -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) {
}
}
}

View File

@@ -0,0 +1,9 @@
{
"groups": [
],
"properties": [
],
"hints": []
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.jzo2o.knife4j.config.Knife4jConfiguration

74
jzo2o-mvc/pom.xml Normal file
View 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>

View File

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

View File

@@ -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 {
}

View 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);
// }
//}

View File

@@ -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 {
}

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

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

View File

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

View File

@@ -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";
}

View File

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

View File

@@ -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;
}
}
}

View File

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

View File

@@ -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