fix(partner): 县在馆下时区县拿全额,县以上酒道馆不再发管理奖

后续订单按离买家最近的县结算;馆在县下或同一人时仍按 50+50。已入账订单不重算。

Co-authored-by: Cursor <cursoragent@cursor.com>
这个提交包含在:
chen
2026-10-16 11:42:58 +08:00
父节点 ba33584936
当前提交 092155c645
共有 3 个文件被更改,包括 75 次插入 和 54 次删除

查看文件

@@ -27,7 +27,9 @@ const teamSplitFile = path.resolve(__dirname, '../../addons/shopro/service/partn
assert(fs.existsSync(teamSplitFile), 'TeamSplitReward.php 文件存在');
const code = fs.readFileSync(teamSplitFile, 'utf8');
assert(code.includes('$start = $venue ? $venue[\'index\'] : 0;'), 'nearestCounty 起始查找索引支持从当前酒道馆节点匹配兼职区县代理');
assert(code.includes('$county = $this->nearest($chain, \'is_county\', $active);'), '区县按离买家最近匹配,不再从酒道馆节点起跳');
assert(code.includes('venue_above_county'), '县在馆下时,县以上酒道馆记为无收益');
assert(code.includes('县在馆下'), '县在馆下时区县管理奖走全额公式');
assert(code.includes('馆兼县'), '支持「馆兼县」专属流水明细公式标识');
// 2. 模拟 TeamSplitReward 核心算法逻辑
@@ -44,32 +46,44 @@ class MockTeamSplitReward {
const result = { venue: null, county: null, skips: [] };
const venue = this.nearest(chain, 'is_venue', active);
const county = this.nearestCounty(chain, venue, active);
const county = this.nearest(chain, 'is_county', active);
if (!venue && !county) {
result.skips.push({ type: 'team_venue', reason: 'not_venue', formula: '链上无酒道馆且无区县' });
return result;
}
if (venue) {
const venueAboveCounty = venue && county && venue.index > county.index;
const venueEarns = venue && !venueAboveCounty;
if (venueEarns) {
if (venue.inactive) {
result.skips.push({ type: 'team_venue', user_id: venue.user_id, reason: 'inactive_30d', formula: '酒道馆 30 日无自购' });
} else {
result.venue = { user_id: venue.user_id, amount: venueSplit, index: venue.index };
}
} else if (venueAboveCounty) {
result.skips.push({ type: 'team_venue', user_id: venue.user_id, reason: 'venue_above_county', formula: '县以上酒道馆无收益' });
}
if (county) {
const amount = venue ? venueSplit : countyFull;
const amount = venueEarns ? venueSplit : countyFull;
if (county.inactive) {
result.skips.push({ type: 'team_county', user_id: county.user_id, reason: 'inactive_30d', formula: '区县 30 日无自购' });
} else {
const formulaText = venue
? (venue.user_id === county.user_id ? `区县管理奖 ${amount}(馆兼县)` : `区县管理奖 ${amount}(馆上)`)
: `区县管理奖 ${amount}(直达)`;
let formulaText;
if (venue && venue.user_id === county.user_id) {
formulaText = `区县管理奖 ${amount}(馆兼县)`;
} else if (venueEarns) {
formulaText = `区县管理奖 ${amount}(馆上)`;
} else if (venueAboveCounty) {
formulaText = `区县管理奖 ${amount}(县在馆下)`;
} else {
formulaText = `区县管理奖 ${amount}(直达)`;
}
result.county = { user_id: county.user_id, amount, index: county.index, formula: formulaText };
}
} else if (venue && !county) {
} else if (venue) {
result.skips.push({ type: 'team_county', user_id: 0, reason: 'not_county', formula: '有馆无区县' });
}
@@ -89,22 +103,6 @@ class MockTeamSplitReward {
return null;
}
nearestCounty(chain, venue, active) {
const start = venue ? venue.index : 0;
for (let i = start; i < chain.length; i++) {
if (!chain[i].is_county) continue;
return {
user_id: chain[i].user_id,
index: i,
inactive: !active.isActive(chain[i]),
node: chain[i]
};
}
if (!venue) {
return this.nearest(chain, 'is_county', active);
}
return null;
}
}
const runner = new MockTeamSplitReward();
@@ -184,6 +182,33 @@ const active = new MockActiveService();
assert(res.venue.amount + res.county.amount === 100, '场景6: 自购节点拆分总额 100 元');
}
// 场景 7:县在馆下面(买家 -> 县13 -> 馆12),馆不拿,县拿 100
{
const chain = [
{ user_id: 162, is_venue: 0, is_county: 0 },
{ user_id: 13, is_venue: 0, is_county: 1, is_active: true },
{ user_id: 12, is_venue: 1, is_county: 1, is_active: true }
];
const res = runner.run({ id: 10000046 }, 162, chain, active);
assert(res.venue === null, '场景7: 县以上的酒道馆不发放管理奖');
assert(res.county !== null && res.county.user_id === 13 && res.county.amount === 100, '场景7: 离买家最近的县获得 100 元');
assert(res.county.formula.includes('县在馆下'), '场景7: 公式标识为「区县管理奖 100(县在馆下)」');
assert(res.skips.some(s => s.reason === 'venue_above_county' && s.user_id === 12), '场景7: 记录馆12无收益');
}
// 场景 8:馆在县下面(买家 -> 馆20 -> 县30),馆和县都拿 50
{
const chain = [
{ user_id: 25, is_venue: 0, is_county: 0 },
{ user_id: 20, is_venue: 1, is_county: 0, is_active: true },
{ user_id: 30, is_venue: 0, is_county: 1, is_active: true }
];
const res = runner.run({ id: 18 }, 25, chain, active);
assert(res.venue !== null && res.venue.user_id === 20 && res.venue.amount === 50, '场景8: 县下方的酒道馆获得 50 元');
assert(res.county !== null && res.county.user_id === 30 && res.county.amount === 50, '场景8: 馆之上的县获得 50 元');
assert(res.skips.length === 0, '场景8: 馆在县下时双方都入账');
}
console.log('\n--------------------------------------------------------');
console.log(`测试执行结果: 通过 ${passed} 项, 失败 ${failed} 项`);
console.log('========================================================\n');