From f5b76ef711f444e4266c587e5dfe98164fc5bc3b Mon Sep 17 00:00:00 2001 From: wuzilong <704541015@qq.com> Date: Wed, 23 Sep 2026 12:36:18 +0800 Subject: [PATCH] docs: add plan-archive data-and-attachments ZIP export design. 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 --- ...an-archive-attachment-zip-export-design.md | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-23-pl-plan-archive-attachment-zip-export-design.md diff --git a/docs/superpowers/specs/2026-09-23-pl-plan-archive-attachment-zip-export-design.md b/docs/superpowers/specs/2026-09-23-pl-plan-archive-attachment-zip-export-design.md new file mode 100644 index 000000000..6ae8ea791 --- /dev/null +++ b/docs/superpowers/specs/2026-09-23-pl-plan-archive-attachment-zip-export-design.md @@ -0,0 +1,217 @@ +# 运输计划归档 · 数据及附件 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 用相对路径超链接(不做 WPS `DISPIMG` 内嵌) + +## 目标 + +在运输计划归档页「导出」下拉中,**保留**现有 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 打包流程 + +1. 按现逻辑查询计划(含 `EXPORT_MAX_ROWS` 校验)。 +2. `build_plan_archive_export_rows(...)` 生成归档行。 +3. 收集附件(§3):单据附件 + 危化三证;按计划去重 URL。 +4. 为每个**唯一计划**确定附件序号 = 该计划在归档表中**首行**的 `rowNo`。 +5. 从对象存储下载文件,写入内存/临时 ZIP: + - `计划归档(yyyyMMddHHmm)/(总表)计划归档.xlsx` + - `计划归档(yyyyMMddHHmm)/附件/{序号}_{类型标签}_{短哈希}.{ext}` +6. 生成 xlsx:原归档列 + `附件_1`…`附件_K`;仅计划首行写 `HYPERLINK`。 +7. 返回 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`,**不**使用 WPS `DISPIMG` / 内嵌 `xl/media`(避免体积翻倍与专有格式)。 +- 文件名带类型标签,便于人工辨认(参考包为 `序号_hash`)。 + +--- + +## 六、边界、上限与错误 + +| 场景 | 行为 | +|------|------| +| 超过 `EXPORT_MAX_ROWS` | 400,提示缩小范围 | +| 建议软上限 | 单次附件文件数约 2000 或总下载约 500MB(实现时定一默认);超限提示缩小范围 | +| 无计划 | 提示无可导出数据 | +| 有计划、零附件 | 仍返回 ZIP(仅 xlsx;可无 `附件/`) | +| 单个文件下载失败 | 跳过该文件;可选写入 `导出说明.txt`;其余继续 | +| 非图片 | 正常入包 + 超链接 | +| 权限 | 与现归档导出相同(租户 + `apply_pl_scope`) | + +--- + +## 七、测试要点 + +1. 下拉四项文案与产物类型正确;筛选 / 已选范围正确。 +2. 解压后目录对齐 §五;从解压目录打开 xlsx,点击附件列能打开对应文件。 +3. 单据 6 类 + 危化三证均出现;与列表单据附件及产品证书一致。 +4. 多段多产品计划:附件只挂首行,前缀=该行序号。 +5. 无附件 / 部分失败 / 超行数行为符合 §六。 +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 组装为独立服务函数,便于单测与后续改命名规则。