Files
dababase-etl-python/api/v1/routes/database.py
2026-03-04 12:17:52 +08:00

358 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import APIRouter, HTTPException, status
from typing import List
import logging
from pydantic import BaseModel
from database_manager import db_manager
from schemas import (
DatabaseConnection, QueryRequest, ExecuteRequest, TableDataRequest,
InsertDataRequest, UpdateDataRequest, DeleteDataRequest,
CreateTableRequest, AlterTableRequest, CommentRequest,
ApiResponse, ConnectionResponse, DatabaseInfo, TableInfo, QueryResult
)
logger = logging.getLogger(__name__)
# 创建路由器
router = APIRouter()
# 数据库连接管理接口
@router.post("/connections", response_model=ApiResponse, summary="创建数据库连接")
async def create_connection(connection: DatabaseConnection):
"""创建数据库连接"""
try:
# 准备额外的连接参数
kwargs = {}
if connection.mode:
kwargs['mode'] = connection.mode
if connection.threaded is not None:
kwargs['threaded'] = connection.threaded
if connection.extra_params:
kwargs.update(connection.extra_params)
connection_id = db_manager.create_connection(
db_type=connection.db_type,
host=connection.host,
port=connection.port,
username=connection.username,
password=connection.password,
database=connection.database,
**kwargs
)
response_data = ConnectionResponse(
connection_id=connection_id,
db_type=connection.db_type,
host=connection.host,
port=connection.port,
database=connection.database
)
return ApiResponse(
success=True,
message="数据库连接创建成功",
data=response_data.dict()
)
except Exception as e:
logger.error(f"创建连接失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"创建连接失败: {str(e)}"
)
@router.post("/connections/test", response_model=ApiResponse, summary="测试是否能连通数据库")
async def test_connection(connection: DatabaseConnection):
"""测试是否能连通数据库"""
try:
kwargs = {}
if connection.mode:
kwargs['mode'] = connection.mode
if connection.threaded is not None:
kwargs['threaded'] = connection.threaded
if connection.extra_params:
kwargs.update(connection.extra_params)
result = db_manager.test_connection(
db_type=connection.db_type,
host=connection.host,
port=connection.port,
username=connection.username,
password=connection.password,
database=connection.database,
**kwargs
)
if not result.get("ok"):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"连接测试失败: {result.get('error')}"
)
return ApiResponse(
success=True,
message="数据库连通性测试成功",
data=result
)
except HTTPException:
raise
except Exception as e:
logger.error(f"连接测试失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"连接测试失败: {str(e)}"
)
@router.get("/connections", response_model=ApiResponse, summary="获取所有连接")
async def list_connections():
"""获取所有活动连接"""
try:
connections = db_manager.list_connections()
return ApiResponse(
success=True,
message="获取连接列表成功",
data=connections
)
except Exception as e:
logger.error(f"获取连接列表失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"获取连接列表失败: {str(e)}"
)
class CloseConnectionRequest(BaseModel):
"""关闭连接请求体模型
Attributes:
connection_id: 需要关闭的连接ID
"""
connection_id: str
@router.post("/connections/close", response_model=ApiResponse, summary="关闭数据库连接POSTJSON传参")
async def close_connection(request: CloseConnectionRequest):
"""关闭数据库连接POST使用JSON Body传参
请求示例:
{"connection_id": "mysql_localhost_3306_test_db"}
"""
try:
db_manager.close_connection(request.connection_id)
return ApiResponse(
success=True,
message="数据库连接已关闭"
)
except Exception as e:
logger.error(f"关闭连接失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"关闭连接失败: {str(e)}"
)
# 数据库信息接口
@router.get("/databases/info", response_model=ApiResponse, summary="获取数据库信息使用query参数")
async def get_database_info(connection_id: str):
"""获取数据库信息通过URL query参数
Params:
- connection_id: 通过URL query传入例如 `?connection_id=xxx`
"""
try:
db_info = db_manager.get_database_info(connection_id)
return ApiResponse(
success=True,
message="获取数据库信息成功",
data=db_info
)
except Exception as e:
logger.error(f"获取数据库信息失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"获取数据库信息失败: {str(e)}"
)
@router.get("/databases/tables/info", response_model=ApiResponse, summary="获取表信息使用query参数")
async def get_table_info(connection_id: str, table_name: str):
"""获取表信息通过URL query参数
Params:
- connection_id: 通过URL query传入例如 `?connection_id=xxx`
- table_name: 通过URL query传入例如 `?table_name=users`
"""
try:
table_info = db_manager.get_table_info(connection_id, table_name)
return ApiResponse(
success=True,
message="获取表信息成功",
data=table_info
)
except Exception as e:
logger.error(f"获取表信息失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"获取表信息失败: {str(e)}"
)
# SQL执行接口
@router.post("/query", response_model=ApiResponse, summary="执行查询SQL")
async def execute_query(request: QueryRequest):
"""执行查询SQL"""
try:
result = db_manager.execute_query(
connection_id=request.connection_id,
sql=request.sql,
params=request.params
)
return ApiResponse(
success=True,
message="查询执行成功",
data=result
)
except Exception as e:
logger.error(f"查询执行失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"查询执行失败: {str(e)}"
)
@router.post("/execute", response_model=ApiResponse, summary="执行非查询SQL")
async def execute_non_query(request: ExecuteRequest):
"""执行非查询SQLINSERT, UPDATE, DELETE"""
try:
affected_rows = db_manager.execute_non_query(
connection_id=request.connection_id,
sql=request.sql,
params=request.params
)
return ApiResponse(
success=True,
message="SQL执行成功",
data={"affected_rows": affected_rows}
)
except Exception as e:
logger.error(f"SQL执行失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"SQL执行失败: {str(e)}"
)
# 表数据CRUD接口
@router.post("/tables/data/select", response_model=ApiResponse, summary="查询表数据")
async def select_table_data(request: TableDataRequest):
"""查询表数据"""
try:
# 构建查询SQL
sql = f"SELECT * FROM {request.table_name}"
if request.where_clause:
sql += f" WHERE {request.where_clause}"
if request.order_by:
sql += f" ORDER BY {request.order_by}"
# 添加分页
offset = (request.page - 1) * request.page_size
sql += f" LIMIT {request.page_size} OFFSET {offset}"
# 执行查询
data = db_manager.execute_query(request.connection_id, sql)
# 获取总数
count_sql = f"SELECT COUNT(*) as total FROM {request.table_name}"
if request.where_clause:
count_sql += f" WHERE {request.where_clause}"
count_result = db_manager.execute_query(request.connection_id, count_sql)
total = count_result[0]['total'] if count_result else 0
result = QueryResult(
data=data,
total=total,
page=request.page,
page_size=request.page_size
)
return ApiResponse(
success=True,
message="查询表数据成功",
data=result.dict()
)
except Exception as e:
logger.error(f"查询表数据失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"查询表数据失败: {str(e)}"
)
@router.post("/tables/data/insert", response_model=ApiResponse, summary="插入表数据")
async def insert_table_data(request: InsertDataRequest):
"""插入表数据"""
try:
# 构建插入SQL
columns = list(request.data.keys())
placeholders = [f":{col}" for col in columns]
sql = f"INSERT INTO {request.table_name} ({', '.join(columns)}) VALUES ({', '.join(placeholders)})"
affected_rows = db_manager.execute_non_query(
connection_id=request.connection_id,
sql=sql,
params=request.data
)
return ApiResponse(
success=True,
message="数据插入成功",
data={"affected_rows": affected_rows}
)
except Exception as e:
logger.error(f"插入数据失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"插入数据失败: {str(e)}"
)
@router.post("/tables/data/update", response_model=ApiResponse, summary="更新表数据改为POST")
async def update_table_data(request: UpdateDataRequest):
"""更新表数据HTTP方法改为POST"""
try:
# 构建更新SQL
set_clauses = [f"{col} = :{col}" for col in request.data.keys()]
sql = f"UPDATE {request.table_name} SET {', '.join(set_clauses)} WHERE {request.where_clause}"
affected_rows = db_manager.execute_non_query(
connection_id=request.connection_id,
sql=sql,
params=request.data
)
return ApiResponse(
success=True,
message="数据更新成功",
data={"affected_rows": affected_rows}
)
except Exception as e:
logger.error(f"更新数据失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"更新数据失败: {str(e)}"
)
@router.post("/tables/data/delete", response_model=ApiResponse, summary="删除表数据改为POST")
async def delete_table_data(request: DeleteDataRequest):
"""删除表数据HTTP方法改为POST"""
try:
sql = f"DELETE FROM {request.table_name} WHERE {request.where_clause}"
affected_rows = db_manager.execute_non_query(
connection_id=request.connection_id,
sql=sql
)
return ApiResponse(
success=True,
message="数据删除成功",
data={"affected_rows": affected_rows}
)
except Exception as e:
logger.error(f"删除数据失败: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"删除数据失败: {str(e)}"
)