审批通过 / 拒绝时可上传附件。在记录 / 审批中心的「审批动态」时间线里,这个附件本应以 📎 文件名 胶囊呈现、点击打开。实测发现三处坏:
- 胶囊没有文件名,只显示通用「附件」。
- 点击胶囊无反应,也无任何报错。
- 即便文件下载下来,文件名是一长串签名 token 乱码(如
eyJrIjoiYXR0YWNo…),content-type 还是 application/octet-stream。
根因一 —— 审批读取路径把描述符 String() 烂了
sys_approval_action.attachments 是 Field.file,存的是富描述符对象 { id, name, url, mimeType, size }(fileId 在写入时被解析成完整描述符),不是 fileId 字符串。但 plugin-approvals 的 rowFromAction 用了 row.attachments.map(String),把每个对象 String() 成字面量 "[object Object]"。listActions 的消费方(审批时间线)拿到的就是这坨垃圾 → chip 无名字、openAttachment("[object Object]") 404 被前端吞掉。
实测证据:修复前 GET /approvals/requests/{id}/actions 返回 attachments: ["[object Object]"]。
根因二 —— 存储下载接口不带文件名 / 类型
预签名下载把字节以 application/octet-stream + 无 Content-Disposition 发出,浏览器只能拿 URL 末段(签名 token)当文件名保存。签名 token 里也没带原始文件名。
方案
approvals(@objectstack/spec + @objectstack/plugin-approvals)
- 新增
ApprovalActionAttachment,ApprovalActionRow.attachments 从 string[] 改为 ApprovalActionAttachment[];rowFromAction 透传描述符({id,name,url,...}),兼容裸字符串 fileId。决策写入 input 仍是 string[],不变。消费方从此不再需要对系统对象 sys_file 的读权限即可拿到名字 + URL。
storage(@objectstack/spec + @objectstack/service-storage)
IStorageService.getSignedUrl / getPresignedDownload 增加可选 PresignedDownloadOptions {filename, contentType, disposition}。
- REST 下载路由(
GET /storage/files/:id/url 与 /:id)传入 sys_file 的 name + mime_type。
- 本地适配器把它们编进签名 token,
_local/raw 路由发 Content-Type + RFC 5987 Content-Disposition(ASCII 回退 + filename*=UTF-8''…);S3 适配器用 ResponseContentType / ResponseContentDisposition 烤进签名 URL。默认 inline,可预览类型仍在浏览器打开,保存时名字正确。
已用 framework app-showcase(审批+存储链路)真机 UI 实测:chip 显示 signed-contract.pdf、点击打开真 PDF、下载响应头 content-type: application/pdf + content-disposition: inline; filename="signed-contract.pdf"。
前端配套:objectstack-ai/objectui#2820。
审批通过 / 拒绝时可上传附件。在记录 / 审批中心的「审批动态」时间线里,这个附件本应以
📎 文件名胶囊呈现、点击打开。实测发现三处坏:eyJrIjoiYXR0YWNo…),content-type还是application/octet-stream。根因一 —— 审批读取路径把描述符
String()烂了sys_approval_action.attachments是Field.file,存的是富描述符对象{ id, name, url, mimeType, size }(fileId 在写入时被解析成完整描述符),不是 fileId 字符串。但plugin-approvals的rowFromAction用了row.attachments.map(String),把每个对象String()成字面量"[object Object]"。listActions的消费方(审批时间线)拿到的就是这坨垃圾 → chip 无名字、openAttachment("[object Object]")404 被前端吞掉。实测证据:修复前
GET /approvals/requests/{id}/actions返回attachments: ["[object Object]"]。根因二 —— 存储下载接口不带文件名 / 类型
预签名下载把字节以
application/octet-stream+ 无Content-Disposition发出,浏览器只能拿 URL 末段(签名 token)当文件名保存。签名 token 里也没带原始文件名。方案
approvals(
@objectstack/spec+@objectstack/plugin-approvals)ApprovalActionAttachment,ApprovalActionRow.attachments从string[]改为ApprovalActionAttachment[];rowFromAction透传描述符({id,name,url,...}),兼容裸字符串 fileId。决策写入 input 仍是string[],不变。消费方从此不再需要对系统对象sys_file的读权限即可拿到名字 + URL。storage(
@objectstack/spec+@objectstack/service-storage)IStorageService.getSignedUrl/getPresignedDownload增加可选PresignedDownloadOptions {filename, contentType, disposition}。GET /storage/files/:id/url与/:id)传入sys_file的name+mime_type。_local/raw路由发Content-Type+ RFC 5987Content-Disposition(ASCII 回退 +filename*=UTF-8''…);S3 适配器用ResponseContentType/ResponseContentDisposition烤进签名 URL。默认inline,可预览类型仍在浏览器打开,保存时名字正确。已用 framework
app-showcase(审批+存储链路)真机 UI 实测:chip 显示signed-contract.pdf、点击打开真 PDF、下载响应头content-type: application/pdf+content-disposition: inline; filename="signed-contract.pdf"。前端配套:objectstack-ai/objectui#2820。