lockForUpdate()->first(); if (!$reward) throw new RuntimeException('数据不存在', HttpStatus::CUSTOM_ERROR); if ((int) $reward->status !== 0) { if ($skipPaid) return false; throw new RuntimeException('奖励已发放,无需再次发放', HttpStatus::CUSTOM_ERROR); } $cutoff = $type === 'rebate' ? Carbon::now('America/New_York')->format('Y-m-d') : Carbon::now('Asia/Shanghai')->format('Y-m'); if ((string) $reward->date >= $cutoff) { throw new RuntimeException('未到发放时间', HttpStatus::CUSTOM_ERROR); } if ($scheduledCutoff !== null && (string) $reward->date >= $scheduledCutoff) return false; if ($type === 'rebate') { $ratio = self::configDecimal('rebate'); $basis = (string) $reward->effective_betting_amount; if (bccomp($basis, '0', 2) < 0) throw new RuntimeException('有效投注金额异常', HttpStatus::CUSTOM_ERROR); $amount = bcmul($basis, $ratio, 2); $reward->rebate_ratio = $ratio; $reward->audited_by = $operator; $changeType = '笔笔返'; $remark = "日期:{$reward->date};有效投注:{$basis};比例:" . bcmul($ratio, '100', 2) . '%'; } else { // Preserve the existing monthly formula: abs(recharge + signed withdrawal). $basis = bcadd((string) $reward->recharge_amount, (string) $reward->withdrawal_amount, 2); if (bccomp($basis, '0', 2) < 0) $basis = bcsub('0', $basis, 2); $amount = '0.00'; $ratio = (string) $reward->backflow_ratio; if (bccomp($basis, self::configDecimal('huishui_restriction'), 2) >= 0) { $ratio = self::configDecimal('huishui_percentage'); $reward->backflow_ratio = $ratio; $amount = bcmul($basis, $ratio, 2); } $changeType = '回水'; $remark = "日期:{$reward->date};盈亏:-{$basis};回水比例:" . bcmul($ratio ?: '0', '100', 2) . '%'; } if (bccomp($amount, '0', 2) > 0) { $wallet = Wallet::where('member_id', $reward->member_id)->lockForUpdate()->first(); if (!$wallet) throw new RuntimeException('会员钱包不存在', HttpStatus::CUSTOM_ERROR); $before = (string) $wallet->available_balance; $wallet->available_balance = bcadd($before, $amount, 10); if (!$wallet->save()) throw new RuntimeException('余额更新失败', HttpStatus::CUSTOM_ERROR); BalanceLogService::addLog($reward->member_id, $amount, $before, $wallet->available_balance, $changeType, $reward->id, $remark); } $reward->amount = $amount; $reward->status = 1; $reward->grant_time = Carbon::now()->timestamp; if (!$reward->save()) throw new RuntimeException('发放失败', HttpStatus::CUSTOM_ERROR); return true; }, 3); } private static function configDecimal(string $field): string { $value = trim((string) Config::where('field', $field)->value('val')); if (!preg_match('/^\d+(?:\.\d+)?$/D', $value)) { throw new RuntimeException('奖励比例或门槛配置异常', HttpStatus::CUSTOM_ERROR); } return $value; } }