diff --git a/lib/mailer.php b/lib/mailer.php
index b6eca0b..b2867fa 100644
--- a/lib/mailer.php
+++ b/lib/mailer.php
@@ -367,6 +367,36 @@ if (!function_exists('fb_mail_layout')) {
. fb_mail_esc($link) . '
';
}
+ /* ---- 私人链接安全提醒 ----
+ *
+ * 用户 2026-11-03:「每次给他发这种链接的时候,都要在链接下面或上面
+ * 给他一个提示:这个链接是你独有的,不要转给别人。」
+ *
+ * 为什么必须提醒:这个链接里带 track_token,它就是**唯一凭证**——
+ * 谁拿到这串东西,谁就能看(甚至回复)这条反馈。而本站不做登录,
+ * 我们没法事后判断"是本人还是捡到链接的人"。
+ * 只要邮件里带了私人链接(personal=true),这条提醒就必须出现。
+ *
+ * 措辞取舍:
+ * - 说清"是什么"(只有你能看的入口),而不只是"别外传"——只讲禁止,
+ * 用户会觉得是免责声明;讲清缘由他才会真的照做。
+ * - 不让用户恐慌:补一句"不需要的话可以删掉这封邮件",给出退路。
+ * - 管理员后台通知里的链接不是私人凭证,不加这段(personal=false)。 */
+ $privNoteHtml = '';
+ if (!empty($o['personal'])) {
+ $privNoteHtml = ''
+ . '| '
+ . ' '
+ . '这个链接只有您能打开,请不要转发 '
+ . ''
+ . '它等同于您在这条反馈上的身份凭证 —— 谁拿到这个链接,谁就能查看和处理这条反馈。'
+ . '请只自己使用,不要发给别人,也不要发到公开的地方。'
+ . '如果不需要继续跟踪,删掉这封邮件即可。 '
+ . ' |
';
+ }
+
// ---- 收件人问候 ----
$introHtml = '';
if ($intro !== '') {
@@ -420,8 +450,10 @@ if (!function_exists('fb_mail_layout')) {
. 'color:#1b2430">' . fb_mail_esc($title) . ''
// 正文
+ // 顺序:按钮 → 私人链接提醒 → 纯文本兜底链接。
+ // 提醒要贴在按钮旁边(同一屏可见),不能埋到邮件最底部。
. '| '
- . $introHtml . $body . $btnHtml . $linkHtml
+ . $introHtml . $body . $btnHtml . $privNoteHtml . $linkHtml
. ' |
'
// 页脚
@@ -914,6 +946,7 @@ if (!function_exists('fb_notify_render')) {
'blocks' => $blocks,
'button' => ['text' => '查看并回复', 'url' => $url],
'link' => $url,
+ 'personal' => true, // 发给提交人:带 token 的私人链接,需提醒勿外传
'recipient' => $to,
];
}
@@ -972,6 +1005,7 @@ if (!function_exists('fb_notify_render')) {
// 下载按钮之外,始终保留「查看这条反馈」的原文链接
'link' => ($btnUrl === $url) ? $url
: ('下载地址:' . $dlUrl . "\n" . '查看反馈:' . $url),
+ 'personal' => true, // 发给提交人
'recipient' => $to,
];
}
@@ -994,6 +1028,7 @@ if (!function_exists('fb_notify_render')) {
],
'button' => ['text' => '查看并跟踪这条反馈', 'url' => $url],
'link' => $url,
+ 'personal' => true, // 发给提交人
'recipient' => $to,
];
}
@@ -1022,6 +1057,7 @@ if (!function_exists('fb_notify_render')) {
'blocks' => $blocks,
'button' => ['text' => '查看这条反馈', 'url' => $url],
'link' => $url,
+ 'personal' => true, // 发给提交人(转为不公开)
'recipient' => $to,
];
}
@@ -1050,6 +1086,10 @@ if (!function_exists('fb_notify_render')) {
? '如果上面的说明没能解决您的疑问,直接回复本邮件即可,我们继续跟进;也可以发到 ' . $mail . '。'
: '如果上面的说明没能解决您的疑问,直接回复本邮件即可,我们继续跟进。'),
];
+ /* 这条已经「已关闭 + 不公开」,不带 token 的链接打不开 ——
+ * 必须给带 token 的私人链接,用户才能回来看上下文、继续补充。
+ * 注意:button 是 layout 层属性,不是 block 类型。 */
+ $closeBtnUrl = fb_thread_link($thread, true);
$layout = [
'software' => $sw,
@@ -1058,6 +1098,8 @@ if (!function_exists('fb_notify_render')) {
'title' => '关于您反馈的问题,我们的说明',
'intro' => ($name !== '' ? $name . ',您好:' : '您好:'),
'blocks' => $blocks,
+ 'button' => ['text' => '查看这条反馈', 'url' => $closeBtnUrl],
+ 'personal' => true, // 发给提交人(关闭说明)
'recipient' => $to,
];
}
@@ -1131,6 +1173,7 @@ if (!function_exists('fb_notify_render')) {
'blocks' => $blocks,
'button' => ['text' => '查看这条反馈', 'url' => $url],
'link' => $url,
+ 'personal' => true, // 发给提交人(回执)
'recipient' => $to,
];
}
@@ -1146,6 +1189,7 @@ if (!function_exists('fb_notify_render')) {
'blocks' => [['type' => 'text', 'text' => '您关注的反馈有更新。']],
'button' => ['text' => '查看详情', 'url' => $url],
'link' => $url,
+ 'personal' => true, // 兜底同样是发给提交人的
'recipient' => $to,
];
}