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, ]; }