11. 异步任务与补偿【基础探测通过,待数据联调】

场景 处理入口
订单超时关闭 php bin/hyperf.php tieup:async:compensate order-timeout --order_no=TO... --limit=100
支付成功后置处理 php bin/hyperf.php tieup:async:compensate payment-delivery --order_no=TO... --limit=100,兼容原 tieup:payment-delivery:repair
已验签失败回调重放 php bin/hyperf.php tieup:async:compensate callback-replay --notify_no=TN... --limit=100
声网 RTC 已验签失败回调重放 php bin/hyperf.php tieup:async:compensate shengwang-replay --callback_no=SWC... --limit=100
融云在线状态失败事件重放 php bin/hyperf.php tieup:async:compensate online-replay --event_hash=... --limit=100
七牛上传已验签失败回调重放 php bin/hyperf.php tieup:async:compensate qiniu-replay --callback_no=QN... --limit=100
退款处理 php bin/hyperf.php tieup:async:compensate refund --refund_no=RF... --limit=100
视频通话爽约判定 php bin/hyperf.php tieup:async:compensate video-no-show --booking_no=VC... --limit=100
视频通话到点结束 php bin/hyperf.php tieup:async:compensate video-call-finish --booking_no=VC... --limit=100
视频审核违规中断补偿 php bin/hyperf.php tieup:async:compensate video-moderation-repair --booking_no=VC... --moderation_no=VM... --limit=100
超级群成员同步 按艺人:php bin/hyperf.php tieup:async:compensate im-members --artist_id=1 --limit=100;定向粉丝:php bin/hyperf.php tieup:async:compensate im-members --artist_id=1 --fan_user_id=10001 --limit=1
订阅成功消息补偿 php bin/hyperf.php tieup:async:compensate im-subscription-message --subscription_no=SUB... --message_no=MSG... --limit=100;两个定位参数均可单独使用
全量补偿 php bin/hyperf.php tieup:async:compensate all --limit=100
内容审核 机审提交、回调、人工兜底均写审核表

说明:

  • 当前项目已引入 hyperf/async-queue 且启用 Crontab 进程;config/autoload/async_queue.php 使用 Redis 队列消费 TieupAsyncCompensationJobConsumerProcess 负责消费,config/autoload/crontab.php 注册默认兜底补偿任务。
  • 队列触发点:支付成功后置发放失败时投递 payment-delivery 单订单补偿;支付/退款/Apple 服务端通知验签通过但业务处理失败时投递 callback-replay 单日志重放;七牛上传回调验签通过但业务处理失败时投递 qiniu-replay 单日志重放;订阅权益发放后 IM 成员同步失败时投递 im-members 单艺人补偿,并投递 im-subscription-message 单订阅消息发送;每分钟订阅到期任务成功把权益更新为已过期后,会按 artist_id + fan_user_id 去重投递 im-members 单成员同步;视频通话预约创建成功后投递延迟 video-no-show 单预约爽约检查,双方爽约生成全额退款单后立即投递 refund 单笔退款;双方开始通话后投递延迟 video-call-finish 单预约到点结束。视频审核违规回调处理时若本地通话已结束但声网频道解散或融云系统消息通知失败,后续由 video-moderation-repair 补偿重试。粉丝画像和回复计数变化不再投递融云消息扩展更新任务。投递失败只记录对应业务日志,不回滚支付、订阅、消息回复计数、预约或上传回调主链路,并由对应 Crontab 继续兜底。
  • 默认定时任务:订单超时关闭、支付发放补偿、订阅到期状态补偿、订阅成功消息补偿和视频通话到点结束每分钟扫描;已验签失败回调重放每 2 分钟扫描;融云在线状态失败事件、七牛上传失败回调、视频爽约判定、视频审核违规中断补偿和退款补偿每 5 分钟扫描;IM 超级群成员同步每小时扫描。IM 消息扩展补偿已停用。所有默认定时任务均启用 singletononOneServer,避免多进程或多节点重复执行。
  • 补偿服务统一落在 TieupAsyncCompensationService,失败日志使用 tieup-async-compensation 日志通道,关键字段包含订单号、预约号、退款号、艺人 ID、粉丝 ID、业务类型、业务 ID 和失败原因。
  • 订单超时关闭仅处理 tieup_order.status in (0,1) 的超时订单,并同步关闭待支付 tieup_user_recharge
  • 支付发放补偿复用正式发放逻辑,支持订阅权益、糖果到账和视频通话预约激活,重复执行由业务单和流水幂等保护。
  • 订阅到期状态补偿按 status=1 AND expires_at<=当前时间 使用组合索引小批量扫描,逐条加行锁并二次检查后更新为状态 2;事务提交后立即按艺人和粉丝定向投递超级群成员重对账,同一批同一成员只投递一次。队列投递失败不会回滚订阅状态,结果会记录 im_member_dispatch_* 计数,并由每小时全量成员任务兜底。人工可执行 php bin/hyperf.php tieup:async:compensate subscription-expiry --subscription_no=SUB... --limit=1000,不指定订阅号时用于历史数据分批修复。
  • 回调重放只处理 tieup_payment_notify_log.verify_status = 1handle_status = 2 且存在 normalized_payload 的支付、退款和 Apple 服务端通知日志;验签失败、未标准化或原始请求不完整的日志不会重放,避免绕过第三方验签。重放仍复用正式订单、退款和订阅状态机,重复执行由订单状态、支付流水、退款单和订阅唯一关系幂等保护。
  • 融云在线状态重放只处理已入库的 tieup_im_online_event.handle_status in (2,3) 事件;用户绑定后可把历史未匹配事件补写到 tieup_user.online_status,用户绑定仍不存在时保持 handle_status = 3。该能力不替代融云原始回调验签,只重试已被回调入口落库的事件。
  • 七牛上传回调重放只处理 tieup_qiniu_upload_callback_log.verify_status = 1handle_status = 2 且存在 normalized_payload 的日志;验签失败、配置缺失、载荷异常或未标准化日志不会重放。重放复用上传记录幂等写入逻辑,重复执行由 tieup_upload_record.upload_nostorage_provider + bucket + object_key 唯一约束保护。
  • 视频爽约按预约开始后 10 分钟判定,只处理状态仍为 1/2started_at 为空的未开始预约;仅粉丝曾进入为艺人爽约并全额退糖果,仅艺人曾进入为粉丝爽约且不退款,双方均未进入或双方都曾进入但始终未开始通话均为双方爽约并全额退糖果。历史上已写 no_show_checked_at 但仍卡在状态 1/2 的记录也会重新进入补偿,已处于爽约终态的历史记录不追补。退款糖果数量按百分之一糖果整数单位计算,生成 tieup_refund_record 后立即投递单笔退款,投递失败由每 5 分钟退款补偿兜底。视频通话到点结束只处理仍为 3=通话中 的预约,完成后写入实际时长并触发收入入账,重复执行由预约状态和收入幂等保护。视频审核违规中断会把仍在通话中的预约更新为 4=已完成 并写实际时长,但按异常完成处理,不触发视频通话收入入账。
  • IM 成员同步会按有效订阅加入超级群,过期或失效成员调用融云移出接口并更新 tieup_artist_im_group_memberim-members 同时提供按艺人全量模式和 artist_id + fan_user_id 定向模式,显式传入 fan_user_id 时必须同时提供有效 artist_id,两个值都必须是正整数。定向任务执行时重新检查该粉丝对艺人的全部订阅,存在其他有效周期或并发续费时保持/恢复成员;无有效订阅时移出,移出后再次检查并在续费已生效时立即重新加入。移出失败会标记 member_status = 3failed_reason,顶层 failed_count 会触发 async-queue 按 10、30、60、300 秒重试;每小时全量成员任务继续作为最终一致性兜底。
  • 当前微信、支付宝主动现金退款网关已封装请求;现金退款补偿会先将本地退款单置为退款中并记录第三方响应,后续扫描 status = 2 的退款单时不重复发起退款,而是调用网关退款查询接口。查询确认成功后同步 tieup_refund_recordtieup_ordertieup_payment_transaction 和订单事件;查询返回失败或关闭时更新退款单失败/关闭状态;查询接口或本地同步临时异常会在 tieup-async-compensation 日志中记录 refund_noorder_notransaction_nopayment_methodpayment_account_id、业务类型和失败阶段,并保留退款中状态等待下轮补偿。糖果退款可直接通过糖果账户服务补偿。