feat(server/devices): users.max_devices_override 单用户设备上限覆盖(NULL走套餐默认)+ 迁移000025
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (pull_request) Successful in 23s
ci-pangolin / Cleartext Scan — Android 禁明文 (pull_request) Successful in 16s
ci-pangolin / OpenAPI Sync Check (pull_request) Successful in 39s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (pull_request) Successful in 19s
ci-pangolin / Flutter — analyze + test (pull_request) Successful in 36s
ci-pangolin / Codegen Drift — token 生成物未漂移 (pull_request) Successful in 3s
ci-pangolin / DS-flow — 原型/跨端同源/代码色单源闸 (pull_request) Successful in 4s
ci-pangolin / Go — build + test (pull_request) Failing after 13s
ci-pangolin / E2E Smoke — L4 进程级端到端 (pull_request) Failing after 10s
ci-pangolin / Go — integration (mysql/redis testcontainers) (pull_request) Failing after 5m8s
ci-pangolin / Golden — 视觉回归 (全量:components/auth/desktop/tablet) (pull_request) Failing after 19s
ci-pangolin / Lint — shellcheck (pull_request) Failing after 13m37s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P9G7E3wmAYL9KeYCVZVsqu
This commit is contained in:
wangjia
2026-07-13 10:15:07 +08:00
parent 3d24451835
commit 07c339c18e
9 changed files with 260 additions and 7 deletions
+20
View File
@@ -299,6 +299,26 @@ func (s *Store) GetFreePlan(ctx context.Context) (Plan, error) {
return p, nil
}
// GetMaxDevicesOverride returns the per-user device-cap override set by
// migration 000025 (users.max_devices_override). NULL or <=0 means "no
// override" (ok=false) — the caller should fall back to the plan-derived cap.
// A positive value always wins, even if it is *smaller* than the plan's cap,
// so operators can also tighten a specific account.
func (s *Store) GetMaxDevicesOverride(ctx context.Context, userID int64) (int, bool, error) {
var override sql.NullInt64
row := s.db.QueryRowContext(ctx, `SELECT max_devices_override FROM users WHERE id=?`, userID)
if err := row.Scan(&override); err != nil {
if err == sql.ErrNoRows {
return 0, false, nil
}
return 0, false, fmt.Errorf("store.GetMaxDevicesOverride: %w", err)
}
if !override.Valid || override.Int64 <= 0 {
return 0, false, nil
}
return int(override.Int64), true, nil
}
// --------------------------------------------------------------------------
// Audit log
// --------------------------------------------------------------------------