from fastapi import APIRouter, Depends, HTTPException, status, Request from fastapi.security import OAuth2PasswordRequestForm from sqlalchemy.orm import Session from typing import Optional from app.core.database import get_db from app.core.security import get_current_user, require_permission from app.models.auth import User, UserRole, UserStatus from app.services.user_service import UserService, TokenService from app.services.audit_service import AuditService, AuditAction router = APIRouter(prefix="/auth", tags=["认证管理"]) @router.post("/login", summary="用户登录") def login( request: Request, form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db) ): """用户登录,获取访问令牌""" user = UserService.authenticate(db, form_data.username, form_data.password) if not user: # 记录失败尝试 AuditService.record( db, action=AuditAction.USER_LOGIN_FAILED, username=form_data.username, method="POST", path=str(request.url.path), ip_address=request.client.host if request.client else "", user_agent=(request.headers.get("user-agent") or "")[:500], status="failed", ) raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="用户名或密码错误", headers={"WWW-Authenticate": "Bearer"}, ) # 更新登录信息 UserService.update_login_info(db, user.id, ip_address=request.client.host if request.client else None) # 创建令牌 tokens = TokenService.create_tokens( db, user, ip_address=request.client.host if request.client else None, user_agent=(request.headers.get("user-agent") or "")[:500], ) AuditService.record( db, action=AuditAction.USER_LOGIN, user=user, method="POST", path=str(request.url.path), ip_address=request.client.host if request.client else "", user_agent=(request.headers.get("user-agent") or "")[:500], ) return { **tokens, "user_info": { "id": user.id, "username": user.username, "email": user.email, "real_name": user.real_name, "role": user.role.value } } @router.post("/refresh", summary="刷新访问令牌") def refresh_token(refresh_token: str, db: Session = Depends(get_db)): """使用刷新令牌获取新的访问令牌""" result = TokenService.refresh_access_token(db, refresh_token) if not result: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的刷新令牌或已过期", headers={"WWW-Authenticate": "Bearer"}, ) return result @router.post("/logout", summary="登出") def logout( request: Request, refresh_token: str, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """用户登出,撤销刷新令牌""" TokenService.revoke_token(db, refresh_token) AuditService.record( db, action=AuditAction.USER_LOGOUT, user=current_user, method="POST", path=str(request.url.path), ip_address=request.client.host if request.client else "", user_agent=(request.headers.get("user-agent") or "")[:500], ) return {"message": "登出成功"} @router.get("/me", summary="获取当前用户信息") def get_current_user_info(current_user: User = Depends(get_current_user)): """获取当前登录用户信息""" return { "id": current_user.id, "username": current_user.username, "email": current_user.email, "phone": current_user.phone, "real_name": current_user.real_name, "role": current_user.role.value, "status": current_user.status.value, "last_login_at": current_user.last_login_at, "email_notification": current_user.email_notification, "wechat_notification": current_user.wechat_notification, "dingtalk_notification": current_user.dingtalk_notification } @router.post("/change-password", summary="修改密码") def change_password( old_password: str, new_password: str, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """用户修改自己的密码""" if len(new_password) < 6: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="新密码长度不能少于6位" ) try: UserService.change_password(db, current_user.id, old_password, new_password) return {"message": "密码修改成功,请重新登录"} except HTTPException: raise except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"密码修改失败: {str(e)}" ) # ========== 用户管理 API (需要管理员权限) ========== @router.get("/users", summary="获取用户列表") def get_users( status: Optional[UserStatus] = None, role: Optional[UserRole] = None, skip: int = 0, limit: int = 50, current_user: User = Depends(require_permission("user:view")), db: Session = Depends(get_db) ): """获取用户列表(需要管理员权限)""" total, items = UserService.get_users(db, skip=skip, limit=limit, status=status, role=role) # 返回脱敏的用户信息 user_list = [] for user in items: user_list.append({ "id": user.id, "username": user.username, "email": user.email, "phone": user.phone, "real_name": user.real_name, "role": user.role.value, "status": user.status.value, "last_login_at": user.last_login_at, "created_at": user.created_at }) return {"total": total, "items": user_list} @router.get("/users/{user_id}", summary="获取用户详情") def get_user( user_id: int, current_user: User = Depends(require_permission("user:view")), db: Session = Depends(get_db) ): """获取用户详情(需要管理员权限)""" user = UserService.get_user_by_id(db, user_id) if not user: raise HTTPException(status_code=404, detail="用户不存在") return { "id": user.id, "username": user.username, "email": user.email, "phone": user.phone, "real_name": user.real_name, "role": user.role.value, "status": user.status.value, "last_login_at": user.last_login_at, "last_login_ip": user.last_login_ip, "login_failed_count": user.login_failed_count, "created_at": user.created_at, "email_notification": user.email_notification, "wechat_notification": user.wechat_notification, "dingtalk_notification": user.dingtalk_notification } @router.post("/users", summary="创建用户", status_code=status.HTTP_201_CREATED) def create_user( username: str, password: str, email: Optional[str] = None, phone: Optional[str] = None, real_name: Optional[str] = None, role: UserRole = UserRole.VIEWER, current_user: User = Depends(require_permission("user:create")), db: Session = Depends(get_db) ): """创建新用户(需要超级管理员权限)""" # 只有超级管理员才能创建管理员 if role in [UserRole.SUPER_ADMIN, UserRole.ADMIN] and current_user.role != UserRole.SUPER_ADMIN: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="无权创建管理员账户" ) if len(password) < 6: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="密码长度不能少于6位" ) user = UserService.create_user( db, username=username, password=password, email=email, phone=phone, real_name=real_name, role=role, created_by=current_user.id ) return { "message": "用户创建成功", "user": { "id": user.id, "username": user.username, "role": user.role.value } } @router.put("/users/{user_id}/status", summary="更新用户状态") def update_user_status( user_id: int, status: UserStatus, current_user: User = Depends(require_permission("user:edit")), db: Session = Depends(get_db) ): """启用/禁用用户""" # 不能禁用自己 if user_id == current_user.id: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="不能禁用自己的账户" ) user = UserService.update_user_status(db, user_id, status) if not user: raise HTTPException(status_code=404, detail="用户不存在") return {"message": "用户状态更新成功", "new_status": status.value} @router.post("/users/{user_id}/unlock", summary="解锁用户") def unlock_user( user_id: int, current_user: User = Depends(require_permission("user:edit")), db: Session = Depends(get_db) ): """解锁被锁定的用户""" user = UserService.unlock_user(db, user_id) if not user: raise HTTPException(status_code=404, detail="用户不存在") return {"message": "用户已解锁"} @router.post("/users/{user_id}/reset-password", summary="重置用户密码") def reset_user_password( user_id: int, new_password: str, current_user: User = Depends(require_permission("user:edit")), db: Session = Depends(get_db) ): """管理员重置用户密码""" if len(new_password) < 6: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="密码长度不能少于6位" ) # 只有超级管理员才能重置其他管理员的密码 target_user = UserService.get_user_by_id(db, user_id) if not target_user: raise HTTPException(status_code=404, detail="用户不存在") if target_user.role in [UserRole.SUPER_ADMIN, UserRole.ADMIN] and current_user.role != UserRole.SUPER_ADMIN: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="无权重置管理员密码" ) success = UserService.reset_password(db, user_id, new_password) if not success: raise HTTPException(status_code=404, detail="用户不存在") # 撤销该用户所有现有令牌,迫使其重新登录 TokenService.revoke_all_user_tokens(db, user_id) return {"message": "密码重置成功,用户所有会话已失效"} @router.delete("/users/{user_id}", summary="删除用户") def delete_user( user_id: int, current_user: User = Depends(require_permission("user:delete")), db: Session = Depends(get_db) ): """删除用户(软删除,标记为禁用)""" # 不能删除自己 if user_id == current_user.id: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="不能删除自己的账户" ) # 只有超级管理员才能删除管理员 target_user = UserService.get_user_by_id(db, user_id) if target_user and target_user.role in [UserRole.SUPER_ADMIN, UserRole.ADMIN]: if current_user.role != UserRole.SUPER_ADMIN: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="无权删除管理员账户" ) success = UserService.delete_user(db, user_id) if not success: raise HTTPException(status_code=404, detail="用户不存在") # 撤销所有令牌 TokenService.revoke_all_user_tokens(db, user_id) return {"message": "用户已删除"}