第 6 / 23 篇

第 5 章:审批必须指向具体行动和有效时间

用户同意“处理这张工单”,不一定意味着允许任何金额、任何账户、任何时刻的后续操作。长任务把审批与执行分开,也把两者之间可能发生的变化放大了。可靠执行需要明确许可的对象、参数、主体与时间边界。

5.1 把批准从一段文字变成一项记录

聊天记录里的一句“可以”可以作为用户交互材料,但不能自动替代执行数据库中的授权事实。系统需要知道同意的是哪份计划,来自哪位主体,属于哪个租户,以及什么时候失效。

示例让审批记录绑定工作流身份与计划指纹。审批人必须具有教学应用中的 approver 角色,且租户与工作流一致。错误角色、错误租户、错误指纹都应被拒绝,工作流继续等待审批。

本书的 Actor 来自受信任的教学调用上下文,实验没有实现生产登录、OIDC 或完整的委托授权协议。这个前提不能被省略。若应用直接接受用户传来的 role=approver 字段,再调用相同数据库方法,代码中的角色比较仍然存在,却已经失去身份可信性。端到端身份与工具委托需要另一层实现。

5.2 审批绑定的是整份可执行意图

审批时展示的账户、数量、动作和相关业务原因,应与执行时使用的数据相符。指纹用于检测结构化意图是否变化,但不能保证审批界面确实向操作者展示了正确内容。

例如界面显示“500 单位”,后台却批准了“5000 单位”的指纹,哈希校验只会稳定保护错误绑定。评审时应检查从持久计划到展示、从点击到审批请求的整条链路。关键参数不能只藏在模型生成的自然语言摘要里。

重新规划后,要么创建新的可审批版本,要么明确撤销旧意图。不能让旧审批自动迁移到新计划,也不应把“审批已通过”的布尔值复制到任意新对象。许可之所以成立,是因为它绑定了一份具体事实。

5.3 排队时间会消耗审批有效期

审批完成时任务还可能排队很久。执行者不能只在审批那一刻检查期限,然后永久保留一个 ready 状态。示例在领取和即将调用工具之前都检查许可,使等待和本地调度造成的明显过期可以被拒绝。

排队时发现审批过期,任务回到等待审批。领取之后、工具调用之前发现过期,也应结束当前领取,保留重新审批的责任。延长 worker 租约不能绕过它,因为租约证明的只是执行者仍在承担工作。

数据库时间让这些判断围绕同一个时钟进行。不同机器的本地时钟若存在偏差,一台判断许可仍有效,另一台可能已经拒绝。使用数据库时间减少了本实验中的跨主机时钟分歧,但不能消除数据库时钟调整、基础设施故障或跨系统时间语义的问题。

5.4 调用前检查仍然存在边界

两次本地检查没有把审批与远程工具执行变成一项原子动作。最后一次检查之后,网络传输和远程处理仍需要时间。如果许可恰好在这段时间中到期,工具可能仍然产生效果。

必须说明合同约束的是“允许开始发送的时刻”,还是“远程服务受理的时刻”,或者“效果完成的时刻”。本书的本地实验检查调用前的执行许可,没有证明远程服务在同一时刻重新验证审批。若业务要求工具侧检查,应把受保护的审批或短期执行凭据传到业务服务,并由它独立核验。

同样,撤销审批并不等于撤销已经发出的请求。任务需要保留可能发生的效果,等待工具回执或核查。把本地许可删掉以后直接把状态写成“没有执行”,会丢失恢复真正需要的事实。

5.5 人工介入也需要可追踪的责任

结果未知时可能需要人工查询后台记录。此时操作者不应简单点击一个“强制重试”按钮,让系统换键调用。更合适的界面应展示原业务身份、计划指纹、尝试历史、最后已知阶段和工具查询证据。

如果必须人工认定已经完成,应记录认定主体、依据与结果身份;如果决定发起新的补偿,也应建立新的业务操作和审批。手工处理并不意味着可以跳过身份与不变量,它只是把无法由当前自动化裁决的责任交给了明确主体。

教学内核保留 unknown 并报告需要人工核查,没有提供一个完整的人工运营控制台。读者构建控制台时,应特别验证权限、并发核查和操作审计,避免两个操作者同时根据过时页面作出冲突决定。

练习。 审批有效期十分钟,任务排队九分钟,工具调用又持续三分钟。请分别为“调用开始前有效”和“远程受理时有效”写出执行合同,指出本地检查能够证明什么,以及需要业务工具增加哪项验证。