billGuard->markConfirmed($billId); } /** * @param array{method?: string|null, proof?: string|null, remark?: string|null} $meta */ public function recordPayment(int $billId, int $amount, int $adminUserId, array $meta = []): void { $bill = DB::table('settlement_bills')->where('id', $billId)->first(); if ($bill === null) { throw new \InvalidArgumentException('bill_not_found'); } $this->billGuard->assertPeriodMutable($billId); $this->billGuard->assertPayable($billId); $amount = min($amount, abs((int) $bill->unpaid_amount)); if ($amount <= 0) { return; } [$payerType, $payerId, $payeeType, $payeeId] = $this->resolvePayerPayee($bill); DB::table('payment_records')->insert([ 'settlement_bill_id' => $billId, 'payer_type' => $payerType, 'payer_id' => $payerId, 'payee_type' => $payeeType, 'payee_id' => $payeeId, 'amount' => $amount, 'method' => $meta['method'] ?? null, 'proof' => $meta['proof'] ?? null, 'remark' => $meta['remark'] ?? null, 'status' => 'confirmed', 'created_by' => $adminUserId, 'confirmed_by' => $adminUserId, 'confirmed_at' => now(), 'created_at' => now(), 'updated_at' => now(), ]); $newPaid = (int) $bill->paid_amount + $amount; $newUnpaid = max(0, (int) $bill->unpaid_amount - $amount); $status = $newUnpaid === 0 ? 'settled' : 'partial_paid'; DB::table('settlement_bills')->where('id', $billId)->update([ 'paid_amount' => $newPaid, 'unpaid_amount' => $newUnpaid, 'status' => $status, 'updated_at' => now(), ]); if ($bill->owner_type === 'player' && (int) $bill->owner_id > 0) { $player = Player::query()->find((int) $bill->owner_id); if ($player !== null) { if ((int) $bill->net_amount > 0) { $this->playerCreditService->releaseFromSettlement($player, $amount, $billId); } elseif ((int) $bill->net_amount < 0) { $this->playerCreditService->applySettlementPayout($player, $amount, $billId); } if ($status === 'settled') { $this->periodCloseRebate->markRebatesSettledForBill($billId); } } } $this->periodCompletion->syncIfReady((int) $bill->settlement_period_id); } /** * net_amount > 0:owner 应付 counterparty;< 0:counterparty 应付 owner。 * * @return array{0: string, 1: int, 2: string, 3: int} */ private function resolvePayerPayee(object $bill): array { if ((int) $bill->net_amount < 0) { return [ (string) $bill->counterparty_type, (int) $bill->counterparty_id, (string) $bill->owner_type, (int) $bill->owner_id, ]; } return [ (string) $bill->owner_type, (int) $bill->owner_id, (string) $bill->counterparty_type, (int) $bill->counterparty_id, ]; } }