""" 用户管理 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": "密码已重置"}