尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

一个审批按钮远远不够:领取、转派、会签、退回与规则版本

一个审批按钮远远不够:领取、转派、会签、退回与规则版本 一个审批按钮远远不够领取、转派、会签、退回与规则版本《企业级 Workflow 实战从审批流到 AI Agent》· 第 09 篇 / 共 24 篇贯穿项目星河设备 AcmeFlow 客户开通中心。本篇交付可领取与转派的人工作业、两人会签、权限撤销、资料退回后的新任务、规则版本冻结以及一个可运行的 SQLite 实验。实验边界人员身份由本地样例表模拟没有接企业身份提供方或生产任务界面第 03 篇 FastAPI PostgreSQL 应用的接入落点在文末说明。运营主管打开一条客户开通申请看到页面上写着“审核通过”。她问了三个问题“谁通过的他当时有没有权限他看的是客户哪一版资料”开发者只找到了approvedtrue和一个更新时间。财务随后发现昨天运营看过的附件今天已经被销售替换后台却仍把旧审批结论当成有效。另一边两位审核员先后点“通过”却没人能说清楚系统要求的是一人通过、两人会签还是先后两级审批。这不是审批页面缺少几个按钮的问题。人工任务需要自己的生命周期、处理身份、对象版本和完成规则。把人放进工作流意味着系统要知道“现在轮到谁处理什么、他凭什么可以处理、处理的是哪一份资料、该决定什么时候才算完成”。从第 08 篇开始我们已经能控制一条审核任务何时失效本篇继续解决任务在有效期内由谁完成以及多人判断怎样汇合。图 1两个审核席位属于同一份申请资料。完成一个席位只能说明一位审核员作了决定按本例 v1 规则两位不同审核员通过后才推进申请。一、从“某人点过通过”到“某项任务被正确完成”一个按钮通常隐藏了多个判断。操作人是否登录他是否属于候选审核组任务有没有被别人领取领取后是否被转派他的权限此刻是否仍有效任务绑定的材料版本与当前申请是否一致这次操作属于第几个会签席位如果前一位要求补充资料其他席位还应不应该继续这些问题不应该分散到前端按钮是否可点击、后端路由参数和数据库触发器里由三处给出相互矛盾的答案。本例把命令集中到待办处理路径。页面展示待办时可以先筛选候选任务但真正领取、转派、完成时必须重新检查权限和任务状态。用户停留在页面十分钟这期间权限可能被撤销材料可能改版另一位审核员可能已经领取。页面上的旧快照不能替代提交时的判断。我们先给人工作业一个清楚的词汇。候选人是当前有资格领取任务的人不等于已经负责领取人是目前持有任务的人处理决定是领取人提交的业务结果会签席位是规则要求的一个独立判断位置。一个申请可以有多个任务一项任务也可能经过领取、释放、转派和完成多次状态变化。申请的整体状态与任务的状态不同两个任务中一项已完成时申请仍可能停在REVIEWING。Camunda 的任务授权文档也将读取、更新、领取和完成区分为不同操作权限并允许结合任务属性上的候选用户、候选组与受理人控制访问。本篇不是复刻 Camunda 产品而是借此说明真实的人工作业不能只靠一个“可以看到列表”的布尔值判断全部操作。参考Camunda User Task Authorization二、先画出任务生命周期再设计 API本例任务从OPEN开始。具有运营审核权限且不是申请人的用户可以尝试领取领取成功后进入CLAIMED保存claimed_by。当前领取人可以转派给另一个有资格的人转派不会重建任务也不抹掉先前领取者而是在审计里记下责任转移。当前领取人作出APPROVE或CHANGES决定以后任务进入COMPLETED。如果其他席位仍在等待申请保持REVIEWING如果所有席位按本版规则通过申请成为APPROVED。若某位审核员要求补充材料申请进入NEEDS_INFO其他未完成任务被取消本轮审核结束。图 2领取和完成是不同命令。被撤销审核资格的人即使曾领取任务也不能在后续完成它。这里没有使用“退回以后把任务重新设成 OPEN”的捷径。退回意味着当前资料需要改变而审批对象即将成为另一个版本。若重用同一任务数据库很难说明decision针对旧资料还是新资料。本例保留旧任务及其审计申请人补交资料时增加material_version并创建新一轮任务。这样页面可以显示完整历史v1 因材料缺失退回v2 等待重新审核。它也让审计人员能够解释一次批准到底依据哪份材料。同理会签规则不能只写成“谁最后点按钮谁改申请状态”。每个席位先形成独立决定汇合时在事务中读取当前版本全部席位。如果所需席位都已由不同的合格人员完成且决定为通过才推进申请。若任一席位退回应停止这一轮并明确下一步由谁补充。后续如果增加“多数通过”“财务必须通过”“法务有否决权”等规则可以扩展为不同任务类型与决策表但不能让一个没有说明的计数器代替业务政策。三、任务、权限、规则、审计分别存什么第 03 篇的主库已经有申请、流程实例、审核任务和迁移历史。本篇在同一语义上增加了四类信息申请保存当前材料版本与绑定的规则版本任务保存席位、对象版本、状态、领取人和决定权限目录说明谁现在可以审核审计保存每次领取、转派、撤权、退回和汇合发生的上下文。教学脚本把这些放在独立 SQLite 中是为了可离线复现不表示已经把新字段迁进 PostgreSQL 主线。实验中形如application_id:v2:review-1的任务标识是便于观察的复合字符串接入主库时继续使用第 03 篇的 UUIDtask_id另外用申请 ID、资料版本与席位做业务唯一键。图 3application_id贯穿四类记录material_version和rule_version决定一个审批结果能否用于当前申请。为什么权限目录不直接复制到任务里候选组可以写入任务但“某用户目前是否属于该组”会随组织调动而变化。若系统只在创建任务时复制人员名单昨天被撤权的用户今天仍可能拿着旧任务完成审批。反过来如果完全不保存任务当时采用的候选规则也无法说明为什么某人曾经可以领取。合适的做法是同时保留当时的规则版本与当前权限事实前者用于解释策略后者用于执行实时授权。审计也不能只靠应用日志。日志可能按保留期轮转也可能只记录一次 HTTP 200不记录审批看的是资料 v1 还是 v2。至少需要保存申请 ID、任务 ID、资料版本、规则版本、动作、行为人和结果。时间戳在生产系统里同样重要本篇的简化脚本为了聚焦逻辑只保存动作顺序与细节不宣称已经提供满足监管要求的不可篡改审计。接入第 03 篇主线时应沿用transition_history的occurred_at和actor_id新增任务事件记录或在业务历史中保存任务 ID 与版本。流程状态也不应直接等同于审核票数。REVIEWING表示这份申请还在本轮审核一张任务的COMPLETED只说明一个席位完成。APPROVED是在满足当前规则时形成的申请结论。后面的签署、到账、ERP、权益仍是另一段流程第 01 篇已经建立的READY ≠ ACTIVE约束并未因为两人会签而消失。把人工作业与申请状态分开才能继续说明“审核通过但还未到账”的正常等待。四、会签必须防止同一人占两个席位本篇示例规定 v1 规则需要两位不同运营审核员v2 规则用于新申请需要三位。真实业务可能需要不同角色而不只是不同人员。本篇先用最小规则展示问题如果创建两个任务却允许 bob 领取并批准两次系统虽然看见“两张通过票”却没有得到“两个人的独立判断”。因此领取和完成时都要检查同一申请、同一资料版本下是否已有该用户的完成记录。图 4申请 A 的 v1 两席已通过申请 B 即使补交到资料 v2仍使用创建时绑定的规则 v1新申请 C 才采用三席规则 v2。规则版本是长期运行流程的基本功。运营今天宣布以后新客户需要三人会签并不自动决定昨天已经交给两人审核的申请怎么处理。可以把旧实例留在旧规则下完成也可以经过明确迁移把在途申请改为新规则并补建任务但这需要业务批准和数据核对。我们在教学实例里选择最清楚的方案创建申请时绑定rule_version该申请的材料更新只改变资料版本不暗中改变会签人数新申请可使用新版本。这样处理不是所有企业的默认答案而是一个可执行、可审计的选择。注意“规则版本”与“资料版本”独立。资料 v2 可能只是补了一份营业执照仍遵守旧会签政策规则 v2 可能要求多一位审核人却不改变客户已经提交的材料。若把两者混成一个version字段开发者迟早会遇到“版本 2 到底代表什么”的争议。本篇数据模型将它们明确分开并把两者都写到任务上完成时验证当前申请仍匹配。Camunda 的多实例文档说明一个活动可以以并行或顺序形式产生多个实例完成条件也需要明确。课程中的“双人会签”正是一个业务完成条件而不是画两条平行线就自动得到的效果将来第 20 篇做 BPMN 对照实验时仍要检查变量汇合与未完成实例的处理。参考Camunda Multi-instance五、撤销权限与转派为什么都要重新核对一个常见漏洞发生在任务列表与完成动作之间。alice 早上是运营组成员领取了任务下午调离岗位管理员撤销了她的审核资格。晚上她在旧浏览器页面点击“通过”。如果后端只检查claimed_by alice这个操作就会成功。我们需要同时检查任务仍归她、任务未完成、资料版本仍有效、她此刻仍具有审核资格。撤权操作还应释放她持有的未完成任务使其他人能重新领取。图 5本篇实验通过先领取、后撤权、再完成的顺序证明“曾经有权限”不能代替提交时的授权。转派也不是简单覆盖一个人名。bob 把任务转给 dave 以后bob 再提交原页面应被拒绝dave 要在当前规则下仍有资格且不能已经占用同一会签中的另一个已完成席位。审计里要同时留下操作者和接收者。若组织要求只有主管能转派需再加入“转派权限”判断本篇教学规则采用当前领取人可以转给另一位合格审核员规则简单但动作依然明确。实验用一张memberships表模拟实时权限。生产环境若从 SSO 或目录服务获取角色还要决定撤权同步延迟、缓存有效期、失败时是否拒绝操作、以及身份服务不可用时如何处理。那些是身份系统与工作流系统的共同约束不能只在 UI 上隐藏按钮。这里使用字符串bob、alice作为样例身份它们不是可用于生产的身份认证机制代码没有会话、令牌或外部目录验证。还有职责分离本例销售提交人不能审核自己发起的申请。这个限制在candidate()中执行不依赖前端是否显示任务。它同样需要基于可信身份如果客户端可以随意传user_id字符串所谓职责分离只是一段可绕过的演示规则。未来接入第 03 篇 FastAPI 时处理人要从认证上下文取得而不是从请求体里无条件信任。六、退回材料后旧审批为什么必须失效客户开通申请在审核中被指出附件不清晰。销售重新上传资料运营决定的对象就变了。即使公司名称、套餐和款项不变旧资料上的审批结论也不能自动覆盖新资料。系统应明确本轮是否允许补充补充后生成哪类新任务已完成的旧任务如何展示。我们选择保留所有旧记录但禁止它们推进新的资料版本。图 6退回结束 v1 审核轮次补充资料生成 v2 的新席位。规则版本可以保持 v1说明业务政策和材料对象是两条不同的版本线。实验里dave 在 v1 席位提交CHANGES申请进入NEEDS_INFO其他未完成席位设为CANCELED。销售提交补充资料后material_version从 1 增加到 2程序根据申请绑定的rule_version1再建立两个新任务。旧任务 ID 包含v1提交旧任务会因状态或版本不符被拒绝。已经完成的退回决定仍保留在审计里不能被删除否则后续无法解释为什么又多了一轮审批。本教学例子没有处理“材料变了但不影响审批”的细粒度差异。真实项目可能把材料分成身份、合同、资质、设备清单等部分某一项变更只需要指定角色重审也可能要求整轮重新开始。关键原则是审批结果应绑定它所审核的对象变更以后依据规则判定是否仍可用。不宜先假定所有变更都失效也不宜默认所有变更都有效业务规则需要对字段和证据的作用范围做出说明。签署与到账事实也有类似但不相同的关系。合同签署往往绑定合同版本只改证明材料是否需要重签要由合同政策决定。到账事实通常关联付款和业务申请不能因为资料重新上传就凭空消失。第 01 篇的模拟已经区分这些事实。本篇围绕审批对象版本不把财务和合同规则强塞进任务表。七、运行代码看四组具名场景怎样落库在本篇目录执行python code/demo.py不需要安装第三方依赖。脚本创建临时 SQLite 文件、写入申请与任务、执行一组命令并用断言检查结果。执行结束临时目录自动清理。代码与预期输出分别见 code/demo.py 和 code/expected-output.txt。claim(db,a1,alice)revoke(db,alice)denied(lambda:complete(db,a1,alice,APPROVE))claim(db,a1,bob)claim(db,a2,carol)assertcomplete(db,a1,bob,APPROVE)REVIEWINGassertcomplete(db,a2,carol,APPROVE)APPROVED09-A 展示撤权和会签。09-B 展示转派、退回、补交资料以及旧任务失效。09-C 使用两个独立 SQLite 连接和两个线程同时领取同一席位只应有一个成功另一个得到冲突。09-D 再打开数据库文件证明待办、规则绑定与审计仍在。这些场景覆盖了最容易在演示时被略过的状态变化代码没有复杂框架读者可以直接修改样例人名和规则人数继续试验。09-C 需要说明实验边界SQLite 的BEGIN IMMEDIATE把写事务串行化因此在单个本地数据库文件里能给这两次领取确定结果。它没有验证 PostgreSQL 主库里的隔离级别、Web 请求重试、分布式身份目录也不能证明“所有并发审批都安全”。接入主线时要用主库的唯一约束、条件更新或行锁和冲突响应重新完成同样验收。SQLite 官方事务文档明确说明BEGIN IMMEDIATE会在已有写事务时受到竞争读者可以据此理解本地实验的约束。参考SQLite Transaction建议自行再做四个改动实验。第一把申请人sales-1加入审核组后尝试领取自己的任务仍应因职责分离被拒绝。第二bob 完成第一席后再尝试占用第二席应被拒绝即使他仍是运营组成员。第三把rule_version2的新申请设为三席其中两人通过以后申请仍应保持REVIEWING。第四在补交 v2 后拿着 v1 的任务 ID 提交旧结果不得影响 v2。每一个预期都能由数据状态和审计记录验证不只是看终端有没有抛异常。八、如何接续第 03 篇主应用第 03 篇的applications已有application_id、tenant_id、material_versionworkflow_instances有state、rule_version、revisiontasks有 UUID 任务 ID、申请版本与完成信息。本篇接续它时应在同一套 ID 和租户范围内演化而不是在前端再造一套“审批表”。具体需要增加候选组或候选资格、claimed_by、任务状态CLAIMED和CANCELED、会签slot、决定与完成时间、规则版本绑定以及任务事件审计。可对(instance_id, application_version, slot)加业务唯一约束不应把实验中方便阅读的复合字符串强行塞进 UUID 主键。第 03 篇已经完成的简单运营审核任务可以视为单席规则 v0迁移旧数据时要给它们清楚的版本解释。下一步的 API 可以是领取任务、转派任务、完成任务、申请补交材料以及管理员撤销审核资格。路由只是入口真正的检查必须在事务中的命令处理函数里。任务完成时先检查租户与可信身份再检查申请状态、资料版本、任务状态和当前权限满足条件后写任务决定汇总同一轮会签结果并写迁移历史。对于任务领取多个请求同抢一个席位要返回明确冲突而不是让后提交的人覆盖领取人。身份系统与待办查询也需要关联。用户在列表中看不到不属于自己的任务是界面体验与保密要求用户即使知道任务 ID 也不能调用完成接口是服务端授权要求。两者缺一不可。若采用缓存的组成员信息需要定义撤权生效时间与缓存失效路径。对高风险审批提交时重新验证权限通常比只相信进入页面时的一次检查更合适。审计应能说明实际使用了哪个身份和哪个权限判断结果。规则版本最好是不可变的定义。发布 v2 时创建新规则而不是修改数据库里 v1 的“需要两人”为三人否则旧申请会在不知情下改变验收条件。需要将旧实例迁到新规则时应提供显式迁移命令说明旧票怎样处理、是否补建任务、是否要求重新审批并把操作者与理由写入历史。第 12 篇会专门讨论长期运行流程的版本与升级本篇先给人工作业一个不会被静默覆盖的规则绑定。领取、指定与转派不该混用一个赋值接口候选任务由有资格的审核员主动领取适合团队共享待办池指定任务由主管或规则直接分派给具体人员转派是已有责任人或授权管理者把任务交给另一人。这三种操作最终都可能改变claimed_by但业务含义和授权主体不同。如果系统只暴露“修改 assignee”接口任何拿到任务 ID 的调用者都可能把任务指派给自己甚至把它转给没有资格的人。接入主应用时命令名应反映动作领取检查候选资格指定检查分派权限转派检查当前持有与接收人资格。任务领取还要处理占用时间。有的人点了领取却请假任务可能长时间停在个人名下。业务可以允许主动释放、主管收回、领取租约到期或超时升级。每一种都有不同的审计释放表示本人归还收回表示授权管理动作租约到期表示系统政策。不能用“隔一段时间把所有 CLAIMED 改回 OPEN”替代这些政策否则审核员在提交表单时可能突然失去任务却不知道为什么。第 08 篇的期限技术能提供触发时刻但决定如何重新分配仍属于人工任务规则。本篇 SQLite 脚本故意只实现领取、转派、权限撤销和完成让每一种决定容易看见。生产待办中心还应考虑代理审批与请假授权、岗位变更、跨部门任务、组织层级、服务账号误用和管理员紧急接管。这些能力不用一开始全建但项目至少要写出“谁有权改变任务责任”的清单。待办系统若只会把任务发给人却没有办法解释任务为什么在某人手上迟早会回到群聊催办。会签的否定结果要写成业务政策很多教程只演示两个人都点通过。真实流程更常遇到一人通过、一人要求补充材料或者一人长时间没有响应。本例采用“任一席位要求补充结束本轮并取消其余未完任务”的政策理由是材料不完整时继续收集赞成票意义不大。另一些场景可能要求所有人完成后再汇总意见或由主管在意见冲突时仲裁。技术上都能实现重要的是不要让 SQL 查询行数决定了业务政策。如果一位审核员已经通过另一位退回补交 v2 后第一位是否需要重新看本例要求重新审核因为对象版本改变旧通过仍保留在历史但不计入新轮次。业务也可能允许某些与变更无关的审批继续有效例如财务只核对付款而材料只更换设备照片。要采用这种细粒度复用系统必须保存每个决定覆盖的字段或证据范围以及变化是否触及该范围。没有这些信息就不能仅凭“第一位已经同意过”自动复用票数。若会签期间审核人离职或权限撤销已完成的决定是否追溯失效也需要业务明确。本篇规则只阻止撤权以后再完成任务并释放未完成任务已经合法提交的历史决定不因以后调岗自动删除。若发现当时身份被冒用或者审批资格在决定发生前就应撤销则应走异常复核或显式作废流程。审计记录必须保留原决定与后续纠正否则团队无法回答为什么同一申请曾被标成通过又重新审核。一个容易忽视的审计问题是“被拒绝的命令”是否需要记录。权限被撤销后 alice 再尝试完成任务申请状态当然不能改变但高风险系统通常仍要在安全日志中记录这次尝试的身份、目标任务、失败原因与请求来源供事后排查。业务审计记录有效的审批决定安全审计记录异常访问与拒绝两者服务的目的不同。本篇脚本为了保持最小只把有效任务动作写入audit用抛错和测试断言验证拒绝正式接入服务时应补充拒绝事件记录并注意不要把完整敏感材料写进日志。还要处理“领取时有权限完成事务中权限刚被撤销”的竞争。若撤权与完成请求落在同一数据库事务边界内二者应按明确顺序生效若权限来自外部目录主库事务无法自动锁住远端目录的状态。这时要定义授权信息的生效口径例如短期有效的权限版本、提交时同步查询或管理员撤权后让已有任务强制失效。任何口径都需要与安全团队一起验证不能宣称一次数据库锁便解决了跨服务权限竞争。本篇 SQLite 样例只有本地成员表因此它演示的是本地顺序不是完整企业身份治理。九、验收与 Workflow Thinking本篇的验收不是“页面上有领取、转派按钮”而是四种可观察行为撤权后旧领取人完成请求被拒绝且任务可再分配同一人不能形成两位审核员的会签结果资料 v1 的任务不能批准 v2旧申请继续遵守旧规则新申请按新规则生成任务。运行输出已经覆盖这些路径正式集成时还要补充身份提供方、租户边界、HTTP 冲突码和数据库迁移验证。Workflow Thinking为什么会签结果不能只用approve_count 1因为一个数字不知道每一票是谁、针对哪一份材料、依据哪个规则也不知道某位审核员撤权或退回后哪些票仍有效。多个任务的决定与版本记录看上去比计数器多但它们保留了业务意义。只有先保存这些事实系统才可能在政策变化或争议出现时重新解释结论。下一篇把已经审核、签署和到账的申请送往 ERP 与服务平台。那里不存在一个能同时提交两个系统的本地事务ERP 建档成功而权益开通失败时任务不再只是“请人看一眼”而需要选择继续、补偿或交给授权人员对账。
返回列表