fix(api): 修复节点流量限额共享统计与父子显隐联动

统一节点流量统计与限额展示口径,节点详情新增昨日流量,
并让今日、昨日和本月使用清晰的半开时间窗口聚合

同 machine_id 或同 host 的节点现在共享当前账期已用流量,
管理端优先使用后端 traffic_limit_snapshot 展示月额度状态,
mi-node 下发的 current_used 也改为共享账期统计

新增 parent_auto_hidden 标记与父节点显隐联动服务,父节点
因自动上线或流量限额变为不可展示时会隐藏当前显示的子节点,
恢复时只恢复这批自动隐藏的子节点,避免覆盖手动操作
This commit is contained in:
yinjianm
2026-04-29 02:24:57 +08:00
parent 922e86070d
commit e847252e12
27 changed files with 2078 additions and 47 deletions
+28
View File
@@ -1,5 +1,33 @@
# CHANGELOG
## [0.6.22] - 2026-04-29
### 修复
- **[node-traffic-limit]**: 修复父节点自动下线后子节点仍可能保持上线的问题;新增 `parent_auto_hidden` 标记和父节点显隐联动服务,自动上线离线、流量限额 suspended 会隐藏当时仍显示的直接子节点,自动恢复或限额重置后只恢复这批由联动逻辑隐藏的子节点,手动隐藏的子节点不被误上线 — by yinjianm
- 方案: [202604290153_parent-node-auto-visibility](archive/2026-04/202604290153_parent-node-auto-visibility/)
- 决策: parent-node-auto-visibility#D001(使用独立父级自动隐藏标记)
## [0.6.21] - 2026-04-29
### 修复
- **[node-traffic-limit]**: 修正节点管理月额度使用量口径;同 `machine_id` 或同 host 节点现在共享当前账期用量,`server/manage/getNodes` 返回 `traffic_limit_snapshot`mi-node 下发的 `traffic_limit.current_used` 也改为共享账期统计,管理端优先显示快照并保留旧 metrics / `u+d` 回退 — by yinjianm
- 方案: [202604290132_shared-node-traffic-limit](archive/2026-04/202604290132_shared-node-traffic-limit/)
- 决策: shared-node-traffic-limit#D001(共享范围优先 machine_id,兜底 host)
## [0.6.20] - 2026-04-29
### 新增
- **[admin-frontend]**: 节点流量详情卡新增“昨日”统计;`server/manage/getNodes` 现在返回 `traffic_stats.today/yesterday/month/total`,今日、昨日和本月均使用半开时间窗口聚合,便于对比“今日下行多、本月上行多”的流量分布来源 — by yinjianm
- 方案: [202604290123_node-traffic-yesterday-stats](archive/2026-04/202604290123_node-traffic-yesterday-stats/)
- 决策: node-traffic-yesterday-stats#D001(保持 u/d 语义并新增后端 yesterday 字段)
## [0.6.19] - 2026-04-29
### 快速修改
- **[node-traffic-limit]**: 修复节点提高月流量额度后管理端仍显示“已限额”的问题;保存配置、缓存 metrics 回写和节点下发配置现在都会按当前已用流量与新额度重新计算 suspended 状态,旧额度产生的 stale metrics 不会再把节点重新标记为限额下线 — by yinjianm
- 类型: 快速修改(无方案包)
- 文件: app/Services/ServerTrafficLimitService.php:16-320, app/Services/ServerService.php:247-252, tests/Unit/ServerTrafficLimitServiceTest.php:60-148, E:/code/go/mi-node/internal/trafficlimit/manager_test.go:120-157
## [0.6.18] - 2026-04-28
### 新增
+3 -3
View File
@@ -3,7 +3,7 @@
```yaml
kb_version: 2
project: Xboard-new
updated_at: 2026-04-28
updated_at: 2026-04-29
active_package:
```
@@ -11,7 +11,7 @@ active_package: 无
- 类型: PHP Laravel 主仓 + `admin-frontend` Vue3 管理端前端
- 当前重点模块: `admin-frontend``deploy``node-gfw-check``node-traffic-limit``order-payment``queue-mail``subscription-protocols`
- 最新归档: `202604281921_node-traffic-limit-enforcement`
- 最新归档: `202604290153_parent-node-auto-visibility`
## 活跃模块
@@ -19,7 +19,7 @@ active_package: 无
- [ci-workflows](modules/ci-workflows.md): GitHub Actions 后端与管理端前端镜像发布工作流、路径触发边界和 GHCR 发布规则
- [deploy](modules/deploy.md): 可复制到服务器的 Xboard Compose 部署模板、环境变量模板和运维脚本
- [node-gfw-check](modules/node-gfw-check.md): 节点墙状态检测任务、父/子节点继承规则、mi-node 检测上报链路
- [node-traffic-limit](modules/node-traffic-limit.md): 节点月流量限额配置、重置调度、metrics 状态回写与 mi-node 强制下线协作
- [node-traffic-limit](modules/node-traffic-limit.md): 节点月流量限额配置、共享账期用量、重置调度、metrics 状态回写与 mi-node 强制下线协作
- [order-payment](modules/order-payment.md): 订单支付成功快照、第三方回调元信息透传与后台支付成功信息展示
- [queue-mail](modules/queue-mail.md): 邮件发送队列、SMTP 运行时配置、Horizon 超时与失败重试边界
- [subscription-protocols](modules/subscription-protocols.md): 客户端订阅导出入口、协议适配器与版本兼容过滤
@@ -0,0 +1,10 @@
{
"status": "completed",
"completed": 5,
"failed": 0,
"pending": 0,
"total": 5,
"percent": 100,
"current": "节点昨日流量统计已实现并完成验证",
"updated_at": "2026-04-29 01:50:00"
}
@@ -0,0 +1,181 @@
# 变更提案: node-traffic-yesterday-stats
## 元信息
```yaml
类型: 修复 + 新功能
方案类型: implementation
优先级: P1
状态: 已规划
创建: 2026-04-29
```
---
## 1. 需求
### 背景
管理后台 `#/nodes` 的节点流量详情卡当前展示“今日 / 本月 / 累计”。用户反馈某节点“今日下行很多,但本月上行最多”看起来不匹配,并要求统计加入“昨日”。
### 目标
- 核对节点统计的上行/下行字段映射,确认是否存在前后端反转或聚合口径错误。
- 在节点流量详情卡中新增“昨日”统计,口径与“今日”一致。
- 收紧统计窗口边界,保证“今日 / 昨日 / 本月 / 累计”各自窗口清晰。
### 约束条件
```yaml
时间约束:
性能约束: 节点列表接口仍按当前批量聚合方式查询,避免逐节点查询
兼容性约束: 保持现有 traffic_stats.today/month/total 字段兼容,新增 yesterday 字段
业务约束: 不改变 StatServer.u/d 的含义,不迁移历史数据
```
### 验收标准
- [ ] `server/manage/getNodes` 响应中的 `traffic_stats` 包含 `yesterday`
- [ ] `today` 只统计当天 `[today, tomorrow)``yesterday` 只统计 `[yesterday, today)``month` 只统计 `[monthStart, nextMonthStart)`
- [ ] 前端节点流量详情卡按“今日 / 昨日 / 本月 / 累计”展示。
- [ ] 后端测试覆盖新窗口边界,前端构建通过。
---
## 2. 方案
### 技术方案
`ManageController` 内扩展节点流量窗口构建:
- `emptyNodeTrafficStats()` 增加 `yesterday` 默认值。
- `buildNodeTrafficStats()` 使用 `strtotime('today')``strtotime('tomorrow')``strtotime('yesterday')` 和下月月初计算窗口。
- `fillTrafficWindow()` 支持可选结束时间,查询使用半开区间:`record_at >= startAt``record_at < endAt`
前端同步:
- `AdminNodeTrafficStats` 增加 `yesterday: TrafficAmount`
- `NodeTrafficDetail.key` 增加 `yesterday`
- `getNodeTrafficDetails()` 在“今日”和“本月”之间插入“昨日”。
测试:
- 新增/扩展 `ManageController` 单元测试,通过反射调用私有构建方法,构造跨天、跨月和未来记录,验证各窗口不会互相污染。
### 影响范围
```yaml
涉及模块:
- admin-frontend: 节点流量详情卡类型与展示行
- backend-admin-api: 节点列表接口 traffic_stats 聚合窗口
- tests: 节点统计窗口单元测试
预计变更文件: 4-6
```
### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| 历史数据中 u/d 语义本身来自节点端上报,若节点端上报方向定义与面板相反,面板无法单独纠正 | 中 | 本次只核对面板字段链路;不改变历史语义,避免误修 |
| 新增窗口可能增加查询次数 | 低 | 仍为按 server_id 批量聚合,仅从 3 个窗口增至 4 个窗口 |
| 月统计加入上界后不再包含未来 record_at | 低 | 这是更严格的窗口口径,符合预期 |
### 方案取舍
```yaml
唯一方案理由: 后端统一输出 yesterday 字段,前端只按接口字段展示,可以保持统计口径单一且兼容现有调用方。
放弃的替代路径:
- 仅前端用 today/month/total 推导昨日: 无法准确还原昨日上行/下行。
- 修改 StatServerJob 的 u/d 写入方向: 当前面板链路内 u=上行、d=下行一致,贸然反转会破坏历史数据和用户统计。
- 新增独立节点详情接口: 本次只影响列表详情卡,新增接口会扩大维护面。
回滚边界: 可独立回退 ManageController 的 yesterday/window 变更、前端类型/展示变更和测试文件,不涉及数据库迁移。
```
---
## 3. 技术设计
### API 设计
#### GET server/manage/getNodes
- **响应新增字段**: `traffic_stats.yesterday`
- **结构**:
```json
{
"traffic_stats": {
"today": {"upload": 0, "download": 0, "total": 0},
"yesterday": {"upload": 0, "download": 0, "total": 0},
"month": {"upload": 0, "download": 0, "total": 0},
"total": {"upload": 0, "download": 0, "total": 0}
}
}
```
### 数据模型
不新增数据表或字段,继续读取 `v2_stat_server` 的日统计记录:
| 字段 | 类型 | 说明 |
|------|------|------|
| `u` | bigint | 上行流量 |
| `d` | bigint | 下行流量 |
| `record_at` | int | 日统计归属日的 00:00:00 Unix 时间戳 |
| `record_type` | char | 本节点页只读取 `d` |
---
## 4. 核心场景
### 场景: 节点流量详情卡查看昨日统计
**模块**: admin-frontend
**条件**: 管理员打开 `#/nodes` 并悬停节点名称
**行为**: 前端读取 `traffic_stats.yesterday` 并渲染“昨日”行
**结果**: 管理员可以直接对比今日、昨日、本月和累计的上行/下行分布
### 场景: 节点列表接口按清晰窗口聚合
**模块**: backend-admin-api
**条件**: `v2_stat_server` 存在昨天、今天、本月其他日期和未来日期记录
**行为**: `server/manage/getNodes` 构建半开时间窗口
**结果**: 今日、昨日、本月统计互不串窗,累计仍覆盖全部历史记录
---
## 5. 技术决策
### node-traffic-yesterday-stats#D001: 保持 u/d 语义并新增后端 yesterday 字段
**日期**: 2026-04-29
**状态**: ✅采纳
**背景**: 用户反馈上行/下行看起来不匹配,同时要求加入昨日统计。代码链路显示前端、接口和入库任务均使用 `u=upload``d=download`
**选项分析**:
| 选项 | 优点 | 缺点 |
|------|------|------|
| A: 后端新增 `traffic_stats.yesterday` 并收紧窗口 | 口径统一、可测试、兼容当前字段 | 增加一个聚合查询 |
| B: 前端推导昨日 | 不改后端 | 无法准确得到昨日上行/下行 |
| C: 反转 u/d 字段 | 可能符合某些节点端方向理解 | 会破坏现有面板语义和历史统计 |
**决策**: 选择方案 A
**理由**: 问题核心是缺少可对比的昨日窗口和窗口边界不够明确,不是面板链路内字段反转。
**影响**: `server/manage/getNodes` 响应字段增加,节点页详情卡增加一行展示。
---
## 6. 验证策略
```yaml
verifyMode: test-first
reviewerFocus:
- app/Http/Controllers/V2/Admin/Server/ManageController.php 的窗口边界和兼容字段
- admin-frontend/src/utils/nodes.ts 的展示顺序与空值兜底
testerFocus:
- php artisan test --filter NodeTrafficStatsTest
- npm run buildadmin-frontend
uiValidation: optional
riskBoundary:
- 不执行数据库删除、重置或生产环境操作
- 不修改历史 StatServer 数据
```
---
## 7. 成果设计
### 设计方向
- **美学基调**: 延续现有 Apple 风格节点详情卡,新增“昨日”作为同等层级数据行,不引入额外视觉系统。
- **记忆点**: 今日与昨日紧邻展示,便于直接比较日流量方向变化。
- **参考**: 现有节点流量 popover。
### 视觉要素
- **配色**: 沿用现有白底、浅灰行底和蓝色总量强调。
- **字体**: 沿用现有管理端字体栈,不新增字体依赖。
- **布局**: 维持纵向统计行结构,顺序为今日、昨日、本月、累计。
- **动效**: 沿用 Element Plus Popover 行为,不新增动效。
- **氛围**: 与当前节点页一致。
### 技术约束
- **可访问性**: 不改变现有 hover/focus 触发方式。
- **响应式**: Popover 宽度维持现状,新增一行不改变表格布局。
@@ -0,0 +1,84 @@
# 任务清单: node-traffic-yesterday-stats
> **@status:** completed | 2026-04-29 01:37
```yaml
@feature: node-traffic-yesterday-stats
@created: 2026-04-29
@status: completed
@mode: R2
```
## LIVE_STATUS
```json
{"status":"completed","completed":5,"failed":0,"pending":0,"total":5,"percent":100,"current":"节点昨日流量统计已实现并完成验证","updated_at":"2026-04-29 01:50:00"}
```
## 进度概览
| 完成 | 失败 | 跳过 | 总数 |
|------|------|------|------|
| 5 | 0 | 0 | 5 |
---
## 任务列表
### 1. 后端统计窗口
- [√] 1.1 修改 `app/Http/Controllers/V2/Admin/Server/ManageController.php`
- 预期变更: `traffic_stats` 增加 `yesterday`,今日/昨日/本月使用半开时间窗口聚合。
- 完成标准: 接口保留 `today/month/total`,新增 `yesterday`,空数据返回 0。
- 验证方式: `php -l app/Http/Controllers/V2/Admin/Server/ManageController.php`; `vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit/Admin/NodeTrafficStatsWindowTest.php`
- depends_on: []
- 完成备注: 已新增 `resolveNodeTrafficWindows()` 并让 `fillTrafficWindow()` 使用 `record_at >= start``record_at < end` 的半开窗口。
### 2. 前端展示
- [√] 2.1 修改 `admin-frontend/src/types/api.d.ts`
- 预期变更: `AdminNodeTrafficStats` 类型增加 `yesterday: TrafficAmount`
- 完成标准: TypeScript 类型与后端响应字段一致。
- 验证方式: `npm run build`
- depends_on: [1.1]
- 完成备注: `AdminNodeTrafficStats` 已包含 `yesterday`
- [√] 2.2 修改 `admin-frontend/src/utils/nodes.ts`
- 预期变更: `getNodeTrafficDetails()` 在今日后展示昨日。
- 完成标准: 节点详情卡顺序为今日、昨日、本月、累计,缺失字段时显示 0。
- 验证方式: `npm run build`
- depends_on: [2.1]
- 完成备注: 节点流量详情顺序已调整为今日、昨日、本月、累计。
### 3. 验证与知识库
- [√] 3.1 新增或更新后端单元测试
- 预期变更: 覆盖今日、昨日、本月和累计窗口边界。
- 完成标准: 测试能证明未来记录不进入今日/月统计,昨日记录独立统计。
- 验证方式: `vendor/bin/phpunit --bootstrap vendor/autoload.php tests/Unit/Admin/NodeTrafficStatsWindowTest.php`
- depends_on: [1.1]
- 完成备注: 已新增窗口边界单元测试,覆盖普通日期和月初日期。
- [√] 3.2 执行构建/测试并同步知识库
- 预期变更: 运行可用验证命令,更新 `.helloagents` 模块文档和变更日志。
- 完成标准: 验证结果记录在执行日志,知识库反映 `traffic_stats.yesterday`
- 验证方式: 文件检查 + 命令输出
- depends_on: [2.2, 3.1]
- 完成备注: 已通过 PHP 语法检查、PHPUnit 单元测试和管理端前端构建,知识库与 CHANGELOG 已同步。
---
## 执行日志
| 时间 | 任务 | 状态 | 备注 |
|------|------|------|------|
| 2026-04-29 01:23:00 | DESIGN | in_progress | 已完成上下文收集和方案包创建 |
| 2026-04-29 01:38:00 | DEVELOP 1.1 | completed | 后端新增 yesterday 窗口并收紧 today/month 上界 |
| 2026-04-29 01:40:00 | DEVELOP 2.1-2.2 | completed | 前端类型和节点详情卡展示已加入“昨日” |
| 2026-04-29 01:44:00 | DEVELOP 3.1 | completed | 新增节点流量窗口边界单元测试 |
| 2026-04-29 01:50:00 | DEVELOP 3.2 | completed | 验证命令通过,知识库同步完成 |
---
## 执行备注
- 面板链路中 `StatServer.u` 对应上行、`StatServer.d` 对应下行;本次不反转历史语义。
- 用户截图中的“今日下行多、本月上行多”本身可能是正常数据分布,因为本月包含今天及之前日期;新增昨日后便于判断差异来自哪一天。
@@ -0,0 +1,179 @@
# 变更提案: shared-node-traffic-limit
## 元信息
```yaml
类型: 修复
方案类型: implementation
优先级: P1
状态: 草稿
创建: 2026-04-29
```
---
## 1. 需求
### 背景
节点管理页的“流量统计”弹层中,“月额度”当前优先使用单个节点的 mi-node metrics 或 `v2_server.u + v2_server.d`。当同一台机器上配置多个节点时,机器月流量额度是共享的,单节点口径会低估或拆散真实使用量。现有“本月”统计也按自然月聚合,不等同于机器供应商从指定重置日开始的账期。
### 目标
- 同一台机器 / 同一 IP 下的多个节点共享月额度使用量。
- 月额度使用量按当前限额配置的上一个重置边界到现在统计,而不是按自然月或单节点累计。
- 节点管理页的“月额度 used / limit”、进度条和限额状态使用后端统一快照。
- 保留“今日 / 昨日 / 本月 / 累计”节点流量统计的现有展示口径,不把自然月统计改成账期统计。
### 约束条件
```yaml
时间约束:
性能约束: 节点列表接口需要避免全表扫描式重复统计,按当前节点集合和共享范围聚合
兼容性约束: 不新增必填数据库字段;未配置 machine_id 的节点仍可按 host/IP 共享
业务约束: 不执行生产数据修复;不改变节点显隐和用户订阅筛选规则
```
### 验收标准
- [ ] 同一 `machine_id` 的两个限额节点,节点管理页显示相同的月额度已用量。
- [ ] 未配置 `machine_id``host` 相同的两个限额节点,节点管理页显示相同的月额度已用量。
- [ ] 月额度已用量从当前时间之前最近一次重置边界开始统计,例如重置日 18 日则按最近一个 18 日 00:00 起算。
- [ ] 不同 `machine_id` 或不同 `host` 的节点不互相累加。
- [ ] 前端在后端缺少共享快照时仍能回退到原有 metrics / `u + d` 展示。
---
## 2. 方案
### 技术方案
`ServerTrafficLimitService` 中集中新增共享限额快照能力:
- 共享范围优先使用 `machine_id`;未绑定机器时使用规范化后的 `host`。空 host 回退为单节点范围。
- 新增当前账期起点计算:根据节点的 `traffic_limit_reset_day / traffic_limit_reset_time / traffic_limit_timezone` 计算“当前时间之前最近一次重置时间”。
- 新增共享账期用量统计:从 `v2_stat_server` 中按共享范围内的 `server_id` 聚合 `record_type='d'``u/d`,统计窗口为账期起点到当前日。若节点未持久化或没有统计来源,再回退到共享范围内的 `u + d`
- `buildNodeConfig()` 继续返回 mi-node 既有 `traffic_limit` 结构,但 `current_used` 改用共享账期用量。
- 管理端 `server/manage/getNodes` 为每个节点追加 `traffic_limit_snapshot`,前端月额度优先使用该快照展示。
- 管理端 TypeScript 类型和 `getNodeTrafficLimitDetail()` 同步增加快照优先级,保持缺省兼容。
### 影响范围
```yaml
涉及模块:
- node-traffic-limit: 限额账期、共享范围和当前用量计算
- admin-frontend: 节点管理页月额度展示数据源
预计变更文件: 5-7
```
### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| `v2_stat_server.record_at` 是日粒度,非 00:00 重置时间无法做到小时级切分 | 中 | 按重置日所在自然日作为统计起点,保留 `traffic_limit_last_reset_at` 和 mi-node metrics 作为运行态辅助;在测试中覆盖 00:00 主路径 |
| 同 host 但实际不是同一机器的节点会被共享统计 | 中 | 优先使用 `machine_id`;未绑定机器时按用户明确要求的同 IP/host 兜底,并在知识库记录规则 |
| 前端旧数据结构缺少新快照 | 低 | 前端保留原有 metrics / `u + d` 回退 |
| 改动影响 mi-node 下发的 `current_used` | 中 | 仅改变限额模块中的用量口径,不改变配置字段名;用单元测试覆盖共享和非共享场景 |
### 方案取舍
```yaml
唯一方案理由: 共享用量属于限额领域逻辑,集中在 ServerTrafficLimitService 能同时服务节点配置下发和管理端展示,避免前端自行猜测同机节点。
放弃的替代路径:
- 仅前端按 host 汇总: 只能修展示,无法修正 mi-node 下发的 current_used,且会复制业务规则
- 新增 machine_quota 表: 能表达机器级额度,但超出本次问题范围,需要新增配置入口和迁移
- 每次 DNS 解析 host 后按真实 IP 汇总: 接口性能和网络副作用不可控,且域名解析会受环境影响
回滚边界: 可独立回退 ServerTrafficLimitService 的共享快照、ManageController 的 traffic_limit_snapshot 返回和前端快照优先展示;不涉及数据库结构回滚
```
---
## 3. 技术设计
### 架构设计
```mermaid
flowchart TD
A[ManageController getNodes] --> B[ServerTrafficLimitService buildSnapshotsForServers]
C[ServerService buildNodeConfig] --> D[ServerTrafficLimitService buildNodeConfig]
B --> E[shared scope: machine_id or host]
D --> E
E --> F[v2_stat_server cycle usage]
B --> G[traffic_limit_snapshot]
G --> H[admin-frontend getNodeTrafficLimitDetail]
```
### API 设计
#### GET `server/manage/getNodes`
- **请求**: 保持不变。
- **响应**: 每个节点新增可选字段:
```json
{
"traffic_limit_snapshot": {
"enabled": true,
"limit": 1073741824000,
"used": 616327110656,
"percent": 57,
"suspended": false,
"status": "normal",
"cycle_start_at": 1776441600,
"last_reset_at": 1776441600,
"next_reset_at": 1779033600,
"scope_key": "host:82.40.33.225",
"scope_node_ids": [327, 272]
}
}
```
### 数据模型
不新增数据库字段。共享范围由现有 `v2_server.machine_id``v2_server.host` 推导。
---
## 4. 核心场景
### 场景: 同 IP 节点共享月额度
**模块**: node-traffic-limit
**条件**: 两个节点 `host` 相同,均启用月流量限额,重置日一致。
**行为**: 管理端打开节点列表并悬停任一节点名称。
**结果**: 两个节点的“月额度”已用量均为该 host 下节点账期流量合计。
### 场景: 绑定机器优先共享
**模块**: node-traffic-limit
**条件**: 两个节点绑定相同 `machine_id`host 可以不同。
**行为**: 后端生成节点列表或 mi-node 配置。
**结果**: 共享范围按 `machine_id` 聚合,不再按 host 分裂。
---
## 5. 技术决策
### shared-node-traffic-limit#D001: 共享范围优先 machine_id,兜底 host
**日期**: 2026-04-29
**状态**: ✅采纳
**背景**: 项目已有 `v2_server_machine``machine_id`,但用户当前问题来自同 IP 多节点共享机器额度,不能要求所有旧节点先补机器绑定。
**选项分析**:
| 选项 | 优点 | 缺点 |
|------|------|------|
| A: 只按 machine_id | 语义最准确 | 旧节点或未绑定机器的同 IP 节点无法修复 |
| B: 只按 host/IP | 满足截图场景 | 已绑定机器但 host 不同的同机节点会被拆散 |
| C: machine_id 优先,host 兜底 | 覆盖新旧两类场景,改动较小 | host 相同但非同机的节点会共享统计 |
**决策**: 选择方案 C。
**理由**: 在不新增配置入口的前提下,C 能覆盖已有机器模型和用户明确的同 IP 场景。
**影响**: `ServerTrafficLimitService` 成为共享范围规则的唯一实现位置,管理端和节点配置下发共用该规则。
---
## 6. 验证策略
```yaml
verifyMode: test-first
reviewerFocus:
- app/Services/ServerTrafficLimitService.php 的账期起点、共享范围和回退逻辑
- app/Http/Controllers/V2/Admin/Server/ManageController.php 的响应兼容性
- admin-frontend/src/utils/nodes.ts 的快照优先级和旧数据回退
testerFocus:
- vendor/bin/phpunit tests/Unit/ServerTrafficLimitServiceTest.php
- vendor/bin/phpunit tests/Unit/Admin/NodeTrafficStatsWindowTest.php
- php -l app/Services/ServerTrafficLimitService.php
- php -l app/Http/Controllers/V2/Admin/Server/ManageController.php
uiValidation: optional
riskBoundary:
- 不执行数据库迁移或生产数据更新
- 不修改删除节点、重置节点流量等破坏性接口语义
```
---
## 7. 成果设计
N/A。本次不调整节点管理页视觉结构,只修正“月额度”展示数据源。
@@ -0,0 +1,81 @@
# 任务清单: shared-node-traffic-limit
> **@status:** completed | 2026-04-29 01:56
```yaml
@feature: shared-node-traffic-limit
@created: 2026-04-29
@status: completed
@mode: R2
```
## LIVE_STATUS
```json
{"status":"completed","completed":5,"failed":0,"pending":0,"total":5,"percent":100,"current":"开发实施、验证和知识库同步完成","updated_at":"2026-04-29 02:08:00"}
```
## 进度概览
| 完成 | 失败 | 跳过 | 总数 |
|------|------|------|------|
| 5 | 0 | 0 | 5 |
---
## 任务列表
### 1. 后端共享限额口径
- [√] 1.1 修改 `app/Services/ServerTrafficLimitService.php`
- 预期变更: 新增共享范围解析、当前账期起点计算、共享账期用量聚合和批量快照输出;`buildNodeConfig()``current_used` 使用共享账期口径。
- 完成标准: 同 `machine_id` 或同 `host` 节点可得到一致 used;不同范围节点互不累加;未启用限额仍返回 disabled。
- 验证方式: `vendor/bin/phpunit tests/Unit/ServerTrafficLimitServiceTest.php`,并执行 `php -l app/Services/ServerTrafficLimitService.php`
- depends_on: []
- [√] 1.2 修改 `app/Http/Controllers/V2/Admin/Server/ManageController.php`
- 预期变更: `getNodes` 批量生成并返回 `traffic_limit_snapshot`;保留 `traffic_stats` 现有自然日/自然月统计。
- 完成标准: 响应中每个节点包含可选快照字段;没有快照时不影响原节点列表返回。
- 验证方式: `php -l app/Http/Controllers/V2/Admin/Server/ManageController.php`,并用相关单元测试覆盖窗口不回归。
- depends_on: [1.1]
### 2. 管理端展示兼容
- [√] 2.1 修改 `admin-frontend/src/types/api.d.ts`
- 预期变更: 为节点接口补充 `AdminNodeTrafficLimitSnapshot``traffic_limit_snapshot` 类型。
- 完成标准: TypeScript 能识别新字段,旧字段类型不被破坏。
- 验证方式: 运行可用的前端类型检查或构建命令;不可用时至少静态检查引用。
- depends_on: [1.2]
- [√] 2.2 修改 `admin-frontend/src/utils/nodes.ts`
- 预期变更: `getNodeTrafficLimitDetail()` 优先使用 `traffic_limit_snapshot` 的 limit、used、status 和 reset 时间,缺失时回退到 metrics / `u + d`
- 完成标准: 有快照时展示共享账期用量;无快照时展示行为与旧版一致。
- 验证方式: 前端类型检查或构建;人工核对逻辑分支。
- depends_on: [2.1]
### 3. 测试与知识库
- [√] 3.1 修改 `tests/Unit/ServerTrafficLimitServiceTest.php``.helloagents/modules/node-traffic-limit.md``.helloagents/modules/admin-frontend.md`
- 预期变更: 增加共享 host / machine 账期用量测试;更新知识库记录共享限额口径和管理端快照字段。
- 完成标准: 测试覆盖同范围聚合、不同范围隔离、账期起点;知识库与代码行为一致。
- 验证方式: 运行后端测试和语法检查;检查知识库描述不再声称限额只按单节点 `u/d`
- depends_on: [1.1, 1.2, 2.2]
---
## 执行日志
| 时间 | 任务 | 状态 | 备注 |
|------|------|------|------|
| 2026-04-29 02:08 | 3.1 | completed | 已补充共享 host / machine / 账期起点 / runtime suspended 测试,并完成知识库同步 |
| 2026-04-29 02:06 | 验证 | completed | PHPUnit 10 tests / 44 assertions 通过;admin-frontend 构建通过 |
| 2026-04-29 02:03 | 2.1-2.2 | completed | 管理端类型与月额度展示已优先消费 `traffic_limit_snapshot` |
| 2026-04-29 01:58 | 1.1-1.2 | completed | 后端已生成共享账期快照并接入 `server/manage/getNodes` |
| 2026-04-29 01:32 | DESIGN | in_progress | 已完成方案设计与任务拆分 |
---
## 执行备注
- 当前执行模式: INTERACTIVE。
- 不新增数据库字段,不执行生产数据操作。
@@ -0,0 +1,171 @@
# 变更提案: parent-node-auto-visibility
## 元信息
```yaml
类型: 修复
方案类型: implementation
优先级: P1
状态: 已设计
创建: 2026-04-29
```
---
## 1. 需求
### 背景
当前父节点自动状态链路存在不一致:
- `ServerAutoOnlineService` 只同步开启 `auto_online` 的节点自身;父节点因离线被自动隐藏时,不会保证其子节点同步隐藏。
- `ServerTrafficLimitService` 将流量限额超额状态写入 `traffic_limit_status`,既有知识库明确记录“不修改 `show`”,因此父节点超额后子节点仍可能保持展示。
- 墙检测已有 `gfw_auto_hidden` 标记,只恢复上次由墙检测自动隐藏的节点;本次需求需要为“父节点自动下线”建立同等可追溯标记,避免误恢复原本手动隐藏的子节点。
### 目标
- 父节点因系统自动逻辑变为不可展示时,自动隐藏当时仍展示的子节点。
- 父节点由系统自动逻辑恢复可展示时,只恢复上一次由该父节点联动逻辑自动隐藏的子节点。
- 原本 `show=0`、管理员手动隐藏、后续被手动调整的子节点不能被误上线。
- 覆盖自动上线同步、流量限额超额/恢复等当前可定位的自动状态入口,并保留墙检测自身的独立标记逻辑。
### 约束条件
```yaml
时间约束: 本轮完成后端实现、迁移、测试和知识库同步
性能约束: 子节点联动只按单个父节点查询/更新,不引入全表循环外的额外扫描
兼容性约束: 不改变现有管理端 API 请求结构,不改变 mi-node 下发协议
业务约束: 只修改系统自动联动产生的 show 状态;不修改 enabled、auto_online、gfw_check_enabled
```
### 验收标准
- [ ] 父节点自动下线时,当前 `show=1` 的子节点被隐藏并打上父级自动隐藏标记。
- [ ] 父节点自动恢复时,仅 `parent_auto_hidden=1` 的子节点恢复展示,原本隐藏的子节点保持隐藏。
- [ ] 管理员手动修改子节点 `show` 时会清除父级自动隐藏标记,后续父节点恢复不会覆盖人工决定。
- [ ] 流量限额从 normal 变为 suspended 时触发子节点隐藏,从 suspended/超额状态恢复为 normal 时触发标记子节点恢复。
- [ ] 相关单元测试通过,至少覆盖自动上线和流量限额两条入口。
---
## 2. 方案
### 技术方案
新增一组父节点联动标记字段到 `v2_server`
- `parent_auto_hidden`: 子节点是否由父节点自动状态联动隐藏。
- `parent_auto_action_at`: 最近一次父节点联动操作时间。
新增 `ServerParentVisibilityService` 作为集中服务:
- `hideChildrenForParent(Server $parent)`: 只隐藏当前 `show=1` 的直接子节点,并设置 `parent_auto_hidden=1`
- `restoreChildrenForParent(Server $parent)`: 只恢复 `parent_auto_hidden=1` 且未被其他自动隐藏原因阻断的直接子节点,并清除标记。
- `clearParentAutoHidden(Server $server)`: 管理员手动调整节点展示状态时清除标记,防止后续自动恢复覆盖人工操作。
接入点:
- `ServerAutoOnlineService`: 父节点自动同步后,根据父节点最终 `show` 决定隐藏或恢复子节点;即使父节点自身状态未变化,也根据当前最终状态补齐子节点联动。
- `ServerTrafficLimitService`: `refreshSchedule()``resetServer()``applyRuntimeMetrics()` 写入限额运行状态后,对父节点执行子节点隐藏/恢复。超额或节点端上报 suspended 时隐藏;恢复 normal 或重置后恢复标记子节点。
- `ManageController`: 在单节点保存、快速更新、批量更新中,手动传入 `show` 时同步清除 `parent_auto_hidden`
### 影响范围
```yaml
涉及模块:
- node-traffic-limit: 限额 suspended/normal 状态影响子节点展示联动
- node-auto-online: 自动上线同步影响父节点子节点展示联动
- admin-server-manage: 手动 show 修改时清理自动联动标记
预计变更文件: 8
```
### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| 恢复子节点时覆盖其他自动隐藏原因 | 中 | 恢复时跳过 `gfw_auto_hidden=1` 的节点,并只处理 `parent_auto_hidden=1` 的子节点 |
| 流量限额状态频繁上报导致重复更新 | 低 | 服务方法先按当前状态筛选,只更新需要变化的子节点 |
| 新字段未迁移导致运行时异常 | 中 | 添加幂等迁移、模型 casts 和测试覆盖 |
### 方案取舍
```yaml
唯一方案理由: 独立 `parent_auto_hidden` 标记能精确表达“上次由父节点联动自动下线”的来源,满足只恢复自动下线子节点的要求,且不会污染墙检测专用字段。
放弃的替代路径:
- 复用 `gfw_auto_hidden`: 会把墙检测和父节点自动联动混在一起,恢复时无法区分原因。
- 不加字段、只按当前 show 推断: 无法判断子节点原本是否手动隐藏,会误上线。
回滚边界: 可回退新增服务接入、模型字段和迁移;数据库字段保留时不会影响旧逻辑,删除字段需单独迁移。
```
---
## 3. 技术设计
### 架构设计
```mermaid
flowchart TD
A[ServerAutoOnlineService] --> C[ServerParentVisibilityService]
B[ServerTrafficLimitService] --> C
D[ManageController manual show] --> C
C --> E[v2_server parent_auto_hidden]
C --> F[v2_server show]
```
### 数据模型
| 字段 | 类型 | 说明 |
|------|------|------|
| parent_auto_hidden | boolean default false | 子节点是否由父节点自动状态联动隐藏 |
| parent_auto_action_at | unsignedBigInteger nullable | 最近一次父节点联动隐藏或恢复时间戳 |
---
## 4. 核心场景
### 场景: 父节点自动下线联动子节点
**模块**: node-auto-online / node-traffic-limit
**条件**: 父节点因自动上线检测离线、流量限额超额或其他系统自动状态变为不可展示;子节点 A 当前 `show=1`,子节点 B 当前 `show=0`
**行为**: 服务隐藏子节点 A 并设置 `parent_auto_hidden=1`,子节点 B 保持隐藏且不设置标记。
**结果**: 父节点恢复时只恢复子节点 A。
### 场景: 父节点自动恢复只恢复上次自动下线子节点
**模块**: node-auto-online / node-traffic-limit
**条件**: 父节点恢复可展示;子节点 A `parent_auto_hidden=1`,子节点 B 是手动隐藏。
**行为**: 服务恢复子节点 A 并清除标记,子节点 B 不变。
**结果**: 不误上线原本未展示的子节点。
### 场景: 管理员手动修改子节点展示状态
**模块**: admin-server-manage
**条件**: 子节点此前由父节点联动隐藏,管理员手动设置 `show`
**行为**: 控制器清除 `parent_auto_hidden``parent_auto_action_at`
**结果**: 后续父节点自动恢复不会覆盖管理员最新选择。
---
## 5. 技术决策
### parent-node-auto-visibility#D001: 使用独立父级自动隐藏标记
**日期**: 2026-04-29
**状态**: ✅采纳
**背景**: 需求要求恢复“上次自动下线”的子节点,不能恢复原本未启用或手动隐藏的子节点。
**选项分析**:
| 选项 | 优点 | 缺点 |
|------|------|------|
| A: 独立 `parent_auto_hidden` 标记 | 语义清晰,可与墙检测、手动隐藏并存 | 需要新增迁移和模型字段 |
| B: 复用 `gfw_auto_hidden` | 改动少 | 原因混淆,容易误恢复墙检测隐藏节点 |
| C: 不持久化标记 | 无数据库变更 | 不能跨进程、跨重启准确恢复 |
**决策**: 选择方案 A
**理由**: 只有持久化来源标记能准确表达“上次被父节点自动联动下线”的子节点集合。
**影响**: `v2_server` 表、节点自动上线服务、流量限额服务、管理端节点状态接口、相关测试。
---
## 6. 验证策略
```yaml
verifyMode: test-first
reviewerFocus:
- app/Services/ServerParentVisibilityService.php 的恢复条件是否避免误上线
- ServerAutoOnlineService 与 ServerTrafficLimitService 的触发时机是否覆盖状态变化
- ManageController 手动 show 修改是否清除自动标记
testerFocus:
- vendor/bin/phpunit tests/Unit/ServerAutoOnlineServiceTest.php tests/Unit/ServerTrafficLimitServiceTest.php
- php -l 新增/修改的 PHP 文件
uiValidation: none
riskBoundary:
- 不执行生产数据库迁移
- 不调用生产 API
- 不修改 mi-node 协议
```
---
## 7. 成果设计
N/A。此次为后端状态联动与数据标记修复,不涉及视觉产出。
@@ -0,0 +1,97 @@
# 任务清单: parent-node-auto-visibility
> **@status:** completed | 2026-04-29 02:07
```yaml
@feature: parent-node-auto-visibility
@created: 2026-04-29
@status: completed
@mode: R2
@type: implementation
```
## LIVE_STATUS
```json
{"status":"completed","completed":6,"failed":0,"pending":0,"total":6,"percent":100,"current":"父节点自动下线联动子节点隐藏与恢复已完成并通过验证","updated_at":"2026-04-29 02:14:00"}
```
## 进度概览
| 完成 | 失败 | 跳过 | 总数 |
|------|------|------|------|
| 6 | 0 | 0 | 6 |
---
## 任务列表
### 1. 数据模型与联动服务
- [√] 1.1 新增 `v2_server` 父级自动隐藏标记字段
- 文件路径或作用范围: `database/migrations/*_add_parent_auto_visibility_fields_to_v2_server_table.php`, `app/Models/Server.php`
- 预期变更: 增加 `parent_auto_hidden``parent_auto_action_at` 字段,补充模型 docblock 和 casts。
- 完成标准: 新字段迁移幂等,模型可布尔/整数转换字段。
- 验证方式: `php -l` 检查新增迁移和模型语法。
- depends_on: []
- [√] 1.2 新增父节点子节点展示联动服务
- 文件路径或作用范围: `app/Services/ServerParentVisibilityService.php`
- 预期变更: 实现隐藏当前展示子节点、恢复被标记子节点、清除手动标记的集中方法。
- 完成标准: 只标记本次自动隐藏的子节点;恢复时不恢复未标记或仍被墙检测隐藏的节点。
- 验证方式: 单元测试覆盖自动隐藏和恢复行为。
- depends_on: [1.1]
### 2. 自动状态入口接入
- [√] 2.1 接入自动上线同步
- 文件路径或作用范围: `app/Services/ServerAutoOnlineService.php`
- 预期变更: 父节点自动同步后根据最终展示状态隐藏或恢复直接子节点;结果统计包含子节点联动更新。
- 完成标准: 父节点离线自动隐藏时同步隐藏展示中的子节点;父节点恢复在线时只恢复 `parent_auto_hidden=1` 的子节点。
- 验证方式: `tests/Unit/ServerAutoOnlineServiceTest.php` 新增断言。
- depends_on: [1.2]
- [√] 2.2 接入流量限额超额和恢复
- 文件路径或作用范围: `app/Services/ServerTrafficLimitService.php`
- 预期变更: `refreshSchedule()``resetServer()``applyRuntimeMetrics()` 状态落库后触发父节点子节点隐藏/恢复。
- 完成标准: suspended 隐藏子节点,normal/reset 恢复被标记子节点,未启用限额不触发误恢复。
- 验证方式: `tests/Unit/ServerTrafficLimitServiceTest.php` 新增断言。
- depends_on: [1.2]
- [√] 2.3 清理手动 show 修改的自动联动标记
- 文件路径或作用范围: `app/Http/Controllers/V2/Admin/Server/ManageController.php`
- 预期变更: 单节点保存、快速更新、批量更新接收 `show` 时清除 `parent_auto_hidden``parent_auto_action_at`
- 完成标准: 手动显示/隐藏子节点后,后续父节点恢复不会覆盖人工决定。
- 验证方式: 代码检查和相关服务测试覆盖标记清除方法。
- depends_on: [1.1, 1.2]
### 3. 验证与知识库
- [√] 3.1 补充测试、知识库和变更记录
- 文件路径或作用范围: `tests/Unit/ServerAutoOnlineServiceTest.php`, `tests/Unit/ServerTrafficLimitServiceTest.php`, `.helloagents/modules/node-traffic-limit.md`, `.helloagents/context.md`, `.helloagents/CHANGELOG.md`
- 预期变更: 增加自动上线与流量限额联动测试,更新知识库说明和变更记录。
- 完成标准: 目标测试通过或明确记录环境阻塞;知识库反映代码事实。
- 验证方式: `vendor/bin/phpunit tests/Unit/ServerAutoOnlineServiceTest.php tests/Unit/ServerTrafficLimitServiceTest.php`
- depends_on: [2.1, 2.2, 2.3]
---
## 执行日志
| 时间 | 任务 | 状态 | 备注 |
|------|------|------|------|
| 2026-04-29 01:53 | 方案设计 | in_progress | 已完成上下文收集与任务拆分 |
| 2026-04-29 02:03 | 1.1-2.3 | completed | 已完成迁移、模型、联动服务和入口接入 |
| 2026-04-29 02:08 | 3.1 | completed | 已补充自动上线和流量限额测试 |
| 2026-04-29 02:14 | 验证 | completed | PHP 语法、PHPStan 和目标 PHPUnit 测试通过 |
---
## 执行备注
- 当前已有遗留方案包 `202604250006_ticket-closed-reply-reopen` 标记 `in_progress`,本任务作为新方案包独立执行。
- 不执行生产数据库迁移,仅提交迁移文件和测试。
- 验证命令:
- `php -l` 检查新增/修改 PHP 文件。
- `vendor\bin\phpstan analyse app\Services\ServerParentVisibilityService.php app\Services\ServerAutoOnlineService.php app\Services\ServerTrafficLimitService.php app\Http\Controllers\V2\Admin\Server\ManageController.php app\Models\Server.php --memory-limit=1G`
- 使用一次性 SQLite 文件执行 `vendor\bin\phpunit tests\Unit\ServerAutoOnlineServiceTest.php tests\Unit\ServerTrafficLimitServiceTest.php`,结果 17 tests / 83 assertions 通过。
+5
View File
@@ -7,6 +7,9 @@
| 时间戳 | 名称 | 类型 | 涉及模块 | 决策 | 结果 |
|--------|------|------|---------|------|------|
| 202604290153 | parent-node-auto-visibility | - | - | - | ✅完成 |
| 202604290132 | shared-node-traffic-limit | implementation | node-traffic-limit,admin-frontend | shared-node-traffic-limit#D001 | ✅完成 |
| 202604290123 | node-traffic-yesterday-stats | implementation | admin-frontend,backend-admin-api | node-traffic-yesterday-stats#D001 | ✅完成 |
| 202604281921 | node-traffic-limit-enforcement | implementation | node-traffic-limit,admin-frontend,mi-node | node-traffic-limit-enforcement#D001,#D002 | ✅完成 |
| 202604281625 | admin-frontend-node-traffic-hover | - | - | - | ✅完成 |
| 202604281632 | admin-frontend-node-auto-online-immediate-sync | - | - | - | ✅完成 |
@@ -47,6 +50,8 @@
## 按月归档
### 2026-04
- [202604290132_shared-node-traffic-limit](./2026-04/202604290132_shared-node-traffic-limit/) - 修正节点管理月额度使用量口径,同 `machine_id` 或同 host 节点共享当前账期用量,并由后端快照统一服务管理端展示和 mi-node 下发
- [202604290123_node-traffic-yesterday-stats](./2026-04/202604290123_node-traffic-yesterday-stats/) - 节点流量详情卡新增“昨日”统计,并让今日、昨日和本月统计按半开窗口聚合,便于核对上行/下行流量分布
- [202604281921_node-traffic-limit-enforcement](./2026-04/202604281921_node-traffic-limit-enforcement/) - 新增节点月流量限额强制下线能力,Xboard 负责配置、重置调度和状态展示,mi-node 负责本地额度累计、内核停止与重置恢复
- [202604281441_fix-admin-node-gfw-null-enabled](./2026-04/202604281441_fix-admin-node-gfw-null-enabled/) - 修复 `parent_id=0` 父节点不会被自动墙检入队导致长期显示“未检测”的问题,并让自动墙检查询对齐项目父节点与启用语义
- [202604281303_xboard-reusable-server-deploy](./2026-04/202604281303_xboard-reusable-server-deploy/) - 新增可复制到服务器的 Xboard Compose 部署模板,补齐独立 `scheduler` 服务,并提供 `.env.example`、初始化/部署/更新/状态检查脚本和部署说明
+3 -3
View File
@@ -54,7 +54,7 @@
- `server/manage/checkGfw`
- `server/manage/resetTraffic`
- `server/manage/batchResetTraffic`
- 节点月流量限额由 Xboard 保存和编排:`v2_server.transfer_enable` 作为月额度,`traffic_limit_*` 字段记录启用、重置日/时间/时区和节点端运行状态;`ServerTrafficLimitService` 负责下发 `traffic_limit`、手动/定时重置、metrics 状态回写和通知 mi-node
- 节点月流量限额由 Xboard 保存和编排:`v2_server.transfer_enable` 作为月额度,`traffic_limit_*` 字段记录启用、重置日/时间/时区和节点端运行状态;`ServerTrafficLimitService` 负责下发 `traffic_limit`、手动/定时重置、metrics 状态回写、父节点限额下线时的子节点联动显隐和通知 mi-node
- 管理端套餐管理现已接入:
- `plan/fetch`
- `plan/save`
@@ -116,9 +116,9 @@
- 管理端路由使用 Hash 模式
- 管理端当前业务路由包含 `/dashboard``/users``/tickets``/nodes``/node-groups``/node-routes``/subscriptions/plans``/subscriptions/orders``/subscriptions/coupons``/subscriptions/gift-cards``/system/config``/system/notices``/system/payments``/system/plugins``/system/themes``/system/knowledge`
- `#/nodes` 当前已升级为真实节点工作台:支持搜索、在线 / 离线筛选、显隐筛选、父/子节点筛选、墙状态筛选、分页浏览、显隐切换、自动上线托管开关、墙检测托管开关、刷新数据、复制、单节点置顶、仅对已勾选节点生效的批量修改 / 批量删除,以及 11 种协议的新增 / 编辑弹窗和排序对话框
- 节点自动上线由后端 `ServerAutoOnlineService` 统一执行,只处理 `auto_online=1` 的节点:在线 / 待同步时自动 `show=1`,离线时自动 `show=0`;管理端保存 / 开启自动上线、REST 心跳和 WebSocket 状态上报会触发当前节点即时同步,`sync:server-auto-online` 每 5 分钟继续兜底;未开启自动上线的节点继续保持手动显隐控制;墙状态为 `blocked` 或仍处于 `gfw_auto_hidden` 且未恢复正常时会否决自动显示
- 节点自动上线由后端 `ServerAutoOnlineService` 统一执行,只处理 `auto_online=1` 的节点:在线 / 待同步时自动 `show=1`,离线时自动 `show=0`父节点自动隐藏时会通过 `parent_auto_hidden` 标记隐藏当时仍显示的直接子节点,父节点自动恢复时只恢复这批子节点;管理端保存 / 开启自动上线、REST 心跳和 WebSocket 状态上报会触发当前节点即时同步,`sync:server-auto-online` 每 5 分钟继续兜底;未开启自动上线的节点继续保持手动显隐控制;墙状态为 `blocked` 或仍处于 `gfw_auto_hidden` 且未恢复正常时会否决自动显示
- 节点自动墙检测由后端 `sync:server-gfw-checks` 定时命令执行,只为开启 `gfw_check_enabled` 的父节点创建检测任务;父节点兼容 `parent_id IS NULL` 与历史 `parent_id=0` 两种表示,`gfw_check_enabled` 仅明确为 `false` 时关闭;子节点不独立检测,但可控制是否随父节点自动隐藏 / 恢复
- 节点新增 / 编辑弹窗支持配置月流量限额、重置日期、重置时间和时区;节点列表流量详情卡会展示月额度、当前已用、限额状态和下次重置。限额超额后的真实下线由 mi-node 本地执行,Xboard 不通过 `show``auto_online` 伪装下线
- 节点新增 / 编辑弹窗支持配置月流量限额、重置日期、重置时间和时区;节点列表流量详情卡会展示月额度、当前已用、限额状态和下次重置。限额超额后的父节点真实下线由 mi-node 本地执行,Xboard 不通过父节点自身 `show``auto_online` 伪装下线;若超额节点是父节点,Xboard 会同步隐藏当时仍显示的直接子节点并在恢复时只恢复 `parent_auto_hidden=1` 的子节点
- Compose 部署必须确保 Laravel Scheduler 持续运行;`deploy/xboard-server/compose.yaml` 通过独立 `scheduler` 服务执行 `php artisan schedule:work`,否则自动墙检测只会在手动触发时创建任务
- Bearer Token 存储于 `sessionStorage/localStorage`
- `admin-frontend` 的视觉方向当前以 Apple 风格为基线,优先纯色分区、系统字体栈和低装饰成本
+2 -2
View File
@@ -2,11 +2,11 @@
| 模块名 | 说明 | 最近更新 |
|--------|------|----------|
| [admin-frontend](admin-frontend.md) | 管理端前端登录、布局、仪表盘、用户管理、节点管理与管理 API 封装 | 2026-04-28 |
| [admin-frontend](admin-frontend.md) | 管理端前端登录、布局、仪表盘、用户管理、节点管理与管理 API 封装 | 2026-04-29 |
| [ci-workflows](ci-workflows.md) | GitHub Actions 镜像发布工作流、路径触发规则与前后端镜像发布边界 | 2026-04-28 |
| [deploy](deploy.md) | 可复制到服务器的 Xboard Compose 部署模板、环境变量模板和运维脚本 | 2026-04-28 |
| [node-gfw-check](node-gfw-check.md) | 节点墙状态检测任务、父/子节点继承规则、mi-node 检测上报链路 | 2026-04-28 |
| [node-traffic-limit](node-traffic-limit.md) | 节点月流量限额配置、重置调度、metrics 状态回写与 mi-node 强制下线协作 | 2026-04-28 |
| [node-traffic-limit](node-traffic-limit.md) | 节点月流量限额配置、共享账期用量、重置调度、metrics 状态回写与 mi-node 强制下线协作 | 2026-04-29 |
| [order-payment](order-payment.md) | 订单支付成功快照、第三方回调元信息透传与后台支付成功信息展示 | 2026-04-25 |
| [queue-mail](queue-mail.md) | 邮件发送队列、SMTP 运行时配置、Horizon 超时与失败重试边界 | 2026-04-28 |
| [subscription-protocols](subscription-protocols.md) | 用户订阅导出入口、协议适配器与 Stash / Clash 系列兼容过滤 | 2026-04-24 |
+2 -2
View File
@@ -46,9 +46,9 @@
- 节点管理页现支持墙状态展示、墙状态筛选与关键词搜索;父节点可通过行级或批量操作发起检测,子节点不单独检测并显示“随父节点”的继承状态
- 节点管理页现支持“墙检测托管”开关、批量设置和刷新数据按钮;父节点开启后参与 `sync:server-gfw-checks` 自动检测,自动墙检统计只计算父节点;子节点不独立检测但可控制是否随父节点自动隐藏 / 恢复
- 节点行级菜单现已补齐“置顶节点”,会复用当前排序结果生成新的顺序 payload 并提交到 `server/manage/sort`
- 节点列表中鼠标悬停节点名称会显示节点流量详情卡;`server/manage/getNodes` 会返回 `traffic_stats.today/month/total`三组数据均来自 `v2_stat_server` 按节点聚合,前端统一按 B/KB/MB/GB/TB 自适应格式化展示上行、下行和合计
- 节点列表中鼠标悬停节点名称会显示节点流量详情卡;`server/manage/getNodes` 会返回 `traffic_stats.today/yesterday/month/total`其中今日、昨日和本月按半开时间窗口从 `v2_stat_server` 聚合,累计不加时间窗口,前端统一按 B/KB/MB/GB/TB 自适应格式化展示上行、下行和合计
- 节点新增 / 编辑弹窗现支持月流量限额配置,字段包含启用开关、月流量额度 GB、重置日期、重置时间和时区;保存时会把 GB 转为字节写入 `transfer_enable`,并提交 `traffic_limit_*` 字段
- 节点流量详情卡会在启用限额时追加“月额度”进度、限额状态和下次重置时间,节点标签区同步显示正常 / 接近额度 / 已限额状态;搜索关键字可匹配“流量限额 / 月流量 / 超额下线”
- 节点流量详情卡会在启用限额时追加“月额度”进度、限额状态和下次重置时间,优先使用 `server/manage/getNodes` 返回的 `traffic_limit_snapshot` 展示同 `machine_id` 或同 host 的共享当前账期用量;后端缺少快照时回退 mi-node metrics / `u+d`节点标签区同步显示正常 / 接近额度 / 已限额状态;搜索关键字可匹配“流量限额 / 月流量 / 超额下线”
- 权限组管理页使用真实后端 `server/group/fetch``server/group/save``server/group/drop`,支持关键字搜索、新增/编辑中央弹窗、删除确认,以及从节点数量列跳转到 `#/nodes?group={id}` 的筛选联动
- 路由管理页使用真实后端 `server/route/fetch``server/route/save``server/route/drop`,支持路由列表、关键词搜索、新增/编辑中央弹窗、删除与动作值展示
- 路由管理页的节点引用摘要由 `server/manage/getNodes` 返回的 `route_ids` 推导,不在前端伪造额外接口
+13 -3
View File
@@ -5,22 +5,32 @@
- 保存节点级月流量限额配置、重置规则和运行状态
- 将限额配置下发给 `mi-node`,由节点端执行真实内核停启
- 在手动重置、定时重置和节点 metrics 回传时同步面板侧状态
- 为管理端和 mi-node 下发提供共享月额度快照,统一计算同机器 / 同 host 节点的当前账期已用流量
## 行为规范
- `v2_server.transfer_enable` 是节点月流量额度,单位为字节;新增字段只负责启用状态、重置日、重置时间、时区和运行状态
- `v2_server.transfer_enable` 是节点月流量额度配置,单位为字节;新增字段只负责启用状态、重置日、重置时间、时区和运行状态
- `traffic_limit_enabled=false``transfer_enable<=0` 时不启用节点限额,`ServerTrafficLimitService::buildNodeConfig()` 仍会下发 disabled 配置,旧行为保持不变
- 重置日支持 `1-31`,短月按当月最后一天计算;重置时间使用 `HH:mm`,时区优先使用节点字段,空值或非法值回退 `config('app.timezone')`
- 月额度使用量按共享范围计算:优先按 `machine_id` 聚合,未绑定机器时按规范化后的 `host` 聚合,空 host 回退单节点范围;同一范围内的节点在管理端快照中返回相同 `used / scope_key / scope_node_ids`
- 共享月额度按当前账期统计:`ServerTrafficLimitService::calculateCurrentCycleStartAt()` 会取当前时间之前最近一次重置边界,`current_used` 优先聚合 `v2_stat_server.record_type='d'``[cycle_start_at, now]` 日粒度窗口内的 `u+d`
- 当前账期没有统计行时回退共享范围内 `v2_server.u + v2_server.d`;同范围任一节点有当前有效的 mi-node runtime metrics 时,快照会取 metrics `used` 的最大值并保留同限额下的 `suspended` 运行态
- `ServerTrafficLimitService::buildNodeConfig()` 下发给 mi-node 的 `traffic_limit.current_used` 使用共享账期口径,不再只使用当前单节点的 `u+d`
- `ServerTrafficLimitService::buildSnapshotsForServers()` 为管理端节点列表批量生成 `traffic_limit_snapshot`,避免前端自行按 IP 猜测共享规则
- 管理端保存节点后调用 `ServerTrafficLimitService::refreshSchedule()` 计算 `traffic_limit_next_reset_at`,并通过 `NodeSyncService::notifyConfigUpdated()` 通知节点更新配置
- 手动重置和定时重置统一走 `ServerTrafficLimitService::resetServer()`:清空节点 `u/d`,恢复 `traffic_limit_status=normal`,记录 `traffic_limit_last_reset_at`,计算下一次重置时间,并触发 `notifyFullSync()`
- 手动重置和定时重置统一走 `ServerTrafficLimitService::resetServer()`:清空当前节点 `u/d`,恢复 `traffic_limit_status=normal`,记录 `traffic_limit_last_reset_at`,计算下一次重置时间,并触发 `notifyFullSync()`;该接口不批量重置同共享范围的其他节点
- `sync:server-traffic-limits` 每分钟扫描到期且启用限额的节点,只处理 `traffic_limit_next_reset_at <= now()` 的记录,不影响未启用限额的节点
- `ServerService::updateMetrics()` 会缓存 `metrics.traffic_limit` 并把节点端 `suspended / last_reset_at / next_reset_at / suspended_at` 写回 `v2_server`
- 限额下线不修改 `show``auto_online` 或墙检测字段;真实下线由 `mi-node` 调用内核 `Stop()` 完成
- 限额下线不修改父节点自身的 `show``auto_online` 或墙检测字段;真实下线由 `mi-node` 调用内核 `Stop()` 完成
- 父节点限额状态变为 `suspended` 时会通过 `ServerParentVisibilityService` 自动隐藏当时仍显示的直接子节点,并写入 `parent_auto_hidden=1`;限额重置或恢复 `normal` 后只恢复这些由父节点联动自动隐藏的子节点,原本手动隐藏的子节点保持隐藏
## 依赖关系
- 依赖 `app/Services/ServerTrafficLimitService.php` 统一处理配置下发、时间计算、状态回写和重置
- 依赖 `app/Services/ServerParentVisibilityService.php` 在父节点限额下线 / 恢复时同步直接子节点显隐
- 依赖 `app/Services/ServerService.php` 在节点配置中追加 `traffic_limit` 并消费节点 metrics
- 依赖 `app/Http/Controllers/V2/Admin/Server/ManageController.php``server/manage/getNodes` 响应中返回 `traffic_limit_snapshot`
- 依赖 `v2_stat_server` 的日统计记录作为当前账期共享已用流量的主要来源
- 依赖 `app/Observers/ServerObserver.php` 在限额配置变化时推送 `sync.config`
- 依赖 `app/Console/Commands/SyncServerTrafficLimits.php` 与 Laravel Scheduler 执行到期重置
- 依赖管理端 `admin-frontend/src/utils/nodeEditor*``admin-frontend/src/utils/nodes.ts``admin-frontend/src/views/nodes/*` 提供配置与展示入口