writer(new PngWriter()) ->data($content) ->size($qrSize) ->margin(0) ->build(); $qrImage = imagecreatefromstring($result->getString()); // 创建画布(加上下方文字区和边距) $canvasWidth = $qrSize + $padding * 2; $canvasHeight = $qrSize + $textHeight + $padding * 2; $image = imagecreatetruecolor($canvasWidth, $canvasHeight); // 背景白色 $white = imagecolorallocate($image, 255, 255, 255); imagefill($image, 0, 0, $white); // 黑色字体 $black = imagecolorallocate($image, 0, 0, 0); // 合并二维码图像 imagecopy($image, $qrImage, $padding, $padding, 0, 0, $qrSize, $qrSize); // 写文字 $textWidth = imagefontwidth($font) * strlen($content); $x = ($canvasWidth - $textWidth) / 2; $y = $qrSize + $padding + 5; imagestring($image, $font, $x, $y, $content, $black); // 生成文件名 $filename = $address. '.png'; $relativePath = 'recharge/' . $filename; $storagePath = storage_path('app/public/' . $relativePath); // 确保目录存在 @mkdir(dirname($storagePath), 0777, true); // 保存图片到文件 imagepng($image, $storagePath); // 清理 imagedestroy($qrImage); imagedestroy($image); // 返回 public 存储路径(可用于 URL) return 'storage/'.$relativePath; // 或返回 Storage::url($relativePath); } /** * 判断指定地址的二维码是否已生成(已存在文件) * * @param string $address 充值地址 * @return */ public static function rechargeQrCodeExists(string $address) { $filename = $address . '.png'; $relativePath = 'recharge/' . $filename; $storagePath = storage_path('app/public/' . $relativePath); $path = ''; if(file_exists($storagePath)){ $path = 'storage/'.$relativePath; } return $path; } /** * @description: 转成树形数据 * @param {*} $list 初始数据 * @param {*} $pid 父id * @param {*} $level 层级 * @param {*} $pid_name pid字段名称 默认pid * @param {*} $id_name 主键id 名称 * @return {*} */ public static function toTree($list,$pid=0,$level=0,$pid_name='pid',$id_name='id') { $arr=[]; $level++; foreach($list as $k => $v){ if($pid==$v[$pid_name]){ $v['level']=$level; $v['children']=self::toTree($list,$v[$id_name],$level,$pid_name,$id_name); $arr[]=$v; } } return $arr; } /** * @description: 实例化TG * @return {*} */ public static function telegram() { return app(Api::class); } // /** // * @description: 群组通知(自动分段发送,支持中文与多字节字符) // * @param string $text 通知内容 // * @param array $keyboard 操作按钮 // * @param string $image 图片路径(可选) // * @param bool $isTop 是否置顶第一条消息 // */ // public static function bettingGroupNotice($text, $keyboard = [], $image = '', $isTop = false) // { // $bettingGroup = Config::where('field', 'betting_group')->first()->val; // $telegram = self::telegram(); // // $maxLen = 1024; // Telegram 限制:最多 1024 个字符 // $textParts = []; // $textLength = mb_strlen($text, 'UTF-8'); // for ($i = 0; $i < $textLength; $i += $maxLen) { // $textParts[] = mb_substr($text, $i, $maxLen, 'UTF-8'); // } // // $firstMessageId = null; // // foreach ($textParts as $index => $partText) { // $botMsg = [ // 'chat_id' => "@{$bettingGroup}", // 'text' => $partText, // ]; // // if (count($keyboard) > 0 && $index === 0) { // $botMsg['reply_markup'] = json_encode(['inline_keyboard' => $keyboard]); // } // // if (!empty($image) && $index === 0) { // // 第一条带图片 // $botMsg['photo'] = InputFile::create($image); // $botMsg['caption'] = $partText; // $botMsg['protect_content'] = true; // $response = $telegram->sendPhoto($botMsg); // } else { // $response = $telegram->sendMessage($botMsg); // } // // if ($isTop && $index === 0 && $response && $response->get('message_id')) { // $firstMessageId = $response->get('message_id'); // } // // // 防止限流(可选) // usleep(300000); // } // // if ($isTop && $firstMessageId) { // $telegram->pinChatMessage([ // 'chat_id' => "@{$bettingGroup}", // 'message_id' => $firstMessageId // ]); // } // } /** * @description: 群组通知 * @param {string} $text 通知内容 * @param {array} $keyboard 操作按钮 * @param {*string} $image 图片 * @return {*} */ public static function bettingGroupNotice($text ,$keyboard = [], $image = '' ,$isTop = false) { $bettingGroup = Config::where('field', 'betting_group')->first()->val; $botMsg = [ 'chat_id' => "@{$bettingGroup}", 'text' => $text ]; if(count($keyboard)>0){ $botMsg['reply_markup'] = json_encode(['inline_keyboard' => $keyboard]); } if(!empty($image)){ $botMsg['photo'] = InputFile::create($image); $botMsg['caption'] = $text; $botMsg['protect_content'] = true; // 防止转发 $response = self::telegram()->sendPhoto($botMsg); }else{ $response = self::telegram()->sendMessage($botMsg); } if($isTop == true){ // 获取消息ID $messageId = $response->get('message_id'); // 置顶消息 self::telegram()->pinChatMessage([ 'chat_id' => "@{$bettingGroup}", 'message_id' => $messageId ]); } } /** * @description: 异步群组通知 * @param {string} $text 通知内容 * @param {array} $keyboard 操作按钮 * @param {*string} $image 图片 * @return {*} */ public static function asyncBettingGroupNotice($text ,$keyboard = [], $image = '' ,$isTop = false) { SendTelegramGroupMessageJob::dispatch($text ,$keyboard ,$image ,$isTop); } /** * @description: 发送消息 * @param {string} $chatId 聊天ID * @param {string} $text 消息内容 * @param {array} $keyboard 操作按钮 * @param {*string} $image 图片 * @return {*} */ public static function sendMessage($chatId ,$text ,$keyboard = [] ,$image = '') { $botMsg = [ 'chat_id' => $chatId, ]; if(count($keyboard)>0){ $botMsg['reply_markup'] = json_encode(['inline_keyboard' => $keyboard]); } if($image != ''){ $botMsg['photo'] = InputFile::create($image); $botMsg['caption'] = $text; $botMsg['protect_content'] = true; // 防止转发 self::telegram()->sendPhoto($botMsg); }else{ $botMsg['text'] = $text; self::telegram()->sendMessage($botMsg); } } /** * @description: 异步发送消息 * @param {string} $chatId 聊天ID * @param {string} $text 消息内容 * @param {array} $keyboard 操作按钮 * @param {*string} $image 图片 * @return {*} */ public static function asyncSendMessage($chatId ,$text ,$keyboard = [] ,$image = '') { SendTelegramMessageJob::dispatch($chatId ,$text ,$keyboard ,$image); } /** * @description: 弹窗提示 * @param {*} $memberId * @param {*} $address * @return {*} */ public static function alertNotice($callbackId ,$text) { self::telegram()->answerCallbackQuery([ 'callback_query_id' => $callbackId, 'text' => $text, 'show_alert' => true // 显示为弹窗 ]); } public static function log($message, $context = []) { Log::error($message, $context); } /** * @description: 获取操作按钮 * @return {*} */ public static function getOperateButton() { $replyInfo = KeyboardService::findOne(['button' => '投注菜单']); if($replyInfo && $replyInfo->buttons){ $buttons = json_decode($replyInfo->buttons, true); foreach ($buttons as $row) { $inlineButton[] = []; foreach ($row as $button) { $btn = ['text' => $button['text']]; if(strpos($button['url'], 'http') === 0){ $btn['url'] = $button['url']; }else{ $btn['callback_data'] = $button['url']; } $inlineButton[count($inlineButton) - 1][] = $btn; // if (isset($button['text'])) { // $btn = ['text' => $button['text']]; // if (isset($button['callback_data'])) { // $btn['callback_data'] = $button['callback_data']; // } // if (isset($button['url'])) { // $btn['url'] = $button['url']; // } // $inlineButton[count($inlineButton) - 1][] = $btn; // } } } $inlineButton = array_values($inlineButton); return $inlineButton; } // $username = config('services.telegram.username'); // $serviceAccount = Config::where('field', 'service_account')->first()->val??''; // $officialChannel = Config::where('field', 'official_channel')->first()->val??''; $inlineButton = []; // $inlineButton[] = [ // ['text' => "查看余额", 'callback_data' => 'balanceAlert'], // ['text' => "✅唯一财务", 'url' => "https://t.me/{$serviceAccount}"] // ]; // $inlineButton[] = [ // ['text' => "近期注单", 'callback_data' => 'betsAlert'], // ['text' => "今日流水", 'callback_data' => 'todayFlowAlert'] // ]; // $inlineButton[] = [ // ['text' => "私聊下注", 'url' => "https://t.me/{$username}"] // ]; // $inlineButton[] = [ // ['text' => "官方频道", 'url' => "https://t.me/{$officialChannel}"] // ]; return $inlineButton; } // 获取字符串最后几个字符 public static function getLastChar($str,$num = 1) { $length = mb_strlen($str, 'UTF-8'); $lastChar = mb_substr($str, $length - 1, $num, 'UTF-8'); return $lastChar; } public static function generateRandomString($length = 8) { $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, strlen($characters) - 1)]; } return $randomString; } public static function generateRandomNumber($length = 8) { $characters = '0123456789'; $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= rand(1, 9); } return $randomString; } public static function hideMiddleDigits($number, $hideCount = 4) { $length = strlen($number); if ($length <= $hideCount) { // 数字太短,全部隐藏 return str_repeat("*", $length); } // 计算中间开始隐藏的位置 $startLen = floor(($length - $hideCount) / 2); $endLen = $length - $hideCount - $startLen; $start = substr($number, 0, $startLen); $end = substr($number, -$endLen); return $start . str_repeat("*", $hideCount) . $end; } // 生成订单号 public static function createOrderNo($prefix = 'pc28_',$memberId = null) { // 处理会员ID,获取后四位 if ($memberId) { $memberSuffix = str_pad(substr($memberId, -4), 4, '0', STR_PAD_LEFT); } else { $memberSuffix = '0000'; // 默认值 } // 时间部分 $timePart = date('YmdHis'); // 随机部分增加唯一性 $randomPart = mt_rand(1000, 9999); return $prefix . $timePart . $randomPart . $memberSuffix; } /** * @description: 生成支付二维码 * @param {*} $address 支付地址 * @return {*} */ public static function createPaymentQrCode($address = '') { // $content = $address; $content = ''; $qrSize = 300; $font = 4; $textHeight = 20; $padding = 10; // 生成二维码图像对象 $result = Builder::create() ->writer(new PngWriter()) ->data($address) ->size($qrSize) ->margin(0) ->build(); $qrImage = imagecreatefromstring($result->getString()); // 创建画布(加上下方文字区和边距) $canvasWidth = $qrSize + $padding * 2; $canvasHeight = $qrSize + $textHeight + $padding * 2; $image = imagecreatetruecolor($canvasWidth, $canvasHeight); // 背景白色 $white = imagecolorallocate($image, 255, 255, 255); imagefill($image, 0, 0, $white); // 黑色字体 $black = imagecolorallocate($image, 0, 0, 0); // 合并二维码图像 imagecopy($image, $qrImage, $padding, $padding, 0, 0, $qrSize, $qrSize); // 写文字 $textWidth = imagefontwidth($font) * strlen($content); $x = ($canvasWidth - $textWidth) / 2; $y = $qrSize + $padding + 5; imagestring($image, $font, $x, $y, $content, $black); $address_name = self::generateRandomString(20).time(); // 生成文件名 $filename = $address_name. '.png'; $relativePath = 'payment/' . $filename; $storagePath = storage_path('app/public/' . $relativePath); // 确保目录存在 @mkdir(dirname($storagePath), 0777, true); // 保存图片到文件 imagepng($image, $storagePath); // 清理 imagedestroy($qrImage); imagedestroy($image); // 返回 public 存储路径(可用于 URL) return 'storage/'.$relativePath; // 或返回 Storage::url($relativePath); } }