import asyncio
import json
import re
import time
import base64
import hashlib
import os
import random
import secrets
import threading
from enum import Enum
from urllib.parse import parse_qs, urlparse
from loguru import logger
import websockets
from utils.xianyu_utils import (
    decrypt, generate_mid, generate_uuid, trans_cookies,
    generate_device_id, generate_sign
)
from config import (
    WEBSOCKET_URL, HEARTBEAT_INTERVAL, HEARTBEAT_TIMEOUT,
    TOKEN_REFRESH_INTERVAL, TOKEN_RETRY_INTERVAL,
    SESSION_KEEPALIVE_INTERVAL, SESSION_KEEPALIVE_RETRY_INTERVAL, COOKIES_STR,
    LOG_CONFIG, AUTO_REPLY, DEFAULT_HEADERS, WEBSOCKET_HEADERS,
    APP_CONFIG, API_ENDPOINTS, YIFAN_API
)
# from app.logging_config import setup_logging  # 已移除，模块不存在
import sys
import aiohttp
from collections import defaultdict, deque
from typing import Any, Dict, Optional, Tuple
from db_manager import db_manager
from utils.notification_dispatcher import (
    dispatch_account_notifications,
    format_notification_template,
    get_notification_template_text,
    guess_verification_type,
    render_notification_template,
)


DELIVERY_BATCH_MAX_UNITS = 10
DELIVERY_BATCH_MAX_CHARS = 1200
PROTECTED_SESSION_COOKIE_FIELDS = (
    'unb',
    'sgcookie',
    'cookie2',
    '_m_h5_tk',
    '_m_h5_tk_enc',
    't',
    'cna',
    'havana_lgc2_77',
    '_tb_token_',
)
REQUIRED_SESSION_COOKIE_FIELDS = (
    'unb',
    'sgcookie',
    'cookie2',
    '_m_h5_tk',
    '_m_h5_tk_enc',
    't',
    'cna',
)

# 滑块验证补丁已废弃，使用集成的 Playwright 登录方法
# 不再需要猴子补丁，所有功能已集成到 XianyuSliderStealth 类中


# ============ Docker环境兼容工具 ============
class _DummyChildWatcher:
    """Docker环境下的虚拟子进程监视器"""
    def __enter__(self): return self
    def __exit__(self, *args): pass
    def is_active(self): return True
    def add_child_handler(self, *args, **kwargs): pass
    def remove_child_handler(self, *args, **kwargs): pass
    def attach_loop(self, *args, **kwargs): pass
    def close(self): pass
    def __del__(self): pass


class _DockerEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
    """Docker环境下的自定义事件循环策略"""
    def get_child_watcher(self):
        return _DummyChildWatcher()


def _is_docker_env() -> bool:
    """检测是否在Docker环境中运行"""
    return bool(os.getenv('DOCKER_ENV') or os.path.exists('/.dockerenv'))


async def _start_playwright_safe(cookie_id: str = "default"):
    """安全启动Playwright，兼容Docker环境
    
    Args:
        cookie_id: 用于日志标识的账号ID
        
    Returns:
        playwright实例，失败返回None
    """
    from playwright.async_api import async_playwright
    
    is_docker = _is_docker_env()
    old_policy = None
    
    if is_docker:
        logger.warning(f"【{cookie_id}】检测到Docker环境，应用asyncio修复")
        old_policy = asyncio.get_event_loop_policy()
        asyncio.set_event_loop_policy(_DockerEventLoopPolicy())
    
    try:
        playwright = await asyncio.wait_for(
            async_playwright().start(),
            timeout=30.0
        )
        if is_docker:
            logger.warning(f"【{cookie_id}】Docker环境下Playwright启动成功")
        return playwright
    except asyncio.TimeoutError:
        logger.error(f"【{cookie_id}】Playwright启动超时")
        return None
    finally:
        if old_policy:
            asyncio.set_event_loop_policy(old_policy)


class ConnectionState(Enum):
    """WebSocket连接状态枚举"""
    DISCONNECTED = "disconnected"  # 未连接
    CONNECTING = "connecting"  # 连接中
    CONNECTED = "connected"  # 已连接
    RECONNECTING = "reconnecting"  # 重连中
    FAILED = "failed"  # 连接失败
    CLOSED = "closed"  # 已关闭


class InitAuthError(Exception):
    """WebSocket 已建立，但初始化鉴权失败。"""


class AutoReplyPauseManager:
    """自动回复暂停管理器"""
    def __init__(self):
        # 存储每个chat_id的暂停信息 {chat_id: pause_until_timestamp}
        self.paused_chats = {}

    def pause_chat(self, chat_id: str, cookie_id: str):
        """暂停指定chat_id的自动回复，使用账号特定的暂停时间"""
        # 获取账号特定的暂停时间
        try:
            from db_manager import db_manager
            pause_minutes = db_manager.get_cookie_pause_duration(cookie_id)
        except Exception as e:
            logger.error(f"获取账号 {cookie_id} 暂停时间失败: {e}，使用默认10分钟")
            pause_minutes = 10

        # 如果暂停时间为0，表示不暂停
        if pause_minutes == 0:
            logger.info(f"【{cookie_id}】检测到手动发出消息，但暂停时间设置为0，不暂停自动回复")
            return

        pause_duration_seconds = pause_minutes * 60
        pause_until = time.time() + pause_duration_seconds
        self.paused_chats[chat_id] = pause_until

        # 计算暂停结束时间
        end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(pause_until))
        logger.info(f"【{cookie_id}】检测到手动发出消息，chat_id {chat_id} 自动回复暂停{pause_minutes}分钟，恢复时间: {end_time}")

    def is_chat_paused(self, chat_id: str) -> bool:
        """检查指定chat_id是否处于暂停状态"""
        if chat_id not in self.paused_chats:
            return False

        current_time = time.time()
        pause_until = self.paused_chats[chat_id]

        if current_time >= pause_until:
            # 暂停时间已过，移除记录
            del self.paused_chats[chat_id]
            return False

        return True

    def get_remaining_pause_time(self, chat_id: str) -> int:
        """获取指定chat_id剩余暂停时间（秒）"""
        if chat_id not in self.paused_chats:
            return 0

        current_time = time.time()
        pause_until = self.paused_chats[chat_id]
        remaining = max(0, int(pause_until - current_time))

        return remaining

    def cleanup_expired_pauses(self):
        """清理已过期的暂停记录"""
        current_time = time.time()
        expired_chats = [chat_id for chat_id, pause_until in self.paused_chats.items()
                        if current_time >= pause_until]

        for chat_id in expired_chats:
            del self.paused_chats[chat_id]


# 全局暂停管理器实例
pause_manager = AutoReplyPauseManager()

def log_captcha_event(cookie_id: str, event_type: str, success: bool = None, details: str = ""):
    """
    简单记录滑块验证事件到txt文件

    Args:
        cookie_id: 账号ID
        event_type: 事件类型 (检测到/开始处理/成功/失败)
        success: 是否成功 (None表示进行中)
        details: 详细信息
    """
    try:
        log_dir = 'logs'
        os.makedirs(log_dir, exist_ok=True)
        log_file = os.path.join(log_dir, 'captcha_verification.txt')

        timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
        status = "成功" if success is True else "失败" if success is False else "进行中"

        log_entry = f"[{timestamp}] 【{cookie_id}】{event_type} - {status}"
        if details:
            log_entry += f" - {details}"
        log_entry += "\n"

        with open(log_file, 'a', encoding='utf-8') as f:
            f.write(log_entry)

    except Exception as e:
        logger.error(f"记录滑块验证日志失败: {e}")

# setup_logging(LOG_CONFIG)  # 已移除，模块不存在

class XianyuLive:
    # 类级别的锁字典，为每个order_id维护一个锁（用于自动发货）
    _order_locks = defaultdict(lambda: asyncio.Lock())
    # 记录锁的最后使用时间，用于清理
    _lock_usage_times = {}
    # 记录锁的持有状态和释放时间 {lock_key: {'locked': bool, 'release_time': float, 'task': asyncio.Task}}
    _lock_hold_info = {}

    # 独立的锁字典，用于订单详情获取（不使用延迟锁机制）
    _order_detail_locks = defaultdict(lambda: asyncio.Lock())
    # 记录订单详情锁的使用时间
    _order_detail_lock_times = {}

    # 商品详情缓存（24小时有效）
    _item_detail_cache = {}  # {item_id: {'detail': str, 'timestamp': float, 'access_time': float}}
    _item_detail_cache_lock = asyncio.Lock()
    _item_detail_cache_max_size = 1000  # 最大缓存1000个商品
    _item_detail_cache_ttl = 24 * 60 * 60  # 24小时TTL

    # 类级别的实例管理字典，用于API调用
    _instances = {}  # {cookie_id: XianyuLive实例}
    _instances_lock = asyncio.Lock()
    
    # 类级别的密码登录时间记录，用于防止重复登录
    _last_password_login_time = {}  # {cookie_id: timestamp}
    _password_login_cooldown = 60  # 密码登录冷却时间：60秒
    _password_login_failure_backoff = {}  # {cookie_id: {'until': float, 'reason': str, 'seconds': int}}

    # 手动刷新状态：用于避免手动刷新与自动滑块/自动Cookie刷新互相踩踏
    _manual_refresh_state = {}  # {cookie_id: {'source': str, 'phase': str, 'started_at': float, 'previous_cookie_refresh_enabled': Optional[bool]}}
    _manual_refresh_lock = threading.Lock()
    _manual_refresh_handoff_ttl = 120  # 刷新交接恢复窗口（秒）

    # 认证恢复锁：同一账号同一时刻只允许一条密码登录恢复链路执行
    _auth_recovery_locks = {}  # {cookie_id: {'owner': str, 'acquired_at': float, 'expires_at': float}}
    _auth_recovery_lock = threading.Lock()
    _auth_recovery_lock_ttl = 240

    # 通用预热 token：用于手动刷新/恢复预检成功后的新实例首轮复用
    _auth_prewarmed_tokens = {}  # {cookie_id: {'token': str, 'timestamp': float, 'source': str}}
    _auth_prewarmed_token_ttl = 180

    # 初始化鉴权失败熔断：区分于 WebSocket 建链失败，避免重连风暴
    _init_auth_failure_state = {}  # {cookie_id: {'count': int, 'window_started_at': float, 'last_failure_at': float, 'last_reason': str, 'circuit_until': float}}
    _init_auth_failure_lock = threading.Lock()
    _init_auth_failure_window = 60
    _init_auth_failure_threshold = 3
    _init_auth_cooldown = 60

    # 扫码登录token预热缓存，避免扫码成功后正式任务立即再次刷新token
    _qr_prewarmed_tokens = {}  # {cookie_id: {'token': str, 'timestamp': float}}
    _qr_prewarmed_token_ttl = 180  # 秒

    # 扫码登录后的短期缓冲状态：首轮 token 刷新命中风控时，先做浏览器侧稳定化再决定是否上滑块
    _qr_login_grace_state = {}  # {cookie_id: {'timestamp': float, 'captcha_buffer_used': bool, 'browser_stabilized': bool}}
    _qr_login_grace_ttl = 180  # 秒

    @classmethod
    def _cleanup_auth_prewarmed_tokens(cls):
        """清理过期的通用预热 token 缓存。"""
        now = time.time()
        expired_cookie_ids = [
            cookie_id
            for cookie_id, token_info in cls._auth_prewarmed_tokens.items()
            if now - token_info.get('timestamp', 0) > cls._auth_prewarmed_token_ttl
        ]
        for cookie_id in expired_cookie_ids:
            cls._auth_prewarmed_tokens.pop(cookie_id, None)

    @classmethod
    def cache_auth_prewarmed_token(cls, cookie_id: str, token: str, source: str = 'generic_auth'):
        """缓存预检成功后的 token，供新实例首轮初始化复用。"""
        if not cookie_id or not token:
            return
        cls._cleanup_auth_prewarmed_tokens()
        cls._auth_prewarmed_tokens[cookie_id] = {
            'token': token,
            'timestamp': time.time(),
            'source': source,
        }

    @classmethod
    def pop_auth_prewarmed_token(cls, cookie_id: str) -> Optional[Dict[str, Any]]:
        """弹出通用预热 token，过期则忽略。"""
        if not cookie_id:
            return None
        cls._cleanup_auth_prewarmed_tokens()
        token_info = cls._auth_prewarmed_tokens.pop(cookie_id, None)
        if not token_info:
            return None
        if time.time() - token_info.get('timestamp', 0) > cls._auth_prewarmed_token_ttl:
            return None
        return token_info

    @classmethod
    def clear_auth_prewarmed_token(cls, cookie_id: str):
        if not cookie_id:
            return
        cls._auth_prewarmed_tokens.pop(cookie_id, None)

    @classmethod
    def _cleanup_qr_prewarmed_tokens(cls):
        """清理过期的扫码预热token缓存"""
        now = time.time()
        expired_cookie_ids = [
            cookie_id
            for cookie_id, token_info in cls._qr_prewarmed_tokens.items()
            if now - token_info.get('timestamp', 0) > cls._qr_prewarmed_token_ttl
        ]
        for cookie_id in expired_cookie_ids:
            cls._qr_prewarmed_tokens.pop(cookie_id, None)

    @classmethod
    def cache_qr_prewarmed_token(cls, cookie_id: str, token: str):
        """缓存扫码登录预热好的token，供后续正式实例直接复用"""
        if not cookie_id or not token:
            return
        cls._cleanup_qr_prewarmed_tokens()
        cls._qr_prewarmed_tokens[cookie_id] = {
            'token': token,
            'timestamp': time.time()
        }

    @classmethod
    def pop_qr_prewarmed_token(cls, cookie_id: str) -> Optional[Dict[str, Any]]:
        """弹出扫码登录预热token，过期则忽略"""
        if not cookie_id:
            return None
        cls._cleanup_qr_prewarmed_tokens()
        token_info = cls._qr_prewarmed_tokens.pop(cookie_id, None)
        if not token_info:
            return None
        if time.time() - token_info.get('timestamp', 0) > cls._qr_prewarmed_token_ttl:
            return None
        return token_info

    @classmethod
    def clear_qr_prewarmed_token(cls, cookie_id: str):
        """清理指定账号的扫码预热token缓存"""
        if not cookie_id:
            return
        cls._qr_prewarmed_tokens.pop(cookie_id, None)

    @classmethod
    def _cleanup_manual_refresh_state(cls):
        """清理过期的刷新交接恢复状态。"""
        now = time.time()
        expired_cookie_ids = []
        with cls._manual_refresh_lock:
            for cookie_id, state in cls._manual_refresh_state.items():
                if state.get('phase') != 'handoff_recovery':
                    continue
                expires_at = state.get('expires_at', 0)
                if expires_at and now > expires_at:
                    expired_cookie_ids.append(cookie_id)

            for cookie_id in expired_cookie_ids:
                cls._manual_refresh_state.pop(cookie_id, None)

        for cookie_id in expired_cookie_ids:
            logger.warning(f"【{cookie_id}】刷新交接恢复状态已过期，自动清理")

    @classmethod
    def get_manual_refresh_state(cls, cookie_id: str) -> Optional[Dict[str, Any]]:
        if not cookie_id:
            return None
        cls._cleanup_manual_refresh_state()
        with cls._manual_refresh_lock:
            state = cls._manual_refresh_state.get(cookie_id)
            return dict(state) if state else None

    @classmethod
    def mark_manual_refresh_handoff(cls, cookie_id: str, source: str = 'manual_refresh_handoff', ttl: int = None) -> Dict[str, Any]:
        """将手动刷新状态切换为交接恢复窗口，允许新实例做初始化恢复。"""
        if not cookie_id:
            return {'updated': False, 'reason': 'empty_cookie_id'}

        live_instance = cls.get_instance(cookie_id)
        previous_cookie_refresh_enabled = None
        if live_instance is not None:
            previous_cookie_refresh_enabled = live_instance.cookie_refresh_enabled

        now = time.time()
        expires_at = now + (ttl or cls._manual_refresh_handoff_ttl)
        with cls._manual_refresh_lock:
            state = cls._manual_refresh_state.get(cookie_id) or {}
            state.update({
                'source': source,
                'phase': 'handoff_recovery',
                'started_at': state.get('started_at', now),
                'updated_at': now,
                'handoff_started_at': now,
                'expires_at': expires_at,
                'slider_failed_bypass_used': state.get('slider_failed_bypass_used', False),
                'previous_cookie_refresh_enabled': state.get('previous_cookie_refresh_enabled', previous_cookie_refresh_enabled),
            })
            cls._manual_refresh_state[cookie_id] = state

        logger.warning(
            f"【{cookie_id}】已进入刷新交接恢复窗口，允许新实例执行初始化恢复 (有效期 {int(expires_at - now)} 秒)"
        )
        return {'updated': True, 'phase': 'handoff_recovery', 'expires_at': expires_at}

    @classmethod
    def consume_manual_refresh_slider_failed_bypass(cls, cookie_id: str) -> bool:
        if not cookie_id:
            return False
        cls._cleanup_manual_refresh_state()
        with cls._manual_refresh_lock:
            state = cls._manual_refresh_state.get(cookie_id)
            if not state or state.get('phase') != 'handoff_recovery':
                return False
            if state.get('slider_failed_bypass_used'):
                return False
            state['slider_failed_bypass_used'] = True
            state['updated_at'] = time.time()
            return True

    @classmethod
    def _cleanup_auth_recovery_locks(cls):
        now = time.time()
        expired_cookie_ids = []
        with cls._auth_recovery_lock:
            for cookie_id, state in cls._auth_recovery_locks.items():
                if now > state.get('expires_at', 0):
                    expired_cookie_ids.append(cookie_id)
            for cookie_id in expired_cookie_ids:
                cls._auth_recovery_locks.pop(cookie_id, None)

    @classmethod
    def acquire_auth_recovery_lock(cls, cookie_id: str, owner: str, ttl: int = None) -> Tuple[bool, Optional[Dict[str, Any]]]:
        if not cookie_id or not owner:
            return False, None
        cls._cleanup_auth_recovery_locks()
        now = time.time()
        expires_at = now + (ttl or cls._auth_recovery_lock_ttl)
        with cls._auth_recovery_lock:
            existing = cls._auth_recovery_locks.get(cookie_id)
            if existing and existing.get('owner') != owner and now <= existing.get('expires_at', 0):
                return False, dict(existing)
            cls._auth_recovery_locks[cookie_id] = {
                'owner': owner,
                'acquired_at': now,
                'expires_at': expires_at,
            }
        return True, None

    @classmethod
    def release_auth_recovery_lock(cls, cookie_id: str, owner: str = None):
        if not cookie_id:
            return
        with cls._auth_recovery_lock:
            existing = cls._auth_recovery_locks.get(cookie_id)
            if not existing:
                return
            if owner and existing.get('owner') != owner:
                return
            cls._auth_recovery_locks.pop(cookie_id, None)

    @classmethod
    def get_init_auth_failure_state(cls, cookie_id: str) -> Optional[Dict[str, Any]]:
        if not cookie_id:
            return None
        with cls._init_auth_failure_lock:
            state = cls._init_auth_failure_state.get(cookie_id)
            if not state:
                return None
            if state.get('circuit_until') and time.time() > state.get('circuit_until', 0):
                state = {
                    'count': 0,
                    'window_started_at': 0,
                    'last_failure_at': state.get('last_failure_at', 0),
                    'last_reason': state.get('last_reason'),
                    'circuit_until': 0,
                }
                cls._init_auth_failure_state[cookie_id] = state
            return dict(state)

    @classmethod
    def record_init_auth_failure(cls, cookie_id: str, reason: str) -> Dict[str, Any]:
        now = time.time()
        with cls._init_auth_failure_lock:
            state = cls._init_auth_failure_state.get(cookie_id) or {
                'count': 0,
                'window_started_at': now,
                'last_failure_at': 0,
                'last_reason': '',
                'circuit_until': 0,
            }
            window_started_at = state.get('window_started_at', 0)
            if not window_started_at or (now - window_started_at) > cls._init_auth_failure_window:
                state['count'] = 0
                state['window_started_at'] = now
                state['circuit_until'] = 0

            state['count'] = int(state.get('count', 0)) + 1
            state['last_failure_at'] = now
            state['last_reason'] = str(reason or '')
            if state['count'] >= cls._init_auth_failure_threshold:
                state['circuit_until'] = now + cls._init_auth_cooldown

            cls._init_auth_failure_state[cookie_id] = state
            return dict(state)

    @classmethod
    def clear_init_auth_failure_state(cls, cookie_id: str):
        if not cookie_id:
            return
        with cls._init_auth_failure_lock:
            cls._init_auth_failure_state.pop(cookie_id, None)

    @classmethod
    def _cleanup_qr_login_grace_state(cls):
        """清理过期的扫码登录缓冲状态"""
        now = time.time()
        expired_cookie_ids = [
            cookie_id
            for cookie_id, state in cls._qr_login_grace_state.items()
            if now - state.get('timestamp', 0) > cls._qr_login_grace_ttl
        ]
        for cookie_id in expired_cookie_ids:
            cls._qr_login_grace_state.pop(cookie_id, None)

    @classmethod
    def mark_qr_login_grace(cls, cookie_id: str, **extra_state):
        """标记账号刚完成扫码登录，后续首轮 token 刷新可走更保守的缓冲分支"""
        if not cookie_id:
            return
        cls._cleanup_qr_login_grace_state()
        state = {
            'timestamp': time.time(),
            'captcha_buffer_used': False,
            'browser_stabilized': False,
        }
        state.update(extra_state)
        cls._qr_login_grace_state[cookie_id] = state

    @classmethod
    def get_qr_login_grace(cls, cookie_id: str) -> Optional[Dict[str, Any]]:
        """获取扫码登录缓冲状态，过期则自动忽略"""
        if not cookie_id:
            return None
        cls._cleanup_qr_login_grace_state()
        state = cls._qr_login_grace_state.get(cookie_id)
        if not state:
            return None
        if time.time() - state.get('timestamp', 0) > cls._qr_login_grace_ttl:
            cls._qr_login_grace_state.pop(cookie_id, None)
            return None
        return state

    @classmethod
    def update_qr_login_grace(cls, cookie_id: str, **updates):
        """更新扫码登录缓冲状态"""
        state = cls.get_qr_login_grace(cookie_id)
        if not state:
            return None
        state.update(updates)
        cls._qr_login_grace_state[cookie_id] = state
        return state

    @classmethod
    def clear_qr_login_grace(cls, cookie_id: str):
        """清理指定账号的扫码登录缓冲状态"""
        if not cookie_id:
            return
        cls._qr_login_grace_state.pop(cookie_id, None)

    @classmethod
    def _cleanup_password_login_failure_backoff(cls):
        """清理已过期的密码登录失败退避状态"""
        now = time.time()
        expired_cookie_ids = [
            cookie_id
            for cookie_id, state in cls._password_login_failure_backoff.items()
            if now >= state.get('until', 0)
        ]
        for cookie_id in expired_cookie_ids:
            cls._password_login_failure_backoff.pop(cookie_id, None)

    @classmethod
    def get_password_login_failure_backoff(cls, cookie_id: str) -> Optional[Dict[str, Any]]:
        """获取当前账号的密码登录失败退避状态"""
        if not cookie_id:
            return None
        cls._cleanup_password_login_failure_backoff()
        return cls._password_login_failure_backoff.get(cookie_id)

    @classmethod
    def clear_password_login_failure_backoff(cls, cookie_id: str):
        """清理指定账号的密码登录失败退避状态"""
        if not cookie_id:
            return
        cls._password_login_failure_backoff.pop(cookie_id, None)

    @classmethod
    def set_password_login_failure_backoff(cls, cookie_id: str, reason: str, seconds: int):
        """设置密码登录失败后的退避时间"""
        if not cookie_id or seconds <= 0:
            return
        cls._password_login_failure_backoff[cookie_id] = {
            'until': time.time() + seconds,
            'reason': reason,
            'seconds': seconds,
            'created_at': time.time(),
        }

    @staticmethod
    def classify_password_login_failure(error_message: str) -> Tuple[str, int]:
        """按失败类型返回(原因标签, 退避秒数)"""
        message = (error_message or "").lower()
        if any(keyword in message for keyword in ["账号密码错误", "账密错误", "用户名或密码错误", "密码错误"]):
            return "credentials", 1800
        if any(keyword in message for keyword in ["前置滑块", "风控", "拦截", "框体错误", "点击框体重试"]):
            return "risk_control", 900
        if any(keyword in message for keyword in ["滑块验证失败", "未找到滑块容器"]):
            return "slider_failed", 600
        if any(
            keyword in message for keyword in [
                "未找到登录表单",
                "未找到登录iframe",
                "session过期且清理会话状态后未找到登录表单",
                "session验证异常且清理会话状态后未找到登录表单",
            ]
        ):
            return "login_form_missing", 90
        if any(keyword in message for keyword in ["页面会话已失效", "target page, context or browser has been closed"]):
            return "unknown", 180
        if any(keyword in message for keyword in ["网络", "timeout", "cannot connect", "连接", "dns", "ssl"]):
            return "network", 180
        return "unknown", 300
    
    def _safe_str(self, e):
        """安全地将异常转换为字符串"""
        try:
            return str(e)
        except Exception:
            try:
                return repr(e)
            except Exception:
                return "未知错误"

    def _mask_secret_value(self, value: str, head: int = 6, tail: int = 4) -> str:
        text = str(value or '')
        if not text:
            return ''
        if len(text) <= head + tail:
            return '***'
        return f"{text[:head]}***{text[-tail:]}"

    def _summarize_cookie_string(self, cookie_string: str) -> str:
        cookie_string = str(cookie_string or '').strip()
        if not cookie_string:
            return 'empty-cookie'

        segments = []
        for part in cookie_string.split(';'):
            part = part.strip()
            if not part:
                continue
            if '=' in part:
                key, value = part.split('=', 1)
                segments.append(f"{key.strip()}={self._mask_secret_value(value.strip(), head=4, tail=2)}")
            else:
                segments.append(self._mask_secret_value(part, head=4, tail=2))

        preview = '; '.join(segments[:6])
        if len(segments) > 6:
            preview += f"; ...(+{len(segments) - 6} fields)"
        return preview

    @staticmethod
    def _new_risk_session_id(prefix: str = 'risk') -> str:
        return f"{prefix}_{secrets.token_hex(8)}"

    def _normalize_risk_trigger_scene(self, trigger_reason: str = None, default: str = 'unknown') -> str:
        text = str(trigger_reason or '').strip()
        if not text:
            return default
        lower_text = text.lower()
        if 'token' in lower_text or 'session' in lower_text or '令牌' in text:
            return 'token_refresh'
        if 'password' in lower_text or '账密' in text or '登录' in text:
            return 'password_login'
        if 'cookie' in lower_text or '连接' in text or '失败' in text:
            return 'auto_cookie_refresh'
        return default

    def _sanitize_verification_meta(self, verification_url: str = None) -> Dict[str, Any]:
        text = str(verification_url or '').strip()
        if not text:
            return {}

        try:
            parsed = urlparse(text)
            if not parsed.scheme and not parsed.netloc:
                return {'verification_source': text[:120]}

            meta: Dict[str, Any] = {
                'verification_host': parsed.netloc or None,
                'verification_path': parsed.path or None,
            }
            query = parse_qs(parsed.query or '')
            x5secdata = query.get('x5secdata', [None])[0]
            if x5secdata:
                meta['verification_token_hash'] = hashlib.sha256(x5secdata.encode('utf-8')).hexdigest()[:16]
            action = query.get('action', [None])[0]
            if action:
                meta['verification_action'] = action
            step = query.get('x5step', [None])[0]
            if step:
                meta['verification_step'] = step
            return {key: value for key, value in meta.items() if value is not None}
        except Exception as e:
            logger.debug(f"【{self.cookie_id}】解析验证链接失败: {self._safe_str(e)}")
            return {'verification_source': text[:120]}

    def _build_risk_event_meta(self, trigger_scene: str = None, verification_url: str = None, extra: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
        payload: Dict[str, Any] = {}
        if trigger_scene:
            payload['trigger_scene'] = trigger_scene
        payload.update(self._sanitize_verification_meta(verification_url))
        if isinstance(extra, dict):
            payload.update({key: value for key, value in extra.items() if value is not None})
        return payload or None

    def _create_risk_log(
        self,
        event_type: str,
        event_description: str,
        processing_status: str = 'processing',
        processing_result: str = None,
        error_message: str = None,
        session_id: str = None,
        trigger_scene: str = None,
        result_code: str = None,
        event_meta: Optional[Dict[str, Any]] = None,
        duration_ms: Optional[int] = None,
    ) -> Optional[int]:
        try:
            return db_manager.add_risk_control_log(
                cookie_id=self.cookie_id,
                event_type=event_type,
                session_id=session_id,
                trigger_scene=trigger_scene,
                result_code=result_code,
                event_description=event_description,
                event_meta=event_meta,
                processing_result=processing_result,
                processing_status=processing_status,
                error_message=error_message,
                duration_ms=duration_ms,
            )
        except Exception as e:
            logger.error(f"【{self.cookie_id}】记录风控日志失败: {self._safe_str(e)}")
            return None

    def _update_risk_log(
        self,
        log_id: Optional[int],
        *,
        event_description: str = None,
        processing_status: str = None,
        processing_result: str = None,
        error_message: str = None,
        session_id: str = None,
        trigger_scene: str = None,
        result_code: str = None,
        event_meta: Optional[Dict[str, Any]] = None,
        duration_ms: Optional[int] = None,
    ) -> None:
        if not log_id:
            return
        try:
            db_manager.update_risk_control_log(
                log_id=log_id,
                event_description=event_description,
                processing_status=processing_status,
                processing_result=processing_result,
                error_message=error_message,
                session_id=session_id,
                trigger_scene=trigger_scene,
                result_code=result_code,
                event_meta=event_meta,
                duration_ms=duration_ms,
            )
        except Exception as e:
            logger.error(f"【{self.cookie_id}】更新风控日志失败: {self._safe_str(e)}")

    @staticmethod
    def _extract_cookie_value(cookie_info: Optional[Dict[str, Any]]) -> str:
        """兼容不同调用方返回字段名，提取cookie字符串"""
        if not cookie_info:
            return ''
        return (
            cookie_info.get('value')
            or cookie_info.get('cookies_str')
            or cookie_info.get('cookie_value')
            or ''
        )

    def _load_proxy_config(self) -> dict:
        """从数据库加载当前账号的代理配置"""
        try:
            proxy_config = db_manager.get_cookie_proxy_config(self.cookie_id)
            return proxy_config
        except Exception as e:
            logger.warning(f"【{self.cookie_id}】加载代理配置失败: {e}，使用默认配置（无代理）")
            return {
                'proxy_type': 'none',
                'proxy_host': '',
                'proxy_port': 0,
                'proxy_user': '',
                'proxy_pass': ''
            }

    def _get_proxy_url(self) -> str:
        """根据代理配置生成代理URL
        
        Returns:
            代理URL字符串，如果没有配置代理则返回None
        """
        if not self.proxy_config or self.proxy_config.get('proxy_type', 'none') == 'none':
            return None
        
        proxy_type = self.proxy_config.get('proxy_type', 'none')
        proxy_host = self.proxy_config.get('proxy_host', '')
        proxy_port = self.proxy_config.get('proxy_port', 0)
        proxy_user = self.proxy_config.get('proxy_user', '')
        proxy_pass = self.proxy_config.get('proxy_pass', '')
        
        if not proxy_host or not proxy_port:
            return None
        
        # 构建代理URL
        if proxy_user and proxy_pass:
            # 带认证的代理
            proxy_url = f"{proxy_type}://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
        else:
            # 无认证的代理
            proxy_url = f"{proxy_type}://{proxy_host}:{proxy_port}"
        
        return proxy_url

    def _set_connection_state(self, new_state: ConnectionState, reason: str = ""):
        """设置连接状态并记录日志"""
        if self.connection_state != new_state:
            old_state = self.connection_state
            self.connection_state = new_state
            self.last_state_change_time = time.time()
            
            # 记录状态转换
            state_msg = f"【{self.cookie_id}】连接状态: {old_state.value} → {new_state.value}"
            if reason:
                state_msg += f" ({reason})"
            
            # 根据状态严重程度选择日志级别
            if new_state == ConnectionState.FAILED:
                logger.error(state_msg)
            elif new_state == ConnectionState.RECONNECTING:
                logger.warning(state_msg)
            elif new_state == ConnectionState.CONNECTED:
                logger.success(state_msg)
            else:
                logger.info(state_msg)

    async def _interruptible_sleep(self, duration: float):
        """可中断的sleep，将长时间sleep拆分成多个短时间sleep，以便及时响应取消信号
        
        Args:
            duration: 总睡眠时间（秒）
        """
        # 将长时间sleep拆分成多个1秒的短sleep，这样可以及时响应取消信号
        chunk_size = 1.0  # 每次sleep 1秒
        remaining = duration
        
        while remaining > 0:
            sleep_time = min(chunk_size, remaining)
            try:
                await asyncio.sleep(sleep_time)
                remaining -= sleep_time
            except asyncio.CancelledError:
                # 如果收到取消信号，立即抛出
                raise

    def _reset_stream_activity_state(self, connected_at: Optional[float] = None):
        """重置当前连接的消息流活性状态。"""
        now = connected_at or time.time()
        self.last_non_heartbeat_message_time = now
        self.last_sync_package_time = 0
        self.last_user_chat_time = 0
        self.last_heartbeat_response = 0
        self.last_sent_heartbeat_mid = None
        self.pending_heartbeat_mids.clear()
        self.last_stream_watchdog_reconnect_time = 0

    def _mark_non_heartbeat_message(self, received_at: Optional[float] = None, *, is_sync_package: bool = False):
        """记录最近一次非心跳业务包时间。"""
        now = received_at or time.time()
        self.last_non_heartbeat_message_time = now
        if is_sync_package:
            self.last_sync_package_time = now
        if self.stream_watchdog_trigger_times:
            self.stream_watchdog_trigger_times.clear()

    async def _force_websocket_reconnect(self, reason: str) -> bool:
        """主动关闭当前WebSocket，让主循环重新建立业务流连接。"""
        ws = self.ws
        if not ws:
            logger.info(f"【{self.cookie_id}】{reason}，但当前没有活跃的WebSocket连接")
            return False

        if getattr(ws, "closed", False):
            logger.info(f"【{self.cookie_id}】{reason}，但当前WebSocket已关闭，等待主循环重连")
            return False

        self._set_connection_state(ConnectionState.RECONNECTING, reason)
        logger.warning(f"【{self.cookie_id}】{reason}，主动关闭当前WebSocket触发重连")
        try:
            await asyncio.wait_for(ws.close(), timeout=2.0)
            logger.warning(f"【{self.cookie_id}】当前WebSocket已关闭，主循环将使用最新状态重新连接")
            return True
        except asyncio.TimeoutError:
            logger.warning(f"【{self.cookie_id}】主动关闭WebSocket超时，等待主循环自行回收连接")
        except Exception as e:
            logger.warning(f"【{self.cookie_id}】主动关闭WebSocket失败: {self._safe_str(e)}")
        return False

    def _record_message_stream_watchdog_trigger(self, occurred_at: Optional[float] = None) -> int:
        """记录业务流看门狗触发次数，便于识别重复假在线。"""
        now = occurred_at or time.time()
        window_seconds = max(60, int(self.message_stream_notification_window or 0))
        while self.stream_watchdog_trigger_times and now - self.stream_watchdog_trigger_times[0] > window_seconds:
            self.stream_watchdog_trigger_times.popleft()
        self.stream_watchdog_trigger_times.append(now)
        return len(self.stream_watchdog_trigger_times)

    async def _maybe_notify_message_stream_stale(self, occurred_at: float, connected_for: float, business_idle: float):
        """仅在短时间重复触发时发送业务流假在线通知，避免单次波动刷屏。"""
        trigger_count = self._record_message_stream_watchdog_trigger(occurred_at)
        if trigger_count < 2:
            return

        window_minutes = max(1, int(self.message_stream_notification_window // 60))
        sync_desc = (
            f"最近同步包距今{(occurred_at - self.last_sync_package_time):.0f}秒"
            if self.last_sync_package_time else
            "当前连接尚未收到同步包"
        )
        user_chat_desc = (
            f"最近真实买家消息距今{(occurred_at - self.last_user_chat_time):.0f}秒"
            if self.last_user_chat_time else
            "当前连接尚未收到真实买家消息"
        )
        notification_message = (
            f"业务消息流疑似假在线，最近{window_minutes}分钟内已连续触发{trigger_count}次自动重连。"
            f"已连接{connected_for:.0f}秒，最近非心跳业务包距今{business_idle:.0f}秒，"
            f"{sync_desc}，{user_chat_desc}"
        )
        await self.send_token_refresh_notification(notification_message, "message_stream_stale")

    async def message_stream_watchdog_loop(self):
        """检测“只有心跳、没有业务包”的假在线状态。"""
        heartbeat_stale_timeout = max(self.heartbeat_timeout * 2, self.heartbeat_interval * 3)
        try:
            while True:
                try:
                    from cookie_manager import manager as cookie_manager
                    if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
                        logger.info(f"【{self.cookie_id}】账号已禁用，停止业务流看门狗")
                        break

                    await self._interruptible_sleep(self.stream_watchdog_check_interval)

                    ws = self.ws
                    if not ws or getattr(ws, "closed", False):
                        continue

                    if not self.last_successful_connection:
                        continue

                    now = time.time()
                    connected_for = now - self.last_successful_connection
                    if connected_for < self.stream_watchdog_grace_period:
                        continue

                    if not self.last_heartbeat_response:
                        continue

                    heartbeat_age = now - self.last_heartbeat_response
                    if heartbeat_age > heartbeat_stale_timeout:
                        continue

                    last_business_at = self.last_non_heartbeat_message_time or self.last_successful_connection
                    business_idle = now - last_business_at
                    if business_idle < self.message_stream_watchdog_timeout:
                        continue

                    if (
                        self.last_stream_watchdog_reconnect_time
                        and now - self.last_stream_watchdog_reconnect_time < self.message_stream_watchdog_timeout / 2
                    ):
                        continue

                    self.last_stream_watchdog_reconnect_time = now
                    if self.last_sync_package_time:
                        sync_status = f"最近同步包距今{(now - self.last_sync_package_time):.0f}秒"
                    else:
                        sync_status = "当前连接尚未收到同步包"
                    if self.last_user_chat_time:
                        user_chat_status = f"，最近真实买家消息距今{(now - self.last_user_chat_time):.0f}秒"
                    else:
                        user_chat_status = "，当前连接尚未收到真实买家消息"

                    logger.warning(
                        f"【{self.cookie_id}】检测到业务流疑似假在线: "
                        f"已连接{connected_for:.0f}秒，最近非心跳业务包距今{business_idle:.0f}秒，{sync_status}{user_chat_status}"
                    )
                    await self._force_websocket_reconnect("业务消息流长时间只有心跳，疑似假在线")
                    await self._maybe_notify_message_stream_stale(now, connected_for, business_idle)
                except asyncio.CancelledError:
                    logger.info(f"【{self.cookie_id}】业务流看门狗收到取消信号，准备退出")
                    raise
                except Exception as e:
                    logger.error(f"【{self.cookie_id}】业务流看门狗异常: {self._safe_str(e)}")
                    await self._interruptible_sleep(30)
        except asyncio.CancelledError:
            logger.info(f"【{self.cookie_id}】业务流看门狗已取消，正在退出...")
            raise
        finally:
            logger.info(f"【{self.cookie_id}】业务流看门狗已退出")

    def _reset_background_tasks(self):
        """直接重置后台任务引用，不等待取消（用于快速重连）
        
        注意：只重置心跳任务，因为只有心跳任务依赖WebSocket连接。
        其他任务（会话保活、业务流看门狗、清理、Cookie刷新）不依赖WebSocket，可以继续运行。
        """
        logger.info(f"【{self.cookie_id}】准备重置后台任务引用（仅重置依赖WebSocket的任务）...")
        
        # 只处理心跳任务（依赖WebSocket，需要重启）
        if self.heartbeat_task:
            status = "已完成" if self.heartbeat_task.done() else "运行中"
            logger.info(f"【{self.cookie_id}】发现心跳任务（状态: {status}），需要重置（因为依赖WebSocket连接）")
            # 尝试取消心跳任务（但不等待）
            if not self.heartbeat_task.done():
                try:
                    self.heartbeat_task.cancel()
                    logger.debug(f"【{self.cookie_id}】已发送取消信号给心跳任务（不等待响应）")
                except Exception as e:
                    logger.warning(f"【{self.cookie_id}】取消心跳任务失败: {e}")
            # 重置心跳任务引用
            self.heartbeat_task = None
            logger.info(f"【{self.cookie_id}】心跳任务引用已重置")
        else:
            logger.info(f"【{self.cookie_id}】没有心跳任务需要重置")
        
        # 检查其他任务的状态（这些任务不依赖WebSocket，不需要重启）
        other_tasks_status = []
        if self.token_refresh_task:
            status = "已完成" if self.token_refresh_task.done() else "运行中"
            other_tasks_status.append(f"Token刷新任务({status})")
        if self.cleanup_task:
            status = "已完成" if self.cleanup_task.done() else "运行中"
            other_tasks_status.append(f"清理任务({status})")
        if self.cookie_refresh_task:
            status = "已完成" if self.cookie_refresh_task.done() else "运行中"
            other_tasks_status.append(f"Cookie刷新任务({status})")
        if self.stream_watchdog_task:
            status = "已完成" if self.stream_watchdog_task.done() else "运行中"
            other_tasks_status.append(f"业务流看门狗({status})")
        
        if other_tasks_status:
            logger.info(f"【{self.cookie_id}】其他任务继续运行（不依赖WebSocket）: {', '.join(other_tasks_status)}")
        else:
            logger.info(f"【{self.cookie_id}】没有其他任务在运行")
        
        logger.info(f"【{self.cookie_id}】任务重置完成，可以立即创建新的心跳任务")

    async def _cancel_background_tasks(self):
        """取消并清理所有后台任务（保留此方法用于程序退出时的完整清理）"""
        try:
            tasks_to_cancel = []
            
            # 收集所有需要取消的任务（只收集未完成的任务）
            if self.heartbeat_task:
                if not self.heartbeat_task.done():
                    tasks_to_cancel.append(("心跳任务", self.heartbeat_task))
                else:
                    logger.debug(f"【{self.cookie_id}】心跳任务已完成，跳过")
                    
            if self.token_refresh_task:
                if not self.token_refresh_task.done():
                    tasks_to_cancel.append(("Token刷新任务", self.token_refresh_task))
                else:
                    logger.debug(f"【{self.cookie_id}】Token刷新任务已完成，跳过")
                    
            if self.cleanup_task:
                if not self.cleanup_task.done():
                    tasks_to_cancel.append(("清理任务", self.cleanup_task))
                else:
                    logger.debug(f"【{self.cookie_id}】清理任务已完成，跳过")
                    
            if self.cookie_refresh_task:
                if not self.cookie_refresh_task.done():
                    tasks_to_cancel.append(("Cookie刷新任务", self.cookie_refresh_task))
                else:
                    logger.debug(f"【{self.cookie_id}】Cookie刷新任务已完成，跳过")
            
            if self.stream_watchdog_task:
                if not self.stream_watchdog_task.done():
                    tasks_to_cancel.append(("业务流看门狗", self.stream_watchdog_task))
                else:
                    logger.debug(f"【{self.cookie_id}】业务流看门狗已完成，跳过")
            
            if not tasks_to_cancel:
                logger.info(f"【{self.cookie_id}】没有后台任务需要取消（所有任务已完成或不存在）")
                # 立即重置任务引用
                self.heartbeat_task = None
                self.token_refresh_task = None
                self.cleanup_task = None
                self.cookie_refresh_task = None
                self.stream_watchdog_task = None
                return
            
            logger.info(f"【{self.cookie_id}】开始取消 {len(tasks_to_cancel)} 个未完成的后台任务...")
            
            # 取消所有任务
            for task_name, task in tasks_to_cancel:
                try:
                    if task.done():
                        logger.info(f"【{self.cookie_id}】任务已完成，跳过取消: {task_name}")
                    else:
                        task.cancel()
                        logger.info(f"【{self.cookie_id}】已发送取消信号: {task_name}")
                except Exception as e:
                    logger.warning(f"【{self.cookie_id}】取消任务失败 {task_name}: {e}")
            
            # 等待所有任务完成取消，使用合理的超时时间
            # 现在任务中已经添加了 await asyncio.sleep(0) 来让出控制权，应该能够响应取消信号
            tasks = [task for _, task in tasks_to_cancel]
            logger.info(f"【{self.cookie_id}】等待 {len(tasks)} 个任务响应取消信号...")
            
            wait_timeout = 5.0  # 增加超时时间到5秒，给任务更多时间响应取消信号
            
            start_time = time.time()
            try:
                # 只等待未完成的任务
                pending_tasks_list = [task for task in tasks if not task.done()]
                
                # 记录每个任务的状态
                for task_name, task in tasks_to_cancel:
                    status = "已完成" if task.done() else "运行中"
                    logger.info(f"【{self.cookie_id}】任务状态: {task_name} - {status}")
                
                if not pending_tasks_list:
                    logger.info(f"【{self.cookie_id}】所有任务已完成，无需等待")
                else:
                    logger.info(f"【{self.cookie_id}】等待 {len(pending_tasks_list)} 个未完成任务响应（超时时间: {wait_timeout}秒）...")
                    try:
                        # 使用 wait 等待任务完成，设置超时
                        logger.debug(f"【{self.cookie_id}】开始调用 asyncio.wait()...")
                        done, pending = await asyncio.wait(
                            pending_tasks_list,
                            timeout=wait_timeout,
                            return_when=asyncio.ALL_COMPLETED
                        )
                        elapsed = time.time() - start_time
                        logger.info(f"【{self.cookie_id}】asyncio.wait() 返回，耗时 {elapsed:.3f}秒，已完成: {len(done)}，未完成: {len(pending)}")
                        
                        # 检查已完成的任务，并记录详细信息
                        for task_name, task in tasks_to_cancel:
                            if task in done:
                                try:
                                    task.result()
                                    logger.warning(f"【{self.cookie_id}】⚠️ 任务正常完成（非取消）: {task_name}")
                                except asyncio.CancelledError:
                                    logger.info(f"【{self.cookie_id}】✅ 任务已成功取消: {task_name}")
                                except Exception as e:
                                    logger.warning(f"【{self.cookie_id}】⚠️ 任务取消时出现异常 {task_name}: {e}")
                        
                        if pending:
                            # 找出未完成的任务名称和详细信息
                            pending_names = []
                            for task_name, task in tasks_to_cancel:
                                if task in pending:
                                    pending_names.append(task_name)
                                    # 记录未完成任务的状态
                                    if task.done():
                                        try:
                                            task.result()
                                            logger.warning(f"【{self.cookie_id}】任务在等待期间完成: {task_name}")
                                        except asyncio.CancelledError:
                                            logger.info(f"【{self.cookie_id}】任务在等待期间被取消: {task_name}")
                                        except Exception as e:
                                            logger.warning(f"【{self.cookie_id}】任务在等待期间异常 {task_name}: {e}")
                                    else:
                                        logger.warning(f"【{self.cookie_id}】任务仍未完成: {task_name} (done={task.done()})")
                            
                            logger.warning(f"【{self.cookie_id}】等待超时 ({elapsed:.3f}秒)，以下任务可能仍在运行: {', '.join(pending_names)}")
                            
                            # 强制取消所有未完成的任务（再次尝试）
                            for task_name, task in tasks_to_cancel:
                                if task in pending and not task.done():
                                    try:
                                        task.cancel()
                                        logger.warning(f"【{self.cookie_id}】强制取消任务: {task_name}")
                                    except Exception as e:
                                        logger.warning(f"【{self.cookie_id}】强制取消任务失败 {task_name}: {e}")
                            
                            # 再等待一小段时间，看是否有任务响应
                            if pending:
                                try:
                                    done2, pending2 = await asyncio.wait(pending, timeout=1.0, return_when=asyncio.ALL_COMPLETED)
                                    for task_name, task in tasks_to_cancel:
                                        if task in done2:
                                            try:
                                                task.result()
                                            except asyncio.CancelledError:
                                                logger.info(f"【{self.cookie_id}】任务在二次等待期间被取消: {task_name}")
                                            except Exception as e:
                                                logger.warning(f"【{self.cookie_id}】任务在二次等待期间异常 {task_name}: {e}")
                                except Exception as e:
                                    logger.warning(f"【{self.cookie_id}】二次等待任务时出错: {e}")
                            
                            logger.warning(f"【{self.cookie_id}】强制继续重连流程，未完成的任务将在后台继续运行（但已标记为取消）")
                        else:
                            logger.info(f"【{self.cookie_id}】所有后台任务已取消 (耗时 {elapsed:.3f}秒)")
                            
                    except Exception as e:
                        elapsed = time.time() - start_time
                        logger.warning(f"【{self.cookie_id}】等待任务时出错 (耗时 {elapsed:.3f}秒): {e}")
                        import traceback
                        logger.warning(f"【{self.cookie_id}】等待任务异常堆栈:\n{traceback.format_exc()}")
                        
            except Exception as e:
                elapsed = time.time() - start_time
                logger.error(f"【{self.cookie_id}】等待任务取消时出错 (耗时 {elapsed:.3f}秒): {e}")
                import traceback
                logger.error(f"【{self.cookie_id}】等待任务取消异常堆栈:\n{traceback.format_exc()}")
            
            logger.info(f"【{self.cookie_id}】任务取消流程完成，继续重连流程")
            
            # 最后检查一次所有任务的状态
            for task_name, task in tasks_to_cancel:
                if task and not task.done():
                    logger.warning(f"【{self.cookie_id}】⚠️ 任务取消流程完成后，任务仍未完成: {task_name} (done={task.done()})")
                elif task and task.done():
                    logger.debug(f"【{self.cookie_id}】✅ 任务已完成: {task_name}")
        
        finally:
            # 使用 finally 确保无论发生什么情况都会重置任务引用
            # 这样可以保证下次重连时所有任务都会被重新创建
            self.heartbeat_task = None
            self.token_refresh_task = None
            self.cleanup_task = None
            self.cookie_refresh_task = None
            self.stream_watchdog_task = None
            logger.info(f"【{self.cookie_id}】后台任务引用已全部重置")

    def _calculate_retry_delay(self, error_msg: str) -> int:
        """根据错误类型和失败次数计算重试延迟"""
        # WebSocket意外断开 - 短延迟
        if "no close frame received or sent" in error_msg:
            return min(3 * self.connection_failures, 15)
        
        # 网络连接问题 - 长延迟
        elif "Connection refused" in error_msg or "timeout" in error_msg.lower():
            return min(10 * self.connection_failures, 60)
        
        # 其他未知错误 - 中等延迟
        else:
            return min(5 * self.connection_failures, 30)

    def _cleanup_instance_caches(self):
        """清理实例级别的缓存，防止内存泄漏"""
        try:
            current_time = time.time()
            cleaned_total = 0
            
            # 清理过期的通知记录（保留30分钟内的，从1小时优化）
            max_notification_age = 1800  # 30分钟（从3600优化）
            expired_notifications = [
                key for key, last_time in self.last_notification_time.items()
                if current_time - last_time > max_notification_age
            ]
            for key in expired_notifications:
                del self.last_notification_time[key]
            if expired_notifications:
                cleaned_total += len(expired_notifications)
                logger.warning(f"【{self.cookie_id}】清理了 {len(expired_notifications)} 个过期通知记录")
            
            # 清理过期的发货记录（保留30分钟内的）
            max_delivery_age = 1800  # 30分钟
            expired_deliveries = [
                order_id for order_id, last_time in self.last_delivery_time.items()
                if current_time - last_time > max_delivery_age
            ]
            for order_id in expired_deliveries:
                del self.last_delivery_time[order_id]
            if expired_deliveries:
                cleaned_total += len(expired_deliveries)
                logger.warning(f"【{self.cookie_id}】清理了 {len(expired_deliveries)} 个过期发货记录")
            
            # 清理过期的订单确认记录（保留30分钟内的）
            max_confirm_age = 1800  # 30分钟
            expired_confirms = [
                order_id for order_id, last_time in self.confirmed_orders.items()
                if current_time - last_time > max_confirm_age
            ]
            for order_id in expired_confirms:
                del self.confirmed_orders[order_id]
            if expired_confirms:
                cleaned_total += len(expired_confirms)
                logger.warning(f"【{self.cookie_id}】清理了 {len(expired_confirms)} 个过期订单确认记录")
            
            # 只有实际清理了内容才记录总数日志
            if cleaned_total > 0:
                logger.info(f"【{self.cookie_id}】实例缓存清理完成，共清理 {cleaned_total} 条记录")
                logger.warning(f"【{self.cookie_id}】当前缓存数量 - 通知: {len(self.last_notification_time)}, 发货: {len(self.last_delivery_time)}, 确认: {len(self.confirmed_orders)}")
        
        except Exception as e:
            logger.error(f"【{self.cookie_id}】清理实例缓存时出错: {self._safe_str(e)}")
    
    async def _cleanup_playwright_cache(self):
        """清理Playwright浏览器临时文件和缓存（Docker环境专用）"""
        try:
            import shutil
            import glob
            
            # 定义需要清理的临时目录路径
            temp_paths = [
                '/tmp/playwright-*',  # Playwright临时会话
                '/tmp/chromium-*',    # Chromium临时文件
                '/ms-playwright/chromium-*/Default/Cache',  # 浏览器缓存
                '/ms-playwright/chromium-*/Default/Code Cache',  # 代码缓存
                '/ms-playwright/chromium-*/Default/GPUCache',  # GPU缓存
            ]
            
            total_cleaned = 0
            total_size_mb = 0
            
            for pattern in temp_paths:
                try:
                    matching_paths = glob.glob(pattern)
                    for path in matching_paths:
                        try:
                            if os.path.exists(path):
                                # 计算大小
                                if os.path.isdir(path):
                                    size = sum(
                                        os.path.getsize(os.path.join(dirpath, filename))
                                        for dirpath, _, filenames in os.walk(path)
                                        for filename in filenames
                                    )
                                    shutil.rmtree(path, ignore_errors=True)
                                else:
                                    size = os.path.getsize(path)
                                    os.remove(path)
                                
                                total_size_mb += size / (1024 * 1024)
                                total_cleaned += 1
                        except Exception as e:
                            logger.warning(f"清理路径 {path} 时出错: {e}")
                except Exception as e:
                    logger.warning(f"匹配路径 {pattern} 时出错: {e}")
            
            if total_cleaned > 0:
                logger.info(f"【{self.cookie_id}】Playwright缓存清理完成: 删除了 {total_cleaned} 个文件/目录，释放 {total_size_mb:.2f} MB")
            else:
                logger.warning(f"【{self.cookie_id}】Playwright缓存清理: 没有需要清理的临时文件")
                
        except Exception as e:
            logger.warning(f"【{self.cookie_id}】清理Playwright缓存时出错: {self._safe_str(e)}")

    async def _cleanup_old_logs(self, retention_days: int = 7):
        """清理过期的日志文件
        
        Args:
            retention_days: 保留的天数，默认7天
            
        Returns:
            清理的文件数量
        """
        try:
            import glob
            from datetime import datetime, timedelta
            
            logs_dir = "logs"
            if not os.path.exists(logs_dir):
                logger.warning(f"【{self.cookie_id}】日志目录不存在: {logs_dir}")
                return 0
            
            # 计算过期时间点
            cutoff_time = datetime.now() - timedelta(days=retention_days)
            
            # 查找所有日志文件（包括.log和.log.zip）
            log_patterns = [
                os.path.join(logs_dir, "xianyu_*.log"),
                os.path.join(logs_dir, "xianyu_*.log.zip"),
                os.path.join(logs_dir, "app_*.log"),
                os.path.join(logs_dir, "app_*.log.zip"),
            ]
            
            total_cleaned = 0
            total_size_mb = 0
            
            for pattern in log_patterns:
                log_files = glob.glob(pattern)
                for log_file in log_files:
                    try:
                        # 获取文件修改时间
                        file_mtime = datetime.fromtimestamp(os.path.getmtime(log_file))
                        
                        # 如果文件早于保留期限，则删除
                        if file_mtime < cutoff_time:
                            file_size = os.path.getsize(log_file)
                            os.remove(log_file)
                            total_size_mb += file_size / (1024 * 1024)
                            total_cleaned += 1
                            logger.debug(f"【{self.cookie_id}】删除过期日志文件: {log_file} (修改时间: {file_mtime})")
                    except Exception as e:
                        logger.warning(f"【{self.cookie_id}】删除日志文件失败 {log_file}: {self._safe_str(e)}")
            
            if total_cleaned > 0:
                logger.info(f"【{self.cookie_id}】日志清理完成: 删除了 {total_cleaned} 个日志文件，释放 {total_size_mb:.2f} MB (保留 {retention_days} 天内的日志)")
            else:
                logger.debug(f"【{self.cookie_id}】日志清理: 没有需要清理的过期日志文件 (保留 {retention_days} 天)")
            
            return total_cleaned
            
        except Exception as e:
            logger.error(f"【{self.cookie_id}】清理日志文件时出错: {self._safe_str(e)}")
            return 0

    def __init__(self, cookies_str=None, cookie_id: str = "default", user_id: int = None, *, register_instance: bool = True):
        """初始化闲鱼直播类"""
        logger.info(f"【{cookie_id}】开始初始化XianyuLive...")

        if not cookies_str:
            cookies_str = COOKIES_STR
        if not cookies_str:
            raise ValueError("未提供cookies，请在global_config.yml中配置COOKIES_STR或通过参数传入")

        logger.info(f"【{cookie_id}】解析cookies...")
        self.cookies = trans_cookies(cookies_str)
        logger.info(f"【{cookie_id}】cookies解析完成，包含字段: {list(self.cookies.keys())}")

        self.cookie_id = cookie_id  # 唯一账号标识
        self.cookies_str = cookies_str  # 保存原始cookie字符串
        self.user_id = user_id  # 保存用户ID，用于token刷新时保持正确的所有者关系
        self.register_instance = bool(register_instance)
        self.base_url = WEBSOCKET_URL

        if 'unb' not in self.cookies:
            raise ValueError(f"【{cookie_id}】Cookie中缺少必需的'unb'字段，当前字段: {list(self.cookies.keys())}")

        self.myid = self.cookies['unb']
        logger.info(f"【{cookie_id}】用户ID: {self.myid}")
        self.device_id = generate_device_id(self.myid)

        # 心跳相关配置
        self.heartbeat_interval = HEARTBEAT_INTERVAL
        self.heartbeat_timeout = HEARTBEAT_TIMEOUT
        self.last_heartbeat_time = 0
        self.last_heartbeat_response = 0
        self.last_sent_heartbeat_mid = None
        self.pending_heartbeat_mids = deque(maxlen=32)
        self.heartbeat_task = None
        self.ws = None
        self.last_non_heartbeat_message_time = 0
        self.last_sync_package_time = 0
        self.last_user_chat_time = 0
        self.last_stream_watchdog_reconnect_time = 0

        # Token刷新相关配置
        self.token_refresh_interval = TOKEN_REFRESH_INTERVAL
        self.token_retry_interval = TOKEN_RETRY_INTERVAL
        self.session_keepalive_interval = SESSION_KEEPALIVE_INTERVAL
        self.session_keepalive_retry_interval = SESSION_KEEPALIVE_RETRY_INTERVAL
        self.last_token_refresh_time = 0
        self.last_session_keepalive_time = 0
        self.current_token = None
        self.token_refresh_task = None
        self.last_token_refresh_status = None  # Token刷新状态追踪
        self.last_token_refresh_error_message = None  # Token刷新失败详情，供通知文案分流
        self.last_session_keepalive_status = None
        self.last_session_keepalive_error_message = None
        self.pending_slider_success_notice = None  # 滑块成功后的延迟成功通知，避免会话未恢复时误报
        self.connection_restart_flag = False  # 连接重启标志
        self.last_init_failure_reason = None
        self.last_init_failure_type = None
        self.init_auth_failures = 0
        self.stream_watchdog_task = None
        self.stream_watchdog_check_interval = max(self.heartbeat_interval, 15)
        self.stream_watchdog_grace_period = max(self.heartbeat_interval * 4, 120)
        self.message_stream_watchdog_timeout = max(self.session_keepalive_interval * 3, 1800)
        self.stream_watchdog_trigger_times = deque(maxlen=8)
        self.message_stream_notification_window = max(self.message_stream_watchdog_timeout * 2, 3600)
        self.message_stream_notification_cooldown = max(self.message_stream_watchdog_timeout, 1800)

        prewarmed_token_info = self.pop_auth_prewarmed_token(self.cookie_id)
        if prewarmed_token_info:
            self.current_token = prewarmed_token_info.get('token')
            self.last_token_refresh_time = prewarmed_token_info.get('timestamp', time.time())
            logger.info(
                f"【{cookie_id}】已复用认证预热token，来源: {prewarmed_token_info.get('source') or 'unknown'}"
            )

        prewarmed_token_info = self.pop_qr_prewarmed_token(self.cookie_id)
        if prewarmed_token_info and not self.current_token:
            self.current_token = prewarmed_token_info.get('token')
            self.last_token_refresh_time = prewarmed_token_info.get('timestamp', time.time())
            logger.info(f"【{cookie_id}】已复用扫码预热token，跳过首次token刷新")

        # 通知防重复机制
        self.last_notification_time = {}  # 记录每种通知类型的最后发送时间
        self.notification_cooldown = 300  # 5分钟内不重复发送相同类型的通知
        self.token_refresh_notification_cooldown = 18000  # Token刷新异常通知冷却时间：3小时
        self.notification_lock = asyncio.Lock()  # 通知防重复机制的异步锁
        self.pending_notification_keys = set()  # 记录发送中的通知，避免并发重复发送

        # 自动发货防重复机制
        self.last_delivery_time = {}  # 记录每个商品的最后发货时间
        self.delivery_cooldown = 600  # 10分钟内不重复发货

        # 自动确认发货防重复机制
        self.confirmed_orders = {}  # 记录已确认发货的订单，防止重复确认
        self.order_confirm_cooldown = 600  # 10分钟内不重复确认同一订单

        # 自动发货已发送订单记录
        self.delivery_sent_orders = set()  # 记录已发货的订单ID，防止重复发货

        self.session = None  # 用于API调用的aiohttp session

        # 代理配置 - 从数据库加载
        self.proxy_config = self._load_proxy_config()
        if self.proxy_config.get('proxy_type', 'none') != 'none':
            logger.info(f"【{cookie_id}】已加载代理配置: {self.proxy_config['proxy_type']}://{self.proxy_config['proxy_host']}:{self.proxy_config['proxy_port']}")

        # 启动定期清理过期暂停记录的任务
        self.cleanup_task = None

        # Cookie刷新定时任务
        self.cookie_refresh_task = None
        self.cookie_refresh_interval = 10800  # 3小时 = 10800秒
        self.last_cookie_refresh_time = 0
        self.cookie_refresh_lock = asyncio.Lock()  # 使用Lock防止重复执行Cookie刷新
        self.cookie_refresh_enabled = True  # 是否启用Cookie刷新功能

        # 扫码登录Cookie刷新标志
        self.last_qr_cookie_refresh_time = 0  # 记录上次扫码登录Cookie刷新时间
        self.qr_cookie_refresh_cooldown = 600  # 扫码登录Cookie刷新后的冷却时间：10分钟

        # 消息接收标识 - 用于控制Cookie刷新
        self.last_message_received_time = 0  # 记录上次收到消息的时间
        self.message_cookie_refresh_cooldown = 300  # 收到消息后5分钟内不执行Cookie刷新

        # 浏览器Cookie刷新成功标志
        self.browser_cookie_refreshed = False  # 标记_refresh_cookies_via_browser是否成功更新过数据库
        self.restarted_in_browser_refresh = False  # 刷新流程内部是否已触发重启（用于去重）


        # 滑块验证相关
        self.captcha_verification_count = 0  # 滑块验证次数计数器
        self.max_captcha_verification_count = 3  # 最大滑块验证次数，防止无限递归
        self.last_slider_success_at = 0.0
        self.last_slider_success_cookie_length = 0
        self.slider_success_reentry_window = 30
        self.post_slider_token_retry_delay = (1.5, 3.0)
        self.token_refresh_lock = asyncio.Lock()  # 防止多个入口并发刷新 token

        # WebSocket连接监控
        self.connection_state = ConnectionState.DISCONNECTED  # 连接状态
        self.connection_failures = 0  # 连续连接失败次数
        self.max_connection_failures = 5  # 最大连续失败次数
        self.last_successful_connection = 0  # 上次成功连接时间
        self.last_state_change_time = time.time()  # 上次状态变化时间

        # 后台任务追踪（用于清理未等待的任务）
        self.background_tasks = set()  # 追踪所有后台任务
        
        # 消息处理并发控制（防止内存泄漏）
        self.message_semaphore = asyncio.Semaphore(100)  # 最多100个并发消息处理任务
        self.active_message_tasks = 0  # 当前活跃的消息处理任务数
        
        # ============ 高性能消息队列系统 ============
        # 消息队列配置
        self.message_queue_enabled = True  # 是否启用消息队列系统
        self.message_queue_max_size = 1000  # 消息队列最大容量
        self.message_queue_workers = 5  # 消息处理工作协程数量
        self.message_expire_seconds = 60  # 消息过期时间（秒），超过此时间的消息将被丢弃
        
        # 消息优先级队列（使用优先级队列实现高优先级消息先处理）
        # 优先级: 0=最高（心跳/ACK）, 1=高（订单消息）, 2=中（聊天消息）, 3=低（其他）
        self.message_queue = asyncio.PriorityQueue(maxsize=self.message_queue_max_size)
        self.message_queue_counter = 0  # 用于保证FIFO顺序的计数器
        self.message_queue_lock = asyncio.Lock()
        
        # 工作协程管理
        self.message_workers = []  # 工作协程列表
        self.message_queue_running = False  # 队列系统运行状态
        
        # 队列监控统计
        self.queue_stats = {
            'received': 0,        # 收到的消息总数
            'processed': 0,       # 处理的消息数
            'dropped_full': 0,    # 因队列满而丢弃的消息数
            'dropped_expired': 0, # 因过期而丢弃的消息数
            'errors': 0,          # 处理错误数
            'last_stats_time': time.time(),  # 上次统计时间
        }

        # 亦凡卡劵账号充值确认流程状态管理
        self.yifan_account_waiting = {}  # 等待账号输入的订单: {chat_id: {buyer_id, rule, order_id, item_id, state, account, create_time}}
        self.yifan_account_lock = asyncio.Lock()  # 状态管理锁

        # 消息防抖管理器：用于处理用户连续发送消息的情况
        # {chat_id: {'task': asyncio.Task, 'last_message': dict, 'timer': float}}
        self.message_debounce_tasks = {}  # 存储每个chat_id的防抖任务
        self._message_debounce_delay = 3  # 防抖延迟默认值（秒），实际值通过property从数据库动态读取
        self.message_debounce_lock = asyncio.Lock()  # 防抖任务管理的锁
        
        # 消息去重机制：防止同一条消息被处理多次
        self.processed_message_ids = {}  # 存储已处理的消息ID和时间戳 {message_id: timestamp}
        self.processed_message_ids_lock = asyncio.Lock()  # 消息ID去重的锁
        self.processed_message_ids_max_size = 10000  # 最大保存10000个消息ID，防止内存泄漏
        self.message_expire_time = 3600  # 消息过期时间（秒），默认1小时后可以重复回复

        # 订单详情补抓任务：详情首次超时时，后台再补抓一次，避免整单丢失
        self.order_detail_retry_tasks = {}
        self.order_detail_force_refresh_marks = {}
        self.order_detail_force_refresh_cooldown = 5

        # 初始化订单状态处理器
        self._init_order_status_handler()

        # 只有长期运行实例才进入全局实例表，避免临时实例污染运行态诊断
        if self.register_instance:
            self._register_instance()

    @property
    def message_debounce_delay(self):
        """动态从数据库读取防抖延迟配置，修改后无需重启"""
        try:
            from db_manager import db_manager
            val = db_manager.get_system_setting('message_debounce_delay')
            return int(val) if val else self._message_debounce_delay
        except Exception:
            return self._message_debounce_delay

    def _init_order_status_handler(self):
        """初始化订单状态处理器"""
        try:
            # 直接导入订单状态处理器
            from order_status_handler import order_status_handler
            self.order_status_handler = order_status_handler
            logger.info(f"【{self.cookie_id}】订单状态处理器已启用")
        except Exception as e:
            logger.error(f"【{self.cookie_id}】初始化订单状态处理器失败: {self._safe_str(e)}")
            self.order_status_handler = None

    def _register_instance(self):
        """注册当前实例到类级别字典"""
        try:
            # 使用同步方式注册，避免在__init__中使用async
            XianyuLive._instances[self.cookie_id] = self
            logger.warning(f"【{self.cookie_id}】实例已注册到全局字典")
        except Exception as e:
            logger.error(f"【{self.cookie_id}】注册实例失败: {self._safe_str(e)}")

    def _unregister_instance(self):
        """从类级别字典中注销当前实例"""
        try:
            if self.cookie_id in XianyuLive._instances:
                del XianyuLive._instances[self.cookie_id]
                logger.warning(f"【{self.cookie_id}】实例已从全局字典中注销")
        except Exception as e:
            logger.error(f"【{self.cookie_id}】注销实例失败: {self._safe_str(e)}")

    @classmethod
    def get_instance(cls, cookie_id: str):
        """获取指定cookie_id的XianyuLive实例"""
        return cls._instances.get(cookie_id)

    @classmethod
    def get_all_instances(cls):
        """获取所有活跃的XianyuLive实例"""
        return dict(cls._instances)

    @classmethod
    def get_instance_count(cls):
        """获取当前活跃实例数量"""
        return len(cls._instances)

    @classmethod
    def is_manual_refresh_active(cls, cookie_id: str, allow_handoff_recovery: bool = False) -> bool:
        """检查指定账号是否处于手动刷新保护期。"""
        if not cookie_id:
            return False
        state = cls.get_manual_refresh_state(cookie_id)
        if not state:
            return False
        phase = state.get('phase') or 'manual_refresh'
        if allow_handoff_recovery and phase == 'handoff_recovery':
            return False
        return True

    @classmethod
    def begin_manual_refresh(cls, cookie_id: str, source: str = "manual_refresh") -> Dict[str, Any]:
        """标记账号进入手动刷新保护期，并暂停自动Cookie刷新"""
        if not cookie_id:
            return {"started": False, "already_active": False, "reason": "empty_cookie_id"}

        live_instance = cls.get_instance(cookie_id)
        previous_cookie_refresh_enabled = None
        if live_instance is not None:
            previous_cookie_refresh_enabled = live_instance.cookie_refresh_enabled

        cls._cleanup_manual_refresh_state()
        with cls._manual_refresh_lock:
            existing = cls._manual_refresh_state.get(cookie_id)
            if existing:
                existing["source"] = source
                existing["phase"] = 'manual_refresh'
                existing["updated_at"] = time.time()
                existing["expires_at"] = None
                return {
                    "started": False,
                    "already_active": True,
                    "previous_cookie_refresh_enabled": existing.get("previous_cookie_refresh_enabled")
                }

            cls._manual_refresh_state[cookie_id] = {
                "source": source,
                "phase": 'manual_refresh',
                "started_at": time.time(),
                "updated_at": time.time(),
                "expires_at": None,
                "previous_cookie_refresh_enabled": previous_cookie_refresh_enabled,
            }

        if live_instance is not None and previous_cookie_refresh_enabled is not None:
            live_instance.enable_cookie_refresh(False)
            logger.warning(f"【{cookie_id}】已进入手动刷新保护期，暂停自动Cookie刷新")
        else:
            logger.warning(f"【{cookie_id}】已进入手动刷新保护期，当前无运行中的账号实例")

        return {
            "started": True,
            "already_active": False,
            "previous_cookie_refresh_enabled": previous_cookie_refresh_enabled
        }

    @classmethod
    def end_manual_refresh(cls, cookie_id: str, source: str = "manual_refresh") -> bool:
        """结束手动刷新保护期，并按原状态恢复自动Cookie刷新"""
        if not cookie_id:
            return False

        cls._cleanup_manual_refresh_state()
        with cls._manual_refresh_lock:
            state = cls._manual_refresh_state.pop(cookie_id, None)

        if state is None:
            return False

        live_instance = cls.get_instance(cookie_id)
        previous_cookie_refresh_enabled = state.get("previous_cookie_refresh_enabled")
        if live_instance is not None and previous_cookie_refresh_enabled is not None:
            live_instance.enable_cookie_refresh(previous_cookie_refresh_enabled)
            if previous_cookie_refresh_enabled:
                # 手动刷新刚结束时，避免新实例立刻再触发一轮自动Cookie刷新。
                live_instance.last_cookie_refresh_time = time.time()
            logger.warning(
                f"【{cookie_id}】手动刷新保护期已结束，恢复自动Cookie刷新: {previous_cookie_refresh_enabled}"
            )
        else:
            logger.warning(f"【{cookie_id}】手动刷新保护期已结束，当前无运行中的账号实例可恢复")

        logger.info(f"【{cookie_id}】结束手动刷新保护期，来源: {source}")
        return True
    
    def _create_tracked_task(self, coro):
        """创建并追踪后台任务，确保异常不会被静默忽略"""
        task = asyncio.create_task(coro)
        self.background_tasks.add(task)
        task.add_done_callback(self.background_tasks.discard)
        return task

    def _sanitize_buyer_nick(self, candidate: Any, *, source: str = "unknown",
                             message_meta: Dict[str, Any] = None, log_prefix: str = "") -> Optional[str]:
        """过滤系统/营销文案，避免污染订单买家昵称。"""
        if candidate is None:
            return None

        text = str(candidate).strip()
        if not text or text in {"未知用户", "unknown", "unknown_user"}:
            return None

        invalid_exact_titles = {
            "订单",
            "全部",
            "交易消息",
            "等待你发货",
            "你人真不错，送你闲鱼小红花",
            "卖家人不错？送Ta闲鱼小红花",
            "快给ta一个评价吧～",
        }
        if text in invalid_exact_titles:
            logger.info(f"{log_prefix} 👤 忽略系统标题型买家昵称({source}): {text}")
            return None

        meta = message_meta if isinstance(message_meta, dict) else {}
        related_notice_texts = []
        for key in ("detailNotice", "reminderContent", "reminderNotice"):
            value = str(meta.get(key, "")).strip()
            if value:
                related_notice_texts.append(value)

        if text in related_notice_texts:
            logger.info(f"{log_prefix} 👤 忽略通知文案型买家昵称({source}): {text}")
            return None

        reminder_title = str(meta.get("reminderTitle", "")).strip()
        if source != "senderNick":
            invalid_keywords = (
                "小红花", "待付款", "待发货", "待刀成", "成功小刀", "闲鱼",
                "交易", "收货", "退款", "评价", "发货", "付款", "拍下",
                "确认", "关闭", "鼓励", "真不错", "全部", "订单",
            )
            if any(keyword in text for keyword in invalid_keywords):
                logger.info(f"{log_prefix} 👤 忽略系统关键词型买家昵称({source}): {text}")
                return None

            if reminder_title == text and len(text) >= 10 and any(ch in text for ch in "，,。！？?!：:～~"):
                logger.info(f"{log_prefix} 👤 忽略长句型买家昵称({source}): {text}")
                return None

        return text

    def _resolve_delivery_log_buyer_nick(self, buyer_nick: Any = None, *, order_id: str = None,
                                         buyer_id: str = None, log_prefix: str = "") -> Optional[str]:
        """为发货日志优先选择可信的买家昵称，避免写入系统卡片标题。"""
        from db_manager import db_manager

        normalized_order_id = str(order_id).strip() if order_id else None
        normalized_buyer_id = str(buyer_id).strip() if buyer_id else None

        try:
            if normalized_order_id:
                order_info = db_manager.get_order_by_id(normalized_order_id)
                if order_info:
                    order_cookie_id = str(order_info.get("cookie_id") or "").strip()
                    if not order_cookie_id or order_cookie_id == str(self.cookie_id).strip():
                        order_buyer_nick = self._sanitize_buyer_nick(
                            order_info.get("buyer_nick"),
                            source="delivery_log_order",
                            log_prefix=log_prefix,
                        )
                        if order_buyer_nick:
                            return order_buyer_nick

                    if not normalized_buyer_id:
                        normalized_buyer_id = str(order_info.get("buyer_id") or "").strip() or None

            if normalized_buyer_id:
                recent_order = db_manager.get_recent_order_by_buyer_id(
                    normalized_buyer_id,
                    cookie_id=self.cookie_id,
                    minutes=60,
                )
                if recent_order:
                    recent_buyer_nick = self._sanitize_buyer_nick(
                        recent_order.get("buyer_nick"),
                        source="delivery_log_recent_order",
                        log_prefix=log_prefix,
                    )
                    if recent_buyer_nick:
                        return recent_buyer_nick
        except Exception as resolve_error:
            logger.warning(f"{log_prefix} 发货日志买家昵称解析失败: {self._safe_str(resolve_error)}")

        return self._sanitize_buyer_nick(
            buyer_nick,
            source="delivery_log_raw",
            log_prefix=log_prefix,
        )

    def _lookup_delivery_order_by_sid(self, sid: str, *, minutes: int = 10,
                                      log_prefix: str = "") -> Dict[str, Any]:
        """根据 sid 查找简化发货对应订单，并区分是否已处理/已关闭。"""
        normalized_sid = str(sid or "").strip()
        if not normalized_sid:
            return {"match_type": "missing", "order": None}

        try:
            pending_orders = db_manager.find_recent_orders_by_match_context(
                sid=normalized_sid,
                cookie_id=self.cookie_id,
                statuses=[
                    "pending_ship",
                    "pending_delivery",
                    "partial_success",
                    "partial_pending_finalize",
                ],
                minutes=minutes,
                limit=5,
            )
        except Exception as lookup_error:
            logger.error(f"{log_prefix} sid兜底查单异常: {self._safe_str(lookup_error)}")
            return {"match_type": "error", "order": None}

        if pending_orders:
            order = pending_orders[0]
            logger.info(
                f"{log_prefix} sid兜底命中待发货订单: sid={normalized_sid}, "
                f"order_id={order.get('order_id')}, status={order.get('order_status') or 'unknown'}"
            )
            return {"match_type": "pending_ship", "order": order}

        try:
            recent_orders = db_manager.find_recent_orders_by_match_context(
                sid=normalized_sid,
                cookie_id=self.cookie_id,
                statuses=[
                    "processing",
                    "pending_payment",
                    "shipped",
                    "completed",
                    "cancelled",
                ],
                minutes=minutes,
                limit=5,
            )
        except Exception as lookup_error:
            logger.error(f"{log_prefix} sid兜底查单异常: {self._safe_str(lookup_error)}")
            return {"match_type": "error", "order": None}

        if not recent_orders:
            return {"match_type": "missing", "order": None}

        order = recent_orders[0]
        order_id = str(order.get("order_id") or "").strip()
        order_status = str(order.get("order_status") or "").strip()
        if order_status == "shipped":
            if self._has_delivery_progress_evidence(order_id):
                match_type = "already_processed"
            else:
                match_type = "suspicious_shipped"
                logger.warning(
                    f"{log_prefix} sid兜底命中可疑已发货订单，检测到无真实发货进度，继续允许纠偏: "
                    f"sid={normalized_sid}, order_id={order_id}, status={order_status}"
                )
        elif order_status == "completed":
            match_type = "already_processed"
        elif order_status == "cancelled":
            match_type = "cancelled"
        elif order_status in {"processing", "pending_payment"}:
            match_type = "not_ready"
        else:
            match_type = "other_status"

        logger.info(
            f"{log_prefix} sid兜底命中订单: sid={normalized_sid}, "
            f"order_id={order.get('order_id')}, status={order_status or 'unknown'}, match_type={match_type}"
        )
        return {"match_type": match_type, "order": order}

    async def _refresh_sid_lookup_if_needed(self, sid: str, sid_lookup: Dict[str, Any], *,
                                            item_id: str = None, buyer_id: str = None,
                                            minutes: int = 10, allow_bargain_ready: bool = False,
                                            log_prefix: str = "") -> Dict[str, Any]:
        """sid 命中未就绪订单时，强刷详情后再判定一次。"""
        recent_order = (sid_lookup or {}).get('order')
        match_type = (sid_lookup or {}).get('match_type', 'missing')

        if not recent_order or match_type not in {'not_ready', 'other_status', 'suspicious_shipped'}:
            return sid_lookup

        order_id = str(recent_order.get('order_id') or '').strip()
        if not order_id:
            return sid_lookup

        refresh_item_id = recent_order.get('item_id') or item_id
        refresh_buyer_id = recent_order.get('buyer_id') or buyer_id
        old_status = recent_order.get('order_status') or 'unknown'

        logger.info(
            f"{log_prefix} sid命中的订单状态未就绪，尝试强制刷新订单详情后重试: "
            f"order_id={order_id}, status={old_status}"
        )

        if not self._reserve_order_detail_force_refresh(
            order_id,
            reason='sid_not_ready',
            log_prefix=log_prefix,
        ):
            return sid_lookup

        try:
            await self.fetch_order_detail_info(
                order_id,
                refresh_item_id,
                refresh_buyer_id,
                sid=sid,
                force_refresh=True
            )
        except Exception as refresh_error:
            logger.warning(f"{log_prefix} sid未就绪订单强刷失败: {self._safe_str(refresh_error)}")
            return sid_lookup

        refreshed_lookup = self._lookup_delivery_order_by_sid(
            sid,
            minutes=minutes,
            log_prefix=log_prefix
        )
        refreshed_order = refreshed_lookup.get('order') or {}

        if (
            allow_bargain_ready and
            refreshed_lookup.get('match_type') == 'not_ready' and
            refreshed_order and
            str(refreshed_order.get('order_status') or '').strip() in {'processing', 'pending_payment'} and
            self._has_bargain_success_evidence(refreshed_order)
        ):
            refreshed_lookup = dict(refreshed_lookup)
            refreshed_lookup['match_type'] = 'bargain_ready'
            logger.info(
                f"{log_prefix} sid强刷后仍未进入待发货，但检测到小刀成功证据，"
                f"改用小刀兜底发货: order_id={refreshed_order.get('order_id') or order_id}, "
                f"status={refreshed_order.get('order_status') or 'unknown'}"
            )

        logger.info(
            f"{log_prefix} sid强刷后重新判定: order_id={refreshed_order.get('order_id') or order_id}, "
            f"status={refreshed_order.get('order_status') or 'unknown'}, "
            f"match_type={refreshed_lookup.get('match_type', 'missing')}"
        )
        return refreshed_lookup

    async def _ensure_item_owned_by_current_account(self, item_id: str, *,
                                                    log_prefix: str = "",
                                                    page_size: int = 50,
                                                    max_pages: int = 3) -> bool:
        """优先查本地缓存，未命中时刷新在售商品列表进行归属校验。"""
        if not item_id or item_id == "未知商品":
            return False

        existing_item = db_manager.get_item_info(self.cookie_id, item_id)
        if existing_item:
            return True

        logger.info(f"{log_prefix} 商品 {item_id} 未命中本地缓存，刷新在售商品列表后重试归属校验")
        try:
            for page_number in range(1, max_pages + 1):
                result = await self.get_item_list_info(page_number=page_number, page_size=page_size)
                if not result.get("success"):
                    logger.warning(f"{log_prefix} 刷新在售商品列表失败，停止归属校验回退: page={page_number}, result={result}")
                    break

                current_items = result.get("items", [])
                if any(str(item.get("id", "")).strip() == str(item_id).strip() for item in current_items):
                    logger.info(f"{log_prefix} 商品 {item_id} 在第 {page_number} 页在售商品列表中命中，归属校验通过")
                    return True

                if len(current_items) < page_size:
                    break
        except Exception as e:
            logger.error(f"{log_prefix} 刷新在售商品列表进行归属校验失败: {self._safe_str(e)}")

        return bool(db_manager.get_item_info(self.cookie_id, item_id))

    # 已知的无效 buyer_id 占位值
    _INVALID_BUYER_IDS = {"unknown_user", "unknown", "", "None", "null", "0", "-", "-1"}

    @classmethod
    def _normalize_buyer_id_value(cls, buyer_id) -> Optional[str]:
        if buyer_id is None:
            return None
        text = str(buyer_id).strip()
        if not text:
            return None
        if text.endswith('@goofish'):
            text = text.split('@')[0].strip()
        return text or None

    @staticmethod
    def _is_trustworthy_buyer_id(buyer_id) -> bool:
        """判断 buyer_id 是否可信，用于防串单校验。
        不可信的值（占位符等）不应参与一致性比对。"""
        normalized_buyer_id = XianyuLive._normalize_buyer_id_value(buyer_id)
        if not normalized_buyer_id:
            return False
        if normalized_buyer_id in XianyuLive._INVALID_BUYER_IDS:
            return False
        if normalized_buyer_id.isdigit() and len(normalized_buyer_id) <= 2:
            return False
        return True

    def _extract_query_value_from_url(self, url_text: Any, key: str) -> Optional[str]:
        text = str(url_text or '').strip()
        if not text:
            return None

        try:
            parsed = urlparse(text)
            query = parse_qs(parsed.query or '')
            value = query.get(key, [None])[0]
            return self._normalize_buyer_id_value(value)
        except Exception as e:
            logger.debug(f"【{self.cookie_id}】解析链接参数失败: key={key}, error={self._safe_str(e)}")
            return None

    def _extract_buyer_id_from_message_meta(self, message_meta: dict, *, meta_label: str,
                                            log_prefix: str = "") -> Tuple[Optional[str], Optional[str]]:
        if not isinstance(message_meta, dict):
            return None, None

        biz_tag_dict = self._load_json_dict(message_meta.get('bizTag', ''))
        candidates = [
            ('reminderUrl.peerUserId', self._extract_query_value_from_url(message_meta.get('reminderUrl'), 'peerUserId')),
            ('bizTag.senderId', self._normalize_buyer_id_value(biz_tag_dict.get('senderId') or biz_tag_dict.get('sender_id'))),
            (f'{meta_label}.senderUserId', self._normalize_buyer_id_value(message_meta.get('senderUserId'))),
        ]

        low_trust_candidates = []
        for source, candidate in candidates:
            if not candidate:
                continue
            if self._is_trustworthy_buyer_id(candidate):
                return candidate, source
            low_trust_candidates.append(f'{source}={candidate}')

        if low_trust_candidates:
            logger.info(
                f"{log_prefix} 👤 检测到低可信买家ID候选，已忽略: {', '.join(low_trust_candidates[:3])}"
            )
        return None, None

    def _select_buyer_identity_for_order_write(self, order_id: str, *, incoming_buyer_id: Any = None,
                                               incoming_buyer_nick: Any = None, existing_order: Dict[str, Any] = None,
                                               buyer_id_source: str = None, buyer_nick_source: str = 'unknown',
                                               log_prefix: str = '') -> Tuple[Optional[str], Optional[str], bool]:
        incoming_buyer_id = self._normalize_buyer_id_value(incoming_buyer_id)
        incoming_buyer_nick = self._sanitize_buyer_nick(
            incoming_buyer_nick,
            source=buyer_nick_source,
            log_prefix=log_prefix,
        )

        existing_buyer_id = self._normalize_buyer_id_value((existing_order or {}).get('buyer_id'))
        existing_buyer_nick = (existing_order or {}).get('buyer_nick')
        existing_buyer_is_trustworthy = self._is_trustworthy_buyer_id(existing_buyer_id)
        incoming_buyer_is_trustworthy = self._is_trustworthy_buyer_id(incoming_buyer_id)
        source_label = buyer_id_source or 'unknown'

        if incoming_buyer_id and incoming_buyer_id == self.myid:
            if existing_order:
                preserved_buyer_id = existing_buyer_id if existing_buyer_id and existing_buyer_id != self.myid else None
                if existing_buyer_nick:
                    incoming_buyer_nick = existing_buyer_nick
                logger.info(
                    f"{log_prefix} 订单 {order_id} 命中自己买家ID保护，继续刷新并保留已有买家信息: "
                    f"incoming_buyer_id={incoming_buyer_id}, preserved_buyer_id={preserved_buyer_id}"
                )
                return preserved_buyer_id, incoming_buyer_nick, False

            logger.info(
                f"{log_prefix} 跳过疑似买家订单 {order_id} 的首次写入，buyer_id={incoming_buyer_id} 等于自己的ID"
            )
            return None, incoming_buyer_nick, True

        if existing_buyer_is_trustworthy:
            if not incoming_buyer_id:
                return existing_buyer_id, incoming_buyer_nick or existing_buyer_nick, False

            if not incoming_buyer_is_trustworthy:
                logger.info(
                    f"{log_prefix} 忽略低可信buyer_id覆盖，保留已有买家信息: "
                    f"order_id={order_id}, incoming_buyer_id={incoming_buyer_id}, "
                    f"incoming_source={source_label}, preserved_buyer_id={existing_buyer_id}"
                )
                return existing_buyer_id, incoming_buyer_nick or existing_buyer_nick, False

            if incoming_buyer_id != existing_buyer_id:
                logger.warning(
                    f"{log_prefix} 检测到买家ID冲突，保留已有可信买家信息: "
                    f"order_id={order_id}, incoming_buyer_id={incoming_buyer_id}, "
                    f"incoming_source={source_label}, preserved_buyer_id={existing_buyer_id}"
                )
                return existing_buyer_id, incoming_buyer_nick or existing_buyer_nick, False

            return existing_buyer_id, incoming_buyer_nick or existing_buyer_nick, False

        if incoming_buyer_is_trustworthy:
            return incoming_buyer_id, incoming_buyer_nick or existing_buyer_nick, False

        if incoming_buyer_id:
            logger.info(
                f"{log_prefix} 检测到低可信buyer_id，暂不写入订单: "
                f"order_id={order_id}, incoming_buyer_id={incoming_buyer_id}, incoming_source={source_label}"
            )

        fallback_buyer_id = existing_buyer_id if existing_buyer_id and existing_buyer_id != self.myid else None
        return fallback_buyer_id, incoming_buyer_nick or existing_buyer_nick, False

    def _extract_order_message_context(self, message: dict, msg_id: str = None) -> Dict[str, Any]:
        """从订单相关消息中提取买家、会话和商品信息。"""
        buyer_id = None
        buyer_id_source = None
        buyer_nick = None
        sid = ""
        item_id = None
        log_prefix = f"【{self.cookie_id}】[{msg_id}]" if msg_id else f"【{self.cookie_id}】"

        try:
            message_1 = message.get("1")
            if isinstance(message_1, str):
                # message['1'] 是字符串，可能是 sid（如 "56226853668@goofish"）或消息ID（如 "4003914207496.PNM"）
                if '@' in message_1:
                    sid = message_1
                else:
                    # PNM 等非 sid 格式，真正的 sid 在 message['2']
                    sid = message.get("2", "") or ""
                buyer_id = None
                # 尝试从 message['4'] 提取 buyer_id（PNM 等格式的 senderUserId 在这里）
                message_4 = message.get("4")
                if isinstance(message_4, dict):
                    buyer_id, buyer_id_source = self._extract_buyer_id_from_message_meta(
                        message_4,
                        meta_label='message[4]',
                        log_prefix=log_prefix,
                    )
                    buyer_nick = self._sanitize_buyer_nick(
                        message_4.get("senderNick"),
                        source="senderNick(msg4)",
                        message_meta=message_4,
                        log_prefix=log_prefix
                    )
                    if not buyer_nick:
                        reminder_title = message_4.get("reminderTitle", "")
                        buyer_nick = self._sanitize_buyer_nick(
                            reminder_title,
                            source="reminderTitle(msg4)",
                            message_meta=message_4,
                            log_prefix=log_prefix
                        )
                        if buyer_nick:
                            logger.info(f"{log_prefix} 👤 从message[4].reminderTitle提取到买家昵称: {buyer_nick}")
                    if buyer_nick:
                        logger.info(f"{log_prefix} 👤 从message[4]提取到买家昵称: {buyer_nick}")
                logger.info(
                    f"{log_prefix} 📌 简化消息，sid: {sid}，buyer_id: {buyer_id}，"
                    f"buyer_id_source: {buyer_id_source or '-'}"
                )
            elif isinstance(message_1, dict):
                if "10" in message_1 and isinstance(message_1["10"], dict):
                    message_10 = message_1["10"]
                    buyer_id, buyer_id_source = self._extract_buyer_id_from_message_meta(
                        message_10,
                        meta_label='message[1][10]',
                        log_prefix=log_prefix,
                    )
                    buyer_nick = self._sanitize_buyer_nick(
                        message_10.get("senderNick"),
                        source="senderNick",
                        message_meta=message_10,
                        log_prefix=log_prefix
                    )
                    if not buyer_nick:
                        reminder_title = message_10.get("reminderTitle", "")
                        buyer_nick = self._sanitize_buyer_nick(
                            reminder_title,
                            source="reminderTitle",
                            message_meta=message_10,
                            log_prefix=log_prefix
                        )
                        if buyer_nick:
                            logger.info(f"{log_prefix} 👤 从reminderTitle提取到买家昵称: {buyer_nick}")
                    if buyer_nick:
                        logger.info(f"{log_prefix} 👤 提取到买家昵称: {buyer_nick}")
                sid = message_1.get("2", "")
                if sid:
                    logger.info(f"{log_prefix} 📌 提取到sid: {sid}")
        except Exception as context_e:
            logger.warning(f"{log_prefix} 提取订单上下文失败: {self._safe_str(context_e)}")

        try:
            if "1" in message and isinstance(message["1"], dict) and "10" in message["1"] and isinstance(message["1"]["10"], dict):
                url_info = message["1"]["10"].get("reminderUrl", "")
                if isinstance(url_info, str) and "itemId=" in url_info:
                    item_id = url_info.split("itemId=")[1].split("&")[0]

            # message['4'] 中也可能包含 reminderUrl（PNM 等格式）
            if not item_id and "4" in message and isinstance(message["4"], dict):
                url_info = message["4"].get("reminderUrl", "")
                if isinstance(url_info, str) and "itemId=" in url_info:
                    item_id = url_info.split("itemId=")[1].split("&")[0]

            if not item_id:
                item_id = self.extract_item_id_from_message(message)
        except Exception as item_e:
            logger.warning(f"{log_prefix} 提取商品ID失败: {self._safe_str(item_e)}")

        return {
            'buyer_id': buyer_id,
            'buyer_id_source': buyer_id_source,
            'buyer_nick': buyer_nick,
            'sid': sid,
            'item_id': item_id,
        }

    def _preload_basic_order_info(self, order_id: str, item_id: str = None, buyer_id: str = None,
                                  sid: str = None, buyer_nick: str = None,
                                  buyer_id_source: str = None) -> bool:
        """在详情抓取前先落基础订单，避免详情超时导致整单丢失。"""
        try:
            existing_order = db_manager.get_order_by_id(order_id)
            buyer_id_to_save, buyer_nick_to_save, should_skip_write = self._select_buyer_identity_for_order_write(
                order_id,
                incoming_buyer_id=buyer_id,
                incoming_buyer_nick=buyer_nick,
                existing_order=existing_order,
                buyer_id_source=buyer_id_source,
                buyer_nick_source="preload",
                log_prefix=f"【{self.cookie_id}】",
            )
            if should_skip_write:
                return False

            success = db_manager.insert_or_update_order(
                order_id=order_id,
                item_id=item_id,
                buyer_id=buyer_id_to_save,
                buyer_nick=buyer_nick_to_save,
                sid=sid,
                cookie_id=self.cookie_id,
                order_status='processing' if not existing_order else None
            )
            if success:
                action = "更新基础订单信息" if existing_order else "基础订单已预入库"
                logger.info(
                    f"【{self.cookie_id}】{action}: order_id={order_id}, item_id={item_id}, "
                    f"buyer_id={buyer_id_to_save}, sid={sid or '-'}"
                )
            else:
                logger.warning(f"【{self.cookie_id}】基础订单预入库失败: {order_id}")
            return success
        except Exception as e:
            logger.error(f"【{self.cookie_id}】基础订单预入库异常: {self._safe_str(e)}")
            return False

    async def _retry_order_detail_after_delay(self, order_id: str, item_id: str = None, buyer_id: str = None,
                                              sid: str = None, buyer_nick: str = None, delay_seconds: int = 30,
                                              buyer_id_source: str = None):
        """订单详情首次抓取失败后，后台延迟补抓一次。"""
        current_task = asyncio.current_task()
        try:
            await asyncio.sleep(delay_seconds)
            logger.info(f"【{self.cookie_id}】开始延迟补抓订单详情: order_id={order_id}, delay={delay_seconds}s")
            result = await self.fetch_order_detail_info(
                order_id,
                item_id,
                buyer_id,
                sid=sid,
                buyer_nick=buyer_nick,
                buyer_id_source=buyer_id_source,
                force_refresh=True
            )
            if result:
                logger.info(f"【{self.cookie_id}】订单详情延迟补抓成功: {order_id}")
            else:
                logger.warning(f"【{self.cookie_id}】订单详情延迟补抓仍失败，保留基础订单: {order_id}")
        except asyncio.CancelledError:
            logger.info(f"【{self.cookie_id}】订单详情延迟补抓任务已取消: {order_id}")
            raise
        except Exception as e:
            logger.error(f"【{self.cookie_id}】订单详情延迟补抓异常: {order_id} - {self._safe_str(e)}")
        finally:
            existing_task = self.order_detail_retry_tasks.get(order_id)
            if existing_task is current_task:
                self.order_detail_retry_tasks.pop(order_id, None)

    def _schedule_order_detail_retry(self, order_id: str, item_id: str = None, buyer_id: str = None,
                                     sid: str = None, buyer_nick: str = None, delay_seconds: int = 30,
                                     buyer_id_source: str = None):
        """调度订单详情补抓任务，避免同一订单重复创建补抓。"""
        existing_task = self.order_detail_retry_tasks.get(order_id)
        if existing_task and not existing_task.done():
            logger.info(f"【{self.cookie_id}】订单详情补抓任务已存在，跳过重复调度: {order_id}")
            return

        task = self._create_tracked_task(
            self._retry_order_detail_after_delay(
                order_id,
                item_id=item_id,
                buyer_id=buyer_id,
                sid=sid,
                buyer_nick=buyer_nick,
                delay_seconds=delay_seconds,
                buyer_id_source=buyer_id_source,
            )
        )
        self.order_detail_retry_tasks[order_id] = task
        logger.info(f"【{self.cookie_id}】已调度订单详情补抓任务: order_id={order_id}, delay={delay_seconds}s")

    # ============ 高性能消息队列系统方法 ============
    
    def _get_message_priority(self, message_data: dict) -> int:
        """
        根据消息类型确定优先级
        
        优先级定义:
        - 0: 最高优先级（心跳响应、ACK确认）- 立即处理
        - 1: 高优先级（订单相关消息）- 优先处理
        - 2: 中优先级（普通聊天消息）- 正常处理
        - 3: 低优先级（系统通知、其他）- 延后处理
        
        Returns:
            int: 优先级值，越小优先级越高
        """
        try:
            # 检查是否是心跳响应
            if isinstance(message_data, dict):
                # 心跳响应
                if message_data.get("code") == 200 and "body" not in message_data:
                    return 0
                
                # 检查消息体
                body = message_data.get("body", {})
                
                # 同步包消息需要进一步分析
                if "syncPushPackage" in body:
                    try:
                        sync_data = body["syncPushPackage"].get("data", [])
                        if sync_data and isinstance(sync_data, list) and len(sync_data) > 0:
                            first_data = sync_data[0]
                            # 检查是否包含订单相关关键词
                            data_str = str(first_data).lower()
                            if any(kw in data_str for kw in ['orderid', 'order_id', 'bizorderid', 'paysucc', 'paid']):
                                return 1  # 订单消息 - 高优先级
                            if 'message' in data_str or 'chat' in data_str:
                                return 2  # 聊天消息 - 中优先级
                    except Exception:
                        pass
                
                # ACK确认消息
                if message_data.get("code") == 200:
                    return 0
            
            return 3  # 默认低优先级
        except Exception as e:
            logger.debug(f"【{self.cookie_id}】解析消息优先级失败: {e}")
            return 3
    
    async def _enqueue_message(self, message_data: dict, websocket, msg_id: str = "unknown") -> bool:
        """
        将消息放入优先级队列
        
        Args:
            message_data: 消息数据
            websocket: WebSocket连接
            msg_id: 消息ID
            
        Returns:
            bool: 是否成功入队
        """
        try:
            # 获取消息优先级
            priority = self._get_message_priority(message_data)
            
            # 创建消息包装对象
            async with self.message_queue_lock:
                self.message_queue_counter += 1
                counter = self.message_queue_counter
            
            message_item = {
                'data': message_data,
                'websocket': websocket,
                'msg_id': msg_id,
                'enqueue_time': time.time(),
                'priority': priority,
            }
            
            # 尝试非阻塞入队
            try:
                self.message_queue.put_nowait((priority, counter, message_item))
                self.queue_stats['received'] += 1
                
                # 高优先级消息日志
                if priority <= 1:
                    logger.info(f"【{self.cookie_id}】📥 高优先级消息入队 [P{priority}][ID:{msg_id}] 队列大小: {self.message_queue.qsize()}")
                else:
                    logger.debug(f"【{self.cookie_id}】📥 消息入队 [P{priority}][ID:{msg_id}] 队列大小: {self.message_queue.qsize()}")
                
                return True
            except asyncio.QueueFull:
                # 队列满时，尝试丢弃最低优先级的旧消息
                self.queue_stats['dropped_full'] += 1
                logger.warning(f"【{self.cookie_id}】⚠️ 消息队列已满({self.message_queue_max_size})，消息[ID:{msg_id}]被丢弃")
                return False
                
        except Exception as e:
            logger.error(f"【{self.cookie_id}】消息入队失败: {self._safe_str(e)}")
            return False
    
    async def _message_worker(self, worker_id: int):
        """
        消息处理工作协程
        
        从队列中取出消息并处理，支持并发处理多个消息
        
        Args:
            worker_id: 工作协程ID
        """
        logger.info(f"【{self.cookie_id}】🔧 消息处理工作协程 #{worker_id} 启动")
        
        while self.message_queue_running:
            try:
                # 设置超时获取，避免无限等待
                try:
                    priority, counter, message_item = await asyncio.wait_for(
                        self.message_queue.get(), 
                        timeout=5.0
                    )
                except asyncio.TimeoutError:
                    # 超时没有消息，继续循环
                    continue
                
                # 检查消息是否过期
                enqueue_time = message_item['enqueue_time']
                age = time.time() - enqueue_time
                if age > self.message_expire_seconds:
                    self.queue_stats['dropped_expired'] += 1
                    logger.warning(f"【{self.cookie_id}】⏰ 工作协程#{worker_id} 丢弃过期消息 [ID:{message_item['msg_id']}] 已等待{age:.1f}秒")
                    self.message_queue.task_done()
                    continue
                
                # 处理消息
                msg_id = message_item['msg_id']
                try:
                    logger.debug(f"【{self.cookie_id}】🔄 工作协程#{worker_id} 开始处理消息 [P{priority}][ID:{msg_id}] 等待{age:.2f}秒")
                    
                    # 使用信号量控制并发
                    async with self.message_semaphore:
                        self.active_message_tasks += 1
                        try:
                            await self.handle_message(
                                message_item['data'],
                                message_item['websocket'],
                                msg_id
                            )
                            self.queue_stats['processed'] += 1
                        finally:
                            self.active_message_tasks -= 1
                    
                    logger.debug(f"【{self.cookie_id}】✅ 工作协程#{worker_id} 完成消息处理 [ID:{msg_id}]")
                    
                except Exception as e:
                    self.queue_stats['errors'] += 1
                    logger.error(f"【{self.cookie_id}】❌ 工作协程#{worker_id} 处理消息失败 [ID:{msg_id}]: {self._safe_str(e)}")
                finally:
                    self.message_queue.task_done()
                    
            except asyncio.CancelledError:
                logger.info(f"【{self.cookie_id}】🛑 消息处理工作协程 #{worker_id} 被取消")
                break
            except Exception as e:
                logger.error(f"【{self.cookie_id}】工作协程#{worker_id} 异常: {self._safe_str(e)}")
                await asyncio.sleep(1)  # 出错后短暂休息
        
        logger.info(f"【{self.cookie_id}】🔧 消息处理工作协程 #{worker_id} 已停止")
    
    async def _start_message_queue_workers(self):
        """启动消息队列工作协程"""
        if not self.message_queue_enabled:
            logger.info(f"【{self.cookie_id}】消息队列系统已禁用，使用传统处理模式")
            return
        
        self.message_queue_running = True
        self.message_workers = []
        
        # 创建多个工作协程
        for i in range(self.message_queue_workers):
            worker_task = self._create_tracked_task(self._message_worker(i))
            self.message_workers.append(worker_task)
        
        # 启动队列监控任务
        self._create_tracked_task(self._queue_stats_monitor())
        
        logger.info(f"【{self.cookie_id}】🚀 消息队列系统已启动，{self.message_queue_workers}个工作协程")
    
    async def _stop_message_queue_workers(self):
        """停止消息队列工作协程"""
        self.message_queue_running = False
        
        # 取消所有工作协程
        for worker_task in self.message_workers:
            if not worker_task.done():
                worker_task.cancel()
        
        # 等待所有工作协程结束
        if self.message_workers:
            await asyncio.gather(*self.message_workers, return_exceptions=True)
        
        self.message_workers = []
        logger.info(f"【{self.cookie_id}】🛑 消息队列系统已停止")
    
    async def _queue_stats_monitor(self):
        """定期输出队列统计信息"""
        while self.message_queue_running:
            try:
                await asyncio.sleep(60)  # 每60秒输出一次统计
                
                if not self.message_queue_running:
                    break
                
                # 计算统计
                stats = self.queue_stats
                elapsed = time.time() - stats['last_stats_time']
                
                if stats['received'] > 0:
                    process_rate = stats['processed'] / elapsed if elapsed > 0 else 0
                    drop_rate = (stats['dropped_full'] + stats['dropped_expired']) / stats['received'] * 100
                    
                    logger.info(
                        f"【{self.cookie_id}】📊 消息队列统计 - "
                        f"队列大小: {self.message_queue.qsize()}/{self.message_queue_max_size} | "
                        f"收到: {stats['received']} | "
                        f"处理: {stats['processed']} | "
                        f"丢弃(满): {stats['dropped_full']} | "
                        f"丢弃(过期): {stats['dropped_expired']} | "
                        f"错误: {stats['errors']} | "
                        f"处理速率: {process_rate:.1f}/s | "
                        f"丢弃率: {drop_rate:.1f}%"
                    )
                    
                    # 如果丢弃率过高，发出警告
                    if drop_rate > 10:
                        logger.warning(f"【{self.cookie_id}】⚠️ 消息丢弃率过高({drop_rate:.1f}%)，建议增加工作协程数量或检查消息处理效率")
                
                # 重置统计
                stats['last_stats_time'] = time.time()
                
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"【{self.cookie_id}】队列监控异常: {self._safe_str(e)}")

    def is_auto_confirm_enabled(self) -> bool:
        """检查当前账号是否启用自动确认发货"""
        try:
            from db_manager import db_manager
            return db_manager.get_auto_confirm(self.cookie_id)
        except Exception as e:
            logger.error(f"【{self.cookie_id}】获取自动确认发货设置失败: {self._safe_str(e)}")
            return True  # 出错时默认启用

    def is_auto_comment_enabled(self) -> bool:
        """检查当前账号是否启用自动好评"""
        try:
            from db_manager import db_manager
            return db_manager.get_auto_comment(self.cookie_id)
        except Exception as e:
            logger.error(f"【{self.cookie_id}】获取自动好评设置失败: {self._safe_str(e)}")
            return False  # 出错时默认禁用

    async def handle_auto_comment(self, message: dict, msg_time: str, msg_id: str = ""):
        """处理自动好评"""
        try:
            # 检查是否启用自动好评
            if not self.is_auto_comment_enabled():
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 未启用自动好评，跳过')
                return False
            
            # 从消息中提取订单ID
            order_id = self._extract_order_id_for_comment(message)
            if not order_id:
                logger.warning(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 无法从评价消息中提取订单ID，跳过自动好评')
                return False
            
            logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 检测到评价提醒，订单ID: {order_id}')
            
            # 获取激活的好评模板
            from db_manager import db_manager
            template = db_manager.get_active_comment_template(self.cookie_id)
            if not template:
                logger.warning(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 未设置激活的好评模板，跳过自动好评')
                return False
            
            comment_content = template.get('content', '')
            if not comment_content:
                logger.warning(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 好评模板内容为空，跳过自动好评')
                return False
            
            logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 使用模板"{template.get("name", "")}"进行好评: {comment_content[:50]}...')
            
            # 调用好评接口
            result = await self._call_comment_api(order_id, comment_content)
            
            if result.get('success'):
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ✅ 订单 {order_id} 自动好评成功')
                return True
            else:
                logger.warning(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ❌ 订单 {order_id} 自动好评失败: {result.get("message", "未知错误")}')
                return False
                
        except Exception as e:
            logger.error(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 自动好评异常: {self._safe_str(e)}')
            return False

    def _extract_order_id_for_comment(self, message: dict) -> str:
        """从评价提醒消息中提取订单ID"""
        try:
            order_id = self._extract_order_id(message)
            if order_id:
                logger.info(f'【{self.cookie_id}】评价提醒消息提取到订单ID: {order_id}')
            return order_id
            
        except Exception as e:
            logger.error(f"【{self.cookie_id}】提取评价订单ID失败: {self._safe_str(e)}")
            return None

    async def _call_comment_api(self, order_id: str, comment: str) -> dict:
        """调用好评接口"""
        import aiohttp
        
        try:
            # 好评接口地址
            comment_api_url = "http://119.29.64.68:8081/comment"
            
            # 获取当前账号的cookie
            cookie_str = self.cookies_str
            
            payload = {
                "cookie_str": cookie_str,
                "order_id": order_id,
                "comment": comment
            }
            
            headers = {
                "accept": "application/json",
                "Content-Type": "application/json"
            }
            
            async with aiohttp.ClientSession() as session:
                async with session.post(comment_api_url, json=payload, headers=headers, timeout=30) as response:
                    if response.status == 200:
                        result = await response.json()
                        return {
                            "success": result.get("status") == "success",
                            "message": result.get("message", "好评成功")
                        }
                    else:
                        error_text = await response.text()
                        logger.error(f"【{self.cookie_id}】好评接口返回错误: status={response.status}, body={error_text}")
                        return {
                            "success": False,
                            "message": f"接口返回错误: {response.status}"
                        }
                        
        except asyncio.TimeoutError:
            logger.error(f"【{self.cookie_id}】好评接口请求超时")
            return {
                "success": False,
                "message": "请求超时"
            }
        except Exception as e:
            logger.error(f"【{self.cookie_id}】调用好评接口异常: {self._safe_str(e)}")
            return {
                "success": False,
                "message": str(e)
            }

    def can_auto_delivery(self, order_id: str) -> bool:
        """检查是否可以进行自动发货（防重复发货）- 基于订单ID"""
        if not order_id:
            # 如果没有订单ID，则不进行冷却检查，允许发货
            return True

        current_time = time.time()
        last_delivery = self.last_delivery_time.get(order_id, 0)

        if current_time - last_delivery < self.delivery_cooldown:
            logger.info(f"【{self.cookie_id}】订单 {order_id} 在冷却期内，跳过自动发货")
            return False

        return True

    def mark_delivery_sent(self, order_id: str, context: str = "自动发货完成"):
        """标记订单已发货"""
        self.delivery_sent_orders.add(order_id)
        self.last_delivery_time[order_id] = time.time()
        logger.info(f"【{self.cookie_id}】订单 {order_id} 已标记为发货")
        
        # 更新订单状态为已发货
        logger.info(f"【{self.cookie_id}】检查自动发货订单状态处理器: handler_exists={self.order_status_handler is not None}")
        if self.order_status_handler:
            logger.info(f"【{self.cookie_id}】准备调用订单状态处理器.handle_auto_delivery_order_status: {order_id}")
            try:
                success = self.order_status_handler.handle_auto_delivery_order_status(
                    order_id=order_id,
                    cookie_id=self.cookie_id,
                    context=context
                )
                logger.info(f"【{self.cookie_id}】订单状态处理器.handle_auto_delivery_order_status返回结果: {success}")
                if success:
                    logger.info(f"【{self.cookie_id}】订单 {order_id} 状态已更新为已发货")
                else:
                    logger.warning(f"【{self.cookie_id}】订单 {order_id} 状态更新为已发货失败")
            except Exception as e:
                logger.error(f"【{self.cookie_id}】订单状态更新失败: {self._safe_str(e)}")
                import traceback
                logger.error(f"【{self.cookie_id}】详细错误信息: {traceback.format_exc()}")
        else:
            logger.warning(f"【{self.cookie_id}】订单状态处理器为None，跳过自动发货状态更新: {order_id}")

    def _activate_delivery_lock(self, lock_key: str, delay_minutes: int = 10):
        """在发货成功后激活订单延迟锁，避免重复发货。"""
        if not lock_key:
            return

        existing_lock = self._lock_hold_info.get(lock_key)
        if existing_lock and existing_lock.get('locked'):
            return

        self._lock_hold_info[lock_key] = {
            'locked': True,
            'lock_time': time.time(),
            'release_time': None,
            'task': None
        }
        delay_task = asyncio.create_task(self._delayed_lock_release(lock_key, delay_minutes=delay_minutes))
        self._lock_hold_info[lock_key]['task'] = delay_task

    def _record_delivery_log(self, order_id: str = None, item_id: str = None, buyer_id: str = None,
                             buyer_nick: str = None, status: str = 'failed', reason: str = None,
                             channel: str = 'auto', rule_meta: dict = None):
        """记录真实发货事件日志（成功/失败）。"""
        try:
            from db_manager import db_manager
            meta = rule_meta or {}
            log_prefix = f"【{self.cookie_id}】"
            resolved_buyer_nick = self._resolve_delivery_log_buyer_nick(
                buyer_nick,
                order_id=order_id,
                buyer_id=buyer_id,
                log_prefix=log_prefix,
            )
            normalized_status = str(status or 'failed').strip().lower()
            if normalized_status not in {'success', 'failed', 'skipped'}:
                normalized_status = 'failed'
            db_manager.create_delivery_log(
                user_id=self.user_id,
                cookie_id=self.cookie_id,
                order_id=order_id,
                item_id=item_id,
                buyer_id=buyer_id,
                buyer_nick=resolved_buyer_nick,
                rule_id=meta.get('rule_id'),
                rule_keyword=meta.get('rule_keyword'),
                card_type=meta.get('card_type'),
                match_mode=meta.get('match_mode'),
                channel=channel or 'auto',
                status=normalized_status,
                reason=self._format_delivery_log_reason(reason, meta)
            )
        except Exception as log_e:
            logger.error(f"【{self.cookie_id}】记录发货日志失败: {self._safe_str(log_e)}")

    def _format_delivery_log_reason(self, reason: str = None, rule_meta: dict = None) -> str:
        """将规格模式上下文拼接到发货日志原因中，便于后续排查。"""
        meta = rule_meta or {}
        context_parts = []

        order_spec_mode = meta.get('order_spec_mode')
        rule_spec_mode = meta.get('rule_spec_mode')
        item_config_mode = meta.get('item_config_mode')

        if order_spec_mode:
            context_parts.append(f"order_spec_mode={order_spec_mode}")
        if rule_spec_mode:
            context_parts.append(f"rule_spec_mode={rule_spec_mode}")
        if item_config_mode:
            context_parts.append(f"item_config_mode={item_config_mode}")

        reason_text = (reason or '').strip()
        if not context_parts:
            return reason_text

        if any(part.split('=')[0] + '=' in reason_text for part in context_parts):
            return reason_text

        if not reason_text:
            reason_text = '未提供发货日志原因'

        return f"{reason_text} [{', '.join(context_parts)}]"

    async def _finalize_delivery_after_send(self, delivery_meta: dict = None, order_id: str = None,
                                            item_id: str = None, skip_confirm: bool = False):
        """在消息发送成功后提交发货副作用：消费卡密、更新计数、确认发货。"""
        meta = delivery_meta or {}

        if not meta.get('success'):
            return {
                'success': False,
                'error': '发货元数据无效，无法提交副作用'
            }

        from db_manager import db_manager

        consume_required = bool(meta.get('data_card_pending_consume'))
        rule_id = meta.get('rule_id')
        card_id = meta.get('card_id')
        card_type = meta.get('card_type')
        expected_line = meta.get('data_line')
        reservation_id = meta.get('data_reservation_id')
        reservation_already_finalized = False

        if consume_required:
            if reservation_id:
                finalize_state = db_manager.finalize_batch_data_reservation(reservation_id)
                if not finalize_state.get('success'):
                    return {
                        'success': False,
                        'error': '批量数据预占完成失败，已中止后续确认发货'
                    }
                reservation_already_finalized = bool(finalize_state.get('already_finalized'))
            elif not card_id or card_type != 'data':
                return {
                    'success': False,
                    'error': '批量数据卡券元数据不完整，无法提交消费'
                }
            else:
                consumed = db_manager.consume_specific_batch_data(card_id, expected_line)
                if not consumed:
                    return {
                        'success': False,
                        'error': '批量数据消费失败，已中止后续确认发货'
                    }

        if rule_id and not consume_required:
            db_manager.increment_delivery_times(rule_id)

        if order_id and not skip_confirm:
            if not self.is_auto_confirm_enabled():
                logger.info(f"自动确认发货已关闭，跳过订单 {order_id}")
            else:
                current_time = time.time()
                should_confirm = True

                if order_id in self.confirmed_orders:
                    last_confirm_time = self.confirmed_orders[order_id]
                    if current_time - last_confirm_time < self.order_confirm_cooldown:
                        logger.info(f"订单 {order_id} 已在 {self.order_confirm_cooldown} 秒内确认过，跳过重复确认")
                        should_confirm = False

                if should_confirm:
                    logger.info(f"开始自动确认发货: 订单ID={order_id}, 商品ID={item_id}")
                    confirm_result = await self.auto_confirm(order_id, item_id)
                    if confirm_result.get('success'):
                        self.confirmed_orders[order_id] = current_time
                        logger.info(f"🎉 自动确认发货成功！订单ID: {order_id}")
                    else:
                        return {
                            'success': False,
                            'error': f"自动确认发货失败: {confirm_result.get('error', '未知错误')}"
                        }

        if rule_id and consume_required and not reservation_already_finalized:
            db_manager.increment_delivery_times(rule_id)

        return {
            'success': True
        }

    def _mark_data_reservation_sent_if_needed(self, delivery_meta: dict = None) -> bool:
        meta = delivery_meta or {}
        reservation_id = meta.get('data_reservation_id')
        if not reservation_id:
            return True

        from db_manager import db_manager
        return db_manager.mark_batch_data_reservation_sent(reservation_id)

    def _release_data_reservation_if_needed(self, delivery_meta: dict = None, error: str = None) -> bool:
        meta = delivery_meta or {}
        reservation_id = meta.get('data_reservation_id')
        if not reservation_id:
            return True

        from db_manager import db_manager
        return db_manager.release_batch_data_reservation(reservation_id, error=error)

    def _get_pending_delivery_finalization_meta(self, order_id: str, delivery_unit_index: int = 1):
        if not order_id:
            return None

        from db_manager import db_manager
        state = db_manager.get_delivery_finalization_state(order_id, delivery_unit_index)
        if not state or state.get('status') != 'sent':
            return None

        delivery_meta = state.get('delivery_meta') or {}
        delivery_meta.setdefault('success', True)
        delivery_meta.setdefault('delivery_unit_index', delivery_unit_index)
        return delivery_meta

    def _persist_delivery_finalization_state(self, order_id: str, item_id: str, buyer_id: str,
                                             delivery_meta: dict = None, channel: str = 'auto',
                                             status: str = 'sent', last_error: str = None) -> bool:
        if not order_id:
            return False

        from db_manager import db_manager
        meta = delivery_meta or {}
        unit_index = int(meta.get('delivery_unit_index') or 1)
        return db_manager.upsert_delivery_finalization_state(
            order_id=order_id,
            unit_index=unit_index,
            cookie_id=self.cookie_id,
            item_id=item_id,
            buyer_id=buyer_id,
            channel=channel,
            status=status,
            delivery_meta=meta,
            last_error=last_error,
        )

    def _summarize_delivery_progress(self, order_id: str, expected_quantity: int = 1):
        if not order_id:
            return {
                'order_id': order_id,
                'expected_quantity': max(1, int(expected_quantity or 1)),
                'aggregate_status': 'pending_ship',
                'finalized_count': 0,
                'pending_finalize_count': 0,
                'remaining_count': max(1, int(expected_quantity or 1)),
                'finalized_unit_indexes': [],
                'pending_finalize_unit_indexes': [],
                'remaining_unit_indexes': list(range(1, max(1, int(expected_quantity or 1)) + 1)),
                'states': [],
            }

        from db_manager import db_manager
        return db_manager.get_delivery_progress_summary(order_id, expected_quantity=expected_quantity)

    def _resolve_external_order_status(self, current_status: str, incoming_status: str, source: str):
        from db_manager import db_manager

        merged_status = db_manager.resolve_external_order_status(current_status, incoming_status, source=source)
        normalized_current = db_manager._normalize_order_status(current_status)

        if merged_status and merged_status != normalized_current:
            return merged_status
        return None

    def _normalize_order_amount_text(self, value: Any):
        text = str(value or '').strip()
        if not text:
            return None
        text = text.replace('¥', '').replace('￥', '').replace(',', '')
        match = re.search(r'\d+(?:\.\d{1,2})?', text)
        if not match:
            return None
        try:
            return f"{float(match.group(0)):.2f}"
        except (TypeError, ValueError):
            return None

    def _parse_order_amount_float(self, value: Any):
        normalized = self._normalize_order_amount_text(value)
        if normalized is None:
            return None
        try:
            return float(normalized)
        except (TypeError, ValueError):
            return None

    def _has_bargain_success_evidence(self, order: dict = None) -> bool:
        order = order or {}
        return bool(order.get('bargain_success_detected'))

    def _mark_order_bargain_flow(self, order_id: str, item_id: str = None, buyer_id: str = None,
                                 sid: str = None, *, apply_configured_price: bool = False,
                                 success_detected=..., context: str = '') -> bool:
        if not order_id:
            return False

        from db_manager import db_manager

        existing_order = db_manager.get_order_by_id(order_id) or {}
        effective_item_id = item_id or existing_order.get('item_id')
        effective_buyer_id = buyer_id or existing_order.get('buyer_id')
        effective_sid = sid or existing_order.get('sid')
        amount_to_save = None

        if apply_configured_price and effective_item_id:
            item_config = db_manager.get_item_info(self.cookie_id, effective_item_id)
            configured_amount = self._normalize_order_amount_text(item_config.get('item_price') if item_config else None)
            configured_amount_value = self._parse_order_amount_float(configured_amount)
            existing_amount_value = self._parse_order_amount_float(existing_order.get('amount'))
            if configured_amount_value is not None and (
                existing_amount_value is None or configured_amount_value < existing_amount_value - 0.009
            ):
                amount_to_save = configured_amount

        success = db_manager.insert_or_update_order(
            order_id=order_id,
            item_id=effective_item_id,
            buyer_id=effective_buyer_id,
            sid=effective_sid,
            amount=amount_to_save,
            cookie_id=self.cookie_id,
            bargain_flow_detected=True,
            bargain_success_detected=success_detected,
        )

        if success:
            logger.info(
                f"【{self.cookie_id}】标记订单为小刀流程: order_id={order_id}, context={context or 'unknown'}, "
                f"apply_configured_price={apply_configured_price}, amount_override={amount_to_save or ''}, "
                f"success_detected={success_detected if success_detected is not ... else 'unchanged'}"
            )
        else:
            logger.warning(
                f"【{self.cookie_id}】标记订单小刀流程失败: order_id={order_id}, context={context or 'unknown'}"
            )
        return success

    def _apply_bargain_amount_override(self, order_id: str, item_id: str, amount: Any, amount_source: str,
                                       existing_order: dict = None, item_config: dict = None):
        existing_order = existing_order or {}
        if not existing_order.get('bargain_flow_detected'):
            return amount, amount_source

        configured_amount = self._normalize_order_amount_text(item_config.get('item_price') if item_config else None)
        configured_amount_value = self._parse_order_amount_float(configured_amount)
        if configured_amount_value is None:
            return amount, amount_source

        incoming_amount = self._normalize_order_amount_text(amount)
        incoming_amount_value = self._parse_order_amount_float(incoming_amount)

        if incoming_amount_value is None:
            logger.warning(
                f"【{self.cookie_id}】小刀订单缺少可信金额，回退为商品配置价: "
                f"order_id={order_id}, item_id={item_id}, configured_amount={configured_amount}"
            )
            return configured_amount, 'bargain_item_price_locked'

        if incoming_amount_value > configured_amount_value + 0.009:
            logger.warning(
                f"【{self.cookie_id}】检测到小刀订单仍返回原价，使用商品配置价覆盖: "
                f"order_id={order_id}, item_id={item_id}, incoming_amount={incoming_amount}, "
                f"configured_amount={configured_amount}, amount_source={amount_source}"
            )
            return configured_amount, 'bargain_item_price_locked'

        return incoming_amount, amount_source

    def _resolve_delivery_progress_order_status(self, current_status: str, aggregate_status: str):
        from db_manager import db_manager

        normalized_current = db_manager._normalize_order_status(current_status)
        normalized_aggregate = db_manager._normalize_order_status(aggregate_status)

        if not normalized_aggregate or normalized_aggregate == 'unknown':
            return None

        if not normalized_current or normalized_current == 'unknown':
            return normalized_aggregate

        if normalized_current in {'completed', 'refunding', 'cancelled'} and normalized_aggregate in {
            'pending_ship', 'partial_success', 'partial_pending_finalize', 'shipped'
        }:
            logger.warning(
                f"【{self.cookie_id}】保留订单终态，忽略发货进度覆盖: current={normalized_current}, incoming={normalized_aggregate}"
            )
            return normalized_current

        if normalized_current == 'shipped' and normalized_aggregate in {'pending_ship', 'partial_success', 'partial_pending_finalize'}:
            logger.warning(
                f"【{self.cookie_id}】保留已发货状态，忽略较低发货进度覆盖: current={normalized_current}, incoming={normalized_aggregate}"
            )
            return normalized_current

        if normalized_current in {'partial_success', 'partial_pending_finalize'} and normalized_aggregate == 'pending_ship':
            logger.warning(
                f"【{self.cookie_id}】保留部分发货状态，忽略待发货覆盖: current={normalized_current}, incoming={normalized_aggregate}"
            )
            return normalized_current

        return normalized_aggregate

    def _sync_order_delivery_progress(self, order_id: str, cookie_id: str, expected_quantity: int = 1,
                                      context: str = "自动发货进度同步"):
        summary = self._summarize_delivery_progress(order_id, expected_quantity=expected_quantity)
        aggregate_status = summary.get('aggregate_status') or 'pending_ship'
        previous_status = None

        try:
            from db_manager import db_manager
            current_order = db_manager.get_order_by_id(order_id) if order_id else None
            previous_status = db_manager._normalize_order_status(current_order.get('order_status')) if current_order else None
        except Exception as e:
            logger.warning(f"【{self.cookie_id}】读取订单旧状态失败: {self._safe_str(e)}")

        logger.info(
            f"【{self.cookie_id}】同步订单发货进度: order_id={order_id}, status={aggregate_status}, "
            f"finalized={summary.get('finalized_count')}/{summary.get('expected_quantity')}, "
            f"pending_finalize={summary.get('pending_finalize_count')}, remaining={summary.get('remaining_count')}"
        )

        status_to_write = self._resolve_delivery_progress_order_status(previous_status, aggregate_status)

        if aggregate_status in {'shipped', 'partial_success', 'partial_pending_finalize'}:
            self.delivery_sent_orders.add(order_id)
            self.last_delivery_time[order_id] = time.time()

        if self.order_status_handler and status_to_write == 'shipped' and previous_status != 'shipped':
            try:
                self.order_status_handler.handle_auto_delivery_order_status(
                    order_id=order_id,
                    cookie_id=cookie_id,
                    context=context
                )
            except Exception as e:
                logger.warning(f"【{self.cookie_id}】通过状态处理器同步已发货状态失败: {self._safe_str(e)}")

        try:
            from db_manager import db_manager
            success = True
            if status_to_write and status_to_write != previous_status:
                success = db_manager.insert_or_update_order(order_id=order_id, order_status=status_to_write, cookie_id=cookie_id)

            if success and status_to_write in {'partial_success', 'partial_pending_finalize'} and previous_status != status_to_write:
                try:
                    from order_event_hub import publish_order_update_event
                    publish_order_update_event(order_id, source='delivery_progress_sync')
                except Exception as publish_e:
                    logger.warning(
                        f"【{self.cookie_id}】发布部分发货实时事件失败: order_id={order_id}, error={self._safe_str(publish_e)}"
                    )
        except Exception as e:
            logger.warning(f"【{self.cookie_id}】写入订单聚合发货状态失败: {self._safe_str(e)}")

        return summary

    async def _delayed_lock_release(self, lock_key: str, delay_minutes: int = 10):
        """
        延迟释放锁的异步任务

        Args:
            lock_key: 锁的键
            delay_minutes: 延迟时间（分钟），默认10分钟
        """
        try:
            delay_seconds = delay_minutes * 60
            logger.info(f"【{self.cookie_id}】订单锁 {lock_key} 将在 {delay_minutes} 分钟后释放")

            # 等待指定时间
            await asyncio.sleep(delay_seconds)

            # 检查锁是否仍然存在且需要释放
            if lock_key in self._lock_hold_info:
                lock_info = self._lock_hold_info[lock_key]
                if lock_info.get('locked', False):
                    # 释放锁
                    lock_info['locked'] = False
                    lock_info['release_time'] = time.time()
                    logger.info(f"【{self.cookie_id}】订单锁 {lock_key} 延迟释放完成")

                    # 清理锁信息（可选，也可以保留用于统计）
                    # del self._lock_hold_info[lock_key]

        except asyncio.CancelledError:
            logger.info(f"【{self.cookie_id}】订单锁 {lock_key} 延迟释放任务被取消")
            raise
        except Exception as e:
            logger.error(f"【{self.cookie_id}】订单锁 {lock_key} 延迟释放失败: {self._safe_str(e)}")

    def is_lock_held(self, lock_key: str) -> bool:
        """
        检查指定的锁是否仍在持有状态

        Args:
            lock_key: 锁的键

        Returns:
            bool: True表示锁仍在持有，False表示锁已释放或不存在
        """
        if lock_key not in self._lock_hold_info:
            return False

        lock_info = self._lock_hold_info[lock_key]
        return lock_info.get('locked', False)

    def cleanup_expired_locks(self, max_age_hours: int = 24):
        """
        清理过期的锁（包括自动发货锁和订单详情锁）

        Args:
            max_age_hours: 锁的最大保留时间（小时），默认24小时
        """
        try:
            current_time = time.time()
            max_age_seconds = max_age_hours * 3600

            # 清理自动发货锁
            expired_delivery_locks = []
            for order_id, last_used in self._lock_usage_times.items():
                if current_time - last_used > max_age_seconds:
                    expired_delivery_locks.append(order_id)

            # 清理过期的自动发货锁
            for order_id in expired_delivery_locks:
                if order_id in self._order_locks:
                    del self._order_locks[order_id]
                if order_id in self._lock_usage_times:
                    del self._lock_usage_times[order_id]
                # 清理锁持有信息
                if order_id in self._lock_hold_info:
                    lock_info = self._lock_hold_info[order_id]
                    # 取消延迟释放任务
                    if 'task' in lock_info and lock_info['task']:
                        lock_info['task'].cancel()
                    del self._lock_hold_info[order_id]

            # 清理订单详情锁
            expired_detail_locks = []
            for order_id, last_used in self._order_detail_lock_times.items():
                if current_time - last_used > max_age_seconds:
                    expired_detail_locks.append(order_id)

            # 清理过期的订单详情锁
            for order_id in expired_detail_locks:
                if order_id in self._order_detail_locks:
                    del self._order_detail_locks[order_id]
                if order_id in self._order_detail_lock_times:
                    del self._order_detail_lock_times[order_id]

            expired_refresh_marks = []
            for order_id, refresh_info in self.order_detail_force_refresh_marks.items():
                refresh_timestamp = refresh_info.get('timestamp', 0) if isinstance(refresh_info, dict) else 0
                if current_time - refresh_timestamp > max_age_seconds:
                    expired_refresh_marks.append(order_id)

            for order_id in expired_refresh_marks:
                self.order_detail_force_refresh_marks.pop(order_id, None)

            total_expired = len(expired_delivery_locks) + len(expired_detail_locks) + len(expired_refresh_marks)
            if total_expired > 0:
                logger.info(
                    f"【{self.cookie_id}】清理了 {total_expired} 个过期锁/标记 "
                    f"(发货锁: {len(expired_delivery_locks)}, 详情锁: {len(expired_detail_locks)}, 刷新标记: {len(expired_refresh_marks)})"
                )
                logger.warning(f"【{self.cookie_id}】当前锁数量 - 发货锁: {len(self._order_locks)}, 详情锁: {len(self._order_detail_locks)}")

        except Exception as e:
            logger.error(f"【{self.cookie_id}】清理过期锁时发生错误: {self._safe_str(e)}")

    def _get_order_status_priority(self, status: str) -> int:
        normalized_status = db_manager._normalize_order_status(status)
        priority_map = {
            'unknown': 0,
            'processing': 10,
            'pending_payment': 15,
            'pending_ship': 20,
            'partial_success': 30,
            'partial_pending_finalize': 30,
            'shipped': 40,
            'completed': 50,
            'refunding': 60,
            'refund_cancelled': 65,
            'cancelled': 70,
        }
        return priority_map.get(normalized_status or 'unknown', 0)

    def _has_delivery_progress_evidence(self, order_id: str) -> bool:
        normalized_order_id = str(order_id or '').strip()
        if not normalized_order_id:
            return False

        try:
            summary = self._summarize_delivery_progress(normalized_order_id, expected_quantity=1) or {}
        except Exception as summary_error:
            logger.warning(
                f"【{self.cookie_id}】读取订单发货进度失败，按已有发货证据处理: "
                f"order_id={normalized_order_id}, error={self._safe_str(summary_error)}"
            )
            return True

        state_count = int(summary.get('state_count') or 0)
        finalized_count = int(summary.get('finalized_count') or 0)
        pending_finalize_count = int(summary.get('pending_finalize_count') or 0)
        return state_count > 0 or finalized_count > 0 or pending_finalize_count > 0

    def _reserve_order_detail_force_refresh(self, order_id: str, *, reason: str,
                                            log_prefix: str = "", cooldown_seconds: float = None) -> bool:
        normalized_order_id = str(order_id or '').strip()
        if not normalized_order_id:
            return False

        cooldown = float(cooldown_seconds or self.order_detail_force_refresh_cooldown or 0)
        now = time.time()
        existing = self.order_detail_force_refresh_marks.get(normalized_order_id) or {}
        last_timestamp = existing.get('timestamp', 0)
        elapsed = now - last_timestamp

        if last_timestamp and cooldown > 0 and elapsed < cooldown:
            logger.info(
                f"{log_prefix} 订单详情强刷命中冷却，跳过重复刷新: "
                f"order_id={normalized_order_id}, reason={reason}, "
                f"last_reason={existing.get('reason', 'unknown')}, remaining={round(cooldown - elapsed, 2)}s"
            )
            return False

        self.order_detail_force_refresh_marks[normalized_order_id] = {
            'timestamp': now,
            'reason': reason,
        }
        return True

    def _should_force_refresh_after_status_signal(self, status_signal: str, current_status: str,
                                                  order_id: str = None) -> bool:
        normalized_signal = db_manager._normalize_order_status(status_signal)
        normalized_current = db_manager._normalize_order_status(current_status)

        if not normalized_signal or normalized_signal == 'unknown':
            return False

        if normalized_signal == 'pending_ship':
            if normalized_current == 'shipped' and not self._has_delivery_progress_evidence(order_id):
                logger.warning(
                    f"【{self.cookie_id}】检测到可疑已发货状态，允许待发货信号继续强刷详情: "
                    f"order_id={order_id or 'unknown'}, current_status={normalized_current}, signal={normalized_signal}"
                )
                return True
            return normalized_current in {None, '', 'unknown', 'processing', 'pending_payment'}

        if normalized_signal == 'shipped':
            return normalized_current in {None, '', 'unknown', 'processing', 'pending_payment', 'pending_ship'}

        if normalized_signal in {'completed', 'cancelled', 'refunding', 'refund_cancelled'}:
            if not normalized_current or normalized_current == 'unknown':
                return True
            return self._get_order_status_priority(normalized_signal) > self._get_order_status_priority(normalized_current)

        return False

    def _should_accept_order_detail_status_correction(self, current_status: str, incoming_status: str,
                                                      incoming_source: str, *, force_refresh: bool,
                                                      order_id: str = None) -> bool:
        normalized_current = db_manager._normalize_order_status(current_status)
        normalized_incoming = db_manager._normalize_order_status(incoming_status)
        normalized_source = str(incoming_source or 'unknown').strip().lower()

        if not force_refresh:
            return False
        if normalized_current != 'shipped' or normalized_incoming != 'pending_ship':
            return False
        if normalized_source not in {'selector', 'button'}:
            return False
        if self._has_delivery_progress_evidence(order_id):
            return False
        return True

    def _should_reject_order_detail_status_update(self, current_status: str, incoming_status: str,
                                                  incoming_source: str, *, force_refresh: bool) -> bool:
        normalized_current = db_manager._normalize_order_status(current_status)
        normalized_incoming = db_manager._normalize_order_status(incoming_status)
        normalized_source = str(incoming_source or 'unknown').strip().lower()

        if normalized_incoming != 'completed' or normalized_source != 'body':
            return False

        if force_refresh and normalized_current in {'shipped', 'pending_ship', 'partial_success', 'partial_pending_finalize'}:
            return True

        return False

    async def _maybe_force_refresh_order_detail_for_signal(self, order_id: str, *, item_id: str = None,
                                                           buyer_id: str = None, sid: str = None,
                                                           buyer_nick: str = None, status_signal: str = None,
                                                           reason: str = "status_signal",
                                                           delay_seconds: float = 0,
                                                           log_prefix: str = "") -> bool:
        normalized_order_id = str(order_id or '').strip()
        if not normalized_order_id:
            return False

        current_order = db_manager.get_order_by_id(normalized_order_id) or {}
        current_status = current_order.get('order_status')
        if not self._should_force_refresh_after_status_signal(status_signal, current_status, normalized_order_id):
            logger.info(
                f"{log_prefix} 当前订单状态无需为该信号强刷详情: order_id={normalized_order_id}, "
                f"signal={status_signal or 'unknown'}, current_status={current_status or 'unknown'}"
            )
            return False

        if not self._reserve_order_detail_force_refresh(
            normalized_order_id,
            reason=reason,
            log_prefix=log_prefix,
        ):
            return False

        if delay_seconds and delay_seconds > 0:
            await asyncio.sleep(delay_seconds)

        latest_order = db_manager.get_order_by_id(normalized_order_id) or {}
        latest_status = latest_order.get('order_status')
        if not self._should_force_refresh_after_status_signal(status_signal, latest_status, normalized_order_id):
            logger.info(
                f"{log_prefix} 延迟后订单状态已更新，无需再强刷详情: order_id={normalized_order_id}, "
                f"signal={status_signal or 'unknown'}, current_status={latest_status or 'unknown'}"
            )
            return False

        refresh_item_id = item_id or latest_order.get('item_id')
        refresh_buyer_id = buyer_id or latest_order.get('buyer_id')
        logger.info(
            f"{log_prefix} 状态信号触发订单详情强刷: order_id={normalized_order_id}, "
            f"signal={status_signal or 'unknown'}, current_status={latest_status or 'unknown'}, reason={reason}"
        )

        try:
            await self.fetch_order_detail_info(
                order_id=normalized_order_id,
                item_id=refresh_item_id,
                buyer_id=refresh_buyer_id,
                sid=sid,
                buyer_nick=buyer_nick,
                force_refresh=True
            )
            return True
        except Exception as refresh_error:
            logger.error(
                f"{log_prefix} 状态信号触发订单详情强刷失败: order_id={normalized_order_id}, "
                f"reason={reason}, error={self._safe_str(refresh_error)}"
            )
            return False


    def _load_json_dict(self, raw_value: Any) -> Dict[str, Any]:
        """安全解析 JSON 对象。"""
        if isinstance(raw_value, dict):
            return raw_value
        if not isinstance(raw_value, str) or not raw_value.strip():
            return {}
        try:
            parsed = json.loads(raw_value)
        except Exception:
            return {}
        return parsed if isinstance(parsed, dict) else {}

    def _extract_message_card_payload(self, message_1: Any) -> Dict[str, Any]:
        """提取消息卡片 JSON 载荷。"""
        if not isinstance(message_1, dict):
            return {}

        try:
            message_6 = message_1.get('6', {})
            if not isinstance(message_6, dict):
                return {}
            message_6_3 = message_6.get('3', {})
            if not isinstance(message_6_3, dict):
                return {}
            payload = message_6_3.get('5', '')
            return self._load_json_dict(payload)
        except Exception:
            return {}

    def _extract_message_button_text(self, message_1: Any) -> str:
        """提取消息卡片按钮文本。"""
        payload = self._extract_message_card_payload(message_1)
        try:
            return str(
                payload.get('dxCard', {})
                .get('item', {})
                .get('main', {})
                .get('exContent', {})
                .get('button', {})
                .get('text', '')
            ).strip()
        except Exception:
            return ''

    def _extract_message_card_title(self, message_1: Any) -> str:
        """提取消息卡片标题。"""
        payload = self._extract_message_card_payload(message_1)
        try:
            return str(
                payload.get('dxCard', {})
                .get('item', {})
                .get('main', {})
                .get('exContent', {})
                .get('title', '')
            ).strip()
        except Exception:
            return ''

    def _classify_message_route(self, *, message: dict, message_1: dict, message_10: dict,
                                send_message: str) -> Dict[str, Any]:
        """将消息路由到订单状态、系统提示、特殊流程或真人聊天。"""
        message_direction = message_1.get('7', 0) if isinstance(message_1, dict) else 0
        content_type = 0
        try:
            message_6 = message_1.get('6', {}) if isinstance(message_1, dict) else {}
            if isinstance(message_6, dict):
                message_6_3 = message_6.get('3', {})
                if isinstance(message_6_3, dict):
                    content_type = message_6_3.get('4', 0)
        except Exception:
            content_type = 0

        biz_tag_raw = str(message_10.get('bizTag', '') or '').strip()
        biz_tag_dict = self._load_json_dict(biz_tag_raw)
        ext_json_dict = self._load_json_dict(message_10.get('extJson', ''))
        task_name = str(biz_tag_dict.get('taskName') or '').strip()
        update_key = str(ext_json_dict.get('updateKey') or '').strip()
        detail_notice = str(message_10.get('detailNotice', '') or '').strip()
        reminder_content = str(message_10.get('reminderContent', '') or send_message or '').strip()
        reminder_title = str(message_10.get('reminderTitle', '') or '').strip()
        reminder_notice = str(message_10.get('reminderNotice', '') or '').strip()
        red_reminder = ''
        if isinstance(message, dict) and isinstance(message.get('3'), dict):
            red_reminder = str(message.get('3', {}).get('redReminder', '') or '').strip()

        button_text = self._extract_message_button_text(message_1)
        card_title = self._extract_message_card_title(message_1)
        session_type = str(message_10.get('sessionType', '1') or '1').strip()
        is_group_message = session_type == '30'
        is_system_biz = bool(task_name) or 'SECURITY' in biz_tag_raw or 'taskId' in biz_tag_raw
        is_system_message = message_direction == 1 or content_type == 6 or is_system_biz

        texts = []
        for raw_text in (
            send_message,
            reminder_content,
            detail_notice,
            reminder_title,
            reminder_notice,
            red_reminder,
            task_name,
            update_key,
            button_text,
            card_title,
        ):
            normalized_text = str(raw_text or '').strip()
            if normalized_text and normalized_text not in texts:
                texts.append(normalized_text)

        special_flow_messages = {
            '[卡片消息]',
            '快给ta一个评价吧~',
            '快给ta一个评价吧～',
        }
        special_flow_titles = {
            '我已小刀，待刀成',
            '我已小刀,待刀成',
            '我已成功小刀，待发货',
            '我已成功小刀,待发货',
        }

        if send_message in special_flow_messages or card_title in special_flow_titles:
            route = 'special_flow'
            order_status_signal = None
        else:
            order_status_signal = None
            closed_markers = (
                '[你关闭了订单，钱款已原路退返]',
                '交易关闭',
                '订单关闭',
                '钱款已原路退返',
            )
            refund_markers = (
                '退款中',
                '退款成功',
                '退货退款',
                '退款关闭',
            )
            completed_markers = (
                '[买家确认收货，交易成功]',
                '[你已确认收货，交易成功]',
                '买家确认收货',
                '交易成功',
            )
            shipped_markers = (
                '[你已发货]',
                '已发货',
                '等待买家收货',
            )
            pending_ship_markers = (
                '[我已付款，等待你发货]',
                '[已付款，待发货]',
                '我已付款，等待你发货',
                '[记得及时发货]',
                '等待你发货',
                '待发货',
                '去发货',
                '付款完成待发货',
                'TRADE_PAID_DONE_SELLER',
            )
            pending_payment_markers = (
                '[我已拍下，待付款]',
                '买家已拍下，待付款',
                '待付款',
                '等待买家付款',
                '已拍下_未付款',
            )
            system_notice_markers = (
                '闲鱼小红花',
                '温馨提醒',
                '曝光卡',
                '蚂蚁森林',
                '能量可领',
                '创建合约',
                '假客服骗钱',
                '订单即将自动确认收货',
                '宝贝性价比如何，去表个态吧',
                '发来一条消息',
                '发来一条新消息',
                '已送出小红花',
                '已收下',
            )

            def _contains_any(markers) -> bool:
                return any(marker and marker in text for text in texts for marker in markers)

            if _contains_any(closed_markers):
                order_status_signal = 'cancelled'
            elif _contains_any(refund_markers):
                order_status_signal = 'refunding'
            elif _contains_any(completed_markers):
                order_status_signal = 'completed'
            elif _contains_any(shipped_markers):
                order_status_signal = 'shipped'
            elif _contains_any(pending_ship_markers):
                order_status_signal = 'pending_ship'
            elif _contains_any(pending_payment_markers):
                order_status_signal = 'pending_payment'

            if is_system_message and order_status_signal:
                route = 'order_status'
            elif _contains_any(system_notice_markers) and (is_system_message or message_direction != 2):
                route = 'system_notice'
            elif is_system_message:
                route = 'system_notice'
            else:
                route = 'user_chat'

        should_notify = False
        if not is_group_message:
            if route == 'user_chat':
                should_notify = True
            elif route == 'order_status' and order_status_signal in {'pending_ship', 'refunding', 'cancelled'}:
                should_notify = True

        return {
            'route': route,
            'order_status_signal': order_status_signal,
            'should_notify': should_notify,
            'allow_auto_reply': route == 'user_chat',
            'is_system_message': is_system_message,
            'is_group_message': is_group_message,
            'message_direction': message_direction,
            'content_type': content_type,
            'task_name': task_name,
            'button_text': button_text,
            'card_title': card_title,
            'texts': texts,
        }

    def _is_auto_delivery_trigger(self, message: str) -> bool:
        """检查消息是否为自动发货触发关键字"""
        # 定义所有自动发货触发关键字
        auto_delivery_keywords = [
            # 系统消息
            '[我已付款，等待你发货]',
            '[已付款，待发货]',
            '我已付款，等待你发货',
            '[记得及时发货]',
        ]

        # 检查消息是否包含任何触发关键字
        for keyword in auto_delivery_keywords:
            if keyword in message:
                return True

        return False

    def _extract_order_id_from_update_key(self, raw_text: Any) -> Optional[str]:
        normalized_text = str(raw_text or '').strip()
        if not normalized_text:
            return None

        direct_match_found = False
        direct_match = re.search(r'updateKey["\']?\s*[:=]\s*["\']([^"\']+)', normalized_text)
        if direct_match:
            direct_match_found = True
            normalized_text = direct_match.group(1)

        colon_parts = [part.strip().strip('"\'') for part in normalized_text.split(':')]
        long_numeric_parts = [part for part in colon_parts if part.isdigit() and len(part) >= 16]
        if long_numeric_parts:
            return long_numeric_parts[0]

        if direct_match_found:
            generic_matches = re.findall(r'\d{16,}', normalized_text)
            if generic_matches:
                return generic_matches[0]
        return None

    def _extract_order_id_from_candidate_text(self, raw_text: Any, source: str = '') -> Optional[str]:
        normalized_text = str(raw_text or '').strip()
        if not normalized_text:
            return None

        patterns = [
            r'orderId(?:=|:|%3[Dd]|\\u003[dD])\s*"?(\d{10,})',
            r'bizOrderId["\']?\s*[:=]\s*"?(\d{10,})',
            r'order[_-]?id["\']?\s*[:=]\s*"?(\d{10,})',
            r'order[_-]?detail\?(?:[^\s#]*?&)?id=(\d{10,})',
            r'order-detail\?(?:[^\s#]*?&)?orderId=(\d{10,})',
        ]

        for pattern in patterns:
            match = re.search(pattern, normalized_text)
            if match:
                return match.group(1)

        source_lower = source.lower()
        text_lower = normalized_text.lower()
        if (
            'updatekey' in source_lower
            or 'updatekey' in text_lower
            or ('trade_' in text_lower and ':' in normalized_text)
            or ('buyer_confirm' in text_lower and ':' in normalized_text)
        ):
            return self._extract_order_id_from_update_key(normalized_text)

        return None

    def _collect_order_id_candidate_texts(self, data: Any, root: str = 'message'):
        candidates = []
        seen = set()

        def add_candidate(source: str, value: Any):
            if value is None:
                return
            normalized_text = str(value).strip()
            if not normalized_text:
                return
            dedupe_key = (source, normalized_text)
            if dedupe_key in seen:
                return
            seen.add(dedupe_key)
            candidates.append((source, normalized_text))

            if normalized_text[:1] in {'{', '['}:
                try:
                    parsed_value = json.loads(normalized_text)
                except Exception:
                    return
                walk_value(parsed_value, f'{source}.json')

        def walk_value(value: Any, source: str):
            if isinstance(value, dict):
                for key, nested_value in value.items():
                    nested_source = f'{source}.{key}'
                    if isinstance(nested_value, (dict, list)):
                        walk_value(nested_value, nested_source)
                    else:
                        add_candidate(nested_source, nested_value)
            elif isinstance(value, list):
                for index, nested_value in enumerate(value[:20]):
                    walk_value(nested_value, f'{source}[{index}]')
            else:
                add_candidate(source, value)

        walk_value(data, root)
        return candidates

    def _extract_order_id(self, message: dict, raw_message_data: dict = None) -> str:
        """从消息中提取订单ID
        
        Args:
            message: 解密后的消息内容
            raw_message_data: 原始的WebSocket消息数据（用于在解密消息中找不到订单ID时进行搜索）
        """
        try:
            # 先查看消息的完整结构
            logger.warning(f"【{self.cookie_id}】🔍 完整消息结构: {message}")

            for source, candidate_text in self._collect_order_id_candidate_texts(message, root='message'):
                order_id = self._extract_order_id_from_candidate_text(candidate_text, source=source)
                if order_id:
                    logger.info(f'【{self.cookie_id}】🎯 最终提取到订单ID: {order_id} (source={source})')
                    return order_id

            if raw_message_data:
                logger.info(f'【{self.cookie_id}】🔍 尝试从原始消息数据中搜索订单ID')
                for source, candidate_text in self._collect_order_id_candidate_texts(raw_message_data, root='raw_message'):
                    order_id = self._extract_order_id_from_candidate_text(candidate_text, source=source)
                    if order_id:
                        logger.info(f'【{self.cookie_id}】🎯 从原始消息提取到订单ID: {order_id} (source={source})')
                        return order_id

                try:
                    sync_data_list = raw_message_data.get("body", {}).get("syncPushPackage", {}).get("data", [])
                    for idx, sync_data_item in enumerate(sync_data_list[:20]):
                        if not isinstance(sync_data_item, dict) or "data" not in sync_data_item:
                            continue

                        item_data = sync_data_item.get("data")
                        if item_data is None:
                            continue

                        try:
                            decoded_data = base64.b64decode(item_data).decode("utf-8")
                        except Exception:
                            decoded_data = item_data

                        for source, candidate_text in self._collect_order_id_candidate_texts(decoded_data, root=f'raw_sync[{idx}]'):
                            order_id = self._extract_order_id_from_candidate_text(candidate_text, source=source)
                            if order_id:
                                logger.info(f'【{self.cookie_id}】🎯 从syncPushPackage.data提取到订单ID: {order_id} (source={source})')
                                return order_id
                except Exception as multi_data_e:
                    logger.warning(f"遍历syncPushPackage.data时出错: {multi_data_e}")

            logger.warning(f'【{self.cookie_id}】❌ 未能从消息中提取到订单ID')
            return None

        except Exception as e:
            logger.error(f"【{self.cookie_id}】提取订单ID失败: {self._safe_str(e)}")
            return None

    async def _handle_simple_message_auto_delivery(self, websocket, order_id: str, item_id: str, 
                                                    user_id: str, chat_id: str, msg_time: str, msg_id: str):
        """处理简化结构消息的自动发货逻辑
        
        专门用于处理简化结构的发货通知消息（message['1']是字符串的情况）
        发货确认统一在 _auto_delivery 内执行，避免重复确认导致漏发
        
        Args:
            websocket: WebSocket连接
            order_id: 订单ID
            item_id: 商品ID
            user_id: 买家用户ID
            chat_id: 聊天ID
            msg_time: 消息时间
            msg_id: 消息ID
        """
        try:
            logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 🚀 开始处理简化消息自动发货: order_id={order_id}, item_id={item_id}')
            
            # 检查商品是否属于当前账号
            if item_id and item_id != "未知商品":
                try:
                    if not await self._ensure_item_owned_by_current_account(
                        item_id,
                        log_prefix=f'[{msg_time}] 【{self.cookie_id}】[{msg_id}]'
                    ):
                        logger.warning(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ❌ 商品 {item_id} 不属于当前账号，跳过自动发货')
                        self._record_delivery_log(
                            order_id=order_id,
                            item_id=item_id,
                            buyer_id=user_id,
                            status='failed',
                            reason='商品不属于当前账号，跳过自动发货',
                            channel='auto'
                        )
                        return
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ✅ 商品 {item_id} 归属验证通过')
                except Exception as e:
                    logger.error(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 检查商品归属失败: {self._safe_str(e)}，跳过自动发货')
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=user_id,
                        status='failed',
                        reason=f'检查商品归属失败: {self._safe_str(e)}',
                        channel='auto'
                    )
                    return
            
            # 检查订单是否已发货
            if not self.can_auto_delivery(order_id):
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 订单 {order_id} 在冷却期内，跳过发货')
                self._record_delivery_log(
                    order_id=order_id,
                    item_id=item_id,
                    buyer_id=user_id,
                    status='skipped',
                    reason='订单在冷却期内，跳过发货',
                    channel='auto'
                )
                return
            
            # 检查延迟锁状态
            lock_key = order_id
            if self.is_lock_held(lock_key):
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 🔒 订单 {lock_key} 延迟锁仍在持有状态，跳过发货')
                self._record_delivery_log(
                    order_id=order_id,
                    item_id=item_id,
                    buyer_id=user_id,
                    status='skipped',
                    reason='订单延迟锁持有中，跳过发货',
                    channel='auto'
                )
                return
            
            # 获取订单锁
            order_lock = self._order_locks[lock_key]
            self._lock_usage_times[lock_key] = time.time()
            
            async with order_lock:
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 获取订单锁成功: {lock_key}')
                
                # 再次检查延迟锁和冷却状态
                if self.is_lock_held(lock_key) or not self.can_auto_delivery(order_id):
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 获取锁后检查发现订单已处理，跳过发货')
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=user_id,
                        status='skipped',
                        reason='获取锁后发现订单已处理，跳过发货',
                        channel='auto'
                    )
                    return

                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 📤 开始执行自动发货内容发送（发送成功后再确认发货）')
                
                # 获取商品标题
                item_title = "待获取商品信息"

                pending_finalize_meta = self._get_pending_delivery_finalization_meta(order_id, 1)
                if pending_finalize_meta:
                    finalize_result = await self._finalize_delivery_after_send(
                        delivery_meta=pending_finalize_meta,
                        order_id=order_id,
                        item_id=item_id
                    )
                    if not finalize_result.get('success'):
                        self._persist_delivery_finalization_state(
                            order_id=order_id,
                            item_id=item_id,
                            buyer_id=user_id,
                            delivery_meta=pending_finalize_meta,
                            channel='auto',
                            status='sent',
                            last_error=finalize_result.get('error') or '补完成 finalize 失败'
                        )
                        self._record_delivery_log(
                            order_id=order_id,
                            item_id=item_id,
                            buyer_id=user_id,
                            status='failed',
                            reason=finalize_result.get('error') or '检测到已发送记录，但补完成发货收尾失败',
                            channel='auto',
                            rule_meta=pending_finalize_meta
                        )
                        await self.send_delivery_failure_notification(
                            send_user_name="买家",
                            send_user_id=user_id,
                            item_id=item_id,
                            error_message=finalize_result.get('error') or '检测到已发送记录，但补完成发货收尾失败',
                            chat_id=chat_id
                        )
                        return

                    self._persist_delivery_finalization_state(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=user_id,
                        delivery_meta=pending_finalize_meta,
                        channel='auto',
                        status='finalized'
                    )
                    self._sync_order_delivery_progress(
                        order_id=order_id,
                        cookie_id=self.cookie_id,
                        expected_quantity=1,
                        context="自动发货补完成收尾成功"
                    )
                    self._activate_delivery_lock(lock_key, delay_minutes=10)
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=user_id,
                        status='success',
                        reason='检测到发货消息已发送，本次补完成收尾成功',
                        channel='auto',
                        rule_meta=pending_finalize_meta
                    )
                    await self.send_delivery_failure_notification(
                        send_user_name="买家",
                        send_user_id=user_id,
                        item_id=item_id,
                        error_message="发货成功",
                        chat_id=chat_id
                    )
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ✅ 简化消息自动发货补完成收尾成功')
                    return
                
                # 调用自动发货方法获取发货内容
                delivery_result = await self._auto_delivery(
                    item_id, item_title, order_id, user_id, chat_id, include_meta=True
                )
                if isinstance(delivery_result, dict):
                    delivery_content = delivery_result.get('content')
                    delivery_error = delivery_result.get('error')
                    delivery_steps = delivery_result.get('delivery_steps') or []
                    delivery_rule_meta = {
                        'rule_id': delivery_result.get('rule_id'),
                        'rule_keyword': delivery_result.get('rule_keyword'),
                        'card_type': delivery_result.get('card_type'),
                        'match_mode': delivery_result.get('match_mode'),
                        'order_spec_mode': delivery_result.get('order_spec_mode'),
                        'rule_spec_mode': delivery_result.get('rule_spec_mode'),
                        'item_config_mode': delivery_result.get('item_config_mode'),
                        'card_id': delivery_result.get('card_id'),
                        'card_description': delivery_result.get('card_description'),
                        'data_card_pending_consume': delivery_result.get('data_card_pending_consume'),
                        'data_line': delivery_result.get('data_line'),
                        'data_reservation_id': delivery_result.get('data_reservation_id'),
                        'data_reservation_status': delivery_result.get('data_reservation_status'),
                        'delivery_unit_index': delivery_result.get('delivery_unit_index')
                    }
                else:
                    delivery_content = delivery_result
                    delivery_error = None
                    delivery_steps = []
                    delivery_rule_meta = {}

                if delivery_content:
                    delivery_rule_meta.setdefault('success', True)
                    if not delivery_steps:
                        delivery_steps = self._build_delivery_steps(
                            delivery_content,
                            delivery_rule_meta.get('card_description', '')
                        )

                    # 发送发货内容
                    user_url = f'https://www.goofish.com/personal?userId={user_id}'
                    
                    try:
                        await self._send_delivery_steps(
                            websocket,
                            chat_id,
                            user_id,
                            delivery_steps,
                            user_url=user_url,
                            log_prefix=f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 自动发货'
                        )

                        if not self._mark_data_reservation_sent_if_needed(delivery_result if isinstance(delivery_result, dict) else delivery_rule_meta):
                            self._release_data_reservation_if_needed(
                                delivery_result if isinstance(delivery_result, dict) else delivery_rule_meta,
                                error='发送成功后标记预占已发送失败'
                            )
                            raise Exception('批量数据预占标记已发送失败')

                        self._persist_delivery_finalization_state(
                            order_id=order_id,
                            item_id=item_id,
                            buyer_id=user_id,
                            delivery_meta=delivery_result if isinstance(delivery_result, dict) else delivery_rule_meta,
                            channel='auto',
                            status='sent'
                        )

                        finalize_result = await self._finalize_delivery_after_send(
                            delivery_meta=delivery_result if isinstance(delivery_result, dict) else delivery_rule_meta,
                            order_id=order_id,
                            item_id=item_id
                        )
                        if not finalize_result.get('success'):
                            self._persist_delivery_finalization_state(
                                order_id=order_id,
                                item_id=item_id,
                                buyer_id=user_id,
                                delivery_meta=delivery_result if isinstance(delivery_result, dict) else delivery_rule_meta,
                                channel='auto',
                                status='sent',
                                last_error=finalize_result.get('error') or '发送成功但提交发货副作用失败'
                            )
                            self._record_delivery_log(
                                order_id=order_id,
                                item_id=item_id,
                                buyer_id=user_id,
                                status='failed',
                                reason=finalize_result.get('error') or '发送成功但提交发货副作用失败',
                                channel='auto',
                                rule_meta=delivery_rule_meta
                            )
                            await self.send_delivery_failure_notification(
                                send_user_name="买家",
                                send_user_id=user_id,
                                item_id=item_id,
                                error_message=finalize_result.get('error') or '发送成功但提交发货副作用失败',
                                chat_id=chat_id
                            )
                            return

                        self._persist_delivery_finalization_state(
                            order_id=order_id,
                            item_id=item_id,
                            buyer_id=user_id,
                            delivery_meta=delivery_result if isinstance(delivery_result, dict) else delivery_rule_meta,
                            channel='auto',
                            status='finalized'
                        )

                        self._sync_order_delivery_progress(
                            order_id=order_id,
                            cookie_id=self.cookie_id,
                            expected_quantity=1,
                            context="自动发货发送成功"
                        )
                        self._activate_delivery_lock(lock_key, delay_minutes=10)

                        self._record_delivery_log(
                            order_id=order_id,
                            item_id=item_id,
                            buyer_id=user_id,
                            status='success',
                            reason='自动发货步骤发送成功',
                            channel='auto',
                            rule_meta=delivery_rule_meta
                        )
                    except Exception as send_e:
                        self._record_delivery_log(
                            order_id=order_id,
                            item_id=item_id,
                            buyer_id=user_id,
                            status='failed',
                            reason=f'自动发货消息发送失败: {self._safe_str(send_e)}',
                            channel='auto',
                            rule_meta=delivery_rule_meta
                        )
                        raise
                    
                    # 发送成功通知
                    await self.send_delivery_failure_notification(
                        send_user_name="买家",
                        send_user_id=user_id,
                        item_id=item_id,
                        error_message="发货成功",
                        chat_id=chat_id
                    )
                    
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ✅ 简化消息自动发货完成')
                else:
                    logger.warning(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ❌ 未找到匹配的发货规则或获取发货内容失败')
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=user_id,
                        status='failed',
                        reason=delivery_error or '未找到匹配的发货规则或获取发货内容失败',
                        channel='auto',
                        rule_meta=delivery_rule_meta
                    )
                    await self.send_delivery_failure_notification(
                        send_user_name="买家",
                        send_user_id=user_id,
                        item_id=item_id,
                        error_message="未找到匹配的发货规则或获取发货内容失败",
                        chat_id=chat_id
                    )

        except Exception as e:
            self._release_data_reservation_if_needed(
                delivery_result if 'delivery_result' in locals() and isinstance(delivery_result, dict) else delivery_rule_meta if 'delivery_rule_meta' in locals() else None,
                error=f'自动发货发送失败: {self._safe_str(e)}'
            )
            self._record_delivery_log(
                order_id=order_id,
                item_id=item_id,
                buyer_id=user_id,
                status='failed',
                reason=f'简化消息自动发货异常: {self._safe_str(e)}',
                channel='auto'
            )
            logger.error(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 简化消息自动发货异常: {self._safe_str(e)}')
            import traceback
            logger.error(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 异常堆栈: {traceback.format_exc()}')

    async def _handle_auto_delivery(self, websocket, message: dict, send_user_name: str, send_user_id: str,
                                   item_id: str, chat_id: str, msg_time: str, message_data: dict = None):
        """统一处理自动发货逻辑
        
        Args:
            message_data: 原始的WebSocket消息数据，用于提取订单ID时的备用搜索
        """
        try:
            from db_manager import db_manager

            # 检查商品是否属于当前cookies
            if item_id and item_id != "未知商品":
                try:
                    if not await self._ensure_item_owned_by_current_account(
                        item_id,
                        log_prefix=f'[{msg_time}] 【{self.cookie_id}】'
                    ):
                        logger.warning(f'[{msg_time}] 【{self.cookie_id}】❌ 商品 {item_id} 不属于当前账号，跳过自动发货')
                        self._record_delivery_log(
                            item_id=item_id,
                            buyer_id=send_user_id,
                            buyer_nick=send_user_name,
                            status='failed',
                            reason='商品不属于当前账号，跳过自动发货',
                            channel='auto'
                        )
                        return
                    logger.warning(f'[{msg_time}] 【{self.cookie_id}】✅ 商品 {item_id} 归属验证通过')
                except Exception as e:
                    logger.error(f'[{msg_time}] 【{self.cookie_id}】检查商品归属失败: {self._safe_str(e)}，跳过自动发货')
                    self._record_delivery_log(
                        item_id=item_id,
                        buyer_id=send_user_id,
                        buyer_nick=send_user_name,
                        status='failed',
                        reason=f'检查商品归属失败: {self._safe_str(e)}',
                        channel='auto'
                    )
                    return

            # 提取订单ID（传递原始消息数据以便在解密消息中找不到时进行备用搜索）
            order_id = self._extract_order_id(message, message_data)

            # 如果order_id不存在，尝试通过sid进行兜底查单
            if not order_id:
                fallback_sid = None
                try:
                    message_1 = message.get('1', {}) if isinstance(message, dict) else {}
                    if isinstance(message_1, dict):
                        # 优先使用会话字段
                        fallback_sid = message_1.get('2', '')

                        # 备用：从reminderUrl里解析sid
                        if not fallback_sid:
                            message_10 = message_1.get('10', {})
                            if isinstance(message_10, dict):
                                reminder_url = message_10.get('reminderUrl', '') or ''
                                sid_match = re.search(r'[?&]sid=([^&]+)', reminder_url)
                                if sid_match:
                                    fallback_sid = sid_match.group(1)
                except Exception as sid_e:
                    logger.warning(f'[{msg_time}] 【{self.cookie_id}】解析sid失败: {self._safe_str(sid_e)}')

                if fallback_sid:
                    try:
                        log_prefix = f'[{msg_time}] 【{self.cookie_id}】'
                        sid_lookup_minutes = 5
                        sid_lookup = self._lookup_delivery_order_by_sid(
                            fallback_sid,
                            minutes=sid_lookup_minutes,
                            log_prefix=log_prefix
                        )
                        sid_lookup = await self._refresh_sid_lookup_if_needed(
                            fallback_sid,
                            sid_lookup,
                            item_id=item_id,
                            buyer_id=send_user_id,
                            minutes=sid_lookup_minutes,
                            allow_bargain_ready=True,
                            log_prefix=log_prefix
                        )
                    except Exception as sid_query_e:
                        logger.error(f'[{msg_time}] 【{self.cookie_id}】sid兜底查单异常: {self._safe_str(sid_query_e)}')
                        sid_lookup = {'match_type': 'error', 'order': None}

                    recent_order = sid_lookup.get('order')
                    sid_match_type = sid_lookup.get('match_type', 'missing')

                    if recent_order and sid_match_type in {'pending_ship', 'bargain_ready'}:
                        fallback_order_id = recent_order.get('order_id')
                        fallback_item_id = recent_order.get('item_id')
                        fallback_buyer_id = recent_order.get('buyer_id')

                        # 防串单：买家不一致直接拒绝（仅当 DB 中的 buyer_id 可信时才校验）
                        if send_user_id and fallback_buyer_id and self._is_trustworthy_buyer_id(fallback_buyer_id) and str(send_user_id) != str(fallback_buyer_id):
                            logger.warning(
                                f'[{msg_time}] 【{self.cookie_id}】❌ sid兜底命中订单但买家不一致，已拒绝发货: '
                                f'send_user_id={send_user_id}, order_buyer_id={fallback_buyer_id}, sid={fallback_sid}'
                            )
                            return

                        # 防串单：商品不一致直接拒绝
                        if item_id and item_id != "未知商品" and fallback_item_id and str(item_id) != str(fallback_item_id):
                            logger.warning(
                                f'[{msg_time}] 【{self.cookie_id}】❌ sid兜底命中订单但商品不一致，已拒绝发货: '
                                f'message_item_id={item_id}, order_item_id={fallback_item_id}, sid={fallback_sid}'
                            )
                            return

                        order_id = fallback_order_id
                        if (not item_id or item_id == "未知商品") and fallback_item_id:
                            item_id = fallback_item_id

                        if sid_match_type == 'bargain_ready':
                            logger.info(
                                f'[{msg_time}] 【{self.cookie_id}】✅ 订单ID提取失败，但检测到小刀成功证据，'
                                f'使用sid兜底直接进入自动发货: sid={fallback_sid}, order_id={order_id}'
                            )

                        logger.info(
                            f'[{msg_time}] 【{self.cookie_id}】✅ 订单ID提取失败，已通过sid兜底定位订单: '
                            f'sid={fallback_sid}, order_id={order_id}, item_id={item_id}'
                        )
                    elif recent_order:
                        fallback_order_id = recent_order.get('order_id')
                        fallback_status = recent_order.get('order_status') or 'unknown'
                        if sid_match_type == 'already_processed':
                            logger.info(
                                f'[{msg_time}] 【{self.cookie_id}】ℹ️ 订单ID提取失败，但sid命中的订单已处理完成，跳过重复发货: '
                                f'sid={fallback_sid}, order_id={fallback_order_id}, status={fallback_status}'
                            )
                        elif sid_match_type == 'cancelled':
                            logger.info(
                                f'[{msg_time}] 【{self.cookie_id}】ℹ️ 订单ID提取失败，但sid命中的订单已关闭，跳过自动发货: '
                                f'sid={fallback_sid}, order_id={fallback_order_id}'
                            )
                        else:
                            logger.info(
                                f'[{msg_time}] 【{self.cookie_id}】ℹ️ 订单ID提取失败，但sid命中的订单当前状态不适合兜底发货，等待后续完整消息: '
                                f'sid={fallback_sid}, order_id={fallback_order_id}, status={fallback_status}'
                            )
                        return
                    else:
                        logger.warning(
                            f'[{msg_time}] 【{self.cookie_id}】❌ 未能提取到订单ID，sid兜底也未命中待发货订单，跳过自动发货 '
                            f'(sid={fallback_sid})'
                        )
                        self._record_delivery_log(
                            item_id=item_id,
                            buyer_id=send_user_id,
                            buyer_nick=send_user_name,
                            status='failed',
                            reason=f'未能提取订单ID且sid未命中待发货订单: sid={fallback_sid}',
                            channel='auto'
                        )
                        return
                else:
                    logger.warning(f'[{msg_time}] 【{self.cookie_id}】❌ 未能提取到订单ID且无可用sid，跳过自动发货')
                    self._record_delivery_log(
                        item_id=item_id,
                        buyer_id=send_user_id,
                        buyer_nick=send_user_name,
                        status='failed',
                        reason='未能提取到订单ID且无可用sid，跳过自动发货',
                        channel='auto'
                    )
                    return

            # 订单ID已提取，将在自动发货时进行确认发货处理
            # 防串单：对直接提取/兜底后的订单进行一致性校验
            try:
                existing_order = db_manager.get_order_by_id(order_id)
            except Exception as order_check_e:
                logger.error(f'[{msg_time}] 【{self.cookie_id}】查询订单一致性校验失败: {self._safe_str(order_check_e)}')
                existing_order = None

            if existing_order:
                existing_buyer_id = existing_order.get('buyer_id')
                existing_item_id = existing_order.get('item_id')

                if send_user_id and existing_buyer_id and self._is_trustworthy_buyer_id(existing_buyer_id) and str(send_user_id) != str(existing_buyer_id):
                    logger.warning(
                        f'[{msg_time}] 【{self.cookie_id}】❌ 订单与当前会话买家不一致，拒绝自动发货: '
                        f'order_id={order_id}, send_user_id={send_user_id}, order_buyer_id={existing_buyer_id}'
                    )
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=send_user_id,
                        buyer_nick=send_user_name,
                        status='failed',
                        reason='订单与当前会话买家不一致，拒绝自动发货',
                        channel='auto'
                    )
                    return

                if item_id and item_id != "未知商品" and existing_item_id and str(item_id) != str(existing_item_id):
                    logger.warning(
                        f'[{msg_time}] 【{self.cookie_id}】❌ 订单与当前会话商品不一致，拒绝自动发货: '
                        f'order_id={order_id}, message_item_id={item_id}, order_item_id={existing_item_id}'
                    )
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=send_user_id,
                        buyer_nick=send_user_name,
                        status='failed',
                        reason='订单与当前会话商品不一致，拒绝自动发货',
                        channel='auto'
                    )
                    return

                if (not item_id or item_id == "未知商品") and existing_item_id:
                    item_id = existing_item_id
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】订单一致性校验补全商品ID: {item_id}')

            logger.info(f'[{msg_time}] 【{self.cookie_id}】提取到订单ID: {order_id}，将在自动发货时处理确认发货')

            # 使用订单ID作为锁的键
            lock_key = order_id

            # 第一重检查：延迟锁状态（在获取锁之前检查，避免不必要的等待）
            if self.is_lock_held(lock_key):
                logger.info(f'[{msg_time}] 【{self.cookie_id}】🔒【提前检查】订单 {lock_key} 延迟锁仍在持有状态，跳过发货')
                self._record_delivery_log(
                    order_id=order_id,
                    item_id=item_id,
                    buyer_id=send_user_id,
                    buyer_nick=send_user_name,
                    status='failed',
                    reason='订单延迟锁持有中，跳过发货',
                    channel='auto'
                )
                return

            # 第二重检查：基于时间的冷却机制
            if not self.can_auto_delivery(order_id):
                logger.info(f'[{msg_time}] 【{self.cookie_id}】订单 {order_id} 在冷却期内，跳过发货')
                self._record_delivery_log(
                    order_id=order_id,
                    item_id=item_id,
                    buyer_id=send_user_id,
                    buyer_nick=send_user_name,
                    status='failed',
                    reason='订单在冷却期内，跳过发货',
                    channel='auto'
                )
                return

            # 获取或创建该订单的锁
            order_lock = self._order_locks[lock_key]

            # 更新锁的使用时间
            self._lock_usage_times[lock_key] = time.time()

            # 使用异步锁防止同一订单的并发处理
            async with order_lock:
                logger.info(f'[{msg_time}] 【{self.cookie_id}】获取订单锁成功: {lock_key}，开始处理自动发货')

                # 第三重检查：获取锁后再次检查延迟锁状态（双重检查，防止在等待锁期间状态发生变化）
                if self.is_lock_held(lock_key):
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】订单 {lock_key} 在获取锁后检查发现延迟锁仍持有，跳过发货')
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=send_user_id,
                        buyer_nick=send_user_name,
                        status='failed',
                        reason='获取锁后发现延迟锁仍持有，跳过发货',
                        channel='auto'
                    )
                    return

                # 第四重检查：获取锁后再次检查冷却状态
                if not self.can_auto_delivery(order_id):
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】订单 {order_id} 在获取锁后检查发现仍在冷却期，跳过发货')
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=send_user_id,
                        buyer_nick=send_user_name,
                        status='failed',
                        reason='获取锁后发现订单仍在冷却期，跳过发货',
                        channel='auto'
                    )
                    return

                # 构造用户URL
                user_url = f'https://www.goofish.com/personal?userId={send_user_id}'

                # 自动发货逻辑
                try:
                    # 设置默认标题（将通过API获取真实商品信息）
                    item_title = "待获取商品信息"

                    logger.info(f"【{self.cookie_id}】准备自动发货: item_id={item_id}, item_title={item_title}")

                    # 检查是否需要多数量发货
                    from db_manager import db_manager
                    quantity_to_send = 1  # 默认发送1个

                    # 检查商品是否开启了多数量发货
                    multi_quantity_delivery = db_manager.get_item_multi_quantity_delivery_status(self.cookie_id, item_id)

                    if multi_quantity_delivery and order_id:
                        logger.info(f"商品 {item_id} 开启了多数量发货，获取订单详情...")
                        try:
                            # 使用现有方法获取订单详情
                            order_detail = await self.fetch_order_detail_info(order_id, item_id, send_user_id)
                            if order_detail and order_detail.get('quantity'):
                                try:
                                    order_quantity = int(order_detail['quantity'])
                                    if order_quantity > 1:
                                        quantity_to_send = order_quantity
                                        logger.info(f"从订单详情获取数量: {order_quantity}，将发送 {quantity_to_send} 个卡券")
                                    else:
                                        logger.info(f"订单数量为 {order_quantity}，发送单个卡券")
                                except (ValueError, TypeError):
                                    logger.warning(f"订单数量格式无效: {order_detail.get('quantity')}，发送单个卡券")
                            else:
                                logger.info(f"未获取到订单数量信息，发送单个卡券")
                        except Exception as e:
                            logger.error(f"获取订单详情失败: {self._safe_str(e)}，发送单个卡券")
                    elif not multi_quantity_delivery:
                        logger.info(f"商品 {item_id} 未开启多数量发货，发送单个卡券")
                    else:
                        logger.info(f"无订单ID，发送单个卡券")

                    successful_send_count = 0
                    last_delivery_error = None
                    prepared_units = []

                    for i in range(quantity_to_send):
                        unit_index = i + 1
                        rule_meta = {}
                        try:
                            pending_finalize_meta = self._get_pending_delivery_finalization_meta(order_id, unit_index)
                            if pending_finalize_meta:
                                finalize_result = await self._finalize_delivery_after_send(
                                    delivery_meta=pending_finalize_meta,
                                    order_id=order_id,
                                    item_id=item_id
                                )
                                if not finalize_result.get('success'):
                                    last_delivery_error = finalize_result.get('error') or f"第 {unit_index} 个卡券补完成收尾失败"
                                    self._persist_delivery_finalization_state(
                                        order_id=order_id,
                                        item_id=item_id,
                                        buyer_id=send_user_id,
                                        delivery_meta=pending_finalize_meta,
                                        channel='auto',
                                        status='sent',
                                        last_error=last_delivery_error
                                    )
                                    self._record_delivery_log(
                                        order_id=order_id,
                                        item_id=item_id,
                                        buyer_id=send_user_id,
                                        buyer_nick=send_user_name,
                                        status='failed',
                                        reason=last_delivery_error,
                                        channel='auto',
                                        rule_meta=pending_finalize_meta
                                    )
                                    logger.error(last_delivery_error)
                                    continue

                                self._persist_delivery_finalization_state(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    delivery_meta=pending_finalize_meta,
                                    channel='auto',
                                    status='finalized'
                                )
                                successful_send_count += 1

                                self._record_delivery_log(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    buyer_nick=send_user_name,
                                    status='success',
                                    reason='检测到发货消息已发送，本次补完成收尾成功',
                                    channel='auto',
                                    rule_meta=pending_finalize_meta
                                )
                                continue

                            delivery_result = await self._auto_delivery(
                                item_id,
                                item_title,
                                order_id,
                                send_user_id,
                                chat_id,
                                send_user_name,
                                include_meta=True,
                                delivery_unit_index=unit_index
                            )

                            if isinstance(delivery_result, dict):
                                delivery_content = delivery_result.get('content')
                                delivery_error = delivery_result.get('error')
                                delivery_steps = delivery_result.get('delivery_steps') or []
                                rule_meta = {
                                    'success': True,
                                    'rule_id': delivery_result.get('rule_id'),
                                    'rule_keyword': delivery_result.get('rule_keyword'),
                                    'card_type': delivery_result.get('card_type'),
                                    'match_mode': delivery_result.get('match_mode'),
                                    'order_spec_mode': delivery_result.get('order_spec_mode'),
                                    'rule_spec_mode': delivery_result.get('rule_spec_mode'),
                                    'item_config_mode': delivery_result.get('item_config_mode'),
                                    'card_id': delivery_result.get('card_id'),
                                    'card_description': delivery_result.get('card_description'),
                                    'data_card_pending_consume': delivery_result.get('data_card_pending_consume'),
                                    'data_line': delivery_result.get('data_line'),
                                    'data_reservation_id': delivery_result.get('data_reservation_id'),
                                    'data_reservation_status': delivery_result.get('data_reservation_status'),
                                    'delivery_unit_index': delivery_result.get('delivery_unit_index')
                                }
                            else:
                                delivery_content = delivery_result
                                delivery_error = None
                                delivery_steps = []

                            if not delivery_content:
                                failure_reason = delivery_error or f"第 {unit_index}/{quantity_to_send} 个卡券内容获取失败"
                                last_delivery_error = failure_reason
                                self._record_delivery_log(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    buyer_nick=send_user_name,
                                    status='failed',
                                    reason=failure_reason,
                                    channel='auto',
                                    rule_meta=rule_meta
                                )
                                logger.warning(failure_reason)
                                continue

                            if not delivery_steps:
                                delivery_steps = self._build_delivery_steps(delivery_content, rule_meta.get('card_description', ''))
                            if not delivery_steps:
                                failure_reason = f"第 {unit_index}/{quantity_to_send} 个卡券发货步骤构建失败"
                                last_delivery_error = failure_reason
                                self._release_data_reservation_if_needed(rule_meta, error=failure_reason)
                                self._record_delivery_log(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    buyer_nick=send_user_name,
                                    status='failed',
                                    reason=failure_reason,
                                    channel='auto',
                                    rule_meta=rule_meta
                                )
                                logger.error(failure_reason)
                                continue

                            prepared_units.append({
                                'unit_index': unit_index,
                                'delivery_steps': delivery_steps,
                                'rule_meta': rule_meta,
                                'card_type': rule_meta.get('card_type'),
                            })

                        except Exception as e:
                            self._release_data_reservation_if_needed(rule_meta, error=f'准备发货失败: {self._safe_str(e)}')
                            last_delivery_error = f"准备第 {unit_index}/{quantity_to_send} 个卡券失败: {self._safe_str(e)}"
                            self._record_delivery_log(
                                order_id=order_id,
                                item_id=item_id,
                                buyer_id=send_user_id,
                                buyer_nick=send_user_name,
                                status='failed',
                                reason=last_delivery_error,
                                channel='auto',
                                rule_meta=rule_meta
                            )
                            logger.error(last_delivery_error)

                    send_groups = self._build_delivery_send_groups(prepared_units, quantity_to_send)
                    total_send_groups = len(send_groups)

                    for group_index, send_group in enumerate(send_groups, start=1):
                        group_units = send_group.get('units') or []
                        if not group_units:
                            continue

                        first_unit = group_units[0]
                        single_unit_index = first_unit.get('unit_index') or 1
                        is_batched_text_group = send_group.get('mode') == 'batched_text'

                        if is_batched_text_group:
                            group_log_prefix = (
                                f'[{msg_time}] 多数量自动发货批次 {group_index}/{total_send_groups} '
                                f'({len(group_units)}个单元, {send_group.get("char_count", 0)}字)'
                            )
                        else:
                            group_log_prefix = f'[{msg_time}] 多数量自动发货 {single_unit_index}/{quantity_to_send}'

                        try:
                            await self._send_delivery_steps(
                                websocket,
                                chat_id,
                                send_user_id,
                                send_group.get('delivery_steps') or [],
                                user_url=user_url,
                                log_prefix=group_log_prefix
                            )
                        except Exception as e:
                            group_error = self._safe_str(e)
                            for prepared_unit in group_units:
                                unit_rule_meta = prepared_unit.get('rule_meta') or {}
                                unit_index = prepared_unit.get('unit_index') or 1
                                self._release_data_reservation_if_needed(
                                    unit_rule_meta,
                                    error=f'发送失败(unit={unit_index}): {group_error}'
                                )
                                last_delivery_error = f"发送第 {unit_index}/{quantity_to_send} 个卡券失败: {group_error}"
                                self._record_delivery_log(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    buyer_nick=send_user_name,
                                    status='failed',
                                    reason=last_delivery_error,
                                    channel='auto',
                                    rule_meta=unit_rule_meta
                                )
                                logger.error(last_delivery_error)
                            continue

                        for prepared_unit in group_units:
                            unit_rule_meta = prepared_unit.get('rule_meta') or {}
                            unit_index = prepared_unit.get('unit_index') or 1
                            unit_delivery_steps = prepared_unit.get('delivery_steps') or []

                            try:
                                if not self._mark_data_reservation_sent_if_needed(unit_rule_meta):
                                    self._release_data_reservation_if_needed(
                                        unit_rule_meta,
                                        error=f'发送成功后标记预占已发送失败(unit={unit_index})'
                                    )
                                    last_delivery_error = f'第 {unit_index} 个卡券发送成功后标记预占已发送失败'
                                    self._record_delivery_log(
                                        order_id=order_id,
                                        item_id=item_id,
                                        buyer_id=send_user_id,
                                        buyer_nick=send_user_name,
                                        status='failed',
                                        reason=last_delivery_error,
                                        channel='auto',
                                        rule_meta=unit_rule_meta
                                    )
                                    logger.error(last_delivery_error)
                                    continue

                                self._persist_delivery_finalization_state(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    delivery_meta=unit_rule_meta,
                                    channel='auto',
                                    status='sent'
                                )

                                finalize_result = await self._finalize_delivery_after_send(
                                    delivery_meta=unit_rule_meta,
                                    order_id=order_id,
                                    item_id=item_id
                                )
                                if not finalize_result.get('success'):
                                    last_delivery_error = finalize_result.get('error') or f"第 {unit_index} 条消息发送成功但提交发货副作用失败"
                                    self._persist_delivery_finalization_state(
                                        order_id=order_id,
                                        item_id=item_id,
                                        buyer_id=send_user_id,
                                        delivery_meta=unit_rule_meta,
                                        channel='auto',
                                        status='sent',
                                        last_error=last_delivery_error
                                    )
                                    self._record_delivery_log(
                                        order_id=order_id,
                                        item_id=item_id,
                                        buyer_id=send_user_id,
                                        buyer_nick=send_user_name,
                                        status='failed',
                                        reason=last_delivery_error,
                                        channel='auto',
                                        rule_meta=unit_rule_meta
                                    )
                                    logger.error(last_delivery_error)
                                    continue

                                self._persist_delivery_finalization_state(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    delivery_meta=unit_rule_meta,
                                    channel='auto',
                                    status='finalized'
                                )

                                successful_send_count += 1

                                has_image_step = any(step.get('type') == 'image' for step in unit_delivery_steps)
                                if has_image_step:
                                    success_reason = '自动发货图片步骤发送成功'
                                elif is_batched_text_group and len(group_units) > 1:
                                    success_reason = '自动发货文本批量合并发送成功'
                                else:
                                    success_reason = '自动发货文本发送成功'

                                self._record_delivery_log(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    buyer_nick=send_user_name,
                                    status='success',
                                    reason=success_reason,
                                    channel='auto',
                                    rule_meta=unit_rule_meta
                                )
                            except Exception as unit_post_error:
                                last_delivery_error = f"第 {unit_index} 个卡券消息已发送，但发送后处理异常: {self._safe_str(unit_post_error)}"
                                self._persist_delivery_finalization_state(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    delivery_meta=unit_rule_meta,
                                    channel='auto',
                                    status='sent',
                                    last_error=last_delivery_error
                                )
                                self._record_delivery_log(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    buyer_nick=send_user_name,
                                    status='failed',
                                    reason=last_delivery_error,
                                    channel='auto',
                                    rule_meta=unit_rule_meta
                                )
                                logger.error(last_delivery_error)

                        if total_send_groups > 1 and group_index < total_send_groups:
                            await asyncio.sleep(1)

                    progress_summary = self._sync_order_delivery_progress(
                        order_id=order_id,
                        cookie_id=self.cookie_id,
                        expected_quantity=quantity_to_send,
                        context="自动发货进度同步"
                    ) if order_id else None

                    if progress_summary and progress_summary.get('aggregate_status') in {'partial_success', 'partial_pending_finalize', 'shipped'}:
                        self._activate_delivery_lock(lock_key, delay_minutes=10)

                    if successful_send_count > 0:
                        if progress_summary and quantity_to_send > 1:
                            aggregate_status = progress_summary.get('aggregate_status')
                            finalized_count = progress_summary.get('finalized_count', 0)
                            pending_finalize_count = progress_summary.get('pending_finalize_count', 0)
                            remaining_count = progress_summary.get('remaining_count', 0)

                            if aggregate_status == 'partial_pending_finalize':
                                notify_message = (
                                    f"多数量发货部分完成，已完成 {finalized_count}/{quantity_to_send}，"
                                    f"待收尾 {pending_finalize_count}，待补发 {remaining_count}"
                                )
                            elif aggregate_status == 'partial_success':
                                notify_message = (
                                    f"多数量发货部分成功，已完成 {finalized_count}/{quantity_to_send}，"
                                    f"待补发 {remaining_count}"
                                )
                            else:
                                notify_message = f"多数量发货成功，共完成 {finalized_count}/{quantity_to_send} 个卡券"
                            await self.send_delivery_failure_notification(send_user_name, send_user_id, item_id, notify_message, chat_id)
                        else:
                            await self.send_delivery_failure_notification(send_user_name, send_user_id, item_id, "发货成功", chat_id)
                    else:
                        logger.warning(f'[{msg_time}] 【自动发货】未找到匹配的发货规则或获取发货内容失败')
                        self._record_delivery_log(
                            order_id=order_id,
                            item_id=item_id,
                            buyer_id=send_user_id,
                            buyer_nick=send_user_name,
                            status='failed',
                            reason=last_delivery_error or "未找到匹配的发货规则或获取发货内容失败",
                            channel='auto'
                        )
                        await self.send_delivery_failure_notification(send_user_name, send_user_id, item_id, last_delivery_error or "未找到匹配的发货规则或获取发货内容失败", chat_id)

                except Exception as e:
                    self._record_delivery_log(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=send_user_id,
                        buyer_nick=send_user_name,
                        status='failed',
                        reason=f"自动发货处理异常: {self._safe_str(e)}",
                        channel='auto'
                    )
                    logger.error(f"自动发货处理异常: {self._safe_str(e)}")
                    # 发送自动发货异常通知
                    await self.send_delivery_failure_notification(send_user_name, send_user_id, item_id, f"自动发货处理异常: {str(e)}", chat_id)

                logger.info(f'[{msg_time}] 【{self.cookie_id}】订单锁释放: {lock_key}，自动发货处理完成')

        except Exception as e:
            self._record_delivery_log(
                item_id=item_id,
                buyer_id=send_user_id,
                buyer_nick=send_user_name,
                status='failed',
                reason=f"统一自动发货处理异常: {self._safe_str(e)}",
                channel='auto'
            )
            logger.error(f"统一自动发货处理异常: {self._safe_str(e)}")



    def _reload_latest_cookies_from_db(self, reason: str = "") -> bool:
        """从数据库重载当前账号最新 Cookie。"""
        try:
            from db_manager import db_manager

            account_info = db_manager.get_cookie_details(self.cookie_id)
            new_cookies_str = self._extract_cookie_value(account_info)
            if new_cookies_str and new_cookies_str != self.cookies_str:
                suffix = f" ({reason})" if reason else ""
                logger.info(f"【{self.cookie_id}】检测到数据库中的cookie已更新，重新加载cookie{suffix}")
                self._set_runtime_cookie_state(cookies_str=new_cookies_str, source=f"db_reload{suffix}")
                logger.warning(f"【{self.cookie_id}】Cookie已从数据库重新加载")
                return True
        except Exception as reload_e:
            logger.warning(f"【{self.cookie_id}】从数据库重新加载cookie失败，继续使用当前cookie: {self._safe_str(reload_e)}")
        return False

    def _serialize_cookies(self, cookies_dict: Optional[Dict[str, Any]] = None) -> str:
        cookies = cookies_dict or self.cookies
        return '; '.join([f"{k}={v}" for k, v in cookies.items() if k])

    def _sync_session_cookie_header(self):
        if self.session and not self.session.closed:
            self.session.headers['cookie'] = self.cookies_str

    def _set_runtime_cookie_state(
        self,
        cookies_str: Optional[str] = None,
        cookies_dict: Optional[Dict[str, Any]] = None,
        source: str = "runtime_update",
    ) -> bool:
        normalized_cookies = dict(cookies_dict or trans_cookies(cookies_str or ""))
        if not normalized_cookies:
            logger.warning(f"【{self.cookie_id}】忽略空Cookie更新: source={source}")
            return False

        previous_cookie_string = self.cookies_str
        previous_unb = self.cookies.get('unb') if isinstance(self.cookies, dict) else None

        self.cookies = normalized_cookies
        self.cookies_str = self._serialize_cookies(normalized_cookies)

        new_unb = self.cookies.get('unb')
        if new_unb and new_unb != previous_unb:
            logger.warning(f"【{self.cookie_id}】Cookie中的unb发生变化: {previous_unb} -> {new_unb} (source={source})")
            self.myid = new_unb
            self.device_id = generate_device_id(self.myid)

        self._sync_session_cookie_header()
        return self.cookies_str != previous_cookie_string

    async def _persist_runtime_cookie_state(
        self,
        cookies_str: Optional[str] = None,
        cookies_dict: Optional[Dict[str, Any]] = None,
        source: str = "runtime_update",
    ) -> bool:
        changed = self._set_runtime_cookie_state(
            cookies_str=cookies_str,
            cookies_dict=cookies_dict,
            source=source,
        )
        if changed:
            await self.update_config_cookies()
        return changed

    def _extract_set_cookie_updates(self, response_headers) -> Dict[str, str]:
        if not response_headers:
            return {}

        set_cookie_values = []
        try:
            if hasattr(response_headers, 'getall') and 'set-cookie' in response_headers:
                set_cookie_values = response_headers.getall('set-cookie', [])
            elif hasattr(response_headers, 'get_all'):
                set_cookie_values = response_headers.get_all('set-cookie', [])
            elif isinstance(response_headers, dict):
                raw_value = response_headers.get('set-cookie') or response_headers.get('Set-Cookie')
                if isinstance(raw_value, list):
                    set_cookie_values = raw_value
                elif raw_value:
                    set_cookie_values = [raw_value]
        except Exception:
            set_cookie_values = []

        updates = {}
        for cookie in set_cookie_values:
            if '=' not in cookie:
                continue
            name, value = cookie.split(';')[0].split('=', 1)
            updates[name.strip()] = value.strip()
        return updates

    async def _apply_response_cookie_updates(self, response_headers, source: str) -> bool:
        updates = self._extract_set_cookie_updates(response_headers)
        if not updates:
            return False

        merged_cookies = dict(self.cookies)
        merged_cookies.update(updates)
        changed = await self._persist_runtime_cookie_state(
            cookies_dict=merged_cookies,
            source=source,
        )
        if changed:
            logger.info(f"【{self.cookie_id}】已应用 {len(updates)} 个响应Cookie更新: source={source}")
        return changed

    def _build_websocket_headers(self) -> Dict[str, str]:
        headers = WEBSOCKET_HEADERS.copy()
        headers['Cookie'] = self.cookies_str
        return headers

    def _mark_slider_success_recovery(self, cookies_str: str = ""):
        self.last_slider_success_at = time.time()
        self.last_slider_success_cookie_length = len(cookies_str or "")

    def _build_cookie_string_with_updates(self, base_cookie_string: str = None, updated_cookies: Optional[Dict[str, Any]] = None) -> str:
        merged_cookies = trans_cookies(base_cookie_string or self.cookies_str)
        for key, value in (updated_cookies or {}).items():
            if key:
                merged_cookies[str(key).strip()] = str(value)
        return self._serialize_cookies(merged_cookies)

    def _mark_pending_slider_success_notice(self, source: str = "token_refresh"):
        self.pending_slider_success_notice = {
            'source': source,
            'timestamp': time.time(),
        }

    def _consume_pending_slider_success_notice(self, max_age_seconds: int = 180) -> Optional[Dict[str, Any]]:
        notice = self.pending_slider_success_notice
        self.pending_slider_success_notice = None
        if not notice:
            return None

        notice_timestamp = float(notice.get('timestamp') or 0)
        if notice_timestamp and (time.time() - notice_timestamp) <= max_age_seconds:
            return notice

        logger.info(f"【{self.cookie_id}】检测到过期的滑块成功待发送通知，已自动丢弃")
        return None

    def _clear_pending_slider_success_notice(self, reason: str = None):
        if self.pending_slider_success_notice:
            suffix = f" ({reason})" if reason else ""
            logger.info(f"【{self.cookie_id}】已清理滑块成功待发送通知{suffix}")
        self.pending_slider_success_notice = None

    def _build_x5_cookie_snapshot(self, cookie_string: str = None, cookies_dict: dict = None) -> Dict[str, Dict[str, Any]]:
        source_dict = cookies_dict if cookies_dict is not None else trans_cookies(cookie_string or self.cookies_str)
        snapshot = {}
        for key in ('x5sec', 'x5secdata'):
            value = source_dict.get(key)
            snapshot[key] = {
                'present': bool(value),
                'length': len(str(value)) if value else 0,
                'hash': hashlib.sha256(str(value).encode('utf-8')).hexdigest()[:12] if value else None,
            }
        return snapshot

    def _log_x5_cookie_snapshot(self, label: str, cookie_string: str = None, cookies_dict: dict = None):
        snapshot = self._build_x5_cookie_snapshot(cookie_string=cookie_string, cookies_dict=cookies_dict)
        parts = []
        for key, info in snapshot.items():
            if info.get('present'):
                parts.append(f"{key}=存在(len={info['length']}, sha={info['hash']})")
            else:
                parts.append(f"{key}=缺失")
        logger.info(f"【{self.cookie_id}】{label}: {', '.join(parts)}")

    @classmethod
    def protected_merge_cookie_dicts(cls, existing_cookies_dict, incoming_cookies_dict):
        """保护性合并 Cookie，避免不完整快照覆盖关键会话字段。"""
        existing = dict(existing_cookies_dict or {})
        incoming = dict(incoming_cookies_dict or {})
        existing_count = len(existing)
        incoming_count = len(incoming)
        existing_unb = str(existing.get('unb') or '').strip()
        incoming_unb = str(incoming.get('unb') or '').strip()
        account_switched = bool(existing_unb and incoming_unb and existing_unb != incoming_unb)

        if account_switched:
            merged = incoming.copy()
        else:
            merged = existing.copy()
            for key, value in incoming.items():
                merged[key] = value

        updated_fields = []
        changed_fields = []
        new_fields = []
        for key, value in incoming.items():
            old_value = existing.get(key)
            if old_value is None:
                updated_fields.append(f"{key}(新增)")
                new_fields.append(key)
            elif old_value != value:
                updated_fields.append(key)
                changed_fields.append(key)

        would_remove_fields = [key for key in existing.keys() if key not in incoming]
        if account_switched:
            removed_fields = list(would_remove_fields)
            preserved_fields = []
            preserved_protected_fields = []
        else:
            removed_fields = []
            preserved_fields = list(would_remove_fields)
            preserved_protected_fields = [
                key for key in would_remove_fields
                if key in PROTECTED_SESSION_COOKIE_FIELDS and existing.get(key)
            ]

        missing_protected_fields = [
            key for key in PROTECTED_SESSION_COOKIE_FIELDS
            if not merged.get(key)
        ]
        missing_required_fields = [
            key for key in REQUIRED_SESSION_COOKIE_FIELDS
            if not merged.get(key)
        ]
        incoming_missing_protected_fields = [
            key for key in PROTECTED_SESSION_COOKIE_FIELDS
            if not incoming.get(key)
        ]
        incoming_missing_required_fields = [
            key for key in REQUIRED_SESSION_COOKIE_FIELDS
            if not incoming.get(key)
        ]

        return {
            'existing_cookies_dict': existing,
            'incoming_cookies_dict': incoming,
            'merged_cookies_dict': merged,
            'existing_count': existing_count,
            'incoming_count': incoming_count,
            'merged_count': len(merged),
            'updated_fields': updated_fields,
            'changed_fields': changed_fields,
            'new_fields': new_fields,
            'would_remove_fields': would_remove_fields,
            'removed_fields': removed_fields,
            'preserved_fields': preserved_fields,
            'preserved_protected_fields': preserved_protected_fields,
            'missing_protected_fields': missing_protected_fields,
            'missing_required_fields': missing_required_fields,
            'incoming_missing_protected_fields': incoming_missing_protected_fields,
            'incoming_missing_required_fields': incoming_missing_required_fields,
            'account_switched': account_switched,
        }

    def _merge_cookie_dicts(self, incoming_cookies_dict, existing_cookies_dict=None):
        """兼容旧调用，返回保护性合并结果。"""
        merge_result = self.protected_merge_cookie_dicts(
            existing_cookies_dict if existing_cookies_dict is not None else trans_cookies(self.cookies_str),
            incoming_cookies_dict,
        )
        return (
            merge_result['existing_cookies_dict'],
            merge_result['merged_cookies_dict'],
            merge_result['updated_fields'],
            merge_result['changed_fields'],
            merge_result['new_fields'],
        )

    def _log_protected_merge_event(self, event_name: str, merge_result: Dict[str, Any]):
        """输出受保护 Cookie 合并审计日志，便于定位快照覆盖问题。"""
        if not merge_result:
            return

        protected_preserved_fields = merge_result.get('preserved_protected_fields') or []
        would_remove_fields = merge_result.get('would_remove_fields') or []
        logger.info(
            f"【{self.cookie_id}】{event_name} "
            f"incoming_count={merge_result.get('incoming_count', 0)} "
            f"existing_count={merge_result.get('existing_count', 0)} "
            f"merged_count={merge_result.get('merged_count', 0)} "
            f"protected_preserved_fields={protected_preserved_fields} "
            f"would_remove_fields={would_remove_fields} "
            f"account_switched={merge_result.get('account_switched', False)}"
        )

    def _log_cookie_merge_summary(self, merged_cookies_dict, updated_fields, changed_fields, new_fields, context: str,
                                  preserved_fields=None, preserved_protected_fields=None,
                                  would_remove_fields=None, removed_fields=None,
                                  missing_protected_fields=None, missing_required_fields=None,
                                  incoming_missing_protected_fields=None, account_switched: bool = False):
        """打印 Cookie 合并结果，重点关注会话关键字段。"""
        context_prefix = f"{context}：" if context else ""
        logger.info(f"【{self.cookie_id}】{context_prefix}合并后cookies包含 {len(merged_cookies_dict)} 个字段")

        if updated_fields:
            logger.info(f"【{self.cookie_id}】{context_prefix}更新的cookie字段: {', '.join(updated_fields)}")
        else:
            logger.info(f"【{self.cookie_id}】{context_prefix}没有cookie字段需要更新")

        if account_switched:
            logger.warning(f"【{self.cookie_id}】{context_prefix}检测到unb变化，按账号切换处理，不保留旧账号Cookie字段")

        if preserved_protected_fields:
            logger.warning(
                f"【{self.cookie_id}】{context_prefix}保护性保留关键字段 ({len(preserved_protected_fields)}个): {', '.join(preserved_protected_fields)}"
            )
        if preserved_fields:
            logger.info(
                f"【{self.cookie_id}】{context_prefix}保留旧Cookie字段 ({len(preserved_fields)}个): {', '.join(preserved_fields)}"
            )
        if would_remove_fields:
            logger.info(
                f"【{self.cookie_id}】{context_prefix}浏览器快照未返回的旧字段 ({len(would_remove_fields)}个): {', '.join(would_remove_fields)}"
            )
        if removed_fields:
            logger.warning(
                f"【{self.cookie_id}】{context_prefix}实际移除旧字段 ({len(removed_fields)}个): {', '.join(removed_fields)}"
            )
        if incoming_missing_protected_fields:
            logger.warning(
                f"【{self.cookie_id}】{context_prefix}新快照缺失的关键字段 ({len(incoming_missing_protected_fields)}个): {', '.join(incoming_missing_protected_fields)}"
            )
        if missing_protected_fields:
            logger.warning(
                f"【{self.cookie_id}】{context_prefix}合并后仍缺失的受保护字段 ({len(missing_protected_fields)}个): {', '.join(missing_protected_fields)}"
            )
        if missing_required_fields:
            logger.error(
                f"【{self.cookie_id}】{context_prefix}合并后仍缺失的核心字段 ({len(missing_required_fields)}个): {', '.join(missing_required_fields)}"
            )

        important_keys = list(PROTECTED_SESSION_COOKIE_FIELDS) + ['x5sec', 'x5secdata']
        logger.info(f"【{self.cookie_id}】{context_prefix}关键字段检查:")
        for key in important_keys:
            if key in merged_cookies_dict:
                val = merged_cookies_dict[key]
                marker = " [已变化]" if key in changed_fields else " [新增]" if key in new_fields else ""
                logger.info(f"【{self.cookie_id}】  ✅ {key}: {'存在' if val else '为空'} (长度: {len(str(val)) if val else 0}){marker}")
            else:
                logger.info(f"【{self.cookie_id}】  ❌ {key}: 缺失")

    def _has_recent_slider_success(self, window_seconds: int = None) -> bool:
        if not self.last_slider_success_at:
            return False
        window = window_seconds or self.slider_success_reentry_window
        return (time.time() - self.last_slider_success_at) <= window

    async def preflight_token_after_manual_refresh(self) -> str:
        """手动刷新成功后的 token 预检，确认新实例可直接完成初始化。

        🔧 增加重试机制：密码登录获取的 Cookie 可能需要短暂时间在服务端生效，
        首次 Token 刷新可能因 session 未就绪而失败，等待后重试可提高成功率。
        """
        logger.info(f"【{self.cookie_id}】开始执行手动刷新后的Token预检...")
        self.last_message_received_time = 0

        max_preflight_retries = 3
        for attempt in range(1, max_preflight_retries + 1):
            token = await self.refresh_token(allow_password_login_recovery=False)
            if token:
                self.cache_auth_prewarmed_token(self.cookie_id, token, source='manual_refresh_handoff')
                logger.info(f"【{self.cookie_id}】手动刷新后的Token预检成功（第{attempt}次），已缓存预热token供新实例复用")
                return token

            if attempt < max_preflight_retries:
                wait_secs = 2.0 * attempt
                logger.warning(
                    f"【{self.cookie_id}】Token预检第{attempt}次失败（状态: {self.last_token_refresh_status}），"
                    f"等待{wait_secs:.0f}秒后重试（Cookie可能尚未在服务端生效）"
                )
                await asyncio.sleep(wait_secs)

        raise InitAuthError(f"手动刷新后的Token预检失败，状态: {self.last_token_refresh_status or 'unknown'}")

    async def refresh_token(self, captcha_retry_count: int = 0, allow_password_login_recovery: bool = True):
        if self.token_refresh_lock.locked():
            logger.info(f"【{self.cookie_id}】Token刷新已有执行中任务，等待当前流程完成后复用结果")

        async with self.token_refresh_lock:
            if (
                captcha_retry_count == 0 and
                self.current_token and
                self.last_token_refresh_status == "success" and
                (time.time() - self.last_token_refresh_time) < 15
            ):
                logger.info(f"【{self.cookie_id}】最近15秒内已有成功的Token刷新结果，直接复用当前Token")
                return self.current_token
            return await self._refresh_token_impl(
                captcha_retry_count,
                allow_password_login_recovery=allow_password_login_recovery,
            )

    def _is_auth_failure_ret(self, ret_value: Any) -> bool:
        if isinstance(ret_value, str):
            ret_text = ret_value
        elif isinstance(ret_value, (list, tuple)):
            ret_text = ' | '.join([str(item) for item in ret_value])
        else:
            ret_text = str(ret_value or '')

        auth_keywords = (
            '令牌过期',
            'session过期',
            'FAIL_SYS_USER_VALIDATE',
            'FAIL_SYS_TOKEN_EXPIRED',
            'FAIL_SYS_TOKEN_EXOIRED',
            'FAIL_SYS_SESSION_EXPIRED',
            'passport.goofish.com',
            'mini_login',
            'login',
        )
        ret_text_lower = ret_text.lower()
        return any(keyword.lower() in ret_text_lower for keyword in auth_keywords)

    async def keep_session_alive(self) -> bool:
        """使用 loginuser.get 轻量维持网页登录态。"""
        self.last_session_keepalive_status = "started"
        self.last_session_keepalive_error_message = None

        try:
            if not self.session:
                await self.create_session()

            self._reload_latest_cookies_from_db("轻量保活前")

            params = {
                'jsv': '2.7.2',
                'appKey': '34839810',
                't': str(int(time.time() * 1000)),
                'sign': '',
                'v': '1.0',
                'type': 'originaljson',
                'accountSite': 'xianyu',
                'dataType': 'json',
                'timeout': '20000',
                'api': 'mtop.taobao.idlemessage.pc.loginuser.get',
                'sessionOption': 'AutoLoginOnly',
                'spm_cnt': 'a21ybx.im.0.0',
                'spm_pre': 'a21ybx.item.want.1.12523da6waCtUp',
                'log_id': '12523da6waCtUp',
            }
            data_val = '{}'
            data = {'data': data_val}

            token = trans_cookies(self.cookies_str).get('_m_h5_tk', '').split('_')[0] if trans_cookies(self.cookies_str).get('_m_h5_tk') else ''
            params['sign'] = generate_sign(params['t'], token, data_val)

            headers = DEFAULT_HEADERS.copy()
            headers['content-type'] = 'application/x-www-form-urlencoded'
            headers['cookie'] = self.cookies_str

            request_kwargs = {}
            if getattr(self, '_http_proxy_url', None):
                request_kwargs['proxy'] = self._http_proxy_url

            api_url = API_ENDPOINTS.get('login_user')
            async with self.session.post(
                api_url,
                params=params,
                data=data,
                headers=headers,
                **request_kwargs,
            ) as response:
                try:
                    res_json = await response.json(content_type=None)
                except Exception:
                    response_text = await response.text()
                    self.last_session_keepalive_status = "response_parse_failed"
                    self.last_session_keepalive_error_message = response_text[:200]
                    logger.warning(f"【{self.cookie_id}】轻量保活响应解析失败: {response_text[:200]}")
                    return False

                await self._apply_response_cookie_updates(response.headers, "session_keepalive")
                ret_value = res_json.get('ret', [])
                if any('SUCCESS::调用成功' in str(ret) for ret in ret_value):
                    self.last_session_keepalive_status = "success"
                    self.last_session_keepalive_error_message = None
                    self.last_session_keepalive_time = time.time()
                    logger.info(f"【{self.cookie_id}】轻量会话保活成功")
                    return True

                error_message = ' | '.join([str(ret) for ret in ret_value]) or '未知错误'
                self.last_session_keepalive_error_message = error_message
                self.last_session_keepalive_status = "auth_failed" if self._is_auth_failure_ret(ret_value) else "api_failed"
                logger.warning(f"【{self.cookie_id}】轻量会话保活失败: {error_message}")
                return False

        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            self.last_session_keepalive_status = "network_failed"
            self.last_session_keepalive_error_message = self._safe_str(e)
            logger.warning(f"【{self.cookie_id}】轻量会话保活网络异常: {self._safe_str(e)}")
            return False
        except Exception as e:
            self.last_session_keepalive_status = "exception"
            self.last_session_keepalive_error_message = self._safe_str(e)
            logger.error(f"【{self.cookie_id}】轻量会话保活异常: {self._safe_str(e)}")
            return False

    async def _refresh_token_impl(self, captcha_retry_count: int = 0, post_slider_session_grace_used: bool = False,
                                  allow_password_login_recovery: bool = True,
                                  manual_refresh_browser_stabilization_used: bool = False,
                                  post_slider_session_retry_count: int = 0):
        """刷新token

        Args:
            captcha_retry_count: 滑块验证重试次数，用于防止无限递归
        """
        # 初始化通知发送标志，避免重复发送通知
        notification_sent = False
        
        try:
            logger.info(f"【{self.cookie_id}】开始刷新token... (滑块验证重试次数: {captcha_retry_count})")
            # 标记本次刷新状态
            self.last_token_refresh_status = "started"
            self.last_token_refresh_error_message = None
            # 重置“刷新流程内已重启”标记，避免多次重启
            self.restarted_in_browser_refresh = False

            # 检查滑块验证重试次数，防止无限递归
            if captcha_retry_count >= self.max_captcha_verification_count:
                logger.error(f"【{self.cookie_id}】滑块验证重试次数已达上限 ({self.max_captcha_verification_count})，停止重试")
                self.last_token_refresh_status = "captcha_max_retries_exceeded"
                self._clear_pending_slider_success_notice("滑块重试次数达到上限")
                await self.send_token_refresh_notification(
                    f"滑块验证重试次数已达上限，请手动处理",
                    "captcha_max_retries_exceeded"
                )
                notification_sent = True
                return None

            # 【消息接收检查】检查是否在消息接收后的冷却时间内，与 cookie_refresh_loop 保持一致
            current_time = time.time()
            time_since_last_message = current_time - self.last_message_received_time
            if self.last_message_received_time > 0 and time_since_last_message < self.message_cookie_refresh_cooldown:
                remaining_time = self.message_cookie_refresh_cooldown - time_since_last_message
                remaining_minutes = int(remaining_time // 60)
                remaining_seconds = int(remaining_time % 60)
                logger.info(f"【{self.cookie_id}】收到消息后冷却中，放弃本次token刷新，还需等待 {remaining_minutes}分{remaining_seconds}秒")
                # 标记为因冷却而跳过（正常情况）
                self.last_token_refresh_status = "skipped_cooldown"
                return None

            # 【重要】在刷新token前，先从数据库重新加载最新的cookie
            # 这样即使用户已经手动更新了cookie，代码也会使用最新的cookie
            logger.info(f"【{self.cookie_id}】开始执行Cookie刷新任务...")
            self._reload_latest_cookies_from_db("token刷新前")

            # 生成更精确的时间戳
            timestamp = str(int(time.time() * 1000))

            params = {
                'jsv': '2.7.2',
                'appKey': '34839810',
                't': timestamp,
                'sign': '',
                'v': '1.0',
                'type': 'originaljson',
                'accountSite': 'xianyu',
                'dataType': 'json',
                'timeout': '20000',
                'api': 'mtop.taobao.idlemessage.pc.login.token',
                'sessionOption': 'AutoLoginOnly',
                'dangerouslySetWindvaneParams': '%5Bobject%20Object%5D',
                'smToken': 'token',
                'queryToken': 'sm',
                'sm': 'sm',
                'spm_cnt': 'a21ybx.im.0.0',
                'spm_pre': 'a21ybx.home.sidebar.1.4c053da6vYwnmf',
                'log_id': '4c053da6vYwnmf'
            }
            data_val = '{"appKey":"444e9908a51d1cb236a27862abc769c9","deviceId":"' + self.device_id + '"}'
            data = {
                'data': data_val,
            }

            # 获取token
            token = trans_cookies(self.cookies_str).get('_m_h5_tk', '').split('_')[0] if trans_cookies(self.cookies_str).get('_m_h5_tk') else ''

            sign = generate_sign(params['t'], token, data_val)
            params['sign'] = sign

            # 发送请求 - 使用与浏览器完全一致的请求头
            headers = {
                'accept': 'application/json',
                'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
                'cache-control': 'no-cache',
                'content-type': 'application/x-www-form-urlencoded',
                'pragma': 'no-cache',
                'priority': 'u=1, i',
                'sec-ch-ua': '"Not;A=Brand";v="99", "Google Chrome";v="139", "Chromium";v="139"',
                'sec-ch-ua-mobile': '?0',
                'sec-ch-ua-platform': '"Windows"',
                'sec-fetch-dest': 'empty',
                'sec-fetch-mode': 'cors',
                'sec-fetch-site': 'same-site',
                'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
                'referer': 'https://www.goofish.com/',
                'origin': 'https://www.goofish.com',
                'cookie': self.cookies_str
            }

            # 发送Token刷新请求
            api_url = API_ENDPOINTS.get('token')
            logger.info(f"【{self.cookie_id}】正在刷新Token... API: {api_url}")
            
            # 详细调试信息（仅debug级别）
            logger.debug(f"【{self.cookie_id}】Token刷新参数: timestamp={params['t']}, sign={sign[:16]}...")

            async with aiohttp.ClientSession() as session:
                async with session.post(
                    api_url,
                    params=params,
                    data=data,
                    headers=headers,
                    timeout=aiohttp.ClientTimeout(total=30)
                ) as response:
                    res_json = await response.json()
                    # 简化日志输出
                    ret_info = res_json.get('ret', [])
                    logger.debug(f"【{self.cookie_id}】Token刷新响应: status={response.status}, ret={ret_info}")

                    response_set_cookies = self._extract_set_cookie_updates(response.headers)

                    transient_recovery_cookies_str = self.cookies_str
                    if response_set_cookies:
                        transient_recovery_cookies_str = self._build_cookie_string_with_updates(
                            self.cookies_str,
                            response_set_cookies
                        )
                        logger.info(
                            f"【{self.cookie_id}】Token预检响应携带 {len(response_set_cookies)} 个临时Cookie，"
                            f"仅用于本次恢复链路，不提前写入数据库"
                        )

                    if isinstance(res_json, dict):
                        ret_value = res_json.get('ret', [])
                        # 检查ret是否包含成功信息
                        if any('SUCCESS::调用成功' in ret for ret in ret_value):
                            if 'data' in res_json and 'accessToken' in res_json['data']:
                                if response_set_cookies:
                                    await self._apply_response_cookie_updates(response.headers, "token_refresh")
                                    logger.warning(f"【{self.cookie_id}】Token刷新成功后已更新Cookie到数据库")

                                new_token = res_json['data']['accessToken']
                                self.current_token = new_token
                                self.last_token_refresh_time = time.time()

                                # 【消息接收时间重置】Token刷新成功后重置消息接收标志，与 cookie_refresh_loop 保持一致
                                self.last_message_received_time = 0
                                logger.warning(f"【{self.cookie_id}】Token刷新成功，已重置消息接收时间标识")
                                self.clear_qr_login_grace(self.cookie_id)
                                self.clear_init_auth_failure_state(self.cookie_id)
                                self.last_init_failure_reason = None
                                self.last_init_failure_type = None
                                self.init_auth_failures = 0

                                logger.info(f"【{self.cookie_id}】Token刷新成功")
                                # 标记为成功
                                self.last_token_refresh_status = "success"
                                self.last_token_refresh_error_message = None
                                if self._consume_pending_slider_success_notice():
                                    await self.send_token_refresh_notification(
                                        "滑块验证通过，账号会话已恢复",
                                        "slider_recovered_success"
                                    )
                                return new_token

                    # 检查是否需要滑块验证
                    if self._need_captcha_verification(res_json):
                        qr_login_grace = self.get_qr_login_grace(self.cookie_id)
                        if qr_login_grace and not qr_login_grace.get('captcha_buffer_used'):
                            logger.warning(f"【{self.cookie_id}】扫码登录后的首轮Token刷新命中风控，先执行浏览器侧Cookie稳定化")
                            log_captcha_event(
                                self.cookie_id,
                                "扫码登录首轮Token刷新命中风控，先执行浏览器侧稳定化",
                                None,
                                f"触发场景: Token刷新, ret={res_json.get('ret', [])}"
                            )
                            self.update_qr_login_grace(
                                self.cookie_id,
                                captcha_buffer_used=True,
                                captcha_detected_at=time.time()
                            )
                            await asyncio.sleep(2)
                            stabilization_success = await self._refresh_cookies_via_browser_page(
                                transient_recovery_cookies_str,
                                restart_on_success=False
                            )
                            if stabilization_success:
                                self.update_qr_login_grace(
                                    self.cookie_id,
                                    browser_stabilized=True,
                                    browser_stabilized_at=time.time()
                                )
                                logger.info(f"【{self.cookie_id}】浏览器侧Cookie稳定化完成，重新尝试Token刷新")
                                return await self._refresh_token_impl(
                                    captcha_retry_count,
                                    post_slider_session_grace_used=post_slider_session_grace_used,
                                    allow_password_login_recovery=allow_password_login_recovery,
                                    manual_refresh_browser_stabilization_used=manual_refresh_browser_stabilization_used,
                                    post_slider_session_retry_count=post_slider_session_retry_count,
                                )
                            logger.warning(f"【{self.cookie_id}】浏览器侧Cookie稳定化未消除风控，继续进入滑块验证")

                        manual_refresh_state = self.get_manual_refresh_state(self.cookie_id)
                        is_manual_refresh_handoff = bool(
                            manual_refresh_state and manual_refresh_state.get('phase') == 'handoff_recovery'
                        )
                        if is_manual_refresh_handoff and not manual_refresh_browser_stabilization_used:
                            logger.warning(f"【{self.cookie_id}】手动刷新交接阶段首轮Token预检命中风控，先执行浏览器侧Cookie稳定化")
                            log_captcha_event(
                                self.cookie_id,
                                "手动刷新交接阶段首轮Token预检命中风控，先执行浏览器侧稳定化",
                                None,
                                f"触发场景: Token刷新, ret={res_json.get('ret', [])}"
                            )
                            before_x5_snapshot = self._build_x5_cookie_snapshot(cookie_string=transient_recovery_cookies_str)
                            self._log_x5_cookie_snapshot("手动刷新交接稳定化前的x5票据", cookie_string=transient_recovery_cookies_str)
                            self.last_token_refresh_status = "manual_refresh_browser_stabilizing"
                            stabilization_success = await self._refresh_cookies_via_browser_page(
                                transient_recovery_cookies_str,
                                restart_on_success=False
                            )
                            if stabilization_success:
                                self._reload_latest_cookies_from_db("手动刷新交接阶段浏览器稳定化")
                                after_x5_snapshot = self._build_x5_cookie_snapshot()
                                self._log_x5_cookie_snapshot("手动刷新交接稳定化后的x5票据")
                                changed_x5_fields = [
                                    key for key in ('x5sec', 'x5secdata')
                                    if before_x5_snapshot.get(key, {}).get('hash') != after_x5_snapshot.get(key, {}).get('hash')
                                ]
                                if changed_x5_fields:
                                    logger.info(
                                        f"【{self.cookie_id}】手动刷新交接阶段浏览器稳定化已更新x5票据: {', '.join(changed_x5_fields)}"
                                    )
                                else:
                                    logger.info(f"【{self.cookie_id}】手动刷新交接阶段浏览器稳定化未观察到x5票据变化，继续重试Token预检")
                                return await self._refresh_token_impl(
                                    captcha_retry_count,
                                    post_slider_session_grace_used=post_slider_session_grace_used,
                                    allow_password_login_recovery=allow_password_login_recovery,
                                    manual_refresh_browser_stabilization_used=True,
                                    post_slider_session_retry_count=post_slider_session_retry_count,
                                )
                            logger.warning(f"【{self.cookie_id}】手动刷新交接阶段浏览器稳定化失败，继续进入滑块验证")

                        if self.is_manual_refresh_active(self.cookie_id, allow_handoff_recovery=True):
                            logger.warning(f"【{self.cookie_id}】检测到手动刷新进行中，跳过自动滑块处理")
                            log_captcha_event(
                                self.cookie_id,
                                "手动刷新进行中，跳过自动滑块处理",
                                None,
                                "触发场景: Token刷新"
                            )
                            self.last_token_refresh_status = "manual_refresh_active"
                            self._clear_pending_slider_success_notice("手动刷新进行中")
                            notification_sent = True
                            return None

                        logger.warning(f"【{self.cookie_id}】检测到需要滑块验证，开始处理...")

                        # 记录滑块验证检测到日志文件
                        verification_url = res_json.get('data', {}).get('url', 'Token刷新时检测')
                        log_captcha_event(self.cookie_id, "检测到滑块验证", None, f"触发场景: Token刷新, URL: {verification_url}")
                        captcha_trigger_scene = 'token_refresh'
                        captcha_session_id = self._new_risk_session_id('slider')
                        captcha_event_meta = self._build_risk_event_meta(
                            trigger_scene=captcha_trigger_scene,
                            verification_url=verification_url,
                            extra={'cookie_id': self.cookie_id}
                        )

                        # 添加风控日志记录
                        log_id = None
                        try:
                            log_id = self._create_risk_log(
                                event_type='slider_captcha',
                                session_id=captcha_session_id,
                                trigger_scene=captcha_trigger_scene,
                                result_code='slider_captcha_detected',
                                event_description='检测到滑块验证（Token刷新）',
                                processing_status='processing',
                                event_meta=captcha_event_meta,
                            )
                            if log_id:
                                logger.info(f"【{self.cookie_id}】风控日志记录成功，ID: {log_id}")
                        except Exception as log_e:
                            logger.error(f"【{self.cookie_id}】记录风控日志失败: {log_e}")

                        try:
                            # 尝试通过滑块验证获取新的cookies
                            captcha_start_time = time.time()
                            new_cookies_str = await self._handle_captcha_verification(res_json)
                            captcha_duration = time.time() - captcha_start_time

                            if new_cookies_str:
                                logger.info(f"【{self.cookie_id}】滑块验证成功，准备重启实例...")

                                # 更新风控日志为成功状态
                                if 'log_id' in locals() and log_id:
                                    self._update_risk_log(
                                        log_id,
                                        session_id=captcha_session_id,
                                        trigger_scene=captcha_trigger_scene,
                                        result_code='slider_captcha_success',
                                        processing_result='滑块验证成功，已获取新Cookie',
                                        processing_status='success',
                                        duration_ms=max(0, int(captcha_duration * 1000)),
                                        event_meta=self._build_risk_event_meta(
                                            trigger_scene=captcha_trigger_scene,
                                            verification_url=verification_url,
                                            extra={
                                                'cookie_id': self.cookie_id,
                                                'cookie_length': len(new_cookies_str),
                                            },
                                        ),
                                    )

                                # 重启实例（cookies已在_handle_captcha_verification中更新到数据库）
                                # await self._restart_instance()

                                # 给浏览器回写票据与数据库落盘留一个稳定窗口，避免刚过块就立即重新命中Session过期
                                settle_delay = random.uniform(*self.post_slider_token_retry_delay)
                                logger.info(
                                    f"【{self.cookie_id}】滑块成功后进入稳定窗口 {settle_delay:.2f}s，再重新尝试Token刷新"
                                )
                                await asyncio.sleep(settle_delay)
                                self._reload_latest_cookies_from_db("滑块成功后的稳定窗口")
                                log_captcha_event(
                                    self.cookie_id,
                                    "滑块成功后重新进入Token刷新",
                                    None,
                                    f"类型: token_reentry_after_slider_success, captcha_retry_count={captcha_retry_count + 1}"
                                )

                                # 重新尝试刷新token（递归调用，但有深度限制）
                                return await self._refresh_token_impl(
                                    captcha_retry_count + 1,
                                    post_slider_session_grace_used=False,
                                    allow_password_login_recovery=allow_password_login_recovery,
                                    manual_refresh_browser_stabilization_used=manual_refresh_browser_stabilization_used,
                                    post_slider_session_retry_count=0,
                                )
                            else:
                                logger.error(f"【{self.cookie_id}】滑块验证失败")

                                # 更新风控日志为失败状态
                                if 'log_id' in locals() and log_id:
                                    self._update_risk_log(
                                        log_id,
                                        session_id=captcha_session_id,
                                        trigger_scene=captcha_trigger_scene,
                                        result_code='slider_captcha_failed',
                                        processing_result='滑块验证失败，未获取到新Cookie',
                                        processing_status='failed',
                                        error_message='未获取到新Cookie',
                                        duration_ms=max(0, int(captcha_duration * 1000)),
                                        event_meta=self._build_risk_event_meta(
                                            trigger_scene=captcha_trigger_scene,
                                            verification_url=verification_url,
                                            extra={'cookie_id': self.cookie_id},
                                        ),
                                    )
                                
                                # 标记已处理，避免后续再发送通用失败通知
                                notification_sent = True
                        except Exception as captcha_e:
                            logger.error(f"【{self.cookie_id}】滑块验证处理异常: {self._safe_str(captcha_e)}")
                            self._clear_pending_slider_success_notice("滑块验证处理异常")

                            # 更新风控日志为异常状态
                            captcha_duration = time.time() - captcha_start_time if 'captcha_start_time' in locals() else 0
                            if 'log_id' in locals() and log_id:
                                self._update_risk_log(
                                    log_id,
                                    session_id=captcha_session_id,
                                    trigger_scene=captcha_trigger_scene,
                                    result_code='slider_captcha_exception',
                                    processing_result='滑块验证处理异常',
                                    processing_status='failed',
                                    error_message=str(captcha_e)[:200],
                                    duration_ms=max(0, int(captcha_duration * 1000)),
                                    event_meta=self._build_risk_event_meta(
                                        trigger_scene=captcha_trigger_scene,
                                        verification_url=verification_url,
                                        extra={'cookie_id': self.cookie_id},
                                    ),
                                )
                            
                            # 标记已处理，避免后续再发送通用失败通知
                            notification_sent = True

                    # 检查是否包含"令牌过期"或"Session过期"
                    if isinstance(res_json, dict):
                        res_json_str = json.dumps(res_json, ensure_ascii=False, separators=(',', ':'))
                        if '令牌过期' in res_json_str or 'Session过期' in res_json_str:
                            # 记录令牌/Session过期到风控日志
                            token_expired_log_id = None
                            token_expired_session_id = self._new_risk_session_id('token')
                            token_expired_started_at = time.time()
                            token_trigger_scene = 'token_refresh'
                            expire_type = '令牌过期' if '令牌过期' in res_json_str else 'Session过期'
                            try:
                                from db_manager import db_manager
                                stale_count = db_manager.mark_stale_risk_control_logs_failed(timeout_minutes=15, cookie_id=self.cookie_id)
                                if stale_count > 0:
                                    logger.warning(f"【{self.cookie_id}】检测到{stale_count}条超时processing风控日志，已自动标记failed")
                                token_expired_log_id = self._create_risk_log(
                                    event_type='token_expired',
                                    session_id=token_expired_session_id,
                                    trigger_scene=token_trigger_scene,
                                    result_code='token_expired_detected',
                                    event_description=f"检测到{expire_type}",
                                    processing_status='processing',
                                    event_meta=self._build_risk_event_meta(
                                        trigger_scene=token_trigger_scene,
                                        extra={'expire_type': expire_type, 'cookie_id': self.cookie_id},
                                    ),
                                )
                            except Exception as log_e:
                                logger.error(f"【{self.cookie_id}】记录风控日志失败: {log_e}")

                            # 调用统一的密码登录刷新方法
                            if self.is_manual_refresh_active(self.cookie_id, allow_handoff_recovery=True):
                                logger.warning(f"【{self.cookie_id}】检测到手动刷新进行中，跳过自动密码登录刷新")
                                if token_expired_log_id:
                                    self._update_risk_log(
                                        token_expired_log_id,
                                        session_id=token_expired_session_id,
                                        trigger_scene=token_trigger_scene,
                                        result_code='manual_refresh_active',
                                        processing_status='failed',
                                        error_message='检测到手动刷新进行中，自动刷新已跳过',
                                        duration_ms=max(0, int((time.time() - token_expired_started_at) * 1000)),
                                        event_meta=self._build_risk_event_meta(
                                            trigger_scene=token_trigger_scene,
                                            extra={'cookie_id': self.cookie_id, 'expire_type': expire_type},
                                        ),
                                    )
                                self.last_token_refresh_status = "manual_refresh_active"
                                self._clear_pending_slider_success_notice("手动刷新进行中")
                                notification_sent = True
                                return None

                            recent_slider_success = self._has_recent_slider_success()
                            max_post_slider_session_retries = 2

                            if recent_slider_success and not post_slider_session_grace_used:
                                grace_delay = random.uniform(1.2, 2.2)
                                logger.warning(
                                    f"【{self.cookie_id}】检测到最近 {self.slider_success_reentry_window}s 内刚通过滑块，"
                                    f"先等待 {grace_delay:.2f}s 并重载Cookie后再试一次Token刷新"
                                )
                                log_captcha_event(
                                    self.cookie_id,
                                    "滑块成功后Session过期，优先重试Token刷新",
                                    None,
                                    f"类型: token_retry_after_recent_slider_success, expire_type={expire_type}"
                                )
                                await asyncio.sleep(grace_delay)
                                self._reload_latest_cookies_from_db("滑块成功后的Session过期缓冲")
                                return await self._refresh_token_impl(
                                    captcha_retry_count,
                                    post_slider_session_grace_used=True,
                                    allow_password_login_recovery=allow_password_login_recovery,
                                    manual_refresh_browser_stabilization_used=manual_refresh_browser_stabilization_used,
                                    post_slider_session_retry_count=post_slider_session_retry_count,
                                )

                            if (
                                recent_slider_success and
                                not allow_password_login_recovery and
                                post_slider_session_retry_count < max_post_slider_session_retries
                            ):
                                settle_retry_attempt = post_slider_session_retry_count + 1
                                settle_delay = random.uniform(2.4, 3.6) + ((settle_retry_attempt - 1) * 0.8)
                                logger.warning(
                                    f"【{self.cookie_id}】预检模式下滑块成功后仍返回{expire_type}，"
                                    f"执行第{settle_retry_attempt}/{max_post_slider_session_retries}次稳定重试，"
                                    f"等待 {settle_delay:.2f}s 后再次尝试Token刷新"
                                )
                                log_captcha_event(
                                    self.cookie_id,
                                    "滑块成功后Session仍未稳定，继续重试Token刷新",
                                    None,
                                    f"类型: token_settle_retry_after_slider, expire_type={expire_type}, "
                                    f"attempt={settle_retry_attempt}/{max_post_slider_session_retries}"
                                )
                                self.last_token_refresh_status = "post_slider_session_settling"
                                await asyncio.sleep(settle_delay)
                                self._reload_latest_cookies_from_db(
                                    f"滑块成功后的第{settle_retry_attempt}次Session稳定重试"
                                )
                                return await self._refresh_token_impl(
                                    captcha_retry_count,
                                    post_slider_session_grace_used=True,
                                    allow_password_login_recovery=allow_password_login_recovery,
                                    manual_refresh_browser_stabilization_used=manual_refresh_browser_stabilization_used,
                                    post_slider_session_retry_count=settle_retry_attempt,
                                )

                            refresh_success = False
                            if allow_password_login_recovery:
                                refresh_success = await self._try_password_login_refresh(
                                    "令牌/Session过期",
                                    risk_session_id=token_expired_session_id,
                                    trigger_scene=token_trigger_scene,
                                    ignore_slider_failed_backoff=recent_slider_success,
                                )
                            else:
                                self.last_token_refresh_status = (
                                    "session_expired_after_slider"
                                    if recent_slider_success else
                                    "session_expired_preflight"
                                )
                                self.last_token_refresh_error_message = f"Token预检返回{expire_type}"
                                logger.warning(f"【{self.cookie_id}】当前为预检模式，跳过密码登录恢复，直接返回Token刷新失败")
                            
                            if token_expired_log_id:
                                self._update_risk_log(
                                    token_expired_log_id,
                                    session_id=token_expired_session_id,
                                    trigger_scene=token_trigger_scene,
                                    result_code='token_refresh_recovered' if refresh_success else 'token_refresh_recovery_failed',
                                    processing_status='success' if refresh_success else 'failed',
                                    processing_result='令牌/Session过期触发自动刷新成功，已进入重试流程' if refresh_success else None,
                                    error_message=None if refresh_success else '令牌/Session过期触发自动刷新失败',
                                    duration_ms=max(0, int((time.time() - token_expired_started_at) * 1000)),
                                    event_meta=self._build_risk_event_meta(
                                        trigger_scene=token_trigger_scene,
                                        extra={'cookie_id': self.cookie_id, 'expire_type': expire_type},
                                    ),
                                )
                            
                            if not refresh_success:
                                if allow_password_login_recovery:
                                    self.last_token_refresh_status = "token_expired_recovery_failed"
                                self._clear_pending_slider_success_notice("恢复流程失败")
                                # 标记已发送通知，避免重复通知
                                notification_sent = True
                                # 返回None，让调用者知道刷新失败
                                return None
                            else:
                                # 刷新成功后，重新尝试获取token
                                return await self._refresh_token_impl(
                                    captcha_retry_count,
                                    post_slider_session_grace_used=False,
                                    allow_password_login_recovery=allow_password_login_recovery,
                                    manual_refresh_browser_stabilization_used=manual_refresh_browser_stabilization_used,
                                    post_slider_session_retry_count=0,
                                )
                                
                                # 刷新失败时继续执行原有的失败处理逻辑

                    if self.last_token_refresh_status in (None, "started"):
                        self.last_token_refresh_status = "token_refresh_failed"
                    self.last_token_refresh_error_message = json.dumps(res_json, ensure_ascii=False, separators=(',', ':'))
                    self._clear_pending_slider_success_notice("Token刷新最终失败")
                    logger.error(f"【{self.cookie_id}】Token刷新失败: {res_json}")

                    # 清空当前token，确保下次重试时重新获取
                    self.current_token = None

                    # 只有在没有发送过通知的情况下才发送Token刷新失败通知
                    # 并且WebSocket未连接时才发送（已连接说明只是暂时失败）
                    if not notification_sent:
                        # 检查WebSocket连接状态
                        is_ws_connected = (
                            self.connection_state == ConnectionState.CONNECTED and 
                            self.ws and 
                            not self.ws.closed
                        )
                        
                        if is_ws_connected:
                            logger.info(f"【{self.cookie_id}】WebSocket连接正常，Token刷新失败可能是暂时的，跳过失败通知")
                        else:
                            logger.warning(f"【{self.cookie_id}】WebSocket未连接，发送Token刷新失败通知")
                            await self.send_token_refresh_notification(f"Token刷新失败: {res_json}", "token_refresh_failed")
                    else:
                        logger.info(f"【{self.cookie_id}】已发送滑块验证相关通知，跳过Token刷新失败通知")
                    return None

        except Exception as e:
            self.last_token_refresh_status = "token_refresh_exception"
            self.last_token_refresh_error_message = self._safe_str(e)
            self._clear_pending_slider_success_notice("Token刷新异常")
            logger.error(f"Token刷新异常: {self._safe_str(e)}")

            # 清空当前token，确保下次重试时重新获取
            self.current_token = None

            # 只有在没有发送过通知的情况下才发送Token刷新异常通知
            # 并且WebSocket未连接时才发送（已连接说明只是暂时失败）
            if not notification_sent:
                # 检查WebSocket连接状态
                is_ws_connected = (
                    self.connection_state == ConnectionState.CONNECTED and 
                    self.ws and 
                    not self.ws.closed
                )
                
                if is_ws_connected:
                    logger.info(f"【{self.cookie_id}】WebSocket连接正常，Token刷新异常可能是暂时的，跳过失败通知")
                else:
                    logger.warning(f"【{self.cookie_id}】WebSocket未连接，发送Token刷新异常通知")
                    await self.send_token_refresh_notification(f"Token刷新异常: {str(e)}", "token_refresh_exception")
            else:
                logger.info(f"【{self.cookie_id}】已发送滑块验证相关通知，跳过Token刷新异常通知")
            return None

    def _need_captcha_verification(self, res_json: dict) -> bool:
        """检查响应是否需要滑块验证"""
        try:
            if not isinstance(res_json, dict):
                return False

            # 记录res_json内容到日志文件
            import json
            res_json_str = json.dumps(res_json, ensure_ascii=False, separators=(',', ':'))
            log_captcha_event(self.cookie_id, "检查滑块验证响应", None, f"res_json内容: {res_json_str}")

            # 检查返回的错误信息
            ret_value = res_json.get('ret', [])
            if not ret_value:
                return False

            # 检查是否包含需要验证的关键词
            captcha_keywords = [
                'FAIL_SYS_USER_VALIDATE',  # 用户验证失败
                'RGV587_ERROR',            # 风控错误
                '哎哟喂,被挤爆啦',          # 被挤爆了
                '哎哟喂，被挤爆啦',         # 被挤爆了（中文逗号）
                '挤爆了',                  # 挤爆了
                '请稍后重试',              # 请稍后重试
                'punish?x5secdata',        # 惩罚页面
                'captcha',                 # 验证码
            ]

            error_msg = str(ret_value[0]) if ret_value else ''

            # 检查错误信息是否包含需要验证的关键词
            for keyword in captcha_keywords:
                if keyword in error_msg:
                    logger.info(f"【{self.cookie_id}】检测到需要滑块验证的关键词: {keyword}")
                    return True

            # 检查data字段中是否包含验证URL
            data = res_json.get('data', {})
            if isinstance(data, dict) and 'url' in data:
                url = data.get('url', '')
                if 'punish' in url or 'captcha' in url or 'validate' in url:
                    logger.info(f"【{self.cookie_id}】检测到验证URL: {url}")
                    return True

            return False

        except Exception as e:
            logger.error(f"【{self.cookie_id}】检查是否需要滑块验证时出错: {self._safe_str(e)}")
            return False

    async def _handle_captcha_verification(self, res_json: dict) -> str:
        """处理滑块验证，返回新的cookies字符串"""
        try:
            logger.info(f"【{self.cookie_id}】开始处理滑块验证...")

            if self.is_manual_refresh_active(self.cookie_id, allow_handoff_recovery=True):
                logger.warning(f"【{self.cookie_id}】手动刷新进行中，取消自动滑块处理")
                log_captcha_event(
                    self.cookie_id,
                    "手动刷新进行中，取消自动滑块处理",
                    None,
                    "自动滑块处理已跳过"
                )
                return None

            # 获取验证URL
            verification_url = None

            # 从data字段获取URL
            data = res_json.get('data', {})
            if isinstance(data, dict) and 'url' in data:
                verification_url = data.get('url')

            # 如果没有找到URL，使用默认的验证页面
            if not verification_url:
                logger.info(f"【{self.cookie_id}】未找到验证URL，认为不需要滑块验证，返回正常")
                return None

            logger.info(f"【{self.cookie_id}】验证URL: {verification_url}")

            # 使用滑块验证器（独立实例，解决并发冲突）
            try:
                # 使用集成的滑块验证方法（无需猴子补丁）
                from utils.xianyu_slider_stealth import XianyuSliderStealth
                logger.info(f"【{self.cookie_id}】XianyuSliderStealth导入成功，使用滑块验证")

                # 创建独立的滑块验证实例（每个用户独立实例，避免并发冲突）
                slider_stealth = XianyuSliderStealth(
                    # user_id=f"{self.cookie_id}_{int(time.time() * 1000)}",  # 使用唯一ID避免冲突
                    user_id=f"{self.cookie_id}",  # 使用唯一ID避免冲突
                    enable_learning=True,  # 启用学习功能
                    headless=True  # 使用有头模式（可视化浏览器）
                )

                # 直接使用异步方法执行滑块验证（避免 ThreadPoolExecutor 导致的 Playwright 初始化问题）
                success, cookies = await slider_stealth.async_run(verification_url)

                if success and cookies:
                    logger.info(f"【{self.cookie_id}】滑块验证成功，获取到新的cookies")

                    current_cookies_dict = trans_cookies(self.cookies_str)
                    x5sec_cookies = {}

                    # 筛选出x5相关的cookies（包括x5sec, x5step等）
                    for cookie_name, cookie_value in cookies.items():
                        cookie_name_lower = cookie_name.lower()
                        if cookie_name_lower.startswith('x5') or 'x5sec' in cookie_name_lower:
                            x5sec_cookies[cookie_name] = cookie_value

                    logger.info(f"【{self.cookie_id}】找到{len(x5sec_cookies)}个x5相关cookies: {list(x5sec_cookies.keys())}")

                    merge_result = self.protected_merge_cookie_dicts(current_cookies_dict, cookies)
                    updated_cookies = merge_result['merged_cookies_dict']
                    updated_fields = merge_result['updated_fields']
                    changed_fields = merge_result['changed_fields']
                    new_fields = merge_result['new_fields']
                    removed_fields = merge_result['removed_fields']
                    preserved_fields = merge_result['preserved_fields']
                    preserved_protected_fields = merge_result['preserved_protected_fields']
                    would_remove_fields = merge_result['would_remove_fields']
                    missing_protected_fields = merge_result['missing_protected_fields']
                    missing_required_fields = merge_result['missing_required_fields']
                    incoming_missing_protected_fields = merge_result['incoming_missing_protected_fields']
                    account_switched = merge_result['account_switched']
                    cookies_str = "; ".join([f"{k}={v}" for k, v in updated_cookies.items()])
                    qr_login_grace = self.get_qr_login_grace(self.cookie_id)
                    merge_event_name = "slider_post_qr_protected_merge" if qr_login_grace else "captcha_protected_merge"
                    self._log_protected_merge_event(merge_event_name, merge_result)

                    self._log_cookie_merge_summary(
                        updated_cookies,
                        updated_fields,
                        changed_fields,
                        new_fields,
                        context="滑块验证成功后Cookie合并",
                        preserved_fields=preserved_fields,
                        preserved_protected_fields=preserved_protected_fields,
                        would_remove_fields=would_remove_fields,
                        removed_fields=removed_fields,
                        missing_protected_fields=missing_protected_fields,
                        missing_required_fields=missing_required_fields,
                        incoming_missing_protected_fields=incoming_missing_protected_fields,
                        account_switched=account_switched,
                    )

                    if missing_required_fields:
                        logger.error(f"【{self.cookie_id}】滑块验证后的Cookie仍缺失核心字段，放弃写回数据库: {', '.join(missing_required_fields)}")
                        return None

                    # 自动更新数据库中的cookie
                    try:
                        # 备份原有cookies
                        old_cookies_str = self.cookies_str
                        old_cookies_dict = self.cookies.copy()

                        # 更新当前实例的cookies（使用合并后的cookies）
                        self._set_runtime_cookie_state(
                            cookies_str=cookies_str,
                            cookies_dict=updated_cookies,
                            source="slider_success",
                        )

                        # 更新数据库中的cookies
                        await self.update_config_cookies()
                        logger.info(f"【{self.cookie_id}】滑块验证成功后，数据库cookies已自动更新")
                        self._mark_slider_success_recovery(cookies_str)
                        self._mark_pending_slider_success_notice("token_refresh")
                        XianyuLive.clear_password_login_failure_backoff(self.cookie_id)
                        logger.info(f"【{self.cookie_id}】滑块验证成功后，已清理密码登录失败退避状态")

                        # 记录成功更新到日志文件，包含关键字段变化和x5相关cookie信息
                        x5sec_cookies_str = "; ".join([f"{k}={v}" for k, v in x5sec_cookies.items()]) if x5sec_cookies else "无"
                        log_captcha_event(self.cookie_id, "滑块验证成功并自动更新数据库", True,
                            f"原有{len(current_cookies_dict)}个cookie项, 浏览器快照{len(cookies)}个, 合并后{len(updated_cookies)}个, 变更字段{len(changed_fields)}个, 新增字段{len(new_fields)}个, 保护保留{len(preserved_protected_fields)}个, 实际移除{len(removed_fields)}个, x5 cookies: {x5sec_cookies_str}")

                    except Exception as update_e:
                        logger.error(f"【{self.cookie_id}】自动更新数据库cookies失败: {self._safe_str(update_e)}")

                        # 回滚cookies
                        self._set_runtime_cookie_state(
                            cookies_str=old_cookies_str,
                            cookies_dict=old_cookies_dict,
                            source="slider_success_rollback",
                        )

                        # 记录更新失败到日志文件，包含获取到的x5 cookies
                        x5sec_cookies_str = "; ".join([f"{k}={v}" for k, v in x5sec_cookies.items()]) if x5sec_cookies else "无"
                        log_captcha_event(self.cookie_id, "滑块验证成功但数据库更新失败", False,
                            f"更新异常: {self._safe_str(update_e)[:100]}, 变更字段{len(changed_fields)}个, 新增字段{len(new_fields)}个, 保护保留{len(preserved_protected_fields)}个, 获取到的x5 cookies: {x5sec_cookies_str}")

                        # 发送更新失败通知
                        await self.send_token_refresh_notification(
                            f"滑块验证成功但数据库更新失败: {self._safe_str(update_e)}",
                            "captcha_success_db_update_failed"
                        )

                        return None

                    return cookies_str
                else:
                    logger.error(f"【{self.cookie_id}】滑块验证失败")

                    # 记录滑块验证失败到日志文件
                    log_captcha_event(self.cookie_id, "滑块验证失败", False,
                        f"XianyuSliderStealth执行失败, 环境: {'Docker' if os.getenv('DOCKER_ENV') else '本地'}")

                    # 发送通知（检查WebSocket连接状态）
                    # 只有在WebSocket未连接时才发送通知，已连接说明可能是暂时性问题
                    is_ws_connected = (
                        self.connection_state == ConnectionState.CONNECTED and 
                        self.ws and 
                        not self.ws.closed
                    )
                    
                    if is_ws_connected:
                        logger.info(f"【{self.cookie_id}】WebSocket连接正常，滑块验证失败可能是暂时的，跳过通知")
                    else:
                        logger.warning(f"【{self.cookie_id}】WebSocket未连接，发送滑块验证失败通知")
                        await self.send_token_refresh_notification(
                            f"滑块验证失败，需要手动处理。验证URL: {verification_url}",
                            "captcha_verification_failed"
                        )
                    return None

            except ImportError as import_e:
                logger.error(f"【{self.cookie_id}】XianyuSliderStealth导入失败: {import_e}")
                logger.error(f"【{self.cookie_id}】请安装Playwright库: pip install playwright")

                # 记录导入失败到日志文件
                log_captcha_event(self.cookie_id, "XianyuSliderStealth导入失败", False,
                    f"Playwright未安装, 错误: {import_e}")

                # 发送通知
                await self.send_token_refresh_notification(
                    f"滑块验证功能不可用，请安装Playwright。验证URL: {verification_url}",
                    "captcha_dependency_missing"
                )
                return None

            except Exception as stealth_e:
                logger.error(f"【{self.cookie_id}】滑块验证异常: {self._safe_str(stealth_e)}")

                # 记录异常到日志文件
                log_captcha_event(self.cookie_id, "滑块验证异常", False,
                    f"执行异常, 错误: {self._safe_str(stealth_e)[:100]}")

                # 发送通知（检查WebSocket连接状态）
                # 只有在WebSocket未连接时才发送通知，已连接说明可能是暂时性问题
                is_ws_connected = (
                    self.connection_state == ConnectionState.CONNECTED and 
                    self.ws and 
                    not self.ws.closed
                )
                
                if is_ws_connected:
                    logger.info(f"【{self.cookie_id}】WebSocket连接正常，滑块验证执行异常可能是暂时的，跳过通知")
                else:
                    logger.warning(f"【{self.cookie_id}】WebSocket未连接，发送滑块验证执行异常通知")
                    await self.send_token_refresh_notification(
                        f"滑块验证执行异常，需要手动处理。验证URL: {verification_url}",
                        "captcha_execution_error"
                    )
                return None



        except Exception as e:
            logger.error(f"【{self.cookie_id}】处理滑块验证时出错: {self._safe_str(e)}")
            return None

    async def _update_cookies_and_restart(self, new_cookies_str: str):
        """更新cookies并重启任务"""
        try:
            logger.info(f"【{self.cookie_id}】开始更新cookies并重启任务...")

            # 验证新cookies的有效性
            if not new_cookies_str or not new_cookies_str.strip():
                logger.error(f"【{self.cookie_id}】新cookies为空，无法更新")
                return False

            # 解析新cookies，确保格式正确
            try:
                new_cookies_dict = trans_cookies(new_cookies_str)
                if not new_cookies_dict:
                    logger.error(f"【{self.cookie_id}】新cookies解析失败，无法更新")
                    return False
                logger.info(f"【{self.cookie_id}】新cookies解析成功，包含 {len(new_cookies_dict)} 个字段")
            except Exception as parse_e:
                logger.error(f"【{self.cookie_id}】新cookies解析异常: {self._safe_str(parse_e)}")
                return False

            # 合并cookies：保留原有cookies，只更新新获取到的字段
            try:
                merge_result = self.protected_merge_cookie_dicts(trans_cookies(self.cookies_str), new_cookies_dict)
                merged_cookies_dict = merge_result['merged_cookies_dict']
                updated_fields = merge_result['updated_fields']
                changed_fields = merge_result['changed_fields']
                new_fields = merge_result['new_fields']
                self._log_protected_merge_event("password_refresh_protected_merge", merge_result)

                self._log_cookie_merge_summary(
                    merged_cookies_dict,
                    updated_fields,
                    changed_fields,
                    new_fields,
                    context="密码登录刷新Cookie",
                    preserved_fields=merge_result['preserved_fields'],
                    preserved_protected_fields=merge_result['preserved_protected_fields'],
                    would_remove_fields=merge_result['would_remove_fields'],
                    removed_fields=merge_result['removed_fields'],
                    missing_protected_fields=merge_result['missing_protected_fields'],
                    missing_required_fields=merge_result['missing_required_fields'],
                    incoming_missing_protected_fields=merge_result['incoming_missing_protected_fields'],
                    account_switched=merge_result['account_switched'],
                )

                if merge_result['missing_required_fields']:
                    logger.error(
                        f"【{self.cookie_id}】密码登录刷新后的Cookie仍缺失核心字段，放弃写回并重启: {', '.join(merge_result['missing_required_fields'])}"
                    )
                    return False

                # 使用合并后的cookies字符串
                new_cookies_str = '; '.join([f"{k}={v}" for k, v in merged_cookies_dict.items()])
                new_cookies_dict = merged_cookies_dict

            except Exception as merge_e:
                logger.error(f"【{self.cookie_id}】cookies合并异常: {self._safe_str(merge_e)}")
                logger.warning(f"【{self.cookie_id}】将使用原始新cookies（不合并）")
                # 如果合并失败，继续使用原始的new_cookies_str

            # 备份原有cookies，以防更新失败需要回滚
            old_cookies_str = self.cookies_str
            old_cookies_dict = self.cookies.copy()

            try:
                # 更新当前实例的cookies
                self._set_runtime_cookie_state(
                    cookies_str=new_cookies_str,
                    cookies_dict=new_cookies_dict,
                    source="password_login_refresh",
                )

                # 更新数据库中的cookies
                await self.update_config_cookies()
                logger.info(f"【{self.cookie_id}】数据库cookies更新成功")

                # ⚠️ 在重启前完成所有需要的操作（如发送通知）
                # 因为重启触发后2秒内任务会被取消，不能再执行任何async操作
                logger.info(f"【{self.cookie_id}】cookies更新成功，准备重启任务...")
                
                # 通过CookieManager重启任务
                logger.info(f"【{self.cookie_id}】通过CookieManager触发重启...")
                await self._restart_instance()
                
                # ⚠️ _restart_instance() 已触发重启，当前任务即将被取消
                # 立即返回，不执行任何后续代码（包括发送通知）
                logger.info(f"【{self.cookie_id}】重启请求已触发，等待任务被取消...")
                return True

            except Exception as update_e:
                logger.error(f"【{self.cookie_id}】更新cookies过程中出错，尝试回滚: {self._safe_str(update_e)}")

                # 回滚cookies
                try:
                    self._set_runtime_cookie_state(
                        cookies_str=old_cookies_str,
                        cookies_dict=old_cookies_dict,
                        source="password_login_refresh_rollback",
                    )
                    await self.update_config_cookies()
                    logger.info(f"【{self.cookie_id}】cookies已回滚到原始状态")
                except Exception as rollback_e:
                    logger.error(f"【{self.cookie_id}】cookies回滚失败: {self._safe_str(rollback_e)}")

                return False

        except Exception as e:
            logger.error(f"【{self.cookie_id}】更新cookies并重启任务时出错: {self._safe_str(e)}")
            return False

    async def update_config_cookies(self):
        """更新数据库中的cookies（不会覆盖账号密码等其他字段）"""
        try:
            from db_manager import db_manager

            # 更新数据库中的Cookie
            if hasattr(self, 'cookie_id') and self.cookie_id:
                try:
                    # 获取当前Cookie的用户ID，避免在刷新时改变所有者
                    current_user_id = None
                    if hasattr(self, 'user_id') and self.user_id:
                        current_user_id = self.user_id

                    # 使用 update_cookie_account_info 避免覆盖其他字段（如 username, password, pause_duration, remark 等）
                    # 这个方法会自动处理新账号和现有账号的情况，不会覆盖账号密码
                    success = db_manager.update_cookie_account_info(
                        self.cookie_id, 
                        cookie_value=self.cookies_str,
                        user_id=current_user_id  # 如果是新账号，需要提供user_id
                    )
                    if not success:
                        # 如果更新失败，记录错误但不使用 save_cookie（避免覆盖账号密码）
                        logger.warning(f"更新Cookie到数据库失败: {self.cookie_id}，但不使用save_cookie避免覆盖账号密码")
                    else:
                        logger.warning(f"已更新Cookie到数据库: {self.cookie_id}")
                except Exception as e:
                    logger.error(f"更新数据库Cookie失败: {self._safe_str(e)}")
                    # 发送数据库更新失败通知
                    await self.send_token_refresh_notification(f"数据库Cookie更新失败: {str(e)}", "db_update_failed")
            else:
                logger.warning("Cookie ID不存在，无法更新数据库")
                # 发送Cookie ID缺失通知
                await self.send_token_refresh_notification("Cookie ID不存在，无法更新数据库", "cookie_id_missing")

        except Exception as e:
            logger.error(f"更新Cookie失败: {self._safe_str(e)}")
            # 发送Cookie更新失败通知
            await self.send_token_refresh_notification(f"Cookie更新失败: {str(e)}", "cookie_update_failed")

    async def _try_password_login_refresh(
        self,
        trigger_reason: str = "令牌/Session过期",
        risk_session_id: Optional[str] = None,
        trigger_scene: Optional[str] = None,
        ignore_slider_failed_backoff: bool = False,
    ):
        """尝试通过密码登录刷新Cookie并重启实例
        
        Args:
            trigger_reason: 触发原因，用于日志记录
            
        Returns:
            bool: 是否成功刷新Cookie
        """
        logger.warning(f"【{self.cookie_id}】检测到{trigger_reason}，准备刷新Cookie并重启实例...")
        trigger_scene = trigger_scene or self._normalize_risk_trigger_scene(trigger_reason, default='auto_cookie_refresh')
        risk_session_id = risk_session_id or self._new_risk_session_id('cookie')
        risk_log_started_at = time.time()
        base_event_meta = {'cookie_id': self.cookie_id, 'trigger_reason': trigger_reason}

        # 记录到风控日志
        refresh_risk_log_id = None
        try:
            stale_count = db_manager.mark_stale_risk_control_logs_failed(timeout_minutes=15, cookie_id=self.cookie_id)
            if stale_count > 0:
                logger.warning(f"【{self.cookie_id}】检测到{stale_count}条超时processing风控日志，已自动标记failed")
            refresh_risk_log_id = self._create_risk_log(
                event_type='cookie_refresh',
                session_id=risk_session_id,
                trigger_scene=trigger_scene,
                result_code='cookie_refresh_started',
                event_description=f"{trigger_reason}触发Cookie刷新",
                processing_status='processing',
                event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
            )
        except Exception as log_e:
            logger.error(f"【{self.cookie_id}】记录风控日志失败: {log_e}")

        if self.is_manual_refresh_active(self.cookie_id, allow_handoff_recovery=True):
            logger.warning(f"【{self.cookie_id}】手动刷新进行中，跳过自动密码登录刷新")
            if refresh_risk_log_id:
                self._update_risk_log(
                    refresh_risk_log_id,
                    session_id=risk_session_id,
                    trigger_scene=trigger_scene,
                    result_code='manual_refresh_active',
                    processing_status='failed',
                    error_message='手动刷新进行中，自动密码登录刷新已跳过',
                    duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                    event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
                )
            return False

        recovery_lock_owner = f"{self.cookie_id}:{trigger_scene or 'auto_cookie_refresh'}:{int(time.time() * 1000)}"
        recovery_lock_acquired = False

        # 检查是否在密码登录冷却期内，避免重复登录
        current_time = time.time()
        failure_backoff = XianyuLive.get_password_login_failure_backoff(self.cookie_id)
        if failure_backoff:
            remaining_time = failure_backoff.get('until', 0) - current_time
            if remaining_time > 0:
                backoff_reason = failure_backoff.get('reason', 'unknown')
                if backoff_reason == 'slider_failed' and (
                    ignore_slider_failed_backoff or self.consume_manual_refresh_slider_failed_bypass(self.cookie_id)
                ):
                    logger.warning(
                        f"【{self.cookie_id}】检测到最近刚通过滑块或处于刷新交接恢复窗口，忽略一次旧的 slider_failed 退避并继续尝试密码登录刷新"
                    )
                    XianyuLive.clear_password_login_failure_backoff(self.cookie_id)
                    failure_backoff = None
                else:
                    logger.warning(
                        f"【{self.cookie_id}】密码登录失败退避中（原因: {backoff_reason}），还需等待 {remaining_time:.1f} 秒"
                    )
                    if refresh_risk_log_id:
                        self._update_risk_log(
                            refresh_risk_log_id,
                            session_id=risk_session_id,
                            trigger_scene=trigger_scene,
                            result_code='password_login_backoff',
                            processing_status='failed',
                            error_message=f"密码登录失败退避中，剩余{remaining_time:.1f}秒",
                            duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                            event_meta=self._build_risk_event_meta(
                                trigger_scene=trigger_scene,
                                extra={**base_event_meta, 'backoff_reason': backoff_reason, 'backoff_seconds': failure_backoff.get('seconds')},
                            ),
                        )
                    return False

        last_password_login = XianyuLive._last_password_login_time.get(self.cookie_id, 0)
        time_since_last_login = current_time - last_password_login
        
        if last_password_login > 0 and time_since_last_login < XianyuLive._password_login_cooldown:
            remaining_time = XianyuLive._password_login_cooldown - time_since_last_login
            logger.warning(f"【{self.cookie_id}】距离上次密码登录仅 {time_since_last_login:.1f} 秒，仍在冷却期内（还需等待 {remaining_time:.1f} 秒），跳过密码登录")
            logger.warning(f"【{self.cookie_id}】提示：如果新Cookie仍然无效，请检查账号状态或手动更新Cookie")
            if refresh_risk_log_id:
                self._update_risk_log(
                    refresh_risk_log_id,
                    session_id=risk_session_id,
                    trigger_scene=trigger_scene,
                    result_code='password_login_cooldown',
                    processing_status='failed',
                    error_message=f"密码登录冷却期内，剩余{remaining_time:.1f}秒",
                    duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                    event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
                )
            return False

        recovery_lock_acquired, existing_lock = XianyuLive.acquire_auth_recovery_lock(
            self.cookie_id,
            recovery_lock_owner,
        )
        if not recovery_lock_acquired:
            existing_owner = (existing_lock or {}).get('owner', 'unknown')
            logger.warning(f"【{self.cookie_id}】认证恢复流程已在执行中，跳过本次重复触发: owner={existing_owner}")
            if refresh_risk_log_id:
                self._update_risk_log(
                    refresh_risk_log_id,
                    session_id=risk_session_id,
                    trigger_scene=trigger_scene,
                    result_code='auth_recovery_in_progress',
                    processing_status='failed',
                    error_message='已有认证恢复流程执行中',
                    duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                    event_meta=self._build_risk_event_meta(
                        trigger_scene=trigger_scene,
                        extra={**base_event_meta, 'active_owner': existing_owner},
                    ),
                )
            return False

        # 记录到日志文件
        log_captcha_event(self.cookie_id, f"{trigger_reason}触发Cookie刷新和实例重启", None,
            f"检测到{trigger_reason}，准备刷新Cookie并重启实例")

        try:
            # 从数据库获取账号登录信息
            account_info = db_manager.get_cookie_details(self.cookie_id)

            if not account_info:
                logger.error(f"【{self.cookie_id}】无法获取账号信息")
                self.last_token_refresh_error_message = "无法获取账号信息"
                if refresh_risk_log_id:
                    self._update_risk_log(
                        refresh_risk_log_id,
                        session_id=risk_session_id,
                        trigger_scene=trigger_scene,
                        result_code='account_info_missing',
                        processing_status='failed',
                        error_message='无法获取账号信息',
                        duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                        event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
                    )
                return False

            # 【重要】先检查数据库中的cookie是否已经更新
            # 如果用户已经手动更新了cookie，就不需要触发密码登录刷新
            db_cookie_value = account_info.get('cookie_value', '')
            if db_cookie_value and db_cookie_value != self.cookies_str:
                logger.info(f"【{self.cookie_id}】检测到数据库中的cookie已更新，重新加载cookie")
                self._set_runtime_cookie_state(cookies_str=db_cookie_value, source="db_cookie_reload_before_password_login")
                logger.info(f"【{self.cookie_id}】Cookie已从数据库重新加载，跳过密码登录刷新")
                if refresh_risk_log_id:
                    self._update_risk_log(
                        refresh_risk_log_id,
                        session_id=risk_session_id,
                        trigger_scene=trigger_scene,
                        result_code='cookie_already_updated',
                        processing_status='success',
                        processing_result='检测到数据库Cookie已更新，自动刷新流程跳过',
                        duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                        event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
                    )
                return True
            
            username = account_info.get('username', '')
            password = account_info.get('password', '')
            show_browser = account_info.get('show_browser', False)
            
            # 检查是否配置了用户名和密码
            if not username or not password:
                logger.warning(f"【{self.cookie_id}】未配置用户名或密码，跳过密码登录刷新")
                self.last_token_refresh_error_message = "未配置用户名或密码，无法自动刷新Cookie"
                await self.send_token_refresh_notification(
                    f"检测到{trigger_reason}，但未配置用户名或密码，无法自动刷新Cookie",
                    "no_credentials"
                )
                if refresh_risk_log_id:
                    self._update_risk_log(
                        refresh_risk_log_id,
                        session_id=risk_session_id,
                        trigger_scene=trigger_scene,
                        result_code='missing_credentials',
                        processing_status='failed',
                        error_message='未配置用户名或密码，无法自动刷新Cookie',
                        duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                        event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
                    )
                return False
            
            # 使用集成的 Playwright 登录方法（无需猴子补丁）
            from utils.xianyu_slider_stealth import XianyuSliderStealth
            browser_mode = "有头" if show_browser else "无头"
            logger.info(f"【{self.cookie_id}】开始使用{browser_mode}浏览器进行密码登录刷新Cookie...")
            logger.info(f"【{self.cookie_id}】使用账号: {username}")
            
            # 创建一个通知回调包装函数，支持接收截图路径和验证链接
            async def notification_callback_wrapper(message: str, screenshot_path: str = None, verification_url: str = None):
                """通知回调包装函数，支持接收截图路径和验证链接"""
                await self.send_token_refresh_notification(
                    error_message=message,
                    notification_type="token_refresh",
                    chat_id=None,
                    attachment_path=screenshot_path,
                    verification_url=verification_url
                )
            
            # 在单独的线程中运行同步的登录方法
            import asyncio
            slider = XianyuSliderStealth(user_id=self.cookie_id, enable_learning=True, headless=not show_browser)
            slider.risk_session_id = risk_session_id
            slider.risk_trigger_scene = trigger_scene
            result = await asyncio.to_thread(
                slider.login_with_password_playwright,
                account=username,
                password=password,
                show_browser=show_browser,
                notification_callback=notification_callback_wrapper,
                force_clean_context=True,
            )
            
            if result:
                logger.info(f"【{self.cookie_id}】密码登录成功，获取到Cookie")
                logger.info(f"【{self.cookie_id}】Cookie内容: {result}")
                XianyuLive.clear_password_login_failure_backoff(self.cookie_id)
                
                # 打印密码登录获取的Cookie字段详情
                logger.info(f"【{self.cookie_id}】========== 密码登录Cookie字段详情 ==========")
                logger.info(f"【{self.cookie_id}】Cookie字段数: {len(result)}")
                logger.info(f"【{self.cookie_id}】Cookie字段列表:")
                for i, (key, value) in enumerate(result.items(), 1):
                    if len(str(value)) > 50:
                        logger.info(f"【{self.cookie_id}】  {i:2d}. {key}: {str(value)[:30]}...{str(value)[-20:]} (长度: {len(str(value))})")
                    else:
                        logger.info(f"【{self.cookie_id}】  {i:2d}. {key}: {value}")
                
                # 检查关键字段
                important_keys = ['unb', '_m_h5_tk', '_m_h5_tk_enc', 'cookie2', 't', 'sgcookie', 'cna']
                logger.info(f"【{self.cookie_id}】关键字段检查:")
                for key in important_keys:
                    if key in result:
                        val = result[key]
                        logger.info(f"【{self.cookie_id}】  ✅ {key}: {'存在' if val else '为空'} (长度: {len(str(val)) if val else 0})")
                    else:
                        logger.info(f"【{self.cookie_id}】  ❌ {key}: 缺失")
                logger.info(f"【{self.cookie_id}】==========================================")
                
                # 将cookie字典转换为字符串格式
                new_cookies_str = '; '.join([f"{k}={v}" for k, v in result.items()])
                logger.info(f"【{self.cookie_id}】Cookie字符串摘要: {self._summarize_cookie_string(new_cookies_str)}")
                
                # 记录密码登录时间，防止重复登录
                XianyuLive._last_password_login_time[self.cookie_id] = time.time()
                logger.warning(f"【{self.cookie_id}】已记录密码登录时间，冷却期 {XianyuLive._password_login_cooldown} 秒")
                
                # ⚠️ 先发送通知，再更新cookies并重启任务
                # 因为重启后当前任务会被取消，不能在重启后发送通知
                try:
                    await self.send_token_refresh_notification(
                        f"账号密码登录成功，Cookie已获取，准备更新并重启",
                        "cookie_refresh_success"
                    )
                except Exception as notify_e:
                    logger.warning(f"【{self.cookie_id}】发送通知失败: {self._safe_str(notify_e)}")
                
                # 更新cookies并重启任务
                update_success = await self._update_cookies_and_restart(new_cookies_str)
                
                if update_success:
                    logger.info(f"【{self.cookie_id}】Cookie更新并重启任务成功")
                    # 更新风控日志状态为成功
                    if refresh_risk_log_id:
                        self._update_risk_log(
                            refresh_risk_log_id,
                            session_id=risk_session_id,
                            trigger_scene=trigger_scene,
                            result_code='cookie_refresh_success',
                            processing_status='success',
                            processing_result='密码登录刷新Cookie成功，实例已重启',
                            duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                            event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
                        )
                    return True
                else:
                    logger.error(f"【{self.cookie_id}】Cookie更新失败")
                    if refresh_risk_log_id:
                        self._update_risk_log(
                            refresh_risk_log_id,
                            session_id=risk_session_id,
                            trigger_scene=trigger_scene,
                            result_code='cookie_save_failed',
                            processing_status='failed',
                            error_message='Cookie获取成功但更新到数据库失败',
                            duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                            event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
                        )
                    return False
                    
            else:
                login_error = getattr(slider, 'last_login_error', '') or "密码登录失败，未获取到Cookie"
                self.last_token_refresh_error_message = login_error
                backoff_reason, backoff_seconds = XianyuLive.classify_password_login_failure(login_error)
                XianyuLive.set_password_login_failure_backoff(self.cookie_id, backoff_reason, backoff_seconds)
                logger.warning(f"【{self.cookie_id}】密码登录失败，未获取到Cookie: {login_error}")
                logger.warning(f"【{self.cookie_id}】已进入失败退避期: {backoff_reason}, {backoff_seconds}秒")
                if refresh_risk_log_id:
                    self._update_risk_log(
                        refresh_risk_log_id,
                        session_id=risk_session_id,
                        trigger_scene=trigger_scene,
                        result_code=f'password_login_{backoff_reason}',
                        processing_status='failed',
                        error_message=login_error[:200],
                        duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                        event_meta=self._build_risk_event_meta(
                            trigger_scene=trigger_scene,
                            extra={**base_event_meta, 'backoff_reason': backoff_reason, 'backoff_seconds': backoff_seconds},
                        ),
                    )
                return False

        except Exception as refresh_e:
            self.last_token_refresh_error_message = self._safe_str(refresh_e)
            backoff_reason, backoff_seconds = XianyuLive.classify_password_login_failure(str(refresh_e))
            XianyuLive.set_password_login_failure_backoff(self.cookie_id, backoff_reason, backoff_seconds)
            logger.error(f"【{self.cookie_id}】Cookie刷新或实例重启失败: {self._safe_str(refresh_e)}")
            import traceback
            logger.error(f"【{self.cookie_id}】详细堆栈:\n{traceback.format_exc()}")
            if refresh_risk_log_id:
                self._update_risk_log(
                    refresh_risk_log_id,
                    session_id=risk_session_id,
                    trigger_scene=trigger_scene,
                    result_code='cookie_refresh_exception',
                    processing_status='failed',
                    error_message=str(refresh_e)[:200],
                    duration_ms=max(0, int((time.time() - risk_log_started_at) * 1000)),
                    event_meta=self._build_risk_event_meta(trigger_scene=trigger_scene, extra=base_event_meta),
                )
            return False
        finally:
            if recovery_lock_acquired:
                XianyuLive.release_auth_recovery_lock(self.cookie_id, recovery_lock_owner)

    async def _verify_cookie_validity(self) -> dict:
        """验证Cookie的有效性，通过实际调用API测试
        
        Returns:
            dict: {
                'valid': bool,  # 总体是否有效
                'confirm_api': bool,  # 确认发货API是否有效
                'image_api': bool,  # 图片上传API是否有效
                'details': str  # 详细信息
            }
        """
        logger.info(f"【{self.cookie_id}】开始验证Cookie有效性（使用真实API调用）...")
        
        result = {
            'valid': True,
            'confirm_api': None,
            'web_session_api': None,
            'image_api': None,
            'details': [],
            'inconclusive': False,
            'relogin_recommended': True,
        }
        
        # 1. 测试确认发货API - 使用测试订单ID实际调用
        # try:
        #     logger.info(f"【{self.cookie_id}】测试确认发货API（使用测试数据实际调用）...")
            
        #     # 确保session存在
        #     if not self.session:
        #         import aiohttp
        #         connector = aiohttp.TCPConnector(limit=100, limit_per_host=30)
        #         timeout = aiohttp.ClientTimeout(total=30)
        #         self.session = aiohttp.ClientSession(connector=connector, timeout=timeout)
            
        #     # 创建临时的确认发货实例
        #     from secure_confirm_decrypted import SecureConfirm
        #     confirm_tester = SecureConfirm(
        #         session=self.session,
        #         cookies_str=self.cookies_str,
        #         cookie_id=self.cookie_id,
        #         main_instance=self
        #     )
            
        #     # 使用一个测试订单ID（不存在的订单ID）
        #     # 如果Cookie有效，应该返回"订单不存在"类的错误
        #     # 如果Cookie无效，会返回"Session过期"错误
        #     test_order_id = "999999999999999999"  # 不存在的测试订单ID
            
        #     # 实际调用API (retry_count=3阻止重试，快速失败)
        #     response = await confirm_tester.auto_confirm(test_order_id, retry_count=3)
            
        #     # 分析响应
        #     if response and isinstance(response, dict):
        #         error_msg = str(response.get('error', ''))
        #         success = response.get('success', False)
                
        #         # 检查是否是Session过期错误
        #         if 'Session过期' in error_msg or 'SESSION_EXPIRED' in error_msg:
        #             logger.warning(f"【{self.cookie_id}】❌ 确认发货API验证失败: Session过期")
        #             result['confirm_api'] = False
        #             result['valid'] = False
        #             result['details'].append("确认发货API: Session过期")
        #         elif '令牌过期' in error_msg:
        #             logger.warning(f"【{self.cookie_id}】❌ 确认发货API验证失败: 令牌过期")
        #             result['confirm_api'] = False
        #             result['valid'] = False
        #             result['details'].append("确认发货API: 令牌过期")
        #         elif success:
        #             # 竟然成功了（不太可能，因为是测试订单ID）
        #             logger.info(f"【{self.cookie_id}】✅ 确认发货API验证通过: API调用成功")
        #             result['confirm_api'] = True
        #             result['details'].append("确认发货API: 通过验证")
        #         elif error_msg and len(error_msg) > 0:
        #             # 有其他错误信息（如订单不存在、重试次数过多等），说明Cookie是有效的
        #             logger.info(f"【{self.cookie_id}】✅ 确认发货API验证通过: Cookie有效（返回业务错误: {error_msg[:50]}）")
        #             result['confirm_api'] = True
        #             result['details'].append(f"确认发货API: 通过验证")
        #         else:
        #             # 没有明确信息，保守认为可能有问题
        #             logger.warning(f"【{self.cookie_id}】⚠️ 确认发货API验证警告: 响应不明确")
        #             result['confirm_api'] = False
        #             result['valid'] = False
        #             result['details'].append("确认发货API: 响应不明确")
        #     else:
        #         # 没有响应，可能有问题
        #         logger.warning(f"【{self.cookie_id}】⚠️ 确认发货API验证警告: 无响应")
        #         result['confirm_api'] = False
        #         result['valid'] = False
        #         result['details'].append("确认发货API: 无响应")
                    
        # except Exception as e:
        #     error_str = self._safe_str(e)
        #     # 检查异常信息中是否包含Session过期
        #     if 'Session过期' in error_str or 'SESSION_EXPIRED' in error_str:
        #         logger.warning(f"【{self.cookie_id}】❌ 确认发货API验证失败: Session过期")
        #         result['confirm_api'] = False
        #         result['valid'] = False
        #         result['details'].append("确认发货API: Session过期")
        #     else:
        #         logger.error(f"【{self.cookie_id}】确认发货API验证异常: {error_str}")
        #         # 网络异常等问题，不一定是Cookie问题，暂时标记为通过
        #         result['confirm_api'] = True
        #         result['details'].append(f"确认发货API: 调用异常(可能非Cookie问题)")
        
        # 2. 测试网页登录态 - 只读访问 IM 页面，检测是否被重定向到登录/验证页
        try:
            logger.info(f"【{self.cookie_id}】测试网页登录态（访问 IM 页面）...")

            if not self.session:
                connector = aiohttp.TCPConnector(limit=100, limit_per_host=30)
                timeout = aiohttp.ClientTimeout(total=30)
                self.session = aiohttp.ClientSession(connector=connector, timeout=timeout)

            async with self.session.get(
                'https://www.goofish.com/im',
                headers={
                    'cookie': self.cookies_str,
                    'Referer': 'https://www.goofish.com/',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                },
                allow_redirects=True
            ) as response:
                final_url = str(response.url)
                page_text = await response.text()

                redirected_to_login = (
                    'passport.goofish.com' in final_url or
                    'mini_login' in final_url or
                    ('mini_login.htm' in page_text and 'alibaba-login-box' in page_text)
                )

                if redirected_to_login or response.status in (401, 403):
                    logger.warning(f"【{self.cookie_id}】❌ 网页登录态验证失败: 已进入登录/验证页 ({final_url})")
                    result['web_session_api'] = False
                    result['valid'] = False
                    result['details'].append("网页登录态: 已重定向到登录/验证页")
                elif response.status >= 500:
                    logger.warning(f"【{self.cookie_id}】⚠️ 网页登录态验证遇到服务端异常: HTTP {response.status}")
                    result['web_session_api'] = None
                    result['inconclusive'] = True
                    if result['valid']:
                        result['relogin_recommended'] = False
                    result['details'].append(f"网页登录态: 服务端异常，结果不确定 (HTTP {response.status})")
                elif response.status == 200:
                    logger.info(f"【{self.cookie_id}】✅ 网页登录态验证通过: {final_url}")
                    result['web_session_api'] = True
                    result['details'].append("网页登录态: 通过验证")
                else:
                    logger.warning(f"【{self.cookie_id}】⚠️ 网页登录态验证结果不明确: HTTP {response.status}, URL={final_url}")
                    result['web_session_api'] = None
                    result['inconclusive'] = True
                    if result['valid']:
                        result['relogin_recommended'] = False
                    result['details'].append(f"网页登录态: 结果不明确 (HTTP {response.status})")

        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            error_str = self._safe_str(e)
            logger.warning(f"【{self.cookie_id}】⚠️ 网页登录态验证网络异常: {error_str}")
            result['web_session_api'] = None
            result['inconclusive'] = True
            if result['valid']:
                result['relogin_recommended'] = False
            result['details'].append(f"网页登录态: 网络异常，结果不确定 ({error_str[:50]})")
        except Exception as e:
            error_str = self._safe_str(e)
            logger.error(f"【{self.cookie_id}】网页登录态验证异常: {error_str}")
            result['web_session_api'] = None
            result['inconclusive'] = True
            if result['valid']:
                result['relogin_recommended'] = False
            result['details'].append(f"网页登录态: 验证异常，结果不确定 - {error_str[:50]}")

        # 3. 测试图片上传API - 创建测试图片并实际上传
        try:
            logger.info(f"【{self.cookie_id}】测试图片上传API（使用测试图片实际上传）...")
            
            # 创建一个最小的测试图片（1x1像素的PNG）
            import tempfile
            import os
            from PIL import Image
            
            # 创建临时目录
            temp_dir = tempfile.gettempdir()
            test_image_path = os.path.join(temp_dir, f'cookie_test_{self.cookie_id}.png')
            
            try:
                # 创建1x1像素的白色图片
                img = Image.new('RGB', (1, 1), color='white')
                img.save(test_image_path, 'PNG')
                logger.info(f"【{self.cookie_id}】已创建测试图片: {test_image_path}")
                
                # 创建图片上传实例
                from utils.image_uploader import ImageUploader
                uploader = ImageUploader(cookies_str=self.cookies_str)
                
                # 创建session
                await uploader.create_session()
                
                try:
                    upload_result = None
                    error_type = None
                    error_message = None

                    for attempt in range(2):
                        upload_result = await uploader.upload_image(test_image_path)
                        if upload_result:
                            break

                        error_type = getattr(uploader, 'last_error_type', None)
                        error_message = getattr(uploader, 'last_error_message', None) or "未知原因"
                        is_retryable_auth = error_type == 'auth' and error_message == '返回登录页面' and result['web_session_api'] is not False
                        if attempt == 0 and is_retryable_auth:
                            logger.warning(
                                f"【{self.cookie_id}】图片上传校验首次返回登录页，但网页登录态仍可访问，1.5秒后重试一次"
                            )
                            await asyncio.sleep(1.5)
                            continue
                        break
                finally:
                    # 确保关闭session
                    await uploader.close_session()
                
                # 分析上传结果
                if upload_result:
                    # 上传成功，Cookie有效
                    logger.info(f"【{self.cookie_id}】✅ 图片上传API验证通过: 上传成功 ({upload_result[:50]}...)")
                    result['image_api'] = True
                    result['details'].append("图片上传API: 通过验证")
                else:
                    error_type = getattr(uploader, 'last_error_type', None)
                    error_message = getattr(uploader, 'last_error_message', None) or "未知原因"
                    if error_type == 'network':
                        logger.warning(f"【{self.cookie_id}】⚠️ 图片上传API验证遇到网络异常，不判定为Cookie失效: {error_message}")
                        result['image_api'] = None
                        result['inconclusive'] = True
                        if result['valid']:
                            result['relogin_recommended'] = False
                        result['details'].append(f"图片上传API: 网络异常，结果不确定 ({error_message[:50]})")
                    elif error_type == 'http' and getattr(uploader, 'last_http_status', None) and uploader.last_http_status >= 500:
                        logger.warning(f"【{self.cookie_id}】⚠️ 图片上传API返回服务端异常，不判定为Cookie失效: HTTP {uploader.last_http_status}")
                        result['image_api'] = None
                        result['inconclusive'] = True
                        if result['valid']:
                            result['relogin_recommended'] = False
                        result['details'].append(f"图片上传API: 服务端异常，结果不确定 (HTTP {uploader.last_http_status})")
                    elif error_type == 'auth' and error_message == '返回登录页面':
                        logger.warning(
                            f"【{self.cookie_id}】❌ 图片上传接口返回登录页，按旧版严格策略判定Cookie失效"
                        )
                        result['image_api'] = False
                        result['valid'] = False
                        result['details'].append("图片上传API: 返回登录页面")
                    else:
                        # 明确认证/会话异常才视为Cookie失效
                        logger.warning(f"【{self.cookie_id}】❌ 图片上传API验证失败: {error_message}")
                        result['image_api'] = False
                        result['valid'] = False
                        result['details'].append(f"图片上传API: {error_message[:50]}")
                
            finally:
                # 清理测试图片
                if os.path.exists(test_image_path):
                    try:
                        os.remove(test_image_path)
                        logger.debug(f"【{self.cookie_id}】已删除测试图片")
                    except Exception:
                        pass
                        
        except Exception as e:
            error_str = self._safe_str(e)
            logger.error(f"【{self.cookie_id}】图片上传API验证异常: {error_str}")
            error_lower = error_str.lower()
            auth_keywords = ['返回登录页面', 'session过期', '令牌过期', 'login', 'mini_login', 'passport.goofish.com']
            if any(keyword.lower() in error_lower for keyword in auth_keywords):
                result['image_api'] = False
                result['valid'] = False
                result['details'].append(f"图片上传API: 验证异常({error_str[:50]})")
            else:
                # 上传校验异常可能是网络或环境问题，不直接判定为Cookie失效
                result['image_api'] = None
                result['inconclusive'] = True
                if result['valid']:
                    result['relogin_recommended'] = False
                result['details'].append(f"图片上传API: 验证异常，结果不确定 - {error_str[:50]}")
        
        if result['image_api'] is False:
            result['valid'] = False
        elif result['web_session_api'] is False and result['image_api'] is not True:
            result['valid'] = False
        elif result['web_session_api'] is False and result['image_api'] is True:
            logger.warning(f"【{self.cookie_id}】❌ 网页登录态与图片上传校验结果不一致，按严格策略判定Cookie失效")
            result['valid'] = False
            result['details'].append("校验结果: 网页登录态与图片上传结果不一致")

        # 汇总结果
        if result['valid']:
            if result['inconclusive']:
                logger.warning(f"【{self.cookie_id}】⚠️ Cookie验证结果不确定: 未发现明确失效证据，但部分校验存在波动或结果矛盾")
            else:
                logger.info(f"【{self.cookie_id}】✅ Cookie验证通过: 所有关键API均可用")
        else:
            logger.warning(f"【{self.cookie_id}】❌ Cookie验证失败:")
            for detail in result['details']:
                logger.warning(f"【{self.cookie_id}】  - {detail}")
        
        result['details'] = '; '.join(result['details'])
        return result

    async def _restart_instance(self):
        """重启XianyuLive实例
        
        ⚠️ 注意：此方法会触发当前任务被取消！
        调用此方法后，当前任务会立即被 CookieManager 取消，
        因此不要在此方法后执行任何重要操作。
        """
        try:
            logger.info(f"【{self.cookie_id}】准备重启实例...")

            # 导入CookieManager
            from cookie_manager import manager as cookie_manager

            if cookie_manager:
                # 通过CookieManager重启实例
                logger.info(f"【{self.cookie_id}】通过CookieManager重启实例...")
                
                # ⚠️ 重要：不要等待重启完成！
                # cookie_manager.update_cookie() 会立即取消当前任务
                # 如果我们等待它完成，会导致 CancelledError 中断等待
                # 正确的做法是：触发重启后立即返回，让任务自然退出
                
                import threading
                
                def trigger_restart():
                    """在后台线程中触发重启，不阻塞当前任务"""
                    try:
                        # 给当前任务足够时间完成清理和退出（避免竞态条件）
                        # 增加到2秒，确保任务有足够时间处理返回和清理
                        import time
                        time.sleep(2.0)
                        
                        # save_to_db=False 因为 update_config_cookies 已经保存过了
                        cookie_manager.update_cookie(self.cookie_id, self.cookies_str, save_to_db=False)
                        logger.info(f"【{self.cookie_id}】实例重启请求已触发")
                    except Exception as e:
                        logger.error(f"【{self.cookie_id}】触发实例重启失败: {e}")
                        import traceback
                        logger.error(f"【{self.cookie_id}】重启失败详情:\n{traceback.format_exc()}")

                # 在后台线程中触发重启
                restart_thread = threading.Thread(target=trigger_restart, daemon=True)
                restart_thread.start()
                
                logger.info(f"【{self.cookie_id}】实例重启已触发，当前任务即将退出...")
                logger.warning(f"【{self.cookie_id}】注意：重启请求已发送，CookieManager将在2秒后取消当前任务并启动新实例")
                    
            else:
                logger.warning(f"【{self.cookie_id}】CookieManager不可用，无法重启实例")

        except Exception as e:
            logger.error(f"【{self.cookie_id}】重启实例失败: {self._safe_str(e)}")
            import traceback
            logger.error(f"【{self.cookie_id}】重启失败堆栈:\n{traceback.format_exc()}")
            # 发送重启失败通知
            try:
                await self.send_token_refresh_notification(f"实例重启失败: {str(e)}", "instance_restart_failed")
            except Exception as notify_e:
                logger.error(f"【{self.cookie_id}】发送重启失败通知时出错: {self._safe_str(notify_e)}")

    async def save_item_info_to_db(self, item_id: str, item_detail: str = None, item_title: str = None):
        """保存商品信息到数据库

        Args:
            item_id: 商品ID
            item_detail: 商品详情内容（可以是任意格式的文本）
            item_title: 商品标题
        """
        try:
            # 跳过以 auto_ 开头的商品ID
            if item_id and item_id.startswith('auto_'):
                logger.warning(f"跳过保存自动生成的商品ID: {item_id}")
                return

            # 验证：如果只有商品ID，没有商品标题和商品详情，则不插入数据库
            if not item_title and not item_detail:
                logger.warning(f"跳过保存商品信息：缺少商品标题和详情 - {item_id}")
                return

            # 如果有商品标题但没有详情，也跳过（根据需求，需要同时有标题和详情）
            if not item_title or not item_detail:
                logger.warning(f"跳过保存商品信息：商品标题或详情不完整 - {item_id}")
                return

            from db_manager import db_manager

            # 直接使用传入的详情内容
            item_data = item_detail

            # 保存到数据库
            success = db_manager.save_item_info(self.cookie_id, item_id, item_data)
            if success:
                logger.info(f"商品信息已保存到数据库: {item_id}")
            else:
                logger.warning(f"保存商品信息到数据库失败: {item_id}")

        except Exception as e:
            logger.error(f"保存商品信息到数据库异常: {self._safe_str(e)}")

    async def save_item_detail_only(self, item_id, item_detail):
        """仅保存商品详情（不影响标题等基本信息）"""
        try:
            from db_manager import db_manager

            # 使用专门的详情更新方法
            success = db_manager.update_item_detail(self.cookie_id, item_id, item_detail)

            if success:
                logger.info(f"商品详情已更新: {item_id}")
            else:
                logger.warning(f"更新商品详情失败: {item_id}")

            return success

        except Exception as e:
            logger.error(f"更新商品详情异常: {self._safe_str(e)}")
            return False

    async def fetch_item_detail_from_api(self, item_id: str, force_refresh: bool = False) -> str:
        """获取商品详情（使用浏览器获取，支持24小时缓存）

        Args:
            item_id: 商品ID
            force_refresh: 是否绕过缓存强制拉取最新详情

        Returns:
            str: 商品详情文本，获取失败返回空字符串
        """
        try:
            # 检查是否启用自动获取功能
            from config import config
            auto_fetch_config = config.get('ITEM_DETAIL', {}).get('auto_fetch', {})

            if not auto_fetch_config.get('enabled', True):
                logger.warning(f"自动获取商品详情功能已禁用: {item_id}")
                return ""

            # 1. 首先检查缓存（24小时有效）
            if not force_refresh:
                async with self._item_detail_cache_lock:
                    if item_id in self._item_detail_cache:
                        cache_data = self._item_detail_cache[item_id]
                        cache_time = cache_data['timestamp']
                        current_time = time.time()

                        # 检查缓存是否在24小时内
                        if current_time - cache_time < self._item_detail_cache_ttl:
                            # 更新访问时间（用于LRU）
                            cache_data['access_time'] = current_time
                            logger.info(f"从缓存获取商品详情: {item_id}")
                            return cache_data['detail']
                        else:
                            # 缓存过期，删除
                            del self._item_detail_cache[item_id]
                            logger.warning(f"缓存已过期，删除: {item_id}")
            else:
                logger.info(f"强制刷新商品详情，跳过缓存: {item_id}")

            # 2. 尝试使用浏览器获取商品详情
            detail_from_browser = await self._fetch_item_detail_from_browser(item_id)
            if detail_from_browser:
                # 保存到缓存（带大小限制）
                await self._add_to_item_cache(item_id, detail_from_browser)
                logger.info(f"成功通过浏览器获取商品详情: {item_id}, 长度: {len(detail_from_browser)}")
                return detail_from_browser

            # 浏览器获取失败
            logger.warning(f"浏览器获取商品详情失败: {item_id}")
            return ""

        except Exception as e:
            logger.error(f"获取商品详情异常: {item_id}, 错误: {self._safe_str(e)}")
            return ""

    async def _add_to_item_cache(self, item_id: str, detail: str):
        """添加商品详情到缓存，实现LRU策略和大小限制
        
        Args:
            item_id: 商品ID
            detail: 商品详情
        """
        async with self._item_detail_cache_lock:
            current_time = time.time()
            
            # 检查缓存大小，如果超过限制则清理
            if len(self._item_detail_cache) >= self._item_detail_cache_max_size:
                # 使用LRU策略删除最久未访问的项
                if self._item_detail_cache:
                    # 找到最久未访问的项
                    oldest_item = min(
                        self._item_detail_cache.items(),
                        key=lambda x: x[1].get('access_time', x[1]['timestamp'])
                    )
                    oldest_item_id = oldest_item[0]
                    del self._item_detail_cache[oldest_item_id]
                    logger.warning(f"缓存已满，删除最旧项: {oldest_item_id}")
            
            # 添加新项到缓存
            self._item_detail_cache[item_id] = {
                'detail': detail,
                'timestamp': current_time,
                'access_time': current_time
            }
            logger.warning(f"添加商品详情到缓存: {item_id}, 当前缓存大小: {len(self._item_detail_cache)}")

    @classmethod
    async def _cleanup_item_cache(cls):
        """清理过期的商品详情缓存"""
        try:
            async with cls._item_detail_cache_lock:
                # 在持有锁时也要能响应取消信号
                await asyncio.sleep(0)
                
                current_time = time.time()
                expired_items = []
                
                # 找出所有过期的项
                for item_id, cache_data in cls._item_detail_cache.items():
                    # 在循环中也要能响应取消信号
                    await asyncio.sleep(0)
                    if current_time - cache_data['timestamp'] >= cls._item_detail_cache_ttl:
                        expired_items.append(item_id)
                
                # 删除过期项
                for item_id in expired_items:
                    await asyncio.sleep(0)  # 让出控制权
                    del cls._item_detail_cache[item_id]
                
                if expired_items:
                    logger.info(f"清理了 {len(expired_items)} 个过期的商品详情缓存")
                
                return len(expired_items)
        except asyncio.CancelledError:
            # 如果被取消，确保锁能正确释放
            raise

    async def _fetch_item_detail_from_browser(self, item_id: str) -> str:
        """使用浏览器获取商品详情"""
        playwright = None
        browser = None
        try:
            from playwright.async_api import async_playwright

            logger.info(f"开始使用浏览器获取商品详情: {item_id}")

            playwright = await async_playwright().start()

            # 启动浏览器（参照order_detail_fetcher的配置）
            browser_args = [
                '--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-dev-shm-usage',
                '--disable-accelerated-2d-canvas',
                '--no-first-run',
                '--no-zygote',
                '--disable-gpu',
                '--disable-background-timer-throttling',
                '--disable-backgrounding-occluded-windows',
                '--disable-renderer-backgrounding',
                '--disable-features=TranslateUI',
                '--disable-ipc-flooding-protection',
                '--disable-extensions',
                '--disable-default-apps',
                '--disable-sync',
                '--disable-translate',
                '--hide-scrollbars',
                '--mute-audio',
                '--no-default-browser-check',
                '--no-pings'
            ]

            # 在Docker环境中添加额外参数
            if os.getenv('DOCKER_ENV'):
                browser_args.extend([
                    # '--single-process',  # 注释掉，避免多用户并发时的进程冲突和资源泄漏
                    '--disable-background-networking',
                    '--disable-client-side-phishing-detection',
                    '--disable-hang-monitor',
                    '--disable-popup-blocking',
                    '--disable-prompt-on-repost',
                    '--disable-web-resources',
                    '--metrics-recording-only',
                    '--safebrowsing-disable-auto-update',
                    '--enable-automation',
                    '--password-store=basic',
                    '--use-mock-keychain'
                ])

            browser = await playwright.chromium.launch(
                headless=True,  # 移动模式使用无头模式
                args=browser_args
            )

            # 创建移动设备浏览器上下文（模拟iPhone）
            context = await browser.new_context(
                viewport={'width': 375, 'height': 812},  # iPhone X/11/12 尺寸
                user_agent='Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 AliApp(TB/11.15.0)',
                device_scale_factor=3,  # iPhone 的屏幕缩放比例
                is_mobile=True,
                has_touch=True
            )

            # 设置Cookie
            cookies = []
            for cookie_pair in self.cookies_str.split('; '):
                if '=' in cookie_pair:
                    name, value = cookie_pair.split('=', 1)
                    cookies.append({
                        'name': name.strip(),
                        'value': value.strip(),
                        'domain': '.goofish.com',
                        'path': '/'
                    })

            await context.add_cookies(cookies)
            logger.info(f"已设置 {len(cookies)} 个Cookie（移动模式）")

            # 创建页面
            page = await context.new_page()

            # 构造移动版商品详情页面URL
            item_url = f"https://h5.m.goofish.com/item?id={item_id}"
            logger.info(f"访问移动版商品页面: {item_url}")

            # 访问页面
            await page.goto(item_url, wait_until='networkidle', timeout=30000)

            # 等待页面完全加载
            await asyncio.sleep(2)

            # 获取商品详情内容
            detail_text = ""
            try:
                # 移动版页面选择器列表（按优先级排序）
                selectors = [
                    '.detailDesc--descText--1FMDTCm',  # 移动版商品详情主选择器
                    'span.rax-text-v2.detailDesc--descText--1FMDTCm',  # 完整选择器
                    '[class*="detailDesc--descText"]',  # 匹配包含detailDesc--descText的类名
                    '[class*="descText"]',  # 匹配包含descText的类名
                    '.desc--GaIUKUQY',  # PC版选择器（备用）
                    '.detail-desc',     # 常见的详情选择器
                    '.item-desc',       # 商品描述
                    '[class*="desc"]',  # 包含desc的类名
                ]
                
                for selector in selectors:
                    try:
                        # 尝试等待元素出现（短超时）
                        await page.wait_for_selector(selector, timeout=3000)
                        detail_element = await page.query_selector(selector)
                        if detail_element:
                            detail_text = await detail_element.inner_text()
                            if detail_text and len(detail_text.strip()) > 0:
                                logger.info(f"成功获取商品详情（选择器: {selector}）: {item_id}, 长度: {len(detail_text)}")
                                return detail_text.strip()
                    except Exception as e:
                        logger.debug(f"选择器 {selector} 未找到: {self._safe_str(e)}")
                        continue
                
                # 如果所有选择器都失败，尝试获取整个页面的文本内容
                logger.warning(f"未找到特定详情元素，尝试获取整个页面内容: {item_id}")
                body_text = await page.inner_text('body')
                if body_text:
                    logger.info(f"获取到页面整体内容: {item_id}, 长度: {len(body_text)}")
                    return body_text.strip()
                else:
                    logger.warning(f"未找到商品详情元素: {item_id}")

            except Exception as e:
                logger.warning(f"获取商品详情元素失败: {item_id}, 错误: {self._safe_str(e)}")

            return ""

        except Exception as e:
            logger.error(f"浏览器获取商品详情异常: {item_id}, 错误: {self._safe_str(e)}")
            return ""
        finally:
            # 确保资源被正确清理
            try:
                if browser:
                    await browser.close()
                    logger.warning(f"Browser已关闭: {item_id}")
            except Exception as e:
                logger.warning(f"关闭browser时出错: {self._safe_str(e)}")
            
            try:
                if playwright:
                    await playwright.stop()
                    logger.warning(f"Playwright已停止: {item_id}")
            except Exception as e:
                logger.warning(f"停止playwright时出错: {self._safe_str(e)}")


    async def save_items_list_to_db(self, items_list, sync_item_details=False):
        """批量保存商品列表信息到数据库（并发安全）

        Args:
            items_list: 从get_item_list_info获取的商品列表
            sync_item_details: 是否同步已存在商品的最新详情
        """
        try:
            from db_manager import db_manager

            # 准备批量数据，区分新商品和需要更新的商品
            batch_new_data = []  # 新商品，保存所有信息
            batch_update_data = []  # 已有商品，只更新标题和价格
            items_need_detail = []  # 需要获取或同步详情的商品列表

            for item in items_list:
                item_id = item.get('id')
                if not item_id or item_id.startswith('auto_'):
                    continue

                # 构造商品详情数据
                item_detail = {
                    'title': item.get('title', ''),
                    'price': item.get('price', ''),
                    'price_text': item.get('price_text', ''),
                    'category_id': item.get('category_id', ''),
                    'auction_type': item.get('auction_type', ''),
                    'item_status': item.get('item_status', 0),
                    'detail_url': item.get('detail_url', ''),
                    'pic_info': item.get('pic_info', {}),
                    'detail_params': item.get('detail_params', {}),
                    'track_params': item.get('track_params', {}),
                    'item_label_data': item.get('item_label_data', {}),
                    'card_type': item.get('card_type', 0)
                }

                # 检查数据库中是否已有该商品
                existing_item = db_manager.get_item_info(self.cookie_id, item_id)
                
                if existing_item:
                    # 商品已存在，先更新标题和价格；商品详情按同步模式单独处理
                    batch_update_data.append({
                        'cookie_id': self.cookie_id,
                        'item_id': item_id,
                        'item_title': item.get('title', ''),
                        'item_price': item.get('price_text', ''),
                        'item_category': str(item.get('category_id', ''))
                    })
                    if sync_item_details:
                        items_need_detail.append({
                            'item_id': item_id,
                            'item_title': item.get('title', '')
                        })
                    logger.debug(f"商品 {item_id} 已存在，将更新标题和价格")
                else:
                    # 新商品，保存所有信息
                    batch_new_data.append({
                        'cookie_id': self.cookie_id,
                        'item_id': item_id,
                        'item_title': item.get('title', ''),
                        'item_description': '',  # 暂时为空
                        'item_category': str(item.get('category_id', '')),
                        'item_price': item.get('price_text', ''),
                        'item_detail': json.dumps(item_detail, ensure_ascii=False)
                    })
                    
                    # 新商品需要获取详情
                    items_need_detail.append({
                        'item_id': item_id,
                        'item_title': item.get('title', '')
                    })
                    logger.debug(f"商品 {item_id} 是新商品，将保存完整信息")

            saved_count = 0
            
            # 保存新商品
            if batch_new_data:
                new_count = db_manager.batch_save_item_basic_info(batch_new_data)
                logger.info(f"新增商品信息: {new_count}/{len(batch_new_data)} 个")
                saved_count += new_count
            
            # 更新已有商品的标题和价格
            if batch_update_data:
                update_count = db_manager.batch_update_item_title_price(batch_update_data)
                logger.info(f"更新商品标题和价格: {update_count}/{len(batch_update_data)} 个")
                saved_count += update_count

            # 异步获取商品详情
            if items_need_detail:
                from config import config
                auto_fetch_config = config.get('ITEM_DETAIL', {}).get('auto_fetch', {})

                if auto_fetch_config.get('enabled', True):
                    action_text = '同步最新详情' if sync_item_details else '获取缺失详情'
                    logger.info(f"准备为 {len(items_need_detail)} 个商品{action_text}...")
                    detail_success_count = await self._fetch_item_details(
                        items_need_detail,
                        force_refresh=sync_item_details,
                    )
                    logger.info(f"成功为 {detail_success_count}/{len(items_need_detail)} 个商品{action_text}")
                else:
                    logger.info(f"有 {len(items_need_detail)} 个商品需要获取详情，但自动获取功能已禁用")

            return saved_count

        except Exception as e:
            logger.error(f"批量保存商品信息异常: {self._safe_str(e)}")
            return 0

    async def _fetch_item_details(self, items_need_detail, force_refresh=False):
        """批量获取或同步商品详情

        Args:
            items_need_detail: 需要获取详情的商品列表
            force_refresh: 是否绕过缓存强制拉取最新详情

        Returns:
            int: 成功获取详情的商品数量
        """
        success_count = 0

        try:
            from db_manager import db_manager
            from config import config

            # 从配置获取并发数量和延迟时间
            auto_fetch_config = config.get('ITEM_DETAIL', {}).get('auto_fetch', {})
            max_concurrent = auto_fetch_config.get('max_concurrent', 3)
            retry_delay = auto_fetch_config.get('retry_delay', 0.5)

            # 限制并发数量，避免对API服务器造成压力
            semaphore = asyncio.Semaphore(max_concurrent)

            async def fetch_single_item_detail(item_info):
                async with semaphore:
                    try:
                        item_id = item_info['item_id']
                        item_title = item_info['item_title']

                        # 获取商品详情
                        item_detail_text = await self.fetch_item_detail_from_api(
                            item_id,
                            force_refresh=force_refresh,
                        )

                        if item_detail_text:
                            # 保存详情到数据库
                            success = await self.save_item_detail_only(item_id, item_detail_text)
                            if success:
                                logger.info(f"✅ 成功获取并保存商品详情: {item_id} - {item_title}")
                                return 1
                            else:
                                logger.warning(f"❌ 获取详情成功但保存失败: {item_id}")
                        else:
                            logger.warning(f"❌ 未能获取商品详情: {item_id} - {item_title}")

                        # 添加延迟，避免请求过于频繁
                        await asyncio.sleep(retry_delay)
                        return 0

                    except Exception as e:
                        logger.error(f"获取单个商品详情异常: {item_info.get('item_id', 'unknown')}, 错误: {self._safe_str(e)}")
                        return 0

            # 并发获取所有商品详情
            tasks = [fetch_single_item_detail(item_info) for item_info in items_need_detail]
            results = await asyncio.gather(*tasks, return_exceptions=True)

            # 统计成功数量
            for result in results:
                if isinstance(result, int):
                    success_count += result
                elif isinstance(result, Exception):
                    logger.error(f"获取商品详情任务异常: {result}")

            return success_count

        except Exception as e:
            logger.error(f"批量获取商品详情异常: {self._safe_str(e)}")
            return success_count

    async def get_item_info(self, item_id, retry_count=0):
        """获取商品信息，自动处理token失效的情况"""
        if retry_count >= 4:  # 最多重试3次
            logger.error("获取商品信息失败，重试次数过多")
            return {"error": "获取商品信息失败，重试次数过多"}

        # 确保session已创建
        if not self.session:
            await self.create_session()

        params = {
            'jsv': '2.7.2',
            'appKey': '34839810',
            't': str(int(time.time()) * 1000),
            'sign': '',
            'v': '1.0',
            'type': 'originaljson',
            'accountSite': 'xianyu',
            'dataType': 'json',
            'timeout': '20000',
            'api': 'mtop.taobao.idle.pc.detail',
            'sessionOption': 'AutoLoginOnly',
            'spm_cnt': 'a21ybx.im.0.0',
        }

        data_val = '{"itemId":"' + item_id + '"}'
        data = {
            'data': data_val,
        }

        # 始终从最新的cookies中获取_m_h5_tk token（刷新后cookies会被更新）
        token = trans_cookies(self.cookies_str).get('_m_h5_tk', '').split('_')[0] if trans_cookies(self.cookies_str).get('_m_h5_tk') else ''

        if token:
            logger.warning(f"使用cookies中的_m_h5_tk token: {self._mask_secret_value(token, head=6, tail=4)}")
        else:
            logger.warning("cookies中没有找到_m_h5_tk token")

        from utils.xianyu_utils import generate_sign
        sign = generate_sign(params['t'], token, data_val)
        params['sign'] = sign

        try:
            async with self.session.post(
                'https://h5api.m.goofish.com/h5/mtop.taobao.idle.pc.detail/1.0/',
                params=params,
                data=data
            ) as response:
                res_json = await response.json()

                if await self._apply_response_cookie_updates(response.headers, "item_detail"):
                    logger.warning("已更新Cookie到数据库")

                logger.warning(f"商品信息获取成功: {res_json}")
                # 检查返回状态
                if isinstance(res_json, dict):
                    ret_value = res_json.get('ret', [])
                    # 检查ret是否包含成功信息
                    if not any('SUCCESS::调用成功' in ret for ret in ret_value):
                        logger.warning(f"商品信息API调用失败，错误信息: {ret_value}")

                        await asyncio.sleep(0.5)
                        return await self.get_item_info(item_id, retry_count + 1)
                    else:
                        logger.warning(f"商品信息获取成功: {item_id}")
                        return res_json
                else:
                    logger.error(f"商品信息API返回格式异常: {res_json}")
                    return await self.get_item_info(item_id, retry_count + 1)

        except Exception as e:
            logger.error(f"商品信息API请求异常: {self._safe_str(e)}")
            await asyncio.sleep(0.5)
            return await self.get_item_info(item_id, retry_count + 1)

    def extract_item_id_from_message(self, message):
        """从消息中提取商品ID的辅助方法"""
        try:
            # 注意: message["1"] 是会话ID(chat_id/cid)，格式如 "56226853668@goofish"
            # 不能从中提取商品ID，否则会把chat_id误当作item_id

            # 方法1: 从message["3"]中提取
            message_3 = message.get('3', {})
            if isinstance(message_3, dict):

                # 从extension中提取
                if 'extension' in message_3:
                    extension = message_3['extension']
                    if isinstance(extension, dict):
                        item_id = extension.get('itemId') or extension.get('item_id')
                        if item_id:
                            logger.info(f"从extension中提取商品ID: {item_id}")
                            return item_id

                # 从bizData中提取
                if 'bizData' in message_3:
                    biz_data = message_3['bizData']
                    if isinstance(biz_data, dict):
                        item_id = biz_data.get('itemId') or biz_data.get('item_id')
                        if item_id:
                            logger.info(f"从bizData中提取商品ID: {item_id}")
                            return item_id

                # 从其他可能的字段中提取
                for key, value in message_3.items():
                    if isinstance(value, dict):
                        item_id = value.get('itemId') or value.get('item_id')
                        if item_id:
                            logger.info(f"从{key}字段中提取商品ID: {item_id}")
                            return item_id

                # 从消息内容中提取数字ID
                content = message_3.get('content', '')
                if isinstance(content, str) and content:
                    id_match = re.search(r'(\d{10,})', content)
                    if id_match:
                        logger.info(f"【{self.cookie_id}】从消息内容中提取商品ID: {id_match.group(1)}")
                        return id_match.group(1)

            # 方法2: 遍历整个消息结构查找可能的商品ID
            # 跳过的字段: "1" 是会话ID(chat_id/cid)，不包含商品ID
            # 跳过可能包含非商品ID的字段
            skip_keys = {'1', 'tradeId', 'trade_id', 'bizId', 'biz_id', 'orderId', 'order_id',
                        'userId', 'user_id', 'senderId', 'sender_id', 'receiverId', 'receiver_id',
                        'chatId', 'chat_id', 'conversationId', 'conversation_id', 'msgId', 'msg_id'}

            def find_item_id_recursive(obj, path=""):
                if isinstance(obj, dict):
                    # 只查找明确命名为 itemId 的字段（不查找通用的 'id' 字段，避免误提取 tradeId 等）
                    for key in ['itemId', 'item_id']:
                        if key in obj and isinstance(obj[key], (str, int)):
                            value = str(obj[key])
                            if len(value) >= 10 and value.isdigit():
                                logger.info(f"从{path}.{key}中提取商品ID: {value}")
                                return value

                    # 递归查找（跳过chat_id和其他非商品ID字段）
                    for key, value in obj.items():
                        if key in skip_keys:
                            continue
                        result = find_item_id_recursive(value, f"{path}.{key}" if path else key)
                        if result:
                            return result

                elif isinstance(obj, str):
                    # 跳过chat_id格式的字符串（如 "56226853668@goofish"）
                    if '@goofish' in obj or '@xianyu' in obj:
                        return None
                    # 只从URL中提取itemId参数，不从普通字符串中提取数字（避免误提取）
                    if 'itemId=' in obj:
                        id_match = re.search(r'itemId=(\d{10,})', obj)
                        if id_match:
                            logger.info(f"从{path}的URL参数中提取商品ID: {id_match.group(1)}")
                            return id_match.group(1)

                return None

            result = find_item_id_recursive(message)
            if result:
                return result

            logger.warning("所有方法都未能提取到商品ID")
            return None

        except Exception as e:
            logger.error(f"提取商品ID失败: {self._safe_str(e)}")
            return None

    def debug_message_structure(self, message, context=""):
        """调试消息结构的辅助方法"""
        try:
            logger.warning(f"[{context}] 消息结构调试:")
            logger.warning(f"  消息类型: {type(message)}")

            if isinstance(message, dict):
                for key, value in message.items():
                    logger.warning(f"  键 '{key}': {type(value)} - {str(value)[:100]}...")

                    # 特别关注可能包含商品ID的字段
                    if key in ["1", "3"] and isinstance(value, dict):
                        logger.warning(f"    详细结构 '{key}':")
                        for sub_key, sub_value in value.items():
                            logger.warning(f"      '{sub_key}': {type(sub_value)} - {str(sub_value)[:50]}...")
            else:
                logger.warning(f"  消息内容: {str(message)[:200]}...")

        except Exception as e:
            logger.error(f"调试消息结构时发生错误: {self._safe_str(e)}")

    async def get_item_specific_reply(self, send_user_name: str, send_user_id: str, send_message: str, item_id: str = None) -> str:
        """获取指定商品回复内容"""
        if not item_id:
            return None

        try:
            from db_manager import db_manager

            item_reply = db_manager.get_item_reply(self.cookie_id, item_id)
            if not item_reply or not item_reply.get('reply_content'):
                return None

            reply_content = item_reply['reply_content']
            logger.info(f"【{self.cookie_id}】使用指定商品回复: 商品ID={item_id}")

            try:
                formatted_reply = reply_content.format(
                    send_user_name=send_user_name,
                    send_user_id=send_user_id,
                    send_message=send_message,
                    item_id=item_id
                )
                logger.info(f"【{self.cookie_id}】指定商品回复内容: {formatted_reply}")
                return formatted_reply
            except Exception as format_error:
                logger.error(f"指定商品回复变量替换失败: {self._safe_str(format_error)}")
                return reply_content

        except Exception as e:
            logger.error(f"获取指定商品回复失败: {self._safe_str(e)}")
            return None

    async def get_default_reply(self, send_user_name: str, send_user_id: str, send_message: str, chat_id: str, item_id: str = None) -> str:
        """获取默认回复内容，支持变量替换和只回复一次功能"""
        try:
            from db_manager import db_manager

            # 获取当前账号的默认回复设置
            default_reply_settings = db_manager.get_default_reply(self.cookie_id)

            if not default_reply_settings or not default_reply_settings.get('enabled', False):
                logger.warning(f"账号 {self.cookie_id} 未启用默认回复")
                return None

            # 检查"只回复一次"功能
            if default_reply_settings.get('reply_once', False) and chat_id:
                # 检查是否已经回复过这个chat_id
                if db_manager.has_default_reply_record(self.cookie_id, chat_id):
                    logger.info(f"【{self.cookie_id}】chat_id {chat_id} 已使用过默认回复，跳过（只回复一次）")
                    return "SKIP_REPLY"

            reply_content = default_reply_settings.get('reply_content', '')
            if not reply_content or (reply_content and reply_content.strip() == ''):
                logger.info(f"账号 {self.cookie_id} 默认回复内容为空，不进行回复")
                return "EMPTY_REPLY"  # 返回特殊标记表示不回复

            # 进行变量替换
            try:
                formatted_reply = reply_content.format(
                    send_user_name=send_user_name,
                    send_user_id=send_user_id,
                    send_message=send_message
                )

                # 如果开启了"只回复一次"功能，记录这次回复
                if default_reply_settings.get('reply_once', False) and chat_id:
                    db_manager.add_default_reply_record(self.cookie_id, chat_id)
                    logger.info(f"【{self.cookie_id}】记录默认回复: chat_id={chat_id}")

                logger.info(f"【{self.cookie_id}】使用默认回复: {formatted_reply}")
                return formatted_reply
            except Exception as format_error:
                logger.error(f"默认回复变量替换失败: {self._safe_str(format_error)}")
                # 如果变量替换失败，返回原始内容
                return reply_content

        except Exception as e:
            logger.error(f"获取默认回复失败: {self._safe_str(e)}")
            return None

    async def get_keyword_reply(self, send_user_name: str, send_user_id: str, send_message: str, item_id: str = None) -> str:
        """获取关键词匹配回复（支持商品ID优先匹配和图片类型）"""
        try:
            from db_manager import db_manager

            # 获取当前账号的关键词列表（包含类型信息）
            keywords = db_manager.get_keywords_with_type(self.cookie_id)

            if not keywords:
                logger.warning(f"账号 {self.cookie_id} 没有配置关键词")
                return None

            # 1. 如果有商品ID，优先匹配该商品ID对应的关键词
            if item_id:
                for keyword_data in keywords:
                    keyword = keyword_data['keyword']
                    reply = keyword_data['reply']
                    keyword_item_id = keyword_data['item_id']
                    keyword_type = keyword_data.get('type', 'text')
                    image_url = keyword_data.get('image_url')

                    if keyword_item_id == item_id and keyword.lower() in send_message.lower():
                        logger.info(f"商品ID关键词匹配成功: 商品{item_id} '{keyword}' (类型: {keyword_type})")

                        # 根据关键词类型处理
                        if keyword_type == 'image' and image_url:
                            # 图片类型关键词，发送图片
                            return await self._handle_image_keyword(keyword, image_url, send_user_name, send_user_id, send_message)
                        else:
                            # 文本类型关键词，检查回复内容是否为空
                            if not reply or (reply and reply.strip() == ''):
                                logger.info(f"商品ID关键词 '{keyword}' 回复内容为空，不进行回复")
                                return "EMPTY_REPLY"  # 返回特殊标记表示匹配到但不回复

                            # 进行变量替换
                            try:
                                formatted_reply = reply.format(
                                    send_user_name=send_user_name,
                                    send_user_id=send_user_id,
                                    send_message=send_message
                                )
                                logger.info(f"商品ID文本关键词回复: {formatted_reply}")
                                return formatted_reply
                            except Exception as format_error:
                                logger.error(f"关键词回复变量替换失败: {self._safe_str(format_error)}")
                                # 如果变量替换失败，返回原始内容
                                return reply

            # 2. 如果商品ID匹配失败或没有商品ID，匹配没有商品ID的通用关键词
            for keyword_data in keywords:
                keyword = keyword_data['keyword']
                reply = keyword_data['reply']
                keyword_item_id = keyword_data['item_id']
                keyword_type = keyword_data.get('type', 'text')
                image_url = keyword_data.get('image_url')

                if not keyword_item_id and keyword.lower() in send_message.lower():
                    logger.info(f"通用关键词匹配成功: '{keyword}' (类型: {keyword_type})")

                    # 根据关键词类型处理
                    if keyword_type == 'image' and image_url:
                        # 图片类型关键词，发送图片
                        return await self._handle_image_keyword(keyword, image_url, send_user_name, send_user_id, send_message)
                    else:
                        # 文本类型关键词，检查回复内容是否为空
                        if not reply or (reply and reply.strip() == ''):
                            logger.info(f"通用关键词 '{keyword}' 回复内容为空，不进行回复")
                            return "EMPTY_REPLY"  # 返回特殊标记表示匹配到但不回复

                        # 进行变量替换
                        try:
                            formatted_reply = reply.format(
                                send_user_name=send_user_name,
                                send_user_id=send_user_id,
                                send_message=send_message
                            )
                            logger.info(f"通用文本关键词回复: {formatted_reply}")
                            return formatted_reply
                        except Exception as format_error:
                            logger.error(f"关键词回复变量替换失败: {self._safe_str(format_error)}")
                            # 如果变量替换失败，返回原始内容
                            return reply

            logger.warning(f"未找到匹配的关键词: {send_message}")
            return None

        except Exception as e:
            logger.error(f"获取关键词回复失败: {self._safe_str(e)}")
            return None

    async def _handle_image_keyword(self, keyword: str, image_url: str, send_user_name: str, send_user_id: str, send_message: str) -> str:
        """处理图片类型关键词"""
        try:
            # 检查图片URL类型
            if self._is_cdn_url(image_url):
                # 已经是CDN链接，直接使用
                logger.info(f"使用已有的CDN图片链接: {image_url}")
                return f"__IMAGE_SEND__{image_url}"

            elif image_url.startswith('/static/uploads/') or image_url.startswith('static/uploads/'):
                # 本地图片，需要上传到闲鱼CDN
                local_image_path = image_url.replace('/static/uploads/', 'static/uploads/')
                if os.path.exists(local_image_path):
                    logger.info(f"准备上传本地图片到闲鱼CDN: {local_image_path}")

                    # 使用图片上传器上传到闲鱼CDN
                    from utils.image_uploader import ImageUploader
                    uploader = ImageUploader(self.cookies_str)

                    async with uploader:
                        cdn_url = await uploader.upload_image(local_image_path)
                        if cdn_url:
                            logger.info(f"图片上传成功，CDN URL: {cdn_url}")
                            # 更新数据库中的图片URL为CDN URL
                            await self._update_keyword_image_url(keyword, cdn_url)
                            image_url = cdn_url
                        else:
                            logger.error(f"图片上传失败: {local_image_path}")
                            logger.error(f"❌ Cookie可能已失效！请检查配置并更新Cookie")
                            return f"抱歉，图片发送失败（Cookie可能已失效，请检查日志）"
                else:
                    logger.error(f"本地图片文件不存在: {local_image_path}")
                    return f"抱歉，图片文件不存在。"

            else:
                # 其他类型的URL（可能是外部链接），直接使用
                logger.info(f"使用外部图片链接: {image_url}")

            # 发送图片（这里返回特殊标记，在调用处处理实际发送）
            return f"__IMAGE_SEND__{image_url}"

        except Exception as e:
            logger.error(f"处理图片关键词失败: {e}")
            return f"抱歉，图片发送失败: {str(e)}"

    def _is_cdn_url(self, url: str) -> bool:
        """检查URL是否是闲鱼CDN链接"""
        if not url:
            return False

        # 闲鱼CDN域名列表
        cdn_domains = [
            'gw.alicdn.com',
            'img.alicdn.com',
            'cloud.goofish.com',
            'goofish.com',
            'taobaocdn.com',
            'tbcdn.cn',
            'aliimg.com'
        ]

        # 检查是否包含CDN域名
        url_lower = url.lower()
        for domain in cdn_domains:
            if domain in url_lower:
                return True

        # 检查是否是HTTPS链接且包含图片特征
        if url_lower.startswith('https://') and any(ext in url_lower for ext in ['.jpg', '.jpeg', '.png', '.gif', '.webp']):
            return True

        return False

    async def _update_keyword_image_url(self, keyword: str, new_image_url: str):
        """更新关键词的图片URL"""
        try:
            from db_manager import db_manager
            success = db_manager.update_keyword_image_url(self.cookie_id, keyword, new_image_url)
            if success:
                logger.info(f"图片URL已更新: {keyword} -> {new_image_url}")
            else:
                logger.warning(f"图片URL更新失败: {keyword}")
        except Exception as e:
            logger.error(f"更新关键词图片URL失败: {e}")

    async def _update_card_image_url(self, card_id: int, new_image_url: str):
        """更新卡券的图片URL"""
        try:
            from db_manager import db_manager
            success = db_manager.update_card_image_url(card_id, new_image_url)
            if success:
                logger.info(f"卡券图片URL已更新: 卡券ID={card_id} -> {new_image_url}")
            else:
                logger.warning(f"卡券图片URL更新失败: 卡券ID={card_id}")
        except Exception as e:
            logger.error(f"更新卡券图片URL失败: {e}")

    async def get_ai_reply(self, send_user_name: str, send_user_id: str, send_message: str, item_id: str, chat_id: str):
        """获取AI回复"""
        try:
            from ai_reply_engine import ai_reply_engine

            # 检查是否启用AI回复
            if not ai_reply_engine.is_ai_enabled(self.cookie_id):
                logger.warning(f"账号 {self.cookie_id} 未启用AI回复")
                return None

            # 从数据库获取商品信息
            from db_manager import db_manager
            item_info_raw = db_manager.get_item_info(self.cookie_id, item_id)

            if not item_info_raw:
                logger.warning(f"数据库中无商品信息: {item_id}")
                # 使用默认商品信息
                item_info = {
                    'title': '商品信息获取失败',
                    'price': 0,
                    'desc': '暂无商品描述'
                }
            else:
                # 解析数据库中的商品信息
                item_info = {
                    'title': item_info_raw.get('item_title', '未知商品'),
                    'price': self._parse_price(item_info_raw.get('item_price', '0')),
                    'desc': item_info_raw.get('item_detail', '暂无商品描述')
                }

            # 生成AI回复
            # 由于外部已实现防抖机制，跳过内部等待（skip_wait=True）
            reply = ai_reply_engine.generate_reply(
                message=send_message,
                item_info=item_info,
                chat_id=chat_id,
                cookie_id=self.cookie_id,
                user_id=send_user_id,
                item_id=item_id,
                skip_wait=True  # 跳过内部等待，因为外部已实现防抖
            )

            if reply:
                logger.info(f"【{self.cookie_id}】AI回复生成成功: {reply}")
                return reply
            else:
                logger.warning(f"AI回复生成失败")
                return None

        except Exception as e:
            logger.error(f"获取AI回复失败: {self._safe_str(e)}")
            return None

    def _parse_price(self, price_str: str) -> float:
        """解析价格字符串为数字"""
        try:
            if not price_str:
                return 0.0
            # 移除非数字字符，保留小数点
            price_clean = re.sub(r'[^\d.]', '', str(price_str))
            return float(price_clean) if price_clean else 0.0
        except Exception:
            return 0.0

    def _get_notification_template(self, template_type: str) -> str:
        """获取通知模板，如果没有自定义模板则返回默认模板"""
        return get_notification_template_text(template_type)

    def _format_template(self, template: str, **kwargs) -> str:
        """格式化模板，将变量替换为实际值"""
        return format_notification_template(template, **kwargs)

    async def send_notification(self, send_user_name: str, send_user_id: str, send_message: str, item_id: str = None, chat_id: str = None):
        """发送消息通知"""
        try:
            import hashlib

            # 过滤系统默认消息，不发送通知
            system_messages = [
                '发来一条消息',
                '发来一条新消息'
            ]

            if send_message in system_messages:
                logger.warning(f"📱 系统消息不发送通知: {send_message}")
                return

            # 生成通知的唯一标识（基于消息内容、chat_id、send_user_id）
            # 用于防重复发送
            notification_key = f"{chat_id or 'unknown'}_{send_user_id}_{send_message}"
            notification_hash = hashlib.md5(notification_key.encode('utf-8')).hexdigest()
            reservation_key = f"msg:{notification_hash}"
            
            # 使用异步锁保护防重复检查，确保并发安全
            async with self.notification_lock:
                # 检查是否在冷却时间内已发送过相同的通知
                current_time = time.time()
                if notification_hash in self.last_notification_time:
                    time_since_last = current_time - self.last_notification_time[notification_hash]
                    if time_since_last < self.notification_cooldown:
                        remaining_seconds = int(self.notification_cooldown - time_since_last)
                        logger.warning(f"📱 通知在冷却期内（剩余 {remaining_seconds} 秒），跳过重复发送 - 账号: {self.cookie_id}, 买家: {send_user_name}, 消息: {send_message[:30]}...")
                        return
                if reservation_key in self.pending_notification_keys:
                    logger.warning(f"📱 相同消息通知正在发送中，跳过重复发送 - 账号: {self.cookie_id}, 买家: {send_user_name}")
                    return
                self.pending_notification_keys.add(reservation_key)

            try:
                logger.info(f"📱 开始发送消息通知 - 账号: {self.cookie_id}, 买家: {send_user_name}")

                notification_msg = render_notification_template(
                    'message',
                    account_id=self.cookie_id,
                    buyer_name=send_user_name,
                    buyer_id=send_user_id,
                    item_id=item_id or '未知',
                    chat_id=chat_id or '未知',
                    message=send_message,
                    time=time.strftime('%Y-%m-%d %H:%M:%S')
                )

                notification_sent = await dispatch_account_notifications(
                    self.cookie_id,
                    notification_msg,
                    title='接收消息通知',
                    notification_type='message',
                )

                if not notification_sent:
                    logger.warning(f"📱 消息通知未发送成功，不进入冷却 - 账号: {self.cookie_id}, 买家: {send_user_name}")
                    return

                async with self.notification_lock:
                    sent_time = time.time()
                    self.last_notification_time[notification_hash] = sent_time
                    expired_keys = [
                        key for key, timestamp in self.last_notification_time.items()
                        if sent_time - timestamp > 3600
                    ]
                    for key in expired_keys:
                        del self.last_notification_time[key]
            finally:
                async with self.notification_lock:
                    self.pending_notification_keys.discard(reservation_key)

        except Exception as e:
            logger.error(f"📱 处理消息通知失败: {self._safe_str(e)}")
            import traceback
            logger.error(f"📱 详细错误信息: {traceback.format_exc()}")

    def _parse_notification_config(self, config: str) -> dict:
        """解析通知配置数据"""
        try:
            import json
            # 尝试解析JSON格式的配置
            return json.loads(config)
        except (json.JSONDecodeError, TypeError):
            # 兼容旧格式（直接字符串）
            return {"config": config}

    async def _send_qq_notification(self, config_data: dict, message: str):
        """发送QQ通知"""
        try:
            import aiohttp

            logger.info(f"📱 QQ通知 - 开始处理配置数据: {config_data}")

            # 解析配置（QQ号码）
            qq_number = config_data.get('qq_number') or config_data.get('config', '')
            qq_number = qq_number.strip() if qq_number else ''

            logger.info(f"📱 QQ通知 - 解析到QQ号码: {qq_number}")

            if not qq_number:
                logger.warning("📱 QQ通知 - QQ号码配置为空，无法发送通知")
                return False

            # 构建请求URL
            api_url = "http://36.111.68.231:3000/sendPrivateMsg"
            params = {
                'qq': qq_number,
                'msg': message
            }

            logger.info(f"📱 QQ通知 - 请求URL: {api_url}")
            logger.info(f"📱 QQ通知 - 请求参数: qq={qq_number}, msg长度={len(message)}")

            # 发送GET请求
            async with aiohttp.ClientSession() as session:
                async with session.get(api_url, params=params, timeout=10) as response:
                    response_text = await response.text()
                    logger.info(f"📱 QQ通知 - 响应状态: {response.status}")

                    # 需求：502 视为成功，且不打印返回内容
                    if response.status == 502:
                        logger.info(f"📱 QQ通知发送成功: {qq_number} (状态码: {response.status})")
                        return True
                    elif response.status == 200:
                        logger.info(f"📱 QQ通知发送成功: {qq_number} (状态码: {response.status})")
                        logger.warning(f"📱 QQ通知 - 响应内容: {response_text}")
                        return True
                    else:
                        logger.warning(f"📱 QQ通知发送失败: HTTP {response.status}")
                        logger.warning(f"📱 QQ通知 - 响应内容: {response_text}")
                        return False

        except Exception as e:
            logger.error(f"📱 发送QQ通知异常: {self._safe_str(e)}")
            import traceback
            logger.error(f"📱 QQ通知异常详情: {traceback.format_exc()}")
            return False

    async def _send_dingtalk_notification(self, config_data: dict, message: str):
        """发送钉钉通知"""
        try:
            import aiohttp
            import json
            import hmac
            import hashlib
            import base64
            import time

            # 解析配置
            webhook_url = config_data.get('webhook_url') or config_data.get('config', '')
            secret = config_data.get('secret', '')

            webhook_url = webhook_url.strip() if webhook_url else ''
            if not webhook_url:
                logger.warning("钉钉通知配置为空")
                return False

            # 如果有加签密钥，生成签名
            if secret:
                timestamp = str(round(time.time() * 1000))
                secret_enc = secret.encode('utf-8')
                string_to_sign = f'{timestamp}\n{secret}'
                string_to_sign_enc = string_to_sign.encode('utf-8')
                hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
                sign = base64.b64encode(hmac_code).decode('utf-8')
                webhook_url += f'&timestamp={timestamp}&sign={sign}'

            data = {
                "msgtype": "markdown",
                "markdown": {
                    "title": "闲鱼管理系统通知",
                    "text": message
                }
            }

            async with aiohttp.ClientSession() as session:
                async with session.post(webhook_url, json=data, timeout=10) as response:
                    if response.status == 200:
                        logger.info(f"钉钉通知发送成功")
                        return True
                    else:
                        logger.warning(f"钉钉通知发送失败: {response.status}")
                        return False

        except Exception as e:
            logger.error(f"发送钉钉通知异常: {self._safe_str(e)}")
            return False

    async def _send_feishu_notification(self, config_data: dict, message: str):
        """发送飞书通知"""
        try:
            import aiohttp
            import json
            import hmac
            import hashlib
            import base64

            logger.info(f"📱 飞书通知 - 开始处理配置数据: {config_data}")

            # 解析配置
            webhook_url = config_data.get('webhook_url', '')
            secret = config_data.get('secret', '')

            logger.info(f"📱 飞书通知 - Webhook URL: {webhook_url[:50]}...")
            logger.info(f"📱 飞书通知 - 是否有签名密钥: {'是' if secret else '否'}")

            if not webhook_url:
                logger.warning("📱 飞书通知 - Webhook URL配置为空，无法发送通知")
                return False

            # 如果有加签密钥，生成签名
            timestamp = str(int(time.time()))
            sign = ""

            if secret:
                string_to_sign = f'{timestamp}\n{secret}'
                hmac_code = hmac.new(
                    string_to_sign.encode('utf-8'),
                    ''.encode('utf-8'),
                    digestmod=hashlib.sha256
                ).digest()
                sign = base64.b64encode(hmac_code).decode('utf-8')
                logger.info(f"📱 飞书通知 - 已生成签名")

            # 构建请求数据
            data = {
                "msg_type": "text",
                "content": {
                    "text": message
                },
                "timestamp": timestamp
            }

            # 如果有签名，添加到请求数据中
            if sign:
                data["sign"] = sign

            logger.info(f"📱 飞书通知 - 请求数据构建完成")

            # 发送POST请求
            async with aiohttp.ClientSession() as session:
                async with session.post(webhook_url, json=data, timeout=10) as response:
                    response_text = await response.text()
                    logger.info(f"📱 飞书通知 - 响应状态: {response.status}")
                    logger.info(f"📱 飞书通知 - 响应内容: {response_text}")

                    if response.status == 200:
                        try:
                            response_json = json.loads(response_text)
                            if response_json.get('code') == 0:
                                logger.info(f"📱 飞书通知发送成功")
                                return True
                            else:
                                logger.warning(f"📱 飞书通知发送失败: {response_json.get('msg', '未知错误')}")
                                return False
                        except json.JSONDecodeError:
                            logger.info(f"📱 飞书通知发送成功（响应格式异常）")
                            return True
                    else:
                        logger.warning(f"📱 飞书通知发送失败: HTTP {response.status}, 响应: {response_text}")
                        return False

        except Exception as e:
            logger.error(f"📱 发送飞书通知异常: {self._safe_str(e)}")
            import traceback
            logger.error(f"📱 飞书通知异常详情: {traceback.format_exc()}")
            return False

    async def _send_bark_notification(self, config_data: dict, message: str):
        """发送Bark通知"""
        try:
            import aiohttp
            import json
            from urllib.parse import quote

            logger.info(f"📱 Bark通知 - 开始处理配置数据: {config_data}")

            # 解析配置
            server_url = config_data.get('server_url', 'https://api.day.app').rstrip('/')
            device_key = config_data.get('device_key', '')
            title = config_data.get('title', '闲鱼管理系统通知')
            sound = config_data.get('sound', 'default')
            icon = config_data.get('icon', '')
            group = config_data.get('group', 'xianyu')
            url = config_data.get('url', '')

            logger.info(f"📱 Bark通知 - 服务器: {server_url}")
            logger.info(f"📱 Bark通知 - 设备密钥: {device_key[:10]}..." if device_key else "📱 Bark通知 - 设备密钥: 未设置")
            logger.info(f"📱 Bark通知 - 标题: {title}")

            if not device_key:
                logger.warning("📱 Bark通知 - 设备密钥配置为空，无法发送通知")
                return False

            # 构建请求URL和数据
            # Bark支持两种方式：URL路径方式和POST JSON方式
            # 这里使用POST JSON方式，更灵活且支持更多参数

            api_url = f"{server_url}/push"

            # 构建请求数据
            data = {
                "device_key": device_key,
                "title": title,
                "body": message,
                "sound": sound,
                "group": group
            }

            # 可选参数
            if icon:
                data["icon"] = icon
            if url:
                data["url"] = url

            logger.info(f"📱 Bark通知 - API地址: {api_url}")
            logger.info(f"📱 Bark通知 - 请求数据构建完成")

            # 发送POST请求
            async with aiohttp.ClientSession() as session:
                async with session.post(api_url, json=data, timeout=10) as response:
                    response_text = await response.text()
                    logger.info(f"📱 Bark通知 - 响应状态: {response.status}")
                    logger.info(f"📱 Bark通知 - 响应内容: {response_text}")

                    if response.status == 200:
                        try:
                            response_json = json.loads(response_text)
                            if response_json.get('code') == 200:
                                logger.info(f"📱 Bark通知发送成功")
                                return True
                            else:
                                logger.warning(f"📱 Bark通知发送失败: {response_json.get('message', '未知错误')}")
                                return False
                        except json.JSONDecodeError:
                            # 某些Bark服务器可能返回纯文本
                            if 'success' in response_text.lower() or 'ok' in response_text.lower():
                                logger.info(f"📱 Bark通知发送成功")
                                return True
                            else:
                                logger.warning(f"📱 Bark通知响应格式异常: {response_text}")
                                return False
                    else:
                        logger.warning(f"📱 Bark通知发送失败: HTTP {response.status}, 响应: {response_text}")
                        return False

        except Exception as e:
            logger.error(f"📱 发送Bark通知异常: {self._safe_str(e)}")
            import traceback
            logger.error(f"📱 Bark通知异常详情: {traceback.format_exc()}")
            return False

    async def _send_email_notification(self, config_data: dict, message: str, attachment_path: str = None):
        """发送邮件通知（支持附件）
        
        Args:
            config_data: 邮件配置
            message: 邮件正文
            attachment_path: 附件文件路径（可选）
        """
        try:
            import smtplib
            from email.mime.text import MIMEText
            from email.mime.multipart import MIMEMultipart
            from email.mime.image import MIMEImage
            import os

            # 解析配置
            smtp_server = config_data.get('smtp_server', '')
            smtp_port = int(config_data.get('smtp_port', 587))
            email_user = config_data.get('email_user', '')
            email_password = config_data.get('email_password', '')
            recipient_email = config_data.get('recipient_email', '')
            smtp_use_tls = config_data.get('smtp_use_tls', smtp_port == 587)  # 修复：添加变量定义

            if not all([smtp_server, email_user, email_password, recipient_email]):
                logger.warning("邮件通知配置不完整")
                return False

            # 创建邮件
            msg = MIMEMultipart()
            msg['From'] = email_user
            msg['To'] = recipient_email
            msg['Subject'] = "闲鱼管理系统通知"

            # 添加邮件正文
            msg.attach(MIMEText(message, 'plain', 'utf-8'))

            # 添加附件（如果有）
            if attachment_path and os.path.exists(attachment_path):
                try:
                    with open(attachment_path, 'rb') as f:
                        img_data = f.read()
                    
                    # 根据文件扩展名判断MIME类型
                    filename = os.path.basename(attachment_path)
                    if attachment_path.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
                        img = MIMEImage(img_data)
                        img.add_header('Content-Disposition', 'attachment', filename=filename)
                        msg.attach(img)
                        logger.info(f"已添加图片附件: {filename}")
                    else:
                        from email.mime.application import MIMEApplication
                        attach = MIMEApplication(img_data)
                        attach.add_header('Content-Disposition', 'attachment', filename=filename)
                        msg.attach(attach)
                        logger.info(f"已添加附件: {filename}")
                except Exception as attach_error:
                    logger.error(f"添加邮件附件失败: {self._safe_str(attach_error)}")

            # 发送邮件
            server = None
            try:
                if smtp_port == 465:
                    # 使用SSL连接（端口465）
                    server = smtplib.SMTP_SSL(smtp_server, smtp_port, timeout=30)
                else:
                    # 使用普通连接，然后升级到TLS（端口587）
                    server = smtplib.SMTP(smtp_server, smtp_port, timeout=30)
                    if smtp_use_tls:
                        server.starttls()
                
                # 尝试登录
                try:
                    server.login(email_user, email_password)
                except smtplib.SMTPAuthenticationError as auth_error:
                    error_code = auth_error.smtp_code if hasattr(auth_error, 'smtp_code') else None
                    error_msg = str(auth_error)
                    
                    # 提供详细的错误提示
                    logger.error(f"邮件SMTP认证失败 (错误码: {error_code})")
                    logger.error(f"邮箱地址: {email_user}")
                    logger.error(f"SMTP服务器: {smtp_server}:{smtp_port}")
                    logger.error(f"错误详情: {error_msg}")
                    
                    # 根据常见错误提供解决建议
                    suggestions = []
                    if 'qq.com' in email_user.lower() or 'qq' in smtp_server.lower():
                        suggestions.append("QQ邮箱需要使用授权码而不是登录密码")
                        suggestions.append("请到QQ邮箱设置 -> 账户 -> 开启SMTP服务 -> 生成授权码")
                    elif 'gmail.com' in email_user.lower() or 'gmail' in smtp_server.lower():
                        suggestions.append("Gmail需要使用应用专用密码")
                        suggestions.append("请到Google账户 -> 安全性 -> 两步验证 -> 应用专用密码")
                        suggestions.append("或启用'允许不够安全的应用访问'（不推荐）")
                    elif '163.com' in email_user.lower() or '126.com' in email_user.lower() or 'yeah.net' in email_user.lower():
                        suggestions.append("网易邮箱需要使用授权码")
                        suggestions.append("请到邮箱设置 -> POP3/SMTP/IMAP -> 开启SMTP服务 -> 生成授权码")
                    else:
                        suggestions.append("请检查邮箱密码/授权码是否正确")
                        suggestions.append("某些邮箱服务商需要使用授权码而不是登录密码")
                        suggestions.append("请查看邮箱服务商的SMTP设置说明")
                    
                    if suggestions:
                        logger.error("解决建议:")
                        for i, suggestion in enumerate(suggestions, 1):
                            logger.error(f"  {i}. {suggestion}")
                    
                    raise  # 重新抛出异常
                
                server.send_message(msg)
                logger.info(f"邮件通知发送成功: {recipient_email}")
                return True

            finally:
                # 确保关闭连接
                if server:
                    try:
                        server.quit()
                    except Exception:
                        try:
                            server.close()
                        except Exception:
                            pass

        except smtplib.SMTPAuthenticationError:
            # 认证错误已在上面处理，这里不再重复记录
            return False
        except smtplib.SMTPException as smtp_error:
            logger.error(f"SMTP协议错误: {self._safe_str(smtp_error)}")
            logger.error(f"SMTP服务器: {smtp_server}:{smtp_port}")
            logger.error(f"请检查SMTP服务器地址和端口配置是否正确")
            return False
        except Exception as e:
            logger.error(f"发送邮件通知异常: {self._safe_str(e)}")
            import traceback
            logger.error(f"邮件发送详细错误: {traceback.format_exc()}")
            return False

    async def _send_webhook_notification(self, config_data: dict, message: str):
        """发送Webhook通知"""
        try:
            import aiohttp
            import json

            # 解析配置
            webhook_url = config_data.get('webhook_url', '')
            http_method = config_data.get('http_method', 'POST').upper()
            headers_str = config_data.get('headers', '{}')

            if not webhook_url:
                logger.warning("Webhook通知配置为空")
                return False

            # 解析自定义请求头
            try:
                custom_headers = json.loads(headers_str) if headers_str else {}
            except json.JSONDecodeError:
                custom_headers = {}

            # 设置默认请求头
            headers = {'Content-Type': 'application/json'}
            headers.update(custom_headers)

            # 构建请求数据
            data = {
                'message': message,
                'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
                'source': 'xianyu-auto-reply'
            }

            async with aiohttp.ClientSession() as session:
                if http_method == 'POST':
                    async with session.post(webhook_url, json=data, headers=headers, timeout=10) as response:
                        if response.status == 200:
                            logger.info(f"Webhook通知发送成功")
                            return True
                        else:
                            logger.warning(f"Webhook通知发送失败: {response.status}")
                            return False
                elif http_method == 'PUT':
                    async with session.put(webhook_url, json=data, headers=headers, timeout=10) as response:
                        if response.status == 200:
                            logger.info(f"Webhook通知发送成功")
                            return True
                        else:
                            logger.warning(f"Webhook通知发送失败: {response.status}")
                            return False
                else:
                    logger.warning(f"不支持的HTTP方法: {http_method}")
                    return False

        except Exception as e:
            logger.error(f"发送Webhook通知异常: {self._safe_str(e)}")
            return False

    async def _send_wechat_notification(self, config_data: dict, message: str):
        """发送微信通知"""
        try:
            import aiohttp
            import json

            # 解析配置
            webhook_url = config_data.get('webhook_url', '')

            if not webhook_url:
                logger.warning("微信通知配置为空")
                return False

            data = {
                "msgtype": "text",
                "text": {
                    "content": message
                }
            }

            async with aiohttp.ClientSession() as session:
                async with session.post(webhook_url, json=data, timeout=10) as response:
                    if response.status == 200:
                        logger.info(f"微信通知发送成功")
                        return True
                    else:
                        logger.warning(f"微信通知发送失败: {response.status}")
                        return False

        except Exception as e:
            logger.error(f"发送微信通知异常: {self._safe_str(e)}")
            return False

    async def _send_telegram_notification(self, config_data: dict, message: str):
        """发送Telegram通知"""
        try:
            import aiohttp

            # 解析配置
            bot_token = config_data.get('bot_token', '')
            chat_id = config_data.get('chat_id', '')

            if not all([bot_token, chat_id]):
                logger.warning("Telegram通知配置不完整")
                return False

            # 构建API URL
            api_url = f"https://api.telegram.org/bot{bot_token}/sendMessage"

            data = {
                'chat_id': chat_id,
                'text': message,
                'parse_mode': 'HTML'
            }

            async with aiohttp.ClientSession() as session:
                async with session.post(api_url, json=data, timeout=10) as response:
                    if response.status == 200:
                        logger.info(f"Telegram通知发送成功")
                        return True
                    else:
                        logger.warning(f"Telegram通知发送失败: {response.status}")
                        return False

        except Exception as e:
            logger.error(f"发送Telegram通知异常: {self._safe_str(e)}")
            return False

    async def send_token_refresh_notification(self, error_message: str, notification_type: str = "token_refresh", chat_id: str = None, attachment_path: str = None, verification_url: str = None):
        """发送Token刷新异常通知（带防重复机制，支持附件）
        
        Args:
            error_message: 错误消息
            notification_type: 通知类型
            chat_id: 聊天ID（可选）
            attachment_path: 附件路径（可选，用于发送截图）
        """
        try:
            # 检查是否是正常的令牌过期，这种情况不需要发送通知
            if notification_type != "token_scheduled_refresh_failed" and self._is_normal_token_expiry(error_message):
                logger.warning(f"检测到正常的令牌过期，跳过通知: {error_message}")
                return

            notification_key = f"token:{notification_type}"

            # 为Token刷新异常通知使用特殊的3小时冷却时间
            # 基于错误消息内容判断是否为Token相关异常
            if notification_type == "message_stream_stale":
                cooldown_time = self.message_stream_notification_cooldown
                cooldown_desc = f"{max(1, int(cooldown_time // 60))}分钟"
            elif self._is_token_related_error(error_message):
                cooldown_time = self.token_refresh_notification_cooldown
                cooldown_desc = "3小时"
            else:
                cooldown_time = self.notification_cooldown
                cooldown_desc = f"{self.notification_cooldown // 60}分钟"

            async with self.notification_lock:
                current_time = time.time()
                last_time = self.last_notification_time.get(notification_key, 0)
                if notification_key in self.pending_notification_keys:
                    logger.warning(f"Token刷新通知正在发送中，跳过重复发送: {notification_type}")
                    return
                if current_time - last_time < cooldown_time:
                    remaining_time = cooldown_time - (current_time - last_time)
                    remaining_hours = int(remaining_time // 3600)
                    remaining_minutes = int((remaining_time % 3600) // 60)
                    remaining_seconds = int(remaining_time % 60)

                    if remaining_hours > 0:
                        time_desc = f"{remaining_hours}小时{remaining_minutes}分钟"
                    elif remaining_minutes > 0:
                        time_desc = f"{remaining_minutes}分钟{remaining_seconds}秒"
                    else:
                        time_desc = f"{remaining_seconds}秒"

                    logger.warning(f"Token刷新通知在冷却期内，跳过发送: {notification_type} (还需等待 {time_desc})")
                    return
                self.pending_notification_keys.add(notification_key)

            # 构造通知消息（使用模板）
            if notification_type in ("slider_success", "slider_recovered_success"):
                slider_status_text = (
                    "账号会话已恢复"
                    if notification_type == "slider_recovered_success"
                    else "cookies已自动更新到数据库"
                )
                notification_msg = render_notification_template(
                    'slider_success',
                    account_id=self.cookie_id,
                    time=time.strftime('%Y-%m-%d %H:%M:%S'),
                    status_text=slider_status_text
                )
            elif "密码登录成功" in error_message or notification_type == "password_login_success":
                notification_msg = render_notification_template(
                    'password_login_success',
                    account_id=self.cookie_id,
                    time=time.strftime('%Y-%m-%d %H:%M:%S'),
                    cookie_count='已获取'
                )
            elif "刷新Cookie成功" in error_message or notification_type == "cookie_refresh_success":
                notification_msg = render_notification_template(
                    'cookie_refresh_success',
                    account_id=self.cookie_id,
                    time=time.strftime('%Y-%m-%d %H:%M:%S'),
                    cookie_count='已获取'
                )
            elif "人脸验证" in error_message or "短信验证" in error_message or "二维码验证" in error_message or "身份验证" in error_message or (verification_url and "passport" in verification_url):
                notification_msg = render_notification_template(
                    'face_verify',
                    account_id=self.cookie_id,
                    time=time.strftime('%Y-%m-%d %H:%M:%S'),
                    verification_url=verification_url or '无',
                    verification_type=guess_verification_type(error_message, verification_url)
                )
            elif verification_url:
                notification_msg = render_notification_template(
                    'token_refresh',
                    account_id=self.cookie_id,
                    time=time.strftime('%Y-%m-%d %H:%M:%S'),
                    error_message=error_message,
                    verification_url=verification_url
                )
            else:
                notification_msg = render_notification_template(
                    'token_refresh',
                    account_id=self.cookie_id,
                    time=time.strftime('%Y-%m-%d %H:%M:%S'),
                    error_message=error_message,
                    verification_url='无'
                )

            logger.info(f"准备发送Token刷新异常通知: {self.cookie_id}")

            notification_sent = await dispatch_account_notifications(
                self.cookie_id,
                notification_msg,
                title='闲鱼管理系统通知',
                notification_type=notification_type,
                attachment_path=attachment_path,
            )

            # 如果成功发送了通知，更新最后发送时间
            if notification_sent:
                current_time = time.time()
                async with self.notification_lock:
                    self.last_notification_time[notification_key] = current_time

                # 根据错误消息内容使用不同的冷却时间
                if notification_type == "message_stream_stale":
                    next_send_time = current_time + self.message_stream_notification_cooldown
                    cooldown_desc = f"{max(1, int(self.message_stream_notification_cooldown // 60))}分钟"
                elif self._is_token_related_error(error_message):
                    next_send_time = current_time + self.token_refresh_notification_cooldown
                    cooldown_desc = "3小时"
                else:
                    next_send_time = current_time + self.notification_cooldown
                    cooldown_desc = f"{self.notification_cooldown // 60}分钟"

                next_send_time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(next_send_time))
                logger.info(f"Token刷新通知已发送，下次可发送时间: {next_send_time_str} (冷却时间: {cooldown_desc})")
            else:
                logger.warning(f"【{self.cookie_id}】Token刷新通知未发送成功，不进入冷却: {notification_type}")

        except Exception as e:
            logger.error(f"处理Token刷新通知失败: {self._safe_str(e)}")
        finally:
            async with self.notification_lock:
                self.pending_notification_keys.discard(f"token:{notification_type}")

    def _is_normal_token_expiry(self, error_message: str) -> bool:
        """检查是否是正常的令牌过期或其他不需要通知的情况"""
        # 不需要发送通知的关键词
        no_notification_keywords = [
            # 正常的令牌过期
            'FAIL_SYS_TOKEN_EXOIRED::令牌过期',
            'FAIL_SYS_TOKEN_EXPIRED::令牌过期',
            'FAIL_SYS_TOKEN_EXOIRED',
            'FAIL_SYS_TOKEN_EXPIRED',
            '令牌过期',
            # Session过期（正常情况）
            'FAIL_SYS_SESSION_EXPIRED::Session过期',
            'FAIL_SYS_SESSION_EXPIRED',
            'Session过期',
            # Token定时刷新失败（会自动重试）
            'Token定时刷新失败，将自动重试',
            'Token定时刷新失败'
        ]

        # 检查错误消息是否包含不需要通知的关键词
        for keyword in no_notification_keywords:
            if keyword in error_message:
                return True

        return False

    def _is_token_related_error(self, error_message: str) -> bool:
        """检查是否是Token相关的错误，需要使用3小时冷却时间"""
        # Token相关错误的关键词
        token_error_keywords = [
            # Token刷新失败相关
            'Token刷新失败',
            'Token刷新异常',
            'token刷新失败',
            'token刷新异常',
            'TOKEN刷新失败',
            'TOKEN刷新异常',
            # 具体的Token错误信息
            'FAIL_SYS_USER_VALIDATE',
            'RGV587_ERROR',
            '哎哟喂,被挤爆啦',
            '请稍后重试',
            'punish?x5secdata',
            'captcha',
            # Token获取失败
            '无法获取有效token',
            '无法获取有效Token',
            'Token获取失败',
            'token获取失败',
            'TOKEN获取失败',
            # Token定时刷新失败
            'Token定时刷新失败',
            'token定时刷新失败',
            'TOKEN定时刷新失败',
            # 初始化Token失败
            '初始化时无法获取有效Token',
            '初始化时无法获取有效token',
            # 其他Token相关错误
            'accessToken',
            'access_token',
            '_m_h5_tk',
            'mtop.taobao.idlemessage.pc.login.token'
        ]

        # 检查错误消息是否包含Token相关的关键词
        error_message_lower = error_message.lower()
        for keyword in token_error_keywords:
            if keyword.lower() in error_message_lower:
                return True

        return False

    def _build_scheduled_token_refresh_error_message(self, last_refresh_status: str) -> str:
        """为定时Token刷新失败选择更准确的通知文案。"""
        if last_refresh_status in {"session_expired_after_slider", "session_expired_preflight"}:
            return "Session已过期，系统自动恢复失败，请重新登录"

        if last_refresh_status == "token_expired_recovery_failed":
            detail = (self.last_token_refresh_error_message or "").lower()
            if "session过期" in detail or "页面会话已失效" in detail:
                return "Session已过期，系统自动恢复失败，请重新登录"

        return "Token定时刷新失败，将自动重试"

    async def send_delivery_failure_notification(self, send_user_name: str, send_user_id: str, item_id: str, error_message: str, chat_id: str = None):
        """发送自动发货失败通知"""
        try:
            notification_message = render_notification_template(
                'delivery',
                account_id=self.cookie_id,
                buyer_name=send_user_name,
                buyer_id=send_user_id,
                item_id=item_id,
                chat_id=chat_id or '未知',
                result=error_message,
                time=time.strftime('%Y-%m-%d %H:%M:%S')
            )

            notification_sent = await dispatch_account_notifications(
                self.cookie_id,
                notification_message,
                title='自动发货通知',
                notification_type='delivery',
            )
            if not notification_sent:
                logger.warning(f"【{self.cookie_id}】自动发货通知未发送成功")

        except Exception as e:
            logger.error(f"发送自动发货通知异常: {self._safe_str(e)}")

    async def auto_confirm(self, order_id, item_id=None, retry_count=0):
        """自动确认发货 - 使用加密模块，不包含延时处理（延时已在_auto_delivery中处理）"""
        try:
            logger.warning(f"【{self.cookie_id}】开始确认发货，订单ID: {order_id}")

            # 导入解密后的确认发货模块
            from secure_confirm_decrypted import SecureConfirm

            # 创建确认实例，传入主界面类实例
            secure_confirm = SecureConfirm(self.session, self.cookies_str, self.cookie_id, self)

            # 传递必要的属性
            secure_confirm.current_token = self.current_token
            secure_confirm.last_token_refresh_time = self.last_token_refresh_time
            secure_confirm.token_refresh_interval = self.token_refresh_interval

            # 调用确认方法，传入item_id用于token刷新
            result = await secure_confirm.auto_confirm(order_id, item_id, retry_count)

            # 同步更新后的cookies和token
            if secure_confirm.cookies_str != self.cookies_str:
                self._set_runtime_cookie_state(
                    cookies_str=secure_confirm.cookies_str,
                    cookies_dict=secure_confirm.cookies,
                    source="secure_confirm_sync",
                )
                logger.warning(f"【{self.cookie_id}】已同步确认发货模块更新的cookies")

            if secure_confirm.current_token != self.current_token:
                self.current_token = secure_confirm.current_token
                self.last_token_refresh_time = secure_confirm.last_token_refresh_time
                logger.warning(f"【{self.cookie_id}】已同步确认发货模块更新的token")

            return result

        except Exception as e:
            logger.error(f"【{self.cookie_id}】加密确认模块调用失败: {self._safe_str(e)}")
            return {"error": f"加密确认模块调用失败: {self._safe_str(e)}", "order_id": order_id}

    async def auto_freeshipping(self, order_id, item_id, buyer_id, retry_count=0):
        """自动免拼发货 - 使用解密模块"""
        try:
            logger.warning(f"【{self.cookie_id}】开始免拼发货，订单ID: {order_id}")

            # 导入解密后的免拼发货模块
            from secure_freeshipping_decrypted import SecureFreeshipping

            # 创建免拼发货实例
            secure_freeshipping = SecureFreeshipping(self.session, self.cookies_str, self.cookie_id)

            # 传递必要的属性
            secure_freeshipping.current_token = self.current_token
            secure_freeshipping.last_token_refresh_time = self.last_token_refresh_time
            secure_freeshipping.token_refresh_interval = self.token_refresh_interval

            # 调用免拼发货方法
            result = await secure_freeshipping.auto_freeshipping(order_id, item_id, buyer_id, retry_count)

            if secure_freeshipping.cookies_str != self.cookies_str:
                self._set_runtime_cookie_state(
                    cookies_str=secure_freeshipping.cookies_str,
                    cookies_dict=secure_freeshipping.cookies,
                    source="secure_freeshipping_sync",
                )
                logger.warning(f"【{self.cookie_id}】已同步免拼发货模块更新的cookies")

            if secure_freeshipping.current_token != self.current_token:
                self.current_token = secure_freeshipping.current_token
                self.last_token_refresh_time = secure_freeshipping.last_token_refresh_time
                logger.warning(f"【{self.cookie_id}】已同步免拼发货模块更新的token")

            return result

        except Exception as e:
            logger.error(f"【{self.cookie_id}】免拼发货模块调用失败: {self._safe_str(e)}")
            return {"error": f"免拼发货模块调用失败: {self._safe_str(e)}", "order_id": order_id}

    async def fetch_order_detail_info(self, order_id: str, item_id: str = None, buyer_id: str = None, debug_headless: bool = None, sid: str = None, force_refresh: bool = False, buyer_nick: str = None, buyer_id_source: str = None):
        """获取订单详情信息（使用独立的锁机制，不受延迟锁影响）

        Args:
            order_id: 订单ID
            item_id: 商品ID
            buyer_id: 买家ID
            debug_headless: 是否使用有头模式调试
            sid: 会话ID（如 56226853668@goofish），用于简化消息匹配订单
            force_refresh: 是否强制刷新（跳过缓存直接从闲鱼获取）
            buyer_nick: 买家昵称（从下单消息中提取）
        """
        # 使用独立的订单详情锁，不与自动发货锁冲突
        order_detail_lock = self._order_detail_locks[order_id]

        # 如果锁绑定了不同的事件循环（如从 Web API 调用），创建新锁
        try:
            current_loop = asyncio.get_running_loop()
            lock_loop = getattr(order_detail_lock, '_loop', None)
            if lock_loop is not None and lock_loop is not current_loop:
                order_detail_lock = asyncio.Lock()
                self._order_detail_locks[order_id] = order_detail_lock
                logger.info(f"【{self.cookie_id}】订单详情锁 {order_id} 事件循环不匹配，已重建")
        except RuntimeError:
            pass

        # 记录订单详情锁的使用时间
        self._order_detail_lock_times[order_id] = time.time()

        async with order_detail_lock:
            logger.info(f"🔍 【{self.cookie_id}】获取订单详情锁 {order_id}，开始处理...")
            
            try:
                logger.info(f"【{self.cookie_id}】开始获取订单详情: {order_id}, sid={sid}")

                # 导入订单详情获取器
                from utils.order_detail_fetcher import fetch_order_detail_simple
                from db_manager import db_manager

                # 获取当前账号的cookie字符串
                cookie_string = self.cookies_str
                logger.warning(f"【{self.cookie_id}】使用Cookie长度: {len(cookie_string) if cookie_string else 0}")

                # 确定是否使用有头模式（调试用）
                headless_mode = True if debug_headless is None else debug_headless
                if not headless_mode:
                    logger.info(f"【{self.cookie_id}】🖥️ 启用有头模式进行调试")

                # 异步获取订单详情（使用当前账号的cookie）
                result = await fetch_order_detail_simple(
                    order_id,
                    cookie_string,
                    headless=headless_mode,
                    force_refresh=force_refresh,
                    cookie_id_for_log=self.cookie_id
                )

                if result:
                    retry_task = self.order_detail_retry_tasks.get(order_id)
                    current_task = asyncio.current_task()
                    if retry_task and retry_task is not current_task and not retry_task.done():
                        retry_task.cancel()
                        self.order_detail_retry_tasks.pop(order_id, None)
                        logger.info(f"【{self.cookie_id}】订单详情已成功获取，取消待执行的补抓任务: {order_id}")

                    logger.info(f"【{self.cookie_id}】订单详情获取成功: {order_id}")
                    logger.info(f"【{self.cookie_id}】页面标题: {result.get('title', '未知')}")

                    def _normalize_optional_text(value):
                        if value is None:
                            return None
                        text = str(value).strip()
                        return text if text else None

                    def _normalize_amount_text(value):
                        text = _normalize_optional_text(value)
                        if not text:
                            return None
                        # 避免将无数字的异常文本写入金额字段
                        if not re.search(r'\d', text):
                            return None
                        return text

                    def _parse_amount_float(value):
                        text = _normalize_amount_text(value)
                        if not text:
                            return None
                        try:
                            return float(text)
                        except (TypeError, ValueError):
                            return None

                    # 获取解析后的规格信息
                    spec_parse_mode = str(result.get('spec_parse_mode') or '').strip() or 'no_spec'
                    spec_name = _normalize_optional_text(result.get('spec_name'))
                    spec_value = _normalize_optional_text(result.get('spec_value'))
                    spec_name_2 = _normalize_optional_text(result.get('spec_name_2'))
                    spec_value_2 = _normalize_optional_text(result.get('spec_value_2'))
                    quantity = _normalize_optional_text(result.get('quantity'))
                    amount = _normalize_amount_text(result.get('amount'))
                    amount_source = _normalize_optional_text(result.get('amount_source')) or 'unknown'
                    platform_created_at = _normalize_optional_text(result.get('platform_created_at'))
                    platform_paid_at = _normalize_optional_text(result.get('platform_paid_at'))
                    platform_completed_at = _normalize_optional_text(result.get('platform_completed_at'))
                    item_config = db_manager.get_item_info(self.cookie_id, item_id) if item_id else None
                    item_config_multi_spec = bool(item_config and item_config.get('is_multi_spec'))
                    item_config_detail = _normalize_optional_text(item_config.get('item_detail')) if item_config else None
                    is_coin_deduction_item = bool(item_config_detail and '闲鱼币抵扣' in item_config_detail)
                    configured_item_amount = _normalize_amount_text(item_config.get('item_price')) if item_config else None
                    configured_item_amount_value = _parse_amount_float(configured_item_amount)

                    if item_config is not None and not item_config_multi_spec and any(
                        [spec_name, spec_value, spec_name_2, spec_value_2]
                    ):
                        logger.warning(
                            f"【{self.cookie_id}】商品配置为无规格，刷新订单详情时忽略解析到的规格信息: "
                            f"order_id={order_id}, item_id={item_id}, "
                            f"spec={spec_name or ''}:{spec_value or ''}, spec2={spec_name_2 or ''}:{spec_value_2 or ''}"
                        )
                        spec_name = None
                        spec_value = None
                        spec_name_2 = None
                        spec_value_2 = None

                    if spec_parse_mode == 'one_spec' and spec_name and spec_value and not (spec_name_2 or spec_value_2):
                        spec_name_2 = ''
                        spec_value_2 = ''
                        logger.info(
                            f"【{self.cookie_id}】订单详情明确解析为单规格，允许清空历史残留的第二规格字段: "
                            f"order_id={order_id}, item_id={item_id}, spec={spec_name}:{spec_value}"
                        )

                    # 获取订单状态（从闲鱼页面解析）
                    raw_order_status = _normalize_optional_text(result.get('order_status'))
                    order_status_source = _normalize_optional_text(result.get('order_status_source')) or 'unknown'
                    # unknown 视为解析失败，不覆盖已有状态
                    order_status = raw_order_status if raw_order_status and raw_order_status.lower() != 'unknown' else None
                    if order_status:
                        logger.info(f"【{self.cookie_id}】📊 订单状态: {order_status} (source={order_status_source})")
                    elif raw_order_status and raw_order_status.lower() == 'unknown':
                        logger.warning(f"【{self.cookie_id}】订单状态解析为unknown，跳过状态字段写库")

                    if spec_name and spec_value:
                        logger.info(f"【{self.cookie_id}】📋 规格名称: {spec_name}")
                        logger.info(f"【{self.cookie_id}】📝 规格值: {spec_value}")
                        if spec_name_2 and spec_value_2:
                            logger.info(f"【{self.cookie_id}】📋 规格2名称: {spec_name_2}")
                            logger.info(f"【{self.cookie_id}】📝 规格2值: {spec_value_2}")
                            print(f"🛍️ 【{self.cookie_id}】订单 {order_id} 规格信息: {spec_name} -> {spec_value}, {spec_name_2} -> {spec_value_2}")
                        else:
                            print(f"🛍️ 【{self.cookie_id}】订单 {order_id} 规格信息: {spec_name} -> {spec_value}")
                    else:
                        logger.warning(f"【{self.cookie_id}】未获取到有效的规格信息")
                        print(f"⚠️ 【{self.cookie_id}】订单 {order_id} 规格信息获取失败")

                    if amount:
                        logger.info(f"【{self.cookie_id}】💰 订单金额: {amount} (source={amount_source})")

                    # 插入或更新订单信息到数据库
                    try:
                        # 对于系统消息误识别出的“自己是买家”场景，保留已有买家信息并继续刷新订单字段
                        existing_order = db_manager.get_order_by_id(order_id)
                        current_order_status = existing_order.get('order_status') if existing_order else None
                        existing_amount = existing_order.get('amount') if existing_order else None
                        existing_amount_value = _parse_amount_float(existing_amount)
                        amount, amount_source = self._apply_bargain_amount_override(
                            order_id,
                            item_id,
                            amount,
                            amount_source,
                            existing_order=existing_order,
                            item_config=item_config,
                        )
                        incoming_amount_value = _parse_amount_float(amount)
                        has_valid_spec = bool(spec_name and spec_value)
                        low_confidence_amount_sources = {
                            'selector_direct',
                            'selector_currency',
                            'text_currency',
                            'unknown',
                        }

                        if (
                            is_coin_deduction_item and existing_amount_value is not None and incoming_amount_value is not None and
                            configured_item_amount_value is not None and existing_amount_value + 0.009 < configured_item_amount_value and
                            abs(incoming_amount_value - configured_item_amount_value) <= 0.009
                        ):
                            logger.warning(
                                f"【{self.cookie_id}】闲鱼币抵扣订单返回原价，保留已有实付金额: "
                                f"order_id={order_id}, existing_amount={existing_amount}, incoming_amount={amount}, "
                                f"configured_amount={configured_item_amount}, amount_source={amount_source}"
                            )
                            amount = _normalize_amount_text(existing_amount)
                            amount_source = 'coin_deduction_preserved_existing'
                            incoming_amount_value = _parse_amount_float(amount)

                        if amount and amount_source in low_confidence_amount_sources and not has_valid_spec and not order_status:
                            if existing_amount_value is not None:
                                logger.warning(
                                    f"【{self.cookie_id}】订单详情返回低置信度金额，保留已有金额: "
                                    f"order_id={order_id}, existing_amount={existing_amount}, incoming_amount={amount}, "
                                    f"amount_source={amount_source}"
                                )
                                amount = _normalize_amount_text(existing_amount)
                                amount_source = 'preserved_existing'
                            else:
                                logger.warning(
                                    f"【{self.cookie_id}】订单详情返回低置信度金额，且缺少规格/状态佐证，跳过写库: "
                                    f"order_id={order_id}, incoming_amount={amount}, amount_source={amount_source}"
                                )
                                amount = None

                        elif (
                            amount and existing_amount_value is not None and incoming_amount_value is not None and
                            abs(existing_amount_value - incoming_amount_value) > 0.009 and
                            not has_valid_spec and not order_status and
                            amount_source not in {'selector_keyword_high', 'selector_keyword_low', 'text_keyword_high', 'text_keyword_low', 'cache'}
                        ):
                            logger.warning(
                                f"【{self.cookie_id}】订单详情金额跳变且缺少规格/状态佐证，保留已有金额: "
                                f"order_id={order_id}, existing_amount={existing_amount}, incoming_amount={amount}, "
                                f"amount_source={amount_source}"
                            )
                            amount = _normalize_amount_text(existing_amount)
                            amount_source = 'preserved_existing'

                        if self._should_reject_order_detail_status_update(
                            current_status=current_order_status,
                            incoming_status=order_status,
                            incoming_source=order_status_source,
                            force_refresh=force_refresh,
                        ):
                            logger.warning(
                                f"【{self.cookie_id}】强制刷新结果仅来自正文，拒绝将订单状态更新为completed: "
                                f"order_id={order_id}, current={current_order_status}, incoming={order_status}, "
                                f"source={order_status_source}"
                            )
                            order_status = None

                        normalized_current_order_status = db_manager._normalize_order_status(current_order_status)
                        normalized_incoming_order_status = db_manager._normalize_order_status(order_status)
                        if self._should_accept_order_detail_status_correction(
                            current_order_status,
                            order_status,
                            order_status_source,
                            force_refresh=force_refresh,
                            order_id=order_id,
                        ):
                            order_status_to_save = normalized_incoming_order_status
                            logger.warning(
                                f"【{self.cookie_id}】检测到可疑已发货状态，允许强刷后的结构化待发货结果纠偏: "
                                f"order_id={order_id}, current={current_order_status}, incoming={order_status}, "
                                f"source={order_status_source}"
                            )
                        else:
                            order_status_to_save = self._resolve_external_order_status(
                                current_order_status,
                                order_status,
                                source='order_detail_refresh'
                            )

                        if (
                            order_status and existing_order and order_status_to_save is None and
                            normalized_current_order_status != normalized_incoming_order_status
                        ):
                            logger.info(
                                f"【{self.cookie_id}】保留订单现有状态，跳过详情页覆盖: "
                                f"order_id={order_id}, current={current_order_status}, incoming={order_status}"
                            )

                        buyer_id_to_save, buyer_nick_to_save, should_skip_write = self._select_buyer_identity_for_order_write(
                            order_id,
                            incoming_buyer_id=buyer_id,
                            incoming_buyer_nick=buyer_nick,
                            existing_order=existing_order,
                            buyer_id_source=buyer_id_source,
                            buyer_nick_source="order_detail",
                            log_prefix=f"【{self.cookie_id}】",
                        )
                        if should_skip_write:
                            return result

                        # 检查cookie_id是否在cookies表中存在
                        cookie_info = db_manager.get_cookie_by_id(self.cookie_id)
                        if not cookie_info:
                            logger.warning(f"Cookie ID {self.cookie_id} 不存在于cookies表中，丢弃订单 {order_id}")
                        else:
                            # 先保存订单基本信息（包含sid和buyer_nick用于简化消息匹配）
                            success = db_manager.insert_or_update_order(
                                order_id=order_id,
                                item_id=item_id,
                                buyer_id=buyer_id_to_save,
                                buyer_nick=buyer_nick_to_save,  # 传递买家昵称
                                sid=sid,
                                spec_name=spec_name,
                                spec_value=spec_value,
                                spec_name_2=spec_name_2,
                                spec_value_2=spec_value_2,
                                quantity=quantity,
                                amount=amount,
                                cookie_id=self.cookie_id,
                                order_status=order_status_to_save,  # 外部详情状态仅在不会回退内部状态时写库
                                platform_created_at=platform_created_at,
                                platform_paid_at=platform_paid_at,
                                platform_completed_at=platform_completed_at
                            )
                            
                            # 使用订单状态处理器设置状态
                            logger.info(f"【{self.cookie_id}】检查订单状态处理器调用条件: success={success}, handler_exists={self.order_status_handler is not None}")
                            if success and self.order_status_handler:
                                logger.info(f"【{self.cookie_id}】准备调用订单状态处理器.handle_order_detail_fetched_status: {order_id}")
                                try:
                                    handler_result = self.order_status_handler.handle_order_detail_fetched_status(
                                        order_id=order_id,
                                        cookie_id=self.cookie_id,
                                        context="订单详情已拉取"
                                    )
                                    logger.info(f"【{self.cookie_id}】订单状态处理器.handle_order_detail_fetched_status返回结果: {handler_result}")
                                    
                                    # 处理待处理队列
                                    logger.info(f"【{self.cookie_id}】准备调用订单状态处理器.on_order_details_fetched: {order_id}")
                                    self.order_status_handler.on_order_details_fetched(order_id)
                                    logger.info(f"【{self.cookie_id}】订单状态处理器.on_order_details_fetched调用成功: {order_id}")
                                except Exception as e:
                                    logger.error(f"【{self.cookie_id}】订单状态处理器调用失败: {self._safe_str(e)}")
                                    import traceback
                                    logger.error(f"【{self.cookie_id}】详细错误信息: {traceback.format_exc()}")
                            else:
                                logger.warning(f"【{self.cookie_id}】订单状态处理器调用条件不满足: success={success}, handler_exists={self.order_status_handler is not None}")

                            if success:
                                logger.info(f"【{self.cookie_id}】订单信息已保存到数据库: {order_id}")
                                print(f"💾 【{self.cookie_id}】订单 {order_id} 信息已保存到数据库")
                            else:
                                logger.warning(f"【{self.cookie_id}】订单信息保存失败: {order_id}")

                    except Exception as db_e:
                        logger.error(f"【{self.cookie_id}】保存订单信息到数据库失败: {self._safe_str(db_e)}")

                    return result
                else:
                    logger.warning(f"【{self.cookie_id}】订单详情获取失败: {order_id}")
                    return None

            except Exception as e:
                logger.error(f"【{self.cookie_id}】获取订单详情异常: {self._safe_str(e)}")
                return None

    async def _auto_delivery(self, item_id: str, item_title: str = None, order_id: str = None, send_user_id: str = None,
                             chat_id: str = None, send_user_name: str = None, include_meta: bool = False,
                             data_preview_index: int = 0, delivery_unit_index: int = 1):
        """自动发货功能 - 匹配规则并准备发货内容，不直接提交副作用。"""
        try:
            matched_rule_context = None
            match_mode_context = None

            def build_result(success: bool, content: str = None, error: str = None, matched_rule: dict = None,
                             match_mode_value: str = None, delivery_steps_value: list = None):
                order_spec_mode_value = 'no_spec'
                item_config_mode_value = 'no_spec'
                rule_spec_mode_value = None

                try:
                    order_spec_mode_value = _get_order_spec_mode()
                except Exception:
                    pass

                try:
                    rule_spec_mode_value = _get_rule_spec_mode(matched_rule) if matched_rule else None
                except Exception:
                    pass

                try:
                    item_config_mode_value = 'spec_enabled' if item_config_multi_spec else 'no_spec'
                except Exception:
                    pass

                if include_meta:
                    return {
                        "success": bool(success),
                        "content": content if success else None,
                        "error": error if not success else None,
                        "rule_id": matched_rule.get('id') if matched_rule else None,
                        "rule_keyword": matched_rule.get('keyword') if matched_rule else None,
                        "card_type": matched_rule.get('card_type') if matched_rule else None,
                        "match_mode": match_mode_value,
                        "order_spec_mode": order_spec_mode_value,
                        "rule_spec_mode": rule_spec_mode_value,
                        "item_config_mode": item_config_mode_value,
                        "card_id": matched_rule.get('card_id') if matched_rule else None,
                        "card_description": matched_rule.get('card_description') if matched_rule else None,
                        "delivery_steps": delivery_steps_value or [],
                        "data_card_pending_consume": False,
                        "data_line": None,
                        "data_reservation_id": None,
                        "data_reservation_status": None,
                        "delivery_unit_index": delivery_unit_index
                    }
                return content if success else None

            from db_manager import db_manager

            logger.info(f"开始自动发货检查: 商品ID={item_id}")

            # 获取商品详细信息
            item_info = None
            search_text = item_title  # 默认使用传入的标题

            if item_id and item_id != "未知商品":
                # 直接从数据库获取商品信息（发货时不再调用API）
                try:
                    logger.info(f"从数据库获取商品信息: {item_id}")
                    db_item_info = db_manager.get_item_info(self.cookie_id, item_id)
                    if db_item_info:
                        item_info = db_item_info
                        # 拼接商品标题和详情作为搜索文本
                        item_title_db = db_item_info.get('item_title', '') or ''
                        item_detail_db = db_item_info.get('item_detail', '') or ''

                        # 如果数据库中没有详情，尝试自动获取
                        if not item_detail_db.strip():
                            from config import config
                            auto_fetch_config = config.get('ITEM_DETAIL', {}).get('auto_fetch', {})

                            if auto_fetch_config.get('enabled', True):
                                logger.info(f"数据库中商品详情为空，尝试自动获取: {item_id}")
                                try:
                                    fetched_detail = await self.fetch_item_detail_from_api(item_id)
                                    if fetched_detail:
                                        # 保存获取到的详情
                                        await self.save_item_detail_only(item_id, fetched_detail)
                                        item_detail_db = fetched_detail
                                        logger.info(f"成功获取并保存商品详情: {item_id}")
                                    else:
                                        logger.warning(f"未能获取到商品详情: {item_id}")
                                except Exception as api_e:
                                    logger.warning(f"获取商品详情失败: {item_id}, 错误: {self._safe_str(api_e)}")
                            else:
                                logger.warning(f"自动获取商品详情功能已禁用，跳过: {item_id}")

                        # 组合搜索文本：商品标题 + 商品详情
                        search_parts = []
                        if item_title_db.strip():
                            search_parts.append(item_title_db.strip())
                        if item_detail_db.strip():
                            search_parts.append(item_detail_db.strip())

                        if search_parts:
                            search_text = ' '.join(search_parts)
                            logger.info(f"使用数据库商品标题+详情作为搜索文本: 标题='{item_title_db}', 详情长度={len(item_detail_db)}")
                            logger.warning(f"完整搜索文本: {search_text[:200]}...")
                        else:
                            logger.warning(f"数据库中商品标题和详情都为空: {item_id}")
                            search_text = item_title or item_id
                    else:
                        logger.warning(f"数据库中未找到商品信息: {item_id}")
                        search_text = item_title or item_id

                except Exception as db_e:
                    logger.warning(f"从数据库获取商品信息失败: {self._safe_str(db_e)}")
                    search_text = item_title or item_id

            if not search_text:
                search_text = item_id or "未知商品"

            logger.info(f"使用搜索文本匹配发货规则: {search_text[:100]}...")

            item_config_multi_spec = db_manager.get_item_multi_spec_status(self.cookie_id, item_id)
            spec_name = ''
            spec_value = ''
            spec_name_2 = ''
            spec_value_2 = ''

            def _apply_spec_from_order_detail(order_detail_data) -> bool:
                nonlocal spec_name, spec_value, spec_name_2, spec_value_2
                if not order_detail_data or not isinstance(order_detail_data, dict):
                    return False
                spec_name = (order_detail_data.get('spec_name') or '').strip()
                spec_value = (order_detail_data.get('spec_value') or '').strip()
                spec_name_2 = (order_detail_data.get('spec_name_2') or '').strip()
                spec_value_2 = (order_detail_data.get('spec_value_2') or '').strip()
                return bool(spec_name and spec_value)

            def _get_order_spec_mode() -> str:
                has_first_spec = bool(spec_name and spec_value)
                has_second_spec = bool(spec_name_2 and spec_value_2)

                if has_first_spec and has_second_spec:
                    return 'two_spec'
                if has_first_spec:
                    return 'one_spec'
                return 'no_spec'

            def _get_rule_spec_mode(rule: dict) -> str:
                if not rule:
                    return 'no_spec'

                rule_spec_name = (rule.get('spec_name') or '').strip()
                rule_spec_value = (rule.get('spec_value') or '').strip()
                rule_spec_name_2 = (rule.get('spec_name_2') or '').strip()
                rule_spec_value_2 = (rule.get('spec_value_2') or '').strip()

                if rule_spec_name and rule_spec_value and rule_spec_name_2 and rule_spec_value_2:
                    return 'two_spec'
                if rule_spec_name and rule_spec_value:
                    return 'one_spec'
                return 'no_spec'

            # 只要有订单ID就尝试拉取订单详情；规格商品缺失规格时自动重试，提升精确命中率
            if order_id:
                logger.info(f"检测到订单ID，获取订单详情用于规则匹配: {order_id}")
                max_detail_attempts = 3 if item_config_multi_spec else 1
                for attempt in range(1, max_detail_attempts + 1):
                    try:
                        force_refresh = attempt > 1
                        if force_refresh:
                            logger.info(f"订单规格信息缺失，开始强刷重试 ({attempt}/{max_detail_attempts}): {order_id}")

                        order_detail = await self.fetch_order_detail_info(
                            order_id,
                            item_id,
                            send_user_id,
                            force_refresh=force_refresh
                        )

                        if _apply_spec_from_order_detail(order_detail):
                            logger.info(f"获取到规格信息: {spec_name} = {spec_value}")
                            if spec_name_2 and spec_value_2:
                                logger.info(f"获取到规格2信息: {spec_name_2} = {spec_value_2}")
                            break

                        if item_config_multi_spec:
                            logger.warning(
                                f"订单详情已获取但未解析到有效规格信息 (尝试 {attempt}/{max_detail_attempts})"
                            )
                        else:
                            logger.info("无规格商品未解析到规格信息，按普通规则继续")
                    except Exception as e:
                        logger.error(
                            f"获取订单详情失败 (尝试 {attempt}/{max_detail_attempts}): {self._safe_str(e)}"
                        )

                    if attempt < max_detail_attempts:
                        await asyncio.sleep(0.6)

                if _get_order_spec_mode() == 'no_spec':
                    try:
                        cached_order = db_manager.get_order_by_id(order_id)
                        if cached_order and _apply_spec_from_order_detail(cached_order):
                            logger.warning(
                                f"订单 {order_id} 从数据库缓存恢复规格成功: "
                                f"{spec_name}:{spec_value}"
                            )
                    except Exception as cache_e:
                        logger.warning(f"订单缓存规格恢复失败: {self._safe_str(cache_e)}")
            else:
                logger.warning("当前无订单ID，跳过订单详情拉取，将仅基于商品文本匹配规则")

            order_spec_mode = _get_order_spec_mode()
            item_config_mode = 'spec_enabled' if item_config_multi_spec else 'no_spec'

            if order_spec_mode != 'no_spec' and item_info is not None and not item_config_multi_spec:
                logger.warning(
                    f"商品已配置为无规格，忽略订单解析到的规格并按普通规则匹配: "
                    f"order_spec_mode={order_spec_mode}, item_id={item_id or 'unknown'}, "
                    f"order_id={order_id or 'unknown'}, spec={spec_name}:{spec_value}"
                )
                spec_name = ''
                spec_value = ''
                spec_name_2 = ''
                spec_value_2 = ''
                order_spec_mode = _get_order_spec_mode()
            elif order_spec_mode == 'no_spec' and item_config_multi_spec:
                block_reason = (
                    f"商品已开启规格匹配，但订单未解析到有效规格信息，已阻断自动发货: "
                    f"order_id={order_id or 'unknown'}, item_id={item_id or 'unknown'}"
                )
                logger.error(block_reason)
                return build_result(False, error=block_reason, match_mode_value='blocked_no_spec_parsed')

            logger.info(
                f"规格模式判定完成: order_spec_mode={order_spec_mode}, "
                f"item_config_mode={item_config_mode}"
            )

            delivery_rules = []
            if order_spec_mode == 'two_spec':
                match_mode = 'two_spec_exact'
                match_mode_context = match_mode
                logger.info(
                    f"尝试精确匹配两组规格发货规则: {search_text[:50]}... "
                    f"[{spec_name}:{spec_value}, {spec_name_2}:{spec_value_2}]"
                )
                delivery_rules = db_manager.get_delivery_rules_by_keyword_and_spec(
                    search_text,
                    spec_name,
                    spec_value,
                    spec_name_2,
                    spec_value_2,
                    user_id=self.user_id,
                    expected_mode='two_spec'
                )
                if not delivery_rules:
                    error_message = "两组规格订单未找到匹配的发货规则"
                    logger.warning(f"{error_message}: {search_text[:50]}...")
                    return build_result(False, error=error_message, match_mode_value='blocked_no_rule')
            elif order_spec_mode == 'one_spec':
                match_mode = 'one_spec_exact'
                match_mode_context = match_mode
                logger.info(
                    f"尝试精确匹配一组规格发货规则: {search_text[:50]}... "
                    f"[{spec_name}:{spec_value}]"
                )
                delivery_rules = db_manager.get_delivery_rules_by_keyword_and_spec(
                    search_text,
                    spec_name,
                    spec_value,
                    spec_name_2,
                    spec_value_2,
                    user_id=self.user_id,
                    expected_mode='one_spec'
                )
                if not delivery_rules:
                    logger.warning(
                        f"一组规格订单未找到精确规格规则，尝试降级匹配普通发货规则: {search_text[:50]}..."
                    )
                    fallback_rules = db_manager.get_delivery_rules_by_keyword(
                        search_text,
                        user_id=self.user_id,
                        only_non_multi_spec=True
                    )
                    if not fallback_rules:
                        error_message = "一组规格订单未找到匹配的发货规则"
                        logger.warning(f"{error_message}: {search_text[:50]}...")
                        return build_result(False, error=error_message, match_mode_value='blocked_no_rule')
                    if len(fallback_rules) != 1:
                        block_reason = (
                            f"一组规格订单精确匹配失败后，普通规则兜底匹配到{len(fallback_rules)}条，"
                            f"已阻断自动发货以避免错发: order_id={order_id or 'unknown'}, "
                            f"item_id={item_id or 'unknown'}"
                        )
                        logger.error(block_reason)
                        return build_result(False, error=block_reason, match_mode_value='blocked_multiple_no_spec_rules')
                    delivery_rules = fallback_rules
                    match_mode = 'one_spec_fallback_no_spec'
                    match_mode_context = match_mode
                    logger.warning(
                        f"一组规格订单已降级命中唯一普通规则: order_id={order_id or 'unknown'}, "
                        f"item_id={item_id or 'unknown'}, rule_id={delivery_rules[0].get('id')}"
                    )
            else:
                match_mode = 'no_spec_match'
                match_mode_context = match_mode
                logger.info(f"无规格订单，尝试匹配普通发货规则: {search_text[:50]}...")
                delivery_rules = db_manager.get_delivery_rules_by_keyword(
                    search_text,
                    user_id=self.user_id,
                    only_non_multi_spec=True
                )
                if not delivery_rules:
                    error_message = "无规格订单未找到匹配的普通发货规则"
                    logger.warning(f"{error_message}: {search_text[:50]}...")
                    return build_result(False, error=error_message, match_mode_value='blocked_no_rule')
                if len(delivery_rules) != 1:
                    block_reason = (
                        f"无规格订单匹配到{len(delivery_rules)}条普通规则，已阻断自动发货以避免错发: "
                        f"order_id={order_id or 'unknown'}, item_id={item_id or 'unknown'}"
                    )
                    logger.error(block_reason)
                    return build_result(False, error=block_reason, match_mode_value='blocked_multiple_no_spec_rules')

            # 使用第一个匹配的规则（按关键字长度降序排列，优先匹配更精确的规则）
            rule = delivery_rules[0]
            matched_rule_context = rule
            rule_spec_mode = _get_rule_spec_mode(rule)

            logger.info(
                f"规则模式判定完成: order_spec_mode={order_spec_mode}, rule_spec_mode={rule_spec_mode}, "
                f"match_mode={match_mode}, rule_id={rule.get('id')}"
            )

            allow_one_spec_fallback = (
                match_mode == 'one_spec_fallback_no_spec'
                and order_spec_mode == 'one_spec'
                and rule_spec_mode == 'no_spec'
            )

            if rule_spec_mode != order_spec_mode and not allow_one_spec_fallback:
                block_reason = (
                    f"订单规格模式与命中规则模式不一致，已阻断自动发货: "
                    f"order_spec_mode={order_spec_mode}, rule_spec_mode={rule_spec_mode}, "
                    f"order_id={order_id or 'unknown'}, item_id={item_id or 'unknown'}, rule_id={rule.get('id')}"
                )
                logger.error(block_reason)
                return build_result(False, error=block_reason, matched_rule=rule, match_mode_value='blocked_rule_mode_mismatch')

            # 注释掉自动发货时的商品信息保存逻辑，避免重复保存导致item_detail字段内容累积
            # 商品信息应该在商品列表获取、订单详情获取等其他环节已经保存过了
            # 保存商品信息到数据库（需要有商品标题才保存）
            # # 尝试获取商品标题
            # item_title_for_save = None
            # try:
            #     from db_manager import db_manager
            #     db_item_info = db_manager.get_item_info(self.cookie_id, item_id)
            #     if db_item_info:
            #         item_title_for_save = db_item_info.get('item_title', '').strip()
            # except:
            #     pass
            # 
            # # 如果有商品标题，则保存商品信息
            # if item_title_for_save:
            #     await self.save_item_info_to_db(item_id, search_text, item_title_for_save)
            # else:
            #     logger.warning(f"跳过保存商品信息：缺少商品标题 - {item_id}")

            # 详细的匹配结果日志
            if order_spec_mode == 'two_spec':
                rule_spec_info = f"{rule['spec_name']}:{rule['spec_value']}, {rule['spec_name_2']}:{rule['spec_value_2']}"
                order_spec_info = f"{spec_name}:{spec_value}, {spec_name_2}:{spec_value_2}"
                logger.info(f"🎯 精确匹配两组规格发货规则: {rule['keyword']} -> {rule['card_name']} [{rule_spec_info}]")
                logger.info(f"📋 订单规格: {order_spec_info} ✅ 匹配卡券规格: {rule_spec_info}")
            elif match_mode == 'one_spec_fallback_no_spec':
                order_spec_info = f"{spec_name}:{spec_value}"
                logger.warning(
                    f"⚠️ 单规格订单降级匹配普通发货规则: {rule['keyword']} -> {rule['card_name']} ({rule['card_type']})"
                )
                logger.warning(f"📋 订单规格: {order_spec_info}，精确规格未命中，已降级到普通规则")
            elif order_spec_mode == 'one_spec':
                rule_spec_info = f"{rule['spec_name']}:{rule['spec_value']}"
                order_spec_info = f"{spec_name}:{spec_value}"
                logger.info(f"🎯 精确匹配一组规格发货规则: {rule['keyword']} -> {rule['card_name']} [{rule_spec_info}]")
                logger.info(f"📋 订单规格: {order_spec_info} ✅ 匹配卡券规格: {rule_spec_info}")
            else:
                logger.info(f"✅ 匹配无规格发货规则: {rule['keyword']} -> {rule['card_name']} ({rule['card_type']})")

            # 获取延时设置
            delay_seconds = rule.get('card_delay_seconds', 0)

            # 执行延时（只准备内容，不执行确认发货）
            if delay_seconds and delay_seconds > 0:
                logger.info(f"检测到发货延时设置: {delay_seconds}秒，开始延时...")
                await asyncio.sleep(delay_seconds)
                logger.info(f"延时完成")

            # 检查是否存在订单ID，只有存在订单ID才处理发货内容
            if order_id:
                # 保存订单基本信息到数据库（如果还没有详细信息）
                try:
                    from db_manager import db_manager

                    # 过滤掉买家订单（如果send_user_id是自己，说明是自己购买的订单）
                    if send_user_id and send_user_id == self.myid:
                        logger.info(f"【{self.cookie_id}】跳过买家订单 {order_id}，buyer_id={send_user_id} 等于自己的ID")
                        # 不保存买家订单，但继续返回发货内容（如果有的话）
                    else:
                        # 检查cookie_id是否在cookies表中存在
                        cookie_info = db_manager.get_cookie_by_id(self.cookie_id)
                        if not cookie_info:
                            logger.warning(f"Cookie ID {self.cookie_id} 不存在于cookies表中，丢弃订单 {order_id}")
                        else:
                            existing_order = db_manager.get_order_by_id(order_id)
                            if not existing_order:
                                # 插入基本订单信息
                                success = db_manager.insert_or_update_order(
                                    order_id=order_id,
                                    item_id=item_id,
                                    buyer_id=send_user_id,
                                    buyer_nick=send_user_name,
                                    cookie_id=self.cookie_id
                                )

                                # 使用订单状态处理器设置状态
                                if success and self.order_status_handler:
                                    try:
                                        self.order_status_handler.handle_order_basic_info_status(
                                            order_id=order_id,
                                            cookie_id=self.cookie_id,
                                            context="自动发货-基本信息"
                                        )
                                    except Exception as e:
                                        logger.error(f"【{self.cookie_id}】订单状态处理器调用失败: {self._safe_str(e)}")

                                if success:
                                    logger.info(f"保存基本订单信息到数据库: {order_id}")
                except Exception as db_e:
                    logger.error(f"保存基本订单信息失败: {self._safe_str(db_e)}")

                # 开始处理发货内容
                logger.info(f"开始处理发货内容，规则: {rule['keyword']} -> {rule['card_name']} ({rule['card_type']})")

                delivery_content = None
                data_line = None
                data_reservation = None

                # 根据卡券类型处理发货内容
                if rule['card_type'] == 'api':
                    # API类型：调用API获取内容，传入订单和商品信息用于动态参数替换
                    delivery_content = await self._get_api_card_content(rule, order_id, item_id, send_user_id, spec_name, spec_value)

                elif rule['card_type'] == 'yifan_api':
                    # 亦凡卡劵API类型：调用亦凡API获取内容
                    delivery_content = await self._get_yifan_api_card_content(rule, order_id, item_id, send_user_id, chat_id)

                elif rule['card_type'] == 'text':
                    # 固定文字类型：直接使用文字内容
                    delivery_content = rule['text_content']

                elif rule['card_type'] == 'data':
                    # 批量数据类型：先原子预占，再发送，避免并发订单拿到同一条卡密
                    data_reservation = db_manager.reserve_batch_data(
                        card_id=rule['card_id'],
                        order_id=order_id,
                        unit_index=delivery_unit_index,
                        cookie_id=self.cookie_id,
                        buyer_id=send_user_id,
                    )
                    if data_reservation:
                        data_line = data_reservation.get('reserved_content')
                        delivery_content = data_line
                    else:
                        delivery_content = None

                elif rule['card_type'] == 'image':
                    # 图片类型：返回图片发送标记，包含卡券ID
                    image_url = rule.get('image_url')
                    if image_url:
                        delivery_content = f"__IMAGE_SEND__{rule['card_id']}|{image_url}"
                        logger.info(f"准备发送图片: {image_url} (卡券ID: {rule['card_id']})")
                    else:
                        logger.error(f"图片卡券缺少图片URL: 卡券ID={rule['card_id']}")
                        delivery_content = None

                if delivery_content:
                    delivery_steps = self._build_delivery_steps(delivery_content, rule.get('card_description', ''))
                    if not delivery_steps:
                        logger.warning(f"发货步骤构建失败: 规则ID={rule['id']}")
                        return build_result(False, error=f"发货步骤构建失败: 规则ID={rule['id']}", matched_rule=rule, match_mode_value=match_mode)

                    if len(delivery_steps) == 1 and delivery_steps[0].get('type') == 'text':
                        final_content = delivery_steps[0].get('content') or ''
                    else:
                        final_content = delivery_content

                    logger.info(f"自动发货内容准备成功: 规则ID={rule['id']}, 步骤数={len(delivery_steps)}")

                    result = build_result(
                        True,
                        content=final_content,
                        matched_rule=rule,
                        match_mode_value=match_mode,
                        delivery_steps_value=delivery_steps
                    )
                    if include_meta and isinstance(result, dict):
                        result['card_id'] = rule.get('card_id')
                        result['data_card_pending_consume'] = bool(rule['card_type'] == 'data')
                        result['data_line'] = data_line
                        result['data_reservation_id'] = data_reservation.get('id') if data_reservation else None
                        result['data_reservation_status'] = data_reservation.get('status') if data_reservation else None
                        result['delivery_unit_index'] = delivery_unit_index
                    return result
                else:
                    logger.warning(f"获取发货内容失败: 规则ID={rule['id']}")
                    return build_result(False, error=f"获取发货内容失败: 规则ID={rule['id']}", matched_rule=rule, match_mode_value=match_mode)
            else:
                # 没有订单ID，记录日志但不处理发货内容
                logger.info(f"⚠️ 未检测到订单ID，跳过发货内容处理。规则: {rule['keyword']} -> {rule['card_name']} ({rule['card_type']})")
                return build_result(False, error="未检测到订单ID，跳过发货内容处理", matched_rule=rule, match_mode_value=match_mode)

        except Exception as e:
            error_text = self._safe_str(e)
            if matched_rule_context:
                rule_label = matched_rule_context.get('keyword') or f"规则ID={matched_rule_context.get('id')}"
                card_type = matched_rule_context.get('card_type') or 'unknown'
                error_message = f"规则已命中({rule_label})，但{card_type}发货处理异常: {error_text}"
            else:
                error_message = f"自动发货异常: {error_text}"
            logger.error(error_message)
            return build_result(
                False,
                error=error_message,
                matched_rule=matched_rule_context,
                match_mode_value=match_mode_context
            )



    def _process_delivery_content_with_description(self, delivery_content: str, card_description: str) -> str:
        """处理发货内容和备注信息，实现变量替换"""
        try:
            # 如果没有备注信息，直接返回发货内容
            if not card_description or not card_description.strip():
                return delivery_content

            # 替换备注中的变量
            processed_description = card_description.replace('{DELIVERY_CONTENT}', delivery_content)

            # 如果备注中包含变量替换，返回处理后的备注
            if '{DELIVERY_CONTENT}' in card_description:
                return processed_description
            else:
                # 如果备注中没有变量，将备注和发货内容组合
                return f"{processed_description}\n\n{delivery_content}"

        except Exception as e:
            logger.error(f"处理备注信息失败: {e}")
            # 出错时返回原始发货内容
            return delivery_content

    def _build_delivery_steps(self, delivery_content: str, card_description: str):
        """构建发货步骤，确保图片卡券和备注按正确顺序发送。"""
        try:
            raw_content = delivery_content if isinstance(delivery_content, str) else str(delivery_content or '')
            description = (card_description or '').strip()
            steps = []

            if raw_content and not raw_content.startswith("__IMAGE_SEND__"):
                final_text = self._process_delivery_content_with_description(raw_content, description)
                return [{'type': 'text', 'content': final_text}] if final_text else []

            def append_text_step(text: str):
                text = (text or '').strip()
                if text:
                    steps.append({'type': 'text', 'content': text})

            def append_payload_step(payload: str):
                payload = (payload or '').strip()
                if payload:
                    if payload.startswith("__IMAGE_SEND__"):
                        steps.append({'type': 'image', 'content': payload})
                    else:
                        steps.append({'type': 'text', 'content': payload})

            if not description:
                append_payload_step(raw_content)
                return steps

            if '{DELIVERY_CONTENT}' in description:
                placeholder = '{DELIVERY_CONTENT}'
                segments = description.split(placeholder)
                for index, segment in enumerate(segments):
                    append_text_step(segment)
                    if index < len(segments) - 1:
                        append_payload_step(raw_content)
                return steps

            append_text_step(description)
            append_payload_step(raw_content)
            return steps
        except Exception as e:
            logger.error(f"构建发货步骤失败: {e}")
            fallback_content = delivery_content if isinstance(delivery_content, str) else str(delivery_content or '')
            if fallback_content:
                return [{'type': 'image' if fallback_content.startswith("__IMAGE_SEND__") else 'text', 'content': fallback_content}]
            return []

    def _can_batch_text_delivery(self, delivery_steps, card_type: str = None) -> bool:
        """仅将 text/data/api 的单条纯文本步骤纳入批量合并发送。"""
        normalized_card_type = str(card_type or '').strip().lower()
        if normalized_card_type not in {'text', 'data', 'api'}:
            return False

        steps = delivery_steps or []
        if len(steps) != 1:
            return False

        step = steps[0] or {}
        if step.get('type') != 'text':
            return False

        return bool((step.get('content') or '').strip())

    def _format_delivery_unit_text(self, text: str, unit_index: int, total_units: int) -> str:
        """为批量发货文本添加全局连续序号。"""
        safe_total_units = max(1, int(total_units or 1))
        safe_unit_index = max(1, int(unit_index or 1))
        prefix = f"【{safe_unit_index}/{safe_total_units}】"
        content = (text or '').strip()
        return f"{prefix}{content}" if content else prefix

    def _apply_delivery_unit_numbering(self, delivery_steps, unit_index: int, total_units: int, card_type: str = None):
        """为多数量订单中的 text/data/api 步骤补充序号。"""
        if max(1, int(total_units or 1)) <= 1:
            return delivery_steps or []

        normalized_card_type = str(card_type or '').strip().lower()
        if normalized_card_type not in {'text', 'data', 'api'}:
            return delivery_steps or []

        steps = [dict(step or {}) for step in (delivery_steps or [])]
        prefix = f"【{max(1, int(unit_index or 1))}/{max(1, int(total_units or 1))}】"

        for step in steps:
            if step.get('type') == 'text':
                step['content'] = f"{prefix}{(step.get('content') or '').strip()}"
                return steps

        return [{'type': 'text', 'content': prefix}] + steps

    def _build_delivery_send_groups(self, prepared_units, total_units: int,
                                    max_units_per_message: int = DELIVERY_BATCH_MAX_UNITS,
                                    max_chars_per_message: int = DELIVERY_BATCH_MAX_CHARS):
        """按数量和字符数双阈值生成发货发送批次。"""
        if max(1, int(total_units or 1)) <= 1:
            return [{
                'mode': 'single',
                'units': [prepared_unit],
                'delivery_steps': prepared_unit.get('delivery_steps') or [],
                'unit_count': 1,
                'char_count': 0,
            } for prepared_unit in sorted(prepared_units or [], key=lambda unit: int(unit.get('unit_index') or 0))]

        groups = []
        current_batch_units = []
        current_batch_chars = 0

        def flush_current_batch():
            nonlocal current_batch_units, current_batch_chars
            if not current_batch_units:
                return

            batched_text = '\n\n'.join(unit['batched_text'] for unit in current_batch_units)
            groups.append({
                'mode': 'batched_text',
                'units': list(current_batch_units),
                'delivery_steps': [{'type': 'text', 'content': batched_text}],
                'unit_count': len(current_batch_units),
                'char_count': len(batched_text),
            })
            current_batch_units = []
            current_batch_chars = 0

        for prepared_unit in sorted(prepared_units or [], key=lambda unit: int(unit.get('unit_index') or 0)):
            delivery_steps = prepared_unit.get('delivery_steps') or []
            rule_meta = prepared_unit.get('rule_meta') or {}
            card_type = prepared_unit.get('card_type') or rule_meta.get('card_type')

            if not self._can_batch_text_delivery(delivery_steps, card_type):
                flush_current_batch()
                numbered_steps = self._apply_delivery_unit_numbering(
                    delivery_steps,
                    prepared_unit.get('unit_index') or 1,
                    total_units,
                    card_type,
                )
                groups.append({
                    'mode': 'single',
                    'units': [prepared_unit],
                    'delivery_steps': numbered_steps,
                    'unit_count': 1,
                    'char_count': 0,
                })
                continue

            numbered_text = self._format_delivery_unit_text(
                delivery_steps[0].get('content') or '',
                prepared_unit.get('unit_index') or 1,
                total_units,
            )

            if len(numbered_text) > max_chars_per_message:
                flush_current_batch()
                logger.warning(
                    f"【{self.cookie_id}】发货单元 {prepared_unit.get('unit_index')} 文本长度 {len(numbered_text)} 超过批量阈值 {max_chars_per_message}，回退为单条发送"
                )
                groups.append({
                    'mode': 'single',
                    'units': [prepared_unit],
                    'delivery_steps': [{'type': 'text', 'content': numbered_text}],
                    'unit_count': 1,
                    'char_count': len(numbered_text),
                })
                continue

            separator_chars = 2 if current_batch_units else 0
            exceeds_unit_limit = len(current_batch_units) >= max_units_per_message
            exceeds_char_limit = current_batch_units and (
                current_batch_chars + separator_chars + len(numbered_text) > max_chars_per_message
            )

            if exceeds_unit_limit or exceeds_char_limit:
                flush_current_batch()

            prepared_unit_with_text = dict(prepared_unit)
            prepared_unit_with_text['batched_text'] = numbered_text
            current_batch_units.append(prepared_unit_with_text)
            current_batch_chars += (2 if len(current_batch_units) > 1 else 0) + len(numbered_text)

        flush_current_batch()
        return groups

    async def _send_delivery_steps(self, websocket, chat_id: str, user_id: str, delivery_steps, user_url: str = None,
                                   log_prefix: str = "自动发货", card_id: int = None):
        """按顺序发送发货步骤，支持文本与图片混排。"""
        steps = delivery_steps or []
        if not steps:
            raise ValueError("发货步骤为空")

        total_steps = len(steps)
        user_url = user_url or f'https://www.goofish.com/personal?userId={user_id}'

        for index, step in enumerate(steps, start=1):
            step_type = step.get('type')
            step_content = step.get('content') or ''

            if step_type == 'image':
                image_data = step_content.replace("__IMAGE_SEND__", "", 1)
                image_card_id = card_id
                image_url = image_data
                if "|" in image_data:
                    card_id_str, image_url = image_data.split("|", 1)
                    try:
                        image_card_id = int(card_id_str)
                    except ValueError:
                        logger.error(f"无效的卡券ID: {card_id_str}")
                        image_card_id = card_id

                await self.send_image_msg(websocket, chat_id, user_id, image_url, card_id=image_card_id)
                logger.info(
                    f"【{log_prefix}】步骤 {index}/{total_steps} 已向 {user_url} 发送图片: {image_url}"
                )
            else:
                await self.send_msg(websocket, chat_id, user_id, step_content)
                logger.info(
                    f"【{log_prefix}】步骤 {index}/{total_steps} 已向 {user_url} 发送文本内容"
                )

            if total_steps > 1 and index < total_steps:
                await asyncio.sleep(0.3)

    async def _get_api_card_content(self, rule, order_id=None, item_id=None, buyer_id=None, spec_name=None, spec_value=None, retry_count=0):
        """调用API获取卡券内容，支持动态参数替换和重试机制"""
        max_retries = 4

        if retry_count >= max_retries:
            logger.error(f"API调用失败，已达到最大重试次数({max_retries})")
            return None

        try:
            import aiohttp
            import json

            api_config = rule.get('api_config')
            if not api_config:
                logger.error(f"API配置为空，规则ID: {rule.get('id')}, 卡券名称: {rule.get('card_name')}")
                logger.warning(f"规则详情: {rule}")
                return None

            # 解析API配置
            if isinstance(api_config, str):
                api_config = json.loads(api_config)

            url = api_config.get('url')
            method = api_config.get('method', 'GET').upper()
            timeout = api_config.get('timeout', 10)
            headers = api_config.get('headers', '{}')
            params = api_config.get('params', '{}')

            # 解析headers和params
            if isinstance(headers, str):
                headers = json.loads(headers)
            if isinstance(params, str):
                params = json.loads(params)

            # 如果是POST请求且有动态参数，进行参数替换
            if method == 'POST' and params:
                params = await self._replace_api_dynamic_params(params, order_id, item_id, buyer_id, spec_name, spec_value)

            retry_info = f" (重试 {retry_count + 1}/{max_retries})" if retry_count > 0 else ""
            logger.info(f"调用API获取卡券: {method} {url}{retry_info}")
            if method == 'POST' and params:
                logger.warning(f"POST请求参数: {json.dumps(params, ensure_ascii=False)}")

            # 确保session存在
            if not self.session:
                await self.create_session()

            # 发起HTTP请求
            timeout_obj = aiohttp.ClientTimeout(total=timeout)

            if method == 'GET':
                async with self.session.get(url, headers=headers, params=params, timeout=timeout_obj) as response:
                    status_code = response.status
                    response_text = await response.text()
            elif method == 'POST':
                async with self.session.post(url, headers=headers, json=params, timeout=timeout_obj) as response:
                    status_code = response.status
                    response_text = await response.text()
            else:
                logger.error(f"不支持的HTTP方法: {method}")
                return None

            if status_code == 200:
                # 尝试解析JSON响应，如果失败则使用原始文本
                try:
                    result = json.loads(response_text)
                    # 如果返回的是对象，尝试提取常见的内容字段
                    if isinstance(result, dict):
                        content = result.get('data') or result.get('content') or result.get('card') or str(result)
                    else:
                        content = str(result)
                except Exception:
                    content = response_text

                logger.info(f"API调用成功，返回内容长度: {len(content)}")
                return content
            else:
                logger.warning(f"API调用失败: {status_code} - {response_text[:200]}...")

                # 如果是服务器错误(5xx)或请求超时，进行重试
                if status_code >= 500 or status_code == 408:
                    if retry_count < max_retries - 1:
                        wait_time = (retry_count + 1) * 2  # 递增等待时间: 2s, 4s, 6s
                        logger.info(f"等待 {wait_time} 秒后重试...")
                        await asyncio.sleep(wait_time)
                        return await self._get_api_card_content(rule, order_id, item_id, buyer_id, spec_name, spec_value, retry_count + 1)

                return None

        except (asyncio.TimeoutError, aiohttp.ClientError) as e:
            logger.warning(f"API调用网络异常: {self._safe_str(e)}")

            # 网络异常也进行重试
            if retry_count < max_retries - 1:
                wait_time = (retry_count + 1) * 2  # 递增等待时间
                logger.info(f"等待 {wait_time} 秒后重试...")
                await asyncio.sleep(wait_time)
                return await self._get_api_card_content(rule, order_id, item_id, buyer_id, spec_name, spec_value, retry_count + 1)
            else:
                logger.error(f"API调用网络异常，已达到最大重试次数: {self._safe_str(e)}")
                return None

        except Exception as e:
            logger.error(f"API调用异常: {self._safe_str(e)}")
            return None

    async def _get_yifan_api_card_content(self, rule, order_id=None, item_id=None, buyer_id=None, chat_id=None):
        """调用亦凡卡劵API获取内容"""
        try:
            import hashlib
            import time
            import aiohttp
            import json
            from urllib.parse import urlencode

            # 获取API配置（存储在api_config字段中）
            api_config = rule.get('api_config')
            if not api_config:
                logger.error(f"亦凡API配置为空，规则ID: {rule.get('id')}, 卡券名称: {rule.get('card_name')}")
                return None

            # 解析API配置
            if isinstance(api_config, str):
                api_config = json.loads(api_config)

            # 亦凡API配置直接存储在api_config字段中
            user_id = api_config.get('user_id')
            user_key = api_config.get('user_key')
            goods_id = api_config.get('goods_id')
            # 回调地址：优先使用卡券配置中的，如果没有则从全局配置读取，最后使用默认地址
            callback_url = (api_config.get('callback_url') or '').strip() or (YIFAN_API.get('callback_url') or '').strip() or 'http://116.196.116.76/yifan.php'
            require_account = api_config.get('require_account', False)

            if not user_id or not user_key or not goods_id:
                logger.error(f"亦凡API配置不完整，规则ID: {rule.get('id')}")
                return None

            # 如果需要充值账号，先进行账号询问和确认流程
            recharge_account = None
            if require_account:
                logger.info(f"亦凡API需要充值账号，开始询问流程")
                recharge_account = await self._ask_for_recharge_account(chat_id, buyer_id, rule, order_id, item_id)
                if recharge_account == "__WAITING_ACCOUNT__":
                    # 已设置等待状态，暂时中断发货流程
                    logger.info(f"已设置等待账号输入状态，暂停发货流程")
                    return None
                elif not recharge_account:
                    logger.error(f"获取充值账号失败，取消发货")
                    return None
                logger.info(f"获取到充值账号: {recharge_account}")

            # 构建API请求参数（所有值都转换为字符串，避免空格问题）
            timestamp = str(int(time.time()))
            params = {
                'userid': str(user_id),
                'timestamp': timestamp,
                'goodsid': str(goods_id),
                'buynum': '1',
            }

            # 如果有回调地址，添加到参数中（签名之前添加）
            if callback_url and callback_url.strip():
                params['callbackurl'] = str(callback_url).strip()

            # 如果有充值账号，添加到参数中
            if recharge_account:
                params['attach'] = str(recharge_account).strip()

            # 生成签名（确保参数值没有空格）
            # 1. 按照key的ascii码从小到大排序
            # 2. 空值不参与签名
            # 3. 使用QueryString格式拼接
            # 4. 尾部追加商户KEY
            # 5. MD5后转成32位小写
            sign_params = {k: str(v).strip() for k, v in params.items() if v is not None and str(v).strip() != ''}
            sorted_keys = sorted(sign_params.keys())
            sign_string = '&'.join([f"{key}={sign_params[key]}" for key in sorted_keys])
            sign_string += user_key
            
            logger.info(f"亦凡API签名字符串: {sign_string}")
            
            sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().lower()
            params['sign'] = sign

            logger.info(f"调用亦凡API: 商户ID={user_id}, 商品ID={goods_id}, 充值账号={recharge_account}, 回调URL={callback_url if callback_url else '无'}")

            # 确保session存在
            if not self.session:
                await self.create_session()

            # 发起API请求（使用data而不是json，发送form格式）
            api_url = "http://price.78shuk.top/dockapiv3/order/create"
            
            timeout_obj = aiohttp.ClientTimeout(total=30)
            async with self.session.post(api_url, data=params, timeout=timeout_obj) as response:
                status_code = response.status
                response_text = await response.text()

                logger.info(f"亦凡API返回状态码: {status_code}, 响应: {response_text}")

                if status_code == 200:
                    try:
                        result = json.loads(response_text)
                        # 根据亦凡API的返回格式处理：code为1表示成功
                        if result.get('code') == 1:
                            # 提取订单信息
                            data = result.get('data', {})
                            order_no = data.get('orderno', '')
                            us_order_no = data.get('usorderno', '')
                            
                            # 构建成功消息
                            success_msg = f"✅ 自动发货订单已提交成功\n\n"
                            success_msg += f"📋 订单信息：\n"
                            success_msg += f"平台订单号: {order_no}\n"
                            if us_order_no:
                                success_msg += f"商家订单号: {us_order_no}\n"
                            
                            # 添加查询地址（从全局配置读取）
                            query_url = YIFAN_API.get('query_url', 'http://116.196.116.76/yifan.php')
                            success_msg += f"\n🔍 查询卡密：\n"
                            success_msg += f"{query_url}\n"
                            success_msg += f"(输入订单号查询)\n"
                            
                            # 添加提示信息
                            success_msg += f"\n⏰ 温馨提示：\n"
                            success_msg += f"订单处理需要一定时间，请耐心等待。\n"
                            success_msg += f"如果1小时后仍未看到卡密信息，\n"
                            success_msg += f"请联系客服处理。"
                            
                            logger.info(f"亦凡API调用成功: order_no={order_no}")
                            
                            # 将亦凡订单号记录到数据库（用于后续回调匹配）
                            if order_id and order_no:
                                try:
                                    from db_manager import db_manager
                                    # 更新订单的亦凡订单号和chat_id
                                    db_manager.update_order_yifan_status(
                                        order_id=order_id,
                                        yifan_orderno=order_no,
                                        delivery_status='processing'
                                    )
                                    if chat_id:
                                        db_manager.update_order_chat_id(order_id, chat_id)
                                    logger.info(f"已记录亦凡订单信息: order_id={order_id}, yifan_orderno={order_no}")
                                except Exception as e:
                                    logger.error(f"记录亦凡订单信息失败: {e}")
                            
                            return success_msg
                        else:
                            # code不为1，下单失败，需要通知用户
                            error_msg = result.get('msg', '未知错误')
                            logger.error(f"亦凡API调用失败: code={result.get('code')}, msg={error_msg}")
                            
                            # 发送通知给用户
                            if chat_id and buyer_id:
                                from db_manager import db_manager
                                notification_msg = f"❌ 自动发货失败\n错误信息: {error_msg}\n请联系客服处理"
                                await self.send_notification("系统", buyer_id, notification_msg, item_id or "unknown", chat_id)
                            
                            return None
                    except Exception as e:
                        logger.error(f"解析亦凡API返回失败: {self._safe_str(e)}")
                        return None
                else:
                    logger.error(f"亦凡API调用失败: HTTP {status_code} - {response_text[:200]}")
                    return None

        except Exception as e:
            logger.error(f"亦凡API调用异常: {self._safe_str(e)}")
            return None

    async def _call_yifan_api_with_account(self, rule, account, order_id=None, item_id=None, buyer_id=None, chat_id=None):
        """使用确认的账号调用亦凡API"""
        try:
            import hashlib
            import time
            import aiohttp
            import json

            # 获取API配置
            api_config = rule.get('api_config')
            if not api_config:
                logger.error(f"亦凡API配置为空")
                return None

            # 解析API配置
            if isinstance(api_config, str):
                api_config = json.loads(api_config)

            # 亦凡API配置直接存储在api_config字段中
            user_id = api_config.get('user_id')
            user_key = api_config.get('user_key')
            goods_id = api_config.get('goods_id')
            callback_url = api_config.get('callback_url', '')

            if not user_id or not user_key or not goods_id:
                logger.error(f"亦凡API配置不完整")
                return None

            # 构建API请求参数（所有值都转换为字符串，避免空格问题）
            timestamp = str(int(time.time()))
            params = {
                'userid': str(user_id),
                'timestamp': timestamp,
                'goodsid': str(goods_id),
                'buynum': '1',
                'attach': str(account).strip()  # 充值账号，去除首尾空格
            }

            # 如果有回调地址，添加到参数中（签名之前添加）
            if callback_url and callback_url.strip():
                params['callbackurl'] = str(callback_url).strip()

            # 生成签名（确保参数值没有空格）
            sign_params = {k: str(v).strip() for k, v in params.items() if v is not None and str(v).strip() != ''}
            sorted_keys = sorted(sign_params.keys())
            sign_string = '&'.join([f"{key}={sign_params[key]}" for key in sorted_keys])
            sign_string += user_key
            
            logger.info(f"亦凡API签名字符串: {sign_string}")
            
            sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().lower()
            params['sign'] = sign

            logger.info(f"调用亦凡API: 商户ID={user_id}, 商品ID={goods_id}, 充值账号={account}, 回调URL={callback_url if callback_url else '无'}")

            # 确保session存在
            if not self.session:
                await self.create_session()

            # 发起API请求（使用data而不是json，发送form格式）
            api_url = "http://price.78shuk.top/dockapiv3/order/create"
            
            timeout_obj = aiohttp.ClientTimeout(total=30)
            async with self.session.post(api_url, data=params, timeout=timeout_obj) as response:
                status_code = response.status
                response_text = await response.text()

                logger.info(f"亦凡API返回状态码: {status_code}, 响应: {response_text}")

                if status_code == 200:
                    try:
                        result = json.loads(response_text)
                        if result.get('code') == 1:
                            # 下单成功
                            data = result.get('data', {})
                            order_no = data.get('orderno', '')
                            us_order_no = data.get('usorderno', '')
                            
                            success_msg = f"✅ 下单成功\n"
                            success_msg += f"订单号: {order_no}\n"
                            if us_order_no:
                                success_msg += f"用户订单号: {us_order_no}\n"
                            success_msg += f"充值账号: {account}\n"
                            success_msg += f"返回信息: {result.get('msg', '提交成功')}\n"
                            success_msg += f"有任何问题，请及时联系客服处理。"
                            
                            logger.info(f"亦凡API调用成功: {success_msg}")
                            return success_msg
                        else:
                            # 下单失败
                            error_msg = result.get('msg', '未知错误')
                            logger.error(f"亦凡API调用失败: code={result.get('code')}, msg={error_msg}")
                            
                            # 发送通知给用户
                            if chat_id and buyer_id:
                                from db_manager import db_manager
                                notification_msg = f"❌ 自动发货失败\n错误信息: {error_msg}\n请联系客服处理"
                                await self.send_notification("系统", buyer_id, notification_msg, item_id or "unknown", chat_id)
                            
                            return None
                    except Exception as e:
                        logger.error(f"解析亦凡API返回失败: {self._safe_str(e)}")
                        return None
                else:
                    logger.error(f"亦凡API调用失败: HTTP {status_code} - {response_text[:200]}")
                    return None

        except Exception as e:
            logger.error(f"亦凡API调用异常: {self._safe_str(e)}")
            return None

    async def _ask_for_recharge_account(self, chat_id, buyer_id, rule, order_id=None, item_id=None):
        """询问客户充值账号并设置等待状态（不阻塞）"""
        try:
            async with self.yifan_account_lock:
                # 设置等待状态
                self.yifan_account_waiting[chat_id] = {
                    'buyer_id': buyer_id,
                    'rule': rule,
                    'order_id': order_id,
                    'item_id': item_id,
                    'state': 'waiting_account',  # waiting_account 或 waiting_confirm
                    'account': None,
                    'create_time': time.time(),
                    'retry_count': 0
                }
            
            # 发送询问消息
            ask_message = "请单独发送您的充值账号，不要有任何其他的文字。如果因为您输错的原因导致错误下单，概不退款。"
            await self.send_msg(self.ws, chat_id, buyer_id, ask_message)
            logger.info(f"已发送充值账号询问消息，等待用户回复")
            
            # 返回特殊标记，表示需要等待用户输入
            return "__WAITING_ACCOUNT__"

        except Exception as e:
            logger.error(f"询问充值账号异常: {self._safe_str(e)}")
            return None

    async def _replace_api_dynamic_params(self, params, order_id=None, item_id=None, buyer_id=None, spec_name=None, spec_value=None):
        """替换API请求参数中的动态参数"""
        try:
            if not params or not isinstance(params, dict):
                return params

            # 获取订单和商品信息
            order_info = None
            item_info = None

            # 如果有订单ID，获取订单信息
            if order_id:
                try:
                    from db_manager import db_manager
                    # 尝试从数据库获取订单信息
                    order_info = db_manager.get_order_by_id(order_id)
                    if not order_info:
                        # 如果数据库中没有，尝试通过API获取
                        order_detail = await self.fetch_order_detail_info(order_id, item_id, buyer_id)
                        if order_detail:
                            order_info = order_detail
                            logger.warning(f"通过API获取到订单信息: {order_id}")
                        else:
                            logger.warning(f"无法获取订单信息: {order_id}")
                    else:
                        logger.warning(f"从数据库获取到订单信息: {order_id}")
                except Exception as e:
                    logger.warning(f"获取订单信息失败: {self._safe_str(e)}")

            # 如果有商品ID，获取商品信息
            if item_id:
                try:
                    from db_manager import db_manager
                    item_info = db_manager.get_item_info(self.cookie_id, item_id)
                    if item_info:
                        logger.warning(f"从数据库获取到商品信息: {item_id}")
                    else:
                        logger.warning(f"无法获取商品信息: {item_id}")
                except Exception as e:
                    logger.warning(f"获取商品信息失败: {self._safe_str(e)}")

            # 构建参数映射
            param_mapping = {
                'order_id': order_id or '',
                'item_id': item_id or '',
                'buyer_id': buyer_id or '',
                'cookie_id': self.cookie_id or '',
                'spec_name': spec_name or '',
                'spec_value': spec_value or '',
                'timestamp': str(int(time.time())),
            }

            # 从订单信息中提取参数
            if order_info:
                param_mapping.update({
                    'order_amount': str(order_info.get('amount', '')),
                    'order_quantity': str(order_info.get('quantity', '')),
                })

            # 从商品信息中提取参数
            if item_info:
                # 处理商品详情，如果是JSON字符串则提取detail字段
                item_detail = item_info.get('item_detail', '')
                if item_detail:
                    try:
                        # 尝试解析JSON
                        import json
                        detail_data = json.loads(item_detail)
                        if isinstance(detail_data, dict) and 'detail' in detail_data:
                            item_detail = detail_data['detail']
                    except (json.JSONDecodeError, TypeError):
                        # 如果不是JSON或解析失败，使用原始字符串
                        pass

                param_mapping.update({
                    'item_detail': item_detail,
                })

            # 递归替换参数
            replaced_params = self._recursive_replace_params(params, param_mapping)

            # 记录替换的参数
            replaced_keys = []
            for key, value in replaced_params.items():
                if isinstance(value, str) and '{' in str(params.get(key, '')):
                    replaced_keys.append(key)

            if replaced_keys:
                logger.info(f"API动态参数替换完成，替换的参数: {replaced_keys}")
                logger.warning(f"参数映射: {param_mapping}")

            return replaced_params

        except Exception as e:
            logger.error(f"替换API动态参数失败: {self._safe_str(e)}")
            return params

    def _recursive_replace_params(self, obj, param_mapping):
        """递归替换参数中的占位符"""
        if isinstance(obj, dict):
            result = {}
            for key, value in obj.items():
                result[key] = self._recursive_replace_params(value, param_mapping)
            return result
        elif isinstance(obj, list):
            return [self._recursive_replace_params(item, param_mapping) for item in obj]
        elif isinstance(obj, str):
            # 替换字符串中的占位符
            result = obj
            for param_key, param_value in param_mapping.items():
                placeholder = f"{{{param_key}}}"
                if placeholder in result:
                    result = result.replace(placeholder, str(param_value))
            return result
        else:
            return obj

    async def token_refresh_loop(self):
        """会话保活循环。轻量保活优先，重型恢复兜底。"""
        try:
            while True:
                try:
                    # 检查账号是否启用
                    from cookie_manager import manager as cookie_manager
                    if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
                        logger.info(f"【{self.cookie_id}】账号已禁用，停止Token刷新循环")
                        break

                    current_time = time.time()
                    if current_time - self.last_session_keepalive_time >= self.session_keepalive_interval:
                        logger.info(f"【{self.cookie_id}】开始执行轻量会话保活...")
                        keepalive_ok = await self.keep_session_alive()
                        if keepalive_ok:
                            await self._interruptible_sleep(60)
                            continue

                        keepalive_status = getattr(self, 'last_session_keepalive_status', None)
                        if keepalive_status == "auth_failed":
                            logger.warning(f"【{self.cookie_id}】轻量保活鉴权失败，尝试执行重型Token恢复流程")
                            new_token = await self.refresh_token()
                            if new_token:
                                self.last_session_keepalive_time = time.time()
                                logger.info(f"【{self.cookie_id}】重型Token恢复成功，主动关闭旧WebSocket以使用新Token重连")
                                await self._force_websocket_reconnect("重型Token恢复成功，准备使用新Token重连")
                                break

                            last_refresh_status = getattr(self, 'last_token_refresh_status', None)
                            benign_refresh_statuses = ("skipped_cooldown", "restarted_after_cookie_refresh")
                            if last_refresh_status not in benign_refresh_statuses:
                                scheduled_error_message = self._build_scheduled_token_refresh_error_message(last_refresh_status)
                                await self.send_token_refresh_notification(
                                    scheduled_error_message,
                                    "token_scheduled_refresh_failed"
                                )
                            logger.warning(
                                f"【{self.cookie_id}】重型Token恢复失败(status={last_refresh_status})，"
                                f"{self.token_retry_interval} 秒后重试"
                            )
                            await self._interruptible_sleep(self.token_retry_interval)
                        else:
                            logger.warning(
                                f"【{self.cookie_id}】轻量保活失败(status={keepalive_status})，"
                                f"{self.session_keepalive_retry_interval} 秒后重试"
                            )
                            await self._interruptible_sleep(self.session_keepalive_retry_interval)
                        continue
                    await self._interruptible_sleep(60)
                except asyncio.CancelledError:
                    # 收到取消信号，立即退出循环
                    logger.info(f"【{self.cookie_id}】Token刷新循环收到取消信号，准备退出")
                    raise
                except Exception as e:
                    logger.error(f"Token刷新循环出错: {self._safe_str(e)}")
                    # 出错后也等待1分钟再重试，使用可中断的sleep
                    try:
                        await self._interruptible_sleep(60)
                    except asyncio.CancelledError:
                        logger.info(f"【{self.cookie_id}】Token刷新循环在重试等待时收到取消信号，准备退出")
                        raise
        except asyncio.CancelledError:
            # 确保CancelledError被正确传播
            logger.info(f"【{self.cookie_id}】Token刷新循环已取消，正在退出...")
            raise
        finally:
            # 确保任务能正常结束
            logger.info(f"【{self.cookie_id}】Token刷新循环已退出")

    async def create_chat(self, ws, toid, item_id='891198795482'):
        msg = {
            "lwp": "/r/SingleChatConversation/create",
            "headers": {
                "mid": generate_mid()
            },
            "body": [
                {
                    "pairFirst": f"{toid}@goofish",
                    "pairSecond": f"{self.myid}@goofish",
                    "bizType": "1",
                    "extension": {
                        "itemId": item_id
                    },
                    "ctx": {
                        "appVersion": "1.0",
                        "platform": "web"
                    }
                }
            ]
        }
        await ws.send(json.dumps(msg))

    async def send_msg(self, ws, cid, toid, text):
        text = {
            "contentType": 1,
            "text": {
                "text": text
            }
        }
        text_base64 = str(base64.b64encode(json.dumps(text).encode('utf-8')), 'utf-8')
        msg = {
            "lwp": "/r/MessageSend/sendByReceiverScope",
            "headers": {
                "mid": generate_mid()
            },
            "body": [
                {
                    "uuid": generate_uuid(),
                    "cid": f"{cid}@goofish",
                    "conversationType": 1,
                    "content": {
                        "contentType": 101,
                        "custom": {
                            "type": 1,
                            "data": text_base64
                        }
                    },
                    "redPointPolicy": 0,
                    "extension": {
                        "extJson": "{}"
                    },
                    "ctx": {
                        "appVersion": "1.0",
                        "platform": "web"
                    },
                    "mtags": {},
                    "msgReadStatusSetting": 1
                },
                {
                    "actualReceivers": [
                        f"{toid}@goofish",
                        f"{self.myid}@goofish"
                    ]
                }
            ]
        }
        await ws.send(json.dumps(msg))

    async def init(self, ws):
        # 如果没有token或者token过期，获取新token
        token_refresh_attempted = False
        if not self.current_token or (time.time() - self.last_token_refresh_time) >= self.token_refresh_interval:
            logger.info(f"【{self.cookie_id}】获取初始token...")
            token_refresh_attempted = True

            await self.refresh_token()

        if not self.current_token:
            self.last_init_failure_type = 'init_auth_failed'
            self.last_init_failure_reason = self.last_token_refresh_status or 'token_missing_after_refresh'
            logger.error(f"【{self.cookie_id}】无法获取有效token，初始化鉴权失败")
            # 只有在没有尝试刷新token的情况下才发送通知，避免与refresh_token中的通知重复
            if not token_refresh_attempted:
                await self.send_token_refresh_notification("初始化时无法获取有效Token", "token_init_failed")
            else:
                logger.info(f"【{self.cookie_id}】由于刚刚尝试过token刷新，跳过重复的初始化失败通知")
            raise InitAuthError(f"Token获取失败(status={self.last_init_failure_reason})")

        self.last_init_failure_type = None
        self.last_init_failure_reason = None
        self.clear_init_auth_failure_state(self.cookie_id)
        self.init_auth_failures = 0

        msg = {
            "lwp": "/reg",
            "headers": {
                "cache-header": "app-key token ua wv",
                "app-key": APP_CONFIG.get('app_key'),
                "token": self.current_token,
                "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 DingTalk(2.1.5) OS(Windows/10) Browser(Chrome/133.0.0.0) DingWeb/2.1.5 IMPaaS DingWeb/2.1.5",
                "dt": "j",
                "wv": "im:3,au:3,sy:6",
                "sync": "0,0;0;0;",
                "did": self.device_id,
                "mid": generate_mid()
            }
        }
        await ws.send(json.dumps(msg))
        await asyncio.sleep(1)
        current_time = int(time.time() * 1000)
        msg = {
            "lwp": "/r/SyncStatus/ackDiff",
            "headers": {"mid": generate_mid()},
            "body": [
                {
                    "pipeline": "sync",
                    "tooLong2Tag": "PNM,1",
                    "channel": "sync",
                    "topic": "sync",
                    "highPts": 0,
                    "pts": current_time * 1000,
                    "seq": 0,
                    "timestamp": current_time
                }
            ]
        }
        await ws.send(json.dumps(msg))
        logger.info(f'【{self.cookie_id}】连接注册完成')

    async def list_all_conversations(self, cid: str, page_size: int = 20):
        """拉取指定会话的历史消息。"""
        headers = self._build_websocket_headers()
        async with await self._create_websocket_connection(headers) as websocket:
            await self.init(websocket)
            send_mid = generate_mid()
            request_msg = {
                "lwp": "/r/MessageManager/listUserMessages",
                "headers": {
                    "mid": send_mid
                },
                "body": [
                    f"{cid}@goofish",
                    False,
                    9007199254740991,
                    page_size,
                    False
                ]
            }
            history_messages = []

            async for raw_message in websocket:
                try:
                    message = json.loads(raw_message)
                except Exception:
                    continue

                try:
                    ack = {
                        "code": 200,
                        "headers": {
                            "mid": message.get("headers", {}).get("mid", generate_mid()),
                            "sid": message.get("headers", {}).get("sid", ""),
                        }
                    }
                    if 'app-key' in message.get("headers", {}):
                        ack["headers"]["app-key"] = message["headers"]["app-key"]
                    if 'ua' in message.get("headers", {}):
                        ack["headers"]["ua"] = message["headers"]["ua"]
                    if 'dt' in message.get("headers", {}):
                        ack["headers"]["dt"] = message["headers"]["dt"]
                    await websocket.send(json.dumps(ack))
                except Exception:
                    pass

                try:
                    if message.get('lwp') == "/s/vulcan":
                        await websocket.send(json.dumps(request_msg))
                        continue

                    recv_mid = message.get("headers", {}).get("mid", "")
                    if recv_mid != send_mid:
                        continue

                    body = message.get("body", {})
                    has_more = body.get("hasMore") == 1
                    next_cursor = body.get("nextCursor")
                    for user_message in body.get("userMessageModels", []):
                        extension = user_message.get("message", {}).get("extension", {})
                        custom_content = user_message.get("message", {}).get("content", {}).get("custom", {})
                        send_message_base64 = custom_content.get("data", "")
                        parsed_message = None
                        if send_message_base64:
                            try:
                                parsed_message = json.loads(base64.b64decode(send_message_base64).decode('utf-8'))
                            except Exception:
                                parsed_message = {"raw": send_message_base64}

                        history_messages.insert(0, {
                            "send_user_id": extension.get("senderUserId", ""),
                            "send_user_name": extension.get("reminderTitle", ""),
                            "message": parsed_message,
                        })

                    if has_more:
                        send_mid = generate_mid()
                        request_msg["headers"]["mid"] = send_mid
                        request_msg["body"][2] = next_cursor
                        await websocket.send(json.dumps(request_msg))
                    else:
                        return history_messages
                except Exception as e:
                    logger.warning(f"【{self.cookie_id}】拉取历史消息时发生异常: {self._safe_str(e)}")
                    return history_messages

        return []

    async def send_heartbeat(self, ws):
        """发送心跳包"""
        # 检查WebSocket连接状态，如果已关闭则不发送
        if ws.closed:
            raise ConnectionError("WebSocket连接已关闭，无法发送心跳")
        
        heartbeat_mid = generate_mid()
        msg = {
            "lwp": "/!",
            "headers": {
                "mid": heartbeat_mid
            }
        }
        # 添加超时保护，避免在WebSocket关闭时阻塞
        try:
            self.last_sent_heartbeat_mid = heartbeat_mid
            self.pending_heartbeat_mids.append(heartbeat_mid)
            await asyncio.wait_for(ws.send(json.dumps(msg)), timeout=2.0)
            self.last_heartbeat_time = time.time()
            logger.warning(f"【{self.cookie_id}】心跳包已发送 [ID:{heartbeat_mid}]")
        except asyncio.TimeoutError:
            raise ConnectionError("心跳发送超时，WebSocket可能已断开")
        except asyncio.CancelledError:
            # 如果被取消，立即重新抛出，不执行后续操作
            raise

    async def heartbeat_loop(self, ws):
        """心跳循环"""
        consecutive_failures = 0
        max_failures = 3  # 连续失败3次后停止心跳

        try:
            while True:
                try:
                    # 检查账号是否启用
                    from cookie_manager import manager as cookie_manager
                    if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
                        logger.info(f"【{self.cookie_id}】账号已禁用，停止心跳循环")
                        break

                    # 检查WebSocket连接状态
                    if ws.closed:
                        logger.warning(f"【{self.cookie_id}】WebSocket连接已关闭，停止心跳循环")
                        break

                    await self.send_heartbeat(ws)
                    consecutive_failures = 0  # 重置失败计数

                    await self._interruptible_sleep(self.heartbeat_interval)

                except asyncio.CancelledError:
                    # 收到取消信号，立即退出循环
                    logger.info(f"【{self.cookie_id}】心跳循环收到取消信号，准备退出")
                    raise  # 重新抛出，让任务正常结束
                except Exception as e:
                    consecutive_failures += 1
                    logger.error(f"心跳发送失败 ({consecutive_failures}/{max_failures}): {self._safe_str(e)}")

                    if consecutive_failures >= max_failures:
                        logger.error(f"【{self.cookie_id}】心跳连续失败{max_failures}次，停止心跳循环")
                        break

                    # 失败后短暂等待再重试，使用可中断的sleep
                    try:
                        await self._interruptible_sleep(5)
                    except asyncio.CancelledError:
                        # 在等待重试时收到取消信号，立即退出
                        logger.info(f"【{self.cookie_id}】心跳循环在重试等待时收到取消信号，准备退出")
                        raise
        except asyncio.CancelledError:
            # 确保CancelledError被正确传播
            logger.info(f"【{self.cookie_id}】心跳循环已取消，正在退出...")
            raise
        finally:
            # 确保任务能正常结束
            logger.info(f"【{self.cookie_id}】心跳循环已退出")

    async def handle_heartbeat_response(self, message_data):
        """处理心跳响应"""
        try:
            if not isinstance(message_data, dict):
                return False

            if message_data.get("code") != 200:
                return False

            if self.is_sync_package(message_data):
                return False

            headers = message_data.get("headers")
            if not isinstance(headers, dict):
                return False

            response_mid = str(headers.get("mid") or "")
            if not response_mid or response_mid not in self.pending_heartbeat_mids:
                return False

            self.last_heartbeat_response = time.time()
            try:
                self.pending_heartbeat_mids.remove(response_mid)
            except ValueError:
                pass
            logger.warning(f"【{self.cookie_id}】心跳响应正常 [ID:{response_mid}]")
            return True
        except Exception as e:
            logger.error(f"处理心跳响应出错: {self._safe_str(e)}")
        return False

    async def pause_cleanup_loop(self):
        """定期清理过期的暂停记录、锁和缓存"""
        try:
            while True:
                try:
                    # 检查账号是否启用
                    from cookie_manager import manager as cookie_manager
                    if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
                        logger.info(f"【{self.cookie_id}】账号已禁用，停止清理循环")
                        break

                    # 清理过期的暂停记录
                    pause_manager.cleanup_expired_pauses()
                    await asyncio.sleep(0)  # 让出控制权，允许检查取消信号

                    # 清理过期的锁（每5分钟清理一次，保留24小时内的锁）
                    self.cleanup_expired_locks(max_age_hours=24)
                    await asyncio.sleep(0)  # 让出控制权，允许检查取消信号

                    # 清理过期的商品详情缓存
                    try:
                        cleaned_count = await self._cleanup_item_cache()
                        if cleaned_count > 0:
                            logger.info(f"【{self.cookie_id}】清理了 {cleaned_count} 个过期的商品详情缓存")
                    except asyncio.CancelledError:
                        raise
                    except Exception as cache_clean_e:
                        logger.warning(f"【{self.cookie_id}】清理商品详情缓存时出错: {cache_clean_e}")

                    # 清理过期的通知、发货和订单确认记录（防止内存泄漏）
                    self._cleanup_instance_caches()
                    await asyncio.sleep(0)  # 让出控制权，允许检查取消信号

                    # 清理QR登录过期会话（每5分钟检查一次）
                    try:
                        from utils.qr_login import qr_login_manager
                        qr_login_manager.cleanup_expired_sessions()
                        await asyncio.sleep(0)  # 让出控制权，允许检查取消信号
                    except asyncio.CancelledError:
                        raise
                    except Exception as qr_clean_e:
                        logger.warning(f"【{self.cookie_id}】清理QR登录会话时出错: {qr_clean_e}")
                    
                    # 清理Playwright浏览器临时文件和缓存（每5分钟检查一次）
                    try:
                        await self._cleanup_playwright_cache()
                    except asyncio.CancelledError:
                        raise
                    except Exception as pw_clean_e:
                        logger.warning(f"【{self.cookie_id}】清理Playwright缓存时出错: {pw_clean_e}")
                    
                    # 清理过期的日志文件（每5分钟检查一次，保留7天）
                    try:
                        cleaned_logs = await self._cleanup_old_logs(retention_days=7)
                        await asyncio.sleep(0)  # 让出控制权，允许检查取消信号
                    except asyncio.CancelledError:
                        raise
                    except Exception as log_clean_e:
                        logger.warning(f"【{self.cookie_id}】清理日志文件时出错: {log_clean_e}")
                    
                    # 清理超时仍处于processing的风控日志（每10分钟一次）
                    # 为避免所有实例同时执行，只让第一个实例执行
                    try:
                        if hasattr(self.__class__, '_last_risk_log_cleanup_time'):
                            last_risk_cleanup = self.__class__._last_risk_log_cleanup_time
                        else:
                            self.__class__._last_risk_log_cleanup_time = 0
                            last_risk_cleanup = 0

                        current_time = time.time()
                        if current_time - last_risk_cleanup > 600:
                            try:
                                cleaned_count = await asyncio.to_thread(
                                    db_manager.mark_stale_risk_control_logs_failed,
                                    timeout_minutes=15
                                )
                                if cleaned_count > 0:
                                    logger.warning(f"【{self.cookie_id}】风控日志超时兜底清理完成，自动关闭 {cleaned_count} 条processing记录")
                                self.__class__._last_risk_log_cleanup_time = current_time
                            except asyncio.CancelledError:
                                logger.warning(f"【{self.cookie_id}】风控日志超时兜底清理被取消")
                                raise
                    except asyncio.CancelledError:
                        raise
                    except Exception as risk_clean_e:
                        logger.error(f"【{self.cookie_id}】清理超时风控日志时出错: {risk_clean_e}")

                    # 清理数据库历史数据（每天一次，保留90天数据）
                    # 为避免所有实例同时执行，只让第一个实例执行
                    try:
                        if hasattr(self.__class__, '_last_db_cleanup_time'):
                            last_cleanup = self.__class__._last_db_cleanup_time
                        else:
                            self.__class__._last_db_cleanup_time = 0
                            last_cleanup = 0
                        
                        current_time = time.time()
                        # 每24小时清理一次
                        if current_time - last_cleanup > 86400:
                            logger.info(f"【{self.cookie_id}】开始执行数据库历史数据清理...")
                            # 数据库清理可能很耗时，使用线程池执行，避免阻塞事件循环
                            # 这样即使清理操作很慢，也能响应取消信号
                            try:
                                stats = await asyncio.to_thread(db_manager.cleanup_old_data, days=90)
                                if 'error' not in stats:
                                    logger.info(f"【{self.cookie_id}】数据库清理完成: {stats}")
                                    self.__class__._last_db_cleanup_time = current_time
                                else:
                                    logger.error(f"【{self.cookie_id}】数据库清理失败: {stats['error']}")
                            except asyncio.CancelledError:
                                logger.warning(f"【{self.cookie_id}】数据库清理被取消")
                                raise
                    except asyncio.CancelledError:
                        raise  # 重新抛出取消信号
                    except Exception as db_clean_e:
                        logger.error(f"【{self.cookie_id}】清理数据库历史数据时出错: {db_clean_e}")

                    # 每5分钟清理一次
                    await self._interruptible_sleep(300)
                except asyncio.CancelledError:
                    # 收到取消信号，立即退出循环
                    logger.info(f"【{self.cookie_id}】清理循环收到取消信号，准备退出")
                    raise
                except Exception as e:
                    logger.error(f"【{self.cookie_id}】清理任务失败: {self._safe_str(e)}")
                    # 出错后也等待5分钟再重试，使用可中断的sleep
                    try:
                        await self._interruptible_sleep(300)
                    except asyncio.CancelledError:
                        logger.info(f"【{self.cookie_id}】清理循环在重试等待时收到取消信号，准备退出")
                        raise
        except asyncio.CancelledError:
            # 确保CancelledError被正确传播
            logger.info(f"【{self.cookie_id}】清理循环已取消，正在退出...")
            raise
        finally:
            # 确保任务能正常结束
            logger.info(f"【{self.cookie_id}】清理循环已退出")


    async def cookie_refresh_loop(self):
        """Cookie刷新定时任务 - 每小时执行一次"""
        try:
            while True:
                try:
                    # 检查账号是否启用
                    from cookie_manager import manager as cookie_manager
                    if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
                        logger.info(f"【{self.cookie_id}】账号已禁用，停止Cookie刷新循环")
                        break

                    # 检查Cookie刷新功能是否启用
                    if not self.cookie_refresh_enabled:
                        logger.warning(f"【{self.cookie_id}】Cookie刷新功能已禁用，跳过执行")
                        await self._interruptible_sleep(300)  # 5分钟后再检查
                        continue

                    if self.is_manual_refresh_active(self.cookie_id):
                        logger.warning(f"【{self.cookie_id}】手动刷新进行中，跳过自动Cookie刷新")
                        await self._interruptible_sleep(60)
                        continue

                    current_time = time.time()
                    if current_time - self.last_cookie_refresh_time >= self.cookie_refresh_interval:
                        # 检查是否在消息接收后的冷却时间内
                        time_since_last_message = current_time - self.last_message_received_time
                        if time_since_last_message < self.message_cookie_refresh_cooldown:
                            remaining_time = self.message_cookie_refresh_cooldown - time_since_last_message
                            remaining_minutes = int(remaining_time // 60)
                            remaining_seconds = int(remaining_time % 60)
                            logger.warning(f"【{self.cookie_id}】收到消息后冷却中，还需等待 {remaining_minutes}分{remaining_seconds}秒 才能执行Cookie刷新")
                        # 检查是否已有Cookie刷新任务在执行
                        elif self.cookie_refresh_lock.locked():
                            logger.warning(f"【{self.cookie_id}】Cookie刷新任务已在执行中，跳过本次触发")
                        else:
                            logger.info(f"【{self.cookie_id}】开始执行Cookie刷新任务...")
                            # 在独立的任务中执行Cookie刷新，避免阻塞主循环
                            asyncio.create_task(self._execute_cookie_refresh(current_time))

                    # 每分钟检查一次是否需要执行
                    await self._interruptible_sleep(60)
                except asyncio.CancelledError:
                    # 收到取消信号，立即退出循环
                    logger.info(f"【{self.cookie_id}】Cookie刷新循环收到取消信号，准备退出")
                    raise
                except Exception as e:
                    logger.error(f"【{self.cookie_id}】Cookie刷新循环失败: {self._safe_str(e)}")
                    # 出错后也等待1分钟再重试，使用可中断的sleep
                    try:
                        await self._interruptible_sleep(60)
                    except asyncio.CancelledError:
                        logger.info(f"【{self.cookie_id}】Cookie刷新循环在重试等待时收到取消信号，准备退出")
                        raise
        except asyncio.CancelledError:
            # 确保CancelledError被正确传播
            logger.info(f"【{self.cookie_id}】Cookie刷新循环已取消，正在退出...")
            raise
        finally:
            # 确保任务能正常结束
            logger.info(f"【{self.cookie_id}】Cookie刷新循环已退出")

    async def _execute_cookie_refresh(self, current_time):
        """独立执行Cookie刷新任务，避免阻塞主循环"""

        # 使用Lock确保原子性，防止重复执行
        async with self.cookie_refresh_lock:
            try:
                clear_message_received_flag = False
                if self.is_manual_refresh_active(self.cookie_id):
                    logger.warning(f"【{self.cookie_id}】手动刷新进行中，取消当前自动Cookie刷新任务")
                    return

                logger.info(f"【{self.cookie_id}】开始Cookie刷新任务，暂时暂停心跳以避免连接冲突...")

                # 暂时暂停心跳任务，避免与浏览器操作冲突
                heartbeat_was_running = False
                if self.heartbeat_task and not self.heartbeat_task.done():
                    heartbeat_was_running = True
                    self.heartbeat_task.cancel()
                    logger.warning(f"【{self.cookie_id}】已暂停心跳任务")

                # 为整个Cookie刷新任务添加超时保护（3分钟，缩短时间减少影响）
                success = await asyncio.wait_for(
                    self._refresh_cookies_via_browser(),
                    timeout=180.0  # 3分钟超时，减少对WebSocket的影响
                )

                # 重新启动心跳任务
                if heartbeat_was_running and self.ws and not self.ws.closed:
                    logger.warning(f"【{self.cookie_id}】重新启动心跳任务")
                    self.heartbeat_task = asyncio.create_task(self.heartbeat_loop(self.ws))

                if success:
                    self.last_cookie_refresh_time = current_time
                    logger.info(f"【{self.cookie_id}】Cookie刷新任务完成，心跳已恢复")
                    
                    # 刷新成功后，验证Cookie有效性
                    logger.info(f"【{self.cookie_id}】开始验证刷新后的Cookie有效性...")
                    try:
                        validation_result = await self._verify_cookie_validity()
                        
                        if not validation_result['valid']:
                            logger.warning(f"【{self.cookie_id}】❌ Cookie验证失败: {validation_result['details']}")
                            if validation_result.get('relogin_recommended', True):
                                logger.warning(f"【{self.cookie_id}】检测到Cookie可能无法用于关键API，尝试通过密码登录重新获取...")
                                
                                # 触发密码登录刷新
                                password_refresh_success = await self._try_password_login_refresh("Cookie验证失败(关键API不可用)")
                                
                                if password_refresh_success:
                                    logger.info(f"【{self.cookie_id}】✅ 密码登录刷新成功，Cookie已更新")
                                    clear_message_received_flag = True
                                else:
                                    logger.warning(f"【{self.cookie_id}】⚠️ 密码登录刷新失败，Cookie可能仍然无效")
                                    # 发送通知
                                    await self.send_token_refresh_notification(
                                        f"Cookie验证失败且密码登录刷新也失败\n验证详情: {validation_result['details']}",
                                        "cookie_validation_failed"
                                    )
                            else:
                                logger.warning(f"【{self.cookie_id}】Cookie验证失败，但当前错误更像网络/环境问题，跳过密码登录刷新")
                        else:
                            if validation_result.get('inconclusive'):
                                logger.warning(f"【{self.cookie_id}】⚠️ Cookie验证结果不确定，保留当前消息冷却标志，等待后续保活再次确认: {validation_result['details']}")
                            else:
                                logger.info(f"【{self.cookie_id}】✅ Cookie验证通过: {validation_result['details']}")
                                clear_message_received_flag = True
                            
                    except Exception as verify_e:
                        logger.error(f"【{self.cookie_id}】Cookie验证过程异常: {self._safe_str(verify_e)}")
                        import traceback
                        logger.error(f"【{self.cookie_id}】详细堆栈:\n{traceback.format_exc()}")
                else:
                    logger.warning(f"【{self.cookie_id}】Cookie刷新任务失败")
                    # 即使失败也要更新时间，避免频繁重试
                    self.last_cookie_refresh_time = current_time

            except asyncio.TimeoutError:
                # 超时也要更新时间，避免频繁重试
                self.last_cookie_refresh_time = current_time
            except Exception as e:
                logger.error(f"【{self.cookie_id}】执行Cookie刷新任务异常: {self._safe_str(e)}")
                # 异常也要更新时间，避免频繁重试
                self.last_cookie_refresh_time = current_time
            finally:
                # 确保心跳任务恢复（如果WebSocket仍然连接）
                if (self.ws and not self.ws.closed and
                    (not self.heartbeat_task or self.heartbeat_task.done())):
                    logger.info(f"【{self.cookie_id}】Cookie刷新完成，心跳任务正常运行")
                    self.heartbeat_task = asyncio.create_task(self.heartbeat_loop(self.ws))

                if clear_message_received_flag:
                    # 仅在刷新链路确认恢复可用后，才清空消息接收标志。
                    self.last_message_received_time = 0
                    logger.warning(f"【{self.cookie_id}】Cookie刷新完成，已清空消息接收标志")
                else:
                    logger.warning(f"【{self.cookie_id}】Cookie刷新未确认恢复可用，保留消息接收标志")



    def enable_cookie_refresh(self, enabled: bool = True):
        """启用或禁用Cookie刷新功能"""
        self.cookie_refresh_enabled = enabled
        status = "启用" if enabled else "禁用"
        logger.info(f"【{self.cookie_id}】Cookie刷新功能已{status}")


    async def refresh_cookies_from_qr_login(self, qr_cookies_str: str, cookie_id: str = None, user_id: int = None):
        """使用扫码登录获取的cookie访问指定界面获取真实cookie并存入数据库

        Args:
            qr_cookies_str: 扫码登录获取的cookie字符串
            cookie_id: 可选的cookie ID，如果不提供则使用当前实例的cookie_id
            user_id: 可选的用户ID，如果不提供则使用当前实例的user_id

        Returns:
            bool: 成功返回True，失败返回False
        """
        playwright = None
        browser = None
        target_cookie_id = cookie_id or self.cookie_id
        target_user_id = user_id or self.user_id

        try:
            import asyncio
            from playwright.async_api import async_playwright
            from utils.xianyu_utils import trans_cookies

            logger.info(f"【{target_cookie_id}】开始使用扫码登录cookie获取真实cookie...")
            logger.info(f"【{target_cookie_id}】扫码cookie长度: {len(qr_cookies_str)}")

            # 解析扫码登录的cookie
            qr_cookies_dict = trans_cookies(qr_cookies_str)
            logger.info(f"【{target_cookie_id}】扫码cookie字段数: {len(qr_cookies_dict)}")

            # 使用统一的Playwright启动方法
            playwright = await _start_playwright_safe(target_cookie_id)
            if not playwright:
                return False

            # 启动浏览器（参照商品搜索的配置）
            browser_args = [
                '--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-dev-shm-usage',
                '--disable-accelerated-2d-canvas',
                '--no-first-run',
                '--no-zygote',
                '--disable-gpu',
                '--disable-background-timer-throttling',
                '--disable-backgrounding-occluded-windows',
                '--disable-renderer-backgrounding',
                '--disable-features=TranslateUI',
                '--disable-ipc-flooding-protection',
                '--disable-extensions',
                '--disable-default-apps',
                '--disable-sync',
                '--disable-translate',
                '--hide-scrollbars',
                '--mute-audio',
                '--no-default-browser-check',
                '--no-pings'
            ]

            # 在Docker环境中添加额外参数
            if os.getenv('DOCKER_ENV'):
                browser_args.extend([
                    # '--single-process',  # 注释掉，避免多用户并发时的进程冲突和资源泄漏
                    '--disable-background-networking',
                    '--disable-client-side-phishing-detection',
                    '--disable-hang-monitor',
                    '--disable-popup-blocking',
                    '--disable-prompt-on-repost',
                    '--disable-web-resources',
                    '--metrics-recording-only',
                    '--safebrowsing-disable-auto-update',
                    '--enable-automation',
                    '--password-store=basic',
                    '--use-mock-keychain'
                ])

            # 使用无头浏览器
            browser = await playwright.chromium.launch(
                headless=True,  # 改回无头模式
                args=browser_args
            )

            # 创建浏览器上下文
            context_options = {
                'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'
            }

            # 使用标准窗口大小
            context_options['viewport'] = {'width': 1920, 'height': 1080}

            context = await browser.new_context(**context_options)

            # 设置扫码登录获取的Cookie
            cookies = []
            for cookie_pair in qr_cookies_str.split('; '):
                if '=' in cookie_pair:
                    name, value = cookie_pair.split('=', 1)
                    cookies.append({
                        'name': name.strip(),
                        'value': value.strip(),
                        'domain': '.goofish.com',
                        'path': '/'
                    })

            await context.add_cookies(cookies)
            logger.info(f"【{target_cookie_id}】已设置 {len(cookies)} 个扫码Cookie到浏览器")

            # 打印设置的扫码Cookie详情
            logger.info(f"【{target_cookie_id}】=== 设置到浏览器的扫码Cookie ===")
            for i, cookie in enumerate(cookies, 1):
                logger.info(f"【{target_cookie_id}】{i:2d}. {cookie['name']}: {cookie['value'][:50]}{'...' if len(cookie['value']) > 50 else ''}")

            # 创建页面
            page = await context.new_page()

            # 等待页面准备
            await asyncio.sleep(0.1)

            # 访问指定页面获取真实cookie
            target_url = "https://www.goofish.com/im"
            logger.info(f"【{target_cookie_id}】访问页面获取真实cookie: {target_url}")

            # 使用更灵活的页面访问策略
            try:
                # 首先尝试较短超时
                await page.goto(target_url, wait_until='domcontentloaded', timeout=15000)
                logger.info(f"【{target_cookie_id}】页面访问成功")
            except Exception as e:
                if 'timeout' in str(e).lower():
                    logger.warning(f"【{target_cookie_id}】页面访问超时，尝试降级策略...")
                    try:
                        # 降级策略：只等待基本加载
                        await page.goto(target_url, wait_until='load', timeout=20000)
                        logger.info(f"【{target_cookie_id}】页面访问成功（降级策略）")
                    except Exception as e2:
                        logger.warning(f"【{target_cookie_id}】降级策略也失败，尝试最基本访问...")
                        # 最后尝试：不等待任何加载完成
                        await page.goto(target_url, timeout=25000)
                        logger.info(f"【{target_cookie_id}】页面访问成功（最基本策略）")
                else:
                    raise e

            # 等待页面完全加载并获取真实cookie
            logger.info(f"【{target_cookie_id}】页面加载完成，等待获取真实cookie...")
            await asyncio.sleep(2)

            # 执行一次刷新以确保获取最新的cookie
            logger.info(f"【{target_cookie_id}】执行页面刷新获取最新cookie...")
            try:
                await page.reload(wait_until='domcontentloaded', timeout=12000)
                logger.info(f"【{target_cookie_id}】页面刷新成功")
            except Exception as e:
                if 'timeout' in str(e).lower():
                    logger.warning(f"【{target_cookie_id}】页面刷新超时，使用降级策略...")
                    await page.reload(wait_until='load', timeout=15000)
                    logger.info(f"【{target_cookie_id}】页面刷新成功（降级策略）")
                else:
                    raise e
            await asyncio.sleep(1)

            # 获取更新后的真实Cookie
            logger.info(f"【{target_cookie_id}】获取真实Cookie...")
            updated_cookies = await context.cookies()

            # 构造新的Cookie字典
            real_cookies_dict = {}
            for cookie in updated_cookies:
                real_cookies_dict[cookie['name']] = cookie['value']

            # 现有账号不要直接整包覆盖旧Cookie，保留扫码前已经存在但本次页面未返回的字段
            from db_manager import db_manager
            existing_cookie = db_manager.get_cookie_details(target_cookie_id)
            existing_cookie_value = self._extract_cookie_value(existing_cookie)
            existing_cookies_dict = {}
            if existing_cookie_value:
                try:
                    existing_cookies_dict = trans_cookies(existing_cookie_value) or {}
                except Exception as merge_e:
                    logger.warning(f"【{target_cookie_id}】解析现有账号Cookie失败，按空基线继续: {self._safe_str(merge_e)}")

            merge_result = self.protected_merge_cookie_dicts(existing_cookies_dict, real_cookies_dict)
            real_cookies_dict = merge_result['merged_cookies_dict']
            if target_cookie_id == self.cookie_id:
                self._log_protected_merge_event("qr_login_protected_merge", merge_result)
            else:
                logger.info(
                    f"【{target_cookie_id}】qr_login_protected_merge "
                    f"incoming_count={merge_result.get('incoming_count', 0)} "
                    f"existing_count={merge_result.get('existing_count', 0)} "
                    f"merged_count={merge_result.get('merged_count', 0)} "
                    f"protected_preserved_fields={merge_result.get('preserved_protected_fields') or []} "
                    f"would_remove_fields={merge_result.get('would_remove_fields') or []} "
                    f"account_switched={merge_result.get('account_switched', False)}"
                )
            if merge_result['updated_fields']:
                logger.info(f"【{target_cookie_id}】扫码登录合并更新Cookie字段: {', '.join(merge_result['updated_fields'])}")
            if merge_result['preserved_fields']:
                logger.info(f"【{target_cookie_id}】扫码登录保留现有Cookie字段 ({len(merge_result['preserved_fields'])}个): {', '.join(merge_result['preserved_fields'])}")
            if merge_result['preserved_protected_fields']:
                logger.warning(f"【{target_cookie_id}】扫码登录保护性保留关键字段: {', '.join(merge_result['preserved_protected_fields'])}")
            if merge_result['account_switched']:
                logger.warning(f"【{target_cookie_id}】扫码登录检测到unb变化，按账号切换处理，不保留旧账号Cookie字段")

            missing_required_fields = merge_result['missing_required_fields']
            if missing_required_fields:
                logger.error(f"【{target_cookie_id}】扫码登录真实Cookie仍缺失核心字段，放弃保存: {', '.join(missing_required_fields)}")
                return False

            # 生成真实cookie字符串
            real_cookies_str = '; '.join([f"{k}={v}" for k, v in real_cookies_dict.items()])

            logger.info(f"【{target_cookie_id}】真实Cookie已获取，包含 {len(real_cookies_dict)} 个字段")
            
            # 打印扫码登录获取的真实Cookie字段详情
            logger.info(f"【{target_cookie_id}】========== 扫码登录真实Cookie字段详情 ==========")
            logger.info(f"【{target_cookie_id}】Cookie字段数: {len(real_cookies_dict)}")
            logger.info(f"【{target_cookie_id}】Cookie字段列表:")
            for i, (key, value) in enumerate(real_cookies_dict.items(), 1):
                if len(str(value)) > 50:
                    logger.info(f"【{target_cookie_id}】  {i:2d}. {key}: {str(value)[:30]}...{str(value)[-20:]} (长度: {len(str(value))})")
                else:
                    logger.info(f"【{target_cookie_id}】  {i:2d}. {key}: {value}")
            
            # 检查关键字段
            important_keys = ['unb', '_m_h5_tk', '_m_h5_tk_enc', 'cookie2', 't', 'sgcookie', 'cna']
            logger.info(f"【{target_cookie_id}】关键字段检查:")
            for key in important_keys:
                if key in real_cookies_dict:
                    val = real_cookies_dict[key]
                    logger.info(f"【{target_cookie_id}】  ✅ {key}: {'存在' if val else '为空'} (长度: {len(str(val)) if val else 0})")
                else:
                    logger.info(f"【{target_cookie_id}】  ❌ {key}: 缺失")
            logger.info(f"【{target_cookie_id}】==========================================")

            # 打印完整的真实Cookie内容
            logger.info(f"【{target_cookie_id}】=== 真实Cookie摘要 ===")
            logger.info(f"【{target_cookie_id}】Cookie字符串长度: {len(real_cookies_str)}")
            logger.info(f"【{target_cookie_id}】Cookie摘要: {self._summarize_cookie_string(real_cookies_str)}")

            # 打印所有Cookie字段的详细信息
            logger.info(f"【{target_cookie_id}】=== Cookie字段详细信息 ===")
            for i, (name, value) in enumerate(real_cookies_dict.items(), 1):
                # 对于长值，显示前后部分
                if len(value) > 50:
                    display_value = f"{value[:20]}...{value[-20:]}"
                else:
                    display_value = value
                logger.info(f"【{target_cookie_id}】{i:2d}. {name}: {display_value}")

            # 打印原始扫码Cookie对比
            logger.info(f"【{target_cookie_id}】=== 扫码Cookie对比 ===")
            logger.info(f"【{target_cookie_id}】扫码Cookie长度: {len(qr_cookies_str)}")
            logger.info(f"【{target_cookie_id}】扫码Cookie字段数: {len(qr_cookies_dict)}")
            logger.info(f"【{target_cookie_id}】真实Cookie长度: {len(real_cookies_str)}")
            logger.info(f"【{target_cookie_id}】真实Cookie字段数: {len(real_cookies_dict)}")
            logger.info(f"【{target_cookie_id}】长度增加: {len(real_cookies_str) - len(qr_cookies_str)} 字符")
            logger.info(f"【{target_cookie_id}】字段增加: {len(real_cookies_dict) - len(qr_cookies_dict)} 个")

            # 检查Cookie变化
            changed_cookies = []
            new_cookies = []
            for name, new_value in real_cookies_dict.items():
                old_value = qr_cookies_dict.get(name)
                if old_value is None:
                    new_cookies.append(name)
                elif old_value != new_value:
                    changed_cookies.append(name)

            # 显示Cookie变化统计
            if changed_cookies:
                logger.info(f"【{target_cookie_id}】发生变化的Cookie字段 ({len(changed_cookies)}个): {', '.join(changed_cookies)}")
            if new_cookies:
                logger.info(f"【{target_cookie_id}】新增的Cookie字段 ({len(new_cookies)}个): {', '.join(new_cookies)}")
            if not changed_cookies and not new_cookies:
                logger.info(f"【{target_cookie_id}】Cookie无变化")

            # 打印重要Cookie字段的完整详情
            important_cookies = ['_m_h5_tk', '_m_h5_tk_enc', 'cookie2', 't', 'sgcookie', 'unb', 'uc1', 'uc3', 'uc4']
            logger.info(f"【{target_cookie_id}】=== 重要Cookie字段完整详情 ===")
            for cookie_name in important_cookies:
                if cookie_name in real_cookies_dict:
                    cookie_value = real_cookies_dict[cookie_name]

                    # 标记是否发生了变化
                    change_mark = " [已变化]" if cookie_name in changed_cookies else " [新增]" if cookie_name in new_cookies else " [无变化]"

                    # 显示完整的cookie值
                    logger.info(f"【{target_cookie_id}】{cookie_name}{change_mark}:")
                    logger.info(f"【{target_cookie_id}】  值: {self._mask_secret_value(cookie_value, head=8, tail=6)}")
                    logger.info(f"【{target_cookie_id}】  长度: {len(cookie_value)}")

                    # 如果有对应的扫码cookie值，显示对比
                    if cookie_name in qr_cookies_dict:
                        old_value = qr_cookies_dict[cookie_name]
                        if old_value != cookie_value:
                            logger.info(f"【{target_cookie_id}】  原值: {self._mask_secret_value(old_value, head=8, tail=6)}")
                            logger.info(f"【{target_cookie_id}】  原长度: {len(old_value)}")
                    logger.info(f"【{target_cookie_id}】  ---")
                else:
                    logger.info(f"【{target_cookie_id}】{cookie_name}: [不存在]")

            # 保存真实Cookie到数据库
            # 检查是否为新账号
            existing_cookie = db_manager.get_cookie_details(target_cookie_id)
            if existing_cookie:
                # 现有账号，使用 update_cookie_account_info 避免覆盖其他字段（如 pause_duration, remark 等）
                success = db_manager.update_cookie_account_info(target_cookie_id, cookie_value=real_cookies_str)
            else:
                # 新账号，使用 save_cookie
                success = db_manager.save_cookie(target_cookie_id, real_cookies_str, target_user_id)

            if success:
                logger.info(f"【{target_cookie_id}】真实Cookie已成功保存到数据库")

                # 如果当前实例的cookie_id匹配，更新实例的cookie信息
                if target_cookie_id == self.cookie_id:
                    self._set_runtime_cookie_state(
                        cookies_str=real_cookies_str,
                        cookies_dict=real_cookies_dict,
                        source="qr_login_refresh",
                    )
                    logger.info(f"【{target_cookie_id}】已更新当前实例的Cookie信息")

                # 更新扫码登录Cookie刷新时间标志
                self.last_qr_cookie_refresh_time = time.time()
                logger.info(f"【{target_cookie_id}】已更新扫码登录Cookie刷新时间标志，_refresh_cookies_via_browser将等待{self.qr_cookie_refresh_cooldown//60}分钟后执行")

                return True
            else:
                logger.error(f"【{target_cookie_id}】保存真实Cookie到数据库失败")
                return False

        except Exception as e:
            logger.error(f"【{target_cookie_id}】使用扫码cookie获取真实cookie失败: {self._safe_str(e)}")
            return False
        finally:
            # 确保资源清理
            try:
                # 先关闭浏览器，再关闭Playwright（顺序很重要）
                if browser:
                    try:
                        await asyncio.wait_for(browser.close(), timeout=5.0)
                        logger.warning(f"【{target_cookie_id}】浏览器关闭完成")
                    except asyncio.TimeoutError:
                        logger.warning(f"【{target_cookie_id}】浏览器关闭超时（5秒），资源可能未完全释放")
                        # 尝试取消浏览器相关的任务
                        try:
                            if hasattr(browser, '_connection'):
                                browser._connection = None
                        except Exception:
                            pass
                    except Exception as e:
                        logger.warning(f"【{target_cookie_id}】关闭浏览器时出错: {self._safe_str(e)}")
                
                # Playwright关闭：使用更短的超时，超时后立即放弃
                if playwright:
                    try:
                        logger.warning(f"【{target_cookie_id}】正在关闭Playwright...")
                        await asyncio.wait_for(playwright.stop(), timeout=2.0)
                        logger.warning(f"【{target_cookie_id}】Playwright关闭完成")
                    except asyncio.TimeoutError:
                        logger.warning(f"【{target_cookie_id}】Playwright关闭超时（2秒），进程可能仍在运行")
                        logger.warning(f"【{target_cookie_id}】提示：如果后续Playwright启动失败，可能需要手动清理残留进程")
                        # 尝试清理Playwright的内部状态
                        try:
                            # 取消可能正在运行的Playwright任务
                            if hasattr(playwright, '_transport'):
                                playwright._transport = None
                        except Exception:
                            pass
                    except Exception as e:
                        logger.warning(f"【{target_cookie_id}】关闭Playwright时出错: {self._safe_str(e)}")
            except Exception as cleanup_e:
                logger.warning(f"【{target_cookie_id}】清理浏览器资源时出错: {self._safe_str(cleanup_e)}")

    async def _refresh_cookies_via_browser_page(self, current_cookies_str: str, restart_on_success: bool = True):
        """使用当前cookie访问指定页面获取真实cookie并更新
        
        这是令牌过期时的备用刷新方案，类似于refresh_cookies_from_qr_login，
        但使用当前的cookie而不是扫码登录的cookie。

        Args:
            current_cookies_str: 当前的cookie字符串
            restart_on_success: 成功后是否立即重启任务。扫码登录后的首轮缓冲只需要稳定 Cookie，不应直接重启。

        Returns:
            bool: 成功返回True，失败返回False
        """
        playwright = None
        browser = None

        try:
            import asyncio
            from playwright.async_api import async_playwright
            from utils.xianyu_utils import trans_cookies

            logger.info(f"【{self.cookie_id}】开始使用当前cookie访问指定页面获取真实cookie...")
            logger.info(f"【{self.cookie_id}】当前cookie长度: {len(current_cookies_str)}")

            # 解析当前的cookie
            current_cookies_dict = trans_cookies(current_cookies_str)
            logger.info(f"【{self.cookie_id}】当前cookie字段数: {len(current_cookies_dict)}")

            # 使用统一的Playwright启动方法
            playwright = await _start_playwright_safe(self.cookie_id)
            if not playwright:
                return False

            # 启动浏览器（参照商品搜索的配置）
            browser_args = [
                '--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-dev-shm-usage',
                '--disable-accelerated-2d-canvas',
                '--no-first-run',
                '--no-zygote',
                '--disable-gpu',
                '--disable-background-timer-throttling',
                '--disable-backgrounding-occluded-windows',
                '--disable-renderer-backgrounding',
                '--disable-features=TranslateUI',
                '--disable-ipc-flooding-protection',
                '--disable-extensions',
                '--disable-default-apps',
                '--disable-sync',
                '--disable-translate',
                '--hide-scrollbars',
                '--mute-audio',
                '--no-default-browser-check',
                '--no-pings'
            ]

            # 在Docker环境中添加额外参数
            if os.getenv('DOCKER_ENV'):
                browser_args.extend([
                    '--disable-background-networking',
                    '--disable-client-side-phishing-detection',
                    '--disable-hang-monitor',
                    '--disable-popup-blocking',
                    '--disable-prompt-on-repost',
                    '--disable-web-resources',
                    '--metrics-recording-only',
                    '--safebrowsing-disable-auto-update',
                    '--enable-automation',
                    '--password-store=basic',
                    '--use-mock-keychain'
                ])

            # 使用无头浏览器
            browser = await playwright.chromium.launch(
                headless=True,
                args=browser_args
            )

            # 创建浏览器上下文
            context_options = {
                'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'
            }

            # 使用标准窗口大小
            context_options['viewport'] = {'width': 1920, 'height': 1080}

            context = await browser.new_context(**context_options)

            # 设置当前的Cookie
            cookies = []
            for cookie_pair in current_cookies_str.split('; '):
                if '=' in cookie_pair:
                    name, value = cookie_pair.split('=', 1)
                    cookies.append({
                        'name': name.strip(),
                        'value': value.strip(),
                        'domain': '.goofish.com',
                        'path': '/'
                    })

            await context.add_cookies(cookies)
            logger.info(f"【{self.cookie_id}】已设置 {len(cookies)} 个当前Cookie到浏览器")

            # 创建页面
            page = await context.new_page()

            # 等待页面准备
            await asyncio.sleep(0.1)

            # 访问指定页面获取真实cookie
            target_url = "https://www.goofish.com/im"
            logger.info(f"【{self.cookie_id}】访问页面获取真实cookie: {target_url}")

            # 使用更灵活的页面访问策略
            try:
                # 首先尝试较短超时
                await page.goto(target_url, wait_until='domcontentloaded', timeout=15000)
                logger.info(f"【{self.cookie_id}】页面访问成功")
            except Exception as e:
                if 'timeout' in str(e).lower():
                    logger.warning(f"【{self.cookie_id}】页面访问超时，尝试降级策略...")
                    try:
                        # 降级策略：只等待基本加载
                        await page.goto(target_url, wait_until='load', timeout=20000)
                        logger.info(f"【{self.cookie_id}】页面访问成功（降级策略）")
                    except Exception as e2:
                        logger.warning(f"【{self.cookie_id}】降级策略也失败，尝试最基本访问...")
                        # 最后尝试：不等待任何加载完成
                        await page.goto(target_url, timeout=25000)
                        logger.info(f"【{self.cookie_id}】页面访问成功（最基本策略）")
                else:
                    raise e

            # 等待页面完全加载并获取真实cookie
            logger.info(f"【{self.cookie_id}】页面加载完成，等待获取真实cookie...")
            await asyncio.sleep(2)

            # 执行一次刷新以确保获取最新的cookie
            logger.info(f"【{self.cookie_id}】执行页面刷新获取最新cookie...")
            try:
                await page.reload(wait_until='domcontentloaded', timeout=12000)
                logger.info(f"【{self.cookie_id}】页面刷新成功")
            except Exception as e:
                if 'timeout' in str(e).lower():
                    logger.warning(f"【{self.cookie_id}】页面刷新超时，使用降级策略...")
                    await page.reload(wait_until='load', timeout=15000)
                    logger.info(f"【{self.cookie_id}】页面刷新成功（降级策略）")
                else:
                    raise e
            await asyncio.sleep(1)

            # 获取更新后的真实Cookie
            logger.info(f"【{self.cookie_id}】获取真实Cookie...")
            updated_cookies = await context.cookies()

            # 构造新的Cookie字典
            real_cookies_dict = {}
            for cookie in updated_cookies:
                real_cookies_dict[cookie['name']] = cookie['value']

            merge_result = self.protected_merge_cookie_dicts(current_cookies_dict, real_cookies_dict)
            real_cookies_dict = merge_result['merged_cookies_dict']
            self._log_protected_merge_event("browser_stabilization_protected_merge", merge_result)

            # 生成真实cookie字符串
            real_cookies_str = '; '.join([f"{k}={v}" for k, v in real_cookies_dict.items()])

            logger.info(f"【{self.cookie_id}】真实Cookie已获取，包含 {len(real_cookies_dict)} 个字段")
            logger.info(f"【{self.cookie_id}】真实Cookie摘要: {self._summarize_cookie_string(real_cookies_str)}")

            self._log_cookie_merge_summary(
                real_cookies_dict,
                merge_result['updated_fields'],
                merge_result['changed_fields'],
                merge_result['new_fields'],
                context="浏览器稳定化Cookie",
                preserved_fields=merge_result['preserved_fields'],
                preserved_protected_fields=merge_result['preserved_protected_fields'],
                would_remove_fields=merge_result['would_remove_fields'],
                removed_fields=merge_result['removed_fields'],
                missing_protected_fields=merge_result['missing_protected_fields'],
                missing_required_fields=merge_result['missing_required_fields'],
                incoming_missing_protected_fields=merge_result['incoming_missing_protected_fields'],
                account_switched=merge_result['account_switched'],
            )

            if merge_result['missing_required_fields']:
                logger.error(f"【{self.cookie_id}】浏览器稳定化后的Cookie仍缺失核心字段，放弃写回数据库: {', '.join(merge_result['missing_required_fields'])}")
                return False

            # 检查Cookie是否有有效更新
            changed_cookies = []
            new_cookies = []
            for name, new_value in real_cookies_dict.items():
                old_value = current_cookies_dict.get(name)
                if old_value is None:
                    new_cookies.append(name)
                elif old_value != new_value:
                    changed_cookies.append(name)

            if not changed_cookies and not new_cookies:
                if restart_on_success:
                    logger.warning(f"【{self.cookie_id}】Cookie无变化，可能当前cookie已失效")
                    return False
                logger.info(f"【{self.cookie_id}】Cookie字段无变化，但浏览器稳定化访问已完成")

            logger.info(f"【{self.cookie_id}】发生变化的Cookie字段 ({len(changed_cookies)}个): {', '.join(changed_cookies[:10])}")
            if new_cookies:
                logger.info(f"【{self.cookie_id}】新增的Cookie字段 ({len(new_cookies)}个): {', '.join(new_cookies[:10])}")

            if restart_on_success:
                # 更新Cookie并重启任务
                logger.info(f"【{self.cookie_id}】开始更新Cookie并重启任务...")
                update_success = await self._update_cookies_and_restart(real_cookies_str)

                if update_success:
                    logger.info(f"【{self.cookie_id}】通过访问指定页面成功更新Cookie并重启任务")
                    return True
                else:
                    logger.error(f"【{self.cookie_id}】更新Cookie或重启任务失败")
                    return False

            old_cookies_str = self.cookies_str
            old_cookies_dict = self.cookies.copy()
            try:
                self._set_runtime_cookie_state(
                    cookies_str=real_cookies_str,
                    cookies_dict=real_cookies_dict,
                    source="stabilize_cookie_snapshot",
                )
                await self.update_config_cookies()
                logger.info(f"【{self.cookie_id}】通过访问指定页面成功稳定当前Cookie（不重启任务）")
                return True
            except Exception as update_e:
                self._set_runtime_cookie_state(
                    cookies_str=old_cookies_str,
                    cookies_dict=old_cookies_dict,
                    source="stabilize_cookie_snapshot_rollback",
                )
                logger.error(f"【{self.cookie_id}】稳定Cookie时更新数据库失败: {self._safe_str(update_e)}")
                return False

        except Exception as e:
            logger.error(f"【{self.cookie_id}】使用当前cookie访问指定页面获取真实cookie失败: {self._safe_str(e)}")
            return False
        finally:
            # 确保资源清理
            try:
                # 先关闭浏览器，再关闭Playwright（顺序很重要）
                if browser:
                    try:
                        await asyncio.wait_for(browser.close(), timeout=5.0)
                        logger.warning(f"【{self.cookie_id}】浏览器关闭完成")
                    except asyncio.TimeoutError:
                        logger.warning(f"【{self.cookie_id}】浏览器关闭超时（5秒），资源可能未完全释放")
                    except Exception as e:
                        logger.warning(f"【{self.cookie_id}】关闭浏览器时出错: {self._safe_str(e)}")
                
                # Playwright关闭：使用更短的超时，超时后立即放弃
                if playwright:
                    try:
                        logger.warning(f"【{self.cookie_id}】正在关闭Playwright...")
                        await asyncio.wait_for(playwright.stop(), timeout=2.0)
                        logger.warning(f"【{self.cookie_id}】Playwright关闭完成")
                    except asyncio.TimeoutError:
                        logger.warning(f"【{self.cookie_id}】Playwright关闭超时（2秒），进程可能仍在运行")
                    except Exception as e:
                        logger.warning(f"【{self.cookie_id}】关闭Playwright时出错: {self._safe_str(e)}")
            except Exception as cleanup_e:
                logger.warning(f"【{self.cookie_id}】清理浏览器资源时出错: {self._safe_str(cleanup_e)}")

    def reset_qr_cookie_refresh_flag(self):
        """重置扫码登录Cookie刷新标志，允许立即执行_refresh_cookies_via_browser"""
        self.last_qr_cookie_refresh_time = 0
        logger.info(f"【{self.cookie_id}】已重置扫码登录Cookie刷新标志")

    def get_qr_cookie_refresh_remaining_time(self) -> int:
        """获取扫码登录Cookie刷新剩余冷却时间（秒）"""
        current_time = time.time()
        time_since_qr_refresh = current_time - self.last_qr_cookie_refresh_time
        remaining_time = max(0, self.qr_cookie_refresh_cooldown - time_since_qr_refresh)
        return int(remaining_time)

    async def _refresh_cookies_via_browser(self, triggered_by_refresh_token: bool = False):
        """通过浏览器访问指定页面刷新Cookie

        Args:
            triggered_by_refresh_token: 是否由refresh_token方法触发，如果是True则设置browser_cookie_refreshed标志
        """


        playwright = None
        browser = None
        try:
            import asyncio
            from playwright.async_api import async_playwright

            # 检查是否需要等待扫码登录Cookie刷新的冷却时间
            current_time = time.time()
            time_since_qr_refresh = current_time - self.last_qr_cookie_refresh_time

            if time_since_qr_refresh < self.qr_cookie_refresh_cooldown:
                remaining_time = self.qr_cookie_refresh_cooldown - time_since_qr_refresh
                remaining_minutes = int(remaining_time // 60)
                remaining_seconds = int(remaining_time % 60)

                logger.info(f"【{self.cookie_id}】扫码登录Cookie刷新冷却中，还需等待 {remaining_minutes}分{remaining_seconds}秒")
                logger.info(f"【{self.cookie_id}】跳过本次浏览器Cookie刷新")
                return False

            logger.info(f"【{self.cookie_id}】开始通过浏览器刷新Cookie...")
            logger.info(f"【{self.cookie_id}】刷新前Cookie长度: {len(self.cookies_str)}")
            logger.info(f"【{self.cookie_id}】刷新前Cookie字段数: {len(self.cookies)}")

            # 使用统一的Playwright启动方法
            playwright = await _start_playwright_safe(self.cookie_id)
            if not playwright:
                return False

            # 启动浏览器（参照商品搜索的配置）
            browser_args = [
                '--no-sandbox',
                '--disable-setuid-sandbox',
                '--disable-dev-shm-usage',
                '--disable-accelerated-2d-canvas',
                '--no-first-run',
                '--no-zygote',
                '--disable-gpu',
                '--disable-background-timer-throttling',
                '--disable-backgrounding-occluded-windows',
                '--disable-renderer-backgrounding',
                '--disable-features=TranslateUI',
                '--disable-ipc-flooding-protection',
                '--disable-extensions',
                '--disable-default-apps',
                '--disable-sync',
                '--disable-translate',
                '--hide-scrollbars',
                '--mute-audio',
                '--no-default-browser-check',
                '--no-pings'
            ]

            # 在Docker环境中添加额外参数
            if os.getenv('DOCKER_ENV'):
                browser_args.extend([
                    # '--single-process',  # 注释掉，避免多用户并发时的进程冲突和资源泄漏
                    '--disable-background-networking',
                    '--disable-client-side-phishing-detection',
                    '--disable-hang-monitor',
                    '--disable-popup-blocking',
                    '--disable-prompt-on-repost',
                    '--disable-web-resources',
                    '--metrics-recording-only',
                    '--safebrowsing-disable-auto-update',
                    '--enable-automation',
                    '--password-store=basic',
                    '--use-mock-keychain'
                ])

            # Cookie刷新模式使用无头浏览器
            browser = await playwright.chromium.launch(
                headless=True,
                args=browser_args
            )

            # 创建浏览器上下文
            context_options = {
                'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'
            }

            # 使用标准窗口大小
            context_options['viewport'] = {'width': 1920, 'height': 1080}

            context = await browser.new_context(**context_options)

            # 设置当前Cookie
            cookies = []
            for cookie_pair in self.cookies_str.split('; '):
                if '=' in cookie_pair:
                    name, value = cookie_pair.split('=', 1)
                    cookies.append({
                        'name': name.strip(),
                        'value': value.strip(),
                        'domain': '.goofish.com',
                        'path': '/'
                    })

            await context.add_cookies(cookies)
            logger.info(f"【{self.cookie_id}】已设置 {len(cookies)} 个Cookie到浏览器")

            # 创建页面
            page = await context.new_page()

            # 等待页面准备
            await asyncio.sleep(0.1)

            # 访问指定页面
            target_url = "https://www.goofish.com/im"
            logger.info(f"【{self.cookie_id}】访问页面: {target_url}")

            # 使用更灵活的页面访问策略
            try:
                # 首先尝试较短超时
                await page.goto(target_url, wait_until='domcontentloaded', timeout=15000)
                logger.info(f"【{self.cookie_id}】页面访问成功")
            except Exception as e:
                if 'timeout' in str(e).lower():
                    logger.warning(f"【{self.cookie_id}】页面访问超时，尝试降级策略...")
                    try:
                        # 降级策略：只等待基本加载
                        await page.goto(target_url, wait_until='load', timeout=20000)
                        logger.info(f"【{self.cookie_id}】页面访问成功（降级策略）")
                    except Exception as e2:
                        logger.warning(f"【{self.cookie_id}】降级策略也失败，尝试最基本访问...")
                        # 最后尝试：不等待任何加载完成
                        await page.goto(target_url, timeout=25000)
                        logger.info(f"【{self.cookie_id}】页面访问成功（最基本策略）")
                else:
                    raise e

            # Cookie刷新模式：执行两次刷新
            logger.info(f"【{self.cookie_id}】页面加载完成，开始刷新...")
            await asyncio.sleep(1)

            # 第一次刷新 - 带重试机制
            logger.info(f"【{self.cookie_id}】执行第一次刷新...")
            try:
                await page.reload(wait_until='domcontentloaded', timeout=12000)
                logger.info(f"【{self.cookie_id}】第一次刷新成功")
            except Exception as e:
                if 'timeout' in str(e).lower():
                    logger.warning(f"【{self.cookie_id}】第一次刷新超时，使用降级策略...")
                    await page.reload(wait_until='load', timeout=15000)
                    logger.info(f"【{self.cookie_id}】第一次刷新成功（降级策略）")
                else:
                    raise e
            await asyncio.sleep(1)

            # 第二次刷新 - 带重试机制
            logger.info(f"【{self.cookie_id}】执行第二次刷新...")
            try:
                await page.reload(wait_until='domcontentloaded', timeout=12000)
                logger.info(f"【{self.cookie_id}】第二次刷新成功")
            except Exception as e:
                if 'timeout' in str(e).lower():
                    logger.warning(f"【{self.cookie_id}】第二次刷新超时，使用降级策略...")
                    await page.reload(wait_until='load', timeout=15000)
                    logger.info(f"【{self.cookie_id}】第二次刷新成功（降级策略）")
                else:
                    raise e
            await asyncio.sleep(1)

            # Cookie刷新模式：正常更新Cookie
            logger.info(f"【{self.cookie_id}】获取更新后的Cookie...")
            updated_cookies = await context.cookies()
            
            # 获取并打印当前页面标题
            page_title = await page.title()
            logger.info(f"【{self.cookie_id}】当前页面标题: {page_title}")

            # 构造新的Cookie字典
            new_cookies_dict = {}
            for cookie in updated_cookies:
                new_cookies_dict[cookie['name']] = cookie['value']

            # 检查Cookie变化
            changed_cookies = []
            new_cookies = []
            for name, new_value in new_cookies_dict.items():
                old_value = self.cookies.get(name)
                if old_value is None:
                    new_cookies.append(name)
                elif old_value != new_value:
                    changed_cookies.append(name)

            merge_result = self.protected_merge_cookie_dicts(self.cookies, new_cookies_dict)
            merged_cookies_dict = merge_result['merged_cookies_dict']
            self._log_protected_merge_event("browser_refresh_protected_merge", merge_result)

            self._log_cookie_merge_summary(
                merged_cookies_dict,
                merge_result['updated_fields'],
                merge_result['changed_fields'],
                merge_result['new_fields'],
                context="浏览器刷新Cookie",
                preserved_fields=merge_result['preserved_fields'],
                preserved_protected_fields=merge_result['preserved_protected_fields'],
                would_remove_fields=merge_result['would_remove_fields'],
                removed_fields=merge_result['removed_fields'],
                missing_protected_fields=merge_result['missing_protected_fields'],
                missing_required_fields=merge_result['missing_required_fields'],
                incoming_missing_protected_fields=merge_result['incoming_missing_protected_fields'],
                account_switched=merge_result['account_switched'],
            )

            if merge_result['missing_required_fields']:
                logger.error(
                    f"【{self.cookie_id}】浏览器刷新后的Cookie仍缺失核心字段，放弃覆盖当前Cookie: {', '.join(merge_result['missing_required_fields'])}"
                )
                return False

            # 更新self.cookies和cookies_str
            self._set_runtime_cookie_state(
                cookies_dict=merged_cookies_dict,
                source="browser_cookie_refresh",
            )

            logger.info(f"【{self.cookie_id}】Cookie已更新，包含 {len(new_cookies_dict)} 个字段")

            # 显示Cookie变化统计
            if changed_cookies:
                logger.info(f"【{self.cookie_id}】发生变化的Cookie字段 ({len(changed_cookies)}个): {', '.join(changed_cookies)}")
            if new_cookies:
                logger.info(f"【{self.cookie_id}】新增的Cookie字段 ({len(new_cookies)}个): {', '.join(new_cookies)}")
            if not changed_cookies and not new_cookies:
                logger.info(f"【{self.cookie_id}】Cookie无变化")

            # 打印完整的更新后Cookie（可选择性启用）
            logger.info(f"【{self.cookie_id}】更新后的Cookie摘要: {self._summarize_cookie_string(self.cookies_str)}")

            # 打印主要的Cookie字段详情
            important_cookies = ['_m_h5_tk', '_m_h5_tk_enc', 'cookie2', 't', 'sgcookie', 'unb', 'uc1', 'uc3', 'uc4']
            logger.info(f"【{self.cookie_id}】重要Cookie字段详情:")
            for cookie_name in important_cookies:
                if cookie_name in new_cookies_dict:
                    cookie_value = new_cookies_dict[cookie_name]
                    # 对于敏感信息，只显示前后几位
                    if len(cookie_value) > 20:
                        display_value = f"{cookie_value[:8]}...{cookie_value[-8:]}"
                    else:
                        display_value = cookie_value

                    # 标记是否发生了变化
                    change_mark = " [已变化]" if cookie_name in changed_cookies else " [新增]" if cookie_name in new_cookies else ""
                    logger.info(f"【{self.cookie_id}】  {cookie_name}: {display_value}{change_mark}")

            # 更新数据库中的Cookie
            await self.update_config_cookies()

            # 只有当由refresh_token触发时才设置浏览器Cookie刷新成功标志
            if triggered_by_refresh_token:
                self.browser_cookie_refreshed = True
                logger.info(f"【{self.cookie_id}】由refresh_token触发，浏览器Cookie刷新成功标志已设置为True")

                # 兜底：直接在此处触发实例重启，避免外层协程在返回后被取消导致未重启
                try:
                    # 标记"刷新流程内已触发重启"，供外层去重
                    self.restarted_in_browser_refresh = True

                    logger.info(f"【{self.cookie_id}】Cookie刷新成功，准备重启实例...(via _refresh_cookies_via_browser)")
                    await self._restart_instance()
                    
                    # ⚠️ _restart_instance() 已触发重启，当前任务即将被取消
                    # 不要等待或执行耗时操作
                    logger.info(f"【{self.cookie_id}】重启请求已触发(via _refresh_cookies_via_browser)")
                    
                    # 标记重启标志（无需主动关闭WS，重启由管理器处理）
                    self.connection_restart_flag = True
                except Exception as e:
                    logger.error(f"【{self.cookie_id}】兜底重启失败: {self._safe_str(e)}")
            else:
                logger.info(f"【{self.cookie_id}】由定时任务触发，不设置浏览器Cookie刷新成功标志")

            logger.info(f"【{self.cookie_id}】Cookie刷新完成")
            return True

        except Exception as e:
            logger.error(f"【{self.cookie_id}】通过浏览器刷新Cookie失败: {self._safe_str(e)}")
            return False
        finally:
            # 异步关闭浏览器：创建清理任务并等待完成，确保资源正确释放
            close_task = None
            try:
                if browser or playwright:
                    # 创建关闭任务
                    close_task = asyncio.create_task(
                        self._async_close_browser(browser, playwright)
                    )
                    logger.info(f"【{self.cookie_id}】浏览器异步关闭任务已启动")
                    
                    # 等待关闭任务完成，但设置超时避免阻塞太久
                    try:
                        await asyncio.wait_for(close_task, timeout=15.0)
                        logger.info(f"【{self.cookie_id}】浏览器关闭任务已完成")
                    except asyncio.TimeoutError:
                        logger.warning(f"【{self.cookie_id}】浏览器关闭任务超时（15秒），强制继续")
                        # 取消任务，避免资源泄漏
                        if not close_task.done():
                            close_task.cancel()
                            try:
                                await close_task
                            except (asyncio.CancelledError, Exception):
                                pass
                    except Exception as wait_e:
                        logger.warning(f"【{self.cookie_id}】等待浏览器关闭任务时出错: {self._safe_str(wait_e)}")
                        # 确保任务被取消
                        if close_task and not close_task.done():
                            close_task.cancel()
                            try:
                                await close_task
                            except (asyncio.CancelledError, Exception):
                                pass
            except Exception as cleanup_e:
                logger.warning(f"【{self.cookie_id}】创建浏览器关闭任务时出错: {self._safe_str(cleanup_e)}")
                # 如果创建任务失败，尝试直接关闭
                if browser or playwright:
                    try:
                        await self._force_close_resources(browser, playwright)
                    except Exception:
                        pass

    async def _async_close_browser(self, browser, playwright):
        """异步关闭：正常关闭，超时后强制关闭"""
        try:
            logger.info(f"【{self.cookie_id}】开始异步关闭浏览器...")  # 改为info级别
            
            # 正常关闭，设置超时
            await asyncio.wait_for(
                self._normal_close_resources(browser, playwright),
                timeout=10.0
            )
            logger.info(f"【{self.cookie_id}】浏览器正常关闭完成")  # 改为info级别
            
        except asyncio.TimeoutError:
            logger.warning(f"【{self.cookie_id}】正常关闭超时，开始强制关闭...")
            await self._force_close_resources(browser, playwright)
            
        except Exception as e:
            logger.warning(f"【{self.cookie_id}】异步关闭时出错，强制关闭: {self._safe_str(e)}")
            await self._force_close_resources(browser, playwright)

    async def _normal_close_resources(self, browser, playwright):
        """正常关闭资源：浏览器+Playwright短超时关闭"""
        try:
            # 先关闭浏览器，再关闭Playwright
            if browser:
                try:
                    # 关闭浏览器，设置超时
                    await asyncio.wait_for(browser.close(), timeout=5.0)
                    logger.info(f"【{self.cookie_id}】浏览器关闭完成")
                except asyncio.TimeoutError:
                    logger.warning(f"【{self.cookie_id}】浏览器关闭超时，尝试强制关闭")
                    try:
                        # 尝试强制关闭
                        if hasattr(browser, '_connection'):
                            browser._connection.dispose()
                    except Exception:
                        pass
                except Exception as e:
                    logger.warning(f"【{self.cookie_id}】关闭浏览器时出错: {e}")
            
            # 关闭Playwright：使用短超时，如果超时就放弃
            if playwright:
                try:
                    logger.info(f"【{self.cookie_id}】正在关闭Playwright...")
                    # 增加超时时间，确保Playwright有足够时间清理资源
                    await asyncio.wait_for(playwright.stop(), timeout=5.0)
                    logger.info(f"【{self.cookie_id}】Playwright关闭完成")
                except asyncio.TimeoutError:
                    logger.warning(f"【{self.cookie_id}】Playwright关闭超时，将自动清理")
                    # 尝试强制清理Playwright的内部连接
                    try:
                        if hasattr(playwright, '_connection'):
                            playwright._connection.dispose()
                    except Exception:
                        pass
                except Exception as e:
                    logger.warning(f"【{self.cookie_id}】关闭Playwright时出错: {e}")
                
        except Exception as e:
            logger.error(f"【{self.cookie_id}】正常关闭时出现异常: {e}")
            raise

    
    async def _force_close_resources(self, browser, playwright):
        """强制关闭资源：强制关闭浏览器+Playwright超时等待"""
        try:
            logger.warning(f"【{self.cookie_id}】开始强制关闭资源...")
            
            # 强制关闭浏览器+Playwright，设置短超时
            force_tasks = []
            if browser:
                force_tasks.append(asyncio.wait_for(browser.close(), timeout=3.0))
            if playwright:
                force_tasks.append(asyncio.wait_for(playwright.stop(), timeout=3.0))
            
            if force_tasks:
                # 使用gather执行，所有失败都会被忽略
                results = await asyncio.gather(*force_tasks, return_exceptions=True)
                
                # 检查是否有超时或异常，尝试强制清理
                for i, result in enumerate(results):
                    if isinstance(result, (asyncio.TimeoutError, Exception)):
                        resource_name = "浏览器" if i == 0 and browser else "Playwright"
                        logger.warning(f"【{self.cookie_id}】{resource_name}强制关闭失败，尝试直接清理连接")
                        try:
                            if i == 0 and browser and hasattr(browser, '_connection'):
                                browser._connection.dispose()
                            elif playwright and hasattr(playwright, '_connection'):
                                playwright._connection.dispose()
                        except Exception:
                            pass
                
                logger.info(f"【{self.cookie_id}】强制关闭完成")
            else:
                logger.info(f"【{self.cookie_id}】没有需要强制关闭的资源")
            
        except Exception as e:
            logger.warning(f"【{self.cookie_id}】强制关闭时出现异常（已忽略）: {e}")

    async def send_msg_once(self, toid, item_id, text):
        """单次发送消息（创建新的WebSocket连接）"""
        headers = self._build_websocket_headers()

        logger.info(f"【{self.cookie_id}】开始单次发送消息: toid={toid}, item_id={item_id}")

        # 兼容不同版本的websockets库
        try:
            async with websockets.connect(
                self.base_url,
                extra_headers=headers,
                close_timeout=5  # 添加关闭超时
            ) as websocket:
                result = await self._handle_websocket_connection(websocket, toid, item_id, text)
                if result:
                    logger.info(f"【{self.cookie_id}】单次发送消息成功")
                else:
                    raise Exception("消息发送失败")
        except TypeError as e:
            # 安全地检查异常信息
            error_msg = self._safe_str(e)

            if "extra_headers" in error_msg:
                logger.warning("websockets库不支持extra_headers参数，使用兼容模式")
                # 使用兼容模式
                async with websockets.connect(
                    self.base_url,
                    additional_headers=headers,
                    close_timeout=5
                ) as websocket:
                    result = await self._handle_websocket_connection(websocket, toid, item_id, text)
                    if result:
                        logger.info(f"【{self.cookie_id}】单次发送消息成功(兼容模式)")
                    else:
                        raise Exception("消息发送失败")
            else:
                raise
        except websockets.exceptions.ConnectionClosedError as e:
            logger.warning(f"【{self.cookie_id}】WebSocket连接关闭: {self._safe_str(e)}")
            # 连接关闭但消息可能已发送，不抛出异常
        except Exception as e:
            logger.error(f"【{self.cookie_id}】单次发送消息异常: {self._safe_str(e)}")
            raise

    async def send_delivery_steps_once(self, toid, item_id, delivery_steps):
        """单次发送发货步骤（创建新的WebSocket连接）。"""
        headers = self._build_websocket_headers()

        logger.info(f"【{self.cookie_id}】开始单次发送发货步骤: toid={toid}, item_id={item_id}, steps={len(delivery_steps or [])}")

        try:
            async with websockets.connect(
                self.base_url,
                extra_headers=headers,
                close_timeout=5
            ) as websocket:
                result = await self._handle_websocket_connection_steps(websocket, toid, item_id, delivery_steps)
                if result:
                    logger.info(f"【{self.cookie_id}】单次发送发货步骤成功")
                else:
                    raise Exception("发货步骤发送失败")
        except TypeError as e:
            error_msg = self._safe_str(e)

            if "extra_headers" in error_msg:
                logger.warning("websockets库不支持extra_headers参数，使用兼容模式发送发货步骤")
                async with websockets.connect(
                    self.base_url,
                    additional_headers=headers,
                    close_timeout=5
                ) as websocket:
                    result = await self._handle_websocket_connection_steps(websocket, toid, item_id, delivery_steps)
                    if result:
                        logger.info(f"【{self.cookie_id}】单次发送发货步骤成功(兼容模式)")
                    else:
                        raise Exception("发货步骤发送失败")
            else:
                raise
        except websockets.exceptions.ConnectionClosedError as e:
            logger.warning(f"【{self.cookie_id}】WebSocket连接关闭: {self._safe_str(e)}")
        except Exception as e:
            logger.error(f"【{self.cookie_id}】单次发送发货步骤异常: {self._safe_str(e)}")
            raise

    async def _handle_websocket_connection_steps(self, websocket, toid, item_id, delivery_steps):
        """处理WebSocket连接的发货步骤发送逻辑。"""
        try:
            await self.init(websocket)
            await self.create_chat(websocket, toid, item_id)

            timeout = 30
            start_time = time.time()

            async for message in websocket:
                try:
                    if time.time() - start_time > timeout:
                        logger.warning(f"【{self.cookie_id}】WebSocket消息等待超时")
                        break

                    logger.info(f"【{self.cookie_id}】message: {message}")
                    message = json.loads(message)
                    cid = message["body"]["singleChatConversation"]["cid"]
                    cid = cid.split('@')[0]
                    await self._send_delivery_steps(
                        websocket,
                        cid,
                        toid,
                        delivery_steps,
                        log_prefix="单次手动发货"
                    )
                    logger.info(f'【{self.cookie_id}】send delivery steps success')
                    return True
                except KeyError:
                    continue
                except Exception as e:
                    logger.warning(f"【{self.cookie_id}】处理消息异常: {self._safe_str(e)}")
                    continue

            logger.warning(f"【{self.cookie_id}】WebSocket连接关闭，未能发送发货步骤")
            return False
        except Exception as e:
            logger.error(f"【{self.cookie_id}】WebSocket发货步骤处理异常: {self._safe_str(e)}")
            return False

    async def _create_websocket_connection(self, headers):
        """创建WebSocket连接，兼容不同版本的websockets库，支持代理配置"""
        import websockets

        # 获取websockets版本用于调试
        websockets_version = getattr(websockets, '__version__', '未知')
        logger.info(f"【{self.cookie_id}】websockets库版本: {websockets_version}")

        # 检查是否需要使用代理
        proxy_url = self._get_proxy_url()
        proxy_sock = None
        
        if proxy_url:
            proxy_type = self.proxy_config.get('proxy_type', 'none')
            logger.info(f"【{self.cookie_id}】WebSocket将通过代理连接: {proxy_type}://{self.proxy_config.get('proxy_host')}:{self.proxy_config.get('proxy_port')}")
            
            try:
                from python_socks.async_.asyncio.v2 import Proxy
                from python_socks import ProxyType as SocksProxyType
                import ssl
                
                # 确定代理类型
                if proxy_type == 'socks5':
                    socks_type = SocksProxyType.SOCKS5
                elif proxy_type == 'socks4':
                    socks_type = SocksProxyType.SOCKS4
                elif proxy_type in ['http', 'https']:
                    socks_type = SocksProxyType.HTTP
                else:
                    socks_type = None
                
                if socks_type:
                    # 解析WebSocket URL获取目标主机和端口
                    import urllib.parse
                    parsed_url = urllib.parse.urlparse(self.base_url)
                    dest_host = parsed_url.hostname
                    dest_port = parsed_url.port or (443 if parsed_url.scheme == 'wss' else 80)
                    
                    # 创建代理连接
                    proxy = Proxy(
                        proxy_type=socks_type,
                        host=self.proxy_config.get('proxy_host'),
                        port=self.proxy_config.get('proxy_port'),
                        username=self.proxy_config.get('proxy_user') or None,
                        password=self.proxy_config.get('proxy_pass') or None
                    )
                    
                    # 通过代理连接到目标服务器
                    proxy_sock = await proxy.connect(
                        dest_host=dest_host,
                        dest_port=dest_port
                    )
                    
                    # 如果是wss，需要升级为SSL
                    if parsed_url.scheme == 'wss':
                        ssl_context = ssl.create_default_context()
                        proxy_sock = ssl_context.wrap_socket(
                            proxy_sock,
                            server_hostname=dest_host
                        )
                    
                    logger.info(f"【{self.cookie_id}】代理连接建立成功")
                    
            except ImportError as e:
                logger.warning(f"【{self.cookie_id}】代理连接需要安装 python-socks: pip install python-socks[asyncio]")
                logger.warning(f"【{self.cookie_id}】将尝试不使用代理进行WebSocket连接")
                proxy_sock = None
            except Exception as e:
                logger.error(f"【{self.cookie_id}】通过代理建立连接失败: {self._safe_str(e)}")
                logger.warning(f"【{self.cookie_id}】将尝试不使用代理进行WebSocket连接")
                proxy_sock = None

        try:
            # 尝试使用extra_headers参数
            connect_kwargs = {
                'extra_headers': headers
            }
            if proxy_sock:
                connect_kwargs['sock'] = proxy_sock
                
            return websockets.connect(
                self.base_url,
                **connect_kwargs
            )
        except Exception as e:
            # 捕获所有异常类型，不仅仅是TypeError
            error_msg = self._safe_str(e)
            logger.warning(f"【{self.cookie_id}】extra_headers参数失败: {error_msg}")

            if "extra_headers" in error_msg or "unexpected keyword argument" in error_msg:
                logger.warning(f"【{self.cookie_id}】websockets库不支持extra_headers参数，尝试additional_headers")
                # 使用additional_headers参数（较新版本）
                try:
                    connect_kwargs = {
                        'additional_headers': headers
                    }
                    if proxy_sock:
                        connect_kwargs['sock'] = proxy_sock
                        
                    return websockets.connect(
                        self.base_url,
                        **connect_kwargs
                    )
                except Exception as e2:
                    error_msg2 = self._safe_str(e2)
                    logger.warning(f"【{self.cookie_id}】additional_headers参数失败: {error_msg2}")

                    if "additional_headers" in error_msg2 or "unexpected keyword argument" in error_msg2:
                        raise RuntimeError(
                            f"当前websockets库不支持header参数，无法安全建立鉴权连接: {error_msg2}"
                        )
                    else:
                        raise e2
            else:
                raise e

    async def _handle_websocket_connection(self, websocket, toid, item_id, text):
        """处理WebSocket连接的具体逻辑"""
        try:
            await self.init(websocket)
            await self.create_chat(websocket, toid, item_id)

            # 添加超时处理，最多等待30秒
            timeout = 30
            start_time = time.time()

            async for message in websocket:
                try:
                    # 检查是否超时
                    if time.time() - start_time > timeout:
                        logger.warning(f"【{self.cookie_id}】WebSocket消息等待超时")
                        break

                    logger.info(f"【{self.cookie_id}】message: {message}")
                    message = json.loads(message)
                    cid = message["body"]["singleChatConversation"]["cid"]
                    cid = cid.split('@')[0]
                    await self.send_msg(websocket, cid, toid, text)
                    logger.info(f'【{self.cookie_id}】send message success')
                    return True
                except KeyError:
                    # 消息格式不符合预期，继续等待
                    continue
                except Exception as e:
                    logger.warning(f"【{self.cookie_id}】处理消息异常: {self._safe_str(e)}")
                    continue

            logger.warning(f"【{self.cookie_id}】WebSocket连接关闭，未能发送消息")
            return False
        except Exception as e:
            logger.error(f"【{self.cookie_id}】WebSocket连接处理异常: {self._safe_str(e)}")
            return False

    def is_chat_message(self, message):
        """判断是否为用户聊天消息"""
        try:
            return (
                isinstance(message, dict)
                and "1" in message
                and isinstance(message["1"], dict)
                and "10" in message["1"]
                and isinstance(message["1"]["10"], dict)
                and "reminderContent" in message["1"]["10"]
            )
        except Exception:
            return False

    def is_sync_package(self, message_data):
        """判断是否为同步包消息"""
        try:
            return (
                isinstance(message_data, dict)
                and "body" in message_data
                and "syncPushPackage" in message_data["body"]
                and "data" in message_data["body"]["syncPushPackage"]
                and len(message_data["body"]["syncPushPackage"]["data"]) > 0
            )
        except Exception:
            return False

    async def create_session(self):
        """创建aiohttp session，支持代理配置"""
        if not self.session:
            # 创建带有cookies和headers的session
            headers = DEFAULT_HEADERS.copy()

            proxy_url = self._get_proxy_url()
            connector = None
            
            if proxy_url:
                proxy_type = self.proxy_config.get('proxy_type', 'none')
                logger.info(f"【{self.cookie_id}】创建带代理的Session: {proxy_type}://{self.proxy_config.get('proxy_host')}:{self.proxy_config.get('proxy_port')}")
                
                if proxy_type == 'socks5':
                    # SOCKS5 代理使用 aiohttp_socks
                    try:
                        from aiohttp_socks import ProxyConnector, ProxyType
                        connector = ProxyConnector(
                            proxy_type=ProxyType.SOCKS5,
                            host=self.proxy_config.get('proxy_host'),
                            port=self.proxy_config.get('proxy_port'),
                            username=self.proxy_config.get('proxy_user') or None,
                            password=self.proxy_config.get('proxy_pass') or None,
                            rdns=True  # 使用代理服务器解析DNS
                        )
                    except ImportError:
                        logger.error(f"【{self.cookie_id}】SOCKS5代理需要安装 aiohttp-socks: pip install aiohttp-socks")
                        connector = None
                else:
                    # HTTP/HTTPS 代理使用 aiohttp 内置支持（通过 trust_env 或在请求时指定）
                    # 注意：aiohttp 的 TCPConnector 不直接支持 proxy 参数
                    # 代理将在每次请求时通过 proxy 参数指定
                    connector = aiohttp.TCPConnector(limit=100, limit_per_host=30)
            else:
                connector = aiohttp.TCPConnector(limit=100, limit_per_host=30)

            self.session = aiohttp.ClientSession(
                headers=headers,
                timeout=aiohttp.ClientTimeout(total=30),
                connector=connector
            )
            self._sync_session_cookie_header()
            
            # 保存代理URL供后续请求使用（HTTP/HTTPS代理）
            self._http_proxy_url = proxy_url if proxy_url and self.proxy_config.get('proxy_type') in ['http', 'https'] else None

    async def close_session(self):
        """关闭aiohttp session"""
        if self.session:
            await self.session.close()
            self.session = None

    async def get_api_reply(self, msg_time, user_url, send_user_id, send_user_name, item_id, send_message, chat_id):
        """调用API获取回复消息"""
        try:
            if not self.session:
                await self.create_session()

            api_config = AUTO_REPLY.get('api', {})
            timeout = aiohttp.ClientTimeout(total=api_config.get('timeout', 10))

            payload = {
                "cookie_id": self.cookie_id,
                "msg_time": msg_time,
                "user_url": user_url,
                "send_user_id": send_user_id,
                "send_user_name": send_user_name,
                "item_id": item_id,
                "send_message": send_message,
                "chat_id": chat_id
            }

            async with self.session.post(
                api_config.get('url', 'http://localhost:8080/xianyu/reply'),
                json=payload,
                timeout=timeout
            ) as response:
                result = await response.json()

                # 将code转换为字符串进行比较，或者直接用数字比较
                if str(result.get('code')) == '200' or result.get('code') == 200:
                    send_msg = result.get('data', {}).get('send_msg')
                    if send_msg:
                        # 格式化消息中的占位符
                        return send_msg.format(
                            send_user_id=payload['send_user_id'],
                            send_user_name=payload['send_user_name'],
                            send_message=payload['send_message']
                        )
                    else:
                        logger.warning("API返回成功但无回复消息")
                        return None
                else:
                    logger.warning(f"API返回错误: {result.get('msg', '未知错误')}")
                    return None

        except asyncio.TimeoutError:
            logger.error("API调用超时")
            return None
        except Exception as e:
            logger.error(f"调用API出错: {self._safe_str(e)}")
            return None

    async def _handle_message_with_semaphore(self, message_data, websocket, msg_id="unknown"):
        """带信号量的消息处理包装器，防止并发任务过多"""
        async with self.message_semaphore:
            self.active_message_tasks += 1
            try:
                await self.handle_message(message_data, websocket, msg_id)
            finally:
                self.active_message_tasks -= 1
                # 定期记录活跃任务数（每100个任务记录一次）
                if self.active_message_tasks % 100 == 0 and self.active_message_tasks > 0:
                    logger.info(f"【{self.cookie_id}】当前活跃消息处理任务数: {self.active_message_tasks}")

    def _extract_message_id(self, message_data: dict) -> str:
        """
        从消息数据中提取消息ID，用于去重
        
        Args:
            message_data: 原始消息数据
            
        Returns:
            消息ID字符串，如果无法提取则返回None
        """
        try:
            # 尝试从 message['1']['10']['bizTag'] 中提取 messageId
            if isinstance(message_data, dict) and "1" in message_data:
                message_1 = message_data.get("1")
                if isinstance(message_1, dict) and "10" in message_1:
                    message_10 = message_1.get("10")
                    if isinstance(message_10, dict) and "bizTag" in message_10:
                        biz_tag = message_10.get("bizTag", "")
                        if isinstance(biz_tag, str):
                            # bizTag 是 JSON 字符串，格式如: '{"sourceId":"S:1","messageId":"984f323c719d4cd0a7b993a0769a33b6"}'
                            try:
                                import json
                                biz_tag_dict = json.loads(biz_tag)
                                if isinstance(biz_tag_dict, dict) and "messageId" in biz_tag_dict:
                                    return biz_tag_dict.get("messageId")
                            except (json.JSONDecodeError, TypeError):
                                pass
                        
                        # 如果 bizTag 解析失败，尝试从 extJson 中提取
                        if "extJson" in message_10:
                            ext_json = message_10.get("extJson", "")
                            if isinstance(ext_json, str):
                                try:
                                    import json
                                    ext_json_dict = json.loads(ext_json)
                                    if isinstance(ext_json_dict, dict) and "messageId" in ext_json_dict:
                                        return ext_json_dict.get("messageId")
                                except (json.JSONDecodeError, TypeError):
                                    pass
        except Exception as e:
            logger.debug(f"【{self.cookie_id}】提取消息ID失败: {self._safe_str(e)}")
        
        return None

    async def _schedule_debounced_reply(self, chat_id: str, message_data: dict, websocket, 
                                       send_user_name: str, send_user_id: str, send_message: str,
                                       item_id: str, msg_time: str):
        """
        调度防抖回复：如果用户连续发送消息，等待用户停止发送后再回复最后一条消息
        
        Args:
            chat_id: 聊天ID
            message_data: 原始消息数据
            websocket: WebSocket连接
            send_user_name: 发送者用户名
            send_user_id: 发送者用户ID
            send_message: 消息内容
            item_id: 商品ID
            msg_time: 消息时间
        """
        # 提取消息ID并检查是否已处理
        message_id = self._extract_message_id(message_data)
        # 如果没有 messageId，使用备用标识（chat_id + send_message + 时间戳）
        if not message_id:
            try:
                # 尝试从消息数据中提取时间戳
                create_time = 0
                if isinstance(message_data, dict) and "1" in message_data:
                    message_1 = message_data.get("1")
                    if isinstance(message_1, dict):
                        create_time = message_1.get("5", 0)
                # 使用组合键作为备用标识
                message_id = f"{chat_id}_{send_message}_{create_time}"
            except Exception:
                # 如果提取失败，使用当前时间戳
                message_id = f"{chat_id}_{send_message}_{int(time.time() * 1000)}"
        
        async with self.processed_message_ids_lock:
            current_time = time.time()
            
            # 检查消息是否已处理且未过期
            if message_id in self.processed_message_ids:
                last_process_time = self.processed_message_ids[message_id]
                time_elapsed = current_time - last_process_time
                
                # 如果消息处理时间未超过1小时，跳过
                if time_elapsed < self.message_expire_time:
                    remaining_time = int(self.message_expire_time - time_elapsed)
                    logger.warning(f"【{self.cookie_id}】消息ID {message_id[:50]}... 已处理过，距离可重复回复还需 {remaining_time} 秒")
                    return
                else:
                    # 超过1小时，可以重新处理
                    logger.info(f"【{self.cookie_id}】消息ID {message_id[:50]}... 已超过 {int(time_elapsed/60)} 分钟，允许重新回复")
            
            # 标记消息ID为已处理（更新或添加时间戳）
            self.processed_message_ids[message_id] = current_time
            
            # 定期清理过期的消息ID
            if len(self.processed_message_ids) > self.processed_message_ids_max_size:
                # 清理超过1小时的旧记录
                expired_ids = [
                    msg_id for msg_id, timestamp in self.processed_message_ids.items()
                    if current_time - timestamp > self.message_expire_time
                ]
                
                for msg_id in expired_ids:
                    del self.processed_message_ids[msg_id]
                
                logger.info(f"【{self.cookie_id}】已清理 {len(expired_ids)} 个过期消息ID")
                
                # 如果清理后仍然过大，删除最旧的一半
                if len(self.processed_message_ids) > self.processed_message_ids_max_size:
                    sorted_ids = sorted(self.processed_message_ids.items(), key=lambda x: x[1])
                    remove_count = len(sorted_ids) // 2
                    for msg_id, _ in sorted_ids[:remove_count]:
                        del self.processed_message_ids[msg_id]
                    logger.info(f"【{self.cookie_id}】消息ID去重字典过大，已清理 {remove_count} 个最旧记录")
        
        async with self.message_debounce_lock:
            # 如果该chat_id已有防抖任务，取消它
            if chat_id in self.message_debounce_tasks:
                old_task = self.message_debounce_tasks[chat_id].get('task')
                if old_task and not old_task.done():
                    old_task.cancel()
                    logger.warning(f"【{self.cookie_id}】取消chat_id {chat_id} 的旧防抖任务")
            
            # 更新最后一条消息信息
            current_timer = time.time()
            self.message_debounce_tasks[chat_id] = {
                'last_message': {
                    'message_data': message_data,
                    'websocket': websocket,
                    'send_user_name': send_user_name,
                    'send_user_id': send_user_id,
                    'send_message': send_message,
                    'item_id': item_id,
                    'msg_time': msg_time
                },
                'timer': current_timer
            }
            
            # 创建新的防抖任务
            async def debounce_task():
                saved_timer = current_timer  # 保存创建任务时的时间戳
                try:
                    # 等待防抖延迟时间
                    await asyncio.sleep(self.message_debounce_delay)
                    
                    # 检查是否仍然是最新的消息（防止在等待期间有新消息）
                    async with self.message_debounce_lock:
                        if chat_id not in self.message_debounce_tasks:
                            return
                        
                        debounce_info = self.message_debounce_tasks[chat_id]
                        # 检查时间戳是否匹配（确保这是最新的消息）
                        if saved_timer != debounce_info['timer']:
                            logger.warning(f"【{self.cookie_id}】chat_id {chat_id} 在防抖期间有新消息，跳过旧消息处理")
                            return
                        
                        # 获取最后一条消息
                        last_msg = debounce_info['last_message']
                        
                        # 从防抖任务中移除
                        del self.message_debounce_tasks[chat_id]
                    
                    # 处理最后一条消息
                    logger.info(f"【{self.cookie_id}】防抖延迟结束，开始处理chat_id {chat_id} 的最后一条消息: {last_msg['send_message'][:30]}...")
                    await self._process_chat_message_reply(
                        last_msg['message_data'],
                        last_msg['websocket'],
                        last_msg['send_user_name'],
                        last_msg['send_user_id'],
                        last_msg['send_message'],
                        last_msg['item_id'],
                        chat_id,
                        last_msg['msg_time']
                    )
                    
                except asyncio.CancelledError:
                    logger.warning(f"【{self.cookie_id}】chat_id {chat_id} 的防抖任务被取消")
                except Exception as e:
                    logger.error(f"【{self.cookie_id}】处理防抖回复时发生错误: {self._safe_str(e)}")
                    # 确保从防抖任务中移除
                    async with self.message_debounce_lock:
                        if chat_id in self.message_debounce_tasks:
                            del self.message_debounce_tasks[chat_id]
            
            task = self._create_tracked_task(debounce_task())
            self.message_debounce_tasks[chat_id]['task'] = task
            logger.warning(f"【{self.cookie_id}】为chat_id {chat_id} 创建防抖任务，延迟 {self.message_debounce_delay} 秒")

    async def _process_chat_message_reply(self, message_data: dict, websocket, send_user_name: str,
                                         send_user_id: str, send_message: str, item_id: str,
                                         chat_id: str, msg_time: str):
        """
        处理聊天消息的回复逻辑（从handle_message中提取出来的核心回复逻辑）
        
        Args:
            message_data: 原始消息数据
            websocket: WebSocket连接
            send_user_name: 发送者用户名
            send_user_id: 发送者用户ID
            send_message: 消息内容
            item_id: 商品ID
            chat_id: 聊天ID
            msg_time: 消息时间
        """
        try:
            # 自动回复消息
            if not AUTO_REPLY.get('enabled', True):
                logger.info(f"[{msg_time}] 【{self.cookie_id}】【系统】自动回复已禁用")
                return

            # 检查该chat_id是否处于暂停状态
            if pause_manager.is_chat_paused(chat_id):
                remaining_time = pause_manager.get_remaining_pause_time(chat_id)
                remaining_minutes = remaining_time // 60
                remaining_seconds = remaining_time % 60
                logger.info(f"[{msg_time}] 【{self.cookie_id}】【系统】chat_id {chat_id} 自动回复已暂停，剩余时间: {remaining_minutes}分{remaining_seconds}秒")
                return

            reply = None
            reply_source = None

            # 按 README 定义的优先级处理：
            # 指定商品回复 > 商品专用关键词 > 通用关键词 > 默认回复 > AI回复
            reply = await self.get_item_specific_reply(send_user_name, send_user_id, send_message, item_id)
            if reply:
                reply_source = '指定商品'
            else:
                # 1. 尝试关键词匹配（内部已区分商品专用关键词和通用关键词）
                reply = await self.get_keyword_reply(send_user_name, send_user_id, send_message, item_id)
                if reply == "EMPTY_REPLY":
                    # 匹配到关键词但回复内容为空，不进行任何回复
                    logger.info(f"[{msg_time}] 【{self.cookie_id}】匹配到空回复关键词，跳过自动回复")
                    return
                elif reply:
                    reply_source = '关键词'  # 标记为关键词回复
                else:
                    # 2. 关键词匹配失败后，使用默认回复兜底
                    reply = await self.get_default_reply(send_user_name, send_user_id, send_message, chat_id, item_id)
                    if reply == "EMPTY_REPLY":
                        logger.info(f"[{msg_time}] 【{self.cookie_id}】默认回复内容为空，跳过自动回复")
                        return
                    elif reply == "SKIP_REPLY":
                        logger.info(f"[{msg_time}] 【{self.cookie_id}】默认回复已命中过当前会话，跳过自动回复")
                        return
                    elif reply:
                        reply_source = '默认'
                    else:
                        # 3. 最后尝试AI回复
                        reply = await self.get_ai_reply(send_user_name, send_user_id, send_message, item_id, chat_id)
                        if reply:
                            reply_source = 'AI'

            # 注意：这里只有商品ID，没有标题和详情，根据新的规则不保存到数据库
            # 商品信息会在其他有完整信息的地方保存（如发货规则匹配时）
            # 消息通知已在收到消息时立即发送，此处不再重复发送

            # 如果有回复内容，发送消息
            if reply:
                # 检查是否是图片发送标记
                if reply.startswith("__IMAGE_SEND__"):
                    # 提取图片URL（关键词回复不包含卡券ID）
                    image_url = reply.replace("__IMAGE_SEND__", "")
                    # 发送图片消息
                    try:
                        await self.send_image_msg(websocket, chat_id, send_user_id, image_url)
                        # 记录发出的图片消息
                        msg_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                        logger.info(f"[{msg_time}] 【{reply_source}图片发出】用户: {send_user_name} (ID: {send_user_id}), 商品({item_id}): 图片 {image_url}")
                    except Exception as e:
                        # 图片发送失败，发送错误提示
                        logger.error(f"图片发送失败: {self._safe_str(e)}")
                        await self.send_msg(websocket, chat_id, send_user_id, "抱歉，图片发送失败，请稍后重试。")
                        msg_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                        logger.error(f"[{msg_time}] 【{reply_source}图片发送失败】用户: {send_user_name} (ID: {send_user_id}), 商品({item_id})")
                else:
                    # 普通文本消息
                    await self.send_msg(websocket, chat_id, send_user_id, reply)
                    # 记录发出的消息
                    msg_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                    logger.info(f"[{msg_time}] 【{reply_source}发出】用户: {send_user_name} (ID: {send_user_id}), 商品({item_id}): {reply}")
            else:
                msg_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                logger.info(f"[{msg_time}] 【{self.cookie_id}】【系统】未找到匹配的回复规则，不回复")
        except Exception as e:
            logger.error(f"处理聊天消息回复时发生错误: {self._safe_str(e)}")

    async def handle_message(self, message_data, websocket, msg_id="unknown"):
        """处理所有类型的消息"""
        # 获取消息大小用于追踪
        msg_size = len(json.dumps(message_data)) if message_data else 0
        logger.info(f"【{self.cookie_id}】[{msg_id}] 🚀 开始处理消息 ({msg_size}字节)")
        
        try:
            # 检查账号是否启用
            from cookie_manager import manager as cookie_manager
            if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
                logger.warning(f"【{self.cookie_id}】[{msg_id}] ⏹️ 账号已禁用，消息处理结束")
                return

            # 发送确认消息
            try:
                message = message_data
                ack = {
                    "code": 200,
                    "headers": {
                        "mid": message["headers"]["mid"] if "mid" in message["headers"] else generate_mid(),
                        "sid": message["headers"]["sid"] if "sid" in message["headers"] else '',
                    }
                }
                if 'app-key' in message["headers"]:
                    ack["headers"]["app-key"] = message["headers"]["app-key"]
                if 'ua' in message["headers"]:
                    ack["headers"]["ua"] = message["headers"]["ua"]
                if 'dt' in message["headers"]:
                    ack["headers"]["dt"] = message["headers"]["dt"]
                await websocket.send(json.dumps(ack))
            except Exception as e:
                logger.debug(f"【{self.cookie_id}】[{msg_id}] 发送ACK失败: {e}")

            # 如果不是同步包消息，直接返回
            if not self.is_sync_package(message_data):
                logger.debug(f"【{self.cookie_id}】[{msg_id}] ⏹️ 非同步包消息，处理结束")
                return

            # 获取并解密数据
            sync_data = message_data["body"]["syncPushPackage"]["data"][0]

            # 检查是否有必要的字段
            if "data" not in sync_data:
                logger.warning(f"【{self.cookie_id}】[{msg_id}] ⚠️ 同步包中无data字段，消息内容: {sync_data}")
                logger.warning(f"【{self.cookie_id}】[{msg_id}] ⏹️ 消息处理结束（缺少data字段）")
                return

            # 解密数据
            message = None
            try:
                data = sync_data["data"]
                logger.debug(f"【{self.cookie_id}】[{msg_id}] 开始解密同步包数据...")
                try:
                    data = base64.b64decode(data).decode("utf-8")
                    parsed_data = json.loads(data)
                    # 处理未加密的消息（如系统提示等）
                    msg_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                    if isinstance(parsed_data, dict) and 'chatType' in parsed_data:
                        logger.warning(f"【{self.cookie_id}】[{msg_id}] ⚠️ 检测到chatType消息，完整内容: {parsed_data}")
                        if 'operation' in parsed_data and 'content' in parsed_data['operation']:
                            content = parsed_data['operation']['content']
                            if 'sessionArouse' in content:
                                # 处理系统引导消息
                                logger.info(f"[{msg_time}] 【{self.cookie_id}】[{msg_id}] 【系统】小闲鱼智能提示:")
                                if 'arouseChatScriptInfo' in content['sessionArouse']:
                                    for qa in content['sessionArouse']['arouseChatScriptInfo']:
                                        logger.info(f"  - {qa['chatScrip']}")
                                logger.info(f"[{msg_time}] 【{self.cookie_id}】[{msg_id}] ⏹️ 系统引导消息处理完成")
                                return
                            elif 'contentType' in content:
                                # 其他类型的未加密消息
                                logger.warning(f"[{msg_time}] 【{self.cookie_id}】[{msg_id}] 【系统】其他类型消息: {content}")
                        # ⚠️ 修复：不能直接return，应该继续处理这条消息
                        # 因为付款消息可能也包含chatType字段
                        logger.warning(f"【{self.cookie_id}】[{msg_id}] ⚠️ chatType消息但不是引导消息，继续处理...")
                        message = parsed_data
                    else:
                        # 如果不是系统消息，将解析的数据作为message
                        logger.debug(f"【{self.cookie_id}】[{msg_id}] 解密成功，正常消息")
                        message = parsed_data
                except Exception as e:
                    # 如果JSON解析失败，尝试解密
                    logger.debug(f"【{self.cookie_id}】[{msg_id}] JSON解析失败，尝试解密...")
                    decrypted_data = decrypt(data)
                    message = json.loads(decrypted_data)
                    logger.debug(f"【{self.cookie_id}】[{msg_id}] 解密成功")
            except Exception as e:
                # ⚠️ 关键：对于解密失败的大消息，记录完整信息
                logger.error(f"【{self.cookie_id}】[{msg_id}] ❌ 消息解密失败: {self._safe_str(e)}")
                if msg_size > 3000:
                    logger.error(f"【{self.cookie_id}】[{msg_id}] ⚠️⚠️⚠️ 大消息({msg_size}字节)解密失败，完整sync_data: {sync_data}")
                    # 尝试记录base64数据的前后部分
                    try:
                        raw_data = sync_data.get("data", "")
                        logger.error(f"【{self.cookie_id}】[{msg_id}] Base64数据长度: {len(raw_data)}")
                        logger.error(f"【{self.cookie_id}】[{msg_id}] Base64前100字符: {raw_data[:100]}")
                        logger.error(f"【{self.cookie_id}】[{msg_id}] Base64后100字符: {raw_data[-100:]}")
                    except Exception:
                        pass
                logger.error(f"【{self.cookie_id}】[{msg_id}] ⏹️ 消息处理结束（解密失败）")
                return

            # 确保message不为空
            if message is None:
                logger.error(f"【{self.cookie_id}】[{msg_id}] ❌ 消息解析后为空")
                if msg_size > 3000:
                    logger.error(f"【{self.cookie_id}】[{msg_id}] ⚠️⚠️⚠️ 大消息({msg_size}字节)解析后为空！")
                logger.error(f"【{self.cookie_id}】[{msg_id}] ⏹️ 消息处理结束（解析后为空）")
                return

            # 确保message是字典类型
            if not isinstance(message, dict):
                logger.error(f"【{self.cookie_id}】[{msg_id}] ❌ 消息格式错误，期望字典但得到: {type(message)}")
                logger.warning(f"【{self.cookie_id}】[{msg_id}] 消息内容: {message}")
                logger.error(f"【{self.cookie_id}】[{msg_id}] ⏹️ 消息处理结束（格式错误）")
                return

            # 【消息接收标识】记录收到消息的时间，用于控制Cookie刷新
            self.last_message_received_time = time.time()
            logger.warning(f"【{self.cookie_id}】[{msg_id}] ✅ 开始处理消息")

            # 【优先处理】尝试获取订单ID并获取订单详情
            order_id = None
            try:
                logger.info(f"【{self.cookie_id}】[{msg_id}] 🔍 开始提取订单ID，消息类型: {type(message)}")
                order_id = self._extract_order_id(message, message_data)
                if order_id:
                    msg_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ✅ 检测到订单ID: {order_id}，开始获取订单详情')

                    order_context = self._extract_order_message_context(message, msg_id=msg_id)
                    temp_user_id = order_context.get('buyer_id')
                    temp_user_id_source = order_context.get('buyer_id_source')
                    temp_item_id = order_context.get('item_id')
                    temp_sid = order_context.get('sid')
                    temp_buyer_nick = order_context.get('buyer_nick')

                    # 通知订单状态处理器订单ID已提取
                    if self.order_status_handler:
                        logger.info(f"【{self.cookie_id}】准备调用订单状态处理器.on_order_id_extracted: {order_id}")
                        try:
                            self.order_status_handler.on_order_id_extracted(
                                order_id,
                                self.cookie_id,
                                message,
                                match_context={
                                    'sid': temp_sid,
                                    'buyer_id': temp_user_id,
                                    'item_id': temp_item_id,
                                }
                            )
                            logger.info(f"【{self.cookie_id}】订单状态处理器.on_order_id_extracted调用成功: {order_id}")
                        except Exception as e:
                            logger.error(f"【{self.cookie_id}】通知订单状态处理器订单ID提取失败: {self._safe_str(e)}")
                            import traceback
                            logger.error(f"【{self.cookie_id}】详细错误信息: {traceback.format_exc()}")
                    else:
                        logger.warning(f"【{self.cookie_id}】订单状态处理器为None，跳过订单ID提取通知: {order_id}")

                    basic_order_saved = self._preload_basic_order_info(
                        order_id,
                        item_id=temp_item_id,
                        buyer_id=temp_user_id,
                        sid=temp_sid,
                        buyer_nick=temp_buyer_nick,
                        buyer_id_source=temp_user_id_source,
                    )

                    # 立即获取订单详情信息
                    try:
                        # 调用订单详情获取方法（传入sid和buyer_nick用于保存到数据库）
                        order_detail = await self.fetch_order_detail_info(
                            order_id,
                            temp_item_id,
                            temp_user_id,
                            sid=temp_sid,
                            buyer_nick=temp_buyer_nick,
                            buyer_id_source=temp_user_id_source,
                        )
                        if order_detail:
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 订单详情获取成功: {order_id}')
                        else:
                            logger.warning(f'[{msg_time}] 【{self.cookie_id}】⚠️ 订单详情获取失败: {order_id}')
                            if basic_order_saved:
                                self._schedule_order_detail_retry(
                                    order_id,
                                    item_id=temp_item_id,
                                    buyer_id=temp_user_id,
                                    sid=temp_sid,
                                    buyer_nick=temp_buyer_nick,
                                    delay_seconds=30,
                                    buyer_id_source=temp_user_id_source,
                                )

                    except Exception as detail_e:
                        logger.error(f'[{msg_time}] 【{self.cookie_id}】❌ 获取订单详情异常: {self._safe_str(detail_e)}')
                        if basic_order_saved:
                            self._schedule_order_detail_retry(
                                order_id,
                                item_id=temp_item_id,
                                buyer_id=temp_user_id,
                                sid=temp_sid,
                                buyer_nick=temp_buyer_nick,
                                delay_seconds=30,
                                buyer_id_source=temp_user_id_source,
                            )
                else:
                    logger.warning(f"【{self.cookie_id}】[{msg_id}] 未检测到订单ID")
            except Exception as e:
                logger.error(f"【{self.cookie_id}】[{msg_id}] 提取订单ID失败: {self._safe_str(e)}")

            # 安全地获取用户ID
            user_id = None
            try:
                message_1 = message.get("1")
                if isinstance(message_1, str):
                    # message['1'] 是字符串（sid 或 PNM 等），尝试从 message['4'] 提取 buyer_id
                    message_4 = message.get("4")
                    if isinstance(message_4, dict):
                        user_id = message_4.get("senderUserId") or None
                elif isinstance(message_1, dict):
                    # 如果message['1']是字典，从message["1"]["10"]["senderUserId"]中提取user_id
                    if "10" in message_1 and isinstance(message_1["10"], dict):
                        user_id = message_1["10"].get("senderUserId") or None
                    else:
                        user_id = None
                else:
                    user_id = None
            except Exception as e:
                logger.warning(f"提取用户ID失败: {self._safe_str(e)}")
                user_id = None



            # 安全地提取商品ID
            item_id = None
            try:
                if "1" in message and isinstance(message["1"], dict) and "10" in message["1"] and isinstance(message["1"]["10"], dict):
                    url_info = message["1"]["10"].get("reminderUrl", "")
                    if isinstance(url_info, str) and "itemId=" in url_info:
                        item_id = url_info.split("itemId=")[1].split("&")[0]

                # 如果没有提取到，使用辅助方法
                if not item_id:
                    item_id = self.extract_item_id_from_message(message)

                if not item_id:
                    item_id = f"auto_{user_id}_{int(time.time())}"
                    logger.warning(f"无法提取商品ID，使用默认值: {item_id}")

            except Exception as e:
                logger.error(f"提取商品ID时发生错误: {self._safe_str(e)}")
                item_id = f"auto_{user_id}_{int(time.time())}"
            # 处理订单状态消息
            try:
                logger.info(f"【{self.cookie_id}】[{msg_id}] 消息内容: {message}")
                msg_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())

                # 安全地检查订单状态
                red_reminder = None
                if isinstance(message, dict) and "3" in message and isinstance(message["3"], dict):
                    red_reminder = message["3"].get("redReminder")

                if red_reminder == '等待买家付款':
                    user_url = f'https://www.goofish.com/personal?userId={user_id}'
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 【系统】等待买家 {user_url} 付款')
                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（等待买家付款）")
                    return
                elif red_reminder == '交易关闭':
                    user_url = f'https://www.goofish.com/personal?userId={user_id}'
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 【系统】买家 {user_url} 交易关闭')

                    # 【修复】更新订单状态到数据库
                    if self.order_status_handler:
                        try:
                            self.order_status_handler.handle_red_reminder_order_status(
                                red_reminder=red_reminder,
                                message=message,
                                user_id=user_id,
                                cookie_id=self.cookie_id,
                                msg_time=msg_time,
                                match_context={
                                    'sid': None,
                                    'buyer_id': user_id,
                                    'item_id': item_id,
                                }
                            )
                        except Exception as e:
                            logger.error(f"【{self.cookie_id}】更新交易关闭订单状态失败: {self._safe_str(e)}")

                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（交易关闭）")
                    return
                elif red_reminder == '等待卖家发货':
                    user_url = f'https://www.goofish.com/personal?userId={user_id}'
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 【系统】交易成功 {user_url} 等待卖家发货')
                    
                    # 【关键修复】对于简化结构的消息（message['1']是字符串），根据sid查找订单信息后触发自动发货
                    # 简化消息结构: {'1': '56226853668@goofish', '2': 1, '3': {'redReminder': '等待卖家发货', ...}}
                    # message['1'] 就是 sid（会话ID）
                    # 【优化】只使用简化消息触发自动发货，完整付款消息已注释
                    if isinstance(message.get('1'), str):
                        logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 🔔 检测到简化结构的发货通知消息，延迟处理')
                        await asyncio.sleep(30)
                        logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 🔔 延迟30秒后处理简化发货')
                        # 检查是否启用自动确认发货
                        if self.is_auto_confirm_enabled():
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ✅ 自动确认发货已启用，开始处理')
                            
                            # 从简化消息中提取sid（会话ID），如 "56226853668@goofish"
                            simple_sid = message.get('1', '')
                            # 提取纯数字部分作为session_id_str
                            session_id_str = simple_sid.split('@')[0] if '@' in str(simple_sid) else simple_sid
                            
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 🔍 简化消息解析: sid={simple_sid}, session_id={session_id_str}')
                            
                            log_prefix = f'[{msg_time}] 【{self.cookie_id}】[{msg_id}]'
                            sid_lookup_minutes = 5
                            sid_lookup = self._lookup_delivery_order_by_sid(
                                simple_sid,
                                minutes=sid_lookup_minutes,
                                log_prefix=log_prefix
                            )
                            sid_lookup = await self._refresh_sid_lookup_if_needed(
                                simple_sid,
                                sid_lookup,
                                item_id=item_id,
                                buyer_id=user_id,
                                minutes=sid_lookup_minutes,
                                allow_bargain_ready=True,
                                log_prefix=log_prefix
                            )
                            recent_order = sid_lookup.get('order')
                            sid_match_type = sid_lookup.get('match_type', 'missing')
                            
                            if recent_order and sid_match_type in {'pending_ship', 'bargain_ready'}:
                                order_id = recent_order.get('order_id')
                                real_item_id = recent_order.get('item_id')
                                simple_user_id = recent_order.get('buyer_id', user_id)  # 从订单中获取buyer_id
                                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ✅ 通过sid从数据库找到订单: order_id={order_id}, item_id={real_item_id}, buyer_id={simple_user_id}')

                                if sid_match_type == 'bargain_ready':
                                    logger.info(
                                        f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ✅ 小刀订单缺少完整待发货卡片，'
                                        f'使用sid+小刀成功证据兜底进入自动发货: order_id={order_id}'
                                    )
                                
                                # 【防重复检查】先检查该订单是否已经在冷却期内（说明完整消息已经处理过）
                                if not self.can_auto_delivery(order_id):
                                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 🔒 订单 {order_id} 已在冷却期内（可能完整消息已处理），跳过简化消息发货')
                                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（订单已处理）")
                                    return
                                
                                # 【防重复检查】检查延迟锁状态
                                if self.is_lock_held(order_id):
                                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 🔒 订单 {order_id} 延迟锁已被持有（可能完整消息正在处理），跳过简化消息发货')
                                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（订单正在处理）")
                                    return
                                
                                # 使用正确的商品ID和订单ID调用自动发货
                                simple_chat_id = session_id_str  # 使用会话ID作为chat_id
                                
                                # 调用自动发货处理（使用简化消息专用方法）
                                await self._handle_simple_message_auto_delivery(
                                    websocket=websocket,
                                    order_id=order_id,
                                    item_id=real_item_id,
                                    user_id=simple_user_id,
                                    chat_id=simple_chat_id,
                                    msg_time=msg_time,
                                    msg_id=msg_id
                                )
                                logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（简化消息自动发货）")
                                return
                            elif recent_order:
                                order_id = recent_order.get('order_id')
                                order_status = recent_order.get('order_status') or 'unknown'
                                if sid_match_type == 'already_processed':
                                    logger.info(
                                        f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ℹ️ sid命中的订单已处理完成，跳过重复发货: '
                                        f'order_id={order_id}, status={order_status}'
                                    )
                                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（订单已处理）")
                                elif sid_match_type == 'cancelled':
                                    logger.info(
                                        f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ℹ️ sid命中的订单已关闭，跳过自动发货: '
                                        f'order_id={order_id}'
                                    )
                                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（订单已关闭）")
                                else:
                                    logger.info(
                                        f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ℹ️ sid命中的订单当前状态不适合简化消息兜底发货，等待后续完整消息: '
                                        f'order_id={order_id}, status={order_status}'
                                    )
                                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（订单状态未就绪）")
                                return
                            else:
                                logger.warning(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ❌ 未找到sid {simple_sid} 的最近订单，跳过自动发货')
                                logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（未找到订单）")
                                return
                        else:
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ⚠️ 未启用自动确认发货，跳过')
                            logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（未启用自动发货）")
                            return
                    # 如果不是简化结构，继续走正常流程
            except Exception:
                pass

            # 判断是否为聊天消息
            if not self.is_chat_message(message):
                logger.warning(f"【{self.cookie_id}】[{msg_id}] ⏹️ 非聊天消息，处理结束")
                return

            # 处理聊天消息
            try:
                # 安全地提取聊天消息信息
                if not (isinstance(message, dict) and "1" in message and isinstance(message["1"], dict)):
                    logger.error(f"【{self.cookie_id}】[{msg_id}] ❌ 消息格式错误：缺少必要的字段结构")
                    logger.error(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（格式错误）")
                    return

                message_1 = message["1"]
                if not isinstance(message_1.get("10"), dict):
                    logger.error(f"【{self.cookie_id}】[{msg_id}] ❌ 消息格式错误：缺少消息详情字段")
                    logger.error(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（缺少详情字段）")
                    return

                create_time = int(message_1.get("5", 0))
                message_10 = message_1["10"]
                send_user_name = message_10.get("senderNick", message_10.get("reminderTitle", "未知用户"))
                send_user_id = message_10.get("senderUserId", "unknown")
                send_message = message_10.get("reminderContent", "")

                chat_id_raw = message_1.get("2", "")
                chat_id = chat_id_raw.split('@')[0] if '@' in str(chat_id_raw) else str(chat_id_raw)

            except Exception as e:
                logger.error(f"【{self.cookie_id}】[{msg_id}] ❌ 提取聊天消息信息失败: {self._safe_str(e)}")
                logger.error(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（提取信息失败）")
                return

            # 格式化消息时间
            msg_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(create_time/1000))



            message_route_info = self._classify_message_route(
                message=message,
                message_1=message_1,
                message_10=message_10,
                send_message=send_message,
            )
            message_route = message_route_info.get('route', 'user_chat')
            order_status_signal = message_route_info.get('order_status_signal')
            should_notify_message = bool(message_route_info.get('should_notify'))
            allow_auto_reply = bool(message_route_info.get('allow_auto_reply'))
            is_system_message = bool(message_route_info.get('is_system_message'))
            is_group_message = bool(message_route_info.get('is_group_message'))
            message_direction = message_route_info.get('message_direction', 0)
            content_type = message_route_info.get('content_type', 0)
            card_title = str(message_route_info.get('card_title') or '').strip()
            special_flow_card_titles = {
                '我已小刀，待刀成',
                '我已小刀,待刀成',
                '我已成功小刀，待发货',
                '我已成功小刀,待发货',
            }

            logger.info(
                f"【{self.cookie_id}】[{msg_id}] 消息分类: route={message_route}, "
                f"status_signal={order_status_signal or 'none'}, notify={should_notify_message}, "
                f"auto_reply={allow_auto_reply}, system={is_system_message}, "
                f"direction={message_direction}, contentType={content_type}"
            )

            if send_user_id == self.myid and not is_system_message:
                logger.info(f"[{msg_time}] 【{self.cookie_id}】[{msg_id}] 【手动发出】 商品({item_id}): {send_message}")

                # 暂停该chat_id的自动回复10分钟
                pause_manager.pause_chat(chat_id, self.cookie_id)

                logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（手动发出消息）")
                return
            elif send_user_id == self.myid and is_system_message:
                logger.info(
                    f"[{msg_time}] 【{self.cookie_id}】[{msg_id}] 检测到系统消息(sender=自己ID)，继续执行状态处理 "
                    f"(direction={message_direction}, contentType={content_type})"
                )
            else:
                logger.info(f"[{msg_time}] 【收到】用户: {send_user_name} (ID: {send_user_id}), 商品({item_id}): {send_message}")
                if message_route == 'user_chat':
                    self.last_user_chat_time = time.time()

                # 【优先处理】检查是否正在等待亦凡卡劵账号输入
                async with self.yifan_account_lock:
                    if chat_id in self.yifan_account_waiting:
                        waiting_info = self.yifan_account_waiting[chat_id]
                        
                        # 检查超时（30分钟）
                        if time.time() - waiting_info['create_time'] > 1800:
                            logger.warning(f"账号输入等待超时，清除等待状态")
                            del self.yifan_account_waiting[chat_id]
                        elif waiting_info['buyer_id'] == send_user_id:
                            # 检查是否为客户真实消息（过滤系统消息）
                            # 真实客户消息: message['1']['7'] = 2, contentType = 1
                            # 系统消息: message['1']['7'] = 1, contentType = 6 (textCard)
                            message_1 = message.get('1', {})
                            message_direction = message_1.get('7', 0) if isinstance(message_1, dict) else 0
                            
                            # 获取contentType
                            content_type = 0
                            try:
                                message_6 = message_1.get('6', {})
                                if isinstance(message_6, dict):
                                    message_6_3 = message_6.get('3', {})
                                    if isinstance(message_6_3, dict):
                                        content_type = message_6_3.get('4', 0)
                            except Exception:
                                pass
                            
                            # 检查bizTag是否包含系统消息标识
                            is_system_msg = False
                            try:
                                message_10 = message_1.get('10', {})
                                if isinstance(message_10, dict):
                                    biz_tag = message_10.get('bizTag', '')
                                    if biz_tag and ('SECURITY' in biz_tag or 'taskName' in biz_tag or 'taskId' in biz_tag):
                                        is_system_msg = True
                            except Exception:
                                pass
                            
                            # 过滤非真实客户消息：
                            # 1. message['1']['7'] != 2 表示不是接收的消息
                            # 2. contentType = 6 表示系统卡片消息
                            # 3. bizTag包含系统标识
                            if message_direction != 2 or content_type == 6 or is_system_msg:
                                logger.info(f"【{self.cookie_id}】[{msg_id}] 收到系统消息，跳过账号确认处理（direction={message_direction}, contentType={content_type}, isSystem={is_system_msg}）")
                                logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（系统消息）")
                                return
                            
                            # 是同一个用户的真实回复
                            if waiting_info['state'] == 'waiting_account':
                                # 等待账号输入阶段
                                account = send_message.strip()
                                if account:
                                    # 保存账号并发送确认消息
                                    waiting_info['account'] = account
                                    waiting_info['state'] = 'waiting_confirm'
                                    
                                    confirm_msg = f"{account}\n这是您要充值的账号，请回答\"是\"，进行确认下单，如果账号不对，请重新输入正确的账号，如果因为您账号输错，导致错误下单，概不退款。"
                                    await self.send_msg(self.ws, chat_id, send_user_id, confirm_msg)
                                    logger.info(f"【{self.cookie_id}】[{msg_id}] 已保存充值账号: {account}，等待用户确认")
                                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（等待账号确认）")
                                    return  # 处理完毕，不再继续其他流程
                                    
                            elif waiting_info['state'] == 'waiting_confirm':
                                # 等待确认阶段
                                user_reply = send_message.strip()
                                
                                if user_reply == '是':
                                    # 用户确认，继续发货流程
                                    logger.info(f"用户确认账号，继续亦凡API发货流程")
                                    account = waiting_info['account']
                                    rule = waiting_info['rule']
                                    order_id_saved = waiting_info.get('order_id')
                                    item_id_saved = waiting_info.get('item_id')
                                    
                                    # 清除等待状态
                                    del self.yifan_account_waiting[chat_id]
                                    
                                    # 继续执行亦凡API调用（带账号）
                                    try:
                                        # 直接调用亦凡API下单
                                        delivery_content = await self._call_yifan_api_with_account(
                                            rule, account, order_id_saved, item_id_saved, send_user_id, chat_id
                                        )
                                        
                                        if delivery_content:
                                            delivery_steps = self._build_delivery_steps(
                                                delivery_content,
                                                rule.get('card_description', '')
                                            )
                                            await self._send_delivery_steps(
                                                self.ws,
                                                chat_id,
                                                send_user_id,
                                                delivery_steps,
                                                log_prefix=f"亦凡账号确认发货 order_id={order_id_saved or 'unknown'}"
                                            )

                                            finalize_result = await self._finalize_delivery_after_send(
                                                delivery_meta={
                                                    'success': True,
                                                    'rule_id': rule.get('id'),
                                                    'card_id': rule.get('card_id'),
                                                    'card_type': rule.get('card_type'),
                                                    'order_spec_mode': None,
                                                    'rule_spec_mode': None,
                                                    'item_config_mode': None,
                                                    'data_card_pending_consume': False,
                                                    'data_line': None
                                                },
                                                order_id=order_id_saved,
                                                item_id=item_id_saved
                                            )
                                            if not finalize_result.get('success'):
                                                self._record_delivery_log(
                                                    order_id=order_id_saved,
                                                    item_id=item_id_saved,
                                                    buyer_id=send_user_id,
                                                    status='failed',
                                                    reason=finalize_result.get('error') or '亦凡账号确认发货发送成功但提交副作用失败',
                                                    channel='auto',
                                                    rule_meta={
                                                        'rule_id': rule.get('id'),
                                                        'rule_keyword': rule.get('keyword'),
                                                        'card_type': rule.get('card_type')
                                                    }
                                                )
                                                await self.send_msg(self.ws, chat_id, send_user_id, "发货消息已发送，但确认发货失败，请稍后刷新订单状态。")
                                                logger.error(f"亦凡API自动发货副作用提交失败: {finalize_result.get('error')}")
                                                return

                                            if order_id_saved:
                                                self.mark_delivery_sent(order_id_saved, context="亦凡账号确认发货发送成功")
                                                self._activate_delivery_lock(order_id_saved, delay_minutes=10)

                                            self._record_delivery_log(
                                                order_id=order_id_saved,
                                                item_id=item_id_saved,
                                                buyer_id=send_user_id,
                                                status='success',
                                                reason='亦凡账号确认发货发送成功',
                                                channel='auto',
                                                rule_meta={
                                                    'rule_id': rule.get('id'),
                                                    'rule_keyword': rule.get('keyword'),
                                                    'card_type': rule.get('card_type')
                                                }
                                            )
                                            logger.info(f"亦凡API自动发货成功")
                                        else:
                                            # 发货失败通知
                                            await self.send_msg(self.ws, chat_id, send_user_id, "抱歉，自动发货失败，请联系客服处理。")
                                    except Exception as e:
                                        logger.error(f"亦凡API发货异常: {self._safe_str(e)}")
                                        await self.send_msg(self.ws, chat_id, send_user_id, "系统异常，请联系客服处理。")
                                    
                                    return  # 处理完毕
                                    
                                else:
                                    # 用户输入的不是"是"，认为是重新输入账号
                                    new_account = user_reply
                                    if new_account:
                                        waiting_info['account'] = new_account
                                        waiting_info['retry_count'] += 1
                                        
                                        # 检查重试次数
                                        if waiting_info['retry_count'] >= 5:
                                            logger.warning(f"【{self.cookie_id}】[{msg_id}] 账号确认重试次数过多，取消发货")
                                            del self.yifan_account_waiting[chat_id]
                                            await self.send_msg(self.ws, chat_id, send_user_id, "账号确认失败次数过多，已取消发货，请重新下单。")
                                            logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（重试次数过多）")
                                            return
                                        
                                        confirm_msg = f"{new_account}\n这是您要充值的账号，请回答\"是\"，进行确认下单，如果账号不对，请重新输入正确的账号，如果因为您账号输错，导致错误下单，概不退款。"
                                        await self.send_msg(self.ws, chat_id, send_user_id, confirm_msg)
                                        logger.info(f"【{self.cookie_id}】[{msg_id}] 用户重新输入账号: {new_account}，再次等待确认")
                                        logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（等待账号重新确认）")
                                        return

                try:
                    if is_group_message:
                        logger.info(f"📱 检测到群组消息（sessionType=30），跳过消息通知")
                    elif should_notify_message:
                        await self.send_notification(send_user_name, send_user_id, send_message, item_id, chat_id)
                    else:
                        logger.info(
                            f"📱 当前消息不发送通知: route={message_route}, "
                            f"status_signal={order_status_signal or 'none'}, message={send_message}"
                        )
                except Exception as notify_error:
                    logger.error(f"📱 发送消息通知失败: {self._safe_str(notify_error)}")




            # 【优先处理】使用订单状态处理器处理系统消息
            if self.order_status_handler:
                try:
                    # 处理系统消息的订单状态更新
                    try:
                        handled = self.order_status_handler.handle_system_message(
                            message=message,
                            send_message=send_message,
                            cookie_id=self.cookie_id,
                            msg_time=msg_time,
                            match_context={
                                'sid': message_1.get('2', '') if isinstance(message_1, dict) else None,
                                'buyer_id': send_user_id,
                                'item_id': item_id,
                            }
                        )
                    except Exception as e:
                        logger.error(f"【{self.cookie_id}】处理系统消息失败: {self._safe_str(e)}")
                        handled = False
                    
                    # 处理红色提醒消息
                    if not handled:
                        try:
                            if isinstance(message, dict) and "3" in message and isinstance(message["3"], dict):
                                red_reminder = message["3"].get("redReminder")
                                user_id = message["3"].get("userId", "unknown")
                                
                                if red_reminder:
                                    try:
                                        self.order_status_handler.handle_red_reminder_message(
                                            message=message,
                                            red_reminder=red_reminder,
                                            user_id=user_id,
                                            cookie_id=self.cookie_id,
                                            msg_time=msg_time,
                                            match_context={
                                                'sid': message_1.get('2', '') if isinstance(message_1, dict) else None,
                                                'buyer_id': send_user_id,
                                                'item_id': item_id,
                                            }
                                        )
                                    except Exception as e:
                                        logger.error(f"【{self.cookie_id}】处理红色提醒消息失败: {self._safe_str(e)}")
                        except Exception as red_e:
                            logger.warning(f"处理红色提醒消息失败: {self._safe_str(red_e)}")
                            
                except Exception as e:
                    logger.error(f"订单状态处理失败: {self._safe_str(e)}")

            # 关键状态消息到达时，按需补刷一次订单详情，避免缓存把状态留在旧值
            if order_id and order_status_signal in {'pending_ship', 'shipped', 'completed', 'cancelled', 'refunding'}:
                try:
                    refresh_sid = ''
                    if isinstance(message_1, dict):
                        refresh_sid = message_1.get("2", "")

                    await self._maybe_force_refresh_order_detail_for_signal(
                        order_id=order_id,
                        item_id=item_id,
                        buyer_id=send_user_id,
                        sid=refresh_sid,
                        buyer_nick=send_user_name,
                        status_signal=order_status_signal,
                        reason=f'message_signal_{order_status_signal}',
                        delay_seconds=1 if order_status_signal == 'pending_ship' else 0,
                        log_prefix=f"【{self.cookie_id}】[{msg_id}]"
                    )
                except Exception as refresh_e:
                    logger.error(
                        f"【{self.cookie_id}】[{msg_id}] 状态消息触发订单详情补刷失败: {self._safe_str(refresh_e)}"
                    )

            # 【优先处理】检查系统消息和自动发货触发消息（不受人工接入暂停影响）
            fallback_ignore_keywords = [
                '不想宝贝被砍价',
                'AI正在帮你回复',
                '发来一条',
                '小心假客服骗钱',
                '蚂蚁森林能量',
                '恭喜你拿到曝光卡',
                '订单即将自动确认收货',
                '温馨提醒：商品信息近期有过变更',
            ]
            if send_message == '[我已拍下，待付款]':
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 系统消息不处理')
                logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（系统消息：待付款）")
                return
            elif send_message == '[你关闭了订单，钱款已原路退返]':
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 系统消息不处理')
                logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（系统消息：订单关闭）")
                return
            elif send_message in [
                '快给ta一个评价吧~',
                '快给ta一个评价吧～',
            ]:
                # 检测到评价提醒消息，尝试自动好评
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 🌟 检测到评价提醒消息: {send_message}')
                await self.handle_auto_comment(message, msg_time, msg_id)
                logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（评价提醒消息）")
                return
            elif message_route == 'system_notice' or any(keyword in send_message for keyword in fallback_ignore_keywords):
                logger.info(
                    f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ⏹️ 系统提示消息不处理: '
                    f'route={message_route}, message={send_message}'
                )
                return
            # 简化消息通过 sid 查找订单，更可靠
            elif message_route == 'order_status' and self._is_auto_delivery_trigger(send_message):
                logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 检测到自动发货触发消息: {send_message}')

                # 只允许系统消息触发自动发货，防止买家手动输入关键字触发
                if not is_system_message:
                    logger.warning(
                        f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ⚠️ 自动发货关键字来自非系统消息，已忽略 '
                        f'(direction={message_direction}, contentType={content_type})'
                    )
                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（非系统触发）")
                    return

                # 检查是否启用自动确认发货
                if not self.is_auto_confirm_enabled():
                    logger.info(f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] 未启用自动确认发货，跳过自动发货')
                    logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（未启用自动发货）")
                    return
                # 使用统一的自动发货处理方法（传递message_data以便提取订单ID）
                await self._handle_auto_delivery(websocket, message, send_user_name, send_user_id,
                                               item_id, chat_id, msg_time, message_data)
                logger.info(f"【{self.cookie_id}】[{msg_id}] ⏹️ 处理结束（自动发货完成）")
                return
            # 【重要】检查小刀流程卡片消息 - 即使在人工接入暂停期间也要处理
            elif send_message == '[卡片消息]' or card_title in special_flow_card_titles:
                # 检查是否为小刀相关卡片消息
                try:
                    # 从消息中提取卡片内容
                    card_title = card_title or None
                    card_message_1 = message.get("1", {}) if isinstance(message, dict) else {}
                    if not card_title and isinstance(card_message_1, dict):
                        if "6" in card_message_1 and isinstance(card_message_1["6"], dict):
                            message_6 = card_message_1["6"]
                            if "3" in message_6 and isinstance(message_6["3"], dict):
                                message_6_3 = message_6["3"]
                                if "5" in message_6_3:
                                    # 解析JSON内容
                                    try:
                                        card_content = json.loads(message_6_3["5"])
                                        if "dxCard" in card_content and "item" in card_content["dxCard"]:
                                            card_item = card_content["dxCard"]["item"]
                                            if "main" in card_item and "exContent" in card_item["main"]:
                                                ex_content = card_item["main"]["exContent"]
                                                card_title = ex_content.get("title", "")
                                    except (json.JSONDecodeError, KeyError) as e:
                                        logger.warning(f"解析卡片消息失败: {e}")

                    # 卡片流程仅接受系统消息，避免伪造卡片触发
                    card_message_direction = card_message_1.get('7', 0) if isinstance(card_message_1, dict) else 0
                    card_content_type = 0
                    card_is_system_biz = False
                    try:
                        card_message_6 = card_message_1.get('6', {}) if isinstance(card_message_1, dict) else {}
                        if isinstance(card_message_6, dict):
                            card_message_6_3 = card_message_6.get('3', {})
                            if isinstance(card_message_6_3, dict):
                                card_content_type = card_message_6_3.get('4', 0)
                    except Exception:
                        pass

                    try:
                        card_message_10 = card_message_1.get('10', {}) if isinstance(card_message_1, dict) else {}
                        if isinstance(card_message_10, dict):
                            biz_tag = card_message_10.get('bizTag', '')
                            if biz_tag and ('SECURITY' in biz_tag or 'taskName' in biz_tag or 'taskId' in biz_tag):
                                card_is_system_biz = True
                    except Exception:
                        pass

                    is_system_card_message = card_message_direction == 1 or card_content_type == 6 or card_is_system_biz
                    if not is_system_card_message:
                        logger.warning(
                            f'[{msg_time}] 【{self.cookie_id}】[{msg_id}] ⚠️ 非系统卡片消息，忽略小刀流程 '
                            f'(direction={card_message_direction}, contentType={card_content_type}, isSystemBiz={card_is_system_biz})'
                        )
                        return

                    waiting_bargain_titles = {"我已小刀，待刀成", "我已小刀,待刀成"}
                    ready_to_ship_titles = {"我已成功小刀，待发货", "我已成功小刀,待发货"}

                    # 第一阶段：待刀成，仅执行免拼，不直接发货
                    if card_title in waiting_bargain_titles:
                        logger.info(f'[{msg_time}] 【{self.cookie_id}】【系统】检测到"{card_title}"，执行免拼流程')
                        
                        # 检查是否启用自动确认发货
                        if not self.is_auto_confirm_enabled():
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】未启用自动确认发货，跳过自动小刀和自动发货')
                            return

                        # 检查商品是否属于当前cookies
                        if item_id and item_id != "未知商品":
                            try:
                                if not await self._ensure_item_owned_by_current_account(
                                    item_id,
                                    log_prefix=f'[{msg_time}] 【{self.cookie_id}】'
                                ):
                                    logger.warning(f'[{msg_time}] 【{self.cookie_id}】❌ 商品 {item_id} 不属于当前账号，跳过免拼发货')
                                    return
                                logger.warning(f'[{msg_time}] 【{self.cookie_id}】✅ 商品 {item_id} 归属验证通过')
                            except Exception as e:
                                logger.error(f'[{msg_time}] 【{self.cookie_id}】检查商品归属失败: {self._safe_str(e)}，跳过免拼发货')
                                return

                        # 提取订单ID（传递原始消息数据以便在解密消息中找不到时进行备用搜索）
                        order_id = self._extract_order_id(message, message_data)
                        if not order_id:
                            logger.warning(f'[{msg_time}] 【{self.cookie_id}】❌ 未能提取到订单ID，无法执行免拼发货')
                            return

                        self._mark_order_bargain_flow(
                            order_id,
                            item_id=item_id,
                            buyer_id=send_user_id,
                            context=card_title or 'waiting_bargain',
                        )

                        # 延迟2秒后执行免拼发货
                        logger.info(f'[{msg_time}] 【{self.cookie_id}】延迟2秒后执行免拼发货...')
                        await asyncio.sleep(2)
                        # 调用自动免拼发货方法
                        result = await self.auto_freeshipping(order_id, item_id, send_user_id)
                        if result.get('success'):
                            self._mark_order_bargain_flow(
                                order_id,
                                item_id=item_id,
                                buyer_id=send_user_id,
                                apply_configured_price=True,
                                success_detected=True,
                                context=f'{card_title or "waiting_bargain"}_success',
                            )
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】✅ 自动免拼发货成功')
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】⏳ 已完成免拼，等待"我已成功小刀，待发货"卡片后再自动发货')
                            return
                        else:
                            logger.warning(f'[{msg_time}] 【{self.cookie_id}】❌ 自动免拼发货失败: {result.get("error", "未知错误")}')
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】⏹️ 免拼失败，不执行自动发货')
                            return

                    # 第二阶段：成功小刀待发货，触发自动发货
                    elif card_title in ready_to_ship_titles:
                        logger.info(f'[{msg_time}] 【{self.cookie_id}】【系统】检测到"{card_title}"，开始自动发货')

                        order_id = self._extract_order_id(message, message_data)
                        if order_id:
                            self._mark_order_bargain_flow(
                                order_id,
                                item_id=item_id,
                                buyer_id=send_user_id,
                                apply_configured_price=True,
                                success_detected=True,
                                context=card_title,
                            )

                        # 检查是否启用自动确认发货
                        if not self.is_auto_confirm_enabled():
                            logger.info(f'[{msg_time}] 【{self.cookie_id}】未启用自动确认发货，跳过自动发货')
                            return

                        await self._handle_auto_delivery(
                            websocket, message, send_user_name, send_user_id,
                            item_id, chat_id, msg_time, message_data
                        )
                        logger.info(f'[{msg_time}] 【{self.cookie_id}】⏹️ 小刀成功待发货卡片处理完成')
                        return
                    else:
                        logger.info(f'[{msg_time}] 【{self.cookie_id}】收到卡片消息，标题: {card_title or "未知"}')
                        # 如果不是目标卡片消息，继续正常处理流程（会受到暂停影响）

                except Exception as e:
                    logger.error(f"处理卡片消息异常: {self._safe_str(e)}")
                    # 如果处理异常，继续正常处理流程（会受到暂停影响）

            # 自动更新买家昵称（补全历史订单的昵称信息）
            # 需要过滤掉系统提示文本，避免将"买家已拍下，待付款"等写入昵称
            if send_user_id and send_user_name:
                valid_buyer_nick = self._sanitize_buyer_nick(
                    send_user_name,
                    source="message_sender",
                    message_meta=message_10 if isinstance(message_10, dict) else None,
                    log_prefix=f"【{self.cookie_id}】[{msg_id}]"
                )
                if valid_buyer_nick:
                    try:
                        from db_manager import db_manager
                        db_manager.update_buyer_nick_by_buyer_id(send_user_id, valid_buyer_nick, self.cookie_id)
                    except Exception as e:
                        logger.debug(f"更新买家昵称失败: {self._safe_str(e)}")

            if not allow_auto_reply:
                logger.info(
                    f"【{self.cookie_id}】[{msg_id}] ⏹️ 当前消息不进入自动回复链: "
                    f"route={message_route}, status_signal={order_status_signal or 'none'}"
                )
                return

            # 使用防抖机制处理聊天消息回复
            # 如果用户连续发送消息，等待用户停止发送后再回复最后一条消息
            await self._schedule_debounced_reply(
                chat_id=chat_id,
                message_data=message_data,
                websocket=websocket,
                send_user_name=send_user_name,
                send_user_id=send_user_id,
                send_message=send_message,
                item_id=item_id,
                msg_time=msg_time
            )

        except Exception as e:
            logger.error(f"【{self.cookie_id}】[{msg_id}] ❌ 处理消息时发生异常: {self._safe_str(e)}")
            if msg_size > 3000:
                logger.error(f"【{self.cookie_id}】[{msg_id}] ⚠️⚠️⚠️ 大消息({msg_size}字节)处理异常！")
            logger.warning(f"【{self.cookie_id}】[{msg_id}] 原始消息: {message_data}")
            import traceback
            logger.error(f"【{self.cookie_id}】[{msg_id}] 异常堆栈: {traceback.format_exc()}")
        finally:
            # 确保每条消息都有明确的处理结束标记
            logger.info(f"【{self.cookie_id}】[{msg_id}] 🏁 消息处理完成 ({msg_size}字节)")

    async def main(self):
        """主程序入口"""
        try:
            logger.info(f"【{self.cookie_id}】开始启动XianyuLive主程序...")
            await self.create_session()  # 创建session
            logger.info(f"【{self.cookie_id}】Session创建完成，开始WebSocket连接循环...")

            while True:
                try:
                    # 检查账号是否启用
                    from cookie_manager import manager as cookie_manager
                    if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
                        logger.info(f"【{self.cookie_id}】账号已禁用，停止主循环")
                        break

                    init_auth_state = self.get_init_auth_failure_state(self.cookie_id) or {}
                    circuit_until = init_auth_state.get('circuit_until', 0)
                    if circuit_until and time.time() < circuit_until:
                        remaining_seconds = max(1, int(circuit_until - time.time()))
                        self._set_connection_state(ConnectionState.RECONNECTING, f"初始化鉴权冷静期剩余{remaining_seconds}秒")
                        logger.warning(
                            f"【{self.cookie_id}】初始化鉴权失败熔断中，暂停发起新的WebSocket连接，剩余 {remaining_seconds} 秒"
                        )
                        await self._interruptible_sleep(remaining_seconds)
                        continue

                    headers = self._build_websocket_headers()

                    # 更新连接状态为连接中
                    self._set_connection_state(ConnectionState.CONNECTING, "准备建立WebSocket连接")
                    logger.info(f"【{self.cookie_id}】WebSocket目标地址: {self.base_url}")

                    # 兼容不同版本的websockets库
                    async with await self._create_websocket_connection(headers) as websocket:
                        self.ws = websocket
                        logger.info(f"【{self.cookie_id}】WebSocket连接建立成功，开始初始化...")

                        try:
                            # 开始初始化
                            await self.init(websocket)
                            logger.info(f"【{self.cookie_id}】WebSocket初始化完成！")

                            # 初始化完成后才设置为已连接状态
                            self._set_connection_state(ConnectionState.CONNECTED, "初始化完成，连接就绪")
                            self.connection_failures = 0
                            self.last_successful_connection = time.time()
                            self._reset_stream_activity_state(self.last_successful_connection)

                            # 记录后台任务启动前的状态
                            logger.warning(f"【{self.cookie_id}】准备启动后台任务 - 当前状态: heartbeat={self.heartbeat_task}, token_refresh={self.token_refresh_task}, cleanup={self.cleanup_task}, cookie_refresh={self.cookie_refresh_task}, stream_watchdog={self.stream_watchdog_task}")
                            
                            # 如果存在心跳任务引用，先清理（心跳任务依赖WebSocket，必须重启）
                            if self.heartbeat_task:
                                logger.warning(f"【{self.cookie_id}】检测到旧心跳任务引用，先清理...")
                                self._reset_background_tasks()

                            # 启动心跳任务（依赖WebSocket，每次重连都需要重启）
                            logger.info(f"【{self.cookie_id}】启动心跳任务...")
                            self.heartbeat_task = asyncio.create_task(self.heartbeat_loop(websocket))

                            # 启动其他后台任务（不依赖WebSocket，只在首次连接时启动）
                            tasks_started = []
                            
                            if not self.token_refresh_task or self.token_refresh_task.done():
                                logger.info(f"【{self.cookie_id}】启动会话保活任务...")
                                self.token_refresh_task = asyncio.create_task(self.token_refresh_loop())
                                tasks_started.append("会话保活")
                            else:
                                logger.info(f"【{self.cookie_id}】Token刷新任务已在运行，跳过启动")

                            if not self.cleanup_task or self.cleanup_task.done():
                                logger.info(f"【{self.cookie_id}】启动暂停记录清理任务...")
                                self.cleanup_task = asyncio.create_task(self.pause_cleanup_loop())
                                tasks_started.append("暂停清理")
                            else:
                                logger.info(f"【{self.cookie_id}】暂停记录清理任务已在运行，跳过启动")

                            if not self.cookie_refresh_task or self.cookie_refresh_task.done():
                                logger.info(f"【{self.cookie_id}】启动Cookie刷新任务...")
                                self.cookie_refresh_task = asyncio.create_task(self.cookie_refresh_loop())
                                tasks_started.append("Cookie刷新")
                            else:
                                logger.info(f"【{self.cookie_id}】Cookie刷新任务已在运行，跳过启动")

                            if not self.stream_watchdog_task or self.stream_watchdog_task.done():
                                logger.info(f"【{self.cookie_id}】启动业务流看门狗任务...")
                                self.stream_watchdog_task = asyncio.create_task(self.message_stream_watchdog_loop())
                                tasks_started.append("业务流看门狗")
                            else:
                                logger.info(f"【{self.cookie_id}】业务流看门狗任务已在运行，跳过启动")

                            # 启动消息队列工作协程（高性能消息处理）
                            if self.message_queue_enabled:
                                await self._start_message_queue_workers()
                                tasks_started.append("消息队列")

                            # 记录所有后台任务状态
                            if tasks_started:
                                logger.info(f"【{self.cookie_id}】✅ 新启动的任务: {', '.join(tasks_started)}")
                            logger.info(f"【{self.cookie_id}】✅ 所有后台任务状态: 心跳(已启动), 会话保活({'运行中' if self.token_refresh_task and not self.token_refresh_task.done() else '已启动'}), 暂停清理({'运行中' if self.cleanup_task and not self.cleanup_task.done() else '已启动'}), Cookie刷新({'运行中' if self.cookie_refresh_task and not self.cookie_refresh_task.done() else '已启动'}), 业务流看门狗({'运行中' if self.stream_watchdog_task and not self.stream_watchdog_task.done() else '已启动'})")
                            
                            logger.info(f"【{self.cookie_id}】开始监听WebSocket消息...")
                            logger.info(f"【{self.cookie_id}】WebSocket连接状态正常，等待服务器消息...")
                            logger.info(f"【{self.cookie_id}】准备进入消息循环...")

                            async for message in websocket:
                                try:
                                    message_data = json.loads(message)
                                    
                                    # 提取消息标识用于日志追踪（防止异步处理导致日志混乱）
                                    msg_id = "unknown"
                                    msg_preview = ""
                                    try:
                                        # 尝试从headers中提取mid
                                        if isinstance(message_data, dict) and "headers" in message_data:
                                            msg_id = message_data["headers"].get("mid", "unknown")
                                        # 尝试提取消息预览（用于区分不同类型的消息）
                                        if isinstance(message_data, dict) and "body" in message_data:
                                            if "syncPushPackage" in message_data["body"]:
                                                msg_preview = "[同步包]"
                                            elif "ack" in str(message_data["body"]).lower():
                                                msg_preview = "[确认]"
                                    except Exception:
                                        pass
                                    
                                    logger.info(f"【{self.cookie_id}】📨 收到消息 [ID:{msg_id}] {msg_preview} {len(message) if message else 0}字节")

                                    # 处理心跳响应（高优先级，直接处理）
                                    if await self.handle_heartbeat_response(message_data):
                                        continue

                                    is_sync_package = self.is_sync_package(message_data)
                                    self._mark_non_heartbeat_message(time.time(), is_sync_package=is_sync_package)

                                    # 处理其他消息
                                    # 使用高性能消息队列系统处理消息，解决消息阻塞问题
                                    if self.message_queue_enabled and self.message_queue_running:
                                        # 消息队列模式：快速入队，由工作协程异步处理
                                        await self._enqueue_message(message_data, websocket, msg_id)
                                    else:
                                        # 传统模式：使用追踪的异步任务处理消息
                                        self._create_tracked_task(self._handle_message_with_semaphore(message_data, websocket, msg_id))

                                except Exception as e:
                                    logger.error(f"处理消息出错: {self._safe_str(e)}")
                                    continue
                        finally:
                            # 停止消息队列工作协程
                            if self.message_queue_enabled and self.message_queue_running:
                                logger.info(f"【{self.cookie_id}】正在停止消息队列工作协程...")
                                await self._stop_message_queue_workers()
                            
                            # 确保在退出 async with 块时清理 WebSocket 引用
                            # 注意：async with 会自动关闭 WebSocket，但我们需要清理引用
                            if self.ws == websocket:
                                self.ws = None
                                logger.info(f"【{self.cookie_id}】WebSocket连接已退出，引用已清理")

                except InitAuthError as e:
                    error_msg = self._safe_str(e)
                    self.current_token = None
                    self.connection_failures = 0
                    init_auth_state = self.record_init_auth_failure(self.cookie_id, error_msg)
                    self.init_auth_failures = int(init_auth_state.get('count', 0))
                    self._set_connection_state(ConnectionState.RECONNECTING, f"初始化鉴权失败第{self.init_auth_failures}次")
                    logger.error(f"【{self.cookie_id}】初始化鉴权失败 ({self.init_auth_failures}/{self._init_auth_failure_threshold})")
                    logger.error(f"【{self.cookie_id}】初始化失败原因: {error_msg}")

                    retry_delay = self._calculate_retry_delay(error_msg)
                    circuit_until = init_auth_state.get('circuit_until', 0)
                    if circuit_until and time.time() < circuit_until:
                        circuit_wait = max(1, int(circuit_until - time.time()))
                        retry_delay = max(retry_delay, circuit_wait)
                        logger.warning(
                            f"【{self.cookie_id}】初始化鉴权失败已达到阈值，进入冷静期 {circuit_wait} 秒后再重试"
                        )
                    else:
                        logger.warning(f"【{self.cookie_id}】将在 {retry_delay} 秒后重试初始化鉴权...")

                    self._reset_background_tasks()
                    await self._interruptible_sleep(retry_delay)
                    logger.info(f"【{self.cookie_id}】初始化鉴权重试等待完成，准备重新建立连接...")
                    continue

                except Exception as e:
                    error_msg = self._safe_str(e)
                    import traceback
                    error_type = type(e).__name__
                    
                    # 检查是否是 ConnectionClosedError（正常的连接关闭）
                    is_connection_closed = (
                        'ConnectionClosedError' in error_type or 
                        'ConnectionClosed' in error_type or
                        'no close frame received or sent' in error_msg or
                        'IncompleteReadError' in error_type
                    )
                    
                    # 对于连接关闭错误，使用警告级别而不是错误级别
                    if is_connection_closed:
                        logger.warning(f"【{self.cookie_id}】WebSocket连接已关闭 ({self.connection_failures + 1}/{self.max_connection_failures})")
                        logger.warning(f"【{self.cookie_id}】关闭原因: {error_msg}")
                    else:
                        self.connection_failures += 1
                    # 更新连接状态为重连中
                    self._set_connection_state(ConnectionState.RECONNECTING, f"第{self.connection_failures}次失败")
                    logger.error(f"【{self.cookie_id}】WebSocket连接异常 ({self.connection_failures}/{self.max_connection_failures})")
                    logger.error(f"【{self.cookie_id}】异常类型: {error_type}")
                    logger.error(f"【{self.cookie_id}】异常信息: {error_msg}")
                    logger.warning(f"【{self.cookie_id}】异常堆栈:\n{traceback.format_exc()}")
                    
                    # 确保清理 WebSocket 引用
                    if self.ws:
                        try:
                            # 检查 WebSocket 是否仍然打开
                            if hasattr(self.ws, 'close_code') and self.ws.close_code is None:
                                # WebSocket 可能仍然打开，尝试关闭
                                try:
                                    await asyncio.wait_for(self.ws.close(), timeout=2.0)
                                except (asyncio.TimeoutError, Exception):
                                    pass
                        except Exception:
                            pass
                        finally:
                            self.ws = None
                            logger.info(f"【{self.cookie_id}】WebSocket引用已清理")
                    
                    # 对于连接关闭错误，也增加失败计数
                    if is_connection_closed:
                        self.connection_failures += 1
                        # 更新连接状态为重连中
                        self._set_connection_state(ConnectionState.RECONNECTING, f"连接关闭，第{self.connection_failures}次重连")

                    # 检查是否超过最大失败次数
                    if self.connection_failures >= self.max_connection_failures:
                        self._set_connection_state(ConnectionState.FAILED, f"连续失败{self.max_connection_failures}次")
                        logger.warning(f"【{self.cookie_id}】连续失败{self.max_connection_failures}次，尝试通过密码登录刷新Cookie...")
                        
                        try:
                            # 调用统一的密码登录刷新方法
                            refresh_success = await self._try_password_login_refresh(
                                f"连续失败{self.max_connection_failures}次",
                                ignore_slider_failed_backoff=self._has_recent_slider_success(),
                            )
                            
                            if refresh_success:
                                logger.info(f"【{self.cookie_id}】✅ 密码登录刷新成功，将重置失败计数并继续重连")
                                # 重置失败计数，因为已经刷新了Cookie
                                self.connection_failures = 0
                                # 更新连接状态
                                self._set_connection_state(ConnectionState.RECONNECTING, "Cookie已刷新，准备重连")
                                # 短暂等待后继续重连循环
                                await asyncio.sleep(2)
                                continue
                            else:
                                logger.warning(f"【{self.cookie_id}】❌ 密码登录刷新失败，将重启实例...")
                        except Exception as refresh_e:
                            logger.error(f"【{self.cookie_id}】密码登录刷新过程异常: {self._safe_str(refresh_e)}")
                            logger.warning(f"【{self.cookie_id}】将重启实例...")
                        
                        # 如果密码登录刷新失败或异常，则重启实例
                        logger.error(f"【{self.cookie_id}】准备重启实例...")
                        self.connection_failures = 0  # 重置失败计数
                        
                        # 先清理后台任务，避免与重启过程冲突
                        logger.info(f"【{self.cookie_id}】重启前先清理后台任务...")
                        try:
                            await asyncio.wait_for(
                                self._cancel_background_tasks(),
                                timeout=8.0  # 给足够时间让任务响应
                            )
                            logger.info(f"【{self.cookie_id}】后台任务已清理完成")
                        except asyncio.TimeoutError:
                            logger.warning(f"【{self.cookie_id}】后台任务清理超时，强制继续重启")
                        except Exception as cleanup_e:
                            logger.error(f"【{self.cookie_id}】后台任务清理失败: {self._safe_str(cleanup_e)}")
                        
                        # 触发重启（不等待完成）
                        await self._restart_instance()
                        
                        # ⚠️ 重要：_restart_instance() 已触发重启，2秒后当前任务会被取消
                        # 不要在这里等待或执行其他操作，让任务自然退出
                        logger.info(f"【{self.cookie_id}】重启请求已触发，主程序即将退出，新实例将自动启动")
                        return  # 退出当前连接循环，等待被取消

                    # 计算重试延迟
                    retry_delay = self._calculate_retry_delay(error_msg)
                    logger.warning(f"【{self.cookie_id}】将在 {retry_delay} 秒后重试连接...")

                    try:
                        # 清空当前token，确保重新连接时会重新获取
                        if self.current_token:
                            logger.warning(f"【{self.cookie_id}】清空当前token，重新连接时将重新获取")
                            self.current_token = None

                        # 直接重置任务引用，不等待取消（快速重连方案）
                        # 这样可以避免等待任务取消导致的阻塞问题
                        logger.info(f"【{self.cookie_id}】准备重置后台任务引用（快速重连模式）...")
                        self._reset_background_tasks()
                        logger.info(f"【{self.cookie_id}】后台任务引用已重置，可以立即重连")

                        # 等待后重试 - 使用可中断的sleep，并定期输出日志证明进程还活着
                        logger.info(f"【{self.cookie_id}】开始等待 {retry_delay} 秒...")
                        # 强制刷新日志缓冲区，确保日志被写入
                        try:
                            sys.stdout.flush()
                        except Exception:
                            pass
                        
                        # 使用可中断的sleep，每5秒输出一次心跳日志
                        chunk_size = 5.0  # 每5秒输出一次日志
                        remaining = retry_delay
                        start_time = time.time()
                        
                        while remaining > 0:
                            sleep_time = min(chunk_size, remaining)
                            try:
                                await asyncio.sleep(sleep_time)
                                remaining -= sleep_time
                                elapsed = time.time() - start_time
                                if remaining > 0:
                                    logger.info(f"【{self.cookie_id}】等待中... 已等待 {elapsed:.1f} 秒，剩余 {remaining:.1f} 秒")
                                    # 定期刷新日志
                                    try:
                                        sys.stdout.flush()
                                    except Exception:
                                        pass
                            except asyncio.CancelledError:
                                logger.warning(f"【{self.cookie_id}】等待期间收到取消信号")
                                raise
                            except Exception as sleep_error:
                                logger.error(f"【{self.cookie_id}】等待期间发生异常: {self._safe_str(sleep_error)}")
                                logger.warning(f"【{self.cookie_id}】等待异常堆栈:\n{traceback.format_exc()}")
                                # 即使出错也继续等待剩余时间
                                if remaining > 0:
                                    await asyncio.sleep(remaining)
                                break
                        
                        logger.info(f"【{self.cookie_id}】等待完成（总耗时 {time.time() - start_time:.1f} 秒），准备重新连接...")
                        # 再次强制刷新日志
                        try:
                            sys.stdout.flush()
                        except Exception:
                            pass
                        
                    except Exception as cleanup_error:
                        logger.error(f"【{self.cookie_id}】清理过程出错: {self._safe_str(cleanup_error)}")
                        logger.warning(f"【{self.cookie_id}】清理异常堆栈:\n{traceback.format_exc()}")
                        # 即使清理失败，也要重置任务引用并等待后重试
                        self.heartbeat_task = None
                        self.token_refresh_task = None
                        self.cleanup_task = None
                        self.cookie_refresh_task = None
                        self.stream_watchdog_task = None
                        logger.warning(f"【{self.cookie_id}】清理失败，已强制重置所有任务引用")
                        # 使用可中断的sleep，并定期输出日志
                        logger.info(f"【{self.cookie_id}】清理失败后开始等待 {retry_delay} 秒...")
                        chunk_size = 5.0
                        remaining = retry_delay
                        start_time = time.time()
                        
                        while remaining > 0:
                            sleep_time = min(chunk_size, remaining)
                            try:
                                await asyncio.sleep(sleep_time)
                                remaining -= sleep_time
                                if remaining > 0:
                                    logger.info(f"【{self.cookie_id}】清理失败后等待中... 剩余 {remaining:.1f} 秒")
                            except asyncio.CancelledError:
                                logger.warning(f"【{self.cookie_id}】清理失败后等待期间收到取消信号")
                                raise
                            except Exception as sleep_error:
                                logger.error(f"【{self.cookie_id}】清理失败后等待期间发生异常: {self._safe_str(sleep_error)}")
                                if remaining > 0:
                                    await asyncio.sleep(remaining)
                                break
                        
                        logger.info(f"【{self.cookie_id}】清理失败后等待完成（总耗时 {time.time() - start_time:.1f} 秒）")
                    
                    # 继续下一次循环
                    logger.info(f"【{self.cookie_id}】开始新一轮WebSocket连接尝试...")
                    continue
        finally:
            # 更新连接状态为已关闭
            self._set_connection_state(ConnectionState.CLOSED, "程序退出")
            
            # 清空当前token
            if self.current_token:
                logger.info(f"【{self.cookie_id}】程序退出，清空当前token")
                self.current_token = None

            # 检查是否还有未取消的后台任务，如果有才执行清理
            has_pending_tasks = any([
                self.heartbeat_task and not self.heartbeat_task.done(),
                self.token_refresh_task and not self.token_refresh_task.done(),
                self.cleanup_task and not self.cleanup_task.done(),
                self.cookie_refresh_task and not self.cookie_refresh_task.done(),
                self.stream_watchdog_task and not self.stream_watchdog_task.done()
            ])
            
            if has_pending_tasks:
                logger.info(f"【{self.cookie_id}】检测到未完成的后台任务，执行清理...")
                # 使用统一的任务清理方法，添加超时保护
                try:
                    await asyncio.wait_for(
                        self._cancel_background_tasks(),
                        timeout=10.0
                    )
                except asyncio.TimeoutError:
                    logger.error(f"【{self.cookie_id}】程序退出时任务取消超时，强制继续")
                except Exception as e:
                    logger.error(f"【{self.cookie_id}】程序退出时任务取消失败: {self._safe_str(e)}")
                finally:
                    # 确保任务引用被重置
                    self.heartbeat_task = None
                    self.token_refresh_task = None
                    self.cleanup_task = None
                    self.cookie_refresh_task = None
                    self.stream_watchdog_task = None
            else:
                logger.info(f"【{self.cookie_id}】所有后台任务已清理完成，跳过重复清理")
                # 确保任务引用被重置
                self.heartbeat_task = None
                self.token_refresh_task = None
                self.cleanup_task = None
                self.cookie_refresh_task = None
                self.stream_watchdog_task = None
            
            # 清理所有后台任务
            if self.background_tasks:
                logger.info(f"【{self.cookie_id}】等待 {len(self.background_tasks)} 个后台任务完成...")
                try:
                    await asyncio.wait_for(
                        asyncio.gather(*self.background_tasks, return_exceptions=True),
                        timeout=10.0  # 10秒超时
                    )
                except asyncio.TimeoutError:
                    logger.warning(f"【{self.cookie_id}】后台任务清理超时，强制继续")
            
            # 确保关闭session
            await self.close_session()

            # 从全局实例字典中注销当前实例
            self._unregister_instance()
            logger.info(f"【{self.cookie_id}】XianyuLive主程序已完全退出")

    async def get_item_list_info(self, page_number=1, page_size=20, retry_count=0, sync_item_details=False):
        """获取商品信息，自动处理token失效的情况

        Args:
            page_number (int): 页码，从1开始
            page_size (int): 每页数量，默认20
            retry_count (int): 重试次数，内部使用
            sync_item_details (bool): 是否同步已存在商品的最新详情
        """
        if retry_count >= 4:  # 最多重试3次
            logger.error("获取商品信息失败，重试次数过多")
            return {"error": "获取商品信息失败，重试次数过多"}

        # 确保session已创建
        if not self.session:
            await self.create_session()

        params = {
            'jsv': '2.7.2',
            'appKey': '34839810',
            't': str(int(time.time()) * 1000),
            'sign': '',
            'v': '1.0',
            'type': 'originaljson',
            'accountSite': 'xianyu',
            'dataType': 'json',
            'timeout': '20000',
            'api': 'mtop.idle.web.xyh.item.list',
            'sessionOption': 'AutoLoginOnly',
            'spm_cnt': 'a21ybx.im.0.0',
            'spm_pre': 'a21ybx.collection.menu.1.272b5141NafCNK'
        }

        data = {
            'needGroupInfo': False,
            'pageNumber': page_number,
            'pageSize': page_size,
            'groupName': '在售',
            'groupId': '58877261',
            'defaultGroup': True,
            "userId": self.myid
        }

        # 始终从最新的cookies中获取_m_h5_tk token（刷新后cookies会被更新）
        token = trans_cookies(self.cookies_str).get('_m_h5_tk', '').split('_')[0] if trans_cookies(self.cookies_str).get('_m_h5_tk') else ''

        logger.warning(f"准备获取商品列表，token: {token}")
        if token:
            logger.warning(f"使用cookies中的_m_h5_tk token: {self._mask_secret_value(token, head=6, tail=4)}")
        else:
            logger.warning("cookies中没有找到_m_h5_tk token")

        # 生成签名
        data_val = json.dumps(data, separators=(',', ':'))
        sign = generate_sign(params['t'], token, data_val)
        params['sign'] = sign

        try:
            async with self.session.post(
                'https://h5api.m.goofish.com/h5/mtop.idle.web.xyh.item.list/1.0/',
                params=params,
                data={'data': data_val}
            ) as response:
                res_json = await response.json()

                if await self._apply_response_cookie_updates(response.headers, "item_list"):
                    logger.warning("已更新Cookie到数据库")

                logger.info(f"商品信息获取响应: {res_json}")

                # 检查响应是否成功
                if res_json.get('ret') and res_json['ret'][0] == 'SUCCESS::调用成功':
                    items_data = res_json.get('data', {})
                    # 从cardList中提取商品信息
                    card_list = items_data.get('cardList', [])

                    # 解析cardList中的商品信息
                    items_list = []
                    for card in card_list:
                        card_data = card.get('cardData', {})
                        if card_data:
                            # 提取商品基本信息
                            item_info = {
                                'id': card_data.get('id', ''),
                                'title': card_data.get('title', ''),
                                'price': card_data.get('priceInfo', {}).get('price', ''),
                                'price_text': card_data.get('priceInfo', {}).get('preText', '') + card_data.get('priceInfo', {}).get('price', ''),
                                'category_id': card_data.get('categoryId', ''),
                                'auction_type': card_data.get('auctionType', ''),
                                'item_status': card_data.get('itemStatus', 0),
                                'detail_url': card_data.get('detailUrl', ''),
                                'pic_info': card_data.get('picInfo', {}),
                                'detail_params': card_data.get('detailParams', {}),
                                'track_params': card_data.get('trackParams', {}),
                                'item_label_data': card_data.get('itemLabelDataVO', {}),
                                'card_type': card.get('cardType', 0)
                            }
                            items_list.append(item_info)

                    logger.info(f"成功获取到 {len(items_list)} 个商品")

                    # 打印商品详细信息到控制台
                    print("\n" + "="*80)
                    print(f"📦 账号 {self.myid} 的商品列表 (第{page_number}页，{len(items_list)} 个商品)")
                    print("="*80)

                    for i, item in enumerate(items_list, 1):
                        print(f"\n🔸 商品 {i}:")
                        print(f"   商品ID: {item.get('id', 'N/A')}")
                        print(f"   商品标题: {item.get('title', 'N/A')}")
                        print(f"   价格: {item.get('price_text', 'N/A')}")
                        print(f"   分类ID: {item.get('category_id', 'N/A')}")
                        print(f"   商品状态: {item.get('item_status', 'N/A')}")
                        print(f"   拍卖类型: {item.get('auction_type', 'N/A')}")
                        print(f"   详情链接: {item.get('detail_url', 'N/A')}")
                        if item.get('pic_info'):
                            pic_info = item['pic_info']
                            print(f"   图片信息: {pic_info.get('width', 'N/A')}x{pic_info.get('height', 'N/A')}")
                            print(f"   图片链接: {pic_info.get('picUrl', 'N/A')}")
                        print(f"   完整信息: {json.dumps(item, ensure_ascii=False, indent=2)}")

                    print("\n" + "="*80)
                    print("✅ 商品列表获取完成")
                    print("="*80)

                    # 自动保存商品信息到数据库
                    if items_list:
                        saved_count = await self.save_items_list_to_db(
                            items_list,
                            sync_item_details=sync_item_details,
                        )
                        logger.info(f"已将 {saved_count} 个商品信息保存到数据库")

                    return {
                        "success": True,
                        "page_number": page_number,
                        "page_size": page_size,
                        "current_count": len(items_list),
                        "items": items_list,
                        "saved_count": saved_count if items_list else 0,
                        "raw_data": items_data  # 保留原始数据以备调试
                    }
                else:
                    # 检查是否是token失效
                    error_msg = res_json.get('ret', [''])[0] if res_json.get('ret') else ''
                    if 'FAIL_SYS_TOKEN_EXOIRED' in error_msg or 'token' in error_msg.lower():
                        logger.warning(f"Token失效，准备重试: {error_msg}")
                        await asyncio.sleep(0.5)
                        return await self.get_item_list_info(
                            page_number,
                            page_size,
                            retry_count + 1,
                            sync_item_details=sync_item_details,
                        )
                    else:
                        logger.error(f"获取商品信息失败: {res_json}")
                        return {"error": f"获取商品信息失败: {error_msg}"}

        except Exception as e:
            logger.error(f"商品信息API请求异常: {self._safe_str(e)}")
            await asyncio.sleep(0.5)
            return await self.get_item_list_info(
                page_number,
                page_size,
                retry_count + 1,
                sync_item_details=sync_item_details,
            )

    async def get_all_items(self, page_size=20, max_pages=None, sync_item_details=False):
        """获取所有商品信息（自动分页）

        Args:
            page_size (int): 每页数量，默认20
            max_pages (int): 最大页数限制，None表示无限制
            sync_item_details (bool): 是否同步已存在商品的最新详情

        Returns:
            dict: 包含所有商品信息的字典
        """
        all_items = []
        page_number = 1
        total_saved = 0

        logger.info(f"开始获取所有商品信息，每页{page_size}条")

        while True:
            if max_pages and page_number > max_pages:
                logger.info(f"达到最大页数限制 {max_pages}，停止获取")
                break

            logger.info(f"正在获取第 {page_number} 页...")
            result = await self.get_item_list_info(
                page_number,
                page_size,
                sync_item_details=sync_item_details,
            )

            if not result.get("success"):
                logger.error(f"获取第 {page_number} 页失败: {result}")
                break

            current_items = result.get("items", [])
            if not current_items:
                logger.info(f"第 {page_number} 页没有数据，获取完成")
                break

            all_items.extend(current_items)
            total_saved += result.get("saved_count", 0)

            logger.info(f"第 {page_number} 页获取到 {len(current_items)} 个商品")

            # 如果当前页商品数量少于页面大小，说明已经是最后一页
            if len(current_items) < page_size:
                logger.info(f"第 {page_number} 页商品数量({len(current_items)})少于页面大小({page_size})，获取完成")
                break

            page_number += 1

            # 添加延迟避免请求过快
            await asyncio.sleep(1)

        logger.info(f"所有商品获取完成，共 {len(all_items)} 个商品，保存了 {total_saved} 个")

        return {
            "success": True,
            "total_pages": page_number,
            "total_count": len(all_items),
            "total_saved": total_saved,
            "items": all_items
        }

    def _get_item_polish_module(self):
        if os.getenv('ITEM_POLISH_IMPL', '').strip().lower() == 'plain':
            from item_polish_module import ItemPolishModule
        else:
            from secure_item_polish_ultra import ItemPolishModule

        return ItemPolishModule(self)

    async def polish_item(self, item_id, retry_count=0):
        """擦亮单个商品。"""
        return await self._get_item_polish_module().polish_item(item_id, retry_count)

    async def _polish_item_backup(self, item_id):
        """使用备用API擦亮商品。"""
        return await self._get_item_polish_module()._polish_item_backup(item_id)

    async def polish_all_items(self):
        """擦亮所有在售商品。"""
        return await self._get_item_polish_module().polish_all_items()

    async def send_image_msg(self, ws, cid, toid, image_url, width=800, height=600, card_id=None):
        """发送图片消息"""
        try:
            # 检查图片URL是否需要上传到CDN
            original_url = image_url

            if self._is_cdn_url(image_url):
                # 已经是CDN链接，直接使用
                logger.info(f"【{self.cookie_id}】使用已有的CDN图片链接: {image_url}")
            elif image_url.startswith('/static/uploads/') or image_url.startswith('static/uploads/'):
                # 本地图片，需要上传到闲鱼CDN
                local_image_path = image_url.replace('/static/uploads/', 'static/uploads/')
                if os.path.exists(local_image_path):
                    logger.info(f"【{self.cookie_id}】准备上传本地图片到闲鱼CDN: {local_image_path}")

                    # 使用图片上传器上传到闲鱼CDN
                    from utils.image_uploader import ImageUploader
                    uploader = ImageUploader(self.cookies_str)

                    async with uploader:
                        cdn_url = await uploader.upload_image(local_image_path)
                        if cdn_url:
                            logger.info(f"【{self.cookie_id}】图片上传成功，CDN URL: {cdn_url}")
                            image_url = cdn_url

                            # 如果是卡券图片，更新数据库中的图片URL
                            if card_id is not None:
                                await self._update_card_image_url(card_id, cdn_url)

                            # 获取实际图片尺寸
                            from utils.image_utils import image_manager
                            try:
                                actual_width, actual_height = image_manager.get_image_size(local_image_path)
                                if actual_width and actual_height:
                                    width, height = actual_width, actual_height
                                    logger.info(f"【{self.cookie_id}】获取到实际图片尺寸: {width}x{height}")
                            except Exception as e:
                                logger.warning(f"【{self.cookie_id}】获取图片尺寸失败，使用默认尺寸: {e}")
                        else:
                            logger.error(f"【{self.cookie_id}】图片上传失败: {local_image_path}")
                            logger.error(f"【{self.cookie_id}】❌ Cookie可能已失效！请检查配置并更新Cookie")
                            raise Exception(f"图片上传失败（Cookie可能已失效）: {local_image_path}")
                else:
                    logger.error(f"【{self.cookie_id}】本地图片文件不存在: {local_image_path}")
                    raise Exception(f"本地图片文件不存在: {local_image_path}")
            else:
                logger.warning(f"【{self.cookie_id}】未知的图片URL格式: {image_url}")

            # 记录详细的图片信息
            logger.info(f"【{self.cookie_id}】准备发送图片消息:")
            logger.info(f"  - 原始URL: {original_url}")
            logger.info(f"  - CDN URL: {image_url}")
            logger.info(f"  - 图片尺寸: {width}x{height}")
            logger.info(f"  - 聊天ID: {cid}")
            logger.info(f"  - 接收者ID: {toid}")

            # 构造图片消息内容 - 使用正确的闲鱼格式
            image_content = {
                "contentType": 2,  # 图片消息类型
                "image": {
                    "pics": [
                        {
                            "height": int(height),
                            "type": 0,
                            "url": image_url,
                            "width": int(width)
                        }
                    ]
                }
            }

            # Base64编码
            content_json = json.dumps(image_content, ensure_ascii=False)
            content_base64 = str(base64.b64encode(content_json.encode('utf-8')), 'utf-8')

            logger.info(f"【{self.cookie_id}】图片内容JSON: {content_json}")
            logger.info(f"【{self.cookie_id}】Base64编码长度: {len(content_base64)}")

            # 构造WebSocket消息（完全参考send_msg的格式）
            msg = {
                "lwp": "/r/MessageSend/sendByReceiverScope",
                "headers": {
                    "mid": generate_mid()
                },
                "body": [
                    {
                        "uuid": generate_uuid(),
                        "cid": f"{cid}@goofish",
                        "conversationType": 1,
                        "content": {
                            "contentType": 101,
                            "custom": {
                                "type": 1,
                                "data": content_base64
                            }
                        },
                        "redPointPolicy": 0,
                        "extension": {
                            "extJson": "{}"
                        },
                        "ctx": {
                            "appVersion": "1.0",
                            "platform": "web"
                        },
                        "mtags": {},
                        "msgReadStatusSetting": 1
                    },
                    {
                        "actualReceivers": [
                            f"{toid}@goofish",
                            f"{self.myid}@goofish"
                        ]
                    }
                ]
            }

            await ws.send(json.dumps(msg))
            logger.info(f"【{self.cookie_id}】图片消息发送成功: {image_url}")

        except Exception as e:
            logger.error(f"【{self.cookie_id}】发送图片消息失败: {self._safe_str(e)}")
            raise

    async def send_image_from_file(self, ws, cid, toid, image_path):
        """从本地文件发送图片"""
        try:
            # 上传图片到闲鱼CDN
            logger.info(f"【{self.cookie_id}】开始上传图片: {image_path}")

            from utils.image_uploader import ImageUploader
            uploader = ImageUploader(self.cookies_str)

            async with uploader:
                image_url = await uploader.upload_image(image_path)

            if image_url:
                # 获取图片信息
                from utils.image_utils import image_manager
                try:
                    from PIL import Image
                    with Image.open(image_path) as img:
                        width, height = img.size
                except Exception as e:
                    logger.warning(f"无法获取图片尺寸，使用默认值: {e}")
                    width, height = 800, 600

                # 发送图片消息
                await self.send_image_msg(ws, cid, toid, image_url, width, height)
                logger.info(f"【{self.cookie_id}】图片发送完成: {image_path} -> {image_url}")
                return True
            else:
                logger.error(f"【{self.cookie_id}】图片上传失败: {image_path}")
                logger.error(f"【{self.cookie_id}】❌ Cookie可能已失效！请检查配置并更新Cookie")
                return False

        except Exception as e:
            logger.error(f"【{self.cookie_id}】从文件发送图片失败: {self._safe_str(e)}")
            return False

if __name__ == '__main__':
    cookies_str = os.getenv('COOKIES_STR')
    xianyuLive = XianyuLive(cookies_str)
    asyncio.run(xianyuLive.main())
