本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:暂存20260913/docs/任务调度独立模块改造计划 (2).md。
SoSite 1.0 — 任务调度独立模块改造计划
文档状态:待确认(2026-08-13)
目标:将「队列任务调度」从「基本信息」设置中提取为独立后台模块,与「搜索设置」并列
现状基线:P8 已完成 queue_mode(sync/cron/cli)业务分支 + cron_trigger.php;本次仅做界面/导航/交互重构,业务逻辑复用
一、需求评估与现状勘察(已读代码确认)
| 勘察项 | 现状 | 结论 |
|---|---|---|
| 侧边栏导航 | app/Views/admin/_sidebar.php L14:「搜索设置」是「站点设置」组最后一项 | 在其后新增「任务调度」菜单项 |
| 路由注册 | index.php L116-125:`/admin/settings/basic | display |
| 设置保存 | SettingsController::basic() L23-27 $keys 含 queue_mode;settings_basic.php L90+ 已有「队列任务调度」区块 | 移除 basic 页区块与保存键,迁至独立页 |
| 队列统计 | Queue::countByStatus(int $queueIndex) 返回单队列 {status: count};Queue::QUEUE_COUNT = 3 | 面板需聚合 3 队列(pending+processing / done / failed) |
| 图标体系 | TemplateCompiler::icon($name) 内置映射:'sync'/'refresh' = 、'clock'/'time' = 、'settings' =  | 菜单图标用 sync(🔄)或 clock(⏳),无需新增图标资源 |
| 手动触发 | 现仅有 cron_trigger.php(web 入口)与 cli/worker.php(CLI) | 建议新增页面内「立即处理」按钮(见三.4) |
二、改造执行计划表(待用户确认后串行实施)
| # | 任务 | 交付物 | 验证方式 |
|---|---|---|---|
| T1 | index.php 注册路由:GET/POST /admin/settings/queue → AdminSettingsController::queueSettings | 路由 | 访问 URL 200 且渲染新页 |
| T2 | SettingsController:新增 queueSettings()(保存 queue_mode + 手动触发 action + 聚合队列统计);basic() 的 $keys 移除 queue_mode | 控制器 | POST 保存后 settings 表更新;basic 页不再保存该项 |
| T3 | app/Views/admin/queue_settings.php:独立页面(标题区 / 三模式卡片 / 队列面板 / 防呆弹窗 / 手动触发按钮) | 视图 | 页面结构与文案按需求逐条核对 |
| T4 | settings_basic.php:删除「队列任务调度」区块(迁走);_sidebar.php:搜索设置下方新增「任务调度」菜单项 | 视图 | 导航出现新项、basic 页无残留区块 |
| T5 | 同步 update/ 镜像 + 更新 CHANGELOG.md | 交付 | fc 比对一致;php -l 全过 |
三、详细设计
3.1 路由(T1)— index.php
在 L125 /settings/search 后追加:
$r->get('/settings/queue', [AdminSettingsController::class, 'queueSettings']);
$r->post('/settings/queue', [AdminSettingsController::class, 'queueSettings']);3.2 控制器(T2)— SettingsController::queueSettings()
参照 searchSettings() 模式:
public function queueSettings()
{
$settingModel = new \app\Models\Setting();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
Csrf::verifyOrDie($_POST['csrf'] ?? '');
$action = $_POST['action'] ?? '';
// 保存模式
if ($action === 'save_mode') {
$mode = $_POST['queue_mode'] ?? 'sync';
if (!in_array($mode, ['sync', 'cron', 'cli'], true)) $mode = 'sync';
$settingModel->setValue('queue_mode', $mode);
Settings::buildCache();
\app\Helpers\AuditLog::log('admin_action', 'setting', 0, "设置队列调度模式: {$mode}");
$this->redirect('/admin/settings/queue?success=1');
}
// 立即处理(手动触发,见 3.4)
if ($action === 'flush') {
$processed = $this->flushQueueNow();
$this->redirect('/admin/settings/queue?msg=flushed_' . $processed);
}
}
$msg = $_GET['success'] ?? '' ? '设置已保存' : '';
if (strpos($_GET['msg'] ?? '', 'flushed_') === 0) {
$msg = '✅ 已立即处理 ' . (int)substr($_GET['msg'], 8) . ' 个待处理任务';
}
$allSettings = $settingModel->getAll();
$this->view('admin/queue_settings', [
'settings' => $allSettings,
'queue_stats' => $this->queueStatsAggregate(), // 聚合三队列
'msg' => $msg,
'error' => $_GET['error'] ?? '',
'__nav_active' => 'settings_queue',
]);
}聚合统计(面板数据源):遍历 3 队列 Queue::countByStatus($i) 汇总:
private function queueStatsAggregate(): array
{
$pending = 0; $done = 0; $failed = 0;
for ($i = 0; $i < Queue::QUEUE_COUNT; $i++) {
$map = Queue::countByStatus($i);
$pending += (int)($map['pending'] ?? 0) + (int)($map['processing'] ?? 0);
$done += (int)($map['done'] ?? 0);
$failed += (int)($map['failed'] ?? 0);
}
return ['pending' => $pending, 'done' => $done, 'failed' => $failed];
}3.3 视图(T3)— app/Views/admin/queue_settings.php
按需求文案逐条渲染(标题区 / 三模式卡片 / 队列面板 / 防呆弹窗 / 手动触发),文案严格采用需求原文:
┌─ 任务调度引擎 ─────────────────────────────────────────────┐
│ 控制发帖、回帖、搜索重建等后台任务的执行方式。选择最适合您 │
│ 服务器环境的模式,以平衡"实时性"与"服务器性能"。 │
│ │
│ (模式选择:单选卡片 / 下拉) │
│ ○ 模式一【同步直写】(推荐) │
│ 适用场景:仅需 PHP 环境,无需额外配置,适合个人测试或 │
│ 廉价虚拟主机。 │
│ 行为特征:发帖/回帖成功后,页面等待搜索索引更新完毕后 │
│ 返回结果。数据 100% 实时,用户体验极致无延迟。 │
│ 技术代价:在极高并发下,可能会轻微增加页面响应时间。 │
│ ○ 模式二【Cron 定时触发】 │
│ 适用场景:拥有服务器 Crontab 权限,但无法常驻 PHP 进程 │
│ 的环境。 │
│ 行为特征:发帖后任务入队(入 SQLite),由您设置的定时 │
│ 任务(如每分钟触发一次)批量处理索引更新。系统 │
│ 会在后台默认保留 1 分钟的"最终一致性"窗口。 │
│ 配置指引:[提示框] * * * * * php /网站根目录/cli/worker.php │
│ --once >> /dev/null 2>&1 │
│ ○ 模式三【CLI 常驻消费】 │
│ 适用场景:拥有 Linux VPS 或独立服务器,支持长期后台进程 │
│ (依赖 Supervisor)。 │
│ 行为特征:极致的异步处理。发帖后任务秒入队列,由后台持续 │
│ 运行的 worker.php 进程实时消费。页面响应速度 │
│ 最快。 │
│ 配置指引:此模式需要安装 supervisor,并启动 php │
│ cli/worker.php 进程。 │
│ │
│ ─────────── 队列面板 ─────────── │
│ ⏳ 待处理任务:N 个 | ✅ 已完成任务:M 个 | 📦 当前模式:XXX │
│ [立即处理待处理任务] [保存配置] │
└─────────────────────────────────────────────────────────────┘技术要点:
- 模式选择:需求要求"留空/下拉选框形式",采用与 basic 页一致的
<select name="queue_mode">+ Alpinex-model联动(复用nginx-snippet/maintenance-box等既有样式类,保持一致观感) - 防呆弹窗:
<form x-on:submit="if(!confirm('切换至异步模式后,原已产生的旧任务将由新模式接管,不会丢失。')) return false">——仅当从 sync 切到 cron/cli 时提示(可用 Alpine 判断当前值 + 新值) - 队列面板:
queue_stats聚合数据渲染;📦 当前模式由$settings['queue_mode'] ?? 'sync'映射为中文(同步直写 / Cron 定时触发 / CLI 常驻消费)
3.4 手动触发按钮(用户征询项,我的建议:增加)
需求问"是否也可以增加手动点击"。建议增加,理由:
- cron/cli 模式下管理员改完配置后希望立即验证,不必等 cron 或手动 ssh 起 worker;
- 复用 worker 同构 handler,实现成本低(约 20 行)。
实现:queueSettings() 的 flush action 调用 flushQueueNow()——对 3 队列各 QueueConsumer::drain(0) 一次(含文件锁防并发,逻辑与 cron_trigger.php 一致):
private function flushQueueNow(): int
{
$lock = fopen(\app\SplitDB\ShardRouter::dataPath() . '/lock/manual_flush.lock', 'c');
if (!$lock || !flock($lock, LOCK_EX | LOCK_NB)) return 0;
$handler = function (string $type, $data, int $taskId): void {
$payload = is_array($data) ? $data : [];
if ($type === 'rebuild_search' && !empty($payload['topic_id'])) {
\app\Helpers\Search::indexThread((int)$payload['topic_id']);
} elseif ($type === 'stats') {
\app\Helpers\Settings::runtimeBuild();
}
};
$total = 0;
for ($i = 0; $i < Queue::QUEUE_COUNT; $i++) {
$total += (new QueueConsumer(Queue::getQueueDb($i), $handler, 300, 3))->drain(0);
}
flock($lock, LOCK_UN); @fclose($lock);
\app\Helpers\AuditLog::log('admin_action', 'queue', 0, "手动处理队列任务 {$total} 个");
return $total;
}3.5 导航与清理(T4)
_sidebar.php搜索设置(L14)后新增:
```html
<a href="/admin/settings/queue" class="sub-link <?php echo ($this->getData('__nav_active') ?? '') === 'settings_queue' ? 'active' : ''; ?>"><?php echo $this->icon('sync', 14); ?> 任务调度</a>
```
(图标:sync/refresh(🔄)或 clock(⏳),均已在 icon 映射内)
settings_basic.php:删除「队列任务调度」区块(含$qmodes定义与下拉框),恢复为纯基本信息页SettingsController::basic():$keys移除'queue_mode'(避免 basic 页覆盖独立页配置)
3.6 交付与回滚点
- 配置存储不变:
settings.queue_mode(值 sync/cron/cli),无迁移需求(老库 get 兜底) - 若用户不想要独立模块,回滚 = 还原 4 个文件(index.php 路由、SettingsController、两个视图)
四、交付清单
| 文件 | 操作 |
|---|---|
index.php | ✏️ 改(+2 行路由) |
app/Controllers/Admin/SettingsController.php | ✏️ 改(+queueSettings / queueStatsAggregate / flushQueueNow;basic 去 queue_mode) |
app/Views/admin/queue_settings.php | 🆕 新建(独立页) |
app/Views/admin/_sidebar.php | ✏️ 改(+菜单项) |
app/Views/admin/settings_basic.php | ✏️ 改(删队列调度区块) |
update/ 全量同步 + CHANGELOG.md 追加 | 📦 交付 |
五、验收流程(用户侧)
- 后台侧边栏「站点设置」组:搜索设置下方出现「任务调度」菜单项(🔄 图标);
- 点击进入
/admin/settings/queue:标题区文案、三模式卡片、队列面板齐全; - 切换「Cron 定时触发」→ 弹防呆确认 → 保存后提示框显示 crontab 命令;队列面板显示当前模式;
- 发帖(cron 模式)→ 队列面板「待处理任务 +1」→ 点「立即处理」→ 任务清空、搜索可见;
- 「基本信息」页不再含队列调度区块。
六、备注
cron_trigger.php(P8 产物)保留不动:虚拟主机无 crontab 权限时仍可作 web 触发备选(本页 Cron 指引按需求显示 crontab 命令,以cli/worker.php --once为主,另在提示框附注 cron_trigger.php 备选一行)。- 全部改动文件
UTF-8 without BOM + LF,php -l校验;自测脚本用后即删。