Files
railway_cloud/app/api/export_excel.py
2025-11-10 09:56:50 +08:00

252 lines
8.5 KiB
Python
Raw Permalink 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, Depends, HTTPException, status
from fastapi.responses import FileResponse, StreamingResponse
from sqlalchemy.orm import Session
from typing import Optional, Dict, Any
from ..core.database import get_db
from ..core.response_code import ResponseCode, ResponseMessage
from ..core.exceptions import BusinessException, DataNotFoundException, AccountNotFoundException
from ..schemas.export_excel import ExportExcelRequest, ExportSettlementRequest, ExportLevelDataRequest
from ..services.section_data import SectionDataService
from ..services.export_excel import ExportExcelService
import logging
import pandas as pd
from io import BytesIO
from datetime import datetime
import os
import tempfile
router = APIRouter(prefix="/export", tags=["数据导出"])
logger = logging.getLogger(__name__)
# 实例化服务类
section_service = SectionDataService()
export_excel_service = ExportExcelService()
@router.post("/section_data")
def export_section_data(
request: ExportExcelRequest,
db: Session = Depends(get_db)
):
"""导出断面数据为Excel文件"""
try:
logger.info(f"导出断面数据,请求参数: section_id={request.section_id}, account_id={request.account_id}")
result = section_service.search_sections_with_checkpoints(
db,
section_id=request.section_id,
mileage=request.mileage,
work_site=request.work_site,
number=request.number,
status=request.status,
account_id=request.account_id,
skip=0,
limit=10000 # 设置一个较大的限制值
)
data_list = result.get('data', [])
if not data_list:
logger.warning("未找到符合条件的断面数据")
return {
"code": ResponseCode.SUCCESS,
"message": "未找到符合条件的数据,无法导出",
"data": None
}
# 转换为DataFrame
df = pd.DataFrame(data_list)
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"断面数据_{timestamp}.xlsx"
# 创建临时文件
temp_dir = tempfile.gettempdir()
file_path = os.path.join(temp_dir, filename)
# 保存Excel文件
with pd.ExcelWriter(file_path, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='断面数据')
# 获取工作表对象以便调整列宽
worksheet = writer.sheets['断面数据']
# 自动调整列宽
for column in worksheet.columns:
max_length = 0
column_letter = column[0].column_letter
for cell in column:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = min(max_length + 2, 50) # 最大宽度限制为50
worksheet.column_dimensions[column_letter].width = adjusted_width
logger.info(f"成功导出{len(data_list)}条断面数据,保存到文件: {file_path}")
# 返回文件下载响应
return FileResponse(
path=file_path,
filename=filename,
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
except Exception as e:
logger.error(f"导出断面数据失败: {str(e)}", exc_info=True)
return {
"code": ResponseCode.EXPORT_FAILED,
"message": f"{ResponseMessage.EXPORT_FAILED}: {str(e)}",
"data": None
}
@router.post("/settlement_data")
def export_settlement_data(
request: ExportSettlementRequest,
db: Session = Depends(get_db)
):
"""导出沉降数据Excel文件包含断面、观测点、水准数据"""
try:
logger.info(f"导出沉降数据,请求参数: project_name={request.project_name}")
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{request.project_name}_沉降数据_{timestamp}.xlsx"
# 创建临时文件
temp_dir = tempfile.gettempdir()
file_path = os.path.join(temp_dir, filename)
# 调用服务层导出数据到文件
export_excel_service.export_settlement_data_to_file(
db,
project_name=request.project_name,
file_path=file_path
)
logger.info(f"成功生成沉降数据Excel文件: {file_path}")
# 返回文件下载响应
return FileResponse(
path=file_path,
filename=filename,
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
except AccountNotFoundException as e:
logger.warning(f"账号不存在: {str(e)}")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail={
"code": e.code,
"message": e.message,
"data": None
}
)
except DataNotFoundException as e:
logger.warning(f"数据不存在: {str(e)}")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail={
"code": e.code,
"message": e.message,
"data": None
}
)
except BusinessException as e:
logger.warning(f"业务异常: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail={
"code": e.code,
"message": e.message,
"data": None
}
)
except Exception as e:
logger.error(f"导出沉降数据失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={
"code": ResponseCode.EXPORT_FAILED,
"message": f"{ResponseMessage.EXPORT_FAILED}: {str(e)}",
"data": None
}
)
@router.post("/level_data")
def export_level_data(
request: ExportLevelDataRequest,
db: Session = Depends(get_db)
):
"""导出水准数据Excel文件以水准数据为主体包含断面、观测点、沉降、原始数据"""
try:
logger.info(f"导出水准数据,请求参数: project_name={request.project_name}")
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{request.project_name}_水准数据_{timestamp}.xlsx"
# 创建临时文件
temp_dir = tempfile.gettempdir()
file_path = os.path.join(temp_dir, filename)
# 调用服务层导出数据到文件
export_excel_service.export_level_data_to_file(
db,
project_name=request.project_name,
file_path=file_path
)
logger.info(f"成功生成水准数据Excel文件: {file_path}")
# 返回文件下载响应
return FileResponse(
path=file_path,
filename=filename,
media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
)
except AccountNotFoundException as e:
logger.warning(f"账号不存在: {str(e)}")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail={
"code": e.code,
"message": e.message,
"data": None
}
)
except DataNotFoundException as e:
logger.warning(f"数据不存在: {str(e)}")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail={
"code": e.code,
"message": e.message,
"data": None
}
)
except BusinessException as e:
logger.warning(f"业务异常: {str(e)}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail={
"code": e.code,
"message": e.message,
"data": None
}
)
except Exception as e:
logger.error(f"导出水准数据失败: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={
"code": ResponseCode.EXPORT_FAILED,
"message": f"{ResponseMessage.EXPORT_FAILED}: {str(e)}",
"data": None
}
)