package admin.asept;

import admin.annotation.CurDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 动态数据源切面类
 * 被切中的，则先判断方法上是否有CurDataSource注解
 * 然后判断方法所属类上是否有CurDataSource注解
 * 其次判断是否配置了包级别的数据源
 * <p>
 * 优先级为方法、类、包
 * 若同时配置则优先按方法上的
 *
 * @Author lzl
 */
@Slf4j
@Aspect
@Order(-1)//设置AOP执行顺序(需要在事务之前，否则事务只发生在默认库中)
@Component
public class DynamicDataSourceAspect {
    // pointCut
    @Pointcut("@annotation(admin.annotation.CurDataSource)")
    public void choseDatasourceByAnnotation() {
    }

    @Pointcut("@within(admin.annotation.CurDataSource)")
    public void choseDatasourceByClass() {
    }

    @Pointcut("execution(* admin.modules.*.service.impl..*(..))")
    public void choseDatasourceByPackage() {
    }

    @Around("choseDatasourceByAnnotation() || choseDatasourceByClass() || choseDatasourceByPackage()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//        log.info("进入AOP环绕通知");
        Signature signature = joinPoint.getSignature();
        String datasourceName = getDatasourceKey(signature);
        if (!Objects.isNull(datasourceName)) {
            DynamicDataSource.setDataSourceName(datasourceName);
        }
        return joinPoint.proceed();
    }

    /**
     * 获取数据源
     *
     * @param signature
     * @return
     */
    private String getDatasourceKey(Signature signature) {
        if (signature == null) {
            return null;
        } else {
            if (signature instanceof MethodSignature) {
                MethodSignature methodSignature = (MethodSignature) signature;
                Method method = methodSignature.getMethod();
                //@CurDataSource 注解是否在方法上
                if (method.isAnnotationPresent(CurDataSource.class)) {
                    return this.dsSettingInMethod(method);
                }

                Class<?> declaringClass = method.getDeclaringClass();
                //@CurDataSource 注解是否在类上
                if (declaringClass.isAnnotationPresent(CurDataSource.class)) {
                    return this.dsSettingInConstructor(declaringClass);
                }
                Package aPackage = declaringClass.getPackage();
                this.dsSettingInPackage(aPackage);
            }
            return null;
        }
    }

    /**
     * 获取类上 使用注解对应的数据源
     *
     * @param declaringClass
     * @return
     */
    private String dsSettingInConstructor(Class<?> declaringClass) {
        CurDataSource dataSource = declaringClass.getAnnotation(CurDataSource.class);
        return dataSource.value();
    }

    /**
     * 获取方法上 使用注解对应的数据源
     *
     * @param method
     * @return
     */
    private String dsSettingInMethod(Method method) {
        CurDataSource dataSource = method.getAnnotation(CurDataSource.class);
        return dataSource.value();
    }

    /**
     * 获取包对应的数据源
     *
     * @param pkg
     */
    private void dsSettingInPackage(Package pkg) {
        DynamicDataSource.usePackageDatasourceKey(pkg.getName());
    }
}
