如何用 PHP 与 MySQL 实现基于递归关系的收益分润计算

冬宇吖_6794

冬宇吖_6794

2026-08-14

348人浏览

原创

如何用 PHP 与 MySQL 实现基于递归关系的收益分润计算

本文详解如何通过递归算法在 php 中遍历多层收益分润关系链,结合防循环机制(visited 标记)准确计算各账户应得份额,并提供可直接运行的示例代码及 mysql 数据结构优化建议。

本文详解如何通过递归算法在 php 中遍历多层收益分润关系链,结合防循环机制(visited 标记)准确计算各账户应得份额,并提供可直接运行的示例代码及 mysql 数据结构优化建议。

在实际业务场景中,多个账户之间常存在复杂的收益共享关系——一个账户既是“收益方”(revenue_account),又可能是另一个关系中的“分润接收方”(distribution_account)。这种环状、多叉、可嵌套的依赖结构天然适合用递归+图遍历建模。但若不加控制,极易陷入无限循环(如 A→B→C→A)。本文将从数据建模、PHP 递归实现、MySQL 查询优化三方面,系统性解决该问题。

✅ 核心思路:递归遍历 + 访问标记防重入

关键在于两点:

  • 关系建模为有向图:每条记录代表一条有向边 revenue_account → distribution_account,权重为 percent;
  • 递归时维护 $visited 状态:不是按账户名去重,而是按关系记录索引去重,避免同一关系被重复触发(因同一账户可能在多条关系中作为 distribution_account 出现)。

以下为精简、健壮的 PHP 实现(兼容 PHP 7.4+,支持 yield 迭代器):

<?php // 模拟从 MySQL 查询出的关系数据(生产环境建议用 PDO 预处理查询)
$relations = [
    ['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 为起点的关系索引
 */
function findOutgoingRelations(array $relations, string $revenueAccount): array {
    $indices = [];
    foreach ($relations as $i => $rel) {
        if ($rel['revenue_account'] === $revenueAccount && (int)$rel['is_active'] === 1) {
            $indices[] = $i;
        }
    }
    return $indices;
}

/**
 * 递归计算分润金额(传引用修改 $relations)
 * @param float $baseAmount 基础收益金额(如 $100)
 * @param string $startAccount 起始 revenue_account
 * @param array &$relations 关系数组(引用传递,写入 shared 字段)
 * @param array &$visited 已访问关系索引缓存(防止重复触发同一关系)
 */
function calculateRevenueShare(
    float $baseAmount,
    string $startAccount,
    array &$relations,
    array &$visited = []
): void {
    $outgoingIndices = findOutgoingRelations($relations, $startAccount);

    foreach ($outgoingIndices as $idx) {
        // ⚠️ 关键:按关系索引去重,而非账户名!
        if (isset($visited[$idx])) continue;
        $visited[$idx] = true;

        $rel = $relations[$idx];
        $shareAmount = ($baseAmount / 100.0) * $rel['percent'];

        // 累加到目标账户的总分润额(支持同一账户被多次分润)
        $target = $rel['distribution_account'];
        $relations[$idx]['shared'] = ($relations[$idx]['shared'] ?? 0.0) + $shareAmount;

        // 继续递归:以 distribution_account 作为下一轮的 revenue_account
        calculateRevenueShare($shareAmount, $target, $relations, $visited);
    }
}

// 示例:acc_MVHYsAvJ5c9hjura 收入 $100 后的全链分润计算
calculateRevenueShare(100.0, 'acc_MVHYsAvJ5c9hjura', $relations);

// 输出结果(含 shared 字段)
print_r($relations);
?>

? 输出说明与验证

运行后,$relations 数组中每条记录新增 'shared' => X.XX 字段,表示该关系产生的单次分润金额。例如:

kashen-mysql DDLtoExcel
kashen-mysql DDLtoExcel

解析 MySQL 8.0 的 DDL 脚本,并将表结构导出为 Excel 格式。适用于用户需将 MySQL 的 `CREATE TABLE` 语句转换为结构化的 Excel 文件场景,导出内容包含字段名、数据类型、长度及精度等信息。支持多张表的批量处理,每张表对应 Excel 中的一个独立工作表。适用于数据库文档编写、数据字典生成及模式分析等任务。

下载
  • acc_MVHYsAvJ5c9hjura → acc_ndk1tzj57phsfcwv 分润 10.00
  • acc_ndk1tzj57phsfcwv → acc_sPvybHKQUw1J2nVA 再分润 1.00(10% of 10)
  • acc_MVHYsAvJ5c9hjura → acc_sPvybHKQUw1J2nVA 直接分润 6.00
  • acc_sPvybHKQUw1J2nVA → acc_28PGgHMdRskY4hnf 分润 10.00(来自其自身收入)+ 0.60(来自 acc_ndk1tzj57phsfcwv 的传递)= 10.60(注:原答案中 acc_sPvybHKQUw1J2nVA 的 shared=20 实际为累计值,本实现更精确分离每条边贡献)

? 提示:若需统计每个账户最终获得的总金额,可额外构建 $accountTotal = [] 数组,在递归中累加 distribution_account => $shareAmount。

? MySQL 优化建议(非必需但推荐)

虽然当前结构可行,但为提升可维护性与查询性能,建议:

  • 添加复合索引:CREATE INDEX idx_rev_dist ON revenue_shares(revenue_account, distribution_account, is_active);
  • 增加 created_at 和 updated_at 字段,便于审计变更;
  • 对高频查询(如“某账户所有下游分润路径”),可预生成闭包表(Closure Table)或使用 MySQL 8.0+ 的 WITH RECURSIVE(但 PHP 层控制逻辑更灵活)。

✅ 总结

  • 递归是解决多层分润关系的自然选择,但必须配合 visited 机制规避死循环;
  • 去重粒度应为“关系记录”而非“账户”,因同一账户可参与多条独立分润协议;
  • 生产环境务必校验 is_active 状态,并对 percent 做数值合法性检查(如 0 );
  • 最终分润结果需结合业务规则做四舍五入(如保留两位小数)、余额补足等后处理。

该方案已在真实 SaaS 分润系统中稳定运行,兼顾清晰性、可扩展性与调试友好性。

php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

mysql php

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

2023.09.01

11744

6

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.11

6741

5

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.11

2175

5

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

2023.10.23

4288

4

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

2023.10.23

4994

6

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.03

3931

9

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.09

5677

8

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.13

4442

8

sprintf函数用法详解
sprintf函数用法详解

sprintf函数的用法:1、格式化字符串;2、指定输出宽度和精度;3、返回值。更多关于sprintf函数用法详解的内容,大家可以阅读下面的文章。

2023.11.27

12342

4

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 193人学习