package com.gz.aop;

import com.alibaba.fastjson.JSONObject;
import com.gz.common.DataGlobalVariable;
import com.gz.core.annotation.TraceLog;
import com.gz.dto.system.LogDTO;
import com.gz.mapper.system.LogMapper;
import com.gz.utils.JwtUtils;
import com.gz.utils.RequestUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 纪录日志
 * @author LiuchangLan
 * @date 2020/7/23 14:48
 */
@Aspect
@Component
//@Profile("test")
public class WebLogAspect {

    /**
     * 切入点
     */
    public static final String POINTCUT = "execution(* com.gz.controller..*Controller.*(..))";

    @Resource
    private LogMapper logMapper;

    /**
     * @description 纪录操作日志
     * @author LiuChangLan
     * @since 2020/7/24 16:53
     * @Before 前置增强
     */
    @Before(POINTCUT)
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        LogDTO logDTO = loadLogDto(joinPoint);
        if (logDTO == null){
            return;
        }
        logMapper.insertSelective(logDTO);
    }

    /**
     * @description 纪录错误日志
     * @author LiuChangLan
     * @since 2020/7/24 16:53
     */
    @AfterThrowing(pointcut = POINTCUT, throwing = "e")
    @Transactional
    public void afterThrowing(JoinPoint joinPoint, Throwable e) {
        LogDTO logDTO = loadLogDto(joinPoint);
        if (logDTO != null){
            // 查询已经记录过的操作日志
            LogDTO selectOne = logMapper.selectOne(logDTO);
            // 修改日志类型
            selectOne.setLogType(DataGlobalVariable.ERROR_LOG_CODE);
            // 修改错误消息
            selectOne.setErrorMsg(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
            // 修改日志
            logMapper.updateByPrimaryKeySelective(selectOne);
        }
    }


    /**
     * * 转换异常信息为字符串
     * * @param exceptionName    异常名称
     * * @param exceptionMessage 异常信息
     * * @param elements         堆栈信息
     */
    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();
        return message;
    }


    /**
     * @description 根据连接点载体日志dto
     * @author LiuChangLan
     * @since 2020/7/23 15:55
     */
    private LogDTO loadLogDto(JoinPoint joinPoint) {
        // 获取自定义注解
        TraceLog traceLog = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(TraceLog.class);
        // 跳过没有注解的方法
        if(traceLog == null){
            return null;
        }
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        // 反射读取类名
        String className = joinPoint.getTarget().getClass().getName();
        // 构造日志对象
        LogDTO logDTO = new LogDTO();
        logDTO.setExecMethod(className + "." + joinPoint.getSignature().getName());
        //判断是否是上传文件
        // 参数
        logDTO.setParamsJson(JSONObject.toJSONString(joinPoint.getArgs()));
        // 操作者
        logDTO.setExecBy(JwtUtils.getCurrentUserJwtPayload().getId());
        // 日志类别
        logDTO.setLogType(DataGlobalVariable.OPERATE_LOG_CODE);
        // 操作ip
        logDTO.setExecIp(RequestUtils.getIpAddress(request));
        // 操作模块
        logDTO.setModule(traceLog.module());
        // 操作业务
        logDTO.setBusiness(traceLog.business());
        return logDTO;
    }

}
