
package com.fs.framework.aspectj;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class ControllerLogAspect {

    @Pointcut("execution(* com.fs.app.controller..*.*(..))")
    public void controllerPointcut() {
    }

    @Around("controllerPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        String fullMethodName = className + "." + methodName;

        Object[] args = joinPoint.getArgs();
        String requestParams = getRequestParams(args);

        log.info("========== 接口调用开始 ==========");
        log.info("接口方法: {}", fullMethodName);
        log.info("请求参数: {}", requestParams);

        Object result = null;
        try {
            result = joinPoint.proceed();

            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;

            log.info("返回结果: {}", JSON.toJSONString(result));
            log.info("接口耗时: {} ms", costTime);
            log.info("========== 接口调用结束 ==========");

            return result;
        } catch (Throwable e) {
            long endTime = System.currentTimeMillis();
            long costTime = endTime - startTime;

            log.error("接口异常: {}", e.getMessage());
            log.error("接口耗时: {} ms", costTime);
            log.error("========== 接口调用异常 ==========");

            throw e;
        }
    }

    private String getRequestParams(Object[] args) {
        if (args == null || args.length == 0) {
            return "无参数";
        }

        StringBuilder params = new StringBuilder();
        for (int i = 0; i < args.length; i++) {
            if (args[i] != null && !isFilterObject(args[i])) {
                try {
                    Object jsonObj = JSON.toJSON(args[i]);
                    params.append(jsonObj.toString());
                    if (i < args.length - 1) {
                        params.append(", ");
                    }
                } catch (Exception e) {
                    params.append(args[i].getClass().getSimpleName());
                }
            }
        }

        return params.length() > 0 ? params.toString() : "无参数";
    }

    private boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Iterator iter = collection.iterator(); iter.hasNext();) {
                return iter.next() instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
                Map.Entry entry = (Map.Entry) iter.next();
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}
