修复:邮件通知两处静默失效(测试暴露)

症状:notify_log 全表为空 → 用户报「完全没发邮件」,后台查不到任何线索。

1. 前 4 个前置判断(开关/收件人/自己回自己/冷却)直接 return 不写日志,
   等于「为什么没发」完全无痕。改为任何退出路径都写 notify_log,
   并带上可读原因。

2. new 事件(新反馈通知管理员)收件人只取 admin_email,该值为空时整条
   通知静默失效 —— 管理员永远收不到新反馈信,界面也无任何提示。
   改为回落 smtp.user / smtp.from;后台勾了「通知管理员」却没填邮箱时
   当场显示警告。

验证:走 fb_notify_event 全事件测试,6 个事件全部写日志、new 能送达。
This commit is contained in:
2026-11-03 07:31:35 +08:00
parent 8a850ca0e5
commit e43f2ce67a
2 changed files with 41 additions and 5 deletions
+15 -2
View File
@@ -1326,8 +1326,21 @@ fb_layout_head('管理后台', ['robots' => 'noindex,nofollow']);
<h3 style="margin:20px 0 8px;font-size:15px">通知规则</h3> <h3 style="margin:20px 0 8px;font-size:15px">通知规则</h3>
<label class="check"><input type="checkbox" name="notify_enabled" value="1"<?php echo fb_cfg('notify.enabled', true) ? ' checked' : ''; ?>> 有人回复时通知提交人</label> <label class="check"><input type="checkbox" name="notify_enabled" value="1"<?php echo fb_cfg('notify.enabled', true) ? ' checked' : ''; ?>> 有人回复时通知提交人</label>
<label class="check"><input type="checkbox" name="notify_on_every_reply" value="1"<?php echo fb_cfg('notify.on_every_reply', false) ? ' checked' : ''; ?>> 每次回复都通知(默认只在官方回复时通知,避免刷屏)</label> <label class="check"><input type="checkbox" name="notify_on_every_reply" value="1"<?php echo fb_cfg('notify.on_every_reply', false) ? ' checked' : ''; ?>> 每次回复都通知(默认只在官方回复时通知,避免刷屏)</label>
<label class="check"><input type="checkbox" name="notify_admin_new_thread" value="1"<?php echo fb_cfg('notify.admin_new_thread', false) ? ' checked' : ''; ?>> 有新反馈时通知管理员 <?php
<span class="hint" style="display:block;margin-left:24px">发到「站点信息」里填的官方邮箱。</span></label> // 勾了「有新反馈时通知管理员」却没填官方邮箱 —— 这条通知会发不出去。
// 之前这个组合完全是静默失效的,用户在后台看不出任何异常。
$adminMailNow = trim((string)fb_cfg('admin_email', ''));
$smtpUserNow = trim((string)fb_cfg('smtp.user', ''));
$newNotifyOn = (bool)fb_cfg('notify.admin_new_thread', false);
$mailMissing = ($adminMailNow === '' && $smtpUserNow === '');
?>
<label class="check"><input type="checkbox" name="notify_admin_new_thread" value="1"<?php echo $newNotifyOn ? ' checked' : ''; ?>> 有新反馈时通知管理员
<span class="hint" style="display:block;margin-left:24px">发到「站点信息」里填的官方邮箱;没填官方邮箱时退而发到 SMTP 发件地址。</span>
<?php if ($newNotifyOn && $mailMissing): ?>
<span class="hint" style="display:block;margin-left:24px;color:#b4553c;font-weight:600">⚠️ 官方邮箱和 SMTP 发件地址都是空的,这条通知目前发不出去。请在上方「官方邮箱」处填写。</span>
<?php elseif ($newNotifyOn && $adminMailNow === ''): ?>
<span class="hint" style="display:block;margin-left:24px;color:#8a6a15">当前会发到 SMTP 发件地址 <?php echo e($smtpUserNow); ?>。若想收到别的信箱,请在「站点信息」填写官方邮箱。</span>
<?php endif; ?></label>
<label class="field" style="max-width:280px"><span>同一帖同一收件人的最短发信间隔(秒)</span> <label class="field" style="max-width:280px"><span>同一帖同一收件人的最短发信间隔(秒)</span>
<?php /* 出厂默认是 300(见 config.example.php 的 notify.cooldown_seconds) */ ?> <?php /* 出厂默认是 300(见 config.example.php 的 notify.cooldown_seconds) */ ?>
<input type="number" name="notify_cooldown_seconds" min="0" max="86400" placeholder="300" <input type="number" name="notify_cooldown_seconds" min="0" max="86400" placeholder="300"
+26 -3
View File
@@ -581,8 +581,20 @@ if (!function_exists('fb_notify_event')) {
function fb_notify_event(PDO $pdo, $reason, array $thread, array $extra = []) { function fb_notify_event(PDO $pdo, $reason, array $thread, array $extra = []) {
$reason = (string)$reason; $reason = (string)$reason;
/* 🔴 铁律:任何一条退出路径都必须写 notify_log。
*
* 原先前 4 个判断(开关 / 收件人 / 自己回自己 / 冷却)直接 return 不记日志,
* 结果就是「用户说没收到信」时后台查不到任何记录 —— 因为压根就没进发信那一步。
* 2026-11-03 排查时 notify_log 全表为空,一度被误判成「发信链路坏了」,
* 实际是这些静默 return 造成的黑箱。日志必须回答「为什么没发」。
*/
$log = function ($to, $ok, $err) use ($pdo, $thread, $reason) {
fb_notify_log2($pdo, $thread, $to, $reason, $ok, $err);
};
// ---- 1. 开关 ---- // ---- 1. 开关 ----
if (!fb_notify_enabled_for($reason)) { if (!fb_notify_enabled_for($reason)) {
$log('', 0, '通知未启用(该事件的开关是关的)');
return [false, '通知未启用']; return [false, '通知未启用'];
} }
@@ -591,30 +603,41 @@ if (!function_exists('fb_notify_event')) {
if ($isAdminNotice) { if ($isAdminNotice) {
$to = trim((string)fb_cfg('admin_email', '')); $to = trim((string)fb_cfg('admin_email', ''));
if ($to === '') { $to = trim((string)fb_cfg('admin.email', '')); } if ($to === '') { $to = trim((string)fb_cfg('admin.email', '')); }
// 还没填官方邮箱时回落到 SMTP 发件地址 —— 至少信能到管理员手上,
// 而不是整个「新反馈通知」功能静默失效(实际就发生过:管理员从不收信,
// 后台也没有任何提示,直到来测才发现)。
if ($to === '') { $to = trim((string)fb_cfg('smtp.user', '')); }
if ($to === '') { $to = trim((string)fb_cfg('smtp.from', '')); }
} else { } else {
$to = trim((string)(isset($thread['author_email']) ? $thread['author_email'] : '')); $to = trim((string)(isset($thread['author_email']) ? $thread['author_email'] : ''));
} }
if ($to === '' || !filter_var($to, FILTER_VALIDATE_EMAIL)) { if ($to === '' || !filter_var($to, FILTER_VALIDATE_EMAIL)) {
return [false, '收件邮箱无效']; $why = $isAdminNotice
? '收件邮箱无效:官方邮箱与 SMTP 发件地址都没填,新反馈通知发不出去'
: '收件邮箱无效:提交人没留邮箱或格式不对';
$log($to, 0, $why);
return [false, $why];
} }
// ---- 3. 自己通知自己就别发了 ---- // ---- 3. 自己通知自己就别发了 ----
if (!$isAdminNotice && !empty($extra['is_admin']) === false if (!$isAdminNotice && !empty($extra['is_admin']) === false
&& !empty($extra['reply']['author_email'])) { && !empty($extra['reply']['author_email'])) {
if (strcasecmp(trim((string)$extra['reply']['author_email']), $to) === 0) { if (strcasecmp(trim((string)$extra['reply']['author_email']), $to) === 0) {
$log($to, 0, '自己回复自己,不发');
return [false, '自己回复自己,不发']; return [false, '自己回复自己,不发'];
} }
} }
// ---- 4. 节流 ---- // ---- 4. 节流 ----
if (fb_notify_throttled($pdo, (int)$thread['id'], $to, $reason)) { if (fb_notify_throttled($pdo, (int)$thread['id'], $to, $reason)) {
$log($to, 0, '冷却中跳过(notify.cooldown_seconds=' . (int)fb_cfg('notify.cooldown_seconds', 300) . ')');
return [false, '冷却中,跳过']; return [false, '冷却中,跳过'];
} }
// ---- 5. SMTP 就绪吗 ---- // ---- 5. SMTP 就绪吗 ----
$smtp = (array)fb_cfg('smtp', []); $smtp = (array)fb_cfg('smtp', []);
if (empty($smtp['enabled']) || trim((string)$smtp['host']) === '') { if (empty($smtp['enabled']) || trim((string)$smtp['host']) === '') {
fb_notify_log2($pdo, $thread, $to, $reason, 0, 'SMTP 未启用或未配置服务器'); $log($to, 0, 'SMTP 未启用或未配置服务器');
return [false, 'SMTP 未启用或未配置服务器']; return [false, 'SMTP 未启用或未配置服务器'];
} }
@@ -623,7 +646,7 @@ if (!function_exists('fb_notify_event')) {
// ---- 7. 发送 ---- // ---- 7. 发送 ----
list($ok, $err) = fb_smtp_send($smtp, $to, $subject, $html); list($ok, $err) = fb_smtp_send($smtp, $to, $subject, $html);
fb_notify_log2($pdo, $thread, $to, $reason, $ok ? 1 : 0, $ok ? null : $err); $log($to, $ok ? 1 : 0, $ok ? null : $err);
return [$ok, $ok ? '' : (string)$err]; return [$ok, $ok ? '' : (string)$err];
} }
} }