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:
KodouWZF
2026-11-02 10:59:03 +08:00
commit 47273ae3fd
2190 changed files with 305040 additions and 0 deletions

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

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

View File

@@ -0,0 +1,12 @@
package com.senmesh.common.constant;
/**
* 缓存常量
*
* @author sr on 2020-04-03
*/
public interface CacheConstants {
String CACHE_NAME_DICT = "dict";
}

View File

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

View File

@@ -0,0 +1,6 @@
package com.senmesh.common.constant;
public interface CommonMqConstants {
}

View File

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

View File

@@ -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 = "操作失败,请检查数据";
}

View File

@@ -0,0 +1,10 @@
package com.senmesh.common.constant;
public interface ExceptionRecordConstants {
/**
* 接口记录系统类型
*/
String SOURCE_HYG = "1";
String SOURCE_XINPAI = "2";
}

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
package com.senmesh.common.dto;
/**
* 基础对象
* @author sr on 2024-04-18
*/
public interface IBaseDTO {
Long getId();
}

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

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
package com.senmesh.common.exception;
public interface IDevopsProvider {
void deal(String msg);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,11 @@
package com.senmesh.common.freemarker.constant;
/**
* @author sr on 2020-04-10
*/
public interface FreemarkerConstants {
/** 数据库表名前缀 **/
String TABLE_PREFIX = "xs_";
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
package com.senmesh.common.log;
/**
* 日志提供者
*
* @author sr on 2020-10-29
*/
public interface ILogProvider {
void log(Object object);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

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

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

View File

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