From fb4d1fb99edcf5134909cf3c1f96704b841d56d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=9D=E5=8D=9C?= Date: Wed, 11 Mar 2026 06:25:43 +0000 Subject: [PATCH] refactor: centralize refund total and refund inconsistent logic on PlatformOrder --- .../Admin/SiteSubscriptionController.php | 26 +--- app/Models/PlatformOrder.php | 35 +++++ .../PlatformOrderRefundInconsistentTest.php | 144 ++++++++++++++++++ 3 files changed, 181 insertions(+), 24 deletions(-) create mode 100644 tests/Unit/PlatformOrderRefundInconsistentTest.php diff --git a/app/Http/Controllers/Admin/SiteSubscriptionController.php b/app/Http/Controllers/Admin/SiteSubscriptionController.php index 334fd3e..80c405e 100644 --- a/app/Http/Controllers/Admin/SiteSubscriptionController.php +++ b/app/Http/Controllers/Admin/SiteSubscriptionController.php @@ -77,13 +77,7 @@ class SiteSubscriptionController extends Controller $noReceiptOrders++; } - $refundTotal = (float) (data_get($meta, 'refund_summary.total_amount') ?? 0); - if ($refundTotal <= 0) { - $refunds = (array) (data_get($meta, 'refund_receipts', []) ?? []); - foreach ($refunds as $r) { - $refundTotal += (float) (data_get($r, 'amount') ?? 0); - } - } + $refundTotal = (float) $o->refundTotal(); if ($refundTotal > 0) { $refundOrders++; @@ -92,23 +86,7 @@ class SiteSubscriptionController extends Controller $noRefundOrders++; } - // refund_inconsistent 口径: - // 1) payment_status=refunded 但 refund_total < paid_amount(按分取整 + 0.01 容差) - // 2) payment_status!=refunded 且 paid_amount>0 且 refund_total >= paid_amount - $paidAmount = (float) ($o->paid_amount ?? 0); - $paymentStatus = (string) ($o->payment_status ?? ''); - $refundTotalCents = (int) round($refundTotal * 100); - $paidCents = (int) round($paidAmount * 100); - - $isRefundInconsistent = false; - if ($paymentStatus === 'refunded') { - // 允许 0.01 容差:refund_total + 0.01 < paid - $isRefundInconsistent = ($refundTotalCents + 1) < $paidCents; - } else { - $isRefundInconsistent = $paidCents > 0 && $refundTotalCents >= $paidCents; - } - - if ($isRefundInconsistent) { + if ($o->isRefundInconsistent()) { $refundInconsistentOrders++; } } diff --git a/app/Models/PlatformOrder.php b/app/Models/PlatformOrder.php index 22306c7..d199b45 100644 --- a/app/Models/PlatformOrder.php +++ b/app/Models/PlatformOrder.php @@ -10,6 +10,41 @@ class PlatformOrder extends Model { use HasFactory; + public function refundTotal(): float + { + // 优先读扁平字段 refund_summary.total_amount + $total = data_get($this->meta, 'refund_summary.total_amount'); + if ($total !== null) { + return (float) $total; + } + + // 回退:遍历 refund_receipts[].amount + $refunds = (array) (data_get($this->meta, 'refund_receipts', []) ?? []); + $sum = 0.0; + foreach ($refunds as $r) { + $sum += (float) (data_get($r, 'amount') ?? 0); + } + + return $sum; + } + + public function isRefundInconsistent(): bool + { + // 口径与平台订单列表 refund_inconsistent 保持一致:按分取整 + 0.01 容差 + $refundTotal = (float) $this->refundTotal(); + $paidAmount = (float) ($this->paid_amount ?? 0); + + $refundCents = (int) round($refundTotal * 100); + $paidCents = (int) round($paidAmount * 100); + + if ((string) $this->payment_status === 'refunded') { + // 允许 0.01 容差:refund_total + 0.01 < paid + return ($refundCents + 1) < $paidCents; + } + + return $paidCents > 0 && $refundCents >= $paidCents; + } + protected $fillable = [ 'merchant_id', 'plan_id', 'site_subscription_id', 'created_by_admin_id', 'order_no', 'order_type', 'status', 'payment_status', 'payment_channel', 'plan_name', 'billing_cycle', 'period_months', 'quantity', 'list_amount', diff --git a/tests/Unit/PlatformOrderRefundInconsistentTest.php b/tests/Unit/PlatformOrderRefundInconsistentTest.php new file mode 100644 index 0000000..2face1a --- /dev/null +++ b/tests/Unit/PlatformOrderRefundInconsistentTest.php @@ -0,0 +1,144 @@ +create([ + 'name' => '单测站点', + 'slug' => 'unit-test-merchant', + 'status' => 'active', + ]); + + $plan = Plan::query()->create([ + 'code' => 'unit_test_plan_refund_total', + 'name' => '单测套餐', + 'billing_cycle' => 'monthly', + 'price' => 10, + 'list_price' => 10, + 'status' => 'active', + 'sort' => 10, + 'published_at' => now(), + ]); + + $order = PlatformOrder::query()->create([ + 'merchant_id' => $merchant->id, + 'plan_id' => $plan->id, + 'order_no' => 'PO_UNIT_REFUND_TOTAL_0001', + 'order_type' => 'subscription', + 'status' => 'activated', + 'payment_status' => 'paid', + 'plan_name' => 'Unit Test Plan', + 'billing_cycle' => 'monthly', + 'period_months' => 1, + 'quantity' => 1, + 'payable_amount' => 10, + 'paid_amount' => 10, + 'placed_at' => now(), + 'meta' => [ + 'refund_receipts' => [ + ['amount' => 3.20], + ['amount' => 1.30], + ], + ], + ]); + + $this->assertEquals(4.50, $order->refundTotal()); + } + + public function test_is_refund_inconsistent_when_status_refunded_but_refund_total_is_less_than_paid_amount(): void + { + $merchant = Merchant::query()->create([ + 'name' => '单测站点2', + 'slug' => 'unit-test-merchant-2', + 'status' => 'active', + ]); + + $plan = Plan::query()->create([ + 'code' => 'unit_test_plan_refund_inconsistent_1', + 'name' => '单测套餐2', + 'billing_cycle' => 'monthly', + 'price' => 10, + 'list_price' => 10, + 'status' => 'active', + 'sort' => 10, + 'published_at' => now(), + ]); + + $order = PlatformOrder::query()->create([ + 'merchant_id' => $merchant->id, + 'plan_id' => $plan->id, + 'order_no' => 'PO_UNIT_REFUND_INCONSISTENT_0001', + 'order_type' => 'subscription', + 'status' => 'activated', + 'payment_status' => 'refunded', + 'plan_name' => 'Unit Test Plan', + 'billing_cycle' => 'monthly', + 'period_months' => 1, + 'quantity' => 1, + 'payable_amount' => 10, + 'paid_amount' => 10, + 'placed_at' => now(), + 'meta' => [ + 'refund_summary' => [ + 'total_amount' => 9.98, + ], + ], + ]); + + $this->assertTrue($order->isRefundInconsistent()); + } + + public function test_is_refund_inconsistent_when_status_not_refunded_but_refund_total_reaches_paid_amount(): void + { + $merchant = Merchant::query()->create([ + 'name' => '单测站点3', + 'slug' => 'unit-test-merchant-3', + 'status' => 'active', + ]); + + $plan = Plan::query()->create([ + 'code' => 'unit_test_plan_refund_inconsistent_2', + 'name' => '单测套餐3', + 'billing_cycle' => 'monthly', + 'price' => 10, + 'list_price' => 10, + 'status' => 'active', + 'sort' => 10, + 'published_at' => now(), + ]); + + $order = PlatformOrder::query()->create([ + 'merchant_id' => $merchant->id, + 'plan_id' => $plan->id, + 'order_no' => 'PO_UNIT_REFUND_INCONSISTENT_0002', + 'order_type' => 'subscription', + 'status' => 'activated', + 'payment_status' => 'paid', + 'plan_name' => 'Unit Test Plan', + 'billing_cycle' => 'monthly', + 'period_months' => 1, + 'quantity' => 1, + 'payable_amount' => 10, + 'paid_amount' => 10, + 'placed_at' => now(), + 'meta' => [ + 'refund_summary' => [ + 'total_amount' => 10.00, + ], + ], + ]); + + $this->assertTrue($order->isRefundInconsistent()); + } +}