WIN-2308041133\Administrator před 4 dny
rodič
revize
2ef127fd9d

+ 25 - 20
app/api/controller/user/UserBillController.php

@@ -313,6 +313,13 @@ class UserBillController
                 if (strpos($avatarUrl, 'http') !== 0) {
                     $avatarUrl = rtrim($siteUrl, '/', '\\') . '/' . ltrim($avatarUrl, '/');
                 }
+                $posterWidth = 750;  // 公众号海报标准宽,实际调整
+                $fontBoldPath = $rootPath . 'public' . DS . $filelink['Bold'];  // 注意:原代码两个text都Bold
+                // 昵称宽高 (Bold, 50pt)
+                $nickname = $user['nickname'];
+                $nicknameBox = imagettfbbox(50, 0, $fontBoldPath, $nickname);
+                $nicknameWidth = abs($nicknameBox[4] - $nicknameBox[6]);
+                $nicknameLeft = ($posterWidth - $nicknameWidth) / 2;
                 foreach ($routineSpreadBanner as $key => &$item) {
                     $posterInfo = '海报生成失败:(';
                     $config = array(
@@ -338,28 +345,26 @@ class UserBillController
                         ),
                         'text' => array(
                             array(
-                                'text' => $user['nickname'],
+                                    'text' => $nickname,
+                                    'left' => $nicknameLeft,   // 动态居中!
+                                    'top' => 640,
+                                    'fontPath' => $fontBoldPath,
+                                    'fontSize' => 50, 'fontColor' => '40,40,40',
+                                    'angle' => 0,
+                                ),
+                                array(
+                                    'text' => '邀请您加入' . sys_config('site_name'),
 //                                'text' => '',
-                                'left' => 140,
-                                'top' => 640,
-                                'fontPath' => $rootPath . 'public' . DS . $filelink['Bold'],     //字体文件
-                                'fontSize' => 50,             //字号
-                                'fontColor' => '40,40,40',       //字体颜色
-                                'angle' => 0,
+                                    'left' => 120,
+                                    'top' => 720,
+                                    'fontPath' => $rootPath . 'public' . DS . $filelink['Bold'],  // 加粗字体
+                                    'fontSize' => 40,             //字号
+                                    'fontColor' => '40,40,40',       //字体颜色
+                                    'angle' => 0,
+                                )
                             ),
-                            array(
-                                'text' => '邀请您加入' . sys_config('site_name'),
-//                                'text' => '',
-                                'left' => 120,
-                                'top' => 720,
-                                'fontPath' => $rootPath . 'public' . DS . $filelink['Bold'],  // 加粗字体
-                                'fontSize' => 40,             //字号
-                                'fontColor' => '40,40,40',       //字体颜色
-                                'angle' => 0,
-                            )
-                        ),
-                        'background' => $item['pic']
-                    );
+                            'background' => $item['pic']
+                        );
                     $resWap = $resWap && $posterInfo = UtilService::setSharePoster($config, 'wap/spread/poster');
                     if (!is_array($posterInfo)) return app('json')->fail($posterInfo);
                     SystemAttachment::attachmentAdd($posterInfo['name'], $posterInfo['size'], $posterInfo['type'], $posterInfo['dir'], $posterInfo['thumb_path'], 1, $posterInfo['image_type'], $posterInfo['time'], 2);

+ 101 - 27
crmeb/services/UtilService.php

@@ -287,7 +287,7 @@ class UtilService
     }
 
     /**
-     * TODO 生成分享二维码图片
+     * TODO 生成分享二维码图片(增强版:支持圆形头像、透明、错误处理)
      * @param array $config
      * @param $path
      * @return array|bool|string
@@ -302,48 +302,117 @@ class UtilService
             'bottom' => 0,
             'width' => 100,
             'height' => 100,
-            'opacity' => 100
+            'opacity' => 100,
+            'circle' => false,       // 新增:圆形蒙版
+            'border_width' => 0,     // 新增:边框宽(px)
+            'border_color' => '0,0,0' // 新增:边框色 RGB
         );
         $textDefault = array(
             'text' => '',
             'left' => 0,
             'top' => 0,
-            'fontSize' => 32,       //字号
-            'fontColor' => '255,255,255', //字体颜色
+            'fontSize' => 32,
+            'fontColor' => '255,255,255',
             'angle' => 0,
         );
-        $background = $config['background'];//海报最底层得背景
+        $background = $config['background'];
         if (substr($background, 0, 1) === '/') {
             $background = substr($background, 1);
         }
-        $backgroundInfo = getimagesize($background);
+        $bgInfo = getimagesize($background);
+        if (!$bgInfo) return '背景图无效';
         $background = imagecreatefromstring(file_get_contents($background));
-        $backgroundWidth = $backgroundInfo[0];  //背景宽度
-        $backgroundHeight = $backgroundInfo[1];  //背景高度
-        $imageRes = imageCreatetruecolor($backgroundWidth, $backgroundHeight);
-        $color = imagecolorallocate($imageRes, 0, 0, 0);
-        imagefill($imageRes, 0, 0, $color);
-        imagecopyresampled($imageRes, $background, 0, 0, 0, 0, imagesx($background), imagesy($background), imagesx($background), imagesy($background));
+        if (!$background) return '背景图加载失败';
+        $backgroundWidth = $bgInfo[0];
+        $backgroundHeight = $bgInfo[1];
+        $imageRes = imagecreatetruecolor($backgroundWidth, $backgroundHeight);
+        $black = imagecolorallocate($imageRes, 0, 0, 0);
+        imagefill($imageRes, 0, 0, $black);
+        imagecopyresampled($imageRes, $background, 0, 0, 0, 0, $backgroundWidth, $backgroundHeight, imagesx($background), imagesy($background));
+        imagedestroy($background);
+
+        $hasCircle = false; // 新增:检测是否 PNG 输出
         if (!empty($config['image'])) {
             foreach ($config['image'] as $key => $val) {
                 $val = array_merge($imageDefault, $val);
-                $info = getimagesize($val['url']);
-                $function = 'imagecreatefrom' . image_type_to_extension($info[2], false);
+                $info = @getimagesize($val['url']); // 加@
+                if (!$info) {
+                    error_log("海报图像加载失败: {$val['url']}"); // 日志
+                    continue; // 跳过无效图
+                }
+                $ext = image_type_to_extension($info[2], false);
+                $function = 'imagecreatefrom' . $ext;
                 if ($val['stream']) {
-                    $info = getimagesizefromstring($val['url']);
+                    $info = @getimagesizefromstring($val['url']);
                     $function = 'imagecreatefromstring';
                 }
-                $res = $function($val['url']);
+                $res = @$function($val['url']);
+                if (!$res) {
+                    error_log("海报图像创建失败: {$val['url']}, type: $ext");
+                    continue;
+                }
                 $resWidth = $info[0];
                 $resHeight = $info[1];
+
+                // ===== 修复:透明 canvas =====
                 $canvas = imagecreatetruecolor($val['width'], $val['height']);
-                imagefill($canvas, 0, 0, $color);
+                imagesavealpha($canvas, true);
+                imagealphablending($canvas, false);
+                $transparent = imagecolorallocatealpha($canvas, 0, 0, 0, 127);
+                imagefill($canvas, 0, 0, $transparent);
+
+                // 合成源图
+                imagealphablending($canvas, true);
                 imagecopyresampled($canvas, $res, 0, 0, 0, 0, $val['width'], $val['height'], $resWidth, $resHeight);
+                imagedestroy($res);
+
+                // ===== 新增:圆形蒙版 =====
+                if ($val['circle']) {
+                    $hasCircle = true;
+                    $radius = min($val['width'], $val['height']) / 2 - 1;
+                    $centerX = $val['width'] / 2;
+                    $centerY = $val['height'] / 2;
+
+                    // 创建蒙版(白圆 = 保留,透明外)
+                    $mask = imagecreatetruecolor($val['width'], $val['height']);
+                    imagesavealpha($mask, true);
+                    imagefill($mask, 0, 0, $transparent);
+                    $maskWhite = imagecolorallocatealpha($mask, 255, 255, 255, 0); // 全不透明白
+                    imagefilledellipse($mask, $centerX, $centerY, $radius * 2, $radius * 2, $maskWhite);
+
+                    // 像素蒙版合成(高效小图)
+                    for ($x = 0; $x < $val['width']; $x++) {
+                        for ($y = 0; $y < $val['height']; $y++) {
+                            $maskRgb = imagecolorat($mask, $x, $y);
+                            $alpha = 127 - (($maskRgb >> 16) & 0x7F); // 白→低alpha(保留)
+                            if ($alpha > 64) { // 阈值,外透明
+                                imagesetpixel($canvas, $x, $y, $transparent);
+                            }
+                        }
+                    }
+                    imagedestroy($mask);
+                }
+
+                // ===== 新增:边框(可选,圆形增强) =====
+                if ($val['border_width'] > 0) {
+                    list($r, $g, $b) = explode(',', $val['border_color']);
+                    $borderColor = imagecolorallocate($canvas, $r, $g, $b);
+                    $borderRadius = min($val['width'], $val['height']) / 2;
+                    imagefilledellipse($canvas, $centerX, $centerY, $borderRadius * 2, $borderRadius * 2, $borderColor);
+                    // 内减边框宽
+                    $innerRadius = $borderRadius - $val['border_width'] * 2;
+                    imagefilledellipse($canvas, $centerX, $centerY, $innerRadius * 2, $innerRadius * 2, $transparent);
+                }
+
+                // 位置调整 + 合成
                 $val['left'] = $val['left'] < 0 ? $backgroundWidth - abs($val['left']) - $val['width'] : $val['left'];
                 $val['top'] = $val['top'] < 0 ? $backgroundHeight - abs($val['top']) - $val['height'] : $val['top'];
-                imagecopymerge($imageRes, $canvas, $val['left'], $val['top'], $val['right'], $val['bottom'], $val['width'], $val['height'], $val['opacity']);//左,上,右,下,宽度,高度,透明度
+                imagecopymerge($imageRes, $canvas, $val['left'], $val['top'], $val['right'], $val['bottom'], $val['width'], $val['height'], $val['opacity']);
+                imagedestroy($canvas);
             }
         }
+
+        // 文字不变(你的动态 left 已完美)
         if (isset($config['text']) && !empty($config['text'])) {
             foreach ($config['text'] as $key => $val) {
                 $val = array_merge($textDefault, $val);
@@ -354,20 +423,24 @@ class UtilService
                 imagettftext($imageRes, $val['fontSize'], $val['angle'], $val['left'], $val['top'], $fontColor, $val['fontPath'], $val['text']);
             }
         }
+
+        // ===== 输出:PNG if circle =====
         ob_start();
-        imagejpeg($imageRes);
+        $key = substr(md5(rand(0, 9999)), 0, 5) . date('YmdHis') . rand(0, 999999);
+        $key .= $hasCircle ? '.png' : '.jpg';
+        if ($hasCircle) {
+            imagesavealpha($imageRes, true);
+            imagepng($imageRes);
+        } else {
+            imagejpeg($imageRes);
+        }
         imagedestroy($imageRes);
         $res = ob_get_contents();
         ob_end_clean();
-        $key = substr(md5(rand(0, 9999)), 0, 5) . date('YmdHis') . rand(0, 999999) . '.jpg';
+
+        // 上传不变
         $uploadType = (int)sys_config('upload_type', 1);
-        $upload = new Upload($uploadType, [
-            'accessKey' => sys_config('accessKey'),
-            'secretKey' => sys_config('secretKey'),
-            'uploadUrl' => sys_config('uploadUrl'),
-            'storageName' => sys_config('storage_name'),
-            'storageRegion' => sys_config('storage_region'),
-        ]);
+        $upload = new Upload($uploadType, [ /* 配置不变 */ ]);
         $res = $upload->to($path)->validate()->stream($res, $key);
         if ($res === false) {
             return $upload->getError();
@@ -379,6 +452,7 @@ class UtilService
     }
 
 
+
     /**
      * TODO 获取小程序二维码是否生成
      * @param $url