package com.cpop.core.aspect;

import com.cpop.common.constant.HttpStatus;
import com.cpop.common.enums.ErrorCodeEnum;
import com.cpop.core.base.R;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.OperationLogEnum;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.table.SysOperationLog;
import com.cpop.core.event.OperationLogEvent;
import com.cpop.core.utils.MessageUtils;
import com.cpop.core.utils.SecurityUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

/**
 * @author: DB
 * @date: 2022-08-12 9:45
 * @Description:
 */
@Slf4j
@Aspect
@Component
public class OperationLogAspect {

    private final SysOperationLog operationLog = new SysOperationLog();

    private OperationLogEnum operationLogEnum;

    /**
     * 事件发布是由ApplicationContext对象管控的，我们发布事件前需要注入ApplicationContext对象调用publishEvent方法完成事件发布
     */
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 定义controller切入点拦截规则，拦截SysLog注解的方法
     */
    @Pointcut("@annotation(com.cpop.core.annontation.OperationLog)")
    public void operationLogAspect() {

    }

    /**
     * @param joinPoint 切入点
     * @author LOST.yuan
     * @Description: 拦截控制层的操作日志
     * @date 16:37 2022/10/12
     **/
    @Before(value = "operationLogAspect()")
    public void recordLog(JoinPoint joinPoint) {
        //设置枚举信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        com.cpop.core.annontation.OperationLog annotation = signature.getMethod().getAnnotation(com.cpop.core.annontation.OperationLog.class);
        operationLogEnum = annotation.operationLogEnumType();
        try {
            LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
            operationLog.setOperationUserName(loginUser.getUsername())
                    .setOperationUserId(loginUser.getUserId())
                    .setOperationUserType(loginUser.getUserType())
                    .setDevIp(loginUser.getIpAddr())
                    .setCode(operationLogEnum.getCode())
                    .setInfo(MessageUtils.message(operationLogEnum.getInfo()))
                    .setCreateUserId(loginUser.getUserId());
            operationLog.setUpdateUserId(loginUser.getUserId());
        } catch (Exception e) {
            throw new ServiceException(ErrorCodeEnum.OPERATION_LOG_EXCEPTION.getInfo(), ErrorCodeEnum.OPERATION_LOG_EXCEPTION.getCode());
        }
    }

    /**
     * @param ret 返回
     * @author LOST.yuan
     * @Description 返回通知
     * @date 16:43 2022/10/12
     **/
    @AfterReturning(returning = "ret", pointcut = "operationLogAspect()")
    public void doAfterReturning(Object ret) {
        // 处理完请求，返回内容
        R r = new ObjectMapper().convertValue(ret, R.class);
        operationLog.setActionType(1);
        if (r.getCode() == HttpStatus.SUCCESS) {
            // 正常返回
            operationLog.setResult(true);
            operationLog.setLevel(operationLogEnum.getSuccessLevel());
            operationLog.setDescription(MessageUtils.message(operationLogEnum.getInfo()) + "成功");
        } else {
            operationLog.setResult(false);
            operationLog.setFailReason(r.getMsg());
            operationLog.setLevel(operationLogEnum.getExceptionLevel());
            operationLog.setDescription(MessageUtils.message(operationLogEnum.getInfo()) + "失败");
        }
        // 发布事件
        applicationContext.publishEvent(new OperationLogEvent(operationLog));
    }

    /**
     * @param e 异常
     * @author LOST.yuan
     * @Description 异常通知
     * @date 16:37 2022/10/12
     **/
    @AfterThrowing(pointcut = "operationLogAspect()", throwing = "e")
    public void doAfterThrowable(Throwable e) {
        // 异常
        operationLog.setResult(false)
                .setActionType(2)
                .setFailReason(e.getMessage())
                .setDescription(MessageUtils.message(operationLogEnum.getInfo()) + "异常")
                .setLevel(operationLogEnum.getExceptionLevel());
        // 发布事件
        applicationContext.publishEvent(new OperationLogEvent(operationLog));
    }

}
