Skip to content

feat(selector): 加权与区域偏好选线 / Weighted & region-aware line auto-selection - #85

Merged
PIKACHUIM merged 1 commit into
PIKACHUIM:mainfrom
suantea:feat/selector-weighted
Sep 20, 2026
Merged

PIKACHUIM merged 1 commit into
PIKACHUIM:mainfrom
suantea:feat/selector-weighted

Conversation

@suantea

@suantea suantea commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

变更内容 / What

多线路池方案(规划 issue #84,对齐 #50/#53/#54)的 M1 里程碑:纯本地、无节点依赖,为后续多节点线路池打底。

  • selector.Line 新增可选字段 Weight / Region(零值 = 旧语义,完全向后兼容)
  • 加权模式:集合内存在任意 Weight != 0 即激活——选线按 score = 有效延迟 / Weight 排序(权重越大越优);加权模式下 Weight <= 0 的线路不参与自动选线,但仍保留在集合中可展示与手动锁定(与现有 toolFilter 锁线语义一致)
  • 区域偏好:SetPreferRegion 后命中偏好区域的可用线路优先(就近优先),区域内仍按 score 排序;偏好区域不可用时自动回退其他区域
  • 防抖(tolerance)比较改用统一 scoreFor,旧纯延迟模式行为不变
  • 新增 6 个单测:加权择优 / 旧行为兼容 / weight-0 排除 / 排除线路仍可锁线 / 区域优先 / 区域不可用回退

检查 / Checks

  • go vet ./service/selector/ 通过
  • go test ./service/selector/ ./service/linereg/ -count=1 全绿(含既有测试,无回归)
  • 后端全子包 go build 通过(根 main 因本机未构建前端 dist 无法 embed,跳过)

设计取舍

  • Weight 语义:全 0(未配置)= 纯延迟旧行为;任一 Weight>0 即加权模式,Weight<=0 显式排除——避免引入指针/开关字段
  • Region 就近优先做"分档优先"而非算术加权,保持 score 标量可比,tolerance 防抖可直接复用
  • 多池分组抽象(PoolSelector)不在此 PR 范围,待远程节点线路接入(M4)再上,避免过度设计

关联

PIKACHUIM

This comment was marked as outdated.

@suantea
suantea force-pushed the feat/selector-weighted branch from 7a5bdc8 to 79498df Compare September 5, 2026 05:42
suantea added a commit to suantea/NetPanel that referenced this pull request Sep 5, 2026
…UIM#50)

- 新增 model.FrpMasterNode:节点注册表(name/region/frps 连接信息/最近心跳);
  node token 仅存 SHA-256,明文只在创建接口返回一次
- 新增 service/frpmaster:
  - Manager:节点 CRUD、token 恒定时间校验、心跳刷新、状态上报原样存储,
    在线状态按 LastSeen 窗口派生(无需后台定时器)
  - config.go:frpc.toml 生成器(frp 0.52+ camelCase;[common] 最小集 +
    tcp/udp/http/https/stcp/xtcp 代理子集,非法代理跳过并注释)
- API:管理面(面板 JWT)GET/POST /frpmaster/nodes、DELETE /frpmaster/nodes/:id、
  GET /frpmaster/nodes/:id/config;节点控制面(node_id+token 认证,无 JWT)
  POST /frpmaster/agent/heartbeat、/agent/status、GET /frpmaster/agent/config
- main.go 接线 + autoMigrate 注册新模型
- 测试:config 生成 6 例、manager 7 例、handler httptest 模拟节点全流程
  (注册拿 token → 心跳 401/200 → 拉取 frpc.toml → 状态上报 → 列表 online)

说明:根 main 包依赖前端 dist(go:embed),本机未构建前端故本地跳过根包
编译;子包 go vet/test/build 全绿(与 M1 PR PIKACHUIM#85 相同的验证口径)。

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
suantea added a commit to suantea/NetPanel that referenced this pull request Sep 5, 2026
叠加 M3(PIKACHUIM#86 frpc Master 控制面)实现多线路池的远程候选来源:

- frpmaster.Manager.Lines():在线节点(心跳窗口内)注册为一条候选线路
  fnode:<id>(Tool=frpc-remote,Address=其 frps 服务端入口);节点离线即
  不再提供,由 linereg SetLines 全量刷新自动清理选线状态与锁线
- linereg.SetRemoteLineProvider:refresh 每轮合并远程线路参与自动测速选线
  (main.go 接线 frpMasterMgr.Lines);本机 BuildLines 语义不变
- frpmaster.SaveLogs + POST /frpmaster/agent/logs:节点日志回传聚合到
  SystemLog(Service=frpmaster,消息带节点 id 前缀;单批上限 100 防打爆)
- 测试:Lines 仅在线节点提供(离线/心跳超时剔除);refresh 合并与移除
  远程线路(7→6 条);SaveLogs 空行跳过/前缀/截断;agent logs 鉴权与
  written 计数

注:候选线路的 Region/Weight 字段依赖 M1(PIKACHUIM#85)合入后补全;节点侧 agent
与隧道/池 UI、MCP 工具暴露留待 M4 后续。

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
@suantea
suantea force-pushed the feat/selector-weighted branch from fc20f75 to 90d41f8 Compare September 5, 2026 07:36

@PIKACHUIM PIKACHUIM left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 感谢贡献 @suantea 持续迭代!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。
⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。
⚠️ 重要提醒:最终是否合并由项目维护者人工判定。

🎯 结论

🔄 Request Changes(出现新冲突,需先 rebase)

🔄 本轮变化

分支 rebase 后追加 2 个公共 commit(ci: fix PR checks + fix(selector): ProbeLines),feature commit(feat(selector): 加权与区域偏好选线)无改动。

🚨 关键问题

[P0] 分支与 main 产生冲突(mergeable: dirty)

compare 显示 ahead 3 / behind 1,冲突文件:

  • .github/workflows/pr-checks.yml

原因:公共 commit ci: fix PR checks - frontend artifact upload/download for go embed 修改了 pr-checks.yml,而 main 上也有对该文件的改动(fix(ci): text error 等),两边同时改导致冲突。

建议:git rebase main 并解决 pr-checks.yml 冲突(合并 main 的 CI 改动 + 本分支的 artifact 上传逻辑),推送后即可恢复 mergeable。

✅ 上轮问题回查

  • 上轮 Approve:加权 + 区域偏好选线实现与测试优秀,仅 P2 注释边界说明。
  • feature 内容本轮无变化,无新代码问题。

🎯 结论:🔄 Request Changes — 仅需解决 pr-checks.yml 冲突即可恢复可合并状态。

多线路池方案(规划 PIKACHUIM#84,对齐 PIKACHUIM#50/PIKACHUIM#53/PIKACHUIM#54)的 M1 里程碑:纯本地、无节点,
selector 单组能力增强,为后续多节点线路池打底。

- Line 新增可选字段 Weight / Region(零值 = 旧语义,完全向后兼容)
- 加权模式:集合内存在任意 Weight != 0 即激活,选线按 score = 有效延迟/Weight
  排序(权重越大越优);加权模式下 Weight <= 0 的线路不参与自动选线,
  但仍保留在集合中可展示与手动锁定(与 toolFilter 锁线语义一致)
- 区域偏好:SetPreferRegion 后命中偏好区域的可用线路优先(就近优先),
  区域内仍按 score 排序;偏好区域不可用时自动回退其他区域
- 防抖比较改用 scoreFor,旧纯延迟模式行为不变
- 新增 6 个单测:加权择优 / 旧行为兼容 / weight-0 排除 / 排除线路可锁线 /
  区域优先 / 区域不可用回退

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
@suantea
suantea force-pushed the feat/selector-weighted branch from 33a8654 to 5191d92 Compare September 10, 2026 14:16

@pikachuren pikachuren left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 感谢 @suantea 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 DeepSeek-V4.1-Flash 模型进行分析。
⚠️ AI 分析结果仅供参考,可能存在误判或遗漏。如您发现任何问题或有不同意见,欢迎随时提出讨论和纠正。
⚠️ 重要提醒:即使 AI 评审认为代码质量良好且建议合并,最终是否合并仍需由项目维护者进行人工判定。项目维护者会综合考虑代码质量、项目规划、技术方向、团队资源等多方面因素做出是否合并的决策。

增量口径说明(重要):上轮评审锚点 33a8654035 已不可按 sha 获取(git fetch origin 33a8654035 返回 couldn't find remote ref,该 commit 已被 rebase 掉、在远端不可达),因此改用内容比对:经 GitHub API 取旧分支的 feature commit 7a5bdc8ccc 及其父 b688e5fc37 的原始文件,与当前 HEAD 5191d92dff 的文件逐文件 diff。比对结果:除 main 后来并入的 ProbeLines 取消修复(c33387d / #88,属上游代码)外,本 PR 自身内容零变化。
行号为本轮 HEAD 文件的真实行号。

🔄 增量评审:33a8654035 → 5191d92dff

本轮新增 1 个 commits / 变更 2 个文件:backend/service/selector/selector.go、backend/service/selector/selector_test.go

(实为「rebase 到最新 main + squash 为单一 commit」:原分支上的 2 个公共 commit ci: fix PR checks、fix(selector): ProbeLines 已随上游并入而消失,功能代码逐字未改。)

新增改动的问题(按 P0/P1/P2 分级):

  • ⚠️ [P2] PR 描述已过时 — 描述中「本地既有 TestProbeLinesCancellation 失败……建议另立小 PR 修复」这一备注在本轮已不适用:main 已于 c33387d(#88)合入同一份取消修复,本分支 selector.go:392-401 的该段与 main 完全一致,不再是本 PR 的分歧点。建议更新描述,避免维护者据此产生额外疑问。

旧问题解决情况(逐条回查上轮两份评审):

  • ✅ [P0] 分支与 main 冲突(mergeable: dirty,冲突文件 .github/workflows/pr-checks.yml)— 已解决。本轮分支为 dc2877b(origin/main) 之上的单一 commit,git merge-base --is-ancestor origin/main refs/remotes/pr/85 成立(线性、无冲突源);冲突根源「公共 commit 改 pr-checks.yml」已被丢弃,而 main 的 pr-checks.yml:48-72 已自带上游 artifact 上传/下载逻辑,不再需要该 commit。CI 侧两个 check 均为 success(Backend build+test / Frontend typecheck+build)。
  • ❌ [P2] selector.go:42 Weight 默认值语义歧义 — 未处理。注释仍写「>1 强化、0~1 弱化,默认 1.0」,而零值实际是 0(未配置),与 weightedMode()「全 0 = 纯延迟模式」的判定口径仍不一致,建议统一为「0 = 未配置(中性)」。
  • ⏳ [P2] 负权重语义 — 部分解决。selector.go:44 已说明「加权模式下 Weight <= 0 的线路不参与自动选线(仍保留在集合中,可展示与手动锁定)」,覆盖了负值被排除的行为;但 selector.go:524(weightedMode 用 != 0)与 selector.go:534(weightOf 用 > 0)的判定差异仍未点明,即「一条 Weight < 0 的线路会激活加权模式、同时把自己排除在自动选线之外」这层组合语义还差一句注释。
  • N/A 上轮「待处理清单」仅上述两条 P2,无其它遗留项。

🎯 结论:✅ Approve — 上轮唯一的 P0(与 main 冲突)已随 rebase+squash 彻底解决、线性可合并且 CI 全绿,功能内容与上次已认可的实现逐字一致,仅余 2 条 P2 注释语义(可与后续 PR 一起收拾)。

@PIKACHUIM
PIKACHUIM merged commit 26c4301 into PIKACHUIM:main Sep 20, 2026
2 checks passed
suantea added a commit to suantea/NetPanel that referenced this pull request Sep 20, 2026
…UIM#50)

- 新增 model.FrpMasterNode:节点注册表(name/region/frps 连接信息/最近心跳);
  node token 仅存 SHA-256,明文只在创建接口返回一次
- 新增 service/frpmaster:
  - Manager:节点 CRUD、token 恒定时间校验、心跳刷新、状态上报原样存储,
    在线状态按 LastSeen 窗口派生(无需后台定时器)
  - config.go:frpc.toml 生成器(frp 0.52+ camelCase;[common] 最小集 +
    tcp/udp/http/https/stcp/xtcp 代理子集,非法代理跳过并注释)
- API:管理面(面板 JWT)GET/POST /frpmaster/nodes、DELETE /frpmaster/nodes/:id、
  GET /frpmaster/nodes/:id/config;节点控制面(node_id+token 认证,无 JWT)
  POST /frpmaster/agent/heartbeat、/agent/status、GET /frpmaster/agent/config
- main.go 接线 + autoMigrate 注册新模型
- 测试:config 生成 6 例、manager 7 例、handler httptest 模拟节点全流程
  (注册拿 token → 心跳 401/200 → 拉取 frpc.toml → 状态上报 → 列表 online)

说明:根 main 包依赖前端 dist(go:embed),本机未构建前端故本地跳过根包
编译;子包 go vet/test/build 全绿(与 M1 PR PIKACHUIM#85 相同的验证口径)。

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
suantea added a commit to suantea/NetPanel that referenced this pull request Sep 20, 2026
…UIM#50)

- 新增 model.FrpMasterNode:节点注册表(name/region/frps 连接信息/最近心跳);
  node token 仅存 SHA-256,明文只在创建接口返回一次
- 新增 service/frpmaster:
  - Manager:节点 CRUD、token 恒定时间校验、心跳刷新、状态上报原样存储,
    在线状态按 LastSeen 窗口派生(无需后台定时器)
  - config.go:frpc.toml 生成器(frp 0.52+ camelCase;[common] 最小集 +
    tcp/udp/http/https/stcp/xtcp 代理子集,非法代理跳过并注释)
- API:管理面(面板 JWT)GET/POST /frpmaster/nodes、DELETE /frpmaster/nodes/:id、
  GET /frpmaster/nodes/:id/config;节点控制面(node_id+token 认证,无 JWT)
  POST /frpmaster/agent/heartbeat、/agent/status、GET /frpmaster/agent/config
- main.go 接线 + autoMigrate 注册新模型
- 测试:config 生成 6 例、manager 7 例、handler httptest 模拟节点全流程
  (注册拿 token → 心跳 401/200 → 拉取 frpc.toml → 状态上报 → 列表 online)

说明:根 main 包依赖前端 dist(go:embed),本机未构建前端故本地跳过根包
编译;子包 go vet/test/build 全绿(与 M1 PR PIKACHUIM#85 相同的验证口径)。

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
suantea added a commit to suantea/NetPanel that referenced this pull request Sep 20, 2026
叠加 M3(PIKACHUIM#86 frpc Master 控制面)实现多线路池的远程候选来源:

- frpmaster.Manager.Lines():在线节点(心跳窗口内)注册为一条候选线路
  fnode:<id>(Tool=frpc-remote,Address=其 frps 服务端入口);节点离线即
  不再提供,由 linereg SetLines 全量刷新自动清理选线状态与锁线
- linereg.SetRemoteLineProvider:refresh 每轮合并远程线路参与自动测速选线
  (main.go 接线 frpMasterMgr.Lines);本机 BuildLines 语义不变
- frpmaster.SaveLogs + POST /frpmaster/agent/logs:节点日志回传聚合到
  SystemLog(Service=frpmaster,消息带节点 id 前缀;单批上限 100 防打爆)
- 测试:Lines 仅在线节点提供(离线/心跳超时剔除);refresh 合并与移除
  远程线路(7→6 条);SaveLogs 空行跳过/前缀/截断;agent logs 鉴权与
  written 计数

注:候选线路的 Region/Weight 字段依赖 M1(PIKACHUIM#85)合入后补全;节点侧 agent
与隧道/池 UI、MCP 工具暴露留待 M4 后续。

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
suantea added a commit to suantea/NetPanel that referenced this pull request Sep 23, 2026
…UIM#50)

- 新增 model.FrpMasterNode:节点注册表(name/region/frps 连接信息/最近心跳);
  node token 仅存 SHA-256,明文只在创建接口返回一次
- 新增 service/frpmaster:
  - Manager:节点 CRUD、token 恒定时间校验、心跳刷新、状态上报原样存储,
    在线状态按 LastSeen 窗口派生(无需后台定时器)
  - config.go:frpc.toml 生成器(frp 0.52+ camelCase;[common] 最小集 +
    tcp/udp/http/https/stcp/xtcp 代理子集,非法代理跳过并注释)
- API:管理面(面板 JWT)GET/POST /frpmaster/nodes、DELETE /frpmaster/nodes/:id、
  GET /frpmaster/nodes/:id/config;节点控制面(node_id+token 认证,无 JWT)
  POST /frpmaster/agent/heartbeat、/agent/status、GET /frpmaster/agent/config
- main.go 接线 + autoMigrate 注册新模型
- 测试:config 生成 6 例、manager 7 例、handler httptest 模拟节点全流程
  (注册拿 token → 心跳 401/200 → 拉取 frpc.toml → 状态上报 → 列表 online)

说明:根 main 包依赖前端 dist(go:embed),本机未构建前端故本地跳过根包
编译;子包 go vet/test/build 全绿(与 M1 PR PIKACHUIM#85 相同的验证口径)。

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
suantea added a commit to suantea/NetPanel that referenced this pull request Sep 23, 2026
叠加 M3(PIKACHUIM#86 frpc Master 控制面)实现多线路池的远程候选来源:

- frpmaster.Manager.Lines():在线节点(心跳窗口内)注册为一条候选线路
  fnode:<id>(Tool=frpc-remote,Address=其 frps 服务端入口);节点离线即
  不再提供,由 linereg SetLines 全量刷新自动清理选线状态与锁线
- linereg.SetRemoteLineProvider:refresh 每轮合并远程线路参与自动测速选线
  (main.go 接线 frpMasterMgr.Lines);本机 BuildLines 语义不变
- frpmaster.SaveLogs + POST /frpmaster/agent/logs:节点日志回传聚合到
  SystemLog(Service=frpmaster,消息带节点 id 前缀;单批上限 100 防打爆)
- 测试:Lines 仅在线节点提供(离线/心跳超时剔除);refresh 合并与移除
  远程线路(7→6 条);SaveLogs 空行跳过/前缀/截断;agent logs 鉴权与
  written 计数

注:候选线路的 Region/Weight 字段依赖 M1(PIKACHUIM#85)合入后补全;节点侧 agent
与隧道/池 UI、MCP 工具暴露留待 M4 后续。

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants