PosterController.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. <?php
  2. namespace addons\Store\api\modules\v1\controllers;
  3. use addons\Store\common\models\StoreGoods;
  4. use api\controllers\OnAuthController;
  5. use api\modules\v1\forms\user\AlipayMiniForm;
  6. use common\components\export\CsvTool;
  7. use common\enums\DiyEnum;
  8. use common\enums\RabbitMqEnum;
  9. use common\enums\RedisKeyEnum;
  10. use common\enums\StatusEnum;
  11. use common\events\census\ShareEvent;
  12. use common\globals\GlobalInvoke;
  13. use common\models\common\Attachment;
  14. use common\models\common\PosterCode;
  15. use common\models\mall\Poster;
  16. use common\models\user\User;
  17. use Grafika\Grafika;
  18. use Grafika\Color;
  19. use Yii;
  20. use yii\helpers\Json;
  21. /**
  22. * 海报控制器
  23. *
  24. *
  25. * Class PosterController
  26. * @package frontend\controllers
  27. */
  28. class PosterController extends OnAuthController
  29. {
  30. public $enableCsrfValidation = false;
  31. public $modelClass = '';
  32. /**
  33. * 不用进行登录验证的方法
  34. *
  35. * 例如: ['index', 'update', 'create', 'view', 'delete']
  36. * 默认全部需要验证
  37. *
  38. * @var array
  39. */
  40. protected $authOptional = ['poster-code-detail','get-share-content'];
  41. /**
  42. * 获取海报
  43. * @return mixed|void
  44. */
  45. public function actionIndex()
  46. {
  47. $retuest = Yii::$app->request;
  48. if ($retuest->isGet) {
  49. $params = \Yii::$app->request->get();
  50. $res = \Yii::$app->services->validate->validate($params, [
  51. [['source'], 'required'],
  52. [['goods_id','source','open_group_id','bargain_order_id','active_id','lucky_group_activity_id'], 'integer'],
  53. [['marketing_type'],'safe']
  54. ]);
  55. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  56. $mallId = $this->mall_id;
  57. $sign = $this->mall['mall_sign'];
  58. // 获取个人信息
  59. $data['user_id'] = $this->user->id ?? '';
  60. $data['nickname'] = $this->user->nickname ?? '';
  61. $data['avatar_url'] = $this->user->avatar_url ?? '';
  62. $save_content = ['pid' => $data['user_id'], 'mall_id' => $mallId, 'source' => $params['source']];// 分享码保存的基本信息
  63. // 根据来源获取不同分享内容
  64. $params['data'] = $data;
  65. if($params['source']==User::SOURCE_SHARE_GOODS){
  66. $goods = StoreGoods::getOne([['id' => $params['goods_id'], 'mall_id' => $mallId]],true,[],false,'id as item_id,store_id,goods_name as share_title,price,original_price,cover_pic as share_img');
  67. if(empty($goods)) return $this->error('商品不存在');
  68. $data = array_merge($data, $goods);
  69. $save_content['item_id'] = $goods['item_id'];
  70. $params['store_id'] = $goods['store_id'];
  71. $save_content['store_id'] = $goods['store_id'];
  72. !empty($params['marketing_type']) && $save_content['marketing_type'] = $params['marketing_type'];
  73. $page = DiyEnum::O2O_GOODS_DETAIL_PATH;
  74. }else{
  75. $params['mall_id'] = $this->mall_id;
  76. $return = GlobalInvoke::exec(GlobalInvoke::INVOKE_GET_POSTER,$this->mall_id,$params);
  77. if(!empty($return['page'])) $page = $return['page'];
  78. if(!empty($return['data'])) $data = $return['data'];
  79. if(!empty($return['save_content'])) $save_content = array_merge($save_content,$return['save_content']);
  80. }
  81. // 查询获取数据
  82. $poster = PosterCode::setData($this->mall_id, $this->user->id, $params['source'], $save_content);
  83. if ($poster == false) return $this->error(PosterCode::getStaticError());
  84. $data['qrcode_url'] = $page . "?sign={$sign}&scene=" . $poster['code'];
  85. // 判断平台是否需要后端生成分享海报
  86. if (in_array($this->platform, [User::PLATFORM_WECHAT,User::PLATFORM_MP_WX,User::PLATFORM_H5,User::PLATFORM_APP,User::PLATFORM_MP_ALI])) {
  87. // 小程序生成二维码
  88. if ($this->platform == User::PLATFORM_MP_WX) {
  89. $applet_url = $this->getAppletQrCode($page, $poster['code']);// 获取小程序二维码
  90. if ($applet_url == false) return $this->error('获取小程序二维码失败');
  91. $data['applet_url'] = $applet_url;
  92. }
  93. if($this->platform == User::PLATFORM_MP_ALI){
  94. // 获取支付宝小程序二维码
  95. $applet_url = $this->getMpAliAppletQrCode($page, "sign={$sign}&scene=" . $poster['code'],$data['share_title'] ?? '');
  96. if ($applet_url == false) return $this->error('获取支付宝小程序二维码失败');
  97. $data['applet_url'] = $applet_url;
  98. }
  99. // TODO 其他平台海报由后台生成
  100. }
  101. //浏览
  102. $event = new ShareEvent($this->mall_id);
  103. \Yii::$app->services->events->dispatch($event, ['mall_id'=>$this->mall_id,'user_id'=>$this->user_id], $event->listeners);
  104. $this->success('获取成功1', $data);
  105. }
  106. }
  107. function base64_image_content($base64_image_content,$path, $fileName = ''){
  108. try {
  109. //匹配出图片的格式
  110. if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)){
  111. $type = $result[2];
  112. if(!file_exists($path)){
  113. //检查是否有该文件夹,如果没有就创建,并给予最高权限
  114. mkdir($path, 0777);
  115. }
  116. $fileName = !empty($fileName) ? $fileName : md5(microtime(true));
  117. if (file_put_contents($path . '/' . $fileName. ".{$type}", base64_decode(str_replace($result[1],'', $base64_image_content)))){
  118. return $fileName . ".{$type}";
  119. }else{
  120. return false;
  121. }
  122. }else{
  123. return false;
  124. }
  125. } catch (\Exception $e) {
  126. return false;
  127. }
  128. }
  129. /**
  130. * 海报分享
  131. * @Author: lun
  132. * @DateTime: 2021/12/24 0024 15:19
  133. * @Copyright: copyright (c) 2021 广东七件事集团
  134. * @return mixed|void
  135. */
  136. public function actionShareList()
  137. {
  138. $retuest = Yii::$app->request;
  139. if ($retuest->isPost) {
  140. $mall_id = $this->mall_id;
  141. $sign = $this->mall['mall_sign'];
  142. $where[] = ['=', 'status', StatusEnum::ENABLED];
  143. $where[] = ['=', 'mall_id', $mall_id];
  144. $select = "id,name,key_word,poster_content";
  145. $params = ['where' => $where, 'order' => 'id desc', 'select' => $select];
  146. $list = Poster::lists($params);
  147. if (empty($list)) return $this->success('获取成功', $list);
  148. foreach ($list as $key => $item) {
  149. $data = [
  150. 'user_id' => $this->user->id,
  151. 'nickname' => $this->user->nickname,
  152. 'avatar_url' => $this->user->avatar_url,
  153. 'item_id' => $item['id'],
  154. ];
  155. // 生成海报对应码
  156. $select_code = "id,mall_id,user_id,source,code,content,applet_code";
  157. if ($key == 0) $select_code.= ",applet_code";
  158. $poster = PosterCode::setData($this->mall_id, $this->user->id, User::SOURCE_SHARE_POSTER, $data, $select_code);
  159. if ($poster == false) return $this->error(PosterCode::getStaticError());
  160. $list[$key]['poster_code_id'] = $poster['id'];
  161. $list[$key]['qrcode_url'] = DiyEnum::HOME_PAGE_PATH . "?sign={$sign}&scene=" . $poster['code'];
  162. // 第一张图片请求获取小程序二维码
  163. if ($this->platform == User::PLATFORM_MP_WX && $key == 0 && empty($poster['applet_code'])) {
  164. $applet_url = $this->getAppletQrCode(DiyEnum::HOME_PAGE_PATH , $poster['code']);// 获取小程序二维码base64数据
  165. if ($applet_url == false) return $this->error('获取小程序二维码失败');
  166. $list[$key]['applet_code'] = $applet_url;
  167. PosterCode::setData($this->mall_id, $this->user->id, User::SOURCE_SHARE_POSTER, array_merge($data,['id' => $poster['id'], 'applet_code' => $applet_url]), $select_code);
  168. }
  169. if ($key == 0) {
  170. // 统计浏览次数
  171. Poster::updateAllCounters(['look_num' => 1],"id=" . $item['id']);
  172. }
  173. }
  174. //浏览
  175. $event = new ShareEvent($this->mall_id);
  176. $data['mall_id'] =$this->mall_id;
  177. \Yii::$app->services->events->dispatch($event, ['mall_id'=>$this->mall_id,'user_id'=>$this->user_id], $event->listeners);
  178. $this->success('获取成功', $list);
  179. }
  180. }
  181. /**
  182. * 根据分享码获取分享内容
  183. * @Author: lun
  184. * @DateTime: 2022/1/14 0014 17:28
  185. * @Copyright: copyright (c) 2021 广东七件事集团
  186. * @return mixed|void
  187. */
  188. public function actionGetShareContent()
  189. {
  190. $retuest = Yii::$app->request;
  191. if ($retuest->isPost) {
  192. $params = Yii::$app->request->post();
  193. $res = Yii::$app->services->validate->validate($params, [
  194. [['scene'], 'string'],
  195. ]);
  196. if ($res === false) return $this->error(Yii::$app->services->validate->getErrorMessage());
  197. $cache = Yii::$app->cache;
  198. $cache_key = RedisKeyEnum::suffix(RedisKeyEnum::POSTER_CODE , "M{$this->mall_id}" . $params['scene'],true);
  199. $content = $cache->get($cache_key);
  200. $data = empty($content) ? [] : Json::decode($content, true);
  201. if(isset($data['user_id'])) $data['share_user_id'] = $data['user_id'];
  202. if($data && $data['pid'] > 0 && $this->user_id){
  203. //查询用户是否有上级
  204. $where[] = ['mall_id' => $this->mall_id];
  205. $where[] = ['id' => $this->user_id];
  206. $user = User::getUser($where);
  207. if($user && $user->temp_parent_id > 0 && !$user->parent_id && $user->temp_parent_id != $data['pid']){
  208. $user->temp_parent_id = $data['pid'];
  209. $user->save();
  210. }
  211. }
  212. $this->success('获取成功', $data);
  213. }
  214. }
  215. /**
  216. * 获取海报码详情
  217. * @Author: lun
  218. * @DateTime: 2021/12/30 0030 14:07
  219. * @Copyright: copyright (c) 2021 广东七件事集团
  220. * @return mixed|void
  221. */
  222. public function actionPosterCodeDetail()
  223. {
  224. $retuest = Yii::$app->request;
  225. if ($retuest->isPost) {
  226. $params = \Yii::$app->request->post();
  227. $res = \Yii::$app->services->validate->validate($params, [
  228. [['scene'], 'string'],
  229. [['poster_code_id'], 'integer'],
  230. ]);
  231. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  232. $mall_id = $this->mall_id;
  233. $is_save = false;
  234. !empty($params['poster_code_id']) && $where['id'] = $params['poster_code_id'];
  235. $where['mall_id'] = $mall_id;
  236. !empty($params['scene']) && $where['code'] = $params['scene'];
  237. $where['status'] = StatusEnum::ENABLED;
  238. $select = "id,applet_code,source,item_id,source,content,code,store_id";
  239. $detail = PosterCode::find()->select($select)->where($where)->one();
  240. if (empty($detail)) return $this->error('暂无内容');
  241. if (!empty($params['poster_code_id']) && $this->platform == User::PLATFORM_MP_WX) {
  242. // 小程序二维码为空则去获取小程序二维码
  243. if (empty($detail['applet_code'])) {
  244. $applet_url = $this->getAppletQrCode(DiyEnum::HOME_PAGE_PATH, $detail['code']);// 获取小程序二维码base64数据
  245. if ($applet_url == false) $this->error('获取小程序二维码失败');
  246. // 获取到base64将其临时存服务器
  247. $detail->applet_code = $applet_url;
  248. $is_save = true;
  249. }
  250. }
  251. // 统计浏览次数
  252. if (!empty($params['poster_code_id'])) {
  253. Poster::updateAllCounters(['look_num' => 1],"id=" . $detail->item_id);
  254. }
  255. // 统计海报扫码次数
  256. if (!empty($params['scene']) && $detail['source'] == User::SOURCE_SHARE_POSTER) {
  257. Poster::updateAllCounters(['scan_code_num' => 1],"id=" . $detail->item_id);
  258. }
  259. // 保存数据
  260. if ($is_save && !$detail->save()) return $this->error("获取错误:" . PosterCode::getStaticError());
  261. // 解析json
  262. $content = Json::decode($detail['content'], true);
  263. if (!empty($content['user_id'])) {
  264. $content = array_merge(['share_user_id' => $content['user_id']], $content);
  265. $detail['content'] = Json::encode($content);
  266. }
  267. $this->success('获取成功', $detail->toArray());
  268. }
  269. }
  270. /**
  271. * 获取小程序二维码
  272. * @Author: lun
  273. * @DateTime: 2021/12/27 0027 17:25
  274. * @Copyright: copyright (c) 2021 广东七件事集团
  275. * @param $page
  276. * @param $scene
  277. * @return mixed|void
  278. */
  279. public function getAppletQrCode($page, $scene)
  280. {
  281. $page = ltrim($page, '/');
  282. $url = '';
  283. /** @var Wechat $wechat */
  284. Yii::$app->params['wechatMiniProgramConfig'] = Yii::$app->services->setting->mall->get($this->mall_id, 'mini_program');
  285. $miniProgram = Yii::$app->wechat->miniProgram;
  286. $response = $miniProgram->app_code->getUnlimit($scene, ['width' => 200, 'page' => $page]);
  287. if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
  288. if (in_array('image/jpeg', $response->getHeader('Content-Type'))) {
  289. //返回base64图片
  290. $content = 'data:image/jpg;base64,'. base64_encode($response->getBody()->getContents());
  291. $path = Yii::getAlias('@attachment') . '/download/qrcode';
  292. if (!file_exists($path)) {// 异步生成小程序图片文件夹
  293. Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, ['path' => $path], PosterCode::class, 'mkdirQrcode');
  294. }
  295. $fileName = $this->base64_image_content($content, $path);
  296. if ($fileName == false) return false;
  297. // 图片上传到云存储
  298. $csv = new CsvTool();
  299. $url = $csv->upload($this->mall_id, 'download/qrcode/' . $fileName);
  300. $config = Yii::$app->services->attachment->getStorageConfig($this->mall_id);
  301. if ($config['drive'] != Attachment::DRIVE_LOCAL) unlink($path .'/'. $fileName);// 删除临时存储的文件
  302. } else {
  303. //返回文字
  304. return $this->error('获取失败:' . $response->getBody()->getContents());
  305. }
  306. }else{
  307. return $this->error($response['errmsg']);
  308. }
  309. return $url;
  310. }
  311. /**
  312. * todo 画海报-暂写在控制器,后面需要移除
  313. * @Author: lun
  314. * @DateTime: 2021/11/15 0015 15:12
  315. * @Copyright: copyright (c) 2021 广东七件事集团
  316. * @throws \Exception
  317. */
  318. public function actionDraw()
  319. {
  320. $editor = Grafika::createEditor();
  321. $font = Yii::$app->basePath.'/web/resources/img/poster/st-heiti-light.ttc';
  322. $data = [];
  323. $canvas = '';
  324. $list = self::getDefaultParams();
  325. foreach ($list as $key => $item) {
  326. switch ($item['type']) {
  327. case 'background':// 背景
  328. $canvas = Grafika::createBlankImage($item['w'],$item['h']);
  329. $editor->fill($canvas, new Color($item['color']));
  330. break;
  331. case 'image':// 图片
  332. $editor->open($data[$key], $item['content']);
  333. $editor->resizeFill($data[$key], $item['w'], $item['h']);
  334. $editor->blend($canvas, $data[$key], 'normal', 1, 'top-left', $item['x'], $item['y']);
  335. break;
  336. case 'text':// 文字
  337. $editor->text($canvas ,$item['content'], $item['size'], $item['x'], $item['y'], new Color($item['color']),$font);
  338. break;
  339. case 'scribe':// 划线
  340. $editor->draw($canvas, Grafika::createDrawingObject('Line',[$item['s_x'],$item['s_y']],[$item['e_x'] + 20 * mb_strlen($item['content'],'UTF8'),$item['e_y']],1,new Color($item['color'])));
  341. break;
  342. }
  343. }
  344. $editor->save($canvas,"edited.jpg");
  345. }
  346. private static function getDefaultParams()
  347. {
  348. $data = [
  349. [
  350. 'type' => 'background',
  351. 'content' => '',
  352. 'color' => '#ffffff',
  353. 'w' => '590',
  354. 'h' => '942',
  355. ],
  356. [
  357. 'type' => 'image',
  358. 'content' => 'resources/img/poster/goods.png',
  359. 'w' => '510',
  360. 'h' => '510',
  361. 'x' => '40',
  362. 'y' => '40',
  363. ],
  364. [
  365. 'type' => 'text',
  366. 'content' => '摇滚便携式榨汁机',
  367. 'size' => '24',
  368. 'color' => '#000000',
  369. 'x' => '40',
  370. 'y' => '582',
  371. ],
  372. [
  373. 'type' => 'text',
  374. 'content' => '¥',
  375. 'size' => '24',
  376. 'color' => '#DB0505',
  377. 'x' => '40',
  378. 'y' => '637',
  379. ],
  380. [
  381. 'type' => 'text',
  382. 'content' => '128.06',
  383. 'size' => '30',
  384. 'color' => '#DB0505',
  385. 'x' => '65',
  386. 'y' => '630',
  387. ],
  388. [
  389. 'type' => 'text',
  390. 'content' => '¥169',
  391. 'size' => '18',
  392. 'color' => '#9C9C9C',
  393. 'x' => '40',
  394. 'y' => '695',
  395. ],
  396. [
  397. 'type' => 'scribe',
  398. 'content' => '¥169',
  399. 'size' => '22',
  400. 'color' => '#9C9C9C',
  401. 's_x' => 40,
  402. 's_y' => 707,
  403. 'e_x' => 40,
  404. 'e_y' => 707,
  405. ],
  406. [
  407. 'type' => 'image',
  408. 'content' => 'resources/img/poster/avatar.jpg',
  409. 'w' => '64',
  410. 'h' => '64',
  411. 'x' => '40',
  412. 'y' => '754',
  413. ],
  414. [
  415. 'type' => 'text',
  416. 'content' => '娜可露露',
  417. 'size' => '18',
  418. 'color' => '#2C2D2E',
  419. 'x' => '116',
  420. 'y' => '775',
  421. ],
  422. [
  423. 'type' => 'text',
  424. 'content' => '长按识别小程序码进入',
  425. 'size' => '14',
  426. 'color' => '#B5B9BC',
  427. 'x' => '40',
  428. 'y' => '842',
  429. ],
  430. ];
  431. return $data;
  432. }
  433. /**
  434. * 获取支付宝小程序二维码
  435. * @param $page
  436. * @param $params
  437. * @param $desc
  438. * @return mixed|void
  439. * @throws
  440. */
  441. public function getMpAliAppletQrCode($page, $params,$desc = '')
  442. {
  443. $page = ltrim($page, '/');
  444. $form = new AlipayMiniForm($this->mall_id);
  445. $res = $form->qrcode($page,$params,$desc);
  446. if(!$res){
  447. return $this->error($form->getError());
  448. }
  449. return $res;
  450. }
  451. }