PHP项目Laravel Slack通知集成

wen PHP项目 5

本文目录导读:

PHP项目Laravel Slack通知集成

  1. 安装必要的包
  2. 配置环境变量
  3. 添加配置到config/services.php
  4. 创建通知类
  5. 创建Slack服务类
  6. 发送通知的示例用法
  7. 创建自定义通知(高级用法)
  8. 定义Slack路由(使用Notification::route)
  9. 事件监听(可选)
  10. 测试和调试
  11. 队列优化(推荐)
  12. 常用Slack消息格式示例
  13. 注意事项:

我来帮你实现Laravel项目的Slack通知集成,这是一个完整的实现方案:

安装必要的包

安装Slack通知相关的包:

# 安装Slack SDK
composer require slack-php/slack-lib
# 安装Guzzle HTTP客户端(用于发送请求)
composer require guzzlehttp/guzzle
# 如果还没有安装通知组件
composer require illuminate/notifications

配置环境变量

.env 文件中添加Slack配置:

# Slack Webhook URL(用于发送通知)
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
# Slack Bot Token(用于高级功能,如发送消息、获取用户信息等)
SLACK_BOT_TOKEN=xoxb-xxxxx-xxxxxxxxx-xxxxxxxxxxxxx
# 默认Slack频道(可选,用于指定默认接收通知的频道)
SLACK_DEFAULT_CHANNEL=general

添加配置到config/services.php

// config/services.php
return [
    // ... 其他服务配置
    'slack' => [
        'webhook_url' => env('SLACK_WEBHOOK_URL'),
        'bot_token' => env('SLACK_BOT_TOKEN'),
        'default_channel' => env('SLACK_DEFAULT_CHANNEL', 'general'),
    ],
];

创建通知类

创建基础的通知类:

php artisan make:notification SlackNotification
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\SlackMessage;
class SlackNotification extends Notification implements ShouldQueue
{
    use Queueable;
    protected $content;
    protected $attachments = [];
    protected $channel;
    public function __construct($content, $attachments = [], $channel = null)
    {
        $this->content = $content;
        $this->attachments = $attachments;
        $this->channel = $channel;
    }
    public function via($notifiable)
    {
        return ['slack'];
    }
    public function toSlack($notifiable)
    {
        $message = (new SlackMessage)
            ->content($this->content)
            ->error(); // 或者 ->success() / ->warning()
        // 设置频道(如果指定了)
        if ($this->channel) {
            $message->to($this->channel);
        }
        // 添加附件
        if (!empty($this->attachments)) {
            foreach ($this->attachments as $attachment) {
                $message->attachment(function ($attach) use ($attachment) {
                    $attach->title($attachment['title'] ?? '')
                           ->content($attachment['content'] ?? '')
                           ->fields($attachment['fields'] ?? [])
                           ->color($attachment['color'] ?? '#2eb886');
                });
            }
        }
        return $message;
    }
}

创建Slack服务类

创建一个专门处理Slack操作的service:

php artisan make:service SlackService
<?php
namespace App\Services;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use App\Notifications\SlackNotification;
use App\Models\User;
class SlackService
{
    protected $client;
    protected $webhookUrl;
    protected $botToken;
    public function __construct()
    {
        $this->client = new Client();
        $this->webhookUrl = config('services.slack.webhook_url');
        $this->botToken = config('services.slack.bot_token');
    }
    /**
     * 发送简单文本消息到Slack
     */
    public function sendMessage($message, $channel = null)
    {
        try {
            $response = $this->client->post($this->webhookUrl, [
                'json' => [
                    'text' => $message,
                    'channel' => $channel ?? config('services.slack.default_channel'),
                ]
            ]);
            return $response->getStatusCode() === 200;
        } catch (\Exception $e) {
            Log::error('Slack message sending failed: ' . $e->getMessage());
            return false;
        }
    }
    /**
     * 发送复杂消息(带附件)
     */
    public function sendComplexMessage(array $messageData, $channel = null)
    {
        try {
            $payload = array_merge([
                'channel' => $channel ?? config('services.slack.default_channel'),
            ], $messageData);
            $response = $this->client->post($this->webhookUrl, [
                'json' => $payload
            ]);
            return $response->getStatusCode() === 200;
        } catch (\Exception $e) {
            Log::error('Slack complex message sending failed: ' . $e->getMessage());
            return false;
        }
    }
    /**
     * 使用Bot Token发送消息(需要高级权限)
     */
    public function sendWithBot($channel, $text, $blocks = [])
    {
        try {
            $response = $this->client->post('https://slack.com/api/chat.postMessage', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->botToken,
                    'Content-Type' => 'application/json',
                ],
                'json' => [
                    'channel' => $channel,
                    'text' => $text,
                    'blocks' => $blocks,
                ]
            ]);
            $result = json_decode($response->getBody(), true);
            if (!$result['ok']) {
                Log::error('Slack Bot message failed: ' . ($result['error'] ?? 'Unknown error'));
                return false;
            }
            return true;
        } catch (\Exception $e) {
            Log::error('Slack Bot message sending failed: ' . $e->getMessage());
            return false;
        }
    }
    /**
     * 发送文件到Slack
     */
    public function sendFile($channel, $filePath, $filename = null)
    {
        try {
            $multipart = [
                [
                    'name' => 'channels',
                    'contents' => $channel,
                ],
                [
                    'name' => 'file',
                    'contents' => fopen($filePath, 'r'),
                    'filename' => $filename ?? basename($filePath),
                ],
            ];
            $response = $this->client->post('https://slack.com/api/files.upload', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->botToken,
                ],
                'multipart' => $multipart
            ]);
            return json_decode($response->getBody(), true);
        } catch (\Exception $e) {
            Log::error('Slack file upload failed: ' . $e->getMessage());
            return false;
        }
    }
    /**
     * 获取Slack用户信息
     */
    public function getUserInfo($userId)
    {
        try {
            $response = $this->client->get('https://slack.com/api/users.info', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->botToken,
                ],
                'query' => [
                    'user' => $userId,
                ]
            ]);
            return json_decode($response->getBody(), true);
        } catch (\Exception $e) {
            Log::error('Slack user info fetch failed: ' . $e->getMessage());
            return null;
        }
    }
}

发送通知的示例用法

在控制器中使用:

<?php
namespace App\Http\Controllers;
use App\Services\SlackService;
use App\Models\User;
use App\Notifications\SlackNotification;
use Illuminate\Support\Facades\Notification;
class NotificationController extends Controller
{
    protected $slackService;
    public function __construct(SlackService $slackService)
    {
        $this->slackService = $slackService;
    }
    /**
     * 发送简单通知
     */
    public function sendBasicNotification()
    {
        // 通过Service发送
        $this->slackService->sendMessage(
            '用户 ' . auth()->user()->name . ' 刚刚完成了注册。',
            '#registration-notifications'
        );
        // 或者通过 Notification facade 发送
        $user = User::find(1);
        $notification = new SlackNotification(
            '新用户注册: ' . $user->email,
            [
                [
                    'title' => '用户详情',
                    'content' => '新用户成功注册了您的应用',
                    'fields' => [
                        '用户名' => $user->name,
                        '邮箱' => $user->email,
                        '注册时间' => $user->created_at->toDateTimeString(),
                    ],
                    'color' => '#36a64f', // 绿色表示成功
                ]
            ],
            '#user-notifications'
        );
        $user->notify($notification);
        // 或者发送给特定用户
        Notification::route('slack', '#general')->notify($notification);
        return response()->json(['message' => '通知已发送']);
    }
    /**
     * 发送订单通知
     */
    public function sendOrderNotification($order)
    {
        $notification = new SlackNotification(
            "新订单 #{$order->id}",
            [
                [
                    'title' => "订单金额: ¥{$order->total}",
                    'content' => "客户: {$order->customer_name}\n商品数量: {$order->items_count}",
                    'fields' => [
                        '订单号码' => $order->order_number,
                        '支付方式' => $order->payment_method,
                        '订单状态' => $order->status,
                    ],
                    'color' => '#F35A00',
                ]
            ],
            '#orders'
        );
        // 发送给管理团队
        Notification::route('slack', '#admin-orders')->notify($notification);
        // 或者通过邮件发送给具体用户
        $manager = User::where('role', 'manager')->first();
        Notification::send($manager, $notification);
        return response()->json(['message' => '订单通知已发送']);
    }
}

创建自定义通知(高级用法)

创建更专用的通知类:

<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\SlackMessage;
class OrderStatusNotification extends Notification
{
    use Queueable;
    private $order;
    private $status;
    public function __construct($order, $status)
    {
        $this->order = $order;
        $this->status = $status;
    }
    public function via($notifiable)
    {
        return ['slack'];
    }
    public function toSlack($notifiable)
    {
        $colors = [
            'completed' => '#36a64f',
            'pending' => '#ff9900',
            'cancelled' => '#ff0000',
        ];
        return (new SlackMessage)
            ->to('#order-notifications')
            ->content(trans("slack.order_status.{$this->status}", [
                'order_id' => $this->order->id,
            ]))
            ->attachment(function ($attachment) use ($colors) {
                $attachment
                    ->title("Order #{$this->order->order_number}")
                    ->content($this->order->description ?? '')
                    ->fields([
                        'Status' => ucfirst($this->status),
                        'Total' => '$' . number_format($this->order->total, 2),
                        'User' => $this->order->user->name,
                    ])
                    ->color($colors[$this->status] ?? '#36036a');
            })
            ->warning(); // 可选:success(), error(), info()
    }
}

定义Slack路由(使用Notification::route)

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Notification;
class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // 定义Slack通知路由
        Notification::extend('slack', function ($app) {
            return new \Illuminate\Notifications\Channels\SlackWebhookChannel();
        });
    }
}

事件监听(可选)

如果需要在特定事件时自动发送通知:

<?php
namespace App\Listeners;
use App\Services\SlackService;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendSlackNotificationListener implements ShouldQueue
{
    protected $slackService;
    public function __construct(SlackService $slackService)
    {
        $this->slackService = $slackService;
    }
    public function handle($event)
    {
        // 根据事件类型发送不同的通知
        switch (true) {
            case $event instanceof \App\Events\UserRegistered:
                $this->slackService->sendMessage(
                    "🚀 新用户注册: {$event->user->email}",
                    '#user-registrations'
                );
                break;
            case $event instanceof \App\Events\OrderCompleted:
                $this->slackService->sendMessage(
                    "✅ 订单完成: #{$event->order->order_number}",
                    '#orders'
                );
                break;
            // 添加更多事件类型...
        }
    }
}

测试和调试

创建测试方法:

<?php
namespace Tests\Feature;
use Tests\TestCase;
use App\Services\SlackService;
use Illuminate\Support\Facades\Notification;
class SlackNotificationTest extends TestCase
{
    public function test_send_slack_message()
    {
        Notification::fake();
        $slackService = new SlackService();
        $result = $slackService->sendMessage('Test message', '#test-channel');
        $this->assertTrue($result);
    }
    public function test_send_notification_via_facade()
    {
        Notification::fake();
        $notification = new \App\Notifications\SlackNotification(
            'Test notification',
            ['title' => 'Test', 'content' => 'Test content']
        );
        Notification::route('slack', '#test')->notify($notification);
        Notification::assertSentOnDemand(
            SlackNotification::class,
            function ($notification, $channels, $notifiable) {
                return $notifiable->routes['slack'] === '#test';
            }
        );
    }
}

队列优化(推荐)

对于大规模通知,建议使用队列:

// 在通知类中设置配置队列
class SlackNotification extends Notification implements ShouldQueue
{
    use Queueable;
    public $connection = 'redis'; // 或 'database'
    public $queue = 'slack-notifications';
    public $delay = 10; // 延迟10秒
    // 失败重试机制
    public $tries = 3;
    public $timeout = 60;
    public function failed(\Exception $e)
    {
        Log::error('Slack notification failed: ' . $e->getMessage());
        // 可以添加失败告警
    }
}

常用Slack消息格式示例

// 带表情和格式的消息
$this->slackService->sendComplexMessage([
    'text' => "*重要通知*\n订单 #12345 已发货 🎉",
    'attachments' => [
        [
            'color' => '#ff0000',
            'blocks' => [
                [
                    'type' => 'section',
                    'text' => [
                        'type' => 'mrkdwn',
                        'text' => "*客户服务通知*\n有新的客户请求需要处理"
                    ]
                ]
            ]
        ]
    ]
]);
// 带交互按钮的消息
$blocks = [
    [
        'type' => 'section',
        'text' => [
            'type' => 'mrkdwn',
            'text' => '需要审批新的订单申请'
        ]
    ],
    [
        'type' => 'actions',
        'elements' => [
            [
                'type' => 'button',
                'text' => [
                    'type' => 'plain_text',
                    'text' => '批准'
                ],
                'style' => 'primary',
                'value' => 'approve'
            ],
            [
                'type' => 'button',
                'text' => [
                    'type' => 'plain_text',
                    'text' => '拒绝'
                ],
                'style' => 'danger',
                'value' => 'reject'
            ]
        ]
    ]
];
$this->slackService->sendWithBot('#approvals', '新审批请求', $blocks);

注意事项:

  1. 安全性:不要在代码中硬编码Slack URL和token,始终使用环境变量
  2. 错误处理:始终添加try-catch和日志记录
  3. 队列使用:对于大量通知,建议使用队列异步处理
  4. 限流:注意Slack API的调用限制(每分钟最多几十次请求)
  5. 测试:在生产环境发送前,先在测试频道测试

这个方案提供了完整的Slack集成功能,可以根据你的具体需求进行调整和扩展。

抱歉,评论功能暂时关闭!