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

// 声明该类所在的包名，表明这个类属于 `com.yami.shop.bean` 包，在Java项目中，包用于对类进行分类组织，方便代码管理和避免类名冲突等情况。
package com.yami.shop.bean;

import java.util.ArrayList;
import java.util.List;

// 导入 `SmsInfoBo` 类，从类名推测它是与短信信息相关的业务对象（Business Object），这个类应该包含了短信相关的各种属性以及对应的业务逻辑所需要的数据结构，会在本类中用于存储和操作短信相关的信息。
import com.yami.shop.bean.bo.SmsInfoBo;
// 导入 `hutool` 工具库中用于集合操作的工具类 `CollectionUtil`，它提供了许多便捷的方法来处理集合相关的常见操作，例如判断集合是否为空、对集合进行合并、拆分等操作，在这里主要用于判断短信信息列表是否为空的情况。
import cn.hutool.core.collection.CollectionUtil;

/**
 * @author lanhai
 * 这个类 `SmsInfoContext` 的作用是作为短信信息的上下文管理类，它利用 `ThreadLocal` 机制来实现在多线程环境下，每个线程都能独立地管理自己的短信信息集合（以 `SmsInfoBo` 列表形式存储），
 * 这样做的好处在于，不同线程在处理与短信相关的业务逻辑时，各自的短信信息不会相互干扰，确保了数据的独立性和安全性，避免了多线程并发访问时可能出现的数据不一致、并发修改等问题。
 * 此类提供了获取、设置、添加以及清理短信信息的相关方法，方便在不同的业务逻辑中对当前线程的短信信息进行操作，使整个短信业务处理在多线程环境下更加有序和可控。
 */
public class SmsInfoContext {

    /**
     * The request holder.
     * 定义一个静态的 `ThreadLocal` 变量 `smsInfoHolder`，用于在每个线程中存储短信信息列表（类型为 `List<SmsInfoBo>`）。
     * `ThreadLocal` 是Java中的一种特殊机制，它的核心特点是让每个线程都有自己独立的变量副本，也就是说，不同线程通过这个 `smsInfoHolder` 访问到的短信信息列表是相互独立的，互不影响，
     * 这就为多线程环境下安全地处理各自线程相关的短信业务数据提供了基础保障，例如在一个Web应用中，多个用户请求可能同时触发短信相关的业务操作，每个请求对应的线程就可以通过这个机制来管理自己的短信信息，不会相互干扰。
     */
    private static ThreadLocal<List<SmsInfoBo>> smsInfoHolder = new ThreadLocal<List<SmsInfoBo>>();

    /**
     * 获取当前线程中存储的短信信息列表。
     * 首先通过 `smsInfoHolder.get()` 方法尝试获取当前线程对应的短信信息列表，这个方法是 `ThreadLocal` 提供的用于获取当前线程副本中存储的对象的方法。
     * 然后使用 `CollectionUtil.isEmpty(list)` 判断获取到的列表是否为空，`CollectionUtil.isEmpty` 是 `hutool` 工具库提供的便捷方法，用于判断集合是否为空（集合为 `null` 或者集合中没有元素都视为空）。
     * 如果为空（意味着当前线程还没有设置过短信信息列表或者之前设置的列表被清理了等情况），则返回一个新创建的空的 `ArrayList`，这样调用这个方法的地方可以放心地使用返回的列表进行后续操作，不用担心出现空指针异常等问题，保证了方法调用的健壮性。
     * 如果获取到的列表不为空，则直接返回当前线程通过 `smsInfoHolder` 存储的短信信息列表，以便后续对已有的短信信息进行操作，比如遍历、修改等，使得业务逻辑可以基于已有的短信信息继续进行处理。
     *
     * @return 当前线程中存储的短信信息列表，如果当前线程原本没有设置过短信信息列表，则返回一个新的空列表。
     */
    public static List<SmsInfoBo> get() {
        List<SmsInfoBo> list = smsInfoHolder.get();
        if (CollectionUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        return smsInfoHolder.get();
    }

    /**
     * 设置当前线程中存储的短信信息列表。
     * 该方法接收一个 `List<SmsInfoBo>` 类型的参数 `smsInfoBos`，这个参数代表了要设置到当前线程的短信信息列表，它包含了在当前业务逻辑下需要处理的短信相关信息。
     * 然后通过 `smsInfoHolder.set(smsInfoBos)` 将这个参数对应的短信信息列表设置到当前线程的 `ThreadLocal` 变量中，`ThreadLocal` 的 `set` 方法会将传入的对象与当前线程进行关联存储，
     * 这样在当前线程后续的其他代码中通过 `get` 方法获取到的就是新设置的这个短信信息列表了，方便在不同的业务逻辑阶段更新当前线程所使用的短信信息数据，确保业务逻辑可以根据最新的短信信息进行相应的处理。
     *
     * @param smsInfoBos 要设置到当前线程的短信信息列表。
     */
    public static void set(List<SmsInfoBo> smsInfoBos) {
        smsInfoHolder.set(smsInfoBos);
    }

    /**
     * 向当前线程中存储的短信信息列表中添加一条短信信息（`SmsInfoBo` 对象）。
     * 首先通过 `smsInfoHolder.get()` 获取当前线程已有的短信信息列表，这一步是为了获取当前线程之前可能已经存储的短信信息，以便后续在这个基础上进行添加操作。
     * 接着使用 `CollectionUtil.isEmpty(smsInfoBos)` 判断这个列表是否为空，同样是利用 `hutool` 工具库的方法来判断集合是否为空。
     * 如果为空，说明当前线程还没有短信信息列表，那么就创建一个新的空的 `ArrayList` 作为初始的短信信息列表，为添加新的短信信息做准备。
     * 然后将传入的短信信息对象 `smsInfoBo` 添加到这个列表中，通过 `smsInfoBos.add(smsInfoBo)` 操作实现向列表中添加元素，完成了短信信息的添加逻辑。
     * 最后再通过 `smsInfoHolder.set(smsInfoBos)` 将更新后的包含了新添加短信信息的列表重新设置回当前线程的 `ThreadLocal` 变量中，确保线程中存储的短信信息列表始终是最新的状态，方便后续继续操作，例如在后续的业务逻辑中再次获取该列表进行遍历、发送短信等操作时，能获取到完整且最新的短信信息。
     *
     * @param smsInfoBo 要添加到当前线程短信信息列表中的短信信息对象。
     */
    public static void put(SmsInfoBo smsInfoBo) {
        List<SmsInfoBo> smsInfoBos = smsInfoHolder.get();
        if (CollectionUtil.isEmpty(smsInfoBos)) {
            smsInfoBos = new ArrayList<>();
        }
        smsInfoBos.add(smsInfoBo);
        smsInfoHolder.set(smsInfoBos);
    }

    /**
     * 清理当前线程中存储的短信信息列表。
     * 首先通过 `smsInfoHolder.get()` 获取当前线程中存储的短信信息列表，判断其是否不为 `null`，这一步是为了避免在 `ThreadLocal` 中没有存储任何对象时进行不必要的移除操作，防止出现空指针异常等问题。
     * 如果不为 `null`，则调用 `smsInfoHolder.remove()` 方法将当前线程对应的短信信息列表从 `ThreadLocal` 变量中移除，`ThreadLocal` 的 `remove` 方法会解除当前线程与存储对象之间的关联，
     * 这样当前线程后续再通过 `get` 等方法获取短信信息列表时，得到的就是初始的默认状态（比如为空的情况）。这个方法常用于在某个业务逻辑执行完毕后，清理当前线程相关的短信信息数据，防止数据残留导致的内存占用以及可能出现的数据混乱等问题，
     * 例如在一次短信发送业务完成后，及时清理当前线程的短信信息，为下一次相关业务操作做好准备，保证每个线程在不同业务阶段的短信信息独立性和数据的整洁性。
     */
    public static void clean() {
        if (smsInfoHolder.get()!= null) {
            smsInfoHolder.remove();
        }
    }
}