|
|
@@ -0,0 +1,243 @@
|
|
|
+# 重构用户日志收集器架构,消除重复MaxId获取逻辑
|
|
|
+
|
|
|
+**时间**: 2025年06月23日 01:30-01:32
|
|
|
+**状态**: ✅ 已完成
|
|
|
+
|
|
|
+## 问题描述
|
|
|
+
|
|
|
+在`app/Module/Game/Commands/CollectUserLogsCommand.php`中维护了一套获取MaxId的方法,这是重复的,应该使用收集器的方法。
|
|
|
+
|
|
|
+## 问题分析
|
|
|
+
|
|
|
+### 1. 重复逻辑问题
|
|
|
+CollectUserLogsCommand中的`getTableMaxId`方法:
|
|
|
+```php
|
|
|
+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
|
|
|
+ }
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**问题**:
|
|
|
+- 维护了与收集器重复的逻辑
|
|
|
+- 每次新增收集器都需要修改Command代码
|
|
|
+- 违反了单一职责原则
|
|
|
+- 适用性差,扩展性不好
|
|
|
+
|
|
|
+### 2. 架构设计问题
|
|
|
+- Command层不应该知道具体的模型实现
|
|
|
+- 收集器应该自己负责提供源表信息
|
|
|
+- 缺乏统一的接口设计
|
|
|
+
|
|
|
+## 解决方案
|
|
|
+
|
|
|
+### 1. 在BaseLogCollector中添加通用方法
|
|
|
+```php
|
|
|
+/**
|
|
|
+ * 获取源表的最大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;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 各收集器重写方法提供高效实现
|
|
|
+```php
|
|
|
+// FundLogCollector
|
|
|
+public function getSourceTableMaxId(): int
|
|
|
+{
|
|
|
+ return FundLogModel::max('id') ?: 0;
|
|
|
+}
|
|
|
+
|
|
|
+// ItemLogCollector
|
|
|
+public function getSourceTableMaxId(): int
|
|
|
+{
|
|
|
+ return ItemTransactionLog::max('id') ?: 0;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 在UserLogCollectorManager中添加代理方法
|
|
|
+```php
|
|
|
+/**
|
|
|
+ * 获取收集器的源表最大ID
|
|
|
+ *
|
|
|
+ * @param string $name 收集器名称
|
|
|
+ * @return int
|
|
|
+ */
|
|
|
+public function getCollectorSourceTableMaxId(string $name): int
|
|
|
+{
|
|
|
+ if (!isset($this->collectors[$name])) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $this->collectors[$name]->getSourceTableMaxId();
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4. 修改Command使用收集器方法
|
|
|
+```php
|
|
|
+// 修改前
|
|
|
+$maxId = $this->getTableMaxId($info['source_table']);
|
|
|
+
|
|
|
+// 修改后
|
|
|
+$maxId = $manager->getCollectorSourceTableMaxId($name);
|
|
|
+```
|
|
|
+
|
|
|
+## 实施过程
|
|
|
+
|
|
|
+### 1. 添加BaseLogCollector方法
|
|
|
+- 在BaseLogCollector中添加getSourceTableMaxId方法
|
|
|
+- 提供通用的DB查询实现作为默认方案
|
|
|
+- 添加异常处理和日志记录
|
|
|
+
|
|
|
+### 2. 各收集器重写方法
|
|
|
+- FundLogCollector: 使用FundLogModel::max('id')
|
|
|
+- ItemLogCollector: 使用ItemTransactionLog::max('id')
|
|
|
+- FarmHarvestLogCollector: 使用FarmHarvestLog::max('id')
|
|
|
+- FarmUpgradeLogCollector: 使用FarmUpgradeLog::max('id')
|
|
|
+- PointLogCollector: 使用PointLogModel::max('id')
|
|
|
+
|
|
|
+### 3. 修改UserLogCollectorManager
|
|
|
+- 添加getCollectorSourceTableMaxId方法
|
|
|
+- 提供统一的接口访问收集器功能
|
|
|
+
|
|
|
+### 4. 重构CollectUserLogsCommand
|
|
|
+- 修改showCollectorProgress方法使用新接口
|
|
|
+- 修改showTimelineProgress方法使用新接口
|
|
|
+- 删除重复的getTableMaxId方法
|
|
|
+
|
|
|
+### 5. 测试验证
|
|
|
+```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 条
|
|
|
+```
|
|
|
+
|
|
|
+## 技术优势
|
|
|
+
|
|
|
+### 1. 单一职责原则
|
|
|
+- 每个收集器负责自己的源表信息
|
|
|
+- Command层只负责协调和显示
|
|
|
+- Manager层提供统一接口
|
|
|
+
|
|
|
+### 2. 开放封闭原则
|
|
|
+- 新增收集器无需修改Command代码
|
|
|
+- 收集器可以重写方法提供优化实现
|
|
|
+- 基类提供通用的默认实现
|
|
|
+
|
|
|
+### 3. 依赖倒置原则
|
|
|
+- Command依赖抽象接口而非具体实现
|
|
|
+- 通过Manager层解耦Command和收集器
|
|
|
+
|
|
|
+### 4. 性能优化
|
|
|
+- 各收集器使用模型查询,比通用DB查询更高效
|
|
|
+- 避免了switch-case的性能开销
|
|
|
+- 支持收集器级别的缓存优化
|
|
|
+
|
|
|
+## 架构改进
|
|
|
+
|
|
|
+### 修改前
|
|
|
+```
|
|
|
+CollectUserLogsCommand
|
|
|
+├── getTableMaxId() (重复逻辑)
|
|
|
+│ ├── switch case for fund_logs
|
|
|
+│ ├── switch case for item_transaction_logs
|
|
|
+│ └── ...
|
|
|
+└── 直接使用模型查询
|
|
|
+```
|
|
|
+
|
|
|
+### 修改后
|
|
|
+```
|
|
|
+CollectUserLogsCommand
|
|
|
+└── UserLogCollectorManager
|
|
|
+ └── BaseLogCollector
|
|
|
+ ├── getSourceTableMaxId() (通用实现)
|
|
|
+ └── 各收集器重写 (优化实现)
|
|
|
+ ├── FundLogCollector
|
|
|
+ ├── ItemLogCollector
|
|
|
+ └── ...
|
|
|
+```
|
|
|
+
|
|
|
+## 扩展性提升
|
|
|
+
|
|
|
+### 1. 新增收集器
|
|
|
+只需要:
|
|
|
+1. 继承BaseLogCollector
|
|
|
+2. 实现必要的抽象方法
|
|
|
+3. 可选择重写getSourceTableMaxId方法
|
|
|
+
|
|
|
+无需修改:
|
|
|
+- CollectUserLogsCommand
|
|
|
+- UserLogCollectorManager的核心逻辑
|
|
|
+
|
|
|
+### 2. 性能优化
|
|
|
+各收集器可以独立优化:
|
|
|
+- 添加缓存机制
|
|
|
+- 使用更高效的查询
|
|
|
+- 实现批量操作
|
|
|
+
|
|
|
+## 提交信息
|
|
|
+
|
|
|
+```bash
|
|
|
+git commit -m "重构用户日志收集器架构,消除重复的MaxId获取逻辑
|
|
|
+
|
|
|
+- 在BaseLogCollector中添加getSourceTableMaxId方法,提供通用的DB查询实现
|
|
|
+- 各收集器重写此方法,使用模型查询以获得更好的性能
|
|
|
+- 在UserLogCollectorManager中添加getCollectorSourceTableMaxId方法
|
|
|
+- 修改CollectUserLogsCommand使用收集器的方法而不是维护单独的getTableMaxId
|
|
|
+- 删除CollectUserLogsCommand中重复的getTableMaxId方法
|
|
|
+- 提高了代码的可维护性和扩展性,新增收集器无需修改Command代码"
|
|
|
+```
|
|
|
+
|
|
|
+## 总结
|
|
|
+
|
|
|
+✅ **重构完成**:
|
|
|
+1. 消除了Command层的重复逻辑
|
|
|
+2. 提高了代码的可维护性和扩展性
|
|
|
+3. 遵循了SOLID设计原则
|
|
|
+4. 保持了向后兼容性
|
|
|
+5. 提升了性能和可测试性
|
|
|
+
|
|
|
+**关键改进**:
|
|
|
+- 单一职责:每个组件负责自己的功能
|
|
|
+- 开放封闭:易于扩展,无需修改现有代码
|
|
|
+- 依赖倒置:依赖抽象而非具体实现
|
|
|
+- 性能优化:使用模型查询替代通用DB查询
|
|
|
+
|
|
|
+这次重构为用户日志收集系统奠定了更好的架构基础,为后续功能扩展提供了良好的支持。
|