From d5c2e1f1a453eaa80687a412378d60f682ef5da7 Mon Sep 17 00:00:00 2001 From: wuzilong <704541015@qq.com> Date: Wed, 23 Sep 2026 12:46:52 +0800 Subject: [PATCH] feat(pl): wire archive_zip export format on plan export API. Reuse archive row building and attach document plus hazard-cert files into a zip response. Co-authored-by: Cursor --- backend/app/api/pl/plan.py | 47 +++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/backend/app/api/pl/plan.py b/backend/app/api/pl/plan.py index 18116a454..2fb9090b7 100644 --- a/backend/app/api/pl/plan.py +++ b/backend/app/api/pl/plan.py @@ -727,7 +727,11 @@ class PlanExportBody(BaseModel): hazard_cert_ready: Optional[int] = Field(default=None, alias="hazardCertReady") ids: Optional[List[str]] = None columns: Optional[List[str]] = None - export_format: Optional[str] = Field(default=None, alias="exportFormat", description="archive=运输计划归档专用导出") + export_format: Optional[str] = Field( + default=None, + alias="exportFormat", + description="archive=运输计划归档 Excel;archive_zip=归档数据及附件 ZIP", + ) model_config = {"populate_by_name": True, "extra": "allow"} @@ -779,8 +783,14 @@ def export_plans( .all() ) date_str = datetime.now().strftime("%Y%m%d") - is_archive_export = (body.export_format or "").strip().lower() == "archive" - if is_archive_export: + export_format = (body.export_format or "").strip().lower() + is_archive_export = export_format == "archive" + is_archive_zip = export_format == "archive_zip" + if len(plans) > EXPORT_MAX_ROWS: + raise ExportValidationError(f"导出超过 {EXPORT_MAX_ROWS} 条,请缩小筛选范围") + if is_archive_export or is_archive_zip: + if not plans: + raise ExportValidationError("没有可导出的数据") transport_ids = [plan.transport_id for plan in plans if plan.transport_id] transports_by_id = _load_transports_by_id(db, tenant_no, transport_ids) items = build_plan_archive_export_rows( @@ -799,6 +809,37 @@ def export_plans( cols = filter_export_columns(PLAN_ARCHIVE_EXPORT_COLUMNS, body.columns) if not user_can_view_archive_prices(db, user): cols = [c for c in cols if c.field not in ARCHIVE_PRICE_EXPORT_FIELDS] + if is_archive_zip: + from app.services.pl_plan_archive_zip_export import ( + build_archive_zip_streaming_response, + collect_hazard_cert_items, + load_plan_products_by_plan_ids, + merge_plan_attachment_items, + ) + + plan_ids = [p.id for p in plans] + docs = load_plan_document_attachments_by_plan_ids( + db, tenant_no=tenant_no, plan_ids=plan_ids + ) + products_by_plan = load_plan_products_by_plan_ids( + db, tenant_no=tenant_no, plan_ids=plan_ids + ) + attachments_by_plan = { + pid: merge_plan_attachment_items( + docs.get(pid, []), + collect_hazard_cert_items(products_by_plan.get(pid, [])), + ) + for pid in plan_ids + } + stamp = datetime.now().strftime("%Y%m%d%H%M") + return build_archive_zip_streaming_response( + db=db, + tenant_no=tenant_no, + columns=cols, + rows=items, + attachments_by_plan=attachments_by_plan, + stamp=stamp, + ) sheet_title = "计划归档" filename_stem = f"计划归档_{date_str}" else: