"""新手引导完成标记的读写(按 user_id + device_id 去重)。 登录时 [is_completed] 判断该 (账号, 设备) 是否走过引导;走完引导时 [mark_completed] 写一条。 device_id 为空(老客户端 / 取不到 ANDROID_ID)一律按"未完成"处理,即照常走引导,不误跳过。 """ from __future__ import annotations from sqlalchemy import delete, select from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session from app.models.onboarding import OnboardingCompletion def is_completed(db: Session, *, user_id: int, device_id: str) -> bool: """该 (账号, 设备) 是否已完成新手引导。device_id 为空 → 一律 False。""" if not device_id: return False stmt = select(OnboardingCompletion.id).where( OnboardingCompletion.user_id == user_id, OnboardingCompletion.device_id == device_id, ) return db.execute(stmt).first() is not None def mark_completed(db: Session, *, user_id: int, device_id: str) -> None: """走完引导时写入。device_id 为空忽略;同 (账号, 设备) 已存在则幂等(靠唯一约束兜并发)。""" if not device_id: return db.add(OnboardingCompletion(user_id=user_id, device_id=device_id)) try: db.commit() except IntegrityError: # 并发 / 重复提交撞唯一约束:已有行即视为成功。 db.rollback() def delete_completion(db: Session, *, user_id: int, device_id: str) -> int: """删该 (账号, 设备) 的引导完成标记 → 下次登录 is_completed=False → 客户端重走。 与 [mark_completed] 互逆。device_id 为空忽略(返 0);无记录也幂等(返 0)。返回删除行数。""" if not device_id: return 0 result = db.execute( delete(OnboardingCompletion).where( OnboardingCompletion.user_id == user_id, OnboardingCompletion.device_id == device_id, ) ) db.commit() return result.rowcount