// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\common\service\qrcode; use Exception; use Grafika\Color; use Grafika\Grafika; use cores\exception\BaseException; use app\common\model\dealer\User as DealerUserModel; use app\common\model\dealer\Setting as SettingModel; /** * 分销二维码 * Class Qrcode * @package app\common\service */ class Poster extends BaseQRcode { // 分销商用户信息 private ?DealerUserModel $dealer; // 分销商海报设置 private string $channel; // 分销商海报设置 private $config; /** * 构造方法 * Poster constructor. * @param DealerUserModel $dealer * @param string $channel 二维码渠道(小程序码、h5码) * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function __construct(DealerUserModel $dealer, string $channel = 'H5') { parent::__construct(); // 分销商用户信息 $this->dealer = $dealer; // 二维码渠道 $this->channel = $channel; // 分销商海报设置 $this->config = SettingModel::getItem('poster', $this->getStoreId()); } /** * 获取分销二维码 * @return string * @throws BaseException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException * @throws Exception */ public function getImage(): string { // 如果已生成过,则直接返回url if (file_exists($this->getPosterPath())) { return $this->getPosterUrl(); } // 商城ID $storeId = (int)$this->dealer['store_id']; // 1. 下载背景图 $backdrop = $this->saveTempImage($storeId, $this->config['backdrop']['src'], 'backdrop'); // 2. 下载用户头像 $avatarUrl = $this->userAvatar(); $avatar = $this->saveTempImage($storeId, $avatarUrl, 'avatar'); // 3. 下载二维码 $scene = 'uid:' . $this->dealer['user_id']; $qrcode = $this->getQrcode($storeId, $scene, null, $this->channel); // 4. 拼接海报图 return $this->savePoster($backdrop, $avatar, $qrcode); } /** * 获取用户头像 * @return string */ private function userAvatar(): string { $avatar = $this->dealer['user']['avatar']; return !empty($avatar) ? $avatar['external_url'] : $this->config['avatar']['src']; } /** * 海报图文件路径 * @return string */ private function getPosterPath(): string { // 保存路径 $tempPath = web_path() . "temp/{$this->dealer['store_id']}/"; !is_dir($tempPath) && mkdir($tempPath, 0755, true); return $tempPath . $this->getPosterName(); } /** * 海报图文件名称 * @return string */ private function getPosterName(): string { return md5("poster_{$this->channel}_{$this->dealer['user_id']}") . '.png'; } /** * 海报图url * @return string */ private function getPosterUrl(): string { return base_url() . "temp/{$this->dealer['store_id']}/" . $this->getPosterName() . '?t=' . time(); } /** * 拼接海报图 * @param string $backdrop 背景图路径 * @param string $avatar 头像路径 * @param string $qrcode 二维码路径 * @return string * @throws Exception */ private function savePoster(string $backdrop, string $avatar, string $qrcode): string { // 实例化图像编辑器 $editor = Grafika::createEditor(['Gd']); // 打开海报背景图 $editor->open($backdropImage, $backdrop); // 生成圆形用户头像 $this->config['avatar']['style'] === 'circle' && $this->circular($avatar, $avatar); // 打开用户头像 $editor->open($avatarImage, $avatar); // 重设用户头像宽高 $avatarWidth = $this->config['avatar']['width'] * 2; $editor->resizeExact($avatarImage, $avatarWidth, $avatarWidth); // 用户头像添加到背景图 $avatarX = $this->config['avatar']['left'] * 2; $avatarY = $this->config['avatar']['top'] * 2; $editor->blend($backdropImage, $avatarImage, 'normal', 1.0, 'top-left', $avatarX, $avatarY); // 生成圆形二维码 // $this->config['qrcode']['style'] === 'circle' && $this->circular($qrcode, $qrcode); // 打开二维码 $editor->open($qrcodeImage, $qrcode); // 重设二维码宽高 $qrcodeWidth = $this->config['qrcode']['width'] * 2; $editor->resizeExact($qrcodeImage, $qrcodeWidth, $qrcodeWidth); // 二维码添加到背景图 $qrcodeX = $this->config['qrcode']['left'] * 2; $qrcodeY = $this->config['qrcode']['top'] * 2; $editor->blend($backdropImage, $qrcodeImage, 'normal', 1.0, 'top-left', $qrcodeX, $qrcodeY); // 写入用户昵称 $fontSize = $this->config['nickName']['fontSize'] * 2 * 0.76; $fontX = $this->config['nickName']['left'] * 2; $fontY = $this->config['nickName']['top'] * 2; $Color = new Color($this->config['nickName']['color']); $fontPath = Grafika::fontsDir() . '/st-heiti-light.ttc'; $editor->text($backdropImage, $this->dealer['user']['nick_name'], $fontSize, $fontX, $fontY, $Color, $fontPath); // 保存图片 $editor->save($backdropImage, $this->getPosterPath()); return $this->getPosterUrl(); } /** * 生成圆形图片 * @param string $imgpath 图片地址 * @param string $saveName 保存文件名,默认空。 */ private function circular(string $imgpath, string $saveName = '') { $srcImg = imagecreatefromstring(file_get_contents($imgpath)); $w = imagesx($srcImg); $h = imagesy($srcImg); $w = $h = min($w, $h); $newImg = imagecreatetruecolor($w, $h); // 这一句一定要有 imagesavealpha($newImg, true); // 拾取一个完全透明的颜色,最后一个参数127为全透明 $bg = imagecolorallocatealpha($newImg, 255, 255, 255, 127); imagefill($newImg, 0, 0, $bg); $r = $w / 2; //圆半径 for ($x = 0; $x < $w; $x++) { for ($y = 0; $y < $h; $y++) { $rgbColor = imagecolorat($srcImg, $x, $y); if (((($x - $r) * ($x - $r) + ($y - $r) * ($y - $r)) < ($r * $r))) { imagesetpixel($newImg, $x, $y, $rgbColor); } } } // 输出图片到文件 imagepng($newImg, $saveName); // 释放空间 imagedestroy($srcImg); imagedestroy($newImg); } }