Skip to content

架構決策記錄 (ADR)

本頁說明

講什麼:出入金系統中關鍵設計決策的背景、可選方案、最終選擇和影響——幫助理解「為什麼是這樣」 適合誰:產品經理評估需求變更影響時、新人理解系統設計邏輯時 前置閱讀系統架構與資料流格式:每條決策按「背景 → 可選方案 → 決定 → 後果」結構記錄 預計閱讀:4 分鐘 負責人:出入金產品團隊

核心要點:每條架構決策記錄了「背景→可選方案→最終選擇→後果」——PM 評估需求變更時,先看相關 ADR 理解「為什麼是這樣」,避免重複踩坑。


ADR-001:入金匹配採用五維度匹配引擎

狀態:已採納 · 生效中

背景: 使用者透過銀行轉帳入金到證券帳戶時,系統需要將銀行流水(Flow)與使用者的入金申請(Apply)進行配對。早期方案是純人工匹配,但隨著使用者量增長,人工處理無法擴展。

可選方案

方案優點缺點
A. 純人工匹配準確率最高不可擴展,處理時效差
B. 單維度匹配(僅金額)簡單誤匹配率高(同金額不同使用者)
C. 五維度匹配(金額+幣種+銀行+日期+卡號)準確率高,可自動化實現複雜,需維護各銀行容差
D. 銀行端回傳唯一 ID最精確大部分銀行不支援(僅 BST 可做到)

決定:採用方案 C,五維度匹配引擎,輔以兩級容差機制(自動入帳容差 + 輔助匹配容差)。

後果

  • 正面:~95% 的入金可自動完成匹配和入帳,運營只需處理 ~5% 的異常
  • 負面:需要為每家銀行單獨維護匹配規則和容差參數(12 家銀行 12 套規則)
  • 變更影響:新增銀行時必須實現對應的 Match 類(如 BocMatch.phpHsbcMatch.php),並配置容差參數

程式碼位置deposit/src/app/Business/Match/ 目錄下各銀行 Match 類


ADR-002:出金採用三步審批工作流(Audit → Confirm → Remittance)

狀態:已採納 · 生效中

背景: 出金涉及真實資金流出,需要嚴格的審批控制。但過於繁瑣的審批會影響使用者體驗。需要在安全和效率之間取得平衡。

可選方案

方案優點缺點
A. 單步審批(直接匯款)最快安全風險高
B. 兩步審批(審核+匯款)簡單通道選擇無法獨立審核
C. 三步審批 + 6 套模板靈活,普通出金跳過 Audit實現複雜
D. 全流程人工最安全時效差,無法自動化

決定:採用方案 C,三步審批(Audit → Confirm → Remittance),透過 6 套模板控制哪些出金需要哪些步驟。普通出金(default 模板)跳過 Audit 直接進入 Confirm。

後果

  • 正面:普通出金只需 2 步,高風險出金有額外審核層
  • 正面:BST 通道滿足 6 個條件時可全自動完成,無需人工
  • 負面:6 套模板的維護成本;新增審批步驟需建立 Step 類
  • 變更影響:新增審批步驟 → 建立 Step 類實現 IFStep 介面 → 更新 $stepTemplates

程式碼位置

  • 模板定義:withdraw/src/app/Business/Task.php$stepTemplates
  • 步驟實現:withdraw/src/app/Business/Tasks/Step/{Audit,Confirm,Remittance}.php

ADR-003:BST 銀證採用「內銀系統一接入 + 銀行適配層」架構

狀態:已採納 · 生效中

背景: 招行、民生、天星三家銀行都支援銀證轉帳(BST),但接入協議完全不同:招行/民生用 SM2 加密 Socket,天星用 HTTPS REST API + Mandate 模型。

可選方案

方案優點缺點
A. 每家銀行獨立系統互不影響重複開發,統一排程困難
B. 統一業務層 + 銀行適配層業務邏輯共享,銀行差異封裝適配層設計複雜
C. 統一協議轉換閘道最簡潔協議差異太大,強行統一會丟失特性

決定:採用方案 B。入金/出金的業務流程(申請、審批、入帳)在上層統一,銀行通訊差異封裝在獨立的服務中(cmb_stock_transms_stock_bank_transaction、天星 API 呼叫層)。

後果

  • 正面:新增 BST 銀行時只需實現適配層,不改動業務邏輯
  • 負面:天星的非同步輪詢模型與招行/民生的即時推送模型差異大,上層需要相容兩種模式
  • 變更影響:天星出金需要額外的輪詢任務(AsbBstTransfer + SyncAsbBstWithdraw),而招行/民生不需要

ADR-004:eDDA/eDDI 並行控制採用 Token Bucket(匯豐)+ 悲觀鎖(恒生)

狀態:已採納 · 生效中

背景: 匯豐和恒生的 eDDI 扣款介面都有並行限制。如果並行請求過多,銀行會拒絕或延遲處理。需要在系統側做速率控制。

可選方案

方案優點缺點
A. 全域互斥鎖最簡單吞吐量極低
B. 匯豐:Token Bucket 樂觀鎖高吞吐,平滑限流實現較複雜
C. 恒生:InnoDB 行鎖串列化嚴格有序,滿足交易時段約束吞吐量受限
D. 訊息佇列限流解耦引入額外基礎設施

決定

  • 匯豐:Token Bucket 樂觀鎖(UPDATE ... WHERE tokens >= 1),每秒 4 個 Token,資料庫持久化
  • 恒生:InnoDB 行鎖悲觀鎖(SELECT ... FOR UPDATE),因為恒生有交易時段要求,需要嚴格串列

兩家銀行採用不同方案的核心原因:恒生需要感知交易時段(非交易時段請求進入 new_blank 狀態),這要求嚴格的請求排序;匯豐無此約束,用 Token Bucket 可獲得更高吞吐。

後果

  • 正面:兩套方案各自適配銀行特點,穩定運行
  • 負面:兩套並行控制機制增加維護成本
  • 變更影響:調整匯豐限流 → 修改 Token Bucket 產生速率;調整恒生 → 修改行鎖粒度

程式碼位置

  • 匯豐:sba_hsbc_eddi/ Token Bucket 實現
  • 恒生:sba_hase_eddi/ 行鎖實現

ADR-005:出金通道選擇採用規則路由 + 人工兜底

狀態:已採納 · 生效中

背景: 系統支援 12 種出金通道(Method),需要根據使用者銀行卡、幣種、金額等條件自動選擇最優通道。但部分場景無法自動判斷。

可選方案

方案優點缺點
A. 全自動路由無需人工無法覆蓋所有邊界情況
B. 自動路由 + method=null 人工兜底覆蓋率高,異常有人工保障運營需要通道知識
C. 全人工選擇最靈活不可擴展

決定:採用方案 B。calcMethod() 根據銀行卡類型自動路由,無法判斷時設 method = null,由運營在 Confirm 步驟手動選擇。

後果

  • 正面:~80% 的出金通道可自動確定
  • 負面method = null 是 Confirm 步驟卡住最常見的原因
  • 變更影響:新增銀行卡類型時必須更新 calcMethod() 路由規則,否則會產生 method=null

程式碼位置withdraw/src/app/Business/CreatorBase.phpcalcMethod()


ADR-006:銀行流水採集採用「一銀行一服務」架構

狀態:已採納 · 生效中

背景: 不同銀行的流水接入協議差異極大:中銀用 B2E XML、匯豐用 MT910 SWIFT、工銀用銀企直聯、EWB 用 CSV 檔案匯入。

可選方案

方案優點缺點
A. 統一流水採集服務集中管理協議差異太大,強行統一不現實
B. 一銀行一獨立服務各服務獨立部署、獨立維護、互不影響服務數量多
C. 按協議類型分組折中分組標準不穩定

決定:採用方案 B。每家銀行有獨立的流水採集服務:

  • 中銀:bochk_flow_go(Go)
  • 匯豐:hsbc_bank_flow_service(Python)
  • 工銀:icbc_be_relay(Python)
  • 廣發:cgb_fps_service(Go)
  • 渣打:scb_service(Go)

所有服務將流水統一寫入 acct_trd_record / {bank}_bank_flow 表,由匹配引擎統一消費。

後果

  • 正面:某家銀行服務故障不影響其他銀行;各服務可用最適合的技術棧
  • 負面:服務數量多(5+),監控和維運成本高
  • 變更影響:新增銀行 → 新建獨立服務 + 實現 Match 類 + 註冊到匹配引擎

ADR-007:入金沖正執行時同步解綁銀行卡

狀態:已採納 · 生效中(有爭議)

背景: 入金沖正(資金退回)後,該筆入金綁定的銀行卡是否應該自動解綁?

可選方案

方案優點缺點
A. 沖正時自動解綁防止同一張卡再次出現問題使用者可能需要用同一張卡重新入金
B. 沖正後保留綁定使用者體驗好風險卡可能繼續使用
C. 按沖正原因決定精細化控制實現複雜

決定:採用方案 A——沖正時一律自動解綁。

後果

  • 正面:降低同一張有問題的銀行卡反覆入金的風險
  • 負面:chargeback 類沖正後,使用者需要重新綁卡才能用同一張卡入金
  • 變更影響:如需改為「不解綁」,移除 deposit/src/app/Business/Reverse.php 中的解綁邏輯

如何使用 ADR

產品經理評估需求變更時

  1. 先查看相關 ADR,理解現有設計的背景和約束
  2. 評估新需求是否與現有 ADR 衝突
  3. 如果需要推翻現有決策,新建 ADR 記錄新決策,並標註舊 ADR 為「已廢棄」

新增 ADR 模板

ADR 模板
markdown
## ADR-XXX:決策標題

**狀態**:提議中 / 已採納 / 已廢棄 / 已替代(被 ADR-YYY 替代)

**背景**
為什麼需要做這個決策?當時面臨什麼問題?

**可選方案**

| 方案 | 優點 | 缺點 |
|------|------|------|

**決定**:選擇了哪個方案,以及核心理由。

**後果**
- 正面影響
- 負面影響
- 變更影響(後續需求變更時需要注意什麼)

**程式碼位置**:涉及的核心程式碼檔案

讀完之後

我想...去看
理解系統整體架構系統架構與資料流
深入 SBA 編排層SBA 資金編排
了解入金業務全景入金方式總覽
了解出金審批流程出金生命週期
這個頁面有幫助嗎?

内部业务文档 · 仅限 moomoo 团队使用