PosterLogic.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | LikeShop100%开源免费商用电商系统
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | 商业版本务必购买商业授权,以免引起法律纠纷
  8. // | 禁止对系统程序代码以任何目的,任何形式的再发布
  9. // | Gitee下载:https://gitee.com/likeshop_gitee/likeshop
  10. // | 访问官网:https://www.likemarket.net
  11. // | 访问社区:https://home.likemarket.net
  12. // | 访问手册:http://doc.likemarket.net
  13. // | 微信公众号:好象科技
  14. // | 好象科技开发团队 版权所有 拥有最终解释权
  15. // +----------------------------------------------------------------------
  16. // | Author: LikeShopTeam
  17. // +----------------------------------------------------------------------
  18. namespace app\common\logic;
  19. use app\common\service\ConfigService;
  20. use app\common\service\FileService;
  21. use app\common\service\storage\Driver;
  22. use app\common\service\WeChatConfigService;
  23. use EasyWeChat\Factory;
  24. use Endroid\QrCode\QrCode;
  25. /**
  26. * 海报逻辑层
  27. * Class PosterLogic
  28. * @package app\common\logic
  29. */
  30. class PosterLogic extends BaseLogic
  31. {
  32. /**
  33. * @notes 生成分销海报
  34. * @param $user
  35. * @param $content
  36. * @param $urlType
  37. * @param $terminal
  38. * @return string
  39. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  40. * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
  41. * @throws \think\Exception
  42. * @author Tab
  43. * @date 2021/8/6 15:09
  44. */
  45. public static function generate($user, $content, $urlType, $terminal)
  46. {
  47. // 获取分享海报背景图
  48. $bgImg = self::getBgImg();
  49. // 二维码保存路径
  50. $saveDir = 'resource/image/shopapi/qr_code/';
  51. if(!file_exists($saveDir)) {
  52. mkdir($saveDir, 0777, true);
  53. }
  54. $saveKey = 'uid'.$user['id'].$urlType.$terminal;
  55. $qrCodeName = md5($saveKey) . '.png';
  56. $qrCodeUrl = public_path() . $saveDir . $qrCodeName;
  57. // 删除旧的二维码
  58. self::delOldQrCode($qrCodeUrl, $saveDir, $qrCodeName);
  59. // 生成二维码
  60. if($urlType == 'path'){
  61. // 小程序码
  62. self::makeMnpQrcode($user['code'], $content, $saveDir, $qrCodeName);
  63. }else{
  64. // 二维码
  65. $qrCode = new QrCode();
  66. $qrCode->setText($content);
  67. $qrCode->setSize(1000);
  68. $qrCode->setWriterByName('png');
  69. $qrCode->writeFile($qrCodeUrl);
  70. }
  71. // 获取海报配置
  72. $posterConfig = self::posterConfig();
  73. // 用户头像判断
  74. $userAvatar = FileService::setFileUrl($user['avatar']);
  75. $userAvatar = FileService::getFileUrl($userAvatar, 'share');
  76. if(!check_file_exists($userAvatar)){
  77. //如果不存在,使用默认头像
  78. $userAvatar = public_path().ConfigService::get('default_image', 'user_avatar');
  79. }
  80. // 使用分享背景图创建一个图像
  81. $bgResource = imagecreatefromstring(file_get_contents($bgImg));
  82. // 合成头像
  83. self::writeImg($bgResource, $userAvatar, $posterConfig['head_pic'],true);
  84. // 合成昵称
  85. $nickname = filterEmoji($user['nickname']);
  86. self::writeText($bgResource, $nickname, $posterConfig['nickname']);
  87. // 合成提示文本
  88. $notice = '长按识别二维码 >>';
  89. self::writeText($bgResource, $notice, $posterConfig['notice']);
  90. // 合成提示文本
  91. $title = auto_adapt($posterConfig['title']['font_size'], 0, $posterConfig['title']['font_face'], '邀请你一起来赚大钱', $posterConfig['title']['w'],$posterConfig['title']['y'],getimagesize($bgImg));
  92. self::writeText($bgResource, $title, $posterConfig['title']);
  93. // 合成邀请码
  94. self::writeText($bgResource, '邀请码 '.$user['code'], $posterConfig['code_text']);
  95. // 合成二维码
  96. self::writeImg($bgResource, $qrCodeUrl, $posterConfig['qr']);
  97. imagepng($bgResource, $qrCodeUrl);
  98. $fileName = $saveDir . $qrCodeName;
  99. $localUrl = ROOT_PATH.'/'.$fileName;
  100. self::upload($localUrl, $fileName);
  101. return $fileName . '?v=' . time();
  102. }
  103. /**
  104. * @notes 获取分享海报背景
  105. * @return array|int|mixed|string
  106. * @author Tab
  107. * @date 2021/8/6 11:27
  108. */
  109. public static function getBgImg()
  110. {
  111. // 分享海报背景图
  112. $bgImg = ConfigService::get('default_image', 'distribution_share_bg');
  113. // 存储引擎
  114. $storage = ConfigService::get('storage', 'default', 'local');
  115. if ($storage == 'local') {
  116. return public_path() .$bgImg;
  117. }
  118. // 非本地存储引擎
  119. $bgImg = FileService::getFileUrl($bgImg);
  120. if (!check_file_exists($bgImg)) {
  121. return ConfigService::get('share', 'poster');
  122. }
  123. return $bgImg;
  124. }
  125. /**
  126. * @notes 删除旧的二维码
  127. * @param $qrCodeUrl
  128. * @param $saveDir
  129. * @param $qrCodeName
  130. * @return bool
  131. * @throws \think\Exception
  132. * @author Tab
  133. * @date 2021/8/6 14:07
  134. */
  135. public static function delOldQrCode($qrCodeUrl, $saveDir, $qrCodeName)
  136. {
  137. // 获取存储引擎
  138. $config = [
  139. 'default' => ConfigService::get('storage', 'default', 'local'),
  140. 'engine' => ConfigService::get('storage_engine')
  141. ];
  142. if ($config['default'] == 'local') {
  143. // 删除本地文件
  144. @unlink($qrCodeUrl);
  145. }else{
  146. // 删除非本地存储引擎上的文件
  147. $storageDriver = new Driver($config);
  148. $fileName = $saveDir . $qrCodeName;
  149. $storageDriver->delete($fileName);
  150. }
  151. }
  152. /**
  153. * @notes 海报配置
  154. * @return array
  155. * @author Tab
  156. * @date 2021/8/6 11:46
  157. */
  158. public static function posterConfig()
  159. {
  160. return [
  161. // 会员头像
  162. 'head_pic' => [
  163. 'w' => 80, 'h' => 80, 'x' => 30, 'y' => 680,
  164. ],
  165. // 会员昵称
  166. 'nickname' => [
  167. 'color' => '#333333', 'font_face' => public_path().'resource/font/SourceHanSansCN-Regular.otf', 'font_size' => 20, 'x' => 120, 'y' => 730,
  168. ],
  169. // 标题
  170. 'title' => [
  171. 'color' => '#333333', 'font_face' => public_path().'resource/font/SourceHanSansCN-Regular.otf', 'font_size' => 20, 'w' => 360, 'x' => 30, 'y' => 810,
  172. ],
  173. // 提醒、长按扫码
  174. 'notice' => [
  175. 'color' => '#333333', 'font_face' => public_path().'resource/font/SourceHanSansCN-Regular.otf', 'font_size' => 20, 'x' => 30, 'y' => 880,
  176. ],
  177. // 邀请码文本
  178. 'code_text' => [
  179. 'color' => '#FF2C3C', 'font_face' => public_path().'resource/font/SourceHanSansCN-Regular.otf', 'font_size' => 20, 'x' => 355, 'y' => 930,
  180. ],
  181. // 二维码
  182. 'qr' => [
  183. 'w' => 170,'h' => 170, 'x' => 370, 'y' => 730,
  184. ],
  185. ];
  186. }
  187. /**
  188. * @notes 获取小程序码
  189. * @param $code
  190. * @param $content
  191. * @param $saveDir
  192. * @param $qrCodeName
  193. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  194. * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
  195. * @author Tab
  196. * @date 2021/8/6 14:16
  197. */
  198. public static function makeMnpQrcode($code,$content,$saveDir,$qrCodeName)
  199. {
  200. $config = WeChatConfigService::getMnpConfig();
  201. $app = Factory::miniProgram($config);
  202. $response = $app->app_code->get($content.'?invite_code='.$code, [
  203. 'width' => 170,
  204. ]);
  205. if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
  206. // 保存小程序码
  207. $response->saveAs($saveDir, $qrCodeName);
  208. return true;
  209. }
  210. return false;
  211. }
  212. /**
  213. * @notes 写入图像
  214. * @param $bgResource
  215. * @param $img
  216. * @param $config
  217. * @param false $isRounded
  218. * @return mixed
  219. * @author Tab
  220. * @date 2021/8/6 14:40
  221. */
  222. public static function writeImg($bgResource, $img, $config, $isRounded = false){
  223. $picImg = imagecreatefromstring(file_get_contents($img));
  224. //切成圆角返回头像资源
  225. $isRounded ? $picImg = rounded_corner($picImg) : '';
  226. $picW = imagesx($picImg);
  227. $picH = imagesy($picImg);
  228. // 圆形头像大图合并到海报
  229. imagecopyresampled($bgResource, $picImg,
  230. $config['x'],
  231. $config['y'],
  232. 0, 0,
  233. $config['w'],
  234. $config['h'],
  235. $picW,
  236. $picH
  237. );
  238. return $bgResource;
  239. }
  240. /**
  241. * @notes 写入文本
  242. * @param $bgResource
  243. * @param $text
  244. * @param $config
  245. * @return mixed
  246. * @author Tab
  247. * @date 2021/8/6 14:42
  248. */
  249. public static function writeText($bgResource, $text, $config){
  250. $fontUri = $config['font_face'];
  251. $fontSize = $config['font_size'];
  252. $color = substr($config['color'],1);
  253. //颜色转换
  254. $color= str_split($color, 2);
  255. $color = array_map('hexdec', $color);
  256. if (empty($color[3]) || $color[3] > 127) {
  257. $color[3] = 0;
  258. }
  259. $fontColor = imagecolorallocatealpha($bgResource, $color[0], $color[1], $color[2], $color[3]);
  260. imagettftext($bgResource, $fontSize,0, $config['x'], $config['y'], $fontColor, $fontUri, $text);
  261. return $bgResource;
  262. }
  263. /**
  264. * @notes 根据不同的存储引擎存储海报
  265. * @param $localUrl
  266. * @param $fileName
  267. * @throws \think\Exception
  268. * @author Tab
  269. * @date 2021/8/6 15:00
  270. */
  271. public static function upload($localUrl, $fileName)
  272. {
  273. $config = [
  274. 'default' => ConfigService::get('storage', 'default', 'local'),
  275. 'engine' => ConfigService::get('storage_engine')
  276. ];
  277. if ($config['default'] != 'local') {
  278. $storageDriver = new Driver($config);
  279. if (!$storageDriver->fetch($localUrl, $fileName)) {
  280. throw new \think\Exception('图片保存出错:' . $storageDriver->getError());
  281. }
  282. //删除本地图片
  283. unlink($fileName);
  284. }
  285. }
  286. /**
  287. * @notes 获取商品海报配置
  288. */
  289. public static function getGoodsConfig($id) {
  290. $defaultStyle = ConfigService::get("poster", 'default_style', 1);
  291. $id = empty($id) ? $defaultStyle : $id;
  292. $config = ConfigService::get('poster', $id, self::defaultGoodsConfig($id));
  293. $config = self::stringToInteger($config);
  294. return $config;
  295. }
  296. /**
  297. * @notes 设置商品海报配置
  298. */
  299. public static function setGoodsConfig($params) {
  300. ConfigService::set('poster', $params['id'], $params);
  301. ConfigService::set('poster', 'default_style', $params['id']);
  302. }
  303. /**
  304. * @notes 商品海报默认配置
  305. */
  306. public static function defaultGoodsConfig($id) {
  307. return [
  308. "id" => $id,
  309. "background_type" => 1,
  310. "background_url" => '',
  311. "show" => [
  312. "user_avtar" => 1,
  313. "user_name" => 1,
  314. "goods_img" => 1,
  315. "goods_name" => 1,
  316. "goods_sale_price" => 1,
  317. "goods_origin_price" => 1,
  318. "qrcode" => 1,
  319. "qrcode_title" => 1,
  320. ],
  321. "qrcode_align" => 2,
  322. "style" => [
  323. "user_name" => '#333333',
  324. "goods_sale_price" => '#FF0610',
  325. "goods_origin_price" => '#999999',
  326. "goods_name" => '#333333',
  327. "qrcode_title" => '#999999',
  328. ]
  329. ];
  330. }
  331. /**
  332. * @notes 获取邀请海报配置
  333. */
  334. public static function getDistributionConfig() {
  335. $config = ConfigService::get('poster', 'distribution', self::defaultDistributionConfig());
  336. $config = self::stringToInteger($config);
  337. return $config;
  338. }
  339. /**
  340. * @notes 邀请海报默认配置
  341. */
  342. public static function defaultDistributionConfig() {
  343. return [
  344. "background_type" => 1,
  345. "background_url" => '',
  346. "show" => [
  347. "user_avtar" => 1,
  348. "user_name" => 1,
  349. "slogan" => 1,
  350. "qrcode" => 1,
  351. "slogan_code" => 1,
  352. ],
  353. "slogan" => '邀请你一起来赚大钱',
  354. "style" => [
  355. "user_name" => '#333333',
  356. "slogan_text" => '#FF0610',
  357. "slogan_code" => '#999999',
  358. ]
  359. ];
  360. }
  361. /**
  362. * @notes 设置邀请海报配置
  363. */
  364. public static function setDistributionConfig($params) {
  365. ConfigService::set('poster', 'distribution', $params);
  366. }
  367. /**
  368. * @notes 字符串数字 转 纯数字
  369. */
  370. public static function stringToInteger($config) {
  371. foreach($config as $key => $value) {
  372. if ($key == 'style' || $key == 'background_url' || $key == 'slogan') {
  373. continue;
  374. }
  375. if ($key == 'show') {
  376. foreach($config[$key] as $k => $v) {
  377. $config[$key][$k] = (int)$v;
  378. }
  379. continue;
  380. }
  381. $config[$key] = (int)$value;
  382. }
  383. return $config;
  384. }
  385. }