main:添加核心文件并初始化项目
新增内容: - 创建基础项目结构。 - 添加 `.gitignore` 和 `.dockerignore` 文件。 - 编写 `pyproject.toml` 和依赖文件。 - 添加算法模块及示例算法。 - 实现核心功能模块(日志、错误处理、指标)。 - 添加开发和运行所需的相关脚本文件及文档。
This commit is contained in:
6
src/functional_scaffold/api/__init__.py
Normal file
6
src/functional_scaffold/api/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""API 模块"""
|
||||
|
||||
from .routes import router
|
||||
from .models import InvokeRequest, InvokeResponse, HealthResponse, ErrorResponse
|
||||
|
||||
__all__ = ["router", "InvokeRequest", "InvokeResponse", "HealthResponse", "ErrorResponse"]
|
||||
20
src/functional_scaffold/api/dependencies.py
Normal file
20
src/functional_scaffold/api/dependencies.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""API 依赖注入"""
|
||||
|
||||
from fastapi import Header, HTTPException
|
||||
from typing import Optional
|
||||
from ..core.tracing import set_request_id, generate_request_id
|
||||
|
||||
|
||||
async def get_request_id(x_request_id: Optional[str] = Header(None)) -> str:
|
||||
"""
|
||||
获取或生成请求ID
|
||||
|
||||
Args:
|
||||
x_request_id: 从请求头获取的请求ID
|
||||
|
||||
Returns:
|
||||
str: 请求ID
|
||||
"""
|
||||
request_id = x_request_id or generate_request_id()
|
||||
set_request_id(request_id)
|
||||
return request_id
|
||||
82
src/functional_scaffold/api/models.py
Normal file
82
src/functional_scaffold/api/models.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""API 数据模型"""
|
||||
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
class InvokeRequest(BaseModel):
|
||||
"""同步调用请求"""
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"number": 17
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
number: int = Field(..., description="待判断的整数")
|
||||
|
||||
|
||||
class InvokeResponse(BaseModel):
|
||||
"""同步调用响应"""
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"status": "success",
|
||||
"result": {
|
||||
"number": 17,
|
||||
"is_prime": True,
|
||||
"factors": [],
|
||||
"algorithm": "trial_division"
|
||||
},
|
||||
"metadata": {
|
||||
"algorithm": "PrimeChecker",
|
||||
"version": "1.0.0",
|
||||
"elapsed_time": 0.001
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
request_id: str = Field(..., description="请求唯一标识")
|
||||
status: str = Field(..., description="处理状态")
|
||||
result: Dict[str, Any] = Field(..., description="算法执行结果")
|
||||
metadata: Dict[str, Any] = Field(..., description="元数据信息")
|
||||
|
||||
|
||||
class HealthResponse(BaseModel):
|
||||
"""健康检查响应"""
|
||||
|
||||
status: str = Field(..., description="健康状态")
|
||||
timestamp: float = Field(..., description="时间戳")
|
||||
|
||||
|
||||
class ReadinessResponse(BaseModel):
|
||||
"""就绪检查响应"""
|
||||
|
||||
status: str = Field(..., description="就绪状态")
|
||||
timestamp: float = Field(..., description="时间戳")
|
||||
checks: Optional[Dict[str, bool]] = Field(None, description="各项检查结果")
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
"""错误响应"""
|
||||
|
||||
model_config = ConfigDict(
|
||||
json_schema_extra={
|
||||
"example": {
|
||||
"error": "VALIDATION_ERROR",
|
||||
"message": "number must be an integer",
|
||||
"details": {"field": "number", "value": "abc"},
|
||||
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
error: str = Field(..., description="错误代码")
|
||||
message: str = Field(..., description="错误消息")
|
||||
details: Optional[Dict[str, Any]] = Field(None, description="错误详情")
|
||||
request_id: Optional[str] = Field(None, description="请求ID")
|
||||
150
src/functional_scaffold/api/routes.py
Normal file
150
src/functional_scaffold/api/routes.py
Normal file
@@ -0,0 +1,150 @@
|
||||
"""API 路由"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Depends, status
|
||||
from fastapi.responses import JSONResponse
|
||||
import time
|
||||
import logging
|
||||
|
||||
from .models import (
|
||||
InvokeRequest,
|
||||
InvokeResponse,
|
||||
HealthResponse,
|
||||
ReadinessResponse,
|
||||
ErrorResponse,
|
||||
)
|
||||
from .dependencies import get_request_id
|
||||
from ..algorithms.prime_checker import PrimeChecker
|
||||
from ..core.errors import FunctionalScaffoldError, ValidationError, AlgorithmError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post(
|
||||
"/invoke",
|
||||
response_model=InvokeResponse,
|
||||
status_code=status.HTTP_200_OK,
|
||||
summary="同步调用算法",
|
||||
description="同步调用质数判断算法,立即返回结果",
|
||||
responses={
|
||||
200: {"description": "成功", "model": InvokeResponse},
|
||||
400: {"description": "请求参数错误", "model": ErrorResponse},
|
||||
500: {"description": "服务器内部错误", "model": ErrorResponse},
|
||||
},
|
||||
)
|
||||
async def invoke_algorithm(
|
||||
request: InvokeRequest,
|
||||
request_id: str = Depends(get_request_id),
|
||||
):
|
||||
"""
|
||||
同步调用质数判断算法
|
||||
|
||||
- **number**: 待判断的整数
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Processing request {request_id} with number={request.number}")
|
||||
|
||||
# 创建算法实例并执行
|
||||
checker = PrimeChecker()
|
||||
execution_result = checker.execute(request.number)
|
||||
|
||||
if not execution_result["success"]:
|
||||
raise AlgorithmError(
|
||||
execution_result.get("error", "Algorithm execution failed"),
|
||||
details=execution_result.get("metadata", {}),
|
||||
)
|
||||
|
||||
return InvokeResponse(
|
||||
request_id=request_id,
|
||||
status="success",
|
||||
result=execution_result["result"],
|
||||
metadata=execution_result["metadata"],
|
||||
)
|
||||
|
||||
except ValidationError as e:
|
||||
logger.warning(f"Validation error for request {request_id}: {e.message}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=e.to_dict(),
|
||||
)
|
||||
|
||||
except AlgorithmError as e:
|
||||
logger.error(f"Algorithm error for request {request_id}: {e.message}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=e.to_dict(),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error for request {request_id}: {str(e)}", exc_info=True)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail={
|
||||
"error": "INTERNAL_ERROR",
|
||||
"message": str(e),
|
||||
"request_id": request_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/healthz",
|
||||
response_model=HealthResponse,
|
||||
summary="健康检查",
|
||||
description="检查服务是否存活",
|
||||
)
|
||||
async def health_check():
|
||||
"""
|
||||
健康检查端点
|
||||
|
||||
返回服务的健康状态,用于存活探针
|
||||
"""
|
||||
return HealthResponse(
|
||||
status="healthy",
|
||||
timestamp=time.time(),
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/readyz",
|
||||
response_model=ReadinessResponse,
|
||||
summary="就绪检查",
|
||||
description="检查服务是否就绪",
|
||||
)
|
||||
async def readiness_check():
|
||||
"""
|
||||
就绪检查端点
|
||||
|
||||
返回服务的就绪状态,用于就绪探针
|
||||
"""
|
||||
# 这里可以添加更多检查,例如数据库连接、外部服务等
|
||||
checks = {
|
||||
"algorithm": True, # 算法模块可用
|
||||
}
|
||||
|
||||
all_ready = all(checks.values())
|
||||
|
||||
return ReadinessResponse(
|
||||
status="ready" if all_ready else "not_ready",
|
||||
timestamp=time.time(),
|
||||
checks=checks,
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/jobs",
|
||||
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
||||
summary="异步任务接口(预留)",
|
||||
description="异步任务接口,当前版本未实现",
|
||||
)
|
||||
async def create_job():
|
||||
"""
|
||||
异步任务接口(预留)
|
||||
|
||||
用于提交长时间运行的任务
|
||||
"""
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
||||
detail={"error": "NOT_IMPLEMENTED", "message": "Async jobs not implemented yet"},
|
||||
)
|
||||
Reference in New Issue
Block a user