You need to enable JavaScript to run this app. 能否通过AJAX调用PHP文件实现倒计时归零后自动触发邮件发送
优惠活动
大模型
产品
解决方案
定价
更多

能否通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.26 16:24:04