package com.elab.redis.interceptor.impl;

import com.elab.redis.annotation.CacheLoopSubmit;
import com.elab.redis.interceptor.ICacheProcessService;
import com.elab.redis.utils.CacheParseUtil;
import org.redisson.api.RLock;
import org.redisson.api.RSet;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 防重复提交
 *
 * @author ： liukx
 * @time ： 2020/7/10 - 10:58
 */
@Component
public class CacheLoopProcessImpl implements ICacheProcessService {

    @Autowired
    private RedissonClient client;

    private Logger logger = LoggerFactory.getLogger(CacheLoopProcessImpl.class);

    @Override
    public boolean subscribe(Method method) {
        return CacheParseUtil.isContainAnnotation(method, CacheLoopSubmit.class);
    }

    @Override
    public Object invokeWithinTransaction(ReflectiveMethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        CacheLoopSubmit cacheLoopSubmit = AnnotationUtils.getAnnotation(method, CacheLoopSubmit.class);
        String[] unionKey = cacheLoopSubmit.unionKey();
        int timeOut = cacheLoopSubmit.timeOut();
        String clazzName = method.getDeclaringClass().getName();
        Object[] arguments = invocation.getArguments();

        String cacheKey = CacheParseUtil.generateUnionKey(unionKey, arguments);
        String lockKey =  clazzName + "." + method.getName();

        RSet<Object> lockObject = client.getSet(lockKey);
        RLock lock = lockObject.getLock(cacheKey);
        try {
            if (lock.tryLock(timeOut, TimeUnit.SECONDS)) {
                Object proceed = invocation.proceed();
                return proceed;
            }
        } catch (InterruptedException e) {
            logger.warn("尝试获取锁超时", e);
        } finally {
            lock.unlock();
        }
        return null;
    }


}
