修复: 用户维度限速在过期节点组分支生效 + 统一 speed_limit 单位为 Mbps

- getServerUserListLogic.getExpiredUsers 之前完全忽略 user_subscribe.speed_limit,
  现在带出用户级覆盖并与过期节点组 speed_limit 取更严(mergeSpeedLimit:0 视为无限制)
- node_group.SpeedLimit 注释从 "KB/s" 修正为 "Mbps"(旧注释是笔误,实际下发节点的
  ServerUser.SpeedLimit 字段语义就是 Mbps,节点端 ppanel-node 按 *1e6/8 换算为 Byte/s)
- apis/node/node.api 给 ServerUser.SpeedLimit 加 Mbps 单位注释
- 新增 TestMergeSpeedLimit 表驱动测试覆盖 8 种边界

主链路(活跃用户、套餐 traffic_limit 阶梯)行为不变,已在生产 (server_id=52)
验证 147 个限速用户下发正确,与 DB 完全对应。
This commit is contained in:
2026-06-12 22:39:15 -07:00
parent 08434cfa32
commit 3d1a31a19f
4 changed files with 61 additions and 7 deletions
@@ -258,13 +258,13 @@ func (l *GetServerUserListLogic) GetServerUserList(req *types.GetServerUserListR
}
}
// 处理过期订阅用户:如果当前节点属于过期节点组,添加符合条件的过期用户
// 处理过期订阅用户:如果当前节点属于过期节点组,添加符合条件的过期用户
// 用户级 speed_limit (user_subscribe.speed_limit) 与过期节点组 speed_limit
// 取更严格的一个 — 0 视为"无限制",正值优先于 0。
if len(nodeGroupIds) > 0 {
expiredUsers, expiredSpeedLimit := l.getExpiredUsers(nodeGroupIds)
for i := range expiredUsers {
if expiredSpeedLimit > 0 {
expiredUsers[i].SpeedLimit = expiredSpeedLimit
}
expiredUsers[i].SpeedLimit = mergeSpeedLimit(expiredUsers[i].SpeedLimit, expiredSpeedLimit)
}
users = append(users, expiredUsers...)
}
@@ -369,14 +369,34 @@ func (l *GetServerUserListLogic) getExpiredUsers(serverNodeGroupIds []int64) ([]
}
seen[userSub.Id] = true
users = append(users, types.ServerUser{
Id: userSub.Id,
UUID: userSub.UUID,
Id: userSub.Id,
UUID: userSub.UUID,
SpeedLimit: userSub.SpeedLimit,
})
}
return users, int64(expiredGroup.SpeedLimit)
}
// mergeSpeedLimit 返回两个速度限制(Mbps)中更严格的一个。
// 0 视为"无限制",因此会被任意正值覆盖;都为 0 时返回 0。
// 用于用户级 speed_limit 与节点组级 speed_limit 的合并:
// - both 0 → 0 (不限速)
// - 仅一个 > 0 → 取该值
// - both > 0 → 取较小者(更严格)
func mergeSpeedLimit(a, b int64) int64 {
if a <= 0 {
return b
}
if b <= 0 {
return a
}
if a < b {
return a
}
return b
}
func (l *GetServerUserListLogic) checkExpiredUserEligibility(userSub *user.Subscribe, expiredGroup *group.NodeGroup) bool {
expiredDays := int(time.Since(userSub.ExpireTime).Hours() / 24)
if expiredDays > expiredGroup.ExpiredDaysLimit {