Files
meijiaka-zy/python-api/app/crud/user.py
T
小鱼开发 923ff63a3d feat: 密码登录功能(验证码/密码双模式 + 忘记密码 + 设置密码)
后端:
- security.py: 新增 bcrypt 密码哈希/校验工具
- auth_service.py: 新增 login_with_password、reset_password_with_sms
- auth.py: 新增 /login-password、/has-password、/set-password、/reset-password 接口
- schemas/auth.py: 新增 PasswordLoginRequest、SetPasswordRequest、ResetPasswordRequest、CheckPasswordResponse
- crud/user.py: 新增 update_password

前端:
- Login.tsx: 支持验证码/密码切换登录,密码模式下显示忘记密码入口
- Login.css: 新增登录方式切换标签、密码输入框样式
- authStore.ts: 新增 loginWithPassword
- Settings.tsx: 新增账号安全区块,显示密码状态,打开设置/修改密码弹窗
- SetPasswordModal.tsx: 设置/修改密码弹窗(旧密码校验、密码显示切换、表单验证)
- ResetPasswordModal.tsx: 忘记密码弹窗(手机号+验证码+新密码重置)

兼容:
- 零数据库迁移,password_hash 字段已存在(nullable)
- 现有接口不变,完全向后兼容旧版本
2026-06-09 23:26:50 +08:00

105 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
用户 CRUD 操作
==============
用户认证相关的数据访问。
"""
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.crud.base import CRUDBase
from app.models.user import User
class UserCRUD(CRUDBase[User]):
"""用户数据访问对象"""
def __init__(self) -> None:
super().__init__(User)
async def get_by_mobile(self, db: AsyncSession, *, mobile: str) -> User | None:
"""根据手机号获取用户"""
result = await db.execute(select(User).where(User.mobile == mobile))
return result.scalar_one_or_none()
async def get_or_create_by_mobile(
self, db: AsyncSession, *, mobile: str, nickname: str | None = None, source: str = "unknown"
) -> User:
"""
根据手机号获取或创建用户
Returns:
已存在或新创建的用户
"""
user = await self.get_by_mobile(db, mobile=mobile)
if user is None:
# 创建新用户
user = await self.create(
db,
obj_in={
"mobile": mobile,
"nickname": nickname or f"用户_{mobile[-4:]}",
"source": source,
},
)
return user
async def update_login_info(
self, db: AsyncSession, *, user_id: str, ip: str | None = None
) -> User | None:
"""
更新用户最后登录信息
"""
from datetime import UTC, datetime
user = await self.get(db, id=user_id)
if user is None:
return None
user.last_login_at = datetime.now(UTC)
if ip:
user.last_login_ip = ip
await db.commit()
await db.refresh(user)
return user
async def update_password(
self, db: AsyncSession, *, user_id: str, password_hash: str
) -> User | None:
"""更新用户密码"""
user = await self.get(db, id=user_id)
if user is None:
return None
user.password_hash = password_hash
await db.commit()
await db.refresh(user)
return user
async def update_extra(
self, db: AsyncSession, *, user_id: str, extra: dict
) -> bool:
"""
原子更新用户 extra 字段(JSONB
使用 SQLAlchemy 的 update 语句避免读-改-写的竞态条件。
"""
from sqlalchemy import update
stmt = (
update(User)
.where(User.id == user_id)
.values(extra=extra)
)
result = await db.execute(stmt)
await db.commit()
return result.rowcount > 0
# 导出实例
user = UserCRUD()