You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
6.5 KiB
188 lines
6.5 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
|
// +----------------------------------------------------------------------
|
|
// | Author: 萤火科技 <admin@yiovo.com>
|
|
// +----------------------------------------------------------------------
|
|
declare (strict_types=1);
|
|
|
|
namespace app\common\service\qrcode;
|
|
|
|
use Exception;
|
|
use Grafika\Color;
|
|
use Grafika\Grafika;
|
|
use app\common\model\Goods as GoodsModel;
|
|
|
|
/**
|
|
* 商品海报
|
|
* Class Goods
|
|
* @package app\common\service\qrcode
|
|
*/
|
|
class Goods extends BaseQRcode
|
|
{
|
|
// 当前客户端 (默认H5)
|
|
private string $channel;
|
|
|
|
// 商品信息
|
|
private GoodsModel $goods;
|
|
|
|
// 用户ID
|
|
private ?int $userId;
|
|
|
|
/**
|
|
* 构造方法
|
|
* Goods constructor.
|
|
* @param $goods
|
|
* @param int|null $userId
|
|
* @param string $channel 二维码渠道(小程序码、h5码)
|
|
*/
|
|
public function __construct(GoodsModel $goods, ?int $userId = null, string $channel = 'H5')
|
|
{
|
|
parent::__construct();
|
|
// 商品信息
|
|
$this->goods = $goods;
|
|
// 当前用户id
|
|
$this->userId = $userId;
|
|
// 当前客户端
|
|
$this->channel = $channel;
|
|
}
|
|
|
|
/**
|
|
* 拼接海报图
|
|
* @return string
|
|
* @throws \cores\exception\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 = $this->goods['store_id'];
|
|
// 商品海报背景图
|
|
$backdrop = __DIR__ . '/resource/goods_bg.png';
|
|
// 下载商品首图
|
|
$goodsUrl = $this->saveTempImage($storeId, $this->goods['goods_image'], 'goods');
|
|
// 小程序码参数
|
|
$scene = "gid:{$this->goods['goods_id']},uid:" . ($this->userId ?: '');
|
|
// 下载小程序码
|
|
$qrcode = $this->getQrcode($storeId, $scene, 'pages/goods/detail', $this->channel);
|
|
// 拼接海报图
|
|
return $this->savePoster($backdrop, $goodsUrl, $qrcode);
|
|
}
|
|
|
|
/**
|
|
* 拼接海报图
|
|
* @param string $backdrop 背景图路径
|
|
* @param string $goodsUrl 商品图路径
|
|
* @param string $qrcode 二维码图路径
|
|
* @return string
|
|
* @throws \Exception
|
|
*/
|
|
private function savePoster(string $backdrop, string $goodsUrl, string $qrcode): string
|
|
{
|
|
// 实例化图像编辑器
|
|
$editor = Grafika::createEditor(['Gd']);
|
|
// 字体文件路径
|
|
$fontPath = Grafika::fontsDir() . '/' . 'st-heiti-light.ttc';
|
|
// 打开海报背景图
|
|
$editor->open($backdropImage, $backdrop);
|
|
// 打开商品图片
|
|
$editor->open($goodsImage, $goodsUrl);
|
|
// 重设商品图片宽高
|
|
$editor->resizeExact($goodsImage, 690, 690);
|
|
// 商品图片添加到背景图
|
|
$editor->blend($backdropImage, $goodsImage, 'normal', 1.0, 'top-left', 30, 30);
|
|
// 商品名称处理换行
|
|
$fontSize = 30;
|
|
$goodsName = $this->wrapText($fontSize, 0, $fontPath, $this->goods['goods_name'], 680, 2);
|
|
// 写入商品名称
|
|
$editor->text($backdropImage, $goodsName, $fontSize, 30, 750, new Color('#333333'), $fontPath);
|
|
// 写入商品价格
|
|
$editor->text($backdropImage, $this->goods['goods_price_min'], 38, 62, 964, new Color('#ff4444'));
|
|
// 打开小程序码
|
|
$editor->open($qrcodeImage, $qrcode);
|
|
// 重设小程序码宽高
|
|
$editor->resizeExact($qrcodeImage, 140, 140);
|
|
// 小程序码添加到背景图
|
|
$editor->blend($backdropImage, $qrcodeImage, 'normal', 1.0, 'top-left', 570, 914);
|
|
// 保存图片
|
|
$editor->save($backdropImage, $this->getPosterPath());
|
|
return $this->getPosterUrl();
|
|
}
|
|
|
|
/**
|
|
* 处理文字超出长度自动换行
|
|
* @param integer $fontsize 字体大小
|
|
* @param integer $angle 角度
|
|
* @param string $fontface 字体名称
|
|
* @param string $string 字符串
|
|
* @param integer $width 预设宽度
|
|
* @param int|null $maxLine 最多行数
|
|
* @return string
|
|
*/
|
|
private function wrapText(int $fontsize, int $angle, string $fontface, string $string, int $width, int $maxLine = null): string
|
|
{
|
|
// 这几个变量分别是 字体大小, 角度, 字体名称, 字符串, 预设宽度
|
|
$content = "";
|
|
// 将字符串拆分成一个个单字 保存到数组 letter 中
|
|
$letter = [];
|
|
for ($i = 0; $i < mb_strlen($string, 'UTF-8'); $i++) {
|
|
$letter[] = mb_substr($string, $i, 1, 'UTF-8');
|
|
}
|
|
$line_count = 0;
|
|
foreach ($letter as $l) {
|
|
$testbox = imagettfbbox($fontsize, $angle, $fontface, $content . ' ' . $l);
|
|
// 判断拼接后的字符串是否超过预设的宽度
|
|
if (($testbox[2] > $width) && ($content !== "")) {
|
|
$line_count++;
|
|
if ($maxLine && $line_count >= $maxLine) {
|
|
$content = mb_substr($content, 0, -1, 'UTF-8') . "...";
|
|
break;
|
|
}
|
|
$content .= "\n";
|
|
}
|
|
$content .= $l;
|
|
}
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* 海报图文件路径
|
|
* @return string
|
|
*/
|
|
private function getPosterPath(): string
|
|
{
|
|
// 保存路径
|
|
$tempPath = web_path() . "temp/{$this->goods['store_id']}/";
|
|
!is_dir($tempPath) && mkdir($tempPath, 0755, true);
|
|
return $tempPath . $this->getPosterName();
|
|
}
|
|
|
|
/**
|
|
* 海报图文件名称
|
|
* @return string
|
|
*/
|
|
private function getPosterName(): string
|
|
{
|
|
return 'goods_' . md5("{$this->userId}_{$this->goods['goods_id']}_{$this->channel}") . '.png';
|
|
}
|
|
|
|
/**
|
|
* 海报图url
|
|
* @return string
|
|
*/
|
|
private function getPosterUrl(): string
|
|
{
|
|
return \base_url() . 'temp/' . $this->goods['store_id'] . '/' . $this->getPosterName() . '?t=' . time();
|
|
}
|
|
} |