数据表结构
CREATE TABLE `yoshop_smtp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`smtp_hostname` varchar(255) NOT NULL DEFAULT '' COMMENT '邮件发送器host,如:smtp.qq.com',
`smtp_username` varchar(255) NOT NULL DEFAULT '' COMMENT '账号',
`smtp_password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
`sort` int(11) NOT NULL DEFAULT 0 COMMENT '排序,越大越靠前',
`is_delete` tinyint(1) NOT NULL DEFAULT 0,
`create_time` int(11) NOT NULL DEFAULT 0,
`update_time` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT;
modal
public function getEffectMail($expireIds = '')
{
$where = [
['is_delete', '=', 0]
];
if ($expireIds) {
$where[] = ['id', 'not in', $expireIds];
}
return $this -> where($where)
->order(['sort' => 'desc', 'create_time' => 'desc'])
->find();
}
controller
<?php
namespace app\common;
use think\facade\Cache;
use think\facade\Env;
use think\facade\Log;
use foxFlyToolClass\FoxLog;
use app\common\model\Smtp as SmtpModel;
use app\common\enum\Mail as MailEnum;
class Mail
{
public function __construct()
{
require app()->getRootPath() . 'extend/phpmailer/PHPMailer.php';
}
/**
* 发送邮件
* 1.发件人必须开启IMAP服务,收件人可不用开启
* 2.内容 标题 必填
* 3.收件人姓名可以为邮箱号
* composer require phpmailer/phpmailer
* @param string $tomail 收件人邮箱
* @param string $subject 主题(标题):QQ邮件发送测试
* @param string $body 内容:<h1>恭喜你,邮件测试成功。</h1>
* @param string $from_name 回复名称(留空则为发件人名称)
* @param null $attachment //附件
*/
public function sendMail($tomail, $subject, $body, $from_name = '', $attachment = null, $retryCount = 0)
{
$maxRetries = 3; // 最大重试次数
if ($retryCount > $maxRetries) {
return false; // 达到最大重试次数后终止
}
$isEnd = false;
$smtpModel = new SmtpModel();
$expireIds = Cache::get(MailEnum::SMTP_EXPIRE_IDS);
$mailConfig = $smtpModel -> getEffectMail($expireIds);
if (!$mailConfig) {
$isEnd = true;
$username = Env::get(MailEnum::SMTP_USERNAME);
$password = Env::get(MailEnum::SMTP_PASSWORD);
$mail_api = Env::get(MailEnum::SMTP_HOSTNAME);
} else {
$username = $mailConfig['smtp_username'];
$password = $mailConfig['smtp_password'];
$mail_api = $mailConfig['smtp_hostname'];
}
$mail = new \PHPMailer\PHPMailer\PHPMailer(); //实例化PHPMailer对象
try{
$mail->CharSet = 'UTF-8'; //设定邮件编码,默认ISO-8859-1,如果发中文此项必须设置,否则乱码
$mail->isSMTP(); // 设定使用SMTP服务 需要提前开通对应邮箱的SMTP服务
$mail->SMTPDebug = 0; // SMTP调试功能 0=关闭 1 = 错误和消息 2 = 消息
$mail->SMTPAuth = true; // 启用 SMTP 验证功能
$mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS; // Enable SSL encryption
$mail->Host = $mail_api; // 企业邮局域名
$mail->Port = 465; //设置ssl连接smtp服务器的远程服务器端口号 可选465或587
// Set the SMTP options to use TLSv1.2
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
)
);
$mail->Username = $username; //邮件发送人的用户名(请填写完整的email地址)
$mail->Password = $password; // 邮件发送人的 密码 (授权码)
$mail->setFrom($username, $from_name ?? $username);
if ($from_name) {
$replyEmail = ''; //留空则为发件人EMAIL
$replyName = $from_name; //回复名称(留空则为发件人名称)
$mail->AddReplyTo($replyEmail, $replyName); //回复的地址
}
$mail->Subject = $subject; //邮件标题
$mail->MsgHTML($body); //邮件内容
$name = $tomail;
$mail->addAddress($tomail, $name); //收件人地址,("收件人email","收件人姓名")
if (is_array($attachment)) { // 添加附件
foreach ($attachment as $file) {
is_file($file) && $mail->AddAttachment($file);
}
}
// TODO: 模拟发送 100 封邮件
for ($i = 1; $i <= 200; $i++) {
$resMain = $mail->send();
if ($resMain) {
FoxLog::log([
'res' => $resMain,
'mail_info' => $mailConfig,
], "Email {$i} success");
} else {
FoxLog::log([
'errir_info' => $mail->ErrorInfo,
'mail_info' => $mailConfig,
], "Email {$i} error");
}
sleep(8); // 每秒发送一封邮件
}
// $resMain = $mail->send();
// FoxLog::log([
// '$resMain' => $resMain,
// ], "$resMain");
// 发送邮箱达到限额,轮询其它发送邮箱重发
if ($mail->ErrorInfo) {
Log::error("Email ErrorInfo: $mail->ErrorInfo");
// FoxLog::log([
// 'error' => $mail->ErrorInfo,
// 'mail' => $mailConfig,
// ], "Email ErrorInfo");
}
if (!$resMain && strpos($mail->ErrorInfo, 'The number of emails exceeds the daily sending limit')) {
// if (!$resMain) {
if ($mailConfig) {
$expireIds = !empty($expireIds) ? explode(',', $expireIds) : [];
$expireIds[] = $mailConfig['id'];
Cache::set(MailEnum::SMTP_EXPIRE_IDS, implode(',', $expireIds), new \DateTime('today 23:59:59'));
}
// 重发
usleep(200); // 200微秒后重发
if (!$isEnd) return $this -> sendMail($tomail, $subject, $body, $attachment, $retryCount + 1);
else Log::error('Email Retry End Error, retrycount: ' . $retryCount);
}
return [
'is_ok' => $resMain,
'error_msg' => $mail -> ErrorInfo,
];
} catch (\Exception $e) {
// FoxLog::log($mail->ErrorInfo, "Email Error");
Log::error('Mailer Error: ' . $mail->ErrorInfo);
return [
'is_ok' => false,
'error_msg' => $mail -> ErrorInfo,
];
}
}
}