初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
This commit is contained in:
commit
78ff261b5a
199 files changed
+69126
No files matched your search
@@ -0,0 +1,334 @@
|
||||
"""
|
||||
用户管理 API(管理员)
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import or_
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
from ...database import get_db, SessionLocal
|
||||
from ...models import User, RegisterSetting
|
||||
from ...auth import get_current_admin, hash_password, verify_password
|
||||
from ...middleware.log_service import LogService
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class AdminChangePasswordRequest(BaseModel):
|
||||
old_password: str
|
||||
new_password: str = Field(min_length=6, max_length=100)
|
||||
|
||||
|
||||
class ResetUserPasswordRequest(BaseModel):
|
||||
new_password: str = Field(min_length=6, max_length=100)
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def get_users(
|
||||
status: str = Query(None, description="用户状态筛选: active/banned"),
|
||||
membership_level: int = Query(None, description="会员等级筛选"),
|
||||
search: str = Query(None, description="搜索用户名或邮箱"),
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
admin_user=Depends(get_current_admin),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""获取用户列表(管理员)"""
|
||||
query = db.query(User)
|
||||
|
||||
if status:
|
||||
query = query.filter(User.status == status)
|
||||
if membership_level is not None:
|
||||
query = query.filter(User.membership_level == membership_level)
|
||||
if search:
|
||||
query = query.filter(
|
||||
or_(
|
||||
User.username.ilike(f"%{search}%"),
|
||||
User.email.ilike(f"%{search}%"),
|
||||
)
|
||||
)
|
||||
|
||||
total = query.count()
|
||||
users = query.order_by(User.created_at.desc()).offset(
|
||||
(page - 1) * page_size
|
||||
).limit(page_size).all()
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"items": [
|
||||
{
|
||||
"id": u.id,
|
||||
"username": u.username,
|
||||
"email": u.email,
|
||||
"phone": u.phone,
|
||||
"role": u.role,
|
||||
"status": u.status,
|
||||
"real_name_status": u.real_name_status,
|
||||
"real_name": u.real_name,
|
||||
"membership_level": u.membership_level,
|
||||
"membership_expire_at": u.membership_expire_at.isoformat() if u.membership_expire_at else None,
|
||||
"auto_renew": u.auto_renew,
|
||||
"created_at": u.created_at.isoformat() if u.created_at else None,
|
||||
"updated_at": u.updated_at.isoformat() if u.updated_at else None,
|
||||
}
|
||||
for u in users
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 静态路由 - 必须放在 /{id} 动态路由之前
|
||||
# ============================================================
|
||||
|
||||
@router.get("/register-settings")
|
||||
async def get_register_settings(
|
||||
admin_user=Depends(get_current_admin),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""获取注册设置"""
|
||||
setting = db.query(RegisterSetting).first()
|
||||
if not setting:
|
||||
return {
|
||||
"phone_required": True,
|
||||
"phone_sms_required": True,
|
||||
"email_required": True,
|
||||
"email_verify_required": True,
|
||||
"real_name_required": False,
|
||||
"aliyun_access_key": "",
|
||||
"aliyun_endpoint": "https://dysmsapi.aliyuncs.com",
|
||||
"sms_sign_name": "",
|
||||
"sms_template_code": "",
|
||||
}
|
||||
|
||||
return {
|
||||
"id": setting.id,
|
||||
"phone_required": setting.phone_required,
|
||||
"phone_sms_required": setting.phone_sms_required,
|
||||
"email_required": setting.email_required,
|
||||
"email_verify_required": setting.email_verify_required,
|
||||
"real_name_required": setting.real_name_required,
|
||||
"aliyun_access_key": setting.aliyun_access_key,
|
||||
"aliyun_endpoint": setting.aliyun_endpoint,
|
||||
"sms_sign_name": getattr(setting, 'sms_sign_name', ''),
|
||||
"sms_template_code": getattr(setting, 'sms_template_code', ''),
|
||||
}
|
||||
|
||||
|
||||
@router.put("/register-settings")
|
||||
async def update_register_settings(
|
||||
phone_required: bool = None,
|
||||
phone_sms_required: bool = None,
|
||||
email_required: bool = None,
|
||||
email_verify_required: bool = None,
|
||||
real_name_required: bool = None,
|
||||
aliyun_access_key: str = None,
|
||||
aliyun_secret_key: str = None,
|
||||
aliyun_endpoint: str = None,
|
||||
sms_sign_name: str = None,
|
||||
sms_template_code: str = None,
|
||||
admin_user=Depends(get_current_admin),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""更新注册设置"""
|
||||
setting = db.query(RegisterSetting).first()
|
||||
if not setting:
|
||||
setting = RegisterSetting(
|
||||
id=str(uuid.uuid4()),
|
||||
phone_required=phone_required if phone_required is not None else True,
|
||||
phone_sms_required=phone_sms_required if phone_sms_required is not None else True,
|
||||
email_required=email_required if email_required is not None else True,
|
||||
email_verify_required=email_verify_required if email_verify_required is not None else True,
|
||||
real_name_required=real_name_required or False,
|
||||
aliyun_access_key=aliyun_access_key,
|
||||
aliyun_secret_key=aliyun_secret_key,
|
||||
aliyun_endpoint=aliyun_endpoint,
|
||||
sms_sign_name=sms_sign_name,
|
||||
sms_template_code=sms_template_code,
|
||||
)
|
||||
db.add(setting)
|
||||
else:
|
||||
if phone_required is not None:
|
||||
setting.phone_required = phone_required
|
||||
if phone_sms_required is not None:
|
||||
setting.phone_sms_required = phone_sms_required
|
||||
if email_required is not None:
|
||||
setting.email_required = email_required
|
||||
if email_verify_required is not None:
|
||||
setting.email_verify_required = email_verify_required
|
||||
if real_name_required is not None:
|
||||
setting.real_name_required = real_name_required
|
||||
if aliyun_access_key is not None:
|
||||
setting.aliyun_access_key = aliyun_access_key
|
||||
if aliyun_secret_key is not None:
|
||||
setting.aliyun_secret_key = aliyun_secret_key
|
||||
if aliyun_endpoint is not None:
|
||||
setting.aliyun_endpoint = aliyun_endpoint
|
||||
if sms_sign_name is not None:
|
||||
setting.sms_sign_name = sms_sign_name
|
||||
if sms_template_code is not None:
|
||||
setting.sms_template_code = sms_template_code
|
||||
setting.updated_at = datetime.now()
|
||||
|
||||
db.commit()
|
||||
|
||||
LogService.log_audit(
|
||||
admin_id=admin_user.id,
|
||||
admin_name=admin_user.username,
|
||||
action="update_register_settings",
|
||||
target_type="register_setting",
|
||||
detail="更新注册设置",
|
||||
)
|
||||
|
||||
return {"message": "注册设置已更新"}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 动态路由
|
||||
# ============================================================
|
||||
|
||||
@router.get("/{id}")
|
||||
async def get_user_detail(
|
||||
id: str,
|
||||
admin_user=Depends(get_current_admin),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""获取用户详情"""
|
||||
user = db.query(User).filter(User.id == id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
|
||||
return {
|
||||
"id": user.id,
|
||||
"username": user.username,
|
||||
"email": user.email,
|
||||
"phone": user.phone,
|
||||
"avatar": user.avatar,
|
||||
"role": user.role,
|
||||
"status": user.status,
|
||||
"real_name_status": user.real_name_status,
|
||||
"real_name": user.real_name,
|
||||
"id_card": user.id_card,
|
||||
"membership_plan_id": user.membership_plan_id,
|
||||
"membership_level": user.membership_level,
|
||||
"membership_expire_at": user.membership_expire_at.isoformat() if user.membership_expire_at else None,
|
||||
"auto_renew": user.auto_renew,
|
||||
"created_at": user.created_at.isoformat() if user.created_at else None,
|
||||
"updated_at": user.updated_at.isoformat() if user.updated_at else None,
|
||||
}
|
||||
|
||||
|
||||
@router.put("/{id}/ban")
|
||||
async def ban_user(
|
||||
id: str,
|
||||
admin_user=Depends(get_current_admin),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""封禁用户"""
|
||||
user = db.query(User).filter(User.id == id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
if user.role == "admin":
|
||||
raise HTTPException(status_code=400, detail="不能封禁管理员账号")
|
||||
|
||||
user.status = "banned"
|
||||
user.updated_at = datetime.now()
|
||||
db.commit()
|
||||
|
||||
LogService.log_audit(
|
||||
admin_id=admin_user.id,
|
||||
admin_name=admin_user.username,
|
||||
action="ban_user",
|
||||
target_type="user",
|
||||
target_id=id,
|
||||
detail=f"封禁用户 {user.username}",
|
||||
)
|
||||
|
||||
return {"message": "用户已封禁"}
|
||||
|
||||
|
||||
@router.put("/{id}/unban")
|
||||
async def unban_user(
|
||||
id: str,
|
||||
admin_user=Depends(get_current_admin),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""解封用户"""
|
||||
user = db.query(User).filter(User.id == id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
|
||||
user.status = "active"
|
||||
user.updated_at = datetime.now()
|
||||
db.commit()
|
||||
|
||||
LogService.log_audit(
|
||||
admin_id=admin_user.id,
|
||||
admin_name=admin_user.username,
|
||||
action="unban_user",
|
||||
target_type="user",
|
||||
target_id=id,
|
||||
detail=f"解封用户 {user.username}",
|
||||
)
|
||||
|
||||
return {"message": "用户已解封"}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 密码管理
|
||||
# ============================================================
|
||||
|
||||
@router.put("/change-password")
|
||||
def admin_change_password(
|
||||
req: AdminChangePasswordRequest,
|
||||
admin_user=Depends(get_current_admin),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""管理员修改自己的密码"""
|
||||
db_user = db.query(User).filter(User.id == admin_user.id).first()
|
||||
if not db_user:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
|
||||
if not verify_password(req.old_password, db_user.password_hash):
|
||||
raise HTTPException(status_code=400, detail="原密码错误")
|
||||
|
||||
db_user.password_hash = hash_password(req.new_password)
|
||||
db_user.updated_at = datetime.now()
|
||||
|
||||
LogService.log_operation(
|
||||
user_id=admin_user.id, username=admin_user.username, action="admin_change_password",
|
||||
target="user", target_id=admin_user.id,
|
||||
detail="管理员修改了自己的密码",
|
||||
)
|
||||
db.commit()
|
||||
|
||||
return {"code": 0, "message": "密码修改成功"}
|
||||
|
||||
|
||||
@router.put("/{id}/reset-password")
|
||||
def admin_reset_user_password(
|
||||
id: str,
|
||||
req: ResetUserPasswordRequest,
|
||||
admin_user=Depends(get_current_admin),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""管理员重置用户密码"""
|
||||
user = db.query(User).filter(User.id == id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="用户不存在")
|
||||
|
||||
user.password_hash = hash_password(req.new_password)
|
||||
user.updated_at = datetime.now()
|
||||
|
||||
LogService.log_operation(
|
||||
user_id=admin_user.id, username=admin_user.username, action="reset_user_password",
|
||||
target="user", target_id=id,
|
||||
detail=f"管理员重置了用户 {user.username} 的密码",
|
||||
)
|
||||
db.commit()
|
||||
|
||||
return {"code": 0, "message": "密码已重置"}
|
||||
Reference in new issue
Block a user