iT邦幫忙

2026 iThome 鐵人賽

DAY 11
1
Software Development

30天打造一套企業PLM系列 第 11

Day 11:Trigger 規則引擎(上)——可設定式自動化

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20260828/201612907aYXvUoUQs.jpg

系列:30 天打造企業級 PLM|面向:後端|素材:Trigger 規則引擎模組

問題場景

「送簽時自動帶入部門主管」「放行時自動把狀態欄改成已發行」「金額欄有值時自動加會財務」,每個部門要的自動化都不一樣,而且永遠會長出新的。一條一條寫成程式,系統很快變成 if-else 沼澤。Mini-PLM 的解法是把規則變成資料:觸發點、條件、動作全部可設定,交給管理員自己組,程式這邊只提供一顆引擎。

Trigger 設定頁實機畫面:

https://ithelp.ithome.com.tw/upload/images/20260828/20161290udT8wMmbCh.png

商業邏輯設計

  • 自動化需求是長尾:核心流程收斂(Day 9),流程上的小自動化卻一直發散,這正是「規則作為資料」最划算的場景
  • 治理要先想清楚。誰能設 trigger?admin only。規則衝突誰優先?priority 欄位,語意由業務定。上線前怎麼驗?Dry-run,不真的執行,只回報「會發生什麼」
  • 可解釋性是業務需求:出了事要能回答「這個值是哪條規則改的」。TriggerExecutionLog 每次執行都留紀錄,定位是稽核工具,debug 只是順便

技術選型與取捨:規則引擎的三件組

架構演進:從 Agile Event Framework / PX 腳本到宣告式規則引擎

在以前 Oracle Agile PLM 中,所有自訂自動化邏輯都依賴 Event FrameworkProcess Extension (Java PX / Groovy PX)

在舊架構下維護自動化,痛點極多:

  1. 開發門檻高:哪怕只是「放行時自動帶入當天日期」,都要寫一支 Java 類別或 Groovy 腳本,手動綁定 Event Action 與 Event Handler,再打包上傳到 WebLogic。
  2. 缺乏防禦與隔離:舊架構缺乏宣告式的失敗策略。一旦 Groovy 腳本寫錯或拋出未捕捉的 Exception,整張表單的流程直接卡死崩潰。
  3. 黑箱排查:沒有結構化的執行紀錄,業務問「為什麼這張單沒自動加人」,工程師只能去爬 WebLogic 巨大雜亂的文字 log。

Mini-PLM 採用宣告式 Trigger 規則引擎,將自動化全部「資料化」:管理員在 UI 就能配置觸發點、條件組與執行動作,並具備沙盒般的失敗策略(WARN_CONTINUE / BLOCK)與完整的 TriggerExecutionLog 稽核日誌。

https://ithelp.ithome.com.tw/upload/images/20260828/20161290VY1kaFGSut.png

觸發點(TriggerPoint)= 什麼時候看規則
條件(TriggerCondition)= 什麼情況下算命中
動作(Executor)= 命中了做什麼

觸發點:生命週期 hook + 執行時機

五個觸發點,各自綁定執行階段。TriggerPointPhaseMatrix 實碼如下,這張表本身就是設計文件:

map.put(TriggerPointEnum.AFTER_FORM_MODIFIED,   TriggerExecutionPhaseEnum.AFTER_COMMIT);
map.put(TriggerPointEnum.BEFORE_ADD_APPROVERS,  TriggerExecutionPhaseEnum.IN_TRANSACTION);
map.put(TriggerPointEnum.AFTER_ACTION_APPROVED, TriggerExecutionPhaseEnum.IN_TRANSACTION);
map.put(TriggerPointEnum.AFTER_ADD_APPROVERS,   TriggerExecutionPhaseEnum.AFTER_COMMIT);
map.put(TriggerPointEnum.AFTER_SYSTEM_EVENT,    TriggerExecutionPhaseEnum.AFTER_COMMIT);

IN_TRANSACTION 跟業務動作同一個交易,可以影響結果、失敗可回滾;AFTER_COMMIT 則是業務先落定、自動化事後跑,失敗不拖累主流程。哪個觸發點掛哪個階段由引擎層寫死,不開放設定,因為這是正確性問題不是偏好問題:加簽核人必須在交易內(要影響本次推進),通知類必須在 commit 後(不能因為通知失敗回滾簽核)。這張矩陣也是 Day 12 交易血淚的預告。

https://ithelp.ithome.com.tw/upload/images/20260828/201612908L60vLp3X9.png

命名這件事同樣踩過一輪。觸發點曾從「系統事件導向」(如 FORM_SAVED)重構為使用者事件導向(AFTER_ACTION_APPROVED):管理員設規則時,腦中的語言是「簽核通過之後」,「XX Service 的 save 之後」對他毫無意義。規則引擎的詞彙表要用設定者的語言。

條件:群組式 AND/OR

條件子表用 groupNo 表達布林邏輯(TriggerConditionEvaluator 的 javadoc 即規格):

/**
 * 判斷結構化條件是否符合;同 groupNo 採 AND,不同 groupNo 採 OR。
 * @return true 表示至少一個群組符合
 */

(A AND B) OR (C AND D),不支援任意巢狀括號。這是刻意的表達力上限:DNF(析取範式)足以覆蓋實務上所有規則,而 UI 只需要「群組內加條件、加新群組」兩個動作,管理員不需要懂布林代數。表達力與可維護性只能挑一邊,我挑了可維護性。

https://ithelp.ithome.com.tw/upload/images/20260828/20161290rILr9lHSxm.png

動作:Executor 註冊表

TriggerResult result = executorRegistry
    .getExecutor(trigger.getActionType())
    .execute(trigger, context);

目前兩顆主力 executor:SET_FIELD(改表單欄位值,支援 $NOW token)與 IMPORT_APPROVER(動態加簽核人,22 種來源 resolver:固定名單、群組、表單欄位指到的人、前一關簽核者的主管、Approver Matrix、External Lookup……Day 10 的兩套路由在這裡以 resolver 形式被複用)。新動作型態就是實作介面加註冊一行,引擎本體不動。

失敗策略:出錯時停不停?

每條 trigger 可設定失敗策略,這其實是把商業風險觀資料化:

策略 行為 適用
WARN_CONTINUE 記 log 繼續 錦上添花類(自動帶值)
DISABLE_ON_ERROR 壞規則自動下線 防止一條爛規則每張單都炸
BLOCK IN_TRANSACTION 時拋出回滾 寧可卡單不可錯簽類

踩坑記錄:整包功能從 dangling blob 救回來

「Previous Step」來源模式(取前一關簽核者,再解析其主管,自動加簽主管的關鍵拼圖)曾經整包消失:前端 UI 沒了、後端 resolver 沒了、entity 欄位沒了。查 git log 一無所獲,因為這批檔案從頭到尾是 untracked,開發時一直沒 git add,某次清理工作目錄就蒸發了。

救援過程像資料考古:git fsck --lost-found 撈 dangling blob,靠 IDE 的 local history 與 blob 內容交叉比對,從 85e6efd 等懸空物件把程式碼一塊塊拼回來,再用四個 fix commit 補齊依賴(entity 欄位、repository 查詢、前端型別)。教訓樸素到不好意思寫:功能沒進版控就不存在。長期 WIP 也要 commit,哪怕丟在私人分支;「等做完再一起 add」是整包蒸發的標準前置條件。

小結

觸發點綁死執行階段,正確性不留給設定;條件用 DNF 群組,表達力讓位可維護性;動作走註冊表,留給擴充。今天講的是引擎怎麼組起來,明天講它最要命的部分。Day 12:REQUIRES_NEW vs REQUIRED 的血淚,同一顆引擎裡兩種交易傳播策略並存的原因。


上一篇
Day 10:簽核路由與外部系統整合
下一篇
Day 12:Trigger 規則引擎(下)——Spring 交易傳播實戰
系列文
30天打造一套企業PLM12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言