23日0130-重构用户日志收集器架构消除重复MaxId获取逻辑.md 6.5 KB

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

时间: 2025年06月23日 01:30-01:32
状态: ✅ 已完成

问题描述

app/Module/Game/Commands/CollectUserLogsCommand.php中维护了一套获取MaxId的方法,这是重复的,应该使用收集器的方法。

问题分析

1. 重复逻辑问题

CollectUserLogsCommand中的getTableMaxId方法:

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中添加通用方法

/**
 * 获取源表的最大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. 各收集器重写方法提供高效实现

// FundLogCollector
public function getSourceTableMaxId(): int
{
    return FundLogModel::max('id') ?: 0;
}

// ItemLogCollector  
public function getSourceTableMaxId(): int
{
    return ItemTransactionLog::max('id') ?: 0;
}

3. 在UserLogCollectorManager中添加代理方法

/**
 * 获取收集器的源表最大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使用收集器方法

// 修改前
$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. 测试验证

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. 性能优化

各收集器可以独立优化:

  • 添加缓存机制
  • 使用更高效的查询
  • 实现批量操作

提交信息

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

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

总结

重构完成

  1. 消除了Command层的重复逻辑
  2. 提高了代码的可维护性和扩展性
  3. 遵循了SOLID设计原则
  4. 保持了向后兼容性
  5. 提升了性能和可测试性

关键改进

  • 单一职责:每个组件负责自己的功能
  • 开放封闭:易于扩展,无需修改现有代码
  • 依赖倒置:依赖抽象而非具体实现
  • 性能优化:使用模型查询替代通用DB查询

这次重构为用户日志收集系统奠定了更好的架构基础,为后续功能扩展提供了良好的支持。