如何将多条订单通知合并为单个通知实例?
解决方案:聚合批量订单通知
要解决批量下单时重复发送通知的问题,核心思路是延迟聚合通知:先缓存短时间内的订单数量,延迟一段时间后再发送一条合并后的通知,而不是每条订单都单独发送。以下是具体实现步骤:
1. 修改订单观察者(OrderObserver)
不再直接发送通知,而是将订单计数存入缓存,并仅调度一次延迟任务处理聚合通知:
class OrderObserver { /** * Handle the Order "created" event. * * @param \App\Models\Order $order * @return void */ public function created(Order $order) { $cacheKey = 'pending_courier_orders'; // 递增缓存中的订单计数,初始为1,过期时间与延迟任务一致 Cache::increment($cacheKey, 1); // 存储订单ID列表(可选,用于后续跳转查看订单) Cache::push("{$cacheKey}_ids", $order->id); // 避免重复调度任务,仅当未调度时触发 if (!Cache::has("{$cacheKey}_task_scheduled")) { SendAggregatedOrderNotification::dispatch()->delay(now()->addMinutes(1)); Cache::put("{$cacheKey}_task_scheduled", true, now()->addMinutes(1)); } } }
2. 创建聚合通知处理任务
新建SendAggregatedOrderNotification任务,负责读取缓存中的订单数据并发送合并通知:
<?php namespace App\Jobs; use App\Models\User; use App\Notifications\OrderReceived; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Cache; class SendAggregatedOrderNotification implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function handle() { $cacheKey = 'pending_courier_orders'; $orderCount = Cache::get($cacheKey, 0); $orderIds = Cache::get("{$cacheKey}_ids", []); // 无待处理订单时直接返回 if ($orderCount <= 0) { return; } // 给所有快递员发送聚合通知 User::couriers()->each(function ($courier) use ($orderCount, $orderIds) { $courier->notify(new OrderReceived($orderCount, $orderIds)); }); // 清理缓存 Cache::forget($cacheKey); Cache::forget("{$cacheKey}_ids"); Cache::forget("{$cacheKey}_task_scheduled"); } }
3. 修改OrderReceived通知类
更新通知类以支持接收订单数量,动态生成合并后的通知内容:
class OrderReceived extends Notification { use Queueable; /** * 待通知的订单数量 * * @var int */ protected int $orderCount; /** * 订单ID列表 * * @var array */ protected array $orderIds; /** * 通知标题 * * @var string */ protected string $title; /** * 通知内容 * * @var string */ protected string $subtitle; /** * Create a new notification instance. * * @return void */ public function __construct(int $orderCount, array $orderIds) { $this->orderCount = $orderCount; $this->orderIds = $orderIds; // 根据订单数量生成对应文案 if ($orderCount === 1) { $this->title = 'Pesanan Diterima!'; $this->subtitle = 'Ada pesanan baru nih! Yuk cek dimana~'; } else { $this->title = "{$orderCount} Pesanan Diterima!"; $this->subtitle = "Ada {$orderCount} pesanan baru nih! Yuk cek dimana~"; } } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return [FcmChannel::class]; } public function toFcm($notifiable) { return FcmMessage::create() ->setData([ 'order_count' => $this->orderCount, 'order_ids' => $this->orderIds ]) ->setNotification( \NotificationChannels\Fcm\Resources\Notification::create() ->setTitle($this->title) ->setBody($this->subtitle) ) ->setAndroid( AndroidConfig::create() ->setFcmOptions(AndroidFcmOptions::create()->setAnalyticsLabel('analytics')) ->setNotification(AndroidNotification::create()->setColor('#0A0A0A')) )->setApns( ApnsConfig::create() ->setFcmOptions(ApnsFcmOptions::create()->setAnalyticsLabel('analytics_ios')) ); } public function toMail($notifiable) { return (new MailMessage) ->line("Anda menerima {$this->orderCount} pesanan baru.") ->action('Lihat Pesanan', url('/orders')) ->line('Terima kasih menggunakan aplikasi kami!'); } public function toArray($notifiable) { return [ 'order_count' => $this->orderCount, 'order_ids' => $this->orderIds ]; } }
关键说明
- 缓存与延迟时间:示例中设置1分钟的延迟和缓存过期时间,可根据业务需求调整(如30秒、2分钟),平衡通知实时性和聚合效果。
- 缓存驱动:确保使用支持递增、列表操作的缓存驱动(如Redis),默认文件缓存无法支持这些操作。
- 任务去重:通过缓存标记避免重复调度任务,防止同一时间段内多次触发通知发送。
内容的提问来源于stack exchange,提问作者Yura
相关产品推荐
相关产品推荐

