init: 信创文件安全传输系统内外网本地 Docker 部署
包含 s-file 后端、React 前端、MeshSafe 安全检测对接,以及内外网两套 compose/nginx/配置(仅前端 28180/28280 对外发布,其余端口不暴露)。 说明:deploy/config/application-iw.yml 与 application-ow.yml 按维护者 明确要求原样入库,其中含本机 Docker 用的库口令、RabbitMQ 口令与 JWT 密钥;上生产前需替换。
This commit is contained in:
253
backend/src/main/java/com/senmesh/common/AlitaResult.java
Normal file
253
backend/src/main/java/com/senmesh/common/AlitaResult.java
Normal file
@@ -0,0 +1,253 @@
|
||||
package com.senmesh.common;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.senmesh.sdk.common.constant.SdkConstants;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用于 ajax 请求的响应工具类
|
||||
*
|
||||
* @author guhong
|
||||
* @date 2019/8/19
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class AlitaResult<T> implements Serializable {
|
||||
|
||||
/** */
|
||||
private static final long serialVersionUID = 5933105804003541576L;
|
||||
|
||||
/** 未登录 * */
|
||||
public static final String UN_LOGIN_CODE = "401";
|
||||
/** 无权限 * */
|
||||
public static final String NO_AUTHORITY_CODE = "402";
|
||||
/** 无查询信息 * */
|
||||
public static final String NO_RESULT_CODE = "405";
|
||||
/** 登录用户多机构 * */
|
||||
public static final String DUPLICATE_ORG_CODE = "600";
|
||||
/** 登录用户多用户 * */
|
||||
public static final String DUPLICATE_USER_CODE = "601";
|
||||
/** 用户未实名 * */
|
||||
public static final String UN_AUTH_CODE = "602";
|
||||
/** 用户更换token **/
|
||||
public static final String CHANGE_TOKEN_CODE = "603";
|
||||
/** 用户未绑定手机号 **/
|
||||
public static final String UN_BINDING_PHONE = "604";
|
||||
/** 登录用户相关异常 **/
|
||||
public static final String LOGIN_ERROR_CODE = "666";
|
||||
/** 系统异常 * */
|
||||
public static final String SYSTEM_ERROR_CODE = "999";
|
||||
/** 业务异常 **/
|
||||
public static final String BUSINESS_ERROR_CODE = "700";
|
||||
/** 校验异常 * */
|
||||
public static final String VALIDATION_ERROR_CODE = "555";
|
||||
/** 操作成功 * */
|
||||
public static final String SUCCESS_CODE = "200";
|
||||
|
||||
public static final String CHANG_TOKEN_MSG = "token变更";
|
||||
|
||||
/** 链路id **/
|
||||
private String requestId;
|
||||
/** 响应信息 */
|
||||
private String msg;
|
||||
/** 响应code */
|
||||
private String code;
|
||||
/** 是否成功 */
|
||||
private Boolean success;
|
||||
/** 操作成功,响应数据 */
|
||||
private T data;
|
||||
/** 操作失败结果 **/
|
||||
private Object errorData;
|
||||
|
||||
/** 临时json **/
|
||||
@JsonIgnore
|
||||
private String tempJson;
|
||||
|
||||
public AlitaResult(String code, String msg, boolean success, T data) {
|
||||
this.msg = msg;
|
||||
this.code = code;
|
||||
this.success = success;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public AlitaResult(String code, String msg, Serializable errorData) {
|
||||
this.msg = msg;
|
||||
this.code = code;
|
||||
this.success = false;
|
||||
this.errorData = errorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个成功的响应对象
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static <T> AlitaResult<T> success() {
|
||||
return success(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个成功的响应对象
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static <T> AlitaResult<T> success(T data) {
|
||||
return new AlitaResult<T>(SUCCESS_CODE, "请求成功", true, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个成功的响应对象
|
||||
*
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static <T> AlitaResult<T> success(String msg, T data) {
|
||||
return new AlitaResult<T>(SUCCESS_CODE, msg, true, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个失败的响应对象
|
||||
*
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static <T> AlitaResult<T> error(String msg) {
|
||||
return new AlitaResult<T>(SYSTEM_ERROR_CODE, msg, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个失败的响应对象
|
||||
*
|
||||
* @param code
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static <T> AlitaResult<T> error(String code, String msg) {
|
||||
return new AlitaResult<T>(code, msg, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个失败的响应对象
|
||||
*
|
||||
* @param code
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static <T> AlitaResult<T> error(String code, String msg, T data) {
|
||||
return new AlitaResult<T>(code, msg, false, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个失败的响应对象
|
||||
*
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public static <T> AlitaResult<T> error(String msg, T data) {
|
||||
return new AlitaResult<T>(SYSTEM_ERROR_CODE, msg, false, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建业务异常对象
|
||||
*
|
||||
* @param <T>
|
||||
* @param msg
|
||||
* @return
|
||||
* @author sr on 2020-07-02
|
||||
*/
|
||||
public static <T> AlitaResult<T> bizError(String msg) {
|
||||
return new AlitaResult<T>(BUSINESS_ERROR_CODE, msg, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个未登录响应对象
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static <T> AlitaResult<T> unLogin() {
|
||||
return new AlitaResult<T>(UN_LOGIN_CODE, "请先登录", false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建未实名响应对象
|
||||
* @param <T>
|
||||
* @return
|
||||
* @author sr on 2021-06-30
|
||||
*/
|
||||
public static <T> AlitaResult<T> unAuth() {
|
||||
return new AlitaResult<T>(UN_AUTH_CODE, "请先实名", false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建未绑定手机号响应对象
|
||||
* @param <T>
|
||||
* @return
|
||||
* @author sr on 2021-06-30
|
||||
*/
|
||||
public static <T> AlitaResult<T> unBinding() {
|
||||
return new AlitaResult<T>(UN_BINDING_PHONE, "请先绑定手机号", false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个未登录响应对象
|
||||
*
|
||||
* @param loginUrl 登录地址
|
||||
* @return
|
||||
* @author sr on 2019-12-29
|
||||
*/
|
||||
public static AlitaResult<String> unLogin(String loginUrl) {
|
||||
return new AlitaResult<String>(UN_LOGIN_CODE, "请先登录", false, loginUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更换token
|
||||
* @param <T>
|
||||
* @param token
|
||||
* @return
|
||||
* @author sr on 2021-08-02
|
||||
*/
|
||||
public static <T> AlitaResult<T> changeToken(Serializable token) {
|
||||
return new AlitaResult<T>(AlitaResult.CHANGE_TOKEN_CODE, CHANG_TOKEN_MSG, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个未登录响应对象
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2019-12-29
|
||||
*/
|
||||
public static <T> AlitaResult<T> noResult() {
|
||||
return new AlitaResult<T>(NO_RESULT_CODE, "无查询信息", false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口调用失败,未能拿到返回对象
|
||||
*
|
||||
* @param <T>
|
||||
* @param msg
|
||||
* @return
|
||||
* @author sr on 2020-04-10
|
||||
*/
|
||||
public static <T> AlitaResult<T> sdkInvokeNotReachError(String msg) {
|
||||
return new AlitaResult<T>(SdkConstants.SDK_INVOKE_NOT_REACH_CODE, msg, false, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"hiding", "unchecked"})
|
||||
public <T> AlitaResult<T> setRequestId(String requestId){
|
||||
this.requestId = requestId;
|
||||
return (AlitaResult<T>) this;
|
||||
}
|
||||
|
||||
public <T> AlitaResult<T> setTempJson(String tempJson){
|
||||
this.tempJson = tempJson;
|
||||
return (AlitaResult<T>) this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.senmesh.common;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.senmesh.common.cache.DefaultCacheProvider;
|
||||
import com.senmesh.common.cache.ICacheProvider;
|
||||
import com.senmesh.common.log.ILogProvider;
|
||||
import com.senmesh.common.log.provider.Slf4jLogProvider;
|
||||
import com.senmesh.common.log.util.LogUtils;
|
||||
import com.senmesh.common.service.IMockProvider;
|
||||
import com.senmesh.common.service.IMockProvider.DefaultMockProvider;
|
||||
import com.senmesh.common.util.EnvironmentUtils;
|
||||
import com.senmesh.common.util.MockUtils;
|
||||
import com.senmesh.component.file.service.IFileStorage;
|
||||
import com.senmesh.component.file.service.storage.LocalFileStorage;
|
||||
import com.senmesh.component.file.util.FileStorageUtils;
|
||||
|
||||
import lombok.var;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-07-24
|
||||
*/
|
||||
@Configuration
|
||||
public class CommonConfiguration {
|
||||
|
||||
@Bean
|
||||
EnvironmentUtils environmentUtils(Environment environment) {
|
||||
return new EnvironmentUtils(environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ICacheProvider.class)
|
||||
@Order(100)
|
||||
DefaultCacheProvider defauCacheProvider() {
|
||||
return new DefaultCacheProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(100)
|
||||
@ConditionalOnMissingBean(ILogProvider.class)
|
||||
Slf4jLogProvider slf4jLogProvider() {
|
||||
return new Slf4jLogProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(200)
|
||||
LogUtils logUtils(ILogProvider iLogProvider) {
|
||||
LogUtils logUtils = new LogUtils();
|
||||
logUtils.setLogProvider(iLogProvider);
|
||||
return logUtils;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(IFileStorage.class)
|
||||
public LocalFileStorage localFileStorage() {
|
||||
return new LocalFileStorage();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FileStorageUtils fileStorageUtils(IFileStorage fileStorage) {
|
||||
return new FileStorageUtils(fileStorage);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(IMockProvider.class)
|
||||
public IMockProvider defaultMockProvider() {
|
||||
return new DefaultMockProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MockUtils mockUtils(IMockProvider mockProvider) {
|
||||
return new MockUtils(mockProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* requestbody string trim
|
||||
* @return
|
||||
* @author sr on 2024-04-19
|
||||
*/
|
||||
@Bean
|
||||
public SimpleModule stringDeserializerModule() {
|
||||
final var module = new SimpleModule();
|
||||
module.addDeserializer(String.class, new StringDeserializer() {
|
||||
@Override
|
||||
public String deserialize(JsonParser p, DeserializationContext ctx) throws java.io.IOException {
|
||||
final var value = super.deserialize(p, ctx);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final var trimmed = value.trim();
|
||||
if (trimmed.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
}
|
||||
});
|
||||
return module;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.senmesh.common;
|
||||
|
||||
import org.jasypt.encryption.StringEncryptor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.senmesh.common.trace.DefaultTracer;
|
||||
import com.senmesh.common.trace.ITracer;
|
||||
import com.senmesh.common.util.JasyptUtils;
|
||||
|
||||
@Configuration
|
||||
public class CoreConfiguration {
|
||||
|
||||
@Bean
|
||||
JasyptUtils jasyptUtils(StringEncryptor stringEncryptor) {
|
||||
return new JasyptUtils(stringEncryptor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = ITracer.class)
|
||||
ITracer defaultTracer() {
|
||||
return new DefaultTracer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.senmesh.common.actuate.health.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 监控健康信息
|
||||
*
|
||||
* @author sr on 2020-07-08
|
||||
* @param <T>
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class HealthDTO<T> {
|
||||
|
||||
private String status;
|
||||
|
||||
private T details;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.senmesh.common.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface AlitaStopWatch {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.senmesh.common.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.senmesh.common.constant.ProjectProfileEnum;
|
||||
|
||||
/**
|
||||
* 校验并mock
|
||||
*
|
||||
* @author sr on 2020-05-20
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface CheckAndMock {
|
||||
|
||||
/**
|
||||
* 属性前缀
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-05-20
|
||||
*/
|
||||
String propertyPrefix() default "";
|
||||
|
||||
/**
|
||||
* 属性配置类
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-05-20
|
||||
*/
|
||||
Class<ICheckAndMockPropertity> checkAndMockPropertity() default ICheckAndMockPropertity.class;
|
||||
|
||||
/**
|
||||
* mock实现类(需注入spring上下文,提供与原方法方法名/参数/返回对象一致的方法)
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-05-20
|
||||
*/
|
||||
Class<?> mockClass();
|
||||
|
||||
// String mockBeanId();
|
||||
|
||||
/**
|
||||
* 指定环境直接mock
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-05-21
|
||||
*/
|
||||
ProjectProfileEnum[] mockIfInProfile() default {};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.senmesh.common.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 校验定时任务是否开启
|
||||
*
|
||||
* @author sr on 2020-05-19
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface CheckJob {
|
||||
|
||||
/**
|
||||
* 配置文件是否开启的key,明确指定为false时才关闭
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-05-19
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.senmesh.common.annotation;
|
||||
|
||||
/**
|
||||
* 需要校验后mock的配置
|
||||
*
|
||||
* @author sr on 2020-05-20
|
||||
*/
|
||||
public interface ICheckAndMockPropertity {
|
||||
|
||||
/**
|
||||
* 是否开启
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-05-21
|
||||
*/
|
||||
Boolean getEnable();
|
||||
|
||||
/**
|
||||
* 次数限制
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-05-21
|
||||
*/
|
||||
Long getNumLimit();
|
||||
|
||||
/**
|
||||
* 属性前缀
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-05-21
|
||||
*/
|
||||
String getPropertityPrefix();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.senmesh.common.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
|
||||
/**
|
||||
* 掩码
|
||||
* @author sr on 2021-11-25
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@JacksonAnnotationsInside
|
||||
public @interface Mask {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.senmesh.common.annotation;
|
||||
|
||||
import com.fasterxml.jackson.databind.introspect.Annotated;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
|
||||
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
|
||||
|
||||
/**
|
||||
* @author sr on 2021-11-25
|
||||
*/
|
||||
public class MaskAnontationIntrospector extends JacksonAnnotationIntrospector {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Object findSerializer(Annotated a) {
|
||||
Mask mask = a.getAnnotation(Mask.class);
|
||||
if (mask != null) {
|
||||
return MaskSerializer.class;
|
||||
}
|
||||
return super.findSerializer(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIgnoreMarker(AnnotatedMember m) {
|
||||
// 直接不打印该字段,则置为true
|
||||
/*if (m.hasAnnotation(Mask.class)) {
|
||||
return true;
|
||||
}*/
|
||||
return super.hasIgnoreMarker(m);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.senmesh.common.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
/**
|
||||
* @author sr on 2021-11-25
|
||||
*/
|
||||
public class MaskSerializer extends StdSerializer<Object> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String mask = "***";
|
||||
|
||||
public MaskSerializer() {
|
||||
super(Object.class);
|
||||
}
|
||||
|
||||
public MaskSerializer(String mask, int count) {
|
||||
this();
|
||||
}
|
||||
|
||||
public MaskSerializer(MaskSerializer src) {
|
||||
super(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Object s, JsonGenerator jsonGenerator,
|
||||
SerializerProvider serializerProvider) throws IOException {
|
||||
jsonGenerator.writeString(mask);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.senmesh.common.annotation.aspect;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
import com.senmesh.common.annotation.AlitaStopWatch;
|
||||
import com.senmesh.common.exception.util.DevopsUtils;
|
||||
import com.senmesh.common.util.EnvironmentUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
public class AlitaStopWatchAspect {
|
||||
|
||||
public static final String DEVOPS_TIME_KEY = "devops.usetime";
|
||||
/** 默认十分钟 **/
|
||||
public static final Long DEFAULT_DEVOPS_TIME = 10 * 60L;
|
||||
|
||||
|
||||
@Around("@annotation(alitaStopWatch)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, AlitaStopWatch alitaStopWatch) throws Throwable {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
try {
|
||||
return joinPoint.proceed();
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
String useTimeStr = EnvironmentUtils.getProperty(DEVOPS_TIME_KEY);
|
||||
Long maxUseTime = DEFAULT_DEVOPS_TIME;
|
||||
try {
|
||||
maxUseTime = Long.parseLong(useTimeStr);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
maxUseTime = 1000 * maxUseTime;
|
||||
Long actualTime = stopWatch.getTotalTimeMillis();
|
||||
String logMsg = joinPoint.getSignature().toShortString() + ",use(ms): " + actualTime;
|
||||
if (actualTime > maxUseTime) {
|
||||
DevopsUtils.deal(logMsg);
|
||||
}
|
||||
log.info(logMsg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.senmesh.common.annotation.aspect;
|
||||
|
||||
import com.senmesh.common.annotation.CheckAndMock;
|
||||
import com.senmesh.common.annotation.ICheckAndMockPropertity;
|
||||
import com.senmesh.common.cache.ICacheProvider;
|
||||
import com.senmesh.common.constant.CommonConstants;
|
||||
import com.senmesh.common.constant.ProjectProfileEnum;
|
||||
import com.senmesh.common.util.SpringContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 校验并mock
|
||||
*
|
||||
* @author sr on 2020-05-19
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
@Order(200)
|
||||
public class CheckAndMockAspect {
|
||||
|
||||
/** 是否开启属性 **/
|
||||
public static final String ENABLE_KEY = "enable";
|
||||
/** 次数限制属性 **/
|
||||
public static final String NUM_LIMIT_KEY = "numLimit";
|
||||
/** 默认次数 **/
|
||||
public static final Long DEFALUT_NUM_LIMIT = 3L;
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Autowired
|
||||
private ICacheProvider iCacheProvider;
|
||||
|
||||
@Around("@annotation(checkAndMock)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, CheckAndMock checkAndMock) throws Throwable {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
ProjectProfileEnum[] profileEnums = checkAndMock.mockIfInProfile();
|
||||
Class<?> mockClass = checkAndMock.mockClass();
|
||||
String currentProfile = environment.getProperty(CommonConstants.PROFILE_KEY);
|
||||
|
||||
Object[] args = joinPoint.getArgs();
|
||||
// 只要配置环境mock,就不校验开关和次数限制
|
||||
if (profileEnums.length > 0) {
|
||||
for (ProjectProfileEnum profileEnum : profileEnums) {
|
||||
if (profileEnum.getValue().equals(currentProfile)) {
|
||||
return mock(mockClass, method, args);
|
||||
}
|
||||
}
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
// 获取属性配置
|
||||
String propertyPrefix = checkAndMock.propertyPrefix();
|
||||
boolean enable = true;
|
||||
Long numLimit = null;
|
||||
if (StringUtils.isNotBlank(propertyPrefix)) {
|
||||
enable = BooleanUtils.toBoolean(
|
||||
environment.getProperty(String.join(CommonConstants.POINT, propertyPrefix, ENABLE_KEY)));
|
||||
try {
|
||||
numLimit = enable ? Long.parseLong(environment
|
||||
.getProperty(String.join(CommonConstants.POINT, propertyPrefix, NUM_LIMIT_KEY))) : 0L;
|
||||
} catch (NumberFormatException e) {
|
||||
log.info("{} numLimit not right, use default numLimit", propertyPrefix);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
ICheckAndMockPropertity properties =
|
||||
SpringContextHolder.getBean(checkAndMock.checkAndMockPropertity());
|
||||
enable = properties.getEnable();
|
||||
numLimit = properties.getNumLimit();
|
||||
propertyPrefix = properties.getPropertityPrefix();
|
||||
} catch (Exception e) {
|
||||
log.info("ICheckAndMockPropertity not found...");
|
||||
enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 开关关闭,都走mock
|
||||
if (!enable) {
|
||||
return mock(mockClass, method, args);
|
||||
}
|
||||
// 开关开启,生产无次数限制,本地测试根据配置/默认次数控制
|
||||
boolean isProd = ProjectProfileEnum.PROD.getValue().equals(currentProfile);
|
||||
if (isProd) {
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
numLimit = numLimit == null ? DEFALUT_NUM_LIMIT : numLimit;
|
||||
String key = String.join(CommonConstants.POINT, propertyPrefix, NUM_LIMIT_KEY);
|
||||
if (iCacheProvider.outLimit(key, numLimit)) {
|
||||
// 超限,mock
|
||||
return mock(mockClass, method, args);
|
||||
}
|
||||
// 次数限制
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mock调用
|
||||
*
|
||||
* @param clazz
|
||||
* @param method
|
||||
* @param objects
|
||||
* @return
|
||||
* @throws Throwable
|
||||
* @author sr on 2020-05-21
|
||||
*/
|
||||
private Object mock(Class<?> clazz, Method method, Object[] objects) throws Throwable {
|
||||
Object mockBean = SpringContextHolder.getBean(clazz);
|
||||
Method mockMethod = mockBean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||
return mockMethod.invoke(mockBean, objects);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.senmesh.common.annotation.aspect;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.senmesh.common.annotation.CheckJob;
|
||||
import com.senmesh.common.constant.CommonConstants;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 校验定时任务是否开启
|
||||
*
|
||||
* @author sr on 2020-05-19
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CheckJobAspect {
|
||||
|
||||
/** 配置无效识别符(只有配置false才失效) **/
|
||||
private String falseValue = "false";
|
||||
|
||||
/** 定时任务配置前缀 **/
|
||||
public static final String JOB_PROPERTY_PREFIX = "job";
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Around("@annotation(checkJob)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, CheckJob checkJob) throws Throwable {
|
||||
String propertityKey = checkJob.value();
|
||||
boolean disable = falseValue.equals(environment
|
||||
.getProperty(String.join(CommonConstants.POINT, JOB_PROPERTY_PREFIX, propertityKey)));
|
||||
if (disable) {
|
||||
log.info("job:{} not enable...", propertityKey);
|
||||
return null;
|
||||
}
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.senmesh.common.annotation.trimAndUpper;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 用于字符串去空格以及大写
|
||||
*
|
||||
* @author Carlye
|
||||
* @date 2022/02/16
|
||||
*/
|
||||
@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface TrimAndUpper {
|
||||
|
||||
/**
|
||||
* 执行类型
|
||||
* ALL: (所有类型都包含)
|
||||
* TRIM: (去两边的空格)
|
||||
* REPLACE_ALL: (去所有的空格)
|
||||
* UPPER_CASE: (转大写)
|
||||
*
|
||||
* @author Carlye
|
||||
* @date 2022/02/16
|
||||
*/
|
||||
public static enum ExecuteType {
|
||||
ALL, TRIM, REPLACE_ALL, UPPER_CASE;
|
||||
}
|
||||
|
||||
ExecuteType value() default ExecuteType.ALL;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.senmesh.common.annotation.trimAndUpper;
|
||||
|
||||
import com.fasterxml.jackson.databind.AnnotationIntrospector;
|
||||
import com.fasterxml.jackson.databind.introspect.Annotated;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
|
||||
|
||||
/**
|
||||
* @author sr on 2021-11-25
|
||||
*/
|
||||
public class TrimAndUpperAnontationIntrospector extends AnnotationIntrospectorPair {
|
||||
|
||||
|
||||
private static final long serialVersionUID = -1755768596246088917L;
|
||||
|
||||
public TrimAndUpperAnontationIntrospector(AnnotationIntrospector p,
|
||||
AnnotationIntrospector s) {
|
||||
super(p, s);
|
||||
}
|
||||
@Override
|
||||
public Object findDeserializer(Annotated a) {
|
||||
TrimAndUpper trim = a.getAnnotation(TrimAndUpper.class);
|
||||
if (trim != null) {
|
||||
return TrimAndUpperDeserializer.class;
|
||||
}
|
||||
return super.findDeserializer(a);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.senmesh.common.annotation.trimAndUpper;
|
||||
|
||||
import com.fasterxml.jackson.databind.AnnotationIntrospector;
|
||||
import com.fasterxml.jackson.databind.DeserializationConfig;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.cfg.BaseSettings;
|
||||
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
|
||||
import java.lang.reflect.Field;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
@Configuration
|
||||
public class TrimAndUpperConfig {
|
||||
|
||||
@Autowired
|
||||
public void addFormatters(FormatterRegistry registry) {
|
||||
TrimAndUpperFormatterFactory trimAndUpperCaseFormatterFactory = new TrimAndUpperFormatterFactory();
|
||||
registry.addFormatterForFieldAnnotation(trimAndUpperCaseFormatterFactory);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void init(RequestMappingHandlerAdapter handlerAdapter){
|
||||
for (HttpMessageConverter<?> messageConverter : handlerAdapter.getMessageConverters()) {
|
||||
try {
|
||||
if (messageConverter instanceof MappingJackson2HttpMessageConverter){
|
||||
MappingJackson2HttpMessageConverter converter1 = (MappingJackson2HttpMessageConverter)messageConverter;
|
||||
ObjectMapper objectMapper = converter1.getObjectMapper();
|
||||
DeserializationConfig deserializationConfig = objectMapper.getDeserializationConfig();
|
||||
|
||||
Field _base = ReflectionUtils.findField(DeserializationConfig.class, "_base");
|
||||
ReflectionUtils.makeAccessible(_base);
|
||||
BaseSettings baseSettings = (BaseSettings)ReflectionUtils.getField(_base, deserializationConfig);
|
||||
|
||||
AnnotationIntrospector annotationIntrospector = baseSettings.getAnnotationIntrospector();
|
||||
if (annotationIntrospector instanceof AnnotationIntrospectorPair){
|
||||
AnnotationIntrospectorPair annotationIntrospectorOld = (AnnotationIntrospectorPair)baseSettings.getAnnotationIntrospector();
|
||||
|
||||
Field _primary = ReflectionUtils.findField(AnnotationIntrospectorPair.class, "_primary");
|
||||
ReflectionUtils.makeAccessible(_primary);
|
||||
AnnotationIntrospector _primaryObj = (AnnotationIntrospector)ReflectionUtils.getField(_primary, annotationIntrospectorOld);
|
||||
|
||||
Field _secondary = ReflectionUtils.findField(AnnotationIntrospectorPair.class, "_secondary");
|
||||
ReflectionUtils.makeAccessible(_secondary);
|
||||
AnnotationIntrospector _secondaryObj = (AnnotationIntrospector)ReflectionUtils.getField(_secondary, annotationIntrospectorOld);
|
||||
|
||||
converter1.getObjectMapper().setAnnotationIntrospector(new TrimAndUpperAnontationIntrospector(_primaryObj,_secondaryObj));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//doNothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.senmesh.common.annotation.trimAndUpper;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.senmesh.common.annotation.trimAndUpper.TrimAndUpper.ExecuteType;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class TrimAndUpperDeserializer extends StdDeserializer<String> implements ContextualDeserializer {
|
||||
|
||||
private static final Map<ExecuteType, TrimAndUpperDeserializer> INSTANCE_MAP = Maps.newHashMap();
|
||||
private static final long serialVersionUID = 3463447204376923373L;
|
||||
|
||||
static {
|
||||
for (ExecuteType value : ExecuteType.values()) {
|
||||
INSTANCE_MAP.put(value,new TrimAndUpperDeserializer(value));
|
||||
}
|
||||
}
|
||||
|
||||
private ExecuteType executeType;
|
||||
|
||||
public TrimAndUpperDeserializer(){
|
||||
super(String.class);
|
||||
}
|
||||
|
||||
public TrimAndUpperDeserializer(ExecuteType executeType){
|
||||
super(String.class);
|
||||
this.executeType = executeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
|
||||
String s = jsonParser.getText();
|
||||
if (!(StringUtils.isEmpty(s) || Objects.isNull(executeType))) {
|
||||
if (ExecuteType.ALL == executeType || ExecuteType.TRIM == executeType) {
|
||||
s = StringUtils.replace(StringUtils.trim(s)," ","");
|
||||
}
|
||||
if (ExecuteType.ALL == executeType || ExecuteType.TRIM == executeType) {
|
||||
s = StringUtils.upperCase(s);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonDeserializer<?> createContextual(DeserializationContext prov, BeanProperty property) throws JsonMappingException {
|
||||
ExecuteType executeType = null;
|
||||
TrimAndUpper ann = null;
|
||||
if (property != null) {
|
||||
ann = property.getAnnotation(TrimAndUpper.class);
|
||||
}
|
||||
if (ann != null) {
|
||||
executeType = ann.value();
|
||||
}
|
||||
return INSTANCE_MAP.get(executeType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.senmesh.common.annotation.trimAndUpper;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.senmesh.common.annotation.trimAndUpper.TrimAndUpper.ExecuteType;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.format.AnnotationFormatterFactory;
|
||||
import org.springframework.format.Formatter;
|
||||
import org.springframework.format.Parser;
|
||||
import org.springframework.format.Printer;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
public class TrimAndUpperFormatterFactory implements
|
||||
AnnotationFormatterFactory<TrimAndUpper> {
|
||||
|
||||
private static final Map<ExecuteType, TrimAndUpperCaseFormatter> TRIMMER_FORMATTER_MAP;
|
||||
|
||||
static {
|
||||
ExecuteType[] values = ExecuteType.values();
|
||||
Map<ExecuteType, TrimAndUpperCaseFormatter> map = Maps.newHashMap();
|
||||
for (ExecuteType type : values) {
|
||||
map.put(type, new TrimAndUpperCaseFormatter(type));
|
||||
}
|
||||
TRIMMER_FORMATTER_MAP = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> getFieldTypes() {
|
||||
return Sets.newHashSet(String.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Printer<?> getPrinter(TrimAndUpper trimAndUpper, Class<?> aClass) {
|
||||
return TRIMMER_FORMATTER_MAP.get(trimAndUpper.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parser<?> getParser(TrimAndUpper trimAndUpper, Class<?> aClass) {
|
||||
return TRIMMER_FORMATTER_MAP.get(trimAndUpper.value());
|
||||
}
|
||||
|
||||
private static class TrimAndUpperCaseFormatter implements Formatter<String> {
|
||||
|
||||
private TrimAndUpper.ExecuteType executeType;
|
||||
|
||||
public TrimAndUpperCaseFormatter(TrimAndUpper.ExecuteType executeType) {
|
||||
this.executeType = executeType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(String value, Locale locale) throws ParseException {
|
||||
if (StringUtils.isEmpty(value) || Objects.isNull(executeType)) {
|
||||
return value;
|
||||
}
|
||||
// 去所有空格
|
||||
if (ExecuteType.ALL == executeType || ExecuteType.REPLACE_ALL == executeType) {
|
||||
value = StringUtils.replace(value, " ", "");
|
||||
}
|
||||
// 转大写
|
||||
if (ExecuteType.ALL == executeType || ExecuteType.UPPER_CASE == executeType) {
|
||||
value = StringUtils.upperCase(value);
|
||||
}
|
||||
//去两边空格
|
||||
if (ExecuteType.ALL == executeType || ExecuteType.TRIM == executeType) {
|
||||
value = StringUtils.trim(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String print(String value, Locale locale) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
backend/src/main/java/com/senmesh/common/cache/DefaultCacheProvider.java
vendored
Normal file
31
backend/src/main/java/com/senmesh/common/cache/DefaultCacheProvider.java
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.senmesh.common.cache;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-07-07
|
||||
*/
|
||||
public class DefaultCacheProvider implements ICacheProvider {
|
||||
|
||||
private Map<String, Long> numLimitMap = Maps.newConcurrentMap();
|
||||
|
||||
@Override
|
||||
public boolean outLimit(final String key, Long maxNumLimit) {
|
||||
Long currentNumLimit = null;
|
||||
final Object lockKey = key.intern();
|
||||
synchronized (lockKey) {
|
||||
currentNumLimit = MapUtils.getLong(numLimitMap, key, 0L);
|
||||
if (currentNumLimit >= maxNumLimit) {
|
||||
return true;
|
||||
}
|
||||
currentNumLimit++;
|
||||
numLimitMap.put(key, currentNumLimit);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
18
backend/src/main/java/com/senmesh/common/cache/ICacheProvider.java
vendored
Normal file
18
backend/src/main/java/com/senmesh/common/cache/ICacheProvider.java
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.senmesh.common.cache;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-07-07
|
||||
*/
|
||||
public interface ICacheProvider {
|
||||
|
||||
/**
|
||||
* 是否超限
|
||||
*
|
||||
* @param key
|
||||
* @param maxNumLimit
|
||||
* @return
|
||||
* @author sr on 2020-07-07
|
||||
*/
|
||||
boolean outLimit(String key, Long maxNumLimit);
|
||||
|
||||
}
|
||||
32
backend/src/main/java/com/senmesh/common/cache/RedisCacheProvier.java
vendored
Normal file
32
backend/src/main/java/com/senmesh/common/cache/RedisCacheProvier.java
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.senmesh.common.cache;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-07-07
|
||||
*/
|
||||
@Service
|
||||
@ConditionalOnClass(RedisTemplate.class)
|
||||
@Order(1)
|
||||
public class RedisCacheProvier implements ICacheProvider{
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Autowired(required = false)
|
||||
private RedisTemplate redisTemplate;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean outLimit(String key, Long maxNumLimit) {
|
||||
Long currentNumLimit = redisTemplate.opsForValue().increment(key);
|
||||
if (currentNumLimit > maxNumLimit) {
|
||||
redisTemplate.opsForValue().decrement(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
/**
|
||||
* 系统枚举
|
||||
*
|
||||
* @author sr on 2020-06-02
|
||||
*/
|
||||
public enum AuthStatusEnum {
|
||||
|
||||
/**
|
||||
* 待认证
|
||||
**/
|
||||
NOT_AUTH(1, "待认证"),
|
||||
/**
|
||||
* 认证中
|
||||
**/
|
||||
AUTHING(2, "认证中"),
|
||||
/**
|
||||
* 认证成功
|
||||
**/
|
||||
AUTH_SUCCESS(3, "认证成功"),
|
||||
/**
|
||||
* 认证失败
|
||||
**/
|
||||
AUTH_FAIL(4, "认证失败");
|
||||
|
||||
/**
|
||||
* 状态类型
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 状态名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
AuthStatusEnum(Integer type, String name) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
/**
|
||||
* 缓存常量
|
||||
*
|
||||
* @author sr on 2020-04-03
|
||||
*/
|
||||
public interface CacheConstants {
|
||||
|
||||
String CACHE_NAME_DICT = "dict";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
/**
|
||||
* 公共常量
|
||||
* @author sr on 2019-12-03
|
||||
*/
|
||||
public interface CommonConstants {
|
||||
|
||||
/** _(下划线) **/
|
||||
String UNDERLINE = "_";
|
||||
/** # **/
|
||||
String POUND = "#";
|
||||
/** & **/
|
||||
String AND = "&";
|
||||
/** ,(逗号) **/
|
||||
String COMMA = ",";
|
||||
/** :(冒号) **/
|
||||
String COLON = ":";
|
||||
/** ;(分号) **/
|
||||
String SEMICOLON = ";";
|
||||
/** . **/
|
||||
String POINT = ".";
|
||||
/** = **/
|
||||
String EQUAL = "=";
|
||||
/** ? **/
|
||||
String QUESTION = "?";
|
||||
/** -(连字号) **/
|
||||
String HYPHEN = "-";
|
||||
/** ...(省略号) **/
|
||||
String ELLIPSIS = "...";
|
||||
|
||||
/** 金额精度:两位 **/
|
||||
int AMOUNT_SCALE = 2;
|
||||
|
||||
/** 金额小数位 **/
|
||||
int AMOUNT_FRACTION = 2;
|
||||
/** 金额整数位 **/
|
||||
int AMOUNT_INTEGER = 14;
|
||||
|
||||
|
||||
/** 项目环境配置key **/
|
||||
String PROFILE_KEY = "spring.profiles.active";
|
||||
/** 项目名称 **/
|
||||
String APPLICATION_NAME_KEY = "spring.application.name";
|
||||
String SYSTEM_CODE_KEY = "system.code";
|
||||
/** security是否使用本地方法 **/
|
||||
String USELOCAL_KEY = "security.useLocal";
|
||||
|
||||
/**
|
||||
* 表字段去空格
|
||||
*/
|
||||
String TRIM_TABLE_FIELDS_ENBALE_KEY = "trim.table.fields";
|
||||
|
||||
/** 开关键 **/
|
||||
String ENABLE_KEY = "enable";
|
||||
|
||||
String FALSE_STR = "false";
|
||||
|
||||
/** 配置值:是 **/
|
||||
String CONFIG_TRUE = "1";
|
||||
/** 配置值:否 **/
|
||||
String CONFIG_FALSE = "0";
|
||||
|
||||
/** 平台机构id **/
|
||||
Long PLATFORM_ORG_ID = 1L;
|
||||
|
||||
/** 用户禁用/注销登录态黑名单key前缀(存Redis,值存在期间该用户已发放的token视为无效) **/
|
||||
String USER_DISABLED_KEY = "user:disabled:";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
public interface CommonMqConstants {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
/**
|
||||
* @author Jerry.li
|
||||
* @description Cookie相关常量
|
||||
* @date 2020/5/11
|
||||
*/
|
||||
public class CookieConstants {
|
||||
|
||||
/**
|
||||
* 登录Cookie名称前缀
|
||||
*/
|
||||
public static final String LOGIN_COOKIE_NAME_PREFIX = "xs_ck_";
|
||||
|
||||
/** 属性配置键:cookie域名 **/
|
||||
public static final String PROP_KEY_COOKIE_DOMAIN = "cookie.domain";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
/**
|
||||
* 异常错误信息
|
||||
*
|
||||
* @author on 2019-11-05
|
||||
*/
|
||||
public interface ExceptionMsgs {
|
||||
|
||||
String ENTITY_HAS_EXISTS = "数据已存在";
|
||||
|
||||
String ENTITY_NONE_EXISTS = "数据不存在";
|
||||
|
||||
String ENTITY_MULTIPLE_EXISTS = "数据存在多条";
|
||||
|
||||
String UPDATE_DATA_NOT_EXIST = "操作失败,请检查数据";
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
public interface ExceptionRecordConstants {
|
||||
|
||||
/**
|
||||
* 接口记录系统类型
|
||||
*/
|
||||
String SOURCE_HYG = "1";
|
||||
String SOURCE_XINPAI = "2";
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
/**
|
||||
* 性别枚举
|
||||
*
|
||||
*/
|
||||
public enum GenderEnum {
|
||||
|
||||
MALE(1, "男"),
|
||||
FEMALE(2, "女"),
|
||||
OTHER(3, "其它");
|
||||
|
||||
private final Integer type;
|
||||
|
||||
private final String desc;
|
||||
|
||||
GenderEnum(Integer type, String desc) {
|
||||
this.type = type;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public Integer getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public static Integer formatFemale(Integer value){
|
||||
// 通过sql 身份证号处理性别条件:0女,1男
|
||||
if(GenderEnum.FEMALE.getType().equals(value)){
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String getVal(Integer val){
|
||||
if(GenderEnum.MALE.getType().equals(val)){
|
||||
return GenderEnum.MALE.desc;
|
||||
} else if(GenderEnum.FEMALE.getType().equals(val)){
|
||||
return GenderEnum.FEMALE.desc;
|
||||
}
|
||||
return GenderEnum.OTHER.desc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
/**
|
||||
* 项目环境枚举
|
||||
* @author sr on 2020-05-20
|
||||
*/
|
||||
public enum ProjectProfileEnum {
|
||||
|
||||
/** 开发 **/
|
||||
DEV("dev"),
|
||||
/** 测试 **/
|
||||
TEST("test"),
|
||||
/** 测试 * */
|
||||
UAT("uat"),
|
||||
/** 生产 **/
|
||||
PROD("prod"),
|
||||
/** 福安 **/
|
||||
FUAN("fuan");
|
||||
|
||||
private String value;
|
||||
|
||||
ProjectProfileEnum(String value){
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.senmesh.common.constant;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 交易账户类型常量
|
||||
* @author sr on 2020-08-19
|
||||
*/
|
||||
public interface TradeAccountTypeConstants {
|
||||
|
||||
/** 交易账号类型:银行卡 **/
|
||||
String TRADE_ACCOUNT_TYPE_BANK = TradeAccountTypeEnum.BANK.getType().toString();
|
||||
/** 交易账号类型:子账号 **/
|
||||
String TRADE_ACCOUNT_TYPE_SUB_ACCOUNT = TradeAccountTypeEnum.SUB_ACCOUNT.getType().toString();
|
||||
/** 交易账号类型:支付宝账号 **/
|
||||
String TRADE_ACCOUNT_TYPE_ALIPAY_ACCOUNT = TradeAccountTypeEnum.ALIPAY_ACCOUNT.getType().toString();
|
||||
/** 交易账号类型:支付宝用户ID **/
|
||||
String TRADE_ACCOUNT_TYPE_ALIPAY_USERID = TradeAccountTypeEnum.ALIPAY_USERID.getType().toString();
|
||||
/** 交易账号类型:微信OPENID **/
|
||||
String TRADE_ACCOUNT_TYPE_WECHAT_OPENID = TradeAccountTypeEnum.WECHAT_OPENID.getType().toString();
|
||||
/** 交易账号类型:平台机构 **/
|
||||
String TRADE_ACCOUNT_TYPE_PLATFORM_ORG = TradeAccountTypeEnum.PLATFORM_ORG.getType().toString();
|
||||
|
||||
/**
|
||||
* 交易账号类型:其他
|
||||
*/
|
||||
String TRADE_ACCOUNT_TYPE_OTHER = TradeAccountTypeEnum.OTHER.getType().toString();
|
||||
|
||||
/**
|
||||
* 帐户类型枚举
|
||||
*
|
||||
* @author Carlye
|
||||
* @date 2021/08/17
|
||||
*/
|
||||
public enum TradeAccountTypeEnum{
|
||||
BANK(1,"银行卡"),
|
||||
SUB_ACCOUNT(2,"内部子账户"),
|
||||
ALIPAY_ACCOUNT(3,"支付宝账号"),
|
||||
ALIPAY_USERID(4,"支付宝用户ID"),
|
||||
WECHAT_OPENID(5,"微信支付"),
|
||||
PLATFORM_ORG(6,"平台机构"),
|
||||
OTHER(99,"其他");
|
||||
|
||||
private final Integer type;
|
||||
|
||||
private final String desc;
|
||||
|
||||
TradeAccountTypeEnum(Integer type,String desc){
|
||||
this.type = type;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public Integer getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public static TradeAccountTypeEnum find(Integer type){
|
||||
if (Objects.isNull(type)){
|
||||
return null;
|
||||
}
|
||||
return Arrays.stream(TradeAccountTypeEnum.values()).filter(e->e.getType().equals(type)).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.senmesh.common.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.senmesh.common.AlitaResult;
|
||||
import com.senmesh.common.util.JsonUtils;
|
||||
import com.senmesh.common.util.RequestUtils;
|
||||
import com.senmesh.commonexport.service.ExportService;
|
||||
import com.senmesh.usercenter.user.dto.UserDTO;
|
||||
|
||||
@RestController
|
||||
public class TestController {
|
||||
|
||||
@Autowired
|
||||
private ExportService exportService;
|
||||
|
||||
@GetMapping("anon/hostIp")
|
||||
public AlitaResult<String> hostIp() {
|
||||
return AlitaResult.success(RequestUtils.getHostIp());
|
||||
}
|
||||
|
||||
@GetMapping("anon/trim")
|
||||
public AlitaResult<String> gettrim(UserDTO userDTO) {
|
||||
return AlitaResult.success(JsonUtils.toJsonString(userDTO));
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("anon/trim")
|
||||
public AlitaResult<String> posttrim(@RequestBody UserDTO userDTO) {
|
||||
return AlitaResult.success(JsonUtils.toJsonString(userDTO));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
56
backend/src/main/java/com/senmesh/common/dto/BaseDTO.java
Normal file
56
backend/src/main/java/com/senmesh/common/dto/BaseDTO.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
|
||||
import com.senmesh.common.validation.Update;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
|
||||
/**
|
||||
* 基础dto
|
||||
*
|
||||
* @author sr on 2019-12-07
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuperBuilder
|
||||
public class BaseDTO implements Serializable,IBaseDTO {
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = 3305990673475531178L;
|
||||
|
||||
@ApiModelProperty("id")
|
||||
@NotNull(message = "id不能为空", groups = {Update.class})
|
||||
private Long id;
|
||||
@ApiModelProperty("是否逻辑删除(0否1是)")
|
||||
private Boolean deleted;
|
||||
@ApiModelProperty("创建人")
|
||||
private Long createBy;
|
||||
@ApiModelProperty("创建时间")
|
||||
// requestparam
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
// requestbody+serial
|
||||
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
@ApiModelProperty("修改人")
|
||||
private Long updateBy;
|
||||
@ApiModelProperty("修改时间")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateTime;
|
||||
private Long proxyBy;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.senmesh.common.util.UserContextHolder;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 基础操作对象
|
||||
*
|
||||
* @author sr on 2020-08-26
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BaseOperateDTO implements Serializable, IOperateDTO{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = -3075506470200622050L;
|
||||
|
||||
/** 操作机构ID **/
|
||||
@JsonIgnore
|
||||
private Long operateOrgId;
|
||||
|
||||
/** 操作用户ID **/
|
||||
@JsonIgnore
|
||||
private Long operateUserId;
|
||||
|
||||
public Long getOperateOrgId() {
|
||||
return operateOrgId == null ? UserContextHolder.getLoginOrgId() : operateOrgId;
|
||||
}
|
||||
|
||||
public Long getOperateUserId() {
|
||||
return operateUserId == null ? UserContextHolder.getLoginUserId() : operateUserId;
|
||||
}
|
||||
|
||||
}
|
||||
13
backend/src/main/java/com/senmesh/common/dto/ButtonDTO.java
Normal file
13
backend/src/main/java/com/senmesh/common/dto/ButtonDTO.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-22
|
||||
*/
|
||||
public class ButtonDTO implements Serializable{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = -8606784819452806185L;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class CasTokenDTO implements Serializable{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = -6818144396647084889L;
|
||||
|
||||
private String token;
|
||||
|
||||
/** 当前token用户id **/
|
||||
private Long userId;
|
||||
|
||||
/** 是否新注册 **/
|
||||
private Boolean register;
|
||||
|
||||
}
|
||||
37
backend/src/main/java/com/senmesh/common/dto/ColumnDTO.java
Normal file
37
backend/src/main/java/com/senmesh/common/dto/ColumnDTO.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Builder.Default;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-22
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ColumnDTO implements Serializable{
|
||||
/** **/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final int DEFAULT_WIDTH = 160;
|
||||
|
||||
/** 标题,如:总条数 **/
|
||||
private String title;
|
||||
/** 下标(同key),如:totalCount **/
|
||||
private String dataIndex;
|
||||
/** 属性,如:totalCount **/
|
||||
private String key;
|
||||
/** 宽度 **/
|
||||
@Default
|
||||
private int width = DEFAULT_WIDTH;
|
||||
/** 对齐方式 **/
|
||||
@Default
|
||||
private String align = "center";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 展示定制的组件
|
||||
*
|
||||
* @author sr on 2020-04-22
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DisplayedComponentDTO<T> implements Serializable{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 消息 **/
|
||||
private MessageDTO message;
|
||||
/** 列配置 **/
|
||||
private List<ColumnDTO> columns;
|
||||
/** 展示数据 **/
|
||||
private List<T> data;
|
||||
/** 按钮 **/
|
||||
private List<ButtonDTO> buttons;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ExceptionRecordDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 入参json
|
||||
*/
|
||||
private String paramJson;
|
||||
/**
|
||||
* 响应json
|
||||
*/
|
||||
private String resultJson;
|
||||
/**
|
||||
* 调用接口地址
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 系统 1 慧用工、2、3、4待定
|
||||
*/
|
||||
private String sys;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class ExportHeadDetailInfoDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2399902503017038944L;
|
||||
/**
|
||||
* 表头
|
||||
*/
|
||||
private String head;
|
||||
|
||||
/**
|
||||
* 下拉列表
|
||||
*/
|
||||
private List<String> explicitList;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import org.apache.commons.compress.utils.Lists;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class ExportHeadInfoDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -3123297330700623056L;
|
||||
|
||||
|
||||
/**
|
||||
* 文件名称
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 表头
|
||||
*/
|
||||
private List<ExportHeadDetailInfoDTO> heads;
|
||||
|
||||
/**
|
||||
* 数据列
|
||||
*/
|
||||
private List<List> datas;
|
||||
|
||||
public String getFileName() {
|
||||
if (Objects.isNull(fileName)){
|
||||
fileName = DateUtil.today() + "_动态模板";
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public List<ExportHeadDetailInfoDTO> getHeads() {
|
||||
if (Objects.isNull(heads)){
|
||||
heads = Lists.newArrayList();
|
||||
}
|
||||
return heads;
|
||||
}
|
||||
|
||||
public List<List> getDatas() {
|
||||
if (Objects.isNull(datas)){
|
||||
datas = Lists.newArrayList();
|
||||
}
|
||||
return datas;
|
||||
}
|
||||
}
|
||||
12
backend/src/main/java/com/senmesh/common/dto/IBaseDTO.java
Normal file
12
backend/src/main/java/com/senmesh/common/dto/IBaseDTO.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
/**
|
||||
* 基础对象
|
||||
* @author sr on 2024-04-18
|
||||
*/
|
||||
public interface IBaseDTO {
|
||||
|
||||
Long getId();
|
||||
|
||||
|
||||
}
|
||||
23
backend/src/main/java/com/senmesh/common/dto/IDisplayed.java
Normal file
23
backend/src/main/java/com/senmesh/common/dto/IDisplayed.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
/**
|
||||
* 展示信息
|
||||
*
|
||||
* @author sr on 2020-07-14
|
||||
*/
|
||||
public interface IDisplayed{
|
||||
/**
|
||||
* 是否展示
|
||||
* @return
|
||||
* @author sr on 2020-07-14
|
||||
*/
|
||||
Boolean getDisplayed();
|
||||
|
||||
/**
|
||||
* 展示组件
|
||||
* @return
|
||||
* @author sr on 2020-07-14
|
||||
*/
|
||||
DisplayedComponentDTO<?> getDisplayedComponentDto();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
/**
|
||||
* 操作对象
|
||||
*
|
||||
* @author sr on 2020-08-26
|
||||
*/
|
||||
public interface IOperateDTO {
|
||||
|
||||
/**
|
||||
* 设置操作机构id
|
||||
*
|
||||
* @param orgId
|
||||
* @author sr on 2020-08-31
|
||||
*/
|
||||
void setOperateOrgId(Long orgId);
|
||||
|
||||
/**
|
||||
* 设置操作用户id
|
||||
*
|
||||
* @param userId
|
||||
* @author sr on 2020-08-31
|
||||
*/
|
||||
void setOperateUserId(Long userId);
|
||||
|
||||
}
|
||||
43
backend/src/main/java/com/senmesh/common/dto/IPage.java
Normal file
43
backend/src/main/java/com/senmesh/common/dto/IPage.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页对象
|
||||
*
|
||||
* @author sr on 2020-08-26
|
||||
*/
|
||||
public interface IPage<T> {
|
||||
|
||||
/**
|
||||
* 第几页
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-08-26
|
||||
*/
|
||||
Integer getPageNo();
|
||||
|
||||
/**
|
||||
* 页宽
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-08-26
|
||||
*/
|
||||
Integer getPageSize();
|
||||
|
||||
|
||||
/**
|
||||
* 获取是否需要分页属性
|
||||
*
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
Boolean getNeedPage();
|
||||
|
||||
/**
|
||||
* 设置分页结果
|
||||
*
|
||||
* @author sr on 2020-08-26
|
||||
*/
|
||||
void setPageResult(int totalCount, List<T> results);
|
||||
|
||||
}
|
||||
28
backend/src/main/java/com/senmesh/common/dto/MessageDTO.java
Normal file
28
backend/src/main/java/com/senmesh/common/dto/MessageDTO.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Builder.Default;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-22
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class MessageDTO implements Serializable{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = 3623225038987713421L;
|
||||
/** 类型:success/info/warning/error **/
|
||||
@Default
|
||||
private String type = "info";
|
||||
/** 内容 **/
|
||||
private String content;
|
||||
|
||||
}
|
||||
24
backend/src/main/java/com/senmesh/common/dto/OrgBaseDTO.java
Normal file
24
backend/src/main/java/com/senmesh/common/dto/OrgBaseDTO.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 机构基础
|
||||
*
|
||||
* @author sr on 2019-12-22
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
@SuperBuilder
|
||||
public class OrgBaseDTO extends BaseDTO {
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = -2595043832455614748L;
|
||||
|
||||
private Long orgId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.senmesh.common.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.senmesh.tkmybatis.pojo.Page;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 分页查询对象
|
||||
*
|
||||
* @author sr on 2020-08-03
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PageQueryDTO<T> extends BaseOperateDTO implements Serializable, IPage<T> {
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
private static final long serialVersionUID = -6370476082571087349L;
|
||||
|
||||
/**
|
||||
* 第几页
|
||||
**/
|
||||
private Integer pageNo = 1;
|
||||
/**
|
||||
* 页宽
|
||||
**/
|
||||
private Integer pageSize = 20;
|
||||
/**
|
||||
* 是否需要分页
|
||||
**/
|
||||
private Boolean needPage = Boolean.TRUE;
|
||||
/**
|
||||
* 分页结果
|
||||
**/
|
||||
@JsonIgnore
|
||||
private Page<T> pageResult;
|
||||
|
||||
@Override
|
||||
public Integer getPageNo() {
|
||||
return Optional.ofNullable(pageNo).orElse(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPageSize() {
|
||||
return Optional.ofNullable(pageSize).orElse(20);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getNeedPage() {
|
||||
return Optional.ofNullable(this.needPage).orElse(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPageResult(int totalCount, List<T> results) {
|
||||
pageResult = new Page<T>(getPageNo(), getPageSize());
|
||||
pageResult.setPageResult(totalCount, results);
|
||||
}
|
||||
|
||||
public void setPageNo(Integer pageNo) {
|
||||
this.pageNo = pageNo;
|
||||
}
|
||||
|
||||
public void setPageSize(Integer pageSize) {
|
||||
if (pageSize != null && pageSize >1000) {
|
||||
this.pageSize = 1000;
|
||||
} else {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
}
|
||||
|
||||
public void setNeedPage(Boolean needPage) {
|
||||
this.needPage = needPage;
|
||||
}
|
||||
|
||||
public Page<T> getPageResult() {
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
public void setPageResult(Page<T> pageResult) {
|
||||
this.pageResult = pageResult;
|
||||
}
|
||||
|
||||
public PageQueryDTO<T> disablePage() {
|
||||
this.setNeedPage(Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getStartPage() {
|
||||
return (getPageNo() - 1) * getPageSize();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package com.senmesh.common.easyexcel;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
|
||||
import com.alibaba.excel.metadata.Head;
|
||||
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||
import com.alibaba.excel.write.handler.AbstractCellWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
|
||||
import com.senmesh.common.dto.ExportHeadDetailInfoDTO;
|
||||
import com.senmesh.common.dto.ExportHeadInfoDTO;
|
||||
|
||||
import org.apache.poi.hssf.util.HSSFColor;
|
||||
import org.apache.poi.ss.usermodel.BorderStyle;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.DataValidation;
|
||||
import org.apache.poi.ss.usermodel.DataValidationConstraint;
|
||||
import org.apache.poi.ss.usermodel.DataValidationHelper;
|
||||
import org.apache.poi.ss.usermodel.FillPatternType;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.apache.poi.ss.usermodel.Name;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.VerticalAlignment;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.util.CellRangeAddressList;
|
||||
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
|
||||
import org.apache.poi.xssf.usermodel.XSSFColor;
|
||||
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
|
||||
|
||||
import java.awt.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
public class DynamicExportTemplateCellWriteHandler extends AbstractCellWriteHandler {
|
||||
|
||||
private Map<Integer, ExportHeadDetailInfoDTO> exportHeadIndexMap = null;
|
||||
|
||||
private Integer hiddenSheetIndex = 1;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
|
||||
super.afterCellDispose(writeSheetHolder, writeTableHolder, cellDataList, cell, head, relativeRowIndex, isHead);
|
||||
int rowIndex = cell.getRowIndex();
|
||||
int columnIndex = cell.getColumnIndex();
|
||||
Workbook workbook = writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook();
|
||||
Sheet sheet = writeSheetHolder.getSheet();
|
||||
if (rowIndex == 0) {
|
||||
Row row = cell.getRow();
|
||||
row.setHeight((short) (33 * 20));
|
||||
ExportHeadInfoDTO exportInfo = (ExportHeadInfoDTO) ImportDataContext.get("exportInfo");
|
||||
|
||||
XSSFCellStyle cellStyle = (XSSFCellStyle) workbook.createCellStyle();
|
||||
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||
cellStyle.setBottomBorderColor(IndexedColors.BLACK.index);
|
||||
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||
cellStyle.setLeftBorderColor(IndexedColors.BLACK.index);
|
||||
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||
cellStyle.setTopBorderColor(IndexedColors.BLACK.index);
|
||||
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||
cellStyle.setRightBorderColor(IndexedColors.BLACK.index);
|
||||
|
||||
cellStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
|
||||
Color color = new Color(91, 155, 213);
|
||||
cellStyle.setFillForegroundColor(new XSSFColor(color,null));
|
||||
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
|
||||
Font font = workbook.createFont();
|
||||
cellStyle.setFont(font);
|
||||
|
||||
font.setFontName("Calibri");
|
||||
font.setColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex());
|
||||
cell.setCellStyle(cellStyle);
|
||||
if (Objects.isNull(exportHeadIndexMap)) {
|
||||
List<ExportHeadDetailInfoDTO> heads = exportInfo.getHeads();
|
||||
exportHeadIndexMap = Maps.newHashMap();
|
||||
for (int i = 0; i < heads.size(); i++) {
|
||||
ExportHeadDetailInfoDTO exportHead = heads.get(i);
|
||||
exportHeadIndexMap.put(i, exportHead);
|
||||
}
|
||||
}
|
||||
|
||||
ExportHeadDetailInfoDTO exportHeadDetailInfoDTO = exportHeadIndexMap.get(columnIndex);
|
||||
String name;
|
||||
if (Objects.isNull(exportHeadDetailInfoDTO)) {
|
||||
name = cell.getStringCellValue();
|
||||
} else {
|
||||
name = exportHeadDetailInfoDTO.getHead();
|
||||
}
|
||||
int length = ((name.getBytes(StandardCharsets.UTF_8).length + cell.toString().length()) * 3) / 5 + 5;
|
||||
sheet.setColumnWidth(columnIndex, length * 290);
|
||||
|
||||
List<String> explicitList = Objects.isNull(exportHeadDetailInfoDTO) ? null : exportHeadDetailInfoDTO.getExplicitList();
|
||||
// 设置下拉属性
|
||||
// if (CollUtil.isNotEmpty(explicitList)) {
|
||||
// DataValidationHelper helper = sheet.getDataValidationHelper();
|
||||
// DataValidationConstraint constraint = helper.createExplicitListConstraint(explicitList.toArray(new String[]{}));
|
||||
// //设置生效的起始行、终止行、起始列、终止列
|
||||
// CellRangeAddressList cellAddressList = new CellRangeAddressList(1,
|
||||
// 65535, columnIndex, columnIndex);
|
||||
// DataValidation validation = helper.createValidation(constraint, cellAddressList);
|
||||
// //适配xls和xlsx
|
||||
// if (validation instanceof HSSFDataValidation) {
|
||||
// validation.setSuppressDropDownArrow(false);
|
||||
// } else {
|
||||
// validation.setSuppressDropDownArrow(true);
|
||||
// validation.setShowErrorBox(true);
|
||||
// }
|
||||
// sheet.addValidationData(validation);
|
||||
// }
|
||||
|
||||
if (CollUtil.isNotEmpty(explicitList)){
|
||||
String hiddenName = "hidden" + hiddenSheetIndex;
|
||||
//1.创建隐藏的sheet页。 起个名字吧!叫"hidden"!
|
||||
Sheet hiddenSheet = workbook.createSheet(hiddenName);
|
||||
//2.循环赋值(为了防止下拉框的行数与隐藏域的行数相对应,将隐藏域加到结束行之后)
|
||||
for (int i = 0; i < explicitList.size(); i++) {
|
||||
hiddenSheet.createRow(65535 + i).createCell(columnIndex).setCellValue(explicitList.get(i));
|
||||
}
|
||||
Name category1Name = workbook.createName();
|
||||
category1Name.setNameName(hiddenName);
|
||||
//3 A1:A代表隐藏域创建第N列createCell(N)时。以A1列开始A行数据获取下拉数组
|
||||
category1Name.setRefersToFormula(hiddenName + "!A1:A" + (explicitList.size() + 65535));
|
||||
//
|
||||
DataValidationHelper helper = sheet.getDataValidationHelper();
|
||||
DataValidationConstraint constraint = helper.createFormulaListConstraint(hiddenName);
|
||||
CellRangeAddressList addressList = new CellRangeAddressList(1, 65535, columnIndex, columnIndex);
|
||||
DataValidation dataValidation = helper.createValidation(constraint, addressList);
|
||||
if (dataValidation instanceof XSSFDataValidation) {
|
||||
// 数据校验
|
||||
dataValidation.setSuppressDropDownArrow(true);
|
||||
dataValidation.setShowErrorBox(true);
|
||||
} else {
|
||||
dataValidation.setSuppressDropDownArrow(false);
|
||||
}
|
||||
// 作用在目标sheet上
|
||||
sheet.addValidationData(dataValidation);
|
||||
// 设置hiddenSheet隐藏
|
||||
workbook.setSheetHidden(hiddenSheetIndex, true);
|
||||
hiddenSheetIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.senmesh.common.easyexcel;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.ExcelWriter;
|
||||
import com.senmesh.common.dto.ExportHeadDetailInfoDTO;
|
||||
import com.senmesh.common.dto.ExportHeadInfoDTO;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
public class ExcelUtil {
|
||||
|
||||
|
||||
private ExcelUtil() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 动态导出excel模板
|
||||
*
|
||||
* @param reqDto 点播dto
|
||||
*/
|
||||
public static void dynamicExportTemplate(ExportHeadInfoDTO reqDto) {
|
||||
if (CollUtil.isEmpty(reqDto.getHeads())) {
|
||||
return;
|
||||
}
|
||||
ImportDataContext.set("exportInfo", reqDto);
|
||||
|
||||
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
HttpServletResponse response = attr.getResponse();
|
||||
if (Objects.isNull(response)) {
|
||||
return;
|
||||
}
|
||||
String fileName = reqDto.getFileName() + ".xlsx";
|
||||
|
||||
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
String fileNamePrefix = StrUtil.subBefore(fileName, ".", true);
|
||||
String fileNameSuffix = StrUtil.subAfter(fileName, ".", true);
|
||||
try {
|
||||
String fileNamePrefix2 = URLEncoder.encode(fileNamePrefix, "UTF-8").replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-Disposition", "attachment;" + fileNamePrefix2 + "." + fileNameSuffix);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
throw new IllegalArgumentException("下载文件异常");
|
||||
}
|
||||
response.addHeader("Param", "no-cache");
|
||||
response.addHeader("Cache-Control", "no-cache");
|
||||
|
||||
try (OutputStream out = response.getOutputStream();) {
|
||||
ExcelWriter writer = EasyExcel.write(out).registerWriteHandler(new DynamicExportTemplateCellWriteHandler()).build();
|
||||
List<List> objects = Lists.newArrayList();
|
||||
objects.add(reqDto.getHeads().stream().map(ExportHeadDetailInfoDTO::getHead).collect(Collectors.toList()));
|
||||
objects.addAll(reqDto.getDatas());
|
||||
writer.write(objects, EasyExcel.writerSheet("sheet").build());
|
||||
writer.finish();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.senmesh.common.easyexcel;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ImportDataContext {
|
||||
public static final ThreadLocal<Map<String, Object>> THREAD_LOCAL_MAP = new ThreadLocal<>();
|
||||
|
||||
public static Object get(String key) {
|
||||
Map<String, Object> stringObjectMap = THREAD_LOCAL_MAP.get();
|
||||
if (Objects.isNull(stringObjectMap)) {
|
||||
THREAD_LOCAL_MAP.set(Maps.newHashMap());
|
||||
return null;
|
||||
}
|
||||
return stringObjectMap.get(key);
|
||||
}
|
||||
|
||||
public static void set(String key,Object value) {
|
||||
Map<String, Object> stringObjectMap = THREAD_LOCAL_MAP.get();
|
||||
if (Objects.isNull(stringObjectMap)) {
|
||||
THREAD_LOCAL_MAP.set(Maps.newHashMap());
|
||||
}
|
||||
THREAD_LOCAL_MAP.get().put(key,value);
|
||||
}
|
||||
|
||||
public static void putAll(Map<String, Object> extraInfoMap){
|
||||
Map<String, Object> stringObjectMap = THREAD_LOCAL_MAP.get();
|
||||
if (Objects.isNull(stringObjectMap)) {
|
||||
THREAD_LOCAL_MAP.set(Maps.newHashMap());
|
||||
}
|
||||
THREAD_LOCAL_MAP.get().putAll(extraInfoMap);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.senmesh.common.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.senmesh.common.dto.IBaseDTO;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuperBuilder
|
||||
public abstract class BaseEntity implements Serializable, Cloneable, IBaseDTO {
|
||||
|
||||
private static final long serialVersionUID = -723338960662087915L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
/**
|
||||
* 是否逻辑删除(0否1是)
|
||||
*/
|
||||
@TableField(value = "deleted", fill = FieldFill.INSERT)
|
||||
@Builder.Default
|
||||
private Boolean deleted = Boolean.FALSE;
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(value = "create_by", fill = FieldFill.INSERT)
|
||||
private Long createBy;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@TableField(value = "create_time", fill = FieldFill.INSERT)
|
||||
private Date createTime;
|
||||
/**
|
||||
* 修改人
|
||||
*/
|
||||
@TableField(value = "update_by", fill = FieldFill.UPDATE)
|
||||
private Long updateBy;
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@TableField(value = "update_time", fill = FieldFill.UPDATE)
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 代理人
|
||||
*/
|
||||
private Long proxyBy;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.senmesh.common.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 机构基础实体
|
||||
* @author sr on 2019-10-19
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuperBuilder
|
||||
public class OrgBaseEntity extends BaseEntity {
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = 2559571969035017003L;
|
||||
|
||||
/** 机构id **/
|
||||
private Long orgId;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.senmesh.common.exception;
|
||||
|
||||
|
||||
/**
|
||||
* 项目所有自定义异常父类
|
||||
*
|
||||
* @author guhong
|
||||
*/
|
||||
public class AlitaException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 3121871576733025786L;
|
||||
|
||||
public AlitaException() {}
|
||||
|
||||
public AlitaException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public AlitaException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public AlitaException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.senmesh.common.exception;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*
|
||||
* @author guhong
|
||||
* @date 2019/8/19
|
||||
*/
|
||||
public class BusinessException extends AlitaException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -3097174055942519169L;
|
||||
|
||||
public BusinessException() {}
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BusinessException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BusinessException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.senmesh.common.exception;
|
||||
|
||||
import com.senmesh.common.AlitaResult;
|
||||
import com.senmesh.common.dto.CasTokenDTO;
|
||||
|
||||
/**
|
||||
* 更新token
|
||||
* @author sr on 2021-07-05
|
||||
*/
|
||||
public class ChangeTokenException extends WithResultException{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = -8897274199463229506L;
|
||||
|
||||
|
||||
public ChangeTokenException(CasTokenDTO casTokenDTO) {
|
||||
super(AlitaResult.changeToken(casTokenDTO));
|
||||
}
|
||||
|
||||
public CasTokenDTO getCasTokenDTO(){
|
||||
return (CasTokenDTO) getResult().getErrorData();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.senmesh.common.exception;
|
||||
|
||||
import com.senmesh.common.exception.util.DevopsUtils;
|
||||
|
||||
/**
|
||||
* 运维业务异常
|
||||
* @author sr on 2022-03-01
|
||||
*/
|
||||
public class DevopsException extends BusinessException{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DevopsException(String message) {
|
||||
super(message);
|
||||
DevopsUtils.deal(message);
|
||||
}
|
||||
|
||||
public DevopsException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
DevopsUtils.deal(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.senmesh.common.exception;
|
||||
|
||||
/**
|
||||
* 已存在业务异常
|
||||
* @author sr on 2022-03-01
|
||||
*/
|
||||
public class ExistException extends BusinessException{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ExistException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ExistException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExistException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.senmesh.common.exception;
|
||||
|
||||
public interface IDevopsProvider {
|
||||
|
||||
void deal(String msg);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.senmesh.common.exception;
|
||||
|
||||
/**
|
||||
* 用户未登录异常
|
||||
*
|
||||
* @author guhong
|
||||
* @date 2019/8/19
|
||||
*/
|
||||
public class NoneLoginException extends AlitaException {
|
||||
|
||||
public static final String ERROR_MSG = "用户未登录";
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -7013134475760472324L;
|
||||
|
||||
public NoneLoginException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
|
||||
public NoneLoginException() {
|
||||
super(ERROR_MSG);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.senmesh.common.exception;
|
||||
|
||||
import com.senmesh.common.AlitaResult;
|
||||
|
||||
/**
|
||||
* 更新token
|
||||
* @author sr on 2021-07-05
|
||||
*/
|
||||
public class WithResultException extends AlitaException{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = -8897274199463229506L;
|
||||
|
||||
private AlitaResult<?> result;
|
||||
|
||||
public WithResultException(AlitaResult<?> result) {
|
||||
super(result.getMsg());
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public WithResultException(AlitaResult<?> result, Throwable cause) {
|
||||
super(result.getMsg(), cause);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public AlitaResult<?> getResult(){
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.senmesh.common.exception.controller;
|
||||
|
||||
import com.senmesh.common.exception.dao.ExceptionLogMapper;
|
||||
import io.swagger.annotations.Api;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 异常日志查看
|
||||
*
|
||||
* @author on 2019-11-05
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/anon/exception")
|
||||
@Api(tags = "异常日志查看")
|
||||
public class ExceptionLogController {
|
||||
|
||||
@Autowired
|
||||
private ExceptionLogMapper exceptionLogMapper;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public String getExceptionMsg(@PathVariable Long id) {
|
||||
try {
|
||||
return exceptionLogMapper.selectById(id).getMsg();
|
||||
} catch (Exception e) {
|
||||
return "未找到对应id错误";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.senmesh.common.exception.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.senmesh.common.exception.entity.ExceptionLog;
|
||||
|
||||
/**
|
||||
* 异常日志
|
||||
*
|
||||
* @author on 2019-11-05
|
||||
*/
|
||||
public interface ExceptionLogMapper extends BaseMapper<ExceptionLog> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.senmesh.common.exception.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 错误日志
|
||||
* @author sr on 2019-10-28
|
||||
*/
|
||||
@TableName("xs_exception_log")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ExceptionLog implements Serializable{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = 3730091070919812549L;
|
||||
|
||||
/** 主键 **/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 创建时间 **/
|
||||
private Date createTime;
|
||||
|
||||
/** 错误信息 **/
|
||||
private String msg;
|
||||
|
||||
public ExceptionLog(Date createTime, String msg) {
|
||||
super();
|
||||
this.createTime = createTime;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package com.senmesh.common.exception.handle;
|
||||
|
||||
import com.senmesh.common.util.EnvironmentUtils;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import com.senmesh.common.AlitaResult;
|
||||
import com.senmesh.common.constant.CommonConstants;
|
||||
import com.senmesh.common.exception.BusinessException;
|
||||
import com.senmesh.common.exception.NoneLoginException;
|
||||
import com.senmesh.common.exception.WithResultException;
|
||||
import com.senmesh.common.log.dto.ExceptionLogDTO;
|
||||
import com.senmesh.common.log.util.LogUtils;
|
||||
import com.senmesh.common.util.SpringContextHolder;
|
||||
import com.senmesh.common.util.TracerUtils;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*
|
||||
* @author guhong
|
||||
* @date 2019/8/19
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalControllerExceptionHandler {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory
|
||||
.getLogger(GlobalControllerExceptionHandler.class);
|
||||
|
||||
@Value("${spring.application.name:}")
|
||||
private String applicationName;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*
|
||||
* @param ex
|
||||
*/
|
||||
@ExceptionHandler(value = {BusinessException.class})
|
||||
public AlitaResult<String> handleBusinessException(BusinessException ex) {
|
||||
String traceId = TracerUtils.getTraceId();
|
||||
String msg;
|
||||
if (StringUtils.isNotBlank(ex.getMessage()) && ex.getMessage().contains(traceId)) {
|
||||
msg = ex.getMessage();
|
||||
}else {
|
||||
msg = String.format("[%s]操作失败:%s", traceId, ex.getMessage());
|
||||
}
|
||||
|
||||
LOGGER.debug(msg, ex);
|
||||
AlitaResult<String> alitaResult = AlitaResult.error(AlitaResult.BUSINESS_ERROR_CODE, msg);
|
||||
alitaResult.setRequestId(traceId);
|
||||
return alitaResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未登录异常
|
||||
*
|
||||
* @param ex
|
||||
*/
|
||||
@ExceptionHandler(value = {NoneLoginException.class})
|
||||
public AlitaResult<String> handleNoneLoginException(NoneLoginException ex) {
|
||||
AlitaResult<String> alitaResult = AlitaResult.unLogin();
|
||||
alitaResult.setRequestId(TracerUtils.getTraceId());
|
||||
return alitaResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理更换token
|
||||
*
|
||||
* @param ex
|
||||
*/
|
||||
@ExceptionHandler(value = {WithResultException.class})
|
||||
public AlitaResult<?> handleChangeTokenException(WithResultException ex) {
|
||||
AlitaResult<?> result = ex.getResult();
|
||||
result.setRequestId(TracerUtils.getTraceId());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理校验异常
|
||||
*
|
||||
* @param ex
|
||||
*/
|
||||
@ExceptionHandler(value = {
|
||||
MethodArgumentNotValidException.class,
|
||||
BindException.class})
|
||||
public AlitaResult<String> handleValidationException(Exception ex) {
|
||||
BindingResult result;
|
||||
if (ex instanceof MethodArgumentNotValidException) {
|
||||
result = ((MethodArgumentNotValidException) ex).getBindingResult();
|
||||
} else {
|
||||
result = ((BindException) ex).getBindingResult();
|
||||
}
|
||||
if (!result.hasErrors()) {
|
||||
return AlitaResult.error("操作失败");
|
||||
}
|
||||
|
||||
StringJoiner joiner = new StringJoiner(",");
|
||||
List<ObjectError> errors = result.getAllErrors();
|
||||
|
||||
errors.forEach(error -> {
|
||||
if (error instanceof FieldError) {
|
||||
joiner.add(((FieldError) error).getField() + "-" + error.getDefaultMessage());
|
||||
} else {
|
||||
joiner.add(error.getDefaultMessage());
|
||||
}
|
||||
});
|
||||
|
||||
String msg = String.format("数据异常:%s", joiner.toString());
|
||||
LOGGER.error(msg);
|
||||
AlitaResult<String> alitaResult = AlitaResult.error(AlitaResult.VALIDATION_ERROR_CODE, msg);
|
||||
alitaResult.setRequestId(TracerUtils.getTraceId());
|
||||
return alitaResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未知的错误
|
||||
*
|
||||
* @param ex
|
||||
*/
|
||||
@ExceptionHandler(value = {ConstraintViolationException.class})
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
public AlitaResult<String> handleConstraintViolationException(ConstraintViolationException ex) {
|
||||
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
|
||||
StringJoiner joiner = new StringJoiner(CommonConstants.COMMA);
|
||||
constraintViolations.stream().forEach(error -> joiner.add(error.getMessage()));
|
||||
String msg = String.format("数据异常:%s", joiner.toString());
|
||||
LOGGER.error(msg);
|
||||
AlitaResult<String> alitaResult = AlitaResult.error(AlitaResult.VALIDATION_ERROR_CODE, msg);
|
||||
alitaResult.setRequestId(TracerUtils.getTraceId());
|
||||
return alitaResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未知的错误
|
||||
*
|
||||
* @param ex
|
||||
*/
|
||||
@ExceptionHandler(value = {Exception.class})
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
public AlitaResult<String> handleUnknownException(Exception ex) {
|
||||
String msg = null;
|
||||
if (ex instanceof HttpRequestMethodNotSupportedException) {
|
||||
msg = "请求错误:" + ((HttpRequestMethodNotSupportedException) ex).getSupportedMethods()[0]
|
||||
+ SpringContextHolder.getRequest().getRequestURI();
|
||||
} else {
|
||||
LOGGER.error("",ex);
|
||||
ExceptionLogDTO exceptionLogDTO = ExceptionLogDTO.builder().traceId(TracerUtils.getTraceId())
|
||||
.msg(ExceptionUtils.getStackTrace(ex)).build();
|
||||
LogUtils.log(exceptionLogDTO);
|
||||
msg = String.format("系统异常[%s#%s],请稍后重试.",
|
||||
EnvironmentUtils.getSystemCode(), exceptionLogDTO.getErrNo()
|
||||
);
|
||||
LOGGER.error("系统异常,错误号:{}", exceptionLogDTO.getErrNo());
|
||||
}
|
||||
|
||||
AlitaResult<String> alitaResult = AlitaResult.error(msg);
|
||||
alitaResult.setRequestId(TracerUtils.getTraceId());
|
||||
return alitaResult;
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder binder) {
|
||||
binder.registerCustomEditor(String.class, new StringTrimmerEditor(" \t\r\n\f", true));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.senmesh.common.exception.util;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.senmesh.common.constant.CommonConstants;
|
||||
import com.senmesh.common.exception.BusinessException;
|
||||
import com.senmesh.common.util.JsonUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* 断言
|
||||
*
|
||||
* @author sr on 2020-09-02
|
||||
*/
|
||||
@Slf4j
|
||||
public class Assert {
|
||||
|
||||
private Assert() {
|
||||
}
|
||||
|
||||
private static void dealLog(String message, Object... logData) {
|
||||
if (logData.length > 0) {
|
||||
log.info(new StringBuilder().append(message).append(CommonConstants.COLON)
|
||||
.append(JsonUtils.toJsonString(logData)).toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* true正常,fasle抛BusinessException
|
||||
*
|
||||
* @param expression
|
||||
* @param message
|
||||
* @author sr on 2020-09-02
|
||||
*/
|
||||
public static void isTrue(boolean expression, String message, Object... logData) {
|
||||
if (!expression) {
|
||||
dealLog(message, logData);
|
||||
throw new BusinessException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* true正常,fasle抛BusinessException
|
||||
*
|
||||
* @param expression 表达式
|
||||
* @param message 消息
|
||||
*/
|
||||
public static void isTrue(boolean expression, Supplier<String> message) {
|
||||
if (!expression) {
|
||||
throw new BusinessException(message.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* true正常,fasle抛指定异常
|
||||
* @param expression
|
||||
* @param e
|
||||
* @author sr on 2021-06-18
|
||||
*/
|
||||
public static void isTrue(boolean expression, RuntimeException e) {
|
||||
if (!expression) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* null正常,notNull抛BusinessException
|
||||
*
|
||||
* @param object
|
||||
* @param message
|
||||
* @author sr on 2020-09-02
|
||||
*/
|
||||
public static void isNull(@Nullable Object object, String message, Object... logData) {
|
||||
if (object != null) {
|
||||
dealLog(message, logData);
|
||||
throw new BusinessException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notNull正常,null抛BusinessException
|
||||
*
|
||||
* @param object
|
||||
* @param message
|
||||
* @author sr on 2020-09-02
|
||||
*/
|
||||
public static void notNull(@Nullable Object object, String message, Object... logData) {
|
||||
if (object == null) {
|
||||
dealLog(message, logData);
|
||||
throw new BusinessException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notNull正常,null抛BusinessException
|
||||
*
|
||||
* @param object 对象
|
||||
* @param message 消息
|
||||
*/
|
||||
public static void notNull(@Nullable Object object, Supplier<String> message) {
|
||||
if (object == null) {
|
||||
throw new BusinessException(message.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notBlank正常,blank抛BusinessException
|
||||
*
|
||||
* @param str
|
||||
* @param message
|
||||
* @author sr on 2021-01-06
|
||||
*/
|
||||
public static void notBlank(String str, String message, Object... logData) {
|
||||
if (StringUtils.isBlank(str)) {
|
||||
dealLog(message, logData);
|
||||
throw new BusinessException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notEmpty(@Nullable Collection<?> collection, String message) {
|
||||
if (CollectionUtils.isEmpty(collection)) {
|
||||
throw new BusinessException(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.senmesh.common.exception.util;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.senmesh.common.exception.IDevopsProvider;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 运维工具类
|
||||
* @author sr on 2022-03-01
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DevopsUtils {
|
||||
|
||||
private static IDevopsProvider devopsProvider;
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setDevopsProvider(IDevopsProvider devopsProvider) {
|
||||
DevopsUtils.devopsProvider = devopsProvider;
|
||||
}
|
||||
|
||||
public static void deal(String msg) {
|
||||
if (devopsProvider == null) {
|
||||
log.error("no devops provider, error:{}", msg);
|
||||
return ;
|
||||
}
|
||||
devopsProvider.deal(msg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.senmesh.common.exception.util;
|
||||
|
||||
import com.senmesh.common.exception.BusinessException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 异常工具类
|
||||
* @author sr on 2019-10-14
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExceptionUtils {
|
||||
|
||||
/**
|
||||
* @Description 抛异常
|
||||
* @Author chenyun.huang
|
||||
* @Date 2020/3/27 15:06
|
||||
**/
|
||||
public static void throwBusinessException(String msg) {
|
||||
ExceptionUtils.throwBusinessException(true,msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据判断抛异常
|
||||
* @param isThrow
|
||||
* @param msg 异常信息
|
||||
* @author sr on 2019-10-14
|
||||
*/
|
||||
public static void throwBusinessException(boolean isThrow, String msg) {
|
||||
if (isThrow) {
|
||||
throw new BusinessException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据判断抛异常
|
||||
* @param isThrow
|
||||
* @param errorMsg 异常信息
|
||||
* @param logMsg 日志信息(info级)
|
||||
* @author sr on 2019-12-18
|
||||
*/
|
||||
public static void throwBusinessException(boolean isThrow, String errorMsg, String logMsg) {
|
||||
if (isThrow) {
|
||||
log.info(logMsg);
|
||||
throw new BusinessException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.senmesh.common.exception.util;
|
||||
|
||||
import com.senmesh.common.util.RandomUtils;
|
||||
|
||||
/**
|
||||
* uuid工具类
|
||||
* {@link RandomUtils}
|
||||
* @author sr on 2019-12-10
|
||||
*/
|
||||
@Deprecated
|
||||
public class UUIDUtils {
|
||||
|
||||
/**
|
||||
* 生成uuid,去掉-
|
||||
* @return
|
||||
* @author sr on 2019-12-10
|
||||
*/
|
||||
public static String generateUUID() {
|
||||
return RandomUtils.generateUUID();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.senmesh.common.freemarker.constant;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-13
|
||||
*/
|
||||
public enum BusinessModelFileEnum {
|
||||
|
||||
/** 常量 **/
|
||||
CONSTANT("Constants.java", "constant", "Constant.ftl"),
|
||||
/** 控制层 **/
|
||||
CONTROLLER_FILE_SUFFIX("Controller.java", "controller", "Controller.ftl"),
|
||||
/** dao **/
|
||||
DAO_FILE_SUFFIX("Mapper.java", "dao", "Dao.ftl"),
|
||||
/** dto **/
|
||||
DTO_FILE_SUFFIX("DTO.java", "dto", "DTO.ftl"),
|
||||
/** operateDto **/
|
||||
OPERATE_DTO_FILE_SUFFIX("OperateDTO.java", "dto", "OperateDTO.ftl"),
|
||||
/** queryDto **/
|
||||
QUERY_DTO_FILE_SUFFIX("QueryDTO.java", "dto", "QueryDTO.ftl"),
|
||||
/** entity **/
|
||||
ENTITY_FILE_SUFFIX(".java", "entity", "entity.ftl"),
|
||||
/** mapper **/
|
||||
MAPPER_FILE_SUFFIX("Mapper.xml", "mapper", "Mapper.ftl"),
|
||||
/** mapstruct **/
|
||||
MAPSTRUCT_FILE_SUFFIX("MapMapper.java", "mapstruct", "MapMapper.ftl"),
|
||||
/** service **/
|
||||
SERVICE_FILE_SUFFIX("Service.java", "service", "Service.ftl"),
|
||||
/** serviceImpl **/
|
||||
SERVICE_IMPL_FILE_SUFFIX("ServiceImpl.java", "service.impl", "ServiceImpl.ftl"),
|
||||
/** util **/
|
||||
UTIL_FILE_SUFFIX("Utils.java", "util", "Util.ftl");
|
||||
|
||||
|
||||
|
||||
/** 生成文件后缀 **/
|
||||
private String generatefileSuffix;
|
||||
/** 包名 **/
|
||||
private String packageName;
|
||||
/** 模版文件后缀 **/
|
||||
private String ftlFile;
|
||||
|
||||
private BusinessModelFileEnum(String fileSuffix, String packageName, String ftlFile) {
|
||||
this.generatefileSuffix = fileSuffix;
|
||||
this.packageName = packageName;
|
||||
this.ftlFile = ftlFile;
|
||||
}
|
||||
|
||||
public String getGeneratefileSuffix() {
|
||||
return generatefileSuffix;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public String getFtlFile() {
|
||||
return ftlFile;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.senmesh.common.freemarker.constant;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-10
|
||||
*/
|
||||
public interface FreemarkerConstants {
|
||||
|
||||
/** 数据库表名前缀 **/
|
||||
String TABLE_PREFIX = "xs_";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.senmesh.common.freemarker.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-10
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class BusinessModelCreateDTO implements Serializable {
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = 3962743989033667188L;
|
||||
|
||||
/**
|
||||
* 文件输出路径-绝对路径,如/work/eclipse/workspace/alita-base/common/src/main/java/com/xxx/common/freemarker/
|
||||
**/
|
||||
private String exportDir;
|
||||
/** 包路径,如com.xxx.dict **/
|
||||
private String packageName;
|
||||
/** 是否创建业务路径,默认创建 **/
|
||||
private Boolean createBusinessPath;
|
||||
/** 数据库表名:为空,则默认xs_entityName(snake小写) **/
|
||||
private String tableName;
|
||||
/** 作者名 **/
|
||||
private String author;
|
||||
/** 实体名,如Dict **/
|
||||
private String entityName;
|
||||
/** 实体名描述,如数据字典 **/
|
||||
private String entityNameDescription;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.senmesh.common.freemarker.util;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.senmesh.common.constant.CommonConstants;
|
||||
import com.senmesh.common.freemarker.constant.BusinessModelFileEnum;
|
||||
import com.senmesh.common.freemarker.constant.FreemarkerConstants;
|
||||
import com.senmesh.common.freemarker.dto.BusinessModelCreateDTO;
|
||||
import com.senmesh.common.util.StrUtils;
|
||||
|
||||
import freemarker.template.Template;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-13
|
||||
*/
|
||||
@Slf4j
|
||||
public class CodeGenerateUtils {
|
||||
|
||||
public static void generateModelFile(BusinessModelCreateDTO businessModelCreateDto) {
|
||||
for (BusinessModelFileEnum businessModelFileEnum : BusinessModelFileEnum.values()) {
|
||||
generateModelFile(businessModelCreateDto, businessModelFileEnum);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean generateModelFile(BusinessModelCreateDTO businessModelCreateDto,
|
||||
BusinessModelFileEnum businessModelFileEnum) {
|
||||
// 文件输出路径/包名路径/实体名+文件后缀
|
||||
String entityName = businessModelCreateDto.getEntityName();
|
||||
String entityNameLowerCase = StrUtils.toLowerCamel(entityName);
|
||||
String entityNameSnake = StrUtils.toSnake(entityNameLowerCase);
|
||||
String packageName = businessModelCreateDto.getPackageName();
|
||||
String tableName = businessModelCreateDto.getTableName();
|
||||
String path = String.join(StringUtils.EMPTY,
|
||||
packageNameToPath(businessModelFileEnum.getPackageName()), File.separator,
|
||||
entityName, businessModelFileEnum.getGeneratefileSuffix());
|
||||
if (!Boolean.FALSE.equals(businessModelCreateDto.getCreateBusinessPath())) {
|
||||
path = String.join(StringUtils.EMPTY, entityName.toLowerCase(), File.separator, path);
|
||||
packageName = String.join(CommonConstants.POINT, packageName, entityName.toLowerCase());
|
||||
}
|
||||
final String templateName = businessModelFileEnum.getFtlFile();
|
||||
File file = new File(businessModelCreateDto.getExportDir(), path);
|
||||
if (!file.getParentFile().exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
Map<String, Object> dataMap = new HashMap<>();
|
||||
try {
|
||||
dataMap.put("packageName", packageName);
|
||||
dataMap.put("author", businessModelCreateDto.getAuthor());
|
||||
dataMap.put("entityName", entityName);
|
||||
dataMap.put("entityNameLowerCamel", entityNameLowerCase);
|
||||
dataMap.put("entityNameSnake", entityNameSnake);
|
||||
dataMap.put("entityNameDescription", businessModelCreateDto.getEntityNameDescription());
|
||||
dataMap.put("tableName",
|
||||
StringUtils.isBlank(tableName) ? FreemarkerConstants.TABLE_PREFIX + entityNameSnake
|
||||
: tableName);
|
||||
|
||||
generateFileByTemplate(templateName, file, dataMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.info("generateFile fail:{}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
log.info("generateFile suc:{}", file.getPath());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void generateFileByTemplate(final String templateName, File file,
|
||||
Map<String, Object> dataMap) throws Exception {
|
||||
Template template = FreeMarkerTemplateUtils.getTemplate(templateName);
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
|
||||
template.process(dataMap, out);
|
||||
}
|
||||
|
||||
public static String packageNameToPath(String packageName) {
|
||||
return packageName.replace('.', File.separatorChar);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.senmesh.common.freemarker.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Map;
|
||||
|
||||
import freemarker.cache.ClassTemplateLoader;
|
||||
import freemarker.cache.NullCacheStorage;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import freemarker.template.TemplateExceptionHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-13
|
||||
*/
|
||||
@Slf4j
|
||||
public class FreeMarkerTemplateUtils {
|
||||
|
||||
private FreeMarkerTemplateUtils() {}
|
||||
|
||||
private static final Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_22);
|
||||
|
||||
static {
|
||||
//这里比较重要,用来指定加载模板所在的路径
|
||||
CONFIGURATION
|
||||
.setTemplateLoader(new ClassTemplateLoader(FreeMarkerTemplateUtils.class, "/ftls"));
|
||||
CONFIGURATION.setDefaultEncoding("UTF-8");
|
||||
CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
||||
CONFIGURATION.setCacheStorage(NullCacheStorage.INSTANCE);
|
||||
CONFIGURATION.setClassicCompatible(true);
|
||||
}
|
||||
|
||||
public static Template getTemplate(String templateName) throws IOException {
|
||||
try {
|
||||
return CONFIGURATION.getTemplate(templateName);
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串模版占位符替换
|
||||
* @param templateStr
|
||||
* @param params
|
||||
* @return
|
||||
* @author sr on 2020-12-21
|
||||
*/
|
||||
public static String formatString(String templateStr, Map<String, Object> params) {
|
||||
try {
|
||||
Template template = new Template("formatString", templateStr, FreeMarkerTemplateUtils.CONFIGURATION);
|
||||
StringWriter result = new StringWriter();
|
||||
template.process(params, result);
|
||||
return result.toString();
|
||||
} catch (IOException | TemplateException e) {
|
||||
log.error("内容占位符替换失败:{}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void clearCache() {
|
||||
CONFIGURATION.clearTemplateCache();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.senmesh.common.function;
|
||||
|
||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TransactionAfterCommit {
|
||||
|
||||
void afterCommit();
|
||||
|
||||
/**
|
||||
* 事务提交后的回调操作
|
||||
*
|
||||
* @param transactionAfterCommit 事务提交后
|
||||
*/
|
||||
static void doSomeThingAfterCommit(TransactionAfterCommit transactionAfterCommit) {
|
||||
TransactionSynchronizationManager
|
||||
.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
transactionAfterCommit.afterCommit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void doSomeThingAfterCompletion(TransactionAfterCommit transactionAfterCommit) {
|
||||
TransactionSynchronizationManager
|
||||
.registerSynchronization(new TransactionSynchronizationAdapter() {
|
||||
@Override
|
||||
public void afterCompletion(int status) {
|
||||
transactionAfterCommit.afterCommit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.senmesh.common.functions;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author chenyun
|
||||
* @description 序列化的Function
|
||||
* @date 2020/9/16 13:38
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SFunction<T, R> extends Function<T, R>, Serializable {
|
||||
|
||||
/**
|
||||
* 解析function为SerializedLambda
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default SerializedLambda serialized() {
|
||||
try {
|
||||
Method replaceMethod = getClass().getDeclaredMethod("writeReplace");
|
||||
replaceMethod.setAccessible(true);
|
||||
return (SerializedLambda) replaceMethod.invoke(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.senmesh.common.log;
|
||||
|
||||
/**
|
||||
* 日志提供者
|
||||
*
|
||||
* @author sr on 2020-10-29
|
||||
*/
|
||||
public interface ILogProvider {
|
||||
|
||||
void log(Object object);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.senmesh.common.log.aspect;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.senmesh.common.AlitaResult;
|
||||
import com.senmesh.common.constant.CommonConstants;
|
||||
import com.senmesh.common.dto.IOperateDTO;
|
||||
import com.senmesh.common.log.dto.OperationLogDTO;
|
||||
import com.senmesh.common.log.util.LogUtils;
|
||||
import com.senmesh.common.util.*;
|
||||
import com.senmesh.security.config.SecurityProperties;
|
||||
import com.senmesh.tkmybatis.pojo.Page;
|
||||
import com.senmesh.usercenter.dto.IUserDetail;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* controller日志
|
||||
*
|
||||
* @author sr on 2020-07-30
|
||||
*/
|
||||
@Component
|
||||
@Aspect
|
||||
@Slf4j
|
||||
public class ControllerLogAspect {
|
||||
|
||||
private static final String ANON_LOGIN_URI = "/anon/user/find_user_detail_dto";
|
||||
|
||||
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties securityProperties;
|
||||
|
||||
|
||||
@Around("@within(org.springframework.web.bind.annotation.RestController)")
|
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
Object result = null;
|
||||
Throwable currentThrowable = null;
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
// 记录mongo日志
|
||||
IUserDetail userDto = UserContextHolder.getLoginUser();
|
||||
OperationLogDTO operationLog = new OperationLogDTO();
|
||||
HttpServletRequest request = SpringContextHolder.getRequest();
|
||||
String requestUri = request.getRequestURL().toString();
|
||||
String traceId = TracerUtils.getTraceId();
|
||||
operationLog.setTraceId(traceId);
|
||||
operationLog.setSpanId(TracerUtils.getSpanId());
|
||||
operationLog.setParentId(TracerUtils.getParentId());
|
||||
operationLog.setLoginUser(userDto);
|
||||
operationLog.setIp(RequestUtils.getIpAddress(request));
|
||||
operationLog.setSystemName(EnvironmentUtils.getApplicationName());
|
||||
operationLog.setRequestMethod(request.getMethod());
|
||||
operationLog.setRequestTime(new Date());
|
||||
operationLog.setRequestUri(requestUri);
|
||||
List<Object> argList = Lists.newArrayList();
|
||||
for (Object object : joinPoint.getArgs()) {
|
||||
// 移除参数分页对象,结果会返回,会记录
|
||||
if (object instanceof Page) {
|
||||
continue;
|
||||
}
|
||||
// 文件对象
|
||||
if (object instanceof MultipartFile) {
|
||||
long fileDes = ((MultipartFile) object).getSize();
|
||||
argList.add("multipartFile size:" + fileDes);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(object instanceof Serializable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//封装参数
|
||||
if (Objects.nonNull(userDto) && Objects.nonNull(object) && object instanceof IOperateDTO) {
|
||||
//前置请求封装数据
|
||||
((IOperateDTO) object).setOperateOrgId(userDto.getOrgId());
|
||||
((IOperateDTO) object).setOperateUserId(userDto.getId());
|
||||
}
|
||||
argList.add(object);
|
||||
}
|
||||
operationLog.setRequestBody(parseString(argList));
|
||||
try {
|
||||
result = joinPoint.proceed();
|
||||
if (result instanceof AlitaResult) {
|
||||
((AlitaResult) result).setRequestId(traceId);
|
||||
}
|
||||
return result;
|
||||
} catch (Throwable t) {
|
||||
currentThrowable = t;
|
||||
throw t;
|
||||
} finally {
|
||||
stopWatch.stop();
|
||||
try {
|
||||
boolean canLog = canLog(requestUri);
|
||||
if (canLog && !requestUri.contains(ANON_LOGIN_URI)) {
|
||||
operationLog.setUseTime(stopWatch.getTotalTimeMillis());
|
||||
if (currentThrowable != null) {
|
||||
operationLog.setResult("调用异常,异常信息:" + currentThrowable);
|
||||
} else {
|
||||
if (result != null && result.getClass().equals(byte[].class)) {
|
||||
operationLog.setResult("byte数组。。。");
|
||||
} else if (result instanceof AlitaResult){
|
||||
if (((AlitaResult<?>) result).getData() instanceof Page){
|
||||
operationLog.setResult("分页结果。。。");
|
||||
} else if (((AlitaResult<?>) result).getData() instanceof List){
|
||||
operationLog.setResult("List结果。。。");
|
||||
} else {
|
||||
operationLog.setResult(parseString(result));
|
||||
}
|
||||
} else {
|
||||
operationLog.setResult(parseString(result));
|
||||
}
|
||||
}
|
||||
LogUtils.log(operationLog);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info("controller log:{}, error:{}", operationLog, e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private String parseString(Object object) {
|
||||
try {
|
||||
return JsonUtils.toJsonString(object, false);
|
||||
} catch (Exception e) {
|
||||
return getStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常的堆栈信息
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
public static String getStackTrace(Throwable t) {
|
||||
StringWriter sw = new StringWriter();
|
||||
|
||||
try (PrintWriter pw = new PrintWriter(sw)) {
|
||||
t.printStackTrace(pw);
|
||||
return sw.toString();
|
||||
} catch (Exception e) {
|
||||
return t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canLog(String requestUri) {
|
||||
String notLogs = securityProperties.getNotLogs();
|
||||
if (StringUtils.isBlank(notLogs)) {
|
||||
return true;
|
||||
}
|
||||
for (String notlog : notLogs.split(CommonConstants.COMMA)) {
|
||||
if (requestUri.contains(notlog)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.senmesh.common.log.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 异常日志
|
||||
*
|
||||
* @author sr on 2020-12-09
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ExceptionLogDTO implements Serializable{
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = -3544344864494146694L;
|
||||
|
||||
private String id;
|
||||
|
||||
/** 错误号 **/
|
||||
private String errNo;
|
||||
|
||||
/** 年月 **/
|
||||
private String ym;
|
||||
|
||||
/** 序号 **/
|
||||
private Long seqId;
|
||||
|
||||
/** 链路id **/
|
||||
private String traceId;
|
||||
|
||||
/** 创建时间 **/
|
||||
@JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
|
||||
/** 错误信息 **/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* ym + seqId(5位补零)
|
||||
* @return
|
||||
* @author sr on 2020-12-09
|
||||
*/
|
||||
public String getErrNo() {
|
||||
if (errNo == null) {
|
||||
if (seqId != null) {
|
||||
this.errNo = ym + StringUtils.leftPad(String.valueOf(this.seqId), 5, "0");
|
||||
} else if (traceId != null) {
|
||||
this.errNo = traceId;
|
||||
}
|
||||
|
||||
}
|
||||
return errNo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.senmesh.common.log.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 操作日志
|
||||
* @author sr on 2020-10-26
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class OperationLogDTO implements Serializable {
|
||||
|
||||
/** **/
|
||||
private static final long serialVersionUID = -6817430348970835175L;
|
||||
|
||||
/** 链路id **/
|
||||
private String traceId;
|
||||
|
||||
/** spanid **/
|
||||
private String spanId;
|
||||
|
||||
/** 上一级链路id **/
|
||||
private String parentId;
|
||||
|
||||
/** 系统名称 **/
|
||||
private String systemName;
|
||||
|
||||
/** ip **/
|
||||
private String ip;
|
||||
|
||||
/** 请求地址 **/
|
||||
private String requestUri;
|
||||
|
||||
/** 请求类型 **/
|
||||
private String requestMethod;
|
||||
|
||||
/** 请求参数 **/
|
||||
private Object requestBody;
|
||||
|
||||
/** 请求时间 **/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date requestTime;
|
||||
|
||||
/** 请求耗时(ms) **/
|
||||
private Long useTime;
|
||||
|
||||
/** 请求结果 **/
|
||||
private Object result;
|
||||
|
||||
/** 当前登录用户信息:属性见上下文用户对象 **/
|
||||
private Object loginUser;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.senmesh.common.log.provider;
|
||||
|
||||
import com.senmesh.common.log.ILogProvider;
|
||||
import com.senmesh.common.util.JsonUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author sr on 2021-01-21
|
||||
*/
|
||||
@Slf4j
|
||||
public class Slf4jLogProvider implements ILogProvider {
|
||||
|
||||
@Override
|
||||
public void log(Object object) {
|
||||
try {
|
||||
log.info("class:{}, obj:{}", object.getClass(), JsonUtils.toJsonString(object));
|
||||
} catch (Exception e) {
|
||||
log.error("cant log:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.senmesh.common.log.util;
|
||||
|
||||
import com.senmesh.common.log.ILogProvider;
|
||||
import com.senmesh.common.log.provider.Slf4jLogProvider;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-10-29
|
||||
*/
|
||||
public class LogUtils {
|
||||
|
||||
private static ILogProvider logProvider = new Slf4jLogProvider();
|
||||
|
||||
public void setLogProvider(ILogProvider logProvider) {
|
||||
LogUtils.logProvider = logProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录日志
|
||||
*
|
||||
* @param object
|
||||
* @author sr on 2020-10-29
|
||||
*/
|
||||
public static void log(Object object) {
|
||||
logProvider.log(object);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.senmesh.common.sensitive;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* 脱敏基本工具类
|
||||
*
|
||||
* @author qpc
|
||||
*/
|
||||
public class SensitiveInfoUtils {
|
||||
|
||||
/**
|
||||
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
|
||||
*/
|
||||
public static String chineseName(final String fullName) {
|
||||
if (StringUtils.isBlank(fullName)) {
|
||||
return "";
|
||||
}
|
||||
final String name = StringUtils.left(fullName, 1);
|
||||
return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
|
||||
}
|
||||
|
||||
/**
|
||||
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
|
||||
*/
|
||||
public static String chineseName(final String familyName, final String givenName) {
|
||||
if (StringUtils.isBlank(familyName) || StringUtils.isBlank(givenName)) {
|
||||
return "";
|
||||
}
|
||||
return chineseName(familyName + givenName);
|
||||
}
|
||||
|
||||
/**
|
||||
* [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
|
||||
*/
|
||||
public static String idCardNum(final String id) {
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return "";
|
||||
}
|
||||
return StringUtils.left(id, 3).concat(StringUtils
|
||||
.removeStart(StringUtils.leftPad(StringUtils.right(id, 3), StringUtils.length(id), "*"),
|
||||
"***"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串中某一段替换成*
|
||||
* <p>
|
||||
* 比如 呵呵哈哈哈或或 , start:0 end:1 则变成 *呵哈哈哈或或
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
public static String exchangeHashKey(String origin, int start, int end) {
|
||||
if (org.apache.commons.lang3.StringUtils.isBlank(origin)) {
|
||||
return null;
|
||||
}
|
||||
char[] chars = origin.toCharArray();
|
||||
int startIndex = start - 1;
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
if (i >= startIndex && i < end) {
|
||||
chars[i] = '*';
|
||||
}
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【身份证号】前三位 和后三位
|
||||
*
|
||||
* @param front
|
||||
* @param end
|
||||
*/
|
||||
public static String idCardNum(String idCardNum, int front, int end) {
|
||||
//身份证不能为空
|
||||
if (StringUtils.isEmpty(idCardNum)) {
|
||||
return "";
|
||||
}
|
||||
//需要截取的长度不能大于身份证号长度
|
||||
if ((front + end) > idCardNum.length()) {
|
||||
return "";
|
||||
}
|
||||
//需要截取的不能小于0
|
||||
if (front < 0 || end < 0) {
|
||||
return "";
|
||||
}
|
||||
//计算*的数量
|
||||
int asteriskCount = idCardNum.length() - (front + end);
|
||||
StringBuffer asteriskStr = new StringBuffer();
|
||||
for (int i = 0; i < asteriskCount; i++) {
|
||||
asteriskStr.append("*");
|
||||
}
|
||||
String regex = "(\\w{" + front + "})(\\w+)(\\w{" + end + "})";
|
||||
return idCardNum.replaceAll(regex, "$1" + asteriskStr + "$3");
|
||||
}
|
||||
|
||||
/**
|
||||
* 【固定电话 前四位,后两位
|
||||
*
|
||||
* @param num
|
||||
* @return
|
||||
*/
|
||||
public static String fixedPhone(String num) {
|
||||
if (StringUtils.isBlank(num)) {
|
||||
return "";
|
||||
}
|
||||
return StringUtils.left(num, 4).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 2), StringUtils.length(num), "*"), "****"));
|
||||
}
|
||||
|
||||
/**
|
||||
* [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
|
||||
*/
|
||||
public static String mobilePhone2(final String num) {
|
||||
if (StringUtils.isBlank(num)) {
|
||||
return "";
|
||||
}
|
||||
return StringUtils.left(num, 3).concat(StringUtils
|
||||
.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"),
|
||||
"***"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 【手机号码】前三位,后两位,其他隐藏,比如135******10
|
||||
*
|
||||
* @param num
|
||||
* @return
|
||||
*/
|
||||
public static String mobilePhone(String num) {
|
||||
if (StringUtils.isBlank(num)) {
|
||||
return "";
|
||||
}
|
||||
return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 2), StringUtils.length(num), "*"), "***"));
|
||||
}
|
||||
|
||||
/**
|
||||
* [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
|
||||
*
|
||||
* @param sensitiveSize 敏感信息长度
|
||||
*/
|
||||
public static String address(final String address, final int sensitiveSize) {
|
||||
if (StringUtils.isBlank(address)) {
|
||||
return "";
|
||||
}
|
||||
if (address.length() <= sensitiveSize) {
|
||||
return address;
|
||||
}
|
||||
final int length = StringUtils.length(address);
|
||||
return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
|
||||
}
|
||||
|
||||
/**
|
||||
* [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
|
||||
*/
|
||||
public static String email(final String email) {
|
||||
if (StringUtils.isBlank(email)) {
|
||||
return "";
|
||||
}
|
||||
final int index = StringUtils.indexOf(email, "@");
|
||||
if (index <= 1) {
|
||||
return email;
|
||||
} else {
|
||||
return StringUtils.rightPad(StringUtils.left(email, 1), index, "*")
|
||||
.concat(StringUtils.mid(email, index, StringUtils.length(email)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
|
||||
*/
|
||||
public static String bankCard(final String cardNum) {
|
||||
if (StringUtils.isBlank(cardNum)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return StringUtils.left(cardNum, 6).concat(StringUtils
|
||||
.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"),
|
||||
"***"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
|
||||
*/
|
||||
public static String shopsCode(final String code) {
|
||||
if (StringUtils.isBlank(code)) {
|
||||
return "";
|
||||
}
|
||||
return StringUtils.rightPad(StringUtils.left(code, 2), StringUtils.length(code), "*");
|
||||
}
|
||||
|
||||
/**
|
||||
* 【密码】密码的全部字符都用*代替,比如:******
|
||||
*
|
||||
* @param password
|
||||
* @return
|
||||
*/
|
||||
public static String password(String password) {
|
||||
if (StringUtils.isBlank(password)) {
|
||||
return "";
|
||||
}
|
||||
String pwd = StringUtils.left(password, 0);
|
||||
return StringUtils.rightPad(pwd, StringUtils.length(password), "*");
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
String bankCard = "6214851215669289";
|
||||
System.out.println(bankCard(bankCard));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.senmesh.common.sensitive;
|
||||
|
||||
|
||||
/**
|
||||
* 脱敏类型枚举
|
||||
*
|
||||
* @author qpc
|
||||
*/
|
||||
public enum SensitiveType {
|
||||
/**
|
||||
* 中文名
|
||||
*/
|
||||
CHINESE_NAME,
|
||||
/**
|
||||
* 身份证号
|
||||
*/
|
||||
ID_CARD,
|
||||
/**
|
||||
* 座机号
|
||||
*/
|
||||
FIXED_PHONE,
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
MOBILE_PHONE,
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
PASSWORD,
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
ADDRESS,
|
||||
/**
|
||||
* 电子邮件
|
||||
*/
|
||||
EMAIL,
|
||||
/**
|
||||
* 银行卡
|
||||
*/
|
||||
BANK_CARD,
|
||||
/**
|
||||
* 公司开户银行联号
|
||||
*/
|
||||
SHOPS_CODE,
|
||||
/**
|
||||
* 自定义正则过滤
|
||||
*/
|
||||
RGE_EXP,
|
||||
|
||||
/**
|
||||
* 空值
|
||||
*/
|
||||
NULL,
|
||||
/**
|
||||
* 自定义处理
|
||||
*/
|
||||
SELF,
|
||||
|
||||
/**
|
||||
* 公司名称
|
||||
*/
|
||||
COMPANY_NAME
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.senmesh.common.sensitive.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.senmesh.common.sensitive.SensitiveType;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* 脱敏AOP注解
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@JacksonAnnotationsInside
|
||||
@JsonSerialize(using = SensitiveInfoSerialize.class)
|
||||
public @interface SensitiveInfo {
|
||||
SensitiveType value() default SensitiveType.RGE_EXP;
|
||||
|
||||
/**
|
||||
* 前缀(保留多少位)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int prefix() default -1;
|
||||
|
||||
|
||||
/**
|
||||
* 后缀(保留多少位)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int suffix() default -1;
|
||||
|
||||
/**
|
||||
* 自定义正则匹配规则
|
||||
*/
|
||||
String regExp() default "";
|
||||
|
||||
/**
|
||||
* 正则替换字符
|
||||
*/
|
||||
String regStr() default "*";
|
||||
|
||||
/**
|
||||
* 自定义处理标识
|
||||
*/
|
||||
String tag() default "self";
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.senmesh.common.sensitive.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
import com.senmesh.common.sensitive.SensitiveType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/**
|
||||
* jackson的脱敏实现
|
||||
*
|
||||
* @author doobo
|
||||
*/
|
||||
@Slf4j
|
||||
public class SensitiveInfoObjectSerialize extends JsonSerializer<Object> implements ContextualSerializer {
|
||||
|
||||
private SensitiveType type;
|
||||
|
||||
public SensitiveInfoObjectSerialize() {
|
||||
}
|
||||
|
||||
public SensitiveInfoObjectSerialize(final SensitiveInfo sensitiveInfo) {
|
||||
this.type = sensitiveInfo.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final Object s, final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
|
||||
try {
|
||||
if (this.type == SensitiveType.NULL && !isBaseType(s.getClass())) {
|
||||
jsonGenerator.writeObject(null);
|
||||
return;
|
||||
}
|
||||
jsonGenerator.writeObject(s);
|
||||
} catch (Exception e) {
|
||||
log.error("脱敏数据处理异常", e);
|
||||
jsonGenerator.writeObject(s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(final SerializerProvider serializerProvider,
|
||||
final BeanProperty beanProperty) throws JsonMappingException {
|
||||
// 为空直接跳过
|
||||
if (beanProperty != null) {
|
||||
// 非 String 类直接跳过
|
||||
if (Objects.equals(beanProperty.getType().getRawClass(), Object.class)) {
|
||||
SensitiveInfo sensitiveInfo = beanProperty.getAnnotation(SensitiveInfo.class);
|
||||
if (sensitiveInfo == null) {
|
||||
sensitiveInfo = beanProperty.getContextAnnotation(SensitiveInfo.class);
|
||||
}
|
||||
if (sensitiveInfo != null && sensitiveInfo.value() == SensitiveType.NULL) {
|
||||
return new SensitiveInfoObjectSerialize(sensitiveInfo);
|
||||
}
|
||||
}
|
||||
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
|
||||
}
|
||||
return serializerProvider.findNullValueSerializer(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断object是否为基本类型
|
||||
*
|
||||
* @param cls
|
||||
*/
|
||||
public boolean isBaseType(Class<?> cls) {
|
||||
return cls.equals(int.class) ||
|
||||
cls.equals(byte.class) ||
|
||||
cls.equals(long.class) ||
|
||||
cls.equals(double.class) ||
|
||||
cls.equals(float.class) ||
|
||||
cls.equals(char.class) ||
|
||||
cls.equals(short.class) ||
|
||||
cls.equals(boolean.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.senmesh.common.sensitive.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
import com.senmesh.common.sensitive.SensitiveInfoUtils;
|
||||
import com.senmesh.common.sensitive.SensitiveType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* jackson的脱敏实现
|
||||
*
|
||||
* @author doobo
|
||||
*/
|
||||
@Slf4j
|
||||
public class SensitiveInfoSerialize extends JsonSerializer<String> implements ContextualSerializer {
|
||||
|
||||
private SensitiveType type;
|
||||
|
||||
private SensitiveInfo sensitiveInfo;
|
||||
|
||||
public SensitiveInfoSerialize() {
|
||||
}
|
||||
|
||||
public SensitiveInfoSerialize(final SensitiveInfo sensitiveInfo) {
|
||||
this.type = sensitiveInfo.value();
|
||||
this.sensitiveInfo = sensitiveInfo;
|
||||
}
|
||||
|
||||
private int getOrDefault(int value, int defaultValue) {
|
||||
return value < 0 ? defaultValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final String s, final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
|
||||
try {
|
||||
//有正则优先使用正则
|
||||
if (StringUtils.isNotBlank(sensitiveInfo.regExp())) {
|
||||
jsonGenerator.writeString(s.replaceAll(sensitiveInfo.regExp(), sensitiveInfo.regStr()));
|
||||
return;
|
||||
}
|
||||
switch (this.type) {
|
||||
case CHINESE_NAME: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.chineseName(s));
|
||||
break;
|
||||
}
|
||||
case ID_CARD:
|
||||
case MOBILE_PHONE: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.idCardNum(s
|
||||
, getOrDefault(sensitiveInfo.prefix(), 3), getOrDefault(sensitiveInfo.suffix(), 4)));
|
||||
break;
|
||||
}
|
||||
case FIXED_PHONE: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.fixedPhone(s));
|
||||
break;
|
||||
}
|
||||
case PASSWORD: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.password(s));
|
||||
break;
|
||||
}
|
||||
case ADDRESS: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.address(s, getOrDefault(sensitiveInfo.prefix(), 8)));
|
||||
break;
|
||||
}
|
||||
case EMAIL: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.email(s));
|
||||
break;
|
||||
}
|
||||
case BANK_CARD: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.bankCard(s));
|
||||
break;
|
||||
}
|
||||
case SHOPS_CODE: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.shopsCode(s));
|
||||
break;
|
||||
}
|
||||
case NULL: {
|
||||
jsonGenerator.writeString((String) null);
|
||||
break;
|
||||
}
|
||||
case COMPANY_NAME: {
|
||||
jsonGenerator.writeString(SensitiveInfoUtils.exchangeHashKey(s
|
||||
, getOrDefault(sensitiveInfo.prefix(), 0), getOrDefault(sensitiveInfo.suffix(), 3)));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
jsonGenerator.writeString(s);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("脱敏数据处理异常", e);
|
||||
jsonGenerator.writeString(s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(final SerializerProvider serializerProvider,
|
||||
final BeanProperty beanProperty) throws JsonMappingException {
|
||||
// 为空直接跳过
|
||||
if (beanProperty != null) {
|
||||
SensitiveInfo sensitiveInfo = beanProperty.getAnnotation(SensitiveInfo.class);
|
||||
// 非 String 类直接跳过
|
||||
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
|
||||
if (sensitiveInfo != null) {
|
||||
// 如果能得到注解,就将注解的 value 传入 SensitiveInfoSerialize
|
||||
return new SensitiveInfoSerialize(sensitiveInfo);
|
||||
}
|
||||
}
|
||||
if (sensitiveInfo != null && sensitiveInfo.value() == SensitiveType.NULL) {
|
||||
return new SensitiveInfoObjectSerialize(sensitiveInfo);
|
||||
}
|
||||
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
|
||||
}
|
||||
return serializerProvider.findNullValueSerializer(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.senmesh.common.sensitive.service;
|
||||
|
||||
import com.senmesh.common.sensitive.SensitiveInfoUtils;
|
||||
|
||||
/**
|
||||
* 默认脱敏工具抽象类
|
||||
* 使用时,可继承并重写(覆盖)相关方法
|
||||
*/
|
||||
public abstract class AbstractSensitiveService implements SensitiveService {
|
||||
@Override
|
||||
public String chineseName(String fullName) {
|
||||
return SensitiveInfoUtils.chineseName(fullName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chineseName(String familyName, String givenName) {
|
||||
return SensitiveInfoUtils.chineseName(familyName, givenName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idCardNum(String id) {
|
||||
return SensitiveInfoUtils.idCardNum(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String idCardNum(String idCardNum, int front, int end) {
|
||||
return SensitiveInfoUtils.idCardNum(idCardNum, front, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fixedPhone(String num) {
|
||||
return SensitiveInfoUtils.fixedPhone(num);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mobilePhone2(String num) {
|
||||
return SensitiveInfoUtils.mobilePhone2(num);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mobilePhone(String num) {
|
||||
return SensitiveInfoUtils.mobilePhone(num);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String address(String address, int sensitiveSize) {
|
||||
return SensitiveInfoUtils.address(address, sensitiveSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String email(String email) {
|
||||
return SensitiveInfoUtils.email(email);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String bankCard(String cardNum) {
|
||||
return SensitiveInfoUtils.bankCard(cardNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shopsCode(String code) {
|
||||
return SensitiveInfoUtils.shopsCode(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String password(String password) {
|
||||
return SensitiveInfoUtils.password(password);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.senmesh.common.sensitive.service;
|
||||
|
||||
/**
|
||||
* 脱敏服务
|
||||
*/
|
||||
public interface SensitiveService {
|
||||
|
||||
/**
|
||||
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
|
||||
*/
|
||||
String chineseName(final String fullName);
|
||||
|
||||
/**
|
||||
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
|
||||
*/
|
||||
String chineseName(final String familyName, final String givenName);
|
||||
|
||||
/**
|
||||
* [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
|
||||
*/
|
||||
String idCardNum(final String id);
|
||||
|
||||
/**
|
||||
* 【身份证号】前三位 和后三位
|
||||
*/
|
||||
String idCardNum(String idCardNum, int front, int end);
|
||||
|
||||
/**
|
||||
* 【固定电话 前四位,后两位
|
||||
*/
|
||||
String fixedPhone(String num);
|
||||
|
||||
/**
|
||||
* [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
|
||||
*/
|
||||
String mobilePhone2(final String num);
|
||||
|
||||
/**
|
||||
* 【手机号码】前三位,后两位,其他隐藏,比如135******10
|
||||
*/
|
||||
String mobilePhone(String num);
|
||||
|
||||
/**
|
||||
* [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
|
||||
*
|
||||
* @param sensitiveSize 敏感信息长度
|
||||
*/
|
||||
String address(final String address, final int sensitiveSize);
|
||||
|
||||
/**
|
||||
* [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
|
||||
*/
|
||||
String email(final String email);
|
||||
|
||||
/**
|
||||
* [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
|
||||
*/
|
||||
String bankCard(final String cardNum);
|
||||
|
||||
/**
|
||||
* [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
|
||||
*/
|
||||
String shopsCode(final String code);
|
||||
|
||||
/**
|
||||
* 【密码】密码的全部字符都用*代替,比如:******
|
||||
*/
|
||||
String password(String password);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.senmesh.common.sensitive.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 脱敏服务工具类
|
||||
*/
|
||||
@Component
|
||||
public class SensitiveServiceUtils {
|
||||
|
||||
/**
|
||||
* 脱敏工具
|
||||
*/
|
||||
private static SensitiveService sensitiveService;
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setSensitiveService(SensitiveService sensitiveService) {
|
||||
SensitiveServiceUtils.sensitiveService = sensitiveService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取脱敏工具类
|
||||
*/
|
||||
public static SensitiveService getSensitiveService() {
|
||||
if (SensitiveServiceUtils.sensitiveService == null) {
|
||||
SensitiveServiceUtils.sensitiveService = new AbstractSensitiveService() {
|
||||
};
|
||||
}
|
||||
return SensitiveServiceUtils.sensitiveService;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.senmesh.common.service;
|
||||
|
||||
|
||||
public interface IMockProvider{
|
||||
|
||||
void mockPlatUser() ;
|
||||
|
||||
void mockUser(Long userId, Long orgId);
|
||||
|
||||
void clearUser();
|
||||
|
||||
|
||||
|
||||
|
||||
public class DefaultMockProvider implements IMockProvider{
|
||||
|
||||
@Override
|
||||
public void mockPlatUser() {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mockUser(Long userId, Long orgId) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearUser() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.senmesh.common.trace;
|
||||
|
||||
import com.senmesh.common.util.RandomUtils;
|
||||
|
||||
public class DefaultTracer implements ITracer{
|
||||
|
||||
@Override
|
||||
public String getTraceId() {
|
||||
return RandomUtils.generateUUID();
|
||||
}
|
||||
|
||||
}
|
||||
15
backend/src/main/java/com/senmesh/common/trace/ITracer.java
Normal file
15
backend/src/main/java/com/senmesh/common/trace/ITracer.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.senmesh.common.trace;
|
||||
|
||||
public interface ITracer {
|
||||
|
||||
String getTraceId();
|
||||
|
||||
default String getSpanId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default String getParentId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
50
backend/src/main/java/com/senmesh/common/util/AESUtils.java
Normal file
50
backend/src/main/java/com/senmesh/common/util/AESUtils.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.senmesh.common.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Security;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
|
||||
/**
|
||||
* @author sr on 2024-02-22
|
||||
*/
|
||||
public class AESUtils {
|
||||
|
||||
public static final String TRANSFORM_ECB_5 = "AES/ECB/PKCS5Padding";
|
||||
public static final String TRANSFORM_ECB_7 = "AES/ECB/PKCS7Padding";
|
||||
public static final String TRANSFORM_ECB = "AES/ECB/NoPadding";
|
||||
|
||||
static {
|
||||
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public static Cipher getCipher(String transform) throws Exception {
|
||||
if (TRANSFORM_ECB_7.equals(transform)) {
|
||||
return Cipher.getInstance(transform, "BC");
|
||||
} else {
|
||||
return Cipher.getInstance(transform);
|
||||
}
|
||||
}
|
||||
|
||||
public static String encrypt(String input, String key, String transform) throws Exception {
|
||||
Cipher cipher = getCipher(transform);
|
||||
SecretKey keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
|
||||
return Base64Utils.encodeToString(cipher.doFinal(input.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
public static String decrypt(String input, String key, String transform)
|
||||
throws Exception {
|
||||
Cipher cipher = getCipher(transform);
|
||||
SecretKey keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec);
|
||||
return new String(cipher.doFinal(Base64Utils.decode(input.getBytes(StandardCharsets.UTF_8))),
|
||||
StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.senmesh.common.util;
|
||||
|
||||
import com.senmesh.common.dto.BaseDTO;
|
||||
import com.senmesh.common.entity.BaseEntity;
|
||||
|
||||
/**
|
||||
* 基础dto工具类
|
||||
* @author sr on 2019-12-07
|
||||
*/
|
||||
public class BaseDtoUtils {
|
||||
|
||||
/**
|
||||
* 创建回写dto数据
|
||||
* @param entity
|
||||
* @param dto
|
||||
* @author sr on 2019-12-07
|
||||
*/
|
||||
public static void createCopy(BaseEntity entity, BaseDTO dto) {
|
||||
dto.setId(entity.getId());
|
||||
dto.setDeleted(entity.getDeleted());
|
||||
dto.setCreateTime(entity.getCreateTime());
|
||||
dto.setCreateBy(entity.getCreateBy());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改回写dto数据
|
||||
* @param entity
|
||||
* @param dto
|
||||
* @author sr on 2019-12-07
|
||||
*/
|
||||
public static void updateCopy(BaseEntity entity, BaseDTO dto) {
|
||||
dto.setUpdateTime(entity.getUpdateTime());
|
||||
dto.setUpdateBy(entity.getUpdateBy());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.senmesh.common.util;
|
||||
|
||||
import com.senmesh.common.exception.util.Assert;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
@Slf4j
|
||||
public class BigDecimalUtils {
|
||||
|
||||
/**
|
||||
* 保留两位小数
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal keepTwo(BigDecimal amount) {
|
||||
if (amount == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return amount.setScale(2, RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转BigDecimal(转换失败,默认值null)
|
||||
* @param amountStr
|
||||
* @return
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
public static BigDecimal parse(String amountStr) {
|
||||
return parse(amountStr, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转BigDecimal
|
||||
*
|
||||
* @param amountStr
|
||||
* @param defaultValue 转换失败默认值
|
||||
* @return
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
public static BigDecimal parse(String amountStr, BigDecimal defaultValue) {
|
||||
if (StringUtils.isBlank(amountStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(amountStr);
|
||||
} catch (NumberFormatException e) {
|
||||
log.info("bigDecimal parse error", e);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 见N+1进N 例如见厘进分
|
||||
* 0.0321->0.04
|
||||
* 0.0310->0.04
|
||||
* 0.0301->0.03
|
||||
* -0.991->-0.990
|
||||
* @param decimal
|
||||
* @return
|
||||
*/
|
||||
public static BigDecimal rounding(BigDecimal decimal, int n){
|
||||
Assert.isTrue(n > 0, "小数位必须大于0");
|
||||
Assert.notNull(decimal, "金额不能为空");
|
||||
return decimal.setScale(n + 1, RoundingMode.DOWN).setScale(n, RoundingMode.UP);
|
||||
}
|
||||
|
||||
}
|
||||
112
backend/src/main/java/com/senmesh/common/util/CookieUtils.java
Normal file
112
backend/src/main/java/com/senmesh/common/util/CookieUtils.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package com.senmesh.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.senmesh.common.constant.CommonConstants;
|
||||
import com.senmesh.common.constant.CookieConstants;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author Jerry.li
|
||||
* @description Cookie工具类
|
||||
* @date 2020/4/2
|
||||
*/
|
||||
@Slf4j
|
||||
public class CookieUtils {
|
||||
|
||||
/**
|
||||
* 获取登录token存储cookie的key
|
||||
*
|
||||
* @return
|
||||
* @author sr on 2020-06-11
|
||||
*/
|
||||
public static String getLoginTokenCookieKey() {
|
||||
String profile = EnvironmentUtils.getProperty(CommonConstants.PROFILE_KEY);
|
||||
return CookieConstants.LOGIN_COOKIE_NAME_PREFIX
|
||||
+ (Strings.isNullOrEmpty(profile) ? null : profile.substring(0, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定key的值
|
||||
*
|
||||
* @param key
|
||||
* @param cookies
|
||||
* @return
|
||||
*/
|
||||
public static String getCookieValue(String key, Cookie[] cookies) {
|
||||
String value = null;
|
||||
if (null != cookies && !StringUtils.isEmpty(key)) {
|
||||
for (Cookie cookie : cookies) {
|
||||
String cookieName = cookie.getName();
|
||||
if (key.equals(cookieName)) {
|
||||
value = cookie.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Cookie
|
||||
*
|
||||
* @param name
|
||||
* @param value
|
||||
* @param domain
|
||||
* @param path
|
||||
* @param maxAge 单位:秒 -1: 代表关闭浏览器时就清除cookie 0:创建完cookie,使用后马上就删除
|
||||
* @return
|
||||
*/
|
||||
public static Cookie createCookie(String name, String value, String domain, String path,
|
||||
int maxAge) {
|
||||
Cookie cookie = new Cookie(name, value);
|
||||
try {
|
||||
if (StringUtils.isNotEmpty(domain)) {
|
||||
cookie.setDomain(domain);
|
||||
}
|
||||
path = StringUtils.isEmpty(path) ? "/" : path;
|
||||
cookie.setPath(path);
|
||||
cookie.setHttpOnly(false);
|
||||
cookie.setMaxAge(maxAge);
|
||||
cookie.setSecure(true);
|
||||
return cookie;
|
||||
} catch (Exception e) {
|
||||
log.error("------创建Cookie时,系统异常:{0}", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除Cookie
|
||||
*
|
||||
* @param name 需要移除的cookie名称
|
||||
* @param domain 指定某个域名,不指定就是清空当前tomcat应用下的所有cookie
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
public static void removeCookie(String name, String domain, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (!StringUtils.isEmpty(name) && null != cookies) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (name.equals(cookie.getName())) {
|
||||
Cookie tempCookie = new Cookie(name, "");
|
||||
tempCookie.setMaxAge(0);
|
||||
tempCookie.setPath("/");
|
||||
tempCookie.setSecure(true);
|
||||
if (!StringUtils.isEmpty(domain)) {
|
||||
tempCookie.setDomain(domain);
|
||||
}
|
||||
response.addCookie(tempCookie);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
335
backend/src/main/java/com/senmesh/common/util/DateUtils.java
Normal file
335
backend/src/main/java/com/senmesh/common/util/DateUtils.java
Normal file
@@ -0,0 +1,335 @@
|
||||
package com.senmesh.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.bytebuddy.dynamic.scaffold.MethodGraph.Linked;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 日期工具类
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
@Slf4j
|
||||
public class DateUtils {
|
||||
|
||||
public static final String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
public static final String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static final String YYYY = "yyyy";
|
||||
public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final FastDateFormat YMDHMS_FORMAT =
|
||||
FastDateFormat.getInstance(DATETIME_PATTERN);
|
||||
|
||||
public static final String DATETIME_PATTERN_NO_SS = "yyyy-MM-dd HH:mm";
|
||||
public static final FastDateFormat YMDHM_FORMAT =
|
||||
FastDateFormat.getInstance(DATETIME_PATTERN_NO_SS);
|
||||
|
||||
/** yyyy-MM-dd **/
|
||||
public static final FastDateFormat YMD_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd");
|
||||
/** yyMMdd **/
|
||||
public static final FastDateFormat YMD_FORMAT2 = FastDateFormat.getInstance("yyyyMMdd");
|
||||
|
||||
/** yyMM **/
|
||||
public static final DateTimeFormatter YM_FORMAT = DateTimeFormatter.ofPattern("yyMM");
|
||||
/** yyyyMM **/
|
||||
public static final DateTimeFormatter YM_FORMAT2 = DateTimeFormatter.ofPattern("yyyyMM");
|
||||
/** yyyy-MM-dd HH:mm:ss **/
|
||||
public static final DateTimeFormatter DATETIME_FORMAT =
|
||||
DateTimeFormatter.ofPattern(DATETIME_PATTERN);
|
||||
|
||||
/**
|
||||
* @param date
|
||||
* @return yyyy-MM-dd HH:mm:ss
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
public static String format(Date date) {
|
||||
return YMDHMS_FORMAT.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param date
|
||||
* @return yyyy-MM-dd HH:mm
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
public static String formatSplitSecond(Date date) {
|
||||
return YMDHM_FORMAT.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dateStr yyyy-MM-dd HH:mm:ss
|
||||
* @return
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
public static Date parse(String dateStr) {
|
||||
if (StringUtils.isBlank(dateStr)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return YMDHMS_FORMAT.parse(dateStr);
|
||||
} catch (ParseException e) {
|
||||
log.error("parse date error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param date
|
||||
* @return yyyy-MM-dd
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
public static String formatToDay(Date date) {
|
||||
return YMD_FORMAT.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dateStr yyyy-MM-dd
|
||||
* @return
|
||||
* @author sr on 2020-08-14
|
||||
*/
|
||||
public static Date parseToDay(String dateStr) {
|
||||
if (StringUtils.isBlank(dateStr)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return YMD_FORMAT.parse(dateStr);
|
||||
} catch (ParseException e) {
|
||||
log.error("parse date error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param date
|
||||
* @return yyMMdd
|
||||
* @author sr on 2022-09-22
|
||||
*/
|
||||
public static String formatToDay2(Date date) {
|
||||
return YMD_FORMAT2.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dateStr yyMMdd
|
||||
* @return
|
||||
* @author sr on 2022-09-22
|
||||
*/
|
||||
public static Date parseToDay2(String dateStr) {
|
||||
if (StringUtils.isBlank(dateStr)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return YMD_FORMAT2.parse(dateStr);
|
||||
} catch (ParseException e) {
|
||||
log.error("parse date error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前年月(yyMM)
|
||||
* @return
|
||||
* @author sr on 2020-12-09
|
||||
*/
|
||||
public static String getCurrentYm() {
|
||||
LocalDate localDate = LocalDate.now();
|
||||
return localDate.format(DateUtils.YM_FORMAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前年月日(yyyy-MM-dd)
|
||||
* @return yyyy-MM-dd
|
||||
* @author sr on 2023-12-07
|
||||
*/
|
||||
public static String getCurrentYmd() {
|
||||
return formatToDay(new Date());
|
||||
}
|
||||
|
||||
public static LocalDate getLocalDate(Date date) {
|
||||
if (date == null) {
|
||||
return LocalDate.now();
|
||||
} else {
|
||||
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
}
|
||||
}
|
||||
|
||||
public static LocalDateTime getLocalDateTime(Date date) {
|
||||
if (date == null) {
|
||||
return LocalDateTime.now();
|
||||
} else {
|
||||
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
public static Date toDate(LocalDateTime date) {
|
||||
ZonedDateTime startZdt = date.atZone(ZoneId.systemDefault());
|
||||
return Date.from(startZdt.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期开始时间字符串(yyyy-MM-dd 00:00:00)
|
||||
* @param date 为空取当前时间
|
||||
* @return
|
||||
* @author sr on 2021-04-19
|
||||
*/
|
||||
public static String getStartDateTimeStr(Date date) {
|
||||
LocalDateTime localDateTime = LocalDateTime.of(getLocalDate(date), LocalTime.MIN);
|
||||
return DATETIME_FORMAT.format(localDateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期开始时间
|
||||
* @return
|
||||
* @author sr on 2021-04-19
|
||||
*/
|
||||
public static Date getCurrentStartDateTime() {
|
||||
return getStartDateTime(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期开始时间
|
||||
* @param date 为空取当前时间
|
||||
* @return
|
||||
* @author sr on 2021-04-19
|
||||
*/
|
||||
public static Date getStartDateTime(Date date) {
|
||||
return toDate(LocalDateTime.of(getLocalDate(date), LocalTime.MIN));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期开始时间字符串(yyyy-MM-dd 23:59:59)
|
||||
* @param date 为空取当前时间
|
||||
* @return
|
||||
* @author sr on 2021-04-19
|
||||
*/
|
||||
public static String getEndDateTimeStr(Date date) {
|
||||
LocalDateTime localDateTime = LocalDateTime.of(getLocalDate(date), LocalTime.MAX);
|
||||
return DATETIME_FORMAT.format(localDateTime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取当前日期结束时间
|
||||
* @return
|
||||
* @author sr on 2021-04-19
|
||||
*/
|
||||
public static Date getCurrentEndDateTime() {
|
||||
return getEndDateTime(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期结束时间
|
||||
* @param date 为空取当前时间
|
||||
* @return
|
||||
* @author sr on 2021-04-19
|
||||
*/
|
||||
public static Date getEndDateTime(Date date) {
|
||||
return toDate(LocalDateTime.of(getLocalDate(date), LocalTime.MAX));
|
||||
}
|
||||
|
||||
/**
|
||||
* 两个日期间相差的天数 这里不包含起始日期,如果想要结果包含起始日期,则需要再结果上+1
|
||||
* @return
|
||||
*/
|
||||
public static int getDifferenceDayCount(Date date1, Date date2) {
|
||||
|
||||
LocalDate startDate = DateUtils.getLocalDate(date1);
|
||||
LocalDate endDate = DateUtils.getLocalDate(date2);
|
||||
//取正数
|
||||
return Math.abs((int) (endDate.toEpochDay() - startDate.toEpochDay()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期添加月
|
||||
* @param date
|
||||
* @param month
|
||||
* @return
|
||||
* @author sr on 2023-03-02
|
||||
*/
|
||||
public static Date addMonth(Date date, int month) {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTime(date);
|
||||
c.add(Calendar.MONTH, month);
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加天
|
||||
* @param date
|
||||
* @param amount
|
||||
* @return
|
||||
* @author sr on 2023-12-22
|
||||
*/
|
||||
public static Date addDays(Date date, int amount) {
|
||||
return add(date, Calendar.DAY_OF_MONTH, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期变化
|
||||
* @param date
|
||||
* @param calendarField {@link Linked Calendar}
|
||||
* @param amount
|
||||
* @return
|
||||
* @author sr on 2023-12-22
|
||||
*/
|
||||
public static Date add(Date date, int calendarField, int amount) {
|
||||
if (date == null) {
|
||||
throw new IllegalArgumentException("The date must not be null");
|
||||
}
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTime(date);
|
||||
c.add(calendarField, amount);
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据日期格式不同计算两个时间内的日期、月份、年
|
||||
* @param beginTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @param dateFormat 日期格式
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getDateBetween(Date beginTime, Date endTime,String dateFormat) {
|
||||
List<String> result = new ArrayList<>();
|
||||
try {
|
||||
//格式化日期
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
|
||||
Calendar min = Calendar.getInstance();
|
||||
min.setTime(beginTime);
|
||||
|
||||
Calendar max = Calendar.getInstance();
|
||||
max.setTime(endTime);
|
||||
|
||||
if(YYYY_MM_DD.equals(dateFormat)){
|
||||
while (min.before(max) || min.equals(max)) {
|
||||
result.add(sdf.format(min.getTime()));
|
||||
min.add(Calendar.DATE, 1);
|
||||
}
|
||||
}else if(YYYY_MM.equals(dateFormat)){
|
||||
while (min.before(max) || min.equals(max)) {
|
||||
result.add(sdf.format(min.getTime()));
|
||||
min.add(Calendar.MONTH, 1);
|
||||
}
|
||||
}else {
|
||||
while (min.before(max) || min.equals(max)) {
|
||||
result.add(String.valueOf(min.get(Calendar.YEAR)));
|
||||
min.add(Calendar.YEAR, 1);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.senmesh.common.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.senmesh.common.dto.ColumnDTO;
|
||||
import com.senmesh.common.dto.DisplayedComponentDTO;
|
||||
import com.senmesh.common.dto.MessageDTO;
|
||||
|
||||
/**
|
||||
* @author sr on 2020-04-22
|
||||
*/
|
||||
public class DisplayedComponentUtils {
|
||||
|
||||
/**
|
||||
* 数组转列配置(index 0标题,1属性key)
|
||||
*
|
||||
* @param columnArray
|
||||
* @return
|
||||
* @author sr on 2020-04-22
|
||||
*/
|
||||
public static ColumnDTO toColumnDTO(String[] columnArray) {
|
||||
return ColumnDTO.builder().title(columnArray[0]).key(columnArray[1]).dataIndex(columnArray[1]).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量数组转列配置(index 0标题,1属性key)
|
||||
*
|
||||
* @param columnArrays
|
||||
* @return
|
||||
* @author sr on 2020-04-22
|
||||
*/
|
||||
public static List<ColumnDTO> toColumnDtos(List<String[]> columnArrays) {
|
||||
return columnArrays.stream().map(DisplayedComponentUtils::toColumnDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建展示对象
|
||||
*
|
||||
* @param <T>
|
||||
* @param msg
|
||||
* @param columnArrays (index 0标题,1属性key)
|
||||
* @param data
|
||||
* @return
|
||||
* @author sr on 2020-04-22
|
||||
*/
|
||||
public static <T> DisplayedComponentDTO<T> create(String msg, List<String[]> columnArrays,
|
||||
List<T> data) {
|
||||
MessageDTO messageDto = MessageDTO.builder().content(msg).build();
|
||||
return new DisplayedComponentDTO<T>(messageDto, toColumnDtos(columnArrays), data,
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user