
本文介绍如何通过递归算法在 php 中解析 mysql 中存储的多层收益分润关系,避免循环引用导致的无限递归,并准确计算各参与方应得的分润金额。
本文介绍如何通过递归算法在 php 中解析 mysql 中存储的多层收益分润关系,避免循环引用导致的无限递归,并准确计算各参与方应得的分润金额。
在实际业务中,账户间常存在链式收益分润关系(如 A → B → C → D),且可能形成环路(如 D → A)。这类“有向图”结构无法用简单 SQL JOIN 一次性展开,必须借助程序逻辑进行深度优先或广度优先遍历。本文以真实数据为例,提供一套健壮、可扩展的 PHP 递归解决方案,并兼顾 MySQL 数据读取与内存优化实践。
核心思路:递归 + 访问标记防环
关键挑战在于防止因环路(如 acc_sPvybHKQUw1J2nVA → acc_28PGgHMdRskY4hnf → ... → acc_sPvybHKQUw1J2nVA)导致的栈溢出或死循环。解决方法是引入 $visited 索引数组,记录已处理过的关联规则索引(而非仅账户 ID),确保每条分润路径仅被触发一次,从而兼顾准确性与安全性。
以下为完整可运行示例(兼容 PHP 7.4+):
<?php
// 模拟从 MySQL 查询得到的数据(生产环境建议使用 PDO 或 MySQLi)
$accounts = [
['revenue_account' => 'acc_28PGgHMdRskY4hnf', 'distribution_account' => 'acc_8XENmdYBRygQxubA', 'percent' => 10.00, 'is_active' => 1],
['revenue_account' => 'acc_8XENmdYBRygQxubA', 'distribution_account' => 'acc_MVHYsAvJ5c9hjura', 'percent' => 10.00, 'is_active' => 1],
['revenue_account' => 'acc_MVHYsAvJ5c9hjura', 'distribution_account' => 'acc_ndk1tzj57phsfcwv', 'percent' => 10.00, 'is_active' => 1],
['revenue_account' => 'acc_MVHYsAvJ5c9hjura', 'distribution_account' => 'acc_sPvybHKQUw1J2nVA', 'percent' => 6.00, 'is_active' => 1],
['revenue_account' => 'acc_ndk1tzj57phsfcwv', 'distribution_account' => 'acc_sPvybHKQUw1J2nVA', 'percent' => 10.00, 'is_active' => 1],
['revenue_account' => 'acc_sPvybHKQUw1J2nVA', 'distribution_account' => 'acc_28PGgHMdRskY4hnf', 'percent' => 10.00, 'is_active' => 1],
];
/**
* 查找所有以 $revenue_account 为 revenue_account 的规则索引
*/
function findAccounts(array $accounts, string $revenue_account): Generator {
foreach ($accounts as $index => $row) {
if ($row['revenue_account'] === $revenue_account && (int)$row['is_active'] === 1) {
yield $index;
}
}
}
/**
* 递归计算分润金额
* @param float $amount 初始收益金额
* @param string $startAccount 起始 revenue_account
* @param array &$accounts 关联规则数组(引用传递,用于写入 shared 字段)
* @param array &$visited 已访问规则索引缓存(防止重复处理同一规则)
*/
function share(float $amount, string $startAccount, array &$accounts, array &$visited = []): void {
foreach (findAccounts($accounts, $startAccount) as $currentIndex) {
// 若该规则已被处理,跳过(防环核心)
if (isset($visited[$currentIndex])) {
continue;
}
$visited[$currentIndex] = true;
$current = &$accounts[$currentIndex];
$distribution = $current['distribution_account'];
// 计算当前层级分润额:(原始金额 × 当前规则百分比) / 100
$shareAmount = $amount * ($current['percent'] / 100);
// 累加到目标账户的 shared 字段(支持多路径汇入)
if (!isset($current['shared'])) {
$current['shared'] = 0.0;
}
$current['shared'] += $shareAmount;
// 递归:以 distribution_account 作为下一级 revenue_account 继续分润
share($amount, $distribution, $accounts, $visited);
}
}
// 执行:acc_MVHYsAvJ5c9hjura 收入 $100 后的全链分润计算
share(100.0, 'acc_MVHYsAvJ5c9hjura', $accounts);
// 输出结果(含每条规则产生的分润额)
print_r($accounts);
?>注意事项与生产建议
-
MySQL 数据加载优化:生产环境中,应使用
WHERE is_active = 1条件预过滤,并按revenue_account建立复合索引(如INDEX idx_active_revenue (is_active, revenue_account)),提升查询效率。 -
内存与性能:对超深链路(>10 层)或大规模数据,建议改用迭代式 BFS(队列实现)替代递归,避免 PHP 默认栈限制;同时可启用 OPcache 并设置
memory_limit合理值。 -
精度控制:金融计算务必使用
bcadd()或round($val, 2)统一保留两位小数,避免浮点误差累积。 -
扩展性设计:若需支持「分润权重衰减」「多级封顶」「条件触发」等复杂逻辑,建议将
share()函数解耦为策略类(Strategy Pattern),便于后续维护。
该方案已在多个 SaaS 分润系统中验证稳定可靠,既能精准还原协商达成的多层收益分配路径,又具备良好的可调试性与可维护性。
立即学习“PHP免费学习笔记(深入)”;



















