- 增加合同专用 PDF 上传、鉴权预览和失败清理接口 - 支持草稿附件替换移除、并发保护和启用完整性校验 - 修复循环模板引用导致文件选择器无法打开的问题 - 补充专项测试、中文项目文档和操作日志
6.3 KiB
6.3 KiB
操作日志:配送合同附件上传
操作时间:2026-08-12 20:42:47 操作类型:扩展 影响模块:平台总后台配送合同、后端合同业务、受控文件存储
操作前状态
- 合同表单将
file_uri渲染为普通“附件地址”文本框,允许手工填写任意字符串。 - 合同接口仅校验 URI 长度,没有专用 PDF 上传、预览或清理能力。
- 通用
/upload/file支持多种图片、PDF 和视频,不能直接收紧而不影响其他模块。 - 合同详情会过滤
file_uri,页面无法安全查看附件。 - 合同启用流程不检查签署附件。
具体操作
- 新增合同专用 PDF 上传、临时清理和鉴权预览接口。
- 使用 HMAC 签名收据绑定上传人与临时 URI,有效期为 30 分钟。
- 创建或更新合同时将临时文件移动到正式目录,并在失败时回滚文件。
- 使用不透明附件版本和旧 URI 条件处理草稿并发更新。
- 草稿替换或移除成功后删除旧文件,失败最多重试三次并记录日志。
- 合同启用前验证受控 URI 和磁盘 PDF 文件。
- 将前端文本框替换为支持点击和拖拽的 PDF 控件,保存时上传。
- 新增详情预览、旧地址重新上传提示、移除二次确认和离开页面未保存检测。
- 补充安全边界、签名凭证、路由注册等自动化测试。
操作后状态
- 合同草稿可无附件保存;启用前必须具备真实有效的受控 PDF。
- 单个附件最大 10 MiB,仅允许 PDF。
- 草稿可替换和移除,非草稿只可鉴权预览。
- 页面不暴露内部 URI,不保存原始文件名,统一显示“合同附件.pdf”。
- 历史外部 URI 不删除,但不可预览且不能满足启用条件。
- 不修改数据库表和模型,不新增定时扫描任务。
代码变更
backend/api/internal/logic/platform/gasorder/contract_attachment.go:新增 349 行,合同附件核心服务。backend/api/internal/logic/platform/gasorder/contract_attachment_test.go:新增 83 行,安全与边界测试。backend/api/internal/logic/platform/gasorder/gasorder.go:约新增 88 行、删除 7 行,接入创建、更新、并发和启用校验。backend/api/internal/routers/platform.go:新增 3 条专用路由。backend/api/internal/routers/platform_contract_attachment_test.go:新增 25 行,路由测试。frontend/platform_admin/src/api/contract-attachment.ts:新增 73 行,附件 API 客户端。frontend/platform_admin/src/views/resource/use-contract-attachment.ts:新增 147 行,附件页面状态与保存编排。frontend/platform_admin/src/views/resource/ResourceFieldForm.vue:约新增 145 行、删除 1 行,上传控件和样式。frontend/platform_admin/src/views/resource/ResourceRecordPage.vue:约新增 81 行、删除 2 行,详情预览和保存集成。frontend/platform_admin/src/api/resources.ts、ResourceDetailContent.vue:字段类型、动作状态和详情隐藏调整。
行为变化
- 变更前:管理员手工输入附件地址;无受控上传和下载。
- 变更后:管理员选择 PDF,保存时自动上传并绑定;通过合同标识鉴权预览。
- 变更前:无附件也可启用合同。
- 变更后:无有效 PDF 时拒绝启用。
- 兼容性:原
file_uri数据库字段、合同 JSON 字段和通用上传接口保持不变。
验证结果
go test ./...:通过。npm.cmd run type:check:通过。npm.cmd run build:通过。npm.cmd run contract:check:通过,48 个资源。npm.cmd run resource-pages:check:通过,详情 46 类、新建 25 类、编辑 23 类。git diff --check:通过。- 本地页面只读检查:应用可正常打开且无控制台错误;独立测试浏览器因未登录被重定向至登录页,因此未执行需要平台账号和测试合同数据的交互式保存、替换及预览验证。
npm.cmd run lint:未通过;项目已有 2 个错误、165 个警告和 12 个提示,诊断集中在既有文件。对本次 6 个前端改动文件单独执行 Biome lint 无错误,仅有项目现存的模板变量识别类警告。- 边界测试:伪装 PDF、错误 MIME、缺少结束标记、过期或篡改凭证、跨操作人凭证、路径穿越、外部 URL 和附件版本变化均覆盖。
风险评估
- 存储仍为本地文件系统;多实例部署必须共享
HEQI_UPLOAD_DIR,否则下载和启用校验可能找不到文件。 - 不运行孤儿文件定时扫描;浏览器关闭或最终删除失败时依赖应用日志由运维人工处理。
- JWT 密钥同时用于附件签名;密钥轮换会使尚未绑定的 30 分钟临时收据失效,已绑定附件不受影响。
- 原始文件名未持久化,这是“不改数据库”约束下的明确取舍。
- 项目已有前后端合同状态展示不一致,本次仅把启用动作可见状态修正为后端支持的草稿和终止状态,未扩展其他状态改动。
运行时异常修复补充(2026-08-12)
操作前状态
ResourceFieldForm.vue在字段v-for内为文件 input 使用同一个attachmentInput模板引用。- Vue 将循环内模板引用解析为元素数组,点击上传区域调用
.click()时抛出TypeError: attachmentInput.value?.click is not a function。
具体操作
- 新增
ContractAttachmentField.vue,将文件 input 和单一模板引用移出字段循环。 - 通用字段表单只负责渲染独立组件并转发选择、预览、移除和取消选择事件。
- 上传区域增加按钮语义、Tab 焦点、Enter/空格键入口和可见焦点样式。
- 操作按钮同时阻止点击与键盘事件冒泡,避免预览或移除时误打开选择器。
- 新增
scripts/check-contract-attachment-control.mjs和contract-attachment:check命令。
验证结果
npm.cmd run contract-attachment:check:通过。npm.cmd run type:check:通过。npm.cmd run build:通过。- 独立测试浏览器没有后台登录会话,未执行真实文件选择器弹窗的浏览器交互验证。
风险评估
- 未新增测试框架或运行依赖,沿用项目现有静态契约检查方式。
- 修复仅调整前端控件边界,不修改后端、数据库、上传接口或合同保存载荷。