首页改介绍页;列表只服务当前软件;新增UUID追踪让提交人看得到未审核反馈

用户提的两个核心问题:

一、首页不该列全部问题
  场景:这套系统是给某个软件配反馈入口、把地址贴进那软件的「意见反馈」里。
  访客总是从某个软件过来的,看别的软件的问题对他没意义。
  ⇒ / 不带 sw 时渲染介绍页(特点介绍 + 「点右上角设置按钮选软件」+ 软件卡片)
     带 sw 才是该软件的问题列表,检索/筛选/分类全在本软件范围内

二、提交后未审核,用户点不进去(真实缺陷)
  邮件里给了「点击查看您刚才提交的反馈」,但 thread.php 只在 visible=1 时
  才给看,未审核的一律 404 / 只看到「等待审核」静态页 —— 链接等于白给。
  ⇒ threads 加 track_token(32位hex = 128位熵),提交时生成:
     - 邮件里所有链接一律带 ?t=<token>(不再是裸 id)
     - 带对 token:可看完整详情、可回复,哪怕未公开/已关闭
     - 没 token 或 token 错:对外一律 404,不泄露存在性
     - 公开列表仍用 id 链接(公开内容谁都能看,用于避免重复提交)

实现细节:
- threads.track_token + 唯一索引;幂等迁移,存量数据自动回填
- fb_track_new_token / fb_track_token_ok(hash_equals 防时序探测)
  / fb_thread_viewable / fb_thread_link(thread, personal)
- thread.php 权限从 fb_thread_public 改为 fb_thread_viewable
- 带 token 看自己未公开的反馈时,顶部给「私人跟踪链接」提示条
- 回复表单与回复后跳转都保留 token,否则回复完就失去访问权
- 提交完成后直接带 token 跳详情页,不再只给静态等待页
- close 类邮件补上「查看这条反馈」按钮(原来完全没链接)
- 顶栏「全部问题」改为「问题列表」且只在选中软件后显示
- 详情/提交页「返回问题列表」按所属软件返回,不再回介绍页
- new.php 无 sw 时不再默认落到第一个软件,改为让用户先选
  (默默替选会让反馈进错软件、通知发错人)

验证:首页 0 条问题卡片且未泄露任何标题、三个软件入口齐全;
未公开#4 无 token 404 / 错 token 404 / 正确 token 200 且显示完整详情
(不再卡在「等待审核」)、提示条与表单 token 都在;
已公开#2 无 token 可看;5 类邮件(回执/通过/回复/状态/关闭)链接全带 token;
两套回归各 30+ 项断言全过。
This commit is contained in:
2026-11-03 09:54:12 +08:00
parent 2f28b1e3a9
commit 8bcee46b8a
9 changed files with 411 additions and 32 deletions
+43 -7
View File
@@ -8,7 +8,11 @@ $pdo = fb_db();
$err = '';
$atts = []; // 校验失败时保留已上传的附件
/* ---- 软件:URL 带 sw 就用它,否则落到默认软件 ---- */
/* ---- 软件:URL 带 sw 就用它 ----
* 🔴 不再「没带 sw 就默认落到第一个软件」:
* 首页现在是介绍页,直接访问 new.php 没有软件上下文,
* 默默替他选一个软件会让反馈进错地方、发错通知邮件。
* 没有软件时给一句引导,让用户先选软件。 */
$swList = fb_softwares(true);
$sw = null;
$swKey = isset($_GET['sw']) ? (string)$_GET['sw'] : '';
@@ -16,8 +20,32 @@ if ($swKey !== '') {
$sw = fb_software($swKey);
if (!$sw || (int)$sw['enabled'] !== 1) { $sw = null; }
}
if ($sw === null && $swList) { $sw = $swList[0]; }
$swId = $sw ? (int)$sw['id'] : 0;
if ($sw === null) {
fb_layout_head('请先选择软件', ['robots' => 'noindex,follow']);
?>
<div class="empty">
<h2 style="margin:0 0 12px">请先选择要给哪个软件提反馈</h2>
<p class="muted" style="margin:0 0 16px">不同软件的问题列表是分开的,请先选一个。</p>
<div class="home-sw-list" style="max-width:420px;margin:0 auto 18px">
<?php foreach ($swList as $s): ?>
<?php
list($c) = fb_software_colors($s);
if ($c === '') { $c = '#0057B8'; }
?>
<a class="home-sw" href="new.php?sw=<?php echo e(urlencode((string)$s['slug'])); ?>"
style="--swc:<?php echo e($c); ?>">
<span class="home-sw-nm"><?php echo e((string)$s['name']); ?></span>
<span class="home-sw-go">去反馈 →</span>
</a>
<?php endforeach; ?>
</div>
<p><a class="btn" href="index.php">返回首页</a></p>
</div>
<?php
fb_layout_foot();
exit;
}
$swId = (int)$sw['id'];
$cats = fb_categories($swId);
$catKeys = [];
@@ -97,7 +125,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
* 状态根本反映不出"还没公开"这个事实。 */
$visible = fb_moderation_on() ? 0 : 1;
$initStatus = $visible === 1 ? 'open' : 'hidden';
$ins = $pdo->prepare('INSERT INTO threads(title, body, category, category_id, software_id, author_name, author_email, author_phone, status, visible, reply_count, created_at, updated_at, ip_hash) VALUES(?,?,?,?,?,?,?,?,?,?,0,?,?,?)');
/* track_token:提交人的私人追踪钥匙,只发到他的邮箱。
* 有了它,即使这条还没审核通过,他也能凭链接看到进度。 */
$trackToken = fb_track_new_token();
$ins = $pdo->prepare('INSERT INTO threads(title, body, category, category_id, software_id, author_name, author_email, author_phone, status, visible, reply_count, created_at, updated_at, ip_hash, track_token) VALUES(?,?,?,?,?,?,?,?,?,?,0,?,?,?,?)');
$ins->execute([
$form['title'], $form['body'],
$curCat ? (string)$curCat['slug'] : 'other', // 老列留个值,便于回退与检索
@@ -105,7 +136,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$swId > 0 ? $swId : null,
$form['name'], $form['email'],
$form['phone'] !== '' ? $form['phone'] : null,
$initStatus, $visible, $now, $now, fb_ip_hash(),
$initStatus, $visible, $now, $now, fb_ip_hash(), $trackToken,
]);
$newId = (int)$pdo->lastInsertId();
fb_attach_save($pdo, $newId, null, $vList);
@@ -138,7 +169,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 发信失败绝不能让用户提交失败 —— 反馈已经存库了
}
fb_redirect('thread.php?id=' . $newId . '&submitted=1');
/* 提交完成后直接带 token 跳到详情页:
* 这样用户当场就能看到自己刚提交的那条(哪怕还没审核),
* 而不是只看到一个「等待审核」的静态提示页。 */
fb_redirect('thread.php?id=' . $newId
. '&t=' . rawurlencode($trackToken)
. '&submitted=1');
}
}
}
@@ -146,7 +182,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
fb_layout_head('我要反馈', ['robots' => 'noindex,follow', 'software' => $sw]);
?>
<div class="crumbs"><a href="index.php">← 返回问题列表</a></div>
<div class="crumbs"><a href="<?php echo e(fb_software_list_url($sw)); ?>">← 返回问题列表</a></div>
<section class="narrow">
<h1>我要反馈</h1>