Skip to Content
操作指南覆核模組管理

管理覆核群組、範本與投遞

情境:公司要由財務主管覆核高金額訂單,並在流程結束後可靠地通知 ERP。

前置條件

公司 manager 才能授予 Review module 存取;manager 角色可管理群組與範本,creator 角色可建立流程。被指派的公司成員可處理自己的 assignment。以下所有 private 請求使用使用者 access token,而且 Review module 是 company-wide,路徑不帶 custom-table scope。

這是管理端流程;要讓資料表的 create/update/delete 自動進入覆核,請接著使用核准流程設定 require_approval 規則與範本。

步驟

1. 授予 Review manager

company manager 可透過 review.access.grant 對一位使用者授予 manager。也可改用 department_id 授權整個部門,但同一個 grant 只能提供 user_iddepartment_id 其中一個。

POST /private/module/review/access Content-Type: application/json { "user_id": "22222222-2222-4222-8222-222222222222", "role_type": "manager" }

保留回應的 access.id;撤權時 review.access.revoke 使用的是這個 ID,不是 user ID。

2. 建立 reviewer group

以 Review manager 呼叫 review.groups.create 建立「訂單覆核人員」。member_ids 必須都是同公司使用者;群組可集中管理未來流程的 reviewer 集合。

POST /private/module/review/groups Content-Type: application/json { "name": "訂單覆核人員", "description": "負責審核高金額訂單", "member_ids": [ "33333333-3333-4333-8333-333333333333" ] }

以下假設 group ID 是 44444444-4444-4444-8444-444444444444。群組成員更新會影響未來 assignment;已建立流程保留建立當下的 gate/assignment snapshot。

群組成員資格刻意寬鬆:這個端點只擋已刪除的使用者。未驗證或已到期的帳號仍可被加入,也會出現在 member_ids 中。「是群組成員」與「算得上 reviewer」是兩件事——見下方誰才算 reviewer

3. 建立可重用 template

review.templates.create 中,gate 依陣列順序執行。範例只有一關,群組中任一人核准即可通過;若要每個符合者都同意,使用適合的 condition/mode。

POST /private/module/review/templates Content-Type: application/json { "name": "高金額訂單覆核", "description": "財務主管單關覆核", "require_signature": false, "gates": [ { "name": "主管覆核", "condition": { "type": "group", "group_id": "44444444-4444-4444-8444-444444444444", "mode": "any" } } ] }

以下假設 template ID 是 55555555-5555-4555-8555-555555555555。範本更新只影響之後建立的流程;既有流程不會被悄悄改寫。資料表 require_approval 規則也可引用這個 template。

直接指名一個人

condition tree 接受三種節點:組合節點 {"type": "and"|"or", "children": [...]}(2–20 個 children)、上面那種 group leaf,以及 user leaf {"type": "user", "user_id": "<36 字元 uuid>"}。指定單一核准人不必再先建一個只有一人的群組。user leaf 可以裸著當整個 condition,也可以掛在任何 and/or 之下、與 group leaf 自由混用;template 與 inline process gate 都支援,而且 template 會原樣帶進 process snapshot。

{ "name": "負責人簽核", "condition": { "type": "or", "children": [ { "type": "group", "group_id": "44444444-4444-4444-8444-444444444444", "mode": "any" }, { "type": "user", "user_id": "33333333-3333-4333-8333-333333333333" } ] } }

該使用者會成為單人選舉人團,拿到剛好一筆 assignment。只有會回傳 group_ids 的地方——建立與詳情回應的 gates[].assignments[]——純直接 reviewer 的 group_ids 會是 [];引擎內部用來標記虛擬選舉人團的鍵在序列化前就被剝掉,永遠不會外流給客戶端。列表回應、pending queue、audit changes 與 webhook body 根本沒有 group_ids 這個欄位,不要在那些地方去找它。同時被 group leaf 與 user leaf 指名的人仍然只有一筆 assignment,其 group_ids 只列出真實的 group id,而他那一票同時算給他所代表的每一個 leaf。

condition 物件是嚴格的

三種節點都會在任何業務驗證之前,以 422 拒絕未知的鍵。允許的鍵集合剛好是 {type, group_id, mode}{type, user_id}{type, children}。user leaf 沒有 mode,所以 {"type": "user", "user_id": "…", "mode": "all"}422——過去這會被接受、多餘的鍵被默默丟掉,讓呼叫端以為自己送出的限制生效了,其實沒有。重新提交任何已儲存或程式產生的 condition JSON 前,先把多餘的鍵清掉。

每個 gate 的上限是:最多 20 個 leaf(group leaf 與 user leaf 合併計算)、深度最多 5、每個 and/or 節點最多 20 個 children,以及 gate 聯集最多 500 位 reviewer。超過 leaf 上限是 Pydantic 驗證 422,訊息落在 detail[].msg,內容是 Value error, condition has 21 leaves, exceeds max 20——不是單一字串的 detail。超過 reviewer 聯集上限則確實是單一字串:gate 1 would assign 501 reviewers, exceeds max 500

誰才算 reviewer

Reviewer 資格現在與認證邊界完全一致。同一個判定式同時管建立時驗證與 gate 啟用時的 snapshot,兩者不可能各說各話。一位使用者要四個條件全中才算數:未刪除、未停用、verified 為 true、未到期——role -2(部門虛擬管理員)豁免到期檢查,因為 get_current_user 也豁免它。

建立時由兩個 422 強制執行,review.templates.createreview.templates.updatereview.processes.create 都適用:

review groups have no active members: ['44444444-4444-4444-8444-444444444444'] review users not found or inactive: ['33333333-3333-4333-8333-333333333333']

第一則現在也會在「群組成員全部未驗證或全部已到期」時觸發。第二則涵蓋 user_id 不存在、已軟刪、已停用、未驗證、已到期或屬於其他公司;最多只列出前五個。在此之前,這種 template 會以 200 通過驗證、第一關照樣啟用,並替永遠無法認證的人開出 assignment——流程卡在 in_review 且沒有任何可達的結局,requester 唯一的出路是取消它。混合群組則只是把不合格成員排除在 snapshot 之外:他們拿不到 assignment,也收不到 review_assigned 通知,而 all 模式的法定人數縮到真正能動作的那些人。

4. 建立流程並設定 terminal webhook

review.processes.create 沒有 draft 狀態:建立成功就成為 in_review、啟用第一個 gate 並產生通知。template_id 與 inline gates 二選一。Webhook 的 events 只列出需要通知的 terminal states。

POST /private/module/review/processes Content-Type: application/json { "title": "訂單 ORD-1042 變更覆核", "description": "確認折扣與付款條件", "content": { "table_id": "66666666-6666-4666-8666-666666666666", "operation": "update", "record_id": "77777777-7777-4777-8777-777777777777" }, "blob_ids": [], "template_id": "55555555-5555-4555-8555-555555555555", "webhook": { "url": "https://example.com/review-events", "method": "POST", "headers": { "X-Review-Source": "teamsync" }, "body": { "process_id": "$review.id", "status": "$review.status", "content": "$review.content" }, "events": ["approved", "rejected", "cancelled"] } }

保留 process.id。Webhook 是投遞通知,不是流程真相來源;ERP 收到後仍應以 process ID 做 idempotent 處理。

上例是 generic manual Review process。由 custom-table require_approval gate 建立的 process,其 title 為 表格「<name>」<operation>審核,而 content 會補上 change_type_labeltable: {id, name, scope, scope_label, scope_id, scope_name},以及 content-level requester。內部 requester 含 user name 與 department;external client 含 platform、在地化 channel label 與 chatroom id/name。這個 content-level requester 是 change provenance,並不是 process list 上的 ReviewProcessSummary.requester。完整代表 payload 見審批流程指南

5. 讓 reviewer 從自己的 pending queue 決策

review.assignments.pending 只會讓每位 reviewer 看見自己的 active assignments。

GET /private/module/review/assignments/pending?skip=0&limit=50

將回應中的 assignment_id 交給 review.assignments.decision 決策。拒絕時需要非空 comment;require_signature: true 時要先上傳 signature blob 並提供其 ID。

POST /private/module/review/assignments/88888888-8888-4888-8888-888888888888/decision Content-Type: application/json { "decision": "approved", "comment": "金額與付款條件正確" }

gate 通過後會啟用下一關;最後一關通過時 process 變成 approved。任何終止性拒絕會變成 rejected

Decision 帶簽名時,process detail 會在該 assignment 同時回傳 signature_blob_id 與內嵌 signature_blob。內嵌 BlobInfoidcreated_aturlcontent_typefilenametagsthumbnail,前端不必再查一次 blob 就能渲染簽名。Pending、無需簽名或 blob row 已不存在時會是 null;把 ID 當成 durable reference,並對內嵌物件做 null check。

一張選票最後會停在 pendingapproveddeniedobsolete 其中之一。obsolete 的意思是從未投票——gate 或流程在沒有這位 reviewer 的情況下就結束了,於是他的列被作廢。真正投出去的票一律保留自己的決定,包含那張讓 gate 定案的票。這件事以前並不成立:讓 gate 通過或失敗的那張核准/拒絕,會被寫回成 obsolete,於是 review.processes.getgates[].assignments[] 中把決策者顯示為 status: "obsolete"——commentdecided_atsignature_blob_id 都還在,唯獨決定本身被抹掉——而 audit log 仍記錄著 decision_approveddecision_denied。流程詳情與稽核軌跡對「是誰結掉這關」說法不一致。所有讓 gate 定案的票都中招:單人 gate 丟失每一次核准、any 模式丟失核准者、all 模式丟失最後一位核准者,讓 gate 失敗的拒絕則丟失拒絕者。取消走同一條規則——只作廢仍為 pending 的票,已投出的維持原狀。

當一關已經沒有人可以審

資格是在每一關啟用時對當下成員重新判定,不是在流程建立時判定一次。若某個決定性 group leaf 的成員(或唯一的直接 reviewer)在這期間全部變成未驗證、已到期、已停用或已刪除,該 leaf 的選舉人團為空,這一關會在啟用時直接失敗,而不是無限期掛著;流程以 rejected 結束,completed_at 有值、current_gate_ordernull。Audit log 會有一列 gate_failed,其 changes.reasongate_unsatisfiable。第二關以後的 gate 走的正是這條路,因為它們的選舉人團要等前一關通過才會 snapshot。以前這些人照樣被 snapshot,流程就永遠停在 in_review

or 節點只要還有一個可滿足的兄弟節點,行為就不同:這一關照常啟用,無法審核的那個人單純拿不到 assignment。

6. 追蹤、稽核或取消流程

requester、參與者與 manager 依各自可見性透過 review.processes.get 讀取 process;管理調查再沿著 review.processes.audit 檢查稽核 log。

review.processes.list 建立相對於呼叫者的 inbox tabs:

GET /private/module/review/processes?role=requested&status=in_review&skip=0&limit=50 GET /private/module/review/processes?role=assigned&status=in_review&skip=0&limit=50

role 只接受 requestedassigned,即使 caller 是 manager 也仍然相對於他本人。省略時回完整可見集合:manager 看全公司,其他使用者看「自己送出」與「目前或曾持有 ballot」的聯集。每筆 summary 會各自回 requested_by_meassigned_to_me,兩者可能同時為 true。只有 requester_id 仍能在呼叫者公司內解析成使用者時,才會填入 requester: {id, name, department_id, department_name};否則 requester 為 null,包括 client/AI 送審,以及送審人已無法在該 tenant 解析的歷史真人流程。若當初有記錄,client/AI 的來源身分仍保留在 process content。Tabs、badges 與送審人標籤應直接用這些欄位,不要為列表每列再打一次 detail request。

GET /private/module/review/processes/99999999-9999-4999-8999-999999999999
GET /private/module/review/processes/99999999-9999-4999-8999-999999999999/audit?skip=0&limit=50

仍為 in_review 時,requester 或 manager 可透過 review.processes.cancel 取消;取消也是 terminal state。

POST /private/module/review/processes/99999999-9999-4999-8999-999999999999/cancel

7. 檢查 webhook deliveries 並安全 retry

review.processes.deliveries 只會在設定 webhook 且流程進入 terminal state 後回傳 delivery;requester 或 manager 可列出它們。

GET /private/module/review/processes/99999999-9999-4999-8999-999999999999/deliveries?skip=0&limit=50

failed,或 pendingsending 超過 10 分鐘的 delivery 呼叫 review.deliveries.retry;伺服器會以 atomic claim 防止兩位管理者同時重送。

POST /private/module/review/deliveries/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/retry

retry 回應為 pending 只代表重新排入;再次列出 deliveries,直到 delivered 或出現可診斷的 failed

你會看到什麼

manager access 建立後可維護 group/template;流程建立後立即有 active assignment;決策會推進 gate 並留下 audit;terminal state 會建立 delivery,失敗投遞可由同一個 delivery ID 安全重試。

常見錯誤

請直接查看存取授權錯誤表群組建立錯誤表範本建立錯誤表流程建立錯誤表決策錯誤表投遞重試錯誤表

試試看

API Playground 用測試公司依序執行 review.access.grantreview.groups.createreview.templates.createreview.processes.createreview.assignments.pendingreview.assignments.decision 與 delivery 查詢;正式 webhook 前先使用可觀察的測試接收端。

Last updated on