Commit 59534eb7 authored by liaozan's avatar liaozan 🏀

Make modules all in one

parent b071d6e0
...@@ -28,4 +28,6 @@ target/ ...@@ -28,4 +28,6 @@ target/
# system ignore # system ignore
.DS_Store .DS_Store
Thumbs.db Thumbs.db
\ No newline at end of file
work/
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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.schbrain.framework</groupId>
<artifactId>commons</artifactId>
<version>${revision}</version>
</parent>
<groupId>com.schbrain.common</groupId>
<artifactId>common-util</artifactId>
<dependencies>
<dependency>
<groupId>com.schbrain.common</groupId>
<artifactId>common</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-blackbird</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.schbrain.common;
import com.schbrain.common.util.support.jackson.ObjectMapperModuleConfiguration;
import com.schbrain.common.util.support.task.ThreadPoolConfiguration;
import com.schbrain.common.util.support.trace.TraceParamAspect;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Import;
/**
* @author liaozan
* @since 2022/1/11
*/
@AutoConfiguration
@Import({TraceParamAspect.class, ThreadPoolConfiguration.class, ObjectMapperModuleConfiguration.class})
public class CommonAutoConfiguration {
}
\ No newline at end of file
package com.schbrain.common.util;
import org.springframework.core.env.Environment;
/**
* @author liaozan
* @since 2021/12/31
*/
public class ApplicationName {
public static String get(Environment environment) {
return environment.getRequiredProperty("spring.application.name");
}
}
package com.schbrain.common.util;
import cn.hutool.core.lang.Singleton;
import cn.hutool.core.util.ReflectUtil;
import com.google.common.base.Joiner;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cglib.beans.BeanCopier;
import java.util.ArrayList;
import java.util.List;
/**
* only support the same property type
*
* @author liaozan
* @since 2022/1/24
*/
public class BeanCopyUtils {
private static final Joiner CACHE_KEY_JOINER = Joiner.on("#");
/**
* copy object list
*/
public static <Source, Target> List<Target> copyList(List<Source> sourceList, Class<Target> targetType) {
if (CollectionUtils.isEmpty(sourceList)) {
return new ArrayList<>(0);
}
return StreamUtils.toList(sourceList, source -> copy(source, targetType), false, true);
}
/**
* copy object
*/
public static <Source, Target> Target copy(Source source, Class<Target> targetType) {
if (source == null || targetType == null) {
return null;
}
return copy(source, ReflectUtil.newInstanceIfPossible(targetType));
}
/**
* copy object
*/
public static <Source, Target> Target copy(Source source, Target target) {
if (source == null || target == null) {
return null;
}
BeanCopier copier = getCopier(source.getClass(), target.getClass());
copier.copy(source, target, null);
return target;
}
private static BeanCopier getCopier(Class<?> sourceClass, Class<?> targetClass) {
String cacheKey = buildCacheKey(sourceClass, targetClass);
return Singleton.get(cacheKey, () -> BeanCopier.create(sourceClass, targetClass, false));
}
private static String buildCacheKey(Class<?> source, Class<?> target) {
return CACHE_KEY_JOINER.join(source.getName(), target.getName());
}
}
\ No newline at end of file
package com.schbrain.common.util;
import cn.hutool.core.bean.BeanUtil;
import com.google.common.base.CaseFormat;
import com.google.common.base.Converter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.annotation.MergedAnnotation;
import java.util.*;
/**
* @author liaozan
* @since 2022/1/11
*/
public class ConfigurationPropertiesUtils {
private static final Converter<String, String> DEFAULT_CONVERTER = CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.LOWER_HYPHEN);
public static Map<String, Object> toMap(Object source) {
return toMap(source, true);
}
public static Map<String, Object> toMap(Object source, boolean ignoreNull) {
return toMap(source, true, DEFAULT_CONVERTER);
}
public static Map<String, Object> toMap(Object source, boolean ignoreNull, Converter<String, String> converter) {
if (source == null) {
return Collections.emptyMap();
}
Class<?> sourceClass = source.getClass();
if (sourceClass.isAnnotationPresent(ConfigurationProperties.class)) {
String prefix = getPrefix(sourceClass);
Map<String, Object> sourceMap = new LinkedHashMap<>();
return BeanUtil.beanToMap(source, sourceMap, ignoreNull, key -> prefix + "." + converter.convert(key));
}
return BeanUtil.beanToMap(source);
}
public static String getPrefix(Class<?> sourceClass) {
ConfigurationProperties configurationProperties = sourceClass.getAnnotation(ConfigurationProperties.class);
MergedAnnotation<ConfigurationProperties> mergedAnnotation = MergedAnnotation.from(configurationProperties);
return mergedAnnotation.getString(MergedAnnotation.VALUE);
}
}
\ No newline at end of file
package com.schbrain.common.util;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
/**
* 注意!! 此类获取 profile 的方法不能用在{@link BeanFactoryPostProcessor} 之前
*
* @author liaozan
* @see cn.hutool.extra.spring.SpringUtil
* @since 2021/12/13
*/
public class EnvUtils {
public static final String DEVELOPMENT = "dev";
public static final String TESTING = "test";
public static final String PRODUCTION = "prod";
public static boolean isDevelopment() {
return isDevelopment(getProfile());
}
public static boolean isDevelopment(String profile) {
return DEVELOPMENT.equals(profile);
}
public static boolean isTesting() {
return isTesting(getProfile());
}
public static boolean isTesting(String profile) {
return TESTING.equals(profile);
}
public static boolean isProduction() {
return isProduction(getProfile());
}
public static boolean isProduction(String profile) {
return PRODUCTION.equals(profile);
}
public static String getProfile() {
Environment environment = SpringUtil.getBean(Environment.class);
return getProfile(environment);
}
public static String getProfile(Environment environment) {
String[] profiles = environment.getActiveProfiles();
if (ArrayUtil.isEmpty(profiles)) {
profiles = environment.getDefaultProfiles();
}
return profiles[0];
}
public static boolean runningOnCloudPlatform(Environment environment) {
CloudPlatform cloudPlatform = CloudPlatform.getActive(environment);
return cloudPlatform != null && cloudPlatform != CloudPlatform.NONE;
}
}
\ No newline at end of file
package com.schbrain.common.util;
import com.alibaba.excel.EasyExcel;
import com.schbrain.common.util.support.excel.bean.ExcelReadResult;
import com.schbrain.common.util.support.excel.exception.ExcelException;
import com.schbrain.common.util.support.excel.listener.*;
import com.schbrain.common.util.support.excel.listener.HierarchicalDataReadListener.ImportedRecord;
import org.apache.commons.collections4.CollectionUtils;
import java.io.*;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
/**
* @author liaozan
* @since 2022/1/6
*/
public class ExcelUtils {
// file
public static ExcelReadResult<Map<Integer, Object>> read(File excelFile) {
return getMapExcelReadResult(excelFile);
}
public static <T> ExcelReadResult<T> readAs(Class<T> contentType, File excelFile) {
return getObjectExcelReadResult(contentType, excelFile);
}
public static <T> ExcelReadResult<T> readAs(Class<T> contentType, File excelFile, ExcelReadListenerBase<T> readListener) {
EasyExcel.read(excelFile, contentType, readListener).doReadAll();
return readListener.getReadResult();
}
public static List<ImportedRecord> readHierarchicalData(File excelFile) {
HierarchicalDataReadListener readListener = new HierarchicalDataReadListener();
return readHierarchicalData(excelFile, readListener);
}
public static List<ImportedRecord> readHierarchicalData(File excelFile, HierarchicalDataReadListener readListener) {
EasyExcel.read(excelFile, readListener).doReadAll();
return readListener.getImportedRecords();
}
// stream
public static ExcelReadResult<Map<Integer, Object>> read(InputStream inputStream) {
return getMapExcelReadResult(inputStream);
}
public static <T> ExcelReadResult<T> readAs(Class<T> contentType, InputStream inputStream) {
return getObjectExcelReadResult(contentType, inputStream);
}
public static <T> ExcelReadResult<T> readAs(Class<T> contentType, InputStream inputStream, ExcelReadListenerBase<T> readListener) {
EasyExcel.read(inputStream, contentType, readListener).doReadAll();
return readListener.getReadResult();
}
public static List<ImportedRecord> readHierarchicalData(InputStream inputStream) {
HierarchicalDataReadListener readListener = new HierarchicalDataReadListener();
return readHierarchicalData(inputStream, readListener);
}
public static List<ImportedRecord> readHierarchicalData(InputStream inputStream, HierarchicalDataReadListener readListener) {
EasyExcel.read(inputStream, readListener).doReadAll();
return readListener.getImportedRecords();
}
// write
public static <T> Path writeTo(Path location, List<T> dataList) {
if (CollectionUtils.isEmpty(dataList)) {
throw new ExcelException("DataList is empty");
}
return writeTo(location, dataList, dataList.get(0).getClass());
}
public static <T> Path writeTo(Path location, List<T> dataList, Class<?> head) {
if (CollectionUtils.isEmpty(dataList)) {
throw new ExcelException("DataList is empty");
}
EasyExcel.write(location.toFile())
.sheet()
.head(head)
.doWrite(dataList);
return location;
}
public static <T> OutputStream writeTo(OutputStream outputStream, List<T> dataList) {
if (CollectionUtils.isEmpty(dataList)) {
throw new ExcelException("DataList is empty");
}
return writeTo(outputStream, dataList, dataList.get(0).getClass());
}
public static <T> OutputStream writeTo(OutputStream outputStream, List<T> dataList, Class<?> head) {
if (CollectionUtils.isEmpty(dataList)) {
throw new ExcelException("DataList is empty");
}
EasyExcel.write(outputStream)
.sheet()
.head(head)
.doWrite(dataList);
return outputStream;
}
private static ExcelReadResult<Map<Integer, Object>> getMapExcelReadResult(Object excelFile) {
ExcelMapDataReadListener readListener = new ExcelMapDataReadListener();
if (excelFile instanceof File) {
EasyExcel.read((File) excelFile, readListener).doReadAll();
} else if (excelFile instanceof InputStream) {
EasyExcel.read((InputStream) excelFile, readListener).doReadAll();
} else {
throw new ExcelException("Unsupported excel file");
}
return readListener.getReadResult();
}
private static <T> ExcelReadResult<T> getObjectExcelReadResult(Class<T> contentType, Object excelFile) {
ExcelReadListenerBase<T> readListener = new ExcelBeanReadListener<>();
if (excelFile instanceof File) {
EasyExcel.read((File) excelFile, contentType, readListener).doReadAll();
} else if (excelFile instanceof InputStream) {
EasyExcel.read((InputStream) excelFile, contentType, readListener).doReadAll();
} else {
throw new ExcelException("Unsupported excel file");
}
return readListener.getReadResult();
}
}
\ No newline at end of file
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.schbrain.common.util;
import java.security.SecureRandom;
import java.util.Random;
/***
* @author adyliu (imxylz@gmail.com)
* @since 1.0
*/
public class IdWorker {
/**
* 生成的自增id的大小减少到18位
*/
private static final long ID_EPOCH = 1420041600000L;
private static final long workerIdBits = 5L;
private static final long datacenterIdBits = 5L;
private static final long maxWorkerId = ~(-1L << workerIdBits);
private static final long maxDatacenterId = ~(-1L << datacenterIdBits);
private static final long sequenceBits = 12L;
private static final long workerIdShift = sequenceBits;
private static final long datacenterIdShift = sequenceBits + workerIdBits;
private static final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private static final long sequenceMask = ~(-1L << sequenceBits);
private static final Random r = new SecureRandom();
// 需要等 r 初始化
private static final IdWorker INSTANCE = new IdWorker(ID_EPOCH);
private final long workerId;
private final long datacenterId;
private final long idEpoch;
private long lastTimestamp = -1L;
private long sequence;
public IdWorker(long idEpoch) {
this(r.nextInt((int) maxWorkerId), r.nextInt((int) maxDatacenterId), 0, idEpoch);
}
public IdWorker(long workerId, long datacenterId, long sequence) {
this(workerId, datacenterId, sequence, 1420041600000L);
}
public IdWorker(long workerId, long datacenterId, long sequence, long idEpoch) {
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = sequence;
this.idEpoch = idEpoch;
if (workerId < 0 || workerId > maxWorkerId) {
throw new IllegalArgumentException("workerId is illegal: " + workerId);
}
if (datacenterId < 0 || datacenterId > maxDatacenterId) {
throw new IllegalArgumentException("datacenterId is illegal: " + workerId);
}
if (idEpoch >= System.currentTimeMillis()) {
throw new IllegalArgumentException("idEpoch is illegal: " + idEpoch);
}
}
public static long getId() {
return INSTANCE.nextId();
}
public static String getIdStr() {
return String.valueOf(INSTANCE.nextId());
}
/**
* get the timestamp (millis second) of id
*
* @param id the nextId
* @return the timestamp of id
*/
public long getIdTimestamp(long id) {
return idEpoch + (id >> timestampLeftShift);
}
private synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new IllegalStateException("Clock moved backwards.");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
lastTimestamp = timestamp;
return ((timestamp - idEpoch) << timestampLeftShift)//
| (datacenterId << datacenterIdShift)//
| (workerId << workerIdShift)//
| sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
\ No newline at end of file
package com.schbrain.common.util;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.util.Enumeration;
/**
* Copy from SpringCloud
*
* @author liaozan
* @since 2021/11/19
*/
@Slf4j
public class InetUtils {
public static HostInfo findFirstNonLoopBackHostInfo() {
InetAddress address = findFirstNonLoopBackAddress();
if (address != null) {
return convertAddress(address);
}
HostInfo hostInfo = new HostInfo();
hostInfo.setHostname("localhost");
hostInfo.setIpAddress("127.0.0.1");
return hostInfo;
}
private static InetAddress findFirstNonLoopBackAddress() {
InetAddress result = null;
try {
int lowest = Integer.MAX_VALUE;
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement();
if (networkInterface.isUp()) {
if (networkInterface.getIndex() < lowest || result == null) {
lowest = networkInterface.getIndex();
} else {
continue;
}
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress address = inetAddresses.nextElement();
if (address instanceof Inet4Address && !address.isLoopbackAddress()) {
result = address;
}
}
}
}
} catch (IOException ex) {
log.error("Cannot get first non-loopBack address", ex);
}
if (result != null) {
return result;
}
try {
return InetAddress.getLocalHost();
} catch (UnknownHostException e) {
log.warn("Unable to retrieve localhost");
}
return null;
}
private static HostInfo convertAddress(final InetAddress address) {
HostInfo hostInfo = new HostInfo();
String hostname = address.getHostName();
hostInfo.setHostname(hostname);
hostInfo.setIpAddress(address.getHostAddress());
return hostInfo;
}
@Data
public static class HostInfo {
public static final String NAME = "machineHostInfo";
private String ipAddress;
private String hostname;
@JsonIgnore
public int getIpAddressAsInt() {
InetAddress inetAddress;
String host = this.ipAddress;
if (host == null) {
host = this.hostname;
}
try {
inetAddress = InetAddress.getByName(host);
} catch (final UnknownHostException e) {
throw new IllegalArgumentException(e);
}
return ByteBuffer.wrap(inetAddress.getAddress()).getInt();
}
}
}
\ No newline at end of file
package com.schbrain.common.util;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.NullNode;
import com.schbrain.common.constants.ResponseActionConstants;
import com.schbrain.common.constants.ResponseCodeConstants;
import com.schbrain.common.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.TypeUtils;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
/**
* @author liaozan
* @since 2021/10/12
*/
@Slf4j
public class JacksonUtils {
private static ObjectMapper OBJECT_MAPPER;
private static ObjectMapper PRETTY_OBJECT_MAPPER;
public static ObjectMapper getObjectMapper() {
if (OBJECT_MAPPER == null) {
// Delay to get ObjectMapper from spring container to keep the same behavior with application
try {
OBJECT_MAPPER = SpringUtil.getBean(ObjectMapper.class).copy();
} catch (Exception e) {
log.warn("Could not get ObjectMapper from Spring Container, return new instance for use");
OBJECT_MAPPER = new ObjectMapper();
}
OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
return OBJECT_MAPPER;
}
public static ObjectMapper getPrettyObjectMapper() {
if (PRETTY_OBJECT_MAPPER == null) {
PRETTY_OBJECT_MAPPER = getObjectMapper().copy().enable(INDENT_OUTPUT);
}
return PRETTY_OBJECT_MAPPER;
}
public static String toJsonString(Object data) {
return toJsonString(data, false);
}
public static String toPrettyJsonString(Object data) {
return toJsonString(data, true);
}
public static String toJsonString(Object data, boolean pretty) {
try {
if (pretty) {
return getPrettyObjectMapper().writeValueAsString(data);
}
return getObjectMapper().writeValueAsString(data);
} catch (Exception e) {
throw new JSONException("Object 转 JSON 出错", e);
}
}
public static JsonNode getJsonNode(String json) {
try {
if (StringUtils.isBlank(json)) {
return NullNode.getInstance();
}
return getObjectMapper().readTree(json);
} catch (Exception e) {
throw new JSONException("JSON 转 JsonNode 出错", e);
}
}
public static <T> T getObjectFromBytes(byte[] bytes, Class<T> type) {
if (ArrayUtils.isEmpty(bytes)) {
return null;
}
try {
return getObjectMapper().readValue(bytes, type);
} catch (Exception e) {
throw new JSONException("Byte 转 Object 出错", e);
}
}
public static <T> T getObjectFromBytes(byte[] bytes, ParameterizedType type) {
if (ArrayUtils.isEmpty(bytes)) {
return null;
}
try {
JavaType valueType = getObjectMapper().getTypeFactory().constructType(type);
return getObjectMapper().readValue(bytes, valueType);
} catch (Exception e) {
throw new JSONException("Byte 转 Object 出错", e);
}
}
public static <T> T getObjectFromBytes(byte[] bytes, TypeReference<T> typeReference) {
if (ArrayUtils.isEmpty(bytes)) {
return null;
}
try {
return getObjectMapper().readValue(bytes, typeReference);
} catch (Exception e) {
throw new JSONException("Byte 转 Object 出错", e);
}
}
public static <T> T getObjectFromBytesWithTypeConstruct(byte[] bytes, Class<T> genericsType, Class<?>... innerTypes) {
if (ArrayUtils.isEmpty(bytes)) {
return null;
}
try {
JavaType valueType = constructType(genericsType, innerTypes);
return getObjectMapper().readValue(bytes, valueType);
} catch (Exception e) {
throw new JSONException("Byte 转 Object 出错", e);
}
}
public static <T> T getObjectFromJson(String json, Class<T> type) {
if (StringUtils.isBlank(json)) {
return null;
}
try {
return getObjectMapper().readValue(json, type);
} catch (Exception e) {
throw new JSONException("JSON 转 Object 出错", e);
}
}
public static <T> T getObjectFromJson(String json, ParameterizedType type) {
if (StringUtils.isBlank(json)) {
return null;
}
try {
JavaType valueType = getObjectMapper().getTypeFactory().constructType(type);
return getObjectMapper().readValue(json, valueType);
} catch (Exception e) {
throw new JSONException("JSON 转 Object 出错", e);
}
}
public static <T> T getObjectFromJson(String json, TypeReference<T> typeReference) {
if (StringUtils.isBlank(json)) {
return null;
}
try {
return getObjectMapper().readValue(json, typeReference);
} catch (Exception e) {
throw new JSONException("JSON 转 Object 出错", e);
}
}
public static <T> T getObjectFromJsonWithTypeConstruct(String json, Class<T> genericsType, Class<?>... innerTypes) {
if (StringUtils.isBlank(json)) {
return null;
}
try {
JavaType valueType = constructType(genericsType, innerTypes);
return getObjectMapper().readValue(json, valueType);
} catch (Exception e) {
throw new JSONException("JSON 转 Object 出错", e);
}
}
public static Map<String, Object> getMapFromJson(String json) {
return getMapFromJson(json, Object.class);
}
public static <V> Map<String, V> getMapFromJson(String json, Class<V> valueType) {
return getMapFromJson(json, String.class, valueType);
}
public static <K, V> Map<K, V> getMapFromJson(String json, Class<K> keyType, Class<V> valueType) {
if (StringUtils.isBlank(json)) {
return new LinkedHashMap<>();
}
try {
JavaType mapType = getObjectMapper().getTypeFactory().constructMapType(LinkedHashMap.class, keyType, valueType);
return getObjectMapper().readValue(json, mapType);
} catch (Exception e) {
throw new JSONException("JSON 转 Map 出错", e);
}
}
public static <T> List<T> getListFromJson(String json, Class<T> itemType) {
if (StringUtils.isBlank(json)) {
return new ArrayList<>();
}
try {
JavaType listType = getObjectMapper().getTypeFactory().constructCollectionType(ArrayList.class, itemType);
return getObjectMapper().readValue(json, listType);
} catch (Exception e) {
throw new JSONException("JSON 转 List 出错", e);
}
}
public static byte[] writeObjectAsBytes(Object data) {
try {
return getObjectMapper().writeValueAsBytes(data);
} catch (Exception e) {
throw new JSONException("Object 转 Byte 出错", e);
}
}
public static JavaType constructType(Class<?> genericsType, Class<?>... innerTypes) {
return getObjectMapper().getTypeFactory().constructType(TypeUtils.parameterize(genericsType, innerTypes));
}
public static <T> T convertValue(Object fromValue, Class<T> toValueType) {
try {
return getObjectMapper().convertValue(fromValue, toValueType);
} catch (Exception e) {
throw new JSONException("JSON 转换出错", e);
}
}
public static <T> T convertValue(Object fromValue, JavaType toValueType) {
try {
return getObjectMapper().convertValue(fromValue, toValueType);
} catch (Exception e) {
throw new JSONException("JSON 转换出错", e);
}
}
public static class JSONException extends BaseException {
private static final long serialVersionUID = 1656914307906296812L;
public JSONException(String message, Throwable cause) {
super(message, cause, ResponseCodeConstants.SERVER_ERROR, ResponseActionConstants.ALERT);
}
}
}
\ No newline at end of file
package com.schbrain.common.util;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.schbrain.common.entity.PageParam;
import com.schbrain.common.entity.PaginationInfo;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import static com.schbrain.common.constants.PageConstants.*;
/**
* @author liaozan
* @since 2022/1/7
*/
public class PageUtils {
public static <T> IPage<T> fromParam(PageParam pageParam) {
return fromParam(pageParam.getPageIndex(), pageParam.getPageSize(), pageParam.isSearchCount());
}
public static <T> IPage<T> fromParam(long pageIndex, long pageSize) {
return fromParam(pageIndex, pageSize, DEFAULT_SEARCH_COUNT);
}
public static <T> IPage<T> fromParam(long pageIndex, long pageSize, boolean searchCount) {
return Page.of(pageIndex, pageSize, searchCount);
}
public static <T> PaginationInfo<T> fromResult(IPage<T> page) {
return fromResult(page, page.getRecords());
}
public static <T, R> PaginationInfo<R> fromResult(IPage<T> page, Function<T, R> mapper) {
return fromResult(page.convert(mapper));
}
public static <T, R> PaginationInfo<R> fromResult(IPage<T> page, List<R> dataList) {
return new PaginationInfo<>(page.getCurrent(), page.getSize(), page.getTotal(), dataList);
}
public static <T, R> PaginationInfo<R> fromResult(PaginationInfo<T> info, Function<T, R> mapper) {
List<R> dataList = StreamUtils.toList(info.getDataList(), mapper);
return fromResult(info, dataList);
}
public static <T, R> PaginationInfo<R> fromResult(PaginationInfo<T> info, List<R> dataList) {
return new PaginationInfo<>(info.getPageIndex(), info.getPageSize(), info.getTotalCount(), dataList);
}
public static <T> PaginationInfo<T> emptyResult(IPage<T> page) {
return new PaginationInfo<>(page.getCurrent(), page.getSize(), DEFAULT_TOTAL_COUNT, Collections.emptyList());
}
public static <T> PaginationInfo<T> emptyResult(PageParam pageParam) {
return new PaginationInfo<>(pageParam.getPageIndex(), pageParam.getPageSize(), DEFAULT_TOTAL_COUNT, Collections.emptyList());
}
public static <T> PaginationInfo<T> emptyResult(PaginationInfo<?> page) {
return new PaginationInfo<>(page.getPageIndex(), page.getPageSize(), DEFAULT_TOTAL_COUNT, Collections.emptyList());
}
public static PageParam toParam(IPage<?> page) {
PageParam pageParam = new PageParam();
pageParam.setPageIndex(Math.toIntExact(page.getCurrent()));
pageParam.setPageSize(Math.toIntExact(page.getSize()));
pageParam.setSearchCount(page.searchCount());
return pageParam;
}
}
\ No newline at end of file
package com.schbrain.common.util;
import cn.hutool.core.util.ArrayUtil;
import com.google.common.collect.Maps;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author liaozan
* @since 2022/5/18
*/
public class ParameterDiscoverUtils {
private static final ParameterNameDiscoverer DISCOVERER = new DefaultParameterNameDiscoverer();
public static Map<String, Object> getMethodArgsMap(Method method, Object[] args) {
String[] parameterNames = DISCOVERER.getParameterNames(method);
if (ArrayUtil.isEmpty(parameterNames)) {
// Return a new instance to avoid external modification causing errors
return new LinkedHashMap<>();
}
Map<String, Object> argsMap = Maps.newLinkedHashMapWithExpectedSize(parameterNames.length);
for (int i = 0; i < parameterNames.length; i++) {
argsMap.put(parameterNames[i], args[i]);
}
return argsMap;
}
}
\ No newline at end of file
package com.schbrain.common.util;
import com.schbrain.common.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.ServerSocket;
/**
* net util
*
* @author xuxueli 2017-11-29 17:00:25
*/
@Slf4j
public class PortUtils {
public static int findAvailablePort(int defaultPort) {
int portTmp = defaultPort;
while (portTmp < 65535) {
if (!isPortUsed(portTmp)) {
return portTmp;
} else {
portTmp++;
}
}
throw new BaseException("no available port.");
}
public static boolean isPortUsed(int port) {
boolean used = false;
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
log.warn("current port[{}] is in use", port);
used = true;
} finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
log.info(e.getMessage(), e);
}
}
}
return used;
}
}
package com.schbrain.common.util;
import org.apache.commons.lang3.StringUtils;
import org.springframework.expression.*;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SpelUtils {
private static final ExpressionParser spELParser = new SpelExpressionParser();
private static final ConcurrentHashMap<String, Expression> expressionCache = new ConcurrentHashMap<>();
public static <T> T parse(String express, Map<String, Object> variables, Class<T> valueType) {
StandardEvaluationContext ctx = new StandardEvaluationContext();
ctx.setVariables(variables);
return parse(express, ctx, valueType);
}
public static <T> T parse(String express, EvaluationContext context, Class<T> valueType) {
if (StringUtils.isBlank(express)) {
return null;
}
return getExpression(express).getValue(context, valueType);
}
private static Expression getExpression(String exp) {
Expression expression = expressionCache.get(exp);
if (null == expression) {
expression = spELParser.parseExpression(exp);
expressionCache.put(exp, expression);
}
return expression;
}
}
\ No newline at end of file
package com.schbrain.common.util;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.*;
/**
* @author liaozan
* @since 2022/8/21
*/
@Slf4j
public class StreamUtils {
public static <T> List<T> filterToList(Collection<T> data, Predicate<T> predicate) {
return filter(data, predicate, Collectors.toList());
}
public static <T> Set<T> filterToSet(Collection<T> data, Predicate<T> predicate) {
return filter(data, predicate, Collectors.toSet());
}
public static <T, C extends Collection<T>> C filter(Collection<T> data, Predicate<T> predicate, Collector<T, ?, C> collector) {
return Optional.ofNullable(data).orElse(emptyList()).stream().filter(predicate).collect(collector);
}
public static <T, E> List<E> toList(Collection<T> data, Function<T, E> mapper) {
return toList(data, mapper, false);
}
public static <T, E> List<E> toList(Collection<T> data, Function<T, E> mapper, boolean distinct) {
return toList(data, mapper, distinct, false);
}
public static <T, E> List<E> toList(Collection<T> data, Function<T, E> mapper, boolean distinct, boolean ignoreNull) {
return extract(data, mapper, distinct, ignoreNull, Collectors.toList());
}
public static <T, E> Set<E> toSet(Collection<T> data, Function<T, E> mapper) {
return toSet(data, mapper, false);
}
public static <T, E> Set<E> toSet(Collection<T> data, Function<T, E> mapper, boolean ignoreNull) {
return extract(data, mapper, ignoreNull, false, Collectors.toSet());
}
public static <K, T> Map<K, T> toMap(Collection<T> data, Function<T, K> keyMapper) {
return toMap(data, keyMapper, false);
}
public static <K, T> Map<K, T> toMap(Collection<T> data, Function<T, K> keyMapper, boolean ordered) {
return toMap(data, keyMapper, Function.identity(), ordered);
}
public static <K, T, V> Map<K, V> toMap(Collection<T> data, Function<T, K> keyMapper, Function<T, V> valueMapper) {
return toMap(data, keyMapper, valueMapper, false);
}
public static <K, T, V> Map<K, V> toMap(Collection<T> data, Function<T, K> keyMapper, Function<T, V> valueMapper, boolean ordered) {
Supplier<Map<K, V>> mapFactory = HashMap::new;
if (ordered) {
mapFactory = LinkedHashMap::new;
}
return toMap(data, keyMapper, valueMapper, mapFactory);
}
public static <K, T, V, M extends Map<K, V>> Map<K, V> toMap(Collection<T> data, Function<T, K> keyMapper, Function<T, V> valueMapper, Supplier<M> mapFactory) {
return Optional.ofNullable(data)
.orElse(emptyList())
.stream()
.collect(Collectors.toMap(keyMapper, valueMapper, (oldValue, newValue) -> {
// Could not get the key when mergeFunction invoke
log.warn("There are multiple values with the same key when toMap, return the old one");
return oldValue;
}, mapFactory));
}
public static <K, T> Map<K, List<T>> groupBy(Collection<T> data, Function<T, K> mapper) {
return groupBy(data, mapper, false);
}
public static <K, T> Map<K, List<T>> groupBy(Collection<T> data, Function<T, K> keyMapper, boolean ignoreNullKey) {
return groupBy(data, keyMapper, Collectors.toList(), ignoreNullKey);
}
public static <K, T, V> Map<K, V> groupBy(Collection<T> data, Function<T, K> mapper, Collector<T, ?, V> collectors) {
return groupBy(data, mapper, collectors, false);
}
public static <K, T, V> Map<K, V> groupBy(Collection<T> data, Function<T, K> mapper, Collector<T, ?, V> collectors, boolean ignoreNullKey) {
return groupBy(data, mapper, Function.identity(), collectors, ignoreNullKey);
}
public static <K, T, V> Map<K, List<V>> groupBy(Collection<T> data, Function<T, K> keyMapper, Function<T, V> valueMapper) {
return groupBy(data, keyMapper, valueMapper, Collectors.toList(), false);
}
public static <K, T, V> Map<K, List<V>> groupBy(Collection<T> data, Function<T, K> keyMapper, Function<T, V> valueMapper, boolean ignoreNullKey) {
return groupBy(data, keyMapper, valueMapper, Collectors.toList(), ignoreNullKey);
}
public static <K, T, V, C> Map<K, C> groupBy(Collection<T> data, Function<T, K> keyMapper, Function<T, V> valueMapper, Collector<V, ?, C> collector) {
return groupBy(data, keyMapper, valueMapper, collector, false);
}
public static <K, T, V, C> Map<K, C> groupBy(Collection<T> data, Function<T, K> keyMapper, Function<T, V> valueMapper, Collector<V, ?, C> collector, boolean ignoreNullKey) {
return groupBy(data, keyMapper, valueMapper, collector, ignoreNullKey, HashMap::new);
}
public static <K, T, V, C> Map<K, C> groupBy(Collection<T> data, Function<T, K> keyMapper, Function<T, V> valueMapper, Collector<V, ?, C> collector, boolean ignoreNullKey, Supplier<Map<K, C>> mapSupplier) {
Stream<T> stream = Optional.ofNullable(data)
.orElse(emptyList())
.stream();
if (ignoreNullKey) {
stream = stream.filter(item -> null != keyMapper.apply(item));
}
return stream.collect(groupingBy(keyMapper, mapSupplier, mapping(valueMapper, collector)));
}
public static <T> String join(Collection<T> data, CharSequence delimiter) {
return join(data, delimiter, Objects::toString);
}
public static <T> String join(Collection<T> data, CharSequence delimiter, Function<T, ? extends CharSequence> toStringFunction) {
return join(data, delimiter, "", "", toStringFunction);
}
public static <T> String join(Collection<T> data, CharSequence delimiter, String prefix, String suffix, Function<T, ? extends CharSequence> toStringFunction) {
return Optional.ofNullable(data)
.orElse(emptyList())
.stream().map(toStringFunction).collect(joining(delimiter, prefix, suffix));
}
public static <T, E, R> R extract(Collection<T> data, Function<T, E> mapper, boolean distinct, boolean ignoreNull, Collector<E, ?, R> collector) {
Predicate<E> predicate = null;
if (ignoreNull) {
predicate = Objects::nonNull;
}
return extract(data, mapper, predicate, distinct, collector);
}
public static <T, E, R> R extract(Collection<T> data, Function<T, E> mapper, Predicate<E> predicate, boolean distinct, Collector<E, ?, R> collector) {
Stream<E> stream = Optional.ofNullable(data)
.orElse(emptyList())
.stream()
.map(mapper);
if (distinct) {
stream = stream.distinct();
}
if (predicate != null) {
stream = stream.filter(predicate);
}
return stream.collect(collector);
}
}
\ No newline at end of file
package com.schbrain.common.util;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.IdUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.slf4j.MDC;
/**
* @author liaozan
* @since 2021/10/10
*/
public class TraceIdUtils {
public static final String TRACE_ID = "traceId";
private static final boolean skywalkingTracePresent;
private static final ThreadLocal<String> TRACE_ID_CONTAINER = InheritableThreadLocal.withInitial(TraceIdUtils::create);
static {
skywalkingTracePresent = ClassLoaderUtil.isPresent("org.apache.skywalking.apm.toolkit.trace.TraceContext", TraceIdUtils.class.getClassLoader());
}
public static String get() {
return TRACE_ID_CONTAINER.get();
}
public static void set(String traceId) {
if (traceId == null) {
return;
}
MDC.put(TRACE_ID, traceId);
TRACE_ID_CONTAINER.set(traceId);
}
public static void clear() {
MDC.remove(TRACE_ID);
TRACE_ID_CONTAINER.remove();
}
private static String create() {
String traceId = MDC.get(TRACE_ID);
if (traceId == null) {
if (skywalkingTracePresent) {
traceId = TraceContext.traceId();
}
if (StringUtils.isBlank(traceId) || "N/A".equals(traceId)) {
traceId = IdUtil.objectId().toUpperCase();
}
MDC.put(TRACE_ID, traceId);
}
return traceId;
}
}
\ No newline at end of file
package com.schbrain.common.util.cipher;
import cn.hutool.crypto.symmetric.AES;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
import java.util.Base64;
import static java.nio.charset.StandardCharsets.UTF_8;
@Slf4j
public class AES128ECBWithPKCS7 {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* encrypt input text
*/
public static String encrypt(String input, String key) {
byte[] encrypted = encryptByte(input, key);
if (null == encrypted) {
return "";
}
return Base64.getEncoder().encodeToString(encrypted);
}
public static byte[] encryptByte(String input, String key) {
byte[] encrypted;
try {
encrypted = new AES("ECB", "PKCS7Padding", key.getBytes(UTF_8)).encrypt(input);
} catch (Exception e) {
log.error("AES128ECBWithPKCS7 encrypt ({}) error! ", input, e);
return null;
}
return encrypted;
}
/**
* decrypt input text
*/
public static String decrypt(String input, String key) {
return decryptByte(Base64.getDecoder().decode(input), key);
}
public static String decryptByte(byte[] input, String key) {
try {
byte[] output = new AES("ECB", "PKCS7Padding", key.getBytes(UTF_8)).decrypt(input);
return new String(output, UTF_8);
} catch (Exception e) {
log.error("AES128ECBWithPKCS7 decryptByte ({}) error!", new String(input, UTF_8), e);
return "";
}
}
}
\ No newline at end of file
package com.schbrain.common.util.log;
import lombok.Data;
@Data
public class LogEvent<T extends LogEventAction> {
/**
* @see com.schbrain.common.constants.LogConstants.ProductTypeEnum
*/
private String product;
private T eventAction;
public LogEvent(String product) {
this.product = product;
}
}
\ No newline at end of file
package com.schbrain.common.util.log;
import java.util.HashMap;
public class LogEventAction extends HashMap<Object, Object> {
private static final long serialVersionUID = -8663962491116732785L;
private static final String NAME = "name";
public LogEventAction(String actionName) {
put(NAME, actionName);
}
public String getActionName() {
return (String) get(NAME);
}
public void setActionName(String actionName) {
put(NAME, actionName);
}
public void setActionParam(Object key, Object value) {
put(key, value);
}
public Object getActionParam(Object key) {
return get(key);
}
}
\ No newline at end of file
package com.schbrain.common.util.log;
import com.schbrain.common.util.JacksonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StatisticLogUtil {
public static final Logger log = LoggerFactory.getLogger(StatisticLogUtil.class);
public static <T extends LogEventAction> void logEvent(LogEvent<T> logEvent) {
if (null != logEvent) {
log.info(JacksonUtils.toJsonString(logEvent));
}
}
}
\ No newline at end of file
package com.schbrain.common.util.properties;
import com.schbrain.common.util.ConfigurationPropertiesUtils;
import org.springframework.core.env.MapPropertySource;
import java.util.Map;
/**
* mark class to ensure property order
*
* @author liaozan
* @since 2021/12/6
*/
public class SchbrainMapPropertySource extends MapPropertySource {
public SchbrainMapPropertySource(String name, Object source) {
this(name, ConfigurationPropertiesUtils.toMap(source));
}
public SchbrainMapPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
}
\ No newline at end of file
package com.schbrain.common.util.support;
import com.schbrain.common.util.ConfigurationPropertiesUtils;
import lombok.Data;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.core.env.ConfigurableEnvironment;
import java.beans.Introspector;
/**
* <b> WARNING!!! </b>
* <p>
* If you want to use a subclass of this class before {@link BeanPostProcessor},
* please load it through {@link com.schbrain.framework.autoconfigure.apollo.util.ConfigUtils}
*
* @author liaozan
* @since 2022/1/10
*/
@Data
public abstract class ConfigurableProperties {
/**
* the namespace of remote config
*/
protected String namespace = getDefaultNamespace();
/**
* the prefix of properties
*/
protected String prefix = getPossiblePrefix();
/**
* the name of propertySource
*/
protected String name = Introspector.decapitalize(getClass().getSimpleName());
public String getDefaultNamespace() {
return "application";
}
@SuppressWarnings({"unchecked", "unused"})
public <T extends ConfigurableProperties> T bindOrCreate(ConfigurableEnvironment environment, boolean afterMerge) {
return (T) Binder.get(environment, bindHandler()).bindOrCreate(getPrefix(), getClass());
}
protected BindHandler bindHandler() {
return BindHandler.DEFAULT;
}
private String getPossiblePrefix() {
ConfigurationProperties annotation = getClass().getAnnotation(ConfigurationProperties.class);
if (annotation == null) {
String className = ConfigurationProperties.class.getName();
String errorDetail = getClass().getSimpleName() + " must annotated @" + className + " or overwrite getPrefix method";
throw new IllegalStateException(errorDetail);
}
return ConfigurationPropertiesUtils.getPrefix(getClass());
}
}
\ No newline at end of file
package com.schbrain.common.util.support;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
/**
* @author liaozan
* @since 2021/3/27
*/
public interface ValidateSupport {
default boolean hasText(String text) {
return StringUtils.isNotBlank(text);
}
default boolean isBlank(String text) {
return !hasText(text);
}
default boolean isNull(Object object) {
return object == null;
}
default boolean isNotNull(Object object) {
return !isNull(object);
}
default boolean isEmpty(Collection<?> collection) {
return CollectionUtils.isEmpty(collection);
}
default boolean isNotEmpty(Collection<?> collection) {
return !isEmpty(collection);
}
default boolean isEmpty(Map<?, ?> map) {
return MapUtils.isEmpty(map);
}
default boolean isNotEmpty(Map<?, ?> map) {
return !isEmpty(map);
}
default String fixNull(String value) {
return fixNull(value, "");
}
default <T> List<T> fixNull(List<T> value) {
return fixNull(value, new ArrayList<>());
}
default <T> Set<T> fixNull(Set<T> value) {
return fixNull(value, new HashSet<>());
}
default <T> T fixNull(T value, T defaultValue) {
return value != null ? value : defaultValue;
}
}
\ No newline at end of file
package com.schbrain.common.util.support.delay;
import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author lik
* @since 2023/4/17
*/
@Slf4j
public abstract class AbstractDelayMessageListener<Message> implements Runnable, InitializingBean, DisposableBean {
private final String threadNamePrefix;
private final String queueName;
private final String listenerName;
private final AtomicBoolean started = new AtomicBoolean(false);
private ExecutorService executor;
public AbstractDelayMessageListener(String queueName) {
this(null, queueName);
}
public AbstractDelayMessageListener(String threadNamePrefix, String queueName) {
this.listenerName = getClass().getSimpleName();
this.threadNamePrefix = threadNamePrefix == null ? listenerName + "#delay-message-" : threadNamePrefix;
this.queueName = queueName;
}
@Override
public void run() {
try {
while (started.get()) {
Message message = DelayedQueueUtils.takeMsg(queueName);
if (log.isDebugEnabled()) {
log.debug("{} receive message :{}", listenerName, message);
}
onMessage(message);
}
} catch (Exception e) {
log.error("{} occur error: {}", listenerName, e.getMessage(), e);
}
}
@Override
public void afterPropertiesSet() {
executor = ThreadUtil.newFixedExecutor(1, threadNamePrefix, true);
started.set(true);
executor.execute(this);
log.info("{} start listen...", listenerName);
}
@Override
public void destroy() {
started.set(false);
executor.shutdown();
log.info("{} stop listen... ", listenerName);
}
/**
* for subClass
*/
protected abstract void onMessage(Message message);
}
\ No newline at end of file
package com.schbrain.common.util.support.delay;
import cn.hutool.core.text.StrPool;
import cn.hutool.extra.spring.SpringUtil;
import com.schbrain.common.exception.BaseException;
import com.schbrain.common.util.ApplicationName;
import org.redisson.api.*;
import org.springframework.beans.BeansException;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* @author lik
* @since 2023/4/17
*/
public class DelayedQueueUtils {
private static final String APPLICATION_NAME = ApplicationName.get(SpringUtil.getBean(Environment.class));
private static final ConcurrentHashMap<String, RBlockingQueue<?>> blockingQueueCache = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, RDelayedQueue<?>> delayedQueueCache = new ConcurrentHashMap<>();
private static RedissonClient CLIENT;
public static <T> void offerMsg(T message, String queueName, Long delay, TimeUnit timeUnit) {
batchOfferMsg(List.of(message), queueName, delay, timeUnit);
}
public static <T> void batchOfferMsg(List<T> messages, String queueName, Long delay, TimeUnit timeUnit) {
if (CollectionUtils.isEmpty(messages)) {
return;
}
RDelayedQueue<T> delayedQueue = getDelayedQueue(queueName);
messages.forEach(msg -> delayedQueue.offer(msg, delay, timeUnit));
}
public static <T> T takeMsg(String queueName) {
RBlockingQueue<T> blockingQueue = getBlockingQueue(queueName);
try {
return blockingQueue.take();
} catch (InterruptedException e) {
throw new BaseException("redis blocking queue thread has been interrupted", e);
}
}
public static <T> Boolean removeMsg(String queueName, T msg) {
if (msg == null) {
return false;
}
RDelayedQueue<Object> delayedQueue = getDelayedQueue(queueName);
return delayedQueue.remove(msg);
}
public static void removeQueue(String queueName) {
String wrappedQueueName = withPrefix(queueName);
RBlockingQueue<?> blockingQueue = blockingQueueCache.get(wrappedQueueName);
RDelayedQueue<?> delayedQueue = delayedQueueCache.get(wrappedQueueName);
if (delayedQueue != null) {
delayedQueue.destroy();
delayedQueueCache.remove(wrappedQueueName);
}
if (blockingQueue != null) {
blockingQueue.delete();
blockingQueueCache.remove(wrappedQueueName);
}
}
@SuppressWarnings("unchecked")
private static <T> RDelayedQueue<T> getDelayedQueue(String queueName) {
String cacheKey = withPrefix(queueName);
return (RDelayedQueue<T>) delayedQueueCache.computeIfAbsent(cacheKey, key -> {
RBlockingQueue<?> blockingQueue = getBlockingQueue(queueName);
return getClient().getDelayedQueue(blockingQueue);
});
}
@SuppressWarnings("unchecked")
private static <T> RBlockingQueue<T> getBlockingQueue(String queueName) {
String cacheKey = withPrefix(queueName);
return (RBlockingQueue<T>) blockingQueueCache.computeIfAbsent(cacheKey, key -> getClient().getBlockingQueue(cacheKey));
}
private static RedissonClient getClient() {
if (CLIENT == null) {
try {
CLIENT = SpringUtil.getBean(RedissonClient.class);
} catch (BeansException e) {
throw new BaseException("Could not get RedissonClient, please put it into Spring container", e);
}
}
return CLIENT;
}
private static String withPrefix(String queueName) {
return String.join(StrPool.COLON, APPLICATION_NAME, "delay-queue", queueName);
}
}
\ No newline at end of file
package com.schbrain.common.util.support.excel.bean;
import com.google.common.collect.Table;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @author liaozan
* @since 2022/1/6
*/
@Data
public class ExcelReadResult<T> {
private List<T> dataList;
private Map<Integer, String> headMap;
private Table<String, Integer, String> errors;
private String errorsAsString;
public boolean hasError() {
return !errors.isEmpty();
}
}
\ No newline at end of file
package com.schbrain.common.util.support.excel.exception;
import com.schbrain.common.exception.BaseException;
/**
* @author liaozan
* @since 2022/1/6
*/
public class ExcelException extends BaseException {
private static final long serialVersionUID = -2338463360273587530L;
public ExcelException(String message) {
super(message);
}
public ExcelException(String message, Throwable cause) {
super(message, cause);
}
}
package com.schbrain.common.util.support.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.metadata.holder.ReadSheetHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import javax.validation.ConstraintViolation;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author liaozan
* @since 2022/1/7
*/
@Slf4j
public class ExcelBeanReadListener<T> extends ExcelReadListenerBase<T> {
@Override
protected boolean validate(T data, AnalysisContext context) {
Set<ConstraintViolation<T>> violations = getValidator().validate(data);
if (CollectionUtils.isEmpty(violations)) {
return true;
}
collectErrorMsg(context, violations);
return false;
}
protected void collectErrorMsg(AnalysisContext context, Set<ConstraintViolation<T>> violations) {
String errorMsg = violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));
ReadSheetHolder currentSheet = context.readSheetHolder();
String sheetName = currentSheet.getSheetName();
Integer rowIndex = currentSheet.getRowIndex();
getErrors().put(sheetName, rowIndex + 1, errorMsg);
}
}
\ No newline at end of file
package com.schbrain.common.util.support.excel.listener;
import java.util.Map;
/**
* @author liaozan
* @since 2022/1/6
*/
public class ExcelMapDataReadListener extends ExcelReadListenerBase<Map<Integer, Object>> {
}
\ No newline at end of file
package com.schbrain.common.util.support.excel.listener;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.schbrain.common.util.support.excel.bean.ExcelReadResult;
import com.schbrain.common.util.support.excel.exception.ExcelException;
import lombok.*;
import javax.validation.Validator;
import java.util.*;
/**
* @author liaozan
* @since 2022/1/6
*/
@Getter(AccessLevel.PROTECTED)
@Setter(AccessLevel.PROTECTED)
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ExcelReadListenerBase<T> extends AnalysisEventListener<T> {
protected final Validator validator = SpringUtil.getBean(Validator.class);
protected List<T> dataList = new LinkedList<>();
protected Map<Integer, String> headers = new HashMap<>();
protected Table<String, Integer, String> errors = HashBasedTable.create();
protected boolean terminateOnValidateFail = false;
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headers = headMap;
}
@Override
public void invoke(T data, AnalysisContext context) {
boolean validated = validate(data, context);
if (!validated) {
if (isTerminateOnValidateFail()) {
throw new ExcelException(getErrorMsg());
}
}
this.dataList.add(data);
}
@Override
public void onException(Exception exception, AnalysisContext context) {
throw new ExcelException(exception.getMessage(), exception);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
public ExcelReadResult<T> getReadResult() {
ExcelReadResult<T> readResult = new ExcelReadResult<>();
readResult.setDataList(dataList);
readResult.setHeadMap(headers);
readResult.setErrors(errors);
readResult.setErrorsAsString(getErrorMsg());
return readResult;
}
protected String getErrorMsg() {
StringBuilder msgBuilder = new StringBuilder();
errors.rowMap().forEach((sheetName, rows) -> {
msgBuilder.append("sheet: [ ").append(sheetName).append(" ] ");
rows.forEach((rowIndex, error) -> {
String formattedErrorMsg = String.format("第%d行: [ %s ] ", rowIndex, error);
msgBuilder.append(formattedErrorMsg);
});
msgBuilder.append("\n");
});
return msgBuilder.toString();
}
protected boolean validate(T data, AnalysisContext context) {
return true;
}
}
\ No newline at end of file
package com.schbrain.common.util.support.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.schbrain.common.util.support.excel.exception.ExcelException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
/**
* 层级数据 excel 读取类
*
* @author liaozan
* @since 2022/3/29
*/
@Slf4j
public class HierarchicalDataReadListener extends ExcelReadListenerBase<Map<Integer, String>> {
private final List<ImportedRecord> importedRecords = new LinkedList<>();
private final Table<Integer, Integer, ImportedRecord> coordinateTable = HashBasedTable.create();
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
super.invoke(data, context);
Integer rowIndex = context.readRowHolder().getRowIndex();
data.forEach((columnIndex, value) -> {
if (StringUtils.isNotBlank(value)) {
buildImportedRow(rowIndex, columnIndex, value);
}
});
}
public List<ImportedRecord> getImportedRecords() {
return importedRecords;
}
@Override
protected boolean validate(Map<Integer, String> data, AnalysisContext context) {
Integer rowIndex = context.readRowHolder().getRowIndex();
if (MapUtils.isEmpty(data)) {
throw new ExcelException(String.format("第 %d 行未读到数据", rowIndex + 1));
}
if (rowIndex == 0) {
if (StringUtils.isBlank(data.get(0))) {
throw new ExcelException("第一行第一列未读到数据");
}
}
return true;
}
protected void buildImportedRow(Integer rowIndex, Integer columnIndex, String text) {
ImportedRecord importedRecord = new ImportedRecord();
importedRecord.setText(text);
coordinateTable.put(rowIndex, columnIndex, importedRecord);
if (columnIndex == 0) {
importedRecords.add(importedRecord);
} else {
int currentRowIndex = rowIndex;
// 定位到前一列的单元格
Map<Integer, ImportedRecord> prevColumn = coordinateTable.column(columnIndex - 1);
// 从当前行往上找,找到非空的记录,即视为 parent
ImportedRecord parent = prevColumn.get(currentRowIndex);
while (parent == null && currentRowIndex > 0) {
parent = prevColumn.get(--currentRowIndex);
}
if (parent == null) {
throw new ExcelException("数据格式错误,请对比模板调整");
}
parent.getChildren().add(importedRecord);
}
}
@Data
public static class ImportedRecord {
private String text;
private List<ImportedRecord> children = new LinkedList<>();
public boolean hasChildren() {
return CollectionUtils.isNotEmpty(children);
}
}
}
\ No newline at end of file
package com.schbrain.common.util.support.jackson;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.*;
import com.fasterxml.jackson.datatype.jsr310.ser.*;
import com.schbrain.common.constants.DateTimeFormatters;
import java.time.*;
/**
* @author liaozan
* @since 2022/8/12
*/
public class JavaTimeModule extends SimpleModule {
private static final long serialVersionUID = -1848725752934764693L;
public JavaTimeModule() {
this.setup();
}
protected void setup() {
this.addSerializer(YearMonth.class, new YearMonthSerializer(DateTimeFormatters.YEAR_MONTH));
this.addSerializer(MonthDay.class, new MonthDaySerializer(DateTimeFormatters.MONTH_DATE));
this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatters.DATE));
this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatters.TIME));
this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatters.DATE_TIME));
this.addDeserializer(YearMonth.class, new YearMonthDeserializer(DateTimeFormatters.YEAR_MONTH));
this.addDeserializer(MonthDay.class, new MonthDayDeserializer(DateTimeFormatters.MONTH_DATE));
this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatters.DATE));
this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatters.TIME));
this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatters.DATE_TIME));
}
}
\ No newline at end of file
package com.schbrain.common.util.support.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.blackbird.BlackbirdModule;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author liaozan
* @since 2022/1/11
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ObjectMapper.class)
public class ObjectMapperModuleConfiguration {
@Bean
public BlackbirdModule blackbirdModule() {
return new BlackbirdModule();
}
@Bean
@ConditionalOnMissingBean
public JavaTimeModule javaTimeModule() {
return new JavaTimeModule();
}
}
\ No newline at end of file
package com.schbrain.common.util.support.lock;
import cn.hutool.core.text.StrPool;
import cn.hutool.extra.spring.SpringUtil;
import com.schbrain.common.exception.BaseException;
import com.schbrain.common.util.ApplicationName;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeansException;
import org.springframework.core.env.Environment;
import java.time.Duration;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* @author liaozan
* @since 2023-03-15
*/
@Slf4j
public class RedisLockUtils {
private static final String APPLICATION_NAME = ApplicationName.get(SpringUtil.getBean(Environment.class));
private static RedissonClient CLIENT;
public static void executeWithLock(String lockName, Runnable action) {
executeWithLock(lockName, Duration.ofSeconds(3), action);
}
public static void executeWithLock(String lockName, Duration timeout, Runnable action) {
executeWithLock(lockName, timeout, () -> {
action.run();
return null;
});
}
public static <T> T executeWithLock(String lockName, Callable<T> action) {
return executeWithLock(lockName, Duration.ofSeconds(3), action);
}
public static <T> T executeWithLock(String lockName, Duration timeout, Callable<T> action) {
RLock lock = getClient().getLock(withPrefix(lockName));
boolean locked;
try {
locked = lock.tryLock(timeout.toMillis(), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
throw new BaseException("Lock thread has been interrupted", e);
}
if (!locked) {
throw new BaseException(String.format("Lock cannot be acquired within %d seconds", timeout.toSeconds()));
}
try {
return action.call();
} catch (Exception e) {
throw new BaseException(e.getMessage(), e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
private static RedissonClient getClient() {
if (CLIENT == null) {
try {
CLIENT = SpringUtil.getBean(RedissonClient.class);
} catch (BeansException e) {
throw new BaseException("Could not get RedissonClient, please put it into Spring container", e);
}
}
return CLIENT;
}
private static String withPrefix(String lockName) {
return String.join(StrPool.COLON, APPLICATION_NAME, "lock", lockName);
}
}
\ No newline at end of file
package com.schbrain.common.util.support.task;
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import java.util.Map;
/**
* @author liaozan
* @since 2022/1/11
*/
public class MdcContextPropagationTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
runnable.run();
};
}
}
\ No newline at end of file
package com.schbrain.common.util.support.task;
import org.springframework.boot.task.TaskExecutorCustomizer;
import org.springframework.boot.task.TaskSchedulerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author liaozan
* @since 2021/11/22
*/
@Configuration(proxyBeanMethods = false)
public class ThreadPoolConfiguration {
@Bean
public TaskExecutorCustomizer mdcSupportTaskExecutorCustomizer() {
return taskExecutor -> {
taskExecutor.setTaskDecorator(new MdcContextPropagationTaskDecorator());
taskExecutor.setThreadFactory(new UnCaughtExceptionHandleThreadFactory(taskExecutor));
};
}
@Bean
public TaskSchedulerCustomizer mdcSupportTaskSchedulerCustomizer() {
return taskScheduler -> taskScheduler.setThreadFactory(new UnCaughtExceptionHandleThreadFactory(taskScheduler));
}
}
\ No newline at end of file
package com.schbrain.common.util.support.task;
import lombok.extern.slf4j.Slf4j;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ThreadFactory;
/**
* @author liaozan
* @since 2022/1/11
*/
public class UnCaughtExceptionHandleThreadFactory implements ThreadFactory {
private final ThreadFactory delegate;
public UnCaughtExceptionHandleThreadFactory(ThreadFactory delegate) {
this.delegate = delegate;
}
@Override
public Thread newThread(Runnable runnable) {
Thread thread = delegate.newThread(runnable);
thread.setUncaughtExceptionHandler(new LoggingUnCaughtExceptionHandler());
return thread;
}
@Slf4j
public static class LoggingUnCaughtExceptionHandler implements UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
log.warn("uncaughtException on {}", thread.getName(), throwable);
thread.interrupt();
}
}
}
\ No newline at end of file
package com.schbrain.common.util.support.trace;
import com.schbrain.common.util.JacksonUtils;
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Map;
import static com.schbrain.common.util.ParameterDiscoverUtils.getMethodArgsMap;
/**
* Please use it only for method
* <p>
* Trace the parameter of method which annotated with this annotation
*
* @author liaozan
* @since 2022/3/31
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TraceParam {
FormatShape shape() default FormatShape.PRETTY_JSON;
enum FormatShape {
RAW {
@Override
protected String format0(Map<String, Object> argsMap) {
return argsMap.toString();
}
},
PRETTY_JSON {
@Override
public String format0(Map<String, Object> argsMap) {
return JacksonUtils.toPrettyJsonString(argsMap);
}
};
public String format(Method method, Object[] args) {
Map<String, Object> methodArgsMap = getMethodArgsMap(method, args);
return format0(methodArgsMap);
}
protected abstract String format0(Map<String, Object> argsMap);
}
}
\ No newline at end of file
package com.schbrain.common.util.support.trace;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Advisor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import java.lang.reflect.Method;
/**
* @author liaozan
* @since 2022/3/31
*/
@Slf4j
@Aspect
@ConditionalOnClass({Advisor.class, Aspect.class})
public class TraceParamAspect {
@Before("@annotation(traceParam)")
public void tracedMethod(JoinPoint joinPoint, TraceParam traceParam) {
try {
tracingParam(joinPoint, traceParam);
} catch (Exception e) {
log.warn("Could not extract args for method annotated with @{}", TraceParam.class.getSimpleName(), e);
}
}
protected void tracingParam(JoinPoint joinPoint, TraceParam annotation) {
if (!(joinPoint instanceof MethodSignature)) {
return;
}
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Class<?> declaringType = methodSignature.getDeclaringType();
Method method = methodSignature.getMethod();
Object[] args = joinPoint.getArgs();
String formattedArgs = annotation.shape().format(method, args);
String content = format(method, formattedArgs);
LoggerFactory.getLogger(declaringType).info(content);
}
protected String format(Method method, Object formattedArgs) {
return String.format("%s\n%s", method.toGenericString(), formattedArgs);
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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.schbrain.framework</groupId>
<artifactId>commons</artifactId>
<version>${revision}</version>
</parent>
<groupId>com.schbrain.common</groupId>
<artifactId>common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.schbrain.common.annotation;
import java.lang.annotation.*;
/**
* 免登注解
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface IgnoreLogin {
/**
* 是否忽略登录
* @return
*/
boolean ignore() default true;
}
package com.schbrain.common.constants;
import java.time.format.DateTimeFormatter;
import static java.time.ZoneId.systemDefault;
import static java.time.format.DateTimeFormatter.ofPattern;
/**
* @author liaozan
* @since 2021/10/15
*/
@SuppressWarnings("unused")
public class DateTimeFormatters {
public static final String YEAR_MONTH_PATTERN = "yyyy-MM";
public static final String MONTH_DATE_PATTERN = "MM-dd";
public static final String DATE_PATTERN = "yyyy-MM-dd";
public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String TIME_PATTERN = "HH:mm:ss";
public static final DateTimeFormatter YEAR_MONTH = ofPattern(YEAR_MONTH_PATTERN).withZone(systemDefault());
public static final DateTimeFormatter MONTH_DATE = ofPattern(MONTH_DATE_PATTERN).withZone(systemDefault());
public static final DateTimeFormatter DATE = ofPattern(DATE_PATTERN).withZone(systemDefault());
public static final DateTimeFormatter DATE_TIME = ofPattern(DATE_TIME_PATTERN).withZone(systemDefault());
public static final DateTimeFormatter TIME = ofPattern(TIME_PATTERN).withZone(systemDefault());
public static final String YEAR_MONTH_WITH_SLASH_PATTERN = "yyyy/MM";
public static final String DATE_WITH_SLASH_PATTERN = "yyyy/MM/dd";
public static final DateTimeFormatter YEAR_MONTH_WITH_SLASH = ofPattern(YEAR_MONTH_WITH_SLASH_PATTERN).withZone(systemDefault());
public static final DateTimeFormatter DATE_WITH_SLASH = ofPattern(DATE_WITH_SLASH_PATTERN).withZone(systemDefault());
}
\ No newline at end of file
package com.schbrain.common.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.NoSuchElementException;
public class LogConstants {
@Getter
@AllArgsConstructor
public enum ProductTypeEnum {
JSC("jsc", "驾驶舱"),
ZS("zs", "中枢"),
ZYB("zyb", "作业宝"),
SZ("sz", "数治");
private final String type;
private final String desc;
public static ProductTypeEnum valueOfType(String type) {
for (ProductTypeEnum productTypeEnum : values()) {
if (productTypeEnum.getType().equals(type)) {
return productTypeEnum;
}
}
throw new NoSuchElementException(type);
}
}
}
\ No newline at end of file
package com.schbrain.common.constants;
/**
* @author liaozan
* @since 2022/8/12
*/
public class PageConstants {
/**
* 分页时是否 count
*/
public static final boolean DEFAULT_SEARCH_COUNT = true;
/**
* 默认页码数
*/
public static final int DEFAULT_PAGE_INDEX = 1;
/**
* 默认分页大小
*/
public static final int DEFAULT_PAGE_SIZE = 20;
/**
* 默认总页数
*/
public static final long DEFAULT_TOTAL_PAGE_COUNT = 1;
/**
* 默认总记录数
*/
public static final long DEFAULT_TOTAL_COUNT = 0;
}
\ No newline at end of file
package com.schbrain.common.constants;
/**
* @author liwu
* @since 2019/3/29
*/
public class ResponseActionConstants {
/**
* 业务无异常时统一返回0
*/
public static final int NO_ACTION = 0;
/**
* 忽略异常
*/
public static final int IGNORE = -1;
/**
* 弹框
*/
public static final int ALERT = -2;
/**
* toast
*/
public static final int TOAST = -3;
/**
* 弹框,点击确定后刷新页面
*/
public static final int ALERT_REFRESH = -4;
}
\ No newline at end of file
package com.schbrain.common.constants;
/**
* @author liwu
* @since 2019/3/29
*/
public class ResponseCodeConstants {
/**
* 成功
*/
public static final int SUCCESS = 0;
/**
* 服务器错误,空指针、数组越界等非业务代码抛出异常
*/
public static final int SERVER_ERROR = -1;
/**
* 非法请求,参数异常、参数格式错误等接口的请求非法性抛出的通用错误
*/
public static final int PARAM_INVALID = -2;
/**
* 无权限
*/
public static final int ACCESS_DENIED = -3;
/**
* 用户未登录,且该接口需要登录
*/
public static final int LOGIN_REQUIRED = -4;
/**
* 系统维护
*/
public static final int SYSTEM_MAINTENANCE = -5;
}
\ No newline at end of file
package com.schbrain.common.entity;
import com.schbrain.common.constants.PageConstants;
import lombok.Data;
import java.io.Serializable;
/**
* @author liaozan
* @since 2022/1/7
*/
@Data
public class PageParam implements Serializable {
private static final long serialVersionUID = 4760680296146863368L;
private int pageIndex = PageConstants.DEFAULT_PAGE_INDEX;
private int pageSize = PageConstants.DEFAULT_PAGE_SIZE;
private boolean searchCount = PageConstants.DEFAULT_SEARCH_COUNT;
}
\ No newline at end of file
package com.schbrain.common.entity;
import lombok.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static com.schbrain.common.constants.PageConstants.*;
/**
* @author liaozan
* @since 2021/10/15
*/
@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PaginationInfo<T> implements Serializable {
public static final long serialVersionUID = 1320105164315113239L;
/**
* 页索引
*/
private long pageIndex = DEFAULT_PAGE_INDEX;
/**
* 每个页面大小
*/
private long pageSize = DEFAULT_PAGE_SIZE;
/**
* 当前结果集记录数量
*/
private long currentPageSize = DEFAULT_PAGE_INDEX;
/**
* 总页面数量
*/
private long totalPageCount = DEFAULT_TOTAL_COUNT;
/**
* 满足条件的记录数量
*/
private long totalCount = DEFAULT_TOTAL_COUNT;
/**
* 是否有前一页
*/
private boolean hasPrevPage = false;
/**
* 是否有下一页
*/
private boolean hasNextPage = false;
/**
* 结果集, Use new ArrayList() instead of collections.emptyList() to prevent errors when users edit it later
*/
private List<T> dataList = new ArrayList<>(0);
public PaginationInfo(long pageIndex, long pageSize, long totalCount) {
this(pageIndex, pageSize, totalCount, new ArrayList<>(0));
}
public PaginationInfo(long pageIndex, long pageSize, long totalCount, List<T> dataList) {
this.setPageIndex(pageIndex);
this.setPageSize(pageSize);
this.setTotalCount(totalCount);
this.setDataList(dataList);
}
public void setPageIndex(long pageIndex) {
if (pageIndex <= 0) {
pageIndex = DEFAULT_PAGE_INDEX;
}
this.pageIndex = pageIndex;
}
public void setPageSize(long pageSize) {
if (pageSize <= 0) {
pageSize = DEFAULT_PAGE_SIZE;
}
this.pageSize = pageSize;
}
public void setTotalCount(long totalCount) {
if (totalCount < 0L) {
totalCount = DEFAULT_TOTAL_COUNT;
}
if (totalCount == 0L) {
this.totalPageCount = DEFAULT_TOTAL_PAGE_COUNT;
} else {
this.totalPageCount = (totalCount - 1L) / this.pageSize + 1L;
}
this.hasPrevPage = this.pageIndex > DEFAULT_PAGE_INDEX;
this.hasNextPage = this.pageIndex < totalPageCount;
this.totalCount = totalCount;
}
public void setDataList(List<T> dataList) {
if (dataList == null) {
dataList = new ArrayList<>(0);
}
this.dataList = dataList;
this.currentPageSize = dataList.size();
}
}
\ No newline at end of file
package com.schbrain.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author huangxi
* @since 2022/08/23
*/
@Getter
@AllArgsConstructor
public enum BooleanEnum {
/**
* true
*/
TRUE(1),
/**
* false
*/
FALSE(0);
private final Integer value;
public static boolean validate(Integer value) {
for (BooleanEnum booleanEnum : values()) {
if (booleanEnum.getValue().equals(value)) {
return true;
}
}
return false;
}
}
\ No newline at end of file
package com.schbrain.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author huangxi
* @since 2022/08/23
*/
@Getter
@AllArgsConstructor
public enum ValidateEnum {
/**
* 有效
*/
VALID(0),
/**
* 无效
*/
INVALID(-1);
private final Integer value;
}
\ No newline at end of file
package com.schbrain.common.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
import static com.schbrain.common.constants.ResponseActionConstants.ALERT;
import static com.schbrain.common.constants.ResponseCodeConstants.SERVER_ERROR;
/**
* @author liaozan
* @since 2021/10/15
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class BaseException extends RuntimeException {
private static final long serialVersionUID = 6235672740816644251L;
protected final int code;
protected final int action;
public BaseException(String message) {
this(message, null);
}
public BaseException(String message, Throwable throwable) {
this(message, throwable, SERVER_ERROR, ALERT);
}
public BaseException(String message, int code, int action) {
this(message, null, code, action);
}
public BaseException(String message, Throwable cause, int code, int action) {
super(message, cause);
this.code = code;
this.action = action;
}
}
\ No newline at end of file
package com.schbrain.common.exception;
import static com.schbrain.common.constants.ResponseActionConstants.ALERT;
import static com.schbrain.common.constants.ResponseCodeConstants.PARAM_INVALID;
/**
* @author liwu
* @since 2019/3/29
*/
public class ParamInvalidException extends BaseException {
private static final long serialVersionUID = -4015658097738003486L;
public ParamInvalidException(String message) {
super(message, PARAM_INVALID, ALERT);
}
}
\ No newline at end of file
package com.schbrain.common.util;
import com.schbrain.common.exception.ParamInvalidException;
import java.util.*;
public class ValidateUtils {
/**
* Constructor. This class should not normally be instantiated.
*/
private ValidateUtils() {
}
public static void isTrue(boolean expression) {
isTrue(expression, "操作有误");
}
public static void isTrue(boolean expression, String message) {
if (!expression) {
throw new ParamInvalidException(message);
}
}
public static void isFalse(boolean expression) {
isFalse(expression, "操作有误");
}
public static void isFalse(boolean expression, String message) {
if (expression) {
throw new ParamInvalidException(message);
}
}
public static void notNull(Object object) {
notNull(object, "The validated object is null");
}
public static void notNull(Object object, String message) {
if (object == null) {
throw new ParamInvalidException(message);
}
}
public static void isNull(Object object) {
isNull(object, "The validated object is not null");
}
public static void isNull(Object object, String message) {
if (object != null) {
throw new ParamInvalidException(message);
}
}
public static void notEmpty(String value) {
notEmpty(value, "The validated string is empty");
}
public static void notEmpty(String value, String message) {
if (null == value || value.isBlank()) {
throw new ParamInvalidException(message);
}
}
public static void isEmpty(String value) {
isEmpty(value, "The validated string is not empty");
}
public static void isEmpty(String value, String message) {
if (value != null && !value.isEmpty()) {
throw new ParamInvalidException(message);
}
}
public static void notEmpty(Object[] array) {
notEmpty(array, "The validated array is empty");
}
public static void notEmpty(Object[] array, String message) {
if (array == null || array.length == 0) {
throw new ParamInvalidException(message);
}
}
public static void isEmpty(Object[] array) {
isEmpty(array, "The validated array is not empty");
}
public static void isEmpty(Object[] array, String message) {
if (array != null && array.length != 0) {
throw new ParamInvalidException(message);
}
}
public static void notEmpty(Collection<?> collection) {
notEmpty(collection, "The validated collection is empty");
}
public static void notEmpty(Collection<?> collection, String message) {
if (collection == null || collection.isEmpty()) {
throw new ParamInvalidException(message);
}
}
public static void isEmpty(Collection<?> collection) {
isEmpty(collection, "The validated collection is not empty");
}
public static void isEmpty(Collection<?> collection, String message) {
if (collection != null && !collection.isEmpty()) {
throw new ParamInvalidException(message);
}
}
public static void notEmpty(Map<?, ?> map) {
notEmpty(map, "The validated map is empty");
}
public static void notEmpty(Map<?, ?> map, String message) {
if (map == null || map.isEmpty()) {
throw new ParamInvalidException(message);
}
}
public static void isEmpty(Map<?, ?> map) {
isEmpty(map, "The validated map is not empty");
}
public static void isEmpty(Map<?, ?> map, String message) {
if (map != null && !map.isEmpty()) {
throw new ParamInvalidException(message);
}
}
public static void noNullElements(Object[] array) {
notNull(array);
for (int i = 0; i < array.length; i++) {
if (array[i] == null) {
throw new ParamInvalidException("The validated array contains null element at index: " + i);
}
}
}
public static void noNullElements(Object[] array, String message) {
notNull(array);
for (Object item : array) {
if (item == null) {
throw new ParamInvalidException(message);
}
}
}
public static void noNullElements(Collection<?> collection, String message) {
notNull(collection);
for (Object item : collection) {
if (item == null) {
throw new ParamInvalidException(message);
}
}
}
public static void noNullElements(Collection<?> collection) {
notNull(collection);
int i = 0;
for (Iterator<?> it = collection.iterator(); it.hasNext(); i++) {
if (it.next() == null) {
throw new ParamInvalidException("The validated collection contains null element at index: " + i);
}
}
}
public static void allElementsOfType(Collection<?> collection, Class<?> clazz, String message) {
notNull(collection);
notNull(clazz);
for (Object item : collection) {
if (!clazz.isInstance(item)) {
throw new ParamInvalidException(message);
}
}
}
public static void allElementsOfType(Collection<?> collection, Class<?> clazz) {
notNull(collection);
notNull(clazz);
int i = 0;
for (Iterator<?> it = collection.iterator(); it.hasNext(); i++) {
if (!clazz.isInstance(it.next())) {
throw new ParamInvalidException("The validated collection contains an element not of type " + clazz.getName() + " at index: " + i);
}
}
}
}
\ No newline at end of file
<?xml version='1.0' encoding='utf-8'?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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.schbrain.framework</groupId>
<artifactId>commons</artifactId>
<version>${revision}</version>
</parent>
<groupId>com.schbrain.common</groupId>
<artifactId>module-tree</artifactId>
<dependencies>
<dependency>
<groupId>com.schbrain.framework</groupId>
<artifactId>schbrain-base-dao</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.schbrain.common.module.tree;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 结构化的树节点
*
* @author hzchengyi
* @since 2019/1/21
*/
@Data
public class StructureTreeNode<NODE> implements Serializable {
private static final long serialVersionUID = -7732621737666937981L;
private NODE node;
private List<StructureTreeNode<NODE>> children;
}
\ No newline at end of file
package com.schbrain.common.module.tree;
/**
* @author hzchengyi
* @since 2019/1/21
*/
public interface TreeNode {
Long getId();
void setId(Long id);
Long getRelateId();
void setRelateId(Long relateId);
Long getParentId();
void setParentId(Long parentId);
Integer getDepth();
void setDepth(Integer depth);
Integer getLft();
void setLft(Integer lft);
Integer getRgt();
void setRgt(Integer rgt);
Integer getValidate();
void setValidate(Integer validate);
Long getDeleteVersion();
void setDeleteVersion(Long deleteVersion);
default boolean isLeaf() {
return getRgt() == getLft() + 1;
}
}
\ No newline at end of file
package com.schbrain.common.module.tree;
import com.github.pagehelper.Page;
import com.schbrain.common.module.tree.constant.TreeConstant;
import com.schbrain.common.module.tree.dao.TreeNodeDao;
import com.schbrain.common.module.tree.event.TreeOperationAware;
import com.schbrain.common.module.tree.event.TreeOperationEvent;
import com.schbrain.framework.dao.BaseDao;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* 节点树处理器
*
* @author hzchengyi
* @since 2019/1/21
*/
public class TreeNodeProcessor<NODE extends TreeNode> {
private final TreeNodeDao<NODE> treeNodeDao;
private final TreeOperationAware<NODE> operationHandler;
public TreeNodeProcessor(BaseDao<NODE> baseDao, TreeOperationAware<NODE> operationHandler) {
this.treeNodeDao = new TreeNodeDao<>(baseDao);
this.operationHandler = operationHandler;
}
public List<NODE> listByNode(NODE node) {
return treeNodeDao.listByNode(node);
}
public NODE getById(Long nodeId) {
return treeNodeDao.getById(nodeId);
}
public List<NODE> listByIds(List<Long> nodeIds) {
return treeNodeDao.listByIds(nodeIds);
}
public Integer getDepth(Long nodeId) {
NODE node = treeNodeDao.getById(nodeId);
if (node == null) {
return null;
}
return node.getDepth();
}
public Integer countChildren(Long relateId, Long nodeId, TreeQueryOption option) {
NODE node = treeNodeDao.getById(nodeId);
if (node == null) {
return null;
}
return treeNodeDao.countChildren(relateId, node, option);
}
public Map<Long, Integer> countChildren(Long relateId, List<Long> nodeIds, TreeQueryOption option) {
Map<Long, Integer> result = new HashMap<>();
for (Long nodeId : nodeIds) {
result.put(nodeId, countChildren(relateId, nodeId, option));
}
return result;
}
@Transactional
public NODE addNode(Long parentId, Long preBroNodeId, NODE newNode) {
operationHandler.before(TreeOperationEvent.ADD, Collections.singletonList(newNode));
treeNodeDao.addNode(parentId, preBroNodeId, newNode);
operationHandler.after(TreeOperationEvent.ADD, Collections.singletonList(newNode));
return newNode;
}
@Transactional
public NODE createTree(NODE newNode) {
if (treeNodeDao.countByRelateId(newNode.getRelateId()) > 0) {
// relateId已经存在,不能创建新树
return null;
}
return addNode(null, null, newNode);
}
public Boolean isSubNode(NODE parent, NODE child) {
return parent.getRelateId().equals(child.getRelateId())
&& parent.getLft() < child.getLft()
&& parent.getRgt() > child.getRgt();
}
public Boolean isRelateTo(Long relateId, List<NODE> nodes) {
for (NODE node : nodes) {
if (!node.getRelateId().equals(relateId)) {
return false;
}
}
return true;
}
public Boolean areBros(List<NODE> nodes) {
if (nodes == null || nodes.isEmpty()) {
return false;
}
Long parentId = nodes.get(0).getParentId();
for (NODE node : nodes) {
if (!parentId.equals(node.getParentId())) {
return false;
}
}
return true;
}
@Transactional
public Boolean moveBros(List<NODE> nodes, NODE parent) {
operationHandler.before(TreeOperationEvent.MOVE, nodes);
if (null == nodes || nodes.isEmpty() || null == parent) {
return false;
}
if (nodes.get(0).getParentId() <= 0) {
return false;
}
if (!areBros(nodes)) {
return false;
}
Long relateId = parent.getRelateId();
if (null == relateId) {
return false;
}
if (!isRelateTo(relateId, nodes)) {
return false;
}
NODE srcParent = treeNodeDao.getById(nodes.get(0).getParentId());
if (null == srcParent) {
return false;
}
NODE targetParent = parent;
// 同级目录下移动,不做任何操作
if (srcParent.getId().equals(targetParent.getId())) {
return true;
}
List<Long> nodeIdList = new ArrayList<>(nodes.size());
for (NODE node : nodes) {
nodeIdList.add(node.getId());
// 判断新的parent不是node的子节点以及parent不是节点本身
if (isSubNode(node, targetParent) || node.getId().equals(targetParent.getId())) {
return false;
}
}
int totalNodes = 0;
Map<Long, Integer> nodeCountMap = new HashMap<>(nodes.size());
for (Long nodeId : nodeIdList) {
Integer nodeCount = countChildren(relateId, nodeId, TreeQueryOption.instance().queryIncludeSelf());
if (null == nodeCount) {
nodeCountMap.put(nodeId, 0);
} else {
int intValue = nodeCount;
nodeCountMap.put(nodeId, intValue);
totalNodes += intValue;
}
}
if (nodes.size() > 1) {
// 将节点进行排序
nodes.sort(Comparator.comparingInt(TreeNode::getLft));
}
NODE farLeftNode = nodes.get(0);
TreeQueryOption queryOption = TreeQueryOption.instance().queryExcludeSelf().queryDirectChildren();
Integer srcParentChildrenCount = treeNodeDao.countChildren(relateId, srcParent, queryOption);
// 如果不是移动srcParent下面的所有节点
if (srcParentChildrenCount > nodes.size()) {
// 将所有节点按顺序移动到最右边
for (NODE node : nodes) {
treeNodeDao.moveNodeToFarRight(node.getId(), nodeCountMap.get(node.getId()), srcParent);
}
// 这个时候farLeftNode发生了变化,重新获取
farLeftNode = treeNodeDao.getById(nodes.get(0).getId());
// targetParent也可能发生了变化,重新获取
targetParent = treeNodeDao.getById(targetParent.getId());
}
Long tempRelateId = farLeftNode.getId() * -1;
// 将相关节点独立出来
for (NODE node : nodes) {
// 将节点的relateId更新成tempRelateId
TreeQueryOption option = TreeQueryOption.instance().queryIncludeSelf();
treeNodeDao.updateSubTreeBySql("SET relate_id = " + tempRelateId, relateId, node.getId(), option);
}
// 把parentId改成targetParent的id
treeNodeDao.updateParentId(nodeIdList, targetParent.getId());
// 执行移动
move(relateId, tempRelateId, farLeftNode, totalNodes, srcParent, targetParent);
operationHandler.after(TreeOperationEvent.MOVE, nodes);
return true;
}
public Boolean isRoot(NODE node) {
return node.getParentId().equals(TreeConstant.ROOT_PARENT_ID);
}
@Transactional
public Boolean delete(Long relateId, List<Long> nodeIdList) {
List<NODE> nodeList = listByIds(nodeIdList);
if (CollectionUtils.isEmpty(nodeList)) {
return false;
}
if (nodeList.size() != nodeIdList.size()) {
return false;
}
for (NODE node : nodeList) {
if (!node.getRelateId().equals(relateId)) {
return false;
}
}
operationHandler.before(TreeOperationEvent.DELETE, nodeList);
for (NODE node : nodeList) {
delete(relateId, node);
}
operationHandler.after(TreeOperationEvent.DELETE, nodeList);
return true;
}
public List<NODE> listParent(Long relateId, NODE node, TreeQueryOption option) {
return treeNodeDao.listParent(relateId, node, option);
}
public StructureTreeNode<NODE> convert2StructureTree(Long relateId) {
List<NODE> nodes = batchListNode(relateId);
Map<Long, List<NODE>> nodeSubNodeMap = getNodeSubNodeMap(nodes);
NODE root = nodeSubNodeMap.get(TreeConstant.ROOT_PARENT_ID).get(0);
return constructTree(root, nodeSubNodeMap);
}
public Boolean update(Long relateId, NODE node) {
node.setValidate(null);
node.setDeleteVersion(null);
node.setLft(null);
node.setRgt(null);
node.setRelateId(null);
node.setParentId(null);
return treeNodeDao.updateNodeById(node);
}
public Integer getTreeHeight(Long relateId, Long nodeId) {
NODE node = treeNodeDao.getById(nodeId);
if (node == null) {
return 0;
}
NODE maxDepthSubLeaf = treeNodeDao.getMaxDepthSubLeaf(relateId, node);
if (maxDepthSubLeaf == null) {
return 0;
}
return maxDepthSubLeaf.getDepth() - node.getDepth() + 1;
}
public Page<NODE> pageByParent(Long parentId, Integer pageIndex, Integer pageSize) {
return treeNodeDao.pageByParent(parentId, pageIndex, pageSize);
}
public List<NODE> listByParent(Long parentId) {
return treeNodeDao.listByParent(parentId);
}
public List<NODE> listByParent(Long parentId, Integer levelCount) {
return treeNodeDao.listByParent(parentId, levelCount);
}
@Transactional
public void repairLeftAndRight(Long rootId) {
doRepairLeftAndRight(getById(rootId), null, null, null, null);
}
public int updateByIds(NODE updateNode, List<Long> nodeIds) {
// 此接口不允许更新树结构相关的字段
allTreeFiledSetNull(updateNode);
return treeNodeDao.updateNodeByIds(updateNode, nodeIds);
}
public List<NODE> listNode(List<Long> nodeIds) {
return treeNodeDao.listByIds(nodeIds);
}
private void move(Long relateId, Long tempRelateId, NODE farLeftNode, int nodeCount, NODE srcParent, NODE targetParent) {
int minLeft, maxLeft, minRight, maxRight, lrDiff;
NODE farRightOfTargetParent = treeNodeDao.getFarRightNode(targetParent.getRelateId(), targetParent.getId());
if ((farRightOfTargetParent == null && farLeftNode.getLft() < targetParent.getLft()) ||
(farRightOfTargetParent != null && farLeftNode.getLft() < farRightOfTargetParent.getRgt())) {
// 往右边移
minLeft = srcParent.getRgt();
maxLeft = targetParent.getRgt();
if (null != farRightOfTargetParent) {
maxLeft = farRightOfTargetParent.getRgt();
}
minRight = srcParent.getRgt();
maxRight = targetParent.getRgt();
lrDiff = -1 * nodeCount * 2;
} else {
// 往左边移
minLeft = targetParent.getRgt();
if (null != farRightOfTargetParent) {
minLeft = farRightOfTargetParent.getRgt();
}
maxLeft = srcParent.getRgt();
minRight = targetParent.getRgt();
maxRight = srcParent.getRgt();
lrDiff = nodeCount * 2;
}
treeNodeDao.updateLeftWithRang(relateId, lrDiff, minLeft, false, maxLeft, false);
treeNodeDao.updateRightWithRang(relateId, lrDiff, minRight, true, maxRight, false);
// 执行上面两句操作以后,targetParent和farRightOfTargetParent的left和right都发生了变化,所以需要重新获取
if (null == farRightOfTargetParent) {
targetParent = treeNodeDao.getById(targetParent.getId());
lrDiff = targetParent.getLft() - farLeftNode.getLft() + 1;
} else {
farRightOfTargetParent = treeNodeDao.getFarRightNode(targetParent.getRelateId(), targetParent.getId());
lrDiff = farRightOfTargetParent.getRgt() - farLeftNode.getLft() + 1;
}
// 更新要移动节点的left、right、depth,并恢复relateId
int depthDiff = targetParent.getDepth() - srcParent.getDepth();
treeNodeDao.updateLRAndDepthAndRelateIdWithRelateId(tempRelateId, relateId, lrDiff, depthDiff);
}
private void delete(Long relateId, NODE node) {
treeNodeDao.deleteSubTree(relateId, node);
}
private List<NODE> batchListNode(Long relateId) {
int pageIndex = 1;
Integer pageSize = 500;
Page<NODE> page = treeNodeDao.page(relateId, pageIndex, pageSize, "id desc");
List<NODE> result = new LinkedList<>(page);
while (pageIndex < page.getPages()) {
page = treeNodeDao.page(relateId, ++pageIndex, pageSize, "id desc");
result.addAll(page);
}
return result;
}
private Map<Long, List<NODE>> getNodeSubNodeMap(List<NODE> nodes) {
Map<Long, List<NODE>> map = new HashMap<>();
for (NODE node : nodes) {
if (map.containsKey(node.getParentId())) {
map.get(node.getParentId()).add(node);
} else {
List<NODE> nodeLevel = new LinkedList<>();
nodeLevel.add(node);
map.put(node.getParentId(), nodeLevel);
}
}
return map;
}
private StructureTreeNode<NODE> constructTree(NODE node, Map<Long, List<NODE>> nodeSubNodeMap) {
StructureTreeNode<NODE> structureNode = new StructureTreeNode<>();
structureNode.setNode(node);
if (nodeSubNodeMap.containsKey(node.getId())) {
List<NODE> childrenNode = nodeSubNodeMap.get(node.getId());
List<StructureTreeNode<NODE>> children = new ArrayList<>();
for (NODE childNode : childrenNode) {
children.add(constructTree(childNode, nodeSubNodeMap));
}
children.sort(Comparator.comparingInt(e -> e.getNode().getLft()));
structureNode.setChildren(children);
} else {
structureNode.setChildren(new LinkedList<>());
}
return structureNode;
}
private void doRepairLeftAndRight(NODE node, Long parentId, Integer parentLeft, Integer parentDepth, NODE preBro) {
int left, right, depth;
if (null == parentId) {
left = 1;
depth = 1;
} else {
left = parentLeft + 1;
depth = parentDepth + 1;
}
if (null != preBro) {
left = preBro.getRgt() + 1;
}
List<NODE> children = treeNodeDao.listByParent(node.getId());
if (null == children || children.isEmpty()) {
right = left + 1;
} else {
NODE preNode = null;
for (NODE child : children) {
doRepairLeftAndRight(child, node.getId(), left, depth, preNode);
preNode = child;
}
right = preNode.getRgt() + 1;
}
if (left != node.getLft() || right != node.getRgt() || depth != node.getDepth()) {
treeNodeDao.updateLRAndDepth(left, right, depth, node.getId());
node.setLft(left);
node.setRgt(right);
node.setDepth(depth);
}
}
private void allTreeFiledSetNull(NODE node) {
node.setValidate(null);
node.setRelateId(null);
node.setParentId(null);
node.setDepth(null);
node.setDeleteVersion(null);
node.setLft(null);
node.setRgt(null);
}
}
\ No newline at end of file
package com.schbrain.common.module.tree;
/**
* @author hzchengyi
* @since 2019/1/21
*/
public class TreeQueryOption {
/**
* 包括节点自身
*/
public static final int TREE_QUERY_SELF_INCLUDE = 0;
/**
* 不包括节点自身
*/
public static final int TREE_QUERY_SELF_EXCLUDE = 1;
/**
* 只包含直接子节点
*/
public static final int TREE_QUERY_CHILDREN_DIRECT = 0;
/**
* 包含所有子节点
*/
public static final int TREE_QUERY_CHILDREN_ALL = 1;
/**
* 深度排序-从根到叶子节点
*/
public static final int TREE_QUERY_DEPTH_ORDER_ROOT_2_LEAF = 0;
/**
* 深度排序-从叶子节点到根
*/
public static final int TREE_QUERY_DEPTH_ORDER_LEAF_2_ROOT = 1;
private int selfIncludeMode;
private int childrenMode;
private int depthOrder;
private TreeQueryOption() {
}
public static TreeQueryOption instance() {
TreeQueryOption option = new TreeQueryOption();
option.selfIncludeMode = TREE_QUERY_SELF_EXCLUDE;
option.childrenMode = TREE_QUERY_CHILDREN_ALL;
option.depthOrder = TREE_QUERY_DEPTH_ORDER_ROOT_2_LEAF;
return option;
}
public Integer getSelfIncludeMode() {
return this.selfIncludeMode;
}
public Integer getChildrenMode() {
return this.childrenMode;
}
public Integer getDepthOrder() {
return this.depthOrder;
}
public TreeQueryOption queryIncludeSelf() {
this.selfIncludeMode = TREE_QUERY_SELF_INCLUDE;
return this;
}
public TreeQueryOption queryExcludeSelf() {
this.selfIncludeMode = TREE_QUERY_SELF_EXCLUDE;
return this;
}
public TreeQueryOption queryDirectChildren() {
this.childrenMode = TREE_QUERY_CHILDREN_DIRECT;
return this;
}
public TreeQueryOption queryAllChildren() {
this.childrenMode = TREE_QUERY_CHILDREN_ALL;
return this;
}
public TreeQueryOption depthOrderRoot2Leaf() {
this.depthOrder = TREE_QUERY_DEPTH_ORDER_ROOT_2_LEAF;
return this;
}
public TreeQueryOption depthOrderLeaf2Root() {
this.depthOrder = TREE_QUERY_DEPTH_ORDER_LEAF_2_ROOT;
return this;
}
}
\ No newline at end of file
package com.schbrain.common.module.tree.constant;
/**
* @author hzchengyi
* @since 2019/1/21
*/
public class TreeConstant {
public static final Long ROOT_PARENT_ID = -1L;
public static final Long NODE_DELETE_VERSION_DEFAULT = 0L;
}
\ No newline at end of file
package com.schbrain.common.module.tree.dao;
import com.github.pagehelper.Page;
import com.schbrain.common.enums.ValidateEnum;
import com.schbrain.common.module.tree.TreeNode;
import com.schbrain.common.module.tree.TreeQueryOption;
import com.schbrain.common.module.tree.constant.TreeConstant;
import com.schbrain.framework.dao.BaseDao;
import com.schbrain.framework.dao.util.SQLUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
/**
* @author hzchengyi
* @since 2019/1/21
*/
public class TreeNodeDao<NODE extends TreeNode> {
private final BaseDao<NODE> baseDao;
public TreeNodeDao(BaseDao<NODE> baseDao) {
this.baseDao = baseDao;
}
public List<NODE> listByNode(NODE node) {
return baseDao.listByObject(node);
}
public NODE getById(Long id) {
return baseDao.getOneByCondition("id = #{id} AND validate = #{validate}", id, ValidateEnum.VALID.getValue());
}
public List<NODE> listByIds(List<Long> ids) {
return baseDao.listByCondition("validate = #{validate} AND " + SQLUtil.buidInClause("id", Long.class, ids), ValidateEnum.VALID.getValue());
}
public Integer countParent(Long relateId, NODE node, TreeQueryOption option) {
StringBuilder condition = new StringBuilder();
List<Object> params = new LinkedList<>();
params.add(relateId);
condition
.append("relate_id = #{relateId} ")
.append(" AND ").append(getParentLeftRange(node, option, params))
.append(" AND ").append(getParentRightRange(node, option, params))
.append(" AND validate = #{validate}");
params.add(ValidateEnum.VALID.getValue());
return baseDao.getCountByCondition(condition.toString(), params.toArray());
}
public Integer countChildren(Long relateId, NODE node, TreeQueryOption option) {
StringBuilder condition = new StringBuilder();
List<Object> params = new LinkedList<>();
params.add(relateId);
condition
.append("relate_id = #{relateId}")
.append(" AND ").append(getChildrenLeftRange(node, option, params))
.append(" AND ").append(getChildrenRightRange(node, option, params))
.append(" AND validate = #{validate}");
params.add(ValidateEnum.VALID.getValue());
if (option.getChildrenMode().equals(TreeQueryOption.TREE_QUERY_CHILDREN_DIRECT)) {
// 只查询直接子节点
condition.append(" AND parent_id = #{parentId} ");
params.add(node.getId());
}
return baseDao.getCountByCondition(condition.toString(), params.toArray());
}
public Integer countByRelateId(Long relateId) {
return baseDao.getCountByCondition("relate_id = #{relateId} AND validate = #{validate}", relateId, ValidateEnum.VALID.getValue());
}
public boolean addNode(Long parentId, Long preBroNodeId, NODE newNode) {
newNode.setValidate(ValidateEnum.VALID.getValue());
newNode.setDeleteVersion(TreeConstant.NODE_DELETE_VERSION_DEFAULT);
NODE parent = null;
if (null != parentId) {
parent = getById(parentId);
}
NODE preBroNode = null;
if (null != preBroNodeId) {
preBroNode = getById(preBroNodeId);
}
// 如果parent和preBroNode都为null,说明是根节点
if (null == parent && null == preBroNode) {
newNode.setLft(1);
newNode.setRgt(2);
newNode.setDepth(1);
newNode.setParentId(TreeConstant.ROOT_PARENT_ID);
return baseDao.add(newNode);
}
// 如果parent不为null并且preBroNode为null,则代表该节点直接为该父节点下的第一个子节点
if (null == preBroNode) {
// 说明是父节点的第一个子节点
newNode.setLft(parent.getLft() + 1);
newNode.setRgt(newNode.getLft() + 1);
newNode.setDepth(parent.getDepth() + 1);
newNode.setParentId(parent.getId());
// 增加父节点右边所有节点的left和right,留出空位
increaseNodesLeftAndRight(newNode.getRelateId(), parent.getLft(), parent.getLft(), false, 2);
// 添加节点
return baseDao.add(newNode);
}
// 处理非唯一叶子节点的情况
newNode.setLft(preBroNode.getRgt() + 1);
newNode.setRgt(newNode.getLft() + 1);
newNode.setDepth(preBroNode.getDepth());
newNode.setParentId(preBroNode.getParentId());
// 增加兄弟节点右边所有节点的left和right,留出空位
increaseNodesLeftAndRight(preBroNode.getRelateId(), preBroNode.getRgt(), preBroNode.getRgt(), false, 2);
// 添加节点
return baseDao.add(newNode);
}
public int deleteSubTree(Long relateId, NODE node) {
if (null == node) {
return 0;
}
if (!relateId.equals(node.getRelateId())) {
return 0;
}
Long deleteVersion = System.currentTimeMillis();
String updateSql = "UPDATE " + baseDao.getTableName()
+ " SET validate = #{validate} , delete_version = #{deleteVersion} WHERE"
+ " relate_id = #{relateId} AND lft >= #{lft} AND rgt <= #{rgt} AND validate = #{validate}";
int nodeCount = baseDao.updateByCompleteSql(
updateSql, ValidateEnum.INVALID.getValue(), deleteVersion,
node.getRelateId(), node.getLft(), node.getRgt(), ValidateEnum.VALID.getValue());
// 更新右边节点的left和right
return decreaseNodesLeftAndRight(node.getRelateId(), node.getRgt(), node.getRgt(), nodeCount * 2);
}
public List<NODE> listParent(Long relateId, NODE node, TreeQueryOption option) {
StringBuilder condition = new StringBuilder();
List<Object> params = new LinkedList<>();
params.add(relateId);
condition
.append("relate_id = #{relateId}")
.append(" AND ").append(getParentLeftRange(node, option, params))
.append(" AND ").append(getParentRightRange(node, option, params))
.append(" AND validate = #{validate} ");
params.add(ValidateEnum.VALID.getValue());
condition.append(getOrderBy(option));
return baseDao.listByCondition(condition.toString(), params.toArray());
}
public Page<NODE> page(Long relateId, Integer pageIndex, Integer pageSize, String orderCondition) {
String condition = "relate_id = #{relateId} AND validate = #{validate}";
if (StringUtils.isNotBlank(orderCondition)) {
condition += " order by " + orderCondition;
}
return baseDao.pageByCondition(pageIndex, pageSize, condition, relateId, ValidateEnum.VALID.getValue());
}
public boolean updateNodeById(NODE node) {
return baseDao.updateById(node);
}
public NODE getMaxDepthSubLeaf(Long relateId, NODE node) {
String condition = " relate_id = #{relateId} AND lft >= #{lft} AND rgt <= #{rgt} AND validate = #{validate} ORDER BY depth DESC";
return baseDao.getOneByCondition(condition, relateId, node.getLft(), node.getRgt(), ValidateEnum.VALID.getValue());
}
public int updateSubTreeBySql(String updateSql, Long relateId, Long nodeId, TreeQueryOption option) {
NODE node = getById(nodeId);
if (node == null) {
return 0;
}
List<Object> params = new LinkedList<>();
String sql = "UPDATE " + baseDao.getTableName() + " " + updateSql
+ " WHERE " + getChildrenLeftRange(node, option, params)
+ " AND " + getChildrenRightRange(node, option, params)
+ " AND relate_id = #{relateId} AND validate = #{validate}";
params.add(relateId);
params.add(ValidateEnum.VALID.getValue());
return baseDao.updateByCompleteSql(sql, params.toArray());
}
public NODE getFarRightNode(Long relateId, Long parentId) {
String condition = "relate_id = #{relateId} AND parent_id = #{parentId} AND validate = #{validate} ORDER BY lft DESC limit 1";
return baseDao.getOneByCondition(condition, relateId, parentId, ValidateEnum.VALID.getValue());
}
public void moveNodeToFarRight(Long nodeId, Integer nodeCount, NODE parent) {
NODE node = getById(nodeId);
// 1.更新节点及所有子节点的relateId
Long tempRelateId = nodeId * -1;
StringBuilder set = new StringBuilder();
set.append("SET relate_id = ").append(tempRelateId);
updateSubTreeBySql(set.toString(), parent.getRelateId(), nodeId, TreeQueryOption.instance().queryIncludeSelf());
// 2.右边的节点更新left和right
set.delete(0, set.length());
set.append("UPDATE ")
.append(baseDao.getTableName()).append(" ")
.append("SET lft = lft - ").append(nodeCount * 2)
.append(", rgt = rgt - ").append(nodeCount * 2)
.append(" WHERE lft > #{lft} AND rgt < #{rgt} AND relate_id = #{relateId} AND validate = #{validate}");
baseDao.updateByCompleteSql(set.toString(), node.getRgt(), parent.getRgt(), parent.getRelateId(), ValidateEnum.VALID.getValue());
// 3.更新节点及所有子节点的left和right,恢复relateId
set.delete(0, set.length());
NODE farRightNode = getFarRightNode(parent.getRelateId(), parent.getId());
int increment = farRightNode.getRgt() - node.getLft() + 1;
updateLRAndDepthAndRelateIdWithRelateId(tempRelateId, parent.getRelateId(), increment, 0);
}
public int updateLeftWithRang(Long relateId, int diff, Integer minLeft, boolean includeMinLeft, Integer maxLeft, boolean includeMaxLeft) {
String sql = "UPDATE " + baseDao.getTableName() + " " +
"SET lft = lft + " + diff + " WHERE lft " +
(includeMinLeft ? ">=" : ">") + " " + minLeft + " AND lft " +
(includeMaxLeft ? "<=" : "<") + " " + maxLeft +
" AND relate_id = #{relateId} AND validate = #{validate}";
return baseDao.updateByCompleteSql(sql, relateId, ValidateEnum.VALID.getValue());
}
public int updateRightWithRang(Long relateId, int diff, Integer minRight, boolean includeMinRight,
Integer maxRight, boolean includeMaxRight) {
String sql = "UPDATE " + baseDao.getTableName() + " " +
"SET rgt = rgt + " + diff + " WHERE rgt " +
(includeMinRight ? ">=" : ">") + " " + minRight + " AND rgt " +
(includeMaxRight ? "<=" : "<") + " " + maxRight +
" AND relate_id = #{relateId} AND validate = #{validate}";
return baseDao.updateByCompleteSql(sql, relateId, ValidateEnum.VALID.getValue());
}
public int updateLRAndDepthAndRelateIdWithRelateId(Long oldRelateId, Long newRelateId, int lrDiff, int depthDiff) {
String sql = "UPDATE " + baseDao.getTableName() + " "
+ "SET relate_id = #{relateId}" + ", lft = lft + " + lrDiff
+ ", rgt = rgt + " + lrDiff
+ ", depth = depth + " + depthDiff
+ " WHERE relate_id = #{relateId} AND validate = #{validate}";
return baseDao.updateByCompleteSql(sql, newRelateId, oldRelateId, ValidateEnum.VALID.getValue());
}
public Page<NODE> pageByParent(Long parentId, Integer pageIndex, Integer pageSize) {
return baseDao.pageByCondition(pageIndex, pageSize, "parent_id = #{parentId} order by lft", parentId);
}
public int updateParentId(List<Long> nodeIds, Long parentId) {
String sql = "UPDATE " + baseDao.getTableName() + " SET parent_id = #{parentId} WHERE " + SQLUtil.buidInClause("id", Long.class, nodeIds);
return baseDao.updateByCompleteSql(sql, parentId);
}
public List<NODE> listByParent(Long parentId) {
String condition = "parent_id = #{parentId} AND validate = #{validate} order by lft ASC";
return baseDao.listByCondition(condition, parentId, ValidateEnum.VALID.getValue());
}
public List<NODE> listByParent(Long parentId, Integer levelCount) {
NODE parent = getById(parentId);
if (null == parent) {
return Collections.emptyList();
}
int depth = parent.getDepth() + levelCount;
String condition = "relate_id = #{relateId} AND lft > #{lft} AND rgt < #{rgt} AND depth <= #{depth} AND validate = #{validate} order by lft ASC";
return baseDao.listByCondition(condition, parent.getRelateId(), parent.getLft(), parent.getRgt(), depth, ValidateEnum.VALID.getValue());
}
public int updateLRAndDepth(Integer left, Integer right, Integer depth, Long nodeId) {
String sql = "UPDATE " + baseDao.getTableName() + " SET lft = #{lft}, rgt = #{rgt}, depth = #{depth} WHERE id = #{id}";
return baseDao.updateByCompleteSql(sql, left, right, depth, nodeId);
}
public int updateNodeByIds(NODE updateNode, List<Long> nodeIds) {
return baseDao.updateByCondition(updateNode, SQLUtil.buidInClause("id", Long.class, nodeIds));
}
private String getParentLeftRange(NODE node, TreeQueryOption option, List<Object> params) {
params.add(node.getLft());
switch (option.getSelfIncludeMode()) {
case TreeQueryOption.TREE_QUERY_SELF_INCLUDE:
return " lft <= #{lft} ";
case TreeQueryOption.TREE_QUERY_SELF_EXCLUDE:
return " lft < #{lft} ";
default:
// never goes here
throw new IllegalArgumentException("param option invalid.");
}
}
private String getParentRightRange(NODE node, TreeQueryOption option, List<Object> params) {
params.add(node.getRgt());
switch (option.getSelfIncludeMode()) {
case TreeQueryOption.TREE_QUERY_SELF_INCLUDE:
return " rgt >= #{rgt} ";
case TreeQueryOption.TREE_QUERY_SELF_EXCLUDE:
return " rgt > #{rgt} ";
default:
// never goes here
throw new IllegalArgumentException("param option invalid.");
}
}
private String getChildrenLeftRange(NODE node, TreeQueryOption option, List<Object> params) {
StringBuilder condition = new StringBuilder();
params.add(node.getLft());
switch (option.getSelfIncludeMode()) {
case TreeQueryOption.TREE_QUERY_SELF_INCLUDE:
condition.append(" lft >= #{lft} ");
break;
case TreeQueryOption.TREE_QUERY_SELF_EXCLUDE:
condition.append(" lft > #{lft} ");
break;
}
return condition.toString();
}
private String getChildrenRightRange(NODE node, TreeQueryOption option, List<Object> params) {
StringBuilder condition = new StringBuilder();
params.add(node.getRgt());
switch (option.getSelfIncludeMode()) {
case TreeQueryOption.TREE_QUERY_SELF_INCLUDE:
condition.append(" rgt <= #{rgt} ");
break;
case TreeQueryOption.TREE_QUERY_SELF_EXCLUDE:
condition.append(" rgt < #{rgt} ");
break;
}
return condition.toString();
}
/**
* 如果是父节点的left和right,则includeRight为true,因为父节点的right值也需要更新;
* 如果是兄弟节点的left和right,则include为false,因为系统节点的right值不需要更新。
*/
private int increaseNodesLeftAndRight(Long relateId, Integer left, Integer right, boolean includeRight, Integer increment) {
StringBuilder sql = new StringBuilder();
sql.append("UPDATE ")
.append(baseDao.getTableName())
.append(" SET lft = lft + ").append(increment)
.append(" WHERE relate_id = #{relateId} AND lft > #{lft} AND validate = #{validate}");
baseDao.updateByCompleteSql(sql.toString(), relateId, left, ValidateEnum.VALID.getValue());
sql.delete(0, sql.length());
sql.append("UPDATE ").append(baseDao.getTableName())
.append(" SET rgt = rgt + ").append(increment)
.append(" WHERE relate_id = #{relateId} AND rgt ")
.append(includeRight ? ">=" : ">")
.append(" #{rgt} AND validate = #{validate}");
return baseDao.updateByCompleteSql(sql.toString(), relateId, right, ValidateEnum.VALID.getValue());
}
private int decreaseNodesLeftAndRight(Long relateId, Integer left, Integer right, Integer decrement) {
StringBuilder sql = new StringBuilder();
sql.append("UPDATE ")
.append(baseDao.getTableName())
.append(" set lft = lft - ").append(decrement)
.append(" WHERE relate_id = #{relateId} AND lft > #{lft} AND validate = #{validate}");
baseDao.updateByCompleteSql(sql.toString(), relateId, left, ValidateEnum.VALID.getValue());
sql.delete(0, sql.length());
sql.append("UPDATE ").append(baseDao.getTableName())
.append(" set rgt = rgt - ").append(decrement)
.append(" WHERE relate_id = #{relateId} AND rgt > #{rgt} AND validate = #{validate}");
return baseDao.updateByCompleteSql(sql.toString(), relateId, right, ValidateEnum.VALID.getValue());
}
private String getOrderBy(TreeQueryOption option) {
switch (option.getDepthOrder()) {
case TreeQueryOption.TREE_QUERY_DEPTH_ORDER_ROOT_2_LEAF:
return " ORDER BY lft ASC";
case TreeQueryOption.TREE_QUERY_DEPTH_ORDER_LEAF_2_ROOT:
return " ORDER BY lft DESC";
default:
// never goes here
throw new RuntimeException("查询错误");
}
}
}
\ No newline at end of file
package com.schbrain.common.module.tree.event;
import com.schbrain.common.module.tree.TreeNode;
import java.util.List;
/**
* Created by hzchengyi on 2019/1/21.
*/
public class EmptyTreeOperationAware<NODE extends TreeNode> implements TreeOperationAware<NODE> {
@Override
public void before(TreeOperationEvent event, List<NODE> nodes) {
}
@Override
public void after(TreeOperationEvent event, List<NODE> nodes) {
}
}
\ No newline at end of file
package com.schbrain.common.module.tree.event;
import com.schbrain.common.module.tree.TreeNode;
import java.util.List;
/**
* Created by hzchengyi on 2019/1/21.
*/
public interface TreeOperationAware<NODE extends TreeNode> {
/**
* 执行操作之前调用
*/
void before(TreeOperationEvent event, List<NODE> nodes);
/**
* 执行操作之后调用
*/
void after(TreeOperationEvent event, List<NODE> nodes);
}
\ No newline at end of file
package com.schbrain.common.module.tree.event;
/**
* Created by hzchengyi on 2019/1/21.
*/
public enum TreeOperationEvent {
ADD,
DELETE,
MOVE;
}
\ No newline at end of file
<?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.schbrain.framework</groupId>
<artifactId>schbrain-parent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>commons</artifactId>
<packaging>pom</packaging>
<modules>
<module>common</module>
<module>web-common</module>
<module>common-util</module>
<module>module-tree</module>
</modules>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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.schbrain.framework</groupId>
<artifactId>commons</artifactId>
<version>${revision}</version>
</parent>
<groupId>com.schbrain.common</groupId>
<artifactId>web-common</artifactId>
<dependencies>
<dependency>
<groupId>com.schbrain.common</groupId>
<artifactId>common-util</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- Used for com.schbrain.common.web.exception.DefaultGlobalExceptionHandler#handleDataAccessException -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.schbrain.common.web;
import com.schbrain.common.web.argument.BodyParamArgumentResolverWebMvcConfigurer;
import com.schbrain.common.web.exception.*;
import com.schbrain.common.web.log.RequestLoggingFilter;
import com.schbrain.common.web.properties.WebProperties;
import com.schbrain.common.web.result.ResponseBodyHandler;
import com.schbrain.common.web.servlet.*;
import com.schbrain.common.web.support.authentication.AuthenticationInterceptor;
import com.schbrain.common.web.support.authentication.Authenticator;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author liaozan
* @since 2021/11/19
*/
@AutoConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(WebProperties.class)
public class WebCommonAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(Authenticator.class)
public AuthenticationInterceptor defaultAuthenticationInterceptor(Authenticator authenticator) {
return new AuthenticationInterceptor(authenticator);
}
@Bean
@ConditionalOnMissingBean
public GlobalExceptionHandler defaultGlobalExceptionHandler() {
return new DefaultGlobalExceptionHandler();
}
@Bean
@ConditionalOnMissingBean
public ExceptionHandlerWebMcvConfigurer defaultExceptionHandlerWebMcvConfigurer(WebProperties webProperties, GlobalExceptionHandler exceptionHandler) {
return new ExceptionHandlerWebMcvConfigurer(webProperties, exceptionHandler);
}
@Bean
@ConditionalOnMissingBean
public BodyParamArgumentResolverWebMvcConfigurer defaultBodyParamArgumentResolverWebMvcConfigurer() {
return new BodyParamArgumentResolverWebMvcConfigurer();
}
@Bean
@Lazy
@ConditionalOnMissingBean
public RestTemplate restTemplate(ObjectProvider<RestTemplateBuilder> restTemplateBuilder) {
RestTemplateBuilder builder = restTemplateBuilder.getIfAvailable();
if (builder == null) {
return new RestTemplate();
}
return builder.build();
}
@Bean
@ConditionalOnMissingBean
public ResponseBodyHandler defaultResponseBodyHandler(WebProperties properties, BeanFactory beanFactory) {
List<String> basePackages = AutoConfigurationPackages.get(beanFactory);
return new ResponseBodyHandler(properties, basePackages);
}
@Bean
@ConditionalOnMissingBean
public TraceIdInitializeServletListener traceIdInitializeServletListener() {
return new TraceIdInitializeServletListener();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingServletContextInitializer characterEncodingServletContextInitializer(WebProperties webProperties) {
return new CharacterEncodingServletContextInitializer(webProperties.getEncoding());
}
@Bean
@ConditionalOnMissingBean
public RequestLoggingFilter requestLoggingFilter(WebProperties properties) {
return new RequestLoggingFilter(properties);
}
@Bean
@ConditionalOnMissingBean
public AllowAllCorsConfigurer allowAllCorsConfigurer() {
return new AllowAllCorsConfigurer();
}
}
\ No newline at end of file
package com.schbrain.common.web.annotation;
import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ValueConstants;
import java.lang.annotation.*;
/**
* @author liaozan
* @see RequestParam
* @since 2022-12-02
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BodyParam {
/**
* @see RequestParam#value()
*/
@AliasFor("name")
String value() default "";
/**
* @see RequestParam#name()
*/
@AliasFor("value")
String name() default "";
/**
* @see RequestParam#required()
*/
boolean required() default true;
/**
* @see RequestParam#defaultValue()
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
\ No newline at end of file
package com.schbrain.common.web.annotation;
import java.lang.annotation.*;
/**
* @author liaozan
* @see com.schbrain.common.web.result.ResponseDTO
* @since 2022/8/29
*/
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseWrapOption {
/**
* 是否忽略返回值处理
*/
boolean ignore() default true;
/**
* 是否忽略异常处理
*/
boolean ignoreException() default true;
}
\ No newline at end of file
package com.schbrain.common.web.argument;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @author liaozan
* @since 2022-12-02
*/
public class BodyParamArgumentResolverWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new BodyParamMethodArgumentResolver());
}
}
\ No newline at end of file
package com.schbrain.common.web.argument;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.schbrain.common.util.JacksonUtils;
import com.schbrain.common.web.annotation.BodyParam;
import lombok.Setter;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
/**
* @author liaozan
* @since 2022-12-02
*/
@Setter
public class BodyParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
private static final String METHOD_BODY_CACHE_KEY = BodyParamMethodArgumentResolver.class.getName() + ".bodyParamCache";
private ObjectMapper objectMapper;
public BodyParamMethodArgumentResolver() {
this(JacksonUtils.getObjectMapper());
}
public BodyParamMethodArgumentResolver(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(BodyParam.class);
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
BodyParam annotation = parameter.getParameterAnnotation(BodyParam.class);
Assert.notNull(annotation, "annotation should never be null");
return new NamedValueInfo(annotation.name(), annotation.required(), annotation.defaultValue());
}
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
JsonNode paramNode = getParamNode(request);
JsonNode parameterValue = paramNode.get(name);
if (parameterValue == null || parameterValue.isNull()) {
return null;
}
Class<?> parameterType = parameter.getParameterType();
return objectMapper.convertValue(parameterValue, parameterType);
}
private JsonNode getParamNode(NativeWebRequest nativeWebRequest) throws IOException {
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(request != null, "request must not be null");
JsonNode paramNode = (JsonNode) request.getAttribute(METHOD_BODY_CACHE_KEY);
if (paramNode == null) {
InputStream inputStream = StreamUtils.nonClosing(request.getInputStream());
paramNode = objectMapper.readTree(inputStream);
request.setAttribute(METHOD_BODY_CACHE_KEY, paramNode);
}
return paramNode;
}
}
\ No newline at end of file
package com.schbrain.common.web.exception;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import com.schbrain.common.exception.BaseException;
import com.schbrain.common.util.EnvUtils;
import com.schbrain.common.web.result.ResponseDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.ClassUtils;
import org.springframework.validation.*;
import org.springframework.web.*;
import org.springframework.web.bind.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.sql.SQLException;
import java.util.*;
import static com.schbrain.common.constants.ResponseCodeConstants.*;
/**
* @author liaozan
* @since 2019/10/14
*/
@Slf4j
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public class DefaultGlobalExceptionHandler implements GlobalExceptionHandler {
/************************************* Base Exception Handing *************************************/
@ExceptionHandler(BaseException.class)
public ResponseDTO<String> handleBaseException(BaseException ex) {
return loggingThenBuildResponse(ex, ex.getCode());
}
/************************************* Common Exception Handing *************************************/
@ExceptionHandler(Throwable.class)
public ResponseDTO<String> handleAll(Throwable ex) {
return loggingThenBuildResponse(ex, SERVER_ERROR);
}
@ExceptionHandler(NullPointerException.class)
public ResponseDTO<String> handleNullPointerException(NullPointerException ex) {
return loggingThenBuildResponse(ex, SERVER_ERROR);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseDTO<String> handleIllegalArgumentException(IllegalArgumentException ex) {
return loggingThenBuildResponse(ex, SERVER_ERROR);
}
@ExceptionHandler(IllegalStateException.class)
public ResponseDTO<String> handleIllegalStateException(IllegalStateException ex) {
return loggingThenBuildResponse(ex, SERVER_ERROR);
}
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseDTO<String> handleNoHandlerFoundException(NoHandlerFoundException ex) {
return loggingThenBuildResponse(ex, PARAM_INVALID);
}
@ExceptionHandler(AsyncRequestTimeoutException.class)
public ResponseDTO<String> handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex) {
return loggingThenBuildResponse(ex, SERVER_ERROR);
}
/************************************* SQL Exception Handing *************************************/
@ExceptionHandler(SQLException.class)
public ResponseDTO<String> handleSQLException(SQLException ex) {
return loggingThenBuildResponse(ex, SERVER_ERROR);
}
@ExceptionHandler(DataAccessException.class)
public ResponseDTO<String> handleDataAccessException(DataAccessException ex) {
return loggingThenBuildResponse(ex, SERVER_ERROR);
}
/************************************* Http Request Exception Handing *************************************/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseDTO<String> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
String errorMsg = StrUtil.format("不支持该HTTP方法: {}, 请使用 {}", ex.getMethod(), Arrays.toString(ex.getSupportedMethods()));
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public ResponseDTO<String> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) {
String errorMsg = StrUtil.format("不支持该媒体类型: {}, 请使用 {}", ex.getContentType(), ex.getSupportedMediaTypes());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
public ResponseDTO<String> handleHttpMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException ex) {
String errorMsg = StrUtil.format("不支持的媒体类型, 请使用 {}", ex.getSupportedMediaTypes());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
/************************************* Method Parameter Exception Handing *************************************/
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseDTO<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
String errorMsg = StrUtil.format("参数解析失败, {}", ex.getMessage());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseDTO<String> handlerMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
Object value = ex.getValue();
String variableName = ex.getName();
Class<?> requiredTypeClass = ex.getRequiredType();
String requiredType = ClassUtils.getQualifiedName(requiredTypeClass == null ? Object.class : requiredTypeClass);
String providedType = ClassUtils.getDescriptiveType(value);
String errorMsg = StrUtil.format("参数类型不匹配, 参数名: {}, 需要: {}, 传入: {} 的 {}", variableName, requiredType, providedType, value);
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(MissingPathVariableException.class)
public ResponseDTO<String> handleMissingPathVariableException(MissingPathVariableException ex) {
String errorMsg = StrUtil.format("丢失路径参数, 参数名: {}, 参数类型: {}", ex.getVariableName(), ex.getParameter().getParameterType());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(MissingRequestCookieException.class)
public ResponseDTO<String> handleMissingRequestCookieException(MissingRequestCookieException ex) {
String errorMsg = StrUtil.format("丢失Cookie参数, 参数名: {}, 参数类型: {}", ex.getCookieName(), ex.getParameter().getParameterType());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(MissingRequestHeaderException.class)
public ResponseDTO<String> handleMissingRequestHeaderException(MissingRequestHeaderException ex) {
String errorMsg = StrUtil.format("丢失Header参数, 参数名: {}, 参数类型: {}", ex.getHeaderName(), ex.getParameter().getParameterType());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(MissingServletRequestPartException.class)
public ResponseDTO<String> handleMissingServletRequestPartException(MissingServletRequestPartException ex) {
String errorMsg = StrUtil.format("丢失参数: {}", ex.getRequestPartName());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseDTO<String> handleServletRequestParameterException(MissingServletRequestParameterException ex) {
String errorMsg = StrUtil.format("丢失Query参数, 参数名: {}, 参数类型: {}", ex.getParameterName(), ex.getParameterType());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(ServletRequestBindingException.class)
public ResponseDTO<String> handleServletRequestBindingException(ServletRequestBindingException ex) {
String errorMsg = StrUtil.format("参数绑定失败: {}", ex.getMessage());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
/************************************* Parameter Binding Exception Handing *************************************/
@ExceptionHandler(BindException.class)
public ResponseDTO<String> handleBindException(BindException ex) {
String errorMsg = buildBindingErrorMsg(ex.getBindingResult());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseDTO<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
String errorMsg = buildBindingErrorMsg(ex.getBindingResult());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseDTO<String> handleConstraintViolationException(ConstraintViolationException ex) {
String errorMsg = buildBindingErrorMsg(ex.getConstraintViolations());
log.error(errorMsg);
return buildResponse(ex, PARAM_INVALID, errorMsg);
}
protected ResponseDTO<String> loggingThenBuildResponse(Throwable throwable, int errorCode) {
Throwable rootCause = ExceptionUtil.getRootCause(throwable);
logError(rootCause);
return buildResponse(rootCause, errorCode, rootCause.getMessage());
}
protected ResponseDTO<String> buildResponse(Throwable throwable, int errorCode, String message) {
boolean production = EnvUtils.isProduction();
ResponseDTO<String> responseDTO = getExceptionResponseMapping(throwable, production);
if (responseDTO != null) {
return responseDTO;
}
if (production) {
return ResponseDTO.error("系统错误", errorCode);
}
if (StringUtils.isBlank(message)) {
return ResponseDTO.error("系统错误", errorCode);
}
return ResponseDTO.error(message, errorCode);
}
protected ResponseDTO<String> getExceptionResponseMapping(Throwable throwable, boolean production) {
return null;
}
protected String buildBindingErrorMsg(BindingResult bindingResult) {
String prefix = "参数验证失败: ";
StringJoiner joiner = new StringJoiner(", ");
for (ObjectError error : bindingResult.getAllErrors()) {
String errorMessage = Optional.ofNullable(error.getDefaultMessage()).orElse("验证失败");
String source;
if (error instanceof FieldError) {
source = ((FieldError) error).getField();
} else {
source = error.getObjectName();
}
joiner.add(source + " " + errorMessage);
}
return prefix + joiner;
}
protected String buildBindingErrorMsg(Set<ConstraintViolation<?>> constraintViolations) {
String prefix = "参数验证失败: ";
StringJoiner joiner = new StringJoiner(", ");
for (ConstraintViolation<?> violation : constraintViolations) {
PathImpl propertyPath = (PathImpl) violation.getPropertyPath();
joiner.add(propertyPath.asString() + " " + violation.getMessage());
}
return prefix + joiner;
}
protected void logError(Throwable throwable) {
String exMsg = ExceptionUtil.getMessage(throwable);
log.error(exMsg, throwable);
}
}
\ No newline at end of file
package com.schbrain.common.web.exception;
import com.schbrain.common.web.properties.WebProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import java.util.List;
/**
* @author liaozan
* @since 2022/8/29
*/
@Slf4j
public class ExceptionHandlerWebMcvConfigurer implements WebMvcConfigurer {
private final WebProperties webProperties;
private final GlobalExceptionHandler globalExceptionHandler;
public ExceptionHandlerWebMcvConfigurer(WebProperties webProperties, GlobalExceptionHandler globalExceptionHandler) {
this.webProperties = webProperties;
this.globalExceptionHandler = globalExceptionHandler;
}
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
if (!webProperties.isEnableGlobalExceptionHandler()) {
log.warn("Global exception handing is disabled");
return;
}
ExceptionHandlerExceptionResolver adviceExceptionResolver = null;
for (HandlerExceptionResolver resolver : resolvers) {
if (resolver instanceof ExceptionHandlerExceptionResolver) {
adviceExceptionResolver = (ExceptionHandlerExceptionResolver) resolver;
break;
}
}
if (adviceExceptionResolver == null) {
log.warn("ExceptionHandlerExceptionResolver is not exist, ignore global exception handing");
return;
}
addGlobalExceptionResolver(resolvers, adviceExceptionResolver);
}
protected void addGlobalExceptionResolver(List<HandlerExceptionResolver> resolvers, ExceptionHandlerExceptionResolver adviceExceptionResolver) {
int index = resolvers.indexOf(adviceExceptionResolver) + 1;
resolvers.add(index, createGlobalExceptionResolver(adviceExceptionResolver));
}
protected GlobalExceptionResolver createGlobalExceptionResolver(ExceptionHandlerExceptionResolver adviceExceptionResolver) {
return new GlobalExceptionResolver(adviceExceptionResolver, webProperties, globalExceptionHandler);
}
}
\ No newline at end of file
package com.schbrain.common.web.exception;
/**
* 标记接口
*
* @author liaozan
* @since 2022/10/26
*/
public interface GlobalExceptionHandler {
}
\ No newline at end of file
package com.schbrain.common.web.exception;
import com.schbrain.common.web.annotation.ResponseWrapOption;
import com.schbrain.common.web.properties.WebProperties;
import com.schbrain.common.web.utils.HandlerMethodAnnotationUtils;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
import org.springframework.web.method.support.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author liaozan
* @since 2022/8/30
*/
@Slf4j
@Data
@EqualsAndHashCode(callSuper = true)
public class GlobalExceptionResolver extends AbstractHandlerMethodExceptionResolver {
private final WebProperties webProperties;
private final HandlerMethodArgumentResolverComposite argumentResolverComposite;
private final HandlerMethodReturnValueHandlerComposite returnValueHandlerComposite;
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64);
private GlobalExceptionHandler exceptionHandler;
private ExceptionHandlerMethodResolver handlerMethodResolver;
public GlobalExceptionResolver(ExceptionHandlerExceptionResolver handlerMethodResolver, WebProperties webProperties,
GlobalExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
this.webProperties = webProperties;
this.handlerMethodResolver = new ExceptionHandlerMethodResolver(exceptionHandler.getClass());
this.argumentResolverComposite = handlerMethodResolver.getArgumentResolvers();
this.returnValueHandlerComposite = handlerMethodResolver.getReturnValueHandlers();
}
@Override
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
if (!webProperties.isWrapResponse()) {
return false;
}
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
ResponseWrapOption responseWrapOption = HandlerMethodAnnotationUtils.getAnnotation(handlerMethod, ResponseWrapOption.class);
if (responseWrapOption == null) {
return true;
}
return Boolean.FALSE.equals(responseWrapOption.ignoreException());
}
return true;
}
@Override
protected final ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerMethod handlerMethod, Exception exception) {
ServletInvocableHandlerMethod exceptionHandlerMethod = createExceptionHandlerMethod(exception, handlerMethod, exceptionHandler);
if (exceptionHandlerMethod == null) {
return null;
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
return doResolveException(webRequest, exceptionHandlerMethod, getArguments(exception, handlerMethod));
}
protected final ModelAndView doResolveException(ServletWebRequest webRequest, ServletInvocableHandlerMethod targetMethod, Object[] arguments) {
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
targetMethod.invokeAndHandle(webRequest, mavContainer, arguments);
} catch (Exception e) {
log.warn("Failure in @ExceptionHandler " + targetMethod, e);
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
return null;
}
protected ServletInvocableHandlerMethod createExceptionHandlerMethod(Exception exception, @Nullable HandlerMethod handlerMethod, Object handler) {
Method targetMethod = resolveTargetMethod(exception, handlerMethod);
if (targetMethod == null) {
return null;
}
ServletInvocableHandlerMethod exceptionHandlerMethod = new ServletInvocableHandlerMethod(handler, targetMethod);
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(argumentResolverComposite);
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(returnValueHandlerComposite);
return exceptionHandlerMethod;
}
protected Method resolveTargetMethod(Exception exception, @Nullable HandlerMethod handlerMethod) {
Method resolvedMethod = null;
if (handlerMethod != null) {
Class<?> handlerType = handlerMethod.getBeanType();
resolvedMethod = getHandlerMethodResolver(handlerType).resolveMethod(exception);
}
if (resolvedMethod == null) {
resolvedMethod = handlerMethodResolver.resolveMethod(exception);
}
return resolvedMethod;
}
private ExceptionHandlerMethodResolver getHandlerMethodResolver(Class<?> handlerType) {
return exceptionHandlerCache.computeIfAbsent(handlerType, key -> new ExceptionHandlerMethodResolver(handlerType));
}
/**
* copy from spring
*/
private Object[] getArguments(Exception exception, HandlerMethod handlerMethod) {
List<Throwable> exceptions = new ArrayList<>();
Throwable exToExpose = exception;
while (exToExpose != null) {
exceptions.add(exToExpose);
Throwable cause = exToExpose.getCause();
exToExpose = (cause != exToExpose ? cause : null);
}
Object[] arguments = new Object[exceptions.size() + 1];
exceptions.toArray(arguments);
arguments[arguments.length - 1] = handlerMethod;
return arguments;
}
}
\ No newline at end of file
package com.schbrain.common.web.servlet;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.time.Duration;
/**
* @author liaozan
* @since 2022/11/19
*/
public class AllowAllCorsConfigurer implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(false)
.allowedOrigins(CorsConfiguration.ALL)
.allowedHeaders(CorsConfiguration.ALL)
.allowedMethods(CorsConfiguration.ALL)
.exposedHeaders(CorsConfiguration.ALL)
.maxAge(Duration.ofHours(1).toSeconds());
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment