事件系统.md 11 KB

任务模块事件系统

1. 概述

任务模块的事件系统是基于Laravel的事件机制实现的,用于处理任务完成、奖励领取等关键业务流程。通过事件系统,可以实现模块间的解耦,提高代码的可维护性和可扩展性。

本文档详细介绍了任务模块的事件系统设计与实现,包括事件类型、事件监听器、事件分发机制以及如何扩展事件系统。

2. 事件类型

任务模块定义了以下核心事件:

2.1 TaskCompletedEvent

当用户完成任务时触发此事件。

事件属性:

/**
 * 用户ID
 * @var int
 */
public int $userId;

/**
 * 任务ID
 * @var int
 */
public int $taskId;

/**
 * 任务名称
 * @var string
 */
public string $taskName;

/**
 * 任务类型
 * @var string
 */
public string $taskType;

/**
 * 完成时间
 * @var string
 */
public string $completedAt;

/**
 * 任务奖励内容
 * @var array
 */
public array $rewards;

触发时机:

  • 用户完成任务的所有条件
  • 系统自动标记任务为已完成
  • 管理员手动标记任务为已完成

2.2 TaskRewardClaimedEvent

当用户领取任务奖励时触发此事件。

事件属性:

/**
 * 用户ID
 * @var int
 */
public int $userId;

/**
 * 任务ID
 * @var int
 */
public int $taskId;

/**
 * 任务名称
 * @var string
 */
public string $taskName;

/**
 * 实际发放的奖励
 * @var array
 */
public array $rewards;

/**
 * 领取时间
 * @var string
 */
public string $claimedAt;

/**
 * 奖励是否成功发放
 * @var bool
 */
public bool $isSuccess;

触发时机:

  • 用户手动领取任务奖励
  • 系统自动发放任务奖励
  • 管理员手动发放任务奖励

3. 事件监听器

任务模块实现了以下事件监听器:

3.1 TaskCompletedListener

监听任务完成事件,执行相关操作。

主要功能:

  • 记录任务完成日志
  • 更新用户任务统计数据
  • 发送任务完成通知
  • 检查成就解锁

实现示例:

/**
 * 处理任务完成事件
 *
 * @param TaskCompletedEvent $event
 * @return void
 */
public function handle($event): void
{
    // 记录日志
    $this->logEvent("用户 {$event->userId} 完成了任务 {$event->taskName}", [
        'user_id' => $event->userId,
        'task_id' => $event->taskId,
        'task_type' => $event->taskType,
        'completed_at' => $event->completedAt,
        'rewards' => $event->rewards
    ]);
    
    // 更新用户统计数据
    // $this->updateUserTaskStats($event->userId, $event->taskType);
    
    // 发送任务完成通知
    $this->sendTaskCompletionNotification($event);
    
    // 检查成就解锁
    $this->checkAchievementUnlock($event);
}

3.2 TaskRewardClaimedListener

监听任务奖励领取事件,执行相关操作。

主要功能:

  • 记录奖励领取日志
  • 更新用户奖励统计数据
  • 发送奖励领取通知
  • 检查特殊奖励触发
  • 处理奖励发放失败情况

实现示例:

/**
 * 处理任务奖励领取事件
 *
 * @param TaskRewardClaimedEvent $event
 * @return void
 */
public function handle($event): void
{
    // 记录日志
    $this->logEvent("用户 {$event->userId} 领取了任务 {$event->taskName} 的奖励", [
        'user_id' => $event->userId,
        'task_id' => $event->taskId,
        'rewards' => $event->rewards,
        'claimed_at' => $event->claimedAt,
        'is_success' => $event->isSuccess
    ]);
    
    // 如果奖励发放成功
    if ($event->isSuccess) {
        // 更新用户统计数据
        // $this->updateUserRewardStats($event->userId, $event->rewards);
        
        // 发送奖励领取通知
        $this->sendRewardClaimedNotification($event);
        
        // 检查特殊奖励触发
        $this->checkSpecialRewardTrigger($event);
    } else {
        // 处理奖励发放失败的情况
        $this->handleRewardFailure($event);
    }
}

4. 事件注册与分发

任务模块通过TaskServiceProvider注册事件和监听器:

/**
 * 应用程序的事件监听器映射
 *
 * @var array
 */
protected $listen = [
    TaskCompletedEvent::class => [
        TaskCompletedListener::class,
    ],
    TaskRewardClaimedEvent::class => [
        TaskRewardClaimedListener::class,
    ],
];

/**
 * 引导服务
 *
 * @return void
 */
public function boot(): void
{
    // 注册事件监听器
    foreach ($this->listen as $event => $listeners) {
        foreach ($listeners as $listener) {
            $this->app['events']->listen($event, $listener);
        }
    }
}

5. 事件分发示例

5.1 分发任务完成事件

use App\Module\Task\Events\TaskCompletedEvent;
use Illuminate\Support\Facades\Event;

// 在任务服务中分发任务完成事件
public function completeTask($userId, $taskId)
{
    // 获取任务信息
    $task = $this->taskRepository->find($taskId);
    
    // 更新任务状态
    $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
    $userTask->status = 2; // 已完成
    $userTask->completed_at = now();
    $userTask->save();
    
    // 获取任务奖励
    $rewards = $this->rewardRepository->getRewardsByTaskId($taskId);
    
    // 分发任务完成事件
    Event::dispatch(new TaskCompletedEvent(
        $userId,
        $taskId,
        $task->name,
        $task->type,
        now()->toDateTimeString(),
        $rewards
    ));
    
    return true;
}

5.2 分发任务奖励领取事件

use App\Module\Task\Events\TaskRewardClaimedEvent;
use Illuminate\Support\Facades\Event;

// 在奖励服务中分发任务奖励领取事件
public function claimReward($userId, $taskId)
{
    // 获取任务信息
    $task = $this->taskRepository->find($taskId);
    
    // 获取任务奖励
    $rewards = $this->rewardRepository->getRewardsByTaskId($taskId);
    
    // 发放奖励
    $isSuccess = $this->dispatchRewards($userId, $rewards);
    
    // 更新任务状态
    if ($isSuccess) {
        $userTask = $this->userTaskRepository->getUserTask($userId, $taskId);
        $userTask->status = 3; // 已领取奖励
        $userTask->rewarded_at = now();
        $userTask->save();
    }
    
    // 分发任务奖励领取事件
    Event::dispatch(new TaskRewardClaimedEvent(
        $userId,
        $taskId,
        $task->name,
        $rewards,
        now()->toDateTimeString(),
        $isSuccess
    ));
    
    return $isSuccess;
}

6. 扩展事件系统

6.1 添加新的事件

要添加新的事件,需要创建事件类并在TaskServiceProvider中注册:

// 创建新的事件类
namespace App\Module\Task\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class TaskResetEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
    
    /**
     * 重置类型
     * @var string
     */
    public string $resetType;
    
    /**
     * 重置时间
     * @var string
     */
    public string $resetTime;
    
    /**
     * 受影响的任务ID列表
     * @var array
     */
    public array $affectedTasks;
    
    /**
     * 创建一个新的事件实例
     *
     * @param string $resetType
     * @param string $resetTime
     * @param array $affectedTasks
     */
    public function __construct(string $resetType, string $resetTime, array $affectedTasks)
    {
        $this->resetType = $resetType;
        $this->resetTime = $resetTime;
        $this->affectedTasks = $affectedTasks;
    }
}

6.2 添加新的监听器

要添加新的监听器,需要创建监听器类并在TaskServiceProvider中注册:

// 创建新的监听器类
namespace App\Module\Task\Listeners;

use App\Module\Task\Events\TaskResetEvent;
use Illuminate\Contracts\Queue\ShouldQueue;

class TaskResetListener extends BaseTaskEventListener implements ShouldQueue
{
    /**
     * 创建监听器实例
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * 处理任务重置事件
     *
     * @param TaskResetEvent $event
     * @return void
     */
    public function handle($event): void
    {
        // 记录日志
        $this->logEvent("任务重置:{$event->resetType}", [
            'reset_type' => $event->resetType,
            'reset_time' => $event->resetTime,
            'affected_tasks' => $event->affectedTasks
        ]);
        
        // 处理任务重置逻辑
        // ...
    }
}

6.3 在TaskServiceProvider中注册新的事件和监听器

/**
 * 应用程序的事件监听器映射
 *
 * @var array
 */
protected $listen = [
    TaskCompletedEvent::class => [
        TaskCompletedListener::class,
    ],
    TaskRewardClaimedEvent::class => [
        TaskRewardClaimedListener::class,
    ],
    TaskResetEvent::class => [
        TaskResetListener::class,
    ],
];

7. 最佳实践

  1. 事件应该是轻量级的:事件对象应该只包含必要的数据,避免包含大量数据或复杂对象
  2. 监听器应该是专注的:每个监听器应该只关注一个特定的功能,避免在一个监听器中实现多个不相关的功能
  3. 使用队列处理耗时操作:对于耗时的操作,应该使用队列处理,避免阻塞主流程
  4. 记录关键日志:在事件处理过程中记录关键日志,便于问题排查
  5. 使用事务确保数据一致性:在涉及多个数据操作的场景中,使用事务确保数据一致性

8. 常见问题与解决方案

8.1 事件没有被触发

可能的原因:

  • 事件没有被正确分发
  • 事件类名或命名空间错误

解决方案:

  • 检查事件分发代码
  • 检查事件类名和命名空间是否正确

8.2 监听器没有执行

可能的原因:

  • 监听器没有被正确注册
  • 监听器类名或命名空间错误
  • 队列配置错误(对于实现ShouldQueue的监听器)

解决方案:

  • 检查TaskServiceProvider中的监听器注册
  • 检查监听器类名和命名空间是否正确
  • 检查队列配置是否正确

8.3 监听器执行但没有预期效果

可能的原因:

  • 监听器逻辑错误
  • 依赖服务异常

解决方案:

  • 检查监听器逻辑
  • 检查依赖服务是否正常
  • 添加更详细的日志,便于排查问题

9. 参考资料

10. 更新历史

日期 版本 更新内容
2023-06-10 1.0 初始版本
2023-06-15 1.1 添加扩展事件系统章节
2023-06-20 1.2 添加常见问题与解决方案