diff --git a/app/Http/Controllers/Admin/PlatformOrderController.php b/app/Http/Controllers/Admin/PlatformOrderController.php index 54e4c35..b37498a 100644 --- a/app/Http/Controllers/Admin/PlatformOrderController.php +++ b/app/Http/Controllers/Admin/PlatformOrderController.php @@ -1101,18 +1101,19 @@ class PlatformOrderController extends Controller } }) ->when(($filters['reconcile_mismatch'] ?? '') !== '', function (Builder $builder) { - // 只看“对账不一致”的订单(粗版):支付回执总额(优先 payment_summary.total_amount)与订单 paid_amount 不一致 + // 只看“对账不一致”的订单:支付回执总额 与订单 paid_amount 不一致 + // 口径:优先使用 meta.payment_summary.total_amount;若为空,则回退汇总 meta.payment_receipts[].amount // 注意:该筛选需要读取 JSON 字段,MySQL/SQLite 写法略有差异。 $driver = $builder->getQuery()->getConnection()->getDriverName(); if ($driver === 'sqlite') { // sqlite 下 JSON_EXTRACT 直接返回标量(数值或字符串),这里用“按分”取整避免浮点误差导致 0.01 边界不稳定 - $builder->whereRaw("JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NOT NULL") - ->whereRaw("ABS(ROUND(CAST(JSON_EXTRACT(meta, '$.payment_summary.total_amount') AS REAL) * 100) - ROUND(paid_amount * 100)) >= 1"); + // total_cents = (payment_summary.total_amount 存在 ? summary*100 : sum(payment_receipts[].amount)*100) + $builder->whereRaw("ABS(ROUND((CASE WHEN JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NOT NULL THEN CAST(JSON_EXTRACT(meta, '$.payment_summary.total_amount') AS REAL) ELSE (SELECT IFNULL(SUM(CAST(JSON_EXTRACT(value, '$.amount') AS REAL)), 0) FROM json_each(COALESCE(JSON_EXTRACT(meta, '$.payment_receipts'), '[]'))) END) * 100) - ROUND(paid_amount * 100)) >= 1"); } else { // MySQL 下 JSON_EXTRACT 返回 JSON,需要 JSON_UNQUOTE 再 cast;同样按分取整避免浮点误差 - $builder->whereRaw("JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NOT NULL") - ->whereRaw("ABS(ROUND(CAST(JSON_UNQUOTE(JSON_EXTRACT(meta, '$.payment_summary.total_amount')) AS DECIMAL(12,2)) * 100) - ROUND(paid_amount * 100)) >= 1"); + // total_cents = (payment_summary.total_amount 存在 ? summary*100 : SUM(payment_receipts[].amount)*100) + $builder->whereRaw("ABS(ROUND((CASE WHEN JSON_EXTRACT(meta, '$.payment_summary.total_amount') IS NOT NULL THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(meta, '$.payment_summary.total_amount')) AS DECIMAL(12,2)) ELSE (SELECT IFNULL(SUM(j.amount), 0) FROM JSON_TABLE(meta, '$.payment_receipts[*]' COLUMNS(amount DECIMAL(12,2) PATH '$.amount')) j) END) * 100) - ROUND(paid_amount * 100)) >= 1"); } }); } diff --git a/tests/Feature/AdminPlatformOrderReconcileMismatchFallbackToReceiptsTest.php b/tests/Feature/AdminPlatformOrderReconcileMismatchFallbackToReceiptsTest.php new file mode 100644 index 0000000..81ba755 --- /dev/null +++ b/tests/Feature/AdminPlatformOrderReconcileMismatchFallbackToReceiptsTest.php @@ -0,0 +1,108 @@ +seed(); + + $this->post('/admin/login', [ + 'email' => 'platform.admin@demo.local', + 'password' => 'Platform@123456', + ])->assertRedirect('/admin'); + } + + public function test_reconcile_mismatch_filter_falls_back_to_payment_receipts_when_no_payment_summary(): void + { + $this->loginAsPlatformAdmin(); + + $merchant = Merchant::query()->firstOrFail(); + $plan = Plan::query()->create([ + 'code' => 'reconcile_mismatch_receipts_fallback', + 'name' => '对账不一致回退回执汇总测试', + 'billing_cycle' => 'monthly', + 'price' => 10, + 'list_price' => 10, + 'status' => 'active', + 'sort' => 10, + 'published_at' => now(), + ]); + + // 不一致:paid_amount=10,但 payment_receipts 合计=9.99(且没有 payment_summary) + PlatformOrder::query()->create([ + 'merchant_id' => $merchant->id, + 'plan_id' => $plan->id, + 'order_no' => 'PO_RECON_FALLBACK_MISMATCH_0001', + 'order_type' => 'new_purchase', + 'status' => 'activated', + 'payment_status' => 'paid', + 'plan_name' => $plan->name, + 'billing_cycle' => $plan->billing_cycle, + 'period_months' => 1, + 'quantity' => 1, + 'payable_amount' => 10, + 'paid_amount' => 10, + 'placed_at' => now(), + 'paid_at' => now(), + 'activated_at' => now(), + 'meta' => [ + 'payment_receipts' => [ + [ + 'type' => 'bank_transfer', + 'channel' => 'bank', + 'amount' => 9.99, + 'paid_at' => now()->toDateTimeString(), + 'created_at' => now()->toDateTimeString(), + 'admin_id' => 1, + ], + ], + ], + ]); + + // 一致:paid_amount=10,payment_receipts 合计=10(且没有 payment_summary) + PlatformOrder::query()->create([ + 'merchant_id' => $merchant->id, + 'plan_id' => $plan->id, + 'order_no' => 'PO_RECON_FALLBACK_MATCH_0002', + 'order_type' => 'new_purchase', + 'status' => 'activated', + 'payment_status' => 'paid', + 'plan_name' => $plan->name, + 'billing_cycle' => $plan->billing_cycle, + 'period_months' => 1, + 'quantity' => 1, + 'payable_amount' => 10, + 'paid_amount' => 10, + 'placed_at' => now(), + 'paid_at' => now(), + 'activated_at' => now(), + 'meta' => [ + 'payment_receipts' => [ + [ + 'type' => 'bank_transfer', + 'channel' => 'bank', + 'amount' => 10.00, + 'paid_at' => now()->toDateTimeString(), + 'created_at' => now()->toDateTimeString(), + 'admin_id' => 1, + ], + ], + ], + ]); + + $this->get('/admin/platform-orders?reconcile_mismatch=1') + ->assertOk() + ->assertSee('PO_RECON_FALLBACK_MISMATCH_0001') + ->assertDontSee('PO_RECON_FALLBACK_MATCH_0002'); + } +}