where('period_id', $recordId) ->where('status', 1) ->order('id', 'asc') ->select() ->toArray(); foreach ($bets as $bet) { $betId = (int) ($bet['id'] ?? 0); if ($betId <= 0) { continue; } $win = self::computeWinAmount($bet, $resultNumber); $jackpot = '0.0000'; $affected = Db::name('bet_order') ->where('id', $betId) ->where('status', 1) ->update([ 'win_amount' => $win, 'jackpot_extra_amount' => $jackpot, 'status' => 2, 'update_time' => $now, ]); if ($affected === 0) { continue; } if (bccomp($win, '0', 4) <= 0) { continue; } self::creditUserPayout($bet, $betId, $win, $now); } } /** * 补偿:库中已结束局次但注单仍为待开奖的,可重复调用(幂等)。 */ public static function settlePendingForEndedRecords(): int { $rows = Db::name('game_record') ->where('status', 4) ->whereNotNull('result_number') ->field(['id', 'result_number']) ->order('id', 'asc') ->select() ->toArray(); $count = 0; foreach ($rows as $row) { $rid = (int) ($row['id'] ?? 0); $rn = (int) ($row['result_number'] ?? 0); if ($rid <= 0 || $rn < 1) { continue; } $pending = Db::name('bet_order') ->where('period_id', $rid) ->where('status', 1) ->count(); if ($pending === 0) { continue; } Db::startTrans(); try { self::settleBetsForDraw($rid, $rn); Db::commit(); $count++; } catch (Throwable $e) { Db::rollback(); throw $e; } } return $count; } /** * 单注应付派彩:命中开奖号码时 unit × (连胜+1) × 33(与 GameLiveService 一致)。 */ public static function computeWinAmount(array $bet, int $resultNumber): string { $pickNumbers = $bet['pick_numbers'] ?? null; if (is_string($pickNumbers)) { $decoded = json_decode($pickNumbers, true); $pickNumbers = is_array($decoded) ? $decoded : []; } if (!is_array($pickNumbers)) { $pickNumbers = []; } if (!in_array($resultNumber, array_map('intval', $pickNumbers), true)) { return '0.0000'; } $unit = (string) ($bet['unit_amount'] ?? '0'); $streak = (int) ($bet['streak_at_bet'] ?? 0); $odds = (string) (($streak + 1) * self::BASE_ODDS); return bcmul($unit, $odds, 4); } private static function creditUserPayout(array $bet, int $betId, string $winAmount, int $now): void { $userId = (int) ($bet['user_id'] ?? 0); if ($userId <= 0) { return; } $idem = 'payout_bet_' . $betId; if (Db::name('user_wallet_record')->where('idempotency_key', $idem)->value('id')) { return; } $user = Db::name('user')->where('id', $userId)->find(); if (!$user) { return; } $before = (string) ($user['coin'] ?? '0'); $after = bcadd($before, $winAmount, 4); Db::name('user_wallet_record')->insert([ 'user_id' => $userId, 'channel_id' => $bet['channel_id'] ?? null, 'biz_type' => 'payout', 'direction' => 1, 'amount' => $winAmount, 'balance_before' => $before, 'balance_after' => $after, 'ref_type' => 'bet_order', 'ref_id' => $betId, 'idempotency_key' => $idem, 'operator_admin_id' => null, 'remark' => '压注派彩', 'create_time' => $now, ]); Db::name('user')->where('id', $userId)->update([ 'coin' => $after, 'update_time' => $now, ]); } }