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();
}
}