跳到内容

幂等、补偿与 Outbox

适用版本:0.7.x 受众:后端开发者、模块维护者

项目的跨模块写操作需要统一处理重复请求、回调重试、补偿重放和异步投递。当前已落地三段共享 contract:spring-open-starter-common 提供稳定幂等键工具,spring-open-core-outbox 提供持久化 Outbox 事件、可靠投递执行器、补偿命令、Webhook 投递和后台运维 API。

幂等键形态

推荐使用四段式作用域键:

text
consumer:resource:action:businessId

字段含义:

字段说明示例
consumer消费方或业务域,使用 kebab-casemallopen-platform
resource资源或单据类型,使用 kebab-caseorderpackage-order
action本次幂等动作,使用 kebab-casepayconsumerefund
businessId业务唯一标识,保留原始大小写,不能包含 :ORDER-100PO-100

示例:

java
String key = IdempotencyKeys.scoped("Open Platform", "Package Order", "Pay", "PO-100");
// open-platform:package-order:pay:PO-100

Core Contract

com.springopen.core.idempotency 提供三个入口:

类型用途
IdempotencyKey包装和校验普通幂等键,保留原始业务大小写
ScopedIdempotencyKey构造和解析四段式作用域键
IdempotencyKeys静态工具,包含 normalize(...)scoped(...) 和长度 / 字符校验

校验规则:

  • 最大长度为 128 个字符。
  • 普通 key 支持 ASCII 字母、数字、._@:/-
  • 四段式 key 的 businessId 不允许包含 :,避免解析歧义。
  • consumerresourceaction 会转为 kebab-case,适合跨模块稳定复用。

使用建议

  • API 请求里的 idempotencyKey 可以先用 IdempotencyKey.of(...) 校验,再进入业务 Service。
  • 服务端从订单号、流水号、回调号等业务事实派生 key 时,优先用 IdempotencyKeys.scoped(...)
  • 同一业务动作必须使用同一 key;不同动作不能复用同一 key。
  • Outbox、补偿、重放和失败观测记录应保存同一 key,方便跨表追踪。

Core Outbox Contract

Core Outbox 模块提供后台 Outbox 事件 contract:

能力API权限
分页 / 详情 / 失败汇总GET /api/v1/admin/outbox/eventsGET /api/v1/admin/outbox/events/{id}GET /api/v1/admin/outbox/events/failure-summaryoutbox:event:view
入队POST /api/v1/admin/outbox/eventsoutbox:event:create
标记失败 / 已投递POST /api/v1/admin/outbox/events/{id}/failPOST /api/v1/admin/outbox/events/{id}/dispatchoutbox:event:update
重放POST /api/v1/admin/outbox/events/{id}/replayPOST /api/v1/admin/outbox/events/replay-batchoutbox:event:replay
跳过POST /api/v1/admin/outbox/events/{id}/skipoutbox:event:skip

状态包括 pendingdispatchingdispatchedfailedskipped。入队时若传入 idempotencyKey 且未删除记录已存在,会直接返回既有事件,避免重复写入同一业务动作。失败标记会增加 retryCount 并在未达到 maxRetries 前计算 nextRetryAt;重放只允许 failed / skipped 事件回到 pending;已投递事件不能再跳过。

入队时显式传入的 traceId 优先;未传入时会沿用当前 TraceIdContext,仍为空则生成新的安全 traceId。补偿命令使用同样规则,因此 HTTP 请求、异步事件、Outbox、Webhook 和后台重试可以按同一 traceId 串联排查。

失败汇总接口只读取 failed / skipped 事件,按 consumereventTypeaggregateTypestatus 聚合,返回总数、仍可重试数量、已耗尽重试数量和最近更新时间。Admin Outbox 运维页的“跨业务失败观测”区域会调用该接口,点击某个汇总项后把事件列表筛选到对应消费方和事件类型,便于继续查看详情、重放、批量重放或人工跳过。

可靠投递执行器

Core Outbox 模块提供 OutboxEventHandler 消费方 SPI 和 OutboxEventDispatcher 批量执行器。消费方只需要注册 handler bean 并在 supports(...) 中匹配事件来源 / 类型,执行器会领取可投递事件、调用 handler,并按结果回写状态。

执行器会原子领取 pending、到达 nextRetryAtfailed、以及超过锁超时时间的 dispatching 事件。handler 成功后事件标记为 dispatched;handler 抛出异常或没有匹配 handler 时事件标记为 failed,并继续复用既有 retryCount / nextRetryAt / maxRetries 重试语义。handler 必须按事件 idempotencyKey 或业务唯一键实现幂等,因为失败重试和超时锁回收都可能再次投递同一事件。

默认调度配置前缀为 spring.open.outbox.dispatcher

配置默认值说明
task-nameoutbox.dispatcherScheduler 任务名
instance-iddefaultWorker 标识,会写入事件锁字段
fixed-delay1m批量投递间隔
lock-timeout10mdispatching 锁超时回收窗口
batch-size50单轮领取上限

自动调度只会在至少存在一个 OutboxEventHandler bean 时注册,避免未接入消费方时把事件批量标记为无 handler 失败。

Webhook 投递底座

Core Outbox 内置 WebhookOutboxEventHandler,可把 Outbox 事件按 HTTP Webhook 投递到外部系统。它复用既有 OutboxEventDispatcher 的领取、失败、重试、锁超时和投递日志语义;HTTP 非配置成功状态响应或网络异常会抛出投递异常,事件继续按 Outbox 的 retryCount / nextRetryAt / maxRetries 规则重试。

默认配置前缀为 spring.open.outbox.webhook

配置默认值说明
enabledfalse是否启用 Webhook handler
consumerwebhookhandler 匹配的 Outbox consumer
endpoint默认投递地址
secret默认 HMAC 签名密钥
methodPOSTHTTP 方法
content-typeapplication/json请求体类型
signing-enabledtrue是否生成签名头
success-status-min200成功状态码下界,包含边界
success-status-max299成功状态码上界,包含边界
connect-timeout5sHTTP 连接超时
request-timeout15s单次请求超时
ssrf.block-private-networkstrue拦截投递到内网 / 回环 / 链路本地 / CGNAT / 任播 / 多播地址的回调
ssrf.allowed-hosts显式放行的目标主机名(精确匹配),用于可信内网 webhook
ssrf.restrict-to-http-schemestrue仅允许 http / https,拦截 file / ftp / gopher 等协议
endpoints.<key>.*命名 endpoint,可按事件类型或 header 选择

SSRF 防护

Webhook 投递常面向业务方自配置的回调地址,投递前由 WebhookSsrfGuard 校验目标:限制协议为 http/https,解析目标主机的全部 IP,拒绝投递到内网、回环、链路本地、CGNAT(100.64.0.0/10)、IPv6 ULA(fc00::/7)、任播或多播地址。校验所有解析结果(而非首个)以降低 DNS rebinding 风险。

合法的内网 webhook(如服务间回调)应通过 ssrf.allowed-hosts 精确放行对应主机,而不是整体关闭 ssrf.block-private-networks;运行时连接由 JDK HttpClient 完成、仍可能重新解析,因此白名单是更安全的放行方式。

endpoint 选择顺序为:Outbox headers 中的 webhook.endpoint-key、事件 eventType 对应的命名 endpoint、默认 endpoint。headers 也可以覆盖 webhook.endpointwebhook.secretwebhook.methodwebhook.content-typewebhook.signing-enabledwebhook.enabled,并可通过 webhook.header.<Header-Name> 写入业务自定义请求头。

投递请求会自动附带事件 ID、事件类型、聚合类型、聚合 ID、trace、时间戳、body SHA-256 和签名头。签名格式为 sha256=<hex>,签名正文由 METHOD、path、query、时间戳、事件 ID 和 body hash 组成,接收方应使用同一 secret 校验。

Outbox 投递失败通知规则

当可靠投递执行器遇到 handler 异常或没有匹配 handler 时,事件仍会先按既有语义标记为 failed,随后尝试发布通知规则事件源 outbox.dispatch-failed。该事件源只在后台已配置启用的运行时通知规则且规则含用户接收人时发送,不猜测管理员;通知发送异常会被隔离,不影响 Outbox 失败重试。

通知 payload 会携带 Outbox 事件 ID、业务事件 ID、消费方、事件类型、聚合类型、聚合 ID、幂等键、当前状态、重试次数、最大重试次数、下次重试时间、trace、是否无 handler、错误码和错误信息。默认跳转目标为 /outbox,去重键按 outbox.dispatch-failed:<consumer>:<eventType>:<eventId>:<errorCode> 生成,并继续复用通知规则里的去重窗口。

业务模块消费桥接

业务模块可按自身事件语义提供 OutboxEventHandler

  • BlogArticleStatusOutboxEventHandlerForumReportStatusOutboxEventHandlerMallDomainStatusOutboxEventHandler 分别由 Blog、Forum、Mall 模块提供;投递时会解析 Outbox payload,并重新发布对应领域事件,确保重放仍沿用既有业务事件语义。
  • OutboxAuditEventHandler 由 Core Outbox 模块提供,支持 Payment 回调、用户余额流水和 Web3 结算审计流;投递时发布 OutboxAuditEvent,默认不产生额外业务副作用,后续消费方可订阅该审计事件做运营、通知或报表扩展。

支付回调业务适配

Payment 异步回调已作为首个真实业务消费方接入 Outbox。回调验签通过、订单存在、交易流水和业务成功 / 退款处理器执行完成后,会在同一事务中写入 Outbox 事件:

字段取值
consumercashier-payment-callback
eventTypepayment.callback.notify / payment.callback.refund
aggregateTypepayment-order
aggregateId支付订单 ID
idempotencyKeycashier-payment:callback:<eventType>:<hash>

payload 会携带 callbackLogIdorderIdoutTradeNotransactionIdprovidereventType、回调状态、交易状态、金额、币种、traceId 和处理时间,headers 会携带 sourceType=cashier.payment.notifytraceId

验签失败、订单不存在或回调无法进入既有支付处理链路时,只保存回调日志,不写 Outbox 事件。重复回调仍会保留各次 callback log,但 Outbox 通过幂等键复用同一业务事件,避免后续消费方重复投递。

用户余额流水业务适配

Money 用户余额流水已接入 Outbox。MoneyBalanceLedgerService 只有在首次写入新流水并完成账户余额更新后,才会在同一事务中写入 Outbox 事件:

字段取值
consumermoney-balance-ledger
eventTypemoney.balance.ledger.applied
aggregateTypeuser-balance-account
aggregateId余额账户 ID
idempotencyKeymoney-balance:ledger:<ledgerIdempotencyKey>

payload 会携带 ledgerIdaccountIduserId、资产编码、流水类型、业务来源、操作人、金额变动、余额前后快照、汇率快照、traceId 和发生时间,headers 会携带 sourceType=money.balance.ledgertraceId

重复 idempotencyKey 调用会直接返回既有余额流水,不重复扣减 / 入账,也不会重复创建 Outbox 事件。

Web3 支付结算业务适配

Web3 支付结算执行器已接入 Outbox。人工复核通过后,执行器会在订单条件更新为成功、写入 web3-settlement 支付流水并完成 Payment 成功处理器后,同事务写入 Outbox 事件:

字段取值
consumermoney-web3-settlement
eventTypemoney.web3.payment-settlement.applied
aggregateTypepayment-order
aggregateId支付订单 ID
idempotencyKeymoney-web3:settlement:<chainName>:<transactionHash>

payload 会携带复核日志、支付订单、支付流水、Provider、订单状态、金额、币种、链、交易哈希、确认数、区块高度、traceId 和发生时间,headers 会携带 sourceType=money.web3.payment-settlementtraceId、复核指纹、动作和链标识。

重复结算会先命中既有订单成功状态并返回 applied=false,不重复写支付流水、不重复分发 Payment 成功处理器,也不会重复创建 Outbox 事件。

Mall 状态事件业务适配

Mall 订单和售后状态变更已接入 Outbox。订单支付成功、支付状态同步成功、取消订单,以及售后创建、审批、拒绝、关闭、退货收货和退款处理等状态变更,会在发布本地事件前同事务写入 Outbox 事件:

字段订单取值售后取值
consumermall-order-statusmall-after-sale-status
eventTypemall.order.status-changedmall.after-sale.status-changed
aggregateTypemall-ordermall-after-sale
aggregateId订单 ID售后单 ID
idempotencyKeymall-order:status:<orderId>:<previousStatus>:<currentStatus>:<reason>mall-after-sale:status:<afterSaleId>:<previousStatus>:<currentStatus>:<refundStatus>:<returnStatus>:<reason>

订单 payload 会携带订单 ID、订单号、用户 ID、变更前后状态、原因、支付状态、支付流水、Provider、Gateway 和发生时间;售后 payload 会携带售后单 ID、售后单号、订单 ID、用户 ID、变更前后状态、退款状态、退货状态、原因、备注和发生时间。headers 会携带 sourceType=mall.order.statussourceType=mall.after-sale.status,并带上业务编号,便于 Outbox 运维页按消费方、事件类型或聚合筛选。

如果状态未变化,Mall 不发布状态事件,也不会写 Outbox。重复执行相同状态快照会通过幂等键复用既有 Outbox 事件,避免后续消费方重复投递。

Mall 模块消费桥接会在 Outbox 投递时重新发布 MallOrderStatusChangedEvent / MallAfterSaleStatusChangedEvent,跨模块重放时仍能触发现有 Mall 状态事件监听链。

Blog 文章状态事件业务适配

Blog 文章状态变更已接入 Outbox。后台处理文章发布、下线、恢复发布或调整发布时间时,BlogArticleService.updateStatus 会在文章状态或发布时间快照变化后写入 Outbox 事件:

字段取值
consumerblog-article-status
eventTypeblog.article.status-changed
aggregateTypeblog-article
aggregateId文章 ID
idempotencyKeyblog-article:status:<articleId>:<previousStatus>:<currentStatus>:<previousPublishedAt>:<currentPublishedAt>

payload 会携带文章 ID、slug、标题、作者 ID、作者名、变更前后状态、变更前后发布时间和发生时间。headers 会携带 module=blogsourceType=blog.article.statussourceId 和发生时间,便于 Outbox 运维页按消费方、事件类型或文章聚合筛选。

如果状态和发布时间快照均未变化,Blog 不写 Outbox。重复提交相同状态快照会通过幂等键复用既有 Outbox 事件,避免后续消费方重复投递。

Blog 模块消费桥接会在 Outbox 投递时重新发布 BlogArticleStatusChangedEvent,跨模块重放时仍能触发现有 Blog 文章状态事件监听链。

Forum 举报状态事件业务适配

Forum 举报状态变更已接入 Outbox。后台处理举报状态时,ForumInteractionService.updateReportStatus 会在状态真实变化后通过可选生产方写入 Outbox 事件:

字段取值
consumerforum-report-status
eventTypeforum.report.status-changed
aggregateTypeforum-report
aggregateId举报 ID
idempotencyKeyforum-report:status:<reportId>:<previousStatus>:<currentStatus>:<operatorUserId>

payload 会携带举报 ID、举报人、目标类型、目标 ID、变更前后状态、操作人和发生时间。headers 会携带 module=forumsourceType=forum.report.statussourceId 和发生时间,便于 Outbox 运维页按消费方、事件类型或举报聚合筛选。

Forum 模块消费桥接会在 Outbox 投递时重新发布 ForumReportStatusChangedEvent,跨模块重放时仍能触发现有 Forum 举报状态事件监听链。

Outbox Compensation Command Contract

Core Outbox 模块提供后台补偿命令 contract:

能力API权限
分页 / 详情 / 失败汇总GET /api/v1/admin/outbox/compensation-commandsGET /api/v1/admin/outbox/compensation-commands/{id}GET /api/v1/admin/outbox/compensation-commands/failure-summaryoutbox:compensation-command:view
入队POST /api/v1/admin/outbox/compensation-commandsoutbox:compensation-command:create
标记运行 / 成功 / 失败POST /api/v1/admin/outbox/compensation-commands/{id}/runningPOST /api/v1/admin/outbox/compensation-commands/{id}/succeedPOST /api/v1/admin/outbox/compensation-commands/{id}/failoutbox:compensation-command:update
重放POST /api/v1/admin/outbox/compensation-commands/{id}/replayPOST /api/v1/admin/outbox/compensation-commands/replay-batchoutbox:compensation-command:replay
跳过POST /api/v1/admin/outbox/compensation-commands/{id}/skipoutbox:compensation-command:skip

状态包括 pendingrunningsucceededfailedskipped。入队时若传入 idempotencyKey 且未删除记录已存在,会直接返回既有命令,避免重复创建同一补偿动作。失败标记会增加 retryCount 并在未达到 maxRetries 前计算 nextRetryAt;重放只允许 failed / skipped 命令回到 pending;成功命令不能再跳过,已跳过命令不能标记成功。

失败汇总接口只读取 failed / skipped 补偿命令,按 consumercommandTypeaggregateTypestatus 聚合,返回总数、仍可重试数量、已耗尽重试数量和最近更新时间。Admin Outbox 运维页的“补偿失败观测”区域会调用该接口,点击某个汇总项后把补偿命令列表筛选到对应消费方和命令类型,便于继续查看详情、重放、批量重放或人工跳过。

Core Outbox 模块提供 CompensationCommandHandler 消费方 SPI 和 CompensationCommandExecutor 批量执行器。消费方注册 handler bean 后在 supports(...) 中匹配 consumer / commandType,执行器会领取可执行命令、调用 handler,并按结果回写状态。

执行器会原子领取 pending、到达 nextRetryAtfailed、以及超过锁超时时间的 running 命令。handler 成功后命令标记为 succeeded;handler 抛出异常或没有匹配 handler 时命令标记为 failed,并继续复用既有 retryCount / nextRetryAt / maxRetries 重试语义。handler 必须按命令 idempotencyKey 或业务唯一键实现幂等,因为失败重试和超时锁回收都可能再次执行同一命令。

执行器测试已覆盖真实失败重放链路:handler 首次抛错时命令进入 failed 并触发失败通知;管理员或调度侧执行重放后,命令回到 pending,待 handler 恢复后再次执行会标记为 succeeded,同时保留既有 retryCount 轨迹,便于排障追踪。

当补偿命令执行器遇到 handler 异常或没有匹配 handler 时,命令仍会先按既有语义标记为 failed,随后尝试发布通知规则事件源 outbox.compensation-command.failed。该事件源只在后台已配置启用的运行时通知规则且规则含用户接收人时发送;通知发送异常会被隔离,不影响命令重试、人工重放或跳过。

通知 payload 会携带补偿命令 ID、业务命令 ID、消费方、命令类型、聚合类型、聚合 ID、幂等键、当前状态、重试次数、最大重试次数、下次重试时间、trace、是否无 handler、错误码和错误信息。默认跳转目标为 /outbox,去重键按 outbox.compensation-command.failed:<consumer>:<commandType>:<commandId>:<errorCode> 生成,并继续复用通知规则里的去重窗口。

默认调度配置前缀为 spring.open.outbox.compensation-command

配置默认值说明
task-nameoutbox.compensation-command.executorScheduler 任务名
instance-iddefaultWorker 标识,会写入命令锁字段
fixed-delay1m批量执行间隔
lock-timeout10mrunning 锁超时回收窗口
batch-size50单轮领取上限

自动调度只会在至少存在一个 CompensationCommandHandler bean 时注册,避免未接入业务 handler 时把命令批量标记为无 handler 失败。

已内置的业务 handler:

消费方命令类型说明
cashier-paymentcashier.payment.compensate-pending-orders复用 Cashier 支付补偿 Service,可按 payload.outTradeNo 单笔补偿,或按 limit / olderThanMinutes 扫描 pending 订单
mallmall.order.release-expired-pending复用 Mall 超时待支付订单释放能力,可按 payload.expiredBeforepayload.limit 扫描并释放过期待支付订单
blogblog.article.update-status复用 Blog 文章状态更新能力,可按 articleIdstatuspublishedAt 重放发布 / 下线状态
blogblog.comment.update-status复用 Blog 评论状态更新能力,可按 commentIdstatus 重放显示 / 隐藏状态
forumforum.report.update-status复用 Forum 举报状态处理能力,可按 reportIdstatusoperatorId 重放举报处理状态
cmscms.content.update-status复用 CMS 内容状态更新能力,可按 contentIdstatusvisibleAtexpiredAt 重放发布 / 下线与展示窗口
short-linkshort-link.link.update-status复用短链接保存能力,可按 shortLinkIdstatus 重放启用 / 停用状态
live-codelive-code.live-code.update-status复用 Live Code 活码保存能力,可按 liveCodeIdstatus 重放启用 / 禁用状态
open-platformopen-platform.app.update-status复用 Open Platform 应用保存能力,可按 appIdstatus 重放启用 / 禁用状态
open-platformopen-platform.package.update-status复用 Open Platform 套餐保存能力,可按 packageIdstatus 重放启用 / 禁用状态

支付补偿命令的 payload/api/v1/admin/cashier/payment-compensations 请求体一致,支持 outTradeNolimitolderThanMinutestraceId。handler 会优先使用 payload 中的 traceId,未传时使用补偿命令自身 traceId。如果补偿结果包含失败项,命令会标记为 failed,继续使用 retryCount / nextRetryAt / maxRetries 重试语义。

Mall 超时订单释放补偿命令的 payload 支持 expiredBefore(ISO-8601 本地时间,例如 2026-06-03T10:15:30)和 limit;字段为空时沿用 Mall 订单服务默认超时窗口和单次数量口径。该命令适合在自动调度异常、人工排障或跨模块补偿演练时,通过 Outbox 运维页统一入队、重放和观察执行状态。

Blog 文章状态补偿命令的 payload 支持 articleIdstatuspublishedAt;也可直接复用 Blog 文章状态 Outbox payload 中的 currentStatus / currentPublishedAt 字段。articleId 缺失时会尝试读取补偿命令的 aggregateId,便于从文章聚合维度重建命令。

Blog 评论状态补偿命令的 payload 支持 commentIdidstatus;也可复用状态事件常见 payload 中的 currentStatus 字段。commentId 缺失时会尝试读取补偿命令的 aggregateId,handler 只允许重放 Blog 评论已发布 / 隐藏状态。

Forum 举报状态补偿命令的 payload 支持 reportIdstatusoperatorIdreasoncontentextraJson;也可直接复用 Forum 举报状态 Outbox payload 中的 currentStatus / operatorUserId 字段。reportId 缺失时会尝试读取补偿命令的 aggregateId,便于从举报聚合维度重建命令。

CMS 内容状态补偿命令的 payload 支持 contentIdstatusvisibleAtexpiredAt;也可复用内容状态事件常见 payload 中的 currentStatuscurrentVisibleAtcurrentExpiredAt 字段。contentId 缺失时会尝试读取补偿命令的 aggregateId,便于从内容聚合维度重建命令。

Short Link 状态补偿命令的 payload 支持 shortLinkIdlinkIdidstatus;也可复用状态事件常见 payload 中的 currentStatus 字段。shortLinkId 缺失时会尝试读取补偿命令的 aggregateId,handler 会先读取短链接当前详情,再使用 accessPasswordAction=keep 构造保存命令,只重放启用 / 停用状态并保留原短码、目标 URL、UTM、过期时间和访问密码。

Live Code 活码状态补偿命令的 payload 支持 liveCodeIdcodeIdidstatus;也可复用状态事件常见 payload 中的 currentStatus 字段。liveCodeId 缺失时会尝试读取补偿命令的 aggregateId,handler 会先读取活码当前详情,再使用 accessPasswordAction=keep 构造保存命令,只重放启用 / 禁用状态并保留原编码、目标 URL、规则分流、二维码样式、有效期和访问密码。

Open Platform 应用状态补偿命令的 payload 支持 appIdapplicationIdidstatus;也可复用状态事件常见 payload 中的 currentStatus 字段。appId 缺失时会尝试读取补偿命令的 aggregateId,handler 会先读取应用当前详情,再构造保存命令,只重放启用 / 禁用状态并保留名称、套餐、白名单、限流、日 / 月配额和备注。

Open Platform 套餐状态补偿命令的 payload 支持 packageIdplanIdidstatus;也可复用状态事件常见 payload 中的 currentStatus 字段。packageId 缺失时会尝试读取补偿命令的 aggregateId,handler 会先读取套餐当前详情和阶梯价格,再构造保存命令,只重放启用 / 禁用状态并保留套餐编码、名称、配额、单价、币种、有效期、排序、备注和阶梯价格。

后续边界

Admin 前端运维页、跨业务失败观测、补偿命令失败观测、补偿命令执行器与调度任务、补偿命令失败通知规则事件源、支付补偿命令 handler、Mall 超时订单释放补偿命令 handler、Blog 文章状态补偿命令 handler、Blog 评论状态补偿命令 handler、Forum 举报状态补偿命令 handler、CMS 内容状态补偿命令 handler、Short Link 状态补偿命令 handler、Live Code 活码状态补偿命令 handler、Open Platform 应用状态补偿命令 handler、Open Platform 套餐状态补偿命令 handler、补偿命令执行器失败后恢复重放演练、支付补偿命令执行器成功 / 失败演练、支付回调消费方适配、用户余额流水生产方适配、Web3 支付结算生产方适配、Mall / Blog / Forum 状态消费桥接和 Audit 消费桥接已完成;更多补偿命令业务 handler 适配和其它跨业务失败演练仍属于后续 P1-FDN-05 功能簇。

Released under the Apache License 2.0.