""" 用户管理 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( body: dict, admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """更新注册设置(接收 JSON Body)""" setting = db.query(RegisterSetting).first() if not setting: setting = RegisterSetting( id=str(uuid.uuid4()), phone_required=body.get("phone_required", True), phone_sms_required=body.get("phone_sms_required", True), email_required=body.get("email_required", True), email_verify_required=body.get("email_verify_required", True), real_name_required=body.get("real_name_required", False), aliyun_access_key=body.get("aliyun_access_key"), aliyun_secret_key=body.get("aliyun_secret_key"), aliyun_endpoint=body.get("aliyun_endpoint"), sms_sign_name=body.get("sms_sign_name"), sms_template_code=body.get("sms_template_code"), ) db.add(setting) else: if "phone_required" in body: setting.phone_required = body["phone_required"] if "phone_sms_required" in body: setting.phone_sms_required = body["phone_sms_required"] if "email_required" in body: setting.email_required = body["email_required"] if "email_verify_required" in body: setting.email_verify_required = body["email_verify_required"] if "real_name_required" in body: setting.real_name_required = body["real_name_required"] if "aliyun_access_key" in body: setting.aliyun_access_key = body["aliyun_access_key"] if "aliyun_secret_key" in body: setting.aliyun_secret_key = body["aliyun_secret_key"] if "aliyun_endpoint" in body: setting.aliyun_endpoint = body["aliyun_endpoint"] if "sms_sign_name" in body: setting.sms_sign_name = body["sms_sign_name"] if "sms_template_code" in body: setting.sms_template_code = body["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": "密码已重置"}