From ac99020c567038a3ec7d97fc8a4224d100aeb87f Mon Sep 17 00:00:00 2001 From: lhx Date: Fri, 7 Nov 2025 11:30:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=87=BAexcel=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=88=9D=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/export_excel.py | 99 +++++++++++++++++++++++++++++++++++++ app/core/response_code.py | 2 + app/main.py | 2 + app/schemas/export_excel.py | 17 +++++++ 4 files changed, 120 insertions(+) create mode 100644 app/api/export_excel.py create mode 100644 app/schemas/export_excel.py diff --git a/app/api/export_excel.py b/app/api/export_excel.py new file mode 100644 index 0000000..12ba163 --- /dev/null +++ b/app/api/export_excel.py @@ -0,0 +1,99 @@ +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 ..schemas.export_excel import ExportExcelRequest +from ..services.section_data import SectionDataService +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() + +@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 + } diff --git a/app/core/response_code.py b/app/core/response_code.py index 196c394..5044e69 100644 --- a/app/core/response_code.py +++ b/app/core/response_code.py @@ -28,6 +28,7 @@ class ResponseCode: DATA_NOT_FOUND = 3004 # 数据不存在 IMPORT_FAILED = 3005 # 导入失败 QUERY_FAILED = 3006 # 查询失败 + EXPORT_FAILED = 3007 # 导出失败 class ResponseMessage: @@ -47,3 +48,4 @@ class ResponseMessage: DATA_NOT_FOUND = "数据不存在" IMPORT_FAILED = "数据导入失败" QUERY_FAILED = "数据查询失败" + EXPORT_FAILED = "数据导出失败" diff --git a/app/main.py b/app/main.py index 2a1d620..5d5e094 100644 --- a/app/main.py +++ b/app/main.py @@ -10,6 +10,7 @@ from .api.account import router as account_router from .api.database import router as database_router from .api.task import router as task_router from .api.comprehensive_data import router as comprehensive_data_router +from .api.export_excel import router as export_excel_router from .utils.scheduler import task_scheduler # 初始化日志系统 @@ -68,6 +69,7 @@ app.include_router(account_router, prefix="/api") app.include_router(database_router, prefix="/api") app.include_router(task_router, prefix="/api") app.include_router(comprehensive_data_router, prefix="/api") +app.include_router(export_excel_router, prefix="/api") # app.include_router(test_router, prefix="/api") # 根路径 diff --git a/app/schemas/export_excel.py b/app/schemas/export_excel.py new file mode 100644 index 0000000..17c63b6 --- /dev/null +++ b/app/schemas/export_excel.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel +from typing import Optional, Dict, Any + +# 导出Excel请求 +class ExportExcelRequest(BaseModel): + section_id: Optional[str] = None + account_id: Optional[str] = None + mileage: Optional[str] = None + work_site: Optional[str] = None + number: Optional[str] = None + status: Optional[str] = None + +# 导出Excel响应 +class ExportExcelResponse(BaseModel): + code: int + message: str + data: Optional[Dict[str, Any]] = None