From 04102f7237efefa744090ed7c25f7b5d0807b679 Mon Sep 17 00:00:00 2001
From: quanwei <419654421@qq.com>
Date: Thu, 05 Feb 2026 18:11:57 +0800
Subject: [PATCH] 完成运营中心提现和运营中心权限管理

---
 admin/app/common/service/business/Poster.php |  381 +++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 333 insertions(+), 48 deletions(-)

diff --git a/admin/app/common/service/business/Poster.php b/admin/app/common/service/business/Poster.php
index 2296aaa..e41e315 100644
--- a/admin/app/common/service/business/Poster.php
+++ b/admin/app/common/service/business/Poster.php
@@ -113,13 +113,31 @@
     private function savePoster($backdrop, $avatarUrl, $imageUrl, $logo)
     {
 
-        // 创建画布
-        list($width, $height) = getimagesize($backdrop);
+        // 获取背景图信息,判断图片类型
+        $backdropInfo = getimagesize($backdrop);
+        $width = $backdropInfo[0];
+        $height = $backdropInfo[1];
+
+        // 根据原图类型创建对应类型的画布,提高质量
+        $imageType = $backdropInfo[2];
+
+        // 创建真彩色画布,确保足够的色彩深度
         $newImage = imagecreatetruecolor($width, $height);
-        $backdropIm = $this->imagEcr($backdrop);
+
+        // 启用抗锯齿
         imageantialias($newImage, true);
-        // 将第一张图片覆盖在新图像上
-        imagecopy($newImage, $backdropIm, 0, 0, 0, 0, $width, $height);
+
+        // 启用alpha混合模式,确保透明度正常
+        imagealphablending($newImage, true);
+
+        // 保存透明度
+        imagesavealpha($newImage, true);
+
+        // 加载背景图
+        $backdropIm = $this->imagEcr($backdrop);
+
+        // 将背景图复制到画布(使用更好的插值方法)
+        imagecopyresampled($newImage, $backdropIm, 0, 0, 0, 0, $width, $height, $width, $height);
         if (!empty($this->config['icon'])) {
             foreach ($this->config['icon'] as $key => $value) {
                 $this->addImagecopy($newImage, $value);
@@ -127,9 +145,6 @@
         }
         // 脱敏处理
         if($this->businessName=='desensitization'){
-            // 保存原始数据以便后续可能需要使用
-            $originalDealer = $this->dealer;
-            
             // 手机号脱敏:保留前3位和后4位
             if(!empty($this->dealer['mobile'])){
                 $this->dealer['mobile'] = $this->maskPhoneNumber($this->dealer['mobile']);
@@ -167,6 +182,7 @@
             $avatarY = $this->config['avatar']['top'];
             // 打开用户头像
             $avatarIm = $this->imagEcr($avatarUrl);
+            // 使用更好的插值方法复制头像(使用imagecopy保持原有质量)
             imagecopy($newImage, $avatarIm, $avatarX, $avatarY, 0, 0, $avatarWidth, $avatarWidth);
         }
         if (is_file($logo) && $this->config['logo']['display'] == 1) {
@@ -182,43 +198,109 @@
             $logoY = $this->config['logo']['top'];
             // 打开用户logo
             $logoImage = $this->imagEcr($logo);
+            // 使用更好的插值方法复制logo(使用imagecopy保持原有质量)
             imagecopy($newImage, $logoImage, $logoX, $logoY, 0, 0, $logoWidth, $logoHeight);
         }
         // 写入用户昵称
-        $this->addText($newImage, 'name');
+        $this->addText($newImage, 'name', '', 0, false, $width);
+        // 写入公司列表
+        $unitBaseTop = 0;
+        $unitFontSize = $this->config['unit'][0]['fontSize'];
         foreach ($this->dealer['unit'] as $key => $value) {
-            // 写入公司
-            $this->addText($newImage, 'unit', '', $key, true);
+            if($key > 0){
+                $this->config['unit'][$key] = $this->config['unit'][0];
+                // 计算偏移量:每个公司增加基于前一个公司实际行数的距离
+                $this->config['unit'][$key]['top'] = $this->config['unit'][0]['top'] + $unitBaseTop;
+            }
+            // 写入公司,获取实际占用的行数
+            $lineCount = $this->addText($newImage, 'unit', '', $key, true, $width);
+            // 计算下一个公司的偏移量:(行数 - 1) * 行高 + 行间距
+            $unitLineHeight = $this->getLineHeight($unitFontSize);
+            $unitLineSpacing = $this->getLineSpacing($unitFontSize);
+            if ($key > 0) {
+                $unitBaseTop += ($lineCount * $unitLineHeight) + $unitLineSpacing;
+            } else {
+                $unitBaseTop = ($lineCount * $unitLineHeight) + $unitLineSpacing;
+            }
         }
+        // 写入职位列表
         if (!empty($this->dealer['duties']) && !empty($this->config['duties'])) {
-           // 写入职位
-            $this->addText($newImage, 'duties');
+            $dutiesBaseTop = 0;
+            $dutiesFontSize = $this->config['duties'][0]['fontSize'];
+            foreach ($this->dealer['duties'] as $key => $value) {
+                if($key > 0){
+                    $this->config['duties'][$key] = $this->config['duties'][0];
+                    // 计算偏移量:每个职位增加基于前一个职位实际行数的距离
+                    $this->config['duties'][$key]['top'] = $this->config['duties'][0]['top'] + $dutiesBaseTop;
+                }
+                // 写入职位,获取实际占用的行数
+                $lineCount = $this->addText($newImage, 'duties','', $key, true, $width);
+                // 计算下一个职位的偏移量:(行数 - 1) * 行高 + 行间距
+                $dutiesLineHeight = $this->getLineHeight($dutiesFontSize);
+                $dutiesLineSpacing = $this->getLineSpacing($dutiesFontSize);
+                if ($key > 0) {
+                    $dutiesBaseTop += ($lineCount * $dutiesLineHeight) + $dutiesLineSpacing;
+                } else {
+                    $dutiesBaseTop = ($lineCount * $dutiesLineHeight) + $dutiesLineSpacing;
+                }
+            }
         }
-        
+
+        // 写入position列表(备用字段)
         if (!empty($this->dealer['position']) && !empty($this->config['position'])) {
+            $positionBaseTop = 0;
+            $positionFontSize = $this->config['position'][0]['fontSize'];
             foreach ($this->dealer['position'] as $key => $value) {
-                // 写入公司
-                $this->addText($newImage, 'position', '', $key, true);
+                if($key > 0){
+                    $this->config['position'][$key] = $this->config['position'][0];
+                    // 计算偏移量:每个position增加基于前一个实际行数的距离
+                    $this->config['position'][$key]['top'] = $this->config['position'][0]['top'] + $positionBaseTop;
+                }
+                // 写入position,获取实际占用的行数
+                $lineCount = $this->addText($newImage, 'position', '', $key, true, $width);
+                // 计算下一个position的偏移量:(行数 - 1) * 行高 + 行间距
+                $positionLineHeight = $this->getLineHeight($positionFontSize);
+                $positionLineSpacing = $this->getLineSpacing($positionFontSize);
+                if ($key > 0) {
+                    $positionBaseTop += ($lineCount * $positionLineHeight) + $positionLineSpacing;
+                } else {
+                    $positionBaseTop = ($lineCount * $positionLineHeight) + $positionLineSpacing;
+                }
             }
         }
         // 写入手机号
-        $this->addText($newImage, 'mobile', '手机:');
+        $this->addText($newImage, 'mobile', '手机:', 0, false, $width);
         if (!empty($this->dealer['wechat']) && !empty($this->config['wechat'])) {
             // 写入微信
-            $this->addText($newImage, 'wechat', '微信:');
+            $this->addText($newImage, 'wechat', '微信:', 0, false, $width);
         }
         // 写入邮箱
         if ($this->dealer['mailbox']) {
             // 写入邮箱
-            $this->addText($newImage, 'mailbox', '邮箱:');
+            $this->addText($newImage, 'mailbox', '邮箱:', 0, false, $width);
         }
         if ($this->dealer['phone']) {
-            $this->addText($newImage, 'phone', '电话:');
+            $this->addText($newImage, 'phone', '电话:', 0, false, $width);
         }
         // 保存图片
-        imagejpeg($newImage, $this->getPosterPath($imageUrl),100); // 根据需要选择合适的函数(如imagepng、imagegif等)
+        // 根据背景图类型选择保存格式
+        $imageType = $backdropInfo[2];
+        $savePath = $this->getPosterPath($imageUrl);
+
+        if ($imageType == IMAGETYPE_PNG) {
+            // PNG格式,使用最高质量(9)
+            imagepng($newImage, $savePath, 9);
+        } elseif ($imageType == IMAGETYPE_GIF) {
+            // GIF格式
+            imagegif($newImage, $savePath);
+        } else {
+            // JPEG格式,使用最高质量(100)
+            imagejpeg($newImage, $savePath, 100);
+        }
+
         // 清理内存
         imagedestroy($newImage);
+        imagedestroy($backdropIm);
         return $this->getPosterUrl($imageUrl);
     }
 
@@ -253,10 +335,17 @@
         imagesavealpha($targetImage, true);
         $bg = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
         imagefill($targetImage, 0, 0, $bg);
-        imagecopyresampled($targetImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
-        imagepng($targetImage, $imageUrl,0); // 根据需要选择合适的函数(如imagepng、imagegif等)
+        imagecopyresampled($targetImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); // 保存时使用最高质量
+        $ext = pathinfo($imageUrl, PATHINFO_EXTENSION);
+        if ($ext == 'png'){
+            imagepng($targetImage, $imageUrl, 9); // PNG使用最高质量(9)
+        }else{
+            imagejpeg($targetImage, $imageUrl, 100); // JPEG使用最高质量
+        }
+
         // 清理内存
         imagedestroy($targetImage);
+        imagedestroy($image);
     }
 
     /**
@@ -282,35 +371,42 @@
      * @param $key
      * @param $type
      * @param $width
-     * @return array|bool
+     * @return array|bool|int 返回实际占用的行数(仅对type=true时有效)
      */
     public function addText($editor, $name, $text = '', $key = 0, $type = false, $width = 0)
     {
         $fontPath = $_SERVER['DOCUMENT_ROOT']. '/fonts/MSYH.TTC';
         if ($type) {
-            list($fontSize, $fontX,$fontY)  = self::SizeLeftTop($this->config[$name][$key]['fontSize'], $this->config[$name][$key]['left'],$this->config[$name][$key]['top']);
-            $colorResource=self::colorResource($this->config[$name][$key]['color'],$editor);
-            return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text . $this->dealer[$name][$key]);
+            // 确保配置数组存在该索引,不存在则使用索引0
+            $configKey = isset($this->config[$name][$key]) ? $key : 0;
+            list($fontSize, $fontX,$fontY)  = self::SizeLeftTop($this->config[$name][$configKey]['fontSize'], $this->config[$name][$configKey]['left'],$this->config[$name][$configKey]['top']);
+            $colorResource=self::colorResource($this->config[$name][$configKey]['color'],$editor);
+            // 智能计算最大宽度
+            $maxWidth = $this->calculateFieldMaxWidth($this->config[$name][$configKey]['left'], $this->config[$name][$configKey]['top'], $width);
+            // 写入支持换行的文本
+            return $this->writeTextWithWrap($editor, $text . $this->dealer[$name][$key], $fontX, $fontY, $fontSize, $colorResource, $fontPath, $maxWidth);
         } else if ($name == 'mobile') {
             list($fontSize, $fontX,$fontY)  = self::SizeLeftTop($this->config[$name]['fontSize'], $this->config[$name]['left'],$this->config[$name]['top']);
             $text = $text . $this->dealer['mobile'];
-            
+
             if (!empty($this->dealer['mobile_phone'])) {
                 $text = $text . ' / ' . $this->dealer['mobile_phone'];
             }
             $colorResource=self::colorResource($this->config[$name]['color'],$editor);
-            return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text);
+            // 智能计算最大宽度
+            $maxWidth = $this->calculateFieldMaxWidth($this->config[$name]['left'], $this->config[$name]['top'], $width);
+            // 写入支持换行的文本
+            $this->writeTextWithWrap($editor, $text, $fontX, $fontY, $fontSize, $colorResource, $fontPath, $maxWidth);
         } else if ($name == 'duties') {
-            list($fontSize, $fontX,$fontY)  = self::SizeLeftTop($this->config[$name][$key]['fontSize'], $this->config[$name][$key]['left'],$this->config[$name][$key]['top']);
+            // 确保配置数组存在该索引,不存在则使用索引0
+            $configKey = isset($this->config[$name][$key]) ? $key : 0;
+            list($fontSize, $fontX,$fontY)  = self::SizeLeftTop($this->config[$name][$configKey]['fontSize'], $this->config[$name][$configKey]['left'],$this->config[$name][$configKey]['top']);
             $duties = $this->dealer['duties'][$key];
-            if (!empty($this->dealer['duties'][1])) {
-                $duties = $duties . ' / ' . $this->dealer['duties'][1];
-            }
-            if (!empty($this->dealer['duties'][2])) {
-                $duties = $duties . ' / ' . $this->dealer['duties'][2];
-            }
             $colorResource=self::colorResource($this->config['duties'][0]['color'],$editor);
-            return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text . $duties);
+            // 智能计算最大宽度
+            $maxWidth = $this->calculateFieldMaxWidth($this->config[$name][$configKey]['left'], $this->config[$name][$configKey]['top'], $width);
+            // 写入支持换行的文本
+            return $this->writeTextWithWrap($editor, $text . $duties, $fontX, $fontY, $fontSize, $colorResource, $fontPath, $maxWidth);
         } else if ($name == 'address') {
             list($fontSize, $fontX,$fontY)  = self::SizeLeftTop($this->config[$name][$key]['fontSize'], $this->config[$name][$key]['left'],$this->config[$name][$key]['top']);
             $title = $this->dealer['address'][0];
@@ -324,11 +420,11 @@
             while ($width < ($titleNum * $this->config['address'][0]['fontSize'] + $the_box + $fontX)) {
                 $titleNum--;
             };
-            if ($strlen > $titleNum && $titleNum) {
+            if ($strlen > $titleNum && $titleNum>0) {
                 $strArr = self::mbStrSplit($title, $titleNum);
             }
             $colorResource=self::colorResource($this->config['address'][0]['color'],$editor);
-            if ($strlen > $titleNum && $titleNum) {
+            if ($strlen > $titleNum && $titleNum>0) {
                 $y = $fontY + 10;
                 foreach ($strArr as $k => $v) {
                     if ($k == 0) {
@@ -346,9 +442,11 @@
         } else {
             list($fontSize, $fontX,$fontY)  = self::SizeLeftTop($this->config[$name]['fontSize'], $this->config[$name]['left'],$this->config[$name]['top']);
             $colorResource=self::colorResource($this->config[$name]['color'],$editor);
-            return imagettftext($editor, $fontSize, 0, $fontX, $fontY, $colorResource, $fontPath, $text . $this->dealer[$name]); // path/to/font.ttf为自定义字体路径
+            // 智能计算最大宽度
+            $maxWidth = $this->calculateFieldMaxWidth($this->config[$name]['left'], $this->config[$name]['top'], $width);
+            // 写入支持换行的文本
+            $this->writeTextWithWrap($editor, $text . $this->dealer[$name], $fontX, $fontY, $fontSize, $colorResource, $fontPath, $maxWidth);
         }
-
     }
 
     /**
@@ -444,11 +542,10 @@
                 $areaCode = $parts[0]; // 区号
                 $number = end($parts); // 号码部分
                 $separator = strpos($phone, '-') !== false ? '-' : ' '; // 保持原始分隔符
-                
+
                 $numLen = strlen($number);
                 if($numLen <= 4) return $phone; // 号码太短不脱敏
-                
-                $numPrefix = substr($number, 0, 0); // 号码部分前半段不显示
+
                 $numSuffix = substr($number, -4); // 保留号码后4位
                 $stars = str_repeat('*', $numLen - 4);
                 
@@ -526,13 +623,201 @@
      * @param $fontSize
      * @param $left
      * @param $top
-     * @return array
+     * @return array [fontSizePt, left, top]
      */
     private function SizeLeftTop($fontSize,$left,$top){
-        $data[0] = $fontSize * 0.76;
-        $data[1]  = $left;
-        $data[2]  = $top + $fontSize;
+        // px到pt转换系数:GD库使用磅(pt)作为字体单位
+        // 使用标准转换: 1pt = 4/3 px, 所以 1px = 3/4 pt = 0.75pt
+        // 但为了更清晰的文字,调整为 1px = 0.8pt
+        $fontSizePt = $fontSize * 0.8;
+
+        // 调整top位置,使文字基线对齐
+        // imagettftext的y坐标是文字基线位置,不是文字顶部
+        // 微软雅黑字体的基线大约在字体高度的85%位置
+        $data[0] = $fontSizePt;  // 字体大小(pt)
+        $data[1]  = (float)$left;  // 左边距(px)
+        $data[2]  = (float)$top + ($fontSize * 0.85);  // 顶部位置(px),加字体大小的85%以校正基线
         return $data;
     }
 
+    /**
+     * 获取文本实际高度
+     * @param $fontSizePx 字体大小(px)
+     * @return float 行高(px)
+     */
+    private function getLineHeight($fontSizePx) {
+        // 行高 = 字体大小 * 1.5 (更舒适的行间距)
+        return $fontSizePx * 1.5;
+    }
+
+    /**
+     * 获取行间距
+     * @param $fontSizePx 字体大小(px)
+     * @return float 行间距(px)
+     */
+    private function getLineSpacing($fontSizePx) {
+        // 行间距 = 字体大小 * 0.5 (50%的字体大小作为间距)
+        return $fontSizePx * 0.5;
+    }
+
+    /**
+     * 智能计算字段的最大宽度
+     * 根据右边是否有其他字段来动态计算maxWidth
+     * @param $left
+     * @param $top
+     * @param $backdropWidth
+     * @return float
+     */
+    private function calculateFieldMaxWidth($left, $top, $backdropWidth = 375)
+    {
+        $rightFields = [];
+        $tolerance = 20; // 容差:判断是否在同一行的像素范围
+
+        // 收集所有可能的字段
+        $fields = [];
+
+        // 公司
+        if (!empty($this->config['unit'][0])) {
+            $fields[] = [
+                'name' => 'unit',
+                'left' => $this->config['unit'][0]['left'],
+                'top' => $this->config['unit'][0]['top']
+            ];
+        }
+
+        // 职位
+        if (!empty($this->config['duties'][0])) {
+            $fields[] = [
+                'name' => 'duties',
+                'left' => $this->config['duties'][0]['left'],
+                'top' => $this->config['duties'][0]['top']
+            ];
+        }
+
+        // 姓名
+        if (!empty($this->config['name'])) {
+            $fields[] = [
+                'name' => 'name',
+                'left' => $this->config['name']['left'],
+                'top' => $this->config['name']['top']
+            ];
+        }
+
+        // 地址
+        if (!empty($this->config['address'][0])) {
+            $fields[] = [
+                'name' => 'address',
+                'left' => $this->config['address'][0]['left'],
+                'top' => $this->config['address'][0]['top']
+            ];
+        }
+
+        // 邮箱
+        if (!empty($this->config['mailbox'])) {
+            $fields[] = [
+                'name' => 'mailbox',
+                'left' => $this->config['mailbox']['left'],
+                'top' => $this->config['mailbox']['top']
+            ];
+        }
+
+        // 手机
+        if (!empty($this->config['mobile'])) {
+            $fields[] = [
+                'name' => 'mobile',
+                'left' => $this->config['mobile']['left'],
+                'top' => $this->config['mobile']['top']
+            ];
+        }
+
+        // 微信
+        if (!empty($this->config['wechat'])) {
+            $fields[] = [
+                'name' => 'wechat',
+                'left' => $this->config['wechat']['left'],
+                'top' => $this->config['wechat']['top']
+            ];
+        }
+
+        // 电话
+        if (!empty($this->config['phone'])) {
+            $fields[] = [
+                'name' => 'phone',
+                'left' => $this->config['phone']['left'],
+                'top' => $this->config['phone']['top']
+            ];
+        }
+
+        // 找出右边最近的字段
+        // 判断标准: left > 当前left, 且top在合理范围内(±20px)
+        foreach ($fields as $field) {
+            if ($field['left'] > $left && abs($field['top'] - $top) <= $tolerance) {
+                $rightFields[] = $field;
+            }
+        }
+
+        // 如果右边有字段,取最近的一个的左边距
+        if (count($rightFields) > 0) {
+            // 按left排序
+            usort($rightFields, function($a, $b) {
+                return $a['left'] - $b['left'];
+            });
+            return max($rightFields[0]['left'] - $left - 10, 50);
+        }
+
+        // 如果右边没有字段,使用名片边缘
+        return max($backdropWidth - $left - 10, 100);
+    }
+
+    /**
+     * 写入支持换行的文本
+     * @param $editor
+     * @param $text
+     * @param $x
+     * @param $y
+     * @param $fontSize
+     * @param $color
+     * @param $fontPath
+     * @param $maxWidth
+     * @return int 返回实际使用的行数
+     */
+    private function writeTextWithWrap($editor, $text, $x, $y, $fontSize, $color, $fontPath, $maxWidth)
+    {
+        $fontSizePt = $fontSize * 0.75; // px转pt
+        $lineHeight = $this->getLineHeight($fontSize); // 获取精确的行高
+        $lines = [];
+        $currentLine = '';
+
+        // 按字符分割文本
+        $chars = preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY);
+
+        foreach ($chars as $char) {
+            $testLine = $currentLine . $char;
+            $bbox = imagettfbbox($fontSizePt, 0, $fontPath, $testLine);
+            $lineWidth = $bbox[2] - $bbox[0];
+
+            // 使用maxWidth减去一点边距,确保不超出边界
+            if ($lineWidth > $maxWidth - 2 && $currentLine !== '') {
+                $lines[] = $currentLine;
+                $currentLine = $char;
+            } else {
+                $currentLine = $testLine;
+            }
+        }
+
+        if ($currentLine !== '') {
+            $lines[] = $currentLine;
+        }
+
+        // 写入每一行
+        foreach ($lines as $index => $line) {
+            // 第一行使用原始y坐标,后续行加上行高偏移
+            $lineY = $y + ($index * $lineHeight);
+            imagettftext($editor, $fontSizePt, 0, $x, $lineY, $color, $fontPath, $line);
+        }
+
+        // 返回实际占用的行数
+        return count($lines);
+    }
+
 }
\ No newline at end of file

--
Gitblit v1.9.2