fix(contribution): correct TEAM_BONUS distribution to adopter instead of referrer
TEAM_BONUS (7.5% = 2.5% × 3 tiers) should be given to the adopter themselves, not to their direct referrer. The unlock conditions are: - T1 (2.5%): Self adoption (always unlocked when adopting) - T2 (2.5%): 2+ direct referrals adopted - T3 (2.5%): 4+ direct referrals adopted Also fixed findAncestorChain to correctly include the first ancestor in the chain instead of skipping it. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
01ff873264
commit
8d97daa524
|
|
@ -61,12 +61,15 @@ export class ContributionCalculationService {
|
|||
ancestorChain = await this.buildAncestorChain(userReferral.referrerAccountSequence);
|
||||
}
|
||||
|
||||
// 获取上线的算力账户(用于判断解锁状态)
|
||||
// 获取认种人的算力账户(用于判断 TEAM_BONUS 解锁状态)
|
||||
const adopterAccount = await this.contributionAccountRepository.findByAccountSequence(adoption.accountSequence);
|
||||
|
||||
// 获取上线的算力账户(用于判断 TEAM_LEVEL 解锁状态)
|
||||
const ancestorAccountSequences = ancestorChain.map((a) => a.accountSequence);
|
||||
const ancestorAccounts = await this.contributionAccountRepository.findByAccountSequences(ancestorAccountSequences);
|
||||
|
||||
// 执行算力计算
|
||||
const result = this.domainCalculator.calculateAdoptionContribution(adoption, ancestorChain, ancestorAccounts);
|
||||
const result = this.domainCalculator.calculateAdoptionContribution(adoption, adopterAccount, ancestorChain, ancestorAccounts);
|
||||
|
||||
// 在事务中保存所有结果
|
||||
await this.unitOfWork.executeInTransaction(async () => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export interface ContributionDistributionResult {
|
|||
// 团队层级贡献值记录(给上线们的)
|
||||
teamLevelRecords: ContributionRecordAggregate[];
|
||||
|
||||
// 团队额外奖励贡献值记录(给直接上线的)
|
||||
// 团队额外奖励贡献值记录(给认种人自己的)
|
||||
teamBonusRecords: ContributionRecordAggregate[];
|
||||
|
||||
// 未分配的贡献值(归总部)
|
||||
|
|
@ -47,12 +47,14 @@ export class ContributionCalculatorService {
|
|||
* 计算认种产生的贡献值分配
|
||||
*
|
||||
* @param adoption 认种记录
|
||||
* @param adopterAccount 认种人的算力账户(用于判断 TEAM_BONUS 解锁状态)
|
||||
* @param ancestorChain 上线链条(从直接上线开始,最多15级)
|
||||
* @param ancestorAccounts 上线的算力账户(用于判断解锁状态)
|
||||
* @param ancestorAccounts 上线的算力账户(用于判断 TEAM_LEVEL 解锁状态)
|
||||
* @returns 分配结果
|
||||
*/
|
||||
calculateAdoptionContribution(
|
||||
adoption: SyncedAdoption,
|
||||
adopterAccount: ContributionAccountAggregate | null,
|
||||
ancestorChain: SyncedReferral[],
|
||||
ancestorAccounts: Map<string, ContributionAccountAggregate>,
|
||||
): ContributionDistributionResult {
|
||||
|
|
@ -104,6 +106,7 @@ export class ContributionCalculatorService {
|
|||
// 3. 团队贡献值 (15%)
|
||||
this.distributeTeamContribution(
|
||||
adoption,
|
||||
adopterAccount,
|
||||
baseContribution,
|
||||
treeCount,
|
||||
ancestorChain,
|
||||
|
|
@ -121,6 +124,7 @@ export class ContributionCalculatorService {
|
|||
*/
|
||||
private distributeTeamContribution(
|
||||
adoption: SyncedAdoption,
|
||||
adopterAccount: ContributionAccountAggregate | null,
|
||||
baseContribution: ContributionAmount,
|
||||
treeCount: number,
|
||||
ancestorChain: SyncedReferral[],
|
||||
|
|
@ -176,49 +180,41 @@ export class ContributionCalculatorService {
|
|||
}
|
||||
}
|
||||
|
||||
// 3.2 额外奖励部分 (7.5% = 2.5% × 3档) - 只给直接上线
|
||||
if (ancestorChain.length > 0) {
|
||||
const directReferrer = ancestorChain[0];
|
||||
const directReferrerAccount = ancestorAccounts.get(directReferrer.accountSequence);
|
||||
const unlockedBonusTiers = directReferrerAccount?.unlockedBonusTiers ?? 0;
|
||||
// 3.2 额外奖励部分 (7.5% = 2.5% × 3档) - 给认种人自己
|
||||
// T1: 自己认种即解锁(无条件,当前正在认种所以一定解锁)
|
||||
// T2: 直推≥2人认种解锁
|
||||
// T3: 直推≥4人认种解锁
|
||||
const directReferralAdoptedCount = adopterAccount?.directReferralAdoptedCount ?? 0;
|
||||
|
||||
for (let tier = 1; tier <= 3; tier++) {
|
||||
if (unlockedBonusTiers >= tier) {
|
||||
// 上线已解锁该档位
|
||||
result.teamBonusRecords.push(
|
||||
ContributionRecordAggregate.createTeamBonus({
|
||||
accountSequence: directReferrer.accountSequence,
|
||||
sourceAdoptionId: adoption.originalAdoptionId,
|
||||
sourceAccountSequence: adoption.accountSequence,
|
||||
treeCount,
|
||||
baseContribution,
|
||||
bonusTier: tier,
|
||||
effectiveDate,
|
||||
expireDate,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
// 上线未解锁该档位,归总部
|
||||
const bonusAmount = baseContribution.multiply(treeCount).multiply(DistributionRate.BONUS_PER.value);
|
||||
result.unallocatedContributions.push({
|
||||
type: `BONUS_TIER_${tier}`,
|
||||
wouldBeAccountSequence: directReferrer.accountSequence,
|
||||
levelDepth: null,
|
||||
amount: bonusAmount,
|
||||
reason: `上线${directReferrer.accountSequence}未解锁第${tier}档奖励(已解锁${unlockedBonusTiers}档)`,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 没有上线,三个2.5%全部归总部
|
||||
for (let tier = 1; tier <= 3; tier++) {
|
||||
// T1 始终解锁(因为当前正在认种)
|
||||
// T2 需要直推≥2人认种
|
||||
// T3 需要直推≥4人认种
|
||||
const effectiveUnlockedBonusTiers = this.calculateUnlockedBonusTiers(true, directReferralAdoptedCount);
|
||||
|
||||
for (let tier = 1; tier <= 3; tier++) {
|
||||
if (effectiveUnlockedBonusTiers >= tier) {
|
||||
// 认种人已解锁该档位,给认种人自己
|
||||
result.teamBonusRecords.push(
|
||||
ContributionRecordAggregate.createTeamBonus({
|
||||
accountSequence: adoption.accountSequence,
|
||||
sourceAdoptionId: adoption.originalAdoptionId,
|
||||
sourceAccountSequence: adoption.accountSequence,
|
||||
treeCount,
|
||||
baseContribution,
|
||||
bonusTier: tier,
|
||||
effectiveDate,
|
||||
expireDate,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
// 认种人未解锁该档位,归总部(冻结/待领取)
|
||||
const bonusAmount = baseContribution.multiply(treeCount).multiply(DistributionRate.BONUS_PER.value);
|
||||
result.unallocatedContributions.push({
|
||||
type: `BONUS_TIER_${tier}`,
|
||||
wouldBeAccountSequence: null,
|
||||
wouldBeAccountSequence: adoption.accountSequence,
|
||||
levelDepth: null,
|
||||
amount: bonusAmount,
|
||||
reason: `认种人无上线`,
|
||||
reason: `认种人${adoption.accountSequence}未解锁第${tier}档奖励(需直推${tier === 2 ? '2人' : '4人'}认种)`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,18 +247,22 @@ export class SyncedDataRepository implements ISyncedDataRepository {
|
|||
let currentSequence = accountSequence;
|
||||
|
||||
for (let i = 0; i < maxLevel; i++) {
|
||||
// 获取当前账户的 referral 记录(包含该账户作为上线的信息)
|
||||
const referral = await this.findSyncedReferralByAccountSequence(currentSequence);
|
||||
if (!referral || !referral.referrerAccountSequence) {
|
||||
if (!referral) {
|
||||
break;
|
||||
}
|
||||
|
||||
const referrer = await this.findSyncedReferralByAccountSequence(referral.referrerAccountSequence);
|
||||
if (!referrer) {
|
||||
// 把当前账户加入上线链条
|
||||
ancestors.push(referral);
|
||||
|
||||
// 如果没有更上级的推荐人,终止
|
||||
if (!referral.referrerAccountSequence) {
|
||||
break;
|
||||
}
|
||||
|
||||
ancestors.push(referrer);
|
||||
currentSequence = referrer.accountSequence;
|
||||
// 继续向上追溯
|
||||
currentSequence = referral.referrerAccountSequence;
|
||||
}
|
||||
|
||||
return ancestors;
|
||||
|
|
|
|||
Loading…
Reference in New Issue