Files
T
yinjianm 1739f7a2f9 feat(api): 新增节点流量悬浮详情与即时自动上线同步
为 server/manage/getNodes 返回节点级今日、本月与累计流量统计,
并在节点管理页名称悬浮层展示上行、下行和合计流量。

同时为自动上线补齐单节点同步入口,在管理端保存、
批量更新以及 REST/WS 心跳后立即同步 show 状态,
避免复制节点后开启自动上线仍需等待定时任务。

另优化管理端前端 Docker 发布流程,默认仅构建 amd64,
并收敛 BuildKit 缓存导出以缩短发布时间
2026-04-28 16:51:35 +08:00

150 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 变更提案: admin-frontend-node-auto-online-immediate-sync
## 元信息
```yaml
类型: 修复
方案类型: implementation
优先级: P1
状态: 已规划
创建: 2026-04-28
```
---
## 1. 需求
### 背景
节点管理页复制节点后,新节点会保留原节点的 `auto_online` 状态但被复制接口强制设为隐藏。管理员修改新节点信息并安装节点后,即使自动上线处于开启状态,节点也不会立即显示,需要手动关闭自动上线、打开显隐、再重新开启自动上线。
代码事实:
- `ManageController::copy()` 复制节点后设置 `show = 0`
- `NodeEditorDialog` 保存会提交 `show``auto_online`,但后端 `save()` 只落库,不主动执行自动上线同步。
- `ServerAutoOnlineService::sync()` 目前只由 `sync:server-auto-online` 定时命令每 5 分钟调用。
- REST 节点心跳 `ServerService::touchNode()` 只更新在线缓存,不会触发自动上线同步;WebSocket 状态上报还绕过了 `touchNode()` 直接写缓存。
### 目标
- 自动上线开启的节点在管理端保存、开启自动上线或节点心跳上报后,立即按当前在线状态和墙检测状态同步 `show`
- 保持现有定时任务兜底能力,避免依赖管理员手动切换显隐。
- 保持墙检测自动隐藏规则不被绕过。
### 约束条件
```yaml
时间约束:
性能约束: 单节点心跳只能同步当前节点,不能触发全量扫描
兼容性约束: 保持 sync:server-auto-online 命令输出结构不变
业务约束: 自动上线仍以 available_status 和墙状态为准;未开启 auto_online 的节点不改变手动显隐
```
### 验收标准
- [ ] `ServerAutoOnlineService` 支持单节点同步,并复用定时全量同步的同一套判定逻辑。
- [ ] 管理端保存节点、开启自动上线、批量开启自动上线后,若节点当前为在线或待同步且未被墙检测否决,应立即 `show=1`
- [ ] 节点心跳上报后,若该节点开启自动上线,应立即同步 `show`,不必等待 Scheduler。
- [ ] 被墙状态或 `gfw_auto_hidden` 未恢复正常时,自动上线不会把节点重新显示。
- [ ] 单元测试覆盖单节点同步和心跳触发场景。
---
## 2. 方案
### 技术方案
`ServerAutoOnlineService` 中的自动上线判定抽出为可复用的 `syncServer(Server $server)` 单节点入口。全量定时命令继续调用 `sync()`,内部复用同一判定函数。
在两个关键触发点调用单节点同步:
- 管理端 `ManageController``save()``update()``batchUpdate()` 在节点开启 `auto_online` 后立即调用。
- `ServerService::touchNode()` 在节点心跳刷新后,如果节点开启 `auto_online`,立即调用;WebSocket 状态上报改为复用 `touchNode()`
### 影响范围
```yaml
涉及模块:
- server-auto-online: 抽取单节点同步入口,复用全量同步逻辑
- admin-server-manage: 保存/开关/批量更新自动上线时立即同步
- server-heartbeat: REST 与 WebSocket 节点心跳后触发当前节点同步
- tests: 补充自动上线单节点与心跳行为验证
预计变更文件: 5
```
### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| 节点心跳频繁导致额外数据库写入 | 中 | 只在 `auto_online=true` 时执行;状态无变化时不保存 |
| 保存接口中 `show``auto_online` 同时提交时语义冲突 | 低 | 自动上线开启时以自动同步判定为准,符合 UI 中显隐开关被托管的文案 |
| 墙检测自动隐藏被误清除 | 中 | 单节点同步复用原有 `isGfwBlocked/isGfwHeld` 判定,只在正常状态时清除 hold |
### 方案取舍
```yaml
唯一方案理由: 问题根因在后端自动上线语义只由定时任务触发;把同步能力收敛到服务层并在业务事件中调用,可以同时覆盖管理端保存、开关和节点真实上线心跳。
放弃的替代路径:
- 仅前端保存后强制提交 show=1: 会绕过离线/被墙判断,且无法覆盖节点稍后才心跳上线的场景。
- 缩短 Scheduler 间隔: 仍不是立即上线,并增加全量扫描频率。
- 复制节点时默认关闭 auto_online: 会改变复制语义,仍要求管理员额外操作。
回滚边界: 可独立回退 ServerAutoOnlineService 单节点入口、ManageController 调用点和 ServerService::touchNode 调用点;数据库结构不变。
```
---
## 3. 技术设计
### 架构设计
```mermaid
flowchart TD
A[管理端保存/开启自动上线] --> B[ManageController]
C[REST/WS 节点心跳上报] --> D[ServerService::touchNode]
B --> E[ServerAutoOnlineService::syncServer]
D --> E
F[sync:server-auto-online] --> G[ServerAutoOnlineService::sync]
G --> E
E --> H[按 available_status + GFW 状态同步 show]
```
### API 设计
不新增外部 API。现有接口保持不变:
- `POST /server/manage/save`
- `POST /server/manage/update`
- `POST /server/manage/batchUpdate`
- 节点心跳/报告接口
### 数据模型
不新增字段。
---
## 4. 核心场景
### 场景: 复制节点后自动上线立即生效
**模块**: admin-frontend / server-auto-online
**条件**: 新节点由复制产生,`show=0``auto_online=1`,节点已经心跳在线且未被墙检测否决。
**行为**: 管理员在节点管理页编辑并保存该节点。
**结果**: 后端保存后立即执行单节点自动上线同步,节点 `show=1`,前端刷新列表后显示为上线。
### 场景: 节点稍后才上线
**模块**: server-heartbeat / server-auto-online
**条件**: 节点保存时尚未心跳,`auto_online=1``show=0`
**行为**: 节点安装完成并上报心跳。
**结果**: REST 与 WebSocket 心跳都会通过 `touchNode()` 更新在线缓存并立即同步当前节点,节点不需要等待下一轮 Scheduler。
---
## 5. 验证策略
```yaml
verifyMode: test-first
reviewerFocus:
- app/Services/ServerAutoOnlineService.php 单节点与全量同步是否共用判定逻辑
- app/Services/ServerService.php 与 app/WebSocket/NodeEventHandlers.php 心跳触发是否只影响 auto_online 节点
- app/Http/Controllers/V2/Admin/Server/ManageController.php 管理端更新后的同步时机
testerFocus:
- vendor/bin/phpunit tests/Unit/ServerAutoOnlineServiceTest.php
- npm --prefix admin-frontend run build
uiValidation: none
riskBoundary:
- 不修改数据库结构
- 不修改节点列表视觉结构
- 不改变未开启 auto_online 节点的手动显隐行为
```
---
## 6. 成果设计
N/A。此任务不包含视觉产出。