Files
kami_apple_exchage/backend/app/services/task_service.py
danial 8bc8e1c664 feat(links): 实现基于权重的轮询算法和链接管理功能
- 新增链接权重字段,支持1-100范围设置
- 修改轮询算法为基于权重的选择机制
- 更新链接API接口返回统一使用LinkInfo模型
- 添加更新链接权重的PATCH端点
- 调整链接仓库查询逻辑,只包含激活状态链接
- 迁移链接相关Pydantic模型到task模块统一管理
- 修改分页响应格式为通用PaginatedResponse包装
- 禁用OpenTelemetry监控配置
2025-09-30 17:02:02 +08:00

440 lines
16 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.

"""
任务服务层
处理任务相关的业务逻辑
"""
import traceback
from datetime import datetime
from typing import Any, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.log import get_logger
from app.core.state_manager import StateType, TaskState, task_state_manager
from app.enums.task import OrderTaskStatus
from app.repositories.task_repository import TaskRepository
from app.schemas.task import (
CardInfo,
GiftCardSubmissionRequest,
GiftCardSubmissionResponse,
GiftCardDetailCreate,
LinkInfo,
TaskListItem,
TaskListResponse,
UserInfo,
)
from app.services.gift_card_service import GiftCardService
logger = get_logger(__name__)
class TaskService:
"""任务服务类"""
def __init__(self, db: AsyncSession):
self.db = db
self.task_repo = TaskRepository(db)
async def get_task_list(self) -> TaskListResponse:
"""
获取任务列表
Returns:
TaskListResponse: 任务列表响应
"""
try:
# 获取所有任务状态
all_states = await task_state_manager.state_manager.list_states(
StateType.TASK
)
all_tasks = [state for state in all_states if isinstance(state, TaskState)]
task_list = []
for task in all_tasks:
# 创建任务列表项
task_item = TaskListItem(
# 任务状态相关字段
task_id=task.identifier,
worker_id=task.worker_id,
status=task.status,
progress=task.progress,
created_at=task.created_at.isoformat() if task.created_at else "",
updated_at=task.updated_at.isoformat() if task.updated_at else "",
completed_at=(
task.completed_at.isoformat() if task.completed_at else None
),
failed_at=task.failed_at.isoformat() if task.failed_at else None,
error_message=task.error_message,
# 订单数据库字段(默认为空,将在 enrich 方法中填充)
order_id=task.worker_id,
order_status=None,
final_order_url=None,
order_failure_reason=None,
order_completed_at=None,
order_created_at=None,
order_updated_at=None,
order_is_deleted=None,
order_deleted_at=None,
# 关联数据(将在 enrich 方法中填充)
user_info=None,
link_info=None,
card_info=None,
)
# 如果有订单ID获取关联的用户、链接和礼品卡信息
order_id = task_item.order_id
if order_id:
task_item = await self._enrich_task_with_order_data(
task_item, order_id
)
task_list.append(task_item)
return TaskListResponse(
success=True,
tasks=task_list,
total=len(task_list),
message="获取任务列表成功",
)
except Exception as e:
logger.error(f"获取任务列表失败: {traceback.format_exc()}")
raise
async def _enrich_task_with_order_data(
self, task_item: TaskListItem, order_id: str
) -> TaskListItem:
"""
使用订单数据丰富任务信息,包含完整的数据库字段
Args:
task_item: 任务项
order_id: 订单ID
Returns:
TaskListItem: 丰富后的任务项,包含所有数据库字段
"""
try:
# 查询订单及其所有关联数据
order = await self.task_repo.get_order_with_all_relations(order_id)
if not order:
return task_item
# 格式化时间函数
def format_datetime(dt: Any) -> str | None:
if dt:
return dt.isoformat() if hasattr(dt, "isoformat") else str(dt)
return None
# 准备用户信息
user_info = None
if order.user_data:
user_data = order.user_data
user_info = UserInfo(
id=user_data.id,
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,
created_at=format_datetime(user_data.created_at),
updated_at=format_datetime(user_data.updated_at),
)
# 准备链接信息
link_info = None
if order.links:
link = order.links
link_info = LinkInfo(
weight=link.weight,
id=link.id,
url=link.url,
amount=link.amount,
status=link.status,
created_at=format_datetime(link.created_at),
updated_at=format_datetime(link.updated_at),
)
# 准备礼品卡信息(支持多张卡片)
card_info = None
gift_card_list = []
if isinstance(order.gift_cards, list):
gift_card_list = order.gift_cards
elif order.gift_cards:
gift_card_list.append(order.gift_cards)
if gift_card_list:
card_info = []
for gift_card in gift_card_list:
card_info_item = CardInfo(
id=gift_card.id,
card_code=gift_card.card_code,
card_value=gift_card.card_value,
status=gift_card.status.value,
failure_reason=gift_card.failure_reason,
order_id=gift_card.order_id,
created_at=format_datetime(gift_card.created_at),
updated_at=format_datetime(gift_card.updated_at),
)
card_info.append(card_info_item)
# 创建新的任务项,包含所有数据库字段
return TaskListItem(
# 任务状态相关字段
task_id=task_item.task_id,
worker_id=task_item.worker_id,
status=task_item.status,
progress=task_item.progress,
created_at=task_item.created_at,
updated_at=task_item.updated_at,
completed_at=task_item.completed_at,
failed_at=task_item.failed_at,
error_message=task_item.error_message,
# 订单数据库字段
order_id=order.id,
order_status=order.status.value,
final_order_url=order.final_order_url,
order_failure_reason=order.failure_reason,
order_completed_at=format_datetime(order.completed_at),
order_created_at=format_datetime(order.created_at),
order_updated_at=format_datetime(order.updated_at),
order_is_deleted=order.is_deleted,
order_deleted_at=format_datetime(order.deleted_at),
# 关联数据
user_info=user_info,
link_info=link_info,
card_info=card_info,
)
except Exception as e:
logger.error(f"获取订单关联数据失败: {traceback.format_exc()}")
return task_item
async def submit_gift_card(
self, request: GiftCardSubmissionRequest
) -> GiftCardSubmissionResponse:
"""
提交礼品卡信息并更新任务状态
Args:
request: 礼品卡提交请求
Returns:
GiftCardSubmissionResponse: 操作结果
"""
try:
# 获取现有任务
task = await task_state_manager.get_task_state(request.task_id)
if not task:
raise ValueError("任务不存在")
# 更新任务结果,包含礼品卡信息
updated_result = task.result.copy() if task.result else {}
updated_result.update(
{
"card_code": request.card_code,
"card_value": request.card_value,
"submitted_at": datetime.now().isoformat(),
}
)
# 把礼品卡数据保存的数据库中
await GiftCardService(self.db).submit_gift_card_info(
GiftCardDetailCreate(
task_id=request.task_id,
card_code=request.card_code,
card_value=request.card_value,
)
)
logger.info(
f"礼品卡信息保存成功任务ID: {request.task_id} 订单ID{task.worker_id}"
)
# 更新任务状态为已接收礼品卡
success = await task_state_manager.set_task_state(
task_id=request.task_id,
worker_id=task.worker_id,
order_id=task.worker_id,
progress=72,
status=OrderTaskStatus.GIFT_CARD_RECEIVED,
result=updated_result,
)
if success:
logger.info(f"礼品卡提交成功任务ID: {request.task_id}")
return GiftCardSubmissionResponse(
success=True, task_id=request.task_id, message="礼品卡提交成功"
)
else:
raise RuntimeError("更新任务状态失败")
except Exception as e:
logger.error(f"提交礼品卡失败: {e}")
raise
async def get_task_by_id(self, task_id: str) -> Optional[TaskListItem]:
"""
根据任务ID获取任务详情
Args:
task_id: 任务ID
Returns:
TaskListItem | None: 任务详情
"""
try:
task = await task_state_manager.get_task_state(task_id)
if not task:
return None
# 获取任务基础信息
task_dict = (
task.model_dump()
if hasattr(task, "model_dump")
else task.model_dump() if hasattr(task, "dict") else vars(task)
)
# 创建任务列表项
created_at = task_dict.get("created_at")
updated_at = task_dict.get("updated_at")
completed_at = task_dict.get("completed_at")
failed_at = task_dict.get("failed_at")
# 转换datetime为字符串
def format_datetime(dt: datetime | str | None) -> str | None:
if isinstance(dt, datetime):
return dt.isoformat()
elif isinstance(dt, str):
return dt
return None
task_item = TaskListItem(
# 任务状态相关字段
task_id=task_dict.get("identifier", ""),
worker_id=task_dict.get("worker_id"),
status=task_dict.get("status", ""),
progress=task_dict.get("progress", 0.0),
created_at=format_datetime(created_at) or "",
updated_at=format_datetime(updated_at) or "",
completed_at=format_datetime(completed_at),
failed_at=format_datetime(failed_at),
error_message=task_dict.get("error_message"),
# 订单数据库字段(默认为空,将在 enrich 方法中填充)
order_id=(
task_dict.get("result", {}).get("order_id")
if task_dict.get("result")
else None
),
order_status=None,
final_order_url=None,
order_failure_reason=None,
order_completed_at=None,
order_created_at=None,
order_updated_at=None,
order_is_deleted=None,
order_deleted_at=None,
# 关联数据(将在 enrich 方法中填充)
user_info=None,
link_info=None,
card_info=None,
)
# 如果有订单ID获取关联的用户、链接和礼品卡信息
order_id = task.worker_id
if order_id:
task_item = await self._enrich_task_with_order_data(task_item, order_id)
return task_item
except Exception as e:
logger.error(f"获取任务详情失败: {e}")
raise
async def get_tasks_by_status(self, status: str) -> list[TaskListItem]:
"""
根据状态获取任务列表
Args:
status: 任务状态
Returns:
list[TaskListItem]: 任务列表
"""
try:
all_tasks = await task_state_manager.state_manager.list_states(
StateType.TASK
)
filtered_tasks = []
for task in all_tasks:
task_dict = (
task.model_dump()
if hasattr(task, "model_dump")
else task.model_dump() if hasattr(task, "dict") else vars(task)
)
if task_dict.get("status") == status:
# 转换datetime为字符串
def format_datetime(dt: datetime | str | None) -> str | None:
if isinstance(dt, datetime):
return dt.isoformat()
elif isinstance(dt, str):
return dt
return None
created_at = task_dict.get("created_at")
updated_at = task_dict.get("updated_at")
completed_at = task_dict.get("completed_at")
failed_at = task_dict.get("failed_at")
task_item = TaskListItem(
# 任务状态相关字段
task_id=task_dict.get("identifier", ""),
worker_id=task_dict.get("worker_id"),
status=task_dict.get("status", ""),
progress=task_dict.get("progress", 0.0),
created_at=format_datetime(created_at) or "",
updated_at=format_datetime(updated_at) or "",
completed_at=format_datetime(completed_at),
failed_at=format_datetime(failed_at),
error_message=task_dict.get("error_message"),
# 订单数据库字段(默认为空,将在 enrich 方法中填充)
order_id=(
task_dict.get("result", {}).get("order_id")
if task_dict.get("result")
else None
),
order_status=None,
final_order_url=None,
order_failure_reason=None,
order_completed_at=None,
order_created_at=None,
order_updated_at=None,
order_is_deleted=None,
order_deleted_at=None,
# 关联数据(将在 enrich 方法中填充)
user_info=None,
link_info=None,
card_info=None,
)
# 如果有订单ID获取关联的用户、链接和礼品卡信息
order_id = task_item.order_id
if order_id:
task_item = await self._enrich_task_with_order_data(
task_item, order_id
)
filtered_tasks.append(task_item)
return filtered_tasks
except Exception as e:
logger.error(f"根据状态获取任务失败: {e}")
raise