Files
s2f/backend/app/services/auth.py
T
freedakgmail 33b4c734aa feat: 完善前后端核心功能模块
后端:
- 新增认证(auth)、任务(tasks)、映射(mappings)、对账(reconciliation)、异常(exceptions)、导出(exports) API
- 新增核心模块: database, security, permissions, tenant, exceptions, error_handlers
- 新增数据模型: user, company, reconciliation_task, field_mapping, uploaded_file 等
- 新增服务层: ai_recognizer, file_parser, file_storage, mapping, reconciliation 等
- 添加数据库迁移脚本

前端:
- 新增登录页面和仪表盘页面
- 新增任务列表、任务详情、字段映射页面
- 新增异常处理页面和规则设置页面
- 新增 API 代理路由 /api/[...path]
- 新增 UI 组件库 (button, card, dialog, input, table 等)
- 新增 auth 组件 (ProtectedRoute, PermissionGate)
- 新增 layout 组件 (Header, Sidebar)
- 新增 mapping 组件 (FieldMappingTable, AISuggestionPanel)
- 新增 API 客户端和 hooks (useAsync, useToast, usePermission 等)
- 新增状态管理 (auth-store, company-store, ui-store)
- 集成 Tailwind CSS 和 shadcn/ui 组件库

其他:
- 添加 Alembic 数据库迁移配置
- 添加初始化示例数据脚本
- 更新项目文档
2026-07-07 09:04:47 +08:00

158 lines
4.5 KiB
Python

from datetime import datetime
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.core.security import create_access_token, hash_password, verify_password, decode_access_token
from app.models.user import User, UserStatus
from app.schemas.user import UserCreate, UserLogin, Token
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login")
class AuthService:
"""认证服务"""
@staticmethod
async def register_user(
db: AsyncSession,
user_data: UserCreate,
) -> User:
"""
注册用户
Args:
db: 数据库会话
user_data: 用户数据
Returns:
创建的用户对象
"""
# 检查邮箱是否已存在
result = await db.execute(select(User).where(User.email == user_data.email))
if result.scalar_one_or_none():
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="邮箱已被注册"
)
# 创建用户
hashed_password = hash_password(user_data.password)
user = User(
company_id=user_data.company_id,
email=user_data.email,
hashed_password=hashed_password,
full_name=user_data.full_name,
role=user_data.role,
)
db.add(user)
await db.commit()
await db.refresh(user)
return user
@staticmethod
async def authenticate_user(
db: AsyncSession,
login_data: UserLogin,
) -> tuple[User, str]:
"""
认证用户并生成 Token
Args:
db: 数据库会话
login_data: 登录数据
Returns:
用户对象和访问令牌
Raises:
HTTPException: 认证失败
"""
# 查找用户
result = await db.execute(select(User).where(User.email == login_data.email))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="邮箱或密码错误"
)
# 验证密码
if not verify_password(login_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="邮箱或密码错误"
)
# 检查用户状态
if user.status != UserStatus.ACTIVE.value:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"用户已被{user.status}"
)
# 更新最后登录时间
user.last_login_at = datetime.utcnow()
await db.commit()
await db.refresh(user)
# 生成 Token
access_token = create_access_token(data={"sub": str(user.id), "email": user.email})
return user, access_token
@staticmethod
async def get_current_user(
token: str = Depends(oauth2_scheme),
db: AsyncSession = Depends(get_db),
) -> User:
"""
获取当前用户
Args:
token: JWT token
db: 数据库会话
Returns:
当前用户对象
Raises:
HTTPException: Token 无效或用户不存在
"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无法验证凭据",
headers={"WWW-Authenticate": "Bearer"},
)
payload = decode_access_token(token)
if not payload:
raise credentials_exception
user_id: str = payload.get("sub")
if not user_id:
raise credentials_exception
result = await db.execute(select(User).where(User.id == int(user_id)))
user = result.scalar_one_or_none()
if not user:
raise credentials_exception
if user.status != UserStatus.ACTIVE.value:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="用户已被禁用"
)
return user
# 单例实例
auth_service = AuthService()