Skip to content

beforeUpdate hook 在 multi:true 批量更新上拿不到 ctx.previous —— sys_fetch_previous_update 依赖 input.id;引擎已为校验取 priorRows 却不喂 hook(17.0.0-rc.2) #5574

Description

@yinlianghui

Part of objectstack-ai/hotcrm#779

现象

ctx.previousbeforeUpdate 上的唯一来源是内建 sys_fetch_previous_update(priority 5,object: '*',由 kernel 的 registerAuditHooks() 装载)——引擎主体只在 afterUpdate 分支里赋 hookContext.previous。而该内建第一行是:

if (hookCtx.input?.id && !hookCtx.previous) { … }

multi: true 的批量更新没有 input.id(目标由 where 圈定),内建取不到任何东西,ctx.previous 在批量路径上恒为 undefined

证据(pinned 17.0.0-rc.2,真 object schema + 真 hook + 内建忠实复刻,priority 299 探针 hook 观测)

=== single-row update (category only) ===
  [prev-fetch] input.id="crm_knowledge_article-…-1" previous=undefined
  [knowledge-hook sees] previous.status="published" input={"id":"…","category":"api"}
  => last_reviewed_at refreshed: true

=== MULTI update (category only, every published row) ===
  [prev-fetch] input.id=undefined previous=undefined
  [knowledge-hook sees] previous.status=undefined input={"category":"troubleshooting"}
  => last_reviewed_at refreshed: false

=== MULTI update writing last_reviewed_at: null ===
  [knowledge-hook sees] previous.status=undefined input={"last_reviewed_at":null}
  => A: last_reviewed_at=null
  => B: last_reviewed_at=null

应用侧影响(hotcrm 实测,详见 hotcrm#779)

hotcrm 有 15 个 hook 文件读 ctx.previous,几乎全用 previous?.x 可选链——批量路径上不抛错,而是静默空转或走进「当作首次转换」分支。已实测的两种后果:批量编辑不刷新知识文章的 last_reviewed_at(打破 stale 报表前提);批量写能把 readonly: truelast_reviewed_at 写成 null(单行路径 hook 会补戳盖掉,批量不会)。守卫语义的 hook 在批量路径上失效方向是放行(如 contract.hook 的 previous?.status === 'activated' 守卫)。

关键问题(设计定夺,不预设是 bug)

引擎在 needsPriorRecord(updateSchema) 为真时确实会取 priorRows(用于校验规则),只是没有把它喂给 hook。所以:

  1. 批量路径不供给 previous 是设计取舍还是遗漏?若是取舍,请在 hook 契约文档里写明「multi:true 上 ctx.previous 不可用」,应用侧好据此重排不变量(挪到校验规则/flow);
  2. 若愿意供给:per-row previous 的代价与语义(N 行 N 份、hook 逐行执行还是一次批量执行)需要平台定夺——priorRows 既然已取,喂给 hook 的增量成本可能有限。

应用侧的修复方向(hook 逐个显式化 vs 不变量搬家)取决于这个答案,hotcrm#779 因此 Blocked-by 本单。

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions