<?php
/**
 * Here is your custom functions.
 */

use addons\webman\Admin;
use addons\webman\model\Channel;
use addons\webman\model\ChannelFinancialRecord;
use addons\webman\model\CommissionRecord;
use addons\webman\model\GamePlatform;
use addons\webman\model\PhoneSmsLog;
use addons\webman\model\Player;
use addons\webman\model\PlayerBankruptcyRecord;
use addons\webman\model\PlayerChipRecord;
use addons\webman\model\PlayerDeliveryRecord;
use addons\webman\model\PlayerExtend;
use addons\webman\model\PlayerLoginRecord;
use addons\webman\model\PlayerMoneyEditLog;
use addons\webman\model\PlayerPlatformCash;
use addons\webman\model\PlayerRechargeRecord;
use addons\webman\model\PlayerRegisterRecord;
use addons\webman\model\PlayerWalletTransfer;
use addons\webman\model\PlayerWithdrawRecord;
use addons\webman\model\PlayGameRecord;
use addons\webman\model\PromoterProfitSettlementRecord;
use addons\webman\model\SystemSetting;
use app\exception\GameException;
use app\exception\PlayerCheckException;
use app\service\game\GameServiceFactory;
use Carbon\Carbon;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Arr;
use Respect\Validation\Exceptions\AllOfException;
use support\Cache;
use support\Db;
use support\Log;
use support\Model;
use support\Response;
use think\Exception;
use Tinywan\Jwt\JwtToken;
use Webman\Push\Api;
use Webman\Push\PushException;
use WebmanTech\LaravelHttpClient\Facades\Http;
use Workbunny\WebmanIpAttribution\Exceptions\IpAttributionException;
use Workbunny\WebmanIpAttribution\Location;
use addons\webman\model\PlayerPromoter;
use addons\webman\model\PromoterProfitRecord;

/**
 * @param array $data
 * @param string $message
 * @return Response
 */
function jsonSuccessResponse(string $message = '', array $data = []): Response
{
    return new Response(200, ['Content-Type' => '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());
    }
}