Browse Source

api接口集成业务日志配置

chenwen 2 years ago
parent
commit
b059fa83d7

+ 8 - 2
src/main/java/com/hb/proj/allconfig/CacheConfig.java

@@ -12,15 +12,21 @@ import io.micrometer.common.util.StringUtils;
 
 @Configuration
 public class CacheConfig {
-
+	
+	public static String TOKEN_HEADER_NAME="token";
+	
 	private static Cache<String,AccessToken> tokenCache=null;
 	
-	public CacheConfig(@Value("${cache.login.expire}") long expireVal) {
+	public CacheConfig(@Value("${cache.token.expire}") long expireVal, @Value("${token.header.name}") String tokenHeaderName) {
 		//System.out.println(expireVal);
 		tokenCache = Caffeine.newBuilder()
 				.expireAfterAccess(expireVal, TimeUnit.SECONDS)
 				.initialCapacity(20)
 				.build();
+		
+		if(StringUtils.isNotBlank(tokenHeaderName)) {
+			TOKEN_HEADER_NAME=tokenHeaderName;
+		}
 	}
 	
 	

+ 2 - 2
src/main/java/com/hb/proj/allconfig/RequestValidateExceptionHandler.java

@@ -50,14 +50,14 @@ public class RequestValidateExceptionHandler {
 	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
 	public RespVO<Object> methodUnsupportExceptionHandler(HttpRequestMethodNotSupportedException e) {
 		logger.error("不支持该请求方式",e);
-		return RespVOBuilder.error(RespVOBuilder.API_CALL_ERROR,"不支持该请求方式",Object.class);
+		return RespVOBuilder.error(RespVOBuilder.API_CALL_ERROR,"不支持该请求方式");
 		
 	}
 	
 	@ExceptionHandler(Exception.class)
 	public RespVO<Object> otherExceptionHandler(Exception e) {
 		logger.error("服务出错",e);
-		return RespVOBuilder.error(RespVOBuilder.API_EXE_ERROR,"服务出错",null);
+		return RespVOBuilder.error(RespVOBuilder.API_EXE_ERROR,"服务出错");
 		
 	}
 }

+ 11 - 7
src/main/java/com/hb/proj/allconfig/SpringMvcConfigurer.java

@@ -12,6 +12,7 @@ import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 import org.springframework.web.filter.CorsFilter;
 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@@ -68,13 +69,16 @@ public class SpringMvcConfigurer implements WebMvcConfigurer {
 
     
     
-	//@Override
-	/*public void addInterceptors(InterceptorRegistry registry) {
-		LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
-        localeInterceptor.setParamName("lang");  //拦截lang参数
-        registry.addInterceptor(localeInterceptor);
-		WebMvcConfigurer.super.addInterceptors(registry);
-	}*/
+	@Override
+	public void addInterceptors(InterceptorRegistry registry) {
+		//LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
+        //localeInterceptor.setParamName("lang");  //拦截lang参数
+		
+		APICallInterceptor  apiCallInterceptor=new APICallInterceptor();
+		registry.addInterceptor(apiCallInterceptor)
+				.excludePathPatterns("/**/login/**");
+		
+	}
 	
 	
 	

+ 15 - 0
src/main/java/com/hb/proj/allconfig/SysLog.java

@@ -0,0 +1,15 @@
+package com.hb.proj.allconfig;
+
+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) //注解放置的目标位置,METHOD是可注解在方法级别上
+@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
+@Documented //生成文档
+public @interface SysLog {
+
+	String value()  default "";
+}

+ 145 - 0
src/main/java/com/hb/proj/allconfig/SysLogAspect.java

@@ -0,0 +1,145 @@
+package com.hb.proj.allconfig;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterReturning;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import com.hb.proj.model.OperationLog;
+import com.hb.proj.model.User;
+import com.hb.proj.sys.service.OperationLogService;
+import com.hb.proj.utils.JacksonUtils;
+import com.hb.proj.utils.RespVO;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * 系统日志切面类
+ * @author cwen
+ *
+ */
+
+@Aspect
+@Component
+@ConditionalOnProperty(prefix = "spring.syslog.aspect",name = "active")
+public class SysLogAspect {
+	
+	@Autowired
+	private OperationLogService  service;
+	
+	@Pointcut("@annotation(com.hb.proj.allconfig.SysLog)")
+    public void logPoincut() {
+    }
+	
+	
+	/**
+	 * 切点通知配置,获取方法信息,调用上下文等作为日志内容,记录到库
+	 * @param joinPoint
+	 */
+	 @AfterReturning(pointcut="logPoincut()",returning="respVO")
+	 public void saveSysLog(JoinPoint joinPoint,Object respVO) {
+		 
+		 if(respVO instanceof RespVO<?>) {
+			 if(((RespVO<?>)respVO).getCode()!=0) {
+				 return;
+			 }
+		 }
+		 
+		 MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+		 Method method = signature.getMethod();
+		 SysLog sysLog = method.getAnnotation(SysLog.class);
+		 
+		 if(sysLog==null) { //没有指定注解的方法,不需要记录日志
+			 return;
+		 }
+		 
+		 ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+	     HttpServletRequest request = servletRequestAttributes.getRequest();
+	     
+	     Object[] args = joinPoint.getArgs();
+	     
+		 OperationLog log=new OperationLog(request.getHeader(CacheConfig.TOKEN_HEADER_NAME),format(sysLog.value(),args));
+		 
+		 String clsMthd=joinPoint.getTarget().getClass().getName()+"."+method.getName();
+		 
+		 
+		 
+		 log.setCallInfo(clsMthd+"("+JacksonUtils.getJSON(args)+")");  //记录调用的方法信息:类名.方法名|参数
+		 
+		 log.setReqUri(request.getRequestURI());
+		
+		 service.add(log);
+	 }
+	 
+	 public String format(String msg,Object[] args) {
+		 if(args==null||args.length==0) {
+			 return msg;
+		 }
+			
+		 List<String>  holders=extractStrs(msg,"\\{([\\w\\.]+)\\}");
+		 if(holders==null||holders.size()==0) {
+			 return msg;
+		 }
+		 
+		 StandardEvaluationContext context = new StandardEvaluationContext(args);
+		 ExpressionParser parser = new SpelExpressionParser();
+		 Object val=null;
+		 for(String holder : holders) {
+			 val=parser.parseExpression(holder.replaceFirst("arg(\\d+)", "[$1]")).getValue(context);
+			 msg=msg.replace("{"+holder+"}", val!=null?val.toString():"");
+		 }
+		
+		 
+		 return msg;
+	}
+	 
+	 
+	 //提取所有符合要求的字符串
+	 public  List<String> extractStrs(String src,String exp){
+			if(src==null||exp==null){
+				return null;
+			}
+			List<String> extractAry=new ArrayList<String>();
+			Pattern p=Pattern.compile(exp);
+			Matcher m=p.matcher(src);
+			while(m.find()){
+				extractAry.add(m.group(1));
+			}
+			return extractAry.size()>0?extractAry:null;
+	 }		
+	 
+	public static void main(String[] args) {
+		SysLogAspect  log=new SysLogAspect();
+		User us=new User();
+		us.setUserName("李四");
+		Object[] params=new Object[] {us,"张三","历史"};
+		System.out.println(log.format("更新数据{arg0.userName},{arg1}", params));
+		
+		/*
+		StandardEvaluationContext context = new StandardEvaluationContext(params);
+		ExpressionParser parser = new SpelExpressionParser();
+		Expression expression=parser.parseExpression("'更新数据'+[0].userName");
+		System.out.println(expression.getValue(context));
+		*/
+		
+		//System.out.println(log.extractStrs("更新数据{arg0.userName}和编号{arg1}","\\{([\\w\\.]+)\\}"));
+		
+		
+		
+	}
+}

+ 80 - 0
src/main/java/com/hb/proj/model/OperationLog.java

@@ -0,0 +1,80 @@
+package com.hb.proj.model;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class OperationLog {
+
+	private String logId;
+	
+	private String operatorId;
+	
+	private Date operateTime;
+	
+	private String operateDesc;
+	
+	private String reqUri;
+	
+	private String callInfo;
+	
+	public OperationLog() {
+		this.logId=UUID.randomUUID().toString().replaceAll("-", "");
+		this.operateTime=new Date();
+	}
+	
+    public OperationLog(String operatorId,String operateDesc) {
+    	this.logId=UUID.randomUUID().toString().replaceAll("-", "");
+		this.operatorId=operatorId;
+		this.operateDesc=operateDesc;
+		this.operateTime=new Date();
+	}
+	
+
+	public String getLogId() {
+		return logId;
+	}
+
+	public void setLogId(String logId) {
+		this.logId = logId;
+	}
+
+	public String getOperatorId() {
+		return operatorId;
+	}
+
+	public void setOperatorId(String operatorId) {
+		this.operatorId = operatorId;
+	}
+
+	public Date getOperateTime() {
+		return operateTime;
+	}
+
+	public void setOperateTime(Date operateTime) {
+		this.operateTime = operateTime;
+	}
+
+	public String getOperateDesc() {
+		return operateDesc;
+	}
+
+	public void setOperateDesc(String operateDesc) {
+		this.operateDesc = operateDesc;
+	}
+
+	public String getReqUri() {
+		return reqUri;
+	}
+
+	public void setReqUri(String reqUri) {
+		this.reqUri = reqUri;
+	}
+
+	public String getCallInfo() {
+		return callInfo;
+	}
+
+	public void setCallInfo(String callInfo) {
+		this.callInfo = callInfo;
+	}
+}

+ 25 - 0
src/main/java/com/hb/proj/sys/service/OperationLogService.java

@@ -0,0 +1,25 @@
+package com.hb.proj.sys.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.hb.proj.model.OperationLog;
+import com.hb.xframework.dao.core.SpringJdbcDAO;
+
+@Service
+public class OperationLogService {
+	
+	private String tabName="t_operation_log";
+
+	@Autowired
+	private SpringJdbcDAO springDAO;
+	
+	
+	/**
+	 * 增加操作日志
+	 * @param log  OperationLog  新加日志对象
+	 */
+	public void add(OperationLog  log) {
+		springDAO.insert(log, tabName);
+	}
+}

+ 1 - 1
src/main/java/com/hb/proj/utils/RespVO.java

@@ -23,9 +23,9 @@ public class RespVO<T>{
 	}
 	
 	public RespVO(int code,T  data,String  msg) {
+		this.code=code;
 		this.data=data;
 		this.msg=msg;
-		this.code=code;
 	}
 
 	

+ 13 - 4
src/main/java/com/hb/proj/utils/RespVOBuilder.java

@@ -1,5 +1,7 @@
 package com.hb.proj.utils;
 
+import org.apache.commons.lang3.StringUtils;
+
 import com.hb.proj.allconfig.LocalConfig;
 import com.hb.xframework.util.MD5Encrypt;
 
@@ -9,18 +11,22 @@ public class RespVOBuilder {
 	
 	public static final int API_EXE_ERROR=500;
 
-	public static RespVO<Object>  ok() {  
-		return new RespVO<Object>();
+	public static <T> RespVO<T>  ok() {  
+		return new RespVO<T>(0,null,getI18n("操作成功"));
 	}
 	
 	public static <T>  RespVO<T>  ok(T data) {
-		return new RespVO<T>(0,data,null);
+		return new RespVO<T>(0,data,getI18n("操作成功"));
 	}
 	
 	public static <T> RespVO<T>  error(String error) {
 		return new RespVO<T>(API_CALL_ERROR,null,getI18n(error));
 	}
 	
+	public static <T> RespVO<T>  error(int code,String error) {
+		return new RespVO<T>(code,null,getI18n(error));
+	}
+	
 	public static <T> RespVO<T>  error(int code,String error,T data) {
 		return new RespVO<T>(code,data,getI18n(error));
 	}
@@ -28,6 +34,9 @@ public class RespVOBuilder {
 	
 	
 	private static String getI18n(String str) {
-		return LocalConfig.get(MD5Encrypt.md5(str),str);
+		if(StringUtils.isBlank(str)) {
+			return null;
+		}
+		return LocalConfig.get(MD5Encrypt.md5(str.trim()),str);
 	}
 }

+ 7 - 2
src/main/resources/application-dev.properties

@@ -20,9 +20,11 @@ spring.jackson.time-zone=GMT+8
 
 #日志配置
 logging.level.com.hb.xframework=DEBUG
+#业务日志配置
+spring.syslog.aspect.active=true
 
 #数据库连接池配置
-spring.datasource.url=jdbc:mysql://localhost:3306/earthquake_info
+spring.datasource.url=jdbc:mysql://localhost:3306/zl_opd
 spring.datasource.username=root
 spring.datasource.password=hb
 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
@@ -30,4 +32,7 @@ spring.datasource.type=com.hb.xframework.dao.util.HikariDataSourceWrap
 spring.datasource.dialect=MySQL
 
 #缓存配置  登录信息过期时间,单位秒
-cache.login.expire=120
+cache.token.expire=120
+
+#token 在请求header中的name,默认为token
+token.header.name=token