package com.ruoyi.common.core.redis;

import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SnowFlakeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * @author wjh
 * 2024/3/15
 */
@Component
public class RedisLock {

    @Autowired
    public RedisTemplate redisTemplate;

    // 锁前缀
    @Value("${lock.prefix}")
    private String lockPrefix;

    // 锁过期时间
    @Value("${lock.timeout}")
    private Long timeout;

    // 重试次数
    @Value("${lock.retry}")
    private Integer retry;


    public boolean unlock(RedisLockKey redisLockKey, Object key) {
        return unlock(redisLockKey.getKey() + ":" + key);
    }

    public boolean lock(RedisLockKey redisLockKey, Object key) {
        return lock(redisLockKey.getKey() + ":" + key);
    }

    /**
     * 获取锁
     */
    public boolean lock(String key) {
        return lock(key, String.valueOf(SnowFlakeUtil.newId()), timeout, TimeUnit.SECONDS, retry);
    }

    /**
     * 获取锁
     */
    public boolean lock(String key, String value, long timeout, TimeUnit unit, int retry) {
        try {
            if (retry < 0) {
                return false;
            }
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockPrefix + ":" + key, value, timeout, unit);
            if (Boolean.TRUE.equals(lock)) {
                return true;
            } else {
                Thread.sleep(100);   // 加锁失败，重试
                return lock(key, value, timeout, unit, retry - 1);
            }
        } catch (Exception e) {
            throw new ServiceException("获取Redis锁异常：" + e.getMessage());
        }
    }

    /**
     * 释放锁
     * @param key
     */
    public boolean unlock(String key) {
        return Boolean.TRUE.equals(redisTemplate.delete(lockPrefix + ":" + key));
    }

}
