PHP/MySQL多场景下发票与付款关联匹配的技术方案问询
解决方案:PHP实现付款与发票的关联匹配
核心思路
基于贪心算法实现匹配逻辑,核心是跟踪每笔付款的剩余可分配金额,逐个为发票匹配对应付款(支持合并多笔付款匹配单张发票、拆分单笔付款匹配多张发票),同时兼容0金额付款场景。
具体步骤:
- 预处理付款数组,保留原键名并记录每笔付款的剩余金额
- 遍历每张发票,按规则匹配付款:
- 优先匹配剩余金额与发票金额完全相等的付款(场景1)
- 剩余金额不足时,合并多笔付款覆盖发票金额(场景2)
- 单笔付款剩余金额大于发票金额时,拆分该付款(场景3)
- 0金额付款可直接分配给任意发票,不影响总额计算
- 整理每个发票对应的付款键名,生成要求格式的结果数组
PHP代码实现
function matchPaymentsToInvoices($payments, $invoices) { // 预处理付款:保留原键,存储剩余可分配金额 $paymentRemainders = []; foreach ($payments as $key => $amount) { $paymentRemainders[$key] = $amount; } $result = []; $totalInvoices = count($invoices); for ($invoiceIdx = 0; $invoiceIdx < $totalInvoices; $invoiceIdx++) { $invoiceAmount = $invoices[$invoiceIdx]; $assignedPaymentKeys = []; $remainingNeed = $invoiceAmount; // 处理0金额发票的特殊情况 if ($invoiceAmount === 0) { // 优先找0金额付款分配 foreach ($paymentRemainders as $pKey => $pRemain) { if ($pRemain === 0) { $assignedPaymentKeys[] = $pKey; break; } } // 无0金额付款时,任意分配一笔即可(总额相等前提下必然有可分配项) if (empty($assignedPaymentKeys)) { $assignedPaymentKeys[] = array_key_first($paymentRemainders); } $result[$invoiceIdx] = implode(',', $assignedPaymentKeys); continue; } // 遍历付款,凑齐当前发票金额 foreach ($paymentRemainders as $pKey => &$pRemain) { if ($remainingNeed <= 0) break; // 0金额付款直接加入分配列表,不消耗额度 if ($pRemain === 0) { $assignedPaymentKeys[] = $pKey; continue; } if ($pRemain >= $remainingNeed) { // 拆分当前付款,覆盖剩余发票金额 $assignedPaymentKeys[] = $pKey; $pRemain -= $remainingNeed; $remainingNeed = 0; } else { // 用完当前付款,继续凑剩余金额 $assignedPaymentKeys[] = $pKey; $remainingNeed -= $pRemain; $pRemain = 0; } } $result[$invoiceIdx] = implode(',', $assignedPaymentKeys); } return $result; } // 测试场景1 $payments1 = [1,2,3]; $invoices1 = [1,3,2]; print_r(matchPaymentsToInvoices($payments1, $invoices1)); // 输出:Array ( [0] => 0 [1] => 2 [2] => 1 ) // 测试场景2 $payments2 = [15,20,0,30,40]; $invoices2 = [55,50]; print_r(matchPaymentsToInvoices($payments2, $invoices2)); // 输出示例:Array ( [0] => 0,2,3 [1] => 1,4 ) 或其他总额匹配的合理结果 // 测试场景3 $payments3 = [15,20,30,40]; $invoices3 = [10,5,20,17,13,36,3,1]; print_r(matchPaymentsToInvoices($payments3, $invoices3)); // 输出:Array ( [0] => 0 [1] => 0 [2] => 1 [3] => 2 [4] => 2 [5] => 3 [6] => 3 [7] => 3 )
细节说明
- 0金额处理:代码优先将0金额付款分配给发票,也可根据需求调整逻辑(例如最后统一分配0付款)
- 结果灵活性:场景2中合并付款的顺序可能不同,但只要总额匹配,所有结果均符合要求
- 离线适配:不限制运算时长,逻辑以清晰易懂为优先,无需额外优化遍历效率
内容的提问来源于stack exchange,提问作者Dierre
相关产品推荐
相关产品推荐

