本卡屬 FR-148(母卡見上方連結),第 ① 棒:地基與任務卡。做完後用 DEV 手灌一個三關任務,三個帳號就能在任務卡上走完送審→核可→結案(含退回)。後續五張卡都依賴本卡。

這一棒在做什麼(白話)

建「任務關卡」需要的資料表,寫送審/核可/退回的後端服務與端點,並在任務卡上畫出「開始 → 1 → 2 → 3 → 結案」的關卡進度、跟著當下那關的人變的按鈕、以及審核歷程彈窗。設定樣板的頁面與規劃頁派人是後面的卡,本卡先用 SQL 手灌一筆測試資料驗畫面。

資料表(一支主線 migration)

全部放 compliance schema。表名用 review_step_*,中文介面叫「任務關卡」。

review_step_templates        樣板:id, uid, tenant_id, name, description, is_default bool, is_builtin bool, 審計欄位
  - 部分唯一索引:每 tenant 最多一筆 is_default=true
  - RLS:FOR ALL「超管或 app_tenant_allowed_for_session(tenant_id)」USING+WITH CHECK
review_step_template_items   樣板的關卡:id, template_id FK, seq (1 起), name, description, back_to_seqs int[]
  - RLS 跟隨樣板
job_executions               加兩欄:review_step_no int NULL(目前第幾關;NULL=零關=現狀), review_template_id int NULL(來源樣板,只記錄)
job_review_steps             任務的關卡(套樣板時複製):id, job_execution_id FK, seq, name, description, back_to_seqs int[]
job_review_step_assignees    第 2 關以後的人:job_execution_id, seq (≥2), user_id, org_unit_id NULL;PK (job_execution_id, seq, user_id, org_unit_id)
job_review_step_logs         歷程:id, job_execution_id, seq, action(submit/approve/reject/reopen), to_seq NULL, user_id, note, created_at timestamptz
  - 三張 job_* 跟隨 job_executions 所屬專案的 RLS

推進/退回服務

新開 app/flow_control/service/review_step_service.py(不要塞進既有 service),四個方法:

submit(job_uid)                 第 1 關的人;關卡 1→2、寫歷程 submit、通知第 2 關的人
approve(job_uid, note)          當下那關的人;非最後一關:+1、寫歷程、通知下一關;
                                最後一關:寫歷程後呼叫 workflow_execution_service.complete_job()
reject(job_uid, to_seq, note)   當下那關的人;note 必填;to_seq 必須在該關 back_to_seqs 內;
                                關卡改 to_seq、寫歷程、通知 to_seq 那關的人
reset_to_first(job_execution)   內部用(卡⑤ 的重開 hook 會呼叫);caller 在 @transaction 內

端點

POST /grc/jobs/<job_uid>/review-steps/submit
POST /grc/jobs/<job_uid>/review-steps/approve   body {note}
POST /grc/jobs/<job_uid>/review-steps/reject    body {to_seq, note}
GET  /grc/jobs/<job_uid>/review-steps           → {template_name, current_seq,
       steps:[{seq, name, description, back_to_seqs, assignees:[{user_uid, name, dept_name}]}],
       logs:[{created_at, seq, step_name, action, to_seq, user_name, note}]}