Capture brainstorming decisions for coexisting Excel and ZIP export, reference-zip folder layout, document attachments plus hazard certs, and relative-path hyperlinks. Co-authored-by: Cursor <cursoragent@cursor.com>
9.2 KiB
运输计划归档 · 数据及附件 ZIP 导出 — 设计文档
日期: 2026-09-23
状态: 待审阅
页面:web-admin/#/pl/plan-module/plan-archive
前置:docs/superpowers/specs/2026-06-25-pl-plan-archive-design.md、现有归档 Excel 导出(exportFormat=archive)、load_plan_document_attachments_by_plan_ids
参考包形态: 用户提供的加油汇总表(yyyyMMddHHmm).zip(根目录 +(总表)*.xlsx+ 扁平附件/{序号}_*.ext)
实现策略: 后端一站式打 ZIP;Excel 用相对路径超链接(不做 WPSDISPIMG内嵌)
目标
在运输计划归档页「导出」下拉中,保留现有 Excel 导出,并新增「数据及附件 ZIP」能力:一次下载包含归档 Excel + 计划相关附件文件,目录结构对齐参考 ZIP;用户须先解压,再在解压目录中打开 Excel,通过超链接打开附件。
已确认决策(2026-09-23)
| # | 决策 | 说明 |
|---|---|---|
| D1 | 并存 | 保留「导出筛选结果 / 导出已选」Excel;新增两项 ZIP |
| D2 | ZIP = 数据 + 附件 | 包内同时有归档 xlsx 与附件文件 |
| D3 | 范围对称 | ZIP 亦拆「筛选结果」「已选」两项 |
| D4 | 结构对齐参考包 | 计划归档(yyyyMMddHHmm)/(总表)计划归档.xlsx + 附件/ 扁平文件;不做 WPS DISPIMG / cellimages 内嵌 |
| D5 | 路径引用 | Excel 附件列用相对路径 HYPERLINK;须先解压再打开 |
| D6 | 单据附件范围 | 与列表「单据附件」一致(6 类,见 §3.1) |
| D7 | 危化品证一并导出 | 计划产品购买证 / 运输证 / 合法使用证明 URL |
| D8 | 后端一站式 | 扩展现有 plan export;浏览器不拼包 |
非目标(本期不做)
- WPS
DISPIMG/xl/cellimages.xml内嵌配图(体积翻倍、实现复杂) - 将图片真正嵌入标准 OOXML drawing(可选二期)
- 异步导出任务中心 / 下载通知
- 车辆/人员证件、承运商附件、合同扫描件、地磅原始磅单文件(除非已落入 §3 附件来源)
- 新增独立权限码(沿用现归档导出能力;需要时再加
pl:plan-archive:export-zip)
一、前端交互
1.1 入口
页面:web-admin/src/views/pl/plan-archive/index.vue
组件:ListExportDropdown(需扩展,或归档页专用包装)
| 菜单项 | 产物 |
|---|---|
| 导出筛选结果 | 归档 .xlsx(现状) |
| 导出已选 | 归档 .xlsx(现状) |
| 导出筛选结果(数据及附件 ZIP) | .zip |
| 导出已选(数据及附件 ZIP) | .zip |
- 无勾选时「导出已选*」仍禁用(与现状一致)。
- ZIP 请求带上与 Excel 相同的筛选参数 /
ids,并设置exportFormat: 'archive_zip'。 - 下载:复用
pickBlobFromExportResponse+triggerBlobDownload;默认文件名计划归档(yyyyMMddHHmm).zip。
1.2 加载与提示
- 按钮
loading直至响应结束。 - 成功:「导出成功」。
- 业务错误(超行数等):展示后端文案。
- 可选:若响应附带「部分附件失败」说明,用 warning 提示(若实现为包内
导出说明.txt,前端可不解析)。
二、后端接口与流程
2.1 接口
- 方法/路径: 现有
POST /api/.../transport/plan/export(与现导出同路由) - 新增取值: 现有字段
exportFormat增加取值archive_zip(与现有archive并列) - 筛选 / ids / columns / 权限: 与
exportFormat=archive相同;价格列仍受user_can_view_archive_prices过滤 - 响应:
Content-Type: application/zip;Content-Disposition含时间戳文件名
2.2 打包流程
- 按现逻辑查询计划(含
EXPORT_MAX_ROWS校验)。 build_plan_archive_export_rows(...)生成归档行。- 收集附件(§3):单据附件 + 危化三证;按计划去重 URL。
- 为每个唯一计划确定附件序号 = 该计划在归档表中首行的
rowNo。 - 从对象存储下载文件,写入内存/临时 ZIP:
计划归档(yyyyMMddHHmm)/(总表)计划归档.xlsx计划归档(yyyyMMddHHmm)/附件/{序号}_{类型标签}_{短哈希}.{ext}
- 生成 xlsx:原归档列 +
附件_1…附件_K;仅计划首行写HYPERLINK。 - 返回 ZIP 流。
2.3 建议实现落点
| 层 | 建议 |
|---|---|
| API | backend/app/api/pl/plan.py → export_plans 分支 archive_zip |
| 服务 | 新建如 pl_plan_archive_zip_export.py:收集附件、命名、写 zip、改 xlsx 附件列 |
| 复用 | build_plan_archive_export_rows、load_plan_document_attachments_by_plan_ids、现有 storage 读接口、export_rows_to_xlsx(或等价)再注入超链接列 |
三、附件范围与命名
3.1 单据附件(与列表「单据附件」一致)
| # | 来源 | 标签示例 | 字段 |
|---|---|---|---|
| 1 | 运输单整单司机回传 | 司机 |
pl_transport.feedback_images |
| 2 | 司机按段回传 | 装货·司机 / 送货#N·司机 |
pl_transport_driver_stop_feedback.attachment_urls |
| 3 | 现场按段回传 | 装货·现场 / 送货#N·现场 |
pl_transport_site_stop_feedback.attachment_urls |
| 4 | 现场链接回传 | 现场 / 现场·{产品} |
pl_transport_site_link.site_feedback_images |
| 5 | 入库单现场 | 入库现场 |
pl_inbound_order.site_feedback_images |
| 6 | 出库单现场 | 出库现场 |
pl_outbound_order.site_feedback_images |
同一计划内相同 URL 去重。文件类型以实际上传为准(图片、PDF 等)。
3.2 危化品证(计划产品)
| 类型 | 字段 |
|---|---|
| 购买证 | pl_plan_product.purchase_cert_url |
| 运输证 | pl_plan_product.transport_cert_url |
| 合法使用证明 | pl_plan_product.legal_use_cert_url |
均为逗号分隔多 URL;空则跳过。多产品时标签可带产品名缩写以防文件名冲突。
3.3 文件命名
{序号两位}_{类型标签}_{短哈希}.{ext}
示例:01_装货·司机_a1b2c3.jpg、01_购买证_d4e5f6.pdf
路径非法字符按现有 zip 命名惯例替换为 _。
3.4 序号规则
- 归档表
序号(rowNo)仍按「段 × 产品」逐行递增(与现 Excel 导出一致)。 - 附件文件前缀 = 该计划首行的
rowNo(两位补零)。 - 同一计划多附件共用同一前缀;Excel 仅在该计划首行填写
附件_*列。
四、Excel 附件列
| 规则 | 说明 |
|---|---|
| 列名 | 附件_1 … 附件_K |
| K | 本批各计划附件数的最大值;上限 20(超出仍写入 附件/ 目录,表内只链前 20 个) |
| 单元格 | 相对路径超链接,如 HYPERLINK("附件/01_装货·司机_a1b2.jpg","装货·司机") |
| 填行 | 仅计划首行;同计划其余行留空 |
| 打开方式 | 用户须解压 ZIP 后,在解压目录中打开 xlsx |
纯 exportFormat=archive 的 Excel 不增加附件列(行为不变)。
五、ZIP 目录形态(对齐参考包)
计划归档(202609231230)/
(总表)计划归档.xlsx
附件/
01_装货·司机_a1b2.jpg
01_购买证_c3d4.pdf
02_送货#1·现场_e5f6.jpg
导出说明.txt ← 仅当存在下载失败等时可选写入
与参考包差异(有意为之):
- Excel 使用标准
HYPERLINK,不使用 WPSDISPIMG/ 内嵌xl/media(避免体积翻倍与专有格式)。 - 文件名带类型标签,便于人工辨认(参考包为
序号_hash)。
六、边界、上限与错误
| 场景 | 行为 |
|---|---|
超过 EXPORT_MAX_ROWS |
400,提示缩小范围 |
| 建议软上限 | 单次附件文件数约 2000 或总下载约 500MB(实现时定一默认);超限提示缩小范围 |
| 无计划 | 提示无可导出数据 |
| 有计划、零附件 | 仍返回 ZIP(仅 xlsx;可无 附件/) |
| 单个文件下载失败 | 跳过该文件;可选写入 导出说明.txt;其余继续 |
| 非图片 | 正常入包 + 超链接 |
| 权限 | 与现归档导出相同(租户 + apply_pl_scope) |
七、测试要点
- 下拉四项文案与产物类型正确;筛选 / 已选范围正确。
- 解压后目录对齐 §五;从解压目录打开 xlsx,点击附件列能打开对应文件。
- 单据 6 类 + 危化三证均出现;与列表单据附件及产品证书一致。
- 多段多产品计划:附件只挂首行,前缀=该行序号。
- 无附件 / 部分失败 / 超行数行为符合 §六。
- 纯 Excel 导出回归:无附件列、无 zip。
建议单测: 附件收集与命名、首行挂载序号、zip 内相对路径字符串;可不测真实 MinIO(mock 字节流)。
八、架构小结
[ListExportDropdown ZIP command]
│ exportFormat=archive_zip + filters/ids
▼
[POST plan/export]
│
├─ build_plan_archive_export_rows
├─ load_plan_document_attachments_by_plan_ids
├─ load plan product cert URLs
├─ fetch storage objects
├─ write xlsx (+ HYPERLINK cols) + 附件/*
└─ stream application/zip
单元边界清晰:导出查询与行构建复用现逻辑;ZIP 组装为独立服务函数,便于单测与后续改命名规则。