import json
import traceback

import aidso_geo.utils.bh_utils as bh_utils
import time
import os, sys
import ast
import requests
from copy import deepcopy
from datetime import datetime
from enum import Enum

import json
from concurrent.futures import ThreadPoolExecutor, as_completed

from aidso_geo.utils.tos_utils import put_string_to_tos, check_file_in_tos, get_tos_file_size
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)

# BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# sys.path.append(BASE_DIR)
from aidso_geo.config.base_config import init_redis
from aidso_geo.utils.ai_utils import ai_get_brand_sentiment_and_mentions, \
    ai_get_brand_sentiment_and_mentions_by_articles, ai_get_product_list, ai_get_product_sentiment_and_mentions, \
    ai_get_product_list_search
from aidso_geo.models import doubao_data_process, deepseek_data_process, yuanbao_data_process, qianwen_data_process, \
    kimi_data_process, wenxin_data_process, baiduai_data_process, doubao_android_data_process, \
    deepseek_android_data_process, douyinai_data_process, qianwen_android_data_process, yuanbao_android_data_process, \
    xiaohongshu_android_data_process
from aidso_geo.utils import ai_utils, spider_interface, ai_interface, url_utils, robot_utils
from aidso_geo.utils import tos_utils
from loguru import logger

redis_client = init_redis()


def _cache_set_json(key: str, obj: dict, ttl: int):
    redis_client.set(key, json.dumps(obj, ensure_ascii=False), ex=ttl)


def commit_task(data, process):
    try:
        data["insertime"] = int(time.time())
        data["status"] = process
        bh_utils.insert_data('geo_commit_task', [data])
        # bh_utils.insert_data('geo_commit_task_log', [data])
        return True
    except Exception as e:
        return False


def get_keyword_ranks(text, keywords):
    if isinstance(keywords, str):
        keywords = [keywords]

    matches = []

    for key in keywords:
        start = 0
        while True:
            pos = text.find(key, start)
            if pos == -1:
                break

            matches.append({
                "word": key,
                "pos": pos
            })

            # 允许继续查找后面的相同关键词
            start = pos + 1

    # 按出现位置排序；同位置时短词优先
    matches.sort(key=lambda x: (x["pos"], len(x["word"])))

    result = []
    for key in keywords:
        rank_list = [
            i + 1
            for i, item in enumerate(matches)
            if item["word"] == key
        ]
        result.append({
            "word": key,
            "rank_list": rank_list
        })

    return result


def calculate_rank_min_and_count_sum(data_list):
    positive_ranks = [item['rank'] for item in data_list if item['rank'] > 0]

    counts = [item['count'] for item in data_list]

    min_rank = min(positive_ranks) if positive_ranks else 0

    count_sum = sum(counts)

    return (min_rank, count_sum)


def process_com_map(data):
    if data.get("comWordsMap") is not None:
        com_words_map = data.get("comWordsMap")

        # 如果 comWordsMap 是字符串，解析成 list
        if isinstance(com_words_map, str):
            try:
                com_words_map = json.loads(com_words_map)
            except Exception:
                com_words_map = ast.literal_eval(com_words_map)

        # 兜底：保证是 list
        if not isinstance(com_words_map, list):
            com_words_map = []

        # 再兜底：过滤掉脏数据，避免里面混入 str
        com_words_map = [x for x in com_words_map if isinstance(x, dict)]

        # 写回 data，后面所有逻辑都用标准结构
        data["comWordsMap"] = com_words_map

    return data


def keyword_map_brand(keyword_list):
    def sql_quote(s: str) -> str:
        # 最基础的单引号转义
        return "'" + s.replace("\\", "\\\\").replace("'", "\\'") + "'"

    brand_map = []
    in_list = ",".join(sql_quote(x) for x in keyword_list)
    query_result = []
    if in_list:
        query_result = bh_utils.query_data(
            f"select brand,keyword from geo_keyword_brand_map where keyword in ({in_list}) group by keyword") or []

    found_keywords = {row.get("keyword") for row in (query_result or []) if row.get("keyword")}

    missing_keywords = [kw for kw in keyword_list if kw not in found_keywords]
    if missing_keywords:
        ai_brand = ai_utils.ai_get_brand_name(missing_keywords) or []
        if ai_brand:
            bh_utils.insert_data("geo_keyword_brand_map", ai_brand)
            brand_map.extend(ai_brand)

    brand_map.extend(query_result)

    return brand_map


def extract_keywords(data):
    if not data:
        return []

    result = []

    for item in data:
        if isinstance(item, str):
            if item.strip():
                result.append(item.strip())
        elif isinstance(item, dict):
            keywords = item.get("keywords", [])
            if isinstance(keywords, list):
                for kw in keywords:
                    if isinstance(kw, str) and kw.strip():
                        result.append(kw.strip())
            elif isinstance(keywords, str) and keywords.strip():
                result.append(keywords.strip())
    return result


def convert_rank_data(data):
    result = []

    for item in data:
        rank_list = item.get("rank_list", []) or []

        result.append({
            "word": item.get("word"),
            "rank": min(rank_list) if rank_list else 0,
            "count": len(rank_list)
        })

    return result


def cache_get_ai_brand_list(taskId, platform, response_content, prompt):
    result = bh_utils.query_data(
        f"select * from geo_ai_rank_list where taskId = '{taskId}' and platform = '{platform}' limit 1"
    )

    if result:
        db_row = result[0]
        ai_brand_list = db_row.get("ai_brand_list")

        if ai_brand_list:
            if isinstance(ai_brand_list, str):
                try:
                    return json.loads(ai_brand_list)
                except Exception:
                    return ai_brand_list
            return ai_brand_list

    ai_result = ai_utils.ai_get_brand_list(response_content, prompt)

    bh_utils.insert_data("geo_ai_rank_list", [{"taskId": taskId, "platform": platform, "ai_brand_list": ai_result}])

    return ai_result


def cache_get_ai_product_list(taskId, platform, response_content, prompt):
    result = bh_utils.query_data(
        f"select * from geo_ai_product_list where taskId = '{taskId}' and platform = '{platform}' limit 1"
    )

    if result:
        db_row = result[0]
        ai_brand_list = db_row.get("ai_brand_list")

        if ai_brand_list:
            if isinstance(ai_brand_list, str):
                try:
                    return json.loads(ai_brand_list)
                except Exception:
                    return ai_brand_list
            return ai_brand_list

    ai_result_search = ai_utils.ai_get_product_list(response_content, prompt)
    ai_result = ai_get_product_list_search(ai_result_search)
    bh_utils.insert_data("geo_ai_product_list", [{"taskId": taskId, "platform": platform, "ai_brand_list": ai_result}])

    return ai_result


def split_word_rank(ai_word_list, brand_words, com_word_list, all_word_rank_list):
    word_rank_map = {}
    for item in all_word_rank_list:
        word = item.get("word")
        if not word:
            continue
        word_rank_map[word] = {
            "rank": item.get("rank", 0),
            "count": item.get("count", 0),
            "brand": item.get("brand", '没有品牌'),
        }

    def build_result(word_list):
        result = []
        if word_list:
            for word in word_list:
                rank_info = word_rank_map.get(word, {})
                result.append({
                    "word": word,
                    "rank": rank_info.get("rank", 0),
                    "count": rank_info.get("count", 0),
                    "brand": rank_info.get("brand", '没有品牌')
                })
        return result

    ai_word_list_with_rank = build_result(ai_word_list)
    brand_words_rank = build_result(brand_words)
    com_word_list_rank = build_result(com_word_list)

    return ai_word_list_with_rank, brand_words_rank, com_word_list_rank


def keyword_with_brand(all_keyword_with_rank_sentiment, all_keyword_with_brand):
    keyword_brand_map = {
        item.get("keyword"): item.get("brand", "没有品牌")
        for item in all_keyword_with_brand
        if item.get("keyword")
    }

    for item in all_keyword_with_rank_sentiment:
        item["brand"] = keyword_brand_map.get(item.get("word"), "没有品牌")

    return all_keyword_with_rank_sentiment


def build_brand_summary(com_word_list_rank, com_brand):
    # 先构造 word -> 明细 的映射
    word_detail_map = {
        item.get("word"): item
        for item in com_word_list_rank
        if item.get("word") is not None
    }

    result = []

    for brand_item in com_brand:
        brand_name = brand_item.get("brand", "")
        keywords = brand_item.get("keywords") or []

        # 收集该品牌下的所有关键词明细
        vos = []
        for kw in keywords:
            detail = word_detail_map.get(kw)
            if detail:
                vos.append(detail.copy())
            else:
                # 如果关键词在 com_word_list_rank 里不存在，补一个默认结构
                vos.append({
                    "word": kw,
                    "rank": 0,
                    "count": 0
                })

        # 计算外层 rank
        non_zero_ranks = [item.get("rank", 0) for item in vos if item.get("rank", 0) != 0]
        final_rank = min(non_zero_ranks) if non_zero_ranks else 0

        # 计算外层 count
        final_count = sum(item.get("count", 0) or 0 for item in vos)

        result.append({
            "word": brand_name,
            "count": final_count,
            "rank": final_rank,
            "vos": vos
        })

    return result


def pick_brand_from_vos(vos):
    if not vos:
        return "没有品牌"

    # 1. 先找 rank 非 0 的，取 rank 最大的那条
    ranked_items = [item for item in vos if item.get("rank", 0) != 0]
    if ranked_items:
        best = max(ranked_items, key=lambda x: x.get("rank", 0))
        return best.get("brand", "没有品牌") or "没有品牌"

    # 2. 如果全是 0，取第一个不是“没有品牌”的
    for item in vos:
        brand = item.get("brand")
        if brand and brand != "没有品牌":
            return brand

    # 3. 如果全都是“没有品牌”
    return "没有品牌"


def fill_outer_brand(data):
    result = []

    for item in data:
        new_item = item.copy()
        vos = new_item.get("vos", [])
        new_item["brand"] = pick_brand_from_vos(vos)
        result.append(new_item)

    return result


def filter_ai_word_list(ai_word_list_with_rank, brand_words_rank, com_word_list_rank):
    # 收集 brand_words_rank 里的 brand
    def normalize_brand(brand):
        if not brand:
            return ""
        return str(brand).strip().lower()

    brand_words_brand_set = {
        normalize_brand(item.get("brand"))
        for item in brand_words_rank
        if item.get("brand")
    }

    com_words_brand_set = {
        normalize_brand(item.get("brand"))
        for item in com_word_list_rank
        if item.get("brand")
    }

    exclude_brand_set = brand_words_brand_set | com_words_brand_set

    result = []
    for item in ai_word_list_with_rank:
        brand = item.get("brand")

        if brand == "没有品牌":
            result.append(item)
            continue

        if normalize_brand(brand) not in exclude_brand_set:
            result.append(item)

    return result


def merge_brand_rank_by_subset(brand_words_rank, ai_word_list_with_rank):
    """
    规则：
    1. 第一轮：word 完全相等（忽略大小写）时，直接匹配
    2. 第二轮：即使 brand_item 已经有 rank/count，也继续参与匹配：
       - word 双向子串匹配（忽略大小写）
    3. 匹配成功后：
       - 第一轮：仍然保持原逻辑，直接用 ai 的 rank / count 覆盖
       - 第二轮：
         - rank 取更小的非 0 值
         - count 取更大的值
       - 删除该 ai 元素（返回剩余 ai 列表）
    4. 一个 ai 元素只能匹配一次
    """

    def to_int(v):
        try:
            return int(v or 0)
        except Exception:
            return 0

    def merge_rank(old_rank, old_count, new_rank, new_count):
        old_rank = to_int(old_rank)
        old_count = to_int(old_count)
        new_rank = to_int(new_rank)
        new_count = to_int(new_count)

        # rank：取更小的非0值，0 视为无效
        if old_rank > 0 and new_rank > 0:
            rank = min(old_rank, new_rank)
        elif old_rank > 0:
            rank = old_rank
        elif new_rank > 0:
            rank = new_rank
        else:
            rank = 0

        # count：取更大值
        count = max(old_count, new_count)

        return rank, count

    brand_list = deepcopy(brand_words_rank)
    ai_list = deepcopy(ai_word_list_with_rank)
    used_ai_index = set()
    first_round_matched_brand_index = set()

    # ========== 第一轮：word 完全相等，忽略 brand ==========
    for brand_index, brand_item in enumerate(brand_list):
        brand_word = str(brand_item.get("word", "") or "").strip()
        brand_word_lower = brand_word.lower()

        if not brand_word_lower:
            continue

        for ai_index, ai_item in enumerate(ai_list):
            if ai_index in used_ai_index:
                continue

            ai_word = str(ai_item.get("word", "") or "").strip()
            ai_word_lower = ai_word.lower()

            if not ai_word_lower:
                continue

            if brand_word_lower == ai_word_lower:
                # 第一轮保持原逻辑：直接覆盖
                brand_item["rank"] = ai_item.get("rank", 0)
                brand_item["count"] = ai_item.get("count", 0)
                used_ai_index.add(ai_index)
                first_round_matched_brand_index.add(brand_index)
                break

    # ========== 第二轮：word 双向子串匹配，忽略 brand ==========
    for brand_index, brand_item in enumerate(brand_list):
        # 这里不再跳过第一轮命中的 brand_item
        brand_word = str(brand_item.get("word", "") or "").strip()
        brand_word_lower = brand_word.lower()

        if not brand_word_lower:
            continue

        for ai_index, ai_item in enumerate(ai_list):
            if ai_index in used_ai_index:
                continue

            ai_word = str(ai_item.get("word", "") or "").strip()
            ai_word_lower = ai_word.lower()

            if not ai_word_lower:
                continue

            if brand_word_lower in ai_word_lower or ai_word_lower in brand_word_lower:
                merged_rank, merged_count = merge_rank(
                    brand_item.get("rank", 0),
                    brand_item.get("count", 0),
                    ai_item.get("rank", 0),
                    ai_item.get("count", 0),
                )
                brand_item["rank"] = merged_rank
                brand_item["count"] = merged_count
                used_ai_index.add(ai_index)
                # 这里不要 break，继续把后面还能匹配的 ai 一并吃掉

    remain_ai_list = [
        item for idx, item in enumerate(ai_list)
        if idx not in used_ai_index
    ]

    return brand_list, remain_ai_list



def to_int(v):
    try:
        return int(v or 0)
    except Exception:
        return 0


def merge_rank(old_rank, old_count, new_rank, new_count):
    old_rank = to_int(old_rank)
    old_count = to_int(old_count)
    new_rank = to_int(new_rank)
    new_count = to_int(new_count)

    # rank：取更小的非 0 值
    if old_rank > 0 and new_rank > 0:
        rank = min(old_rank, new_rank)
    elif old_rank > 0:
        rank = old_rank
    elif new_rank > 0:
        rank = new_rank
    else:
        rank = 0

    # count：取更大值
    count = max(old_count, new_count)

    return rank, count


def merge_com_and_ai_by_subset(brand_words_rank, ai_word_list_with_rank):
    """
    规则：
    1. 第一轮：vos 中的 word 和 ai.word 完全相等（忽略大小写）时，直接匹配
    2. 第二轮：即使 vos 已经有 rank/count，也继续参与匹配：
       - word 双向子串匹配（忽略大小写）
    3. 匹配成功后：
       - 第一轮：直接用 ai 的 rank / count 覆盖 vos 的 rank / count
       - 第二轮：
         - rank 取更小的非 0 值
         - count 取更大的值
       - 删除该 ai 元素（返回剩余 ai 列表）
    4. 一个 ai 元素只能匹配一次
    5. 外层 brand_item 的 rank / count 不变，只更新 vos
    """

    brand_list = deepcopy(brand_words_rank)
    ai_list = deepcopy(ai_word_list_with_rank)

    used_ai_index = set()

    # ========== 第一轮：vos.word 完全相等，忽略 brand ==========
    for brand_item in brand_list:
        vos = brand_item.get("vos", [])
        if not isinstance(vos, list):
            continue

        for vo in vos:
            vo_word = str(vo.get("word", "") or "").strip()
            vo_word_lower = vo_word.lower()

            if not vo_word_lower:
                continue

            for ai_index, ai_item in enumerate(ai_list):
                if ai_index in used_ai_index:
                    continue

                ai_word = str(ai_item.get("word", "") or "").strip()
                ai_word_lower = ai_word.lower()

                if not ai_word_lower:
                    continue

                if vo_word_lower == ai_word_lower:
                    vo["rank"] = ai_item.get("rank", 0)
                    vo["count"] = ai_item.get("count", 0)
                    used_ai_index.add(ai_index)
                    break

    # ========== 第二轮：vos.word 双向子串匹配，忽略 brand ==========
    for brand_item in brand_list:
        vos = brand_item.get("vos", [])
        if not isinstance(vos, list):
            continue

        for vo in vos:
            vo_word = str(vo.get("word", "") or "").strip()
            vo_word_lower = vo_word.lower()

            if not vo_word_lower:
                continue

            for ai_index, ai_item in enumerate(ai_list):
                if ai_index in used_ai_index:
                    continue

                ai_word = str(ai_item.get("word", "") or "").strip()
                ai_word_lower = ai_word.lower()

                if not ai_word_lower:
                    continue

                if vo_word_lower in ai_word_lower or ai_word_lower in vo_word_lower:
                    merged_rank, merged_count = merge_rank(
                        vo.get("rank", 0),
                        vo.get("count", 0),
                        ai_item.get("rank", 0),
                        ai_item.get("count", 0),
                    )
                    vo["rank"] = merged_rank
                    vo["count"] = merged_count
                    used_ai_index.add(ai_index)
                    # 第二轮不 break，继续吃后面还能匹配的 ai

    remain_ai_list = [
        item for idx, item in enumerate(ai_list)
        if idx not in used_ai_index
    ]

    return brand_list, remain_ai_list

def to_int_trans(v):
    try:
        return int(v or 0)
    except Exception:
        return 0

def rebuild_count_and_rank(data_list):
    """
    重新计算外层 count 和 rank
    规则：
    1. 外层 count = vos 中所有 count 之和
    2. 外层 rank = vos 中所有大于 0 的 rank 的最小值
    3. 如果 vos 中没有大于 0 的 rank，则外层 rank = 0
    """
    result = deepcopy(data_list)

    for item in result:
        vos = item.get("vos", [])
        if not isinstance(vos, list):
            item["count"] = 0
            item["rank"] = 0
            continue

        total_count = 0
        rank_list = []

        for vo in vos:
            if not isinstance(vo, dict):
                continue

            count = to_int_trans(vo.get("count", 0))
            rank = to_int_trans(vo.get("rank", 0))

            total_count += count

            if rank > 0:
                rank_list.append(rank)

        item["count"] = total_count
        item["rank"] = min(rank_list) if rank_list else 0

    return result


def get_all_brands(*data_lists):
    brands = []
    seen = set()

    for data_list in data_lists:
        if not data_list:
            continue

        for item in data_list:
            brand = item.get("brand")
            if not brand or brand == "没有品牌":
                brand = item.get("word")

            if brand and brand not in seen:
                seen.add(brand)
                brands.append(brand)

    return brands


def get_all_words(*data_lists):
    brands = []
    seen = set()

    for data_list in data_lists:
        if not data_list:
            continue

        for item in data_list:
            brand = item.get("word")
            seen.add(brand)
            brands.append(brand)

    return brands


def parse_mentions(value):
    if not value:
        return ""
    if isinstance(value, list):
        return ", ".join(str(x) for x in value if x)
    if isinstance(value, str):
        try:
            arr = json.loads(value)
            if isinstance(arr, list):
                return ", ".join(str(x) for x in arr if x)
        except Exception:
            return value
    return ""


def cache_ai_get_brand_sentiment_and_mentions(prompt, brands, response_content, task_id, platform, req_id):
    if not brands:
        return []

    seen = set()
    unique_brand_items = []

    for item in brands:
        if not isinstance(item, dict):
            continue

        brand = item.get("brand")
        if brand and brand not in seen:
            seen.add(brand)
            unique_brand_items.append({
                "brand": brand,
                "keyword": item.get("keyword", ""),
                "type": item.get("type", "")
            })

    if not unique_brand_items:
        return []

    unique_brands = [item["brand"] for item in unique_brand_items]

    # brand -> keyword/type 映射，插入 geo_brand_mention_list
    brand_info_map = {
        item["brand"]: {
            "keyword": item.get("keyword", ""),
            "type": item.get("type", "")
        }
        for item in unique_brand_items
    }

    # SQL 单引号转义
    def _escape_sql(s):
        return str(s).replace("'", "''")

    in_str = ",".join(f"'{_escape_sql(brand)}'" for brand in unique_brands)

    sql = f"""
        select *
        from geo_ai_brand_sentiment_and_mentions
        where taskId = '{_escape_sql(task_id)}'
          and platform = '{_escape_sql(platform)}'
          and brand_name in ({in_str})
    """


    db_result = bh_utils.query_data(sql) or []
    brand_mention_list = []
    for item in db_result:
        brand_n = item.get("brand_name")
        info = brand_info_map.get(brand_n, {})
        brand_mention_list .append(
            {
                "req_id": req_id,
                "taskId": task_id,
                "prompt": prompt,
                "platform": platform,
                "keyword": info.get("keyword", ""),
                "type": info.get("type", ""),
                "brand_name": item.get("brand_name"),
                "positive_mentions": parse_mentions(item.get("positive_mentions")),
                "negative_mentions": parse_mentions(item.get("negative_mentions"))
            }
        )

    bh_utils.insert_data("geo_brand_mention_list", brand_mention_list)
    # 数据库中已有的 brand
    db_brand_set = {
        item.get("brand_name")
        for item in db_result
        if item.get("brand_name")
    }

    # 找出缺失的 brand
    missing_brands = [
        brand
        for brand in unique_brands
        if brand not in db_brand_set
    ]

    # 如果数据库里已经有全部 brands，直接返回
    if not missing_brands:
        db_map = {
            item.get("brand_name"): item
            for item in db_result
            if item.get("brand_name")
        }

        return [
            db_map[brand]
            for brand in unique_brands
            if brand in db_map
        ]

    # 只对缺失的 brands 调 AI
    ai_result = ai_get_brand_sentiment_and_mentions(missing_brands, response_content) or []

    # 只补充原来缓存表需要的字段，不加 keyword/type
    for ar in ai_result:
        ar["taskId"] = task_id
        ar["platform"] = platform

    # 插入 AI 缓存表：
    if ai_result:
        bh_utils.insert_data("geo_ai_brand_sentiment_and_mentions", ai_result)

    all_result = db_result + ai_result

    # 插入 geo_brand_mention_list：
    result = []

    for item in all_result:
        brand_name = item.get("brand_name")
        info = brand_info_map.get(brand_name, {})

        result.append({
            "req_id": req_id,
            "prompt": prompt,
            "taskId": task_id,
            "platform": platform,
            "brand_name": brand_name,
            "keyword": info.get("keyword", ""),
            "type": info.get("type", ""),
            "positive_mentions": parse_mentions(item.get("positive_mentions")),
            "negative_mentions": parse_mentions(item.get("negative_mentions"))
        })

    if result:
        bh_utils.insert_data("geo_brand_mention_list", result)

    return all_result


def cache_ai_get_product_sentiment_and_mentions(products, response_content, task_id, platform, req_id):
    """
    逻辑：
    1. 先查数据库中 task_id + platform + brands 的已有结果
    2. 如果数据库中已包含所有 brands，直接返回数据库结果
    3. 如果只缺部分 brands，则只对缺失 brands 调用 AI
    4. 将缺失 brands 的 AI 结果插入数据库
    5. 返回完整结果
    """

    if not products:
        return []

    # 去重并保持顺序
    seen = set()
    unique_brands = []
    for brand in products:
        if brand and brand not in seen:
            seen.add(brand)
            unique_brands.append(brand)

    # SQL 单引号转义
    def _escape_sql(s):
        return str(s).replace("'", "''")

    in_str = ",".join(f"'{_escape_sql(brand)}'" for brand in unique_brands)

    sql = f"""
        select *
        from geo_ai_product_sentiment_and_mentions
        where taskId = '{_escape_sql(task_id)}'
          and platform = '{_escape_sql(platform)}'
          and brand_name in ({in_str})
    """
    db_result = bh_utils.query_data(sql) or []

    # 数据库中已有的 brand
    db_brand_set = {
        item.get("brand_name")
        for item in db_result
        if item.get("brand_name")
    }

    # 找出缺失的 brand
    missing_brands = [brand for brand in unique_brands if brand not in db_brand_set]

    # 如果数据库里已经有全部 brands，直接返回
    if not missing_brands:
        # 按传入 brands 顺序返回
        db_map = {item.get("brand_name"): item for item in db_result if item.get("brand_name")}

        return [db_map[brand] for brand in unique_brands if brand in db_map]

    # 只对缺失的 brands 调 AI
    ai_result = ai_get_product_sentiment_and_mentions(missing_brands, response_content) or []

    # 补充公共字段
    for ar in ai_result:
        ar["taskId"] = task_id
        ar["platform"] = platform

    # 插入数据库
    if ai_result:
        bh_utils.insert_data("geo_ai_product_sentiment_and_mentions", ai_result)

    all_result = db_result + ai_result

    result = [
        {
            "req_id": req_id,
            "brand_name": item.get("brand_name"),
            "positive_mentions": parse_mentions(item.get("positive_mentions")),
            "negative_mentions": parse_mentions(item.get("negative_mentions"))
        }
        for item in all_result
    ]

    bh_utils.insert_data("geo_product_mention_list", result)

    return all_result


def replace_word_by_brand(data_list):
    if not data_list:
        return []

    result = []
    for item in data_list:
        if not isinstance(item, dict):
            continue

        new_item = item.copy()
        brand = new_item.get("brand")

        if brand and brand != "没有品牌":
            new_item["word"] = brand

        result.append(new_item)

    return result


def keep_min_rank_by_brand(data_list):
    """
    相同 brand 只保留一条，保留 rank 最小的那条
    """
    brand_map = {}

    for item in data_list or []:
        brand = item.get("brand")
        rank = item.get("rank", 0)

        if brand not in brand_map:
            brand_map[brand] = item
        else:
            old_rank = brand_map[brand].get("rank", 0)
            if rank < old_rank:
                brand_map[brand] = item

    return list(brand_map.values())


sentiment_map = {
    "正向": "POSITIVE",
    "负向": "NEGATIVE",
    "中性": "NEUTRAL"
}


def merge_sentiment_to_brand_words(brand_words_list_with_rank, all_sentiment_and_mentions):
    # 构建 brand_name -> sentiment 映射
    word_sentiment_maps = {}
    for item in all_sentiment_and_mentions:
        brand_name = item.get("brand_name")
        sentiment = item.get("sentiment", "中性")
        if brand_name:
            word_sentiment_maps[brand_name] = sentiment
    result = []
    for item in brand_words_list_with_rank:
        new_item = dict(item)
        word = new_item.get("word")
        count = new_item.get("count", 0)
        if count > 0:
            new_item["sentiment"] = sentiment_map[word_sentiment_maps.get(word, "中性")]

        result.append(new_item)

    return result


def build_mentions_list(brand_words_list_with_rank, all_sentiment_and_mentions):
    valid_words = set()

    for item in brand_words_list_with_rank or []:
        word = str(item.get("word") or "").strip()
        try:
            count = int(item.get("count") or 0)
        except Exception:
            count = 0

        if word and count > 0:
            valid_words.add(word)

    all_positive = []
    all_negative = []

    for item in all_sentiment_and_mentions or []:
        brand_name = str(item.get("brand_name") or "").strip()
        if brand_name not in valid_words:
            continue

        positive_mentions = item.get("positive_mentions") or "[]"
        negative_mentions = item.get("negative_mentions") or "[]"

        if isinstance(positive_mentions, str):
            try:
                positive_mentions = json.loads(positive_mentions)
            except Exception:
                positive_mentions = []
        elif not isinstance(positive_mentions, list):
            positive_mentions = []

        if isinstance(negative_mentions, str):
            try:
                negative_mentions = json.loads(negative_mentions)
            except Exception:
                negative_mentions = []
        elif not isinstance(negative_mentions, list):
            negative_mentions = []

        all_positive.extend([str(x).strip() for x in positive_mentions if str(x).strip()])
        all_negative.extend([str(x).strip() for x in negative_mentions if str(x).strip()])

    return [
        {
            "negativeMentions": list(dict.fromkeys(all_negative)),
            "positiveMentions": list(dict.fromkeys(all_positive))
        }
    ]


def get_first_sentiment(data):
    for item in data or []:
        sentiment = item.get("sentiment")
        if sentiment:
            return sentiment


def get_first_score(data):
    for item in data or []:
        score = item.get("sentimentScore")
        if score:
            return score


def mark_source_mentioned(source_list, word_list):
    result = []

    # 去重并去掉空值
    words = [w.strip() for w in word_list if w and str(w).strip()]

    for item in source_list:
        snippet = item.get("snippet", "") or ""
        mentioned = 1 if any(word in snippet for word in words) else 0
        new_item = {}
        new_item["sourceId"] = item.get("sourceId")
        new_item["hasBrand"] = mentioned
        result.append(new_item)

    return result

def norm_source_id(v):
    if v is None:
        return ""
    return str(v).strip()

def merge_mentioned(snippet_mention, quotes):
    mentioned_map = {item["sourceId"]: item["hasBrand"] for item in snippet_mention}
    for item in quotes:
        item["hasBrand"] = mentioned_map.get(item["sourceId"], 0)
    return quotes


def merge_sentiment(snippet_mention, quotes):
    mentioned_map = {norm_source_id(item.get("sourceId")): item.get("sentiment", "NEUTRAL")for item in snippet_mention}
    for item in quotes:
        sid = norm_source_id(item.get("sourceId"))
        item["sentiment"] = mentioned_map.get(sid, "NEUTRAL")

    return quotes


def format_mentions(data_list):
    for item in data_list:
        positive_mentions = item.get("positive_mentions", "[]")
        negative_mentions = item.get("negative_mentions", "[]")

        try:
            item["positive_mentions"] = json.loads(positive_mentions) if isinstance(positive_mentions,
                                                                                    str) else positive_mentions
        except Exception:
            item["positive_mentions"] = []

        try:
            item["negative_mentions"] = json.loads(negative_mentions) if isinstance(negative_mentions,
                                                                                    str) else negative_mentions
        except Exception:
            item["negative_mentions"] = []

    return data_list


def process_brand_sentiment_by_snippets(prompt, platform, taskId, reqId, brand_words, snippet_list):
    """
    根据 brand_words 和 snippet_list 调用 ai_get_brand_sentiment_and_mentions

    参数:
        brand_words: list[str]
        snippet_list: list[dict]，每项至少包含 snippet 字段

    返回:
        list[dict]
    """
    results = []

    if not brand_words or not snippet_list:
        return results

    db_result = bh_utils.query_data(f"select * from geo_quote_mention_list where reqId = '{reqId}'")
    if db_result:
        return format_mentions(db_result)

    # 全量兜底 brand_str
    fallback_brand_str = [",".join(dict.fromkeys(brand_words))]
    articles_result = ai_get_brand_sentiment_and_mentions_by_articles(fallback_brand_str, snippet_list)
    today = datetime.now().strftime("%Y%m%d")
    for articles in articles_result:
        de = {
            "reqId": reqId,
            "taskId": taskId,
            "platform": platform,
            "prompt": prompt,
            "sourceId": articles.get("sourceId"),
            "sentiment": sentiment_map[articles.get('sentiment', '中性')],
            "positive_mentions": articles.get('positive_mentions'),
            "negative_mentions": articles.get('negative_mentions'),
            "pt": today
        }
        results.append(de)

    bh_utils.insert_data("geo_quote_mention_list", results)

    return results


def calc_brand_favorability_simple(data_list):
    result = []

    for item in data_list:
        pos_count = len(item.get("positive_mentions", []) or [])
        neg_count = len(item.get("negative_mentions", []) or [])
        total = pos_count + neg_count
        favorability = int((pos_count / total) * 100) if total > 0 else 50

        result.append({
            "brand_name": item.get("brand_name"),
            "favorability_score": favorability
        })

    return result


def attach_favorability_score(score_list, word_list):
    # 先构建品牌分数字典
    score_map = {
        item.get("brand_name"): item.get("favorability_score", 0)
        for item in score_list
    }

    result = []
    for item in word_list:
        new_item = dict(item)
        new_item["sentimentScore"] = score_map.get(item.get("brand"), 0)
        result.append(new_item)

    return result


def attach_favorability_brand_score(score_list, word_list):
    # 先构建品牌分数字典

    score_map = {
        item.get("brand_name"): item.get("favorability_score", 0)
        for item in score_list
    }

    result = []
    for item in word_list:
        new_item = dict(item)
        new_item["sentimentScore"] = score_map.get(item.get("word"), 0)
        result.append(new_item)

    return result


def replace_no_brand_inplace(data_list):
    for item in data_list:
        if item.get("brand") == "没有品牌":
            item["brand"] = item.get("keyword")
    return data_list


def add_keyword_type(ai_word_list, brand_words, com_word_list, all_keyword_with_brand):
    """
    根据 keyword 属于哪个 list，给 all_keyword_with_brand 每一项增加 type

    type:
    - brand: 自有品牌词
    - com: 竞品词
    - ai: AI 识别词
    - unknown: 未匹配
    """

    ai_set = set(ai_word_list or [])
    brand_set = set(brand_words or [])
    com_set = set(com_word_list or [])

    result = []

    for item in all_keyword_with_brand:
        brand = item.get("brand")
        keyword = item.get("keyword")

        if keyword in brand_set:
            word_type = "brand"
        elif keyword in com_set:
            word_type = "com"
        elif keyword in ai_set:
            word_type = "ai"
        else:
            word_type = "unknown"

        result.append({
            "brand": brand,
            "keyword": keyword,
            "type": word_type
        })

    return result


def result_v2(response_content, data):
    data = process_com_map(data)
    reqId = data.get('reqId')
    type = data.get('type')
    keywords = data.get('keywords')
    product_map = data.get('productWordsMap')
    prompt = data.get('prompt')
    platform = data.get('platform')
    taskId = data.get('taskId')
    brand_words = data.get('brandWords', [])
    comWords = data.get('comWords', [])
    if data.get('comWordsMap'):
        comWords = data.get('comWordsMap')

    com_word_list = extract_keywords(comWords)
    # 获取ai所有的词
    # -------------------------
    # 获取ai提及词
    ai_word_list = cache_get_ai_brand_list(taskId, platform, response_content, prompt)
    # 获取所有词
    all_word_list = []
    if isinstance(ai_word_list, list):
        all_word_list += ai_word_list
    if isinstance(brand_words, list):
        all_word_list += brand_words
    if isinstance(com_word_list, list):
        all_word_list += com_word_list


    brand_words_list_with_rank = []
    com_word_list_with_rank = []
    ai_word_list_with_rank = []
    keywords_list_with_rank = []
    product_map_list_with_rank = []
    product_map_list_with_rank_result = []
    all_product_list_with_rank = []

    all_word_set_list = list(set(all_word_list))
    ## 去除空字符串 死循环
    all_word_set_list = [str(x).strip() for x in all_word_set_list if str(x).strip()]
    # 获取所有词的排名 word + rank_list
    all_word_rank_list = get_keyword_ranks(response_content, all_word_set_list)
    # 获取所有词的排名 word + rank + count

    all_keyword_with_rank = convert_rank_data(all_word_rank_list)

    # 获取所有词的品牌
    all_keyword_with_brand = keyword_map_brand(all_word_list)

    # 追加所有词的品牌
    all_keyword_with_rank_sentiment_brand = keyword_with_brand(all_keyword_with_rank, all_keyword_with_brand)

    # 拆分为 all_词 品牌词 竞品词
    ai_word_list_with_rank, brand_words_list_with_rank, com_word_list_with_rank = split_word_rank(ai_word_list,
                                                                                                  brand_words,
                                                                                                  com_word_list,
                                                                                                  all_keyword_with_rank_sentiment_brand)

    # ---------------------------------调整排名-----------------------------

    brand_words_list_with_rank, ai_word_list_with_rank = merge_brand_rank_by_subset(brand_words_list_with_rank,
                                                                                    ai_word_list_with_rank)

    # 兼容map场景
    if data.get('comWordsMap'):
        # 计算出vos与外层数据
        com_word_list_with_rank = build_brand_summary(com_word_list_with_rank, comWords)
        # 外层品牌
        com_word_list_with_rank = fill_outer_brand(com_word_list_with_rank)
        # 去除竞品词在ai中的
        com_word_list_with_rank, ai_word_list_with_rank = merge_com_and_ai_by_subset(com_word_list_with_rank,
                                                                                     ai_word_list_with_rank)
        # 重新计算外层rank count
        com_word_list_with_rank = rebuild_count_and_rank(com_word_list_with_rank)


    else:
        # list 调整竞争品牌词排名 去除竞争品牌词中ai的排名
        com_word_list_with_rank, ai_word_list_with_rank = merge_brand_rank_by_subset(com_word_list_with_rank,
                                                                                     ai_word_list_with_rank)
    # ---------------------------------调整排名-----------------------------

    # 去除all
    ai_word_list_with_rank = filter_ai_word_list(ai_word_list_with_rank, brand_words_list_with_rank,
                                                 com_word_list_with_rank)

    # ---------------------------------处理品牌-----------------------------

    ai_word_list_with_rank = replace_word_by_brand(ai_word_list_with_rank)
    # 同一品牌只返回一个
    ai_word_list_with_rank = keep_min_rank_by_brand(ai_word_list_with_rank)
    # ---------------------------------处理品牌-----------------------------

    # ---------------------------------情感倾向-----------------------------
    # 获取所有品牌 或者词
    brands = get_all_brands(ai_word_list_with_rank) + get_all_words(brand_words_list_with_rank, com_word_list_with_rank)

    # 获取所有词情感倾向 正面词 负面词
    all_keyword_with_brand_replace = replace_no_brand_inplace(all_keyword_with_brand)
    brands_with_type = add_keyword_type(ai_word_list, brand_words, com_word_list, all_keyword_with_brand_replace)

    all_sentiment_and_mentions = cache_ai_get_brand_sentiment_and_mentions(prompt, brands_with_type, response_content,
                                                                           taskId, platform, reqId)
    all_sentiment_and_mentions_list = format_mentions(all_sentiment_and_mentions)
    all_sentiment_and_mentions_score = calc_brand_favorability_simple(all_sentiment_and_mentions_list)

    #
    brand_words_list_with_rank = merge_sentiment_to_brand_words(brand_words_list_with_rank, all_sentiment_and_mentions)
    com_word_list_with_rank = merge_sentiment_to_brand_words(com_word_list_with_rank, all_sentiment_and_mentions)
    ai_word_list_with_rank = merge_sentiment_to_brand_words(ai_word_list_with_rank, all_sentiment_and_mentions)
    # 计算分
    brand_words_list_with_rank = attach_favorability_brand_score(all_sentiment_and_mentions_score,
                                                                 brand_words_list_with_rank)

    com_word_list_with_rank = attach_favorability_brand_score(all_sentiment_and_mentions_score, com_word_list_with_rank)
    ai_word_list_with_rank = attach_favorability_score(all_sentiment_and_mentions_score, ai_word_list_with_rank)

    mentionsList = build_mentions_list(brand_words_list_with_rank, all_sentiment_and_mentions)
    # ---------------------------------情感倾向-----------------------------

    # ---------------------------------最小排名最小次数-----------------------------
    min_com_rank, com_count_sum = calculate_rank_min_and_count_sum(com_word_list_with_rank)
    min_brand_rank, brand_count_sum = calculate_rank_min_and_count_sum(brand_words_list_with_rank)
    # ---------------------------------最小排名最小次数-----------------------------

    # ---------------------------------关键词-----------------------------
    if keywords:
        keywords = [str(x).strip() for x in keywords if str(x).strip()]

        keywords_rank_list = get_keyword_ranks(response_content, keywords)
        keywords_list_with_rank = convert_rank_data(keywords_rank_list)

    # ---------------------------------产品词-----------------------------
    if product_map:
        # 获取所有产品词
        product_list = extract_keywords(product_map)

        # 获取ai提取产品词
        ai_product_list = cache_get_ai_product_list(taskId, platform, response_content, prompt)

        if isinstance(ai_product_list, list):
            product_list += ai_product_list

        product_list = list(set(product_list))

        product_list = [str(x).strip() for x in product_list if str(x).strip()]


        # 获取所有产品排名
        product_rank_list = get_keyword_ranks(response_content, product_list)


        all_product_list_with_rank = convert_rank_data(product_rank_list)


        # 所有产品
        product_all = get_all_words(all_product_list_with_rank)
        # 获取产品的情感倾向以及正面次负面词
        all_product_sentiment_and_mentions = cache_ai_get_product_sentiment_and_mentions(product_all, response_content,
                                                                                         taskId,
                                                                                         platform, reqId)
        # 计算所有的分
        all_product_sentiment_and_mentions = format_mentions(all_product_sentiment_and_mentions)
        all_product_sentiment_and_mentions_score = calc_brand_favorability_simple(all_product_sentiment_and_mentions)

        # 获取产品词的排名
        product_map_list_with_rank = build_brand_summary(all_product_list_with_rank, product_map)


        # 获取所有词的情感倾向
        product_map_list_with_rank = merge_sentiment_to_brand_words(product_map_list_with_rank,
                                                                    all_product_sentiment_and_mentions)

        # 删除ai中出现的品牌词

        product_map_list_with_rank, all_product_list_with_rank = merge_com_and_ai_by_subset(product_map_list_with_rank,
                                                                                            all_product_list_with_rank)

        # 获取产品词的分

        product_map_list_with_rank = attach_favorability_brand_score(all_product_sentiment_and_mentions_score,
                                                                     product_map_list_with_rank)

        product_map_list_with_rank_result = rebuild_count_and_rank(product_map_list_with_rank)

        # 获取所有品牌的分
        all_product_list_with_rank = attach_favorability_brand_score(all_product_sentiment_and_mentions_score,
                                                                     all_product_list_with_rank)

        all_product_list_with_rank = merge_sentiment_to_brand_words(all_product_list_with_rank,
                                                                    all_product_sentiment_and_mentions)

    # ---------------------------------引用来源-----------------------------
    path = f'geo/{taskId}/{platform}/quote.txt'
    quto = tos_utils.get_string_from_tos(path)
    snippet_list = []
    quotes = []
    if quto:
        json_quto = json.loads(quto)
        for q in json_quto:
            t = {
                "img": q.get("site_icon"),
                "platform": q.get("platform"),
                "sourceId": q.get("quto_id"),
                "title": q.get("title"),
                "url": q.get("url"),
                "site_name": q.get("site_name")
            }
            quotes.append(t)
        for q in json_quto:
            k = {
                "snippet": q.get("snippet"),
                "sourceId": q.get("quto_id"),
            }
            snippet_list.append(k)
    if quotes:
        prompt = data["prompt"]
        for item in quotes:
            item["prompt"] = prompt

    if snippet_list and brand_words and type != 'stream':
        # if snippet_list and brand_words :
        snippet_mention = mark_source_mentioned(snippet_list, brand_words)
        quotes = merge_mentioned(snippet_mention, quotes)

        sentiment_list = process_brand_sentiment_by_snippets(prompt, platform, taskId, reqId, brand_words, snippet_list)
        quotes = merge_sentiment(sentiment_list, quotes)

    # ---------------------------------引用来源-----------------------------

    # ---------------------------------结果整合-----------------------------
    result = {
        "brandVos": brand_words_list_with_rank,
        "competitorVos": com_word_list_with_rank,
        "keywordsVos": keywords_list_with_rank,
        "productVos": product_map_list_with_rank_result,
        "productAllVos": all_product_list_with_rank,
        "allVos": ai_word_list_with_rank,
        "fullVos": [],
        "minBrandRank": min_brand_rank,
        "minCompetitorRank": min_com_rank,
        "platform": platform,
        "prompt": prompt,
        "reqId": reqId,
        "sources": quotes,
        "taskId": taskId,
        "totalBrandCount": brand_count_sum,
        "totalCompetitorCount": com_count_sum,
        "mentionsList": mentionsList
    }
    # 修复里外不一致
    result["productVos"] = rebuild_count_and_rank(result.get("productVos") or [])
    # 情感分
    pos_count = len(mentionsList[0].get("positiveMentions", []) or [])
    neg_count = len(mentionsList[0].get("negativeMentions", []) or [])
    total = pos_count + neg_count
    sentimentScore = int((pos_count / total) * 100) if total > 0 else 50
    result['sentimentScore'] = sentimentScore

    # 品牌情感倾向
    if brand_words_list_with_rank:
        sentiment_result = get_first_sentiment(brand_words_list_with_rank)
        if sentiment_result:
            result['sentiment'] = sentiment_result


    # ---------------------------------结果整合-----------------------------

    # ---------------------------------上传结果-----------------------------
    tos_utils.put_string_to_tos(f'geo/{taskId}/{platform}/result.json', result)
    commit_task(data, 'SUCCESS')
    resp_cache_key = f"geo:task_check:resp:{reqId}"
    resp = {
        "code": 200,
        "msg": "success",
        "data": {
            "status": 'ING',
            "result": {}
        }
    }
    _cache_set_json(resp_cache_key, resp, 1)
    logger.success(f"{data['reqId']}--{data['platform']}--{data['prompt']}--ALL:SUCCESS")
    # ---------------------------------上传结果-----------------------------
    return True


class QueueType(Enum):
    STREAM = (1, "stream")
    STREAM_BATCH = (2, "stream_batch")
    BATCH = (3, "batch")
    SUCCESS = (4, "success")

    def __init__(self, priority, queue_name):
        self.priority = priority
        self.queue_name = queue_name

    @classmethod
    def get_by_name(cls, name):
        """根据队列名称获取枚举实例"""
        for queue in cls:
            if queue.queue_name == name:
                return queue
        raise ValueError(f"不存在名为 {name} 的队列")

    @classmethod
    def get_next_priority_queue(cls, current_queue):
        """根据当前队列获取下一个优先级的队列（最后一个返回自身）"""
        # 获取所有队列并按优先级排序（确保顺序正确）
        sorted_queues = sorted(cls, key=lambda q: q.priority)
        current_index = sorted_queues.index(current_queue)

        # 下一个索引：如果是最后一个则保持当前索引，否则+1
        next_index = current_index + 1 if current_index < len(sorted_queues) - 1 else current_index
        return sorted_queues[next_index]


def handle_task_failure(task_type: str) -> str:
    """
    接收队列类型名称，返回下一个优先级的队列名称
    :param task_type: 原始队列名称（如 "stream"、"batch" 等）
    :return: 下一个优先级的队列名称
    """
    try:
        # 根据名称获取原始队列枚举实例
        original_queue = QueueType.get_by_name(task_type)
    except ValueError:
        raise ValueError(f"不支持的任务类型: {task_type}")

    # 获取下一个优先级的队列
    next_queue = QueueType.get_next_priority_queue(original_queue)
    return next_queue.queue_name


def platform_process(data):
    PLATFORM_PROCESS_MAP = {
        'DB': doubao_data_process.doubao_process_original_data,
        'DP': deepseek_data_process.deepseek_process_original_data,
        'TXYB': yuanbao_data_process.yuanbao_process_original_data,
        'KIMI': kimi_data_process.kimi_process_original_data,
        'WXYY': wenxin_data_process.wenxin_process_original_data,
        'TYQW': qianwen_data_process.qianwen_process_original_data,
        'BDAI': baiduai_data_process.baiduai_process_original_data,
        'DPA': deepseek_android_data_process.deepseek_android_process_original_data,
        'DOUBA': doubao_android_data_process.doubao_mobile_process_original_data,
        'DYAI': douyinai_data_process.douyin_ai_process_original_data,
        'TYQWA': qianwen_android_data_process.qianwen_android_process_original_data,
        'TXYBA': yuanbao_android_data_process.yuanbao_android_process_original_data,
        'XHSA': xiaohongshu_android_data_process.xiaohongshu_android_process_original_data,
    }

    try:
        commit_task(data, 'PROCESSING')
        platform = data["platform"]
        original_path = f'geo/{data["taskId"]}/{platform}/original.text'
        context_path = f'geo/{data["taskId"]}/{platform}/context.txt'
        response_content = None


        # ——————————————###################
        # has_original = check_file_in_tos(original_path)
        # has_context = check_file_in_tos(context_path)
        # # 重新跑逻辑 当俩个都有走下面
        # if has_original and has_context:
        #     process_func = PLATFORM_PROCESS_MAP.get(platform)
        #     if process_func:
        #         _, _, _, _, response_content, _ = process_func(data)
        # # 2. 只有 context：直接读 context
        # elif has_context:
        #     response_content = tos_utils.get_string_from_tos(context_path)
        #
        # # 3. 只有 original：跑平台解析逻辑
        # elif has_original:
        #     process_func = PLATFORM_PROCESS_MAP.get(platform)
        #     if process_func:
        #         _, _, _, _, response_content, _ = process_func(data)
        # ——————————————###################

        # 修复
        # process_func = PLATFORM_PROCESS_MAP.get(platform)
        # if process_func:
        #     _, _, _, _, response_content, _ = process_func(data)


        # -------------
        if check_file_in_tos(context_path):
            response_content = tos_utils.get_string_from_tos(context_path)
        else:
            process_func = PLATFORM_PROCESS_MAP.get(platform)
            if process_func:
                _, _, _, _, response_content, _ = process_func(data)
        # -------------
        if response_content:
            result_v2(response_content, data)
        else:
            scheduler(data)

        return True
    except Exception as e:
        logger.exception(
            f"{data['reqId']}--{data['platform']}--{data['prompt']}--PROCESS_FAIL"
        )
        logger.error(f"{data['reqId']}--{data['platform']}--{data['prompt']}--{e}--PROCESS_FAIL", exc_info=True)
        robot_utils.feishu_tobot(f"{data['reqId']}--{data['platform']}--{data['prompt']}--{e}--PROCESS_FAIL")
        commit_task(data, 'PROCESS_FAIL')
        scheduler(data)
        return False


def main_process(data):
    task_id = data.get('taskId')
    platform = data.get('platform')
    type = data.get('type')
    req_id = data.get('reqId')
    prompt = data.get('prompt')
    data["prompt"] = prompt.strip()
    data["search_enabled"] = data.get('searchEnabled', '1')
    if data.get('thinkingEnabled'):
        data["thinking_enabled"] = data.get('thinkingEnabled')
    check = bh_utils.query_data(
        f"select status from geo_commit_task where taskId ='{task_id}' and platform = '{platform}'")
    if any(item.get("status") == "SUCCESS" for item in (check or [])):
        platform_process(data)
    else:
        if type == 'stream':
            commit_task(data, 'ING')
            logger.success(f"{data['reqId']}--{data['platform']}--{data['prompt']}--任务提交--{type}")
            bool_result = spider_interface.get_platform_response(data)
            if bool_result:
                size = get_tos_file_size(f"geo/{task_id}/{platform}/original.text")
                if size is None or size < 1024:
                    scheduler(data)
                else:
                    logger.success(f"{req_id}--{platform}--{prompt}--ING_IN_TOS")
                    platform_process(data)
            else:
                logger.error(f"{req_id}--{platform}--{prompt}--400")
                scheduler(data)
        elif type in ('stream_batch', 'batch') and not check:
            task_send_queue(data, type)

def success_quto(data,url_list):
    platform = data.get('platform')
    quto_list = []
    for index, item in enumerate(url_list):
        raw_data = {
            "url": item.get('url'),
            "title": item.get('title'),
            "snippet": item.get('summary'),
            "index": index,
            "published_at": item.get('publish_time'),
            "site_name": item.get('site_name'),
            "site_icon": item.get('logo_url'),
        }
        quto_list.append(raw_data)

    urls = []

    for c in quto_list:
        c['task_id'] = data.get('taskId')
        c['platform'] = platform
        u = c.get('url')
        if isinstance(u, str) and u.startswith('http'):
            urls.append(u)
        else:
            urls.append(c.get('title'))

    url_ids_map = url_utils.generate_numeric_url_id_v2(urls)

    for c in quto_list:
        u = c.get('url')
        if isinstance(u, str) and u.startswith('http'):
            key = u
        else:
            key = c.get('title')
        c['quto_id'] = url_ids_map.get(key)

    bh_utils.insert_data("geo_quote_result_v2", quto_list)
    put_string_to_tos(f"geo/{data.get('taskId')}/{data.get('platform')}/quote.txt", quto_list)

def process_success(data):
    data['type'] = 'success'
    platform = data.get('platform')
    think = data.get('thinkingEnabled')
    prompt = data.get('prompt')

    if platform in ('DB','DOUBA'):
        search_keywords, url_list, think_content, response_content = ai_interface.get_doubao_message(prompt,think)
        if url_list:
            success_quto(data,url_list)
        if search_keywords:
            put_string_to_tos(f"geo/{data.get('taskId')}/{data.get('platform')}/search_keyword.txt", search_keywords)
        if think_content:
            put_string_to_tos(f"geo/{data.get('taskId')}/{data.get('platform')}/think.txt", think_content)
    elif platform in ('DP','DPA','BDAI','WXYY'):
        search_keywords, url_list, think_content, response_content = ai_interface.get_deepseek_message(prompt,think)
        if url_list:
            success_quto(data,url_list)
        if search_keywords:
            put_string_to_tos(f"geo/{data.get('taskId')}/{data.get('platform')}/search_keyword.txt", search_keywords)
        if think_content:
            put_string_to_tos(f"geo/{data.get('taskId')}/{data.get('platform')}/think.txt", think_content)
    elif platform in ('TXYB','TXYBA'):
        search_keywords, url_list, think_content, response_content = ai_interface.get_yuanbao_message(prompt,think)
        if think_content:
            put_string_to_tos(f"geo/{data.get('taskId')}/{data.get('platform')}/think.txt", think_content)
    elif platform in ('TYQW','TYQWA'):
        search_keywords, url_list, think_content, response_content = ai_interface.get_qianwewn_message(prompt,think)
    elif platform == 'KIMI':
        search_keywords, url_list, think_content, response_content = ai_interface.get_kimi_message(prompt,think)
    else:
        search_keywords, url_list, think_content, response_content = ai_interface.get_doubao_message(data.get('prompt'),think)
    put_string_to_tos(f"geo/{data.get('taskId')}/{data.get('platform')}/context.txt", response_content)
    result_v2(response_content, data)


def process_call_back(task_data, result):
    task_id = task_data.get('taskId')
    platform = task_data.get('platform')
    req_id = task_data.get('reqId')
    spider_data = result.get('data')

    if spider_data == 400:
        scheduler(task_data)
    else:
        logger.success(f"{req_id}--{platform}-------CALL_BACK_START")
        put_string_to_tos(f'geo/{task_id}/{platform}/original.text', spider_data)
        req_check = bh_utils.query_data(f"select status from geo_commit_task where reqId ='{req_id}'")
        req_status = ""
        if req_check and len(req_check) > 0:
            req_status = req_check[0].get("status", "")
        if req_check and req_status != 'SUCCESS':
            size = get_tos_file_size(f"geo/{task_id}/{platform}/original.text")
            if size is None or size < 1024:
                scheduler(task_data)
            else:
                platform_process(task_data)


def scheduler(data):
    commit_task(data, 'ING')
    task_id = data.get('taskId')
    platform = data.get('platform')
    req_id = data.get('reqId')
    fail_queue_type = data.get('type')
    prompt = data.get('prompt')
    degrade_count_key = f"geo:degrade_count:{task_id}:{platform}"
    degrade_count = redis_client.incr(degrade_count_key)
    if degrade_count == 1:
        redis_client.expire(degrade_count_key, 7200)

    if degrade_count > 6:
        logger.success(f"{data['reqId']}--{data['platform']}--{data['prompt']}--降级次数超过6次，直接走success")
        process_success(data)
        return
    if fail_queue_type == 'batch':
        batch_retry_key = f"geo:batch_retry_count:{task_id}:{platform}"

        batch_retry_count = redis_client.incr(batch_retry_key)
        if batch_retry_count == 1:
            redis_client.expire(batch_retry_key, 7200)

        if batch_retry_count <= 1:
            data['type'] = 'batch'
            key = f"{platform}:geo:batch:list"

            logger.success(
                f"{req_id}--{platform}--{prompt}--batch失败，重新投递batch--第{batch_retry_count}次batch重试"
            )

            redis_client.lpush(key, json.dumps(data))
            return

        logger.success(
            f"{req_id}--{platform}--{prompt}--batch已重试{batch_retry_count - 1}次，直接走success"
        )
        process_success(data)
        return

    next_queue_type = handle_task_failure(fail_queue_type)
    logger.success(
        f"{data['reqId']}--{data['platform']}--{data['prompt']}--降级成功-当前{next_queue_type}--第{degrade_count}次降级")

    if next_queue_type == 'success':
        process_success(data)
    else:
        data['type'] = next_queue_type
        key = f"{platform}:geo:{next_queue_type}:list"
        redis_client.lpush(key, json.dumps(data))


def task_send_queue(data, queue):
    if data.get('thinkingEnabled'):
        data["thinking_enabled"] = str(data.get('thinkingEnabled'))
    if data.get('searchEnabled'):
        data["search_enabled"] = str(data.get('searchEnabled'))

    data["search_enabled"] = "1"
    commit_task(data, 'ING')
    logger.success(f"{data['reqId']}--{data['platform']}--{data['prompt']}--任务提交--{queue}")
    redis_client.lpush(f"{data['platform']}:geo:{queue}:list", json.dumps(data))

def handle_item(i):
    if i.get('comWordsMap'):
        i['comWordsMap'] = json.loads(i.get('comWordsMap'))
    if i.get('brandWords'):
        i['brandWords'] = json.loads(i.get('brandWords'))
    if i.get('comWords'):
        i['comWords'] = json.loads(i.get('comWords'))
    if i.get('keywords'):
        i['keywords'] = json.loads(i.get('keywords'))
    if i.get('productWordsMap'):
        i['productWordsMap'] = json.loads(i.get('productWordsMap'))
    type_t = i.get('type')
    # type_t = 'batch'

    # return task_send_queue(i,type_t)
    return platform_process(i)


def run_data(PAGE_SIZE,MAX_WORKERS):
    logger.success(
       "开始执行"
    )
    last_insertime = 1781253622
    while True:
        query_sql = f"""
            SELECT *
            FROM geo_commit_task
            WHERE insertime < {last_insertime}
              AND status = 'SUCCESS'
            ORDER BY insertime DESC
            LIMIT {PAGE_SIZE}
        """

        data_list = bh_utils.query_data(query_sql) or []

        if not data_list:
            logger.success("处理结束")
            break

        # 关键：处理之前先保存本批次最后一条的原始 insertime
        next_last_insertime = int(data_list[-1].get("insertime"))

        logger.success(
            f"本批次查询到 {len(data_list)} 条，"
            f"当前 last_insertime={last_insertime}, "
            f"下一批 next_last_insertime={next_last_insertime}"
        )

        with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
            futures = [executor.submit(handle_item, i) for i in data_list]

            for future in as_completed(futures):
                try:
                    future.result()
                except Exception as e:
                    logger.exception(f"platform_process 执行异常: {e}")

        # 用处理前保存的原始游标推进
        last_insertime = next_last_insertime

        logger.success(f"本批次处理完成，更新 last_insertime={last_insertime}")

if __name__ == '__main__':

    data_list = bh_utils.query_data(f"select * from geo_commit_task where reqId in ('bf40001b-8d61-4503-b2d3-0af731e229c5', '4bd855a5-72bf-4520-8e06-c4f5a1fc0600', '2bfa5b82-36a5-449f-8493-959ee3bbb925', '9c9d2ae9-9e76-49e3-8828-c9986a7d0e42', 'ea72e8fb-9c49-4ac1-8405-3e09d89109e8', '8ff96555-59ca-40d2-83df-49bebee2f14b', '4a8e1ddc-4a04-44d5-a844-4102bbb5de82', '6cb81cc6-c1ad-4048-92d8-726db55643c8', '8b3c1d1d-187d-4aa1-969c-3ecff3200474', '5a229c46-951a-4453-b5c0-31915f09a34c', '15c8ae47-a3bb-459a-8d2e-b3fc1d781b25', 'a8766be2-0b77-4d45-97ae-e392a14b1235', '9f4dfef7-ec0c-43ae-aa62-6182ccc154cd', 'b4b67f8e-5f62-416d-867f-a6bfeb03fec9', 'ad502392-8971-4d41-acdc-d621ced48d44', '2894a6f1-304c-4967-9bf6-01735b7d07fa', '1434c93d-286a-41e0-8a87-fab30e8643f1', '9159f6f9-a14a-4780-8071-a68c2cc86470', '0bf855c3-b02c-4190-8c78-07440fd45460', 'db667eaa-954d-4c17-bbfb-ff1b6c8b2884', '45ddd439-c3fe-4c1c-a043-82469a922852', 'eee14bc1-aeb9-4675-abcd-03005ef4bb3b', '53c379d2-59f8-4896-8324-1272f47afc01', 'f1ca97c2-f0c1-4b88-8d98-eb2f8e09773d', '907b0b1f-4cab-40bc-9b76-4627a1d20002', '4c41a904-f827-4333-9de5-1a19e02a2310', 'bfad5f84-2b4d-40df-9626-53fcc04c1821', 'c88e028c-7231-44be-8ff3-6f01a5641958', '6ab5f197-fc2c-48b4-a623-a1475e18a296', 'e9011298-eff0-41cf-a615-0da38de90c61', 'd96b2cc3-19c0-4071-87bc-eeee79b56738', 'ef33c851-5905-471b-9ede-7a397241fac9', 'e024eed7-ded3-4762-a371-526df3fab2b7', 'a270ef21-5c42-4561-844e-919d4939a6fd', '5323e6fc-8cb9-41e8-bf6e-6a0029de6d79', '52205174-6196-45a0-83ff-2e13ea67bd21', 'a25f8ca6-e167-495e-902c-fda5924b7358', '8d75a117-6715-4a4d-be87-d775887fda02', '566aa1af-1d0c-4991-8bc1-f9a365565bf7', '731a3210-9c1b-4f56-9ed4-cbed089ebec6', 'fe43b199-1b76-40da-bb51-6db918bd2433', '31b85bc5-9733-42dc-b7de-e951611adcff', '82d98e7d-0d26-4a6f-bb57-386f1fa2a644', '04168c94-bfc5-463d-afb0-db1f3f621a3c', 'b409a24f-9dad-4b0b-bf89-d2d131adc604', '9172e001-f3fe-41ee-9f50-f9057402b1b8', '38a0ef4b-d2d2-46aa-ac55-1a9894492fec', '6b6dff37-58aa-4568-b8fb-ef9dfeb8687a', '0ccc8c10-98ed-4de7-a7cb-df8d91033049', '52ebfda5-f361-4219-b13d-e68b80f5f4dc', '5a614832-d810-4d25-a99d-5ad052bdf74b', 'e485dbcd-1adc-4266-9c98-181ff839b725', 'd876f0a4-8af5-4759-9e90-93c24b34988d', 'bce3e99b-1571-446b-a51a-1a7169664e2e', 'f3691fb6-1ae4-432c-9235-a3ba2180792c', 'f7473336-c6be-4a9a-aeb9-5a05575c7afa', '09de1d3e-ecbe-4d96-94c3-dcf20bd4b2e1', '57a957ad-b886-407f-86b3-920283467d24', '3d351fb1-087d-402e-8b8a-89c0c100da70', 'a5a15982-bad2-4e5d-a11e-64bfc62db2d2', '786f457f-21ff-45d5-83d0-feeda777c8f6', '995b5a9f-00ef-4815-a4bb-7820cc247526', '6dad6430-222a-4e17-af26-ef3ee235fd87', '72e76210-aac1-4128-acb8-edf8cc82e0ff', 'a9a7a77c-9759-4bfb-a2fb-ef1dd333a5a0', '7994f1a3-72c2-42b1-a9b9-b0546ca3af3f', 'b2e6fe28-18f6-4b63-8b1f-970e6ffc5314', '585a164b-51c5-45cc-98bf-99da169c1428', 'a93a5ddb-7397-4173-a9cf-32ac0d75254b', 'fd6e3405-e15f-43dd-aa28-3490129d4829', 'f9bdafd3-68be-49c8-845c-6cca281f2005', '506d494c-ffd1-4577-9235-6277674384be', '537f6f68-b1d7-47ac-95f2-cd90712ec436', '5663a0b5-b6ab-4c8e-9641-2e4ec54eab9e', 'ff795c31-6714-4768-b901-9e46213f9fff', '431c12a4-ac03-41c5-a51a-49b254e42dbd', '70ea0fb0-6ba3-4814-bb83-06ce803fba35', 'faf3ea2c-3e8c-4d7c-8c2f-c09b8e948ba8', 'ba652223-b3f9-4477-92ea-006341002f00', 'f36060da-533e-4b5e-83a6-cb451ea7790d', 'b65471db-cc67-4c81-840c-2699f8a774f2', '314e8df6-4c61-4cef-a555-aef061649604', '1dff82d4-99ed-44f5-a54e-c81c3aaaec25', '188c433c-a27b-46fb-bb60-2483479e0028', '30fab92d-a525-450b-8eee-225e80f7176f', '16def5ff-03dd-4531-85d4-29c62263ffdd', '9a483dce-8bc2-4034-9849-b018e32b0401', 'c028ae43-e64a-4459-ada0-f66fcee01b9f', '1b0d627c-c83d-429a-a5f2-07415ab726f5', 'b53bdd98-02a9-4d0b-acbb-bdce1517f548', '2e593821-3e45-48f8-9e65-2f87783b6791', 'ac7c28d0-8434-4c87-b111-0e31475b4780', '36ac2b59-bc5e-4ab5-8f1d-081e2fd4e2d0', 'b94bbd67-43e3-4375-add5-938c230212ad', 'b2712722-287d-452f-a156-afa2fce1d8c5', 'c6b8d248-91d8-461a-befc-56f9880339b6', '52071265-0f57-4e2a-bb15-3686336c6798', '78f87e9f-060f-40c9-af0a-d9df30e158fb', '242b29a1-0366-44cc-b957-2899cc1de414', 'a421eead-7c9a-427f-a32a-e071f8981b55', '363dfd5d-3780-4b13-acbf-d0c1c0a435c3', '1ea11981-7e62-4bd5-b7ef-5c7895bdc0a9', 'a0af0bc7-6259-4ce3-91f9-396052e149ba', '94b26831-774e-44aa-9a03-4fc8d2adc3f9', 'a37110b2-1958-4e82-b8c9-b71fb488345a', '52358880-065b-497a-bcfd-7a349ca9f70b', 'ccfdf77c-ab9f-4993-ba2f-95081f6709be', '16275f45-5bc9-4211-b49d-009f3aa2b3c1', '73ba0426-8034-4048-a0f8-260c4386ba7e', '56dccc58-e275-4087-aafd-ef106fa50ec3', '2a79d57f-f822-408e-a434-70e99d8116c8', '8eb402aa-c2f5-4ebb-9639-38818b17f7c6', 'ae5d40c2-9c61-47f3-acb6-a307a21629ee', '84b5f3a9-2cd8-49f2-a503-961cd93a72ff', 'ced1b1a8-6643-43b8-8d72-e875db20f824', '54aee434-8b4e-43ab-aae6-89697a050346', '162cf675-317a-4aa1-9a69-cdc89959c312', '7410d7b2-f906-4d07-8afd-890517e89086', '811faaa5-cc53-414b-85da-b26633d5ff30', 'c0e85a99-f2ab-4e53-8d44-86515e1bd160', '755081c7-526b-438d-bd35-85874d9896df', '5162f6a9-cf03-4fe9-8037-c59a9054873a', 'fefd000c-f20b-4d7d-8894-fe24f5e60972', 'a8f3eb4a-b131-4687-bd19-704e4df1c83f', '35f581a8-81ca-4815-8d76-4ede6c3b12a7', '3092cb31-ad99-42e9-88d8-cde9189d7343', '11a40b0b-8a69-4699-9b15-35fe6d2f47dc', 'ff6ec4f8-bdad-4fc5-8d6e-35aa71d5b6d1', '1c3a3d01-b003-43b8-a307-1b5210271582', 'e635f465-904e-4382-bf57-f467267b8d05', '9456980e-d881-427b-8321-5eb4d8174496', '7011b355-a70e-42e8-8872-b7cee4a65458', '883eff91-4413-41ff-8623-63bd702ce48d', 'd42ccaba-2cd2-43a4-b1de-7ee87d517fc6', '5f6cd6e5-9c75-48b5-9170-6b860a143c9e', '7969df6b-4043-4f46-a076-e1f5958e16fe', '32c82f1d-5b7c-4159-93f9-1e69d0b37390', '076e94b8-38c0-4925-a525-40c99fc3c287', 'bbe02369-dec7-4df1-9269-fc34d02ed5ab', 'c2507823-6161-431e-b070-841b3eb025ac', '6a3c0982-bc23-4e2c-b544-bbc7a7e1102c', '4a9ee5a2-86dd-476a-b327-8e86856275c0', 'c6523148-1f38-4755-8618-fe553b28be4d', 'ef29ca1a-d4c4-4bfb-b19c-829f3617daa8', '3da6b170-c21c-44d8-aa8f-5cbfe52853dc', '8cba9040-0125-483a-9f0a-2ae494edfef1', '441ed58d-cba1-44f1-8d93-1deb30803f05', 'ad8b6c41-c79a-4d7a-9038-da807cd2c3a2', '2fca5442-97c3-4447-837a-e079596cfa49', '8b67b130-4527-4046-8e17-ee7657f4e3f8', 'd55b4f95-f87b-40fa-91a4-ac9f532d1fa9', 'dbebbe31-4d63-4e45-a808-0d587a516fea', '63ec3052-124d-4269-bc96-724e8fd0c696', '6359a42d-1a3c-4278-878d-9059b2868b1c', 'bcf3e4c7-8abc-4925-848e-6ee6f5ef4f81', '7c721329-aa8b-4b6e-9c7f-983e01c681a9', '821672c3-f5c9-40a1-a2c2-38d99dbb6295', 'f58eb4e5-6d7e-4175-9ec3-b390a24dd08d', 'c6a734d8-81fe-427e-a4de-2d287945348a', '0be08232-f73b-4f1d-a7aa-6360d7f060d8', 'e72c3014-8c86-495f-befa-06add5e6b49f', 'bb5f33fe-7fbc-4ffe-8d49-6136a3507562', 'dd654eca-9ea0-456b-aad1-c6d467da5043', 'be4fa1fd-1607-4a34-a882-617a8603cbfd', '587d89d5-2c24-4384-a87e-25cffeb36c9e', '254d34ea-164d-4f89-b429-909382f128be', 'ca831443-9b33-4289-8cb5-9f998f8395b2', 'ce2af2ba-a75a-4fd3-9f27-1c66211d3776', '5b14c760-ec96-4001-9229-5b10e637fda7', 'a318046a-8ef4-4608-87a7-7bf6dbd7878c', '79971335-d6be-4241-ad79-29e04fbfbea6', '5ff06647-bdf5-4bb5-b9da-f3ba4e07dbbd', 'e083e02a-fa64-4aac-acf8-3c250b29e3a4', 'e1bf5e07-0466-47b0-944f-0227857d9c53', '77ea9a37-4bd9-4915-97db-03ed3a0ef0e8', '439d4170-3850-4672-9114-2aa508ffe0b5', 'da6a4b25-72ec-4204-9f67-a680ac5b2044', '99f57dd2-4692-4b91-8e1d-a5fea5d4c392', '6014c11a-5bd0-4868-8b4e-ec82718ea824', '517ac58e-a640-43f7-a29c-27957fa5eafe', '5473c2aa-8310-45bf-a31e-3da2d09b0a3d', 'e071b90a-17ab-49a5-8693-983b1b236aa4', '20676c6a-8e87-4c05-b038-7baf03382438', 'e48622f6-bac8-4f64-97b3-85216ee23faa', '2eafd6f2-a322-40bc-9bd4-c4f39ae9c0b1', 'b992a9c1-54f2-4052-9275-becde64105c6', '174fed38-6eb4-4b34-a32b-d0fd02ba6ec9', 'ca75f32f-bbd0-4fe7-8f51-357a17836636', '2b56a83b-bcc6-4cd4-a0fc-127ee64eb403', 'b23d08c1-8ee3-4918-ba3f-318893386d7c', 'a804e760-9f4c-44d6-b250-b42f60cd038b', 'a3469365-f62c-40fc-8474-a332e60bbbd5', 'dd6c5847-fea6-4999-9e2f-e6ce4e49eae9', '395387b1-f004-490d-8fa9-5694f53ae2c4', '1f80ceeb-b367-4339-a1b2-c019a614b7bf', 'ded7da48-3f9e-4ac3-90b2-43680c37fab4', 'fa047aec-0ce9-44e0-b16e-c022105d2eab', '9452211a-5b9e-4a12-ba78-7b203f27b316', '503d24f4-6be9-44d6-83cc-dd8cab94faaf', '168ab8cf-d308-4def-80fc-79d0512c307c', '5e5276fe-cc3c-468c-9fc8-d1b792c3fb56', '6b117094-5b84-4db4-b0b4-81b45d29071b', '2af0a60d-40a1-43ac-98bf-0af459355c2a', '8df03ff6-0773-41b2-8f2e-ddcaa80eb372', '247d36c2-5a84-4a2f-ba4d-b440499c1129', '0779f6c8-bbda-4da6-a1ab-4987b4c87682', '8cb54a92-6f27-453b-aeb5-d65f1a6fee42', 'e4c0c9ab-92a8-4157-8320-6b90aa042900', '027075b2-914c-4fd1-97fb-29facb23eee6', '13b3c697-0e29-4572-82bf-39397c857da2', '79c535c2-6808-4363-b92a-955736e80df9', '900f48d1-9e84-458e-95e9-3dc6d83db2a0', '0384feac-9892-4308-bf44-320413e5b40e', '2584c886-3263-4ac9-80e3-9fd6926a0679', '6fffe475-4758-4bd7-a7de-c35d4fac93b3', '5a674826-2e5e-41ff-98cf-1261d318c28e', 'dea0b5c9-b59d-4b68-98b9-46f807c36767', 'dfe1ff57-532b-4b12-964f-18371c258da4', 'e8fff4a5-eecd-4176-b0b6-e29d5d17716f', '28b76447-c567-4adb-856a-31ac18c45625', '7570f94d-2865-48e2-8403-42d1bb3153fc', '3eab693f-7437-471b-8089-dfa124eabe6b', '46220d92-2210-4689-a174-3fdd032176ea', '355e602a-7d9e-49a6-bd56-0385e51c5871', '151e7bde-3bea-4147-bf9b-33d7c659b850', 'abd42a6b-d4e1-4cda-a5d4-eefb1c3d40ea', 'a836e514-713e-43eb-8fd1-010a17b4a95d', '2ec78ce8-aa44-4c2b-98ce-9ffe4cc94e24', 'a8b563ff-eaa7-4ce6-9456-a07576ff0f1e', '4a1d3c26-563b-4529-89ff-03f587339d6c', '48658ba8-13d0-4d7f-a109-97e065025785', 'a2048589-b765-4c41-87d9-a7d6a794ba19', 'f8d78176-db0f-417d-967b-bccea3113b96', 'd0de35b4-35c4-4e0b-8f1f-c3e6165617b6', 'b3584b63-82c7-4247-aa81-8e4c964f763b', '8b597866-efca-411b-8e1d-26501d126be0', '7da59cd5-7805-4e7d-97de-e81ea9ee9c87', '421e59b4-5696-413e-b34b-a0a462e3e65a', 'e34a5dd7-1ae5-43b4-bc19-8f341d722302', '6eba3b67-8268-4b11-ae37-33530c54072d', '52e44436-0669-48ef-a390-87c0d9f5b361', '79af5bc3-21da-460f-972b-dc6464e5e86e', 'dc1d4596-6d7d-499b-983c-0b9327002110', '599934f5-f93a-40b2-adc6-a95ade372506', '0c9eee8e-8879-496d-877e-bf3694fe3941', 'bb086ede-336f-41dd-9d95-956a643b3cba', '6bff3c03-63c4-40bf-bc74-4ee05021fa78', '84e1251e-bcf4-4e54-b4ac-d25ba15628e6', '4f163ca0-8f4e-4a6c-bdb9-e53168d41a70', 'ac43a975-24c7-480b-b41a-96fc4bbc4cf8', '53c1ab99-a849-48d6-8bc4-ae20eeea5c25', '8e447282-5c59-441c-a809-a9fdadeeae4a', '39b76e19-1ca1-4038-a317-4fc2c7095382', 'a149b36c-ca29-40af-82a9-6e6685b05ae2', '3fed89a1-f32f-46ac-b824-c4335049f475', 'e30255ab-4637-4916-8e4e-8dd6590e723e', 'e35a71df-4e36-4a6c-b119-0d7b06b389e9', '3351133e-446f-48ed-a287-504467fac509', '3b921e45-9dac-4040-b958-fadea978fd18', '5aeffbc2-d780-45f6-8536-987fe1a6c8ab', '1b9efe11-baf9-4a7d-a246-de7c3a8f7d57', '6ffd9e39-6c4e-46bf-aa6f-90135983cc44', '459a978f-aaa0-4ef2-8bdc-a4cd114114e1', '399a1883-70e1-4061-85e8-af240e34ecfd', '421a135d-23ae-4634-8131-19c70f499308', 'dd89f15e-8225-41ba-a9bf-cb8f556f50e0', '7f4a503c-2e33-46b3-9eed-9acfdf19d698', '56b7cb46-764d-4c49-ad46-2ded00eeca41', 'd108ab77-c97f-4a1e-83b4-e2661f37c702', 'afc79de4-988f-4727-b377-4e774e60173c', 'f59fa5b9-1a59-4227-b502-0ba14f9c76a4', 'eff21f09-1204-4b33-92b2-6dfadb25a063', 'f31bd585-2713-42b4-975b-6a66e12690ba', 'ffdd984a-cae9-441a-8071-2b739670832d', 'e59322dc-ae40-45c8-b6e3-7b607bc7d6f9', '92ad3fa3-6bda-422c-968c-72e3ad20340d', '28de3f40-d7e9-4276-8c39-c85dd684a880', '20a9d828-ef72-4902-add5-dc969d49af90', 'a6c7ca11-022f-41f7-b58d-40d142f3cf62', 'c1c0ab02-3d35-4cf3-b12d-d388ba456931', 'cc6a0d86-7201-4811-931b-b92b0cd3c40c', 'd87ede24-0ea1-4671-9e35-3ae0e6816f9b', 'b24b5aea-c379-410e-b4b3-33ddc21e8239', '662cfe27-92f2-4e3c-8904-6350e6402ae4', 'bdb0b481-a338-493c-a5cc-7485107ab8d7', '87e154c7-e639-444f-af83-4e7d8a58e36d', 'c2176086-dcb5-45b7-9d7d-a81206585a5c', '41fe0974-29ef-43b2-a2fc-49cd70866ffc', 'a24a777f-6284-4196-95ee-e5935c7dbf75', 'b70612cc-e4c8-4f87-b71a-daf677305552', '2af2ae3f-36ec-4770-bd9b-49bc8365c7cc', 'ba490fe8-e4a7-4f4d-b548-17ec92fb5eab', '4b861309-54c6-4b18-881e-c47b56f54dc1', 'cfe372f2-cb93-4493-82a3-e03570a05822', '77558826-9bd1-4069-8bdd-93efe80a7456', '1f39eb96-6a34-4dfb-b6a8-800941ecec93', 'ea928847-3c47-4c44-87cb-676130a1a362', 'c9deed16-eb5c-4830-bb88-49be1b8f01b8', '71275eb3-b57b-4feb-8dcf-8f4bbbe43721', '82ae8e0f-460f-497b-9b1a-72ccb97a4fd7', 'bf0511f5-7f9d-4047-9a20-65a9adeb871b', '726f7daa-0697-40ba-ab3b-a03c2bdc9d36', 'e24b8981-7bec-44db-8bb8-f7d6e445b753', 'ded44def-2b6d-4ab2-82fd-c2f9ad9d2330', 'cf4f47c0-b279-4dfa-b6b1-93f2bf18710a', '5d7ac5ad-4b83-43df-8f13-c010f020bdfc', '0ee66e0c-f9f2-4209-84a5-cc4f3e55d7d9', '1381c3f1-84d6-4bea-88f2-91c6b6068c92', 'f7b6c15c-4333-46e5-b933-d4f33c6b6118', '6be72ddb-b26d-4149-8dc9-d6bf1b2a3be1', '67ea9b40-7c45-42f4-aeae-2690a007509a', '10256902-5305-42c0-8cb5-af6a7067402d', '07637709-5620-41bc-bbe8-c26956c39644', 'b6e3b383-91e6-45d5-b636-f9cb9572e8e7', '8737c8bd-2d8d-4558-b3cf-bab5b6ccef00', '9569e1f9-40d7-4709-9bf7-8125d1b8fe8f', '3841bf69-dbf9-4f4f-a72f-3e0c602861bb', '6ae68c4f-8dd0-4341-b7a8-36a1b9e31b4f', '8a892a3f-f101-43f8-8493-8c732aecdc6d', 'b48e8bee-3725-4fff-acbb-4934c7ef8ad3', 'ac09b74f-70ef-41a2-b224-1fc8428e77a2', 'd71cdb47-66dc-4ba4-a822-ec5b45b643bf', '79d82e83-24b4-4a6e-aab0-a4e5292ae40f', 'f6cdbdd7-cba0-46a8-bc89-6394521ee660', 'b4107531-478b-47b0-aed0-5bdc3732afff', '8b6e94c4-3426-4500-a53c-defe2d1634cd', 'c6490f70-6567-4707-9dfd-edf7beee5d74', '2b506a0f-bae0-40ef-bbbf-42c3c1cef7c0', '9997d151-18de-4472-b191-0bff34b7d25e', 'd971c14d-fc21-4617-aaca-3327ca359aec', '75c21a6d-de3f-43cd-8aeb-e5e35c5f82c0', '5baa289a-076b-4121-ab1c-afd245af15dd', 'ac9ae286-b11a-47fc-afd1-bfa04a8cd4ec', '90d2be72-1c2a-4eee-9975-def00eb21c1b', '6bf83b67-c765-4c9f-922e-67052c87b264', 'c5878a7a-ed75-4162-b33b-8b303a5133d4', '2fd24e7d-8da2-4e62-a90a-650a90cdd47e', 'd77cfa98-b4bc-4961-a17c-09ca9d3259bf', '2b7d9c38-99e2-47aa-a4a9-bc94ef7b5db1', 'feb8ea9d-0b42-4050-a78f-7dcf90d1c5d6', '49b2ad03-580b-44ec-a7f8-7459e8b45f45', 'ced6765f-21c4-49b3-be91-20cf33101fbd', '315074bc-20d1-422e-8647-8ab8645c4957', '5e7e7b09-333e-40c9-9012-e874cda4a233', '7d0a02cc-0b7a-40c9-8e8e-12ad8181220e', '281af83c-facd-4f73-9ced-f59209fc735f', '5707d541-9ec9-4f2e-9585-183fc47fe9c3', '275552cd-cc02-4cfe-8baf-b5f524fe8428', 'e8fcc500-7f5c-4350-a674-17c3142ac04f', 'aeb39982-4743-4bdb-bd43-ac911ec7d2f3', '0fbbddbe-a53e-4b69-b8d5-982f82b9e195', '0e7162d0-7946-470c-ac04-27c66eef4e91', '0ed2c4db-4322-479b-872c-9f19ab6be064', 'b8d6c1db-a19a-42a3-bc1d-c4e5354f5e77', 'e03ba3d2-6451-4c8e-870d-0f0308be159a', '63119c45-324f-4023-a220-13eb0dba06b9', '1020eb85-d509-4392-ab0e-9780da4bed2c', '28f88eae-a10e-43ea-831e-2d647ae30b62', 'd9b2ae59-1a9b-4007-bb1d-86e4b9b6cbc2', '72181f7f-9ec3-497e-9e94-f9b175fa7483', '7f6f7142-1c22-4c23-b883-8dca3d5f09b5', 'ef6503de-f9da-478a-a72c-a118e6a5f185', 'd71d5fee-eda1-42bc-94f6-9ad7a15ccbc6', '1f14e8e2-f671-41b8-a648-ad4149a0e825', '56776f19-617e-48b8-a791-3f9442ef02c0', 'a5520473-8a3f-48df-b1f8-1a52371f9829', 'b2beeccb-46ae-408c-96fd-cf6bc25082f0', '0ba15bac-5bd9-44b4-8b87-0b45f45be33d', '8f76482a-fa8a-49e5-a4f9-06131ee66b53', 'a3169f69-5aa3-40b6-82c0-bb01862816fa', 'be8af161-31d2-4b40-84b4-04e8712f319c', '1ec2f116-f22d-483c-b873-b338e832f68f', 'fc06e780-1eaf-4194-9bf8-1a1f55d69894', '20e6546a-be5f-466a-be96-371a81fc9e8a', 'df944cc5-368a-430c-aa29-862d7211f679', '30d1c267-74cc-4c6b-bb85-4f63a23a9a65', 'b1dd07ce-0fb4-4af9-8cc0-4fcbcba2a1a8', 'ef925a4e-da3e-4d5f-ac86-24f00f9496e2', '6ba64cc0-b98f-47bd-ba1d-451645793abf', '28b41dd9-1908-4548-871f-380b35e46c55', '2651b335-f76a-429e-8276-11bcb82c3f1c', '0d769b5e-d2e2-4215-93fb-17d7725bd2e4', 'ce636d68-3ca8-4237-b101-5a2c5a75ecc7', 'b440a510-48e1-46c3-b00e-060cb1a8264a', '6cccca5e-cc8c-48e1-a4ba-12dc2875361c', '498123e1-a71b-4409-833d-b5784a814dca', '558bf73d-7b19-45b4-ba3d-80f7101b9efa', '58019e5b-8fa8-4cad-8377-d0892f08db99', 'fb471c52-586d-43d8-96cb-856a58bd367a', '1ac1db2e-2f03-4809-8c3f-25aac2582584', '55240e9c-fdec-4dcd-a096-7c64adad3bac', '50956782-0db0-4c53-867f-e2d3a9f726b1', '22ceac1a-2104-4c1c-837a-e511ba4851c0', '3f082a05-bca5-4c0a-b5e4-8553ff3940c6', '0c44c4e7-3932-48fa-a9b6-bb1bcd96927e', '3488ffea-0478-4c47-b54a-6d25c7e0c080', '846b50ba-10a8-4bd8-8fff-39131f289fc3', 'c642fd06-54cf-464d-86f3-a81acbb3afbc', '51ba2021-a0c8-4701-863e-35f525800662', '6ee6b9b3-47aa-4dae-b611-f3772ce61d08', '836c22b6-5554-47c8-80eb-85f45318988e', 'ed053aea-54da-4d5b-911f-68fcf1e6dbd1', '05f27796-a626-4f72-bfac-47fa11173c85', 'd44b5afc-85a3-420b-b05f-c56e1aaebc4d', '0b3e3726-244a-4e83-8b74-5ac4a38c3b88', '9c883862-97ed-413c-8900-0f65a6fc24e2', '2fc7d7b8-14d5-4978-b9c0-ff0bcfe9c417', '37eafb1c-ef97-4b10-bd7a-423de9c640ce', '95b79d2f-637e-4042-b30b-9157572c33f9', '0d149ec4-579f-4a98-9b9b-1b08e70b8e61', '988d2710-2cd2-4261-a785-91dbaeb5f230', '3a298f3a-e19b-41a1-99ff-e033755ae261', '1549c3f9-2c00-49df-a68b-5f5265b0a2ec', '00d389dd-02d7-48c3-8afe-a2112c5521ad', '95ed74f8-3c60-400c-b588-5ceea21bc1a7', '76c3aae4-aa21-4880-8570-0b50648befd5', '217ba36f-ac68-4005-9c02-9a65ca26ed6c', '094844b7-6b14-4902-bb09-4f135e8205f9', '0066c945-d003-428e-8cb3-d4017763e77e', 'bc41cd36-57dc-443d-82ff-9bbb77e956f7', 'c644093a-42d8-4232-95d0-fca5dbddb309', 'bef990af-0d46-48df-aa3a-a848d71734d4', '6acfd507-b340-4a75-8897-5e14639a1e25', 'a2c9caa2-fc8d-4b40-b861-cd67d69652dd', 'c5185717-320d-4f6b-90e4-7552cf7194b2', 'd1a2138e-e7e0-4ed1-abdb-9dc40b056ba3', 'ede34bc3-57b6-418e-a3fa-306a790fb9b2', '4a74382d-86da-45aa-85cc-41602037c4b4', '3ff5b6a7-4afa-4eee-b542-a4e451612dbf', 'ea5cd7fa-fa73-4001-8864-dadd40b97685', 'c8d4ba96-7061-4809-b68a-beadacd14c56', 'a2844420-148d-48c0-bc15-7d46b96902b9', '61de072e-8236-4d8f-abda-9b475bb6abd0', '0b2ede91-b2b4-41a0-acad-cc9c247d32cf', '7b1b17e6-1819-4196-bdee-2a75de027d0d', 'ca371a16-ce6e-4fe7-8adc-db030268076d', '04474bca-e680-4cc2-b200-f6a43202c498', '625cfde4-c3ef-4107-9d83-e8e58e15e979', '20749117-5de3-4063-9ea8-8673aab4661f', 'e59c55d2-4ec7-4873-b109-f945d7aae170', 'dc4fa4da-d701-4e5d-b158-773c5143caa4', '62e76ea6-bbb8-4a33-b8ae-b86a7009693a', '14c4188d-57fd-405b-99ee-a57e65e94061', '9c179d1d-8a8c-4d5c-b533-c07b1cc73c3b', '5a0fa4be-ba74-4c2f-bc23-ac018c668382', 'b8dcb237-4311-43c5-b5eb-9380a4eee6fe', 'fb1827ba-040c-4213-9e5d-1aff285f90b7', '58eca738-0d6e-4ebb-b984-568de39979b2', 'b2c4aa3d-b469-496e-9502-fb1a0baa477b', '4450c2dd-bb62-418b-9ccd-1d1471ec0635', 'a5bc7ed0-86ea-4906-b01e-cf760d05c318', '84b98f9f-6ca1-47fe-830e-72403ce00927', '8a2426c9-5ff9-4a42-8d48-00b91f7301bf', 'e03fc5a7-ce82-4126-a935-63fdec203d0c', '288e6cb8-94f6-4063-8fdc-ef4f702ec6c0', '3168b39b-d3e3-4621-b15e-ffad678f7055', 'edfbe39e-9686-419f-88eb-ff8a965c1796', '6e45e38d-371a-4cff-990b-371f58c67fbd', '48b019be-0623-4ffa-8f6b-e199324882dd', 'e4792c23-471f-4700-bd2b-a4e2999a36ee', 'c9506a4e-99f3-42cb-80fb-100408cd4278', '94ae5111-7ac0-4af7-b46d-9b7cc695f4ef', '628c4f39-f06f-43b6-b97c-0a60a4b4c9f6', 'e4078be2-431b-49f1-9008-b05e32fbbe79', '403a7af0-c3b2-4974-9b68-28a0d02483fb', '0b30b343-bf1e-4631-a5b1-9b3b06a72734', '42eb7e9d-847b-45a9-8dab-43256eef13c6', '2f02342d-75c4-41bb-b230-506a85408880', '19bd4fe6-0df9-4e82-99f3-74c9466f1da4', '1e94c0b4-6e4d-4773-ab4e-12384e17cb37', '0fe21fd4-39ad-46f2-b67b-79805a088e74', 'b23d9fcd-0ac0-48f3-83bc-92cc84e64063', '3cec5188-f452-4334-b941-174b5c8c4723', 'a11b075b-6eac-4c61-8e33-04793b6f6937', 'def12eb4-e139-4448-b687-4da941b3841d', '15639293-2e55-4da2-9aa7-fdce8cc97355', '11b62c2d-7d7d-472b-b44f-17d6b67f8281', '09725cd3-1bdf-458e-95f1-90db62590764', '5b1b1142-dd6d-4217-b728-1092fed22ed1', 'c3411143-20ab-4e5d-a35d-103f890c51f7', '3cdb7291-afd0-4211-b592-fb5bc825f16b', 'f4de9c4e-455f-4aee-b8c4-3f4b2321c794', 'a4b5f6b9-9675-4482-b75d-6bca5ccdefdc', '7c641264-22c8-4594-9aad-05bad376fc27', 'd5ef895b-318d-4586-b77b-94ae48dee88c', '472d1455-0159-49a6-a0f2-fab55ba480be', '4a5b1e8e-a7d0-433b-99d8-c1542f20980c', '99b96577-9c55-43f8-b375-6c8fc063b3a8', '48499582-9161-4e8f-b24e-c9445d163cee', '17d69078-4ec4-42b1-a44d-42ef87e7743d', '6d58becc-a1b4-4266-a465-759ed8b8dc86', '7c2c56e9-830c-405d-9549-1f2f02f4349e', '38838e0d-1134-4260-a585-4a05c5890ef6', 'ee7d5a30-df97-488c-8104-5f80f6d46ebb', 'e4f6216e-7ff1-43d4-8cb4-f12aef4f1d99', '23da67e6-9f7f-41d3-aae8-d3d046a2d4e7', '1f0a64cc-dd73-47be-bd81-61768f47b613', 'c03bf985-171c-474b-825c-bc7e7588ac05', '1e3d1864-1e71-43a8-a317-6dbda229da13', '808447d7-bcf4-4762-954d-5c0ef0aa75cd', 'e6e01d17-862e-4b68-9ac4-104c3776b11e', 'f9200ac3-505c-4e7d-9ad5-1761785c9f9c', '42c035dd-5a3c-4f6b-8dae-49454ae49927', '319acb3c-1991-4df3-80f6-6abfb37645e8', '63e3da43-f7fb-46e0-9c05-ea8843149ce4', 'b3ae58c3-3ea8-4707-8ba0-ffb6797e086f', '0591b65a-748e-43a7-a787-60ab7e0a1ec1', '2c5a86db-267d-4c1d-8682-d9171bdcfd10', '2df7a7ea-7559-4e5e-a5cd-65417c2be5f3', 'e6403f6a-5bf9-4ece-ac82-4d9919f324a1', '5887a342-b1e6-47e9-a908-0c24d863c61e', '262cb89d-fa06-4d36-9c51-8a45296e9402', '7b0ec8a4-85a6-4732-aedf-0d773ce3fc90', 'e76e9a25-d2b7-4bcc-b224-6ac6364fd123', '413fb4fd-1539-4ecd-b7b4-305b33451189', 'bf519355-c1fe-40d7-bf40-0dd2124fc348', 'd52cbde1-66c4-437a-b149-06283fc8e411', '65124c4d-3c8e-4736-98ba-35dd3f223f9e', '1fadfb93-cd07-4ce6-8b8c-7edd55f05d73', '32674092-4b82-4732-8618-9bb3be184250', 'dade7f45-1efe-4e83-99e9-7fd3aa96e902', '6c35bb10-b438-40f5-8e21-9089c1540851', '7609dcb2-e93a-474b-8c98-58aa2af9abde', '9fbfe153-dae6-4e60-adfa-150b76156366', '3d9492e6-0b98-4e37-9394-e5a2fd3aaea4', '32a9a45f-d036-4380-9637-a439bdc795f4', '0859d430-73f0-4e88-b0c5-36da53987095', 'fb39a40a-547d-4be9-a7ba-4bdc11ddc707', '6b0c52d4-2c84-4b0c-822c-aedde286698a', '5e8c5e74-60e8-42f6-9111-7ba40def39f9', 'dccfdd20-013e-49e9-bcd9-a5740e3ced89', '45fbfd59-796f-4a60-b05d-87860e89f2d7', 'dd46f904-9d5f-4abe-8c9a-63df22cd9934', 'ed3a2d12-943d-423e-b5cf-861c074b7131', '522d1e00-d4c4-421b-8f58-ec9d8d44e281', '9e928d95-d7ce-4a14-9135-728384dc120e', '3a9ec7d4-14cb-42d6-9c68-2e13a29a02e0', '66d7ee8e-1538-4471-b157-ed687a78b2f1', '16d06807-1cef-4b65-9efa-1877af32ebd2', '82b93202-c0af-4438-8486-81e9f8e81780', '22c4a407-e8e4-4475-9ca6-c608ff09c448', 'd98b4df0-64b3-4cdd-9fa2-77fbdf3308dc', '6734540d-a9e3-45c5-b799-41781555b5e3', '7f5739b1-14b1-41b1-bdc2-e910cac347d0', '74a756a4-1530-4e25-8ed7-e1459102479f', 'dfeebaf0-d5bf-4388-8b45-5d624388b691', 'f0da234c-0c81-4e3c-854c-73edb97f3554', '1c5ecaa4-73fa-411b-a97a-25a932d3f6da', '77b496cd-91e3-427f-b01b-51b5ad804762', '99707365-e00a-4864-a0e0-009ba2775117', '0c58d94e-8783-4bf5-9fbc-842c510e7d11', '7ce994f4-9cee-4c15-9737-0fb58b9f754b', '5ab9a82a-b7be-4b62-95c1-c76f289bd2e1', 'd8f5b972-47e4-41ff-8d4e-1e6543548463', '7e4a1fc7-6e7f-46ce-90d4-d2c8444593e6', 'd311d2b5-0d22-449f-b8f6-ae77620d2c94', '6aa98476-378d-4e34-92a9-0a9d9a187aa2', 'a9db3ded-5543-4c25-ae5d-651cfad3d621', '7195e248-987f-42d8-8ea7-61e4dfdd0559', '82f6d638-f120-4d0a-b7bd-134b810bc163', '016d5bfd-8728-4eac-a3cd-60d1a951c65e', '44e43598-ff62-409e-8544-0fe9a309ec01', '071902af-1a7b-4727-8bba-dc76e648aa2e', '5feaffcb-6565-405a-9f60-87477fe04480', '53c3bc2d-767e-4362-81c7-54f23a687b52', '3c53d003-1876-4be8-b72d-9002cb8cdd38', 'b1d089c8-a2c6-43d4-a2e1-a37568f3a4b4', '14dc010f-c0c9-4102-8a96-25054f314022', '5842d69e-69c2-481c-b7b4-e872506a1ed6', '01ceee1a-36f0-45c8-bcc3-950fa83c3474', '3be199d8-b559-4cb4-aae4-8fe0a4981e04', '30f4a07c-c0b8-42c2-a695-9bc4caf571be', 'a00c13a0-e9e2-4088-a156-17ad655cb8e5', '2731a0cc-bf81-4ce2-a7c1-edd992caa5c6', '36cefa27-0058-488c-8b8c-22494d240607', '790eed9c-171b-4a04-bfb2-33170da33313', '7f31bff5-5ec3-4bc1-bb5f-00a2fdd754de', '8b7f0753-3656-4f50-a943-0be28753cd63', 'ec95335d-96e0-4184-8edb-4360e000d99e', '63cccbe7-dd23-4794-8481-5ed3593095e8', '407f5b38-961d-4dca-892d-079b93175882', 'c9f82bd0-308d-47d3-8685-be89cb4e7fe4', '5f629ebd-5022-4dc4-ace6-ad66f53278f1', '5496c104-3f87-452c-8641-f006726f2f11', '58ceef0d-169e-4152-a9fe-a3c454274dce', '0c4a48d8-9bf8-4345-9cfe-89bb2307e727', '64145e5a-5a28-4358-8a11-cee9cb031a20', '7fcb7857-a5b0-443e-86c0-c0dcf7574490', '55fa3474-9c20-4c7b-b935-f52a97109246', '1e0150d1-921d-425c-90f3-7170ddc2b6e8', 'e50c30f9-5001-4b18-92ef-b7773d6ee50f', '3d7a3b66-8c51-4d01-85ba-e4ebc9eb5a28', '21e0005d-5898-4c25-a8fa-8ca7e0ef295a', '4a00fd81-5b7b-44bb-b133-51ed769ca93d', '71efb70b-b334-4d81-95e6-be4f97bac7e2', 'e8108d85-1079-4f43-889d-fb92b2fbff36', '924d7904-91e9-4af0-9aff-ddb4f82a33f5', '6469f094-148f-4663-b3b7-979647c3b8ee', 'a87c0482-f96e-4d0f-97ba-373be0711d9d', '230ad7a6-99d4-4d7d-a2f6-9ba92c868f49', 'f9b1b485-4a08-45f9-8ecc-ac9fbe76ec20', '01891e41-1503-4990-b8b6-1722fb89c787', 'f11171ce-27ec-472c-8569-627eb0069088', '5d447235-ec33-4006-8044-9b8515b471bb', '316dee50-de11-4831-92be-0c77bcaf84c9', '21a654a0-c9df-4922-a755-690376607ee3', '17bffc82-049e-41ad-b4fa-33fe6c656e91', '67496c9d-9b75-4d9c-b630-1a566fdf86e7', 'b47313e9-cb8a-4369-af3f-434c96e0e3ea', 'dd80a267-ddd6-4bb8-9d04-8495272ccee8', '9b1cbb31-f4e0-4333-8680-b05d18da4566', '98a8d26e-7c3c-4506-8309-dffa7362a2f0', 'cb0c3943-6042-45b2-84c9-f266ac49c04f', '794caa64-a714-489b-92c2-4114d67f95f1', '84de94bd-0cca-43ca-a9e8-acf7e11af93f', '571a2254-57cf-4747-88da-4a3a22cea2be', '379a670b-e06f-4a4e-8616-d0d9497ecdc6', 'dd235e6b-93fd-4f5e-a7a5-520c22d93e0f', '1bd9da38-ac5d-4b09-b014-d7fece70ed78', '5af56cd7-4099-49d4-900d-e7a6ad76348c', 'd50ec47c-f09f-4680-ba20-3a36b2005de8', '6229f155-a778-44c4-8a03-76b734dfbc59', 'e8e58bed-9a65-411f-a6d7-e29a7dd694f1', '167c6848-887c-49be-9e52-927b7ced283c', 'c52371f9-a166-4c07-af89-08c7b0bafb28', '63049214-caae-4765-8dd2-a5b6771263df', '6ccdc065-0d53-44d7-b078-37dbf182b81e', '9ab20d76-6423-4da2-9f9a-6606d185de20', '24a4bf9a-78f2-46cb-8f89-bcd10518aacf', 'bed201a2-480c-4244-a815-0b1976ba5cbd', '2f8b50f4-c5d1-4782-a763-0a762b5a2af6', 'f2b2903f-2d10-446e-a3f0-3787b2c7db35', 'eda8e1f2-7ec9-4f94-acef-951f51e9ada5', '397ab6bd-4dfb-4b78-9377-b73dc4167a5a', '8d912117-69aa-4dba-bc0f-f0a4bcf90cf0', 'e00df57c-7def-4f59-a34d-ad0d338327d4', '3e723abe-9711-4744-a412-f608e576d7fa', '01279644-42c1-4fa7-b059-fcd93ce691d4', '4a5fbcfe-ae04-40a4-bfa5-98671b386cdc', '730e899d-eba0-4416-a045-cf8626764af3', 'e2c96c4d-af86-4f8b-890a-42cf25f490b5', '73fb131d-77dd-46ad-b9c7-b93a62b16735', '27ad0efb-1e37-4c16-935c-f92f2ca8a5e8', 'b8c56b9b-58a2-4c8f-9358-45ebf43fe344', '17547e29-1f60-4213-9178-a0d4e286b17f', '971b6980-c34a-44bc-8bc1-34067bfceded', '4ec9f059-bd0f-422a-8f09-fd5bf8250b4a', '14972c6d-85c5-4724-ae70-64faadeef89f', 'ba6d06fd-acc4-4fca-a86f-2ec4fc9e94e2', '370727e8-adb1-4b90-837d-f87f6c7a9c2c', '9de88d0e-343f-42e1-95ec-580c4b060d89', '0d4a73d8-1809-4e6e-832a-6ca3d7adaf97', '8c12fab2-357f-415f-840e-8e3b133335ee', '357619da-1711-4f2c-ab37-14b56d42ca2b', '0d126e70-1fcf-4e30-9b55-e8df3c159cb1', '465d8a1d-912d-45dc-81cf-561344021942', '86a81026-8255-4365-a11a-b9c2f7ce510f', '63fc9f24-2042-4a2d-be6c-4cec45283d45', 'a38ef1ea-43d2-4051-aca5-553ea1be9b01', '2ba1b984-1e11-45be-be4d-27612dd5eeac', 'ff00b357-47a3-4fe1-9b5f-20e2f87df233', 'ea25b981-1cac-4ff8-9a32-b2715207220c', 'b6c03ab8-72d0-4c02-a1ef-887bd0d7cdd0', '4869fb8d-dc26-403d-ac06-b206b3dded90', '076a058b-fc92-4e55-b3c6-88629cceace4', '390ac719-04fd-4a31-979b-b047bc4298d0', '1c7b948a-c74b-4a93-bf4a-57743726dedd', '9e14070f-b196-4c33-8afb-b44470e2fec1', '84729264-21cb-406d-8426-8412a033ee30', '03397151-ed8b-48d6-a6ac-ab028a0de52e', '3c635a90-d48c-4a77-b970-31c8986f460b', '9d3e61e1-3306-477e-8892-2528ff34b860', '2adfbc3e-af2a-4371-949f-eab5d3af7c8d', '5d625220-0afb-483c-bae7-f76da372702c', 'c1460525-5294-4620-8aa2-9128d9fb3ccb', '092918c9-f622-45d3-85ff-c18140a9ea30', 'e0fba9e1-3ed5-4ab3-b48c-a2ea887471bc', 'b9a0e83f-4d94-41bd-a151-eee77ad9ef85', 'bb6b0115-52d9-44cd-b9aa-180af002d3dc', 'd8bb5e7a-c9e5-4a17-88a9-41ed493ad7ce', '8df626ab-e65d-4538-89b9-d87e7a9cbe52', '1c4571f2-981d-4ea2-9405-b5f2521adee2', '3a327611-c50b-4e0c-8f02-df08a1799e12', 'ae74f246-0954-4e28-937e-b1649cc7f71a', '23d0565e-2b30-4673-9421-34239db27d2a', '8f8f75ca-2144-46d2-9abc-346e37ec5458', '545a50b7-bc90-4ffa-bba0-826434d7542a', 'ec81565a-20b7-48c9-a7b5-74ae4b0cf639', '47008f5d-ccf9-40bc-8379-19f3da8ed91e', 'b691d167-e1c9-47f2-b97e-d2801c4df7b6', '12e9b217-291d-46ab-b601-5500b18a5c2c', 'cc99c52b-107a-44b8-9e49-8cdf17fcf042', 'b0b523aa-dcb0-4933-84bf-fcb4c72360ec', '8954ef04-0352-4366-95f7-a955767c185b', 'd53f6cbc-79c8-4571-802c-2f10ad96f2a0', '21c156d0-d6a2-4cd9-ba98-2d15185a282b', 'b7bd55e3-1c68-4f74-bdb9-dbf75f9b0211', 'f571560b-0b43-4f4b-948e-49f4790b3b29', 'ad500721-6634-4fa8-90d7-66eaa8847017', '4f1216cd-c2d8-4cd2-a1bf-2f7fe1eaa6c3', '51321a61-b29f-4657-a206-b509fffd6e92', 'e7a8148e-f164-456c-b50c-8eaf9d375722', 'a5faacef-0bfb-4eff-a880-25445c7dcb21', '64325b02-8b9b-48d6-add7-aceff0c6ef64', '778869a5-3fa2-4bf1-95f5-fe3882f2f993', 'fc6c7d40-7305-4c4b-a171-b1bbc71750de', 'a718212e-500d-4f7f-80bc-eb4a67aab083', '907a69ab-2acb-4eb8-9adc-191a4fd731e1', 'eca50ecc-79f8-411c-acd4-48e0e0ff378b', '02f92c04-4c04-4a80-9232-a6ae75cad381', '29d2fad3-107c-4720-9747-4532977c3f32', 'b61d286e-4b49-4660-8935-46c0ebd29a9e', '3ad28a07-133e-48f2-9035-8f8d1eb8c24c', '5aed71c3-cec6-405c-a439-a89de2809c40', '62b60af3-6d66-4011-bdcc-5094c9fec08b', '4992e948-d246-450d-ae1a-4b62f94e83d9', '858dd3d2-d1b8-4246-8859-8abf9b584dce', '6b218bb6-c248-40d3-90ce-ae668e3cc181', '93e31082-53b6-4474-bfc0-7e49c9c22889', '348462d6-0f6d-4905-8140-5edc753bc081', '3435d6ef-f176-4c39-9f35-1c6b72d9064b', '92cd0f21-1c47-4455-b586-75a714e0ddc1', '98be591d-cbb0-4f98-b98f-603a0edfccce', '6b910791-4559-4315-a268-a8f48e4d53d3', '92970f2a-6ce9-4804-9462-75bec75ce0f9', 'db81e8d3-169a-444e-b3b3-07f6edb92b9d', 'a458f83a-460f-477c-a251-2f98b1e97e83', 'ba4317f8-158c-44c5-911c-c39a8fca944c', '9189833d-aee8-450f-bca6-a5f325366617', 'a37a0845-7960-4669-a375-d26c8bd44a86', '7709638b-0fa5-4f1b-84a5-d48f968f3521', 'd0104f21-6657-414a-b81f-68b61a37cfda', 'bba9a586-f7c5-40f9-8902-db3359a2e5cd', 'a004fcd6-c740-4e50-817d-14b835222af7', '69c7f735-d9e4-4736-b493-f8af3ba8ab8f', 'b34485e4-f867-4c7d-be78-1a6da15ada1a', 'fa0429c4-beed-46ac-b934-b6553661e66e', 'c7fe2148-1712-4bba-aaea-dcd22c7b19d5', '6254032e-b05b-4538-bfbb-810e2a8090c5', '9a6104d4-4891-43ca-b9ed-06dde338cbeb', '5264cc79-87a2-43fb-b108-b21f9ff14ac6', '07f9e5d1-088e-40e5-b19a-b3ce274936d5', 'b79aa19e-a20e-4a48-9a06-458f45c88169', '44adcf38-9934-403d-93b7-ef965733e0e8', '51047dfb-8484-440d-853c-dc2d5fdc08cc', '1a6e0307-6500-47ae-a83a-a2ae25e804d4', '295c35ad-fc64-4c1e-a09a-248741a8ca4c', '10e69822-172f-4082-bbf1-7a96888c40ef', 'f26dd696-0483-418f-8fd6-757bcb0689a7', 'f18a38d8-62b6-4d16-951a-80a231dc012a', '459a48e9-3000-4842-8096-a514416cb0dd', 'bc7cc776-5dd5-4605-b127-b9fd88be7fac', '83529f64-d9da-4dde-89d6-2bb59d638c1d', '4a9e3255-27f0-49e9-bbce-a21464dd65cf', '96a3c56f-9763-49e4-b0ca-18aac7debceb', 'dec77de0-c16a-4a1f-aac0-0496566ce2f6', '938b78a5-d590-44e3-a9de-8171e8cc4ca9', '569ede5f-d8d9-4d23-8981-c31f46cd262c', '0ddbc4db-e644-488d-b7ee-983a1cf845aa', '8eff7180-cbf7-4ae3-9ce3-3ca511c18073', '6ed6d827-004a-4711-9f76-77b95cee30df', '9ba655bc-4d45-4352-8983-cf68c2387ccb', 'dae82764-0ddd-47cc-9adb-c6fbbaf045dd', 'f23c8858-f783-4574-9c1e-98ce586bf847', '33cb55a1-69f9-4008-bc2e-b52c79bbc323', 'b40dec95-54a1-4ed1-934f-6d12de959135', '208cb593-d430-4899-8589-352fceee36ab', 'ec4f9191-7d27-49c4-9870-961edb557c61', 'adb23cf0-7366-48ea-b205-4c5c33b6a004', '2f69c6e6-a23d-4c89-a5b5-5b1772255c6f', '10553b96-1562-4b60-b009-b47bbf2caf0b', 'c55d1874-ef29-4134-985e-9f46ffb01d12', 'de2966dd-b1aa-412f-8c38-24b09ee42900', 'f704d700-653c-4077-b179-edf6e23de54a', '3933efcc-4a3f-4e05-bbad-3c883066408e', '9b83e4ca-ffa1-41cc-8417-9d6acdee1b73', '85d0a8fd-c4f1-4293-9e5c-9bae6555c081', 'd3947ae9-4256-43ea-945a-4b47d81f77a0', 'c2f05948-e3ed-4561-81b7-8b6ad00192ea', '8df27e61-1945-45c2-be25-cf486adaef89', 'b6dee762-6eeb-4203-9666-8e763649bc45', '11e4d54f-7feb-4beb-a781-8097a1c0942e', '5c345e65-1114-4305-80ad-886dbed67c94', '79df99b3-cf1f-45f1-9f40-5feeff3b5770', '42600237-3f7c-4a23-b64b-690df1ff31e0', '6c76c7df-f89b-4601-bf0c-d908870c3eac', 'cff56c2e-534e-4260-8668-658e0525114e', '1e9cfff6-81e0-457e-837b-097d6412b6ba', '5abf3284-dfb4-4d41-9f94-aa6efd81ee03', '27a947c6-1908-4885-8f51-5808515edd9e', '1df91a7a-793e-4a90-a3e6-5c1e2d42a7a2', 'c0f80b50-302d-4b27-b5c4-95568b2c1acf', 'd14c4162-55e4-4bc5-be89-eb7eb95a5edb', '9c60a122-43a3-4829-bd36-57698fde4760', '002c926d-6597-4e43-9a9d-0e59c0a6045f', '3ce331fc-ba60-4b08-85d5-0fb0242162b6', 'c22da8c9-bcc8-4395-bcd8-3438ca677415', '9c73abbf-a568-4870-b308-19cb887d3aa7', 'fa6ccc0f-80e0-49bf-b382-9ecbc2f7a553', '5ba11fc2-6d44-4d92-846e-d86eb3540870', '5cc6a2cf-7788-4692-8e58-3ed717fc82c8', 'b04f3256-8ff6-4fd5-91f6-afcf8f97ab24', '087612fc-0882-4476-8bc6-e92636a493fb', 'bba77020-6014-4bff-a358-a44973081fdd', 'd115acd0-4e66-467e-bd91-bfacff22a027', '41cbd1d9-84bd-40f5-9a88-7a0e785465df', 'f0dd7346-191b-467b-92ab-8c608a62d0e1', 'b6197e87-8bb6-4cdb-ad8f-a8d6791bbd10', 'a90f89e3-21dc-4dbf-b190-1b0914902c60', '05940512-9b1c-416f-a080-a11f2845a2e6', '3d677b41-eff7-46df-aa7e-2f263ab2c3ae', '9f7ab86d-3f56-4360-a778-abec6869b9ed', 'e768a51c-57a2-492b-b4c7-0dc84ab684fb', 'f5a66f4e-8662-45f6-a6cd-cd9cb1bc2d3d', 'fc958fe1-e2d9-48b6-8332-77fb38be9660', 'e0a88233-681c-437d-8a10-57b761935078', '2e74bb17-6a84-4c41-8f19-9e6655aee45d', '3fde8d8e-3c6f-4876-9abe-8c093acdd143', 'bd382090-2b14-4f60-955c-fa30777ac5e0', '66dd86af-8c0b-4d2a-a4fa-c1f9c05dfe72', '1bcf2a70-a654-4074-b32e-b8cccf8437c1', '3001aa71-13cf-4351-9983-b66d8e2fbcc4', 'e83046d0-0fa3-4062-bc3d-c96fb40b86c5', 'babf5fb1-288e-4722-8fdb-2d8a1a605b0b', 'bab3cedb-411c-4ba6-9991-d25babecbddd', '45cd6933-f1d7-4717-aa18-0b82afbcdc9b', '640bcf39-4b4e-49c3-a960-af0722a3f52b', 'f7a6d15c-a432-4511-ae81-dafa899047b5', 'f0d2b09e-9334-41d5-8f0b-0ebf73544c30', '10eba0c6-0050-498d-a4f3-c25969d8f7b2', '90d82b32-bd2b-4b19-9022-af0ae689a614', 'bbc0521f-67ea-4522-bd11-d2d34b0c19ef', '72d54498-df51-49d8-8f93-9bf3685c2457', '01bf0843-dce1-4e35-ad0b-6e047d37f626', '750d6223-dcba-4aed-980e-1f0aec82e481', 'c2a01240-f0bd-4e9c-ba04-f6fab69dd6f0', 'eecb78cb-2914-42fc-93c9-fd780fc348ce', '432d727e-a000-43d8-a90f-7f99a3a88e50', '7f913acf-e11f-480a-940d-36604c40738f', '89117e8b-615e-4f0d-ae8a-bbed09b4b8c4', '9d2f9f12-6f32-42b3-9c6d-99d12b08b274', '44c41b4a-98e8-457b-b63a-a0eed9b88c4f', '00361e01-e800-40c8-a52a-be8571888e85', '7ceaa4d7-bae0-420d-ba97-b090c126d02a', '28c21643-3c12-4b7e-9a41-3178b16b6c2a', 'f33846ec-c97b-427d-8021-bd3f8caa2fee', '9731458a-47ed-49b0-9844-d6fe73c3b0d8', '1c1a71d0-0da4-4801-b1e8-7c728fe72cc9', 'a991ab97-5e75-4113-99ce-16047f1a2c31', 'b66dac18-16d7-468a-b8ae-790dbb5f92eb', 'd560dd01-54ed-4cee-839a-a83a233c6961', '35a6d4de-2a55-4f0e-be21-6238a1c81275', '6dc6aba4-2bcb-4499-9427-acc37aff6e3f', 'd91469f4-5cba-4b29-b67a-e712eeca40cc', 'b0d49a6f-c182-4367-9e72-e75af435f697', '9ccfcfb1-a9ef-450c-a755-7c2392f72901', '3cabf1ec-e10a-4053-a40e-e2f6dec99fad', '12d2fffd-b1c7-4ade-97de-40c121d98b03') and platform = 'TXYBA'")
    # data_list = bh_utils.query_data(query_sql)
    # print(data_list)
    # # #
    # # #
    # # # # #
    def handle_item(i):
        if i.get('comWordsMap'):
            i['comWordsMap'] = json.loads(i.get('comWordsMap'))
        if i.get('brandWords'):
            i['brandWords'] = json.loads(i.get('brandWords'))
        if i.get('comWords'):
            i['comWords'] = json.loads(i.get('comWords'))
        if i.get('keywords'):
            i['keywords'] = json.loads(i.get('keywords'))
        if i.get('productWordsMap'):
            i['productWordsMap'] = json.loads(i.get('productWordsMap'))
        type_t = i.get('type')
        # type_t = 'batch'
        # commit_task(i,'ING')
        # return task_send_queue(i,type_t)
        return platform_process(i)


    if data_list:
        with ThreadPoolExecutor(max_workers=30) as executor:
            futures = [executor.submit(handle_item, i) for i in data_list]

            for future in as_completed(futures):
                try:
                    future.result()
                except Exception as e:
                    logger.exception(f"platform_process 执行异常: {e}")

    # keywords = []
    # get_keyword_ranks()
