本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/待办事宜/FlintHub_下版本待办清单.md。
FlintHub 下版本待办清单
来源:2026-09-14 全面体检(黑盒渗透 + 白盒静态审计 + 竞品对比 + 写入并发核实)
状态:V1.0.0 已发布,以下条目统一下版本处理
说明:每条含「位置 / 问题 / 改法 / 验证」,可直接作为开发工单使用
目录
一、建议的版本切分
按「风险 × 收益」把待办分成三批,避免一次性改动过大:
| 批次 | 内容 | 特点 |
|---|---|---|
| V1.0.1(可随时发) | 性能优化 2 项 + 安全加固 5 项 + 打磨 4 项 | 改动小、风险低、收益明确,适合做补丁版 |
| V1.1.0 | 前端资源优化、CSP 收紧、反代适配 | 需改动模板/构建/配置,需回归测试 |
| V1.2.0 | 最小测试集、文档改进 | 需持续投入,非一次性工作 |
如果只挑一件先做:先做第二章的性能优化——改两处代码,回帖路径直接少 4 次全表 COUNT。
二、性能优化(新发现,收益最高)
P-1 统计刷新不对称:新增走全量 COUNT,而增量机制已存在
| 项 | 内容 |
|---|---|
| 位置 | app/Models/Post.php:104、app/Models/Thread.php:116 |
| 问题 | sync 模式下,发帖/回帖调用 Settings::runtimeBuild(),它会执行 4 次全量 COUNT(reply_index / topic_index / users / blogs)并写 4 次 settings 表。<br>但删除方向(Post.php:347、Thread.php:632)用的却是 Settings::runtimeIncr() 增量机制——便宜的那条路已经写好了,只是没用在新增方向。 |
| 影响 | 每次回帖额外产生 4 次 COUNT + 4 次写,且全挤在 main_index.sqlite / business.sqlite 两个全局单点上。 |
| 改法 | ```php
// Post.php:104
- \app\Helpers\Settings::runtimeBuild();
+ \app\Helpers\Settings::runtimeIncr('total_posts');
// Thread.php:116
- \app\Helpers\Settings::runtimeBuild();
+ \app\Helpers\Settings::runtimeIncr('total_threads');
| **漂移风险** | 原设计用全量重建来校正漂移(见 `Post.php:316` 注释)。改用增量后,建议:
① 保留后台维护页的「重建统计」按钮作为人工校准入口(**该按钮已存在**,无需新增);
② 可在 `cron_trigger.php` 里挂一个低频(如每日一次)的 `runtimeBuild()` 兜底校准。 |
| **验证** | 用 `bench_write.php` 跑改前/改后对比,看 P95 与吞吐变化。 |
### P-2 `reply_index` 缺 `status` 单列索引
| 项 | 内容 |
|:---|:---|
| **位置** | `app/SplitDB/Schema.php:733-746`(`ensureMainIndex()`) |
| **问题** | `reply_index` 目前只有 `idx_reply_index_pid(pid)` 与 `idx_reply_index_uid(uid, status)`。而 `Settings::runtimeBuildSingle('total_posts')` 执行的是 `SELECT COUNT(*) FROM reply_index WHERE status = 0` —— **首列是 uid 的复合索引用不上**,退化为全表扫描。 |
| **影响** | 16 万行量级的全表扫描,且发生在 `main_index.sqlite` 上,与写入串行。 |
| **改法** | 在 `ensureMainIndex()` 内追加(放在 `CREATE TABLE` 之后,注意该文件已有「索引顺序红线」的注释):
```sql
CREATE INDEX IF NOT EXISTS idx_reply_index_status ON reply_index (status);| 备注 | 即使做了 P-1,本项仍建议保留——后台「重建统计」按钮和 cli/worker.php 仍会走 runtimeBuildSingle(),索引能让它们快起来。 |
| 验证 | 执行 EXPLAIN QUERY PLAN SELECT COUNT(*) FROM reply_index WHERE status = 0;,确认由 SCAN 变为 SEARCH ... USING COVERING INDEX。 |
三、发布阻断项(4 项)
B-1 前端静态资源零优化
| 项 | 内容 |
|---|---|
| 位置 | assets/css/modern.css(75KB)、assets/js/editor.js(83KB)、assets/css/admin.css(45KB);nginx-server.conf |
| 问题 | 自研 CSS/JS 全部未压缩;静态资源响应既无 Cache-Control/Expires,也未 gzip(仅 HTML 开了 gzip)。 |
| 影响 | 访客唯一能肉眼感知的性能问题。后端做到 3~10ms 渲染,却可能被 200KB+ 未压缩资源拖慢首屏;缺 Cache-Control 导致回访用户重复下载。 |
| 改法 | ① Nginx 对 css/js/svg 开 gzip 或 gzip_static;<br>② 加 expires 30d; + Cache-Control: public, immutable(现有 ?v=filemtime 版本参数已具备缓存击穿能力);<br>③ 对自研 CSS/JS 产出 .min 版本。 |
| 验证 | curl -I 检查 Content-Encoding: gzip 与 Cache-Control;Lighthouse 看首屏指标。 |
B-2 无自动化测试
| 项 | 内容 |
|---|---|
| 位置 | 全库无 tests/、无 PHPUnit |
| 问题 | 55,662 行 / 390 文件,升级策略为「完整包覆盖 + 7 个 migrate_* 脚本」,任何改动都缺安全网。 |
| 影响 | 数据迁移脚本一旦有误,损失不可逆 —— 这是发布后最大的实际风险。 |
| 改法 | 不要求全覆盖,优先给「改了就会出事」的部分补最小测试集:<br>① ShardRouter 的桶计算与 32→64 扩容路由;<br>② migrate_ / rebuild_ CLI 脚本的幂等性与回滚;<br>③ Permission::canView / canAttach 权限判定;<br>④ Content::sanitizeHtml 的 XSS 用例。<br>可用无 Composer 依赖的手写断言脚本,接入现有 cli/ 体系即可。 |
B-3 HTTPS 判定不支持反向代理
| 项 | 内容 |
|---|---|
| 位置 | 共 5 处:app/Core/Controller.php:79、app/Controllers/AuthController.php:539、app/Helpers/RememberMe.php:97、app/Helpers/PageCache.php:81、install.php:45,215 |
| 问题 | 统一写作 isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',不识别 X-Forwarded-Proto。 |
| 影响 | 部署在 Nginx / 宝塔 / 隧道 / CDN 之后(TLS 在反代层终止)时:Session Cookie 与 remember_token 的 Secure 标志失效、HSTS 头不发送 → 会话凭据可能在明文 HTTP 下泄露。 |
| 佐证 | app/Helpers/AuditLog.php:76 已实现 X-Forwarded-For + isTrustedProxy(),说明系统具备反代意识,此处属实现不一致。 |
| 改法 | 封装统一入口,替换 5 处调用:<br>```php
final class Request {
public static function isSecure(): bool {
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') return true;
$remote = $_SERVER['REMOTE_ADDR'] ?? '';
if (AuditLog::isTrustedProxy($remote)) {
$proto = strtolower(trim(explode(',', $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')[0]));
if ($proto === 'https') return true;
}
return false;
}
}
### B-4 CSP 形同虚设 + 版本信息外泄
| 项 | 内容 |
|:---|:---|
| **位置** | `app/Core/Controller.php:77`;Nginx `server_tokens`;PHP `expose_php` |
| **问题** | `script-src 'self' 'unsafe-eval' 'unsafe-inline'` —— 两个指令使 CSP 对脚本注入的拦截能力归零;响应头暴露 `Server: nginx/1.29.8` 与 `X-Powered-By: PHP/8.2.30`。 |
| **改法** | ① 脚本标签加 `nonce`,移除 `unsafe-eval`(Alpine.js 换 build 版即可无 eval);
② Nginx `server_tokens off;`;
③ `php.ini` 设 `expose_php = Off`。 |
| **备注** | 需同步改造模板中的 `干跑:只打印将要写入的数据量与桶命中分布,不落盘
php cli/bench_write.php --mode=hot --thread=测试帖ID --concurrency=32 --per=20
正式执行(必须显式 --yes)
php cli/bench_write.php --mode=hot --thread=测试帖ID --concurrency=32 --per=20 --yes
php cli/bench_write.php --mode=spread --thread=测试帖ID --concurrency=32 --per=20 --yes
php cli/bench_write.php --mode=id --concurrency=64 --per=100 --yes
清理压测数据
php cli/bench_write.php --cleanup --thread=测试帖ID