Signed-off-by: chy <chy@163.com>

This commit is contained in:
chy
2026-02-02 23:31:39 +08:00
commit f6d2459f1f
1499 changed files with 289491 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
package top.lidee.taie.log;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import top.lidee.taie.annotation.condition.ConditionalOnComponent;
import top.lidee.taie.log.aspect.LideeAuditLogAspect;
import top.lidee.taie.log.config.LideeAuditLogProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.client.RestTemplate;
/**
* 日志组件
*
* @author lr
* @since 2021-01-15
*/
@Configuration
@EnableConfigurationProperties(LideeAuditLogProperties.class)
@ConditionalOnComponent(LideeAuditLogProperties.COMPONENT_NAME)
public class LideeLogAutoConfiguration {
private static Logger logger = LoggerFactory.getLogger(LideeLogAutoConfiguration.class);
@Value("${lidee.threadPool.corePoolSize:5}")
private Integer corePoolSize;
@Value("${lidee.threadPool.maxPoolSize:50}")
private Integer maxPoolSize;
@Value("${lidee.threadPool.queueCapacity:100}")
private Integer queueCapacity;
@Value("${lidee.threadPool.keepAliveInSeconds:300}")
private Integer keepAliveInSeconds;
public LideeLogAutoConfiguration() {
logger.info("spring lidee subscribes audit-log is actived");
}
@Bean
public LideeAuditLogAspect auditLogAspect() {
return new LideeAuditLogAspect();
}
@Bean(name = "logRestTemplate")
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Bean(name = "threadPoolLideeLogExecutor")
@Primary
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
//线程池维护线程的最少数量
poolTaskExecutor.setCorePoolSize(corePoolSize);
//线程池维护线程的最大数量
poolTaskExecutor.setMaxPoolSize(maxPoolSize);
//线程池所使用的缓冲队列
poolTaskExecutor.setQueueCapacity(queueCapacity);
//线程池维护线程所允许的空闲时间
poolTaskExecutor.setKeepAliveSeconds(keepAliveInSeconds);
poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
poolTaskExecutor.setRejectedExecutionHandler(new CallerRunsPolicy());
poolTaskExecutor.setThreadGroupName("lideeThreadGroup");
poolTaskExecutor.setThreadNamePrefix("lidee");
return poolTaskExecutor;
}
}

View File

@@ -0,0 +1,307 @@
package top.lidee.taie.log.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import top.lidee.taie.annotation.Permission;
import top.lidee.taie.annotation.log.LideeAuditLog;
import top.lidee.taie.constant.LideeConstant;
import top.lidee.taie.holder.UserContentHolder;
import top.lidee.taie.log.config.LideeAuditLogProperties;
import top.lidee.taie.log.event.AuditLogApplicationEvent;
import top.lidee.taie.utils.ApplicationContextUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
/**
* 操作日志切面处理
*/
@Aspect
public class LideeAuditLogAspect {
private static final Logger log = LoggerFactory.getLogger(LideeAuditLogAspect.class);
@Autowired
private LideeAuditLogProperties lideeAuditLogProperties;
@Value("/${spring.application.projectCode:''}")
private String projectCode;
@Resource(name = "logRestTemplate")
private RestTemplate restTemplate;
@Resource(name = "threadPoolLideeLogExecutor")
private ThreadPoolTaskExecutor threadPoolLideeLogExecutor;
/**
* 统计请求的处理时间
*/
private ThreadLocal<Long> startTime = new ThreadLocal<>();
// 配置织入点
@Pointcut("@annotation(top.lidee.taie.annotation.log.LideeAuditLog)")
public void logPointCut() {
}
@Before("logPointCut()")
public void doBefore() {
startTime.set(System.currentTimeMillis());
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
handleLog(joinPoint, null, jsonResult);
startTime.remove();
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
handleLog(joinPoint, e, null);
startTime.remove();
}
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
try {
// 获得注解
LideeAuditLog tag = getAnnotationLog(joinPoint);
if (null == tag) {
return;
}
LogOperation operLog = new LogOperation();
//设置url
HttpServletRequest request = getRequest();
operLog.setTenantCode(UserContentHolder.getTenantCode());
operLog.setOrgCode(UserContentHolder.getOrgCode());
operLog.setTerminal(request.getHeader(LideeConstant.SYS_CODE));
operLog.setLocale(request.getHeader(LideeConstant.LOCALE));
operLog.setUserName(UserContentHolder.getUsername());
operLog.setCreateBy(UserContentHolder.getUsername());
operLog.setRequestUrl(projectCode + request.getRequestURI());
// 设置请求方式
operLog.setRequestMethod(request.getMethod());
operLog.setRequestTime(new Date(startTime.get()));
operLog.setResponseTime(new Date());
//获取ip
operLog.setSourceIp(request.getHeader(LideeConstant.SOURCE_IP));
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, tag, operLog);
if (Objects.nonNull(auditLogHandler)) {
auditLogHandler.initLogInfo(request, operLog,jsonResult,e,tag,getResponse());
}else {
// 返回参数
if (jsonResult != null) {
operLog.setResponseParam("" + jsonResult);
}
if (e != null) {
operLog.setResponseParam(e.getMessage());
}
}
if (lideeAuditLogProperties.isPublishEvent()) {
ApplicationContextUtils.publishEvent(new AuditLogApplicationEvent(this, operLog));
}
//执行回调
if (!StringUtils.isEmpty(lideeAuditLogProperties.getCallbackUrl())) {
threadPoolLideeLogExecutor.execute(() -> {
restTemplateCallback(operLog, request);
});
}
} catch (Exception exp) {
// 记录本地异常日志
log.error("--Log:error--{},ex:{}", exp.getMessage(),e, exp);
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param log 日志
* @param operLog 操作日志
* @throws Exception
*/
public void getControllerMethodDescription(JoinPoint joinPoint, LideeAuditLog log,
LogOperation operLog) throws Exception {
// 设置标题
operLog.setPageTitle(log.pageTitle());
// 是否需要保存request参数和值
if (log.isSaveRequestData()) {
// 获取参数的信息,传入到数据库中。
setRequestValue(joinPoint, operLog);
}
if (!log.isSaveResponseData()) {
operLog.setResponseParam(null);
}
}
/**
* 获取请求的参数放到log中
*
* @param operLog 操作日志
* @throws Exception 异常
*/
private void setRequestValue(JoinPoint pjp, LogOperation operLog) throws Exception {
String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
String params = argsArrayToString(pjp.getArgs());
operLog.setRequestParam(params);
} else if (HttpMethod.GET.name().equals(requestMethod)) {
Map<String, String[]> paramMap = getRequest().getParameterMap();
operLog.setRequestParam(JSON.toJSONString(paramMap));
} else {
Map<?, ?> paramsMap = (Map<?, ?>) getRequest().getAttribute("HandlerMapping" + ".uriTemplateVariables");
if (null != paramsMap) {
operLog.setRequestParam(paramsMap.toString());
}
}
String ts = getRequest().getHeader(LideeConstant.REQUEST_TIMESTAMP);
operLog.setReqTimeStamp(ts);
// 方法上有 @PermissionpageTitle取值修改为:菜单名称-权限码名称
try {
Signature sig = pjp.getSignature();
MethodSignature ms = (MethodSignature) sig;
Object target = pjp.getTarget();
Method currMethod = target.getClass().getMethod(ms.getName(), ms.getParameterTypes());
Permission permission = target.getClass().getAnnotation(Permission.class);
if (Objects.nonNull(permission)) {
String m = permission.name();
String operation = "";
Permission[] arr = currMethod.getAnnotationsByType(Permission.class);
if (!ObjectUtils.isEmpty(arr)) {
operation = arr[0].name();
operLog.setPageTitle(m + "-" + operation);
}
}
} catch (Exception ignore) {
}
}
/**
* 是否存在注解,如果存在就获取
*/
private LideeAuditLog getAnnotationLog(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(LideeAuditLog.class);
}
return null;
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray) {
String params = "";
if (paramsArray != null && paramsArray.length > 0) {
for (int i = 0; i < paramsArray.length; i++) {
if (!isFilterObject(paramsArray[i])) {
try {
Object jsonObj = JSON.toJSON(paramsArray[i]);
params += jsonObj.toString() + " ";
} catch (Exception e) {
}
}
}
}
return params.trim();
}
/**
* 判断是否需要过滤的对象。
*
* @param o 对象信息。
* @return 如果是需要过滤的对象则返回true否则返回false。
*/
public boolean isFilterObject(final Object o) {
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
}
public static ServletRequestAttributes getRequestAttributes() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
/**
* 获取request
*/
public static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
public static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
/**
* 微服务模式下,通过回调方法传输日志数据
*
* @param logOperation
*/
private void restTemplateCallback(LogOperation logOperation, HttpServletRequest request) {
String url = lideeAuditLogProperties.getCallbackUrl();
if (auditLogHandler != null) {
auditLogHandler.push(url, logOperation);
return;
}
try {
log.info("--Log:callBack:url-{}--", url);
HttpHeaders headers_new = new HttpHeaders();
headers_new.setContentType(MediaType.APPLICATION_JSON);
headers_new.set("Accept", "application/json;charset=UTF-8");
headers_new.set("Authorization", request.getHeader(LideeConstant.Authorization));
HttpEntity entity = new HttpEntity(logOperation, headers_new);
JSONObject responseBody = restTemplate.postForObject(url, entity, JSONObject.class);
log.info("--Log:callBack:response-{}", responseBody);
} catch (Exception e) {
log.error("--Log:callBack:error--{}", e.getMessage());
}
}
@Autowired(required = false)
private LideeAuditLogHandler auditLogHandler;
}

View File

@@ -0,0 +1,34 @@
package top.lidee.taie.log.aspect;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import top.lidee.taie.annotation.log.LideeAuditLog;
/***
* @author wongbin
* @date 2022/06/15
*/
public interface LideeAuditLogHandler {
/***
* 客户端推送日志数据接口
* @param url
* @param data
*/
void push(String url,Object data);
/**
* 日志填充扩展点
* @param request
* @param ex
* @param response
* @param result
* @param tag
* @param operLog
*/
default void initLogInfo(HttpServletRequest request,
LogOperation operLog, Object result, Exception ex, LideeAuditLog tag,
HttpServletResponse response){
};
}

View File

@@ -0,0 +1,213 @@
package top.lidee.taie.log.aspect;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
/**
* 日志相关数据
* @author lr
*/
public class LogOperation implements Serializable {
/**
* 租户
*/
private String tenantCode;
private String userName;
private String createBy;
/**
* 请求路径
*/
private String requestUrl;
/**
* 页面或按钮标题
*/
private String pageTitle;
/**
* 请求参数
*/
private String requestParam;
/**
* 响应参数
*/
private String responseParam;
/**
* 来源IP
*/
private String sourceIp;
/**
* 请求方式get/post/put/delete
*/
private String requestMethod;
/**
* 访问时间
*/
private Date requestTime;
/*上游请求时间戳从header里获取*/
private String reqTimeStamp;
private Date responseTime;
private String orgCode;
private String terminal;
private String locale;
private Map extend;
@Override
public String toString() {
return "LogOperation{" +
"tenantCode='" + tenantCode + '\'' +
", userName='" + userName + '\'' +
", createBy='" + createBy + '\'' +
", requestUrl='" + requestUrl + '\'' +
", pageTitle='" + pageTitle + '\'' +
", requestParam='" + requestParam + '\'' +
", responseParam='" + responseParam + '\'' +
", sourceIp='" + sourceIp + '\'' +
", requestMethod='" + requestMethod + '\'' +
", requestTime=" + requestTime +
", responseTime=" + responseTime +
", reqTimeStamp='" + reqTimeStamp + '\'' +
", orgCode='" + orgCode + '\'' +
", terminal='" + terminal + '\'' +
", locale='" + locale + '\'' +
'}';
}
public Map getExtend() {
return extend;
}
public void setExtend(Map extend) {
this.extend = extend;
}
public String getReqTimeStamp() {
return reqTimeStamp;
}
public void setReqTimeStamp(String reqTimeStamp) {
this.reqTimeStamp = reqTimeStamp;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public String getTenantCode() {
return tenantCode;
}
public void setTenantCode(String tenantCode) {
this.tenantCode = tenantCode;
}
public String getRequestUrl() {
return requestUrl;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
public String getPageTitle() {
return pageTitle;
}
public void setPageTitle(String pageTitle) {
this.pageTitle = pageTitle;
}
public String getRequestParam() {
return requestParam;
}
public void setRequestParam(String requestParam) {
this.requestParam = requestParam;
}
public String getResponseParam() {
return responseParam;
}
public void setResponseParam(String responseParam) {
this.responseParam = responseParam;
}
public String getSourceIp() {
return sourceIp;
}
public void setSourceIp(String sourceIp) {
this.sourceIp = sourceIp;
}
public String getRequestMethod() {
return requestMethod;
}
public void setRequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}
public Date getRequestTime() {
return requestTime;
}
public void setRequestTime(Date requestTime) {
this.requestTime = requestTime;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setOrgCode(String orgCode) {
this.orgCode = orgCode;
}
public String getOrgCode() {
return orgCode;
}
public void setTerminal(String terminal) {
this.terminal = terminal;
}
public String getTerminal() {
return terminal;
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getLocale() {
return locale;
}
public Date getResponseTime() {
return responseTime;
}
public void setResponseTime(Date responseTime) {
this.responseTime = responseTime;
}
}

View File

@@ -0,0 +1,42 @@
package top.lidee.taie.log.config;
import top.lidee.taie.constant.LideeConstant;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 审计日志属性
* @author lr
* @since 2021-01-20
*/
@ConfigurationProperties(prefix = LideeConstant.COMPONENT_PREFIX + LideeAuditLogProperties.COMPONENT_NAME)
public class LideeAuditLogProperties {
/**
* 组件名称
*/
public final static String COMPONENT_NAME = "audit-log";
/**
* 微服务架构,可以设置回调地址来获取日志数据
*/
public String callbackUrl;
/**
* 单体模式下,可以设置监听事件获取日志数据,
*/
public boolean publishEvent;
public String getCallbackUrl() {
return callbackUrl;
}
public void setCallbackUrl(String callbackUrl) {
this.callbackUrl = callbackUrl;
}
public boolean isPublishEvent() {
return publishEvent;
}
public void setPublishEvent(boolean publishEvent) {
this.publishEvent = publishEvent;
}
}

View File

@@ -0,0 +1,27 @@
package top.lidee.taie.log.event;
import top.lidee.taie.log.aspect.LogOperation;
import org.springframework.context.ApplicationEvent;
/**
* 审计日志事件
*
* @author lr
* @since 2021-01-20
*/
public class AuditLogApplicationEvent extends ApplicationEvent {
private LogOperation logOperation;
public void setLogOperation(LogOperation logOperation) {
this.logOperation = logOperation;
}
public AuditLogApplicationEvent(Object obj, LogOperation logOperation) {
super(obj);
setLogOperation(logOperation);
}
public LogOperation getLogOperation() {
return this.logOperation;
}
}

View File

@@ -0,0 +1 @@
top.lidee.taie.annotation.enabled.EnabledLideeConfiguration=top.lidee.taie.log.LideeLogAutoConfiguration