| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- <?php
- namespace App\Services;
- use App\Constants\HttpStatus;
- use App\Models\Backflow;
- use App\Models\Rebate;
- use App\Models\Wallet;
- use App\Models\WalletBonus;
- use Carbon\Carbon;
- use Illuminate\Support\Facades\DB;
- use RuntimeException;
- /** Shared transaction for manual and automatic payouts. Never trusts a previously loaded reward row. */
- class RewardGrantService
- {
- public static function grant(string $type, int $id, string $operator, bool $skipPaid = false, ?string $scheduledCutoff = null): bool
- {
- if (!in_array($type, RewardGrantSettingsService::TYPES, true)) {
- throw new RuntimeException('奖励类型错误', HttpStatus::CUSTOM_ERROR);
- }
- return DB::transaction(function () use ($type, $id, $operator, $skipPaid, $scheduledCutoff) {
- $model = $type === 'rebate' ? Rebate::class : Backflow::class;
- $reward = $model::whereKey($id)->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') {
- $result = RewardBasisService::rebate($reward);
- $basis = $result['effective_amount'];
- $reward->betting_amount = $result['betting_amount'];
- $reward->effective_betting_amount = $basis;
- $reward->rebate_ratio = bccomp($basis, '0', 2) > 0 ? bcdiv($result['amount'], $basis, 6) : '0';
- $reward->audited_by = $operator;
- $changeType = '笔笔返';
- $remark = "日期:{$reward->date};有效投注:{$basis}";
- foreach ($result['details'] as $detail) {
- if ($detail['rule_id'] === null) continue;
- $remark .= ";{$detail['label']}:{$detail['effective_amount']}×{$detail['percentage']}%";
- }
- } else {
- $result = RewardBasisService::backflow($reward);
- $reward->loss_amount = $result['loss_amount'];
- $reward->backflow_ratio = '0';
- $changeType = '回水';
- $remark = "日期:{$reward->date};亏损:{$result['loss_amount']}";
- foreach ($result['details'] as $detail) {
- if ($detail['rule_id'] === null) continue;
- $remark .= ";{$detail['label']}亏损{$detail['loss_amount']}奖励{$detail['amount']}";
- }
- }
- $amount = $result['amount'];
- $reward->details = $result['details'];
- $reward->need_flow = $result['need_flow'];
- 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);
- if (bccomp($result['need_flow'], '0', 2) > 0) {
- WalletBonus::addData($reward->member_id, $type === 'rebate' ? 4 : 5, $amount, 0, $reward->id, $result['need_flow']);
- }
- }
- $reward->amount = $amount;
- $reward->status = 1;
- $reward->grant_time = Carbon::now()->timestamp;
- if (!$reward->save()) throw new RuntimeException('发放失败', HttpStatus::CUSTOM_ERROR);
- return true;
- }, 3);
- }
- }
|