Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

DetailView「审批中已锁定」band 不读节点 lockRecord:lockRecord:false 的审批节点上照样显示锁定 + 撤回 #2902

Closed

Description

@baozhoutao

现象

一条 record_change flow 串了多个 approval 节点,各节点 lockRecord 配置不同时:

  • 服务端行为是正确的 —— lockRecord: false 的节点上记录可以正常编辑保存(plugin-approvals 的 beforeUpdate 锁钩子里明确 if (config?.lockRecord === false) return;)。
  • 但 Console 记录详情页顶部的「审批中已锁定 / Locked for approval」band + 「撤回审批 / Recall approval」按钮,在所有 pending 节点上都照常渲染,不区分当前节点的 lockRecord。

结果是:同一个 band 在「记录能改」和「记录被锁死」两种完全相反的状态下长得一模一样,用户无法从界面区分。

复现

  1. 定义一个 record_change flow,串两个 approval 节点:
    • 节点 A:lockRecord: false
    • 节点 B:lockRecord: true
  2. 提交审批,流程停在节点 A。
  3. 打开该记录的详情页。

实际:顶部渲染「Locked for approval」band 和「Recall approval」按钮。但从「编辑」进去改任意字段并保存 —— 保存成功,值正常落库。

  1. 审批通过,流程进入节点 B。再次打开详情页 —— band 与节点 A 完全一致,但此时编辑保存会被服务端拒绝:
    RECORD_LOCKED: record is locked while an approval is in progress

预期:当前 pending 节点的 lockRecord 为 false 时不应渲染锁定 band;或者至少让文案区分「审批中(可编辑)」与「审批中已锁定」两种状态。

影响

提示与平台自身的行为不一致 —— 平台明明支持按节点关闭锁、而且这个开关真的生效,但 UI 不读它。

实际业务里的表现:审批链上的单人节点(部门负责人 / 厂长)通常配 lockRecord: false,本意就是允许审批人在审批时补充内容;但审批人看到「已锁定」就不会去尝试编辑,这个能力等于不存在。反过来,真正锁死的并行会审节点,用户又得点进编辑表单填完一屏,才在点保存时被服务端拒掉。

我们这边测试同学据此得出了「整个审批流程都会锁记录」的结论,与实际情况不符,排查花了不少时间。

定位线索

  • band 渲染在 packages/plugin-detail(DetailView approval band,参见 src/__tests__/DetailView.approvalBand.test.tsx),i18n key detail.lockedByApproval
  • 相关历史改动:"Locked for approval" band never shows on backends that track the lock via approval requests only (no approval_status field) #2618 fix(detail): show the "Locked for approval" band on request-tracked backends —— 该次修复让 band 在「存在 pending approval request」时显示,疑似未带上节点 lockRecord 的判定
  • 服务端权威判定在 objectstack packages/plugins/plugin-approvals/src/lifecycle-hooks.ts;节点配置快照已经存在 sys_approval_request.node_config_json 里,band 可以从同一来源读出 lockRecord,不需要额外接口

环境

  • @objectstack/plugin-approvals 16.1.0
  • Console(objectui)随该版本

Activity

  1. baozhoutao commented on Jul 28, 2026

    @baozhoutao
    ContributorAuthor

    定位完成,修复已提 PR。补充两条查证结论,其中一条要更正 issue 里的定位线索。

    一、「band 可以从同一来源读出 lockRecord,不需要额外接口」—— 这条不成立

    node_config_json 里确实有 lockRecord,但它到不了客户端。framework 的 rowFromRequest(packages/plugins/plugin-approvals/src/approval-service.ts:236)是一份显式白名单投影:它解析了 node_config_json,只从里面挑 __flowLabel / __nodeLabel / __round / escalation.timeoutHours / decisionOutputs 出来,lockRecord 一个字都没往外发;ApprovalRequestRow 上也没有其它字段承载锁信息。

    所以 GET /api/v1/approvals/requests 的消费方能拿到的最强事实就是「存在一条 pending 请求」,Console 单独改修不了。修复跨两个仓库,framework 先行。

    二、是 bug,且是「从来没接出来」,不是回归

    lockRecord 是 spec 里正式的节点配置项(ApprovalNodeConfigSchema,z.boolean().default(true)),服务端严格执行 —— 平台自己定义、自己执行的开关,UI 不读,属于平台内部行为不一致。

    git 追溯确认不是回归:objectui 的 approvalLocked 由 #2572 引入时就是「approval_status 或有 pending request」的二值判断,#2618 只是补了第二个来源,从未有过节点粒度;framework 侧的行投影也从第一版起就没带过锁信号。这个能力自诞生起就是 server-only。

    三、影响面比 issue 描述的更大

    issue 只提了 band 文案和撤回按钮,但同一个信号还掐着 canEdit(RecordDetailView.tsx):

    canEdit={... && !approvalLocked}

    所以 lockRecord: false 的节点上行内编辑被 Console 整个禁掉 —— 铅笔不出现、enter() 空转。你们是从独立的「编辑」表单路由进去才存成功的,那条路没接这个门。也就是说「审批人可以在审批时补充内容」这个能力,比 issue 里描述的更彻底地不存在。

    修复

    两条建议路径都做了:既按节点区分是否渲染锁定 band,也让文案区分两种状态,这样对老后端也能优雅降级。

    真机实测

    showcase 上推了一条真实审批走完两个节点(Manager Review lockRecord:false → Executive Review lockRecord:true):

    1. 停在 Manager Review,API lock_record: false → band「审批中 · 可编辑」,撤回按钮在,编辑可用
    2. 双击「已花费」行内编辑,420,000 → 431,000 保存成功,落库
    3. 批准进 Executive Review(lock_record: true)→ band 当场翻成「审批中已锁定」
    4. 再双击同一字段 → 不进编辑,保持只读

    正是 issue 里那个「两个节点长得一模一样」的场景,现在两态可区分了。

  2. baozhoutao commented on Jul 28, 2026

    @baozhoutao
    ContributorAuthor

    已与另一条相关问题合并到单条 issue,便于一起看:objectstack-ai/objectstack#3794

    合并原因:这条(lockRecord: false 的节点仍显示「审批中已锁定」band)与「readonlyWhen 字段被静默丢弃却返回 200」是同一个用户旅程里两个相反方向的失真 —— 能改的说锁了、改不了的说成功了,分开看都不完整。

    本条的修复落点仍在 objectui packages/plugin-detail,详见合并后 issue 的「问题一」小节。

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions