Skip to content

资金执行模块设计理念详解

一、核心问题:为什么需要资金执行模块?

1.1 财务预记账 vs 资金执行的本质区别

财务预记账(Financial Accrual Recording)

  • 本质:会计记录层面的操作
  • 作用:生成会计分录、冻结账户余额、记录借贷关系
  • 状态:PENDING(待确认)→ POSTED(已确认)/ CANCELLED(已取消)
  • 比喻:就像你在银行柜台填写转账单,钱被冻结了,但还没有真正转出去

资金执行(Fund Execution)

  • 本质:真实的资金划拨操作
  • 作用:调用实际的支付通道、银行接口、清算系统,完成资金转移
  • 状态:PENDING → PROCESSING → SUCCESS / FAIL
  • 比喻:银行柜员拿着你的转账单,真正去执行转账操作

1.2 为什么要分离这两个模块?

传统做法(不分离):
订单创建 → 直接调用支付接口 → 成功后记账
问题:
1. 支付失败了,账没记怎么办?
2. 记账成功了,支付失败了怎么办?
3. 网络超时了,到底成功没成功?
4. 如何重试?如何补偿?

LFPay做法(分离):
订单创建 → 预记账(冻结余额)→ 资金执行 → 确认记账
优点:
1. 预记账成功 = 资金已锁定,不会超支
2. 资金执行失败 = 自动取消记账,解冻余额
3. 状态清晰,可追溯,可重试
4. 解耦业务逻辑和支付通道

二、资金执行模块的架构设计

2.1 整体架构图

┌─────────────────────────────────────────────────────────────┐
│                      订单服务层                              │
│  (Order Service)                                            │
│  - 创建订单                                                  │
│  - 生成账户明细 (t_order_account_detail)                    │
└────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                  财务预记账服务                              │
│  (Financial Accrual Recording Service)                      │
│  - 生成会计分录 (PENDING状态)                               │
│  - 冻结账户余额 (available → frozen)                        │
│  - 写入流水记录                                              │
│  - 借贷平衡校验                                              │
└────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                  资金执行引擎                                │
│  (Fund Execution Engine)                                    │
│                                                              │
│  ┌──────────────────────────────────────────────────┐      │
│  │  1. 执行入口治理 (Entry Gateway)                 │      │
│  │     - 幂等控制 (idempotent_key)                  │      │
│  │     - 状态管理 (INIT/READY/DISPATCHED/...)       │      │
│  │     - 风控挂起 (risk_hold)                       │      │
│  └──────────────────────────────────────────────────┘      │
│                     │                                        │
│                     ▼                                        │
│  ┌──────────────────────────────────────────────────┐      │
│  │  2. 策略路由引擎 (Strategy Router)               │      │
│  │     - 规则匹配 (金额/场景/风险等级)              │      │
│  │     - 执行模式选择 (SYNC/ASYNC/BATCH)            │      │
│  │     - 执行器选择 (WALLET/BANK/CLEARING)          │      │
│  └──────────────────────────────────────────────────┘      │
│                     │                                        │
│                     ▼                                        │
│  ┌──────────────────────────────────────────────────┐      │
│  │  3. 任务调度器 (Task Scheduler)                  │      │
│  │     - 同步执行 (立即返回结果)                    │      │
│  │     - 异步执行 (MQ/线程池)                       │      │
│  │     - 批量执行 (定时任务)                        │      │
│  └──────────────────────────────────────────────────┘      │
│                     │                                        │
│                     ▼                                        │
│  ┌──────────────────────────────────────────────────┐      │
│  │  4. 执行器层 (Executor Layer)                    │      │
│  │     ┌────────────┬────────────┬────────────┐     │      │
│  │     │ 钱包执行器 │ 银行执行器 │ 清算执行器 │     │      │
│  │     │  (Wallet)  │   (Bank)   │ (Clearing) │     │      │
│  │     └────────────┴────────────┴────────────┘     │      │
│  └──────────────────────────────────────────────────┘      │
│                     │                                        │
│                     ▼                                        │
│  ┌──────────────────────────────────────────────────┐      │
│  │  5. 补偿与重试机制 (Compensate & Retry)          │      │
│  │     - 失败重试 (指数退避)                        │      │
│  │     - 超时补偿                                    │      │
│  │     - 人工介入                                    │      │
│  └──────────────────────────────────────────────────┘      │
└────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│              确认记账 / 取消记账                             │
│  - 成功 → confirmAccounting() → POSTED                      │
│  - 失败 → cancelAccounting() → CANCELLED                    │
└─────────────────────────────────────────────────────────────┘

2.2 核心设计原则

原则1:幂等性保证

java
// 每次执行都生成唯一的幂等Key
String idempotentKey = orderNo + "_" + tradeRecordId + "_" + stage;

// 数据库唯一索引保证不会重复执行
UNIQUE KEY uq_idempotent(idempotent_key)

原则2:状态机驱动

INIT (初始化)

READY (准备就绪,预记账成功)

DISPATCHED (已分发到执行器)

EXECUTING (执行中)

SUCCESS (成功) / FAIL (失败)

原则3:异步解耦

同步场景:小额转账、实时到账
异步场景:大额转账、批量代付
批量场景:工资发放、分润结算

三、具体执行流程详解

3.1 场景1:钱包内部转账(同步执行)

用户A向用户B转账100元

步骤1:预记账
  - 生成分录:
    借:用户B钱包 100元
    贷:用户A钱包 100元
  - 冻结用户A的100元 (available: 1000 → 900, frozen: 0 → 100)
  - 状态:PENDING

步骤2:资金执行(同步)
  - 入口治理:生成幂等Key,检查是否重复
  - 策略路由:匹配到"钱包内部转账"规则 → WALLET执行器 + SYNC模式
  - 执行器调用:
    ```java
    walletExecutor.transfer(
        fromUserId: A,
        toUserId: B,
        amount: 100
    )
    ```
  - 执行逻辑:
    1. 扣减A的冻结余额 (frozen: 100 → 0)
    2. 扣减A的总余额 (total: 1000 → 900)
    3. 增加B的可用余额 (available: 500 → 600)
    4. 增加B的总余额 (total: 500 → 600)
  - 记录执行任务:task_no, status=SUCCESS

步骤3:确认记账
  - 更新分录状态:PENDING → POSTED
  - 写入流水记录:t_acct_ledger
  - 完成

3.2 场景2:银行提现(异步执行)

用户A提现到银行卡5000元

步骤1:预记账
  - 生成分录:
    借:银行账户 5000元
    贷:用户A钱包 5000元
  - 冻结用户A的5000元
  - 状态:PENDING

步骤2:资金执行(异步)
  - 入口治理:生成幂等Key
  - 策略路由:匹配到"银行提现"规则 → BANK执行器 + ASYNC模式
  - 任务调度:
    1. 创建执行任务:t_fund_execute_task
    2. 发送到MQ:fund_execute_queue
    3. 立即返回:status=PENDING
  
  - 异步消费者处理:
    ```java
    bankExecutor.withdraw(
        userId: A,
        bankCard: "6222...1234",
        amount: 5000
    )
    ```
  - 调用银行接口:
    1. 发起代付请求
    2. 等待银行回调(可能需要几分钟)
    3. 更新任务状态:PROCESSING → SUCCESS

步骤3:确认记账(异步回调触发)
  - 收到银行成功回调
  - 更新分录状态:PENDING → POSTED
  - 扣减A的余额
  - 完成

3.3 场景3:批量代付(批量执行)

商户给100个用户发工资

步骤1:预记账(批量)
  - 生成100条分录(同一个batch_no)
  - 冻结商户账户总金额
  - 状态:PENDING

步骤2:资金执行(批量)
  - 入口治理:生成批次记录 t_fund_execute_batch
  - 策略路由:匹配到"批量代付"规则 → BANK执行器 + BATCH模式
  - 任务调度:
    1. 创建100个执行任务
    2. 分批发送到MQ(每批10条)
    3. 返回批次号
  
  - 批量处理:
    1. 调用银行批量代付接口
    2. 逐笔更新任务状态
    3. 失败的任务自动重试
    4. 超过重试次数的进入补偿流程

步骤3:确认记账(分批确认)
  - 每笔成功后更新对应分录
  - 批次全部完成后更新批次状态

四、为什么需要这么复杂的设计?

4.1 解决的核心问题

问题1:分布式事务

传统方案:
订单服务 → 账户服务 → 支付通道
如果支付通道失败,如何回滚账户服务的操作?

LFPay方案:
预记账(冻结)→ 资金执行 → 确认记账
失败了就取消记账,解冻余额,不需要回滚

问题2:幂等性

场景:用户点击两次"提现"按钮
传统方案:可能扣款两次
LFPay方案:idempotent_key保证只执行一次

问题3:可追溯性

场景:用户投诉"钱扣了但没到账"
传统方案:日志分散,难以排查
LFPay方案:
- t_acct_entry:会计分录
- t_fund_execute_task:执行任务
- t_fund_execute_compensate:补偿记录
完整的链路追踪

问题4:灵活扩展

新增支付通道:
1. 实现新的Executor接口
2. 配置新的策略规则
3. 不需要修改核心代码

4.2 架构优势

优势1:职责分离

财务预记账:负责会计准确性
资金执行:负责支付可靠性
各司其职,互不干扰

优势2:异步解耦

订单服务不需要等待银行接口返回
用户体验更好,系统吞吐量更高

优势3:容错能力

- 自动重试
- 超时补偿
- 人工介入
- 状态可恢复

优势4:监控告警

- 执行成功率
- 平均执行时长
- 失败原因分析
- 补偿任务数量

五、与现有代码的对应关系

5.1 已实现的部分(transactionexecution模块)

java
// 财务预记账服务
IFinancialAccrualRecording
  - executePreAccounting()    // 预记账
  - confirmAccounting()        // 确认记账
  - cancelAccounting()         // 取消记账

// 资金执行服务(简化版)
IFundExecution
  - executeFund()              // 执行资金转移
  - queryExecutionStatus()     // 查询执行状态
  - retryExecution()           // 重试执行

当前实现的重点

  • ✅ 会计分录的生成和状态管理
  • ✅ 预记账、确认记账、取消记账的完整流程
  • ⚠️ 资金执行部分标记为TODO(需要后续实现)

5.2 需要补充的部分(资金执行引擎)

java
// 1. 执行入口治理
FundExecuteEntryService
  - checkIdempotent()          // 幂等检查
  - updateExecutionStatus()    // 状态更新

// 2. 策略路由引擎
StrategyRouterService
  - matchStrategy()            // 策略匹配
  - selectExecutor()           // 执行器选择

// 3. 执行器接口
interface FundExecutor {
    ExecuteResult execute(ExecuteContext context);
}

// 4. 具体执行器
WalletExecutor implements FundExecutor
BankExecutor implements FundExecutor
ClearingExecutor implements FundExecutor

// 5. 任务调度器
TaskSchedulerService
  - dispatchSyncTask()         // 同步调度
  - dispatchAsyncTask()        // 异步调度
  - dispatchBatchTask()        // 批量调度

// 6. 补偿服务
CompensateService
  - createCompensateRecord()   // 创建补偿记录
  - executeCompensate()        // 执行补偿

六、实施建议

6.1 第一阶段:完善财务预记账(已完成)

  • ✅ 会计分录生成
  • ✅ 状态流转
  • ✅ 借贷平衡校验

6.2 第二阶段:实现简单执行器

java
// 先实现钱包内部转账(同步)
WalletExecutor
  - 调用账户服务API
  - 完成余额扣减和增加
  - 返回执行结果

6.3 第三阶段:引入策略路由

java
// 根据订单类型选择执行器
if (orderType == "WALLET_TRANSFER") {
    return walletExecutor;
} else if (orderType == "BANK_WITHDRAW") {
    return bankExecutor;
}

6.4 第四阶段:完善异步和批量

java
// 引入MQ
// 引入定时任务
// 引入补偿机制

七、总结

核心理念

财务预记账 = 会计层面的承诺(我承诺要转这笔钱)
资金执行   = 支付层面的兑现(我真的转了这笔钱)
确认记账   = 会计层面的确认(我确认转账成功了)

为什么分离?

  1. 职责清晰:会计归会计,支付归支付
  2. 容错能力:失败了可以回滚,不会乱账
  3. 可追溯性:每一步都有记录
  4. 可扩展性:新增支付通道不影响会计逻辑

类比理解

传统做法 = 你去银行转账,柜员直接操作,成功就成功,失败就失败
LFPay做法 = 你去银行转账:
  1. 柜员先填单(预记账)
  2. 柜员去操作(资金执行)
  3. 操作成功后盖章(确认记账)
  4. 操作失败就撕单(取消记账)

这样设计虽然复杂,但是安全、可靠、可追溯,这是支付系统的核心要求。