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
        ]);
    }
}
相关推荐
手可摘星河9 分钟前
php中 cli和cgi的区别
开发语言·php
EasyDSS1 小时前
国标GB28181-2022平台EasyGBS:安防监控中P2P的穿透方法
网络协议·php·音视频·p2p
liuyunshengsir3 小时前
Squid代理服务器的安装使用
开发语言·php
007php00712 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
卜及中15 小时前
【Linux】资源隔离机制 — 命名空间(Namespace)详解
linux·服务器·php
m0_5485147716 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
网络安全King17 小时前
网络安全 - SQL Injection
sql·web安全·php
图图爱上壮壮妈18 小时前
PHP中实现拓扑算法
开发语言·算法·php
奥顺互联V1 天前
一次性部署:使用Docker部署PHP应用
大数据·mysql·开源·php
小张认为的测试1 天前
Linux性能监控命令_nmon 安装与使用以及生成分析Excel图表
linux·服务器·测试工具·自动化·php·excel·压力测试