邮件:带私人链接时统一加「这个链接只有您能打开,请不要转发」提醒

用户要求:每次发这种链接,都要在链接上/下给他一个提示 ——
这个链接是独有的,不要转给别人、不要让别人看到,是跟踪反馈的唯一凭证。

实现:在 fb_mail_layout() 里加 $privNoteHtml,
layout 传 personal=true 才渲染,位置在「按钮之后、纯文本链接之前」。
这样只要新增邮件是发给提交人的,标一下就自动带上,不会漏。

覆盖 7 处(全部发给提交人的):
  回复通知 / 状态变更 / 审核通过 / 转为不公开 / 关闭说明 / 提交回执 / 兜底
不覆盖 1 处(管理员通知,按钮指向后台,不是私人凭证)

提醒文案(斟酌过,讲清缘由而不只是禁止):
  标题:这个链接只有您能打开,请不要转发
  正文:它等同于您在这条反馈上的身份凭证 —— 谁拿到这个链接,
        谁就能查看和处理这条反馈。请只自己使用,不要发给别人,
        也不要发到公开的地方。如果不需要继续跟踪,删掉这封邮件即可。
  → 说清「是什么」而不是只喊「别外传」,用户才会真的照做;
    补一句退路(删掉邮件即可),避免像是免责声明。

验证:7 类邮件全部含提醒且都在页脚之前;管理员通知不含;
像素级检查确认视觉顺序为「蓝色按钮 y=719 → 黄色提醒块 y=874..982」,
即提醒在按钮下方、正文区内,不是埋在邮件底部。
This commit is contained in:
2026-11-03 10:08:49 +08:00
parent 8763e8e17f
commit 8b3e49e091
+45 -1
View File
@@ -367,6 +367,36 @@ if (!function_exists('fb_mail_layout')) {
. fb_mail_esc($link) . '</span></p>'; . fb_mail_esc($link) . '</span></p>';
} }
/* ---- 私人链接安全提醒 ----
*
* 用户 2026-11-03:「每次给他发这种链接的时候,都要在链接下面或上面
* 给他一个提示:这个链接是你独有的,不要转给别人。」
*
* 为什么必须提醒:这个链接里带 track_token,它就是**唯一凭证**——
* 谁拿到这串东西,谁就能看(甚至回复)这条反馈。而本站不做登录,
* 我们没法事后判断"是本人还是捡到链接的人"。
* 只要邮件里带了私人链接(personal=true),这条提醒就必须出现。
*
* 措辞取舍:
* - 说清"是什么"(只有你能看的入口),而不只是"别外传"——只讲禁止,
* 用户会觉得是免责声明;讲清缘由他才会真的照做。
* - 不让用户恐慌:补一句"不需要的话可以删掉这封邮件",给出退路。
* - 管理员后台通知里的链接不是私人凭证,不加这段(personal=false)。 */
$privNoteHtml = '';
if (!empty($o['personal'])) {
$privNoteHtml = '<table role="presentation" cellpadding="0" cellspacing="0" border="0"'
. ' width="100%" style="margin:0 0 18px">'
. '<tr><td style="padding:11px 14px;background:#fff8e6;'
. 'border-left:3px solid #d99b1f;border-radius:6px">'
. '<p style="margin:0 0 5px;font-size:13px;font-weight:600;color:#6b5313">'
. '这个链接只有您能打开,请不要转发</p>'
. '<p style="margin:0;font-size:12.5px;line-height:1.7;color:#7a6420">'
. '它等同于您在这条反馈上的身份凭证 —— 谁拿到这个链接,谁就能查看和处理这条反馈。'
. '请只自己使用,不要发给别人,也不要发到公开的地方。'
. '如果不需要继续跟踪,删掉这封邮件即可。</p>'
. '</td></tr></table>';
}
// ---- 收件人问候 ---- // ---- 收件人问候 ----
$introHtml = ''; $introHtml = '';
if ($intro !== '') { if ($intro !== '') {
@@ -420,8 +450,10 @@ if (!function_exists('fb_mail_layout')) {
. 'color:#1b2430">' . fb_mail_esc($title) . '</h1></td></tr>' . 'color:#1b2430">' . fb_mail_esc($title) . '</h1></td></tr>'
// 正文 // 正文
// 顺序:按钮 → 私人链接提醒 → 纯文本兜底链接。
// 提醒要贴在按钮旁边(同一屏可见),不能埋到邮件最底部。
. '<tr><td style="padding:16px 32px 8px">' . '<tr><td style="padding:16px 32px 8px">'
. $introHtml . $body . $btnHtml . $linkHtml . $introHtml . $body . $btnHtml . $privNoteHtml . $linkHtml
. '</td></tr>' . '</td></tr>'
// 页脚 // 页脚
@@ -914,6 +946,7 @@ if (!function_exists('fb_notify_render')) {
'blocks' => $blocks, 'blocks' => $blocks,
'button' => ['text' => '查看并回复', 'url' => $url], 'button' => ['text' => '查看并回复', 'url' => $url],
'link' => $url, 'link' => $url,
'personal' => true, // 发给提交人:带 token 的私人链接,需提醒勿外传
'recipient' => $to, 'recipient' => $to,
]; ];
} }
@@ -972,6 +1005,7 @@ if (!function_exists('fb_notify_render')) {
// 下载按钮之外,始终保留「查看这条反馈」的原文链接 // 下载按钮之外,始终保留「查看这条反馈」的原文链接
'link' => ($btnUrl === $url) ? $url 'link' => ($btnUrl === $url) ? $url
: ('下载地址:' . $dlUrl . "\n" . '查看反馈:' . $url), : ('下载地址:' . $dlUrl . "\n" . '查看反馈:' . $url),
'personal' => true, // 发给提交人
'recipient' => $to, 'recipient' => $to,
]; ];
} }
@@ -994,6 +1028,7 @@ if (!function_exists('fb_notify_render')) {
], ],
'button' => ['text' => '查看并跟踪这条反馈', 'url' => $url], 'button' => ['text' => '查看并跟踪这条反馈', 'url' => $url],
'link' => $url, 'link' => $url,
'personal' => true, // 发给提交人
'recipient' => $to, 'recipient' => $to,
]; ];
} }
@@ -1022,6 +1057,7 @@ if (!function_exists('fb_notify_render')) {
'blocks' => $blocks, 'blocks' => $blocks,
'button' => ['text' => '查看这条反馈', 'url' => $url], 'button' => ['text' => '查看这条反馈', 'url' => $url],
'link' => $url, 'link' => $url,
'personal' => true, // 发给提交人(转为不公开)
'recipient' => $to, 'recipient' => $to,
]; ];
} }
@@ -1050,6 +1086,10 @@ if (!function_exists('fb_notify_render')) {
? '如果上面的说明没能解决您的疑问,直接回复本邮件即可,我们继续跟进;也可以发到 ' . $mail . '。' ? '如果上面的说明没能解决您的疑问,直接回复本邮件即可,我们继续跟进;也可以发到 ' . $mail . '。'
: '如果上面的说明没能解决您的疑问,直接回复本邮件即可,我们继续跟进。'), : '如果上面的说明没能解决您的疑问,直接回复本邮件即可,我们继续跟进。'),
]; ];
/* 这条已经「已关闭 + 不公开」,不带 token 的链接打不开 ——
* 必须给带 token 的私人链接,用户才能回来看上下文、继续补充。
* 注意:button 是 layout 层属性,不是 block 类型。 */
$closeBtnUrl = fb_thread_link($thread, true);
$layout = [ $layout = [
'software' => $sw, 'software' => $sw,
@@ -1058,6 +1098,8 @@ if (!function_exists('fb_notify_render')) {
'title' => '关于您反馈的问题,我们的说明', 'title' => '关于您反馈的问题,我们的说明',
'intro' => ($name !== '' ? $name . ',您好:' : '您好:'), 'intro' => ($name !== '' ? $name . ',您好:' : '您好:'),
'blocks' => $blocks, 'blocks' => $blocks,
'button' => ['text' => '查看这条反馈', 'url' => $closeBtnUrl],
'personal' => true, // 发给提交人(关闭说明)
'recipient' => $to, 'recipient' => $to,
]; ];
} }
@@ -1131,6 +1173,7 @@ if (!function_exists('fb_notify_render')) {
'blocks' => $blocks, 'blocks' => $blocks,
'button' => ['text' => '查看这条反馈', 'url' => $url], 'button' => ['text' => '查看这条反馈', 'url' => $url],
'link' => $url, 'link' => $url,
'personal' => true, // 发给提交人(回执)
'recipient' => $to, 'recipient' => $to,
]; ];
} }
@@ -1146,6 +1189,7 @@ if (!function_exists('fb_notify_render')) {
'blocks' => [['type' => 'text', 'text' => '您关注的反馈有更新。']], 'blocks' => [['type' => 'text', 'text' => '您关注的反馈有更新。']],
'button' => ['text' => '查看详情', 'url' => $url], 'button' => ['text' => '查看详情', 'url' => $url],
'link' => $url, 'link' => $url,
'personal' => true, // 兜底同样是发给提交人的
'recipient' => $to, 'recipient' => $to,
]; ];
} }