Files
kami_spider_monorepo/core/responses.py
danial 0e41e7acce feat(core): 初始化核心配置和部署文件
- 添加 .env.example 环境变量配置示例
- 添加 .gitignore 忽略文件配置
- 添加 core/config.py 配置管理模块
- 添加 deployments/k8s/configmap.yaml Kubernetes 配置
- 添加 core/database.py 数据库连接管理模块
- 添加 core/dependencies.py 全局依赖模块
- 添加 DEPENDENCIES_UPDATED.md 依赖更新记录
- 添加 deployments/k8s/deployment.yaml Kubernetes 部署配置- 添加 deployments/swarm/docker-compose.swarm.yml Docker Swarm 部署配置
- 添加 deployments/docker/docker-compose.yml Docker 部署配置
- 添加 deployments/docker/Dockerfile 应用镜像构建文件
- 添加 middleware/error_handler.py 全局异常处理中间件
2025-11-01 14:32:29 +08:00

393 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
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 Config:
json_schema_extra = {
"example": {
"code": 0,
"message": "Success",
"data": {"user_id": 12345, "username": "john_doe"},
"trace_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"timestamp": "2024-01-15T10:30:00Z"
}
}
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=0,
message=message,
data=data,
trace_id=trace_id,
timestamp=datetime.utcnow()
)
def error(
code: int,
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 with data=None
Example:
return error(code=1001, message="Login failed: Invalid credentials")
"""
if code <= 0:
raise ValueError("Error code must be greater than 0")
return ApiResponse(
code=code,
message=message,
data=None,
trace_id=trace_id,
timestamp=datetime.utcnow()
)
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:
"""
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
# Unknown Errors (9000-9999)
UNKNOWN_ERROR = 9000
# 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"