option('force') && !$this->isAutoCollectEnabled()) { $this->warn("⚠️ 自动收集日志功能已禁用"); $this->line("💡 如需强制执行,请使用 --force 选项"); return 0; } // 显示统计信息 if ($this->option('statistics')) { $manager = new UserLogCollectorManager(); $collectorsInfo = $manager->getCollectorsInfo(); $this->info("📊 收集器统计信息:"); $this->line(""); $this->line("收集器数量: " . count($collectorsInfo)); // 动态显示所有收集器 foreach ($collectorsInfo as $name => $info) { $icon = $this->getCollectorIcon($info['source_type']); $description = $this->getCollectorNameDescription($name); $this->line("- {$name}: {$icon} {$description}"); } $this->line(""); // 显示用户日志表统计 try { $totalUserLogs = \App\Module\Game\Models\UserLog::count(); $this->line("📋 用户日志表统计:"); $this->line(" 📝 总日志数: {$totalUserLogs}"); if ($totalUserLogs > 0) { $latestLog = \App\Module\Game\Models\UserLog::orderBy('created_at', 'desc')->first(); $oldestLog = \App\Module\Game\Models\UserLog::orderBy('created_at', 'asc')->first(); $this->line(" 🕐 最新日志时间: " . $latestLog->created_at); $this->line(" 🕐 最旧日志时间: " . $oldestLog->created_at); // 按类型统计 - 动态获取所有收集器类型 $this->line(" 📊 按类型统计:"); foreach ($collectorsInfo as $name => $info) { $sourceType = $info['source_type']; $count = \App\Module\Game\Models\UserLog::where('source_type', $sourceType)->count(); $icon = $this->getCollectorIcon($sourceType); $description = $this->getCollectorDescription($sourceType); $this->line(" {$icon} {$description}: {$count}"); } } } catch (\Exception $e) { $this->line(" ⚠️ 无法获取用户日志统计: " . $e->getMessage()); } $this->line(""); return 0; } $manager = new UserLogCollectorManager(); // 显示收集器信息 if ($this->option('info')) { $this->showCollectorsInfo($manager); return 0; } // 执行日志收集 return $this->executeCollection($manager); } /** * 执行日志收集 * * @param UserLogCollectorManager $manager * @return int */ private function executeCollection(UserLogCollectorManager $manager): int { $detail = $this->option('detail'); $limit = (int)$this->option('limit'); $force = $this->option('force'); try { if ($force) { $this->info("🚀 强制执行用户日志收集..."); $this->line("⚡ 忽略自动收集配置,强制执行收集任务"); } else { $this->info("🚀 开始按时间线收集用户日志..."); } if ($detail) { $this->showTimelineProgress(); } $result = $this->executeTimelineCollection($manager, $limit, $detail); $this->displayTimelineResult($result); return 0; } catch (\Exception $e) { $this->error("❌ 日志收集失败: {$e->getMessage()}"); return 1; } } /** * 从user_logs表获取最后处理的时间戳 * * @param string $sourceTable * @param string $sourceType * @return int */ private function getLastProcessedTimestampFromUserLogs(string $sourceTable, string $sourceType): int { try { $lastLog = \App\Module\Game\Models\UserLog::where('source_table', $sourceTable) ->where('source_type', $sourceType) ->orderBy('created_at', 'desc') ->first(); if (!$lastLog) { return 0; } // 返回user_logs记录的创建时间戳 return strtotime($lastLog->created_at); } catch (\Exception $e) { return 0; } } /** * 显示收集器信息 * * @param UserLogCollectorManager $manager * @return void */ private function showCollectorsInfo(UserLogCollectorManager $manager): void { $this->info("📋 注册的收集器信息:"); $this->line(""); $collectorsInfo = $manager->getCollectorsInfo(); foreach ($collectorsInfo as $info) { $this->line("收集器: {$info['name']}"); $this->line(" 类名: {$info['class']}"); $this->line(" 源表: {$info['source_table']}"); $this->line(" 类型: {$info['source_type']}"); $this->line(""); } } /** * 显示统计信息 * * @param UserLogCollectorManager $manager * @return void */ private function showStats(UserLogCollectorManager $manager): void { $collectorsInfo = $manager->getCollectorsInfo(); $this->line(""); $this->line("收集器数量: " . count($collectorsInfo)); // 动态显示所有收集器 foreach ($collectorsInfo as $name => $info) { $icon = $this->getCollectorIcon($info['source_type']); $description = $this->getCollectorNameDescription($name); $this->line("- {$name}: {$icon} {$description}"); } $this->line(""); } /** * 获取收集器图标 * * @param string $sourceType * @return string */ private function getCollectorIcon(string $sourceType): string { $icons = [ 'fund' => '💰', 'item' => '📦', 'farm' => '🌾', 'point' => '⭐', 'pet' => '🐾', 'system' => '⚙️', ]; return $icons[$sourceType] ?? '📄'; } /** * 获取收集器描述 * * @param string $sourceType * @return string */ private function getCollectorDescription(string $sourceType): string { $descriptions = [ 'fund' => '资金日志收集器', 'item' => '物品日志收集器', 'farm' => '农场日志收集器', 'point' => '积分日志收集器', 'pet' => '宠物日志收集器', 'system' => '系统日志收集器', ]; return $descriptions[$sourceType] ?? '未知类型收集器'; } /** * 获取收集器名称描述(基于收集器名称而非源类型) * * @param string $collectorName * @return string */ private function getCollectorNameDescription(string $collectorName): string { $descriptions = [ 'fund' => '资金日志收集器', 'item' => '物品日志收集器', 'farm_harvest' => '农场收获日志收集器', 'farm_upgrade' => '农场升级日志收集器', 'point' => '积分日志收集器', 'pet' => '宠物日志收集器', 'system' => '系统日志收集器', ]; return $descriptions[$collectorName] ?? '未知收集器'; } /** * 获取表的记录总数 * * @param string $tableName * @return int */ private function getTableRecordCount(string $tableName): int { try { // 根据表名使用对应的模型 switch ($tableName) { case 'fund_logs': return \App\Module\Fund\Models\FundLogModel::count(); case 'item_transaction_logs': return \App\Module\GameItems\Models\ItemTransactionLog::count(); case 'farm_harvest_logs': return \App\Module\Farm\Models\FarmHarvestLog::count(); case 'farm_upgrade_logs': return \App\Module\Farm\Models\FarmUpgradeLog::count(); case 'point_logs': return \App\Module\Point\Models\PointLogModel::count(); default: // 回退到直接查询 return \Illuminate\Support\Facades\DB::table($tableName)->count(); } } catch (\Exception $e) { return 0; } } /** * 显示进度条 * * @param float $percent * @return void */ private function displayProgressBar(float $percent): void { $barLength = 20; $filledLength = (int)round(($percent / 100) * $barLength); $emptyLength = $barLength - $filledLength; $bar = str_repeat('█', $filledLength) . str_repeat('░', $emptyLength); $this->line(" 📊 [{$bar}] {$percent}%"); } /** * 执行时间线收集 * 改为让每个收集器独立处理,避免全局时间戳的复杂性 * * @param UserLogCollectorManager $manager * @param int $limit * @param bool $detail * @return array */ private function executeTimelineCollection(UserLogCollectorManager $manager, int $limit, bool $detail): array { $startTime = microtime(true); if ($detail) { $this->line("📊 执行各收集器的日志收集..."); } // 直接执行所有收集器,传递限制参数 $results = $manager->collectAll($limit); if ($results['total_processed'] == 0) { return [ 'processed_count' => 0, 'execution_time' => round((microtime(true) - $startTime) * 1000, 2), 'status' => 'success', 'message' => '没有新记录需要处理', 'timestamp' => now()->toDateTimeString() ]; } if ($detail) { $this->line("📝 开始处理各收集器..."); foreach ($results['collectors'] as $collectorName => $result) { $this->line(" {$collectorName}: 处理了 {$result['processed_count']} 条记录"); } } $endTime = microtime(true); return [ 'processed_count' => $results['total_processed'], 'execution_time' => round(($endTime - $startTime) * 1000, 2), 'status' => 'success', 'timestamp' => now()->toDateTimeString(), 'details' => $results['collectors'] ]; } /** * 从所有收集器获取按时间线排序的记录 * * @param UserLogCollectorManager $manager * @param int $lastTimestamp * @param int $limit * @return array */ private function getAllRecordsByTimeline(UserLogCollectorManager $manager, int $lastTimestamp, int $limit): array { $allRecords = []; $collectorsInfo = $manager->getCollectorsInfo(); foreach ($collectorsInfo as $name => $info) { $collector = $manager->getCollector($name); if (!$collector) continue; try { $records = $collector->getNewRecordsByTimePublic($lastTimestamp); foreach ($records as $record) { $timestamp = $collector->getRecordTimestampPublic($record); $allRecords[] = [ 'id' => $record->id, 'timestamp' => $timestamp, 'collector' => $collector, 'record' => $record, 'source_type' => $info['source_type'] ]; } } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error("获取收集器记录失败", [ 'collector' => $name, 'error' => $e->getMessage() ]); } } // 限制记录数量 if (count($allRecords) > $limit) { // 先按时间排序,然后取前N条 usort($allRecords, function($a, $b) { $timeA = $a['timestamp']; $timeB = $b['timestamp']; if ($timeA == $timeB) { return $a['id'] <=> $b['id']; } return $timeA <=> $timeB; }); $allRecords = array_slice($allRecords, 0, $limit); } return $allRecords; } /** * 显示时间线进度 * * @return void */ private function showTimelineProgress(): void { $lastTimestamp = $this->getGlobalLastProcessedTimestamp(); $this->line("📈 时间线处理进度:"); $this->line(" 🕐 最后处理时间: " . ($lastTimestamp > 0 ? date('Y-m-d H:i:s', $lastTimestamp) : '未开始') . ""); $this->line(""); } /** * 显示时间线收集结果 * * @param array $result * @return void */ private function displayTimelineResult(array $result): void { $this->line(""); if ($result['status'] === 'success') { $this->info("✅ 时间线收集完成!"); $this->line("📊 处理统计:"); $this->line(" 📝 处理记录数: {$result['processed_count']}"); $this->line(" ⏱️ 执行时间: {$result['execution_time']}ms"); $this->line(" 🕐 完成时间: {$result['timestamp']}"); if (isset($result['last_timestamp'])) { $this->line(" 🎯 最新的处理时间: " . date('Y-m-d H:i:s', $result['last_timestamp']) . ""); } if ($result['processed_count'] > 0) { $avgTime = round($result['execution_time'] / $result['processed_count'], 2); $this->line(" 📈 平均处理时间: {$avgTime}ms/条"); } if (isset($result['message'])) { $this->line(" 💡 {$result['message']}"); } } else { $this->error("❌ 时间线收集失败!"); if (isset($result['error'])) { $this->line("🚨 错误信息:"); $this->line(" {$result['error']}"); } } $this->line(""); } /** * 获取全局最后处理时间戳 * 从各个收集器获取最后处理的原始记录时间戳,取最小值 * * @return int */ private function getGlobalLastProcessedTimestamp(): int { try { $manager = new \App\Module\Game\Logics\UserLogCollectorManager(); $collectorsInfo = $manager->getCollectorsInfo(); $minTimestamp = PHP_INT_MAX; $hasValidTimestamp = false; foreach ($collectorsInfo as $name => $info) { $collector = $manager->getCollector($name); if (!$collector) continue; // 获取该收集器最后处理的时间戳 $lastProcessedTimestamp = $this->getLastProcessedTimestampFromUserLogs($info['source_table'], $info['source_type']); if ($lastProcessedTimestamp > 0) { $minTimestamp = min($minTimestamp, $lastProcessedTimestamp); $hasValidTimestamp = true; } } return $hasValidTimestamp ? $minTimestamp : 0; } catch (\Exception $e) { return 0; } } /** * 更新全局最后处理时间戳 * 不再需要手动更新,因为进度通过user_logs表自动追踪 * * @param int $timestamp * @return void */ private function updateGlobalLastProcessedTimestamp(int $timestamp): void { // 不再需要手动更新,进度通过user_logs表自动追踪 // 这个方法保留是为了兼容性 } /** * 显示用户日志表统计 * * @return void */ private function showUserLogStats(): void { try { // 使用模型查询,避免表名前缀问题 $totalUserLogs = \App\Module\Game\Models\UserLog::count(); $todayUserLogs = \App\Module\Game\Models\UserLog::whereDate('created_at', now()->toDateString())->count(); $this->line("📋 用户日志表统计:"); $this->line(" 📝 总日志数: {$totalUserLogs}"); $this->line(" 📅 今日新增: {$todayUserLogs}"); // 按来源类型统计 $sourceStats = \App\Module\Game\Models\UserLog::select('source_type', \Illuminate\Support\Facades\DB::raw('count(*) as count')) ->groupBy('source_type') ->get(); if ($sourceStats->isNotEmpty()) { $this->line(" 📊 按来源类型统计:"); foreach ($sourceStats as $stat) { $this->line(" {$stat->source_type}: {$stat->count}"); } } } catch (\Exception $e) { $this->line(" ⚠️ 无法获取用户日志统计信息: " . $e->getMessage()); } } /** * 显示单个收集器结果 * * @param array $result * @return void */ private function displaySingleResult(array $result): void { $this->line(""); if ($result['status'] === 'success') { $this->info("✅ 收集完成!"); $this->line("📊 处理统计:"); $this->line(" 📝 处理记录数: {$result['processed_count']}"); $this->line(" ⏱️ 执行时间: {$result['execution_time']}ms"); $this->line(" 🕐 完成时间: {$result['timestamp']}"); if ($result['processed_count'] > 0) { $avgTime = round($result['execution_time'] / $result['processed_count'], 2); $this->line(" 📈 平均处理时间: {$avgTime}ms/条"); } } else { $this->error("❌ 收集失败!"); $this->line("🚨 错误信息:"); $this->line(" {$result['error']}"); } $this->line(""); } /** * 显示所有收集器结果 * * @param array $results * @return void */ private function displayAllResults(array $results): void { $this->line(""); $this->info("🎉 所有收集器执行完成!"); $this->line(""); // 显示总体统计 $this->line("📊 总体统计:"); $this->line(" 📝 总处理记录数: {$results['total_processed']}"); $this->line(" ⏱️ 总执行时间: {$results['total_execution_time']}ms"); $this->line(" 🕐 完成时间: {$results['timestamp']}"); if ($results['total_processed'] > 0) { $avgTime = round($results['total_execution_time'] / $results['total_processed'], 2); $this->line(" 📈 平均处理时间: {$avgTime}ms/条"); } $this->line(""); // 显示各收集器详情 $this->line("📋 各收集器详情:"); $successCount = 0; $failureCount = 0; foreach ($results['collectors'] as $name => $result) { if ($result['status'] === 'success') { $status = '✅ 成功'; $successCount++; } else { $status = '❌ 失败'; $failureCount++; } $this->line(" 🔧 {$name}: {$status}"); $this->line(" 📝 处理记录: {$result['processed_count']} 条"); $this->line(" ⏱️ 执行时间: {$result['execution_time']} ms"); if ($result['status'] === 'error') { $this->line(" 🚨 错误信息: {$result['error']}"); } elseif ($result['processed_count'] > 0) { $avgTime = round($result['execution_time'] / $result['processed_count'], 2); $this->line(" 📈 平均时间: {$avgTime} ms/条"); } $this->line(""); } // 显示执行摘要 $totalCollectors = $successCount + $failureCount; $this->line("📈 执行摘要:"); $this->line(" 🎯 成功收集器: {$successCount}/{$totalCollectors}"); if ($failureCount > 0) { $this->line(" ⚠️ 失败收集器: {$failureCount}/{$totalCollectors}"); } $this->line(""); } /** * 检查是否允许自动收集日志 * * @return bool */ private function isAutoCollectEnabled(): bool { return GameConfigService::isAutoCollectEnabled(); } }