跳到内容

Web3 链上事件补偿任务

受众:开发者 摘要:Web3 事件补偿专题:失败检测 / 重试策略 / 补偿任务 / 幂等保证。

Web3 链上事件补偿任务用于把“链上事件扫描”和“业务状态修复”拆开处理。它不直接签名、不广播、不托管私钥,也不默认修改支付订单;它负责稳定地发现链上事件、去重、重试、记录日志,并把需要业务处理的事件交给对应 handler。

这类任务适合:

  • ERC-20 / ERC-721 / ERC-1155 Transfer 事件归档。
  • Web3 支付到账候选发现。
  • 链上交易确认提醒。
  • 资产流水补扫。
  • 权益发放或撤销补偿。
  • 多实例环境下的链上任务重试。

设计边界

层级职责
Web3 starterRPC、事件 ABI、topic 编码、事件查询、扫描窗口计算、事件扫描编排
Money Web3检查点持久化、扫描日志、后台运维、Scheduler 自动扫描、失败通知
业务模块注册扫描器、处理事件、业务幂等、订单或资产状态更新
Scheduler周期触发、多实例任务协调、失败重试
Queue可选异步处理耗时业务逻辑
Notification可选失败通知和人工复核提醒

Web3 starter 不落库、不加分布式锁、不启动任务、不更新支付或资产状态。业务状态更新必须放在业务 Service 中。

已有能力

当前已具备:

  • Web3BlockScans:计算稳定扫描窗口。
  • Web3.scanEvents(...):按检查点执行事件查询和 handler 回调。
  • Web3EventScanner:业务扫描器 SPI。
  • Web3EventScannerRegistry:按 scannerName 查找扫描器。
  • Web3EventIdempotencyKeys:生成事件幂等 key。
  • money_web3_scan_checkpoint:检查点持久化。
  • money_web3_scan_log:扫描执行日志。
  • Money Web3 自动扫描 Scheduler 任务。
  • Money Web3 手动全量触发和单检查点重跑入口。
  • 扫描失败 Notification 通知。
  • 业务模块内注册的 Web3 事件处理器。

标准流程

推荐补偿任务按以下流程运行:

  1. 后台配置或创建 money_web3_scan_checkpoint
  2. 检查点保存 chainNamescannerNamenextBlock、确认数和单次扫描跨度。
  3. Scheduler 周期读取 active 检查点。
  4. 根据 scannerNameWeb3EventScannerRegistry 找到业务扫描器。
  5. 通过 Web3.scanEvents(...) 读取链头、计算稳定窗口并查询事件日志。
  6. 业务 handler 对每条事件生成幂等 key。
  7. 业务 handler 在事务内写入业务表或发起后续 Queue job。
  8. handler 全部成功后推进检查点。
  9. 失败时不推进检查点,记录 money_web3_scan_log 并标记检查点异常。
  10. 必要时发送 Notification 失败提醒,运维修复后单检查点重跑。

检查点

检查点是补偿任务的运行游标。核心字段:

字段说明
chainName链配置名称,例如 bscethereum
scannerName扫描器名称,例如 sample-erc20-transfer
nextBlock下一次待扫描起始区块
lastScannedBlock上次成功扫描结束区块
requiredConfirmations扫描确认数
blockRangeSize单次最大扫描跨度
statusactivepausederror

重要规则:

  • nextBlock 只能在本轮业务处理全部成功后推进。
  • error 状态不会被自动扫描。
  • paused 状态适合临时停用某个业务扫描器。
  • 大范围历史补扫要降低 blockRangeSize,避免 RPC 超时或服务商限流。

扫描器

业务模块通过 Web3EventScanner 注册自己的扫描器:

java
@Component
public class SampleErc20TransferScanner implements Web3EventScanner {

    @Override
    public String scannerName() {
        return "sample-erc20-transfer";
    }

    @Override
    public Web3EventLogQuery query(Web3BlockScanCheckpoint checkpoint) {
        return Web3EventLogQuery.builder()
                .chainName(checkpoint.getChainName())
                .event(Web3TokenEvents.ERC20_TRANSFER)
                .address("0x...")
                .build();
    }

    @Override
    public void handle(Web3EventScanContext context, Web3EventLog log) {
        // 在业务 Service 中完成幂等落库。
    }
}

命名建议:

  • 使用小写短横线。
  • 名称表达业务含义,不要只写 transfer
  • 示例:web3-payment-confirmationasset-erc20-transfermember-nft-rights

幂等规则

链上事件补偿必须以事件唯一 key 做幂等。

推荐 key:

场景key
同一链交易日志唯一Web3EventIdempotencyKeys.transactionLog(...)
多合约多事件归档Web3EventIdempotencyKeys.contractEventLog(...)

业务表必须建立唯一约束。handler 收到重复事件时,应返回已处理结果,而不是抛出不可恢复异常。

示例:

java
String key = Web3EventIdempotencyKeys.contractEventLog(
        log.chainName(),
        log.transactionHash(),
        log.logIndex(),
        log.address(),
        Web3TokenEvents.ERC20_TRANSFER.signature());

失败处理

失败时的处理原则:

  • 不推进检查点。
  • 写入扫描日志。
  • 将检查点标记为 error
  • 记录错误消息,便于后台排查。
  • 可发送 Notification 失败提醒。
  • 修复原因后,用单检查点触发入口重跑。

常见失败:

失败类型处理建议
RPC 超时降低 blockRangeSize,检查节点限流
扫描器不存在确认业务模块已加载并注册 scanner
事件解析失败检查 ABI、topic 和合约地址
业务唯一键冲突handler 应兼容重复事件
业务表写入失败修复业务数据或迁移后重跑

后台入口

检查点管理:

http
GET    /api/v1/admin/web3/scan-checkpoints
GET    /api/v1/admin/web3/scan-checkpoints/{id}
GET    /api/v1/admin/web3/scan-checkpoints/{id}/next-window
POST   /api/v1/admin/web3/scan-checkpoints
PUT    /api/v1/admin/web3/scan-checkpoints/{id}
POST   /api/v1/admin/web3/scan-checkpoints/{id}/advance
POST   /api/v1/admin/web3/scan-checkpoints/{id}/pause
POST   /api/v1/admin/web3/scan-checkpoints/{id}/resume
POST   /api/v1/admin/web3/scan-checkpoints/{id}/mark-error
DELETE /api/v1/admin/web3/scan-checkpoints/{id}
DELETE /api/v1/admin/web3/scan-checkpoints

扫描触发:

http
POST /api/v1/admin/web3/scans/trigger
POST /api/v1/admin/web3/scans/checkpoints/{id}/trigger

扫描日志:

http
GET    /api/v1/admin/web3/scan-logs
GET    /api/v1/admin/web3/scan-logs/{id}
DELETE /api/v1/admin/web3/scan-logs/{id}
DELETE /api/v1/admin/web3/scan-logs

配置

yaml
spring:
  open:
    money:
      web3:
        enabled: true
        scanner:
          enabled: true
          auto-startup: true
          task-name: money.web3.scan
          instance-id: default
          fixed-delay: 1m
          limit: 50
          failure-notification:
            enabled: true
            channels:
              - log
            subject: Web3 scan failed
            recipient-type: ""
            recipient-value: ""

配置说明:

配置说明
enabled是否启用 Money Web3 运维能力
scanner.enabled是否启用扫描任务能力
scanner.auto-startup是否随应用启动注册 Scheduler 任务
scanner.fixed-delay自动扫描间隔
scanner.limit单轮最多扫描检查点数量
failure-notification.enabled是否启用失败通知
failure-notification.channels通知通道,例如 logdatabasewebsocketmail

与支付结算的关系

链上事件补偿任务可以发现 Web3 支付候选事件,但不等于自动结算。

支付结算仍需要:

  • 本地订单状态校验。
  • 金额、收款地址、Token 合约和链 ID 校验。
  • 事件唯一 key 幂等。
  • reviewFingerprint 或同等复核机制。
  • 支付流水和订单状态事务写入。
  • 审计和通知。

自动结算设计见 Web3 支付自动结算设计

上线检查

上线前至少确认:

  • 每个扫描器都有明确的 scannerName
  • 每个业务表都有事件幂等唯一约束。
  • 每条链的 requiredConfirmations 合理。
  • 历史补扫设置了安全的 blockRangeSize
  • 业务 handler 失败不会吞异常。
  • 扫描失败通知已经接入可见通道。
  • 运维知道如何暂停、恢复、标记异常和单检查点重跑。
  • 生产 RPC 支持所需历史区块范围。

补偿任务的第一原则是:宁可不推进游标,也不要在业务处理失败后假装扫描成功。

Released under the Apache License 2.0.