<?php
namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;

/**
 * 数据统计中心定时任务
 * 
 * 该类负责定时统计商城核心业务数据，包括：
 * 1. 核心交易指标（成交额、订单数、客单价、利润率等）
 * 2. 访客统计（基于小程序行为埋点表）
 * 3. 注册用户统计
 * 4. 转化漏斗分析（访问→浏览→加购→下单→支付）
 * 5. 趋势数据（按天统计的成交额、订单数、注册增长趋势）
 * 6. 客单价分布
 * 7. 实时订单动态
 * 
 * 定时执行时间：每天凌晨1点40分
 * 统计范围：近7天、近30天、近90天
 */
class DataStatsCenter extends Command
{
    /**
     * 缓存过期时间（25小时）
     * 设置为25小时是为了确保当天的数据缓存能覆盖到第二天任务执行前
     */
    const CACHE_TTL = 90000;

    /**
     * 统计快照表名称
     */
    const STATS_TABLE = 'shopro_data_stats_center';

    /**
     * 默认统计时间范围（单位：天）
     */
    const TIME_RANGES = [7, 30, 90];

    /**
     * 控制台输出对象
     * @var Output
     */
    protected $output = null;

    /**
     * 配置命令信息
     */
    protected function configure()
    {
        $this->setName('shopro:data-stats-center')
             ->setDescription('每天凌晨1点40执行的数据统计中心任务');
    }

    /**
     * 自定义日志记录方法
     * 
     * @param string $level 日志级别: info/warning/error
     * @param string $message 日志内容
     */
    protected function customLog($level, $message)
    {
        // 格式化日志消息，添加时间戳和级别标识
        $timestamp = date('Y-m-d H:i:s');
        $formattedMessage = "[{$timestamp}] [{$level}] {$message}";
        
        // 如果有控制台输出对象，同时输出到控制台
        if ($this->output) {
            // 根据级别使用不同颜色输出
            switch ($level) {
                case 'error':
                    $this->output->writeln("<error>{$formattedMessage}</error>");
                    break;
                case 'warning':
                    $this->output->writeln("<comment>{$formattedMessage}</comment>");
                    break;
                default:
                    $this->output->writeln("<info>{$formattedMessage}</info>");
                    break;
            }
        }

        // 记录到自定义日志文件
        custom_log($message, 'stat_center', $level);
    }

    /**
     * 命令执行入口方法
     * 
     * @param Input $input 输入参数
     * @param Output $output 输出对象
     */
    public function execute(Input $input, Output $output)
    {
        $this->output = $output;
        $this->customLog('info', '========== 开始执行数据统计中心定时任务 ==========');

        try {
            // 确保统计快照表存在
            // $this->ensureStatsTable();

            // 获取当前时间作为快照时间
            $snapshotTime = time();
            $snapshotDate = date('Y-m-d', $snapshotTime);
            $allStats = [];

            // 遍历所有统计时间范围（7天、30天、90天）
            foreach (self::TIME_RANGES as $days) {
                $rangeKey = $days . 'days';
                $this->customLog('info', '---------- 开始统计近' . $days . '天数据 ----------');

                // 构建统计数据
                $stats = $this->buildStatsPayload($days, $snapshotTime);
                // 持久化到数据库
                $this->persistStats($snapshotDate, $rangeKey, $stats);

                // 缓存统计结果
                cache($this->buildCacheKey($rangeKey), $stats, self::CACHE_TTL);
                $allStats[$rangeKey] = $stats;

                $this->customLog('info', '近' . $days . '天统计完成，缓存已更新');
            }

            // 缓存所有时间范围的统计数据
            cache($this->buildCacheKey('all'), $allStats, self::CACHE_TTL);

            $this->customLog('info', '[' . date('Y-m-d H:i:s') . ']数据统计中心定时任务执行完成');
        } catch (\Exception $e) {
            // 记录异常日志并输出到控制台
            format_log_error_custom_name($e, '[数据统计中心定时任务执行失败]', '', 'stat_center');
        }
    }

    /**
     * 自动创建统计快照表（如果不存在）
     * 
     * 表结构包含：
     * - 快照日期、时间范围
     * - 统计开始/结束时间戳
     * - 各统计模块的JSON数据字段
     */
    protected function ensureStatsTable()
    {
        $table = $this->getFullTableName(self::STATS_TABLE);

        $sql = "CREATE TABLE IF NOT EXISTS `{$table}` (
            `id` int unsigned NOT NULL AUTO_INCREMENT,
            `snapshot_date` date NOT NULL COMMENT '快照日期',
            `time_range` varchar(20) NOT NULL DEFAULT '' COMMENT '时间范围',
            `range_start` int unsigned NOT NULL DEFAULT 0 COMMENT '统计开始时间',
            `range_end` int unsigned NOT NULL DEFAULT 0 COMMENT '统计结束时间',
            `summary_json` mediumtext COMMENT '顶部卡片统计',
            `trend_json` mediumtext COMMENT '趋势数据',
            `funnel_json` text COMMENT '转化漏斗',
            `distribution_json` text COMMENT '客单价分布',
            `recent_orders_json` mediumtext COMMENT '实时订单动态',
            `extra_json` mediumtext COMMENT '扩展信息',
            `created_at` int unsigned NOT NULL DEFAULT 0 COMMENT '创建时间',
            `updated_at` int unsigned NOT NULL DEFAULT 0 COMMENT '更新时间',
            PRIMARY KEY (`id`),
            UNIQUE KEY `uniq_snapshot_range` (`snapshot_date`, `time_range`),
            KEY `idx_time_range` (`time_range`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据统计中心快照表'";

        Db::execute($sql);
    }

    /**
     * 构建整包统计数据
     * 
     * @param int $days 统计天数
     * @param int $snapshotTime 快照时间戳
     * @return array 包含所有统计模块的完整数据
     */
    protected function buildStatsPayload($days, $snapshotTime)
    {
        // 构建当前统计周期和上一统计周期的时间范围        
        $currentRange = $this->buildRange($days, $snapshotTime);
        $previousRange = $this->buildRange($days, $currentRange['start'] - 1);

        $this->customLog('info', '周期时间：[ ' . date('Y-m-d', $currentRange['start']) . '] 至 [ ' . date('Y-m-d', $currentRange['end']) . ']');

        // 获取当前周期和上一周期的核心交易指标 （成交总额、净利润率，客单价，订单数）
        $this->customLog('info', PHP_EOL . '核心指标-开始构建 ： 成交总额、净利润率，客单价，订单数，访客数，累计注册用户数，转化率');
        $currentRevenue = $this->getRevenueMetrics($currentRange['start'], $currentRange['end']);
        $this->customLog('info', '核心指标-当前周期-核心交易指标 成交总额、净利润率，客单价，订单数 : ' . json_encode($currentRevenue));
        $previousRevenue = $this->getRevenueMetrics($previousRange['start'], $previousRange['end']);
        $this->customLog('info', '核心指标-上一周期-核心交易指标 成交总额、净利润率，客单价，订单数 : ' . json_encode($previousRevenue));

        // 获取当前周期和上一周期的访客数
        $currentVisitors = $this->getVisitorCount($currentRange['start'], $currentRange['end']);
        $this->customLog('info', '核心指标-当前周期-访客数 : ' . $currentVisitors);
        $previousVisitors = $this->getVisitorCount($previousRange['start'], $previousRange['end']);
        $this->customLog('info', '核心指标-上一周期-访客数 : ' . $previousVisitors);

        // 获取累计注册用户数（截至当前周期结束和上一周期结束）
        $currentRegisterTotal = $this->getRegisterCount(0, $currentRange['end']);
        $this->customLog('info', '核心指标-当前周期-累计注册用户数 : ' . $currentRegisterTotal);
        $previousRegisterTotal = $this->getRegisterCount(0, $previousRange['end']);
        $this->customLog('info', '核心指标-上一周期-累计注册用户数 : ' . $previousRegisterTotal);
        
        // 计算转化率（购买用户数/访客数）
        $currentConversion = $currentVisitors > 0 ? round($currentRevenue['buyer_count'] / $currentVisitors * 100, 2) : 0;
        $previousConversion = $previousVisitors > 0 ? round($previousRevenue['buyer_count'] / $previousVisitors * 100, 2) : 0;
        $this->customLog('info', '核心指标-当前周期-转化率 : ' . $currentConversion . '  核心指标-上一周期-转化率 : ' . $previousConversion . PHP_EOL);
        
        // 构建趋势数据
        $this->customLog('info', '趋势数据-开始构建 ： 成交总额趋势、订单数趋势、注册用户增长趋势');
        $trendData = [
            'amount' => $this->buildAmountTrend($days, $snapshotTime),
            'orders' => $this->buildOrderTrend($days, $snapshotTime),
            'register' => $this->buildRegisterTrend($days, $snapshotTime),
        ];
        $this->customLog('info', '趋势数据-统计结果 : ' . json_encode($trendData) . PHP_EOL);

        // 构建漏斗数据
        $this->customLog('info', '转化漏斗数据-开始构建');
        $funnelData = $this->buildFunnelData($currentRange['start'], $currentRange['end']);
        $this->customLog('info', '转化漏斗数据-统计结果 : ' . json_encode($funnelData) . PHP_EOL);

        // 构建价格分布
        $this->customLog('info', '客单价分布-开始构建');
        $priceDistribution = $this->buildPriceDistribution($currentRange['start'], $currentRange['end']);
        $this->customLog('info', '客单价分布-统计结果 : ' . json_encode($priceDistribution) . PHP_EOL);

        // 获取最近订单 页面单独分页访问，不需要在快照中包含，暂时去掉
        // $this->customLog('info', '开始获取当前统计周期最近订单');
        // $recentOrders = $this->getRecentOrders(10);

        // 组装完整的统计数据结构
        return [
            'snapshot_date' => date('Y-m-d', $snapshotTime),
            'time_range' => $days . 'days',
            'days' => $days,
            'generated_at' => $snapshotTime,
            'range' => [
                'start' => $currentRange['start'],
                'end' => $currentRange['end'],
                'start_text' => date('Y-m-d H:i:s', $currentRange['start']),
                'end_text' => date('Y-m-d H:i:s', $currentRange['end']),
            ],
            'summary' => [
                'total_amount' => $this->buildMetricCard('成交总额', $currentRevenue['total_amount'], $previousRevenue['total_amount'], 'money'),
                'profit_rate' => $this->buildMetricCard('净利率', $currentRevenue['profit_rate'], $previousRevenue['profit_rate'], 'percent'),
                'avg_amount' => $this->buildMetricCard('客单价', $currentRevenue['avg_amount'], $previousRevenue['avg_amount'], 'money'),
                'order_count' => $this->buildMetricCard('订单数', $currentRevenue['order_count'], $previousRevenue['order_count'], 'count'),
                'visitor_count' => $this->buildMetricCard('访客数', $currentVisitors, $previousVisitors, 'count'),
                'conversion_rate' => $this->buildMetricCard('转化率', $currentConversion, $previousConversion, 'percent'),
                'register_users' => $this->buildMetricCard('注册用户', $currentRegisterTotal, $previousRegisterTotal, 'count'),
            ],
            'trend' => $trendData,
            'funnel' => $funnelData,
            'price_distribution' => $priceDistribution,
            // 'recent_orders' => $recentOrders,
            'extra' => [
                'current_revenue' => $currentRevenue,
                'previous_revenue' => $previousRevenue,
            ],
        ];
    }

    /**
     * 保存统计快照到数据库
     * 
     * @param string $snapshotDate 快照日期（Y-m-d格式）
     * @param string $timeRange 时间范围标识（如 '7days'）
     * @param array $stats 统计数据数组
     */
    protected function persistStats($snapshotDate, $timeRange, array $stats)
    {
        $now = time();
        $data = [
            'snapshot_date' => $snapshotDate,
            'time_range' => $timeRange,
            'range_start' => $stats['range']['start'],
            'range_end' => $stats['range']['end'],
            'summary_json' => json_encode($stats['summary'], JSON_UNESCAPED_UNICODE),
            'trend_json' => json_encode($stats['trend'], JSON_UNESCAPED_UNICODE),
            'funnel_json' => json_encode($stats['funnel'], JSON_UNESCAPED_UNICODE),
            'distribution_json' => json_encode($stats['price_distribution'], JSON_UNESCAPED_UNICODE),
            // 'recent_orders_json' => json_encode($stats['recent_orders'], JSON_UNESCAPED_UNICODE),
            'extra_json' => json_encode($stats['extra'], JSON_UNESCAPED_UNICODE),
            'updated_at' => $now,
        ];

        // 检查是否已存在同日期同时间范围的记录
        $row = Db::name(self::STATS_TABLE)
            ->where('snapshot_date', $snapshotDate)
            ->where('time_range', $timeRange)
            ->find();

        if ($row) {
            // 更新已存在的记录
            Db::name(self::STATS_TABLE)->where('id', $row['id'])->update($data);
        } else {
            // 插入新记录
            $data['created_at'] = $now;
            Db::name(self::STATS_TABLE)->insert($data);
        }
    }

    /**
     * 构建统计时间区间
     * 
     * @param int $days 统计天数
     * @param int $endTime 结束时间戳
     * @return array 包含 start 和 end 的时间范围数组
     */
    protected function buildRange($days, $endTime)
    {
        $endTime = strtotime(date('Y-m-d 23:59:59', $endTime));
        // 计算开始时间：从结束时间往前推 days-1 天的 00:00:00
        $startTime = strtotime(date('Y-m-d 00:00:00', strtotime('-' . ($days - 1) . ' days', $endTime)));

        return [
            'start' => $startTime,
            'end' => $endTime,
        ];
    }

    /**
     * 汇总核心交易指标
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return array 包含成交额、订单数、客单价、成本、退款、利润、利润率
     */
    protected function getRevenueMetrics($startTime, $endTime)
    {
        // 统计成交额（已支付和已完成订单的支付金额总和）
        $totalAmount = round((float)Db::name('shopro_order')
            ->whereIn('status', ['paid', 'completed'])
            ->where('paid_time', 'between', [$startTime, $endTime])
            ->sum('pay_fee'), 2);

        // 统计订单数
        $orderCount = (int)Db::name('shopro_order')
            ->whereIn('status', ['paid', 'completed'])
            ->where('paid_time', 'between', [$startTime, $endTime])
            ->count();

        // 统计购买用户数（去重）-用于计算转化率
        $buyerCount = (int)Db::name('shopro_order')
            ->whereIn('status', ['paid', 'completed'])
            ->where('paid_time', 'between', [$startTime, $endTime])
            ->where('user_id', '>', 0)
            ->count('DISTINCT user_id');

        // 计算客单价（成交额/购买用户数）
        $avgAmount = $buyerCount > 0 ? round($totalAmount / $buyerCount, 2) : 0;

        // 统计商品成本（成本价 × 数量）
        $table = $this->getFullTableName('shopro_order_item');
        $orderTable = $this->getFullTableName('shopro_order');
        $sql = "SELECT SUM(item.cost_price * item.goods_num) AS total FROM `{$table}` item JOIN `{$orderTable}` o ON o.id = item.order_id WHERE o.status IN ('paid', 'completed') AND o.paid_time BETWEEN ? AND ?";
        $result = Db::query($sql, [$startTime, $endTime]);
        $costAmount = round((float)(isset($result[0]['total']) ? $result[0]['total'] : 0), 2);

        // 统计退款金额
        $refundAmount = round((float)Db::name('shopro_refund')
            ->where('status', 'completed')
            ->where('createtime', 'between', [$startTime, $endTime])
            ->sum('refund_fee'), 2);

        // 计算利润和利润率
        $profitAmount = round($totalAmount - $refundAmount - $costAmount, 2);
        $profitRate = $totalAmount > 0 ? round($profitAmount / $totalAmount * 100, 2) : 0;

        return [
            'total_amount' => $totalAmount,
            'order_count' => $orderCount,
            'buyer_count' => $buyerCount,
            'avg_amount' => $avgAmount,
            'cost_amount' => $costAmount,
            'refund_amount' => $refundAmount,
            'profit_amount' => $profitAmount,
            'profit_rate' => $profitRate,
        ];
    }

    /**
     * 获取访客数
     * 
     * 用户识别策略：
     * 1. 优先使用 user_id（登录用户）
     * 2. 其次使用 session_id（未登录但有会话的用户）
     * 3. 最后使用 ip（完全匿名访客）
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 去重后的访客数量
     */
    protected function getVisitorCount($startTime, $endTime)
    {
        try {
            $table = $this->getFullTableName('shopro_miniprogram_track');
            $sql = "SELECT COUNT(DISTINCT CASE
                        WHEN user_id > 0 THEN CONCAT('u_', user_id)
                        WHEN session_id <> '' THEN CONCAT('s_', session_id)
                        ELSE CONCAT('i_', ip)
                    END) AS total
                    FROM `{$table}`
                    WHERE create_time BETWEEN ? AND ?";

            $result = Db::query($sql, [$startTime, $endTime]);
            
            $count = isset($result[0]['total']) ? (int)$result[0]['total'] : 0;

            return $count;
        } catch (\Exception $e) {
            $this->customLog('warning', '埋点访客统计失败：' . $e->getMessage());
            return 0;
        }
    }

    /**
     * 获取注册用户数
     * 
     * @param int $startTime 开始时间戳（0表示从最早开始）
     * @param int $endTime 结束时间戳
     * @return int 注册用户数量
     */
    protected function getRegisterCount($startTime, $endTime)
    {
        $query = Db::name('user');

        if ($startTime > 0) {
            $query->where('createtime', 'between', [$startTime, $endTime]);
        } else {
            // startTime=0 表示统计截至 endTime 的累计注册用户数
            $query->where('createtime', '<=', $endTime);
        }

        return (int)$query->count();
    }

    /**
     * 构建转化漏斗数据
     * 
     * 使用小程序行为埋点表获取各阶段数据，确保数据源一致性
     * 漏斗阶段：访问 → 浏览商品 → 加入购物车 → 提交订单 → 完成支付
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return array 漏斗数据数组
     */
    protected function buildFunnelData($startTime, $endTime)
    {
        try {
            // 获取各阶段的用户数
            $this->customLog('info', '转化漏斗数据-开始获取当前统计周期访问用户数');
            $visit = $this->getTrackVisitorCount($startTime, $endTime);
            $browse = $this->getTrackGoodViewUserCount($startTime, $endTime);
            $cart = $this->getTrackCartAddUserCount($startTime, $endTime);
            $submitOrder = $this->getTrackOrderConfirmUserCount($startTime, $endTime);
            $pay = $this->getTrackPayConfirmUserCount($startTime, $endTime);

            // 获取各阶段的事件数
            $this->customLog('info', '转化漏斗数据-开始获取当前统计周期访问事件数');
            $visitCount = $this->getTrackVisitCount($startTime, $endTime);
            $browseCount = $this->getTrackGoodViewCount($startTime, $endTime);
            $cartCount = $this->getTrackCartAddCount($startTime, $endTime);
            $submitCount = $this->getTrackOrderConfirmCount($startTime, $endTime);
            $payCount = $this->getTrackPayConfirmCount($startTime, $endTime);

            // 计算基准值（避免除以0）
            $base = max($visit, 1);

            // 组装漏斗数据
            return [
                ['key' => 'visit', 'label' => '访问', 'value' => $visit, 'rate' => 100, 'event_count' => $visitCount],
                ['key' => 'browse', 'label' => '浏览商品', 'value' => $browse, 'rate' => round($browse / $base * 100, 2), 'event_count' => $browseCount],
                ['key' => 'cart', 'label' => '加入购物车', 'value' => $cart, 'rate' => round($cart / $base * 100, 2), 'event_count' => $cartCount],
                ['key' => 'submit_order', 'label' => '提交订单', 'value' => $submitOrder, 'rate' => round($submitOrder / $base * 100, 2), 'event_count' => $submitCount],
                ['key' => 'pay', 'label' => '完成支付', 'value' => $pay, 'rate' => round($pay / $base * 100, 2), 'event_count' => $payCount],
            ];
        } catch (\Exception $e) {
            $this->customLog('warning', '使用埋点表构建转化漏斗失败' . $e->getMessage());
            return [];
        }
    }

    /**
     * 获取埋点表中访问用户数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 去重后的访问用户数
     */
    protected function getTrackVisitorCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(DISTINCT CASE
                    WHEN user_id > 0 THEN CONCAT('u_', user_id)
                    WHEN session_id <> '' THEN CONCAT('s_', session_id)
                    ELSE CONCAT('i_', ip)
                END) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND (event_code = 'index_view' OR page_path LIKE '/pages/index/%')";
        $result = Db::query($sql, [$startTime, $endTime]);
        $this->customLog('info', '当前统计周期访问用户数: '.json_encode($result[0]['total'] ?? 0));
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中访问事件数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 访问事件总数
     */
    protected function getTrackVisitCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(*) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND (event_code = 'index_view' OR page_path LIKE '/pages/index/%')";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中浏览商品用户数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 去重后的浏览商品用户数
     */
    protected function getTrackGoodViewUserCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(DISTINCT CASE
                    WHEN user_id > 0 THEN CONCAT('u_', user_id)
                    WHEN session_id <> '' THEN CONCAT('s_', session_id)
                    ELSE CONCAT('i_', ip)
                END) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND (event_code = 'good_view' )";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中浏览商品事件数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 浏览商品事件总数
     */
    protected function getTrackGoodViewCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(*) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND (event_code = 'good_view' OR page_path LIKE '/pages/goods/%')";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中加入购物车用户数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 去重后的加入购物车用户数
     */
    protected function getTrackCartAddUserCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(DISTINCT CASE
                    WHEN user_id > 0 THEN CONCAT('u_', user_id)
                    WHEN session_id <> '' THEN CONCAT('s_', session_id)
                    ELSE CONCAT('i_', ip)
                END) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND event_code = 'cart_add'";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中加入购物车事件数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 加入购物车事件总数
     */
    protected function getTrackCartAddCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(*) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND event_code = 'cart_add'";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中提交订单用户数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 去重后的提交订单用户数
     */
    protected function getTrackOrderConfirmUserCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(DISTINCT CASE
                    WHEN user_id > 0 THEN CONCAT('u_', user_id)
                    WHEN session_id <> '' THEN CONCAT('s_', session_id)
                    ELSE CONCAT('i_', ip)
                END) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND event_code = 'order_confirm'";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中提交订单事件数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 提交订单事件总数
     */
    protected function getTrackOrderConfirmCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(*) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND event_code = 'order_confirm'";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中完成支付用户数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 去重后的完成支付用户数
     */
    protected function getTrackPayConfirmUserCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(DISTINCT CASE
                    WHEN user_id > 0 THEN CONCAT('u_', user_id)
                    WHEN session_id <> '' THEN CONCAT('s_', session_id)
                    ELSE CONCAT('i_', ip)
                END) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND event_code = 'pay_confirm'";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 获取埋点表中完成支付事件数
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return int 完成支付事件总数
     */
    protected function getTrackPayConfirmCount($startTime, $endTime)
    {
        $table = $this->getFullTableName('shopro_miniprogram_track');
        $sql = "SELECT COUNT(*) as total
                FROM `{$table}`
                WHERE create_time BETWEEN ? AND ?
                    AND event_code = 'pay_confirm'";
        $result = Db::query($sql, [$startTime, $endTime]);
        return (int)($result[0]['total'] ?? 0);
    }

    /**
     * 构建成交额趋势数据
     * 
     * @param int $days 统计天数
     * @param int $snapshotTime 快照时间戳
     * @return array 包含日期标签和成交额数值的趋势数据
     */
    protected function buildAmountTrend($days, $snapshotTime)
    {
        return $this->buildDailySeries($days, $snapshotTime, function ($startTime, $endTime) {
            return round((float)Db::name('shopro_order')
                ->whereIn('status', ['paid', 'completed'])
                ->where('paid_time', 'between', [$startTime, $endTime])
                ->sum('pay_fee'), 2);
        }, '成交额');
    }

    /**
     * 构建订单数趋势数据
     * 
     * @param int $days 统计天数
     * @param int $snapshotTime 快照时间戳
     * @return array 包含日期标签和订单数数值的趋势数据
     */
    protected function buildOrderTrend($days, $snapshotTime)
    {
        return $this->buildDailySeries($days, $snapshotTime, function ($startTime, $endTime) {
            return (int)Db::name('shopro_order')
                ->whereIn('status', ['paid', 'completed'])
                ->where('paid_time', 'between', [$startTime, $endTime])
                ->count();
        }, '订单数');
    }

    /**
     * 构建注册用户增长趋势数据
     * 
     * @param int $days 统计天数
     * @param int $snapshotTime 快照时间戳
     * @return array 包含日期标签、每日新增用户数和累计用户数的趋势数据
     */
    protected function buildRegisterTrend($days, $snapshotTime)
    {
        $range = $this->buildRange($days, $snapshotTime);
        // 获取统计周期开始前的累计注册用户数
        $baseTotal = (int)Db::name('user')->where('createtime', '<', $range['start'])->count();

        $labels = [];
        $newUsers = [];
        $totalUsers = [];
        $runningTotal = $baseTotal;

        // 按天遍历统计周期
        for ($i = $days - 1; $i >= 0; $i--) {
            $dayStart = strtotime(date('Y-m-d 00:00:00', strtotime('-' . $i . ' days', $snapshotTime)));
            $dayEnd = min(strtotime(date('Y-m-d 23:59:59', $dayStart)), $snapshotTime);

            // 统计当天新增用户数
            $newCount = (int)Db::name('user')
                ->where('createtime', 'between', [$dayStart, $dayEnd])
                ->count();

            // 累计用户数
            $runningTotal += $newCount;
            $labels[] = date('m-d', $dayStart);
            $newUsers[] = $newCount;
            $totalUsers[] = $runningTotal;
        }

        return [
            'title' => '注册用户增长',
            'labels' => $labels,
            'new_users' => $newUsers,
            'total_users' => $totalUsers,
            'current_total' => end($totalUsers) ?: 0,
        ];
    }

    /**
     * 通用日趋势构建方法
     * 
     * @param int $days 统计天数
     * @param int $snapshotTime 快照时间戳
     * @param callable $callback 数据查询回调函数
     * @param string $title 趋势标题
     * @return array 趋势数据数组
     */
    protected function buildDailySeries($days, $snapshotTime, callable $callback, $title)
    {
        $labels = [];
        $values = [];

        // 按天遍历统计周期
        for ($i = $days - 1; $i >= 0; $i--) {
            $dayStart = strtotime(date('Y-m-d 00:00:00', strtotime('-' . $i . ' days', $snapshotTime)));
            $dayEnd = min(strtotime(date('Y-m-d 23:59:59', $dayStart)), $snapshotTime);

            $labels[] = date('m-d', $dayStart);
            $values[] = $callback($dayStart, $dayEnd);
        }

        return [
            'title' => $title,
            'labels' => $labels,
            'values' => $values,
        ];
    }

    /**
     * 构建客单价分布数据
     * 
     * 客单价区间：0-100、100-300、300-500、500-1000、1000+
     * 
     * @param int $startTime 开始时间戳
     * @param int $endTime 结束时间戳
     * @return array 客单价分布数据数组
     */
    protected function buildPriceDistribution($startTime, $endTime)
    {
        // 查询已支付和已完成订单的支付金额
        $orders = Db::name('shopro_order')
            ->whereIn('status', ['paid', 'completed'])
            ->where('paid_time', 'between', [$startTime, $endTime])
            ->field('pay_fee')
            ->select();

        // 初始化分布区间
        $distribution = [
            '0-100' => 0,
            '100-300' => 0,
            '300-500' => 0,
            '500-1000' => 0,
            '1000+' => 0,
        ];

        $total = count($orders);

        // 统计每个区间的订单数
        foreach ($orders as $order) {
            $amount = (float)$order['pay_fee'];

            if ($amount < 100) {
                $distribution['0-100']++;
            } elseif ($amount < 300) {
                $distribution['100-300']++;
            } elseif ($amount < 500) {
                $distribution['300-500']++;
            } elseif ($amount < 1000) {
                $distribution['500-1000']++;
            } else {
                $distribution['1000+']++;
            }
        }

        // 组装结果（包含区间标签、数量、占比）
        $result = [];
        foreach ($distribution as $label => $count) {
            $result[] = [
                'label' => $label,
                'count' => $count,
                'rate' => $total > 0 ? round($count / $total * 100, 2) : 0,
            ];
        }

        return $result;
    }

    /**
     * 获取实时订单动态
     * 
     * @param int $limit 返回订单数量限制
     * @return array 订单列表（包含订单号、用户信息、商品信息、金额、时间）
     */
    protected function getRecentOrders($limit = 10)
    {
        // 查询最近支付的订单
        $orders = Db::name('shopro_order')
            ->whereIn('status', ['paid', 'completed'])
            ->where('paid_time', '>', 0)
            ->order('paid_time desc')
            ->limit($limit)
            ->field('id,order_sn,user_id,pay_fee,paid_time')
            ->select();

        $result = [];
        foreach ($orders as $order) {
            // 获取用户信息
            $user = Db::name('user')->where('id', $order['user_id'])->field('nickname,mobile')->find();
            // 获取订单商品信息
            $item = Db::name('shopro_order_item')->where('order_id', $order['id'])->field('goods_title')->find();

            $result[] = [
                'order_id' => (int)$order['id'],
                'order_sn' => $order['order_sn'],
                'user' => $user && $user['nickname'] ? $user['nickname'] : ('用户#' . $order['user_id']),
                'mobile' => $user && $user['mobile'] ? $user['mobile'] : '',
                'goods_title' => $item ? $item['goods_title'] : '',
                'amount' => round((float)$order['pay_fee'], 2),
                'time' => date('Y-m-d H:i:s', (int)$order['paid_time']),
            ];
        }

        return $result;
    }

    /**
     * 生成卡片指标数据结构
     * 
     * @param string $label 指标标签
     * @param mixed $currentValue 当前周期值
     * @param mixed $previousValue 上一周期值
     * @param string $type 类型：count（计数）、money（金额）、percent（百分比）
     * @return array 卡片指标数据
     */
    protected function buildMetricCard($label, $currentValue, $previousValue, $type = 'count')
    {
        // 按类型规格化数值
        $currentValue = $this->normalizeByType($currentValue, $type);
        $previousValue = $this->normalizeByType($previousValue, $type);
        $compareValue = $this->normalizeByType($currentValue - $previousValue, $type);

        // 计算环比增长率（避免除以0）
        if ((float)$previousValue == 0.0) {
            $compareRate = (float)$currentValue > 0 ? 100.00 : 0.00;
        } else {
            $compareRate = round((($currentValue - $previousValue) / $previousValue) * 100, 2);
        }

        return [
            'label' => $label,
            'value' => $currentValue,
            'formatted' => $this->formatMetric($currentValue, $type),
            'previous_value' => $previousValue,
            'compare_value' => $compareValue,
            'compare_rate' => $compareRate,
            'compare_formatted' => $this->formatCompareRate($compareRate),
        ];
    }

    /**
     * 按类型规格化数值
     * 
     * @param mixed $value 原始值
     * @param string $type 类型：count（取整）、其他（保留2位小数）
     * @return float|int 规格化后的值
     */
    protected function normalizeByType($value, $type)
    {
        if ($type === 'count') {
            return (int)round($value);
        }

        return round((float)$value, 2);
    }

    /**
     * 指标格式化
     * 
     * @param mixed $value 原始值
     * @param string $type 类型：count、money、percent
     * @return string 格式化后的字符串
     */
    protected function formatMetric($value, $type)
    {
        if ($type === 'percent') {
            return round((float)$value, 2) . '%';
        }

        if ($type === 'money') {
            return $this->formatLargeNumber((float)$value, true);
        }

        return $this->formatLargeNumber((float)$value, false);
    }

    /**
     * 环比增长率格式化
     * 
     * @param float $rate 增长率
     * @return string 格式化后的增长率字符串（带正负号）
     */
    protected function formatCompareRate($rate)
    {
        $prefix = $rate > 0 ? '+' : '';
        return $prefix . round((float)$rate, 2) . '%';
    }

    /**
     * 大数格式化
     * 
     * 将大数值转换为更易读的格式：亿、万
     * 
     * @param float $value 原始数值
     * @param bool $isMoney 是否为金额（决定小数位数）
     * @return string 格式化后的字符串
     */
    protected function formatLargeNumber($value, $isMoney = false)
    {
        $precision = $isMoney ? 2 : 0;

        if (abs($value) >= 100000000) {
            return round($value / 100000000, 2) . '亿';
        }

        if (abs($value) >= 10000) {
            return round($value / 10000, 2) . '万';
        }

        return number_format($value, $precision, '.', '');
    }

    /**
     * 构建缓存键
     * 
     * @param string $timeRange 时间范围标识
     * @return string 缓存键名
     */
    protected function buildCacheKey($timeRange)
    {
        return 'shopro:data_stats_center:' . $timeRange;
    }

    /**
     * 获取完整表名（带表前缀）
     * 
     * @param string $table 表名
     * @return string 完整表名
     */
    protected function getFullTableName($table)
    {
        return config('database.prefix') . $table;
    }
}
?>