package com.greenorange.promotion.aop; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.greenorange.promotion.annotation.MyLog; import com.greenorange.promotion.model.entity.SysOperLog; import com.greenorange.promotion.service.log.SysOperLogService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import java.io.BufferedReader; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.URL; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 切面处理类,记录操作日志到数据库 */ @Aspect @Component public class OperLogAspect { @Resource private SysOperLogService sysOperLogService; // 为了记录方法的执行时间 ThreadLocal startTime = new ThreadLocal<>(); @Before("@annotation(myLog)") public void beforeMethod(JoinPoint joinPoint, MyLog myLog){ startTime.set(System.currentTimeMillis()); } /** * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行 * @param joinPoint 切入点 * @param result 返回结果 */ @AfterReturning(value = "@annotation(myLog)", returning = "result") public void saveOperLog(JoinPoint joinPoint, MyLog myLog, Object result) { // 获取RequestAttributes RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); // 从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); try { // 从切面织入点处通过反射机制获取织入点处的方法 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 获取切入点所在的方法 Method method = signature.getMethod(); // 获取操作 SysOperLog sysOperLog = new SysOperLog(); sysOperLog.setTitle(myLog.title());//设置模块名称 sysOperLog.setContent(myLog.content());//设置日志内容 // 将入参转换成json String params = argsArrayToString(joinPoint.getArgs()); // 获取请求的类名 String className = joinPoint.getTarget().getClass().getName(); // 获取请求的方法名 String methodName = method.getName(); methodName = className + "." + methodName + "()"; sysOperLog.setMethod(methodName); //设置请求方法 sysOperLog.setRequestMethod(request.getMethod());//设置请求方式 sysOperLog.setRequestParam(params); // 请求参数 sysOperLog.setResponseResult(JSON.toJSONString(result)); // 返回结果 // 从request中取出账号 String userAccount = (String) request.getAttribute("userAccount"); sysOperLog.setOperName(userAccount); // 获取用户名(真实环境中,肯定有工具类获取当前登录者的账号或ID的,或者从token中解析而来) String ip = getIp(request); sysOperLog.setIp(ip); // IP地址 String ipLocation = getIpLocation(ip); sysOperLog.setIpLocation(ipLocation); // IP归属地(真是环境中可以调用第三方API根据IP地址,查询归属地) sysOperLog.setRequestUrl(request.getRequestURI()); // 请求URI sysOperLog.setOperTime(new Date()); // 时间 sysOperLog.setStatus(0);//操作状态(0正常 1异常) Long takeTime = System.currentTimeMillis() - startTime.get();//记录方法执行耗时时间(单位:毫秒) sysOperLog.setTakeTime(takeTime); //插入数据库 sysOperLogService.save(sysOperLog); } catch (Exception e) { e.printStackTrace(); } } /** * 设置操作异常切入点记录异常日志 扫描所有controller包下操作 * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行 */ @AfterThrowing(pointcut = "execution(* com.greenorange.promotion.controller..*.*(..))", throwing = "e") public void saveExceptionLog(JoinPoint joinPoint, Throwable e) { // 获取RequestAttributes RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); // 从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); SysOperLog sysOperLog = new SysOperLog(); try { // 从切面织入点处通过反射机制获取织入点处的方法 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 获取切入点所在的方法 Method method = signature.getMethod(); // 获取请求的类名 String className = joinPoint.getTarget().getClass().getName(); // 获取请求的方法名 String methodName = method.getName(); methodName = className + "." + methodName + "()"; // 获取操作 MyLog myLog = method.getAnnotation(MyLog.class); if (myLog != null) { sysOperLog.setTitle(myLog.title());//设置模块名称 sysOperLog.setContent(myLog.content());//设置日志内容 } // 将入参转换成json String params = argsArrayToString(joinPoint.getArgs()); sysOperLog.setMethod(methodName); //设置请求方法 sysOperLog.setRequestMethod(request.getMethod());//设置请求方式 sysOperLog.setRequestParam(params); // 请求参数 // 从request中取出账号 String userAccount = (String) request.getAttribute("userAccount"); sysOperLog.setOperName(userAccount); // 获取用户名(真实环境中,肯定有工具类获取当前登录者的账号或ID的,或者从token中解析而来) String ip = getIp(request); sysOperLog.setIp(ip); // IP地址 String ipLocation = getIpLocation(ip); sysOperLog.setIpLocation(ipLocation); // IP归属地(真是环境中可以调用第三方API根据IP地址,查询归属地) sysOperLog.setRequestUrl(request.getRequestURI()); // 请求URI sysOperLog.setOperTime(new Date()); // 时间 sysOperLog.setStatus(1);//操作状态(0正常 1异常) sysOperLog.setErrorMsg(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));//记录异常信息 //插入数据库 sysOperLogService.save(sysOperLog); } catch (Exception e2) { e2.printStackTrace(); } } /** * 转换异常信息为字符串 */ public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) { StringBuffer strbuff = new StringBuffer(); for (StackTraceElement stet : elements) { strbuff.append(stet + "\n"); } String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString(); message = substring(message,0 ,2000); return message; } /** * 参数拼装 */ private String argsArrayToString(Object[] paramsArray) { String params = ""; if (paramsArray != null && paramsArray.length > 0) { for (Object o : paramsArray) { if (o != null) { try { Object jsonObj = JSON.toJSON(o); params += jsonObj.toString() + " "; } catch (Exception e) { e.printStackTrace(); } } } } return params.trim(); } //字符串截取 public static String substring(String str, int start, int end) { if (str == null) { return null; } else { if (end < 0) { end += str.length(); } if (start < 0) { start += str.length(); } if (end > str.length()) { end = str.length(); } if (start > end) { return ""; } else { if (start < 0) { start = 0; } if (end < 0) { end = 0; } return str.substring(start, end); } } } /** * 转换request 请求参数 * @param paramMap request获取的参数数组 */ public Map converMap(Map paramMap) { Map returnMap = new HashMap<>(); for (String key : paramMap.keySet()) { returnMap.put(key, paramMap.get(key)[0]); } return returnMap; } //根据HttpServletRequest获取访问者的IP地址 public String getIp(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } public String getIpLocation(String ip) { try { // 发送请求到ip-api服务 String url = "http://ip-api.com/json/" + ip + "?lang=zh-CN"; // 获取中文结果 URL obj = new URL(url); HttpURLConnection con = (HttpURLConnection) obj.openConnection(); con.setRequestMethod("GET"); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); // 解析JSON返回结果 JSONObject jsonResponse = JSONObject.parseObject(response.toString()); String region = jsonResponse.getString("regionName"); // 省 String city = jsonResponse.getString("city"); // 城市 String country = jsonResponse.getString("country"); // 国家 return city + ", " + region + ", " + country; // 返回位置 } catch (Exception e) { e.printStackTrace(); } return "未知"; } }