feat: restore source parity and harden agent runtime
This commit is contained in:
@@ -0,0 +1,626 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""报表数据与预览 API"""
|
||||
import base64
|
||||
import logging
|
||||
import re
|
||||
from typing import Any, Dict
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, UploadFile
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
from app.config import settings
|
||||
from core.file_manager.service import FileManagerService
|
||||
from online_dev.report_manager.exceptions import ReportServiceException
|
||||
from online_dev.report_manager.schema import (
|
||||
ReportDownImgIn,
|
||||
ReportImportExcelOut,
|
||||
ReportPreviewIn,
|
||||
ReportPreviewOut,
|
||||
ReportUploadOut,
|
||||
)
|
||||
from online_dev.report_manager.service import ReportService
|
||||
from online_dev.report_manager.version_service import ReportVersionService
|
||||
from online_dev.report_manager.dataset_bridge import ReportDatasetBridge
|
||||
from online_dev.report_manager.engine.chart_data import build_chart_data
|
||||
from online_dev.report_manager.engine.convert import transform
|
||||
from online_dev.report_manager.engine.expression_eval import detect_expression_cycles
|
||||
from online_dev.report_manager.engine.export_excel import snapshot_to_xlsx_bytes
|
||||
from online_dev.report_manager.engine.export_pdf import snapshot_to_pdf_bytes
|
||||
from online_dev.report_manager.engine.import_excel import parse_excel_to_grid
|
||||
from online_dev.report_manager.engine.watermark import build_watermark_payload
|
||||
from online_dev.report_manager.engine.convert_lookup import build_lookup_cache_from_db
|
||||
from online_dev.report_manager.engine.parameter_resolver import (
|
||||
build_system_params,
|
||||
merge_preview_params,
|
||||
)
|
||||
from online_dev.report_manager.engine.preview_guard import collect_preview_warnings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/report/data", tags=["报表数据"])
|
||||
|
||||
|
||||
def _build_file_access_url(file_obj) -> str:
|
||||
if file_obj.url:
|
||||
return file_obj.url
|
||||
base_url = getattr(settings, "BASE_URL", "http://localhost:8000")
|
||||
if file_obj.storage_type == "local" and file_obj.storage_path:
|
||||
return f"{base_url}/api/file_manager/file/download?path={file_obj.storage_path}"
|
||||
return f"{base_url}/api/file_manager/url/{file_obj.id}"
|
||||
|
||||
|
||||
def _flatten_query_list(query_list: list) -> list:
|
||||
"""兼容 JNPF 按 sheet 包装与 ZQ 扁平 queryList。"""
|
||||
if not query_list:
|
||||
return []
|
||||
if isinstance(query_list[0], dict) and query_list[0].get("queryList") is not None:
|
||||
flat: list = []
|
||||
for block in query_list:
|
||||
if not isinstance(block, dict):
|
||||
continue
|
||||
for item in block.get("queryList") or []:
|
||||
if isinstance(item, dict):
|
||||
flat.append(item)
|
||||
return flat
|
||||
return [x for x in query_list if isinstance(x, dict)]
|
||||
|
||||
|
||||
def _default_params_from_query_list(query_list: list) -> Dict[str, Any]:
|
||||
"""从 query_list 提取默认参数值"""
|
||||
params: Dict[str, Any] = {}
|
||||
for item in _flatten_query_list(query_list):
|
||||
field = item.get("field") or item.get("vModel") or item.get("prop")
|
||||
if not field:
|
||||
continue
|
||||
default_val = item.get("defaultValue")
|
||||
if default_val is None and "value" in item:
|
||||
default_val = item.get("value")
|
||||
if default_val is not None:
|
||||
params[field] = default_val
|
||||
return params
|
||||
|
||||
|
||||
def _query_list_for_sheet(query_list: list, sheet_id: str) -> list:
|
||||
"""按 sheet 过滤 query 项;扁平结构返回全部。"""
|
||||
if not query_list:
|
||||
return []
|
||||
if isinstance(query_list[0], dict) and query_list[0].get("queryList") is not None:
|
||||
for block in query_list:
|
||||
if str(block.get("sheet") or "") == str(sheet_id):
|
||||
return block.get("queryList") or []
|
||||
return _flatten_query_list(query_list)
|
||||
return query_list
|
||||
|
||||
|
||||
def _parse_preview_draft_field(value: Any) -> Any:
|
||||
if value is None:
|
||||
return None
|
||||
return ReportVersionService._parse_json_field(value, None)
|
||||
|
||||
|
||||
async def _build_preview(
|
||||
db: AsyncSession,
|
||||
version,
|
||||
template,
|
||||
params: Dict[str, Any],
|
||||
request=None,
|
||||
*,
|
||||
snapshot_override: Any = None,
|
||||
cells_override: Any = None,
|
||||
query_list_override: Any = None,
|
||||
sort_list_override: Any = None,
|
||||
column_list_override: Any = None,
|
||||
fence_list_override: Any = None,
|
||||
convert_config_override: Any = None,
|
||||
) -> Dict[str, Any]:
|
||||
snapshot = (
|
||||
snapshot_override
|
||||
if snapshot_override is not None
|
||||
else (version.snapshot or {})
|
||||
)
|
||||
cells = cells_override if cells_override is not None else (version.cells or {})
|
||||
query_list = (
|
||||
query_list_override
|
||||
if query_list_override is not None
|
||||
else (version.query_list or [])
|
||||
)
|
||||
sort_list = (
|
||||
sort_list_override
|
||||
if sort_list_override is not None
|
||||
else (version.sort_list or [])
|
||||
)
|
||||
column_list = (
|
||||
column_list_override
|
||||
if column_list_override is not None
|
||||
else (version.column_list or [])
|
||||
)
|
||||
fence_list = (
|
||||
fence_list_override
|
||||
if fence_list_override is not None
|
||||
else (version.fence_list or [])
|
||||
)
|
||||
convert_config = (
|
||||
convert_config_override
|
||||
if convert_config_override is not None
|
||||
else (version.convert_config or {})
|
||||
)
|
||||
query_defaults = _default_params_from_query_list(query_list)
|
||||
user_id = getattr(getattr(request, "state", None), "user_id", None) if request else None
|
||||
user_name = ""
|
||||
dept_name = ""
|
||||
if user_id:
|
||||
try:
|
||||
from core.user.service import UserService
|
||||
from core.dept.service import DeptService
|
||||
|
||||
user = await UserService.get(db, user_id)
|
||||
if user:
|
||||
user_name = user.name or user.username or ""
|
||||
if user.dept_id:
|
||||
dept = await DeptService.get(db, user.dept_id)
|
||||
if dept:
|
||||
dept_name = dept.name or ""
|
||||
except Exception:
|
||||
pass
|
||||
system_params = build_system_params(
|
||||
user_id=str(user_id) if user_id else None,
|
||||
user_name=user_name,
|
||||
dept_name=dept_name,
|
||||
)
|
||||
merged_params = merge_preview_params(query_defaults, params, system_params)
|
||||
lookup = await build_lookup_cache_from_db(db)
|
||||
datasets = await ReportDatasetBridge.fetch_all(
|
||||
db,
|
||||
version.id,
|
||||
merged_params,
|
||||
sort_list=sort_list,
|
||||
version_convert=convert_config,
|
||||
lookup=lookup,
|
||||
)
|
||||
filled = transform(
|
||||
snapshot,
|
||||
cells,
|
||||
datasets,
|
||||
merged_params,
|
||||
column_list=column_list,
|
||||
fence_list=fence_list,
|
||||
)
|
||||
chart_data = build_chart_data(cells, datasets)
|
||||
watermark = build_watermark_payload(
|
||||
bool(template.allow_watermark),
|
||||
template.watermark_config or {},
|
||||
template_name=template.name or "",
|
||||
)
|
||||
warnings = list(detect_expression_cycles(cells, snapshot) or [])
|
||||
warnings.extend(collect_preview_warnings(datasets=datasets, snapshot=filled))
|
||||
return {
|
||||
"snapshot": filled,
|
||||
"cells": cells,
|
||||
"queryList": query_list,
|
||||
"chartData": chart_data,
|
||||
"allowExport": bool(template.allow_export),
|
||||
"allowPrint": bool(template.allow_print),
|
||||
"allowWatermark": watermark["show"],
|
||||
"watermarkConfig": watermark["config"],
|
||||
"watermark": watermark,
|
||||
"fullName": template.name,
|
||||
"warnings": warnings,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{version_id}/preview", summary="预览(设计/运行)")
|
||||
async def preview_version(
|
||||
request: Request,
|
||||
version_id: str,
|
||||
body: ReportPreviewIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
version = await ReportVersionService.get(db, version_id)
|
||||
template = await ReportService.get(db, version.template_id)
|
||||
data = await _build_preview(db, version, template, body.params or {}, request)
|
||||
return data
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/preview-template", summary="按模板预览(启用中版本)")
|
||||
async def preview_template(
|
||||
request: Request,
|
||||
body: ReportPreviewIn,
|
||||
template_id: str = Query(None, alias="templateId"),
|
||||
template_code: str = Query(None, alias="templateCode"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
if template_code:
|
||||
template = await ReportService.get_by_code(db, template_code)
|
||||
elif template_id:
|
||||
template = await ReportService.get(db, template_id)
|
||||
else:
|
||||
raise ReportServiceException("需要 template_id 或 template_code")
|
||||
version = await ReportVersionService.get_active(db, template.id)
|
||||
if not version:
|
||||
version = await ReportVersionService.get_designing_or_latest(db, template.id)
|
||||
if not version:
|
||||
raise ReportServiceException("没有可用版本")
|
||||
data = await _build_preview(db, version, template, body.params or {}, request)
|
||||
return data
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/query-list/{template_id}", summary="查询条件列表")
|
||||
async def get_query_list(template_id: str, db: AsyncSession = Depends(get_db)):
|
||||
try:
|
||||
version = await ReportVersionService.get_active(db, template_id)
|
||||
if not version:
|
||||
version = await ReportVersionService.get_designing_or_latest(db, template_id)
|
||||
if not version:
|
||||
return {"queryList": []}
|
||||
return {"queryList": version.query_list or []}
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/query-list/code/{code}", summary="按编码获取查询条件")
|
||||
async def get_query_list_by_code(code: str, db: AsyncSession = Depends(get_db)):
|
||||
try:
|
||||
template = await ReportService.get_by_code(db, code)
|
||||
version = await ReportVersionService.get_active(db, template.id)
|
||||
if not version:
|
||||
version = await ReportVersionService.get_designing_or_latest(db, template.id)
|
||||
if not version:
|
||||
return {"queryList": [], "templateId": template.id}
|
||||
return {
|
||||
"queryList": version.query_list or [],
|
||||
"templateId": template.id,
|
||||
"fullName": template.name,
|
||||
}
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/{version_id}/preview-design", summary="设计态预览")
|
||||
async def preview_design(
|
||||
request: Request,
|
||||
version_id: str,
|
||||
body: ReportPreviewIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""设计器内预览;若传入 snapshot/cells 则使用当前编辑器草稿"""
|
||||
try:
|
||||
version = await ReportVersionService.get(db, version_id)
|
||||
template = await ReportService.get(db, version.template_id)
|
||||
draft_snapshot = _parse_preview_draft_field(body.snapshot)
|
||||
draft_cells = _parse_preview_draft_field(body.cells)
|
||||
draft_query_list = _parse_preview_draft_field(body.query_list)
|
||||
draft_sort_list = _parse_preview_draft_field(body.sort_list)
|
||||
draft_column_list = _parse_preview_draft_field(body.column_list)
|
||||
draft_fence_list = _parse_preview_draft_field(body.fence_list)
|
||||
draft_convert_config = _parse_preview_draft_field(body.convert_config)
|
||||
return await _build_preview(
|
||||
db,
|
||||
version,
|
||||
template,
|
||||
body.params or {},
|
||||
request,
|
||||
snapshot_override=draft_snapshot,
|
||||
cells_override=draft_cells,
|
||||
query_list_override=draft_query_list,
|
||||
sort_list_override=draft_sort_list,
|
||||
column_list_override=draft_column_list,
|
||||
fence_list_override=draft_fence_list,
|
||||
convert_config_override=draft_convert_config,
|
||||
)
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/{version_id}/export-excel-design", summary="设计态导出 Excel")
|
||||
async def export_excel_design(
|
||||
request: Request,
|
||||
version_id: str,
|
||||
body: ReportPreviewIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""设计器内导出;若传入 snapshot/cells 则使用当前编辑器草稿"""
|
||||
try:
|
||||
version = await ReportVersionService.get(db, version_id)
|
||||
template = await ReportService.get(db, version.template_id)
|
||||
draft_snapshot = _parse_preview_draft_field(body.snapshot)
|
||||
draft_cells = _parse_preview_draft_field(body.cells)
|
||||
draft_query_list = _parse_preview_draft_field(body.query_list)
|
||||
draft_sort_list = _parse_preview_draft_field(body.sort_list)
|
||||
draft_column_list = _parse_preview_draft_field(body.column_list)
|
||||
draft_fence_list = _parse_preview_draft_field(body.fence_list)
|
||||
draft_convert_config = _parse_preview_draft_field(body.convert_config)
|
||||
return await _export_excel_response(
|
||||
db,
|
||||
version,
|
||||
template,
|
||||
body.params or {},
|
||||
request,
|
||||
snapshot_override=draft_snapshot,
|
||||
cells_override=draft_cells,
|
||||
query_list_override=draft_query_list,
|
||||
sort_list_override=draft_sort_list,
|
||||
column_list_override=draft_column_list,
|
||||
fence_list_override=draft_fence_list,
|
||||
convert_config_override=draft_convert_config,
|
||||
)
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
async def _export_excel_response(
|
||||
db: AsyncSession,
|
||||
version,
|
||||
template,
|
||||
params: Dict[str, Any],
|
||||
request=None,
|
||||
*,
|
||||
snapshot_override: Any = None,
|
||||
cells_override: Any = None,
|
||||
query_list_override: Any = None,
|
||||
sort_list_override: Any = None,
|
||||
column_list_override: Any = None,
|
||||
fence_list_override: Any = None,
|
||||
convert_config_override: Any = None,
|
||||
):
|
||||
if not template.allow_export:
|
||||
raise ReportServiceException("该报表不允许导出")
|
||||
preview = await _build_preview(
|
||||
db,
|
||||
version,
|
||||
template,
|
||||
params,
|
||||
request,
|
||||
snapshot_override=snapshot_override,
|
||||
cells_override=cells_override,
|
||||
query_list_override=query_list_override,
|
||||
sort_list_override=sort_list_override,
|
||||
column_list_override=column_list_override,
|
||||
fence_list_override=fence_list_override,
|
||||
convert_config_override=convert_config_override,
|
||||
)
|
||||
watermark = preview.get("watermark") or {}
|
||||
wm_text = ""
|
||||
if watermark.get("show"):
|
||||
wm_text = str((watermark.get("config") or {}).get("content") or "")
|
||||
base_url = str(request.base_url).rstrip("/") if request else getattr(settings, "BASE_URL", "")
|
||||
from online_dev.report_manager.engine.export_excel_extras import build_fetch_url
|
||||
|
||||
content = snapshot_to_xlsx_bytes(
|
||||
preview.get("snapshot") or {},
|
||||
watermark_text=wm_text,
|
||||
fetch_url=build_fetch_url(base_url),
|
||||
)
|
||||
filename = f"{template.code or 'report'}.xlsx"
|
||||
return StreamingResponse(
|
||||
iter([content]),
|
||||
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
)
|
||||
|
||||
|
||||
async def _export_pdf_response(
|
||||
db: AsyncSession,
|
||||
version,
|
||||
template,
|
||||
params: Dict[str, Any],
|
||||
request=None,
|
||||
):
|
||||
if not template.allow_export:
|
||||
raise ReportServiceException("该报表不允许导出")
|
||||
preview = await _build_preview(db, version, template, params, request)
|
||||
watermark = preview.get("watermark") or {}
|
||||
wm_text = ""
|
||||
if watermark.get("show"):
|
||||
wm_text = str((watermark.get("config") or {}).get("content") or "")
|
||||
content = snapshot_to_pdf_bytes(
|
||||
preview.get("snapshot") or {},
|
||||
title=template.name or "",
|
||||
watermark_text=wm_text,
|
||||
)
|
||||
filename = f"{template.code or 'report'}.pdf"
|
||||
return StreamingResponse(
|
||||
iter([content]),
|
||||
media_type="application/pdf",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{version_id}/export-excel", summary="导出 Excel(按版本)")
|
||||
async def export_excel_version(
|
||||
request: Request,
|
||||
version_id: str,
|
||||
body: ReportPreviewIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
version = await ReportVersionService.get(db, version_id)
|
||||
template = await ReportService.get(db, version.template_id)
|
||||
return await _export_excel_response(db, version, template, body.params or {}, request)
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/upload/file", response_model=ReportUploadOut, summary="上传文件(图片等)")
|
||||
async def upload_report_file(
|
||||
file: UploadFile = File(...),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
content = await file.read()
|
||||
if not content:
|
||||
raise ReportServiceException("文件为空")
|
||||
filename = file.filename or "upload.bin"
|
||||
file_obj = await FileManagerService.upload_file(
|
||||
db=db,
|
||||
file_content=content,
|
||||
filename=filename,
|
||||
file_size=len(content),
|
||||
is_public=True,
|
||||
source="report",
|
||||
)
|
||||
return ReportUploadOut(name=file_obj.name, url=_build_file_access_url(file_obj))
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.exception("报表文件上传失败")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/downImg", response_model=ReportUploadOut, summary="远端/Base64 图片转存")
|
||||
async def download_remote_image(
|
||||
body: ReportDownImgIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
img_value = (body.img_value or "").strip()
|
||||
if not img_value:
|
||||
raise ReportServiceException("图片内容为空")
|
||||
|
||||
img_type = (body.img_type or "").upper()
|
||||
content: bytes
|
||||
ext = ".jpeg"
|
||||
|
||||
if img_type == "BASE64":
|
||||
base64_img = img_value
|
||||
match = re.search(r"data:image/(\w+);base64,", img_value)
|
||||
if match:
|
||||
ext = f".{match.group(1)}"
|
||||
base64_img = img_value.split(",", 1)[-1]
|
||||
content = base64.b64decode(base64_img)
|
||||
else:
|
||||
async with httpx.AsyncClient(timeout=30.0, follow_redirects=True) as client:
|
||||
resp = await client.get(img_value)
|
||||
resp.raise_for_status()
|
||||
content = resp.content
|
||||
ctype = resp.headers.get("content-type", "")
|
||||
if "png" in ctype:
|
||||
ext = ".png"
|
||||
elif "gif" in ctype:
|
||||
ext = ".gif"
|
||||
elif "webp" in ctype:
|
||||
ext = ".webp"
|
||||
|
||||
if not content:
|
||||
raise ReportServiceException("无法获取图片数据")
|
||||
|
||||
file_obj = await FileManagerService.upload_file(
|
||||
db=db,
|
||||
file_content=content,
|
||||
filename=f"report_img{ext}",
|
||||
file_size=len(content),
|
||||
is_public=True,
|
||||
source="report",
|
||||
)
|
||||
return ReportUploadOut(name=file_obj.name, url=_build_file_access_url(file_obj))
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.exception("报表图片转存失败")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/import-excel", response_model=ReportImportExcelOut, summary="导入 Excel 到网格")
|
||||
@router.post("/ImportExcel", response_model=ReportImportExcelOut, summary="导入 Excel(JNPF 兼容路径)")
|
||||
async def import_excel_file(
|
||||
file: UploadFile = File(...),
|
||||
):
|
||||
try:
|
||||
content = await file.read()
|
||||
if not content:
|
||||
raise ReportServiceException("文件为空")
|
||||
grid = parse_excel_to_grid(content)
|
||||
return ReportImportExcelOut(
|
||||
rowsCount=grid["rowsCount"],
|
||||
colsCount=grid["colsCount"],
|
||||
data=grid["data"],
|
||||
)
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
logger.exception("Excel 解析失败")
|
||||
raise HTTPException(status_code=400, detail=f"Excel 解析失败: {e}")
|
||||
|
||||
|
||||
@router.post("/export-excel/template", summary="导出 Excel(按模板启用版本)")
|
||||
async def export_excel_template(
|
||||
request: Request,
|
||||
body: ReportPreviewIn,
|
||||
template_id: str = Query(None, alias="templateId"),
|
||||
template_code: str = Query(None, alias="templateCode"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
if template_code:
|
||||
template = await ReportService.get_by_code(db, template_code)
|
||||
elif template_id:
|
||||
template = await ReportService.get(db, template_id)
|
||||
else:
|
||||
raise ReportServiceException("需要 template_id 或 template_code")
|
||||
version = await ReportVersionService.get_active(db, template.id)
|
||||
if not version:
|
||||
version = await ReportVersionService.get_designing_or_latest(db, template.id)
|
||||
if not version:
|
||||
raise ReportServiceException("没有可用版本")
|
||||
return await _export_excel_response(db, version, template, body.params or {}, request)
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/export-pdf/template", summary="导出 PDF(按模板启用版本)")
|
||||
async def export_pdf_template(
|
||||
request: Request,
|
||||
body: ReportPreviewIn,
|
||||
template_id: str = Query(None, alias="templateId"),
|
||||
template_code: str = Query(None, alias="templateCode"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
if template_code:
|
||||
template = await ReportService.get_by_code(db, template_code)
|
||||
elif template_id:
|
||||
template = await ReportService.get(db, template_id)
|
||||
else:
|
||||
raise ReportServiceException("需要 template_id 或 template_code")
|
||||
version = await ReportVersionService.get_active(db, template.id)
|
||||
if not version:
|
||||
version = await ReportVersionService.get_designing_or_latest(db, template.id)
|
||||
if not version:
|
||||
raise ReportServiceException("没有可用版本")
|
||||
return await _export_pdf_response(db, version, template, body.params or {}, request)
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/export-pdf/template", summary="导出 PDF(按模板启用版本)")
|
||||
async def export_pdf_template(
|
||||
request: Request,
|
||||
body: ReportPreviewIn,
|
||||
template_id: str = Query(None, alias="templateId"),
|
||||
template_code: str = Query(None, alias="templateCode"),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
try:
|
||||
if template_code:
|
||||
template = await ReportService.get_by_code(db, template_code)
|
||||
elif template_id:
|
||||
template = await ReportService.get(db, template_id)
|
||||
else:
|
||||
raise ReportServiceException("需要 template_id 或 template_code")
|
||||
version = await ReportVersionService.get_active(db, template.id)
|
||||
if not version:
|
||||
version = await ReportVersionService.get_designing_or_latest(db, template.id)
|
||||
if not version:
|
||||
raise ReportServiceException("没有可用版本")
|
||||
return await _export_pdf_response(db, version, template, body.params or {}, request)
|
||||
except ReportServiceException as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
Reference in New Issue
Block a user