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

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

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

6.5 KiB
Raw Blame History

变更提案: admin-frontend-node-auto-online-immediate-sync

元信息

类型: 修复
方案类型: implementation
优先级: P1
状态: 已规划
创建: 2026-04-28

1. 需求

背景

节点管理页复制节点后,新节点会保留原节点的 auto_online 状态但被复制接口强制设为隐藏。管理员修改新节点信息并安装节点后,即使自动上线处于开启状态,节点也不会立即显示,需要手动关闭自动上线、打开显隐、再重新开启自动上线。

代码事实:

  • ManageController::copy() 复制节点后设置 show = 0
  • NodeEditorDialog 保存会提交 showauto_online,但后端 save() 只落库,不主动执行自动上线同步。
  • ServerAutoOnlineService::sync() 目前只由 sync:server-auto-online 定时命令每 5 分钟调用。
  • REST 节点心跳 ServerService::touchNode() 只更新在线缓存,不会触发自动上线同步;WebSocket 状态上报还绕过了 touchNode() 直接写缓存。

目标

  • 自动上线开启的节点在管理端保存、开启自动上线或节点心跳上报后,立即按当前在线状态和墙检测状态同步 show
  • 保持现有定时任务兜底能力,避免依赖管理员手动切换显隐。
  • 保持墙检测自动隐藏规则不被绕过。

约束条件

时间约束: 
性能约束: 单节点心跳只能同步当前节点,不能触发全量扫描
兼容性约束: 保持 sync:server-auto-online 命令输出结构不变
业务约束: 自动上线仍以 available_status 和墙状态为准;未开启 auto_online 的节点不改变手动显隐

验收标准

  • ServerAutoOnlineService 支持单节点同步,并复用定时全量同步的同一套判定逻辑。
  • 管理端保存节点、开启自动上线、批量开启自动上线后,若节点当前为在线或待同步且未被墙检测否决,应立即 show=1
  • 节点心跳上报后,若该节点开启自动上线,应立即同步 show,不必等待 Scheduler。
  • 被墙状态或 gfw_auto_hidden 未恢复正常时,自动上线不会把节点重新显示。
  • 单元测试覆盖单节点同步和心跳触发场景。

2. 方案

技术方案

ServerAutoOnlineService 中的自动上线判定抽出为可复用的 syncServer(Server $server) 单节点入口。全量定时命令继续调用 sync(),内部复用同一判定函数。

在两个关键触发点调用单节点同步:

  • 管理端 ManageControllersave()update()batchUpdate() 在节点开启 auto_online 后立即调用。
  • ServerService::touchNode() 在节点心跳刷新后,如果节点开启 auto_online,立即调用;WebSocket 状态上报改为复用 touchNode()

影响范围

涉及模块:
  - server-auto-online: 抽取单节点同步入口,复用全量同步逻辑
  - admin-server-manage: 保存/开关/批量更新自动上线时立即同步
  - server-heartbeat: REST 与 WebSocket 节点心跳后触发当前节点同步
  - tests: 补充自动上线单节点与心跳行为验证
预计变更文件: 5

风险评估

风险 等级 应对
节点心跳频繁导致额外数据库写入 只在 auto_online=true 时执行;状态无变化时不保存
保存接口中 showauto_online 同时提交时语义冲突 自动上线开启时以自动同步判定为准,符合 UI 中显隐开关被托管的文案
墙检测自动隐藏被误清除 单节点同步复用原有 isGfwBlocked/isGfwHeld 判定,只在正常状态时清除 hold

方案取舍

唯一方案理由: 问题根因在后端自动上线语义只由定时任务触发;把同步能力收敛到服务层并在业务事件中调用,可以同时覆盖管理端保存、开关和节点真实上线心跳。
放弃的替代路径:
  - 仅前端保存后强制提交 show=1: 会绕过离线/被墙判断,且无法覆盖节点稍后才心跳上线的场景。
  - 缩短 Scheduler 间隔: 仍不是立即上线,并增加全量扫描频率。
  - 复制节点时默认关闭 auto_online: 会改变复制语义,仍要求管理员额外操作。
回滚边界: 可独立回退 ServerAutoOnlineService 单节点入口、ManageController 调用点和 ServerService::touchNode 调用点;数据库结构不变。

3. 技术设计

架构设计

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=0auto_online=1,节点已经心跳在线且未被墙检测否决。 行为: 管理员在节点管理页编辑并保存该节点。 结果: 后端保存后立即执行单节点自动上线同步,节点 show=1,前端刷新列表后显示为上线。

场景: 节点稍后才上线

模块: server-heartbeat / server-auto-online 条件: 节点保存时尚未心跳,auto_online=1show=0行为: 节点安装完成并上报心跳。 结果: REST 与 WebSocket 心跳都会通过 touchNode() 更新在线缓存并立即同步当前节点,节点不需要等待下一轮 Scheduler。


5. 验证策略

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。此任务不包含视觉产出。