jiachenlong/backend/app/routers/users.py

315 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# users.py - 用户管理路由
# Version: 0.0.1 (2026-04-19)
# 更新:新增 dealCount 字段,从 Information 表统计用户发布的行情数量
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query, Body
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.auth import get_current_user
from app.models.models import User, Collection
from app.schemas.schemas import UserResponse, UserUpdate
router = APIRouter(prefix="/api", tags=["用户"])
# ============ 当前用户接口 ============
@router.get("/users/me") # 无 response_model避免 Pydantic 序列化问题
def get_current_user_info(
current_user: User = Depends(get_current_user)
):
"""获取当前登录用户信息"""
return {
"id": current_user.f99_90_id,
"username": current_user.f01_01_name,
"f99_90_id": current_user.f99_90_id,
"f01_01_name": current_user.f01_01_name,
"email": current_user.email,
"phone": current_user.phone,
"avatar": current_user.avatar,
"address": current_user.address,
"bio": current_user.bio,
"role": current_user.role,
"level": current_user.f99_94_level,
"aiCount": current_user.f99_95_ai_count or 0,
"searchCount": current_user.f99_96_search_count or 0,
"collectionCount": current_user.f99_97_collection_count or 0,
"phoneVerified": current_user.f01_06_phone_verified or False,
"loginCount": current_user.f99_98_login_count or 0,
"lastLogin": current_user.f99_99_last_login.isoformat() if current_user.f99_99_last_login else None,
"gender": current_user.f01_07_gender,
"birthday": current_user.f01_08_birthday.isoformat() if current_user.f01_08_birthday else None,
"region": current_user.f01_09_region,
"realnameVerified": current_user.f01_10_realname_verified or False,
"points": current_user.f99_100_points or 0,
"balance": float(current_user.f01_11_balance) if current_user.f01_11_balance else 0,
"totalAmount": float(current_user.f01_12_total_amount) if current_user.f01_12_total_amount else 0,
"inviteCode": current_user.f01_13_invite_code,
"user_code": current_user.user_code,
"f99_92_created_at": current_user.f99_92_created_at.isoformat() if current_user.f99_92_created_at else None,
"f99_93_updated_at": current_user.f99_93_updated_at.isoformat() if current_user.f99_93_updated_at else None
}
@router.put("/users/me") # 无 response_model避免 Pydantic 序列化问题
def update_current_user(
user_update: UserUpdate,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""更新当前用户信息"""
import logging
logger = logging.getLogger(__name__)
# 获取用户ID
user_id = current_user.f99_90_id
logger.info(f"Updating user {user_id}, data={user_update.model_dump()}")
# 在当前session中重新查询用户
user = db.query(User).filter(User.f99_90_id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
# 更新字段
update_data = user_update.model_dump(exclude_unset=True)
for field, value in update_data.items():
if field == 'f01_01_name':
user.f01_01_name = value
elif field == 'username':
pass # skip, already handled as f01_01_name
elif hasattr(user, field):
setattr(user, field, value)
# 强制刷新以确保更新被提交
db.flush()
db.commit()
db.refresh(user)
logger.info(f"After commit, user email={user.email}")
return user
# ============ 管理员用户管理 ============
admin_router = APIRouter(prefix="/api/admin/users", tags=["用户管理"])
@admin_router.get("")
def get_users(
page: int = Query(1, ge=1),
limit: int = Query(20, ge=1, le=100),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取用户列表(仅管理员)"""
if current_user.role not in ["admin", "editor"]:
raise HTTPException(status_code=403, detail="无权访问")
total = db.query(User).count()
from sqlalchemy import case
users = db.query(User).order_by(
case(
(User.user_code == None, 1),
else_=0
),
User.user_code.asc()
).offset((page-1)*limit).limit(limit).all()
user_list = []
for u in users:
# 统计每个用户的藏品数量
count = db.query(Collection).filter(Collection.f99_91_user_id == u.f99_90_id).count()
# 统计每个用户发布的行情数量info_type='deal'
deal_count = db.query(Information).filter(
Information.user_id == u.f99_90_id,
Information.info_type == "deal"
).count()
user_list.append({
"id": u.f99_90_id,
"username": u.f01_01_name,
"email": u.email,
"phone": u.phone,
"role": u.role,
"user_code": u.user_code,
"created_at": u.f99_92_created_at.isoformat() if u.f99_92_created_at else None,
"collectionCount": count,
"dealCount": deal_count,
"level": u.f99_94_level,
"aiCount": u.f99_95_ai_count,
"searchCount": u.f99_96_search_count,
"loginCount": u.f99_98_login_count,
"points": u.f99_100_points,
"balance": float(u.f01_11_balance) if u.f01_11_balance else 0,
"totalAmount": float(u.f01_12_total_amount) if u.f01_12_total_amount else 0,
"phoneVerified": u.f01_06_phone_verified,
})
return user_list
@admin_router.get("/{user_id}")
def get_user(
user_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取单个用户信息"""
if current_user.role not in ["admin", "editor"]:
raise HTTPException(status_code=403, detail="无权访问")
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return {
"id": user.id,
"username": user.username,
"email": user.email,
"phone": user.phone,
"role": user.role,
"created_at": user.created_at.isoformat() if user.created_at else None
}
@admin_router.get("/{user_id}/collections")
def get_user_collections(
user_id: str,
limit: int = Query(100, ge=1, le=100),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取指定用户的藏品列表"""
if current_user.role not in ["admin", "editor"]:
raise HTTPException(status_code=403, detail="无权访问")
collections = db.query(Collection).filter(
Collection.user_id == user_id
).limit(limit).all()
return [c.code for c in collections]
@admin_router.get("/{user_id}/count")
def get_user_collection_count(
user_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取指定用户的藏品数量"""
if current_user.role not in ["admin", "editor"]:
raise HTTPException(status_code=403, detail="E00050: 仅管理员可访问")
count = db.query(Collection).filter(Collection.user_id == user_id).count()
return {"count": count}
@admin_router.put("/{user_id}")
def update_user(
user_id: str,
username: Optional[str] = Body(None),
email: Optional[str] = Body(None),
role: Optional[str] = Body(None),
password: Optional[str] = Body(None),
user_code: Optional[str] = Body(None),
level: Optional[str] = Body(None),
points: Optional[int] = Body(None),
balance: Optional[float] = Body(None),
totalAmount: Optional[float] = Body(None),
aiCount: Optional[int] = Body(None),
searchCount: Optional[int] = Body(None),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""更新用户信息(仅管理员)"""
if current_user.role not in ["admin", "editor"]:
raise HTTPException(status_code=403, detail="E00050: 仅管理员可访问")
user = db.query(User).filter(User.f99_90_id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="E00051: 用户不存在")
# 更新基本信息
if username:
user.f01_01_name = username
if email:
user.email = email
if role is not None:
user.role = role
# 更新会员等级
if level is not None:
user.f99_94_level = level
# 更新积分
if points is not None:
user.f99_100_points = points
# 更新余额
if balance is not None:
user.f01_11_balance = balance
# 更新累计金额
if totalAmount is not None:
user.f01_12_total_amount = totalAmount
# 更新AI识别次数
if aiCount is not None:
user.f99_95_ai_count = aiCount
# 更新寻号次数
if searchCount is not None:
user.f99_96_search_count = searchCount
# 更新用户编码
if user_code is not None:
# 只有非空字符串才检查唯一性
user_code_str = user_code.strip() if user_code else ''
if user_code_str:
existing = db.query(User).filter(
User.user_code == user_code_str,
User.f99_90_id != user_id
).first()
if existing:
raise HTTPException(status_code=400, detail="E00052: 该用户编码已被其他用户使用")
user.user_code = user_code_str
else:
user.user_code = None
# 更新密码
if password and password.strip():
from app.core.auth import get_password_hash
user.password = get_password_hash(password)
db.commit()
db.refresh(user)
return {
"id": user.f99_90_id,
"username": user.f01_01_name,
"email": user.email,
"role": user.role,
"message": "更新成功"
}
@admin_router.delete("/{user_id}")
def delete_user(
user_id: str,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""删除用户(仅管理员)"""
if current_user.role not in ["admin", "editor"]:
raise HTTPException(status_code=403, detail="E00050: 仅管理员可访问")
# 不能删除自己
if user_id == str(current_user.f99_90_id):
raise HTTPException(status_code=400, detail="E00052: 不能删除自己")
user = db.query(User).filter(User.f99_90_id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="E00051: 用户不存在")
db.delete(user)
db.commit()
return {"message": "删除成功"}