fix:1.修复数据库表初始化逻辑
This commit is contained in:
@@ -5,6 +5,7 @@ from fastapi import APIRouter, HTTPException, Request, Depends
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from app.user_manager import user_manager, User
|
from app.user_manager import user_manager, User
|
||||||
|
from app.user_password import password_manager
|
||||||
|
|
||||||
router = APIRouter(prefix="/users", tags=["用户管理"])
|
router = APIRouter(prefix="/users", tags=["用户管理"])
|
||||||
|
|
||||||
@@ -29,6 +30,11 @@ class SetAdminRequest(BaseModel):
|
|||||||
is_admin: bool
|
is_admin: bool
|
||||||
|
|
||||||
|
|
||||||
|
class ResetPasswordRequest(BaseModel):
|
||||||
|
user_id: str
|
||||||
|
new_password: Optional[str] = None # 如果为空则使用默认密码
|
||||||
|
|
||||||
|
|
||||||
@router.get("/current")
|
@router.get("/current")
|
||||||
async def get_current_user(user: User = Depends(require_login)):
|
async def get_current_user(user: User = Depends(require_login)):
|
||||||
"""获取当前登录用户信息"""
|
"""获取当前登录用户信息"""
|
||||||
@@ -123,3 +129,61 @@ async def get_user(
|
|||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
return user.dict()
|
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)}"
|
||||||
|
)
|
||||||
+17
-1
@@ -27,7 +27,23 @@ logger = get_logger(__name__)
|
|||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
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
|
yield
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Dropdown, Avatar, Space, Typography, message, Modal, Table, Button, Tag, Popconfirm, Pagination, Form, Input } from 'antd';
|
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 { authApi, userApi } from '../services/api';
|
||||||
import type { User } from '../types';
|
import type { User } from '../types';
|
||||||
import type { MenuProps } from 'antd';
|
import type { MenuProps } from 'antd';
|
||||||
@@ -87,6 +87,55 @@ export default function UserMenu() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleResetPassword = async (userId: string, username: string) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '重置用户密码',
|
||||||
|
content: (
|
||||||
|
<div>
|
||||||
|
<p>确定要重置用户 <strong>{username}</strong> 的密码吗?</p>
|
||||||
|
<p style={{ color: '#8c8c8c', fontSize: 12 }}>密码将被重置为默认密码:<strong>{username}@666</strong></p>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
okText: '确定重置',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
const result = await userApi.resetPassword(userId);
|
||||||
|
if (result.default_password) {
|
||||||
|
Modal.success({
|
||||||
|
title: '密码重置成功',
|
||||||
|
content: (
|
||||||
|
<div>
|
||||||
|
<p>用户 <strong>{result.username}</strong> 的密码已重置为:</p>
|
||||||
|
<p style={{
|
||||||
|
padding: '8px 12px',
|
||||||
|
background: '#f5f5f5',
|
||||||
|
borderRadius: 4,
|
||||||
|
fontSize: 16,
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
color: '#1890ff',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}>
|
||||||
|
{result.default_password}
|
||||||
|
</p>
|
||||||
|
<p style={{ color: '#ff4d4f', fontSize: 12, marginTop: 8 }}>
|
||||||
|
请将此密码告知用户,建议用户登录后立即修改密码
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.success('密码重置成功');
|
||||||
|
}
|
||||||
|
loadUsers();
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('重置密码失败:', error);
|
||||||
|
message.error(error.response?.data?.detail || '重置密码失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleChangePassword = async (values: { oldPassword: string; newPassword: string }) => {
|
const handleChangePassword = async (values: { oldPassword: string; newPassword: string }) => {
|
||||||
try {
|
try {
|
||||||
setChangingPassword(true);
|
setChangingPassword(true);
|
||||||
@@ -186,11 +235,11 @@ export default function UserMenu() {
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
width: 200,
|
width: 280,
|
||||||
render: (_: unknown, record: User) => {
|
render: (_: unknown, record: User) => {
|
||||||
const isSelf = record.user_id === currentUser?.user_id;
|
const isSelf = record.user_id === currentUser?.user_id;
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space size="small">
|
||||||
{record.is_admin ? (
|
{record.is_admin ? (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定要取消管理员权限吗?"
|
title="确定要取消管理员权限吗?"
|
||||||
@@ -210,6 +259,15 @@ export default function UserMenu() {
|
|||||||
设为管理员
|
设为管理员
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<KeyOutlined />}
|
||||||
|
onClick={() => handleResetPassword(record.user_id, record.username)}
|
||||||
|
disabled={isSelf}
|
||||||
|
title={isSelf ? '不能重置自己的密码' : '重置为默认密码'}
|
||||||
|
>
|
||||||
|
重置密码
|
||||||
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定要删除该用户吗?此操作不可恢复!"
|
title="确定要删除该用户吗?此操作不可恢复!"
|
||||||
onConfirm={() => handleDeleteUser(record.user_id)}
|
onConfirm={() => handleDeleteUser(record.user_id)}
|
||||||
|
|||||||
@@ -156,6 +156,14 @@ export const userApi = {
|
|||||||
deleteUser: (userId: string) => api.delete(`/users/${userId}`),
|
deleteUser: (userId: string) => api.delete(`/users/${userId}`),
|
||||||
|
|
||||||
getUser: (userId: string) => api.get<unknown, User>(`/users/${userId}`),
|
getUser: (userId: string) => api.get<unknown, User>(`/users/${userId}`),
|
||||||
|
|
||||||
|
resetPassword: (userId: string, newPassword?: string) =>
|
||||||
|
api.post<unknown, {
|
||||||
|
message: string;
|
||||||
|
user_id: string;
|
||||||
|
username: string;
|
||||||
|
default_password?: string;
|
||||||
|
}>('/users/reset-password', { user_id: userId, new_password: newPassword }),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const settingsApi = {
|
export const settingsApi = {
|
||||||
|
|||||||
Reference in New Issue
Block a user