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

443 lines
15 KiB
Python

"""
任务相关的Pydantic模型
包含统一的用户、链接、礼品卡和分页响应模型
"""
from datetime import datetime
from enum import Enum
from typing import Any, Generic, List, TypeVar
from pydantic import BaseModel, ConfigDict, Field, field_validator
from app.enums.task import OrderTaskStatus
from app.models.orders import OrderStatus
from app.models.giftcards import GiftCardStatus
class LinkStatus(str, Enum):
"""链接状态枚举"""
ACTIVE = "active"
INACTIVE = "inactive"
T = TypeVar("T")
class ProcessOrderRequest(BaseModel):
"""处理单个订单请求"""
order_id: str
priority: int | None = 5
class BatchProcessRequest(BaseModel):
"""批量处理订单请求"""
order_ids: list[str]
batch_size: int | None = 5
class TaskStatusResponse(BaseModel):
"""任务状态响应"""
task_id: str
status: str
progress: float
result: dict[str, Any] | None = None
error: str | None = None
class WorkerStatusResponse(BaseModel):
"""工作进程状态响应"""
worker_id: str
status: str
current_order_id: str | None = None
progress: float
start_time: str | None = None
class QueueStatsResponse(BaseModel):
"""队列统计响应"""
success: bool
stats: dict[str, Any]
timestamp: str
class TaskControlRequest(BaseModel):
"""任务控制请求模型"""
is_paused: bool = Field(..., description="是否暂停任务")
reason: str = Field("", description="控制原因")
class TaskControlResponse(BaseModel):
"""任务控制响应模型"""
success: bool = Field(..., description="操作是否成功")
is_paused: bool = Field(..., description="当前暂停状态")
reason: str = Field(..., description="控制原因")
message: str = Field(..., description="响应消息")
class TaskStateResponse(BaseModel):
"""任务状态响应模型"""
success: bool = Field(..., description="操作是否成功")
is_paused: bool = Field(..., description="当前暂停状态")
reason: str = Field(..., description="控制原因")
message: str = Field(..., description="响应消息")
class UserInfo(BaseModel):
"""统一用户信息响应模型 - 与数据库字段一致"""
id: str = Field(..., description="用户数据ID")
first_name: str = Field(..., description="名字")
last_name: str = Field(..., description="姓氏")
email: str = Field(..., description="邮箱")
phone: str = Field(..., description="电话")
street_address: str = Field(..., description="街道地址")
city: str = Field(..., description="城市")
state: str = Field(..., description="州/省")
zip_code: str = Field(..., description="邮政编码")
created_at: str = Field(description="创建时间")
updated_at: str = Field(description="更新时间")
model_config = ConfigDict(from_attributes=True)
class LinkInfo(BaseModel):
"""统一链接信息响应模型 - 与数据库字段一致"""
id: str = Field(..., description="链接ID")
url: str = Field(..., description="链接地址")
amount: float = Field(..., description="金额")
weight: int = Field(..., description="权重(1-100)")
status: LinkStatus = Field(..., description="链接状态")
created_at: str = Field(description="创建时间")
updated_at: str = Field(description="更新时间")
model_config = ConfigDict(from_attributes=True)
class CardInfo(BaseModel):
"""统一礼品卡信息响应模型 - 与数据库字段一致"""
id: str = Field(..., description="礼品卡ID")
card_code: str = Field(..., description="礼品卡号码")
card_value: float = Field(..., description="卡片面额")
status: str = Field(..., description="礼品卡状态")
failure_reason: str | None = Field(None, description="失败原因")
order_id: str = Field(..., description="关联订单ID")
created_at: str = Field(description="创建时间")
updated_at: str = Field(description="更新时间")
model_config = ConfigDict(from_attributes=True)
class TaskListItem(BaseModel):
"""任务列表项模型 - 包含完整的数据库字段"""
# 任务状态相关字段
task_id: str = Field(..., description="任务ID")
worker_id: str | None = Field(None, description="工作节点ID")
status: OrderTaskStatus = Field(..., description="任务状态")
progress: float = Field(..., description="进度")
created_at: str = Field(..., description="任务创建时间")
updated_at: str = Field(..., description="任务更新时间")
completed_at: str | None = Field(None, description="任务完成时间")
failed_at: str | None = Field(None, description="任务失败时间")
error_message: str | None = Field(None, description="错误信息")
# 订单数据库字段
order_id: str | None = Field(None, description="订单ID")
order_status: str | None = Field(None, description="订单状态")
final_order_url: str | None = Field(None, description="最终订单URL")
order_failure_reason: str | None = Field(None, description="订单失败原因")
order_completed_at: str | None = Field(None, description="订单完成时间")
order_created_at: str | None = Field(None, description="订单创建时间")
order_updated_at: str | None = Field(None, description="订单更新时间")
order_is_deleted: bool | None = Field(None, description="订单是否已删除")
order_deleted_at: str | None = Field(None, description="订单删除时间")
# 关联数据
user_info: UserInfo | None = Field(None, description="用户信息")
link_info: LinkInfo | None = Field(None, description="链接信息")
card_info: list[CardInfo] | None = Field(None, description="礼品卡信息列表")
class TaskListResponse(BaseModel):
"""任务列表响应模型"""
success: bool = Field(..., description="操作是否成功")
tasks: list[TaskListItem] = Field(..., description="任务列表")
total: int = Field(..., description="任务总数")
message: str = Field(..., description="响应消息")
class GiftCardRequest(BaseModel):
"""礼品卡请求模型"""
card_code: str = Field(..., description="礼品卡代码")
card_value: float = Field(..., description="礼品卡面值")
order_id: str = Field(..., description="关联订单ID")
metadata: dict[str, Any] = Field(default_factory=dict, description="额外数据")
class GiftCardResponse(BaseModel):
"""礼品卡响应模型"""
success: bool = Field(..., description="操作是否成功")
task_id: str = Field(..., description="创建的任务ID")
card_code: str = Field(..., description="礼品卡代码")
message: str = Field(..., description="响应消息")
class GiftCardSubmissionRequest(BaseModel):
"""礼品卡提交请求模型"""
task_id: str = Field(..., description="任务ID")
card_code: str = Field(..., description="礼品卡代码")
card_value: float = Field(..., description="礼品卡面值")
class GiftCardSubmissionResponse(BaseModel):
"""礼品卡提交响应模型"""
success: bool = Field(..., description="操作是否成功")
task_id: str = Field(..., description="任务ID")
message: str = Field(..., description="响应消息")
# 合并现有的礼品卡schema
class GiftCardInfoCreate(BaseModel):
"""创建礼品卡信息的schema"""
order_id: str = Field(..., description="关联订单ID")
card_code: str = Field(..., description="礼品卡代码")
card_value: float = Field(..., description="礼品卡面值")
model_config = {"from_attributes": True}
class GiftCardInfoResponse(BaseModel):
"""礼品卡信息响应模型 - 与数据表字段完全一致"""
id: str = Field(..., description="礼品卡ID")
card_code: str = Field(..., description="礼品卡号码")
status: str = Field(..., description="礼品卡状态")
failure_reason: str | None = Field(None, description="失败原因")
order_result_id: str = Field(..., description="订单结果ID")
created_at: str = Field(..., description="创建时间")
updated_at: str = Field(..., description="更新时间")
model_config = {"from_attributes": True}
class GiftCardDetailCreate(BaseModel):
"""礼品卡详细信息输入schema"""
task_id: str = Field(..., description="任务ID")
card_code: str = Field(..., description="礼品卡代码")
card_value: float = Field(..., description="礼品卡面值")
model_config = {"from_attributes": True}
class DeleteAllDataResponse(BaseModel):
"""删除所有数据响应模型"""
success: bool = Field(..., description="操作是否成功")
deleted_tables: dict[str, int] = Field(..., description="各表删除的记录数")
message: str = Field(..., description="响应消息")
class PaginatedResponse(BaseModel, Generic[T]):
"""统一分页响应模型"""
items: List[T] = Field(..., description="数据项列表")
total: int = Field(..., description="总记录数")
page: int = Field(..., description="当前页码")
size: int = Field(..., description="每页大小")
pages: int = Field(..., description="总页数")
model_config = ConfigDict(from_attributes=True, arbitrary_types_allowed=True)
# 用户数据相关模型
class UserDataBase(BaseModel):
"""用户数据基础模型"""
first_name: str = Field(..., description="名字", max_length=255)
last_name: str = Field(..., description="姓氏", max_length=255)
email: str = Field(..., description="邮箱", max_length=255)
phone: str = Field(..., description="电话", max_length=50)
street_address: str = Field(..., description="街道地址", max_length=500)
city: str = Field(..., description="城市", max_length=255)
state: str = Field(..., description="州/省", max_length=255)
zip_code: str = Field(..., description="邮编", max_length=20)
@field_validator("email")
@classmethod
def validate_email(cls, v):
"""验证邮箱格式"""
import re
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
if not re.match(pattern, v):
raise ValueError("邮箱格式不正确")
return v
class UserDataCreate(UserDataBase):
"""创建用户数据请求模型"""
pass
class UserDataUpdate(BaseModel):
"""更新用户数据请求模型"""
first_name: str | None = Field(None, description="名字", max_length=255)
last_name: str | None = Field(None, description="姓氏", max_length=255)
email: str | None = Field(None, description="邮箱", max_length=255)
phone: str | None = Field(None, description="电话", max_length=50)
street_address: str | None = Field(None, description="街道地址", max_length=500)
city: str | None = Field(None, description="城市", max_length=255)
state: str | None = Field(None, description="州/省", max_length=255)
zip_code: str | None = Field(None, description="邮编", max_length=20)
class UserDataResponse(UserDataBase):
"""用户数据响应模型"""
id: str = Field(..., description="用户数据ID")
created_at: str = Field(..., description="创建时间")
updated_at: str = Field(..., description="更新时间")
model_config = ConfigDict(from_attributes=True)
class UserDataUploadResponse(BaseModel):
"""用户数据上传响应模型"""
user_data: UserDataResponse
message: str = Field(..., description="响应消息")
class UserDataStatsResponse(BaseModel):
"""用户数据统计响应模型"""
total_users: int = Field(..., description="总用户数")
total_orders: int = Field(..., description="总订单数")
recent_uploads: int = Field(..., description="最近上传数量")
success_rate: float = Field(..., description="成功率")
class BulkDeleteUserDataResponse(BaseModel):
"""批量删除用户数据响应模型"""
total_users: int = Field(..., description="总用户数")
deleted_users: int = Field(..., description="已删除用户数")
skipped_users: int = Field(..., description="跳过的用户数")
message: str = Field(..., description="响应消息")
# 链接相关模型
class LinkBase(BaseModel):
"""链接基础模型"""
url: str = Field(..., description="链接URL", max_length=255)
amount: float = Field(..., description="金额", gt=0)
weight: int = Field(1, description="权重(1-100)", ge=1, le=100)
status: LinkStatus = Field(LinkStatus.ACTIVE, description="链接状态")
class LinkCreate(LinkBase):
"""创建链接请求模型"""
pass
class LinkUpdate(BaseModel):
"""更新链接请求模型"""
url: str | None = Field(None, description="链接URL", max_length=255)
amount: float | None = Field(None, description="金额", gt=0)
weight: int | None = Field(None, description="权重(1-100)", ge=1, le=100)
status: LinkStatus | None = Field(None, description="链接状态")
model_config = ConfigDict(use_enum_values=True)
class LinkPoolResponse(BaseModel):
"""轮询池响应模型"""
link: LinkInfo
pool_position: int = Field(..., description="在轮询池中的位置")
class LinkStatsResponse(BaseModel):
"""链接统计响应模型"""
total_links: int = Field(..., description="总链接数")
total_orders: int = Field(..., description="总订单数")
average_amount: float = Field(..., description="平均金额")
min_amount: float = Field(..., description="最小金额")
max_amount: float = Field(..., description="最大金额")
# 订单相关模型
class OrderStatsResponse(BaseModel):
"""订单统计响应"""
total: int
pending: int
processing: int
success: int
failed: int
last_update: str
class OrderDetailResponse(BaseModel):
"""订单详情响应 - 与数据库结构完全一致"""
id: str = Field(..., description="订单ID")
status: OrderStatus = Field(..., description="订单状态")
created_at: str = Field(..., description="创建时间")
updated_at: str = Field(..., description="更新时间")
final_order_url: str | None = Field(None, description="最终订单URL")
final_order_id: str | None = Field(None, description="最终苹果订单ID")
failure_reason: str | None = Field(None, description="失败原因")
user_data_id: str = Field(..., description="用户数据ID")
links_id: str = Field(..., description="链接ID")
# 关联关系
user_data: UserInfo = Field(description="用户数据")
links: LinkInfo = Field(description="链接信息")
gift_cards: list[CardInfo] = Field(default_factory=list, description="礼品卡列表")
model_config = ConfigDict(from_attributes=True)
class UploadUrlRequest(BaseModel):
"""上传URL请求"""
url: str = Field(..., min_length=1, description="上传URL")
thread_id: str | None = Field(None, description="线程ID")
class UploadUrlResponse(BaseModel):
"""上传URL响应"""
success: bool
message: str
upload_config_id: str
url: str