跳到内容

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

检查点 ​

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

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

重要规则:

  • 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-confirmation、asset-erc20-transfer、member-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通知通道,例如 log、database、websocket、mail

与支付结算的关系 ​

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

支付结算仍需要:

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

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

上线检查 ​

上线前至少确认:

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

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

Released under the Apache License 2.0.