From 1506ceb7e8aec44d9d60006c4c52e4d03f378d3a Mon Sep 17 00:00:00 2001 From: zhx Date: Tue, 14 Jul 2026 11:01:36 +0800 Subject: [PATCH] fix(wallet): skip rebate task without tenant config --- .../vip_daily_coin_rebate_integration_test.go | 14 ++++++++++++++ .../mysql/vip_daily_coin_rebate_repository.go | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/services/wallet-service/internal/service/wallet/vip_daily_coin_rebate_integration_test.go b/services/wallet-service/internal/service/wallet/vip_daily_coin_rebate_integration_test.go index c01c2f3e..c4d0b1bb 100644 --- a/services/wallet-service/internal/service/wallet/vip_daily_coin_rebate_integration_test.go +++ b/services/wallet-service/internal/service/wallet/vip_daily_coin_rebate_integration_test.go @@ -14,6 +14,20 @@ import ( "hyapp/services/wallet-service/internal/testutil/mysqltest" ) +func TestVipDailyCoinRebateMissingProgramIsNoOp(t *testing.T) { + repository := mysqltest.NewRepository(t) + service := walletservice.New(repository) + taskDay := time.Now().UTC().Format("2006-01-02") + + result, err := service.ProcessVipDailyCoinRebateBatch( + appcode.WithContext(context.Background(), "huwaa"), + ledger.ProcessVipDailyCoinRebateBatchCommand{AppCode: "huwaa", TaskDay: taskDay, BatchSize: 100}, + ) + if err != nil || result.HasMore || result.CreatedCount != 0 || result.ScannedCount != 0 { + t.Fatalf("missing VIP program must be an empty successful batch: result=%+v err=%v", result, err) + } +} + // TestVipDailyCoinRebateUTCWindowSnapshotAndClaim 覆盖返现最关键的不可变边界: // UTC 日切资格、run 配置快照、分页重放、App 隔离、available outbox 和并发领取仅入账一次。 func TestVipDailyCoinRebateUTCWindowSnapshotAndClaim(t *testing.T) { diff --git a/services/wallet-service/internal/storage/mysql/vip_daily_coin_rebate_repository.go b/services/wallet-service/internal/storage/mysql/vip_daily_coin_rebate_repository.go index b015d5be..95ee7428 100644 --- a/services/wallet-service/internal/storage/mysql/vip_daily_coin_rebate_repository.go +++ b/services/wallet-service/internal/storage/mysql/vip_daily_coin_rebate_repository.go @@ -76,7 +76,12 @@ func (r *Repository) ProcessVipDailyCoinRebateBatch(ctx context.Context, command // 避免两个不存在键的 gap lock 互相等待,并保证金额与 config_version 来自同一快照。 program, lockErr := r.queryVipProgramConfig(ctx, tx.QueryRowContext(ctx, vipProgramSelectSQL()+` FOR UPDATE`, command.AppCode)) if errors.Is(lockErr, sql.ErrNoRows) { - return ledger.ProcessVipDailyCoinRebateBatchResult{}, xerr.New(xerr.NotFound, "vip program config not found") + // 租户注册表驱动的 cron 会覆盖所有启用 App;未配置 VIP 的租户没有返现工作, + // 应按空批次结束,而不是每分钟制造不可重试的 NotFound 告警。 + if err := tx.Commit(); err != nil { + return ledger.ProcessVipDailyCoinRebateBatchResult{}, err + } + return ledger.ProcessVipDailyCoinRebateBatchResult{}, nil } if lockErr != nil { return ledger.ProcessVipDailyCoinRebateBatchResult{}, lockErr