laravel发邮件多账号轮流切换发邮件

由于本人项目发邮件次数比较多,而每个邮箱地址最大上限每日1000封受限。

为了避免第一个邮箱账号达到每日上限(邮件异常会返回错误码554,提示:Expected response code 354 but got code "554", with message "554 5.5.3 RP:TRC),项目还继续发送邮件,则启动备用邮箱号发送。

第一步:首先先配置.env配置备用邮件账号数据

复制代码
#备用邮箱号
BACKUP_MAIL_DRIVER=smtp
BACKUP_MAIL_HOST=smtp.qiye.163.com
BACKUP_MAIL_PORT=465
BACKUP_MAIL_USERNAME=XXXXX@kingyee.com.cn
BACKUP_MAIL_PASSWORD=XXXXXXXXXXX
BACKUP_MAIL_FROM_ADDRESS=XXXXX@kingyee.com.cn
BACKUP_MAIL_FROM_NAME=XXXXX系统(备用)
BACKUP_MAIL_ENCRYPTION=ssl

第二步:在config目录新建文件名为backupMail.php

复制代码
<?php
/**
 * 备用邮箱号 邮件配置
 */
return [

    /*
    |--------------------------------------------------------------------------
    | Mail Driver
    |--------------------------------------------------------------------------
    |
    | Laravel supports both SMTP and PHP's "mail" function as drivers for the
    | sending of e-mail. You may specify which one you're using throughout
    | your application here. By default, Laravel is setup for SMTP mail.
    |
    | Supported: "smtp", "sendmail", "mailgun", "mandrill", "ses",
    |            "sparkpost", "log", "array"
    |
    */

    'driver' => env('BACKUP_MAIL_DRIVER', 'smtp'),

    /*
    |--------------------------------------------------------------------------
    | SMTP Host Address
    |--------------------------------------------------------------------------
    |
    | Here you may provide the host address of the SMTP server used by your
    | applications. A default option is provided that is compatible with
    | the Mailgun mail service which will provide reliable deliveries.
    |
    */

    'host' => env('BACKUP_MAIL_HOST', 'smtp.mailgun.org'),

    /*
    |--------------------------------------------------------------------------
    | SMTP Host Port
    |--------------------------------------------------------------------------
    |
    | This is the SMTP port used by your application to deliver e-mails to
    | users of the application. Like the host we have set this value to
    | stay compatible with the Mailgun e-mail application by default.
    |
    */

    'port' => env('BACKUP_MAIL_PORT', 587),

    /*
    |--------------------------------------------------------------------------
    | Global "From" Address
    |--------------------------------------------------------------------------
    |
    | You may wish for all e-mails sent by your application to be sent from
    | the same address. Here, you may specify a name and address that is
    | used globally for all e-mails that are sent by your application.
    |
    */

    'from' => [
        'address' => env('BACKUP_MAIL_FROM_ADDRESS', 'hello@example.com'),
        'name' => env('BACKUP_MAIL_FROM_NAME', 'Example'),
    ],

    /*
    |--------------------------------------------------------------------------
    | E-Mail Encryption Protocol
    |--------------------------------------------------------------------------
    |
    | Here you may specify the encryption protocol that should be used when
    | the application send e-mail messages. A sensible default using the
    | transport layer security protocol should provide great security.
    |
    */

    'encryption' => env('BACKUP_MAIL_ENCRYPTION', 'tls'),

    /*
    |--------------------------------------------------------------------------
    | SMTP Server Username
    |--------------------------------------------------------------------------
    |
    | If your SMTP server requires a username for authentication, you should
    | set it here. This will get used to authenticate with your server on
    | connection. You may also set the "password" value below this one.
    |
    */

    'username' => env('BACKUP_MAIL_USERNAME'),

    'password' => env('BACKUP_MAIL_PASSWORD'),

    /*
    |--------------------------------------------------------------------------
    | Sendmail System Path
    |--------------------------------------------------------------------------
    |
    | When using the "sendmail" driver to send e-mails, we will need to know
    | the path to where Sendmail lives on this server. A default path has
    | been provided here, which will work well on most of your systems.
    |
    */

    'sendmail' => '/usr/sbin/sendmail -bs',

    /*
    |--------------------------------------------------------------------------
    | Markdown Mail Settings
    |--------------------------------------------------------------------------
    |
    | If you are using Markdown based email rendering, you may configure your
    | theme and component paths here, allowing you to customize the design
    | of the emails. Or, you may simply stick with the Laravel defaults!
    |
    */

    'markdown' => [
        'theme' => 'default',

        'paths' => [
            resource_path('views/vendor/mail'),
        ],
    ],
];

第三步:在Services目录新建个EmailServices.php文件。

复制代码
<?php
/**
 * email service
 */

namespace App\Services;

use App\Jobs\SendMailJob;
use App\Models\MedliveConfig;
use App\Models\MeetingFlowProject;
use App\Models\MeetingOrder;
use App\Models\SendEmailLog;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Mail;
use App\Mail\OrderShipped;
use Illuminate\Http\Request;

class EmailServices
{
    /**
     * 邮件通知
     * @param array $aRequests 请求数据
     * @param array $aAddressee 收件人
     * @param array $aCC 抄送人
     * @param string $subject 邮件主题
     * @param string $viewName 邮件视图名
     * @param bool $checkEmailRule 校验邮箱规则
     * @param int $user_id 用户id
     * @param string $mailConfig 邮件配置文件名
     */
    public function sendEmail($aRequests, $aAddressee = [], $aCC = [], $subject = '', $viewName = '', $checkEmailRule = true, $user_id = 0, $mailConfig = 'mail')
    {
        if (empty($aAddressee) || $aAddressee == '') {
            $aAddressee = [];
        }

        if (empty($aCC) || $aCC == '') {
            $aCC = [];
        }

        if (config('app.env') == 'local') {
            // 本地
            $aAddressee = ['xxxx@medlive.cn'];
            $aCC = [];
        }

        try {
            // info('----发邮件----:');
            // info('收件人:' . json_encode(array_filter($aAddressee)));
            // info('抄送人:' . json_encode(array_filter($aCC)));
            // info('请求数据:' . json_encode($aRequests));
            // info('--------------:');
            if ($checkEmailRule) {
                $aAddressee = checkSendEmail($aAddressee);
                $aCC = checkSendEmail($aCC);
            }
            SendMailJob::dispatch($aAddressee, $aCC, $aRequests, $subject, $viewName, $user_id, $mailConfig);
        } catch (\Throwable $e) {
            if (config('app.env') == 'production') {
                $aErrorRequest = [
                    'msg' => '发送邮件失败,错误信息:' . $e->getMessage(),
                    'aAddressee' => '收件人:' . json_encode($aAddressee),
                    'aCC' => '抄送人:' . json_encode($aCC),
                    'requestData' => '请求信息:' . json_encode($aRequests)
                ];
                SendMailJob::dispatch(explode('、', MedliveConfig::getValueByKey('accept_error_msg')), [], $aErrorRequest, '发送邮件失败', 'sendWrong', $user_id, 'backupMail');
            }
            info('发送邮件失败,错误信息1:' . $e->getMessage());
        }
    }


    /**
     * 测试发邮件
     */
    public static function testSendEmail()
    {
        try {
            echo('----发邮件开始----<br>');
            $mailConfig = \request('source');
            if ($mailConfig) {
                $mailConfig = 'backupMail';
            } else {
                $mailConfig = 'mail';
            }
            $aAddressee = ['xxxx@medlive.cn']; //本地测试,收件人
            $aCC = [];//本地测试 抄送人
            SendMailJob::dispatch($aAddressee, $aCC, ['wrong_emails' => []], '测试发邮件', 'wrongNotice', 0, $mailConfig);
            echo('----发邮件结束----<br>');
        } catch (\Throwable $e) {
            dd('发送邮件失败,错误信息:' . $e->getMessage());
        }

    }
}

第四步:由于发邮件使用的是队列,在Mail目录新建OrderShipped.php文件

复制代码
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class OrderShipped extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * order 实例。
     *
     * @var Order
     */
    public $order;
    public $subject;
    public $viewName;
    public $mailConfig;

    /**
     * 创建一个新消息实例。
     *
     * @return void
     */
    public function __construct($order, $subject = '', $viewName = '', $mailConfig = 'mail')
    {
        $this->subject = $subject;
        $this->order = $order;
        $this->viewName = !empty($viewName) ? $viewName : 'shipped';
        $this->mailConfig = $mailConfig;
    }

    /**
     * 构建消息。
     *
     * @return $this
     */
    public function build()
    {
        $build = $this->from(config($this->mailConfig . '.username'), config($this->mailConfig . '.from.name'))
            ->view('emails.orders.' . $this->viewName)
            ->subject($this->subject);
        if (data_get($this->order, 'push_file')) {
            $push_file = public_path('/uploadfile' . data_get($this->order, 'push_file'));
            if (file_exists($push_file)) {
                $build = $build->attach($push_file, ['as' => '稿件附件.' . pathinfo($push_file, PATHINFO_EXTENSION)]);
            }
        }

        if (data_get($this->order, 'headimg_path')) {
            $headimg_path = public_path('/uploadfile' . data_get($this->order, 'headimg_path'));
            if (file_exists($headimg_path)) {
                $build = $build->attach($headimg_path, ['as' => '头图附件.' . pathinfo($headimg_path, PATHINFO_EXTENSION)]);
            }
        }

        if (data_get($this->order, 'video_path')) {
            $video_path = public_path('/uploadfile' . data_get($this->order, 'video_path'));
            if (file_exists($video_path)) {
                $build = $build->attach($video_path, ['as' => '视频附件.' . pathinfo($video_path, PATHINFO_EXTENSION)]);
            }
        }

        if (data_get($this->order, 'live_screenshot', [])) {
            //视频截图
            foreach ($this->order['live_screenshot'] as $k => $file) {
                $file = public_path('/uploadfile' . '/' . $file);
                if (file_exists($file)) {
                    $build = $build->attach($file, ['as' => '视频截图' . ($k + 1) . '.' . pathinfo($file, PATHINFO_EXTENSION)]);
                }
            }
        }

        if (data_get($this->order, 'live_data_file', [])) {
            //直播数据附件
            foreach ($this->order['live_data_file'] as $k => $live_data_file) {
                $live_data_file = public_path('/uploadfile' . '/' . $live_data_file);
                if (file_exists($live_data_file)) {
                    $build = $build->attach($live_data_file, ['as' => '直播数据附件' . ($k + 1) . '.' . pathinfo($live_data_file, PATHINFO_EXTENSION)]);
                }
            }
        }


        return $build;

    }
}

第五步:在Jobs目录新建文件SendMailJob.php

复制代码
<?php

namespace App\Jobs;

use App\Mail\OrderShipped;
use App\Models\MedliveConfig;
use App\Models\SendEmailLog;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Mail;

class SendMailJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $aAddressee;
    protected $aCC;
    protected $aRequests;
    protected $subject;
    protected $viewName;
    protected $user_id;
    public $tries = 3;
    protected $mailConfig;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($aAddressee, $aCC, $aRequests, $subject, $viewName, $user_id = 0, $mailConfig = 'mail')
    {
        $this->aAddressee = $aAddressee;
        $this->aCC = $aCC;
        $this->aRequests = $aRequests;
        $this->subject = $subject;
        $this->viewName = $viewName;
        $this->user_id = $user_id;
        $this->mailConfig = $mailConfig;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // 设置新的邮箱账号
        $transport = new \Swift_SmtpTransport(config($this->mailConfig . '.host'), config($this->mailConfig . '.port'), config($this->mailConfig . '.encryption'));
        $transport->setUsername(config($this->mailConfig . '.username'));
        $transport->setPassword(config($this->mailConfig . '.password'));
        $mailer = new \Swift_Mailer($transport);
        // 发送前实例化
        app(Mailer::class)->setSwiftMailer($mailer);

        info('发送的邮箱账号地址------' . config($this->mailConfig . '.from.address'));

        try {
            Mail::to(array_filter($this->aAddressee))
                ->cc(array_filter($this->aCC))
                ->send(new OrderShipped($this->aRequests, $this->subject, $this->viewName, $this->mailConfig));
            $is_success = 1;
        } catch (\Exception $e) {
            if (config('app.env') == 'production') {
                $aErrorRequest = [
                    'msg' => '发送邮件失败,错误信息:' . $e->getMessage(),
                    'aAddressee' => '收件人:' . json_encode($this->aAddressee),
                    'aCC' => '抄送人:' . json_encode($this->aCC),
                    'requestData' => '请求信息:' . json_encode($this->aRequests)
                ];
                SendMailJob::dispatch(explode('、', MedliveConfig::getValueByKey('accept_error_msg')), [], $aErrorRequest, '发送邮件失败', 'sendWrong', $this->user_id, 'backupMail');
            }
            info(config($this->mailConfig . '.from.address') . '------发送邮件队列抛出异常,错误信息2:' . $e->getMessage());
            if ($e->getCode() == 554) {
                /*
                 * Expected response code 354 but got code "554", with message "554 5.5.3 RP:TRC
                 * 发送的邮件内容包含了未被网易许可的信息,或违背了网易的反垃圾服务条款,或是达到最大上限每日1000封受限。
                 * 超出使用备用账号
                 */
                info('------超出每日邮箱最大上限------使用备用账号发送-----');
                SendMailJob::dispatch($this->aAddressee, $this->aCC, $this->aRequests, $this->subject, $this->viewName, $this->user_id, 'backupMail');
            }
            $is_success = 0;
        }

        //保存发邮件数据
        SendEmailLog::saveSendLog([
            'requestData' => json_encode($this->aRequests),
            'addressee' => json_encode($this->aAddressee),
            'cc' => json_encode($this->aCC),
            'subject' => $this->subject,
            'template_view_name' => $this->viewName,
            'failures' => json_encode(Mail::failures()),
            'user_id' => $this->user_id,
            'mail_from_address' => config($this->mailConfig . '.from.address'),
            'is_success' => $is_success
        ]);
    }
}
相关推荐
ServBay1 天前
告别面条代码,PSL 5.0 重构 PHP 性能与安全天花板
后端·php
多厘3 天前
别再手写 psr-4 了!用 Composer 隐藏魔法干掉上千行烂配置
laravel
JaguarJack4 天前
FrankenPHP 原生支持 Windows 了
后端·php·服务端
BingoGo4 天前
FrankenPHP 原生支持 Windows 了
后端·php
JaguarJack5 天前
PHP 的异步编程 该怎么选择
后端·php·服务端
BingoGo5 天前
PHP 的异步编程 该怎么选择
后端·php
JaguarJack5 天前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay6 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954487 天前
CTF 伪协议
php
BingoGo9 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php