You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PHP和MySQL识别数据集递归收益分成关系并避免死循环

实现方案

不用调整现有表结构即可实现需求,核心是通过链路级已访问节点标记拦截循环引用,同时提前加载规则减少数据库IO,用整数计算规避金额精度问题。

一、表结构相关准备

现有表结构完全满足需求,只需要给revenue_account字段加普通索引,优化单账户规则查询效率即可,不需要新增字段或修改表关系。

二、核心实现逻辑
  • 规则预加载:不要每次递归都查询数据库,脚本启动时一次性拉取所有生效的分成规则,组装成「收益账户=>对应分成规则列表」的内存映射,大幅降低IO开销。如果规则总量超过10万条,也可以改成递归时按账户单查,加了索引之后性能足够。
  • 循环拦截:递归时通过值传递的数组存储当前分账分支已经经过的所有账户,每次处理下一级分成前,先判断分成接收方是否在当前分支的已访问列表中,如果存在说明出现循环引用,直接跳过该条规则终止当前分支递归,从根源避免无限循环。注意必须用值传递数组,不能用引用传递,否则不同分支的已访问标记会互相污染,导致合法的跨分支同节点被误拦截。
  • 精度处理:所有金额计算统一转成最小货币单位(比如美分、分)用整数运算,禁止直接用浮点数乘除,避免浮点数精度误差导致分账金额不平。如果业务需要保留多位小数,可以替换为PHP的bcmath高精度函数计算。

三、可直接运行的PHP代码示例

规则查询SQL:

SELECT revenue_account, distribution_account, percent 
FROM 你的分成表名 
WHERE is_active = 1;

PHP实现代码:

<?php
// 数据库连接
$pdo = new PDO('mysql:host=127.0.0.1;dbname=你的库名;charset=utf8mb4', '数据库账号', '数据库密码');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 加载所有有效分成规则,组装内存映射
$stmt = $pdo->query("SELECT revenue_account, distribution_account, percent FROM 你的分成表名 WHERE is_active = 1");
$ruleMap = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    $revAccount = $row['revenue_account'];
    if (!isset($ruleMap[$revAccount])) {
        $ruleMap[$revAccount] = [];
    }
    $ruleMap[$revAccount][] = [
        'dist_account' => $row['distribution_account'],
        'percent'      => (float)$row['percent']
    ];
}

/**
 * 递归计算全链路分成
 * @param string $currentAccount 当前产生可分收益的账户
 * @param int $amount 当前账户待分账的金额,单位:美分
 * @param array $visitedPath 当前分账分支已遍历的账户列表,用于拦截循环
 * @param array $result 最终分账结果,引用传递累计金额
 * @param array $ruleMap 全量分成规则映射
 * @param int $maxDepth 最大递归深度兜底,防止栈溢出
 * @param int $currentDepth 当前递归深度
 * @return void
 */
function calculateSplit(
    string $currentAccount,
    int $amount,
    array $visitedPath,
    array &$result,
    array $ruleMap,
    int $maxDepth = 20,
    int $currentDepth = 0
): void
{
    // 超过最大递归深度直接终止,兜底防护
    if ($currentDepth > $maxDepth) {
        return;
    }
    // 将当前账户加入已访问路径
    $visitedPath[] = $currentAccount;
    // 当前账户无分成规则,终止分支
    if (!isset($ruleMap[$currentAccount])) {
        return;
    }
    foreach ($ruleMap[$currentAccount] as $rule) {
        $distAccount = $rule['dist_account'];
        // 分成方已在当前链路中,出现循环,直接跳过
        if (in_array($distAccount, $visitedPath, true)) {
            continue;
        }
        // 计算当前规则分成金额,整数运算避免精度问题
        $splitAmount = (int)round($amount * $rule['percent'] / 100);
        if ($splitAmount <= 0) {
            continue;
        }
        // 累计分成金额
        if (!isset($result[$distAccount])) {
            $result[$distAccount] = 0;
        }
        $result[$distAccount] += $splitAmount;
        // 递归处理分成方的下游分账,已访问路径值传递,各分支互不干扰
        calculateSplit(
            $distAccount,
            $splitAmount,
            $visitedPath,
            $result,
            $ruleMap,
            $maxDepth,
            $currentDepth + 1
        );
    }
}

// 业务调用示例:acc_MVHYsAvJ5c9hjura产生100美元收益=10000美分
$splitResult = [];
calculateSplit('acc_MVHYsAvJ5c9hjura', 10000, [], $splitResult, $ruleMap);

// 输出结果(单位转美元)
foreach ($splitResult as $account => $totalCent) {
    echo sprintf("账户%s 应得分成:%.2f美元\n", $account, $totalCent / 100);
}

四、逻辑验证(基于给出的示例数据)

初始账户acc_MVHYsAvJ5c9hjura产生100美元收益时,执行逻辑如下:

  1. 第一分支:主账户给acc_ndk1tzj57phsfcwv分10%(10美元)→ 该账户给acc_sPvybHKQUw1J2nVA分10%(1美元)→ 该账户给acc_28PGgHMdRskY4hnf分10%(0.1美元)→ 该账户给acc_8XENmdYBRygQxubA分10%(0.01美元)→ 该账户的分成方是初始账户acc_MVHYsAvJ5c9hjura,检测到已在当前链路中,直接终止该分支,不会循环。
  2. 第二分支:主账户给acc_sPvybHKQUw1J2nVA分6%(6美元)→ 该账户给acc_28PGgHMdRskY4hnf分10%(0.6美元)→ 后续链路同上,检测到循环时自动终止。
    两个分支独立计算,不会互相干扰,也不会出现无限递归。

内容的提问来源于stack exchange,提问作者user1730452

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 05:06:18