Files
lotteryLaravel/app/Services/Wallet/WalletTransferMainSiteReconcileChecker.php
kang 1e7b2b31ee feat: enhance reconciliation job processing and reporting features
- Updated ReconcileItemIndexController to include admin user validation and improved transfer order handling.
- Refactored ReconcileJobStoreController to ensure date range handling is more precise with start and end of day adjustments.
- Enhanced various report controllers to include currency code in responses for better financial clarity.
- Introduced new methods in AdminReconcileJobService for wallet transfer job creation and improved item scanning logic.
- Added wallet lookup idempotent path configuration for integration services to enhance API interactions.
2026-06-16 13:50:55 +08:00

91 lines
2.9 KiB
PHP

<?php
namespace App\Services\Wallet;
use App\Models\Player;
use App\Models\TransferOrder;
use Illuminate\Support\Collection;
/** 扫描结果追加主站幂等记录核对项。 */
final class WalletTransferMainSiteReconcileChecker
{
public function __construct(
private readonly HttpMainSiteWalletIdempotentProbeClient $probeClient,
) {}
/**
* @param list<array{side_a_ref: string, side_b_ref: ?string, difference_amount: int, status: string}> $items
* @param Collection<int, TransferOrder> $orders
* @return list<array{side_a_ref: string, side_b_ref: ?string, difference_amount: int, status: string}>
*/
public function appendIssues(array $items, Collection $orders): array
{
$existingTransferNos = collect($items)
->map(fn (array $row): string => (string) ($row['side_a_ref'] ?? ''))
->filter()
->flip();
$players = Player::query()
->whereIn('id', $orders->pluck('player_id')->unique()->all())
->get()
->keyBy('id');
foreach ($orders as $order) {
if (! $this->shouldProbeMainSite($order)) {
continue;
}
$transferNo = (string) $order->transfer_no;
if ($existingTransferNos->has($transferNo)) {
continue;
}
$player = $players->get((int) $order->player_id);
if (! $player instanceof Player) {
continue;
}
$probe = $this->probeClient->probe($player, (string) $order->idempotent_key);
if ($probe->status === MainSiteWalletIdempotentProbeResult::STATUS_SKIPPED) {
continue;
}
if ($probe->status === MainSiteWalletIdempotentProbeResult::STATUS_NOT_FOUND) {
$items[] = [
'side_a_ref' => $transferNo,
'side_b_ref' => null,
'difference_amount' => (int) $order->amount,
'status' => 'main_site_record_missing',
];
continue;
}
if ($probe->status === MainSiteWalletIdempotentProbeResult::STATUS_FAILED) {
$items[] = [
'side_a_ref' => $transferNo,
'side_b_ref' => $probe->externalRefNo,
'difference_amount' => (int) $order->amount,
'status' => 'main_site_failed',
];
}
}
return $items;
}
private function shouldProbeMainSite(TransferOrder $order): bool
{
if (trim((string) $order->external_ref_no) !== '') {
return true;
}
if ($order->status === 'success') {
return true;
}
return $order->direction === 'in'
&& $order->status === 'pending_reconcile'
&& $order->fail_reason === 'lottery_credit_failed';
}
}