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; } } /** * 执行单个收集器并显示进度 * * @param UserLogCollectorManager $manager * @param string $collectorName * @param bool $detail * @param bool $showProgress * @return array */ private function executeCollectorWithProgress(UserLogCollectorManager $manager, string $collectorName, bool $detail, bool $showProgress): array { if ($detail) { $this->line("📊 检查收集器状态..."); $this->showCollectorProgress($manager, $collectorName); } $startTime = microtime(true); $result = $manager->collectByName($collectorName); $endTime = microtime(true); if ($detail) { $this->line("⏱️ 执行时间: " . round(($endTime - $startTime) * 1000, 2) . "ms"); } return $result; } /** * 执行所有收集器并显示进度 * * @param UserLogCollectorManager $manager * @param bool $detail * @param bool $showProgress * @return array */ private function executeAllCollectorsWithProgress(UserLogCollectorManager $manager, bool $detail, bool $showProgress): array { $collectorsInfo = $manager->getCollectorsInfo(); if ($detail) { $this->line("📋 收集器总数: " . count($collectorsInfo)); $this->line(""); } $results = $manager->collectAll(); return $results; } /** * 显示收集器详细信息 * * @param UserLogCollectorManager $manager * @param string $collectorName * @return void */ private function showCollectorDetails(UserLogCollectorManager $manager, string $collectorName): void { $collectorsInfo = $manager->getCollectorsInfo(); if (!isset($collectorsInfo[$collectorName])) { $this->error("收集器 {$collectorName} 不存在"); return; } $info = $collectorsInfo[$collectorName]; $enabledStatus = $info['enabled'] ? '✅ 启用' : '❌ 禁用'; $this->line("📝 收集器详情:"); $this->line(" 名称: {$info['name']}"); $this->line(" 状态: {$enabledStatus}"); $this->line(" 类名: {$info['class']}"); $this->line(" 源表: {$info['source_table']}"); $this->line(" 类型: {$info['source_type']}"); $this->line(""); } /** * 显示收集器进度信息 * * @param UserLogCollectorManager $manager * @param string $collectorName * @return void */ private function showCollectorProgress(UserLogCollectorManager $manager, string $collectorName): void { $collectorsInfo = $manager->getCollectorsInfo(); $info = $collectorsInfo[$collectorName]; // 从user_logs表获取最后处理的记录ID $lastProcessedId = $this->getLastProcessedIdFromUserLogs($info['source_table'], $info['source_type']); // 使用收集器获取源表的最大ID $maxId = $manager->getCollectorSourceTableMaxId($collectorName); // 计算待处理记录数 $pendingCount = max(0, $maxId - $lastProcessedId); $this->line("📈 处理进度:"); $this->line(" 最后处理ID: {$lastProcessedId}"); $this->line(" 最后处理时间: " . ($lastProcessedTimestamp > 0 ? date('Y-m-d H:i:s', $lastProcessedTimestamp) : '未开始') . ""); $this->line(" 源表最大ID: {$maxId}"); $this->line(" 待处理记录: {$pendingCount}"); $this->line(""); } /** * 从user_logs表获取最后处理的ID * * @param string $sourceTable * @param string $sourceType * @return int */ private function getLastProcessedIdFromUserLogs(string $sourceTable, string $sourceType): int { try { // 对于农场收集器,需要查询多个表 if ($sourceType === 'farm') { $lastLog = \App\Module\Game\Models\UserLog::where('source_type', $sourceType) ->whereIn('source_table', ['farm_harvest_logs', 'farm_upgrade_logs']) ->orderBy('source_id', 'desc') ->first(); } elseif ($sourceTable === 'fund_logs') { // 对于fund_logs表,使用动态source_type,只按source_table查询 // 这与BaseLogCollector的getLastProcessedId方法保持一致 $maxId = \App\Module\Game\Models\UserLog::where('source_table', $sourceTable) ->max('source_id'); return $maxId ?: 0; } else { $lastLog = \App\Module\Game\Models\UserLog::where('source_table', $sourceTable) ->where('source_type', $sourceType) ->orderBy('source_id', 'desc') ->first(); } return $lastLog ? $lastLog->source_id : 0; } catch (\Exception $e) { return 0; } } // 注意:getLastProcessedTimestampFromUserLogs方法已移除 // 当前使用基于ID的进度追踪,不再需要时间戳追踪 /** * 显示收集器信息 * * @param UserLogCollectorManager $manager * @return void */ private function showCollectorsInfo(UserLogCollectorManager $manager): void { $this->info("📋 注册的收集器信息:"); $this->line(""); $collectorsInfo = $manager->getCollectorsInfo(); foreach ($collectorsInfo as $info) { $enabledStatus = $info['enabled'] ? '✅ 启用' : '❌ 禁用'; $this->line("收集器: {$info['name']} ({$enabledStatus})"); $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'] ]; } // 注意:getAllRecordsByTimeline方法已移除 // 当前使用基于ID的进度追踪,每个收集器独立处理,不再需要全局时间线排序 /** * 显示收集器进度信息 * 基于ID的进度追踪,显示各收集器的处理状态 * * @return void */ private function showTimelineProgress(): void { $this->line("📈 收集器进度状态:"); try { $manager = new \App\Module\Game\Logics\UserLogCollectorManager(); $collectorsInfo = $manager->getCollectorsInfo(); foreach ($collectorsInfo as $name => $info) { $lastProcessedId = $this->getLastProcessedIdFromUserLogs($info['source_table'], $info['source_type']); $maxId = $manager->getCollectorSourceTableMaxId($name); $pendingCount = max(0, $maxId - $lastProcessedId); $this->line(" 🔧 {$name}: 最后处理ID {$lastProcessedId}, 待处理 {$pendingCount} 条"); } } catch (\Exception $e) { $this->line(" ⚠️ 无法获取进度信息: " . $e->getMessage()); } $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(""); } // 注意:以下时间戳相关方法已移除,因为当前使用基于ID的进度追踪: // - getGlobalLastProcessedTimestamp(): 全局时间戳追踪已废弃 // - updateGlobalLastProcessedTimestamp(): 手动更新时间戳已废弃 // // 当前进度追踪机制: // 1. 每个收集器通过getLastProcessedId()获取最后处理的记录ID // 2. 进度通过user_logs表中的source_id字段自动维护 // 3. 无需手动重置或更新进度 /** * 显示用户日志表统计 * * @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/条"); } } elseif ($result['status'] === 'disabled') { $this->comment("⏸️ 收集器已禁用!"); $this->line("💡 提示:"); $this->line(" 收集器已通过游戏配置禁用,如需启用请修改相关配置项"); } 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++; } elseif ($result['status'] === 'disabled') { $status = '⏸️ 禁用'; } 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['status'] === 'disabled') { $this->line(" 💡 收集器已禁用,可通过游戏配置启用"); } 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(); } }