由于本人项目发邮件次数比较多,而每个邮箱地址最大上限每日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
]);
}
}