← 文档索引

设备数量限制 + 超限 UX(设计 · #16)

Pangolin · 真正启用套餐设备上限 + 非硬拒登的「移除设备」体验 · 2026-07-01

plans.max_devices(free 1 / pro 3 / team 10)过去只展示、不强制:登录注册写死 MaxDevices=0CheckDeviceQuota 是死代码。本次真正启用上限,按卡在登录(非硬拒登) + 选择移除/一键踢最旧 + 服务端自动清理久不活跃落地。

1. 关键决策

维度选择理由
执行点登录(+ 连接 backstop 待硬化)登录照常签发 token,超限只返回 device_limit 信号、客户端挡在「移除设备」页;不硬拒登。
解决方式选择移除 + 一键「移除最久未用」兼顾控制与便捷;移除即调现成 DeleteDevice(撤销会话+删行+召回 dp_uuid 凭证→设备立即离线登出)。
免费重装 churn服务端自动清理久不活跃count 只算近 staleWindow=30dlast_seen)活跃设备;登录 best-effort 删超期僵尸行 → 重装刷新的 UUID 自愈,不锁死用户。

2. 数据库

无 schema 变更:复用 plans.max_devicesdevices.last_seensessions。仅新增常量 staleWindow=30d。 活跃计数 SELECT COUNT(1) FROM devices WHERE user_id=? AND last_seen IS NOT NULL AND last_seen > now-30d

另:本次把 pro 上限从 5 改为 3(seed + 迁移 000019;线上需 migrate up 生效)。

3. 后端

internal/devices
internal/auth(登录闸,非硬拒登)

登录响应(超限时):

{ "access_token":"…", "refresh_token":"…", "expires_in":900,
  "device_limit": { "max_devices":1,
    "devices":[ {"uuid":"…","name":"iPhone","platform":"ios","last_seen":"2026-06-30T…Z"} ] } }

待硬化 连接 backstop(ConnectNode 拒超限)本轮从简未做:登录闸是服务端权威信号、客户端据此自挡;后续在 Entitlement + NodeStore 加 count 做服务端硬拦。

4. 前端

5. 验证

6. 不在本轮