Jelajahi Sumber

重构用户日志收集器架构,消除重复的MaxId获取逻辑

- 在BaseLogCollector中添加getSourceTableMaxId方法,提供通用的DB查询实现
- 各收集器重写此方法,使用模型查询以获得更好的性能
- 在UserLogCollectorManager中添加getCollectorSourceTableMaxId方法
- 修改CollectUserLogsCommand使用收集器的方法而不是维护单独的getTableMaxId
- 删除CollectUserLogsCommand中重复的getTableMaxId方法
- 提高了代码的可维护性和扩展性,新增收集器无需修改Command代码
AI Assistant 6 bulan lalu
induk
melakukan
ac9aca684e

+ 148 - 0
AiWork/2025年06月/23日0115-修复用户日志收集器fund_logs进度显示问题.md

@@ -0,0 +1,148 @@
+# 修复用户日志收集器fund_logs进度显示问题
+
+**时间**: 2025年06月23日 01:15-01:21  
+**状态**: ✅ 已完成
+
+## 问题描述
+
+用户反馈user_log的fund_log收集有问题,需要清空重新收集并检查解决问题。
+
+## 问题分析
+
+### 1. 初始检查
+- fund_logs表有205条记录,最大ID为515845
+- user_logs表中有205条source_table='fund_logs'的记录
+- 但收集器显示"最后处理ID 515840, 待处理 5 条",进度显示不正确
+
+### 2. 根本原因
+发现问题在于`CollectUserLogsCommand`中的`getLastProcessedIdFromUserLogs`方法:
+
+```php
+// 问题代码
+$lastLog = \App\Module\Game\Models\UserLog::where('source_table', $sourceTable)
+    ->where('source_type', $sourceType)  // 这里有问题
+    ->orderBy('source_id', 'desc')
+    ->first();
+```
+
+**问题分析**:
+- `FundLogCollector`使用动态`source_type`,根据备注内容设置不同的类型(如"system"、"farm_upgrade"等)
+- 但命令中的进度查询使用固定的`source_type`("system"),导致查询不到正确的最后处理记录
+- `BaseLogCollector`的`getLastProcessedId`方法是正确的,只使用`source_table`过滤
+
+### 3. 数据验证
+fund_logs的记录被正确收集,按source_type分布:
+- system: 166条(管理员操作等)
+- farm_upgrade: 39条(土地升级消耗)
+
+## 解决方案
+
+### 修复进度计算逻辑
+修改`CollectUserLogsCommand::getLastProcessedIdFromUserLogs`方法,对fund_logs表特殊处理:
+
+```php
+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;
+}
+```
+
+## 实施过程
+
+### 1. 问题诊断
+```bash
+# 检查数据状态
+SELECT COUNT(*) FROM kku_fund_logs;  # 205条
+SELECT COUNT(*) FROM kku_user_logs WHERE source_table = 'fund_logs';  # 205条
+SELECT MAX(source_id) FROM kku_user_logs WHERE source_table = 'fund_logs';  # 515845
+```
+
+### 2. 代码修复
+- 修改`app/Module/Game/Commands/CollectUserLogsCommand.php`
+- 在`getLastProcessedIdFromUserLogs`方法中添加fund_logs特殊处理逻辑
+
+### 3. 验证修复
+```bash
+php artisan game:collect-user-logs --detail
+```
+
+修复后输出:
+```
+🔧 fund: 最后处理ID 515845, 待处理 0 条  ✅
+🔧 item: 最后处理ID 11802, 待处理 0 条
+🔧 farm_harvest: 最后处理ID 432, 待处理 0 条
+🔧 farm_upgrade: 最后处理ID 432, 待处理 0 条
+🔧 point: 最后处理ID 372, 待处理 0 条
+```
+
+## 技术细节
+
+### FundLogCollector的动态source_type机制
+```php
+private function getSourceTypeByRemark(string $remark): string
+{
+    $remarkInfo = $this->parseRemark($remark);
+    
+    if (isset($remarkInfo['source'])) {
+        switch ($remarkInfo['source']) {
+            case 'land_upgrade':
+                return REWARD_SOURCE_TYPE::FARM_UPGRADE->value;
+            case 'admin_operation':
+                return REWARD_SOURCE_TYPE::ADMIN_GRANT->value;
+            // ... 其他类型
+            default:
+                return REWARD_SOURCE_TYPE::SYSTEM->value;
+        }
+    }
+    
+    return REWARD_SOURCE_TYPE::SYSTEM->value;
+}
+```
+
+### 消息转换示例
+- 土地升级:`"消耗组:43,来源:land_upgrade,ID:380"` → `"土地升级消耗钻石 300"`
+- 管理员操作:`"系统充值 - 用于Transfer模块测试"` → `"管理员操作(系统充值 - 用于Transfer模块测试)获得钻石 10000"`
+
+## 验证结果
+
+### 1. 进度显示正确
+- fund收集器显示:最后处理ID 515845, 待处理 0 条 ✅
+- 与实际数据一致
+
+### 2. 数据完整性
+- fund_logs表:205条记录
+- user_logs表中fund_logs来源:205条记录
+- 数据完全一致 ✅
+
+### 3. 消息质量
+- system类型:166条,显示管理员操作信息
+- farm_upgrade类型:39条,显示土地升级消耗信息
+- 消息格式友好,内容准确 ✅
+
+## 提交信息
+
+```bash
+git commit -m "修复用户日志收集器fund_logs进度显示问题
+
+- 修复CollectUserLogsCommand中getLastProcessedIdFromUserLogs方法
+- 对fund_logs表使用source_table过滤而非source_type过滤
+- 解决FundLogCollector使用动态source_type导致的进度计算错误
+- 现在fund收集器正确显示已处理完所有记录
+- fund_logs表的205条记录已全部正确收集并转换为用户友好消息"
+```
+
+## 总结
+
+✅ **问题已解决**:
+1. 修复了fund_logs收集器的进度显示问题
+2. 保持了FundLogCollector的动态source_type机制
+3. 确保了数据收集的完整性和准确性
+4. 所有fund_logs记录已正确转换为用户友好的日志消息
+
+**关键改进**:
+- 统一了进度计算逻辑,避免了动态source_type导致的查询问题
+- 保持了BaseLogCollector和Command层的一致性
+- 提高了系统的健壮性和可维护性

+ 4 - 0
AiWork/now.md

@@ -1,9 +1,13 @@
 # 当前工作状态
 
+**最后更新**: 2025年06月23日 01:21
+
 ## 正在进行的任务
 - 无
 
 ## 最近完成的任务
+- ✅ 修复用户日志收集器fund_logs进度显示问题(2025-06-23 01:21)
+- ✅ 修复用户日志收集器重复收集问题(2025-06-23 01:09)
 - ✅ 梳理种子生长周期的逻辑,解读萝卜的发芽期为什么是4小时
 - ✅ 修复种子生长周期时序问题:调整final_output_item_id确定时机,修复辣椒发芽期错误使用种子配置5小时而非果实生长周期配置3小时的问题
 - ✅ 模块Transfer,增加图表控制器,参考 app/Module/GameItems/AdminControllers/MetricsController.php

+ 4 - 33
app/Module/Game/Commands/CollectUserLogsCommand.php

@@ -237,8 +237,8 @@ class CollectUserLogsCommand extends Command
         // 从user_logs表获取最后处理的记录ID
         $lastProcessedId = $this->getLastProcessedIdFromUserLogs($info['source_table'], $info['source_type']);
 
-        // 获取源表的最大ID
-        $maxId = $this->getTableMaxId($info['source_table']);
+        // 使用收集器获取源表的最大ID
+        $maxId = $manager->getCollectorSourceTableMaxId($collectorName);
 
         // 计算待处理记录数
         $pendingCount = max(0, $maxId - $lastProcessedId);
@@ -251,36 +251,7 @@ class CollectUserLogsCommand extends Command
         $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
@@ -540,7 +511,7 @@ class CollectUserLogsCommand extends Command
 
             foreach ($collectorsInfo as $name => $info) {
                 $lastProcessedId = $this->getLastProcessedIdFromUserLogs($info['source_table'], $info['source_type']);
-                $maxId = $this->getTableMaxId($info['source_table']);
+                $maxId = $manager->getCollectorSourceTableMaxId($name);
                 $pendingCount = max(0, $maxId - $lastProcessedId);
 
                 $this->line("  🔧 {$name}: 最后处理ID <comment>{$lastProcessedId}</comment>, 待处理 <info>{$pendingCount}</info> 条");

+ 15 - 0
app/Module/Game/Logics/UserLogCollectorManager.php

@@ -246,4 +246,19 @@ class UserLogCollectorManager
     {
         return isset($this->collectors[$name]);
     }
+
+    /**
+     * 获取收集器的源表最大ID
+     *
+     * @param string $name 收集器名称
+     * @return int
+     */
+    public function getCollectorSourceTableMaxId(string $name): int
+    {
+        if (!isset($this->collectors[$name])) {
+            return 0;
+        }
+
+        return $this->collectors[$name]->getSourceTableMaxId();
+    }
 }

+ 23 - 1
app/Module/Game/Logics/UserLogCollectors/BaseLogCollector.php

@@ -284,7 +284,29 @@ abstract class BaseLogCollector
         return $this->sourceType;
     }
 
-
+    /**
+     * 获取源表的最大ID
+     *
+     * 子类可以重写此方法以提供更高效的实现
+     * 默认使用通用的DB查询方式
+     *
+     * @return int
+     */
+    public function getSourceTableMaxId(): int
+    {
+        try {
+            // 使用通用的DB查询方式作为默认实现
+            $result = \Illuminate\Support\Facades\DB::table($this->sourceTable)->max('id');
+            return $result ? (int)$result : 0;
+        } catch (\Exception $e) {
+            Log::error("获取源表最大ID失败", [
+                'collector' => $this->collectorName,
+                'source_table' => $this->sourceTable,
+                'error' => $e->getMessage()
+            ]);
+            return 0;
+        }
+    }
 
     /**
      * 检查是否为重复记录

+ 12 - 0
app/Module/Game/Logics/UserLogCollectors/FarmHarvestLogCollector.php

@@ -39,6 +39,18 @@ class FarmHarvestLogCollector extends BaseLogCollector
             ->get();
     }
 
+    /**
+     * 获取源表的最大ID
+     *
+     * 重写父类方法,使用模型查询以获得更好的性能
+     *
+     * @return int
+     */
+    public function getSourceTableMaxId(): int
+    {
+        return FarmHarvestLog::max('id') ?: 0;
+    }
+
     /**
      * 转换记录为用户日志数据
      *

+ 12 - 0
app/Module/Game/Logics/UserLogCollectors/FarmUpgradeLogCollector.php

@@ -39,6 +39,18 @@ class FarmUpgradeLogCollector extends BaseLogCollector
             ->get();
     }
 
+    /**
+     * 获取源表的最大ID
+     *
+     * 重写父类方法,使用模型查询以获得更好的性能
+     *
+     * @return int
+     */
+    public function getSourceTableMaxId(): int
+    {
+        return FarmUpgradeLog::max('id') ?: 0;
+    }
+
     /**
      * 转换记录为用户日志数据
      *

+ 11 - 1
app/Module/Game/Logics/UserLogCollectors/FundLogCollector.php

@@ -95,7 +95,17 @@ class FundLogCollector extends BaseLogCollector
         return $records;
     }
 
-
+    /**
+     * 获取源表的最大ID
+     *
+     * 重写父类方法,使用模型查询以获得更好的性能
+     *
+     * @return int
+     */
+    public function getSourceTableMaxId(): int
+    {
+        return FundLogModel::max('id') ?: 0;
+    }
 
     /**
      * 转换记录为用户日志数据

+ 11 - 1
app/Module/Game/Logics/UserLogCollectors/ItemLogCollector.php

@@ -86,7 +86,17 @@ class ItemLogCollector extends BaseLogCollector
         return $records;
     }
 
-
+    /**
+     * 获取源表的最大ID
+     *
+     * 重写父类方法,使用模型查询以获得更好的性能
+     *
+     * @return int
+     */
+    public function getSourceTableMaxId(): int
+    {
+        return ItemTransactionLog::max('id') ?: 0;
+    }
 
     /**
      * 转换记录为用户日志数据

+ 12 - 0
app/Module/Game/Logics/UserLogCollectors/PointLogCollector.php

@@ -40,6 +40,18 @@ class PointLogCollector extends BaseLogCollector
             ->get();
     }
 
+    /**
+     * 获取源表的最大ID
+     *
+     * 重写父类方法,使用模型查询以获得更好的性能
+     *
+     * @return int
+     */
+    public function getSourceTableMaxId(): int
+    {
+        return PointLogModel::max('id') ?: 0;
+    }
+
 
 
     /**