Repository navigation
feat(selector): 加权与区域偏好选线 / Weighted & region-aware line auto-selection - #85
Conversation
7a5bdc8 to
79498df
Compare
…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>
叠加 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>
fc20f75 to
90d41f8
Compare
PIKACHUIM
left a comment
There was a problem hiding this comment.
🙏 感谢贡献 @suantea 持续迭代!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 Claude Opus 5 模型进行分析。
🎯 结论
🔄 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>
33a8654 to
5191d92
Compare
pikachuren
left a comment
There was a problem hiding this comment.
🙏 感谢 @suantea 提交!
🤖 AI 自动审核声明:本评审报告由 AI 自动生成,当前使用 DeepSeek-V4.1-Flash 模型进行分析。
增量口径说明(重要):上轮评审锚点
33a8654035已不可按 sha 获取(git fetch origin 33a8654035返回couldn't find remote ref,该 commit 已被 rebase 掉、在远端不可达),因此改用内容比对:经 GitHub API 取旧分支的 feature commit7a5bdc8ccc及其父b688e5fc37的原始文件,与当前 HEAD5191d92dff的文件逐文件 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:42Weight 默认值语义歧义 — 未处理。注释仍写「>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 一起收拾)。
…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>
…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>
叠加 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>
…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>
叠加 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>
变更内容 / What
多线路池方案(规划 issue #84,对齐 #50/#53/#54)的 M1 里程碑:纯本地、无节点依赖,为后续多节点线路池打底。
selector.Line新增可选字段Weight/Region(零值 = 旧语义,完全向后兼容)Weight != 0即激活——选线按score = 有效延迟 / Weight排序(权重越大越优);加权模式下Weight <= 0的线路不参与自动选线,但仍保留在集合中可展示与手动锁定(与现有 toolFilter 锁线语义一致)SetPreferRegion后命中偏好区域的可用线路优先(就近优先),区域内仍按 score 排序;偏好区域不可用时自动回退其他区域scoreFor,旧纯延迟模式行为不变检查 / Checks
go vet ./service/selector/通过go test ./service/selector/ ./service/linereg/ -count=1全绿(含既有测试,无回归)go build通过(根 main 因本机未构建前端 dist 无法 embed,跳过)设计取舍
关联