QrcodeController.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. <?php
  2. namespace app\frontend\modules\member\controllers;
  3. use app\common\components\ApiController;
  4. use app\common\facades\Setting;
  5. use app\common\helpers\ImageHelper;
  6. use app\common\services\MiniFileLimitService;
  7. use app\common\services\Utils;
  8. use app\frontend\models\Member;
  9. use app\frontend\modules\member\models\MemberModel;
  10. use Yunshop\NewPoster\services\CreateCode;
  11. use Yunshop\Poster\models\Poster;
  12. use Yunshop\Poster\models\PosterRecord;
  13. use GuzzleHttp\Client;
  14. class QrcodeController extends ApiController
  15. {
  16. protected $type;
  17. protected $host;
  18. protected $uid;
  19. protected $posterModel;
  20. protected $memberModel;
  21. protected $poster_id;
  22. const WE_CHAT_SHOW_QR_CODE_URL = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=';
  23. /**
  24. * 会员中心推广二维码(包含会员是否有生成海报权限)
  25. * @return \Illuminate\Http\JsonResponse
  26. * @throws \app\common\exceptions\AppException
  27. */
  28. public function getPoster()
  29. {
  30. $this->type = intval(request()->type) == 2?2:1;
  31. $this->host = $host = request()->getSchemeAndHttpHost();
  32. $this->uid = \YunShop::app()->getMemberId();
  33. $is_agent = Member::current()->yzMember->is_agent;
  34. $this->memberModel = $memberModel = Member::uniacid()
  35. ->select('uid', 'avatar', 'nickname')
  36. ->with('yzMember')
  37. ->ofUid($this->uid)
  38. ->first();
  39. //新海报
  40. if (\YunShop::plugin()->get('new-poster')) {
  41. $this->type = intval(request()->type);
  42. $this->poster_id = intval(request()->poster_id);
  43. if ($this->poster_id) {
  44. $this->posterModel = $posterModel = \Yunshop\NewPoster\models\Poster::uniacid()
  45. ->where('id',$this->poster_id)
  46. ->first();
  47. } else {
  48. $this->posterModel = $posterModel = \Yunshop\NewPoster\models\Poster::uniacid()
  49. ->whereRaw('FIND_IN_SET('.$this->type.',center_show)')
  50. ->first();
  51. }
  52. if (!$this->posterModel) {
  53. //默认二维码
  54. if ($this->createPoster()) {
  55. return $this->successJson('ok', [
  56. 'image_url' => $this->createPoster(),
  57. 'center_show'=> '0'
  58. ]);
  59. } else {
  60. return $this->errorJson('生成海报失败');
  61. }
  62. }
  63. $this->type = $this->posterModel->poster_type; //取海报的类型
  64. $file_name = $this->getFileName($this->posterModel, $this->type, true);
  65. $posterRecord = \Yunshop\NewPoster\models\PosterRecord::where([
  66. 'member_id' => $this->uid,
  67. 'poster_type' => $this->type,
  68. 'poster_id' => $this->posterModel->id
  69. ])->orderby('id', 'desc')->first();
  70. if ($posterRecord) {
  71. $file_res = strstr($posterRecord->url, $file_name);
  72. if ($file_res) {
  73. return $this->successJson('ok', [
  74. 'image_url' => $posterRecord->url.'?='.str_random(6),
  75. 'center_show'=> '0',
  76. ]);
  77. }
  78. }
  79. if ($posterModel->is_ago == 0) {
  80. return $this->successJson('ok', [
  81. 'image_url' => '',
  82. 'center_show'=> '2'
  83. ]);
  84. }
  85. if ($posterModel->is_open || (!$posterModel->is_open && $is_agent)) {
  86. $poster_style = json_decode($posterModel['style_data'], true);
  87. foreach ($poster_style as &$item) {
  88. $item = $this->getRealParams($item);
  89. switch ($item['type']) {
  90. case 'head' :
  91. $item['src'] = ImageHelper::fix_wechatAvatar($memberModel->avatar_image);
  92. break;
  93. case 'nickname' :
  94. $item['src'] = $memberModel->nickname;
  95. break;
  96. case 'qr' :
  97. $item['src'] = $this->qrByType($item, $this->type); //$type 海报类型
  98. break;
  99. case 'img' :
  100. $item['src'] = yz_tomedia($item['src']);
  101. break;
  102. case 'invite' :
  103. $item['src'] = $this->memberModel->yzMember->invite_code;
  104. break;
  105. case 'mid' :
  106. $item['src'] = $this->memberModel->uid;
  107. break;
  108. case 'shopqr' :
  109. $item['src'] = $this->qrByType($item, 5);
  110. break;
  111. }
  112. }
  113. $posterModel['style_data'] = $poster_style;
  114. $posterModel['background'] = yz_tomedia($posterModel['background']);
  115. $posterModel['new'] = true;
  116. $posterModel['center_show'] = 1;
  117. return $this->successJson('ok', $posterModel);
  118. }
  119. }
  120. //旧海报
  121. if (\YunShop::plugin()->get('poster') && \Schema::hasColumn('yz_poster', 'center_show')) {
  122. $posterModel = Poster::uniacid()->where('center_show', 1)->first();
  123. if (!$posterModel) {
  124. //默认二维码
  125. if ($this->createPoster()) {
  126. return $this->successJson('ok', [
  127. 'image_url' => $this->createPoster(),
  128. 'center_show'=> '0'
  129. ]);
  130. } else {
  131. return $this->errorJson('生成海报失败');
  132. }
  133. }
  134. $file_name = $this->getFileName($posterModel, $this->type, false);
  135. $posterRecord = PosterRecord::where([
  136. 'member_id'=>$this->uid,
  137. 'poster_id'=>$posterModel->id,
  138. ])->orderby('id', 'desc')->first();
  139. if ($posterRecord) {
  140. $file_res = strstr($posterRecord->url, $file_name);
  141. if ($file_res) {
  142. return $this->successJson('ok', [
  143. 'image_url' => $posterRecord->url.'?='.str_random(6),
  144. 'center_show'=> '0',
  145. ]);
  146. }
  147. }
  148. //判断是否由后台生成海报 0后台生成海报 1前端生成
  149. if ($posterModel->is_ago == 0) {
  150. return $this->successJson('ok', [
  151. 'image_url' => '',
  152. 'center_show'=> '2'
  153. ]);
  154. }
  155. if ($posterModel->is_open || (!$posterModel->is_open && $is_agent)) {
  156. $poster_info = $posterModel->toArray();
  157. $params = json_decode($poster_info['style_data'], true);
  158. foreach ($params as $key => $item) {
  159. $item = $this->getRealParams($item);
  160. switch ($item['type']) {
  161. case 'head':
  162. $item['src'] = ImageHelper::fix_wechatAvatar($memberModel->avatar_image);
  163. break;
  164. case 'qr_shop':
  165. $item['src'] = $host . yzAppUrl('home', ['mid' => $this->memberModel->uid]);
  166. break;
  167. case 'qr_app_share':
  168. $item['src'] = $host . yzAppUrl('member/scaneditmobile', ['mid' => $this->memberModel->uid , 'app_type' => 7]);
  169. break;
  170. case 'nickname':
  171. $item['src'] = $memberModel->nickname;
  172. break;
  173. case 'img':
  174. $item['src'] = yz_tomedia($item['src']);
  175. break;
  176. }
  177. $params[$key] = $item;
  178. }
  179. $poster_info['style_data'] = $params;
  180. $poster_info['new'] = false;
  181. if ($poster_info) {
  182. return $this->successJson('ok', $poster_info);
  183. }
  184. }
  185. }
  186. //默认二维码
  187. if ($this->createPoster()) {
  188. return $this->successJson('ok', ['image_url' => $this->createPoster(),'center_show'=> '0']);
  189. } else {
  190. return $this->errorJson('生成二维码失败');
  191. }
  192. }
  193. /**
  194. * 海报记录接口
  195. */
  196. public function posterRecord()
  197. {
  198. $image_url = request()->image;
  199. $poster_id = request()->poster_id;
  200. $is_new = request()->is_new;
  201. $uid = \YunShop::app()->getMemberId();
  202. if (!$image_url || !$poster_id) {
  203. return $this->errorJson('缺少参数');
  204. }
  205. if (app('plugins')->isEnabled('new-poster') && $is_new) {
  206. $poster = \Yunshop\NewPoster\models\Poster::find($poster_id);
  207. $poster_record = \Yunshop\NewPoster\models\PosterRecord::where([
  208. 'poster_type' => $poster->poster_type,
  209. 'url' => $image_url,
  210. ])->orderBy('id', 'desc')->first();
  211. if ($poster_record) {
  212. $poster_record->url = $image_url;
  213. $poster_record->save();
  214. } else {
  215. $data = [
  216. 'url' => $image_url,
  217. 'poster_id' => $poster_id,
  218. 'member_id' => $uid,
  219. 'poster_type' => $poster->poster_type,
  220. 'created_at' => time(),
  221. ];
  222. \Yunshop\NewPoster\models\PosterRecord::create($data);
  223. }
  224. }
  225. if (app('plugins')->isEnabled('poster') && !$is_new) {
  226. $poster_record = PosterRecord::where(['url'=>$image_url])->orderBy('id', 'desc')->first();
  227. if ($poster_record) {
  228. $poster_record->url = $image_url;
  229. $poster_record->save();
  230. } else {
  231. $data = [
  232. 'url' => $image_url,
  233. 'poster_id' => $poster_id,
  234. 'member_id' => $uid,
  235. 'created_at' => time(),
  236. ];
  237. PosterRecord::create($data);
  238. }
  239. }
  240. return $this->successJson('成功');
  241. }
  242. /**
  243. * 生成默认海报
  244. * @return string
  245. */
  246. private function createPoster()
  247. {
  248. $width = 320;
  249. $height = 540;
  250. $logo_width = 40;
  251. $logo_height = 40;
  252. $font_size = 15;
  253. $font_size_show = 20;
  254. $member_id = \YunShop::app()->getMemberId();
  255. $shopInfo = Setting::get('shop.shop');
  256. $shopName = $shopInfo['name'] ?: '商城'; //todo 默认值需要更新
  257. $shopLogo = $shopInfo['logo'] ? replace_yunshop(yz_tomedia($shopInfo['logo'])) : base_path() . '/static/images/logo.png'; //todo 默认值需要更新
  258. $shopImg = $shopInfo['signimg'] ? replace_yunshop(yz_tomedia($shopInfo['signimg'])) : base_path() . '/static/images/photo-mr.jpg'; //todo 默认值需要更新
  259. $str_lenght = $logo_width + $font_size_show * mb_strlen($shopName);
  260. $space = ($width - $str_lenght) / 2;
  261. $uniacid = \YunShop::app()->uniacid;
  262. $path = storage_path('app/public/personalposter/' . $uniacid);
  263. Utils::mkdirs($path);
  264. $md5 = md5($member_id . $shopInfo['name'] . $shopInfo['logo'] . $shopInfo['signimg'] . $this->type . '2'); //用于标识组成元素是否有变化
  265. $extend = '.png';
  266. $file = $md5 . $extend;
  267. if (!file_exists($path . '/' . $file)) {
  268. $targetImg = imagecreatetruecolor($width, $height);
  269. $white = imagecolorallocate($targetImg, 255, 255, 255);
  270. imagefill($targetImg, 0, 0, $white);
  271. $imgSource = imagecreatefromstring(\Curl::to($shopImg)->get());
  272. $logoSource = imagecreatefromstring(\Curl::to($shopLogo)->get());
  273. if (2 == $this->type and request()->input('ingress') == 'weChatApplet') {
  274. $qrcode = MemberModel::getWxacode();
  275. $qrSource = imagecreatefromstring(\Curl::to($qrcode)->get());
  276. } else {
  277. $qrcode = MemberModel::getAgentQR();
  278. $qrSource = imagecreatefromstring(\Curl::to($qrcode)->get());
  279. }
  280. $fingerPrintImg = imagecreatefromstring(file_get_contents($this->getImgUrl('ewm.png')));
  281. $mergeData = [
  282. 'dst_left' => $space,
  283. 'dst_top' => 10,
  284. 'dst_width' => $logo_width,
  285. 'dst_height' => $logo_height,
  286. ];
  287. self::mergeImage($targetImg, $logoSource, $mergeData); //合并商城logo图片
  288. $mergeData = [
  289. 'size' => $font_size,
  290. 'left' => $space + $logo_width + 10,
  291. 'top' => 37,
  292. ];
  293. self::mergeText($targetImg, $shopName, $mergeData);//合并商城名称(文字)
  294. $mergeData = [
  295. 'dst_left' => 0,
  296. 'dst_top' => 60,
  297. 'dst_width' => 320,
  298. 'dst_height' => 320,
  299. ];
  300. self::mergeImage($targetImg, $imgSource, $mergeData); //合并商城海报图片
  301. $mergeData = [
  302. 'dst_left' => 0,
  303. 'dst_top' => 380,
  304. 'dst_width' => 160,
  305. 'dst_height' => 160,
  306. ];
  307. self::mergeImage($targetImg, $fingerPrintImg, $mergeData); //合并指纹图片
  308. if ($this->type == 2) {
  309. $mergeData = [
  310. 'dst_left' => 180,
  311. 'dst_top' => 390,
  312. 'dst_width' => 120,
  313. 'dst_height' => 120,
  314. ];
  315. } else {
  316. $mergeData = [
  317. 'dst_left' => 160,
  318. 'dst_top' => 380,
  319. 'dst_width' => 160,
  320. 'dst_height' => 160,
  321. ];
  322. }
  323. self::mergeImage($targetImg, $qrSource, $mergeData); //合并二维码图片
  324. header("Content-Type: image/png");
  325. $imgPath = $path . "/" . $file;
  326. imagepng($targetImg, $imgPath);
  327. }
  328. $file = $path . '/' . $file;
  329. $imgUrl = ImageHelper::getImageUrl($file).'?='.str_random(6);
  330. return $imgUrl;
  331. }
  332. //合并图片并指定图片大小
  333. private static function mergeImage($destinationImg, $sourceImg, $data)
  334. {
  335. $w = imagesx($sourceImg);
  336. $h = imagesy($sourceImg);
  337. imagecopyresampled($destinationImg,$sourceImg,$data['dst_left'],$data['dst_top'],0,0,$data['dst_width'],$data['dst_height'],$w,$h);
  338. imagedestroy($sourceImg);
  339. return $destinationImg;
  340. }
  341. //合并字符串
  342. private static function mergeText($destinationImg, $text, $data)
  343. {
  344. putenv('GDFONTPATH=' . base_path('static/fonts'));
  345. $font = "source_han_sans";
  346. $black = imagecolorallocate($destinationImg, 0, 0, 0);
  347. imagettftext($destinationImg, $data['size'], 0, $data['left'], $data['top'], $black, $font, $text);
  348. return $destinationImg;
  349. }
  350. //获取图片url
  351. private function getImgUrl($file)
  352. {
  353. if (config('app.framework') == 'platform') {
  354. return request()->getSchemeAndHttpHost().'/addons/yun_shop/static/app/images/'.$file;
  355. } else {
  356. return base_path() . '/static/app/images/'.$file;
  357. }
  358. }
  359. //处理坐标
  360. private function getRealParams($params)
  361. {
  362. $params['left'] = intval(str_replace('px', '', $params['left'])) * 2;
  363. $params['top'] = intval(str_replace('px', '', $params['top'])) * 2;
  364. $params['width'] = intval(str_replace('px', '', $params['width'])) * 2;
  365. $params['height'] = intval(str_replace('px', '', $params['height'])) * 2;
  366. $params['size'] = intval(str_replace('px', '', $params['size'])) * 2;
  367. $params['src'] = yz_tomedia($params['src']);
  368. return $params;
  369. }
  370. //根据type获取各类二维码
  371. protected function qrByType($item, $type)
  372. {
  373. switch ($type) {
  374. case 1 :
  375. $url = $this->getQrCodeUrl();
  376. break;
  377. case 2 :
  378. $url = $this->getMiniCode($item['mini_link']);
  379. break;
  380. case 5 :
  381. $url = $this->getQrShopImage($item['h5_link']);
  382. break;
  383. case 7 :
  384. $url = $this->getAppShareImage();
  385. break;
  386. default :
  387. $url = '';
  388. break;
  389. }
  390. return $url;
  391. }
  392. //关注二维码
  393. protected function getQrCodeUrl()
  394. {
  395. $client = new Client;
  396. $res = $client->request('GET', (new CreateCode($this->memberModel, $this->posterModel, $this->host))->getQrCodeUrl());
  397. $extend = 'png';
  398. $filename = 'fans_' . \YunShop::app()->uniacid . '_' . \YunShop::app()->getMemberId() . '.' . $extend;
  399. $paths = \Storage::url('app/public/qr/');
  400. $paths_change = ltrim($paths, '/');
  401. file_put_contents(base_path($paths_change) . $filename, $res->getBody());
  402. return $this->host . config('app.webPath') . $paths . $filename;
  403. }
  404. //小程序二维码
  405. protected function getMiniCode($link)
  406. {
  407. $res = (new CreateCode($this->memberModel, $this->posterModel, $this->host))->getMiniCode($link);
  408. if ($res == '') {
  409. return '';
  410. }
  411. $extend = 'png';
  412. $filename = 'mini_' . \YunShop::app()->uniacid . '_' . \YunShop::app()->getMemberId() . '.' . $extend;
  413. $paths = \Storage::url('app/public/qr/');
  414. $paths_change = ltrim($paths, '/');
  415. file_put_contents(base_path($paths_change) . $filename, $res);
  416. return $this->host . config('app.webPath') . $paths . $filename.'?v='.str_random(6);
  417. }
  418. //商城二维码
  419. protected function getQrShopImage($link)
  420. {
  421. $res = (new CreateCode($this->memberModel, $this->posterModel, $this->host))->getQrShopImage($link);
  422. $extend = 'png';
  423. $filename = 'shop_' . \YunShop::app()->uniacid . '_' . \YunShop::app()->getMemberId() . '.' . $extend;
  424. $paths = \Storage::url('app/public/qr/');
  425. $paths_change = ltrim($paths, '/');
  426. file_put_contents(base_path($paths_change) . $filename, $res);
  427. return $this->host . config('app.webPath') . $paths . $filename;
  428. }
  429. //app二维码
  430. protected function getAppShareImage()
  431. {
  432. $res = (new CreateCode($this->memberModel, $this->posterModel, $this->host))->getAppShareImage();
  433. $extend = 'png';
  434. $filename = 'share_' . \YunShop::app()->uniacid . '_' . \YunShop::app()->getMemberId() . '.' . $extend;
  435. $paths = \Storage::url('app/public/qr/');
  436. $paths_change = ltrim($paths, '/');
  437. file_put_contents(base_path($paths_change) . $filename, $res);
  438. return $this->host . config('app.webPath') . $paths . $filename;
  439. }
  440. /**
  441. * 海报上传本地
  442. * @return \Illuminate\Http\JsonResponse
  443. */
  444. public function uploadLocal()
  445. {
  446. $file = request()->file('file');
  447. $ingress = request()->ingress;
  448. $poster_id = request()->poster_id;
  449. if (!$file) {
  450. return $this->errorJson('请传入正确参数.');
  451. }
  452. if (!$file->isValid()) {
  453. return $this->errorJson('上传失败.');
  454. }
  455. if ($ingress) {
  456. if ($file->getClientSize() > 1024*1024) {
  457. return $this->errorJson('小程序图片安全验证图片不能大于1M');
  458. }
  459. $check_result = (new MiniFileLimitService())->checkImg($file);
  460. if ($check_result['errcode'] == 87014) {
  461. return $this->errorJson('内容含有违法违规信息');
  462. }
  463. }
  464. $realPath = $file->getRealPath(); //临时文件的绝对路径
  465. $is_new = request()->is_new;
  466. if ($is_new) {
  467. $posterModel = \Yunshop\NewPoster\models\Poster::find($poster_id);
  468. $type = $posterModel->poster_type;
  469. } else {
  470. $posterModel = Poster::find($poster_id);
  471. $type = request()->type == 2 ? 2 : 1;
  472. }
  473. if (!$posterModel) {
  474. return $this->errorJson('海报已删除');
  475. }
  476. $file_name = $this->getFileName($posterModel, $type, $is_new);
  477. $path = storage_path('app/public/poster/' . \YunShop::app()->uniacid);
  478. if (!file_exists($path)) {
  479. Utils::mkdirs($path);
  480. }
  481. $full_path = $path . DIRECTORY_SEPARATOR . $file_name;
  482. file_put_contents($full_path, file_get_contents($realPath));
  483. return $this->successJson('ok', [
  484. 'img_url' => $this->getPosterUrl($file_name),
  485. ]);
  486. }
  487. private function getFileName($posterModel, $type, $is_new_poster)
  488. {
  489. $file = md5(json_encode([
  490. 'memberId' => \YunShop::app()->getMemberId(),
  491. 'posterId' => $posterModel->id,
  492. 'uniacid' => \YunShop::app()->uniacid,
  493. 'background' => $posterModel->background,
  494. 'style_data' => $posterModel->style_data,
  495. ]));
  496. if ($is_new_poster) {
  497. return $file.'_new_'.$type.'.png';
  498. } else {
  499. return $file.'_'.$type.'.png';
  500. }
  501. }
  502. private function getPosterUrl($file_name)
  503. {
  504. if (config('app.framework') == 'platform') {
  505. return request()->getSchemeAndHttpHost().DIRECTORY_SEPARATOR.'storage/app/public/poster/'.\YunShop::app()->uniacid.DIRECTORY_SEPARATOR.$file_name;
  506. } else {
  507. return request()->getSchemeAndHttpHost().DIRECTORY_SEPARATOR.'addons/yun_shop/storage/app/public/poster/'.\YunShop::app()->uniacid.DIRECTORY_SEPARATOR.$file_name;
  508. }
  509. }
  510. }