/**
* @description: 获取用户海报URL
* @param {Request} $request
* @return {*}
*/
public function getPosterUrl(Request $request)
{
$tel = '15200000000';
$posterUrl = self::createPoster($tel);
$posterUrl = Config::get('app.web_url') . substr($posterUrl, 1);
return $this->showSuccess($posterUrl);
}
/**
* @description: 生成用户海报
* @param {string} $tel
* @return {*}
*/
private function createPoster(string $tel): string
{
// ========== 1. 前置校验 ==========
if (empty($tel)) {
throw new \InvalidArgumentException('手机号不能为空');
}
// 海报保存目录(绝对路径,避免相对路径问题)
$saveDir = app()->getRootPath() . 'public/poster/';
// 确保目录存在且有写入权限
if (!is_dir($saveDir)) {
mkdir($saveDir, 0755, true);
}
if (!is_writable($saveDir)) {
throw new \RuntimeException('海报目录无写入权限:' . $saveDir);
}
$savePath = $saveDir . $tel . '.jpg';
// 已存在则直接返回(避免重复生成)
if (file_exists($savePath)) {
return '/poster/' . $tel . '.jpg';
}
// ========== 2. 加载背景图(增加异常处理) ==========
try {
$backgroundUrl = '/poster/background.jpg';
if (empty($backgroundUrl)) {
throw new \RuntimeException('未配置海报背景图');
}
// 处理远程背景图(如果是URL先下载)
if (str_starts_with($backgroundUrl, 'http')) {
$backgroundContent = file_get_contents($backgroundUrl);
if (!$backgroundContent) {
throw new \RuntimeException('下载背景图失败:' . $backgroundUrl);
}
$background = imagecreatefromstring($backgroundContent);
} else {
// 本地图片(转为绝对路径)
$backgroundAbsPath = app()->getRootPath() . 'public' . $backgroundUrl;
$backgroundInfo = getimagesize($backgroundAbsPath);
if (!$backgroundInfo) {
throw new \RuntimeException('背景图无效:' . $backgroundAbsPath);
}
$backgroundFun = 'imagecreatefrom' . image_type_to_extension($backgroundInfo[2], false);
$background = $backgroundFun($backgroundAbsPath);
}
if (!$background) {
throw new \RuntimeException('创建背景图资源失败');
}
// 一次性获取背景尺寸(避免重复调用)
$bgW = imagesx($background);
$bgH = imagesy($background);
} catch (\Exception $e) {
throw new \RuntimeException('加载背景图失败:' . $e->getMessage());
}
// ========== 3. 创建画布(优化初始化逻辑) ==========
$imageRes = imagecreatetruecolor($bgW, $bgH);
if (!$imageRes) {
imagedestroy($background); // 提前释放资源
throw new \RuntimeException('创建画布失败');
}
// 开启透明支持(避免黑色背景)
imagesavealpha($imageRes, true);
$transparentColor = imagecolorallocatealpha($imageRes, 0, 0, 0, 127);
imagefill($imageRes, 0, 0, $transparentColor);
// 复制背景图到画布(复用已获取的尺寸)
imagecopyresampled($imageRes, $background, 0, 0, 0, 0, $bgW, $bgH, $bgW, $bgH);
imagedestroy($background); // 背景图用完立即释放
// ========== 4. 获取小程序二维码(增加超时/错误处理) ==========
try {
$accessToken = $this->getAccessToken();
if (empty($accessToken)) {
throw new \RuntimeException('获取微信access_token失败');
}
$url = 'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=' . $accessToken;
$data = [
'scene' => 'form_id=' . (request()->user_id ?? ''),
'page' => 'pages/index/index',
'check_path' => false,
'env_version' => 'release',
];
// 初始化客户端(增加超时控制)
$client = new Client(['timeout' => 10]); // 10秒超时
$response = $client->request('POST', $url, [
'json' => $data,
'headers' => ['Content-Type' => 'application/json'],
]);
$content = $response->getBody()->getContents();
// 校验二维码是否有效(微信返回错误时是JSON,不是图片)
$json = json_decode($content, true);
if (json_last_error() === JSON_ERROR_NONE && isset($json['errcode'])) {
throw new \RuntimeException('获取二维码失败:' . $json['errmsg'] . '(code:' . $json['errcode'] . ')');
}
$qrcode = imagecreatefromstring($content);
if (!$qrcode) {
throw new \RuntimeException('创建二维码图像资源失败');
}
// 二维码尺寸(复用)
$codeW = imagesx($qrcode);
$codeH = imagesy($qrcode);
} catch (\Exception $e) {
imagedestroy($imageRes); // 释放画布资源
throw new \RuntimeException('获取二维码失败:' . $e->getMessage());
}
// ========== 5. 合成二维码到画布(优化坐标计算) ==========
$codeX = $bgW - $codeW - 160; // 右间距160
$codeY = $bgH - $codeH - 400; // 下间距400
// 确保二维码坐标不超出画布
$codeX = max(0, $codeX);
$codeY = max(0, $codeY);
imagecopyresampled($imageRes, $qrcode, $codeX, $codeY, 0, 0, $codeW, $codeH, $codeW, $codeH);
imagedestroy($qrcode); // 二维码用完立即释放
// ========== 6. 保存图片(优化压缩/错误处理) ==========
$success = imagejpeg($imageRes, $savePath, 90);
imagedestroy($imageRes); // 最后释放画布
if (!$success) {
throw new \RuntimeException('保存海报失败:' . $savePath);
}
// ========== 7. 返回相对路径 ==========
return '/poster/' . $tel . '.jpg';
}