Skip to Content
操作指南核准流程

建立自訂資料表核准流程

本指南把 require_approval、Review 模組與 staged changes 串成一條可運作的流程。關鍵觀念是:符合規則的 write 會先建立 immutable intent 與 review process;只有最終核准後,worker 才用真正 CRUD 路徑套用,當時有效的 rules、ACL 與 invariant 仍會再判斷。

1. 設定 Review 模組管理權

Company manager 先指定一位 Review manager。Target 可為 user 或 department,兩者只能擇一:

curl -X POST "$BASE_URL/private/module/review/access" \ -H "Authorization: Bearer $COMPANY_MANAGER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "user_id": "11111111-1111-4111-8111-111111111111", "role_type": "manager" }'

Review manager 可管理 groups 與 templates;一般 reviewer 不必有 module grant,pending assignment 本身就是讀取 process 與投票的授權。詳見 review.access.grant

2. 建立 reviewer group

curl -X POST "$BASE_URL/private/module/review/groups" \ -H "Authorization: Bearer $REVIEW_MANAGER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "高額訂單覆核人員", "description": "負責核准高額訂單變更", "member_ids": ["22222222-2222-4222-8222-222222222222"] }'

保存回傳的 group_id。Group members 必須是同公司 users;後續修改 group 不會偷偷改寫已建立 process 的歷史 assignments。端點見 review.groups.create

3. 建立 Review template

curl -X POST "$BASE_URL/private/module/review/templates" \ -H "Authorization: Bearer $REVIEW_MANAGER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "高額訂單覆核", "description": "單一主管關卡", "require_signature": false, "gates": [ { "name": "主管覆核", "condition": { "type": "group", "group_id": "33333333-3333-4333-8333-333333333333", "mode": "any" } } ] }'

保存 template_id。Table rule 只能引用同公司、未刪除且 gate groups 仍有效的 template。Template 更新只影響之後建立的 process。端點見 review.templates.create

Gate 的 condition 不限於 group。{"type": "user", "user_id": "<36 字元 uuid>"} 這種 leaf 直接指名一位核准人,所以「只要一個人簽」的關卡不必再為它建一個只有一人的群組;這種 leaf 也可以和 group leaf 一起掛在 andor 之下自由組合。被指名的人必須是同公司的活躍使用者,否則 template 儲存時就是 422 review users not found or inactive: [...]——在前面擋掉,而不是生出一個沒有人能決定的流程。condition 文法、各項上限,以及產生的 assignment 上 group_ids 長什麼樣,見覆核模組管理

4. 加入 require_approval rule

Table moderator 先 GET 完整 rules、加入候選規則、preview,再整份 PUT

{ "type": "require_approval", "name": "高額訂單需核准", "label": "高額訂單變更", "events": ["create", "update", "delete", "restore"], "template_id": "44444444-4444-4444-8444-444444444444", "when": [ { "column": "金額", "op": "gte", "value": 100000 } ] }

每張表最多一條 approval rule。有 pending staged changes 時不能修改或移除它;create/update trigger actions 也不能指向這張表。完整欄位見 require_approvalrules.set

5. Writer 送出變更

Writer 照常呼叫 record create/update/delete/restore。若規則命中,private API 回 409

{ "detail": { "error": "approval_required", "process_id": "55555555-5555-4555-8555-555555555555", "rule_id": "rule_a1b2c3d4", "rule_label": "高額訂單變更", "staged_change_id": "66666666-6666-4666-8666-666666666666" } }

把這個 response 當作「已送審」而非一般失敗。保存兩個 IDs、停止自動 retry,並顯示 pending 狀態。同一目標已 pending 時是 approval_pending;每位 actor 在每張表的 500 筆 pending bucket,或整張表的 50 筆 pending create bucket 滿時,會回 structured 429 staged_cap_exceeded。前端應直接用 response 的 detail.scopedetail.limit 顯示佇列已滿:scope: "actor", limit: 500 是 actor/table 額度,scope: "table", limit: 50 是 pending-create 額度;不要把數字寫死,也不要從本次操作自行猜 scope。

自動建立的 Review process 已針對審核中心在地化。Title 為 表格「<table name>」<operation label>審核,例如 表格「訂單」修改資料審核content 直接帶出渲染 held change 所需的 context,不必再逐筆查 table/scope:

{ "record_id": "33333333-3333-4333-8333-333333333333", "change_type": "update", "change_type_label": "修改資料", "table": { "id": "22222222-2222-4222-8222-222222222222", "name": "訂單", "scope": "chatroom", "scope_label": "聊天室", "scope_id": "11111111-1111-4111-8111-111111111111", "scope_name": "訂單客服" }, "requester": { "type": "user", "id": "44444444-4444-4444-8444-444444444444", "name": "王小明", "channel_label": "內部使用者", "department_id": "55555555-5555-4555-8555-555555555555", "department_name": "財務部" }, "diff": { "金額": { "old": 1000, "new": 1200 } } }

若寫入來自 external client,content.requester 改為帶 type: "external_client"idnameplatform、在地化 channel_labelchatroom_idchatroom_name。名稱 enrichment 由 tenant 錨定並 fail-soft。這個 content-level requester 描述「誰造成 custom-table change」;它不同於 process list 頂層的 requester,後者描述 generic Review submission 的人類送審者。

Moderator 可用 stagedChanges.listpending / applying / applied / apply_failed / discarded 追蹤 table-side 狀態。

一批就是一次覆核,不是每個動作一次。 當規則命中同步的 POST .../records/bulk——或與它對應的 AI 工具 custom_tables_bulk_record_actions——gate 會在動作迴圈之前先跑,並只暫存一筆 change_type: "batch_actions" 的 staged change 與一個 Review process,涵蓋整批動作。沒有任何一半會先執行,而覆核者的一個決定就放行或作廢整批。不要預期每一列都有一筆 staged change,也不要在整批 pending 時單獨重送其中的動作。

在 AI 這一側,這些結果是終局的,不是暫時性的。 佇列滿的拒絕以前是最大的陷阱:staged_cap_exceeded 是 HTTP 429,落在工具箱只解讀 400409422 的範圍之外,傳到 AI 手上變成通用的「發生內部錯誤」。於是它不斷重試一個在簽核人清空佇列前不可能成功的寫入,而使用者完全沒被告知這跟核准有關。現在 AI 拿到的是帶著上限值的政策訊息——簽核佇列已滿:這張表待審核的變更已達上限(<limit>)…——與「變更已送審」「該記錄已有變更在審」並列,成為它在同一輪內不該重試的三種結果。詳見 Agent 工具箱

6. Reviewer 作 assignment decision

Reviewer 先列出只屬於自己的 pending assignments:

curl "$BASE_URL/private/module/review/assignments/pending?skip=0&limit=50" \ -H "Authorization: Bearer $REVIEWER_TOKEN"

再對一筆 assignment 投票:

curl -X POST \ "$BASE_URL/private/module/review/assignments/77777777-7777-4777-8777-777777777777/decision" \ -H "Authorization: Bearer $REVIEWER_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "decision": "approved", "comment": "金額與附件已核對" }'

拒絕時使用 "decision": "denied",且 comment 必須非空。Template 若要求 signature,先上傳 reviewer 自己的 image blob,再帶 signature_blob_id。Ballot 已決定時重送會 409。參考 review.assignments.pendingreview.assignments.decision

7. Release 或 discard

最後一個 gate 核准後,Review process 變成 approved,內部 consumer 會 claim staged change 並以真實 CRUD 套用:

pending → applying → applied ↘ apply_failed

此時不會沿用送審當下的「已通過」假設;若 record version、unique key、ACL posture、channel scope 或 invariant 已變更,套用可進入 apply_failed。產品應同時觀察 Review process 與 staged-change status,不能只看到 approved 就立即顯示資料已落地。

任何 gate denied 或 process cancelled 時,變更不落地並進入 discard 路徑。Table moderator 也可對不再需要或需人工清理的 IDs 呼叫 stagedChanges.discard;回應是逐 ID outcome,請逐筆檢查,不要只看 top-level HTTP 200。

若 approved apply 因 queue 交付卡住,平台 operator 使用 staged-change requeue,而不是替 reviewer 再投一次票。

要在不手寫整套導覽 UI 的情況下練習這條流程,最後請開啟流程精靈

Last updated on