diff --git a/backend/services/contribution-service/src/application/services/bonus-claim.service.ts b/backend/services/contribution-service/src/application/services/bonus-claim.service.ts index 47f8048c..0b432f0e 100644 --- a/backend/services/contribution-service/src/application/services/bonus-claim.service.ts +++ b/backend/services/contribution-service/src/application/services/bonus-claim.service.ts @@ -591,13 +591,17 @@ export class BonusClaimService { expectedLevelDepth: number, expectedBonusTiers: number, ): Promise { - // 增量更新直推认种数 - const previousCount = account.directReferralAdoptedCount; - if (newDirectReferralAdoptedCount > previousCount) { - for (let i = previousCount; i < newDirectReferralAdoptedCount; i++) { - account.incrementDirectReferralAdoptedCount(); - } - } + // 使用 setDirectReferralAdoptedCount 无条件重算解锁状态。 + // + // 不能用「if (newCount > previousCount) increment」的方式,原因: + // 当直推已全部认种(DB count 已正确)但 markAsAdopted() 在此之后才 + // 被调用时,markAsAdopted 会硬编码 level=5/bonus=1,导致 level 错误, + // 但 count 不变,条件判断永远为 false,backfill 无法修复。 + // + // setDirectReferralAdoptedCount 内部调用 updateUnlockStatus(), + // 无论 count 是否变化都会根据 hasAdopted + count 重新计算正确的 + // unlockedLevelDepth 和 unlockedBonusTiers。 + account.setDirectReferralAdoptedCount(newDirectReferralAdoptedCount); await this.contributionAccountRepository.save(account); diff --git a/backend/services/contribution-service/src/infrastructure/persistence/repositories/synced-data.repository.ts b/backend/services/contribution-service/src/infrastructure/persistence/repositories/synced-data.repository.ts index 3fa94be3..e46d4f4d 100644 --- a/backend/services/contribution-service/src/infrastructure/persistence/repositories/synced-data.repository.ts +++ b/backend/services/contribution-service/src/infrastructure/persistence/repositories/synced-data.repository.ts @@ -291,11 +291,14 @@ export class SyncedDataRepository implements ISyncedDataRepository { const accountSequences = directReferrals.map((r) => r.accountSequence); - // 只统计有 MINING_ENABLED 状态认种记录的直推用户数 + // 解锁阈值按「人头」计:常规认种和预种均以「该直推用户是否有任意一条 + // MINING_ENABLED 记录」为准,每人算 1,不论认种了几棵树或几份预种。 + // 预种 handler 会在用户首次付款时向 synced_adoptions 插入一条 + // treeCount=0、status=MINING_ENABLED 的 marker,确保预种用户也能被计入。 const adoptedCount = await this.client.syncedAdoption.findMany({ where: { accountSequence: { in: accountSequences }, - status: 'MINING_ENABLED', // 只统计最终成功的认种订单 + status: 'MINING_ENABLED', }, distinct: ['accountSequence'], });