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 团队使用