'application/json'], json_encode([ 'code' => 200, 'msg' => $message, 'data' => $data, ], JSON_UNESCAPED_UNICODE)); } /** * @param array $data * @param string $message * @param integer $code * @return Response */ function jsonFailResponse(string $message = '', array $data = [], int $code = 100): Response { return new Response(200, ['Content-Type' => 'application/json'], json_encode([ 'code' => $code, 'msg' => $message, 'data' => $data, ], JSON_UNESCAPED_UNICODE)); } /** * 生成唯一邀请码 * @return string */ function createCode(): string { $code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $rand = $code[rand(0, 25)] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99)); for ($a = md5($rand, true), $s = '0123456789ABCDEFGHIJKLMNOPQRSTUV', $d = '', $f = 0; $f < 8; $g = ord($a[$f]), $d .= $s[($g ^ ord($a[$f + 8])) - $g & 0x1F], $f++) ; return $d; } /** * 获取验证消息 * @param AllOfException $e * @return mixed */ function getValidationMessages(AllOfException $e) { $message = $e->getMessages([ 'notOptional' => trans('required', [], 'validator'), 'notEmpty' => trans('required', [], 'validator'), 'email' => trans('email', [], 'validator'), 'idCard' => trans('idCard', [], 'validator'), 'url' => trans('url', [], 'validator'), 'number' => trans('number', [], 'validator'), 'integer' => trans('integer', [], 'validator'), 'float' => trans('float', [], 'validator'), 'mobile' => trans('mobile', [], 'validator'), 'length' => trans('length', [], 'validator'), 'alpha' => trans('alpha', [], 'validator'), 'alnum' => trans('alnum', [], 'validator'), 'alphaDash' => trans('alphaDash', [], 'validator'), 'chs' => trans('chs', [], 'validator'), 'chsAlpha' => trans('chsAlpha', [], 'validator'), 'chsAlphaNum' => trans('chsAlphaNum', [], 'validator'), 'chsDash' => trans('chsDash', [], 'validator'), 'equals' => trans('equals', [], 'validator'), 'in' => trans('in', [], 'validator'), 'image' => trans('image', [], 'validator'), 'creditCard' => trans('creditCard', [], 'validator'), 'digit' => trans('digit', [], 'validator'), 'base64' => trans('base64', [], 'validator'), 'arrayVal' => trans('arrayVal', [], 'validator'), ])['key']; $message = is_array($message) ? Arr::first($message) : $message; return $message ?? trans('validation_error', [], 'message'); } /** * 生成uuid * @return string */ function gen_uuid(): string { do { $timestamp = time(); $randomNumber = str_pad(rand(0, 9999), 4, '0', STR_PAD_LEFT); $uniqueNumericId = substr($timestamp, -5) . $randomNumber; } while (Player::query()->where('uuid', $uniqueNumericId)->withTrashed()->exists()); return $uniqueNumericId; } /** * 金额转换 * @param $number * @return float|int */ function floorToCoinsSecondNumber($number) { return floor($number * 100) / 100; } /** * 添加用户登录信息 * @param $id * @return PlayerLoginRecord|Model */ function addLoginRecord($id) { $ip = request()->getRealIp(); if (!empty($ip)) { try { $location = new Location(); $result = $location->getLocation($ip); } catch (IpAttributionException $exception) { Log::error('获取ip信息错误'); } } $country_name = ($result['country'] ?? '') . ($result['city'] ?? ''); $domain = isset($_SERVER['HTTP_ORIGIN']) ? parse_url($_SERVER['HTTP_ORIGIN']) : null; return PlayerLoginRecord::create([ 'player_id' => $id, 'login_domain' => !empty($domain) ? $domain['host'] : null, 'ip' => $ip, 'country_name' => $country_name, 'city_name' => $result['city'] ?? '', 'remark' => $request->remark ?? null, 'department_id' => request()->department_id, ]); } /** * 添加用户注册信息 * @param $id * @param $type * @param $department_id * @return PlayerRegisterRecord|Model */ function addRegisterRecord($id, $type, $department_id) { $ip = request()->getRealIp(); if (!empty($ip)) { try { $location = new Location(); $result = $location->getLocation($ip); } catch (IpAttributionException $exception) { Log::error('获取ip信息错误'); } } $country_name = ($result['country'] ?? '') . ($result['city'] ?? ''); $domain = isset($_SERVER['HTTP_ORIGIN']) ? parse_url($_SERVER['HTTP_ORIGIN']) : null; return PlayerRegisterRecord::create([ 'player_id' => $id, 'register_domain' => !empty($domain) ? $domain['host'] : null, 'ip' => $ip, 'country_name' => $country_name, 'city_name' => $result['city'] ?? '', 'device' => 'app', 'type' => $type, 'department_id' => $department_id, ]); } /** * 保存头像到本地 * @param $avatar * @return string */ function saveAvatar($avatar): string { if (empty($avatar)) { return ''; } try { if (strpos($avatar, 'http://') === 0 || strpos($avatar, 'https://') === 0) { $client = new Client(['verify' => false]); //忽略SSL错误 $fileName = md5($avatar) . '.jpg'; $path = public_path() . '/storage/avatar/'; if (!is_dir($path) && !mkdir($path, 0777, true)) { throw new Exception('创建文件夹失败'); } $client->request('GET', $avatar, ['sink' => public_path('/storage/avatar/' . $fileName)]); } else { throw new Exception('网络地址错误'); } } catch (Exception|GuzzleException $e) { Log::error('保存头像错误' . $e->getMessage()); return ''; } return '/storage/avatar/' . $fileName; } /** * 生成唯一单号 * @return string */ function createOrderNo(): string { $yCode = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; return $yCode[intval(date('Y')) - 2011] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99)); } /** * 设置短信key * @param string $phone 手机号 * @param int $type 模式 1 为修改密码短信 * @return string */ function setSmsKey(string $phone, int $type): string { switch ($type) { case PhoneSmsLog::TYPE_LOGIN: return 'sms-login' . $phone; case PhoneSmsLog::TYPE_REGISTER: return 'sms-register' . $phone; case PhoneSmsLog::TYPE_CHANGE_PASSWORD: return 'sms-change-password' . $phone; case PhoneSmsLog::TYPE_CHANGE_PAY_PASSWORD: return 'sms-change-pay-password' . $phone; case PhoneSmsLog::TYPE_CHANGE_PHONE: return 'sms-change-phone' . $phone; case PhoneSmsLog::TYPE_BIND_NEW_PHONE: return 'sms-type-bind-new-phone' . $phone; case PhoneSmsLog::TYPE_TALK_BIND: return 'sms-type-talk-bind' . $phone; default: return 'sms-' . $phone; } } /** * 验证短信 * @param string $country_code 国家编号 * @param string $phone 手机号 * @param string $code 验证码 * @param int $type 类型 * @return string */ function verifySMS(string $country_code, string $phone, string $code, int $type): string { switch ($country_code) { case PhoneSmsLog::COUNTRY_CODE_JP: $phone = ltrim($phone, '0'); break; case PhoneSmsLog::COUNTRY_CODE_MY: $phone = ltrim($phone, '0'); break; default: break; } $phoneCode = Cache::get(setSmsKey($phone, $type)); return $phoneCode == $code; } /** * 获取短信消息 * @param int $type 模式 1 为修改密码短信 * @param string $source 来源 * @return string */ function getContent(int $type, string $source): string { switch ($type) { case PhoneSmsLog::TYPE_LOGIN: return config($source . '-sms.login_content'); case PhoneSmsLog::TYPE_REGISTER: return config($source . '-sms.register_content'); case PhoneSmsLog::TYPE_CHANGE_PASSWORD: return config($source . '-sms.change_password_content'); case PhoneSmsLog::TYPE_CHANGE_PAY_PASSWORD: return config($source . '-sms.change_pay_password'); case PhoneSmsLog::TYPE_CHANGE_PHONE: return config($source . '-sms.change_phone'); case PhoneSmsLog::TYPE_BIND_NEW_PHONE: return config($source . '-sms.bind_new_phone'); case PhoneSmsLog::TYPE_TALK_BIND: return config($source . '-sms.talk_bind'); default: return config($source . '-sms.sm_content'); } } /** * 提现订单回滚 * @param PlayerWithdrawRecord $playerWithdrawRecord * @param string $rejectReason * @param int $withdrawStatus * @return string * @throws Exception */ function withdrawBack(PlayerWithdrawRecord $playerWithdrawRecord, string $rejectReason = '', int $withdrawStatus = PlayerWithdrawRecord::STATUS_PENDING_REJECT): string { DB::beginTransaction(); try { // 更新提现订单 $playerWithdrawRecord->status = $withdrawStatus; $playerWithdrawRecord->reject_reason = $rejectReason; $playerWithdrawRecord->finish_time = date('Y-m-d H:i:s'); $playerWithdrawRecord->user_id = Admin::id() ?? 0; $playerWithdrawRecord->user_name = !empty(Admin::user()) ? Admin::user()->username : ''; // 更新玩家钱包 $beforeGameAmount = $playerWithdrawRecord->player->wallet->money; $playerWithdrawRecord->player->wallet->money = bcadd($playerWithdrawRecord->player->wallet->money, $playerWithdrawRecord->coins, 2); // 跟新玩家统计 $playerWithdrawRecord->player->player_extend->withdraw_amount = bcsub($playerWithdrawRecord->player->player_extend->withdraw_amount, $playerWithdrawRecord->coins, 2); $playerWithdrawRecord->push(); //寫入金流明細 $playerDeliveryRecord = new PlayerDeliveryRecord; $playerDeliveryRecord->player_id = $playerWithdrawRecord->player_id; $playerDeliveryRecord->department_id = $playerWithdrawRecord->department_id; $playerDeliveryRecord->target = $playerWithdrawRecord->getTable(); $playerDeliveryRecord->target_id = $playerWithdrawRecord->id; $playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_WITHDRAWAL_BACK; $playerDeliveryRecord->source = 'withdraw_back'; $playerDeliveryRecord->amount = $playerWithdrawRecord->coins; $playerDeliveryRecord->amount_before = $beforeGameAmount; $playerDeliveryRecord->amount_after = $playerWithdrawRecord->player->wallet->money; $playerDeliveryRecord->tradeno = $playerWithdrawRecord->tradeno ?? ''; $playerDeliveryRecord->remark = $playerWithdrawRecord->remark ?? ''; $playerDeliveryRecord->save(); DB::commit(); sendSocketMessage('private-recharge_withdrawal', [ 'msg_type' => 'withdrawal', 'player_id' => $playerWithdrawRecord->player_id, 'amount' => $playerWithdrawRecord->player->wallet->money, ]); } catch (\Exception $e) { DB::rollBack(); throw new Exception($e->getMessage()); } return true; } /** * 添加渠道财务操作 * @param $target * @param $action * @return void */ function saveChannelFinancialRecord($target, $action) { $channelFinancialRecord = new ChannelFinancialRecord(); $channelFinancialRecord->action = $action; $channelFinancialRecord->department_id = Admin::user()->department_id ?? 0; $channelFinancialRecord->player_id = $target->player_id ?? 0; $channelFinancialRecord->target = $target->getTable(); $channelFinancialRecord->target_id = $target->id; $channelFinancialRecord->user_id = Admin::id() ?? 0; $channelFinancialRecord->tradeno = $target->tradeno ?? ''; $channelFinancialRecord->user_name = Admin::user()->username ?? ''; $channelFinancialRecord->save(); } /** * 上传base64图片 * @param $img * @param $path * @return false|string */ function uploadBaseImg($img, $path) { if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $img, $result)) { $type = $result[2];//图片后缀 $savePath = '/storage/' . $path . '/' . date("Ymd", time()) . "/"; $newPath = public_path() . $savePath; if (!file_exists($newPath)) { //检查是否有该文件夹,如果没有就创建,并给予最高权限 mkdir($newPath, 0755, true); } $filename = time() . '_' . uniqid() . ".{$type}"; //文件名 $newPath = $newPath . $filename; //写入操作 if (file_put_contents($newPath, base64_decode(str_replace($result[1], '', $img)))) { return env('APP_URL', 'http://127.0.0.1:8787') . $savePath . $filename; } return false; } return false; } /** * 检查充值订单取消超时订单 * @throws Exception */ function cancelRecharge() { /** @var SystemSetting $setting */ $setting = SystemSetting::where('status', 1)->where('feature', 'recharge_order_expiration')->first(); if (!empty($setting)) { $playerRechargeRecord = PlayerRechargeRecord::where('type', PlayerRechargeRecord::TYPE_REGULAR) ->where('status', PlayerRechargeRecord::STATUS_WAIT) ->where('created_at', '<', Carbon::now()->subMinutes($setting->num)) ->get(); /** @var PlayerRechargeRecord $order */ foreach ($playerRechargeRecord as $order) { $order->status = PlayerRechargeRecord::STATUS_RECHARGED_SYSTEM_CANCEL; $order->cancel_time = date('Y-m-d H:i:s'); $order->save(); } } } /** * 发送socket消息 * @param $channels * @param $content * @param string $form * @return bool|string * @throws PushException */ function sendSocketMessage($channels, $content, string $form = 'system') { try { // 发送进入保留状态消息 $api = new Api( config('plugin.webman.push.app.api'), config('plugin.webman.push.app.app_key'), config('plugin.webman.push.app.app_secret') ); return $api->trigger($channels, 'message', [ 'from_uid' => $form, 'content' => json_encode($content) ]); } catch (Exception $e) { Log::error('sendSocketMessage', [$e->getMessage()]); return false; } } /** * 获取渠道信息 * @param $siteId * @return array */ function getChannel($siteId): array { $cacheKey = "channel_" . $siteId; $channel = Cache::get($cacheKey); if (empty($channel)) { $channel = Channel::where('id', $siteId)->whereNull('deleted_at')->first()->toArray(); if (!empty($channel)) { $cacheKey = "channel_" . $channel->site_id; Cache::set($cacheKey, $channel->toArray()); } else { return []; } } return $channel; } /** * 获取堆栈信息 * @return void */ function getStackList(): void { $line = []; $debugList = array_reverse(debug_backtrace()); foreach ($debugList as $key => $val) { $class = $val['class'] ?? ""; $arg = $val['args']; $parameter = ''; $stringLine = ''; if (!empty($arg) && is_array($arg)) { foreach ($arg as $v) { $className = $v; if (is_object($v)) { $className = get_class($v); } elseif (is_array($v)) { $className = json_encode($v); } $parameter .= $className . ','; } } $stringLine .= '程序执行' . $key . ':=>'; $stringLine .= '[1.所在文件(' . $val['file'] . ')],'; $stringLine .= '[2.函数调用情况[第' . $val['line'] . '行]:' . $class . '->' . $val['function'] . '(' . $parameter . ')]' . "\n"; $line[] = $stringLine; } Log::error("堆栈信息", $line); } /** * 获取毫秒级 * @return float */ function millisecond(): float { list($millisecond, $sec) = explode(' ', microtime()); return (float)sprintf('%.0f', (floatval($millisecond) + floatval($sec)) * 1000); } /** * 创建玩家 * @param $departmentId * @param $data * @return Player * @throws Exception */ function createPlayer($departmentId, $data): Player { /** @var Channel $channel */ $channel = Channel::where('department_id', $departmentId)->first(); if (empty($channel)) { throw new Exception(trans('channel_not_found', [], 'message')); } DB::beginTransaction(); try { $count = Player::whereBetween('created_at', [date('Y-m-d') . ' 00:00:00', date('Y-m-d') . ' 23:59:59'])->count('*'); if (empty($data['avatar'])) { $defAvatars = config('def_avatar') ?? []; $randomKey = array_rand($defAvatars); $randomAvatar = $defAvatars[$randomKey] ?? ''; } else { $randomAvatar = $data['avatar']; } $player = new Player(); $player->uuid = gen_uuid(); $player->type = Player::TYPE_PLAYER; $player->currency = $channel->currency; $player->department_id = $channel->department_id; $player->avatar = $randomAvatar; $player->device_number = $data['device_number'] ?? ''; $player->name = $data['name'] ?? 'channel_' . $departmentId . date('Ymd') . $count; $player->facebook_id = $data['facebook_id'] ?? ''; $player->phone = $data['phone'] ?? ''; if(isset($data['password'])){ $player->password = $data['password']; } if(isset($data['recommend_id'])){ $player->recommend_id = $data['recommend_id']; } $player->recommended_code = $data['recommended_code'] ?? ''; $player->status = Player::STATUS_ENABLE; $player->recommend_code = createCode(); $player->save(); addPlayerExtend($player, [ 'email' => $data['email'] ?? '' ]); addRegisterRecord($player->id, PlayerRegisterRecord::TYPE_CLIENT, $player->department_id); DB::commit(); } catch (\Exception $e) { DB::rollBack(); Log::error(trans('register_player_failed', [], 'message') . $e->getMessage()); throw new Exception(); } unset($player->password); return $player; } /** * 增加玩家扩展信息 * @param Player $player * @param array $extend * @return void */ function addPlayerExtend(Player $player, array $extend = []) { $registerPresent = SystemSetting::where('feature', 'register_present')->where('status', 1)->value('num') ?? 0; $playerPlatformCash = new PlayerPlatformCash(); $playerPlatformCash->player_id = $player->id; $playerPlatformCash->platform_id = PlayerPlatformCash::PLATFORM_SELF; $playerPlatformCash->money = $registerPresent; $playerPlatformCash->save(); $playerExtend = new PlayerExtend(); $playerExtend->player_id = $player->id; $playerExtend->email = $extend['email']; $playerExtend->save(); if (isset($registerPresent) && $registerPresent > 0) { //添加玩家钱包日志 $playerMoneyEditLog = new PlayerMoneyEditLog; $playerMoneyEditLog->player_id = $player->id; $playerMoneyEditLog->department_id = $player->department_id; $playerMoneyEditLog->type = PlayerMoneyEditLog::TYPE_INCREASE; $playerMoneyEditLog->action = PlayerMoneyEditLog::OTHER; $playerMoneyEditLog->tradeno = date('YmdHis') . rand(10000, 99999); $playerMoneyEditLog->currency = $player->currency; $playerMoneyEditLog->money = $registerPresent; $playerMoneyEditLog->inmoney = $registerPresent; $playerMoneyEditLog->remark = ''; $playerMoneyEditLog->user_id = Admin::id() ?? 0; $playerMoneyEditLog->user_name = !empty(Admin::user()) ? Admin::user()->toArray()['username'] : trans('system_automatic', [], 'message'); $playerMoneyEditLog->save(); //寫入金流明細 $playerDeliveryRecord = new PlayerDeliveryRecord; $playerDeliveryRecord->player_id = $player->id; $playerDeliveryRecord->department_id = $player->department_id; $playerDeliveryRecord->target = $playerMoneyEditLog->getTable(); $playerDeliveryRecord->target_id = $playerMoneyEditLog->id; $playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_REGISTER_PRESENT; $playerDeliveryRecord->source = 'register_present'; $playerDeliveryRecord->amount = $playerMoneyEditLog->money; $playerDeliveryRecord->amount_before = 0; $playerDeliveryRecord->amount_after = $registerPresent; $playerDeliveryRecord->tradeno = $playerMoneyEditLog->tradeno ?? ''; $playerDeliveryRecord->remark = $playerMoneyEditLog->remark ?? ''; $playerDeliveryRecord->save(); } } /** * @return Player * @throws GameException * @throws PlayerCheckException * @throws \Exception */ function checkPlayer(): Player { $departmentId = request()->department_id; $id = JwtToken::getCurrentId(); /** @var Player $player */ $player = Player::where('id', $id)->where('department_id', $departmentId)->first(); if (empty($player)) { throw new PlayerCheckException(trans('player_not_fount', [], 'message'), 100); } if ($player->status == Player::STATUS_STOP) { throw new PlayerCheckException(trans('player_stop', [], 'message'), 100); } return $player; } /** * 组装请求 * @param string $url * @param array $params * @return array|mixed * @throws Exception */ function doCurl(string $url, array $params = []) { $response = Http::timeout(7) ->contentType('application/json') ->accept('application/json') ->asJson() ->post($url, $params); if (!$response->ok()) { throw new Exception(trans('system_busy', [], 'message')); } $data = $response->json(); if (empty($data)) { throw new Exception(trans('system_busy', [], 'message')); } return $data; } /** * 组装请求 * @param $id * @param array $range * @return array|mixed|null */ function getGamePlayerNum($id, array $range = []) { $cacheKey = 'game_player_num_' . $id; $playerNum = Cache::get($cacheKey); if ($playerNum === null) { $playerNum = rand($range[0] ?? 0, $range[1] ?? 0); Cache::set($cacheKey, $playerNum, 30 * 60); } return $playerNum; } /** * 分润结算 * @return void */ function commissionSettlement() { $date = Carbon::now()->subDay()->format('Y-m-d'); $data = PlayGameRecord::query() ->selectRaw('SUM(bet) - SUM(win) AS damage_amount, player_id, parent_player_id, department_id,sum(deficit) as deficit') ->whereDate('created_at', $date) ->where('status', 0) ->groupBy('player_id', 'parent_player_id', 'department_id') ->get(); Log::info('分润结算开始------------------'); if ($data->isEmpty()) { return; } // 提取父玩家ID $playerIds = $data->pluck('parent_player_id')->unique()->toArray(); // 获取玩家数据 $players = Player::query() ->with(['player_extend']) ->whereIn('id', $playerIds) ->get() ->keyBy('id'); // 获取系统设置 $systemSettingMap = SystemSetting::query() ->where('status', 1) ->where('feature', 'commission_setting') ->get() ->keyBy('department_id'); $commissionRecord = []; $unsettledCommissionAmount = []; foreach ($data->toArray() as $item) { /** @var SystemSetting $systemSettingData */ //边玩边赚,只返给上级 $systemSettingData = $systemSettingMap->get($item['department_id']); if(empty($systemSettingData)){ continue; } $systemSetting = json_decode($systemSettingData->content, true); if ($item['deficit'] == 0) { continue; } if ($systemSetting && !empty($systemSetting['commission_damage']) && is_numeric($systemSetting['commission_damage']) && $systemSetting['commission_damage'] > 0) { $commissionAmount = max(bcmul($item['damage_amount'], bcdiv($systemSetting['commission_damage'], 100, 2), 2), 0); /** @var Player $player */ $player = $players->get($item['parent_player_id']); if ($player) { $commissionRecord[] = [ 'player_id' => $item['player_id'], 'department_id' => $item['department_id'], 'parent_player_id' => $item['parent_player_id'], 'recharge_amount' => 0, 'chip_amount' => 0, 'total_amount' => $player->player_extend->commission_amount, 'damage_amount' => $item['damage_amount'], 'amount' => $commissionAmount, 'ratio' => $systemSetting['commission_damage'], 'date' => $date, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]; if (!isset($unsettledCommissionAmount[$item['parent_player_id']])) { $unsettledCommissionAmount[$item['parent_player_id']] = $commissionAmount; } else { $unsettledCommissionAmount[$item['parent_player_id']] = bcadd($unsettledCommissionAmount[$item['parent_player_id']], $commissionAmount, 2); } } } } // 前天发生账变的玩家 $playerList = Player::with(['wallet']) ->whereHas('wallet', function ($query) use ($date) { $query->where('updated_at', '>=', $date . ' 00:00:00'); }) ->get(); // 账变记录 $playerDeliveryRecords = collect(PlayerDeliveryRecord::whereDate('updated_at', '2024-11-26') ->groupBy('player_id') ->whereIn('type', [ PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_ADD, PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_DEDUCT, PlayerDeliveryRecord::TYPE_REGISTER_PRESENT, PlayerDeliveryRecord::TYPE_GAME_OUT, PlayerDeliveryRecord::TYPE_GAME_IN, PlayerDeliveryRecord::TYPE_RECHARGE, ]) ->get([ DB::raw("SUM(if(`type`=" . PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_ADD . ",`amount`,0)) as admin_add_amount"), DB::raw("SUM(if(`type`=" . PlayerDeliveryRecord::TYPE_MODIFIED_AMOUNT_DEDUCT . ",`amount`,0)) as admin_deduct_amount"), DB::raw("SUM(if(`type`=" . PlayerDeliveryRecord::TYPE_REGISTER_PRESENT . ",`amount`,0)) as present_amount"), DB::raw("SUM(if(`type`=" . PlayerDeliveryRecord::TYPE_RECHARGE . ",`amount`,0)) as recharge_amount"), 'player_id' ])->toArray()) ->keyBy('player_id') ->toArray(); // 提现记录 $playerWithdrawRecord = collect(PlayerWithdrawRecord::whereDate('updated_at', $date) ->where('status', PlayerWithdrawRecord::STATUS_SUCCESS) ->groupBy('player_id') ->get([ DB::raw('SUM(`money`) as withdraw_amount'), 'player_id' ])) ->keyBy('player_id') ->toArray(); /** @var Player $player */ foreach ($playerList as $player) { $change = [ 'recharge_amount' => $playerDeliveryRecords[$player->id]['recharge_amount'] ?? 0, 'admin_add_amount' => $playerDeliveryRecords[$player->id]['admin_add_amount'] ?? 0, 'admin_deduct_amount' => $playerDeliveryRecords[$player->id]['admin_deduct_amount'] ?? 0, 'present_amount' => $playerDeliveryRecords[$player->id]['present_amount'] ?? 0, 'bonus_amount' => $playerDeliveryRecords[$player->id]['bonus_amount'] ?? 0, 'withdraw_amount' => $playerWithdrawRecord[$player->id]['withdraw_amount'] ?? 0, 'date' => $date, ]; $change_list[$player->id] = $change; } DB::beginTransaction(); try { $playerUpdates = []; $playerDeliveryRecord = []; $promoterProfitRecords = []; // 保存所有的PromoterProfitRecord foreach ($unsettledCommissionAmount as $key => $amount) { /** @var Player $player */ $player = $players->get($key); if ($player) { $commissionAmount = bcadd($amount, $player->player_extend->unsettled_commission_amount, 2); if ($commissionAmount > 0) { $player->player_extend->commission_amount = bcadd($player->player_extend->commission_amount, $commissionAmount, 2); $player->player_extend->unsettled_commission_amount = 0; $beforeAmount = $player->wallet->money; $player->wallet->money = bcadd($player->wallet->money, $commissionAmount, 2); $playerDeliveryRecord[] = [ 'player_id' => $player->id, 'department_id' => $player->department_id, 'target' => 'commission_record', 'target_id' => 0, 'type' => PlayerDeliveryRecord::TYPE_COMMISSION, 'source' => 'commission', 'amount' => $commissionAmount, 'amount_before' => $beforeAmount, 'amount_after' => $player->wallet->money, 'tradeno' => '', 'remark' => '', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'), ]; } else { $player->player_extend->unsettled_commission_amount = $commissionAmount; } $playerUpdates[] = $player; } } if (!empty($playerUpdates)) { /** @var Player $player */ foreach ($playerUpdates as $player) { $player->push(); } } if (!empty($commissionRecord)) { CommissionRecord::query()->insert($commissionRecord); } if (!empty($playerDeliveryRecord)) { PlayerDeliveryRecord::query()->insert($playerDeliveryRecord); } PlayGameRecord::query() ->whereDate('created_at', $date) ->update([ 'status' => 1, 'action_at' => date('Y-m-d H:i:s'), ]); foreach ($data as $value) { $up_ids = []; //玩家为代理 if(isset($value->player->player_promoter->path)){ $path = $value->player->player_promoter->path; $up_ids = explode(',',$path); //代理不给自己返利 array_pop($up_ids); }else{ //查询玩家无限上级 if(isset($value->player->recommend_id)){ $path = PlayerPromoter::where('player_id',$value->player->recommend_id)->value('path'); $up_ids = explode(',',$path); }else{ //未绑定上级的玩家不参与返利计算 continue; } } $up_list = PlayerPromoter::whereIn('player_id',$up_ids)->orderBy('id','asc')->get(); foreach($up_list as $k => &$up_val){ $player_profit_amount = 0;//直属玩家提供的分润 $diff_ratio = 0;//每级代理获得的分润比例 if(isset($up_list[$k+1])){ //上级代理获得的返利比例 $diff_ratio = bcsub($up_val->ratio, $up_list[$k+1]->ratio, 2); }else{ $diff_ratio = $up_val->ratio; $player_profit_amount = bcdiv(bcmul($diff_ratio, $value->deficit, 2),100,2); } $rebate = bcdiv(bcmul($diff_ratio, $value->deficit, 2),100,2); //更新推广员信息表 $up_val->player_profit_amount = bcadd($up_val->player_profit_amount,$player_profit_amount,2);//直属玩家提供的分润 $up_val->profit_amount = bcadd($up_val->profit_amount,$rebate,2);//总分润 $team_withdraw_total_amount = isset($change_list[$up_val->player_id]['withdraw_amount']) ? $change_list[$up_val->player_id]['withdraw_amount'] : 0; $up_val->team_withdraw_total_amount = bcadd($up_val->team_withdraw_total_amount, $team_withdraw_total_amount, 2); $team_recharge_total_amount = isset($change_list[$up_val->player_id]['recharge_amount']) ? $change_list[$up_val->player_id]['recharge_amount'] : 0; $up_val->team_recharge_total_amount = bcadd($up_val->team_recharge_total_amount, $team_recharge_total_amount, 2); $up_val->total_profit_amount = bcadd($up_val->total_profit_amount, $rebate, 2); $up_val->team_total_profit_amount = bcadd($up_val->team_total_profit_amount, bcmul($value->deficit,bcdiv($up_val->ratio,100,2), 2),2); $up_val->team_profit_amount = bcadd($up_val->team_profit_amount, bcmul($value->deficit,bcdiv($up_val->ratio,100,2), 2), 2); $up_val->save(); $promoter_profit_record = []; $promoter_profit_record['player_id'] = $value->player_id;//玩家id $promoter_profit_record['department_id'] = $up_val->department_id; $promoter_profit_record['promoter_player_id'] = $up_val->player_id;//获得分润的id $promoter_profit_record['source_player_id'] = $value->player->recommend_id;//玩家上级id $promoter_profit_record['withdraw_amount'] = isset($change_list[$up_val->player_id]['withdraw_amount']) ? $change_list[$up_val->player_id]['withdraw_amount'] : 0; $promoter_profit_record['recharge_amount'] = isset($change_list[$up_val->player_id]['recharge_amount']) ? $change_list[$up_val->player_id]['recharge_amount'] : 0; $promoter_profit_record['bonus_amount'] = isset($change_list[$up_val->player_id]['bonus_amount']) ? $change_list[$up_val->player_id]['bonus_amount'] : 0; $promoter_profit_record['admin_deduct_amount'] = isset($change_list[$up_val->player_id]['admin_deduct_amount']) ? $change_list[$up_val->player_id]['admin_deduct_amount'] : 0; $promoter_profit_record['admin_add_amount'] = isset($change_list[$up_val->player_id]['admin_add_amount']) ? $change_list[$up_val->player_id]['admin_add_amount'] : 0; $promoter_profit_record['present_amount'] = isset($change_list[$up_val->player_id]['present_amount']) ? $change_list[$up_val->player_id]['present_amount'] : 0; $promoter_profit_record['ratio'] = $up_val->ratio; $promoter_profit_record['actual_ratio'] = $diff_ratio; $promoter_profit_record['date'] = $date; $promoter_profit_record['model'] = PromoterProfitRecord::MODEL_TASK; $promoter_profit_record['profit_amount'] = $rebate; $promoter_profit_record['player_profit_amount'] = $player_profit_amount; $created_at = Carbon::now(); $promoter_profit_record['created_at'] = $created_at; $promoter_profit_record['updated_at'] = $created_at; $promoterProfitRecords[] = $promoter_profit_record; } unset($up_val); } PromoterProfitRecord::insert($promoterProfitRecords); DB::commit(); } catch (\Exception $e) { DB::rollBack(); Log::error('CommissionSettlement', [$e->getMessage(), $e->getTraceAsString()]); } Log::info('分润结算结束------------------'); } /** * 客损返水结算 * @return void */ function damageRebate() { $date = Carbon::now()->subDay()->format('Y-m-d'); $data = CommissionRecord::query()->where('date', $date)->get(); Log::info('客损返水结算------------------'); if ($data->isEmpty()) { return; } // 提取玩家ID $playerIds = $data->pluck('player_id')->unique()->toArray(); // 获取玩家数据 $players = Player::query() ->with(['wallet', 'player_level']) ->whereIn('id', $playerIds) ->get() ->keyBy('id'); DB::beginTransaction(); try { /** @var CommissionRecord $item */ foreach ($data as $item) { /** @var Player $player */ $player = $players->get($item->player_id); $playerLevel = $player->player_level; if (!empty($playerLevel) && $playerLevel->damage_rebate_ratio > 0 && $item->damage_amount > 0) { $damageAmount = bcmul($item->damage_amount, $playerLevel->damage_rebate_ratio, 2); $beforeGameAmount = $player->wallet->money; if ($damageAmount > 0) { // 更新钱包 $player->wallet->money = bcadd($player->wallet->money, $damageAmount, 2); if ($playerLevel->chip_multiple) { $chipAmount = bcmul($damageAmount, $playerLevel->chip_multiple); if ($chipAmount) { $beforeMustChipAmount = $player->must_chip_amount; $player->must_chip_amount = bcadd($player->must_chip_amount, $chipAmount, 2); // 记录打码量明细 $playerChipRecord = new PlayerChipRecord(); $playerChipRecord->player_id = $player->id; $playerChipRecord->department_id = $player->department_id; $playerChipRecord->type = PlayerChipRecord::TYPE_INC; $playerChipRecord->record_type = PlayerChipRecord::RECORD_TYPE_BET_REBATE; $playerChipRecord->amount = $chipAmount; $playerChipRecord->chip_amount = 0; $playerChipRecord->before_chip_amount = $player->chip_amount; $playerChipRecord->after_chip_amount = $player->chip_amount; $playerChipRecord->must_chip_amount = $chipAmount; $playerChipRecord->before_must_chip_amount = $beforeMustChipAmount; $playerChipRecord->after_must_chip_amount = $player->must_chip_amount; $playerChipRecord->source_type = CommissionRecord::class; $playerChipRecord->source_id = $item->id; $playerChipRecord->save(); } } // 根据打码量返水玩家 $playerDeliveryRecord = new PlayerDeliveryRecord; $playerDeliveryRecord->player_id = $player->id; $playerDeliveryRecord->department_id = $player->department_id; $playerDeliveryRecord->target = $item->getTable(); $playerDeliveryRecord->target_id = $item->id; $playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_DAMAGE_REBATE; $playerDeliveryRecord->source = 'damage_rebate'; $playerDeliveryRecord->amount = $damageAmount; $playerDeliveryRecord->amount_before = $beforeGameAmount; $playerDeliveryRecord->amount_after = $player->wallet->money; $playerDeliveryRecord->tradeno = ''; $playerDeliveryRecord->remark = ''; $playerDeliveryRecord->save(); } } $player->push(); } DB::commit(); } catch (\Exception $e) { DB::rollBack(); Log::error('damageRebate', [$e->getMessage(), $e->getTraceAsString()]); } Log::info('客损返水结算结束------------------'); } /** * 玩家等级选项 * @return array */ function playerLevelOptions(): array { $options = []; for ($i = 0; $i <= 13; $i++) { if ($i == 0) { $options[$i] = admin_trans('player.no_level'); } else { $options[$i] = admin_trans('player.level.' . $i); } } return $options; } /** * 自动生成密码 * @return string */ function generateRandomPassword(): string { $uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; $lowercase = 'abcdefghijklmnopqrstuvwxyz'; $numbers = '0123456789'; $password = ''; $password .= $uppercase[rand(0, strlen($uppercase) - 1)]; $password .= $lowercase[rand(0, strlen($lowercase) - 1)]; $password .= $numbers[rand(0, strlen($numbers) - 1)]; $length = rand(7, 12); // Random length between 7 and 12 $remainingLength = $length - 3; $allChars = $uppercase . $lowercase . $numbers; for ($i = 0; $i < $remainingLength; $i++) { $password .= $allChars[rand(0, strlen($allChars) - 1)]; } return str_shuffle($password); } /** * 生成用户名 * @param int $length * @return string */ function generateUniqueUsername(int $length = 10): string { $characters = 'abcdefghijklmnopqrstuvwxyz0123456789'; // 可选的字符集合 $uniqueUsername = ''; // 初始化唯一用户名 // 生成不重复的用户名 for ($i = 0; $i < $length; $i++) { $uniqueUsername .= $characters[rand(0, strlen($characters) - 1)]; } return $uniqueUsername; } /** * 组装请求 * @param string $url * @param array $params * @return array|mixed|null * @throws Exception */ function dogGetCurl(string $url, array $params = []) { $response = Http::timeout(7) ->contentType('application/json') ->accept('application/json') ->asJson() ->get($url, $params); if (!$response->ok()) { throw new Exception(trans('system_busy', [], 'message')); } $data = $response->json(); if (empty($data)) { throw new Exception(trans('system_busy', [], 'message')); } return $data; } /** * 设置推广员 * @param $id * @param $ratio * @param $name * @return true * @throws Exception */ function setPromoter($id, $ratio, $name,$recommend_id): bool { DB::beginTransaction(); try { /** @var Player $player */ $player = Player::find($id); if (empty($player)) { throw new Exception(trans('player_not_found', [], 'message')); } if (!empty($player->player_promoter)) { throw new Exception(trans('player_is_promoter', [], 'message')); } if ($player->status == Player::STATUS_STOP) { throw new Exception(trans('player_stop', [], 'message')); } if($recommend_id != $player->recommend_id){ throw new Exception(trans('not_sub_recommend_player', [], 'message')); } $promoter = new PlayerPromoter(); /** @var PlayerPromoter $parentPromoter */ $parentPromoter = PlayerPromoter::where('player_id', $player->recommend_id)->first(); $maxRatio = $parentPromoter->ratio ?? 100; if ($ratio > $maxRatio) { throw new Exception(trans('ratio_max_error', ['{max_ratio}' => $maxRatio], 'message')); } $orgPromoter = $player->is_promoter; $path = []; if (isset($parentPromoter->path) && !empty($parentPromoter->path)) { $path = explode(',', $parentPromoter->path); } $path[] = $player->id; $promoter->ratio = $ratio; $promoter->player_id = $player->id; $promoter->recommend_id = $parentPromoter->player_id ?? 0; $promoter->department_id = $player->department_id; $promoter->name = !empty($name) ? $name : $player->name; $promoter->path = implode(',', $path); $promoter->save(); // 更新玩家信息 $player->is_promoter = 1; $player->recommend_code = createCode(); $player->save(); $parentPromoter && $orgPromoter == 0 && $parentPromoter->increment('team_num'); DB::commit(); } catch (\Exception $e) { DB::rollBack(); throw new Exception($e->getMessage()); } return true; } /** * 验证手机号格式 * @param $phoneNumber * @return true */ function validateMalaysianPhoneNumber($phoneNumber): bool { $pattern = '/^(01\d{8,9}|(\+60|60|0060)(0?1)\d{8,9})$/'; return preg_match($pattern, $phoneNumber); } /** * post表单请求 * @param string $url * @param array $params * @return array|mixed * @throws Exception */ function doFormCurl(string $url, array $params = []) { $response = Http::timeout(10)->asForm()->post($url, $params); if (!$response->ok()) { throw new Exception(trans('system_busy', [], 'message')); } $data = $response->json(); if (empty($data)) { throw new Exception(trans('system_busy', [], 'message')); } return $data; } /** * 结算 * @param $id * @param int $userId * @param string $userName * @return void * @throws Exception */ function doSettlement($id, int $userId = 0, string $userName = '') { /** @var PlayerPromoter $playerPromoter */ $playerPromoter = PlayerPromoter::where('player_id', $id)->first(); if (empty($playerPromoter)) { throw new Exception(trans('profit_amount_not_found', [], 'message')); } if ($playerPromoter->status == 0) { throw new Exception(trans('player_promoter_has_disable', [], 'message')); } if (!isset($playerPromoter->profit_amount)) { throw new Exception(trans('profit_amount_not_found', [], 'message')); } $profitAmount = PromoterProfitRecord::where('status', PromoterProfitRecord::STATUS_UNCOMPLETED) ->where('promoter_player_id', $id) ->first([ DB::raw('SUM(`withdraw_amount`) as total_withdraw_amount'), DB::raw('SUM(`recharge_amount`) as total_recharge_amount'), DB::raw('SUM(`bonus_amount`) as total_bonus_amount'), DB::raw('SUM(`admin_deduct_amount`) as total_admin_deduct_amount'), DB::raw('SUM(`admin_add_amount`) as total_admin_add_amount'), DB::raw('SUM(`present_amount`) as total_present_amount'), DB::raw('SUM(`profit_amount`) as total_profit_amount'), DB::raw('SUM(`player_profit_amount`) as total_player_profit_amount'), ]) ->toArray(); DB::beginTransaction(); try { $promoterProfitSettlementRecord = new PromoterProfitSettlementRecord(); $promoterProfitSettlementRecord->department_id = $playerPromoter->player->department_id; $promoterProfitSettlementRecord->promoter_player_id = $playerPromoter->player_id; $promoterProfitSettlementRecord->total_withdraw_amount = $profitAmount['total_withdraw_amount'] ?? 0; $promoterProfitSettlementRecord->total_recharge_amount = $profitAmount['total_recharge_amount'] ?? 0; $promoterProfitSettlementRecord->total_bonus_amount = $profitAmount['total_bonus_amount'] ?? 0; $promoterProfitSettlementRecord->total_admin_deduct_amount = $profitAmount['total_admin_deduct_amount'] ?? 0; $promoterProfitSettlementRecord->total_admin_add_amount = $profitAmount['total_admin_add_amount'] ?? 0; $promoterProfitSettlementRecord->total_present_amount = $profitAmount['total_present_amount'] ?? 0; $promoterProfitSettlementRecord->total_profit_amount = $profitAmount['total_profit_amount'] ?? 0; $promoterProfitSettlementRecord->total_player_profit_amount = $profitAmount['total_player_profit_amount'] ?? 0; $promoterProfitSettlementRecord->last_profit_amount = $playerPromoter->last_profit_amount; $promoterProfitSettlementRecord->adjust_amount = $playerPromoter->adjust_amount; $promoterProfitSettlementRecord->type = PromoterProfitSettlementRecord::TYPE_SETTLEMENT; $promoterProfitSettlementRecord->tradeno = createOrderNo(); $promoterProfitSettlementRecord->user_id = $userId; $promoterProfitSettlementRecord->user_name = $userName; $settlement = $amount = bcadd($promoterProfitSettlementRecord->total_profit_amount, $promoterProfitSettlementRecord->adjust_amount, 2); if ($amount > 0) { if ($playerPromoter->settlement_amount < 0) { $diffAmount = bcadd($amount, $playerPromoter->settlement_amount, 2); $settlement = max($diffAmount, 0); } } $promoterProfitSettlementRecord->actual_amount = $settlement; $promoterProfitSettlementRecord->save(); // 更新结算报表 PromoterProfitRecord::where('status', PromoterProfitRecord::STATUS_UNCOMPLETED) ->where('promoter_player_id', $id) ->update([ 'status' => PromoterProfitRecord::STATUS_COMPLETED, 'settlement_time' => date('Y-m-d H:i:s'), 'settlement_tradeno' => $promoterProfitSettlementRecord->tradeno, 'settlement_id' => $promoterProfitSettlementRecord->id, ]); // 结算后这些数据清零 $playerPromoter->profit_amount = 0; $playerPromoter->player_profit_amount = 0; $playerPromoter->team_recharge_total_amount = 0; $playerPromoter->team_withdraw_total_amount = 0; $playerPromoter->adjust_amount = 0; // 更新数据 $playerPromoter->team_profit_amount = bcsub($playerPromoter->team_profit_amount, $promoterProfitSettlementRecord->total_profit_amount, 2); $playerPromoter->last_profit_amount = $settlement; $playerPromoter->settlement_amount = bcadd($playerPromoter->settlement_amount, $amount, 2); $playerPromoter->team_settlement_amount = bcadd($playerPromoter->team_settlement_amount, $promoterProfitSettlementRecord->total_profit_amount, 2); $playerPromoter->last_settlement_time = date('Y-m-d', strtotime('-1 day')); if (!empty($playerPromoter->path)) { PlayerPromoter::where('player_id', '!=', $playerPromoter->player_id) ->whereIn('player_id', explode(',', $playerPromoter->path)) ->update([ 'team_profit_amount' => DB::raw("team_profit_amount - {$promoterProfitSettlementRecord->total_profit_amount}"), 'team_settlement_amount' => DB::raw("team_settlement_amount + $promoterProfitSettlementRecord->total_profit_amount"), ]); } if ($settlement > 0) { // 增加钱包余额 $amountBefore = $playerPromoter->player->wallet->money; $amountAfter = bcadd($amountBefore, $settlement, 2); $playerDeliveryRecord = new PlayerDeliveryRecord; $playerDeliveryRecord->player_id = $playerPromoter->player_id; $playerDeliveryRecord->department_id = $playerPromoter->department_id; $playerDeliveryRecord->target = $promoterProfitSettlementRecord->getTable(); $playerDeliveryRecord->target_id = $promoterProfitSettlementRecord->id; $playerDeliveryRecord->type = PlayerDeliveryRecord::TYPE_PROFIT; $playerDeliveryRecord->source = 'profit'; $playerDeliveryRecord->amount = $settlement; $playerDeliveryRecord->amount_before = $amountBefore; $playerDeliveryRecord->amount_after = $amountAfter; $playerDeliveryRecord->tradeno = $promoterProfitSettlementRecord->tradeno ?? ''; $playerDeliveryRecord->remark = ''; $playerDeliveryRecord->save(); $playerPromoter->player->wallet->money = $amountAfter; } $playerPromoter->push(); DB::commit(); } catch (\Exception $e) { DB::rollback(); throw new Exception($e->getMessage()); } }