/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.common.annotation;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * `RedisLock`是一个自定义的注解，用于标记需要使用Redis实现分布式锁的方法。在分布式系统中，当多个实例或者线程可能同时访问和修改同一份资源时，
 * 为了保证数据的一致性和避免并发冲突，常常需要使用分布式锁机制来控制对关键资源的访问，而这个注解就是为了方便地在代码中指定哪些方法需要这样的分布式锁保护，
 * 并且可以配置锁相关的一些关键参数，如锁的名称、对应的Redis键、过期时间等。
 *
 * @author lanhai
 */
@Target(ElementType.METHOD) // 表明该注解可以应用在方法上，意味着只能将这个注解标记在方法声明处，用于对方法添加分布式锁相关的配置。
@Retention(RetentionPolicy.RUNTIME) // 指定注解的保留策略为运行时，即在运行时可以通过反射等机制获取到该注解的相关信息，从而根据注解配置来实现分布式锁的逻辑。
@Documented // 表示该注解会被包含在生成的JavaDoc文档中，方便开发人员查看和了解该注解的相关信息。
public @interface RedisLock {

    /**
     * redis锁名字，用于在Redis中标识这个锁，方便在查看Redis存储的锁信息或者进行锁相关的管理操作（如排查锁问题、手动释放锁等）时，能够快速区分不同的锁。
     * 默认值为空字符串，在使用注解时，如果没有显式指定这个值，就会使用默认的空字符串作为锁的名称，通常建议根据具体业务场景和方法功能来赋予一个有意义的名称，比如根据业务模块和操作类型命名，像 "orderService_createOrder_lock" 表示订单服务中创建订单操作对应的锁。
     */
    String lockName() default "";

    /**
     * redis锁key支持spel表达式，这个key是在Redis中实际存储锁信息的键，通过支持spel表达式（Spring Expression Language，一种强大的表达式语言，用于在运行时动态地解析表达式并获取对应的值），
     * 可以根据方法的参数、上下文等信息灵活地生成不同的键值，使得锁的应用更加贴合具体的业务场景和数据情况。例如，可以根据传入的订单ID动态生成对应的锁键，像 "#orderId" 这样的spel表达式，在实际应用时会根据方法调用传入的订单ID参数来确定具体的键值，确保不同的业务数据对应的锁是独立且准确的。默认值为空字符串，同样在未指定时使用默认值，不过一般都需要根据实际情况进行配置。
     */
    String key() default "";

    /**
     * 过期秒数，用于指定锁在Redis中自动过期的时间，单位是毫秒（因为属性名是 `expire`，虽然描述里写的是秒，但从代码实现角度结合 `TimeUnit` 来看实际是毫秒），默认为5000毫秒（即5秒）。
     * 设置合适的过期时间很重要，如果过期时间过短，可能导致业务逻辑还未执行完锁就提前释放了，从而出现并发问题；而过期时间过长，又可能导致锁长时间占用资源，影响其他需要获取该锁的操作，降低系统的并发性能。默认值提供了一个基础的时间设置，在实际应用中可以根据具体方法执行的预期时长等因素进行调整。
     *
     * @return 轮询锁的时间，这里从功能角度理解，实际就是返回锁的过期时间设置值，方便在实现分布式锁逻辑时获取并使用这个时间参数来控制锁的有效期。
     */
    int expire() default 5000;

    /**
     * 超时时间单位，用于明确前面 `expire` 属性所指定的时间的单位，默认是 `TimeUnit.MILLISECONDS`（毫秒），这样和 `expire` 属性默认的以毫秒为单位的时间值相匹配。
     * 不过，通过这个属性也可以灵活地将时间单位调整为其他如秒、分钟等，只要符合业务需求以及和 `expire` 的值配合合理即可，提供了更灵活的时间配置方式来适应不同场景下对锁过期时间的要求。
     *
     * @return 秒，这里文档注释里写的返回值是“秒”不太准确，实际返回的是 `TimeUnit` 枚举类型中的一种时间单位，用于明确 `expire` 属性对应的时间单位，在代码实现中更多是和 `expire` 配合使用来确定准确的时间范围。
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}