diff --git a/backend/app/api/users.py b/backend/app/api/users.py index 623a085..6b72707 100644 --- a/backend/app/api/users.py +++ b/backend/app/api/users.py @@ -5,6 +5,7 @@ from fastapi import APIRouter, HTTPException, Request, Depends from pydantic import BaseModel from typing import List, Optional from app.user_manager import user_manager, User +from app.user_password import password_manager router = APIRouter(prefix="/users", tags=["用户管理"]) @@ -29,6 +30,11 @@ class SetAdminRequest(BaseModel): is_admin: bool +class ResetPasswordRequest(BaseModel): + user_id: str + new_password: Optional[str] = None # 如果为空则使用默认密码 + + @router.get("/current") async def get_current_user(user: User = Depends(require_login)): """获取当前登录用户信息""" @@ -122,4 +128,62 @@ async def get_user( if not user: raise HTTPException(status_code=404, detail="用户不存在") - return user.dict() \ No newline at end of file + return user.dict() + + +@router.post("/reset-password") +async def reset_user_password( + data: ResetPasswordRequest, + admin_user: User = Depends(require_admin) +): + """ + 重置用户密码(仅管理员) + + 如果提供了 new_password,则设置为指定密码 + 如果未提供 new_password,则重置为默认密码(username@666) + + 限制: + - 不能重置自己的密码(应该使用修改密码功能) + """ + # 检查是否尝试重置自己的密码 + if data.user_id == admin_user.user_id: + raise HTTPException( + status_code=400, + detail="不能重置自己的密码,请使用修改密码功能" + ) + + # 检查目标用户是否存在 + target_user = await user_manager.get_user(data.user_id) + if not target_user: + raise HTTPException( + status_code=404, + detail="目标用户不存在" + ) + + # 重置密码 + try: + actual_password = await password_manager.set_password( + target_user.user_id, + target_user.username, + data.new_password + ) + + # 如果使用了默认密码,返回密码供管理员告知用户 + message = "密码重置成功" + response_data = { + "message": message, + "user_id": data.user_id, + "username": target_user.username + } + + if not data.new_password: + response_data["default_password"] = actual_password + response_data["message"] = f"密码已重置为默认密码: {actual_password}" + + return response_data + + except Exception as e: + raise HTTPException( + status_code=500, + detail=f"重置密码失败: {str(e)}" + ) \ No newline at end of file diff --git a/backend/app/main.py b/backend/app/main.py index 589b685..8c97c05 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -27,7 +27,23 @@ logger = get_logger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """应用生命周期管理""" - logger.info("应用启动,等待用户登录...") + logger.info("应用启动,初始化数据库表结构...") + + # 在应用启动时初始化数据库表结构 + try: + from app.database import get_engine, Base + + # 使用全局引擎创建所有表 + engine = await get_engine("_global_init_") + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + + logger.info("✅ 数据库表结构初始化成功") + except Exception as e: + logger.error(f"❌ 数据库表结构初始化失败: {str(e)}", exc_info=True) + # 不阻止应用启动,允许在后续操作中重试 + + logger.info("应用启动完成,等待用户登录...") yield diff --git a/frontend/src/components/UserMenu.tsx b/frontend/src/components/UserMenu.tsx index d88e763..05891ec 100644 --- a/frontend/src/components/UserMenu.tsx +++ b/frontend/src/components/UserMenu.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { Dropdown, Avatar, Space, Typography, message, Modal, Table, Button, Tag, Popconfirm, Pagination, Form, Input } from 'antd'; -import { UserOutlined, LogoutOutlined, TeamOutlined, CrownOutlined, LockOutlined } from '@ant-design/icons'; +import { UserOutlined, LogoutOutlined, TeamOutlined, CrownOutlined, LockOutlined, KeyOutlined } from '@ant-design/icons'; import { authApi, userApi } from '../services/api'; import type { User } from '../types'; import type { MenuProps } from 'antd'; @@ -87,6 +87,55 @@ export default function UserMenu() { } }; + const handleResetPassword = async (userId: string, username: string) => { + Modal.confirm({ + title: '重置用户密码', + content: ( +
确定要重置用户 {username} 的密码吗?
+密码将被重置为默认密码:{username}@666
+用户 {result.username} 的密码已重置为:
++ {result.default_password} +
++ 请将此密码告知用户,建议用户登录后立即修改密码 +
+