feat(platform-orders): 支持支付回执留痕(meta.payment_receipts)

This commit is contained in:
萝卜
2026-03-10 16:11:38 +00:00
parent 2d5369eeab
commit 9bc1b82e8d
3 changed files with 114 additions and 0 deletions

View File

@@ -302,6 +302,24 @@ class PlatformOrderController extends Controller
$order->paid_at = $order->paid_at ?: $now; $order->paid_at = $order->paid_at ?: $now;
$order->activated_at = $order->activated_at ?: $now; $order->activated_at = $order->activated_at ?: $now;
$order->paid_amount = $order->paid_amount > 0 ? $order->paid_amount : $order->payable_amount; $order->paid_amount = $order->paid_amount > 0 ? $order->paid_amount : $order->payable_amount;
// 兼容:若尚未写入“支付回执”,则自动补一条(可治理)
$meta = (array) ($order->meta ?? []);
$receipts = (array) (data_get($meta, 'payment_receipts', []) ?? []);
if (count($receipts) === 0) {
$receipts[] = [
'type' => 'manual_mark_paid',
'channel' => (string) ($order->payment_channel ?? ''),
'amount' => (float) $order->paid_amount,
'paid_at' => $order->paid_at ? $order->paid_at->format('Y-m-d H:i:s') : $now->toDateTimeString(),
'note' => '由【标记支付并生效】自动补记(可治理)',
'created_at' => $now->toDateTimeString(),
'admin_id' => $admin->id,
];
data_set($meta, 'payment_receipts', $receipts);
$order->meta = $meta;
}
$order->save(); $order->save();
// 立刻同步订阅 // 立刻同步订阅
@@ -328,6 +346,40 @@ class PlatformOrderController extends Controller
return redirect()->back()->with('success', '订单已标记支付并生效,订阅已同步:' . $subscription->subscription_no); return redirect()->back()->with('success', '订单已标记支付并生效,订阅已同步:' . $subscription->subscription_no);
} }
public function addPaymentReceipt(Request $request, PlatformOrder $order): RedirectResponse
{
$admin = $this->ensurePlatformAdmin($request);
$data = $request->validate([
'type' => ['required', 'string', 'max:30'],
'channel' => ['nullable', 'string', 'max:30'],
'amount' => ['required', 'numeric', 'min:0'],
'paid_at' => ['nullable', 'date'],
'note' => ['nullable', 'string', 'max:2000'],
]);
$now = now();
$meta = (array) ($order->meta ?? []);
$receipts = (array) (data_get($meta, 'payment_receipts', []) ?? []);
$receipts[] = [
'type' => (string) $data['type'],
'channel' => (string) ($data['channel'] ?? ''),
'amount' => (float) $data['amount'],
'paid_at' => $data['paid_at'] ? (string) $data['paid_at'] : null,
'note' => (string) ($data['note'] ?? ''),
'created_at' => $now->toDateTimeString(),
'admin_id' => $admin->id,
];
data_set($meta, 'payment_receipts', $receipts);
$order->meta = $meta;
$order->save();
return redirect()->back()->with('success', '已追加支付回执记录(仅用于对账留痕,不自动改状态)。');
}
public function markActivated(Request $request, PlatformOrder $order): RedirectResponse public function markActivated(Request $request, PlatformOrder $order): RedirectResponse
{ {
$admin = $this->ensurePlatformAdmin($request); $admin = $this->ensurePlatformAdmin($request);

View File

@@ -86,6 +86,7 @@
$activation = data_get($order->meta, 'subscription_activation'); $activation = data_get($order->meta, 'subscription_activation');
$activationError = data_get($order->meta, 'subscription_activation_error'); $activationError = data_get($order->meta, 'subscription_activation_error');
$audit = (array) (data_get($order->meta, 'audit', []) ?? []); $audit = (array) (data_get($order->meta, 'audit', []) ?? []);
$paymentReceipts = (array) (data_get($order->meta, 'payment_receipts', []) ?? []);
@endphp @endphp
@php @php
@@ -107,6 +108,64 @@
@endif @endif
</div> </div>
<div class="card mb-20">
<h3>支付回执(对账留痕)</h3>
<p class="muted muted-tight">用于“线下收款/转账/人工核对”的留痕记录(当前阶段先落 meta不引入独立表</p>
@if(count($paymentReceipts) > 0)
@php $items = array_slice(array_reverse($paymentReceipts), 0, 20); @endphp
<table>
<thead>
<tr>
<th style="width:140px;">类型</th>
<th style="width:120px;">渠道</th>
<th style="width:120px;">金额</th>
<th style="width:200px;">支付时间</th>
<th style="width:160px;">记录时间</th>
<th style="width:100px;">管理员</th>
<th>备注</th>
</tr>
</thead>
<tbody>
@foreach($items as $r)
<tr>
<td>{{ data_get($r, 'type') ?: '-' }}</td>
<td>{{ data_get($r, 'channel') ?: '-' }}</td>
<td>¥{{ number_format((float) (data_get($r, 'amount') ?? 0), 2) }}</td>
<td>{{ data_get($r, 'paid_at') ?: '-' }}</td>
<td>{{ data_get($r, 'created_at') ?: '-' }}</td>
<td>{{ data_get($r, 'admin_id') ?: '-' }}</td>
<td class="muted">{{ data_get($r, 'note') ?: '' }}</td>
</tr>
@endforeach
</tbody>
</table>
@else
<p class="muted">暂无支付回执记录。</p>
@endif
<details style="margin-top:12px;">
<summary class="muted">追加一条支付回执(不自动改状态)</summary>
<form method="post" action="/admin/platform-orders/{{ $order->id }}/add-payment-receipt" style="margin-top:10px;">
@csrf
<div class="grid-3">
<input type="text" name="type" placeholder="类型bank_transfer/现金/支付宝等" value="bank_transfer">
<input type="text" name="channel" placeholder="渠道(可选)" value="{{ (string) ($order->payment_channel ?? '') }}">
<input type="number" step="0.01" name="amount" placeholder="金额" value="{{ number_format((float) ($order->paid_amount > 0 ? $order->paid_amount : $order->payable_amount), 2, '.', '') }}">
</div>
<div style="margin-top:10px;">
<input type="text" name="paid_at" placeholder="支付时间YYYY-mm-dd HH:ii:ss可选" value="{{ optional($order->paid_at)->format('Y-m-d H:i:s') }}">
</div>
<div style="margin-top:10px;">
<textarea name="note" placeholder="备注(可选,用于对账说明)" rows="3" style="width:100%;"></textarea>
</div>
<div style="margin-top:10px;">
<button type="submit">追加回执记录</button>
</div>
</form>
</details>
</div>
<div class="card mb-20"> <div class="card mb-20">
<h3>订阅同步记录</h3> <h3>订阅同步记录</h3>
@if($activation) @if($activation)
@@ -145,6 +204,8 @@
$auditActionLabels = [ $auditActionLabels = [
'clear_sync_error' => '清除同步失败标记', 'clear_sync_error' => '清除同步失败标记',
'batch_activate_subscription' => '批量同步订阅', 'batch_activate_subscription' => '批量同步订阅',
'mark_activated' => '仅标记为已生效',
'batch_mark_activated' => '批量仅标记为已生效',
]; ];
@endphp @endphp
<table> <table>

View File

@@ -109,6 +109,7 @@ Route::prefix('admin')->group(function () {
Route::get('/platform-orders/{order}', [PlatformOrderController::class, 'show']); Route::get('/platform-orders/{order}', [PlatformOrderController::class, 'show']);
Route::post('/platform-orders/{order}/activate-subscription', [PlatformOrderController::class, 'activateSubscription']); Route::post('/platform-orders/{order}/activate-subscription', [PlatformOrderController::class, 'activateSubscription']);
Route::post('/platform-orders/{order}/mark-paid-and-activate', [PlatformOrderController::class, 'markPaidAndActivate']); Route::post('/platform-orders/{order}/mark-paid-and-activate', [PlatformOrderController::class, 'markPaidAndActivate']);
Route::post('/platform-orders/{order}/add-payment-receipt', [PlatformOrderController::class, 'addPaymentReceipt']);
Route::post('/platform-orders/{order}/mark-activated', [PlatformOrderController::class, 'markActivated']); Route::post('/platform-orders/{order}/mark-activated', [PlatformOrderController::class, 'markActivated']);
Route::get('/site-subscriptions', [SiteSubscriptionController::class, 'index']); Route::get('/site-subscriptions', [SiteSubscriptionController::class, 'index']);