Skip to content

feat(rest): 导出接口新增 ?template=true —— 输出只含「可填列」的 xlsx 导入模板 #18386

Description

@baozhoutao

Ruled: 5904855243 · letter Q1 A · Q2 C — ISecurityService.getWritableFields is added and the template narrows by it (round 2 on PR #20683, Part of #18386: acceptance 6 is a hand check in four spreadsheet apps, so the card stays open for it after the PR lands; ruling 5906743185); the column rule keeps system and drops hidden; acceptance 1 is corrected accordingly · 2026-09-30T05:41Z

问题

导入模板目前没有统一实现,两个仓库各做了一套,且都把用户填了也写不进去的列放进了模板。

objectui 的「下载模板」按钮前端自行拼 CSV,列取自 importTargetFields 的全量结果;framework 这边 GET /data/:object/export 的默认列是 buildFieldMetaMap(schema) 的全部 key,只按 FLS 收窄,不看 system / hidden / readonly。注释里 rest-server.ts 明确写了「an empty export doubles as an import template」——但空导出只保证「没有数据行」,没回答「表头该有哪些列」。

实测一个业务对象(项目),模板前 7 列是 registry 注入的系统字段:

列 字段 readonly hidden system
组织标识 organization_id ✅ ✅ ✅
创建时间 created_at ✅ — ✅
创建人 created_by ✅ — ✅
更新时间 updated_at ✅ — ✅
更新人 updated_by ✅ — ✅
所有者 owner_id ❌ — ✅
所属业务单元 owning_business_unit_id ✅ ✅ ✅

因为是 registry 注入,每个对象都这样,不是个别对象配错。

比「列太多」更严重的是:这些 readonly 列会被 stripReadonlyFields 在写入时静默剥掉(engine.ts)。用户照模板填了「创建人」,导入成功、无报错、数据没进去。模板在教用户填注定被丢弃的列。

为什么不能直接改导出的默认列

导出和模板的诉求相反:

所有者 / 修改时间
导出(我要看数据) 要 —— 列表上显示了,导出就该有
模板(我要填数据) 不要 —— 填了会被平台盖掉或 strip

同一套列规则无法同时满足。所以导出的列规则保持不变,模板作为新模式引入 —— 这样对现网调用方零影响。

方案:一个接口,两个模式

GET /data/:object/export                 → 现状,不改
GET /data/:object/export?template=true   → 新增

目标列规则

导出模式(不改) 模板模式(新增)
起点 schema 全字段 schema 全字段
system 保留 排除
hidden 保留 保留(可写即进模板,裁定 5904855243 Q2 C)
readonly 保留 排除
formula / summary 保留 排除
autonumber 保留 排除
FLS ∩ 可读 ∩ 可写
?fields= 显式指定 照办,不收窄 照办,不收窄
数据行 有 0 行
列顺序 作者声明顺序 作者声明顺序(不重排,必填靠 * 标记)

判据一句话:「这列我填了,导入后真的会落库吗?」答"会"才进模板。 每条排除都对应引擎里一个真实的丢弃或拒绝行为,不是审美判断。

输出格式:xlsx(不是 CSV)

模板必须表达值域,CSV 做不到:

CSV xlsx
封闭值域下拉 做不到 Excel 数据验证,点选不会错
multiselect 用逗号分隔 与 CSV 分隔符打架 无冲突
值域说明 只能污染表头 独立 sheet
前导零(00123) 被 Excel 吃成 123 可声明文本单元格

具体结构:

  • Sheet1「模板」:表头(必填带 *)+ 一行示例值;select / radio / boolean 列挂数据验证下拉
  • Sheet2「填写说明」:每字段一行 —— 合法值、格式、分隔符;lookup 写「填 <目标对象> 的名称」。同时兼作下拉数据源(Excel 内联下拉有 255 字符上限,选项多时必须引用区域)

值域怎么写

导入端(import-coerce.ts)实际接受的范围比模板告诉用户的宽得多,模板应如实传达:

类型 导入端接受 模板该给
select / radio ① value 精确 ② label 大小写不敏感 ③ 翻译后 label label(用户看得懂,导入端认)
multiselect 按 , ; 、 换行 拆分 示例用 、 连两个真实选项;说明列出四种分隔符
boolean true/t/yes/y/1/on/是/对/✓/√ + 反面 示例给「是」,不是 true
lookup / reference 显示名 → resolveRef 解析 说明写「填名称」,提示重名会 reference_ambiguous
number 容忍 1,234 / $¥€£¥ / 25% / (100) 说明里写明

实现要点

已定的边界

  • upsert 匹配列:模板不预置 autonumber。要 upsert 的人自己加列,映射步骤里照样选得到 —— 功能不丢。理由:为少数场景在模板里塞一列「填了新建时无效」的东西,又回到"教人填没用的列"的老问题。
  • 示例值行:保留。它传达格式,Sheet2 说明里提示用户删除。

验收

  1. 任一业务对象 ?template=true,下载的 xlsx 中不含任何 system / readonly / formula / summary / autonumber 列;作者声明 hidden: true 且可写入的字段保留(裁定 5904855243 Q2 C)
  2. 保留列的顺序与作者 fields 声明顺序一致;必填且没有默认值的列,表头带 *(裁定 5896083518 Q3 A)
  3. select / boolean 列在 Excel 中可下拉选择
  4. 不带 ?template=true 的导出,列与本 issue 之前逐字节一致(现网零行为变更)
  5. 下载的模板填入数据后原样导回,不产生 invalid_option / invalid_boolean
  6. 跨表格软件实测:Excel / WPS / Numbers / Google Sheets 下拉均正常 —— 单测证明不了,需真下载真打开

关联

objectui 侧改动(「下载模板」改调本接口、删除前端 CSV 生成)见 objectstack-ai/objectui 的对应 issue。本 issue 落地后 objectui 那侧才能动。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specenhancementNew feature or requestpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions