"""user 表的 CRUD 工具函数。 业务策略 = "注册即登录":phone 不存在则 insert,存在则更新 last_login_at。 """ from __future__ import annotations from datetime import datetime, timezone from sqlalchemy import select from sqlalchemy.orm import Session from app.models.user import User def get_user_by_id(db: Session, user_id: int) -> User | None: return db.get(User, user_id) def get_user_by_phone(db: Session, phone: str) -> User | None: stmt = select(User).where(User.phone == phone) return db.execute(stmt).scalar_one_or_none() def upsert_user_for_login( db: Session, *, phone: str, register_channel: str, ) -> User: """登录入口统一调:有则更新 last_login_at,无则新建。 返回 commit 后的 user。调用方拿到 user.id 直接签 JWT。 """ user = get_user_by_phone(db, phone) now = datetime.now(timezone.utc) if user is None: user = User( phone=phone, register_channel=register_channel, last_login_at=now, ) db.add(user) else: user.last_login_at = now # 被禁用的账号被尝试登录时,不自动启用,直接抛(在调用方处理) db.commit() db.refresh(user) return user def update_nickname(db: Session, user: User, *, nickname: str) -> User: user.nickname = nickname db.commit() db.refresh(user) return user def set_avatar_url(db: Session, user: User, *, avatar_url: str) -> User: user.avatar_url = avatar_url db.commit() db.refresh(user) return user def soft_delete_account(db: Session, user: User) -> None: """注销账号:软删除 + 匿名化。 把 phone 改成 `deleted_` 释放唯一约束,允许同号码重新注册成全新账号; 清空 PII(昵称/头像),保留行用于审计。status=deleted 后将无法再登录该行 (登录接口校验 status==active)。 """ user.status = "deleted" user.phone = f"deleted_{user.id}" user.nickname = None user.avatar_url = None db.commit()