能否通过AJAX调用PHP文件实现倒计时归零后自动触发邮件发送
前端倒计时触发逻辑修正
原代码存在3个核心问题:
- 倒计时结束判断逻辑错误:代码中计算分钟时额外硬减了2,用
minutesEnd < 0作为触发条件,并非基于总倒计时时长的真实剩余值判断,很容易提前触发,甚至在倒计时还没走到0的时候就误触发AJAX请求。 - 冗余嵌套:把
$(document).ready()写在setInterval的每秒回调里完全多余——setInterval的执行前提是页面DOM已经加载完成,这套嵌套不会让代码更稳定,只会增加无意义的代码层级。 - 无防重复请求机制:因为定时器每秒执行一次,一旦进入触发分支,会在连续多秒内重复发送相同的AJAX请求,直接导致后端多次执行邮件发送逻辑。
修正后的前端代码:
var end = "<?php echo $endate ?>"; var countDownEnd = new Date(end).getTime(); // 增加触发标记,防止重复发请求 let emailTriggered = false; var y = setInterval(function() { var noww = new Date().getTime(); var distanceEnd = countDownEnd - noww; var daysEnd = Math.floor(distanceEnd / (1000 * 60 * 60 * 24)); var hoursEnd = Math.floor((distanceEnd % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); var minutesEnd = Math.floor((distanceEnd % (1000 * 60 * 60)) / (1000 * 60)); var secondsEnd = Math.floor((distanceEnd % (1000 * 60)) / 1000); // 剩余时间小于0说明真正倒计时归零 if (distanceEnd <= 0) { document.getElementById("daysEnd").innerHTML = 0; document.getElementById("hoursEnd").innerHTML = 0; document.getElementById("minutesEnd").innerHTML = 0; document.getElementById("secondsEnd").innerHTML = 0; // 只在第一次进入归零状态时发请求 if (!emailTriggered) { emailTriggered = true; $.ajax({ url: "../php/reminders.php", type: "POST" }); } clearInterval(y); return; } document.getElementById("daysEnd").innerHTML = daysEnd; document.getElementById("hoursEnd").innerHTML = hoursEnd; document.getElementById("minutesEnd").innerHTML = minutesEnd; document.getElementById("secondsEnd").innerHTML = secondsEnd; }, 1000);
后端reminders.php逻辑修正
原代码存在4个核心问题:
- PHPMailer使用逻辑错误:邮件实例在循环外创建,循环内仅重复添加收件人、重复设置SMTP配置,直到循环结束才执行一次send(),会导致所有收件人被合并到同一封邮件、甚至发信参数覆盖的问题。
- 语法错误:
$mail->Username = myemail@gmail.com';行首缺少单引号,会直接触发PHP解析报错,脚本无法运行。 - 发信逻辑位置错误:
$mail->send()写在循环外,没有包裹错误捕获,也没有在每次发信后清空上一次的收件人、邮件内容,会导致后续邮件携带之前的历史收件人。 - 数据库连接无校验:
mysqli_connect()执行失败后没有做中断处理,后续查询会直接抛出空结果错误。
修正后的后端代码:
<?php session_start(); use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\Exception; // 用相对路径引入autoload,避免绝对路径在部署时报错 require __DIR__ . '/../../vendor/autoload.php'; // 校验数据库连接 $db = mysqli_connect('localhost', 'root', '', 'admin_man'); if (!$db) { die("数据库连接失败: " . mysqli_connect_error()); } $reminders = mysqli_query($db, "SELECT * FROM student WHERE voting_status = 'not yet voted'"); // 提前初始化SMTP配置,只需要设置一次 $mail = new PHPMailer(TRUE); try { $mail->isSMTP(); $mail->Host = 'smtp.gmail.com'; $mail->SMTPAuth = TRUE; $mail->SMTPSecure = 'tls'; $mail->Username = 'myemail@gmail.com'; $mail->Password = 'mypassword'; $mail->Port = 587; $mail->setFrom('myemail', 'aw'); $mail->Subject = 'sample'; $mail->Body = 'Reminders'; $mail->SMTPOptions = array( 'ssl' => array( 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true ) ); } catch (Exception $e) { die("SMTP配置错误: " . $e->errorMessage()); } // 循环处理每个收件人 while ($row = mysqli_fetch_array($reminders)) { try { // 每次发信前清空上一次的收件人,避免重复叠加 $mail->clearAddresses(); $mail->addAddress($row['email']); $mail->send(); } catch (Exception $e) { // 记录单个收件人发信失败的信息,不中断整个循环 error_log("给 {$row['email']} 发信失败: " . $e->errorMessage()); } } ?>
注意:前端触发的邮件发送机制本身存在可靠性缺陷:只要用户关闭了倒计时所在的页面,就不会触发发信请求,也无法保证所有访问过页面的用户都能触发到归零的逻辑。如果需要100%可靠的定时发信,更推荐将reminders.php配置为服务端定时任务,到约定时间点由服务器直接执行脚本,完全不依赖前端页面是否打开。
内容的提问来源于stack exchange,提问作者stribb
相关产品推荐
相关产品推荐

