2025-03-17 14:58:05 +08:00
|
|
|
|
# app.py
|
|
|
|
|
from fastapi import FastAPI, Request, HTTPException
|
|
|
|
|
from fastapi.responses import JSONResponse
|
|
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
|
import logging
|
|
|
|
|
import time
|
2025-03-18 18:34:03 +08:00
|
|
|
|
import traceback
|
2025-03-17 14:58:05 +08:00
|
|
|
|
from utils.logger import setup_logger
|
2025-03-17 18:31:20 +08:00
|
|
|
|
from config.component_config import register_all_components
|
2025-03-17 14:58:05 +08:00
|
|
|
|
from config.settings import (
|
|
|
|
|
AppConfig, ServerConfig, ApiConfig, CorsConfig
|
|
|
|
|
)
|
|
|
|
|
from api.task_api import router as task_router
|
|
|
|
|
from api.workflow_api import router as workflow_router
|
|
|
|
|
from api.component_api import router as component_router
|
2025-03-18 18:34:03 +08:00
|
|
|
|
from api.task_param_api import router as task_params_router
|
2025-03-17 14:58:05 +08:00
|
|
|
|
from core.exceptions import TianfengTaskError
|
|
|
|
|
from config.api_config import ApiResponseCode, ApiResponseMessage
|
|
|
|
|
from config.component_config import ComponentCategoryConfig
|
2025-03-18 18:34:03 +08:00
|
|
|
|
from api.task_instance_api import router as task_instance_router
|
2025-03-17 14:58:05 +08:00
|
|
|
|
# 导入数据库相关模块
|
|
|
|
|
from config.database import DBConfig, CacheConfig, db_session
|
|
|
|
|
import data.models # 导入所有模型以确保它们被注册
|
|
|
|
|
from data.models.component import ComponentCategory, ComponentType, Component, ComponentCategoryEnum
|
|
|
|
|
# 导入Lifespan
|
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
|
|
|
|
|
|
# 设置日志
|
|
|
|
|
logger = setup_logger()
|
|
|
|
|
|
|
|
|
|
# 定义Lifespan上下文管理器
|
|
|
|
|
@asynccontextmanager
|
|
|
|
|
async def lifespan(app: FastAPI):
|
|
|
|
|
"""
|
|
|
|
|
应用生命周期管理
|
|
|
|
|
在应用启动时执行初始化操作,在应用关闭时执行清理操作
|
|
|
|
|
"""
|
|
|
|
|
# 应用启动时执行
|
|
|
|
|
logger.info("应用启动")
|
|
|
|
|
|
|
|
|
|
yield # 应用运行期间
|
|
|
|
|
|
|
|
|
|
# 应用关闭时执行
|
|
|
|
|
logger.info("应用关闭")
|
|
|
|
|
DBConfig.shutdown_session()
|
|
|
|
|
|
|
|
|
|
# 创建FastAPI应用,使用Lifespan
|
|
|
|
|
app = FastAPI(
|
|
|
|
|
title=ApiConfig.TITLE,
|
|
|
|
|
description=ApiConfig.DESCRIPTION,
|
|
|
|
|
version=ApiConfig.VERSION,
|
|
|
|
|
lifespan=lifespan
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 添加CORS中间件
|
|
|
|
|
app.add_middleware(
|
|
|
|
|
CORSMiddleware,
|
|
|
|
|
allow_origins=CorsConfig.ALLOW_ORIGINS,
|
|
|
|
|
allow_credentials=CorsConfig.ALLOW_CREDENTIALS,
|
|
|
|
|
allow_methods=CorsConfig.ALLOW_METHODS,
|
|
|
|
|
allow_headers=CorsConfig.ALLOW_HEADERS,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 初始化数据库
|
|
|
|
|
def init_database():
|
|
|
|
|
"""初始化数据库,创建所有表"""
|
|
|
|
|
try:
|
|
|
|
|
logger.info("开始初始化数据库...")
|
2025-03-18 18:34:03 +08:00
|
|
|
|
|
2025-03-17 14:58:05 +08:00
|
|
|
|
# 初始化数据库表
|
2025-03-18 18:34:03 +08:00
|
|
|
|
logger.info("开始创建数据库表...")
|
|
|
|
|
try:
|
|
|
|
|
DBConfig.init_db()
|
|
|
|
|
logger.info("数据库表创建成功")
|
|
|
|
|
except Exception as table_err:
|
|
|
|
|
logger.error(f"数据库表创建失败: {str(table_err)}")
|
|
|
|
|
# 打印详细错误信息和堆栈跟踪
|
|
|
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
raise
|
2025-03-17 14:58:05 +08:00
|
|
|
|
|
|
|
|
|
# 初始化基础数据
|
|
|
|
|
logger.info("开始初始化基础数据...")
|
2025-03-18 18:34:03 +08:00
|
|
|
|
try:
|
|
|
|
|
init_base_data(db_session)
|
|
|
|
|
logger.info("基础数据初始化成功")
|
|
|
|
|
except Exception as data_err:
|
|
|
|
|
logger.error(f"基础数据初始化失败: {str(data_err)}")
|
|
|
|
|
# 打印详细错误信息和堆栈跟踪
|
|
|
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
raise
|
2025-03-17 14:58:05 +08:00
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"数据库初始化失败: {str(e)}")
|
2025-03-18 18:34:03 +08:00
|
|
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
# 继续执行程序,但记录错误
|
|
|
|
|
return False
|
2025-03-17 14:58:05 +08:00
|
|
|
|
|
|
|
|
|
def init_base_data(db_session):
|
|
|
|
|
"""初始化基础数据"""
|
|
|
|
|
try:
|
2025-03-18 18:34:03 +08:00
|
|
|
|
# 导入需要的模型
|
|
|
|
|
logger.info("检查基础数据...")
|
|
|
|
|
|
2025-03-17 14:58:05 +08:00
|
|
|
|
# 检查是否已存在组件分类
|
|
|
|
|
existing_categories = db_session.query(ComponentCategory).filter(ComponentCategory.is_deleted == False).all()
|
|
|
|
|
if existing_categories:
|
2025-03-18 18:34:03 +08:00
|
|
|
|
logger.info(f"已存在 {len(existing_categories)} 个组件分类,跳过初始化")
|
2025-03-17 14:58:05 +08:00
|
|
|
|
return
|
|
|
|
|
|
2025-03-18 18:34:03 +08:00
|
|
|
|
logger.info("开始创建组件分类...")
|
2025-03-17 14:58:05 +08:00
|
|
|
|
# 创建组件分类
|
|
|
|
|
categories = []
|
|
|
|
|
for category_enum in ComponentCategoryEnum:
|
2025-03-18 18:34:03 +08:00
|
|
|
|
logger.info(f"创建组件分类: {category_enum.value}")
|
2025-03-17 14:58:05 +08:00
|
|
|
|
category = ComponentCategory(
|
|
|
|
|
name=ComponentCategoryConfig.get_category_name(category_enum),
|
|
|
|
|
code=category_enum,
|
|
|
|
|
description=ComponentCategoryConfig.get_category_description(category_enum),
|
|
|
|
|
icon=f"icon-{category_enum.value}",
|
|
|
|
|
order=ComponentCategoryConfig.get_category_order(category_enum)
|
|
|
|
|
)
|
|
|
|
|
categories.append(category)
|
|
|
|
|
db_session.add(category)
|
|
|
|
|
|
2025-03-18 18:34:03 +08:00
|
|
|
|
logger.info("提交组件分类到数据库...")
|
2025-03-17 14:58:05 +08:00
|
|
|
|
db_session.commit()
|
|
|
|
|
logger.info(f"创建了 {len(categories)} 个组件分类")
|
|
|
|
|
|
|
|
|
|
# 这里可以添加更多基础数据的初始化,如组件类型、系统组件等
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"基础数据初始化失败: {str(e)}")
|
2025-03-18 18:34:03 +08:00
|
|
|
|
logger.error(traceback.format_exc())
|
|
|
|
|
db_session.rollback()
|
2025-03-17 14:58:05 +08:00
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
# 初始化数据库
|
2025-03-18 18:34:03 +08:00
|
|
|
|
init_result = init_database()
|
|
|
|
|
if not init_result:
|
|
|
|
|
logger.warning("数据库初始化失败,但程序将继续执行。请检查日志获取详细错误信息。")
|
2025-03-17 14:58:05 +08:00
|
|
|
|
|
|
|
|
|
# 注册所有组件
|
2025-03-18 18:34:03 +08:00
|
|
|
|
try:
|
|
|
|
|
register_all_components()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"组件注册失败: {str(e)}")
|
|
|
|
|
logger.error(traceback.format_exc())
|
2025-03-17 14:58:05 +08:00
|
|
|
|
|
|
|
|
|
# 注册API路由
|
|
|
|
|
app.include_router(task_router, prefix=ApiConfig.PREFIX)
|
|
|
|
|
app.include_router(workflow_router, prefix=ApiConfig.PREFIX)
|
|
|
|
|
app.include_router(component_router, prefix=ApiConfig.PREFIX)
|
2025-03-18 18:34:03 +08:00
|
|
|
|
app.include_router(task_instance_router, prefix=ApiConfig.PREFIX)
|
|
|
|
|
app.include_router(task_params_router, prefix=ApiConfig.PREFIX)
|
|
|
|
|
|
2025-03-17 14:58:05 +08:00
|
|
|
|
|
|
|
|
|
# 请求中间件
|
|
|
|
|
@app.middleware("http")
|
|
|
|
|
async def add_process_time_header(request: Request, call_next):
|
|
|
|
|
start_time = time.time()
|
|
|
|
|
response = await call_next(request)
|
|
|
|
|
process_time = time.time() - start_time
|
|
|
|
|
response.headers["X-Process-Time"] = str(process_time)
|
|
|
|
|
return response
|
|
|
|
|
|
|
|
|
|
# 数据库会话中间件
|
|
|
|
|
@app.middleware("http")
|
|
|
|
|
async def db_session_middleware(request: Request, call_next):
|
|
|
|
|
try:
|
|
|
|
|
response = await call_next(request)
|
|
|
|
|
return response
|
|
|
|
|
finally:
|
|
|
|
|
db_session.remove()
|
|
|
|
|
|
|
|
|
|
# 全局异常处理
|
|
|
|
|
@app.exception_handler(TianfengTaskError)
|
|
|
|
|
async def tianfeng_task_error_handler(request: Request, exc: TianfengTaskError):
|
|
|
|
|
"""处理天风任务模块异常"""
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=ApiResponseCode.BAD_REQUEST,
|
|
|
|
|
content={
|
|
|
|
|
"code": ApiResponseCode.BAD_REQUEST,
|
|
|
|
|
"message": str(exc),
|
|
|
|
|
"data": None
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(Exception)
|
|
|
|
|
async def global_exception_handler(request: Request, exc: Exception):
|
|
|
|
|
"""处理通用异常"""
|
|
|
|
|
logger.exception("未处理的异常")
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=ApiResponseCode.SERVER_ERROR,
|
|
|
|
|
content={
|
|
|
|
|
"code": ApiResponseCode.SERVER_ERROR,
|
|
|
|
|
"message": f"{ApiResponseMessage.SERVER_ERROR}: {str(exc)}",
|
|
|
|
|
"data": None
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 健康检查接口
|
|
|
|
|
@app.get(f"{ApiConfig.PREFIX}/health")
|
|
|
|
|
async def health_check():
|
|
|
|
|
"""健康检查"""
|
|
|
|
|
return {
|
|
|
|
|
"code": ApiResponseCode.SUCCESS,
|
|
|
|
|
"message": "服务正常",
|
|
|
|
|
"data": {
|
|
|
|
|
"app_name": AppConfig.NAME,
|
|
|
|
|
"app_version": AppConfig.VERSION,
|
|
|
|
|
"status": "healthy"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 主函数
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
import uvicorn
|
|
|
|
|
logger.info(f"启动天风任务模块服务,调试模式: {AppConfig.DEBUG}")
|
|
|
|
|
uvicorn.run("app:app", host=ServerConfig.HOST, port=ServerConfig.PORT, reload=ServerConfig.RELOAD)
|