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');
try {
$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];
$this->line("📝 收集器详情:");
$this->line(" 名称: {$info['name']}");
$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表获取最后处理的记录
$lastProcessedId = $this->getLastProcessedIdFromUserLogs($info['source_table'], $info['source_type']);
$lastProcessedTimestamp = $this->getLastProcessedTimestampFromUserLogs($info['source_table'], $info['source_type']);
// 获取源表的最大ID
$maxId = $this->getTableMaxId($info['source_table']);
// 计算待处理记录数
$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("");
}
/**
* 获取表的最大ID
*
* @param string $tableName
* @return int
*/
private function getTableMaxId(string $tableName): int
{
try {
// 根据表名使用对应的模型
switch ($tableName) {
case 'fund_logs':
return \App\Module\Fund\Models\FundLogModel::max('id') ?: 0;
case 'item_transaction_logs':
return \App\Module\GameItems\Models\ItemTransactionLog::max('id') ?: 0;
case 'farm_harvest_logs':
return \App\Module\Farm\Models\FarmHarvestLog::max('id') ?: 0;
case 'farm_upgrade_logs':
return \App\Module\Farm\Models\FarmUpgradeLog::max('id') ?: 0;
case 'point_logs':
return \App\Module\Point\Models\PointLogModel::max('id') ?: 0;
default:
// 回退到直接查询
$result = \Illuminate\Support\Facades\DB::table($tableName)->max('id');
return $result ? (int)$result : 0;
}
} catch (\Exception $e) {
return 0;
}
}
/**
* 从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();
} 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;
}
}
/**
* 从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("");
}
}