Files
kami_spider_monorepo/core/responses.py
danial 6c768b6e7b feat(jd): 添加京东相关路由及苹果权益充值功能
- 新增jd模块基础路由,整合app_store和payment子路由
- 实现苹果权益充值接口,支持苹果、携程及沃尔玛多个渠道
- 实现卡号密码查询接口,支持不同类别订单查询
- 新增短信认证相关接口,实现短信验证码发送及短信登录
- 新增商品管理接口,支持SKU详情查询及账号类下单功能
- 新增订单管理接口,实现订单删除功能
- 实现支付相关接口,增加刷新支付参数功能
- 定义完整请求及响应数据模型,确保接口数据规范
- 编写AppStoreSpider类,封装苹果应用内订单处理逻辑
- 引入多种代理池及请求重试机制,增强接口稳定性
- 添加详细日志记录,便于请求追踪与错误排查
2025-11-03 19:35:39 +08:00

388 lines
11 KiB
Python

"""
RESTful response format with generic types.
Provides unified response structure for all API endpoints.
"""
from typing import TypeVar, Generic, Optional, Any
from datetime import datetime
from pydantic import BaseModel, Field
from enum import IntEnum
T = TypeVar("T")
class ApiResponse(BaseModel, Generic[T]):
"""
Unified API response structure.
Both success and error responses use this structure.
Attributes:
code: Business status code (0 = success, >0 = error)
message: Human-readable message
data: Response payload (typed) or None for errors
trace_id: Request trace ID for debugging
timestamp: Response timestamp in ISO 8601 format
"""
code: int = Field(description="Business status code (0=success, >0=error)")
message: str = Field(description="Human-readable message")
data: Optional[T] = Field(default=None, description="Response payload")
trace_id: str = Field(description="Request trace ID")
timestamp: datetime = Field(
default_factory=datetime.utcnow, description="Response timestamp"
)
class PaginationMeta(BaseModel):
"""Pagination metadata."""
total: int = Field(description="Total number of records")
page: int = Field(description="Current page number (1-based)")
page_size: int = Field(description="Number of records per page")
total_pages: int = Field(description="Total number of pages")
class PaginatedData(BaseModel, Generic[T]):
"""
Paginated data structure.
Attributes:
items: List of items for current page
pagination: Pagination metadata
"""
items: list[T] = Field(description="List of items")
pagination: PaginationMeta = Field(description="Pagination metadata")
def success(
data: Optional[T] = None, message: str = "Success", trace_id: str = ""
) -> ApiResponse[T]:
"""
Create success response.
Args:
data: Response payload
message: Success message
trace_id: Request trace ID
Returns:
ApiResponse: Success response with code=0
Example:
return success(data={"user_id": 123}, message="User created")
"""
return ApiResponse(
code=BusinessCode.SUCCESS,
message=message,
data=data,
trace_id=trace_id,
timestamp=datetime.now(),
)
def error(code: "BusinessCode", data: Any=None, message: str="", trace_id: str = "") -> ApiResponse[None]:
"""
Create error response.
Args:
code: Business error code (must be > 0)
message: Error message
trace_id: Request trace ID
Returns:
ApiResponse: Error response
Example:
return error(code=1001, message="Login failed: Invalid credentials")
"""
if code <= 0:
raise ValueError("Error code must be greater than 0")
if message == "":
message = BusinessCode(code).name
return ApiResponse(
code=code,
message=message,
data=data,
trace_id=trace_id,
timestamp=datetime.now(),
)
def paginated(
items: list[T],
total: int,
page: int,
page_size: int,
message: str = "Success",
trace_id: str = "",
) -> ApiResponse[PaginatedData[T]]:
"""
Create paginated response.
Args:
items: List of items for current page
total: Total number of records
page: Current page number (1-based)
page_size: Number of records per page
message: Success message
trace_id: Request trace ID
Returns:
ApiResponse: Paginated response
Example:
return paginated(
items=[user1, user2],
total=100,
page=1,
page_size=10
)
"""
total_pages = (total + page_size - 1) // page_size if page_size > 0 else 0
pagination_meta = PaginationMeta(
total=total, page=page, page_size=page_size, total_pages=total_pages
)
paginated_data = PaginatedData(items=items, pagination=pagination_meta)
return ApiResponse(
code=0,
message=message,
data=paginated_data,
trace_id=trace_id,
timestamp=datetime.utcnow(),
)
# Business code constants
class BusinessCode(IntEnum):
"""
Business status code definitions.
Code ranges:
0: Success
1000-1999: Authentication & Authorization
2000-2999: Business Logic Errors
3000-3999: Validation Errors
4000-4999: Resource Errors
5000-5999: System Errors
9000-9999: Unknown Errors
"""
# Success
SUCCESS = 0
# Authentication & Authorization (1000-1999)
LOGIN_FAILED = 1001
TOKEN_EXPIRED = 1002
INSUFFICIENT_PERMISSIONS = 1003
INVALID_TOKEN = 1004
# Business Logic Errors (2000-2999)
ORDER_CREATION_FAILED = 2001
PAYMENT_FAILED = 2002
INSUFFICIENT_BALANCE = 2003
OPERATION_NOT_ALLOWED = 2004
# Validation Errors (3000-3999)
INVALID_INPUT = 3001
MISSING_REQUIRED_FIELD = 3002
INVALID_FORMAT = 3003
# Resource Errors (4000-4999)
RESOURCE_NOT_FOUND = 4001
RESOURCE_ALREADY_EXISTS = 4002
RESOURCE_CONFLICT = 4003
# System Errors (5000-5999)
DATABASE_ERROR = 5001
EXTERNAL_SERVICE_ERROR = 5002
CACHE_ERROR = 5003
INTERNAL_ERROR = 5004
# 没有实现
NOT_IMPLEMENTED = 5004
# Unknown Errors (9000-9999)
UNKNOWN_ERROR = 9000
# 京东充值的错误
JD_ORDER_FACE_PRICE_ERR = 10001
JD_ORDER_NORMAL_ERR = 10002
JD_ORDER_CK_ERR = 10003
JD_ORDER_TYPE_NOT_SUPPORTED_ERR = 10004
JD_ORDER_EXPIRED_ERR = 10005
JD_ORDER_STOCK_ERR = 10006
JD_ORDER_RISK_ERR = 10007
# Common error response examples for OpenAPI documentation
ERROR_RESPONSES = {
400: {
"description": "Bad Request - Validation or business logic error",
"content": {
"application/json": {
"example": {
"code": 3001,
"message": "Validation error: email - field required",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
401: {
"description": "Unauthorized - Authentication failed",
"content": {
"application/json": {
"example": {
"code": 1001,
"message": "Login failed: Invalid credentials",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
403: {
"description": "Forbidden - Insufficient permissions",
"content": {
"application/json": {
"example": {
"code": 1003,
"message": "Insufficient permissions to perform this action",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
404: {
"description": "Not Found - Resource does not exist",
"content": {
"application/json": {
"example": {
"code": 4001,
"message": "User not found",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
409: {
"description": "Conflict - Resource already exists or conflicts",
"content": {
"application/json": {
"example": {
"code": 4002,
"message": "User already exists",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
422: {
"description": "Unprocessable Entity - Validation error",
"content": {
"application/json": {
"example": {
"code": 3001,
"message": "Validation error: body -> email - value is not a valid email address",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
500: {
"description": "Internal Server Error - Unexpected error",
"content": {
"application/json": {
"example": {
"code": 9000,
"message": "An unexpected error occurred",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
502: {
"description": "Bad Gateway - External service error",
"content": {
"application/json": {
"example": {
"code": 5002,
"message": "External service unavailable",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
503: {
"description": "Service Unavailable - Database or cache error",
"content": {
"application/json": {
"example": {
"code": 5001,
"message": "Database error occurred",
"data": None,
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z",
}
}
},
},
}
# Error message templates
class ErrorMessage:
"""Predefined error messages for common errors."""
# Authentication
LOGIN_FAILED = "Login failed: Invalid credentials"
TOKEN_EXPIRED = "Authentication token has expired"
INSUFFICIENT_PERMISSIONS = "Insufficient permissions to perform this action"
INVALID_TOKEN = "Invalid authentication token"
# Business Logic
ORDER_CREATION_FAILED = "Order creation failed: {reason}"
PAYMENT_FAILED = "Payment processing failed: {reason}"
INSUFFICIENT_BALANCE = "Insufficient account balance"
OPERATION_NOT_ALLOWED = "Operation not allowed: {reason}"
# Validation
INVALID_INPUT = "Invalid input: {field}"
MISSING_REQUIRED_FIELD = "Missing required field: {field}"
INVALID_FORMAT = "Invalid format: {field}"
# Resources
RESOURCE_NOT_FOUND = "{resource} not found"
RESOURCE_ALREADY_EXISTS = "{resource} already exists"
RESOURCE_CONFLICT = "{resource} conflict: {reason}"
# System
DATABASE_ERROR = "Database error occurred"
EXTERNAL_SERVICE_ERROR = "External service unavailable"
CACHE_ERROR = "Cache service error"
# Unknown
UNKNOWN_ERROR = "An unexpected error occurred"