query('SELECT COUNT(*) FROM attachments')->fetchColumn(); $gone = 0; if ($days > 0) { $gone = fb_attach_cleanup($pdo, $days, 500); } echo "附件:共 {$kept} 个,已清理超过 {$days} 天的附件 {$gone} 个(剩余 " . (int)$pdo->query('SELECT COUNT(*) FROM attachments')->fetchColumn() . " 个,占用 " . fb_size_h(fb_attach_total_bytes($pdo)) . ")。\n"; /* ---------------- 1a. 清理「待入库」孤儿登记 ---------------- * 上传成功但一直没提交表单的附件,会在 pending_attachments 里留下登记。 * 超过 2 小时仍没转正的,清掉登记(media.php 之后就不再认这些键)。 * ⚠️ 只删登记表记录;OSS 上对应的孤儿对象由保留期清理策略另行处理。 */ $pgone = fb_attach_pending_gc(7200); if ($pgone > 0) { echo "待入库登记:清理 {$pgone} 条过期未提交记录。\n"; } /* ---------------- 1b. 附件存储凭证自检 ---------------- * 顺手确认一次「能不能从 ECS 实例 RAM 角色拿到上传凭证」:角色名写错、 * 角色没授予实例、元数据服务被网络策略挡住时,这里会直接说明原因, * 比等用户上传失败再来排查容易得多。 */ if (fb_oss_enabled()) { $cs = fb_oss_cred_status(); $line = '附件存储凭证:' . $cs['label'] . '(AK 尾号 ' . ($cs['ak_tail'] !== '' ? '…' . $cs['ak_tail'] : '无') . ')'; if ($cs['has_token']) { $line .= ',已取得临时安全令牌'; } if ($cs['expire'] > 0) { $line .= ',有效期至 ' . date('Y-m-d H:i', $cs['expire']); } if (!$cs['ok']) { $line .= ' ⚠️ 取不到凭证:' . ($cs['error'] !== '' ? $cs['error'] : '未知原因'); } echo $line . "。\n"; // 连通性自检:能否真正访问所配置的桶(内网 endpoint 跨地域时会在这里暴露) $probe = fb_oss_probe(); echo '桶连通性:' . ($probe['ok'] ? '✅ ' : '❌ ') . $probe['host'] . '(' . ($probe['internal'] ? '内网' : '公网') . ' Endpoint,HTTP ' . $probe['status'] . ')— ' . $probe['detail'] . "。\n"; if (!$probe['ok'] && $probe['internal'] && $probe['status'] === 0) { echo " ↳ 提示:内网 Endpoint 只有与桶【同地域】的阿里云 ECS 能访问。\n" . " 若本机与桶不同地域,请到后台「站点设置」关闭『使用内网 Endpoint』,或改用同地域的桶。\n"; } } else { echo "附件存储未启用(请在 config.php 的 oss 段填写 endpoint / bucket / instance_role 并置 enabled = true)。\n"; } /* ---------------- 2. 通知邮件补发 ---------------- */ $smtp = (array)fb_cfg('smtp'); if (empty($smtp['enabled'])) { echo "SMTP 未启用,跳过邮件补发。\n"; exit; } $limit = 50; $rows = $pdo->query( 'SELECT * FROM notify_log WHERE ok = 0 AND tries < 5 ORDER BY id ASC LIMIT ' . $limit )->fetchAll(); $sent = 0; $fail = 0; foreach ($rows as $n) { $st = $pdo->prepare('SELECT * FROM threads WHERE id = ?'); $st->execute([(int)$n['thread_id']]); $thread = $st->fetch(); if (!$thread) { continue; } $st = $pdo->prepare('SELECT * FROM replies WHERE id = ?'); $st->execute([(int)$n['reply_id']]); $reply = $st->fetch(); if (!$reply) { continue; } $isAdmin = !empty($reply['is_admin']); $subject = '【' . fb_cfg('site_name') . '】有人回复了你的问题:' . fb_excerpt($thread['title'], 40); $html = fb_notify_html($thread, $reply, $isAdmin); list($ok, $err) = fb_smtp_send($smtp, (string)$n['to_email'], $subject, $html); $up = $pdo->prepare('UPDATE notify_log SET ok = ?, tries = tries + 1, err = ? WHERE id = ?'); $up->execute([$ok ? 1 : 0, $ok ? null : $err, (int)$n['id']]); if ($ok) { $sent++; } else { $fail++; } } echo "邮件:成功 {$sent} 封,失败 {$fail} 封(最多处理 {$limit} 条待发记录)。\n";