Files
kami_apple_exchage/backend/app/services/user_data_service.py
danial 5c486e34d3 docs(项目): 添加项目文档并进行代码调整
- 新增 CODEBUDDY.md、GEMINI.md、GEMINI_CN.md 等项目文档
- 更新 Dockerfile 和其他配置文件
- 优化部分代码结构,如 orders.py、tasks.py 等
- 新增 .dockerignore 文件
2025-09-12 19:38:24 +08:00

312 lines
9.0 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.

"""
用户数据服务层
处理用户数据相关的业务逻辑
"""
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from celery.result import AsyncResult
from app.core.redis_manager import redis_manager
from app.repositories.repository_factory import RepositoryFactory
from app.schemas.user_data import (
UserDataCreate,
UserDataUpdate,
UserDataResponse,
UserDataListResponse,
UserDataUploadResponse,
UserDataStatsResponse,
UserInfoResponse,
UserDataBase,
)
from app.models.user_data import UserData
from app.models.orders import OrderResultStatus
from app.core.log import get_logger
# 延迟导入避免循环依赖
def get_create_order_task():
from app.core.celery_app import celery_app
return celery_app.tasks["create_order_task"]
logger = get_logger(__name__)
class UserDataService:
"""用户数据服务类"""
def __init__(self, db: AsyncSession):
self.db = db
self.repo_factory = RepositoryFactory(db)
async def upload_user_data(
self, user_data: UserDataCreate
) -> UserDataUploadResponse:
"""
上传用户数据并触发创建订单任务
Args:
user_data: 用户数据创建模型
Returns:
上传响应包含任务ID
"""
# 检查邮箱是否已存在
existing_user = await self.repo_factory.user_data.get_user_by_email(
user_data.email
)
if existing_user:
raise ValueError(f"邮箱已存在: {user_data.email}")
# 创建用户数据
user = await self.repo_factory.user_data.create(
first_name=user_data.first_name,
last_name=user_data.last_name,
email=user_data.email,
phone=user_data.phone,
street_address=user_data.street_address,
city=user_data.city,
state=user_data.state,
zip_code=user_data.zip_code,
)
logger.info(f"用户数据创建成功: {user.id}")
await redis_manager.save_user_data_id(user.id)
return UserDataUploadResponse(
user_data=self._convert_to_response(user),
message="创建成功",
)
async def get_user_data(self, user_id: str) -> UserDataResponse | None:
"""
获取单个用户数据
Args:
user_id: 用户ID
Returns:
用户数据响应或None
"""
user = await self.repo_factory.user_data.get_by_id(user_id)
if not user:
return None
return self._convert_to_response(user)
async def get_user_info(self, user_id: str) -> UserInfoResponse | None:
"""
获取用户完整信息(包含所有数据库字段)
Args:
user_id: 用户ID
Returns:
用户完整信息响应或None
"""
user = await self.repo_factory.user_data.get_by_id(user_id)
if not user:
return None
return self._convert_to_info_response(user)
async def update_user_data(
self, user_id: str, user_data: UserDataUpdate
) -> UserDataResponse | None:
"""
更新用户数据
Args:
user_id: 用户ID
user_data: 更新数据
Returns:
更新后的用户数据响应或None
"""
# 检查用户是否存在
existing_user = await self.repo_factory.user_data.get_by_id(user_id)
if not existing_user:
return None
# 如果更新邮箱,检查是否与其他用户冲突
if user_data.email and user_data.email != existing_user.email:
email_conflict = await self.repo_factory.user_data.get_user_by_email(
user_data.email
)
if email_conflict and email_conflict.id != user_id:
raise ValueError(f"邮箱已存在: {user_data.email}")
# 更新用户数据
update_data = {
k: v for k, v in user_data.dict(exclude_unset=True).items() if v is not None
}
updated_user = await self.repo_factory.user_data.update_by_id(
user_id, **update_data
)
if updated_user:
logger.info(f"用户数据更新成功: {user_id}")
return self._convert_to_response(updated_user)
return None
async def delete_user_data(self, user_id: str) -> bool:
"""
删除用户数据
Args:
user_id: 用户ID
Returns:
是否删除成功
"""
# 检查是否有关联订单
user_with_orders = await self.repo_factory.user_data.get_user_with_orders(
user_id
)
if user_with_orders and user_with_orders.orders:
raise ValueError("无法删除有关联订单的用户数据")
success = await self.repo_factory.user_data.delete_by_id(user_id)
if success:
logger.info(f"用户数据删除成功: {user_id}")
return success
async def get_user_data_list(
self,
page: int = 1,
size: int = 20,
city: str | None = None,
state: str | None = None,
country: str | None = None,
name_pattern: str | None = None,
) -> UserDataListResponse:
"""
获取用户数据列表
Args:
page: 页码
size: 每页大小
city: 城市过滤
state: 州/省过滤
country: 国家过滤
name_pattern: 姓名模糊搜索
Returns:
用户数据列表响应
"""
filters = {}
# 构建过滤条件
if city:
users = await self.repo_factory.user_data.get_users_by_city(city)
elif state:
users = await self.repo_factory.user_data.get_users_by_state(state)
elif name_pattern:
users = await self.repo_factory.user_data.search_users_by_name(name_pattern)
else:
# 使用分页查询
if country:
filters["country"] = country
result = await self.repo_factory.user_data.get_paginated(
page=page, page_size=size, **filters
)
users = result.items
total = result.total
pages = result.pages
return UserDataListResponse(
items=[self._convert_to_response(user) for user in users],
total=total,
page=page,
size=size,
pages=pages,
)
# 对于非分页查询,手动计算分页
total = len(users)
start_idx = (page - 1) * size
end_idx = start_idx + size
page_users = users[start_idx:end_idx]
pages = (total + size - 1) // size
return UserDataListResponse(
items=[self._convert_to_response(user) for user in page_users],
total=total,
page=page,
size=size,
pages=pages,
)
async def get_task_status(self, task_id: str) -> dict[str, Any]:
"""
获取任务状态
Args:
task_id: 任务ID
Returns:
任务状态信息
"""
try:
result = AsyncResult(task_id)
return {
"task_id": task_id,
"status": result.status,
"result": result.result if result.ready() else None,
"info": result.info,
}
except Exception as e:
logger.error(f"获取任务状态失败: {str(e)}")
return {"task_id": task_id, "status": "UNKNOWN", "error": str(e)}
def _convert_to_response(self, user: UserData) -> UserDataResponse:
"""
将用户数据模型转换为响应模型
Args:
user: 用户数据模型
Returns:
用户数据响应模型
"""
return UserDataResponse(
id=user.id,
first_name=user.first_name,
last_name=user.last_name,
email=user.email,
phone=user.phone,
street_address=user.street_address,
city=user.city,
state=user.state,
zip_code=user.zip_code,
created_at=user.created_at.isoformat(),
updated_at=user.updated_at.isoformat(),
)
def _convert_to_info_response(self, user: UserData) -> UserInfoResponse:
"""
将用户数据模型转换为完整信息响应模型
Args:
user: 用户数据模型
Returns:
用户完整信息响应模型
"""
return UserInfoResponse(
id=user.id,
first_name=user.first_name,
last_name=user.last_name,
email=user.email,
phone=user.phone,
street_address=user.street_address,
city=user.city,
state=user.state,
zip_code=user.zip_code,
created_at=user.created_at.isoformat(),
updated_at=user.updated_at.isoformat(),
full_name=user.full_name,
full_address=user.full_address,
)