package org.jk.rag.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.jk.common.core.config.DeepseekConfig;
import org.jk.common.sse.core.SseEmitterManager;
import org.jk.common.sse.dto.SseMessageDto;
import org.jk.rag.domain.entity.*;
import org.jk.rag.mapper.JkDocumentMapper;
import org.jk.rag.mapper.JkKnowledgeMapper;
import org.jk.rag.service.ConfigSyncService;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
@RequiredArgsConstructor
public class RedisSerialRateLimiter {

    private final RedissonClient redissonClient;
    private final ConfigSyncService configSyncService;
    private final JkDocumentMapper jkDocumentMapper;
    private final DeepseekConfig deepseekConfig;
    private final JkKnowledgeMapper jkKnowledgeMapper;
    private final SseEmitterManager sseEmitterManager;

    private static final Logger logger = LoggerFactory.getLogger(RedisSerialRateLimiter.class);
    private static final String QUEUE_PREFIX = "company_queue:";
    private static final String CONCURRENT_HASH_KEY = "company:current:concurrent";
    private volatile boolean scanRunning = true;
    // 全局默认并发数（可统一修改，替代硬编码的2）
    private static final Integer DEFAULT_MAX_CONCURRENT = 2;

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class SerializableTask implements Serializable {
        private String taskId;
        private String companyId;
        private String taskContent;
        private long submitTime;
    }

    /**
     * 提交任务 - 核心逻辑：严格按maxConcurrent限制并发，入队后不自动触发消费
     */
    public CompletableFuture<Boolean> submitTask(String companyId, String taskContent, Runnable task, Boolean flg) {
        // 前置校验：companyId不能为空
        if (companyId == null || companyId.trim().isEmpty()) {
            logger.error("提交任务失败：companyId不能为空");
            return CompletableFuture.completedFuture(false);
        }
        return CompletableFuture.supplyAsync(() -> {
            try {
                Integer maxConcurrent = getMaxConcurrentFromDatabase(companyId);
                // 二次校验：确保maxConcurrent有效
                if (maxConcurrent == null || maxConcurrent <= 0) {
                    logger.error("公司 {} 提交任务失败：有效并发配置为null/≤0，拒绝执行", companyId);
                    return false;
                }
                logger.info("公司 {} 提交任务【{}】，最终生效最大并发配置: {}", companyId, taskContent.substring(0, 20), maxConcurrent);

                // 1. 严格获取执行许可（CAS保证原子性）
                boolean acquired = tryAcquireConcurrent(companyId, maxConcurrent);
                if (acquired) {
                    // 2. 原子化二次校验：用Redis原子操作避免竞态
                    acquired = atomicCheckAndRelease(companyId, maxConcurrent);
                }

                if (acquired) {
                    executeTaskInParallel(companyId, task, maxConcurrent, flg);
                    return true;
                } else {
                    // 3. 未获取到许可，任务入队（不自动触发消费）
                    SerializableTask serializableTask = new SerializableTask(
                        generateTaskId(),
                        companyId,
                        taskContent,
                        System.currentTimeMillis()
                    );
                    String queueKey = QUEUE_PREFIX + companyId;
                    RBlockingQueue<SerializableTask> taskQueue = redissonClient.getBlockingQueue(queueKey);
                    taskQueue.put(serializableTask);
                    logger.info("公司 {} 任务【{}】入队（当前并发已满，队列长度: {}）",
                        companyId, serializableTask.getTaskId(), taskQueue.size());
                    return false;
                }
            } catch (Exception e) {
                logger.error("公司 {} 提交任务失败", companyId, e);
                return false;
            }
        });
    }

    /**
     * 并行执行任务 - 正常/异常都释放许可，不自动触发消费
     */
    private void executeTaskInParallel(String companyId, Runnable task, Integer maxConcurrent, Boolean flg) {
        String taskThreadName = "Task-" + companyId + "-" + System.currentTimeMillis() + "-" + (int) (Math.random() * 1000);
        Thread taskThread = new Thread(() -> {
            Thread.currentThread().setName(taskThreadName);
            int currentCount = getCurrentConcurrent(companyId);
            logger.info("线程 {} 开始执行公司 {} 的任务 | 当前并发数: {}/{}",
                taskThreadName, companyId, currentCount, maxConcurrent);

            try {
                long startTime = System.currentTimeMillis();
                task.run();
                long costTime = System.currentTimeMillis() - startTime;
                logger.info("线程 {} 执行完成，耗时 {}ms | 公司 {} 执行完成，准备释放许可",
                    taskThreadName, costTime, companyId);
            } catch (Exception e) {
                logger.error("线程 {} 执行失败", taskThreadName, e);
                // 无论正常/异常，都释放并发许可
                try {
                    releaseConcurrent(companyId);
                    int afterCount = getCurrentConcurrent(companyId);
                    logger.info("线程 {} 释放许可后，公司 {} 并发数: {}/{}",
                        taskThreadName, companyId, afterCount, maxConcurrent);
                } catch (Exception ee) {
                    logger.error("线程 {} 释放计数失败，强制删除计数器", taskThreadName, ee);
                    String counterKey = CONCURRENT_HASH_KEY + ":" + companyId;
                    redissonClient.getAtomicLong(counterKey).delete();
                }
            }
        });
        taskThread.setDaemon(true);
        taskThread.start();
    }

    /**
     * 释放指定公司的一个队列任务（Controller唯一调用入口）
     */
    public ReleaseResult releaseOneQueueTask(String companyId) {
        if (companyId == null || companyId.trim().isEmpty()) {
            logger.error("释放队列任务失败：companyId不能为空");
            return new ReleaseResult(false, "companyId不能为空", 0, 0);
        }
        companyId = companyId.trim();

        try {
            String queueKey = QUEUE_PREFIX + companyId;
            RBlockingQueue<SerializableTask> taskQueue = redissonClient.getBlockingQueue(queueKey);
            int queueSize = taskQueue.size();
            int beforeReleaseCount = getCurrentConcurrent(companyId);

            // 队列为空时不释放，直接返回
            if (queueSize == 0) {
                logger.info("公司 {} 释放队列任务：队列为空，无需释放 | 当前并发数: {}", companyId, beforeReleaseCount);
                return new ReleaseResult(true, "队列为空，无需释放", 0, beforeReleaseCount);
            }

            // 手动触发队列消费
            logger.info("公司 {} 手动触发队列消费，当前队列长度: {} | 并发数: {}",
                companyId, queueSize, beforeReleaseCount);
            triggerQueueConsumption(companyId, false);

            int latestQueueSize = taskQueue.size();
            int afterCount = getCurrentConcurrent(companyId);
            logger.info("公司 {} 手动触发消费完成 | 队列长度: {} | 当前并发数: {}",
                companyId, latestQueueSize, afterCount);
            return new ReleaseResult(true, "手动触发队列消费成功", latestQueueSize, afterCount);

        } catch (Exception e) {
            logger.error("公司 {} 手动释放队列任务失败", companyId, e);
            return new ReleaseResult(false, "释放失败：" + e.getMessage(), 0, getCurrentConcurrent(companyId));
        }
    }

    /**
     * 触发队列消费 - 精准校准脏计数，严格按maxConcurrent执行
     */
    private void triggerQueueConsumption(String companyId, Boolean flg) {
        CompletableFuture.runAsync(() -> {
            String lockKey = "consume_lock:" + companyId;
            RLock consumeLock = redissonClient.getLock(lockKey);
            boolean locked = false;
            try {
                locked = consumeLock.tryLock(0, 5, TimeUnit.SECONDS);
                if (!locked) {
                    logger.debug("公司 {} 已有消费线程在执行，跳过触发", companyId);
                    return;
                }

                Integer maxConcurrent = getMaxConcurrentFromDatabase(companyId);
                if (maxConcurrent == null || maxConcurrent <= 0) {
                    logger.error("公司 {} 消费队列失败：有效并发配置为null/≤0", companyId);
                    return;
                }
                String queueKey = QUEUE_PREFIX + companyId;
                RBlockingQueue<SerializableTask> taskQueue = redissonClient.getBlockingQueue(queueKey);
                int redisCount = getCurrentConcurrent(companyId);
                int activeThreadCount = getActiveTaskThreadCount(companyId);

                logger.info("公司 {} 消费前校准 | Redis计数: {} | 实际活跃线程数: {} | max: {} | 队列长度: {}",
                    companyId, redisCount, activeThreadCount, maxConcurrent, taskQueue.size());

                // 精准脏计数校准
                if (redisCount > activeThreadCount) {
                    logger.warn("公司 {} 检测到脏计数，校准 | Redis计数: {} → 实际活跃线程数: {}",
                        companyId, redisCount, activeThreadCount);
                    String counterKey = CONCURRENT_HASH_KEY + ":" + companyId;
                    redissonClient.getAtomicLong(counterKey).set(activeThreadCount);
                    redisCount = activeThreadCount;
                }
                if (redisCount > maxConcurrent) {
                    logger.warn("公司 {} 计数溢出，校准 | Redis计数: {} → max: {}", companyId, redisCount, maxConcurrent);
                    String counterKey = CONCURRENT_HASH_KEY + ":" + companyId;
                    redissonClient.getAtomicLong(counterKey).set(maxConcurrent);
                    redisCount = maxConcurrent;
                }

                // 严格判断：计数满/队列为空则返回
                if (redisCount >= maxConcurrent || taskQueue.isEmpty()) {
                    logger.debug("公司 {} 无需消费，直接返回 | 计数: {}/{} | 队列长度: {}",
                        companyId, redisCount, maxConcurrent, taskQueue.size());
                    return;
                }

                // 获取许可执行队列任务
                boolean acquired = tryAcquireConcurrent(companyId, maxConcurrent);
                if (!acquired) {
                    logger.error("公司 {} 申请队列任务许可失败（计数={}，max={}）",
                        companyId, redisCount, maxConcurrent);
                    return;
                }

                SerializableTask task = taskQueue.poll(1, TimeUnit.SECONDS);
                if (task == null) {
                    logger.debug("公司 {} 队列无任务，释放许可", companyId);
                    releaseConcurrent(companyId);
                    return;
                }

                logger.info("公司 {} 取出队列任务【{}】执行 | 当前计数: {}/{}",
                    companyId, task.getTaskId(), getCurrentConcurrent(companyId), maxConcurrent);
                executeQueuedTask(companyId, task, maxConcurrent, flg);

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                logger.error("公司 {} 获取消费锁被中断", companyId, e);
            } catch (Exception e) {
                logger.error("公司 {} 消费队列失败", companyId, e);
            } finally {
                if (locked && consumeLock.isHeldByCurrentThread()) {
                    try {
                        consumeLock.unlock();
                    } catch (IllegalMonitorStateException e) {
                        logger.warn("公司 {} 释放消费锁异常，忽略", companyId);
                    }
                }
            }
        });
    }

    /**
     * 执行队列任务 - 正常/异常都释放许可，不自动触发消费
     */
    private void executeQueuedTask(String companyId, SerializableTask task, Integer maxConcurrent, Boolean flg) {
        String taskThreadName = "QueueTask-" + companyId + "-" + task.getTaskId();
        Thread taskThread = new Thread(() -> {
            Thread.currentThread().setName(taskThreadName);
            int currentCount = getCurrentConcurrent(companyId);
            logger.info("线程 {} 开始执行队列任务 | 当前并发数: {}/{}",
                taskThreadName, companyId, currentCount, maxConcurrent);

            try {
                long startTime = System.currentTimeMillis();
                Runnable reconstructedTask = reconstructTaskFromContent(task.getTaskContent(), flg);
                reconstructedTask.run();
                long costTime = System.currentTimeMillis() - startTime;
                logger.info("线程 {} 队列任务执行完成，耗时 {}ms | 准备释放许可", taskThreadName, costTime);
            } catch (Exception e) {
                logger.error("线程 {} 队列任务执行失败", taskThreadName, e);
                // 无论正常/异常，都释放并发许可
                try {
                    releaseConcurrent(companyId);
                    int afterCount = getCurrentConcurrent(companyId);
                    logger.info("线程 {} 释放许可后，公司 {} 并发数: {}/{}",
                        taskThreadName, companyId, afterCount, maxConcurrent);
                } catch (Exception ee) {
                    logger.error("线程 {} 释放队列任务计数失败，强制删除计数器", taskThreadName, ee);
                    String counterKey = CONCURRENT_HASH_KEY + ":" + companyId;
                    redissonClient.getAtomicLong(counterKey).delete();
                }
            }
        });
        taskThread.setDaemon(true);
        taskThread.start();
    }

    // ==================== 核心原子操作 ====================
    /**
     * 原子获取并发许可 - 严格CAS，确保并发数≤max
     */
    private boolean tryAcquireConcurrent(String companyId, Integer maxConcurrent) {
        String counterKey = CONCURRENT_HASH_KEY + ":" + companyId;
        RAtomicLong concurrentCounter = redissonClient.getAtomicLong(counterKey);

        int retryCount = 0;
        while (retryCount < 3) {
            long current = concurrentCounter.get();
            if (current >= maxConcurrent) {
                logger.info("公司 {} 并发已满 | current={}/max={} | 重试次数: {}",
                    companyId, current, maxConcurrent, retryCount);
                return false;
            }
            // CAS原子递增，确保同一时间只有一个线程成功
            if (concurrentCounter.compareAndSet(current, current + 1)) {
                concurrentCounter.expire(3600, TimeUnit.SECONDS); // 1小时过期，防止内存泄漏
                logger.info("公司 {} CAS成功 | current={} → {} / max={} | 重试次数: {}",
                    companyId, current, current + 1, maxConcurrent, retryCount);
                return true;
            }
            retryCount++;
            logger.debug("公司 {} CAS重试 | current={}已被修改 | 重试次数: {}",
                companyId, current, retryCount);
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
        logger.error("公司 {} CAS重试3次失败，放弃获取许可", companyId);
        return false;
    }

    /**
     * 原子释放并发许可 - 仅递减，避免负数
     */
    public void releaseConcurrent(String companyId) {
        String counterKey = CONCURRENT_HASH_KEY + ":" + companyId;
        RAtomicLong concurrentCounter = redissonClient.getAtomicLong(counterKey);

        long current = concurrentCounter.get();
        if (current > 0) {
            long newCurrent = concurrentCounter.decrementAndGet();
            logger.info("公司 {} 释放许可 | current={} → {}", companyId, current, newCurrent);
            if (newCurrent <= 0) {
                concurrentCounter.delete();
                logger.debug("公司 {} 计数器已清空", companyId);
            }
        } else {
            concurrentCounter.delete();
            logger.debug("公司 {} 无计数，删除计数器", companyId);
        }
    }

    /**
     * 原子化二次校验 - 修复竞态问题
     */
    private boolean atomicCheckAndRelease(String companyId, Integer maxConcurrent) {
        String counterKey = CONCURRENT_HASH_KEY + ":" + companyId;
        RAtomicLong concurrentCounter = redissonClient.getAtomicLong(counterKey);
        long current = concurrentCounter.get();
        if (current > maxConcurrent) {
            logger.error("公司 {} 并发超限，原子释放许可 | current={}/max={}", companyId, current, maxConcurrent);
            releaseConcurrent(companyId);
            return false;
        }
        return true;
    }

    /**
     * 获取当前并发数 - 纯读取
     */
    private Integer getCurrentConcurrent(String companyId) {
        String counterKey = CONCURRENT_HASH_KEY + ":" + companyId;
        RAtomicLong concurrentCounter = redissonClient.getAtomicLong(counterKey);
        return (int) concurrentCounter.get();
    }

    // ==================== 关键：获取实际活跃任务线程数 ====================
    private int getActiveTaskThreadCount(String companyId) {
        AtomicInteger activeCount = new AtomicInteger(0);
        Thread.getAllStackTraces().keySet().forEach(thread -> {
            String threadName = thread.getName();
            // 匹配并行任务和队列任务的线程名
            if (thread.isAlive() && (
                threadName.startsWith("Task-" + companyId + "-") ||
                    threadName.startsWith("QueueTask-" + companyId + "-")
            )) {
                activeCount.incrementAndGet();
            }
        });
        return activeCount.get();
    }

    // ==================== 核心修复：增强版数据库配置获取 ====================
    /**
     * 从数据库获取最大并发数 - 增强日志+空值校验+异常兜底
     */
    private Integer getMaxConcurrentFromDatabase(String companyId) {
        Integer maxConcurrent = null;
        try {
            // 调用配置服务获取数据库值
            maxConcurrent = configSyncService.getMaxConcurrent(companyId);
            // 打印原始获取值（关键排查日志）
            logger.info("公司 {} 从配置服务获取原始最大并发数: {}", companyId, maxConcurrent);

            // 校验配置有效性
            if (maxConcurrent != null && maxConcurrent > 0) {
                logger.info("公司 {} 数据库配置有效，使用并发数: {}", companyId, maxConcurrent);
                return maxConcurrent;
            } else {
                // 配置无效：null/0/负数
                logger.warn("公司 {} 数据库配置无效（值: {}），请检查数据库配置！将使用默认值: {}",
                    companyId, maxConcurrent, DEFAULT_MAX_CONCURRENT);
                return DEFAULT_MAX_CONCURRENT;
            }
        } catch (Exception e) {
            // 获取失败：配置服务异常/SQL异常/网络异常
            logger.error("公司 {} 从数据库获取并发配置失败！异常原因: {}",
                companyId, e.getMessage(), e);
            logger.warn("公司 {} 获取配置失败，将使用默认值: {}", companyId, DEFAULT_MAX_CONCURRENT);
            return DEFAULT_MAX_CONCURRENT;
        }
    }

    // ==================== 辅助方法（无修改，保留原有逻辑） ====================
    private Runnable reconstructTaskFromContent(String taskContent, Boolean flg) {
        return () -> {
            logger.info("=== 开始执行队列任务 ===");
            try {
                Map<String, Object> contentMap = JSON.parseObject(taskContent);
                Map<String, Object> documentParamsMap = (Map<String, Object>) contentMap.get("documentParams");
                JkDocumentParams documentParams = JSON.parseObject(
                    JSON.toJSONString(documentParamsMap),
                    JkDocumentParams.class
                );
                executeUploadDocumentLogic(documentParams, flg);
            } catch (Exception e) {
                logger.error("队列任务执行失败", e);
                throw new RuntimeException("任务执行失败", e);
            }
        };
    }

    private void executeUploadDocumentLogic(JkDocumentParams documentParams, Boolean flg) {
        try {
            logger.info("开始执行文档上传: {}", documentParams);
            jkDocumentMapper.updateDocument(JkDocument.builder().documentId(documentParams.getDocumentId()).status("0").build());
            List<UploadDocumentParams> params = new ArrayList<>();
            UploadDocumentParams param = new UploadDocumentParams();
            param.setDocument_id(documentParams.getDocumentId());
            param.setName(documentParams.getName() + "." + documentParams.getSuffix());
            param.setUrl(documentParams.getUrl());
            param.setParsingType(documentParams.getParsingType());
            param.setCustomSeparator(documentParams.getCustomSeparator());
            param.setRemark(documentParams.getRemark());
            param.setUserId(documentParams.getUserId());
            param.setTenantId(documentParams.getTenantId());
            param.setStandardClassification(documentParams.getStandardClassification());
            if (flg) {
                JkSliceDocument oldDocument = jkDocumentMapper.selectDocumentByOssId(documentParams.getOssId(),documentParams.getCustomSeparator(),documentParams.getSliceValue(),documentParams.getParsingType(),documentParams.getStandardClassification());
                if(null != oldDocument && !oldDocument.getDocumentId().equals(documentParams.getDocumentId())) {
                    if(oldDocument.getStatus().equals("0")) {
                        jkDocumentMapper.updateDocument(JkDocument.builder().documentId(documentParams.getDocumentId()).status("2").errorMessage("重复文件上传错误").build());
                        SseMessageDto sseMessageDto = new SseMessageDto();
                        sseMessageDto.setUserIds(List.of(Long.valueOf(documentParams.getCreateBy())));
                        sseMessageDto.setMessage("{\"documentId\":\""+documentParams.getDocumentId()+"\",\"message\":\"error\",\"progress\":0,\"status\":\"2\"}");
                        sseEmitterManager.publishMessage(sseMessageDto);
                        return;
                    }
                    param.setOldDocumentId(null != oldDocument ? oldDocument.getDocumentId():null);
                    param.setOldKnowledgeId(null != oldDocument ? oldDocument.getKnowledgeId():null);
                }
            }
            if(documentParams.getSuffix().contains("md")) {
                param.setMarkDownFlg("true");
            }
            params.add(param);
            JkDocumentSettings settings = new JkDocumentSettings();
            settings.setKnowledgeId(documentParams.getKnowledge_id());
            analysisFile(params, settings, "upload");
        } catch (Exception e) {
            logger.error("文档上传失败", e);
            throw new RuntimeException("文档上传失败", e);
        }
    }

    private JSONObject analysisFile(List<UploadDocumentParams> result, JkDocumentSettings settings, String flag) {
        String url = deepseekConfig.getBaseurl() + deepseekConfig.getUploadKnowledge();
        List<JkKnowledge> knowledgeList = jkKnowledgeMapper.selectAllKnowledgeList(JkKnowledge.builder().knowledgeId(settings.getKnowledgeId()).build());
        JSONObject json = new JSONObject();
        json.put("knowledge_id", settings.getKnowledgeId());
        json.put("embedding_id", knowledgeList.get(0).getEmbeddingId());
        json.put("docs", result);
        json.put("userId", result.get(0).getUserId());
        json.put("tenantId", result.get(0).getTenantId());
        json.put("customSeparator", result.get(0).getCustomSeparator());
        json.put("parsingType", result.get(0).getParsingType());
        json.put("oldDocumentId",null != result.get(0).getOldDocumentId() ? result.get(0).getOldDocumentId() : null);
        json.put("oldKnowledgeId",null != result.get(0).getOldKnowledgeId() ? result.get(0).getOldKnowledgeId() : null);
        if (null != result.get(0).getRemark()) {
            json.put("remark", result.get(0).getRemark());
        }
        if (result.get(0).getStandardClassification().equals("2")) {
            json.put("excel_parsing", 1);
        } else {
            json.put("excel_parsing", 0);
        }
        json.put("flag", "upload".equals(flag) ? "upload" : "update");

        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), json.toJSONString());
        Request request = buildPostRequest(url, requestBody);
        OkHttpClient client = buildOkHttpClient();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                String body = response.body().string();
                JSONObject obj = JSON.parseObject(body);
                Integer code = obj.getInteger("code");
                if (code == 200) {
                    logger.info("上传文档调用python接口成功,返回内容：{}", body);
                } else {
                    logger.info("上传文档调用python接口失败,返回内容：{}", body);
                }
                return obj;
            } else {
                logger.error("上传文档调用python接口失败返回code：{}", response.code());
                return null;
            }
        } catch (IOException e) {
            logger.error("上传文档调用python接口失败", e.getMessage());
            return null;
        }
    }

    private Request buildPostRequest(String url, RequestBody requestBody) {
        return new Request.Builder()
            .addHeader("User-Agent", "insomnia/10.3.1")
            .addHeader("Content-Type", "application/json")
            .url(url).post(requestBody).build();
    }

    private OkHttpClient buildOkHttpClient() {
        return new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(50, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.MINUTES)
            .build();
    }

    private String generateTaskId() {
        return "task-" + System.currentTimeMillis() + "-" + (int) (Math.random() * 1000);
    }

    // ==================== 全局扫描线程（仅检测队列，不触发消费） ====================
    @PostConstruct
    public void startGlobalQueueScanner() {
        Thread scannerThread = new Thread(() -> {
            logger.info("全局队列扫描线程已启动（仅检测队列长度，不触发消费）");
            while (scanRunning) {
                try {
                    Iterable<String> queueKeys = redissonClient.getKeys().getKeysByPattern(QUEUE_PREFIX + "*");
                    for (String queueKey : queueKeys) {
                        String companyId = queueKey.replace(QUEUE_PREFIX, "");
                        // 仅检测队列长度，打印日志
                        int queueSize = redissonClient.getBlockingQueue(queueKey).size();
                        logger.debug("全局扫描：公司 {} 队列长度: {}", companyId, queueSize);
                    }
                    TimeUnit.SECONDS.sleep(5); // 延长扫描间隔，减少Redis压力
                } catch (Exception e) {
                    logger.error("全局队列扫描异常", e);
                }
            }
        }, "GlobalQueueScanner");
        scannerThread.setDaemon(true);
        scannerThread.start();
    }

    public void stopScanner() {
        scanRunning = false;
    }

    /**
     * 释放结果返回体
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class ReleaseResult implements Serializable {
        private boolean success;    // 是否成功
        private String msg;        // 结果描述
        private int queueSize;     // 目标公司队列当前长度
        private int currentConcurrent; // 当前并发数
    }
}
