🎉初次提交
This commit is contained in:
242
router/user/__init__.py
Normal file
242
router/user/__init__.py
Normal file
@@ -0,0 +1,242 @@
|
||||
import base64
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from database.creator import get_db_session
|
||||
from database.user import UserProfile, User
|
||||
from provider.loveac.authme import fetch_user_by_token, AuthmeRequest
|
||||
from utils.file_manager import file_manager
|
||||
from .model import (
|
||||
UserProfileResponse,
|
||||
GetUserProfileRequest,
|
||||
UpdateUserProfileRequest,
|
||||
UserProfileData,
|
||||
UserSettings,
|
||||
)
|
||||
|
||||
user_router = APIRouter(prefix="/api/v1/user")
|
||||
|
||||
|
||||
@user_router.post("/profile/get", summary="获取用户资料")
|
||||
async def get_user_profile(
|
||||
data: GetUserProfileRequest,
|
||||
asyncsession: AsyncSession = Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
获取用户资料
|
||||
:param data: GetUserProfileRequest
|
||||
:return: UserProfileResponse
|
||||
"""
|
||||
try:
|
||||
# 使用token验证获取用户
|
||||
authme_request = AuthmeRequest(token=data.token)
|
||||
user = await fetch_user_by_token(authme_request, asyncsession)
|
||||
|
||||
async with asyncsession as session:
|
||||
result = await session.execute(
|
||||
select(UserProfile).where(UserProfile.userid == user.userid)
|
||||
)
|
||||
profile = result.scalars().first()
|
||||
|
||||
if not profile:
|
||||
# 如果用户资料不存在,创建默认资料
|
||||
profile = UserProfile(
|
||||
userid=user.userid,
|
||||
avatar_filename=None,
|
||||
background_filename=None,
|
||||
nickname=None,
|
||||
settings_filename=None
|
||||
)
|
||||
session.add(profile)
|
||||
await session.commit()
|
||||
|
||||
# 获取头像数据
|
||||
avatar_data = None
|
||||
if profile.avatar_filename:
|
||||
avatar_bytes = await file_manager.get_avatar(profile.avatar_filename)
|
||||
if avatar_bytes:
|
||||
# 转换为base64
|
||||
avatar_data = base64.b64encode(avatar_bytes).decode('utf-8')
|
||||
# 根据文件扩展名添加data URI前缀
|
||||
if profile.avatar_filename.endswith('.png'):
|
||||
avatar_data = f"data:image/png;base64,{avatar_data}"
|
||||
elif profile.avatar_filename.endswith(('.jpg', '.jpeg')):
|
||||
avatar_data = f"data:image/jpeg;base64,{avatar_data}"
|
||||
elif profile.avatar_filename.endswith('.gif'):
|
||||
avatar_data = f"data:image/gif;base64,{avatar_data}"
|
||||
|
||||
# 获取背景数据
|
||||
background_data = None
|
||||
if profile.background_filename:
|
||||
background_bytes = await file_manager.get_background(profile.background_filename)
|
||||
if background_bytes:
|
||||
# 转换为base64
|
||||
background_data = base64.b64encode(background_bytes).decode('utf-8')
|
||||
# 根据文件扩展名添加data URI前缀
|
||||
if profile.background_filename.endswith('.png'):
|
||||
background_data = f"data:image/png;base64,{background_data}"
|
||||
elif profile.background_filename.endswith(('.jpg', '.jpeg')):
|
||||
background_data = f"data:image/jpeg;base64,{background_data}"
|
||||
elif profile.background_filename.endswith('.gif'):
|
||||
background_data = f"data:image/gif;base64,{background_data}"
|
||||
elif profile.background_filename.endswith('.webp'):
|
||||
background_data = f"data:image/webp;base64,{background_data}"
|
||||
|
||||
# 获取设置数据
|
||||
settings_data = None
|
||||
if profile.settings_filename:
|
||||
settings_dict = await file_manager.get_settings(profile.settings_filename)
|
||||
if settings_dict:
|
||||
settings_data = UserSettings(**settings_dict)
|
||||
|
||||
profile_data = UserProfileData(
|
||||
userid=profile.userid,
|
||||
avatar=avatar_data,
|
||||
background=background_data,
|
||||
nickname=profile.nickname,
|
||||
settings=settings_data,
|
||||
)
|
||||
|
||||
return UserProfileResponse(
|
||||
code=200,
|
||||
message="获取用户资料成功",
|
||||
data=profile_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return UserProfileResponse(
|
||||
code=500,
|
||||
message=f"获取用户资料失败: {str(e)}",
|
||||
data=None
|
||||
)
|
||||
|
||||
|
||||
@user_router.post("/profile/update", summary="更新用户资料")
|
||||
async def update_user_profile(
|
||||
data: UpdateUserProfileRequest,
|
||||
asyncsession: AsyncSession = Depends(get_db_session),
|
||||
):
|
||||
"""
|
||||
更新用户资料
|
||||
:param data: UpdateUserProfileRequest
|
||||
:return: UserProfileResponse
|
||||
"""
|
||||
try:
|
||||
# 使用token验证获取用户
|
||||
authme_request = AuthmeRequest(token=data.token)
|
||||
user = await fetch_user_by_token(authme_request, asyncsession)
|
||||
|
||||
async with asyncsession as session:
|
||||
result = await session.execute(
|
||||
select(UserProfile).where(UserProfile.userid == user.userid)
|
||||
)
|
||||
profile = result.scalars().first()
|
||||
|
||||
if not profile:
|
||||
# 如果用户资料不存在,创建新的
|
||||
profile = UserProfile(
|
||||
userid=user.userid,
|
||||
avatar_filename=None,
|
||||
background_filename=None,
|
||||
nickname=data.nickname,
|
||||
settings_filename=None
|
||||
)
|
||||
session.add(profile)
|
||||
else:
|
||||
# 更新昵称
|
||||
if data.nickname is not None:
|
||||
profile.nickname = data.nickname
|
||||
|
||||
# 处理头像更新
|
||||
if data.avatar is not None:
|
||||
if data.avatar: # 如果头像不为空
|
||||
new_avatar_filename = await file_manager.save_avatar(user.userid, data.avatar)
|
||||
profile.avatar_filename = new_avatar_filename
|
||||
else: # 如果头像为空,表示删除头像
|
||||
if profile.avatar_filename:
|
||||
await file_manager.delete_avatar(profile.avatar_filename)
|
||||
profile.avatar_filename = None
|
||||
|
||||
# 处理背景更新
|
||||
if data.background is not None:
|
||||
if data.background: # 如果背景不为空
|
||||
new_background_filename = await file_manager.save_background(user.userid, data.background)
|
||||
profile.background_filename = new_background_filename
|
||||
else: # 如果背景为空,表示删除背景
|
||||
if profile.background_filename:
|
||||
await file_manager.delete_background(profile.background_filename)
|
||||
profile.background_filename = None
|
||||
|
||||
# 处理设置更新
|
||||
if data.settings is not None:
|
||||
if data.settings: # 如果设置不为空
|
||||
# data.settings在model验证时已经被转换为UserSettings对象
|
||||
if isinstance(data.settings, UserSettings):
|
||||
settings_dict = data.settings.model_dump()
|
||||
new_settings_filename = await file_manager.save_settings(user.userid, settings_dict)
|
||||
profile.settings_filename = new_settings_filename
|
||||
else:
|
||||
# 如果不是UserSettings对象,说明验证有问题
|
||||
raise ValueError(f"Settings对象类型错误: {type(data.settings)}")
|
||||
else: # 如果设置为空,表示删除设置
|
||||
if profile.settings_filename:
|
||||
await file_manager.delete_settings(profile.settings_filename)
|
||||
profile.settings_filename = None
|
||||
|
||||
await session.commit()
|
||||
await session.refresh(profile)
|
||||
|
||||
# 获取更新后的数据
|
||||
avatar_data = None
|
||||
if profile.avatar_filename:
|
||||
avatar_bytes = await file_manager.get_avatar(profile.avatar_filename)
|
||||
if avatar_bytes:
|
||||
avatar_data = base64.b64encode(avatar_bytes).decode('utf-8')
|
||||
if profile.avatar_filename.endswith('.png'):
|
||||
avatar_data = f"data:image/png;base64,{avatar_data}"
|
||||
elif profile.avatar_filename.endswith(('.jpg', '.jpeg')):
|
||||
avatar_data = f"data:image/jpeg;base64,{avatar_data}"
|
||||
elif profile.avatar_filename.endswith('.gif'):
|
||||
avatar_data = f"data:image/gif;base64,{avatar_data}"
|
||||
|
||||
background_data = None
|
||||
if profile.background_filename:
|
||||
background_bytes = await file_manager.get_background(profile.background_filename)
|
||||
if background_bytes:
|
||||
background_data = base64.b64encode(background_bytes).decode('utf-8')
|
||||
if profile.background_filename.endswith('.png'):
|
||||
background_data = f"data:image/png;base64,{background_data}"
|
||||
elif profile.background_filename.endswith(('.jpg', '.jpeg')):
|
||||
background_data = f"data:image/jpeg;base64,{background_data}"
|
||||
elif profile.background_filename.endswith('.gif'):
|
||||
background_data = f"data:image/gif;base64,{background_data}"
|
||||
elif profile.background_filename.endswith('.webp'):
|
||||
background_data = f"data:image/webp;base64,{background_data}"
|
||||
|
||||
settings_data = None
|
||||
if profile.settings_filename:
|
||||
settings_dict = await file_manager.get_settings(profile.settings_filename)
|
||||
if settings_dict:
|
||||
settings_data = UserSettings(**settings_dict)
|
||||
|
||||
profile_data = UserProfileData(
|
||||
userid=profile.userid,
|
||||
avatar=avatar_data,
|
||||
background=background_data,
|
||||
nickname=profile.nickname,
|
||||
settings=settings_data,
|
||||
)
|
||||
|
||||
return UserProfileResponse(
|
||||
code=200,
|
||||
message="更新用户资料成功",
|
||||
data=profile_data
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return UserProfileResponse(
|
||||
code=500,
|
||||
message=f"更新用户资料失败: {str(e)}",
|
||||
data=None
|
||||
)
|
||||
79
router/user/model.py
Normal file
79
router/user/model.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from router.common_model import BaseResponse
|
||||
from typing import Optional, Dict, Any, Union
|
||||
import json
|
||||
|
||||
|
||||
class UserSettings(BaseModel):
|
||||
"""用户设置模型"""
|
||||
theme: str = Field(..., description="主题模式")
|
||||
lightModeOpacity: float = Field(..., description="浅色模式透明度", ge=0.0, le=1.0)
|
||||
lightModeBrightness: float = Field(..., description="浅色模式亮度", ge=0.0, le=1.0)
|
||||
darkModeOpacity: float = Field(..., description="深色模式透明度", ge=0.0, le=1.0)
|
||||
darkModeBrightness: float = Field(..., description="深色模式亮度", ge=0.0, le=1.0)
|
||||
backgroundBlur: float = Field(..., description="背景模糊强度", ge=0.0, le=1.0)
|
||||
|
||||
@field_validator('theme')
|
||||
def validate_theme(cls, v):
|
||||
"""验证主题值"""
|
||||
valid_themes = ['light', 'dark', 'system', 'ThemeMode.light', 'ThemeMode.dark', 'ThemeMode.system']
|
||||
if v not in valid_themes:
|
||||
raise ValueError(f"无效的主题值: {v},有效值: {valid_themes}")
|
||||
return v
|
||||
|
||||
|
||||
class UserProfileData(BaseModel):
|
||||
"""用户资料数据模型"""
|
||||
userid: str = Field(..., description="用户ID")
|
||||
avatar: Optional[str] = Field(None, description="用户头像base64数据")
|
||||
background: Optional[str] = Field(None, description="用户背景base64数据")
|
||||
nickname: Optional[str] = Field(None, description="用户昵称")
|
||||
settings: Optional[UserSettings] = Field(None, description="用户设置对象")
|
||||
|
||||
|
||||
class GetUserProfileRequest(BaseModel):
|
||||
"""获取用户资料请求模型"""
|
||||
token: str = Field(..., description="用户认证token")
|
||||
|
||||
|
||||
class UpdateUserProfileRequest(BaseModel):
|
||||
"""更新用户资料请求模型"""
|
||||
token: str = Field(..., description="用户认证token")
|
||||
avatar: Optional[str] = Field(None, description="用户头像base64编码数据")
|
||||
background: Optional[str] = Field(None, description="用户背景base64编码数据")
|
||||
nickname: Optional[str] = Field(None, description="用户昵称")
|
||||
settings: Optional[Union[UserSettings, str]] = Field(None, description="用户设置对象或JSON字符串")
|
||||
|
||||
@field_validator('settings')
|
||||
def parse_settings(cls, v):
|
||||
"""解析settings字段,支持字符串和对象两种格式"""
|
||||
if v is None:
|
||||
return v
|
||||
|
||||
# 如果已经是UserSettings对象,直接返回
|
||||
if isinstance(v, UserSettings):
|
||||
return v
|
||||
|
||||
# 如果是字符串,尝试解析为JSON然后创建UserSettings对象
|
||||
if isinstance(v, str):
|
||||
try:
|
||||
settings_dict = json.loads(v)
|
||||
return UserSettings(**settings_dict)
|
||||
except json.JSONDecodeError as e:
|
||||
raise ValueError(f"settings字段JSON格式错误: {str(e)}")
|
||||
except Exception as e:
|
||||
raise ValueError(f"settings字段验证失败: {str(e)}")
|
||||
|
||||
# 如果是字典,直接创建UserSettings对象
|
||||
if isinstance(v, dict):
|
||||
try:
|
||||
return UserSettings(**v)
|
||||
except Exception as e:
|
||||
raise ValueError(f"settings字段验证失败: {str(e)}")
|
||||
|
||||
raise ValueError("settings字段必须是JSON字符串、字典或UserSettings对象")
|
||||
|
||||
|
||||
class UserProfileResponse(BaseResponse[UserProfileData]):
|
||||
"""用户资料响应模型"""
|
||||
pass
|
||||
Reference in New Issue
Block a user