feat(server): 设备上限登录闸(超限非硬拒登,返回 device_limit 信号)#16
启用设备数量限制的服务端部分。登录照常成功签发 token(非硬拒登),但若账户活跃 设备数超套餐上限,登录响应带 device_limit 信号,客户端据此弹「移除设备」页。 - devices/store.go:CountActiveDevices(last_seen 近 staleWindow)+ PruneStaleDevices (删超期僵尸行,免费版重装 churn 自愈) - devices/service.go:staleWindow=30d;DeviceLimitStatus + CheckDeviceLimit (best-effort prune → ResolvePlan → 活跃 count > cap 即 Over,附活跃设备列表) - auth:DeviceRegistrar 加 CheckDeviceLimit;recordLogin 回传 *DeviceLimit; LoginOutcome.DeviceLimit;Login 透传;handler tokenPairResponse.device_limit(omitempty) - main.go:authDeviceRegistrar 适配 devices.CheckDeviceLimit → auth.DeviceLimit - 测试:auth 登录透传超限信号(仍签发 token);devices within/over/prune-stale 连接侧 backstop(服务端硬拦)本轮从简未做,作为后续硬化(登录闸为客户端可信信号)。 DB 无 schema 变更。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -538,3 +538,22 @@ func (a authDeviceRegistrar) RegisterDevice(ctx context.Context, userID int64, m
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// CheckDeviceLimit adapts devices.CheckDeviceLimit → auth.DeviceLimit for the login
|
||||
// over-limit gate. Returns nil (within cap) unless the account is over its plan cap.
|
||||
func (a authDeviceRegistrar) CheckDeviceLimit(ctx context.Context, userID int64) (*auth.DeviceLimit, error) {
|
||||
st, apiErr := a.svc.CheckDeviceLimit(ctx, userID)
|
||||
if apiErr != nil {
|
||||
return nil, apiErr
|
||||
}
|
||||
if st == nil || !st.Over {
|
||||
return nil, nil
|
||||
}
|
||||
briefs := make([]auth.DeviceBrief, 0, len(st.Devices))
|
||||
for _, d := range st.Devices {
|
||||
briefs = append(briefs, auth.DeviceBrief{
|
||||
UUID: d.UUID, Name: d.Name, Platform: d.Platform, LastSeen: d.LastSeen,
|
||||
})
|
||||
}
|
||||
return &auth.DeviceLimit{MaxDevices: st.MaxDevices, Devices: briefs}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user