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 <cursoragent@cursor.com>
This commit is contained in:
1 parent
c270d14057
commit
f5b76ef711
1 file changed
+217
@@ -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 组装为独立服务函数,便于单测与后续改命名规则。
|
||||||
Reference in new issue
Block a user