初始提交: Qihu-AIFlow v2.0.1 完整项目

- 前端 React+TypeScript (源码+构建产物)
- 后端 Python FastAPI
- 数据库迁移 001-015
- Docker部署配置
- 微信ClawBot API频道绑定
- 项目文档 README.md
This commit is contained in:
qyyfw committed 2026-07-02 14:40:46 +08:00
commit 78ff261b5a
199 files changed
+69126

No files matched your search

+334
View File
@@ -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": "密码已重置"}