<?php
// 功能：网站模板标签解析文件
// 作者：启肯科技
// Q Q：328493711
// 微信：13738373177（同手机）
// 官网：http://www.qiken.com
// 说明：QKCMS-轻量级AI智能内容管理建站系统
// 版权：© 2025 温州启肯网络有限公司 保留所有权利，盗版必究。

if (!isset($_SESSION)) {
    session_start();
}
include_once dirname(__FILE__) . '/../common.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'parse' && isset($_POST['content'])) {
    $content = $_POST['content'];
    echo processTemplateContent($content);
    exit;
}

function convertHtmlTags($content)
{
    return preg_replace_callback('/<!--\[([\s\S]+?)\]-->/', function ($matches) {
        $tag_content = $matches[1];
        if (stripos($tag_content, 'article:latest') === 0) return '{latest_articles}';
        if (stripos($tag_content, 'stats:articles_count') === 0) return '{total_posts}';
        if (stripos($tag_content, 'stats:users_count') === 0) return '{total_members}';
        if (stripos($tag_content, 'category:list') === 0) return '{total_categories} 个分类';
        $tag_content = str_replace(':', '_', $tag_content);
        return '{' . $tag_content . '}';
    }, $content);
}

function getPropertyText($property_value)
{
    if (is_null($property_value) || $property_value === '' || $property_value === false) return null;

    // 从数据库获取属性名称
    if (is_numeric($property_value)) {
        $num_value = intval($property_value);
        $property_name = getArticlePropertyName($num_value);
        if ($property_name) return $property_name;
    }

    // 兼容旧版字符串映射
    $string_map = array('普通' => '普通', 'normal' => '普通', '头条' => '头条', 'top' => '头条', 'headline' => '头条', '热门' => '热门', 'hot' => '热门', '推荐' => '推荐', 'recommend' => '推荐', '公告' => '公告', 'special' => '公告', '精华' => '公告', '给力' => '给力', 'awesome' => '给力');
    $str_value = strtolower(trim(strval($property_value)));
    if (isset($string_map[$str_value])) return $string_map[$str_value];

    return null;
}

function getPropertyTagWithColor($property_value)
{
    if (is_null($property_value) || $property_value === '' || $property_value === false) return null;

    $property_id = null;
    $property_name = null;
    $property_color = '#999999';

    if (is_numeric($property_value)) {
        $property_id = intval($property_value);
    } else {
        $string_map = array('普通' => 0, 'normal' => 0, '头条' => 1, 'top' => 1, 'headline' => 1, '热门' => 2, 'hot' => 2, '推荐' => 3, 'recommend' => 3, '公告' => 4, 'special' => 4, '精华' => 4, '给力' => 5, 'awesome' => 5);
        $str_value = strtolower(trim(strval($property_value)));
        if (isset($string_map[$str_value])) {
            $property_id = $string_map[$str_value];
        }
    }

    if ($property_id !== null) {
        $properties = getArticleProperties(false);
        if (isset($properties[$property_id])) {
            $property_name = $properties[$property_id]['name'];
            if (isset($properties[$property_id]['color']) && !empty($properties[$property_id]['color'])) {
                $property_color = $properties[$property_id]['color'];
            }
        }
    }

    if ($property_name) {
        return '<span class="article-property" style="--property-color: ' . $property_color . '">' . $property_name . '</span>';
    }

    return null;
}

function processTemplateContent($content, $article_id = 0)
{
    $content = convertHtmlTags($content);
    if (function_exists('qkcms_tag_parser')) return qkcms_tag_parser($content, $article_id);
    return $content;
}

$qkcms_debug_env = getenv('QKCMS_DEBUG');
$qkcms_debug = ($qkcms_debug_env === '1' || strtolower((string)$qkcms_debug_env) === 'true');
ini_set('display_errors', $qkcms_debug ? '1' : '0');
ini_set('display_startup_errors', $qkcms_debug ? '1' : '0');
ini_set('log_errors', '1');
error_reporting(E_ALL);

// #region debug-point A:tplrdr-bootstrap
if (!function_exists('qk_tplrdr_debug_event')) {
    function qk_tplrdr_debug_event($hypothesis_id, $location, $msg, $data = array())
    {
        static $debug_url = null;
        static $debug_session = null;
        static $debug_ready = null;
        if ($debug_ready === null) {
            $debug_ready = false;
            $env_path = dirname(__FILE__) . '/../aaa/.dbg/catgroup-500-error.env';
            if (is_file($env_path)) {
                $lines = @file($env_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
                if (is_array($lines)) {
                    foreach ($lines as $line) {
                        if (strpos($line, 'DEBUG_SERVER_URL=') === 0) $debug_url = trim(substr($line, 17));
                        else if (strpos($line, 'DEBUG_SESSION_ID=') === 0) $debug_session = trim(substr($line, 17));
                    }
                }
            }
            $debug_ready = ($debug_url !== null && $debug_url !== '' && $debug_session !== null && $debug_session !== '');
        }
        if (!$debug_ready) return;
        $payload = json_encode(array(
            'sessionId' => $debug_session,
            'runId' => 'pre-fix',
            'hypothesisId' => $hypothesis_id,
            'location' => $location,
            'msg' => $msg,
            'data' => $data,
            'ts' => round(microtime(true) * 1000)
        ), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($payload === false) return;
        @file_get_contents($debug_url, false, stream_context_create(array(
            'http' => array(
                'method' => 'POST',
                'header' => "Content-Type: application/json\r\nConnection: close\r\n",
                'content' => $payload,
                'timeout' => 1
            )
        )));
    }

    register_shutdown_function(function () {
        $error = error_get_last();
        if (!$error) return;
        if (!in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR), true)) return;
        qk_tplrdr_debug_event('E', 'tplrdr.php:shutdown', '[DEBUG] tplrdr fatal shutdown', array(
            'type' => $error['type'],
            'message' => $error['message'],
            'file' => $error['file'],
            'line' => $error['line'],
            'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''
        ));
    });
}
// #endregion

if (!function_exists('close_db_connection')) {
    function close_db_connection($mysqli)
    {
        try {
            if ($mysqli && is_object($mysqli) && method_exists($mysqli, 'close') && $mysqli instanceof mysqli) {
                return $mysqli->close();
            }
        } catch (Exception $e) {
        }
        return false;
    }
}

function is_valid_db_connection($db)
{
    static $validation_cache = array();
    if (!($db instanceof mysqli)) return false;

    $db_key = spl_object_hash($db);
    if (array_key_exists($db_key, $validation_cache)) {
        return $validation_cache[$db_key];
    }

    if ($db->connect_error || @$db->errno) {
        $validation_cache[$db_key] = false;
        return false;
    }

    try {
        $validation_cache[$db_key] = @$db->ping();
    } catch (Exception $e) {
        $validation_cache[$db_key] = false;
    }

    return $validation_cache[$db_key];
}

function table_exists($db, $table_name)
{
    try {
        $result = $db->query("SHOW TABLES LIKE '$table_name'");
        return $result && $result->num_rows > 0;
    } catch (Exception $e) {
        return false;
    }
}

function execute_db_query($db, $query, $params = array(), $return_type = 'single')
{
    static $query_cache = array();
    try {
        $normalized_query = ltrim($query);
        $is_cacheable = preg_match('/^(SELECT|SHOW|DESCRIBE)\s/i', $normalized_query) === 1;
        $db_key = is_object($db) ? spl_object_hash($db) : 'default';
        $cache_key = $db_key . '|' . $return_type . '|' . md5($query . '|' . serialize($params));
        if ($is_cacheable && array_key_exists($cache_key, $query_cache)) {
            return $query_cache[$cache_key];
        }
        $stmt = $db->prepare($query);
        if (!$stmt) throw new Exception('预处理语句失败: ' . $db->error);
        if (!empty($params)) {
            $types = str_repeat('s', count($params));
            $stmt->bind_param($types, ...$params);
        }
        $stmt->execute();
        $result = null;
        switch ($return_type) {
            case 'single':
                $result_obj = $stmt->get_result();
                if ($result_obj) {
                    $row = $result_obj->fetch_assoc();
                    $result = $row ? array_values($row)[0] : null;
                    $result_obj->free();
                } else {
                    $result = null;
                }
                break;
            case 'count':
                $result_obj = $stmt->get_result();
                $row = $result_obj->fetch_assoc();
                $result = isset($row['count']) ? intval($row['count']) : 0;
                $result_obj->free();
                break;
            case 'all':
                $result_obj = $stmt->get_result();
                $result = array();
                while ($row = $result_obj->fetch_assoc()) array_push($result, $row);
                $result_obj->free();
                break;
            default:
                $result = $stmt->affected_rows;
        }
        $stmt->close();
        if ($is_cacheable) {
            $query_cache[$cache_key] = $result;
        }
        return $result;
    } catch (Exception $e) {
        return null;
    }
}

$tag_mapping = array(
    'site_title' => array('type' => 'field', 'table' => 'settings', 'field' => 'site_title'),
    'site_name' => array('type' => 'field', 'table' => 'settings', 'field' => 'site_name'),
    'logo' => array('type' => 'field', 'table' => 'settings', 'field' => 'logo'),
    'site_description' => array('type' => 'field', 'table' => 'settings', 'field' => 'site_description'),
    'site_keywords' => array('type' => 'field', 'table' => 'settings', 'field' => 'site_keywords'),
    'site_copyright' => array('type' => 'field', 'table' => 'settings', 'field' => 'site_copyright'),
    'primary_color' => array('type' => 'field', 'table' => 'settings', 'field' => 'primary_color'),
    'site_Prohw' => array('type' => 'field', 'table' => 'settings', 'field' => 'Prohw'),
    'user_nickname' => array('type' => 'session', 'description' => '当前登录用户昵称'),
    'user_avatar' => array('type' => 'session', 'description' => '当前登录用户头像URL'),
    'user_email' => array('type' => 'session', 'description' => '当前登录用户邮箱'),
    'user_status' => array('type' => 'session', 'description' => '当前登录用户状态，1表示正常，0表示被封禁'),
    'total_members' => array('type' => 'count', 'table' => 'users'),
    'total_comments' => array('type' => 'count', 'table' => 'comments'),
    'total_categories' => array('type' => 'count', 'table' => 'categories'),
    'total_reports' => array('type' => 'count', 'table' => 'reports'),
    'total_posts' => array('type' => 'count', 'table' => 'posts'),
    'total_visit' => array('type' => 'count', 'table' => 'visit'),
    'total_order' => array('type' => 'custom', 'handler' => 'get_total_order_count'),
    'total_keywords' => array('type' => 'keywords_count', 'table' => 'posts'),
    'current_position' => array('type' => 'custom', 'handler' => 'get_current_position'),
    'posts_id' => array('type' => 'special', 'description' => '文章ID，从请求参数获取'),
    'posts_title' => array('type' => 'field', 'table' => 'posts', 'field' => 'title'),
    'posts_content' => array('type' => 'custom', 'handler' => 'get_formatted_post_content'),
    'posts_raw_content' => array('type' => 'field', 'table' => 'posts', 'field' => 'content'),
    'posts_description' => array('type' => 'field', 'table' => 'posts', 'field' => 'description'),
    'posts_keywords' => array('type' => 'field', 'table' => 'posts', 'field' => 'keywords'),
    'posts_keywords_links' => array('type' => 'custom', 'handler' => 'get_posts_keywords_with_links'),
    'posts_created' => array('type' => 'field', 'table' => 'posts', 'field' => 'created_at'),
    'posts_views' => array('type' => 'field', 'table' => 'posts', 'field' => 'views'),
    'posts_shares' => array('type' => 'field', 'table' => 'posts', 'field' => 'shares'),
    'posts_likes' => array('type' => 'field', 'table' => 'posts', 'field' => 'likes'),
    'posts_thumbnail' => array('type' => 'field', 'table' => 'posts', 'field' => 'thumbnail'),
    'posts_property' => array('type' => 'field', 'table' => 'posts', 'field' => 'property'),
    'posts_comments' => array('type' => 'count', 'table' => 'comments', 'group' => 'posts_id'),
    'posts_share_count' => array('type' => 'custom', 'handler' => 'get_post_share_count'),
    'posts_report_count' => array('type' => 'custom', 'handler' => 'get_post_report_count'),
    'posts_like_count' => array('type' => 'field', 'table' => 'posts', 'field' => 'likes'),
    'posts_category' => array('type' => 'join', 'table' => 'categories', 'field' => 'name', 'join' => 'posts.category_id = categories.id'),
    'posts_category_url' => array('type' => 'url', 'table' => 'categories', 'field' => 'id', 'join' => 'posts.category_id = categories.id'),
    'posts_author' => array('type' => 'custom', 'handler' => 'get_post_author'),
    'posts_author_id' => array('type' => 'custom', 'handler' => 'get_post_author_id'),
    'posts_author_avatar' => array('type' => 'custom', 'handler' => 'get_post_author_avatar'),
    'posts_url' => array('type' => 'url', 'table' => 'posts', 'field' => 'id'),
    'posts_tool_url' => array('type' => 'field', 'table' => 'posts', 'field' => 'tool_url'),
    'posts_redirect_url' => array('type' => 'field', 'table' => 'posts', 'field' => 'redirect_url'),
    'posts_status_switch' => array('type' => 'field', 'table' => 'posts', 'field' => 'status_switch'),
    'posts_price' => array('type' => 'field', 'table' => 'posts', 'field' => 'price'),
    'cat_id' => array('type' => 'field', 'table' => 'categories', 'field' => 'id'),
    'category_id' => array('type' => 'field', 'table' => 'categories', 'field' => 'id'),
    'cat_name' => array('type' => 'field', 'table' => 'categories', 'field' => 'name'),
    'cat_seo_title' => array('type' => 'custom', 'handler' => 'get_cat_seo_title'),
    'cat_title' => array('type' => 'custom', 'handler' => 'get_cat_title'),
    'cat_description' => array('type' => 'field', 'table' => 'categories', 'field' => 'description'),
    'cat_keywords' => array('type' => 'custom', 'handler' => 'get_cat_keywords'),
    'cat_keywords_links' => array('type' => 'custom', 'handler' => 'get_cat_keywords_with_links'),
    'cat_thumbnail' => array('type' => 'field', 'table' => 'categories', 'field' => 'thumbnail'),
    'cat_url' => array('type' => 'url', 'table' => 'categories', 'field' => 'id'),
    'cat_posts' => array('type' => 'count', 'table' => 'posts', 'group' => 'category_id')
);

function get_default_user_value($tag)
{
    switch ($tag) {
        case 'user_nickname':
            return "游客朋友";
        case 'user_levels':
            return "游客";
        case 'user_avatar':
            return defined('DEFAULT_AVATAR_PATH') ? DEFAULT_AVATAR_PATH : '/assets/images/avatar.png';
        case 'user_register_time':
        case 'user_created_at':
            return "未登录";
        case 'user_status':
            return "正常";
        case 'user_points':
            return 0;
        case 'user_email':
            return "未绑定";
        default:
            return null;
    }
}

function qkcms_get_user_profile_row($db, $user_id)
{
    static $user_profile_cache = array();
    $user_id = intval($user_id);
    if ($user_id <= 0) return null;
    $db_key = is_object($db) ? spl_object_hash($db) : 'default';
    $cache_key = $db_key . '|' . $user_id;
    if (array_key_exists($cache_key, $user_profile_cache)) {
        return $user_profile_cache[$cache_key];
    }

    $user_table = findTable($db, array('users', 'members', 'qkcms_user', 'admin_user', 'site_user'));
    if (!$user_table) {
        $user_profile_cache[$cache_key] = null;
        return null;
    }

    $user_id_field = findField($db, $user_table, array('id', 'user_id')) ?: 'id';
    $username_field = findField($db, $user_table, array('username', 'nickname', 'user_name', 'loginname', 'name'));
    $avatar_field = findField($db, $user_table, array('avatar', 'user_avatar'));
    $email_field = findField($db, $user_table, array('email', 'user_email'));
    $level_field = findField($db, $user_table, array('levels_id', 'level_id', 'group_id'));
    $created_field = findField($db, $user_table, array('created_at', 'create_time', 'reg_time', 'register_time'));
    $status_field = findField($db, $user_table, array('status', 'state', 'user_status'));
    $points_field = findField($db, $user_table, array('points', 'score', 'credit'));

    $select_fields = array("$user_table.$user_id_field AS qk_user_id");
    if ($username_field) $select_fields[] = "$user_table.$username_field AS qk_username";
    if ($avatar_field) $select_fields[] = "$user_table.$avatar_field AS qk_avatar";
    if ($email_field) $select_fields[] = "$user_table.$email_field AS qk_email";
    if ($level_field) $select_fields[] = "$user_table.$level_field AS qk_level_id";
    if ($created_field) $select_fields[] = "$user_table.$created_field AS qk_created_at";
    if ($status_field) $select_fields[] = "$user_table.$status_field AS qk_status";
    if ($points_field) $select_fields[] = "$user_table.$points_field AS qk_points";

    $join_sql = '';
    $levels_table = null;
    $levels_name_field = null;
    if ($level_field) {
        $levels_table = findTable($db, array('levels', 'user_levels', 'member_levels'));
        if ($levels_table) {
            $levels_id_field = findField($db, $levels_table, array('id', 'level_id')) ?: 'id';
            $levels_name_field = findField($db, $levels_table, array('name', 'title', 'level_name'));
            if ($levels_name_field) {
                $select_fields[] = "$levels_table.$levels_name_field AS qk_level_name";
                $join_sql = " LEFT JOIN $levels_table ON $user_table.$level_field = $levels_table.$levels_id_field";
            }
        }
    }

    $sql = "SELECT " . implode(', ', $select_fields) . " FROM $user_table$join_sql WHERE $user_table.$user_id_field = ? LIMIT 1";
    $row = execute_db_query($db, $sql, array($user_id), 'all');
    $user_profile_cache[$cache_key] = (!empty($row) && isset($row[0])) ? $row[0] : null;
    return $user_profile_cache[$cache_key];
}

function get_user_info_from_session($tag)
{
    if (!isset($_SESSION)) @session_start();
    if (isset($_SESSION[$tag])) {
        if ($tag === 'user_status') {
            $value = $_SESSION[$tag];
            return ($value === 0 || $value === '0' || $value === '被封禁') ? '被封禁' : '正常';
        }
        return $_SESSION[$tag];
    }
    return null;
}

function format_time($timestamp)
{
    if (empty($timestamp)) return '未知';
    if (is_numeric($timestamp)) {
        return date('Y-m-d', $timestamp);
    }
    $date = strtotime($timestamp);
    if ($date) {
        return date('Y-m-d', $date);
    }
    return $timestamp;
}

function get_user_info($db, $tag)
{
    static $user_info_cache = array();
    if (!isset($_SESSION)) @session_start();
    $session_user_id = isset($_SESSION['user_id']) ? intval($_SESSION['user_id']) : 0;
    $request_uri = isset($_SERVER['REQUEST_URI']) ? (string)$_SERVER['REQUEST_URI'] : '';
    $cache_key = $tag . '|' . $session_user_id . '|' . md5($request_uri);
    if (array_key_exists($cache_key, $user_info_cache)) {
        return $user_info_cache[$cache_key];
    }
    // 头像字段不直接从session获取，优先从数据库查询
    if ($tag !== 'user_avatar' && isset($_SESSION[$tag])) {
        $user_info_cache[$cache_key] = $_SESSION[$tag];
        return $_SESSION[$tag];
    }

    // 检查是否是访问用户中心页面（Member_id页面）
    $is_member_page = false;
    $visited_user_id = 0;
    if (isset($_SERVER['REQUEST_URI'])) {
        $request_uri = $_SERVER['REQUEST_URI'];
        if (preg_match('/^\/Member_id(\d+)\.html/i', $request_uri, $matches)) {
            $is_member_page = true;
            $visited_user_id = intval($matches[1]);
        }
    }

    if (!isset($_SESSION['user_id'])) {
        // 未登录用户访问其他用户的空间
        if ($is_member_page && $visited_user_id > 0) {
            $profile = qkcms_get_user_profile_row($db, $visited_user_id);
            if ($profile) {
                switch ($tag) {
                    case 'user_id':
                        $value = intval($profile['qk_user_id']);
                        break;
                    case 'user_nickname':
                        $value = isset($profile['qk_username']) && $profile['qk_username'] !== '' ? $profile['qk_username'] : '游客朋友';
                        break;
                    case 'user_avatar':
                        $value = isset($profile['qk_avatar']) ? trim((string)$profile['qk_avatar']) : '';
                        if ($value === '') $value = DEFAULT_AVATAR_PATH;
                        elseif (strpos($value, 'http') !== 0 && strpos($value, '/') !== 0) $value = '/' . $value;
                        break;
                    case 'user_email':
                        $value = isset($profile['qk_email']) && $profile['qk_email'] !== '' ? $profile['qk_email'] : '未绑定';
                        break;
                    case 'user_levels':
                        $value = isset($profile['qk_level_name']) && $profile['qk_level_name'] !== '' ? $profile['qk_level_name'] : '游客';
                        break;
                    case 'user_register_time':
                    case 'user_created_at':
                        $value = format_time(isset($profile['qk_created_at']) ? $profile['qk_created_at'] : null);
                        break;
                    case 'user_status':
                        $status_value = isset($profile['qk_status']) ? $profile['qk_status'] : null;
                        $value = ($status_value === 0 || $status_value === '0' || $status_value === '被封禁') ? '被封禁' : '正常';
                        break;
                    case 'user_points':
                        $value = isset($profile['qk_points']) ? intval($profile['qk_points']) : 0;
                        break;
                    default:
                        $value = null;
                }
                $user_info_cache[$cache_key] = $value;
                return $value;
            }
        }

        switch ($tag) {
            case 'user_id':
                $user_info_cache[$cache_key] = 0;
                return 0;
            case 'user_nickname':
                $user_info_cache[$cache_key] = "游客朋友";
                return "游客朋友";
            case 'user_levels':
                $user_info_cache[$cache_key] = "游客";
                return "游客";
            case 'user_avatar':
                $user_info_cache[$cache_key] = DEFAULT_AVATAR_PATH;
                return DEFAULT_AVATAR_PATH;
            case 'user_register_time':
            case 'user_created_at':
                $user_info_cache[$cache_key] = "未登录";
                return "未登录";
            case 'user_status':
                $user_info_cache[$cache_key] = "正常";
                return "正常";
            case 'user_email':
                $user_info_cache[$cache_key] = "未绑定";
                return "未绑定";
            case 'user_points':
                $user_info_cache[$cache_key] = 0;
                return 0;
        }
        $user_info_cache[$cache_key] = null;
        return null;
    }

    // 头像字段优先从数据库获取，确保获取最新数据
    if ($tag === 'user_avatar' && isset($_SESSION['user_id'])) {
        // 继续执行下面的数据库查询逻辑
    } elseif (isset($_SESSION[$tag])) {
        // 其他字段如果session中已经有该值，直接返回
        return $_SESSION[$tag];
    }

    if (isset($_SESSION['user_id'])) {
        $user_id = $_SESSION['user_id'];

        // 安全检查：确保user_id是有效的数字
        if (!is_numeric($user_id) || $user_id <= 0) {
            return get_default_user_value($tag);
        }

        if ($tag === 'user_id') {
            $user_info_cache[$cache_key] = intval($user_id);
            return intval($user_id);
        }

        $profile = qkcms_get_user_profile_row($db, $user_id);
        if ($profile) {
            switch ($tag) {
                case 'user_nickname':
                    $value = isset($profile['qk_username']) && $profile['qk_username'] !== '' ? $profile['qk_username'] : '游客朋友';
                    break;
                case 'user_avatar':
                    $value = isset($profile['qk_avatar']) ? trim((string)$profile['qk_avatar']) : '';
                    if ($value === '') $value = DEFAULT_AVATAR_PATH;
                    elseif (strpos($value, 'http') !== 0 && strpos($value, '/') !== 0) $value = '/' . $value;
                    break;
                case 'user_email':
                    $value = isset($profile['qk_email']) && $profile['qk_email'] !== '' ? $profile['qk_email'] : '未绑定';
                    break;
                case 'user_levels':
                    $value = isset($profile['qk_level_name']) && $profile['qk_level_name'] !== '' ? $profile['qk_level_name'] : '游客';
                    break;
                case 'user_register_time':
                case 'user_created_at':
                    $value = format_time(isset($profile['qk_created_at']) ? $profile['qk_created_at'] : null);
                    break;
                case 'user_status':
                    $status_value = isset($profile['qk_status']) ? $profile['qk_status'] : null;
                    $value = ($status_value === 0 || $status_value === '0' || $status_value === '被封禁') ? '被封禁' : '正常';
                    break;
                case 'user_points':
                    $value = isset($profile['qk_points']) ? intval($profile['qk_points']) : 0;
                    break;
                default:
                    $value = null;
            }

            if ($value !== null) {
                if ($tag !== 'user_avatar') {
                    $_SESSION[$tag] = $value;
                }
                $user_info_cache[$cache_key] = $value;
                return $value;
            }
        }
    }
    $user_info_cache[$cache_key] = get_user_info_from_session($tag);
    return $user_info_cache[$cache_key];
}

function get_field_value($db, $mapping, $tag, $article_id)
{
    $table = $mapping['table'];
    $field = $mapping['field'];
    if ($table === 'settings') {
        $settings_row = qkcms_get_settings_row($db);
        return is_array($settings_row) && array_key_exists($field, $settings_row) ? $settings_row[$field] : null;
    }

    if (strpos($tag, 'posts_') === 0) {
        $use_id = $article_id > 0 ? $article_id : get_article_id();
        if ($use_id <= 0) {
            if ($tag === 'posts_thumbnail') return DEFAULT_THUMBNAIL_PATH;
            return null;
        }
        $result = execute_db_query($db, "SELECT $field FROM $table WHERE id = ? LIMIT 1", array($use_id), 'single');
        if ($tag === 'posts_thumbnail') return check_file_exists_or_default($result);
        if ($result === null || $result === '') return null;
        if ($tag === 'posts_property') return getPropertyText($result);
        return $result;
    }

    if (strpos($tag, 'cat_') === 0 || $tag === 'category_id') {
        $cat_id = get_category_id();
        if ($cat_id <= 0) return null;
        $result = execute_db_query($db, "SELECT $field FROM $table WHERE id = ? LIMIT 1", array($cat_id), 'single');
        if ($result === null || $result === '') return null;
        if ($tag === 'cat_thumbnail') return check_file_exists_or_default($result);
        return $result;
    }
    return null;
}

function get_count_value($db, $mapping, $tag, $article_id)
{
    $table = $mapping['table'];
    $condition = isset($mapping['condition']) ? " WHERE {$mapping['condition']}" : '';
    $group = isset($mapping['group']) ? " GROUP BY {$mapping['group']}" : '';

    if (($tag === 'total_comments' || $tag === 'posts_comments') && $table === 'comments') {
        $status_field = findField($db, $table, ['status', 'check_status', 'approve_status']);
        if ($status_field) {
            $condition = $condition ? $condition . " AND $status_field = 1" : " WHERE $status_field = 1";
        }
    }

    if (strpos($tag, 'posts_') === 0) {
        $use_id = $article_id > 0 ? $article_id : get_article_id();
        if ($use_id <= 0) return 0;
        $posts_field = ($table === 'comments' || $table === 'reports') ? 'post_id' : 'id';
        $condition = $condition ? $condition . " AND $posts_field = ?" : " WHERE $posts_field = ?";
        $group = '';
        return execute_db_query($db, "SELECT COUNT(*) as count FROM $table$condition$group LIMIT 1", array($use_id), 'count');
    }

    if (strpos($tag, 'cat_') === 0 && $table === 'posts') {
        $cat_id = get_category_id();
        if ($cat_id <= 0) return 0;
        $article_table = findTable($db, ['articles', 'posts', 'news', 'blog_posts']);
        if ($article_table) {
            $article_cat_field = findField($db, $article_table, ['category_id', 'cat_id', 'cid']);
            if ($article_cat_field) {
                $count_sql = "SELECT COUNT(*) as count FROM $article_table WHERE $article_cat_field = $cat_id";
                $count_result = $db->query($count_sql);
                if ($count_result) {
                    $count_row = $count_result->fetch_assoc();
                    $count = isset($count_row['count']) ? $count_row['count'] : 0;
                    $count_result->free();
                    return $count;
                }
            }
        }
        return 0;
    }
    return execute_db_query($db, "SELECT COUNT(*) as count FROM $table$condition$group LIMIT 1", array(), 'count');
}

function check_file_exists_or_default($file_path, $default_path = DEFAULT_THUMBNAIL_PATH)
{
    static $file_exists_cache = array();
    if (empty($file_path)) return $default_path;

    if (qk_is_local_media_path($file_path)) {
        $relative_path = qk_normalize_media_path($file_path);
        if (isset($_SERVER['DOCUMENT_ROOT'])) {
            $local_path = $_SERVER['DOCUMENT_ROOT'] . $relative_path;
            if (!array_key_exists($local_path, $file_exists_cache)) {
                $file_exists_cache[$local_path] = file_exists($local_path);
            }
            if ($file_exists_cache[$local_path]) return $relative_path;
        }
        return $default_path;
    } elseif (strpos($file_path, '://') !== false) {
        return $file_path;
    }

    $relative_path = $file_path;
    if (strpos($file_path, '/') !== 0) $relative_path = '/' . $file_path;

    if (isset($_SERVER['DOCUMENT_ROOT'])) {
        $local_path = $_SERVER['DOCUMENT_ROOT'] . $relative_path;
        if (!array_key_exists($local_path, $file_exists_cache)) {
            $file_exists_cache[$local_path] = file_exists($local_path);
        }
        if ($file_exists_cache[$local_path]) return $relative_path;
    }
    return $default_path;
}

function generate_category_svg_icon($category_name, $category_id)
{
    // 获取首个字符，如果名称为空则使用默认字符
    $first_char = '分';
    if (!empty($category_name)) {
        $first_char = mb_substr($category_name, 0, 1, 'UTF-8');
    }

    $pastel_colors = [
        '#FFB3BA',
        '#FFDFBA',
        '#FFFFBA',
        '#BAFFC9',
        '#BAE1FF',
        '#E0BBE4',
        '#FEC8D8',
        '#FFDFD3',
        '#B5EAD7',
        '#C7CEEA',
        '#F0E68C',
        '#DDA0DD',
        '#98FB98',
        '#AFEEEE',
        '#FFA07A',
        '#FFB6C1',
        '#87CEFA',
        '#F8C8DC',
        '#F0E68C',
        '#B0C4DE',
        '#FFDAB9',
        '#98D8C8',
        '#F7DC6F',
        '#DDBEA9',
        '#85C1E9',
        '#F8B739',
        '#A8E6CF',
        '#FFB7B2',
        '#C7CEEA',
        '#B5EAD7',
        '#FFDAC1',
        '#E2F0CB',
        '#C5C9E6',
        '#F6EAC2',
        '#FF9AA2',
        '#FFB7B2',
        '#FFDAC1',
        '#E2F0CB',
        '#B5EAD7',
        '#C7CEEA'
    ];
    $color_index = $category_id % count($pastel_colors);
    $bg_color = $pastel_colors[$color_index];

    // 使用正确的XML编码，字符水平和垂直居中
    $svg_content = '<?xml version="1.0" encoding="UTF-8"?>' .
        '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">' .
        '<rect width="100" height="100" fill="' . $bg_color . '"/>' .
        '<text x="50" y="50" font-size="45" font-family="Arial, Microsoft YaHei, sans-serif" font-weight="700" font-style="normal" fill="#333333" text-anchor="middle" dominant-baseline="central">' . htmlspecialchars($first_char, ENT_QUOTES, 'UTF-8') . '</text>' .
        '</svg>';

    return 'data:image/svg+xml;base64,' . base64_encode($svg_content);
}

function get_article_id()
{
    // 1. 从GET参数获取
    if (isset($_GET['id']) && is_numeric($_GET['id'])) return intval($_GET['id']);
    if (isset($_GET['aid']) && is_numeric($_GET['aid'])) return intval($_GET['aid']);

    // 2. 从URL路径匹配标准格式（如 post_id123.html, article_id123.html）
    if (isset($_SERVER['REQUEST_URI'])) {
        if (preg_match('/(?:post|article|goods|list|cat|category|tag|member|user)_id(\d+)\.html/i', $_SERVER['REQUEST_URI'], $matches)) {
            return intval($matches[1]);
        }
    }

    // 3. 从自定义URL获取（查询数据库）
    $current_uri = trim($_SERVER['REQUEST_URI'] ?? '', '/');
    $current_path = explode('?', $current_uri)[0];

    if (!empty($current_path)) {
        $db = get_db_connection();
        if ($db) {
            // 查询文章表
            $row = execute_db_query($db, "SELECT id FROM posts WHERE custom_url = ? AND status = 1 LIMIT 1", [$current_path], 'row');
            if ($row) return intval($row['id']);

            // 查询商品表
            $row = execute_db_query($db, "SELECT id FROM goods WHERE custom_url = ? LIMIT 1", [$current_path], 'row');
            if ($row) return intval($row['id']);

            // 查询分类表
            $row = execute_db_query($db, "SELECT id FROM categories WHERE custom_url = ? LIMIT 1", [$current_path], 'row');
            if ($row) return intval($row['id']);
        }
    }

    return 0;
}

function get_category_id()
{
    if (isset($_GET['software_id']) && is_numeric($_GET['software_id'])) return intval($_GET['software_id']);
    if (isset($_GET['category_id']) && is_numeric($_GET['category_id'])) return intval($_GET['category_id']);
    if (isset($_GET['cid']) && is_numeric($_GET['cid'])) return intval($_GET['cid']);
    if (isset($_GET['cat_id']) && is_numeric($_GET['cat_id'])) return intval($_GET['cat_id']);
    if (isset($_GET['id']) && is_numeric($_GET['id'])) return intval($_GET['id']);
    if (isset($_SERVER['REQUEST_URI']) && preg_match('/list_id(\d+)\.html/', $_SERVER['REQUEST_URI'], $matches)) return intval($matches[1]);
    $catgroup_id = get_catgroup_id();
    if ($catgroup_id > 0) {
        $db = get_db_connection();
        if ($db && is_valid_db_connection($db)) {
            static $catgroup_category_cache = array();
            if (isset($catgroup_category_cache[$catgroup_id])) return intval($catgroup_category_cache[$catgroup_id]);
            $category_table = findTable($db, array('categories', 'qkcms_category', 'board', 'article_category', 'news_category', 'blog_category', 'post_category', 'site_category', 'content_category', 'product_category', 'cat', 'cats'));
            if ($category_table) {
                $id_field = findField($db, $category_table, array('id', 'category_id'));
                $parent_field = findField($db, $category_table, array('parent_id', 'pid'));
                $group_field = findField($db, $category_table, array('archiveid', 'catgroup_id'));
                if ($id_field && $group_field) {
                    $sort_field = findField($db, $category_table, array('sort_order', 'sort'));
                    $where = array("{$group_field} = " . intval($catgroup_id));
                    if ($parent_field) $where[] = "{$parent_field} = 0";
                    $sql = "SELECT {$id_field} AS id FROM {$category_table} WHERE " . implode(' AND ', $where);
                    if ($sort_field) $sql .= " ORDER BY {$sort_field} ASC, {$id_field} ASC";
                    else $sql .= " ORDER BY {$id_field} ASC";
                    $sql .= " LIMIT 1";
                    $row = execute_db_query($db, $sql, array(), 'row');
                    if ($row && isset($row['id']) && intval($row['id']) > 0) {
                        $catgroup_category_cache[$catgroup_id] = intval($row['id']);
                        return intval($row['id']);
                    }
                }
            }
            $catgroup_category_cache[$catgroup_id] = 0;
        }
    }
    $current_uri = trim($_SERVER['REQUEST_URI'] ?? '', '/');
    $current_path = explode('?', $current_uri)[0];
    if ($current_path !== '') {
        $db = get_db_connection();
        if ($db && is_valid_db_connection($db)) {
            static $category_custom_url_cache = array();
            if (isset($category_custom_url_cache[$current_path])) return intval($category_custom_url_cache[$current_path]);
            $category_table = findTable($db, array('categories', 'qkcms_category', 'board', 'article_category', 'news_category', 'blog_category', 'post_category', 'site_category', 'content_category', 'product_category', 'cat', 'cats'));
            if ($category_table) {
                $id_field = findField($db, $category_table, array('id', 'category_id'));
                $custom_url_field = findField($db, $category_table, array('custom_url', 'customurl', 'url_alias', 'alias'));
                if ($id_field && $custom_url_field) {
                    $row = execute_db_query($db, "SELECT {$id_field} AS id FROM {$category_table} WHERE {$custom_url_field} = ? LIMIT 1", array($current_path), 'row');
                    if ($row && isset($row['id']) && intval($row['id']) > 0) {
                        $category_custom_url_cache[$current_path] = intval($row['id']);
                        return intval($row['id']);
                    }
                }
            }
            $category_custom_url_cache[$current_path] = 0;
        }
    }
    return 0;
}

function get_catgroup_id()
{
    if (isset($_GET['catgroup_id']) && is_numeric($_GET['catgroup_id'])) return intval($_GET['catgroup_id']);
    if (isset($_GET['archiveid']) && is_numeric($_GET['archiveid'])) return intval($_GET['archiveid']);
    if (isset($_GET['group_id']) && is_numeric($_GET['group_id'])) return intval($_GET['group_id']);
    if (isset($_GET['cgid']) && is_numeric($_GET['cgid'])) return intval($_GET['cgid']);
    if (isset($_SERVER['REQUEST_URI']) && preg_match('/cg_id(\d+)\.html/i', $_SERVER['REQUEST_URI'], $matches)) return intval($matches[1]);

    $current_uri = trim($_SERVER['REQUEST_URI'] ?? '', '/');
    $current_path = explode('?', $current_uri)[0];
    if ($current_path !== '') {
        $db = get_db_connection();
        if ($db && is_valid_db_connection($db)) {
            $catgroup_table = findTable($db, ['catgroup', 'qkcms_catgroup']);
            if ($catgroup_table) {
                $sql = "SELECT id FROM {$catgroup_table} WHERE custom_url = ? LIMIT 1";
                $row = execute_db_query($db, $sql, [$current_path], 'row');
                if ($row && isset($row['id'])) return intval($row['id']);
            }
        }
    }
    return 0;
}

function get_keywords_count($db, $table)
{
    $sql = "SELECT COUNT(DISTINCT keywords) FROM $table WHERE keywords IS NOT NULL AND keywords != ''";
    return execute_db_query($db, $sql, array(), 'single');
}

function get_total_order_count($db, $article_id)
{
    return getOrderCount();
}

function get_join_value($db, $mapping, $tag, $article_id)
{
    $table = $mapping['table'];
    $field = $mapping['field'];
    $join = $mapping['join'];
    return execute_db_query($db, "SELECT $table.$field FROM $table JOIN posts ON $join WHERE posts.id = ? LIMIT 1", array($article_id), 'single');
}

function get_url_value($db, $mapping, $tag, $article_id)
{
    $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
    $domain = $protocol . '://' . $_SERVER['HTTP_HOST'];

    if ($tag === 'cat_url') {
        $cat_id = get_category_id();
        if ($cat_id > 0) return $domain . '/list_id' . $cat_id . '.php';
        try {
            $stmt = $db->prepare("SELECT category_id FROM posts WHERE id = ? LIMIT 1");
            $stmt->bind_param("i", $article_id);
            $stmt->execute();
            $result = $stmt->get_result();
            $row = $result->fetch_assoc();
            $category_id = $row ? $row['category_id'] : 0;
            $stmt->close();
            if ($category_id > 0) return $domain . '/list_id' . $category_id . '.php';
        } catch (Exception $e) {
        }
    }

    if (isset($mapping['join']) && !empty($mapping['join'])) {
        $table = $mapping['table'];
        $field = $mapping['field'];
        $join = $mapping['join'];
        $target_id = execute_db_query($db, "SELECT $table.$field FROM $table JOIN posts ON $join WHERE posts.id = ? LIMIT 1", array($article_id), 'single');
        if (!empty($target_id) && $table === 'categories') return $domain . '/list_id' . $target_id . '.php';
    }

    if ($tag === 'posts_url' || $mapping['table'] === 'posts') {
        try {
            $stmt = $db->prepare("SELECT custom_url FROM posts WHERE id = ? LIMIT 1");
            $stmt->bind_param("i", $article_id);
            $stmt->execute();
            $result = $stmt->get_result();
            $row = $result->fetch_assoc();
            $custom_url = $row ? $row['custom_url'] : '';
            $stmt->close();
            if ($custom_url) {
                return $domain . '/' . $custom_url;
            }
        } catch (Exception $e) {
        }
        return $domain . '/post_id' . $article_id . (qkcms_is_home_request_for_article_url() ? '.html' : '.php');
    }
    return '';
}

function get_post_comment_count($db, $article_id)
{
    $query = "SELECT COUNT(*) as count FROM comments WHERE post_id = ?";
    return execute_db_query($db, $query, array($article_id), 'count') ?: 0;
}

function qkcms_is_home_request_for_article_url()
{
    $request_uri = isset($_SERVER['REQUEST_URI']) ? (string)$_SERVER['REQUEST_URI'] : '/';
    $request_path = parse_url($request_uri, PHP_URL_PATH);
    if (!is_string($request_path) || $request_path === '') $request_path = '/';
    return $request_path === '/' || $request_path === '/index.html' || $request_path === '/index.php';
}

function get_post_share_count($db, $article_id)
{
    if ($article_id > 0) return execute_db_query($db, "SELECT COUNT(*) as count FROM shares WHERE content_type = 'post' AND content_id = ?", array($article_id), 'count');
    return 0;
}

function get_post_like_count($db, $article_id)
{
    if ($article_id > 0) return execute_db_query($db, "SELECT COUNT(*) as count FROM likes WHERE content_type = 'post' AND content_id = ?", array($article_id), 'count');
    return 0;
}

function get_post_report_count($db, $article_id)
{
    $use_id = $article_id;
    if ($use_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $use_id = intval($_GET['id']);
    if ($use_id <= 0) return 0;
    try {
        $query = "SELECT COUNT(*) as count FROM reports WHERE post_id = ?";
        $result = execute_db_query($db, $query, array($use_id), 'count');
        if (!$result || $result == 0) {
            $query = "SELECT COUNT(*) as count FROM reports WHERE content_type = 'post' AND content_id = ?";
            $result = execute_db_query($db, $query, [$use_id], 'count');
        }
        return (int)$result;
    } catch (Exception $e) {
        return 0;
    }
}

function get_post_author($db, $article_id)
{
    $use_id = $article_id;
    if ($use_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $use_id = intval($_GET['id']);
    if ($use_id <= 0) return null;
    try {
        $query = "SELECT u.username FROM users u JOIN posts p ON u.id = p.user_id WHERE p.id = ?";
        $authorName = execute_db_query($db, $query, array($use_id), 'single');
        if (!$authorName) {
            $user_id_query = "SELECT user_id FROM posts WHERE id = ?";
            $user_id = execute_db_query($db, $user_id_query, array($use_id), 'single');
            if ($user_id) {
                $username_query = "SELECT username FROM users WHERE id = ?";
                $authorName = execute_db_query($db, $username_query, array($user_id), 'single');
            }
        }
        return $authorName ?: null;
    } catch (Exception $e) {
        return null;
    }
}

function get_post_author_id($db, $article_id)
{
    $use_id = $article_id;
    if ($use_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $use_id = intval($_GET['id']);
    if ($use_id <= 0) return null;
    try {
        $user_id_query = "SELECT user_id FROM posts WHERE id = ?";
        return execute_db_query($db, $user_id_query, array($use_id), 'single');
    } catch (Exception $e) {
        return null;
    }
}

function get_post_author_avatar($db, $article_id)
{
    $use_id = $article_id;
    if ($use_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $use_id = intval($_GET['id']);
    if ($use_id <= 0) return null;
    try {
        $user_id_query = "SELECT user_id FROM posts WHERE id = ?";
        $user_id = execute_db_query($db, $user_id_query, [$use_id], 'single');
        if (!$user_id) return null;
        $avatar_query = "SELECT avatar FROM users WHERE id = ?";
        $avatar = execute_db_query($db, $avatar_query, array($user_id), 'single');
        return ($avatar && !empty($avatar)) ? $avatar : DEFAULT_AVATAR_PATH;
    } catch (Exception $e) {
        return DEFAULT_AVATAR_PATH;
    }
}

function qkcms_parse_keywords($keywords, $limit = 0)
{
    $keywords = trim((string)$keywords);
    if ($keywords === '') return array();
    $items = preg_split('/[\s,，、|]+/u', str_replace('，', ',', $keywords), -1, PREG_SPLIT_NO_EMPTY);
    $result = array();
    foreach ($items as $item) {
        $tag = trim($item);
        if ($tag !== '') $result[] = $tag;
    }
    if ($limit > 0) $result = array_slice($result, 0, $limit);
    return $result;
}

function get_posts_keywords_with_links($db, $article_id)
{
    $use_id = $article_id;
    if ($use_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $use_id = intval($_GET['id']);
    if ($use_id <= 0) return '';
    try {
        $query = "SELECT keywords FROM posts WHERE id = ? LIMIT 1";
        $keywords = execute_db_query($db, $query, array($use_id), 'single');
        $items = qkcms_parse_keywords($keywords, 0);
        if (empty($items)) return '';
        $html = '';
        foreach ($items as $item) {
            $html .= '<a href="/tag.html?tag=' . urlencode($item) . '">' . htmlspecialchars($item) . '</a>';
        }
        return $html;
    } catch (Exception $e) {
        return '';
    }
}

function get_cat_keywords($db, $article_id)
{
    $cat_id = get_category_id();
    if ($cat_id <= 0) return '';
    try {
        $query = "SELECT keywords FROM categories WHERE id = ? LIMIT 1";
        $keywords = execute_db_query($db, $query, array($cat_id), 'single');
        $items = qkcms_parse_keywords($keywords, 5);
        return implode(',', $items);
    } catch (Exception $e) {
        return '';
    }
}

function get_cat_keywords_with_links($db, $article_id)
{
    $cat_id = get_category_id();
    if ($cat_id <= 0) return '';
    try {
        $query = "SELECT keywords FROM categories WHERE id = ? LIMIT 1";
        $keywords = execute_db_query($db, $query, array($cat_id), 'single');
        $items = qkcms_parse_keywords($keywords, 5);
        if (empty($items)) return '';
        $html = '';
        foreach ($items as $item) {
            $html .= '<a href="/tag.html?tag=' . urlencode($item) . '" class="tag3">' . htmlspecialchars($item) . '</a>';
        }
        return $html;
    } catch (Exception $e) {
        return '';
    }
}

function get_cat_seo_title($db, $article_id)
{
    $cat_id = get_category_id();
    if ($cat_id <= 0) return '';
    try {
        $query = "SELECT seo_title FROM categories WHERE id = ? LIMIT 1";
        $seo_title = execute_db_query($db, $query, array($cat_id), 'single');
        return $seo_title !== null ? trim((string)$seo_title) : '';
    } catch (Exception $e) {
        return '';
    }
}

function get_cat_title($db, $article_id)
{
    $cat_id = get_category_id();
    if ($cat_id <= 0) return '';
    try {
        $query = "SELECT seo_title, name FROM categories WHERE id = ? LIMIT 1";
        $row = execute_db_query($db, $query, array($cat_id), 'row');
        if (!$row || !is_array($row)) return '';
        $seo_title = isset($row['seo_title']) ? trim((string)$row['seo_title']) : '';
        if ($seo_title !== '') return $seo_title;
        return isset($row['name']) ? (string)$row['name'] : '';
    } catch (Exception $e) {
        return '';
    }
}

function get_current_position($db, $article_id = 0)
{
    $position_parts = array();
    $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
    $domain = $protocol . '://' . $_SERVER['HTTP_HOST'];
    array_push($position_parts, '<a href="' . $domain . '" title="首页">首页</a>');

    $request_uri = $_SERVER['REQUEST_URI'];
    $script_name = basename($_SERVER['SCRIPT_NAME']);

    if ($script_name === 'search.php' || isset($_GET['keyword'])) {
        $position_parts[] = '<span>搜索结果</span>';
    } else if (
        $script_name === 'post_id.php' ||
        (isset($_GET['id']) && is_numeric($_GET['id']) && !isset($_GET['category_id']) && !isset($_GET['cat_id']))
    ) {
        $use_id = $article_id > 0 ? $article_id : (isset($_GET['id']) ? intval($_GET['id']) : 0);
        if ($use_id > 0) {
            $category_id = execute_db_query($db, "SELECT category_id FROM posts WHERE id = ?", array($use_id), 'single');
            if ($category_id && $category_id > 0) {
                $category_name = execute_db_query($db, "SELECT name FROM categories WHERE id = ?", array($category_id), 'single');
                if ($category_name) {
                    $position_parts[] = '<a href="' . $domain . '/list_id' . $category_id . '.html" title="' . htmlspecialchars($category_name) . '">' . htmlspecialchars($category_name) . '</a>';
                }
            }
            $article_title = execute_db_query($db, "SELECT title FROM posts WHERE id = ?", array($use_id), 'single');
            if ($article_title) {
                $position_parts[] = '<span>' . htmlspecialchars($article_title) . '</span>';
            }
        }
    } else {
        $cat_id = 0;
        if (preg_match('/list_id(\d+)\.html/', $request_uri, $matches)) {
            $cat_id = intval($matches[1]);
        } else if (preg_match('/^list_id(\d+)\.html$/', $script_name, $matches)) {
            $cat_id = intval($matches[1]);
        } else if (isset($_GET['category_id']) && is_numeric($_GET['category_id'])) {
            $cat_id = intval($_GET['category_id']);
        } else if (isset($_GET['cat_id']) && is_numeric($_GET['cat_id'])) {
            $cat_id = intval($_GET['cat_id']);
        } else if (isset($_GET['cid']) && is_numeric($_GET['cid'])) {
            $cat_id = intval($_GET['cid']);
        } else if (isset($_GET['id']) && is_numeric($_GET['id'])) {
            $cat_id = intval($_GET['id']);
        }

        if ($cat_id > 0) {
            $category_name = execute_db_query($db, "SELECT name FROM categories WHERE id = ?", array($cat_id), 'single');
            if ($category_name) {
                $position_parts[] = '<a href="' . $domain . '/list_id' . $cat_id . '.html" title="' . htmlspecialchars($category_name) . '">' . htmlspecialchars($category_name) . '</a>';
            } else {
                $position_parts[] = '<span title="未找到分类信息，ID: ' . $cat_id . '">分类列表</span>';
            }
        }
    }

    return implode(' _ ', $position_parts);
}

function get_database_result($tag, $db, $article_id = 0)
{
    static $database_result_cache = array();
    // 处理插件配置参数标签，格式：plugin_插件ID_config_参数名
    if (strpos($tag, 'plugin_') === 0 && strpos($tag, '_config_') !== false) {
        $parts = explode('_', $tag);
        if (count($parts) >= 4) {
            $plugin_id = intval($parts[1]);
            $param_name = implode('_', array_slice($parts, 3));

            // 引入common.php中的get_plugin_by_id函数
            if (!function_exists('get_plugin_by_id')) {
                require_once __DIR__ . '/../common.php';
            }

            $plugin = get_plugin_by_id($plugin_id);
            if ($plugin && isset($plugin['config_params'][$param_name])) {
                return $plugin['config_params'][$param_name];
            }
        }
        return null;
    }

    $db_connected = is_valid_db_connection($db);
    if (!$db_connected) {
        $user_tags = ['user_nickname', 'user_avatar', 'user_levels', 'user_register_time', 'user_created_at', 'user_status', 'user_id', 'user_points', 'user_email'];
        if (in_array($tag, $user_tags)) return get_user_info_from_session($tag);
        return null;
    }

    $is_post_tag = strpos($tag, 'posts_') === 0;
    if ($is_post_tag && $article_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $article_id = intval($_GET['id']);
    if ($is_post_tag && $article_id <= 0) return null;

    $request_uri = isset($_SERVER['REQUEST_URI']) ? (string)$_SERVER['REQUEST_URI'] : '';
    $session_user_id = isset($_SESSION['user_id']) ? intval($_SESSION['user_id']) : 0;
    $db_key = is_object($db) ? spl_object_hash($db) : 'default';
    $cache_key = $db_key . '|' . $tag . '|' . intval($article_id) . '|' . $session_user_id . '|' . md5($request_uri);
    if (array_key_exists($cache_key, $database_result_cache)) {
        return $database_result_cache[$cache_key];
    }

    $user_tags = ['user_nickname', 'user_avatar', 'user_levels', 'user_register_time', 'user_created_at', 'user_status', 'user_id', 'user_points', 'user_email'];
    if (in_array($tag, $user_tags)) return get_user_info($db, $tag);

    global $tag_mapping;
    if (!isset($tag_mapping[$tag])) {
        $database_result_cache[$cache_key] = null;
        return null;
    }

    $mapping = $tag_mapping[$tag];
    try {
        switch ($mapping['type']) {
            case 'special':
                if ($tag === 'posts_id') {
                    if ($article_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $article_id = intval($_GET['id']);
                    $database_result_cache[$cache_key] = $article_id > 0 ? $article_id : null;
                    return $database_result_cache[$cache_key];
                }
                $database_result_cache[$cache_key] = null;
                return null;
            case 'session':
                $database_result_cache[$cache_key] = get_user_info_from_session($tag);
                return $database_result_cache[$cache_key];
            case 'field':
                $database_result_cache[$cache_key] = get_field_value($db, $mapping, $tag, $article_id);
                return $database_result_cache[$cache_key];
            case 'count':
                $database_result_cache[$cache_key] = get_count_value($db, $mapping, $tag, $article_id);
                return $database_result_cache[$cache_key];
            case 'keywords_count':
                $database_result_cache[$cache_key] = get_keywords_count($db, $mapping['table']);
                return $database_result_cache[$cache_key];
            case 'join':
                $database_result_cache[$cache_key] = get_join_value($db, $mapping, $tag, $article_id);
                return $database_result_cache[$cache_key];
            case 'url':
                $database_result_cache[$cache_key] = get_url_value($db, $mapping, $tag, $article_id);
                return $database_result_cache[$cache_key];
            case 'custom':
                if (isset($mapping['handler']) && function_exists($mapping['handler'])) {
                    $database_result_cache[$cache_key] = call_user_func($mapping['handler'], $db, $article_id);
                    return $database_result_cache[$cache_key];
                }
                $database_result_cache[$cache_key] = null;
                return null;
            default:
                $database_result_cache[$cache_key] = null;
                return null;
        }
    } catch (Exception $e) {
        $database_result_cache[$cache_key] = null;
        return null;
    }
}

if (!function_exists('formatTime')) {
    function formatTime($time)
    {
        if (empty($time)) return "刚刚";
        if (is_numeric($time)) $timestamp = $time;
        else {
            $timestamp = strtotime($time);
            if ($timestamp === false) return $time;
        }
        $diff = time() - $timestamp;
        if ($diff < 60) return "刚刚";
        elseif ($diff < 3600) return floor($diff / 60) . "分钟前";
        elseif ($diff < 86400) return floor($diff / 3600) . "小时前";
        elseif ($diff < 604800) return floor($diff / 86400) . "天前";
        else return date('Y-m-d', $timestamp);
    }
}

function findTable($db, $tables)
{
    static $table_cache = array();
    $db_key = is_object($db) ? spl_object_hash($db) : 'default';
    $cache_key = $db_key . '|' . implode('|', $tables);
    if (array_key_exists($cache_key, $table_cache)) {
        return $table_cache[$cache_key];
    }
    foreach ($tables as $table) {
        $check_sql = "SHOW TABLES LIKE '$table'";
        $result = $db->query($check_sql);
        if ($result && $result->num_rows > 0) {
            $result->free();
            $table_cache[$cache_key] = $table;
            return $table;
        }
    }
    $table_cache[$cache_key] = null;
    return null;
}

function findField($db, $table, $fields)
{
    static $field_cache = array();
    if (!$table) return null;
    $db_key = is_object($db) ? spl_object_hash($db) : 'default';
    $cache_key = $db_key . '|' . $table . '|' . implode('|', $fields);
    if (array_key_exists($cache_key, $field_cache)) {
        return $field_cache[$cache_key];
    }
    $desc_result = $db->query("DESCRIBE $table");
    if ($desc_result) {
        while ($field = $desc_result->fetch_assoc()) {
            if (in_array($field['Field'], $fields)) {
                $desc_result->free();
                $field_cache[$cache_key] = $field['Field'];
                return $field['Field'];
            }
        }
        $desc_result->free();
    }
    $field_cache[$cache_key] = null;
    return null;
}

function getFieldMap($db, $table, $field_patterns)
{
    static $field_map_cache = array();
    if (!$table) return array();
    $db_key = is_object($db) ? spl_object_hash($db) : 'default';
    $cache_key = $db_key . '|' . $table . '|' . md5(serialize($field_patterns));
    if (isset($field_map_cache[$cache_key])) {
        return $field_map_cache[$cache_key];
    }
    $field_map = array();
    $desc_result = $db->query("DESCRIBE $table");
    if ($desc_result) {
        while ($field = $desc_result->fetch_assoc()) {
            foreach ($field_patterns as $key => $patterns) {
                if (in_array($field['Field'], $patterns)) $field_map[$key] = $field['Field'];
            }
        }
        $desc_result->free();
    }
    $field_map_cache[$cache_key] = $field_map;
    return $field_map;
}

function qkcms_unique_positive_ints($values)
{
    $unique = array();
    foreach ((array)$values as $value) {
        $int_value = intval($value);
        if ($int_value > 0) $unique[$int_value] = $int_value;
    }
    return array_values($unique);
}

function qkcms_fetch_rows_indexed_by_id($db, $table, $id_field, $ids, $select_fields)
{
    $rows = array();
    $ids = qkcms_unique_positive_ints($ids);
    if (!$table || !$id_field || empty($ids)) return $rows;

    $safe_select_fields = array();
    foreach ((array)$select_fields as $field) {
        if ($field && !in_array($field, $safe_select_fields, true)) {
            $safe_select_fields[] = $field;
        }
    }
    if (!in_array($id_field, $safe_select_fields, true)) {
        array_unshift($safe_select_fields, $id_field);
    }

    $sql = "SELECT " . implode(', ', $safe_select_fields) . " FROM $table WHERE $id_field IN (" . implode(',', $ids) . ")";
    $result = $db->query($sql);
    if ($result) {
        while ($row = $result->fetch_assoc()) {
            if (isset($row[$id_field])) {
                $rows[intval($row[$id_field])] = $row;
            }
        }
        $result->free();
    }
    return $rows;
}

function qkcms_fetch_group_count_map($db, $table, $group_field, $ids)
{
    $count_map = array();
    $ids = qkcms_unique_positive_ints($ids);
    if (!$table || !$group_field || empty($ids)) return $count_map;

    $sql = "SELECT $group_field AS group_id, COUNT(*) AS count_total FROM $table WHERE $group_field IN (" . implode(',', $ids) . ") GROUP BY $group_field";
    $result = $db->query($sql);
    if ($result) {
        while ($row = $result->fetch_assoc()) {
            $count_map[intval($row['group_id'])] = intval($row['count_total']);
        }
        $result->free();
    }
    return $count_map;
}

function qkcms_request_result_cache($bucket, $db, $params, $value = null, $has_value = false)
{
    static $request_cache = array();
    $db_key = is_object($db) ? spl_object_hash($db) : 'default';
    $cache_key = $db_key . '|' . $bucket . '|' . md5(serialize($params));
    if ($has_value) {
        $request_cache[$cache_key] = $value;
        return $value;
    }
    return array_key_exists($cache_key, $request_cache) ? $request_cache[$cache_key] : null;
}

function qkcms_build_article_preload($articles, $field_map, $db, $user_table = null)
{
    $preload = array(
        'users' => array(),
        'categories' => array(),
        'comments' => array(),
        'user_fields' => array(),
        'category_fields' => array()
    );
    if (empty($articles) || !$db || !is_valid_db_connection($db)) return $preload;

    $article_ids = array();
    $user_ids = array();
    $category_ids = array();

    foreach ($articles as $article) {
        if (isset($field_map['id']) && isset($article[$field_map['id']])) {
            $article_ids[] = $article[$field_map['id']];
        }
        if (isset($field_map['user_id']) && isset($article[$field_map['user_id']])) {
            $user_ids[] = $article[$field_map['user_id']];
        }
        if (isset($field_map['category_id']) && isset($article[$field_map['category_id']])) {
            $category_ids[] = $article[$field_map['category_id']];
        }
    }

    $user_ids = qkcms_unique_positive_ints($user_ids);
    $category_ids = qkcms_unique_positive_ints($category_ids);
    $article_ids = qkcms_unique_positive_ints($article_ids);

    if ($user_table && !empty($user_ids)) {
        $user_id_field = findField($db, $user_table, array('id', 'user_id')) ?: 'id';
        $name_field = findField($db, $user_table, array('nickname', 'username', 'user_name', 'loginname', 'name'));
        $avatar_field = findField($db, $user_table, array('avatar', 'user_avatar'));
        $level_field = findField($db, $user_table, array('levels_id'));
        $preload['user_fields'] = array(
            'id' => $user_id_field,
            'name' => $name_field,
            'avatar' => $avatar_field,
            'level' => $level_field
        );
        $user_select_fields = array($user_id_field);
        if ($name_field) $user_select_fields[] = $name_field;
        if ($avatar_field) $user_select_fields[] = $avatar_field;
        if ($level_field) $user_select_fields[] = $level_field;
        $preload['users'] = qkcms_fetch_rows_indexed_by_id($db, $user_table, $user_id_field, $user_ids, $user_select_fields);
    }

    if (!empty($category_ids)) {
        $category_table = findTable($db, array('categories', 'qkcms_category', 'article_category', 'cat', 'cats'));
        if ($category_table) {
            $category_id_field = findField($db, $category_table, array('id', 'category_id')) ?: 'id';
            $category_name_field = findField($db, $category_table, array('name', 'category_name', 'title'));
            $category_custom_url_field = findField($db, $category_table, array('custom_url', 'customurl', 'url_alias', 'alias'));
            $preload['category_fields'] = array(
                'id' => $category_id_field,
                'name' => $category_name_field,
                'custom_url' => $category_custom_url_field
            );
            $category_select_fields = array($category_id_field);
            if ($category_name_field) $category_select_fields[] = $category_name_field;
            if ($category_custom_url_field) $category_select_fields[] = $category_custom_url_field;
            $preload['categories'] = qkcms_fetch_rows_indexed_by_id($db, $category_table, $category_id_field, $category_ids, $category_select_fields);
        }
    }

    if (!isset($field_map['comments']) && !empty($article_ids)) {
        $comment_table = findTable($db, array('comments', 'qkcms_comment', 'qkcms_comment', 'article_comment'));
        if ($comment_table) {
            $comment_article_field = findField($db, $comment_table, array('article_id', 'post_id', 'content_id'));
            if ($comment_article_field) {
                $preload['comments'] = qkcms_fetch_group_count_map($db, $comment_table, $comment_article_field, $article_ids);
            }
        }
    }

    return $preload;
}

function qkcms_get_user_statistics_map($user_ids, $db)
{
    $user_ids = qkcms_unique_positive_ints($user_ids);
    $stats_map = array();
    foreach ($user_ids as $user_id) {
        $stats_map[$user_id] = array(
            'like_count' => 0,
            'report_count' => 0,
            'share_count' => 0,
            'post_count' => 0
        );
    }
    if (empty($user_ids) || !$db || !is_valid_db_connection($db)) return $stats_map;

    $post_table = findTable($db, array('posts', 'articles', 'qkcms_article', 'qkcms_article', 'content'));
    if ($post_table) {
        $field_map = getFieldMap($db, $post_table, array('author_id' => array('author_id', 'user_id', 'post_author')));
        if (isset($field_map['author_id'])) {
            $count_map = qkcms_fetch_group_count_map($db, $post_table, $field_map['author_id'], $user_ids);
            foreach ($count_map as $user_id => $count) {
                $stats_map[$user_id]['post_count'] = $count;
            }
        }
    }

    $likes_table = findTable($db, array('likes'));
    if ($likes_table) {
        $likes_field_map = getFieldMap($db, $likes_table, array('user_id' => array('user_id', 'uid', 'userid')));
        if (isset($likes_field_map['user_id'])) {
            $count_map = qkcms_fetch_group_count_map($db, $likes_table, $likes_field_map['user_id'], $user_ids);
            foreach ($count_map as $user_id => $count) {
                $stats_map[$user_id]['like_count'] = $count;
            }
        }
    }

    $reports_table = findTable($db, array('reports'));
    if ($reports_table) {
        $reports_field_map = getFieldMap($db, $reports_table, array('user_id' => array('user_id', 'uid', 'userid', 'reporter_id')));
        if (isset($reports_field_map['user_id'])) {
            $count_map = qkcms_fetch_group_count_map($db, $reports_table, $reports_field_map['user_id'], $user_ids);
            foreach ($count_map as $user_id => $count) {
                $stats_map[$user_id]['report_count'] = $count;
            }
        }
    }

    return $stats_map;
}

function qkcms_get_settings_row($db)
{
    static $settings_cache = array();
    $db_key = is_object($db) ? spl_object_hash($db) : 'default';
    if (array_key_exists($db_key, $settings_cache)) {
        return $settings_cache[$db_key];
    }

    $settings_row = null;
    if ($db && is_valid_db_connection($db)) {
        $result = $db->query("SELECT * FROM settings LIMIT 1");
        if ($result) {
            $settings_row = $result->fetch_assoc();
            $result->free();
        }
    }
    $settings_cache[$db_key] = $settings_row;
    return $settings_row;
}

function qkcms_get_navigation_children_map($db, $nav_table, $parent_ids, $title_field, $url_field, $sort_field, $target_field, $status_field, $parent_field)
{
    $children_map = array();
    $parent_ids = qkcms_unique_positive_ints($parent_ids);
    if (!$nav_table || !$parent_field || empty($parent_ids)) return $children_map;

    $sql = "SELECT id, $title_field, $url_field, $sort_field";
    if ($target_field) $sql .= ", $target_field";
    if ($status_field) $sql .= ", $status_field";
    $sql .= ", $parent_field FROM $nav_table WHERE $parent_field IN (" . implode(',', $parent_ids) . ")";
    if ($status_field) $sql .= " AND $status_field = 1";
    $sql .= " ORDER BY $parent_field ASC, $sort_field ASC, id ASC";

    $result = $db->query($sql);
    if ($result) {
        while ($item = $result->fetch_assoc()) {
            $parent_id = intval($item[$parent_field]);
            if (!isset($children_map[$parent_id])) $children_map[$parent_id] = array();
            $target_value = '_self';
            if ($target_field && isset($item[$target_field]) && !empty($item[$target_field])) {
                $target_value = $item[$target_field];
            }
            $children_map[$parent_id][] = array(
                'id' => $item['id'],
                'title' => $item[$title_field],
                'url' => $item[$url_field],
                'target' => $target_value
            );
        }
        $result->free();
    }
    return $children_map;
}

function qkcms_get_catgroup_categories_batch($db, $category_table, $field_map, $show_all, $sort, $keyword, $exclude_ids, $min_count = 0)
{
    static $batch_cache = array();
    $db_key = is_object($db) ? spl_object_hash($db) : 'default';
    $cache_key = $db_key . '|' . $category_table . '|' . md5(serialize(array(
        'show_all' => intval($show_all),
        'sort' => intval($sort),
        'keyword' => (string)$keyword,
        'exclude_ids' => array_values((array)$exclude_ids),
        'field_map' => $field_map
    )));
    if (isset($batch_cache[$cache_key])) {
        return $batch_cache[$cache_key];
    }
    $grouped = array();
    if (!$category_table || !isset($field_map['id']) || !isset($field_map['catgroup_id'])) {
        $batch_cache[$cache_key] = $grouped;
        return $grouped;
    }

    $select_fields = array($field_map['id']);
    foreach (array('name', 'description', 'keywords', 'parent_id', 'thumbnail', 'custom_url', 'display', 'catgroup_id', 'virtual_top', 'virtual_top_anchor') as $field) {
        if (isset($field_map[$field]) && !in_array($field_map[$field], $select_fields, true)) {
            $select_fields[] = $field_map[$field];
        }
    }

    $conditions = array();
    $display_field = findField($db, $category_table, array('display', 'show_home', 'home_display'));
    if ($display_field && intval($show_all) === 2) $conditions[] = "$display_field = 1";
    if (isset($field_map['parent_id'])) $conditions[] = $field_map['parent_id'] . " = 0";
    if (isset($field_map['virtual_top'])) $conditions[] = $field_map['virtual_top'] . " = 0";
    $conditions[] = $field_map['catgroup_id'] . " > 0";
    if (!empty($exclude_ids)) $conditions[] = $field_map['id'] . " NOT IN (" . implode(',', array_map('intval', $exclude_ids)) . ")";

    if ($keyword !== '') {
        $keyword_condition = array();
        $keyword_safe = $db->real_escape_string($keyword);
        if (isset($field_map['name'])) $keyword_condition[] = $field_map['name'] . " LIKE '%$keyword_safe%'";
        if (isset($field_map['description'])) $keyword_condition[] = $field_map['description'] . " LIKE '%$keyword_safe%'";
        if (isset($field_map['keywords'])) $keyword_condition[] = $field_map['keywords'] . " LIKE '%$keyword_safe%'";
        if (!empty($keyword_condition)) $conditions[] = "(" . implode(' OR ', $keyword_condition) . ")";
    }

    $sql = "SELECT " . implode(', ', $select_fields) . " FROM $category_table";
    if (!empty($conditions)) {
        $sql .= " WHERE " . implode(' AND ', $conditions);
    }

    $time_field = findField($db, $category_table, array('created_at', 'updated_at', 'add_time', 'create_time'));
    $sort_field = isset($field_map['sort_order']) ? $field_map['sort_order'] : $field_map['id'];
    $category_id_field = $field_map['id'];
    $article_table = findTable($db, array('articles', 'posts', 'news', 'blog_posts'));
    $article_cat_field = $article_table ? findField($db, $article_table, array('category_id', 'cat_id', 'cid')) : '';
    $article_time_field = $article_table ? findField($db, $article_table, array('created_at', 'updated_at', 'add_time', 'create_time', 'id')) : '';

    switch (intval($sort)) {
        case 1:
            if ($time_field) $sql .= " ORDER BY $time_field DESC, {$category_id_field} DESC";
            else $sql .= " ORDER BY {$category_id_field} DESC";
            break;
        case 2:
            if ($time_field) $sql .= " ORDER BY $time_field ASC, {$category_id_field} ASC";
            else $sql .= " ORDER BY {$category_id_field} ASC";
            break;
        case 3:
            $sql .= " ORDER BY $sort_field ASC, {$category_id_field} ASC";
            break;
        case 4:
            $sql .= " ORDER BY RAND()";
            break;
        case 6:
            if ($article_table && $article_cat_field && $article_time_field) {
                $latest_article_order = "(SELECT MAX($article_time_field) FROM $article_table WHERE $article_table.$article_cat_field = $category_table.$category_id_field)";
                $sql .= " ORDER BY COALESCE($latest_article_order, 0) DESC, $sort_field ASC, $category_id_field ASC";
            } elseif ($time_field) {
                $sql .= " ORDER BY $time_field DESC, $category_id_field DESC";
            } else {
                $sql .= " ORDER BY $category_id_field DESC";
            }
            break;
        default:
            if ($time_field) $sql .= " ORDER BY $time_field DESC, {$category_id_field} DESC";
            else $sql .= " ORDER BY {$category_id_field} DESC";
    }

    $result = $db->query($sql);
    $category_rows = array();
    if ($result) {
        while ($category = $result->fetch_assoc()) {
            $category_rows[] = $category;
        }
        $result->free();
    }

    $category_ids = array();
    foreach ($category_rows as $category) {
        $category_ids[] = $category[$category_id_field];
    }
    $count_map = ($article_table && $article_cat_field) ? qkcms_fetch_group_count_map($db, $article_table, $article_cat_field, $category_ids) : array();

    foreach ($category_rows as $category) {
        $category_id = intval($category[$category_id_field]);
        $group_id = intval($category[$field_map['catgroup_id']]);
        if ($group_id <= 0) continue;

        $count = isset($count_map[$category_id]) ? $count_map[$category_id] : 0;
        if (intval($min_count) > 0 && $count < intval($min_count)) continue;
        $category_name = isset($field_map['name']) ? $category[$field_map['name']] : '';
        $thumbnail = generate_category_svg_icon($category_name, $category_id);

        if (isset($field_map['thumbnail']) && isset($category[$field_map['thumbnail']])) {
            $db_thumbnail = trim($category[$field_map['thumbnail']]);
            if (!empty($db_thumbnail) && $db_thumbnail !== '/assets/images/moren.jpg' && $db_thumbnail !== DEFAULT_THUMBNAIL_PATH) {
                if (check_file_exists_or_default($db_thumbnail, '') !== '') $thumbnail = $db_thumbnail;
            }
        }

        $category_url = "/list_id$category_id.html";
        if (isset($field_map['custom_url']) && isset($category[$field_map['custom_url']])) {
            $cat_custom_url_val = trim((string)$category[$field_map['custom_url']]);
            if ($cat_custom_url_val !== '') {
                $category_url = '/' . ltrim($cat_custom_url_val, '/');
            }
        }

        if (!isset($grouped[$group_id])) $grouped[$group_id] = array();
        $grouped[$group_id][] = array(
            'id' => $category_id,
            'name' => $category_name,
            'description' => isset($field_map['description']) ? $category[$field_map['description']] : '',
            'keywords' => isset($field_map['keywords']) ? $category[$field_map['keywords']] : '',
            'parent_id' => isset($field_map['parent_id']) ? $category[$field_map['parent_id']] : 0,
            'url' => $category_url,
            'count' => $count,
            'thumbnail' => $thumbnail
        );
    }

    if (intval($sort) === 3) {
        foreach ($grouped as $group_id => $items) {
            usort($items, function ($a, $b) {
                return $b['count'] - $a['count'];
            });
            $grouped[$group_id] = $items;
        }
    }

    $batch_cache[$cache_key] = $grouped;
    return $grouped;
}

function qkcms_get_friendlink_data($params)
{
    $cached_result = qkcms_request_result_cache('friendlink', null, $params);
    if ($cached_result !== null) {
        return $cached_result;
    }

    $items = array();
    $row = isset($params['row']) ? max(0, intval($params['row'])) : 0;

    if (!function_exists('get_plugin_by_id')) {
        require_once dirname(__FILE__) . '/../common.php';
    }

    if (function_exists('get_plugin_by_id')) {
        $plugin = get_plugin_by_id(14);
        $friendlink_data = array();
        if ($plugin && isset($plugin['config_params']['memos'])) {
            $raw = $plugin['config_params']['memos'];
            if (is_string($raw) && $raw !== '') {
                $decoded = json_decode($raw, true);
                if (is_array($decoded)) {
                    $friendlink_data = $decoded;
                }
            } elseif (is_array($raw)) {
                $friendlink_data = $raw;
            }
        }

        if ($row > 0) {
            $friendlink_data = array_slice($friendlink_data, 0, $row);
        }

        foreach ($friendlink_data as $link) {
            if (!isset($link['siteName']) || !isset($link['siteUrl'])) {
                continue;
            }
            $title = trim((string)$link['siteName']);
            $url = trim((string)$link['siteUrl']);
            if ($title === '' || $url === '') {
                continue;
            }
            $items[] = array(
                'title' => htmlspecialchars($title, ENT_QUOTES, 'UTF-8'),
                'url' => htmlspecialchars($url, ENT_QUOTES, 'UTF-8')
            );
        }
    }

    return qkcms_request_result_cache('friendlink', null, $params, $items, true);
}

function parseAttributes($attributes)
{
    $params = array();

    // 先将属性字符串中的换行符和连续空格替换为单个空格，避免解析错误
    $attributes = str_replace(array("\n", "\r"), " ", $attributes);
    $attributes = preg_replace('/\s+/', ' ', $attributes);

    // 改进正则表达式，支持更复杂的属性值格式
    preg_match_all('/(\w+)\s*=\s*([\'"]([\s\S]*?)[\'"]|<?php[\s\S]*?\?>|<?=[\s\S]*?\?>|[^\s}]+)/', $attributes, $matches, PREG_SET_ORDER);
    foreach ($matches as $match) {
        $key = $match[1];
        $value = $match[2];

        // 处理带引号的属性值
        if (($value[0] === '\'' || $value[0] === '"') && $value[strlen($value) - 1] === $value[0]) {
            $value = substr($value, 1, -1);
        }
        // 处理PHP代码
        elseif (strpos($value, '<?php') === 0 || strpos($value, '<?=') === 0) {
            $value = '';
        }

        // 去除首尾空格
        $value = trim($value);

        $params[$key] = $value;
    }
    return $params;
}

function getCategoryName($db, $category_id)
{
    static $category_name_cache = array();
    $cache_key = intval($category_id);
    if (array_key_exists($cache_key, $category_name_cache)) {
        return $category_name_cache[$cache_key];
    }
    $category_table = findTable($db, ['categories', 'qkcms_category', 'article_category', 'cat', 'cats']);
    if ($category_table) {
        $name_field = findField($db, $category_table, ['name', 'category_name', 'title']);
        if (!$name_field) $name_field = 'name';
        $sql = "SELECT $name_field FROM $category_table WHERE id = ? LIMIT 1";
        $stmt = $db->prepare($sql);
        if ($stmt) {
            $stmt->bind_param('i', $category_id);
            $stmt->execute();
            $result = $stmt->get_result();
        } else $result = null;
        if ($result && $result->num_rows > 0) {
            $row = $result->fetch_assoc();
            $result->free();
            $category_name_cache[$cache_key] = $row[$name_field];
            return $row[$name_field];
        }
        if ($result) $result->free();
        if ($stmt) $stmt->close();
    }
    $category_name_cache[$cache_key] = null;
    return null;
}

function getNavigationData($params, $db)
{
    $cached_result = qkcms_request_result_cache('navigation', $db, $params);
    if ($cached_result !== null) return $cached_result;
    $row = isset($params['row']) ? intval($params['row']) : 10;
    $nav_table = findTable($db, ['navigation', 'navigations', 'qkcms_navigation', 'menu', 'menus']);
    $navigation = array();
    if ($nav_table) {
        $title_field = findField($db, $nav_table, ['title', 'name']) ?: 'id';
        $url_field = findField($db, $nav_table, ['url', 'href']) ?: 'id';
        $sort_field = findField($db, $nav_table, ['display_order', 'sort_order', 'sort']) ?: 'id';
        $target_field = findField($db, $nav_table, ['target', 'open_target']) ?: '';
        $status_field = findField($db, $nav_table, ['status']) ?: '';
        $parent_field = findField($db, $nav_table, ['parent_id']) ?: '';

        // 获取所有顶级导航项
        $sql = "SELECT id, $title_field, $url_field, $sort_field";
        if ($target_field) $sql .= ", $target_field";
        if ($status_field) $sql .= ", $status_field";
        if ($parent_field) $sql .= ", $parent_field";
        $sql .= " FROM $nav_table";

        $sql_params = array();
        $where_conditions = array();

        if ($status_field) $where_conditions[] = "$status_field = 1";
        if ($parent_field) $where_conditions[] = "$parent_field = 0";
        if (!empty($where_conditions)) $sql .= " WHERE " . implode(" AND ", $where_conditions);
        $sql .= " ORDER BY $sort_field ASC, id ASC LIMIT ?";
        $sql_params[] = $row;

        $stmt = $db->prepare($sql);
        if ($stmt) {
            $types = str_repeat('s', count($sql_params) - 1) . 'i';
            $stmt->bind_param($types, ...$sql_params);
            $stmt->execute();
            $result = $stmt->get_result();
        } else $result = null;

        if ($result && $result->num_rows > 0) {
            $nav_rows = array();
            while ($item = $result->fetch_assoc()) {
                $nav_rows[] = $item;
            }
            $result->free();

            $parent_ids = array();
            if ($parent_field) {
                foreach ($nav_rows as $item) {
                    $parent_ids[] = $item['id'];
                }
            }
            $children_map = $parent_field ? qkcms_get_navigation_children_map($db, $nav_table, $parent_ids, $title_field, $url_field, $sort_field, $target_field, $status_field, $parent_field) : array();

            foreach ($nav_rows as $item) {
                $target_value = '_self';
                if ($target_field && isset($item[$target_field]) && !empty($item[$target_field])) {
                    $target_value = $item[$target_field];
                }

                $nav_item = array(
                    'id' => $item['id'],
                    'title' => $item[$title_field],
                    'url' => $item[$url_field],
                    'target' => $target_value
                );

                // 获取子菜单
                if ($parent_field) {
                    $children = isset($children_map[intval($item['id'])]) ? $children_map[intval($item['id'])] : array();
                    if (is_array($children)) {
                        $nav_item['children'] = $children;
                    }
                }

                $navigation[] = $nav_item;
            }
        }
    }
    return qkcms_request_result_cache('navigation', $db, $params, $navigation, true);
}

function getNavigationChildren($db, $nav_table, $parent_id, $title_field, $url_field, $sort_field, $target_field, $status_field, $parent_field)
{
    $sql = "SELECT id, $title_field, $url_field, $sort_field";
    if ($target_field) $sql .= ", $target_field";
    if ($status_field) $sql .= ", $status_field";
    if ($parent_field) $sql .= ", $parent_field";
    $sql .= " FROM $nav_table WHERE $parent_field = ?";

    $where_conditions = array();
    // 添加状态条件，只获取启用的子导航项
    if ($status_field) $where_conditions[] = "$status_field = 1";
    if (!empty($where_conditions)) $sql .= " AND " . implode(" AND ", $where_conditions);
    $sql .= " ORDER BY $sort_field ASC, id ASC";

    $stmt = $db->prepare($sql);
    if ($stmt) {
        $stmt->bind_param('i', $parent_id);
        $stmt->execute();
        $result = $stmt->get_result();

        $children = array();
        if ($result && $result->num_rows > 0) {
            while ($item = $result->fetch_assoc()) {
                $target_value = '_self';
                if ($target_field && isset($item[$target_field]) && !empty($item[$target_field])) {
                    $target_value = $item[$target_field];
                }

                $child_item = array(
                    'id' => $item['id'],
                    'title' => $item[$title_field],
                    'url' => $item[$url_field],
                    'target' => $target_value
                );
                $children[] = $child_item;
            }
            $result->free();
        }
        return $children;
    }
    return null;
}

function get_property_text_from_article($article, $field_map = null)
{
    if (!is_array($article)) return null;

    $property_value = null;

    // 如果有field_map，使用映射的字段名
    if ($field_map && isset($field_map['property'])) {
        $property_field = $field_map['property'];
        if (isset($article[$property_field]) && $article[$property_field] !== '' && $article[$property_field] !== null) {
            $property_value = $article[$property_field];
        }
    }

    // 如果没有找到，尝试常见的字段名
    if ($property_value === null) {
        $property_fields = array('property', 'article_property', 'post_property', 'flag', 'article_flag', 'post_flag');
        foreach ($property_fields as $field) {
            if (isset($article[$field]) && $article[$field] !== '' && $article[$field] !== null) {
                $property_value = $article[$field];
                break;
            }
        }
    }

    if ($property_value !== null) {
        return getPropertyTagWithColor($property_value);
    }

    return null;
}

function qkcms_is_homepage_request()
{
    $current_uri = trim($_SERVER['REQUEST_URI'] ?? '', '/');
    $current_path = explode('?', $current_uri)[0];
    if ($current_path === '' || $current_path === '/' || strtolower($current_path) === 'index.php' || strtolower($current_path) === 'index.html') {
        return true;
    }

    return false;
}

function qkcms_get_current_list_page_category_id($db)
{
    if (!$db || !is_valid_db_connection($db)) return 0;
    if (qkcms_is_homepage_request()) return 0;
    if (get_catgroup_id() > 0) return 0;
    if (isset($_GET['software_id']) && is_numeric($_GET['software_id'])) return intval($_GET['software_id']);
    if (isset($_GET['category_id']) && is_numeric($_GET['category_id'])) return intval($_GET['category_id']);
    if (isset($_GET['cid']) && is_numeric($_GET['cid'])) return intval($_GET['cid']);
    if (isset($_GET['cat_id']) && is_numeric($_GET['cat_id'])) return intval($_GET['cat_id']);
    if (isset($_SERVER['REQUEST_URI']) && preg_match('/list_id(\d+)\.html/i', $_SERVER['REQUEST_URI'], $matches)) return intval($matches[1]);

    $script_name = strtolower(basename($_SERVER['SCRIPT_NAME'] ?? ''));
    if (($script_name === 'list.php' || $script_name === 'list.html' || $script_name === 'category.php' || $script_name === 'category.html' || $script_name === 'cat.php' || $script_name === 'cat.html') && isset($_GET['id']) && is_numeric($_GET['id'])) {
        return intval($_GET['id']);
    }

    $current_uri = trim($_SERVER['REQUEST_URI'] ?? '', '/');
    $current_path = explode('?', $current_uri)[0];
    if ($current_path === '') return 0;
    if (preg_match('/^(search|tag|member|user|post|article|goods)(\.php|\.html)$/i', $current_path)) return 0;

    $reserved_prefixes = array('post_id', 'article_id', 'goods_id', 'tag_', 'search_', 'member_', 'user_', 'cg_id', 'list_id');
    foreach ($reserved_prefixes as $prefix) {
        if (stripos($current_path, $prefix) === 0) return 0;
    }

    static $category_custom_url_cache = array();
    if (isset($category_custom_url_cache[$current_path])) return intval($category_custom_url_cache[$current_path]);

    $category_table = findTable($db, array('categories', 'qkcms_category', 'board', 'article_category', 'news_category', 'blog_category', 'post_category', 'site_category', 'content_category', 'product_category', 'cat', 'cats'));
    if ($category_table) {
        $id_field = findField($db, $category_table, array('id', 'category_id'));
        $custom_url_field = findField($db, $category_table, array('custom_url', 'customurl', 'url_alias', 'alias'));
        if ($id_field && $custom_url_field) {
            $row = execute_db_query($db, "SELECT {$id_field} AS id FROM {$category_table} WHERE {$custom_url_field} = ? LIMIT 1", array($current_path), 'row');
            if ($row && isset($row['id']) && intval($row['id']) > 0) {
                $category_custom_url_cache[$current_path] = intval($row['id']);
                return intval($row['id']);
            }
        }
    }

    $category_custom_url_cache[$current_path] = 0;
    return 0;
}

function qkcms_get_category_parent_id($db, $category_id)
{
    $category_id = intval($category_id);
    if ($category_id <= 0 || !$db || !is_valid_db_connection($db)) return 0;

    static $parent_cache = array();
    if (isset($parent_cache[$category_id])) return intval($parent_cache[$category_id]);

    $category_table = findTable($db, array('categories', 'qkcms_category', 'board', 'article_category', 'news_category', 'blog_category', 'post_category', 'site_category', 'content_category', 'product_category', 'cat', 'cats'));
    if ($category_table) {
        $id_field = findField($db, $category_table, array('id', 'category_id'));
        $parent_field = findField($db, $category_table, array('parent_id', 'pid'));
        if ($id_field && $parent_field) {
            $parent_id = execute_db_query($db, "SELECT {$parent_field} FROM {$category_table} WHERE {$id_field} = ? LIMIT 1", array($category_id), 'single');
            $parent_cache[$category_id] = intval($parent_id);
            return intval($parent_id);
        }
    }

    $parent_cache[$category_id] = 0;
    return 0;
}

function qkcms_get_articles_auto_top_type($db, $params = array())
{
    $loop_category_id = isset($params['category_id']) ? intval($params['category_id']) : 0;
    if ($loop_category_id > 0) return 2;
    if (qkcms_is_homepage_request()) return 1;
    if (get_catgroup_id() > 0) return 2;

    $current_category_id = qkcms_get_current_list_page_category_id($db);
    if ($current_category_id > 0) return 2;

    return 0;
}

function qkcms_build_article_loop_item($article, $field_map, $db, $titlelen, $description_len, $col, $user_table = null, $preload = array())
{
    $article_id = isset($field_map['id']) && isset($article[$field_map['id']]) ? $article[$field_map['id']] : '';
    $title = isset($field_map['title']) && isset($article[$field_map['title']]) ? $article[$field_map['title']] : '';
    if ($titlelen > 0 && mb_strlen($title, 'UTF-8') > $titlelen) $title = mb_substr($title, 0, $titlelen, 'UTF-8') . '...';

    $user_info_map = isset($preload['users']) && is_array($preload['users']) ? $preload['users'] : array();
    $category_info_map = isset($preload['categories']) && is_array($preload['categories']) ? $preload['categories'] : array();
    $comment_count_map = isset($preload['comments']) && is_array($preload['comments']) ? $preload['comments'] : array();
    $user_fields = isset($preload['user_fields']) && is_array($preload['user_fields']) ? $preload['user_fields'] : array();
    $category_fields = isset($preload['category_fields']) && is_array($preload['category_fields']) ? $preload['category_fields'] : array();

    $author = null;
    $author_level = null;
    $author_avatar = null;
    if (isset($field_map['author']) && isset($article[$field_map['author']])) {
        $author = $article[$field_map['author']];
    } elseif (isset($field_map['user_id']) && isset($article[$field_map['user_id']]) && $article[$field_map['user_id']] > 0 && $user_table) {
        $user_id_val = $article[$field_map['user_id']];
        if (isset($user_info_map[$user_id_val])) {
            $user_info = $user_info_map[$user_id_val];
            $name_field = isset($user_fields['name']) ? $user_fields['name'] : null;
            $avatar_field = isset($user_fields['avatar']) ? $user_fields['avatar'] : null;
            $level_field = isset($user_fields['level']) ? $user_fields['level'] : null;
            if ($name_field && isset($user_info[$name_field])) $author = $user_info[$name_field];
            if ($avatar_field && isset($user_info[$avatar_field]) && !empty(trim($user_info[$avatar_field]))) {
                $author_avatar = $user_info[$avatar_field];
            } else {
                $author_avatar = DEFAULT_AVATAR_PATH;
            }
            if ($level_field && isset($user_info[$level_field])) {
                $levels_id = $user_info[$level_field];
                if ($levels_id == 1) $author_level = '游客';
                elseif ($levels_id == 2) $author_level = 'VIP用户';
                elseif ($levels_id == 3) $author_level = '管理员';
                else $author_level = '游客';
            }
        } else {
            $name_field = findField($db, $user_table, ['nickname', 'username', 'user_name', 'loginname', 'name']);
            $avatar_field = findField($db, $user_table, ['avatar', 'user_avatar']);
            $level_field = findField($db, $user_table, ['levels_id']);
            $user_select_fields = array();
            if ($name_field) $user_select_fields[] = $name_field;
            if ($avatar_field) $user_select_fields[] = $avatar_field;
            if ($level_field) $user_select_fields[] = $level_field;
            if (!empty($user_select_fields)) {
                $user_result = $db->query("SELECT " . implode(', ', $user_select_fields) . " FROM $user_table WHERE id = $user_id_val LIMIT 1");
                if ($user_result && $user_result->num_rows > 0) {
                    $user_info = $user_result->fetch_assoc();
                    if ($name_field && isset($user_info[$name_field])) $author = $user_info[$name_field];
                    if ($avatar_field && isset($user_info[$avatar_field]) && !empty(trim($user_info[$avatar_field]))) {
                        $author_avatar = $user_info[$avatar_field];
                    } else {
                        $author_avatar = DEFAULT_AVATAR_PATH;
                    }
                    if ($level_field && isset($user_info[$level_field])) {
                        $levels_id = $user_info[$level_field];
                        if ($levels_id == 1) $author_level = '游客';
                        elseif ($levels_id == 2) $author_level = 'VIP用户';
                        elseif ($levels_id == 3) $author_level = '管理员';
                        else $author_level = '游客';
                    }
                    $user_result->free();
                }
            }
        }
    }

    $description = '';
    if (isset($field_map['description']) && isset($article[$field_map['description']])) {
        $description = $article[$field_map['description']];
        $description = strip_tags($description);
        $description = preg_replace('/\s+/', ' ', $description);
        $description = trim($description);
    } elseif (isset($field_map['content']) && isset($article[$field_map['content']])) {
        $description = $article[$field_map['content']];
        $description = strip_tags($description);
        $description = preg_replace('/\s+/', ' ', $description);
        $description = trim($description);
    }
    if (empty($description) && isset($field_map['content']) && isset($article[$field_map['content']])) {
        $description = $article[$field_map['content']];
        $description = strip_tags($description);
        $description = preg_replace('/\s+/', ' ', $description);
        $description = trim($description);
    }
    if (mb_strlen($description, 'UTF-8') > $description_len) $description = mb_substr($description, 0, $description_len, 'UTF-8') . '...';

    $category = null;
    if (isset($field_map['category_name']) && isset($article[$field_map['category_name']])) $category = $article[$field_map['category_name']];
    elseif (isset($field_map['category_id']) && isset($article[$field_map['category_id']])) {
        $category_id_val = intval($article[$field_map['category_id']]);
        if ($category_id_val > 0 && isset($category_info_map[$category_id_val])) {
            $category_row = $category_info_map[$category_id_val];
            $category_name_field = isset($category_fields['name']) ? $category_fields['name'] : null;
            if ($category_name_field && isset($category_row[$category_name_field])) {
                $category = $category_row[$category_name_field];
            }
        }
        if ($category === null) $category = getCategoryName($db, $article[$field_map['category_id']]);
    }

    $comments_count = 0;
    if ($article_id && isset($comment_count_map[intval($article_id)])) {
        $comments_count = intval($comment_count_map[intval($article_id)]);
    } elseif (isset($article['comments_count'])) {
        $comments_count = intval($article['comments_count']);
    } elseif (isset($field_map['comments']) && isset($article[$field_map['comments']])) {
        $comments_count = intval($article[$field_map['comments']]);
    } elseif ($article_id) {
        $comment_table = findTable($db, ['comments', 'qkcms_comment', 'qkcms_comment', 'article_comment']);
        if ($comment_table) {
            $comment_article_field = findField($db, $comment_table, ['article_id', 'post_id', 'content_id']);
            if ($comment_article_field) {
                $comment_sql = "SELECT COUNT(*) as comment_count FROM $comment_table WHERE $comment_article_field = $article_id";
                $comment_result = $db->query($comment_sql);
                if ($comment_result) {
                    $comment_row = $comment_result->fetch_assoc();
                    $comments_count = isset($comment_row['comment_count']) ? $comment_row['comment_count'] : 0;
                    $comment_result->free();
                }
            }
        }
    }

    $like_count = 0;
    if (isset($field_map['likes']) && isset($article[$field_map['likes']])) {
        $like_count = intval($article[$field_map['likes']]);
    }

    $cat_url = null;
    if (isset($field_map['category_id']) && isset($article[$field_map['category_id']])) {
        $raw_category_id = $article[$field_map['category_id']];
        $category_id_val = intval($raw_category_id);
        if ($category_id_val > 0) {
            $cat_custom_url = null;
            if (isset($category_info_map[$category_id_val])) {
                $category_row = $category_info_map[$category_id_val];
                $cat_custom_url_field = isset($category_fields['custom_url']) ? $category_fields['custom_url'] : null;
                if ($cat_custom_url_field && isset($category_row[$cat_custom_url_field])) {
                    $cat_custom_url = trim((string)$category_row[$cat_custom_url_field]);
                }
            } else {
                $category_table = findTable($db, ['categories', 'qkcms_category', 'article_category', 'cat', 'cats']);
                if ($category_table) {
                    $cat_id_field = findField($db, $category_table, ['id', 'category_id']);
                    $cat_custom_url_field = findField($db, $category_table, ['custom_url', 'customurl', 'url_alias', 'alias']);
                    if ($cat_id_field && $cat_custom_url_field) {
                        $cat_sql = "SELECT $cat_custom_url_field FROM $category_table WHERE $cat_id_field = $category_id_val LIMIT 1";
                        $cat_result = $db->query($cat_sql);
                        if ($cat_result && $cat_result->num_rows > 0) {
                            $cat_row = $cat_result->fetch_assoc();
                            $cat_custom_url = isset($cat_row[$cat_custom_url_field]) ? trim((string)$cat_row[$cat_custom_url_field]) : '';
                            $cat_result->free();
                        }
                    }
                }
            }
            if ($cat_custom_url !== null && $cat_custom_url !== '') {
                $cat_url = '/' . ltrim($cat_custom_url, '/');
            } else {
                $cat_url = '/list_id' . $category_id_val . '.php';
            }
        }
    }

    $thumbnail = DEFAULT_THUMBNAIL_PATH;
    if (isset($field_map['thumbnail']) && isset($article[$field_map['thumbnail']]) && !empty(trim($article[$field_map['thumbnail']]))) {
        $db_thumbnail = trim($article[$field_map['thumbnail']]);
        $thumbnail = check_file_exists_or_default($db_thumbnail);
    }

    $date_str = isset($field_map['date']) && isset($article[$field_map['date']]) ? $article[$field_map['date']] : null;
    $date_mo = null;
    $date_dy = null;
    $date_only = null;
    if ($date_str && strpos($date_str, '-') !== false) {
        $date_parts = explode(' ', $date_str);
        $date_only = $date_parts[0];
        $date_segments = explode('-', $date_only);
        if (count($date_segments) >= 3) {
            $date_mo = intval($date_segments[1]);
            $date_dy = intval($date_segments[2]);
        }
    }

    $author_id = null;
    if (isset($field_map['user_id']) && isset($article[$field_map['user_id']])) {
        $author_id = $article[$field_map['user_id']];
    }

    $article_url = $article_id ? '/post_id' . $article_id . (qkcms_is_home_request_for_article_url() ? '.html' : '.php') : null;
    if (isset($field_map['custom_url']) && isset($article[$field_map['custom_url']])) {
        $custom_url_val = trim((string)$article[$field_map['custom_url']]);
        if ($custom_url_val !== '') {
            $article_url = '/' . ltrim($custom_url_val, '/');
        }
    }

    $keywords_raw_value = isset($field_map['keywords']) && isset($article[$field_map['keywords']]) ? $article[$field_map['keywords']] : '';
    $keywords_items = qkcms_parse_keywords($keywords_raw_value, 3);
    $keywords_value = implode(',', $keywords_items);
    $keywords_links = '';
    if (!empty($keywords_items)) {
        foreach ($keywords_items as $keyword_text) {
            $keywords_links .= '<a href="/tag.html?tag=' . urlencode($keyword_text) . '" class="article-tag">' . htmlspecialchars($keyword_text) . '</a>';
        }
    }

    return array(
        'id' => $article_id,
        'title' => htmlspecialchars($title),
        'url' => $article_url,
        'thumbnail' => $thumbnail,
        'author' => $author,
        'author_id' => $author_id,
        'author_level' => $author_level,
        'author_avatar' => $author_avatar,
        'date' => $date_str,
        'date_only' => $date_only,
        'date_mo' => $date_mo,
        'date_dy' => $date_dy,
        'views' => isset($field_map['views']) && isset($article[$field_map['views']]) ? $article[$field_map['views']] : 0,
        'comments' => $comments_count,
        'like_count' => $like_count,
        'category' => $category,
        'cat_url' => $cat_url,
        'description' => $description,
        'article_description' => $description,
        'keywords' => $keywords_value,
        'keywords_links' => $keywords_links,
        'property' => get_property_text_from_article($article, $field_map),
        'col' => $col,
        'col_class' => 'col-' . (12 / $col)
    );
}

function getArticlesData($params, $db)
{
    $cached_result = qkcms_request_result_cache('articles', $db, $params);
    if ($cached_result !== null) return $cached_result;
    $row = isset($params['row']) ? intval($params['row']) : 5;
    $page = isset($params['page']) ? intval($params['page']) : 1;
    $page = max(1, $page);
    $titlelen = isset($params['titlelen']) ? intval($params['titlelen']) : 40;
    $col = isset($params['col']) ? intval($params['col']) : 1;
    $category_id = isset($params['category_id']) ? intval($params['category_id']) : 0;
    $description_len = isset($params['description_len']) ? intval($params['description_len']) : 100;
    $keyword = isset($params['keyword']) ? $params['keyword'] : '';
    $tag = isset($params['tag']) ? $params['tag'] : '';
    $min_views = isset($params['min_views']) ? intval($params['min_views']) : 0;
    $min_comments = isset($params['min_comments']) ? intval($params['min_comments']) : 0;
    $days_old = isset($params['days_old']) ? intval($params['days_old']) : 0;
    $user_id = isset($params['user_id']) ? intval($params['user_id']) : 0;
    $exclude_ids = isset($params['exclude_ids']) ? $params['exclude_ids'] : '';
    $status = isset($params['status']) ? $params['status'] : '';
    $property = isset($params['property']) ? $params['property'] : '';
    $requested_order = isset($params['order']) ? strtolower(trim((string)$params['order'])) : 'latest';

    // 自动获取当前文章ID
    $current_article_id = 0;
    if (isset($_GET['id']) && is_numeric($_GET['id'])) $current_article_id = intval($_GET['id']);
    elseif (isset($_GET['article_id']) && is_numeric($_GET['article_id'])) $current_article_id = intval($_GET['article_id']);
    elseif (isset($_GET['post_id']) && is_numeric($_GET['post_id'])) $current_article_id = intval($_GET['post_id']);
    elseif (isset($_GET['article']) && is_numeric($_GET['article'])) $current_article_id = intval($_GET['article']);
    elseif (isset($_GET['post']) && is_numeric($_GET['post'])) $current_article_id = intval($_GET['post']);

    // 如果当前有文章ID，自动添加到排除列表
    if ($current_article_id > 0) {
        if (!empty($exclude_ids)) {
            $exclude_ids .= ',' . $current_article_id;
        } else {
            $exclude_ids = strval($current_article_id);
        }
    }

    $order_map = array(
        'hot' => array('views', 'view_count', 'hits', 'clicks'),
        'latest' => array('id', 'created_at', 'create_time', 'publish_time', 'post_time'),
        'comment' => array('comments', 'comment_count', 'comments_count'),
        'keywords' => array('keywords', 'tags'),
        'updated' => array('updated_at', 'update_time', 'modified_at', 'modify_time')
    );

    $order_field = 'id';
    $order_direction = isset($params['order_direction']) ? $params['order_direction'] : 'DESC';
    $article_table = findTable($db, ['articles', 'posts', 'news', 'content', 'article', 'blog']);
    $articles = array();
    $total = 0;
    $conditions = array();

    if ($article_table) {
        $field_patterns = array(
            'id' => array('id', 'article_id', 'post_id'),
            'title' => array('title', 'article_title', 'post_title'),
            'content' => array('content', 'body', 'text'),
            'description' => array('description', 'summary'),
            'date' => array('created_at', 'create_time', 'publish_time', 'post_time', 'date', 'time'),
            'views' => array('views', 'view_count', 'hits', 'clicks'),
            'likes' => array('likes', 'like_count', 'praise_count'),
            'comments' => array('comments', 'comment_count'),
            'category_id' => array('category_id', 'cat_id', 'cid'),
            'category_name' => array('category', 'cat_name', 'category_name'),
            'author' => array('author', 'user_name', 'username', 'writer', 'nickname'),
            'user_id' => array('user_id', 'author_id', 'uid'),
            'thumbnail' => array('thumbnail', 'thumb', 'image', 'img', 'cover'),
            'keywords' => array('keywords', 'tags'),
            'property' => array('property', 'article_property', 'post_property', 'flag', 'article_flag', 'post_flag', 'type', 'article_type'),
            'custom_url' => array('custom_url', 'customurl', 'url_alias', 'alias'),
            'top_type' => array('top_type')
        );

        $field_map = getFieldMap($db, $article_table, $field_patterns);

        if (isset($params['order']) && $params['order'] === 'keywords') {
            $current_article_keyword = '';
            $current_article_id = 0;

            if (isset($_GET['id']) && is_numeric($_GET['id'])) $current_article_id = intval($_GET['id']);
            elseif (isset($_GET['article_id']) && is_numeric($_GET['article_id'])) $current_article_id = intval($_GET['article_id']);
            elseif (isset($_GET['post_id']) && is_numeric($_GET['post_id'])) $current_article_id = intval($_GET['post_id']);
            elseif (isset($_GET['article']) && is_numeric($_GET['article'])) $current_article_id = intval($_GET['article']);
            elseif (isset($_GET['post']) && is_numeric($_GET['post'])) $current_article_id = intval($_GET['post']);

            if ($current_article_id > 0 && isset($field_map['keywords'])) {
                $current_sql = "SELECT {$field_map['keywords']} FROM $article_table WHERE {$field_map['id']} = $current_article_id LIMIT 1";
                $current_result = $db->query($current_sql);
                if ($current_result && $current_result->num_rows > 0) {
                    $current_row = $current_result->fetch_assoc();
                    $keywords_str = isset($current_row[$field_map['keywords']]) ? $current_row[$field_map['keywords']] : '';
                    if (!empty($keywords_str)) {
                        $keywords_array = array_map('trim', explode(',', $keywords_str));
                        $current_article_keyword = isset($keywords_array[0]) ? $keywords_array[0] : '';
                    }
                    $current_result->free();
                }
            }

            if (!empty($current_article_keyword) && isset($field_map['keywords'])) {
                $current_article_keyword = $db->real_escape_string($current_article_keyword);
                $keyword_condition = "({$field_map['keywords']} = '$current_article_keyword' OR {$field_map['keywords']} LIKE '$current_article_keyword,%') AND {$field_map['id']} != $current_article_id";
                $conditions[] = $keyword_condition;
                $order_field = 'id';
            }
        } elseif (isset($params['order']) && isset($order_map[$params['order']])) {
            $order_field = findField($db, $article_table, $order_map[$params['order']]) ?: 'id';
        }

        $id_field = isset($field_map['id']) ? $field_map['id'] : 'id';
        $select_fields = array($id_field);

        foreach (['title', 'content', 'description', 'date', 'views', 'likes', 'comments', 'category_id', 'category_name', 'author', 'user_id', 'thumbnail', 'keywords', 'property', 'status', 'custom_url'] as $field) {
            if (isset($field_map[$field])) $select_fields[] = $field_map[$field];
        }

        if (isset($params['order']) && $params['order'] === 'comment') {
            if (isset($field_map['comments']) && $field_map['comments']) {
                $sql = "SELECT " . implode(', ', $select_fields) . " FROM $article_table";
                $count_sql = "SELECT COUNT(*) as total FROM $article_table";
                $order_field = $field_map['comments'];
            } else {
                $comment_table = findTable($db, ['comments', 'qkcms_comment', 'qkcms_comment', 'article_comment']);
                if ($comment_table) {
                    $comment_article_field = findField($db, $comment_table, ['article_id', 'post_id', 'content_id']);
                    if ($comment_article_field) {
                        $id_field = isset($field_map['id']) ? $field_map['id'] : 'id';
                        $comment_stats_sql = "(SELECT $comment_article_field AS article_ref_id, COUNT(*) AS comments_count FROM $comment_table GROUP BY $comment_article_field) qk_comment_stats";
                        $sql = "SELECT " . implode(', ', $select_fields) . ", COALESCE(qk_comment_stats.comments_count, 0) as comments_count 
                       FROM $article_table
                       LEFT JOIN $comment_stats_sql ON qk_comment_stats.article_ref_id = $article_table.$id_field";
                        $count_sql = "SELECT COUNT(*) as total FROM $article_table";
                        $order_field = 'comments_count';
                    } else {
                        $sql = "SELECT " . implode(', ', $select_fields) . " FROM $article_table";
                        $count_sql = "SELECT COUNT(*) as total FROM $article_table";
                    }
                } else {
                    $sql = "SELECT " . implode(', ', $select_fields) . " FROM $article_table";
                    $count_sql = "SELECT COUNT(*) as total FROM $article_table";
                }
            }
        } else {
            $sql = "SELECT " . implode(', ', $select_fields) . " FROM $article_table";
            $count_sql = "SELECT COUNT(*) as total FROM $article_table";
        }
        $base_sql = $sql;
        $base_count_sql = $count_sql;

        // category_id: -1表示全站, 0表示当前文章所在分类, >0表示指定分类
        if ($category_id === 0) {
            // 获取当前文章所在分类
            $current_article_id = isset($params['article_id']) ? intval($params['article_id']) : 0;
            if ($current_article_id <= 0) {
                if (isset($_GET['id']) && is_numeric($_GET['id'])) $current_article_id = intval($_GET['id']);
                elseif (isset($_GET['article_id']) && is_numeric($_GET['article_id'])) $current_article_id = intval($_GET['article_id']);
                elseif (isset($_GET['post_id']) && is_numeric($_GET['post_id'])) $current_article_id = intval($_GET['post_id']);
            }
            if ($current_article_id > 0 && isset($field_map['category_id']) && isset($field_map['id'])) {
                $cat_sql = "SELECT {$field_map['category_id']} FROM $article_table WHERE {$field_map['id']} = $current_article_id LIMIT 1";
                $cat_result = $db->query($cat_sql);
                if ($cat_result && $cat_result->num_rows > 0) {
                    $cat_row = $cat_result->fetch_assoc();
                    $current_category_id = isset($cat_row[$field_map['category_id']]) ? intval($cat_row[$field_map['category_id']]) : 0;
                    if ($current_category_id > 0) {
                        $conditions[] = $field_map['category_id'] . " = $current_category_id";
                    }
                    $cat_result->free();
                }
            }
        } elseif ($category_id > 0 && isset($field_map['category_id'])) {
            $category_ids = [$category_id];
            $category_table = findTable($db, ['categories', 'qkcms_category', 'article_category', 'cat', 'cats']);
            if ($category_table) {
                $cat_id_field = findField($db, $category_table, ['id', 'category_id']);
                $cat_parent_field = findField($db, $category_table, ['parent_id', 'pid']);
                if ($cat_id_field && $cat_parent_field) {
                    $child_sql = "SELECT $cat_id_field AS cid FROM $category_table WHERE $cat_parent_field = " . intval($category_id);
                    $child_result = $db->query($child_sql);
                    if ($child_result) {
                        while ($child_row = $child_result->fetch_assoc()) {
                            if (isset($child_row['cid']) && intval($child_row['cid']) > 0) {
                                $category_ids[] = intval($child_row['cid']);
                            }
                        }
                        $child_result->free();
                    }
                }
            }
            $category_ids = array_values(array_unique(array_filter($category_ids, function ($v) {
                return intval($v) > 0;
            })));
            if (count($category_ids) > 1) {
                $conditions[] = $field_map['category_id'] . " IN (" . implode(',', $category_ids) . ")";
            } else {
                $conditions[] = $field_map['category_id'] . " = $category_id";
            }
        }
        // category_id = -1 时不添加分类筛选条件，表示全站
        if (!empty($property) && isset($field_map['property'])) {
            $property_value = $db->real_escape_string($property);
            $conditions[] = $field_map['property'] . " = '$property_value'";
        }
        if ($user_id > 0 && isset($field_map['user_id'])) $conditions[] = $field_map['user_id'] . " = $user_id";
        if (!empty($keyword)) {
            $keyword_condition = array();
            $keyword = $db->real_escape_string($keyword);
            if (isset($field_map['title'])) $keyword_condition[] = $field_map['title'] . " LIKE '%$keyword%'";
            if (isset($field_map['content'])) $keyword_condition[] = $field_map['content'] . " LIKE '%$keyword%'";
            if (isset($field_map['description'])) $keyword_condition[] = $field_map['description'] . " LIKE '%$keyword%'";
            if (isset($field_map['keywords'])) $keyword_condition[] = $field_map['keywords'] . " LIKE '%$keyword%'";
            if (!empty($keyword_condition)) $conditions[] = "(" . implode(' OR ', $keyword_condition) . ")";
        }

        // 记录是否需要使用PHP进行tag=2模式的筛选
        $use_php_tag_filter = false;
        $target_tag = '';

        // 添加tag筛选条件，支持tag=1,目标词和tag=2,目标词两种模式
        if (!empty($tag)) {
            // 处理中文引号
            $tag = str_replace(['"', '“', '”'], "'", $tag);

            // 解析tag参数，格式：tag=1,目标词 或 tag=2,目标词
            $tag_parts = explode(',', $tag, 2);
            $tag_mode = isset($tag_parts[0]) ? trim($tag_parts[0]) : '1'; // 默认模式1，去除首尾空格
            $tag_value = isset($tag_parts[1]) ? trim($tag_parts[1]) : trim($tag_parts[0]); // 去除首尾空格

            // 获取当前文章的ID
            $current_article_id = 0;
            if (isset($_GET['id']) && is_numeric($_GET['id'])) {
                $current_article_id = intval($_GET['id']);
            }

            // 如果tag_value是"TAG词"，则获取当前文章的第一个关键词
            if ($tag_value === 'TAG词' && $current_article_id > 0 && isset($field_map['keywords']) && isset($field_map['id'])) {
                try {
                    $current_sql = "SELECT {$field_map['keywords']} FROM $article_table WHERE {$field_map['id']} = $current_article_id LIMIT 1";
                    $current_result = $db->query($current_sql);
                    if ($current_result && $current_result->num_rows > 0) {
                        $current_row = $current_result->fetch_assoc();
                        $keywords_str = isset($current_row[$field_map['keywords']]) ? $current_row[$field_map['keywords']] : '';
                        if (!empty($keywords_str)) {
                            // 提取第一个关键词
                            $keywords_array = array_map('trim', explode(',', $keywords_str));
                            $tag_value = isset($keywords_array[0]) ? $keywords_array[0] : '';
                        }
                        $current_result->free();
                    }
                } catch (Exception $e) {
                    // 出错时保持原tag_value
                }
            }

            // 只有当tag_value不为空时才添加筛选条件
            if (!empty($tag_value)) {
                $tag_value = $db->real_escape_string($tag_value);
                $tag_condition = array();

                if ($tag_mode === '1') {
                    // 模式1：根据文章标题、描述、所有关键词只要包含就筛选出来（原有逻辑）
                    if (isset($field_map['title'])) $tag_condition[] = $field_map['title'] . " LIKE '%$tag_value%'";
                    if (isset($field_map['description'])) $tag_condition[] = $field_map['description'] . " LIKE '%$tag_value%'";
                    if (isset($field_map['keywords'])) $tag_condition[] = $field_map['keywords'] . " LIKE '%$tag_value%'";
                } elseif ($tag_mode === '2') {
                    // 模式2：匹配文章关键词中的精确匹配，同时匹配标题包含关键词
                    // 先获取所有关键词不为空的文章，然后在PHP中进行精确匹配
                    if (isset($field_map['keywords'])) {
                        $tag_condition[] = $field_map['keywords'] . " != ''";
                        $tag_condition[] = $field_map['keywords'] . " IS NOT NULL";
                    }
                    // 同时也匹配标题包含关键词的文章
                    if (isset($field_map['title'])) {
                        $tag_condition[] = $field_map['title'] . " LIKE '%$tag_value%'";
                    }
                    $use_php_tag_filter = true;
                    $target_tag = $tag_value;
                }

                if (!empty($tag_condition)) {
                    $conditions[] = "(" . implode(' OR ', $tag_condition) . ")";
                }
            }
        }
        if ($min_views > 0 && isset($field_map['views'])) $conditions[] = $field_map['views'] . " >= $min_views";
        if ($min_comments > 0 && isset($field_map['comments'])) $conditions[] = $field_map['comments'] . " >= $min_comments";
        if ($days_old > 0 && isset($field_map['date'])) {
            $time_threshold = date('Y-m-d H:i:s', strtotime("-$days_old days"));
            $conditions[] = $field_map['date'] . " >= '$time_threshold'";
        }
        if (!empty($exclude_ids) && isset($field_map['id'])) {
            $ids = array_map('intval', explode(',', $exclude_ids));
            if (!empty($ids)) $conditions[] = $field_map['id'] . " NOT IN (" . implode(',', $ids) . ")";
        }
        if (!empty($status) && isset($field_map['status'])) $conditions[] = $field_map['status'] . " = '$status'";

        $where_sql = '';
        if (!empty($conditions)) {
            $where_sql = " WHERE " . implode(' AND ', $conditions);
        }
        $sql = $base_sql . $where_sql;
        $count_sql = $base_count_sql . $where_sql;

        $count_result = $db->query($count_sql);
        if ($count_result && $count_result->num_rows > 0) {
            $count_row = $count_result->fetch_assoc();
            $total = intval($count_row['total']);
            $count_result->free();
        }

        $auto_top_type = 0;
        $auto_top_ids = array();
        $auto_top_rows = array();
        $auto_top_count = 0;
        $can_apply_auto_top = $row > 0 && !$use_php_tag_filter && isset($field_map['top_type']) && in_array($requested_order, array('', 'latest'), true);
        if ($can_apply_auto_top) {
            $auto_top_type = qkcms_get_articles_auto_top_type($db, $params);
            if ($auto_top_type > 0) {
                $top_conditions = $conditions;
                if ($auto_top_type === 2) {
                    $top_conditions[] = $field_map['top_type'] . " IN (2,3)";
                } else {
                    $top_conditions[] = $field_map['top_type'] . " = " . intval($auto_top_type);
                }
                $top_where_sql = " WHERE " . implode(' AND ', $top_conditions);
                $top_order_field = isset($field_map['date']) ? $field_map['date'] : $id_field;
                $top_limit = min(3, $row);
                $top_sql = "SELECT " . implode(', ', $select_fields) . " FROM $article_table" . $top_where_sql . " ORDER BY {$top_order_field} DESC, {$id_field} DESC LIMIT {$top_limit}";
                $top_result = $db->query($top_sql);
                if ($top_result && $top_result->num_rows > 0) {
                    while ($top_row = $top_result->fetch_assoc()) {
                        $top_id = isset($top_row[$id_field]) ? intval($top_row[$id_field]) : 0;
                        if ($top_id > 0) {
                            $auto_top_ids[] = $top_id;
                            $auto_top_rows[] = $top_row;
                        }
                    }
                    $top_result->free();
                }
                $auto_top_count = count($auto_top_ids);
            }
        }

        // 如果使用PHP进行tag=2模式筛选，需要获取更多文章
        $temp_row = $row;
        if ($use_php_tag_filter) {
            $temp_row = min(100, $total > 0 ? $total : 100);
        }

        $user_table = isset($field_map['user_id']) ? findTable($db, ['users', 'members', 'qkcms_user']) : null;
        $article_rows = array();
        if ($auto_top_count > 0 && $page === 1) {
            foreach ($auto_top_rows as $top_article) {
                $article_rows[] = $top_article;
            }
        }

        $offset = ($page - 1) * $temp_row;
        $limit = $temp_row;
        $query_conditions = $conditions;
        if ($auto_top_count > 0) {
            $query_conditions[] = $id_field . " NOT IN (" . implode(',', $auto_top_ids) . ")";
            if ($page === 1) {
                $limit = max(0, $temp_row - $auto_top_count);
                $offset = 0;
            } else {
                $offset = max(0, $offset - $auto_top_count);
            }
        }

        if ($limit > 0) {
            $query_where_sql = '';
            if (!empty($query_conditions)) {
                $query_where_sql = " WHERE " . implode(' AND ', $query_conditions);
            }
            $query_sql = $base_sql . $query_where_sql . " ORDER BY $order_field $order_direction LIMIT $offset, $limit";
            $result = $db->query($query_sql);
            if ($result && $result->num_rows > 0) {
                while ($article = $result->fetch_assoc()) {
                    $article_rows[] = $article;
                }
                $result->free();
            }
        }
        if (!empty($article_rows)) {
            $article_preload = qkcms_build_article_preload($article_rows, $field_map, $db, $user_table);
            foreach ($article_rows as $article_row) {
                $articles[] = qkcms_build_article_loop_item($article_row, $field_map, $db, $titlelen, $description_len, $col, $user_table, $article_preload);
            }
        }
    }

    // 如果需要使用PHP进行tag=2模式的筛选
    if ($use_php_tag_filter && !empty($target_tag) && isset($field_map['keywords'])) {
        $filtered_articles = array();

        foreach ($articles as $article) {
            $matched = false;

            // 检查标题是否包含目标关键词
            $title = isset($article['title']) ? $article['title'] : '';
            if (strpos($title, $target_tag) !== false) {
                $matched = true;
            }

            // 检查关键词是否包含目标关键词
            if (!$matched) {
                $keywords_str = $article['keywords'];
                if (!empty($keywords_str)) {
                    // 使用简单的explode分割关键词，避免正则表达式乱码问题
                    $keywords = array_map('trim', explode(',', $keywords_str));
                    // 检查是否包含目标关键词（支持包含匹配）
                    foreach ($keywords as $keyword) {
                        if (strpos($keyword, $target_tag) !== false || strpos($target_tag, $keyword) !== false) {
                            $matched = true;
                            break;
                        }
                    }
                }
            }

            if ($matched) {
                $filtered_articles[] = $article;
            }
        }

        // 更新文章列表和总数
        $articles = $filtered_articles;
        $total = count($articles);

        // 如果使用PHP进行tag=2模式筛选，需要限制结果数量
        if ($use_php_tag_filter) {
            $articles = array_slice($articles, 0, $row);
            $total = count($articles);
        }
    }

    $result_payload = array(
        'articles' => $articles,
        'total' => $total
    );
    return qkcms_request_result_cache('articles', $db, $params, $result_payload, true);
}

function getCategoriesData($params, $db)
{
    $cached_result = qkcms_request_result_cache('categories', $db, $params);
    if ($cached_result !== null) return $cached_result;
    $row = isset($params['row']) ? intval($params['row']) : 10;
    $page_key = isset($params['page_key']) ? intval($params['page_key']) : (isset($params['page']) ? intval($params['page']) : 0);
    $parent_id = isset($params['parent_id']) ? intval($params['parent_id']) : -1;
    $show_all = isset($params['sa']) ? intval($params['sa']) : 2;
    $sort = isset($params['sort']) ? intval($params['sort']) : 1;
    $keyword = isset($params['keyword']) ? $params['keyword'] : '';
    $min_count = isset($params['min_count']) ? intval($params['min_count']) : 0;
    $catgroup_id = isset($params['catgroup_id']) ? intval($params['catgroup_id']) : (isset($params['archiveid']) ? intval($params['archiveid']) : -1);
    $catgroup_item_type = isset($params['_catgroup_item_type']) ? trim((string)$params['_catgroup_item_type']) : '';
    $catgroup_item_id = isset($params['_catgroup_item_id']) ? intval($params['_catgroup_item_id']) : 0;
    $filter_raw = '';
    if (isset($params['filter'])) $filter_raw = trim((string)$params['filter']);
    else if (isset($params['Filter'])) $filter_raw = trim((string)$params['Filter']);
    $exclude_ids = array();
    if ($filter_raw !== '') {
        $parts = explode(',', $filter_raw);
        foreach ($parts as $part) {
            $id = intval(trim($part));
            if ($id > 0) $exclude_ids[$id] = $id;
        }
        $exclude_ids = array_values($exclude_ids);
    }

    // 只有绑定了分页标识(page_key > 0)，才从URL中获取实际页码
    $current_page = 1;
    if ($page_key > 0) {
        // 支持两种格式：1. ?page=1  2. _page1.html
        if (isset($_GET['page']) && is_numeric($_GET['page'])) {
            $current_page = max(1, intval($_GET['page']));
        } else {
            // 从URL路径中提取页码
            $current_url = $_SERVER['REQUEST_URI'];
            if (preg_match('/_page(\d+)\.html$/i', $current_url, $matches)) {
                $current_page = max(1, intval($matches[1]));
            }
        }
    }

    $category_table = findTable($db, ['categories', 'qkcms_category', 'qkcms_category', 'board', 'article_category', 'news_category', 'blog_category', 'post_category', 'site_category', 'content_category', 'product_category', 'cat', 'cats']);
    $categories = array();
    if (get_catgroup_id() > 0) {
        // #region debug-point C:categories-start
        qk_tplrdr_debug_event('C', 'tplrdr.php:getCategoriesData', '[DEBUG] categories start for catgroup request', array(
            'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
            'catgroup_id' => get_catgroup_id(),
            'parent_id' => $parent_id,
            'sort' => $sort,
            'row' => $row,
            'catgroup_item_type' => $catgroup_item_type,
            'catgroup_item_id' => $catgroup_item_id,
            'filter' => $filter_raw
        ));
        // #endregion
    }

    if ($category_table) {
        $field_patterns = array(
            'id' => array('id', 'category_id'),
            'name' => array('name', 'category_name'),
            'description' => array('description'),
            'keywords' => array('keywords', 'tags', 'tag'),
            'parent_id' => array('parent_id', 'pid'),
            'catgroup_id' => array('catgroup_id', 'archiveid'),
            'sort_order' => array('sort_order', 'sort'),
            'thumbnail' => array('thumbnail', 'thumb', 'image', 'img'),
            'custom_url' => array('custom_url', 'customurl', 'url_alias', 'alias'),
            'display' => array('display', 'show_home', 'home_display'),
            'virtual_top' => array('virtual_top'),
            'virtual_top_anchor' => array('virtual_top_anchor')
        );
        
        $field_map = getFieldMap($db, $category_table, $field_patterns);
        $select_fields = array((isset($field_map['id']) ? $field_map['id'] : 'id'));
        foreach (['name', 'description', 'keywords', 'parent_id', 'thumbnail', 'custom_url', 'display', 'catgroup_id', 'virtual_top', 'virtual_top_anchor'] as $field) {
            if (isset($field_map[$field])) $select_fields[] = $field_map[$field];
        }
        
        $conditions = array();
        $display_field = findField($db, $category_table, ['display', 'show_home', 'home_display']);
        if ($display_field && $show_all == 2) $conditions[] = "$display_field = 1";
        if ($catgroup_item_type === 'virtual') return qkcms_request_result_cache('categories', $db, $params, array(), true);
        if ($catgroup_item_type === 'catgroup' && $catgroup_item_id > 0) {
            $grouped_categories = qkcms_get_catgroup_categories_batch($db, $category_table, $field_map, $show_all, $sort, $keyword, $exclude_ids, $min_count);
            $categories = isset($grouped_categories[$catgroup_item_id]) ? $grouped_categories[$catgroup_item_id] : array();
            if ($row > 0) $categories = array_slice($categories, 0, $row);
            return qkcms_request_result_cache('categories', $db, $params, $categories, true);
        } else {
            if ($parent_id >= 0 && isset($field_map['parent_id'])) $conditions[] = $field_map['parent_id'] . " = $parent_id";
            if ($catgroup_id >= 0 && isset($field_map['catgroup_id'])) $conditions[] = $field_map['catgroup_id'] . " = $catgroup_id";
        }
        if (!empty($exclude_ids) && isset($field_map['id'])) $conditions[] = $field_map['id'] . " NOT IN (" . implode(',', $exclude_ids) . ")";

        if (!empty($keyword)) {
            $keyword_condition = [];
            $keyword = $db->real_escape_string($keyword);
            if (isset($field_map['name'])) $keyword_condition[] = $field_map['name'] . " LIKE '%$keyword%'";
            if (isset($field_map['description'])) $keyword_condition[] = $field_map['description'] . " LIKE '%$keyword%'";
            if (isset($field_map['keywords'])) $keyword_condition[] = $field_map['keywords'] . " LIKE '%$keyword%'";
            if (!empty($keyword_condition)) $conditions[] = "(" . implode(' OR ', $keyword_condition) . ")";
        }

        $where_clause = !empty($conditions) ? " WHERE " . implode(' AND ', $conditions) : "";

        if ($page_key > 0) {
            $total_sql = "SELECT COUNT(*) as total FROM $category_table" . $where_clause;
            $total_result = $db->query($total_sql);
            $total = ($total_result && $total_result->num_rows > 0) ? intval($total_result->fetch_assoc()['total']) : 0;
            if ($total_result) $total_result->free();

            global $qkcms_total_records;
            if (!isset($qkcms_total_records)) {
                $qkcms_total_records = array();
            }
            $qkcms_total_records[$page_key] = $total;
            global $qkcms_current_row;
            if (!isset($qkcms_current_row)) {
                $qkcms_current_row = array();
            }
            $qkcms_current_row[$page_key] = $row;
        }

        // 构建分页查询
        $offset = ($current_page - 1) * $row;
        $sql = "SELECT " . implode(', ', $select_fields) . " FROM $category_table" . $where_clause;

        $time_field = findField($db, $category_table, ['created_at', 'updated_at', 'add_time', 'create_time']);
        $sort_field = isset($field_map['sort_order']) ? $field_map['sort_order'] : 'id';
        $category_id_field = isset($field_map['id']) ? $field_map['id'] : 'id';
        $article_table = findTable($db, ['articles', 'posts', 'news', 'blog_posts']);
        $article_cat_field = $article_table ? findField($db, $article_table, ['category_id', 'cat_id', 'cid']) : '';
        $article_time_field = $article_table ? findField($db, $article_table, ['created_at', 'updated_at', 'add_time', 'create_time', 'id']) : '';

        switch ($sort) {
            case 1:
                if ($time_field) $sql .= " ORDER BY $time_field DESC, $category_id_field DESC";
                else $sql .= " ORDER BY $category_id_field DESC";
                break;
            case 2:
                if ($time_field) $sql .= " ORDER BY $time_field ASC, $category_id_field ASC";
                else $sql .= " ORDER BY $category_id_field ASC";
                break;
            case 3:
                $sql .= " ORDER BY $sort_field ASC, $category_id_field ASC";
                break;
            case 4:
                $sql .= " ORDER BY RAND()";
                break;
            case 6:
                if ($article_table && $article_cat_field && $article_time_field) {
                    $latest_article_order = "(SELECT MAX($article_time_field) FROM $article_table WHERE $article_table.$article_cat_field = $category_table.$category_id_field)";
                    $sql .= " ORDER BY COALESCE($latest_article_order, 0) DESC, $sort_field ASC, $category_id_field ASC";
                } else if ($time_field) {
                    $sql .= " ORDER BY $time_field DESC, $category_id_field DESC";
                } else {
                    $sql .= " ORDER BY $category_id_field DESC";
                }
                break;
            default:
                if ($time_field) $sql .= " ORDER BY $time_field DESC, $category_id_field DESC";
                else $sql .= " ORDER BY $category_id_field DESC";
        }

        // 添加分页限制
        $sql .= " LIMIT $offset, $row";

        $result = $db->query($sql);
        if ($result === false && get_catgroup_id() > 0) {
            // #region debug-point C:categories-query-failed
            qk_tplrdr_debug_event('C', 'tplrdr.php:getCategoriesData', '[DEBUG] categories query failed', array(
                'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
                'catgroup_id' => get_catgroup_id(),
                'sql' => $sql,
                'db_error' => isset($db->error) ? $db->error : ''
            ));
            // #endregion
        }
        if ($result) {
            $category_rows = array();
            if ($result->num_rows > 0) {
                while ($category = $result->fetch_assoc()) {
                    $category_rows[] = $category;
                }
            }
            $result->free();

            $category_ids = array();
            foreach ($category_rows as $category) {
                $category_ids[] = $category[isset($field_map['id']) ? $field_map['id'] : 'id'];
            }
            $count_map = ($article_table && $article_cat_field) ? qkcms_fetch_group_count_map($db, $article_table, $article_cat_field, $category_ids) : array();

            foreach ($category_rows as $category) {
                    $category_id = intval($category[isset($field_map['id']) ? $field_map['id'] : 'id']);
                    $count = isset($count_map[$category_id]) ? $count_map[$category_id] : 0;
                    $category_name = isset($field_map['name']) ? $category[$field_map['name']] : '';
                    // 默认生成SVG图标
                    $thumbnail = generate_category_svg_icon($category_name, $category_id);

                    // 检查数据库中是否有缩略图
                    if (isset($field_map['thumbnail']) && isset($category[$field_map['thumbnail']])) {
                        $db_thumbnail = trim($category[$field_map['thumbnail']]);
                        // 只有当缩略图不为空且不是默认路径时才检查
                        if (!empty($db_thumbnail) && $db_thumbnail !== '/assets/images/moren.jpg' && $db_thumbnail !== DEFAULT_THUMBNAIL_PATH) {
                            if (check_file_exists_or_default($db_thumbnail, '') !== '') {
                                $thumbnail = $db_thumbnail;
                            }
                        }
                    }

                    $category_url = "/list_id$category_id.html";
                    if (isset($field_map['custom_url']) && isset($category[$field_map['custom_url']])) {
                        $cat_custom_url_val = trim((string)$category[$field_map['custom_url']]);
                        if ($cat_custom_url_val !== '') {
                            $category_url = '/' . ltrim($cat_custom_url_val, '/');
                        }
                    }

                    $categories[] = [
                        'id' => $category_id,
                        'name' => $category_name,
                        'description' => isset($field_map['description']) ? $category[$field_map['description']] : '',
                        'keywords' => isset($field_map['keywords']) ? $category[$field_map['keywords']] : '',
                        'parent_id' => isset($field_map['parent_id']) ? $category[$field_map['parent_id']] : 0,
                        'url' => $category_url,
                        'count' => $count,
                        'thumbnail' => $thumbnail
                    ];
            }
        }
    }

    if ($sort == 3) {
        usort($categories, function ($a, $b) {
            return $b['count'] - $a['count'];
        });
    }

    return qkcms_request_result_cache('categories', $db, $params, $categories, true);
}

function getCatgroupData($params, $db)
{
    $cached_result = qkcms_request_result_cache('catgroup', $db, $params);
    if ($cached_result !== null) return $cached_result;

    $row = isset($params['row']) ? intval($params['row']) : 0;
    $order = isset($params['order']) ? intval($params['order']) : 1;
    $current = isset($params['current']) ? intval($params['current']) : 0;
    $filter_catgroup_id = 0;
    if (isset($params['id']) && intval($params['id']) > 0) $filter_catgroup_id = intval($params['id']);
    else if (isset($params['catgroup_id']) && intval($params['catgroup_id']) > 0) $filter_catgroup_id = intval($params['catgroup_id']);
    else if (isset($params['archiveid']) && intval($params['archiveid']) > 0) $filter_catgroup_id = intval($params['archiveid']);
    if ($filter_catgroup_id <= 0 && $current === 1) $filter_catgroup_id = get_catgroup_id();
    $filter_raw = '';
    if (isset($params['filter'])) $filter_raw = trim((string)$params['filter']);
    else if (isset($params['Filter'])) $filter_raw = trim((string)$params['Filter']);
    $exclude_ids = array();
    if ($filter_raw !== '') {
        $parts = explode(',', $filter_raw);
        foreach ($parts as $part) {
            $id = intval(trim($part));
            if ($id > 0) $exclude_ids[$id] = $id;
        }
        $exclude_ids = array_values($exclude_ids);
    }
    
    $catgroup_table = findTable($db, ['catgroup', 'qkcms_catgroup']);
    $catgroups = array();
    if ($current === 1 || $filter_catgroup_id > 0 || get_catgroup_id() > 0) {
        // #region debug-point B:catgroup-start
        qk_tplrdr_debug_event('B', 'tplrdr.php:getCatgroupData', '[DEBUG] catgroup start', array(
            'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
            'current' => $current,
            'filter_catgroup_id' => $filter_catgroup_id,
            'resolved_catgroup_id' => get_catgroup_id(),
            'order' => $order,
            'row' => $row,
            'table' => $catgroup_table
        ));
        // #endregion
    }
    
    if ($catgroup_table) {
        $field_patterns = array(
            'id' => array('id'),
            'name' => array('name'),
            'color' => array('color'),
            'sort' => array('sort'),
            'custom_url' => array('custom_url'),
            'seo_title' => array('seo_title'),
            'seo_keywords' => array('seo_keywords'),
            'seo_description' => array('seo_description'),
            'created' => array('created', 'created_at'),
            'updated' => array('updated', 'updated_at')
        );
        
        $field_map = getFieldMap($db, $catgroup_table, $field_patterns);
        $select_fields = array((isset($field_map['id']) ? $field_map['id'] : 'id'));
        foreach (['name', 'color', 'sort', 'custom_url', 'seo_title', 'seo_keywords', 'seo_description', 'created', 'updated'] as $field) {
            if (isset($field_map[$field])) $select_fields[] = $field_map[$field];
        }

        $sql = "SELECT " . implode(', ', $select_fields) . " FROM $catgroup_table";
        $id_field = isset($field_map['id']) ? $field_map['id'] : 'id';
        $conditions = array();
        if ($filter_catgroup_id > 0) {
            $conditions[] = "{$id_field} = " . intval($filter_catgroup_id);
        }
        if (!empty($exclude_ids)) {
            $conditions[] = "{$id_field} NOT IN (" . implode(',', $exclude_ids) . ")";
        }
        if (!empty($conditions)) {
            $sql .= " WHERE " . implode(' AND ', $conditions);
        }
        
        switch ($order) {
            case 1:
                $created_field = isset($field_map['created']) ? $field_map['created'] : $id_field;
                $sql .= " ORDER BY $created_field DESC, $id_field DESC";
                break;
            case 2:
                $updated_field = isset($field_map['updated']) ? $field_map['updated'] : $id_field;
                $sql .= " ORDER BY $updated_field DESC, $id_field DESC";
                break;
            default:
                $sort_field = isset($field_map['sort']) ? $field_map['sort'] : $id_field;
                $sql .= " ORDER BY $sort_field ASC, $id_field ASC";
        }
        
        if ($row > 0) {
            $sql .= " LIMIT $row";
        }
        
        $result = $db->query($sql);
        if ($result === false) {
            // #region debug-point B:catgroup-query-failed
            qk_tplrdr_debug_event('B', 'tplrdr.php:getCatgroupData', '[DEBUG] catgroup query failed', array(
                'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
                'filter_catgroup_id' => $filter_catgroup_id,
                'resolved_catgroup_id' => get_catgroup_id(),
                'sql' => $sql,
                'db_error' => isset($db->error) ? $db->error : ''
            ));
            // #endregion
        }
        if ($result) {
            if ($result->num_rows > 0) {
                $real_groups = array();
                while ($catgroup = $result->fetch_assoc()) {
                    $group_id = intval($catgroup[isset($field_map['id']) ? $field_map['id'] : 'id']);
                    $group_keywords_raw = isset($field_map['seo_keywords']) ? $catgroup[$field_map['seo_keywords']] : '';
                    $group_keywords_items = qkcms_parse_keywords($group_keywords_raw, 5);
                    $group_keywords = implode(',', $group_keywords_items);
                    $group_keywords_links = '';
                    foreach ($group_keywords_items as $group_keyword_item) {
                        $group_keywords_links .= '<a href="/tag.html?tag=' . urlencode($group_keyword_item) . '" class="tag3 catgroup-tag">' . htmlspecialchars($group_keyword_item) . '</a>';
                    }
                    $group_url = '/cg_id' . $group_id . '.html';
                    if (isset($field_map['custom_url']) && isset($catgroup[$field_map['custom_url']])) {
                        $group_custom_url = trim((string)$catgroup[$field_map['custom_url']]);
                        if ($group_custom_url !== '') $group_url = '/' . ltrim($group_custom_url, '/');
                    }
                    $real_groups[] = [
                        'id' => $group_id,
                        'url' => $group_url,
                        'item_type' => 'catgroup',
                        'group_name' => isset($field_map['name']) ? $catgroup[$field_map['name']] : '',
                        'color' => isset($field_map['color']) ? $catgroup[$field_map['color']] : '',
                        'sort' => isset($field_map['sort']) ? $catgroup[$field_map['sort']] : '',
                        'group_title' => isset($field_map['seo_title']) ? $catgroup[$field_map['seo_title']] : '',
                        'group_keywords' => $group_keywords,
                        'group_keywords_links' => $group_keywords_links,
                        'group_description' => isset($field_map['seo_description']) ? $catgroup[$field_map['seo_description']] : '',
                        'keywords' => $group_keywords,
                        'keywords_links' => $group_keywords_links,
                        'description' => isset($field_map['seo_description']) ? $catgroup[$field_map['seo_description']] : '',
                        'created' => isset($field_map['created']) ? $catgroup[$field_map['created']] : '',
                        'updated' => isset($field_map['updated']) ? $catgroup[$field_map['updated']] : ''
                    ];
                }
                $catgroups = $real_groups;
                if ($current !== 1 && $filter_catgroup_id <= 0) {
                    $category_table = findTable($db, ['categories', 'qkcms_category', 'qkcms_category', 'board', 'article_category', 'news_category', 'blog_category', 'post_category', 'site_category', 'content_category', 'product_category', 'cat', 'cats']);
                    if ($category_table) {
                        $category_field_patterns = array(
                            'id' => array('id', 'category_id'),
                            'name' => array('name', 'category_name'),
                            'description' => array('description'),
                            'keywords' => array('keywords', 'tags', 'tag'),
                            'catgroup_id' => array('catgroup_id', 'archiveid'),
                            'sort_order' => array('sort_order', 'sort'),
                            'custom_url' => array('custom_url', 'customurl', 'url_alias', 'alias'),
                            'display' => array('display', 'show_home', 'home_display'),
                            'virtual_top' => array('virtual_top'),
                            'virtual_top_anchor' => array('virtual_top_anchor'),
                            'seo_title' => array('seo_title')
                        );
                        $category_field_map = getFieldMap($db, $category_table, $category_field_patterns);
                        if (isset($category_field_map['id']) && isset($category_field_map['name']) && isset($category_field_map['virtual_top'])) {
                            $category_select_fields = array($category_field_map['id']);
                            foreach (['name', 'description', 'keywords', 'catgroup_id', 'sort_order', 'custom_url', 'display', 'virtual_top', 'virtual_top_anchor', 'seo_title'] as $field) {
                                if (isset($category_field_map[$field])) $category_select_fields[] = $category_field_map[$field];
                            }
                            $virtual_conditions = array($category_field_map['virtual_top'] . " = 1");
                            if (isset($category_field_map['catgroup_id'])) $virtual_conditions[] = $category_field_map['catgroup_id'] . " = 0";
                            if (isset($category_field_map['display'])) $virtual_conditions[] = $category_field_map['display'] . " = 1";
                            $virtual_sql = "SELECT " . implode(', ', $category_select_fields) . " FROM $category_table WHERE " . implode(' AND ', $virtual_conditions);
                            $virtual_sort_field = isset($category_field_map['sort_order']) ? $category_field_map['sort_order'] : $category_field_map['id'];
                            $virtual_sql .= " ORDER BY $virtual_sort_field ASC, " . $category_field_map['id'] . " DESC";
                            $virtual_result = $db->query($virtual_sql);
                            if ($virtual_result) {
                                $virtual_before = array();
                                $virtual_after = array();
                                $group_lookup = array();
                                foreach ($real_groups as $group_item) {
                                    $group_lookup[intval($group_item['id'])] = true;
                                }
                                while ($virtual_item = $virtual_result->fetch_assoc()) {
                                    $anchor = isset($category_field_map['virtual_top_anchor']) ? intval($virtual_item[$category_field_map['virtual_top_anchor']]) : 0;
                                    $anchor_id = abs($anchor);
                                    if ($anchor_id <= 0 || !isset($group_lookup[$anchor_id])) continue;
                                    $virtual_id = intval($virtual_item[$category_field_map['id']]);
                                    $virtual_keywords_raw = isset($category_field_map['keywords']) ? $virtual_item[$category_field_map['keywords']] : '';
                                    $virtual_keywords_items = qkcms_parse_keywords($virtual_keywords_raw, 5);
                                    $virtual_keywords = implode(',', $virtual_keywords_items);
                                    $virtual_keywords_links = '';
                                    foreach ($virtual_keywords_items as $virtual_keyword_item) {
                                        $virtual_keywords_links .= '<a href="/tag.html?tag=' . urlencode($virtual_keyword_item) . '" class="tag3 catgroup-tag">' . htmlspecialchars($virtual_keyword_item) . '</a>';
                                    }
                                    $virtual_url = '/list_id' . $virtual_id . '.html';
                                    if (isset($category_field_map['custom_url']) && isset($virtual_item[$category_field_map['custom_url']])) {
                                        $virtual_custom_url = trim((string)$virtual_item[$category_field_map['custom_url']]);
                                        if ($virtual_custom_url !== '') $virtual_url = '/' . ltrim($virtual_custom_url, '/');
                                    }
                                    $mixed_item = array(
                                        'id' => $virtual_id,
                                        'url' => $virtual_url,
                                        'item_type' => 'virtual',
                                        'anchor_id' => $anchor_id,
                                        'group_name' => isset($category_field_map['name']) ? $virtual_item[$category_field_map['name']] : '',
                                        'color' => '',
                                        'sort' => isset($category_field_map['sort_order']) ? $virtual_item[$category_field_map['sort_order']] : '',
                                        'group_title' => isset($category_field_map['seo_title']) ? $virtual_item[$category_field_map['seo_title']] : '',
                                        'group_keywords' => $virtual_keywords,
                                        'group_keywords_links' => $virtual_keywords_links,
                                        'group_description' => isset($category_field_map['description']) ? $virtual_item[$category_field_map['description']] : '',
                                        'keywords' => $virtual_keywords,
                                        'keywords_links' => $virtual_keywords_links,
                                        'description' => isset($category_field_map['description']) ? $virtual_item[$category_field_map['description']] : '',
                                        'created' => '',
                                        'updated' => ''
                                    );
                                    if ($anchor < 0) {
                                        if (!isset($virtual_before[$anchor_id])) $virtual_before[$anchor_id] = array();
                                        $virtual_before[$anchor_id][] = $mixed_item;
                                    } else {
                                        if (!isset($virtual_after[$anchor_id])) $virtual_after[$anchor_id] = array();
                                        $virtual_after[$anchor_id][] = $mixed_item;
                                    }
                                }
                                $virtual_result->free();
                                $mixed_groups = array();
                                foreach ($real_groups as $group_item) {
                                    $group_id = intval($group_item['id']);
                                    if (!empty($virtual_before[$group_id])) {
                                        foreach ($virtual_before[$group_id] as $virtual_item) $mixed_groups[] = $virtual_item;
                                    }
                                    $mixed_groups[] = $group_item;
                                    if (!empty($virtual_after[$group_id])) {
                                        foreach ($virtual_after[$group_id] as $virtual_item) $mixed_groups[] = $virtual_item;
                                    }
                                }
                                $catgroups = $mixed_groups;
                            }
                        }
                    }
                }
            }
            $result->free();
        }
    }
    
    return qkcms_request_result_cache('catgroup', $db, $params, $catgroups, true);
}

function getCountFromTable($db, $table, $count_field, $content_id, $content_type, $field_map, $comment_field, $comment_data)
{
    $count = 0;
    // 对于评论，始终从 comments 表的 likes 字段读取点赞数，而不是从 likes 表统计
    if ($content_type === 'comment') {
        $count = isset($field_map[$comment_field]) && isset($comment_data[$field_map[$comment_field]]) ?
            intval($comment_data[$field_map[$comment_field]]) : 0;
    } else {
        $table_exists = table_exists($db, $table);
        if ($table_exists) {
            $sql = "SELECT COUNT(*) as $count_field FROM $table WHERE content_type = '$content_type' AND content_id = $content_id";
            $result = $db->query($sql);
            if ($result && $result->num_rows > 0) {
                $data = $result->fetch_assoc();
                $count = isset($data[$count_field]) ? intval($data[$count_field]) : 0;
                $result->free();
            }
        } else {
            $count = isset($field_map[$comment_field]) && isset($comment_data[$field_map[$comment_field]]) ?
                intval($comment_data[$field_map[$comment_field]]) : 0;
        }
    }
    return $count;
}

function getCommentsData($params, $db)
{
    /**
     * 获取评论数据
     * @param array $params 参数数组
     * @param object $db 数据库连接对象
     * @return array 评论数据数组
     */
    $row = isset($params['row']) ? intval($params['row']) : 10;
    $page = isset($params['page']) ? intval($params['page']) : 1;
    $article_id_param = isset($params['article_id']) ? intval($params['article_id']) : (isset($params['id']) ? intval($params['id']) : 0);

    // 获取是否保留HTML标签的参数，默认为1（保留）
    $allow_html = isset($params['allow_html']) ? intval($params['allow_html']) : 1;

    $article_id = 0;
    if ($article_id_param == 1) {
        $article_id = 0;
    } elseif ($article_id_param == 2) {
        if (isset($_GET['id'])) $article_id = intval($_GET['id']);
        elseif (isset($_GET['article_id'])) $article_id = intval($_GET['article_id']);
    } else {
        $article_id = $article_id_param;
    }

    $user_id = isset($params['user_id']) ? intval($params['user_id']) : 0;
    $keyword = isset($params['keyword']) ? $params['keyword'] : '';
    $days_old = isset($params['days_old']) ? intval($params['days_old']) : 0;
    $order = isset($params['order']) ? intval($params['order']) : 1;
    $date_format = isset($params['date_format']) ? intval($params['date_format']) : 1;
    $type = isset($params['type']) ? $params['type'] : '';

    $comments = [];
    $total = 0;
    $comment_table = findTable($db, ['comments', 'qkcms_comment', 'qkcms_comment', 'article_comment']);

    if ($comment_table) {
        $user_table = findTable($db, ['users', 'members', 'qkcms_user', 'qkcms_user']);
        $article_table = findTable($db, ['articles', 'posts', 'news', 'blog_posts']);

        $field_patterns = [
            'id' => ['id', 'comment_id'],
            'content' => ['content', 'comment_content'],
            'user_id' => ['user_id', 'uid', 'author_id'],
            'article_id' => ['article_id', 'post_id', 'content_id'],
            'created_at' => ['created_at', 'create_time', 'comment_time'],
            'likes' => ['likes', 'like_count', 'praise_count'],
            'reports' => ['reports', 'report_count', 'jubao_count'],
            'dislike' => ['dislike_count', 'dislike'],
            'parent_id' => ['parent_id'],
            'reply_to' => ['reply_to', 'reply_username']
        ];

        $field_map = getFieldMap($db, $comment_table, $field_patterns);
        $select_fields = [(isset($field_map['id']) ? $field_map['id'] : 'id')];
        foreach (['content', 'user_id', 'article_id', 'created_at', 'likes', 'reports', 'dislike', 'parent_id', 'reply_to'] as $field) {
            if (isset($field_map[$field])) $select_fields[] = $field_map[$field];
        }

        $conditions = [];
        if ($article_id > 0 && isset($field_map['article_id'])) $conditions[] = $field_map['article_id'] . " = $article_id";

        $status_field = findField($db, $comment_table, ['status', 'check_status', 'approve_status']);
        if ($status_field) $conditions[] = $status_field . " = 1";

        if (isset($field_map['parent_id'])) $conditions[] = $field_map['parent_id'] . " = 0";
        if ($user_id > 0 && isset($field_map['user_id'])) $conditions[] = $field_map['user_id'] . " = $user_id";

        // 添加type字段条件
        $type_field = findField($db, $comment_table, ['type']);
        if ($type_field && !empty($type)) {
            $type = $db->real_escape_string($type);
            $conditions[] = $type_field . " = '$type'";
        }

        if (!empty($keyword) && isset($field_map['content'])) {
            $keyword = $db->real_escape_string($keyword);
            $conditions[] = $field_map['content'] . " LIKE '%$keyword%'";
        }

        if ($days_old > 0 && isset($field_map['created_at'])) {
            $time_threshold = date('Y-m-d H:i:s', strtotime("-$days_old days"));
            $conditions[] = $field_map['created_at'] . " >= '$time_threshold'";
        }

        $offset = ($page - 1) * $row;
        $sort_field = isset($field_map['created_at']) ? $field_map['created_at'] : 'id';
        $condition_string = !empty($conditions) ? " WHERE " . implode(' AND ', $conditions) : '';

        $count_sql = "SELECT COUNT(*) as total FROM $comment_table" . $condition_string;
        $count_result = $db->query($count_sql);
        if ($count_result && $count_result->num_rows > 0) {
            $count_row = $count_result->fetch_assoc();
            $total = intval($count_row['total']);
            $count_result->free();
        }

        $sql = "";
        switch ($order) {
            case 2:
                $order_sql = " ORDER BY $sort_field ASC";
                $sql = "SELECT " . implode(', ', $select_fields) . " FROM $comment_table" . $condition_string . $order_sql . " LIMIT $offset, $row";
                break;
            case 3:
                $like_field = findField($db, $comment_table, ['likes', 'like_count', 'praise_count']);
                if ($like_field) {
                    $order_sql = " ORDER BY $like_field DESC, $sort_field DESC";
                    $sql = "SELECT " . implode(', ', $select_fields) . " FROM $comment_table" . $condition_string . $order_sql . " LIMIT $offset, $row";
                } else {
                    $likes_table_exists = table_exists($db, 'likes');
                    if ($likes_table_exists) {
                        $hot_condition_string = $condition_string;
                        $sql = "SELECT c.*, SUM(CASE WHEN l.type = 'like' THEN 1 ELSE 0 END) AS like_count FROM $comment_table c LEFT JOIN likes l ON c.id = l.content_id AND l.content_type = 'comment'";
                        $sql .= $hot_condition_string;
                        $sql .= " GROUP BY c.id ORDER BY like_count DESC, $sort_field DESC LIMIT $offset, $row";
                    } else {
                        $order_sql = " ORDER BY $sort_field DESC";
                        $sql = "SELECT " . implode(', ', $select_fields) . " FROM $comment_table" . $condition_string . $order_sql . " LIMIT $offset, $row";
                    }
                }
                break;
            case 1:
            default:
                $order_sql = " ORDER BY $sort_field DESC";
                $sql = "SELECT " . implode(', ', $select_fields) . " FROM $comment_table" . $condition_string . $order_sql . " LIMIT $offset, $row";
                break;
        }

        if (empty($sql)) return array();
        $result = $db->query($sql);
        if (!$result) return array();

        if ($result->num_rows > 0) {
            $comment_id_counter = 1;
            while ($comment = $result->fetch_assoc()) {
                $comment_id = $comment[isset($field_map['id']) ? $field_map['id'] : 'id'];
                $content = isset($field_map['content']) ? $comment[$field_map['content']] : '';
                $created_at = isset($field_map['created_at']) ? $comment[$field_map['created_at']] : time();
                $parent_id = isset($field_map['parent_id']) ? intval($field_map['parent_id']) : 0;

                // 根据参数决定是否保留HTML标签
                if ($allow_html == 0) {
                    // 不保留HTML标签，只保留链接
                    $content = strip_tags($content, '<a>');
                } else {
                    // 保留图片标签，移除其他HTML标签
                    $content = preg_replace_callback('/<img([^>]*)src=["\']([^"\']*)["\']([^>]*)>/i', function ($matches) {
                        $img_src = $matches[2];
                        $valid_img_src = check_file_exists_or_default($img_src);
                        return '<img' . $matches[1] . 'src="' . $valid_img_src . '"' . $matches[3] . '>';
                    }, $content);
                }

                $author = null;
                $avatar = DEFAULT_AVATAR_PATH;
                $user_level = '游客';

                if (isset($field_map['user_id']) && $comment[$field_map['user_id']] > 0 && $user_table) {
                    $user_id_val = $comment[$field_map['user_id']];
                    $user_sql = "SELECT * FROM $user_table WHERE id = $user_id_val LIMIT 1";
                    $user_result = $db->query($user_sql);
                    if ($user_result && $user_result->num_rows > 0) {
                        $user_info = $user_result->fetch_assoc();
                        $name_field = findField($db, $user_table, ['nickname', 'username', 'user_name', 'loginname', 'name']);
                        if ($name_field && isset($user_info[$name_field]) && !empty($user_info[$name_field])) {
                            $author = $user_info[$name_field];
                        }
                        $avatar_field = findField($db, $user_table, ['avatar', 'user_avatar', 'headimg', 'headimgurl']);
                        if ($avatar_field && isset($user_info[$avatar_field]) && !empty($user_info[$avatar_field])) {
                            $avatar = $user_info[$avatar_field];
                        }
                        $levels_id = isset($user_info['levels_id']) ? intval($user_info['levels_id']) : 1;
                        if ($levels_id == 2) $user_level = 'VIP用户';
                        elseif ($levels_id == 3) $user_level = '管理员';
                        $user_result->free();
                    }
                }

                $article_title = null;
                $article_url = null;
                if (isset($field_map['article_id']) && $comment[$field_map['article_id']] > 0 && $article_table) {
                    $article_id_val = $comment[$field_map['article_id']];
                    $title_field = findField($db, $article_table, ['title', 'subject', 'article_title', 'post_title']) ?: 'title';
                    $article_sql = "SELECT $title_field FROM $article_table WHERE id = $article_id_val LIMIT 1";
                    $article_result = $db->query($article_sql);
                    if ($article_result && $article_result->num_rows > 0) {
                        $article_info = $article_result->fetch_assoc();
                        $article_title = $article_info[$title_field];
                        $article_url = "post_id$article_id_val.html";
                        $article_result->free();
                    }
                }

                $formatted_date = '';
                if ($date_format == 2) {
                    if (is_numeric($created_at)) $formatted_date = date('Y-n-j', $created_at);
                    else $formatted_date = date('Y-n-j', strtotime($created_at));
                } else {
                    $formatted_date = formatTime($created_at);
                }

                $like_count = getCountFromTable($db, 'likes', 'likes_count', $comment_id, 'comment', $field_map, 'likes', $comment);
                $report_count = getCountFromTable($db, 'reports', 'report_count', $comment_id, 'comment', $field_map, 'reports', $comment);
                $dislike_count = getCountFromTable($db, 'dislike', 'dislike_count', $comment_id, 'comment', $field_map, 'dislike', $comment);

                // 获取主评论用户ID
                $author_id = isset($field_map['user_id']) ? $comment[$field_map['user_id']] : 0;

                $comment_item = [
                    'id' => $comment_id,
                    'content' => $content,
                    'author' => $author,
                    'author_id' => $author_id,
                    'avatar' => $avatar,
                    'date' => $formatted_date,
                    'article_title' => htmlspecialchars($article_title),
                    'article_url' => $article_url,
                    'like_count' => $like_count,
                    'report_count' => $report_count,
                    'dislike_count' => $dislike_count,
                    'parent_id' => $parent_id,
                    'user_level' => $user_level,
                    'replies' => []
                ];

                if (isset($field_map['parent_id'])) {
                    $reply_sql = "SELECT " . implode(', ', $select_fields) . " FROM $comment_table WHERE " . $field_map['parent_id'] . " = $comment_id";
                    if ($status_field) $reply_sql .= " AND $status_field = 1";
                    $reply_sql .= " ORDER BY $sort_field ASC";

                    $reply_result = $db->query($reply_sql);
                    $replies = [];

                    if ($reply_result && $reply_result->num_rows > 0) {
                        $reply_id_counter = 1;
                        while ($reply = $reply_result->fetch_assoc()) {
                            $reply_id = $reply[isset($field_map['id']) ? $field_map['id'] : 'id'];
                            $reply_content = isset($field_map['content']) ? $reply[$field_map['content']] : '';
                            $reply_created_at = isset($field_map['created_at']) ? $reply[$field_map['created_at']] : time();
                            $reply_author = null;
                            $reply_avatar = DEFAULT_AVATAR_PATH;

                            // 根据参数决定是否保留HTML标签
                            if ($allow_html == 0) {
                                // 不保留HTML标签，只保留链接
                                $reply_content = strip_tags($reply_content, '<a>');
                            } else {
                                // 保留图片标签，移除其他HTML标签
                                $reply_content = preg_replace_callback('/<img([^>]*)src=["\']([^"\']*)["\']([^>]*)>/i', function ($matches) {
                                    $img_src = $matches[2];
                                    $valid_img_src = check_file_exists_or_default($img_src);
                                    return '<img' . $matches[1] . 'src="' . $valid_img_src . '"' . $matches[3] . '>';
                                }, $reply_content);
                            }

                            if (isset($field_map['user_id']) && $reply[$field_map['user_id']] > 0 && $user_table) {
                                $user_id_val = $reply[$field_map['user_id']];
                                $user_sql = "SELECT * FROM $user_table WHERE id = $user_id_val LIMIT 1";
                                $user_result = $db->query($user_sql);
                                if ($user_result && $user_result->num_rows > 0) {
                                    $user_info = $user_result->fetch_assoc();
                                    $name_field = findField($db, $user_table, ['nickname', 'username', 'user_name', 'loginname', 'name']);
                                    if ($name_field && isset($user_info[$name_field]) && !empty($user_info[$name_field])) {
                                        $reply_author = $user_info[$name_field];
                                    }
                                    $avatar_field = findField($db, $user_table, ['avatar', 'user_avatar', 'headimg', 'headimgurl']);
                                    if ($avatar_field && isset($user_info[$avatar_field]) && !empty($user_info[$avatar_field])) {
                                        $reply_avatar = $user_info[$avatar_field];
                                    }
                                    $reply_levels_id = isset($user_info['levels_id']) ? intval($user_info['levels_id']) : 1;
                                    $reply_level = '游客';
                                    if ($reply_levels_id == 2) $reply_level = 'VIP用户';
                                    elseif ($reply_levels_id == 3) $reply_level = '管理员';
                                    $user_result->free();
                                }
                            }

                            $reply_formatted_date = '';
                            if ($date_format == 2) {
                                if (is_numeric($reply_created_at)) $reply_formatted_date = date('Y-n-j', $reply_created_at);
                                else $reply_formatted_date = date('Y-n-j', strtotime($reply_created_at));
                            } else {
                                $reply_formatted_date = formatTime($reply_created_at);
                            }

                            $reply_to_username = $author;
                            if (isset($field_map['reply_to']) && isset($reply[$field_map['reply_to']]) && !empty($reply[$field_map['reply_to']])) {
                                $reply_to_username = $reply[$field_map['reply_to']];
                            }

                            $reply_like_count = getCountFromTable($db, 'likes', 'likes_count', $reply_id, 'comment', $field_map, 'likes', $reply);
                            $reply_dislike_count = getCountFromTable($db, 'dislike', 'dislike_count', $reply_id, 'comment', $field_map, 'dislike', $reply);

                            // 获取回复评论用户ID
                            $reply_user_id = isset($field_map['user_id']) ? $reply[$field_map['user_id']] : 0;

                            $replies[] = [
                                'id' => $reply_id,
                                'content' => $reply_content,
                                'author' => $reply_author,
                                'user_id' => $reply_user_id,
                                'avatar' => $reply_avatar,
                                'date' => $reply_formatted_date,
                                'created_at' => $reply_created_at,
                                'parent_id' => $comment_id,
                                'reply_to' => $reply_to_username,
                                'like_count' => $reply_like_count,
                                'dislike_count' => $reply_dislike_count,
                                'level' => isset($reply_level) ? $reply_level : '游客'
                            ];
                            $reply_id_counter++;
                        }
                        $reply_result->free();
                    }

                    $comment_item['replies'] = $replies;
                }

                $comments[] = $comment_item;
                $comment_id_counter++;
            }
        }
        $result->free();
    }

    return [
        'comments' => $comments,
        'total' => $total
    ];
}

function getHotTagsData($params, $db)
{
    $cached_result = qkcms_request_result_cache('tags', $db, $params);
    if ($cached_result !== null) return $cached_result;
    $row = isset($params['row']) ? intval($params['row']) : 10;
    $min_count = isset($params['min_count']) ? intval($params['min_count']) : 0;
    $max_length = isset($params['max_length']) ? intval($params['max_length']) : 0;
    $order = isset($params['order']) ? intval($params['order']) : 1;
    $tags = [];
    $article_table = findTable($db, ['articles', 'posts', 'news', 'blog_posts', 'content']);

    if ($article_table) {
        $keyword_field = findField($db, $article_table, ['keywords', 'tags', 'keyword']);
        if ($keyword_field) {
            $all_keywords = [];
            $time_field = findField($db, $article_table, ['created_at', 'create_time', 'post_time', 'pub_time']);

            $sql = "SELECT $keyword_field";
            if ($time_field && $order == 1) $sql .= ", $time_field";
            $sql .= " FROM $article_table WHERE $keyword_field IS NOT NULL AND $keyword_field != ''";
            if ($time_field && $order == 1) $sql .= " ORDER BY $time_field DESC";

            $result = $db->query($sql);
            if ($result) {
                $keyword_times = [];
                while ($row_data = $result->fetch_assoc()) {
                    $keywords_str = trim($row_data[$keyword_field]);
                    if (!empty($keywords_str)) {
                        $keywords = preg_split('/[,，;；\s]+/', $keywords_str, -1, PREG_SPLIT_NO_EMPTY);
                        foreach ($keywords as $keyword) {
                            $keyword = trim($keyword);
                            if (mb_strlen($keyword, 'UTF-8') > 0 && $keyword !== '') {
                                if (!isset($all_keywords[$keyword])) $all_keywords[$keyword] = 0;
                                $all_keywords[$keyword]++;
                                if ($time_field && $order == 1) {
                                    if (!isset($keyword_times[$keyword]) || (isset($row_data[$time_field]) && $row_data[$time_field] > $keyword_times[$keyword])) {
                                        $keyword_times[$keyword] = isset($row_data[$time_field]) ? $row_data[$time_field] : time();
                                    }
                                }
                            }
                        }
                    }
                }
                $result->free();
            }

            if (!empty($all_keywords)) {
                switch ($order) {
                    case 1:
                        $sorted_keys = array_keys($all_keywords);
                        usort($sorted_keys, function ($a, $b) use ($keyword_times) {
                            $timeA = isset($keyword_times[$a]) ? $keyword_times[$a] : 0;
                            $timeB = isset($keyword_times[$b]) ? $keyword_times[$b] : 0;
                            return $timeB - $timeA;
                        });
                        $sorted_keywords = [];
                        foreach ($sorted_keys as $key) $sorted_keywords[$key] = $all_keywords[$key];
                        $all_keywords = $sorted_keywords;
                        break;
                    case 2:
                    default:
                        arsort($all_keywords);
                        break;
                    case 3:
                        uksort($all_keywords, function ($a, $b) {
                            return rand(-1, 1);
                        });
                        break;
                }

                $all_keywords = array_slice($all_keywords, 0, $row, true);
                foreach ($all_keywords as $name => $count) {
                    $name = trim($name);
                    if (mb_strlen($name, 'UTF-8') > 0 && $name !== '' && ($max_length == 0 || mb_strlen($name, 'UTF-8') <= $max_length) && $count >= $min_count) {
                        $tags[] = ['name' => htmlspecialchars($name), 'count' => $count, 'url' => "tag.html?tag=" . urlencode($name)];
                    }
                }
            }
        }
    }
    return qkcms_request_result_cache('tags', $db, $params, $tags, true);
}

function getLatestMembersData($params, $db)
{
    $cached_result = qkcms_request_result_cache('members', $db, $params);
    if ($cached_result !== null) return $cached_result;
    $row = isset($params['row']) ? intval($params['row']) : 10;
    $type = isset($params['type']) ? intval($params['type']) : 1;
    $keyword = isset($params['keyword']) ? $params['keyword'] : '';
    $days_old = isset($params['days_old']) ? intval($params['days_old']) : 0;
    $members = [];

    $user_table = findTable($db, ['users', 'members', 'qkcms_user', 'qkcms_user', 'admin_user', 'site_user']);
    if ($user_table) {
        $field_patterns = [
            'id' => ['id', 'user_id'],
            'username' => ['username', 'user_name', 'nickname'],
            'avatar' => ['avatar', 'user_avatar'],
            'created_at' => ['created_at', 'create_time', 'reg_time', 'register_time'],
            'last_login' => ['last_login', 'last_login_time', 'lastvisit']
        ];
        $field_map = getFieldMap($db, $user_table, $field_patterns);

        $sql = "SELECT " . (isset($field_map['id']) ? $field_map['id'] : 'id');
        if (isset($field_map['username'])) $sql .= ", " . $field_map['username'];
        if (isset($field_map['avatar'])) $sql .= ", " . $field_map['avatar'];
        if (isset($field_map['created_at'])) $sql .= ", " . $field_map['created_at'];
        if (isset($field_map['last_login'])) $sql .= ", " . $field_map['last_login'];
        $sql .= ", levels_id";
        $sql .= " FROM $user_table";

        $conditions = [];
        if (!empty($keyword)) {
            $keyword_condition = [];
            $keyword = $db->real_escape_string($keyword);
            if (isset($field_map['username'])) $keyword_condition[] = $field_map['username'] . " LIKE '%$keyword%'";
            if (!empty($keyword_condition)) $conditions[] = "(" . implode(' OR ', $keyword_condition) . ")";
        }
        if ($days_old > 0 && isset($field_map['created_at'])) {
            $time_threshold = date('Y-m-d H:i:s', strtotime("-$days_old days"));
            $conditions[] = $field_map['created_at'] . " >= '$time_threshold'";
        }
        if (!empty($conditions)) $sql .= " WHERE " . implode(' AND ', $conditions);

        switch ($type) {
            case 1:
                $order_field = isset($field_map['created_at']) ? $field_map['created_at'] : 'id';
                $sql .= " ORDER BY $order_field DESC";
                break;
            case 2:
                $order_field = isset($field_map['last_login']) ? $field_map['last_login'] : (isset($field_map['created_at']) ? $field_map['created_at'] : 'id');
                $sql .= " ORDER BY $order_field DESC";
                break;
            case 3:
                $sql .= " ORDER BY RAND()";
                break;
            case 4:
                // 最近访问文章的用户
                $sql = "SELECT u." . (isset($field_map['id']) ? $field_map['id'] : 'id');
                if (isset($field_map['username'])) $sql .= ", " . $field_map['username'];
                if (isset($field_map['avatar'])) $sql .= ", " . $field_map['avatar'];
                if (isset($field_map['created_at'])) $sql .= ", " . $field_map['created_at'];
                if (isset($field_map['last_login'])) $sql .= ", " . $field_map['last_login'];
                $sql .= ", u.levels_id";
                $sql .= " FROM $user_table u";
                $sql .= " JOIN usvisits av ON u.id = av.visitorid";
                $sql .= " WHERE av.visitedid < 0";
                if (!empty($conditions)) $sql .= " AND " . implode(' AND ', $conditions);
                $sql .= " GROUP BY u.id";
                $sql .= " ORDER BY MAX(av.visittime) DESC";
                break;
            default:
                $order_field = isset($field_map['created_at']) ? $field_map['created_at'] : 'id';
                $sql .= " ORDER BY $order_field DESC";
        }

        $sql .= " LIMIT $row";
        $result = $db->query($sql);
        if ($result) {
            $user_rows = [];
            if ($result->num_rows > 0) {
                while ($user = $result->fetch_assoc()) {
                    $user_rows[] = $user;
                }
            }
            $result->free();

            $user_ids = [];
            foreach ($user_rows as $user) {
                $user_ids[] = $user[isset($field_map['id']) ? $field_map['id'] : 'id'];
            }
            $stats_map = qkcms_get_user_statistics_map($user_ids, $db);

            foreach ($user_rows as $user) {
                $user_id = intval($user[isset($field_map['id']) ? $field_map['id'] : 'id']);
                $stats = isset($stats_map[$user_id]) ? $stats_map[$user_id] : [
                    'like_count' => 0,
                    'report_count' => 0,
                    'share_count' => 0,
                    'post_count' => 0
                ];
                $levels_id = isset($user['levels_id']) ? intval($user['levels_id']) : 1;
                $level = '游客';
                if ($levels_id == 2) $level = 'VIP用户';
                elseif ($levels_id == 3) $level = '管理员';

                $members[] = [
                    'id' => $user_id,
                    'username' => isset($field_map['username']) ? $user[$field_map['username']] : '',
                    'avatar' => isset($field_map['avatar']) && !empty($user[$field_map['avatar']]) ? $user[$field_map['avatar']] : DEFAULT_AVATAR_PATH,
                    'created_at' => isset($field_map['created_at']) ? $user[$field_map['created_at']] : null,
                    'last_login' => isset($field_map['last_login']) && !empty($user[$field_map['last_login']]) ? $user[$field_map['last_login']] : null,
                    'level' => $level,
                    'like_count' => $stats['like_count'],
                    'report_count' => $stats['report_count'],
                    'share_count' => $stats['share_count'],
                    'post_count' => $stats['post_count'],
                    'user_center_url' => "/Member_id{$user_id}.html"
                ];
            }
        }
    }
    return qkcms_request_result_cache('members', $db, $params, $members, true);
}

/**
 * 获取搜索词数据
 * @param array $params 参数数组，包含row（条数）和order（排序方式）
 * @param object $db 数据库连接对象
 * @return array 搜索词数据数组
 */
function getSearchData($params, $db)
{
    $row = isset($params['row']) ? intval($params['row']) : 5;
    $order = isset($params['order']) ? intval($params['order']) : 1;
    $search_data = [];

    // 查找search表
    $search_table = findTable($db, ['search', 'searches', 'search_log', 'search_history']);
    if (!$search_table) {
        return $search_data;
    }

    // 查找users表用于获取用户名
    $user_table = findTable($db, ['users', 'members', 'qkcms_user', 'qkcms_user', 'admin_user', 'site_user']);

    // 第一步：统计每个搜索词的出现次数
    $search_term_counts = [];
    $count_sql = "SELECT search_term, COUNT(*) as count FROM $search_table GROUP BY search_term";
    $count_result = $db->query($count_sql);
    if ($count_result && $count_result->num_rows > 0) {
        while ($row_data = $count_result->fetch_assoc()) {
            $search_term_counts[$row_data['search_term']] = $row_data['count'];
        }
        $count_result->close();
    }

    // 第二步：获取搜索词数据，根据排序方式处理
    switch ($order) {
        case 1:
            // 最新的搜索词
            // 先按search_term分组获取最新的一条记录，然后按时间排序
            $sql = "SELECT s.id, s.username, s.search_term, s.search_time 
                   FROM $search_table s 
                   JOIN (SELECT search_term, MAX(search_time) as max_time FROM $search_table GROUP BY search_term) t 
                   ON s.search_term = t.search_term AND s.search_time = t.max_time 
                   ORDER BY s.search_time DESC 
                   LIMIT $row";
            break;
        case 2:
            // 热度最高的搜索词（根据统计数）
            // 先按search_term分组获取最新的一条记录，然后按统计数排序
            $sql = "SELECT s.id, s.username, s.search_term, s.search_time 
                   FROM $search_table s 
                   JOIN (SELECT search_term, MAX(search_time) as max_time FROM $search_table GROUP BY search_term) t 
                   ON s.search_term = t.search_term AND s.search_time = t.max_time 
                   ORDER BY (SELECT COUNT(*) FROM $search_table WHERE search_term = s.search_term) DESC 
                   LIMIT $row";
            break;
        case 3:
            // 随机匹配
            // 先按search_term分组获取最新的一条记录，然后随机排序
            $sql = "SELECT s.id, s.username, s.search_term, s.search_time 
                   FROM $search_table s 
                   JOIN (SELECT search_term, MAX(search_time) as max_time FROM $search_table GROUP BY search_term) t 
                   ON s.search_term = t.search_term AND s.search_time = t.max_time 
                   ORDER BY RAND() 
                   LIMIT $row";
            break;
        default:
            // 默认最新的搜索词
            $sql = "SELECT s.id, s.username, s.search_term, s.search_time 
                   FROM $search_table s 
                   JOIN (SELECT search_term, MAX(search_time) as max_time FROM $search_table GROUP BY search_term) t 
                   ON s.search_term = t.search_term AND s.search_time = t.max_time 
                   ORDER BY s.search_time DESC 
                   LIMIT $row";
    }

    $result = $db->query($sql);
    if ($result && $result->num_rows > 0) {
        $processed_terms = [];
        $item_count = 0;

        while (($item = $result->fetch_assoc()) && $item_count < $row) {
            $search_term = $item['search_term'];

            // 确保每个搜索词只出现一次，无论排序方式如何
            if (in_array($search_term, $processed_terms)) {
                continue;
            }

            $processed_terms[] = $search_term;
            $item_count++;

            // 获取用户名，现在直接从search表中获取
            $username = !empty($item['username']) ? $item['username'] : '游客';

            // 构建搜索词数据
            $search_data[] = [
                'id' => $item['id'],
                'user_id' => htmlspecialchars($username), // 保持兼容旧标签
                'username' => htmlspecialchars($username),
                'search_term' => htmlspecialchars($search_term),
                'search_time' => formatTime($item['search_time']),
                'count' => isset($search_term_counts[$search_term]) ? $search_term_counts[$search_term] : 1
            ];
        }
        $result->close();
    }

    return $search_data;
}

function getUserStatistics($user_id, $db)
{
    $stats = [
        'like_count' => 0,
        'report_count' => 0,
        'share_count' => 0,
        'post_count' => 0
    ];

    $post_table = findTable($db, ['posts', 'articles', 'qkcms_article', 'qkcms_article', 'content']);
    if ($post_table) {
        $field_patterns = ['id' => ['id', 'article_id'], 'author_id' => ['author_id', 'user_id', 'post_author']];
        $field_map = getFieldMap($db, $post_table, $field_patterns);
        if (isset($field_map['author_id'])) {
            $author_field = $field_map['author_id'];
            $sql = "SELECT COUNT(*) as count FROM $post_table WHERE $author_field = $user_id";
            $result = $db->query($sql);
            if ($result && $row = $result->fetch_assoc()) $stats['post_count'] = intval($row['count']);
            if ($result) $result->free();
        }
    }

    $likes_table = findTable($db, ['likes']);
    if ($likes_table) {
        $likes_field_patterns = ['user_id' => ['user_id', 'uid', 'userid']];
        $likes_field_map = getFieldMap($db, $likes_table, $likes_field_patterns);
        if (isset($likes_field_map['user_id'])) {
            $user_field = $likes_field_map['user_id'];
            $sql = "SELECT COUNT(*) as count FROM $likes_table WHERE $user_field = $user_id";
            $result = $db->query($sql);
            if ($result && $row = $result->fetch_assoc()) $stats['like_count'] = intval($row['count']);
            if ($result) $result->free();
        }
    }

    $reports_table = findTable($db, ['reports']);
    if ($reports_table) {
        $reports_field_patterns = ['user_id' => ['user_id', 'uid', 'userid', 'reporter_id']];
        $reports_field_map = getFieldMap($db, $reports_table, $reports_field_patterns);
        if (isset($reports_field_map['user_id'])) {
            $user_field = $reports_field_map['user_id'];
            $sql = "SELECT COUNT(*) as count FROM $reports_table WHERE $user_field = $user_id";
            $result = $db->query($sql);
            if ($result && $row = $result->fetch_assoc()) $stats['report_count'] = intval($row['count']);
            if ($result) $result->free();
        }
    }

    return $stats;
}

function processContent($content, $db = null, $article_id = 0)
{
    global $qkcms_current_row, $qkcms_total_records, $qkcms_total_comments;
    if (!isset($qkcms_current_row) || !is_array($qkcms_current_row)) $qkcms_current_row = [];
    if (!isset($qkcms_total_records) || !is_array($qkcms_total_records)) $qkcms_total_records = [];
    if (!isset($qkcms_total_comments) || !is_array($qkcms_total_comments)) $qkcms_total_comments = [];

    if (preg_match('/\{qkcms:page/i', $content)) {
        include_once dirname(__FILE__) . '/page.php';
    }

    $data_tags = ['navigation', 'articles', 'categories', 'catgroup', 'comments', 'tags', 'members', 'search', 'friendlink'];
    $content = preg_replace_callback('/\{qkcms:(' . implode('|', $data_tags) . ')([^}]*?)\}([\s\S]*?)\{\/qkcms:\1\}/i', function ($matches) use ($db, $article_id) {
        $tag_name = strtolower($matches[1]);
        $attributes_str = $matches[2];
        $inner_content = $matches[3];
        $parsed_attributes = parseAttributes($attributes_str);
        $row = isset($parsed_attributes['row']) ? intval($parsed_attributes['row']) : 5;
        $parsed_attributes['row'] = $row;
        // 同时支持page和page_key属性，将page属性的值赋给page_key
        $page_key = isset($parsed_attributes['page_key']) ? intval($parsed_attributes['page_key']) : 0;
        if ($page_key <= 0) {
            $page_key = isset($parsed_attributes['page']) ? intval($parsed_attributes['page']) : 0;
        }
        $parsed_attributes['page_key'] = $page_key;

        $limit = $row;
        if ($article_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $article_id = intval($_GET['id']);
        if ($tag_name === 'categories' && isset($_GET['id']) && intval($_GET['id']) > 0) $parsed_attributes['parent_id'] = intval($_GET['id']);

        if (!$db || !is_object($db) || !method_exists($db, 'query')) $db = get_db_connection();
        $items = [];
        $db_connected = $db && is_object($db) && method_exists($db, 'query') && is_valid_db_connection($db);

        if ($db_connected) {
            switch ($tag_name) {
                case 'navigation':
                    $items = getNavigationData($parsed_attributes, $db);
                    break;
                case 'articles':
                    $parsed_attributes['article_id'] = $article_id;
                    if (!isset($parsed_attributes['col'])) $parsed_attributes['col'] = 1;
                    $current_page = 1;
                    $current_url = $_SERVER['REQUEST_URI'];

                    if ($page_key > 0) {
                        if (isset($_GET['page']) && is_numeric($_GET['page'])) $current_page = max(1, intval($_GET['page']));
                        else if (preg_match('/_page(\d+)\.html$/i', $current_url, $matches)) $current_page = max(1, intval($matches[1]));
                        $parsed_attributes['page'] = $current_page;
                        global $qkcms_current_row, $qkcms_total_records;
                        $qkcms_current_row[$page_key] = $parsed_attributes['row'];
                        $result = getArticlesData($parsed_attributes, $db);
                        $items = $result['articles'];
                        $total = $result['total'];
                        $qkcms_total_records[$page_key] = $total;
                    } else {
                        $result = getArticlesData($parsed_attributes, $db);
                        $items = $result['articles'];
                    }
                    break;
                case 'categories':
                    $items = getCategoriesData($parsed_attributes, $db);
                    break;
                case 'catgroup':
                    $items = getCatgroupData($parsed_attributes, $db);
                    break;
                case 'comments':
                    if (!isset($parsed_attributes['article_id'])) $parsed_attributes['article_id'] = $article_id;
                    $current_page = 1;
                    $current_url = $_SERVER['REQUEST_URI'];

                    if ($page_key > 0) {
                        if (isset($_GET['page']) && is_numeric($_GET['page'])) $current_page = max(1, intval($_GET['page']));
                        else if (preg_match('/_comments(\d+)\.html$/i', $current_url, $matches)) $current_page = max(1, intval($matches[1]));
                        else if (preg_match('/_page(\d+)\.html$/i', $current_url, $matches)) $current_page = max(1, intval($matches[1]));
                        $parsed_attributes['page'] = $current_page;
                        global $qkcms_current_row, $qkcms_total_comments;
                        $qkcms_current_row[$page_key] = $parsed_attributes['row'];
                        $result = getCommentsData($parsed_attributes, $db);
                        $items = $result['comments'];
                        $total = $result['total'];
                        $qkcms_total_comments[$page_key] = $total;
                    } else {
                        $result = getCommentsData($parsed_attributes, $db);
                        $items = $result['comments'];
                    }
                    break;
                case 'tags':
                    $items = getHotTagsData($parsed_attributes, $db);
                    break;
                case 'members':
                    $items = getLatestMembersData($parsed_attributes, $db);
                    break;
                case 'search':
                    $items = getSearchData($parsed_attributes, $db);
                    break;
                case 'friendlink':
                    $items = qkcms_get_friendlink_data($parsed_attributes);
                    break;
            }
        }

        $replacement = '';
        if (!is_array($items)) $items = array();
        if (!empty($items)) {
            if ($tag_name === 'articles') {
                $col = isset($parsed_attributes['col']) ? intval($parsed_attributes['col']) : 1;
                $col = max(1, min(6, $col));
                $columns = array_fill(0, $col, '');
                $column_index = 0;
                $title_id = 1;
                foreach ($items as $item) {
                    $item['title_id'] = $title_id;
                    $title_id++;
                    $item_content = $inner_content;
                    foreach ($item as $key => $value) {
                        $value = is_array($value) ? json_encode($value) : strval($value);
                        $item_content = str_replace('[row.' . $key . ']', $value, $item_content);
                        $item_content = str_replace('[' . $key . ']', $value, $item_content);
                        $item_content = str_replace('{' . $key . '}', $value, $item_content);
                    }
                    $columns[$column_index] .= $item_content;
                    $column_index = ($column_index + 1) % $col;
                }
                if ($col === 1) {
                    $replacement .= '<div class="articles-list">' . implode('', $columns) . '</div>';
                } else {
                    $replacement .= '<style>.renderer-table { display: table; width: 100%; border-collapse: separate; border-spacing: 0px 0; } .renderer-row { display: table-row; } .renderer-cell { display: table-cell; vertical-align: top; } @media (max-width: 768px) { .renderer-table { display: block; } .renderer-row { display: block; } .renderer-cell { display: block; width: 100% !important; } }</style>';
                    $replacement .= '<div class="renderer-table">';
                    $replacement .= '<div class="renderer-row">';
                    foreach ($columns as $column_content) {
                        $column_width = (100 / $col) . '%';
                        $replacement .= '<div class="renderer-cell" style="width: ' . $column_width . '">';
                        $replacement .= $column_content;
                        $replacement .= '</div>';
                    }
                    $replacement .= '</div>';
                    $replacement .= '</div>';
                }
            } else {
                $comment_id = 1;
                foreach ($items as $item) {
                    if ($tag_name === 'tags' && (!isset($item['name']) || empty(trim($item['name'])) || mb_strlen(trim($item['name']), 'UTF-8') === 0)) continue;
                    $item['comment_id'] = $comment_id;
                    $item_content = $inner_content;

                    $articles_placeholders = [];
                    $categories_placeholders = [];
                    if ($tag_name === 'categories' && isset($item['id'])) {
                        $category_id_for_articles = intval($item['id']);
                        $item_content = preg_replace_callback('/\{qkcms:articles([^}]*)\}/i', function ($matches) use ($category_id_for_articles) {
                            $attributes = $matches[1];
                            if (preg_match('/\bcategory_id\s*=\s*[\'"]?[-\d]+[\'"]?/i', $attributes)) {
                                $attributes = preg_replace('/\bcategory_id\s*=\s*[\'"]?[-\d]+[\'"]?/i', 'category_id=' . $category_id_for_articles, $attributes);
                            } else {
                                $attributes .= ' category_id=' . $category_id_for_articles;
                            }
                            return '{qkcms:articles' . $attributes . '}';
                        }, $item_content);

                        $item_content = preg_replace_callback('/\{qkcms:articles[^}]*\}[\s\S]*?\{\/qkcms:articles\}/i', function ($matches) use (&$articles_placeholders) {
                            $placeholder = '<!--QKCMS_ARTICLES_BLOCK_' . count($articles_placeholders) . '-->';
                            $articles_placeholders[$placeholder] = $matches[0];
                            return $placeholder;
                        }, $item_content);
                    }
                    if ($tag_name === 'catgroup') {
                        $item_content = preg_replace_callback('/\{qkcms:categories[^}]*\}[\s\S]*?\{\/qkcms:categories\}/i', function ($matches) use (&$categories_placeholders) {
                            $placeholder = '<!--QKCMS_CATEGORIES_BLOCK_' . count($categories_placeholders) . '-->';
                            $categories_placeholders[$placeholder] = $matches[0];
                            return $placeholder;
                        }, $item_content);
                    }

                    // 处理导航子菜单
                    if ($tag_name === 'navigation') {
                        if (preg_match('/\{sub_navigation([^}]*)\}([\s\S]*?)\{\/sub_navigation\}/', $item_content, $sub_matches)) {
                            $sub_attributes_str = isset($sub_matches[1]) ? $sub_matches[1] : '';
                            $sub_template = $sub_matches[2];
                            $sub_content = '';
                            $sub_id = 1;
                            if (isset($item['children']) && is_array($item['children'])) {
                                foreach ($item['children'] as $child) {
                                    $child['sub_id'] = $sub_id;
                                    $child_content = $sub_template;
                                    foreach ($child as $child_key => $child_value) {
                                        $child_value = is_array($child_value) ? json_encode($child_value) : strval($child_value);
                                        $child_content = str_replace('[sub.' . $child_key . ']', $child_value, $child_content);
                                        $child_content = str_replace('{sub.' . $child_key . '}', $child_value, $child_content);
                                    }
                                    $sub_content .= $child_content;
                                    $sub_id++;
                                }
                            }
                            // 只有当有子导航项时才添加 sub-nav 容器
                            if (!empty($sub_content)) {
                                $sub_content = '<ul class="sub-nav"><div class="sub-nav-top-line"></div>' . $sub_content . '</ul>';
                            }
                            // 替换整个 sub_navigation 标签块
                            $item_content = str_replace($sub_matches[0], $sub_content, $item_content);
                        }
                    }

                    foreach ($item as $key => $value) {
                        if ($tag_name === 'comments' && $key === 'replies' && is_array($value)) {
                            $replies_content = '';
                            if (preg_match('/\{reply_comment([^\}]*)\}(.*?)\[\/reply_comment\]/s', $item_content, $reply_matches)) {
                                $reply_attributes_str = isset($reply_matches[1]) ? $reply_matches[1] : '';
                                $reply_template = $reply_matches[2];
                                $reply_parsed_attributes = [];
                                if (preg_match_all('/\s+([\w.]+)=(["][^"]*"|[^\s}]*)/', $reply_attributes_str, $attr_matches, PREG_SET_ORDER)) {
                                    foreach ($attr_matches as $match) {
                                        $attr_name = $match[1];
                                        $attr_value = str_replace('"', '', $match[2]);
                                        $reply_parsed_attributes[$attr_name] = $attr_value;
                                    }
                                }
                                $reply_limit = isset($reply_parsed_attributes['row']) ? intval($reply_parsed_attributes['row']) : 5;
                                $reply_date_format = 1;
                                if (isset($reply_parsed_attributes['reply.date'])) $reply_date_format = intval($reply_parsed_attributes['reply.date']);
                                $reply_id = 1;
                                $value = array_slice($value, 0, $reply_limit);
                                foreach ($value as $reply) {
                                    $reply['reply_id'] = $reply_id;
                                    $reply['parent_comment_id'] = $comment_id;
                                    if (isset($reply['created_at'])) {
                                        $reply_created_at = $reply['created_at'];
                                        if ($reply_date_format == 2) {
                                            if (is_numeric($reply_created_at)) $reply['date'] = date('Y-n-j', $reply_created_at);
                                            else $reply['date'] = date('Y-n-j', strtotime($reply_created_at));
                                        } else $reply['date'] = formatTime($reply_created_at);
                                    }
                                    $reply_item_content = $reply_template;
                                    foreach ($reply as $reply_key => $reply_value) {
                                        $reply_value = is_array($reply_value) ? json_encode($reply_value) : strval($reply_value);
                                        $reply_item_content = str_replace('[reply.' . $reply_key . ']', $reply_value, $reply_item_content);
                                        $reply_item_content = str_replace('{reply.' . $reply_key . '}', $reply_value, $reply_item_content);
                                        $reply_item_content = str_replace('[user_reply.' . $reply_key . ']', $reply_value, $reply_item_content);
                                        $reply_item_content = str_replace('{user_reply.' . $reply_key . '}', $reply_value, $reply_item_content);
                                    }
                                    $replies_content .= $reply_item_content;
                                    $reply_id++;
                                }
                                // 构建完整的标签模式以确保正确替换
                                $full_tag_pattern = '/\{reply_comment([^\}]*)\}(.*?)\[\/reply_comment\]/s';
                                $item_content = preg_replace($full_tag_pattern, $replies_content, $item_content, 1);
                            }
                            $replies_count = is_array($value) ? count($value) : 0;
                            $item_content = str_replace('[replies.length]', $replies_count, $item_content);
                            $item_content = str_replace('{replies.length}', $replies_count, $item_content);
                            $value = '';
                        } else $value = is_array($value) ? json_encode($value) : strval($value);
                        $item_content = str_replace('[row.' . $key . ']', $value, $item_content);
                        $item_content = str_replace('[' . $key . ']', $value, $item_content);
                        $item_content = str_replace('{' . $key . '}', $value, $item_content);
                    }

                    if (!empty($articles_placeholders)) {
                        foreach ($articles_placeholders as $placeholder => $block_content) {
                            $item_content = str_replace($placeholder, $block_content, $item_content);
                        }
                    }
                    if (!empty($categories_placeholders)) {
                        foreach ($categories_placeholders as $placeholder => $block_content) {
                            $item_content = str_replace($placeholder, $block_content, $item_content);
                        }
                    }

                    // 处理 catgroup 嵌套 categories 的情况：将当前归档ID传递给内部的 categories 标签
                    if ($tag_name === 'catgroup' && isset($item['id'])) {
                        $catgroup_id = intval($item['id']);
                        $catgroup_item_type = isset($item['item_type']) ? trim((string)$item['item_type']) : 'catgroup';
                        $item_content = preg_replace_callback('/\{qkcms:categories([^}]*)\}/i', function($matches) use ($catgroup_id, $catgroup_item_type) {
                            $attributes = $matches[1];
                            $attributes = preg_replace('/\s+_catgroup_item_type\s*=\s*([\'"][^\'"]*[\'"]|[^\s}]*)/i', '', $attributes);
                            $attributes = preg_replace('/\s+_catgroup_item_id\s*=\s*([\'"][^\'"]*[\'"]|[^\s}]*)/i', '', $attributes);
                            $attributes .= ' _catgroup_item_type="' . $catgroup_item_type . '" _catgroup_item_id="' . $catgroup_id . '"';
                            return '{qkcms:categories' . $attributes . '}';
                        }, $item_content);
                    }

                    // 递归解析内部的标签
                    $item_content = qkcms_tag_parser($item_content, $article_id);

                    $replacement .= $item_content;
                    $comment_id++;
                }
            }
        }
        return $replacement;
    }, $content);

    $content = qkcms_goods_tag_parser($content);

    $content = preg_replace_callback('/\{qkcms:page([^}]*?)\}([\s\S]*?)\{\/qkcms:page\}/i', function ($matches) use ($db, $article_id) {
        $attributes_str = $matches[1];
        $inner_content = $matches[2];
        $parsed_attributes = parseAttributes($attributes_str);
        $type = isset($parsed_attributes['type']) ? intval($parsed_attributes['type']) : 1;
        $page_key = isset($parsed_attributes['page']) ? intval($parsed_attributes['page']) : 0;
        $replacement = '';
        if ($page_key > 0) $replacement = generate_pagination($type, $inner_content, $page_key);
        else $replacement = '你的分页未绑定到目标列表';
        return $replacement;
    }, $content);

    return $content;
}

function handle_request()
{
    if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
        $action = isset($_POST['action']) ? $_POST['action'] : (isset($_GET['action']) ? $_GET['action'] : 'get_value');
        $tag = isset($_POST['tag']) ? $_POST['tag'] : (isset($_GET['tag']) ? $_GET['tag'] : '');
        $article_id = isset($_POST['posts_id']) ? intval($_POST['posts_id']) : (isset($_GET['posts_id']) ? intval($_GET['posts_id']) : 0);

        if ($article_id === 0 && strpos($tag, 'posts_') === 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $article_id = intval($_GET['id']);

        switch ($action) {
            case 'get_value':
                header('Content-Type: application/json; charset=utf-8');
                if ($tag) {
                    $db = get_db_connection();
                    if (!is_valid_db_connection($db)) {
                        echo json_encode(['result' => null, 'error' => '数据库连接失败']);
                        return;
                    }
                    $result = get_database_result($tag, $db, $article_id);
                    close_db_connection($db);
                    echo json_encode(['result' => $result, 'tag' => $tag, 'posts_id' => $article_id]);
                } else echo json_encode(['result' => null, 'error' => '标签名不能为空']);
                break;
            case 'process_loop':
                header('Content-Type: application/json; charset=utf-8');
                if (isset($_POST['tag_content'])) {
                    $tag_content = $_POST['tag_content'];
                    $db = get_db_connection();
                    if (!is_valid_db_connection($db)) {
                        echo json_encode(['result' => '', 'success' => false, 'error' => '数据库连接失败']);
                        return;
                    }
                    $result = processContent($tag_content, $db);
                    close_db_connection($db);
                    echo json_encode(['result' => $result, 'success' => true]);
                } else echo json_encode(['result' => '', 'success' => false, 'error' => '标签内容不能为空']);
                break;
            default:
                header('Content-Type: application/json; charset=utf-8');
                $response = ['success' => false, 'data' => null, 'tag' => $tag];
                if ($tag) {
                    $db = get_db_connection();
                    if (!is_valid_db_connection($db)) $response['error'] = '数据库连接失败';
                    else {
                        global $tag_mapping;
                        $tag_info = isset($tag_mapping[$tag]) ? $tag_mapping[$tag] : null;
                        if ($tag_info) {
                            $value = get_database_result($tag, $db, $article_id);
                            if ($value !== null) {
                                $response['success'] = true;
                                $response['data'] = $value;
                            } else $response['error'] = '无法获取标签值';
                        } else $response['error'] = '标签不存在';
                        close_db_connection($db);
                    }
                } else $response['error'] = '标签名不能为空';
                echo json_encode($response);
                break;
        }
        exit;
    }
}

// 只处理带有action参数的请求，避免拦截Member页面的保存请求
if (isset($_GET['action']) || (isset($_POST['action']) && $_POST['action'] !== '')) {
    handle_request();
    exit;
}

function qkcms_tag_parser($content, $article_id = 0)
{
    if (isset($_SERVER['REQUEST_URI']) && preg_match('/cg_id\d+\.html/i', $_SERVER['REQUEST_URI'])) {
        // #region debug-point D:tag-parser-start
        qk_tplrdr_debug_event('D', 'tplrdr.php:qkcms_tag_parser', '[DEBUG] tag parser start for catgroup page', array(
            'request_uri' => $_SERVER['REQUEST_URI'],
            'article_id' => $article_id,
            'content_length' => strlen((string)$content)
        ));
        // #endregion
    }
    if (empty($article_id)) {
        if (isset($_GET['id']) && is_numeric($_GET['id'])) $article_id = intval($_GET['id']);
        else if (isset($_GET['aid']) && is_numeric($_GET['aid'])) $article_id = intval($_GET['aid']);
        else if (isset($_GET['cid']) && is_numeric($_GET['cid'])) $article_id = intval($_GET['cid']);
        else if (isset($_GET['category_id']) && is_numeric($_GET['category_id'])) $article_id = intval($_GET['category_id']);
        else if (isset($_GET['cat_id']) && is_numeric($_GET['cat_id'])) $article_id = intval($_GET['cat_id']);
        else if (isset($_GET['software_id']) && is_numeric($_GET['software_id'])) $article_id = intval($_GET['software_id']);
    }

    // 统一的文件引入处理 - 支持.html和.php文件
    $content = preg_replace_callback('/\{qkcms:([a-zA-Z0-9_]+\.(html|php))\}/i', function ($matches) use ($article_id) {
        $filename = $matches[1];
        $extension = strtolower($matches[2]);
        $file_path = dirname(__FILE__) . '/' . $filename;

        if (!file_exists($file_path) || !is_readable($file_path)) {
            return '[' . $filename . ' not found]';
        }

        // 设置当前模板文件全局变量，供comment.php等使用
        $GLOBALS['current_template_file'] = $file_path;

        // PHP文件：使用include执行，支持AJAX处理
        if ($extension === 'php') {
            if (!defined('QKCMS_TEMPLATE_EMBED')) define('QKCMS_TEMPLATE_EMBED', true);

            // 将article_id设置到全局变量，供被include的PHP文件使用
            $GLOBALS['qkcms_article_id'] = $article_id;

            ob_start();
            include $file_path;
            $output = ob_get_clean();

            return qkcms_tag_parser($output, $article_id);
        }

        // HTML文件：直接读取内容并解析
        $file_content = file_get_contents($file_path);
        return qkcms_tag_parser($file_content, $article_id);
    }, $content);

    static $db_conn = null;
    if ($db_conn === null) {
        $db_conn = get_db_connection();
        $db_connected = $db_conn && is_object($db_conn) && is_valid_db_connection($db_conn);
    }

    $tag_placeholders = [];
    $content = preg_replace_callback('/(<td\s+class="tag-label">)(.*?)(<\/td>)/', function ($matches) use (&$tag_placeholders) {
        $placeholder = '<!--TAG_PLACEHOLDER_' . count($tag_placeholders) . '-->';
        $tag_placeholders[$placeholder] = $matches[0];
        return $placeholder;
    }, $content);


    // 先处理带有命名空间的单值标签，如 {qkcms:total_search}
    $content = preg_replace_callback('/\{qkcms:([a-zA-Z0-9_]+)\}/', function ($matches) use ($db_conn, $article_id) {
        $tag = $matches[1];
        $current_id = $article_id;

        // 处理新增的搜索和标签总数标签
        if ($tag === 'total_search' || $tag === 'total_tag') {
            // 获取当前页面类型，支持URL路由和直接访问
            $is_search_page = false;
            $is_tag_page = false;

            // 检查GET参数
            if (isset($_GET['search']) || isset($_GET['keyword'])) {
                $is_search_page = true;
            } elseif (isset($_GET['tag'])) {
                $is_tag_page = true;
            }
            // 检查请求URI
            elseif (isset($_SERVER['REQUEST_URI'])) {
                $request_uri = $_SERVER['REQUEST_URI'];
                $is_search_page = strpos($request_uri, '/search_') !== false || strpos($request_uri, 'search=') !== false || strpos($request_uri, 'keyword=') !== false;
                $is_tag_page = strpos($request_uri, '/tag_') !== false || strpos($request_uri, 'tag=') !== false;
            }
            // 兼容直接访问的情况
            else {
                $current_file = basename($_SERVER['SCRIPT_FILENAME']);
                $is_search_page = $current_file === 'search.php' || $current_file === 'search.html' || preg_match('/^search_/', $current_file);
                $is_tag_page = $current_file === 'tag.php' || $current_file === 'tag.html' || preg_match('/^tag_/', $current_file);
            }

            // 只有在搜索页面或标签页面才有效
            if (!$is_search_page && !$is_tag_page) {
                return '[' . $tag . ']';
            }

            // 获取tag参数值（来自GET或默认）
            $tag_value = '';
            if (isset($_GET['search'])) {
                $tag_value = $_GET['search'];
            } elseif (isset($_GET['keyword'])) {
                $tag_value = $_GET['keyword'];
            } elseif (isset($_GET['tag'])) {
                $tag_value = $_GET['tag'];
            }

            if (empty($tag_value)) {
                return '0';
            }

            // 调用getArticlesData获取总数
            if ($db_conn && is_object($db_conn) && is_valid_db_connection($db_conn)) {
                $params = array(
                    'tag' => '1,' . $tag_value, // 使用tag=1模式
                    'row' => 0, // 不限制数量，只获取总数
                    'page' => 1
                );
                $result = getArticlesData($params, $db_conn);
                return strval($result['total']);
            }
            return '0';
        }

        // 处理其他带有命名空间的单值标签
        if ((strpos($tag, 'cat_') === 0 || $tag === 'category_id') && isset($_GET['cid']) && is_numeric($_GET['cid'])) $current_id = intval($_GET['cid']);
        $db_connected = $db_conn && is_object($db_conn) && is_valid_db_connection($db_conn);
        $value = null;
        if ($db_connected) $value = get_database_result($tag, $db_conn, $current_id);

        $text_tags = ['posts_keywords', 'posts_description', 'cat_name', 'cat_title', 'cat_seo_title', 'cat_description', 'cat_keywords', 'logo'];
        $content_thumbnail_tags = ['posts_thumbnail', 'cat_thumbnail'];
        $avatar_tag = 'user_avatar';



        if (in_array($tag, $content_thumbnail_tags) && ($value === null || $value === '')) return DEFAULT_THUMBNAIL_PATH;
        if ($tag === $avatar_tag && ($value === null || $value === '')) return DEFAULT_AVATAR_PATH;
        if (in_array($tag, $text_tags) && ($value === null || $value === '')) return '';
        if ($value === null) {
            // 保留 goods_ 开头的标签，由商品标签解析器处理
            if (strpos($tag, 'goods_') === 0) {
                return '{qkcms:' . $tag . '}';
            }
            return '[' . $tag . ']';
        }
        return $value;
    }, $content);

    // 继续处理无命名空间的单值标签，如 {total_posts}，但跳过 {sub.*} 和 {sub_navigation} 标签，这些由 processContent 函数处理
    $content = preg_replace_callback('/\{([a-zA-Z0-9_\/.]+)\}/', function ($matches) use ($db_conn, $article_id) {
        $tag = $matches[1];
        // 跳过 sub_navigation 标签，这些由 processContent 函数处理
        if ($tag === 'sub_navigation' || $tag === '/sub_navigation' || strpos($tag, 'sub.') === 0) {
            return '{' . $tag . '}';
        }
        // 跳过 goods. 开头的标签，这些是商品循环内部标签
        if (strpos($tag, 'goods.') === 0) {
            return '{' . $tag . '}';
        }
        // 跳过 image. 开头的标签，这些是商品图片循环内部标签
        if (strpos($tag, 'image.') === 0) {
            return '{' . $tag . '}';
        }
        // cat_url 在文章循环中由 processContent 按每条文章数据替换，不能在全局阶段提前解析
        if ($tag === 'cat_url') {
            return '{' . $tag . '}';
        }
        $current_id = $article_id;

        if ((strpos($tag, 'cat_') === 0 || $tag === 'category_id') && isset($_GET['cid']) && is_numeric($_GET['cid'])) $current_id = intval($_GET['cid']);
        $db_connected = $db_conn && is_object($db_conn) && is_valid_db_connection($db_conn);
        $value = null;
        if ($db_connected) $value = get_database_result($tag, $db_conn, $current_id);

        $text_tags = ['posts_keywords', 'posts_description', 'cat_name', 'cat_title', 'cat_seo_title', 'cat_description', 'cat_keywords', 'logo'];
        $content_thumbnail_tags = ['posts_thumbnail', 'cat_thumbnail'];
        $avatar_tag = 'user_avatar';



        if (in_array($tag, $content_thumbnail_tags) && ($value === null || $value === '')) return DEFAULT_THUMBNAIL_PATH;
        if ($tag === $avatar_tag && ($value === null || $value === '')) return DEFAULT_AVATAR_PATH;
        if (in_array($tag, $text_tags) && ($value === null || $value === '')) return '';
        if ($value === null) {
            // 保留 goods_ 开头的标签，由商品标签解析器处理
            if (strpos($tag, 'goods_') === 0) {
                return '{' . $tag . '}';
            }
            return '[' . $tag . ']';
        }
        return $value;
    }, $content);

    $content = processContent($content, $db_conn, $article_id);

    if (!empty($tag_placeholders)) {
        foreach ($tag_placeholders as $placeholder => $original_content) $content = str_replace($placeholder, $original_content, $content);
    }
    if (is_string($content) && $content !== '' && preg_match('/<head\b[^>]*>[\s\S]*<\/head>/i', $content, $head_match)) {
        $head_block = $head_match[0];
        if (!preg_match('/<link[^>]+rel=["\'][^"\']*(?:\bicon\b|\bshortcut\s+icon\b)[^"\']*["\'][^>]*>/i', $head_block)) {
            $favicon_data_uri = "data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2064%2064'%3E%3Crect%20width%3D'64'%20height%3D'64'%20rx%3D'12'%20fill%3D'%2310b981'%2F%3E%3Ctext%20x%3D'32'%20y%3D'45'%20text-anchor%3D'middle'%20fill%3D'%23fff'%20font-size%3D'46'%20font-family%3D'Arial%2CHelvetica%2Csans-serif'%20font-weight%3D'900'%3EQ%3C%2Ftext%3E%3C%2Fsvg%3E";
            $favicon_links = "\n<link rel=\"icon\" href=\"" . $favicon_data_uri . "\" type=\"image/svg+xml\" />\n<link rel=\"shortcut icon\" href=\"" . $favicon_data_uri . "\" type=\"image/svg+xml\" />\n";
            $content = preg_replace('/<\/head>/i', $favicon_links . '</head>', $content, 1);
        }
    }
    return $content;
}

function qkcms_init_tag_parser($article_id = 0)
{
    return ob_start(function ($buffer) use ($article_id) {
        return qkcms_tag_parser($buffer, $article_id);
    });
}

function qkcms_close_tag_parser()
{
    ob_end_flush();
}

if (!defined('QKCMS_TAG_PARSER_DISABLED')) define('QKCMS_TAG_PARSER_DISABLED', false);
if (!defined('QKCMS_TAG_PARSER_AUTO_INIT')) define('QKCMS_TAG_PARSER_AUTO_INIT', false);

if (!QKCMS_TAG_PARSER_DISABLED && QKCMS_TAG_PARSER_AUTO_INIT && !defined('QKCMS_TAG_PARSER_INITIALIZED')) {
    define('QKCMS_TAG_PARSER_INITIALIZED', true);
    $current_article_id = 0;
    qkcms_init_tag_parser($current_article_id);
}

function qkcms_start_tag_parser($article_id = 0)
{
    if (QKCMS_TAG_PARSER_DISABLED) return false;
    if (!defined('QKCMS_TAG_PARSER_INITIALIZED')) {
        define('QKCMS_TAG_PARSER_INITIALIZED', true);
        return qkcms_init_tag_parser($article_id);
    }
    return true;
}

function qkcms_stop_tag_parser()
{
    if (defined('QKCMS_TAG_PARSER_INITIALIZED')) qkcms_close_tag_parser();
}

function formatContent($content, $hasPurchased = true)
{
    // 如果用户已购买，移除购买内容标识符
    if ($hasPurchased) {
        $content = str_replace('[购买内容开始]', '', $content);
        $content = str_replace('[购买内容结束]', '', $content);
    }
    // 如果内容已经包含HTML标签，不进行nl2br处理，保持原始HTML结构
    if (preg_match('/<[^>]+>/', $content)) {
        return $content;
    }
    // 纯文本内容，转换换行符为<br>
    return nl2br($content);
}

function get_formatted_post_content($db, $article_id)
{
    $use_id = $article_id;
    if ($use_id <= 0 && isset($_GET['id']) && is_numeric($_GET['id'])) $use_id = intval($_GET['id']);
    if ($use_id <= 0) return '';
    try {
        $content_query = "SELECT content FROM posts WHERE id = ?";
        $content = execute_db_query($db, $content_query, array($use_id), 'single');

        // 检查用户是否已购买该文章
        $hasPurchased = false;
        // 确保会话已启动
        if (!isset($_SESSION)) {
            @session_start();
        }
        if (isset($_SESSION['user_id'])) {
            $user_id = $_SESSION['user_id'];
            $bought_query = "SELECT bought FROM users WHERE id = ?";
            $bought = execute_db_query($db, $bought_query, array($user_id), 'single');
            if ($bought) {
                $purchasedPosts = explode(',', $bought);
                $hasPurchased = in_array(strval($use_id), $purchasedPosts);
            }
        }

        return formatContent($content ?: '', $hasPurchased);
    } catch (Exception $e) {
        return '';
    }
}



//所有的商品标签解析代码写下面

// 商品标签映射配置
$GLOBALS['goods_tag_mapping'] = array(
    'goods_id' => array('type' => 'special', 'description' => '商品ID，从请求参数获取'),
    'goods_title' => array('type' => 'field', 'table' => 'goods', 'field' => 'title'),
    'goods_description' => array('type' => 'field', 'table' => 'goods', 'field' => 'description'),
    'goods_keywords' => array('type' => 'field', 'table' => 'goods', 'field' => 'keywords'),
    'goods_price' => array('type' => 'field', 'table' => 'goods', 'field' => 'price'),
    'goods_sales' => array('type' => 'field', 'table' => 'goods', 'field' => 'sales'),
    'goods_stock' => array('type' => 'field', 'table' => 'goods', 'field' => 'stock'),
    'goods_category' => array('type' => 'join', 'table' => 'categories', 'field' => 'name', 'join' => 'goods.category_id = categories.id'),
    'goods_category_name' => array('type' => 'join', 'table' => 'categories', 'field' => 'name', 'join' => 'goods.category_id = categories.id'),
    'goods_content' => array('type' => 'field', 'table' => 'goods', 'field' => 'content'),
    'goods_status' => array('type' => 'field', 'table' => 'goods', 'field' => 'status'),
    'goods_url' => array('type' => 'url', 'table' => 'goods', 'field' => 'id'),
    'goods_category_url' => array('type' => 'url', 'table' => 'categories', 'field' => 'id', 'join' => 'goods.category_id = categories.id'),
    'goods_thumbnail' => array('type' => 'field', 'table' => 'goods', 'field' => 'image'),
    'goods_created_at' => array('type' => 'field', 'table' => 'goods', 'field' => 'create_time'),
    'goods_updated_at' => array('type' => 'field', 'table' => 'goods', 'field' => 'update_time'),
    'goods_comments' => array('type' => 'special', 'description' => '商品评论数量'),
    'goods_mp4url' => array('type' => 'field', 'table' => 'goods', 'field' => 'mp4url')
);

if (!defined('DEFAULT_THUMBNAIL_PATH')) {
    define('DEFAULT_THUMBNAIL_PATH', '/assets/images/moren.jpg');
}

function get_goods_id()
{
    if (isset($_GET['id']) && is_numeric($_GET['id'])) return intval($_GET['id']);
    if (isset($_GET['gid']) && is_numeric($_GET['gid'])) return intval($_GET['gid']);
    if (isset($_SERVER['REQUEST_URI']) && preg_match('/goods_id(\d+)\.html/', $_SERVER['REQUEST_URI'], $matches)) return intval($matches[1]);
    return 0;
}

function get_goods_comments_count($goods_id)
{
    if (!$goods_id) return '0';
    global $conn;
    if (!$conn) return '0';

    $sql = "SELECT COUNT(*) as count FROM comments WHERE post_id = ? AND type = 'goods' AND status = 1";
    $stmt = mysqli_prepare($conn, $sql);
    if (!$stmt) return '0';

    mysqli_stmt_bind_param($stmt, 'i', $goods_id);
    mysqli_stmt_execute($stmt);
    $result = mysqli_stmt_get_result($stmt);
    if ($row = mysqli_fetch_assoc($result)) {
        return $row['count'];
    }
    return '0';
}

function get_goods_field_value($db, $mapping, $tag, $goods_id)
{
    if (!$goods_id) return '';
    global $conn;
    $db_conn = $db ? $db : $conn;
    if (!$db_conn) return '';
    $table = isset($mapping['table']) ? $mapping['table'] : 'goods';
    $field = isset($mapping['field']) ? $mapping['field'] : str_replace('goods_', '', $tag);

    if (isset($mapping['join'])) {
        // join 格式: goods.category_id = categories.id
        $join_parts = explode('=', $mapping['join']);
        $left_side = trim($join_parts[0]);  // goods.category_id
        $right_side = trim($join_parts[1]); // categories.id

        $left_parts = explode('.', $left_side);
        $right_parts = explode('.', $right_side);

        $left_table = $left_parts[0];  // goods
        $left_field = $left_parts[1];  // category_id
        $right_table = $right_parts[0]; // categories
        $right_field = $right_parts[1]; // id

        $sql = "SELECT t2.{$field} FROM {$left_table} t1 JOIN {$right_table} t2 ON t1.{$left_field} = t2.{$right_field} WHERE t1.id = {$goods_id}";
    } else {
        $sql = "SELECT {$field} FROM {$table} WHERE id = {$goods_id}";
    }
    $result = execute_query($db_conn, $sql);
    if ($result && is_object($result)) {
        $row = $result->fetch_assoc();
        if ($row && isset($row[$field])) {
            $value = $row[$field];
            if ($tag === 'goods_thumbnail') {
                // 处理多张图片的情况，只返回第一张
                if (empty($value)) {
                    return DEFAULT_THUMBNAIL_PATH;
                }
                // 尝试解析JSON格式
                $images = json_decode($value, true);
                if (is_array($images) && !empty($images)) {
                    return qk_normalize_media_path($images[0]);
                }
                // 尝试用 | 分隔符分割
                if (strpos($value, '|') !== false) {
                    $images = explode('|', $value);
                    return qk_normalize_media_path(trim($images[0]));
                }
                // 单张图片
                return qk_normalize_media_path($value);
            }
            if ($tag === 'goods_price') {
                return number_format((float)$value, 2);
            }
            if ($tag === 'goods_url') {
                return '/goods_id' . $value . '.html';
            }
            if ($tag === 'goods_category_url') {
                return '/list_id' . $value . '.html';
            }
            if ($tag === 'goods_status') {
                return $value == 1 ? '上架' : '下架';
            }
            return $value;
        }
    }
    return '';
}

function get_goods_list_loop($db, $params, $goods_id)
{
    global $conn;
    $db_conn = $db ? $db : $conn;
    if (!$db_conn) return array();
    $row = isset($params['row']) ? intval($params['row']) : 0;
    $limit = $row > 0 ? $row : (isset($params['limit']) ? intval($params['limit']) : 8);
    $order = isset($params['order']) ? $params['order'] : 'id desc';
    $where = isset($params['where']) ? $params['where'] : 'status = 1';
    if (isset($params['category_id']) && is_numeric($params['category_id']) && intval($params['category_id']) > 0) {
        $where .= " AND category_id = " . intval($params['category_id']);
    }
    // 排除当前商品ID
    $exclude_id = isset($params['exclude_id']) ? intval($params['exclude_id']) : $goods_id;
    if ($exclude_id > 0) {
        $where .= " AND id != {$exclude_id}";
    }
    $page_key = isset($params['page_key']) ? intval($params['page_key']) : (isset($params['page']) ? intval($params['page']) : 0);
    $current_page = 1;
    if ($page_key > 0) {
        if (isset($_GET['page']) && is_numeric($_GET['page'])) $current_page = max(1, intval($_GET['page']));
        else if (isset($_SERVER['REQUEST_URI']) && preg_match('/_page(\d+)\.html$/i', $_SERVER['REQUEST_URI'], $m)) $current_page = max(1, intval($m[1]));
    }
    $offset = max(0, ($current_page - 1) * $limit);
    if ($page_key > 0) {
        global $qkcms_total_goods, $qkcms_current_goods_row;
        if (!isset($qkcms_total_goods) || !is_array($qkcms_total_goods)) $qkcms_total_goods = [];
        if (!isset($qkcms_current_goods_row) || !is_array($qkcms_current_goods_row)) $qkcms_current_goods_row = [];
        $qkcms_current_goods_row[$page_key] = $limit;
        $count_result = execute_query($db_conn, "SELECT COUNT(*) as total FROM goods WHERE {$where}");
        if ($count_result && is_object($count_result) && ($count_row = $count_result->fetch_assoc())) $qkcms_total_goods[$page_key] = intval($count_row['total']);
        else $qkcms_total_goods[$page_key] = 0;
    }
    $sql = $page_key > 0 ? "SELECT * FROM goods WHERE {$where} ORDER BY {$order} LIMIT {$offset}, {$limit}" : "SELECT * FROM goods WHERE {$where} ORDER BY {$order} LIMIT {$limit}";
    $result = execute_query($db_conn, $sql);
    if ($result && is_object($result)) {
        return $result->fetch_all(MYSQLI_ASSOC);
    }
    return array();
}

function get_hot_goods_loop($db, $params, $goods_id)
{
    global $conn;
    $db_conn = $db ? $db : $conn;
    if (!$db_conn) return array();
    $row = isset($params['row']) ? intval($params['row']) : 0;
    $limit = $row > 0 ? $row : (isset($params['limit']) ? intval($params['limit']) : 8);
    $where = isset($params['where']) ? $params['where'] : 'status = 1';
    if (isset($params['category_id']) && is_numeric($params['category_id']) && intval($params['category_id']) > 0) {
        $where .= " AND category_id = " . intval($params['category_id']);
    }
    $exclude_id = isset($params['exclude_id']) ? intval($params['exclude_id']) : $goods_id;
    if ($exclude_id > 0) {
        $where .= " AND id != {$exclude_id}";
    }
    $page_key = isset($params['page_key']) ? intval($params['page_key']) : (isset($params['page']) ? intval($params['page']) : 0);
    $current_page = 1;
    if ($page_key > 0) {
        if (isset($_GET['page']) && is_numeric($_GET['page'])) $current_page = max(1, intval($_GET['page']));
        else if (isset($_SERVER['REQUEST_URI']) && preg_match('/_page(\d+)\.html$/i', $_SERVER['REQUEST_URI'], $m)) $current_page = max(1, intval($m[1]));
    }
    $offset = max(0, ($current_page - 1) * $limit);
    if ($page_key > 0) {
        global $qkcms_total_goods, $qkcms_current_goods_row;
        if (!isset($qkcms_total_goods) || !is_array($qkcms_total_goods)) $qkcms_total_goods = [];
        if (!isset($qkcms_current_goods_row) || !is_array($qkcms_current_goods_row)) $qkcms_current_goods_row = [];
        $qkcms_current_goods_row[$page_key] = $limit;
        $count_result = execute_query($db_conn, "SELECT COUNT(*) as total FROM goods WHERE {$where}");
        if ($count_result && is_object($count_result) && ($count_row = $count_result->fetch_assoc())) $qkcms_total_goods[$page_key] = intval($count_row['total']);
        else $qkcms_total_goods[$page_key] = 0;
    }
    $sql = $page_key > 0 ? "SELECT * FROM goods WHERE {$where} ORDER BY sales DESC, id DESC LIMIT {$offset}, {$limit}" : "SELECT * FROM goods WHERE {$where} ORDER BY sales DESC, id DESC LIMIT {$limit}";
    $result = execute_query($db_conn, $sql);
    if ($result && is_object($result)) {
        return $result->fetch_all(MYSQLI_ASSOC);
    }
    return array();
}

function get_new_goods_loop($db, $params, $goods_id)
{
    global $conn;
    $db_conn = $db ? $db : $conn;
    if (!$db_conn) return array();
    $row = isset($params['row']) ? intval($params['row']) : 0;
    $limit = $row > 0 ? $row : (isset($params['limit']) ? intval($params['limit']) : 8);
    $where = isset($params['where']) ? $params['where'] : 'status = 1';
    if (isset($params['category_id']) && is_numeric($params['category_id']) && intval($params['category_id']) > 0) {
        $where .= " AND category_id = " . intval($params['category_id']);
    }
    $exclude_id = isset($params['exclude_id']) ? intval($params['exclude_id']) : $goods_id;
    if ($exclude_id > 0) {
        $where .= " AND id != {$exclude_id}";
    }
    $page_key = isset($params['page_key']) ? intval($params['page_key']) : (isset($params['page']) ? intval($params['page']) : 0);
    $current_page = 1;
    if ($page_key > 0) {
        if (isset($_GET['page']) && is_numeric($_GET['page'])) $current_page = max(1, intval($_GET['page']));
        else if (isset($_SERVER['REQUEST_URI']) && preg_match('/_page(\d+)\.html$/i', $_SERVER['REQUEST_URI'], $m)) $current_page = max(1, intval($m[1]));
    }
    $offset = max(0, ($current_page - 1) * $limit);
    if ($page_key > 0) {
        global $qkcms_total_goods, $qkcms_current_goods_row;
        if (!isset($qkcms_total_goods) || !is_array($qkcms_total_goods)) $qkcms_total_goods = [];
        if (!isset($qkcms_current_goods_row) || !is_array($qkcms_current_goods_row)) $qkcms_current_goods_row = [];
        $qkcms_current_goods_row[$page_key] = $limit;
        $count_result = execute_query($db_conn, "SELECT COUNT(*) as total FROM goods WHERE {$where}");
        if ($count_result && is_object($count_result) && ($count_row = $count_result->fetch_assoc())) $qkcms_total_goods[$page_key] = intval($count_row['total']);
        else $qkcms_total_goods[$page_key] = 0;
    }
    $sql = $page_key > 0 ? "SELECT * FROM goods WHERE {$where} ORDER BY create_time DESC, id DESC LIMIT {$offset}, {$limit}" : "SELECT * FROM goods WHERE {$where} ORDER BY create_time DESC, id DESC LIMIT {$limit}";
    $result = execute_query($db_conn, $sql);
    if ($result && is_object($result)) {
        return $result->fetch_all(MYSQLI_ASSOC);
    }
    return array();
}

function get_related_goods_loop($db, $params, $goods_id)
{
    if (!$goods_id) return array();
    global $conn;
    $db_conn = $db ? $db : $conn;
    if (!$db_conn) return array();
    $row = isset($params['row']) ? intval($params['row']) : 0;
    $limit = $row > 0 ? $row : (isset($params['limit']) ? intval($params['limit']) : 4);

    // 获取当前商品的关键词
    $goods_result = execute_query($db_conn, "SELECT keywords, title FROM goods WHERE id = {$goods_id}");
    if (!$goods_result || !is_object($goods_result)) return array();
    $goods_row = $goods_result->fetch_assoc();
    if (!$goods_row) return array();

    $keywords = $goods_row['keywords'];
    $title = $goods_row['title'];

    // 构建查询条件
    $where_conditions = array("id != {$goods_id}", "status = 1");

    // 如果有关键词，按关键词匹配
    if (!empty($keywords)) {
        $keyword_array = explode(',', $keywords);
        $keyword_conditions = array();
        foreach ($keyword_array as $kw) {
            $kw = trim($kw);
            if (!empty($kw)) {
                $kw_escaped = mysqli_real_escape_string($db_conn, $kw);
                $keyword_conditions[] = "title LIKE '%{$kw_escaped}%' OR keywords LIKE '%{$kw_escaped}%'";
            }
        }
        if (!empty($keyword_conditions)) {
            $where_conditions[] = '(' . implode(' OR ', $keyword_conditions) . ')';
        }
    }

    $where_clause = implode(' AND ', $where_conditions);
    $sql = "SELECT * FROM goods WHERE {$where_clause} ORDER BY RAND() LIMIT {$limit}";
    $result = execute_query($db_conn, $sql);
    if ($result && is_object($result)) {
        return $result->fetch_all(MYSQLI_ASSOC);
    }
    return array();
}

function get_price_asc_goods_loop($db, $params, $goods_id)
{
    global $conn;
    $db_conn = $db ? $db : $conn;
    if (!$db_conn) return array();
    $row = isset($params['row']) ? intval($params['row']) : 0;
    $limit = $row > 0 ? $row : (isset($params['limit']) ? intval($params['limit']) : 8);
    $where = isset($params['where']) ? ' AND ' . $params['where'] : '';
    // 排除当前商品ID
    $exclude_id = isset($params['exclude_id']) ? intval($params['exclude_id']) : $goods_id;
    if ($exclude_id > 0) {
        $where .= " AND id != {$exclude_id}";
    }
    $page_key = isset($params['page_key']) ? intval($params['page_key']) : (isset($params['page']) ? intval($params['page']) : 0);
    $current_page = 1;
    if ($page_key > 0) {
        if (isset($_GET['page']) && is_numeric($_GET['page'])) $current_page = max(1, intval($_GET['page']));
        else if (isset($_SERVER['REQUEST_URI']) && preg_match('/_page(\d+)\.html$/i', $_SERVER['REQUEST_URI'], $m)) $current_page = max(1, intval($m[1]));
    }
    $offset = max(0, ($current_page - 1) * $limit);
    if ($page_key > 0) {
        global $qkcms_total_goods, $qkcms_current_goods_row;
        if (!isset($qkcms_total_goods) || !is_array($qkcms_total_goods)) $qkcms_total_goods = [];
        if (!isset($qkcms_current_goods_row) || !is_array($qkcms_current_goods_row)) $qkcms_current_goods_row = [];
        $qkcms_current_goods_row[$page_key] = $limit;
        $count_result = execute_query($db_conn, "SELECT COUNT(*) as total FROM goods WHERE status = 1 {$where}");
        if ($count_result && is_object($count_result) && ($count_row = $count_result->fetch_assoc())) $qkcms_total_goods[$page_key] = intval($count_row['total']);
        else $qkcms_total_goods[$page_key] = 0;
    }
    $sql = $page_key > 0 ? "SELECT * FROM goods WHERE status = 1 {$where} ORDER BY price ASC LIMIT {$offset}, {$limit}" : "SELECT * FROM goods WHERE status = 1 {$where} ORDER BY price ASC LIMIT {$limit}";
    $result = execute_query($db_conn, $sql);
    if ($result && is_object($result)) {
        return $result->fetch_all(MYSQLI_ASSOC);
    }
    return array();
}

function get_price_desc_goods_loop($db, $params, $goods_id)
{
    global $conn;
    $db_conn = $db ? $db : $conn;
    if (!$db_conn) return array();
    $row = isset($params['row']) ? intval($params['row']) : 0;
    $limit = $row > 0 ? $row : (isset($params['limit']) ? intval($params['limit']) : 8);
    $where = isset($params['where']) ? ' AND ' . $params['where'] : '';
    // 排除当前商品ID
    $exclude_id = isset($params['exclude_id']) ? intval($params['exclude_id']) : $goods_id;
    if ($exclude_id > 0) {
        $where .= " AND id != {$exclude_id}";
    }
    $page_key = isset($params['page_key']) ? intval($params['page_key']) : (isset($params['page']) ? intval($params['page']) : 0);
    $current_page = 1;
    if ($page_key > 0) {
        if (isset($_GET['page']) && is_numeric($_GET['page'])) $current_page = max(1, intval($_GET['page']));
        else if (isset($_SERVER['REQUEST_URI']) && preg_match('/_page(\d+)\.html$/i', $_SERVER['REQUEST_URI'], $m)) $current_page = max(1, intval($m[1]));
    }
    $offset = max(0, ($current_page - 1) * $limit);
    if ($page_key > 0) {
        global $qkcms_total_goods, $qkcms_current_goods_row;
        if (!isset($qkcms_total_goods) || !is_array($qkcms_total_goods)) $qkcms_total_goods = [];
        if (!isset($qkcms_current_goods_row) || !is_array($qkcms_current_goods_row)) $qkcms_current_goods_row = [];
        $qkcms_current_goods_row[$page_key] = $limit;
        $count_result = execute_query($db_conn, "SELECT COUNT(*) as total FROM goods WHERE status = 1 {$where}");
        if ($count_result && is_object($count_result) && ($count_row = $count_result->fetch_assoc())) $qkcms_total_goods[$page_key] = intval($count_row['total']);
        else $qkcms_total_goods[$page_key] = 0;
    }
    $sql = $page_key > 0 ? "SELECT * FROM goods WHERE status = 1 {$where} ORDER BY price DESC LIMIT {$offset}, {$limit}" : "SELECT * FROM goods WHERE status = 1 {$where} ORDER BY price DESC LIMIT {$limit}";
    $result = execute_query($db_conn, $sql);
    if ($result && is_object($result)) {
        return $result->fetch_all(MYSQLI_ASSOC);
    }
    return array();
}

function parse_goods_loop_content($loop_items, $loop_content)
{
    $result = '';
    if (!is_array($loop_items)) return $result;
    global $conn;
    foreach ($loop_items as $item) {
        $item_content = $loop_content;
        $item_content = str_replace('{goods.id}', $item['id'], $item_content);
        $item_content = str_replace('{goods.title}', $item['title'], $item_content);
        $item_content = str_replace('{goods.price}', number_format((float)$item['price'], 2), $item_content);
        $item_content = str_replace('{goods.sales}', $item['sales'], $item_content);
        $item_content = str_replace('{goods.stock}', $item['stock'], $item_content);
        // 处理时间标签
        if (isset($item['create_time'])) {
            $item_content = str_replace('{goods.create_time}', $item['create_time'], $item_content);
        }
        if (isset($item['update_time'])) {
            $item_content = str_replace('{goods.update_time}', $item['update_time'], $item_content);
        }
        // 处理分类名称和URL
        if (isset($item['category_id']) && $item['category_id'] > 0) {
            $category_name = getCategoryName($conn, $item['category_id']);
            $item_content = str_replace('{goods.category_name}', $category_name, $item_content);
            $item_content = str_replace('{goods.category_url}', '/list_id' . $item['category_id'] . '.html', $item_content);
        } else {
            $item_content = str_replace('{goods.category_name}', '', $item_content);
            $item_content = str_replace('{goods.category_url}', '', $item_content);
        }
        // 处理规格字段
        if (isset($item['specs'])) {
            $item_content = str_replace('{goods.specs}', $item['specs'], $item_content);
        } else {
            $item_content = str_replace('{goods.specs}', '', $item_content);
        }
        // 处理缩略图，如果为空或不存在则使用默认缩略图
        $thumbnail = DEFAULT_THUMBNAIL_PATH;
        if (!empty($item['image'])) {
            // 尝试解析JSON格式的图片数组
            $images = json_decode($item['image'], true);
            if (is_array($images) && !empty($images)) {
                $thumbnail = qk_normalize_media_path($images[0]);
            } elseif (strpos($item['image'], '|') !== false) {
                // 用 | 分隔符分割，取第一张
                $images = explode('|', $item['image']);
                $thumbnail = qk_normalize_media_path(trim($images[0]));
            } else {
                $thumbnail = qk_normalize_media_path($item['image']);
            }
        }
        $item_content = str_replace('{goods.thumbnail}', $thumbnail, $item_content);
        $item_content = str_replace('{goods.url}', '/goods_id' . $item['id'] . '.html', $item_content);
        $result .= $item_content;
    }
    return $result;
}

function get_goods_images($goods_id)
{
    if (!$goods_id) return array(array('title' => '默认图片', 'url' => DEFAULT_THUMBNAIL_PATH));
    global $conn;
    $db_conn = $conn;
    if (!$db_conn) return array(array('title' => '默认图片', 'url' => DEFAULT_THUMBNAIL_PATH));
    $result = execute_query($db_conn, "SELECT image FROM goods WHERE id = {$goods_id}");
    if ($result && is_object($result)) {
        $row = $result->fetch_assoc();
        if ($row && !empty($row['image'])) {
            // 先尝试解析JSON格式
            $images = json_decode($row['image'], true);
            if (is_array($images) && !empty($images)) {
                $formatted_images = array();
                foreach ($images as $index => $image_url) {
                    // 从图片URL中提取文件名作为标题
                    $image_name = basename($image_url);
                    $image_title = pathinfo($image_name, PATHINFO_FILENAME);
                    // 如果标题为空，使用默认标题
                    if (empty($image_title)) {
                        $image_title = '图片' . ($index + 1);
                    }
                    $formatted_images[] = array(
                        'title' => $image_title,
                        'url' => qk_normalize_media_path($image_url)
                    );
                }
                return $formatted_images;
            }
            // 如果不是JSON，尝试用 | 分隔符分割
            if (strpos($row['image'], '|') !== false) {
                $image_urls = explode('|', $row['image']);
                // 过滤空值并重新索引数组
                $image_urls = array_filter($image_urls, function ($v) {
                    return !empty(trim($v));
                });
                // 如果过滤后为空，返回默认缩略图
                if (empty($image_urls)) {
                    return array(array('title' => '默认图片', 'url' => DEFAULT_THUMBNAIL_PATH));
                }
                $formatted_images = array();
                foreach ($image_urls as $index => $image_url) {
                    // 从图片URL中提取文件名作为标题
                    $image_name = basename($image_url);
                    $image_title = pathinfo($image_name, PATHINFO_FILENAME);
                    // 如果标题为空，使用默认标题
                    if (empty($image_title)) {
                        $image_title = '图片' . ($index + 1);
                    }
                    $formatted_images[] = array(
                        'title' => $image_title,
                        'url' => qk_normalize_media_path($image_url)
                    );
                }
                return $formatted_images;
            }
            // 单张图片
            $image_url = $row['image'];
            // 从图片URL中提取文件名作为标题
            $image_name = basename($image_url);
            $image_title = pathinfo($image_name, PATHINFO_FILENAME);
            // 如果标题为空，使用默认标题
            if (empty($image_title)) {
                $image_title = '图片1';
            }
            return array(array(
                'title' => $image_title,
                'url' => qk_normalize_media_path($image_url)
            ));
        }
    }
    return array(array('title' => '默认图片', 'url' => DEFAULT_THUMBNAIL_PATH));
}

function get_goods_bind_images($goods_id)
{
    if (!$goods_id) return array();
    global $conn;
    $db_conn = $conn;
    if (!$db_conn) return array();

    // 从当前商品的 parent_id 字段获取绑定的商品ID
    $result = execute_query($db_conn, "SELECT parent_id FROM goods WHERE id = {$goods_id}");
    if ($result && is_object($result)) {
        $row = $result->fetch_assoc();
        if ($row && !empty($row['parent_id'])) {
            $parent_ids = $row['parent_id'];
            // 处理 parent_id 可能是逗号分隔的多个ID
            $parent_id_array = explode(',', $parent_ids);
            $formatted_images = array();

            foreach ($parent_id_array as $index => $parent_id) {
                $parent_id = trim($parent_id);
                if (!empty($parent_id)) {
                    // 获取绑定商品的图片、标题、价格
                    $parent_result = execute_query($db_conn, "SELECT id, title, price, image FROM goods WHERE id = {$parent_id}");
                    
                    // 检查商品是否存在
                    if ($parent_result && is_object($parent_result)) {
                        $parent_row = $parent_result->fetch_assoc();
                        if ($parent_row) {
                            // 商品存在，继续处理
                            $first_image = DEFAULT_THUMBNAIL_PATH;
                            $goods_title = isset($parent_row['title']) ? $parent_row['title'] : '';
                            $goods_price = isset($parent_row['price']) ? number_format((float)$parent_row['price'], 2) : '0.00';

                            // 解析绑定商品的图片，只取第一张
                            if (!empty($parent_row['image'])) {
                                $parent_images = $parent_row['image'];

                                // 尝试解析JSON格式
                                $json_images = json_decode($parent_images, true);
                                if (is_array($json_images) && !empty($json_images)) {
                                    $first_image = qk_normalize_media_path($json_images[0]);
                                } elseif (strpos($parent_images, '|') !== false) {
                                    // 用 | 分隔符分割，取第一张
                                    $image_array = explode('|', $parent_images);
                                    $first_image = qk_normalize_media_path(trim($image_array[0]));
                                } else {
                                    $first_image = qk_normalize_media_path($parent_images);
                                }
                            }

                            // 如果没有标题，从图片URL中提取文件名作为标题
                            if (empty($goods_title)) {
                                $image_name = basename($first_image);
                                $goods_title = pathinfo($image_name, PATHINFO_FILENAME);
                            }
                            // 如果标题为空，使用默认标题
                            if (empty($goods_title)) {
                                $goods_title = '绑定商品' . ($index + 1);
                            }

                            // 生成商品链接
                            $goods_url = '/goods_id' . $parent_id . '.html';

                            // 添加商品数据
                            $formatted_images[] = array(
                                'title' => $goods_title,
                                'url' => $first_image,
                                'goods_url' => $goods_url,
                                'goods_price' => $goods_price
                            );
                        }
                    }
                }
            }

            // 只返回存在的商品数据
            return $formatted_images;
        }
    }

    return array();
}

function get_goods_specs_images($goods_id)
{
    if (!$goods_id) return array(array('title' => '默认规格', 'url' => DEFAULT_THUMBNAIL_PATH, 'price' => '0.00', 'stock' => '0'));
    global $conn;
    $db_conn = $conn;
    if (!$db_conn) return array(array('title' => '默认规格', 'url' => DEFAULT_THUMBNAIL_PATH, 'price' => '0.00', 'stock' => '0'));

    // 从 goods 表的 specs 字段获取规格属性
    // 使用参数化查询，防止SQL注入
    $stmt = $db_conn->prepare("SELECT specs FROM goods WHERE id = ?");
    $stmt->bind_param("i", $goods_id);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result && is_object($result)) {
        $row = $result->fetch_assoc();
        if ($row && !empty($row['specs'])) {
            $specs_data = $row['specs'];
            $specs = array();

            // 尝试解析JSON格式的规格数据
            $specs_array = json_decode($specs_data, true);
            if (is_array($specs_array)) {
                // 处理JSON格式的规格数据
                $specs = process_specs_array($specs_array);
            } elseif (strpos($specs_data, '|') !== false) {
                // 用 | 分隔符分割的简单格式
                $items = explode('|', $specs_data);
                foreach ($items as $index => $item) {
                    $item = trim($item);
                    if (!empty($item)) {
                        $specs[] = array(
                            'title' => $item,
                            'url' => (filter_var($item, FILTER_VALIDATE_URL) || qk_is_local_media_path($item)) ? qk_normalize_media_path($item) : DEFAULT_THUMBNAIL_PATH,
                            'price' => '0.00',
                            'stock' => '0'
                        );
                    }
                }
            } elseif (strpos($specs_data, ',') !== false) {
                // 用逗号分隔符分割的简单格式
                $items = explode(',', $specs_data);
                foreach ($items as $index => $item) {
                    $item = trim($item);
                    if (!empty($item)) {
                        $specs[] = array(
                            'title' => $item,
                            'url' => (filter_var($item, FILTER_VALIDATE_URL) || qk_is_local_media_path($item)) ? qk_normalize_media_path($item) : DEFAULT_THUMBNAIL_PATH,
                            'price' => '0.00',
                            'stock' => '0'
                        );
                    }
                }
            } else {
                // 单个规格项
                $specs[] = array(
                    'title' => $specs_data,
                    'url' => (filter_var($specs_data, FILTER_VALIDATE_URL) || qk_is_local_media_path($specs_data)) ? qk_normalize_media_path($specs_data) : DEFAULT_THUMBNAIL_PATH,
                    'price' => '0.00',
                    'stock' => '0'
                );
            }

            // 关闭语句
            $stmt->close();

            // 如果没有找到规格，返回默认规格
            return !empty($specs) ? $specs : array(array('title' => '默认规格', 'url' => DEFAULT_THUMBNAIL_PATH, 'price' => '0.00', 'stock' => '0'));
        }
    }

    // 关闭语句
    $stmt->close();

    return array(array('title' => '默认规格', 'url' => DEFAULT_THUMBNAIL_PATH, 'price' => '0.00', 'stock' => '0'));
}

// 辅助函数：处理规格数组
function process_specs_array($array)
{
    $specs = array();
    foreach ($array as $item) {
        if (is_array($item)) {
            // 如果是数组，提取标题和图片
            $title = isset($item['title']) ? $item['title'] : (isset($item['name']) ? $item['name'] : '规格');
            $image = !empty($item['image']) ? $item['image'] : DEFAULT_THUMBNAIL_PATH;
            $price = isset($item['price']) ? number_format((float)$item['price'], 2) : '0.00';
            $stock = isset($item['stock']) ? $item['stock'] : '0';
            $specs[] = array(
                'title' => $title,
                'url' => $image,
                'price' => $price,
                'stock' => $stock
            );
        } elseif (filter_var($item, FILTER_VALIDATE_URL) || qk_is_local_media_path($item)) {
            // 如果是URL，作为图片
            $image_name = basename($item);
            $title = pathinfo($image_name, PATHINFO_FILENAME);
            $specs[] = array(
                'title' => $title,
                'url' => qk_normalize_media_path($item),
                'price' => '0.00',
                'stock' => '0'
            );
        } else {
            // 否则作为文字规格
            $specs[] = array(
                'title' => $item,
                'url' => DEFAULT_THUMBNAIL_PATH,
                'price' => '0.00',
                'stock' => '0'
            );
        }
    }
    return $specs;
}

function parse_goods_images_loop($images, $loop_content, $goods_id = 0)
{
    $result = '';
    if (!is_array($images) || empty($images)) return $result;

    // 如果提供了商品ID，获取商品的规格字段
    $goods_specs = '';
    if ($goods_id > 0) {
        global $conn;
        $db_conn = $conn;
        if ($db_conn) {
            // 使用参数化查询，防止SQL注入
            $stmt = $db_conn->prepare("SELECT specs FROM goods WHERE id = ?");
            $stmt->bind_param("i", $goods_id);
            $stmt->execute();
            $result_specs = $stmt->get_result();
            if ($result_specs && is_object($result_specs)) {
                $row_specs = $result_specs->fetch_assoc();
                if ($row_specs && !empty($row_specs['specs'])) {
                    $goods_specs = $row_specs['specs'];
                }
            }
            $stmt->close();
        }
    }

    foreach ($images as $index => $image) {
        $item_content = $loop_content;

        // 处理不同格式的图片数据
        if (is_array($image) && isset($image['url'])) {
            // 关联数组格式（规格属性或绑定商品）
            $image_url = $image['url'];
            $image_title = isset($image['title']) ? $image['title'] : '图片' . ($index + 1);
            $image_price = isset($image['price']) ? $image['price'] : '0.00';
            $image_stock = isset($image['stock']) ? $image['stock'] : '0';
            // 绑定商品的链接和价格
            $image_goods_url = isset($image['goods_url']) ? $image['goods_url'] : '';
            $image_goods_price = isset($image['goods_price']) ? $image['goods_price'] : '0.00';
        } else {
            // 简单URL格式
            $image_url = !empty($image) ? $image : DEFAULT_THUMBNAIL_PATH;
            // 从图片URL中提取文件名作为标题
            $image_name = basename($image_url);
            $image_title = pathinfo($image_name, PATHINFO_FILENAME);
            // 如果标题为空，使用默认标题
            if (empty($image_title)) {
                $image_title = '图片' . ($index + 1);
            }
            $image_price = '0.00';
            $image_stock = '0';
            $image_goods_url = '';
            $image_goods_price = '0.00';
        }

        // 使用数组批量替换标签，提高性能
        $replacements = array(
            '{image.url}' => $image_url,
            '{image.index}' => $index + 1,
            '{image.title}' => $image_title,
            '{image.alt}' => $image_title, // 保持兼容
            '{image.price}' => $image_price,
            '{image.stock}' => $image_stock,
            '{image.goods_url}' => $image_goods_url,
            '{image.goods_price}' => $image_goods_price,
            '{goods.specs}' => $goods_specs
        );

        $item_content = str_replace(array_keys($replacements), array_values($replacements), $item_content);
        $result .= $item_content;
    }
    return $result;
}

function qkcms_goods_tag_parser($content, $goods_id = 0)
{
    if (!$goods_id) {
        $goods_id = get_goods_id();
    }

    // 解析商品图片循环标签 {qkcms:goods_images type="xxx"}...{/qkcms:goods_images}
    $pattern = '/\{qkcms:goods_images\s*([^}]*)\}(.*?)\{\/qkcms:goods_images\}/s';
    $content = preg_replace_callback($pattern, function ($matches) use ($goods_id) {
        $params_str = $matches[1];
        $loop_content = $matches[2];

        $params = parseAttributes($params_str);

        // 获取类型参数，默认为 images
        $type = isset($params['type']) ? $params['type'] : 'images';

        // 根据类型获取图片
        $images = array();
        switch ($type) {
            case 'images':
                $images = get_goods_images($goods_id);
                break;
            case 'bind':
                $images = get_goods_bind_images($goods_id);
                break;
            case 'specs':
                $images = get_goods_specs_images($goods_id);
                break;
            default:
                $images = get_goods_images($goods_id);
        }

        return parse_goods_images_loop($images, $loop_content, $goods_id);
    }, $content);

    // 解析循环标签 {qkcms:goods type="xxx" ...}...{/qkcms:goods}
    $pattern = '/\{qkcms:goods\s+([^}]*)\}(.*?)\{\/qkcms:goods\}/s';
    $content = preg_replace_callback($pattern, function ($matches) use ($goods_id) {
        $params_str = $matches[1];
        $loop_content = $matches[2];
        $params = parseAttributes($params_str);
        $type = isset($params['type']) ? $params['type'] : 'list';
        if ($type === 'hot') {
            $loop_items = get_hot_goods_loop(null, $params, $goods_id);
        } else if ($type === 'new') {
            $loop_items = get_new_goods_loop(null, $params, $goods_id);
        } else if ($type === 'related') {
            $loop_items = get_related_goods_loop(null, $params, $goods_id);
        } else if ($type === 'price_asc') {
            $loop_items = get_price_asc_goods_loop(null, $params, $goods_id);
        } else if ($type === 'price_desc') {
            $loop_items = get_price_desc_goods_loop(null, $params, $goods_id);
        } else {
            // 默认按最新排序
            $loop_items = get_new_goods_loop(null, $params, $goods_id);
        }
        return parse_goods_loop_content($loop_items, $loop_content);
    }, $content);

    // 解析单值标签 {qkcms:goods_xxx} 和 {goods_xxx}
    $pattern = '/\{(?:qkcms:)?(goods_[a-z0-9_]+)\}/';
    $content = preg_replace_callback($pattern, function ($matches) use ($goods_id) {
        $tag = $matches[1];
        $mapping = isset($GLOBALS['goods_tag_mapping'][$tag]) ? $GLOBALS['goods_tag_mapping'][$tag] : null;
        if ($mapping) {
            if ($mapping['type'] === 'special' && $tag === 'goods_id') {
                return $goods_id ? $goods_id : '';
            }
            if ($mapping['type'] === 'special' && $tag === 'goods_comments') {
                return get_goods_comments_count($goods_id);
            }
            return get_goods_field_value(null, $mapping, $tag, $goods_id);
        }
        return $matches[0];
    }, $content);

    return $content;
}
