request->absoluteUrl);\Yii::getLogger()->flush(true); if (!\Yii::$app->request->isGet) { return $this->error('请求方式错误'); } // 视频类型,1:公共视频列表;2:喜欢的视频;3:自己发布的视频 $video_type = \Yii::$app->request->get('video_type'); $user_id = \Yii::$app->request->get('user_id'); $tag_id = \Yii::$app->request->get('tag_id'); $share_video_id = \Yii::$app->request->get('video_id'); $page = \Yii::$app->request->get('page', 1); $limit = \Yii::$app->request->get('limit', $this->pageSize); if (empty($video_type) || !in_array($video_type, [1, 2, 3])) return $this->error('参数错误:video_type'); if ($video_type != 1 && empty($user_id)) return $this->error('参数错误:个人中心内查看视频列表,user_id 不能为空'); $wheres[] = ['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'is_blacklist' => 0]; // 如果不是查看自己的视频则必须是审核通过后的视频 if (3 != $video_type || (3 == $video_type && $user_id != $this->user_id)) { // 不是自己的视频列表必须是审核通过的视频 $wheres[] = ['check_status' => AddonsShortVideoEnum::VIDEO_AUDIT_STATUS1]; } if (3 == $video_type) { // 个人中心--我的作品 $wheres[] = ['user_id' => $user_id]; } // 喜欢的视频 if (2 == $video_type) { $likes = ShortVideoLike::find() ->select('video_id,user_id') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'user_id' => $user_id]) ->asArray() ->indexBy('video_id') ->all(); $wheres[] = ['id' => array_column($likes, 'video_id')]; } $config = \Yii::$app->services->setting->addons->get($this->mall_id, AddonsEnum::ADDONS_NAME_SHORT_VIDEO, 'basic'); if (1 == $video_type && $config['is_shuffle'] == 1 && !empty($this->user_id)) { $wheres[]=['not exists', ShortVideoUserWatch::find()->alias('svb')->where(['status' => StatusEnum::ENABLED])->andWhere(['svb.user_id' => $this->user_id]) ->andWhere(new Expression('svb.video_id=sv.id'))]; $_GET['page'] = 1; } $select = 'id,mall_id,user_id,is_top,title,content,video_url,first_frame,video_wide,video_long,related_type,related_goods_id,related_store_id,related_address,related_detailed_address,related_lon,related_lat,comment_num,like_num,look_people_num,look_complete_num,look_num,share_num,share_click_num,created_at,open_integral_award,open_money_award,check_status,check_remark,duration_seconds,tag_id'; $order_by = "is_top desc,top_time desc,created_at desc,share_num desc,like_num desc,look_num desc"; // 按照标签搜索 if (!empty($tag_id)) $wheres[] = new Expression("FIND_IN_SET('$tag_id', tag_id)"); $params = [ 'alias' => 'sv', 'select' => $select, 'where' => $wheres, 'order' => $order_by, 'page' => $page, 'limit' => $limit, ]; $videos = ShortVideo::listPage($params); $video_ids = array_column($videos['list'], 'id'); if($page == 1 && $share_video_id && !in_array($share_video_id, $video_ids)) { $share_video_info = ShortVideo::getOne([['id' => $share_video_id]],true); if($share_video_info) $videos['list'] = array_merge([$share_video_info], $videos['list']); } if (empty($videos['list'])) { if (is_array($videos['list'])) { return $this->success('success', $videos); } else { return $this->error(ShortVideo::getStaticError()); } } else { if (($videos['count'] < 20 || $videos['page_count'] <= 1) && !empty($this->user_id)) { Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, ['mall_id' => $this->mall_id, 'user_id' => $this->user_id], ShortVideoUserWatch::class, 'changeWatchStatus'); } // shuffle($videos['list']); } if (empty($user_id)) { $user_ids = array_column($videos['list'], 'user_id'); } else { $user_ids = $user_id; } // 会员 $users = User::find() ->select('id,nickname,avatar_url') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'id' => $user_ids]) ->asArray() ->indexBy('id') ->all(); // 关注 $follow_users = UserFollow::find() ->select('user_id') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'follow_id' => $user_ids]) ->asArray() ->column(); // 商品 $good_ids = array_column($videos['list'], 'related_goods_id'); $goods = Goods::find() ->select('id,goods_name,cover_pic,price,sales_num') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'id' => $good_ids, 'is_on_sale' => 1]) ->asArray() ->indexBy('id') ->all(); // 店铺 $store_ids = array_column($videos['list'], 'related_store_id'); // TODO:店铺暂时没有 $stores = []; // 观看奖励 $video_ids = array_column($videos['list'], 'id'); $rewards = ShortVideoBrowseAward::find() ->select('video_id,user_id') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'video_id' => $video_ids, 'user_id' => $this->user_id, 'user_type' => 0]) ->asArray() ->indexBy('video_id') ->all(); // 点赞 if (empty($likes)) { $likes = ShortVideoLike::find() ->select('video_id,user_id') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'video_id' => $video_ids, 'user_id' => $this->user_id]) ->asArray() ->indexBy('video_id') ->all(); } // 关联标签 $video_tags = []; $tag_id_arr = array_column($videos['list'], 'tag_id', 'id'); if (!empty($tag_id_arr)) { $tag_ids = []; foreach ($tag_id_arr as $tag_id_str) { $tag_ids = array_merge($tag_ids, explode(',', $tag_id_str)); } $tag_all = ShortVideoTag::find() ->select('id,name') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'id' => $tag_ids]) ->orderBy('recommend_time desc,created_at desc') ->asArray() ->indexBy('id') ->all(); foreach ($tag_id_arr as $video_id => $tag_id_str) { $video_tag_ids = explode(',', $tag_id_str); foreach ($video_tag_ids as $tag_id) { if (!empty($tag_all[$tag_id])) $video_tags[$video_id][] = $tag_all[$tag_id]; } } } // 分享 $shares = ShortVideoShare::find() ->select('user_id,video_id') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'video_id' => $video_ids, 'user_id' => $this->user_id]) ->asArray() ->indexBy('video_id') ->all(); foreach ($videos['list'] as $index => &$video) { $user = $users[$video['user_id']] ?? []; $reward = $rewards[$video['id']] ?? []; $like = $likes[$video['id']] ?? []; $share = $shares[$video['id']] ?? []; $video['nickname'] = $user['nickname'] ?? '默认会员'; $video['avatar_url'] = $user['avatar_url'] ?? \Yii::$app->request->hostInfo . '/resources/img/common/default-avatar.png'; $video['good_info'] = $goods[$video['related_goods_id']] ?? []; $video['store_info'] = $stores[$video['related_store_id']] ?? []; $video['is_send_browse_reward'] = empty($reward) ? 0 : 1; $video['is_liked'] = empty($like) ? 0 : 1; $video['tags'] = $video_tags[$video['id']] ?? []; if (1 == $video['open_integral_award'] || 1 == $video['open_money_award']) { $video['is_reward'] = 1; } else { $video['is_reward'] = 0; } if ($this->user_id == $video['user_id']) { $video['is_follow'] = 0; } else { $video['is_follow'] = in_array($this->user_id, $follow_users) ? 1 : 0; } $video['is_share'] = empty($share) ? 0 : 1; if (!empty($share_video_id)) { // 进来此区间说明是从分享链接点击进来短视频首页 if ($video['id'] == $share_video_id) { $share_video = $video; unset($videos['list'][$index]); } } } if (!empty($share_video)) { array_unshift($videos['list'], $share_video); $video_update_params = [ 'id' => $share_video['id'], 'share_click_num' => $share_video['share_click_num'] + 1, ]; ShortVideo::setData($this->mall_id, $video_update_params); } $configs = ShortVideoConfig::getConfig($this->mall_id); $share_info = [ 'share_img' => $configs['share_img'] ?? '', 'share_title' => $configs['share_title'] ?? '', 'share_content' => $configs['share_content'] ?? '', ]; $videos['share_info'] = $share_info; $videos['status_map'] = AddonsShortVideoEnum::getAuditStatusMap(); return $this->success('success', $videos); } public function actionCreateVideo() { if (!\Yii::$app->request->isPost) { return $this->error('请求方式错误'); } $configs = ShortVideoConfig::getConfig($this->mall_id); if (empty($configs)) return $this->error('系统配置错误,请联系管理员'); if (1 != $configs['is_enabled']) return $this->error('短视频应用未开启,请联系管理员'); $rst = ShortVideo::userCanPush($this->mall_id, $this->user_id); if (!$rst['is_can_push']) { return $this->error($rst['errmsg']); } $post = \Yii::$app->request->post(); $id = $post['id'] ?? 0; $rules = [ [['title', 'content', 'video_url', 'tag_id', 'related_type'], 'required'], ['related_goods_id', 'required', 'when' => function ($model) { return $model->related_type == AddonsShortVideoEnum::VIDEO_RELATE_TYPE1; }], ['related_store_id', 'required', 'when' => function ($model) { return $model->related_type == AddonsShortVideoEnum::VIDEO_RELATE_TYPE2; }], ['related_address', 'required', 'when' => function ($model) { return $model->related_type == AddonsShortVideoEnum::VIDEO_RELATE_TYPE3; }], ['related_lat', 'required', 'when' => function ($model) { return $model->related_type == AddonsShortVideoEnum::VIDEO_RELATE_TYPE3; }], ['related_lat', 'required', 'when' => function ($model) { return $model->related_type == AddonsShortVideoEnum::VIDEO_RELATE_TYPE3; }], ['related_detailed_address', 'required', 'when' => function ($model) { return $model->related_type == AddonsShortVideoEnum::VIDEO_RELATE_TYPE3; }], [['related_type', 'related_goods_id', 'related_store_id'], 'number'], [['related_goods_id', 'related_store_id'], 'default', 'value' => 0], [['related_address', 'related_detailed_address','first_frame'], 'default', 'value' => ''], [['related_lat', 'related_lon'], 'default', 'value' => '0'], ['related_type', 'in', 'range' => [1, 2, 3, 4]], [['duration_seconds'], 'number'] ]; $res = \Yii::$app->services->validate->validate($post, $rules); if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage()); $post['id'] = $id; if (AddonsShortVideoEnum::CLIENT_STATUS1 == $configs['client_status']) { //验证标题内容是否有敏感词汇 $sensitive = new SensitiveWords(); $textStatus = $sensitive->getTextStatus($post['title'], $this->mall_id); if ($textStatus['status'] != 0) { return $this->error('视频标题包含敏感词汇:' . $textStatus['msg']); } $textStatus = $sensitive->getTextStatus($post['content'], $this->mall_id); if ($textStatus['status'] != 0) { return $this->error('视频描述内容包含敏感词汇:' . $textStatus['msg']); } } if (1 == $post['related_type']) { $good = Goods::findOne(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'id' => $post['related_goods_id'], 'is_on_sale' => 1]); if (empty($good)) return $this->error('关联的商品不存在'); } if (2 == $post['related_type']) { // 关联店铺 // TODO: 验证店铺 } $tag = ShortVideoTag::findOne(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'id' => $post['tag_id']]); if (empty($tag)) return $this->error('关联的标签不存在'); if (1 == $configs['is_check']) { $audit_status = 0; } else { $audit_status = 1; } // 增加索引搜索,saas的表中有3000万条数据,模糊搜索url,会很久 $attachment = Attachment::find() ->select(['id', 'width', 'height', 'duration_str', 'duration_seconds']) ->where(['mall_id' => $this->mall_id,'is_delete' => 0,'is_recycle' => 0,'url' => $post['video_url']]) ->from('qimall_attachment USE INDEX (idx_mall_delete_recycle_url)') ->one(); $post['duration_seconds'] = ceil($post['duration_seconds']); $video_data = [ 'user_id' => $this->user_id, 'video_wide' => $attachment->width ?? 0, 'video_long' => $attachment->height ?? 0, 'check_status' => $audit_status, 'is_blacklist' => 0, 'is_top' => 0, 'status' => 1, 'duration_str' => $attachment->duration_str ?? '0', 'duration_seconds' => $post['duration_seconds'] ?? 0, ]; $video_data = array_merge($post, $video_data); $res = ShortVideo::setData($this->mall_id, $video_data); if (false === $res) return $this->error('发布视频失败: ' . ShortVideo::getStaticError()); return $this->success('发布视频成功', ['is_check' => $configs['is_check']]); } /** * @name: * @msg: 获取视频详情 * @param {*} * @return {*} */ public function actionDetail() { if (!\Yii::$app->request->isGet) { return $this->error('请求方式错误'); } $video_id = \Yii::$app->request->get('video_id'); if (empty($video_id)) return $this->error('参数错误'); $video = ShortVideo::find() ->select('id,mall_id,title,content,video_url,first_frame,related_type,related_goods_id,related_store_id,related_address,related_detailed_address,related_lon,related_lat,tag_id,check_remark,check_status,created_at,duration_seconds') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'is_blacklist' => 0, 'id' => $video_id]) ->asArray() ->one(); if (empty($video)) return $this->error('视频不存在'); $good = []; $store = []; $tags = []; if (1 == $video['related_type']) { // 关联商品 $good = Goods::find() ->select('id,goods_name,price,cover_pic') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'is_on_sale' => 1, 'id' => $video['related_goods_id']]) ->asArray() ->one(); } elseif (2 == $video['related_type']) { // 关联店铺 } if (!empty($video['tag_id'])) { $tag_ids = explode(',', $video['tag_id']); $tags = ShortVideoTag::find() ->select('id,mall_id,name,is_recommend,created_at') ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'id' => $tag_ids]) ->asArray() ->all(); } $video['good_info'] = $good; $video['store_info'] = $store; $video['tags'] = $tags; return $this->success('success', $video); } /** * @name: * @msg: 视频点赞 * @param {*} * @return {*} */ public function actionPraiseVideo() { if (!\Yii::$app->request->isGet) { return $this->error('请求方式错误'); } $video_id = \Yii::$app->request->get('video_id'); $type = \Yii::$app->request->get('type'); if (empty($video_id) || (empty($type) || !in_array($type, ['praise', 'unpraise']))) return $this->error('参数错误'); $video = ShortVideo::findOne(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'id' => $video_id, 'check_status' => 1, 'is_blacklist' => 0]); if (empty($video)) return $this->error('视频不存在'); $like = ShortVideoLike::findOne(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'video_id' => $video_id, 'user_id' => $this->user_id]); if (empty($like)) { if ('praise' == $type) { $like_data = [ 'user_id' => $this->user_id, 'video_id' => $video_id, ]; $res_like = ShortVideoLike::setData($this->mall_id, $like_data); $notice = '点赞成功'; } else { return $this->success('取消成功'); } } else { if ('praise' == $type) { return $this->success('点赞成功'); } else { $like_data = [ 'id' => $like->id, 'video_id' => $video_id, 'status' => StatusEnum::DELETE, ]; $res_like = ShortVideoLike::setData($this->mall_id, $like_data); $notice = '取消成功'; } } if (false === $res_like) return $this->error('点赞失败:' . ShortVideoLike::getStaticError()); return $this->success($notice); } /** * @name: * @msg: 关注/取消关注会员 * @param {*} * @return {*} */ public function actionFollow() { if (!\Yii::$app->request->isGet) { return $this->error('请求方式错误'); } $user_id = \Yii::$app->request->get('user_id'); $type = \Yii::$app->request->get('type'); if (empty($user_id) || (empty($type) || !in_array($type, ['follow', 'unfollow']))) return $this->error('参数错误'); if ($user_id == $this->user_id) return $this->error('不可以关注自己'); $follow_user = User::findOne(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'id' => $user_id]); if (empty($follow_user)) return $this->error('关注的会员不存在'); $op = '关注'; $params = [ 'user_id' => $this->user_id, 'follow_id' => $user_id, 'status' => StatusEnum::ENABLED, ]; if ('unfollow' == $type) { $params['status'] = StatusEnum::DELETE; $op = '取消关注'; } $res = ShortVideoFollow::setData($this->mall_id, $params); if (false === $res) return $this->error($op . '失败:' . ShortVideoFollow::getStaticError()); return $this->success($op . '成功'); } /** * @name: * @msg: 浏览视频 * @param {integer} $video_id * @param {integer} $share_user_id * @return {*} */ public function actionVideoBrowse() { if (!\Yii::$app->request->isGet) { return $this->error('请求方式错误'); } // 如果没有登录,则不记录浏览记录 if (empty($this->user_id)) { return $this->success('ok'); } $video_id = \Yii::$app->request->get('video_id'); $share_user_id = \Yii::$app->request->get('share_user_id') ?? 0; if (empty($video_id)) return $this->error('参数错误'); if (!empty($share_user_id)) { $share_log = ShortVideoShare::findOne(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED, 'user_id' => $share_user_id, 'video_id' => $video_id]); if (empty($share_log)) return $this->error('分享记录不存在'); } $params = [ 'video_id' => $video_id, 'user_id' => $this->user_id, 'share_user_id' => $share_user_id, ]; $res = ShortVideo::browse($this->mall_id, $params); if (false === $res) return $this->error('操作失败'); return $this->success('操作成功', ['browse_log_id' => $res->id]); } /** * @name: * @msg: 删除视频 * @param {integer} $video_id * @return {*} */ public function actionVideoDelete() { if (!\Yii::$app->request->isPost) { return $this->error('请求方式错误'); } $video_id = \Yii::$app->request->post('video_id'); if (empty($video_id) || !is_numeric($video_id)) return $this->error('参数错误'); $params = [ 'id' => $video_id, 'user_id' => $this->user_id, 'status' => StatusEnum::DELETE, ]; $res = ShortVideo::setData($this->mall_id, $params); if (false === $res) return $this->error('删除失败:' . ShortVideo::getStaticError()); return $this->success('删除成功'); } /** * @name: * @msg: 视频分享 * @param {integer} $video_id * @return {*} */ public function actionVideoShare() { if (!\Yii::$app->request->isPost) { return $this->error('请求方式错误'); } $video_id = \Yii::$app->request->post('video_id'); if (empty($video_id) || !is_numeric($video_id)) return $this->error('参数错误'); $params = [ 'video_id' => $video_id, 'user_id' => $this->user_id, ]; $res = ShortVideoShare::setData($this->mall_id, $params); if (false === $res) return $this->error('分享失败:' . ShortVideo::getStaticError()); return $this->success('分享成功', ['share_log_id' => $res->id]); } /** * @name: * @msg: 会员完整观看视频,发放奖励 * @param {integer} $video_id 视频id * @param {integer} $browse_log_id 观看视频记录id * @return {*} */ public function actionWatchComplete() { if (!\Yii::$app->request->isPost) { return $this->error('请求方式错误'); } $video_id = \Yii::$app->request->post('video_id'); $share_user_id = \Yii::$app->request->post('share_user_id', 0); if (empty($video_id) || !is_numeric($video_id)) return $this->error('参数错误'); $params = [ 'video_id' => $video_id, 'user_id' => $this->user_id, 'share_user_id' => $share_user_id, ]; $lock_key = RedisKeyEnum::suffix(AddonsShortVideoEnum::LOCK_SHORT_VIDEO_BROWSE_AWARD ,$video_id); $identification = uniqid(); try { if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new \Exception('当前访问人数过多'); $res = ShortVideoBrowseAward::grantReward($this->mall_id, $params); if (false === $res) throw new \Exception('奖励发放失败:' . ShortVideo::getStaticError()); LockHelper::ulock($lock_key,$identification); return $this->success('奖励发放成功', $res); }catch (\Exception $e){ LockHelper::ulock($lock_key,$identification); return $this->success($e->getMessage()); } } //获取酒馆插件短视频打卡配置数据 public function actionGetPubMethodData() { $request = Yii::$app->request; if ($request->isGet) { try { $get = $request->get(); $res = Yii::$app->services->validate->validate($get, [ [['video_id'], 'integer'], ]); if (!$res) throw new Exception(Yii::$app->services->validate->getErrorMessage()); $data = [ 'video_id' => $get['video_id'], 'user_id' => $this->user_id, 'task_list' => [], //视频任务列表 'show_video_work' => 0, //是否显示视频任务 'read_second' => 0, //视频任务进度条 'read_second_list' => [], //观看视频累计时长触发奖励节点 ]; //获取其他插件视频配置 $event = new VideoTaskConfig($this->mall_id, $data); Yii::$app->services->events->dispatch($event); $data = $event->getResultForm(); return $this->success('获取成功', $data); } catch (\Exception $e) { return $this->error($e->getMessage()); } } } //添加完成视频观看数据 public function actionSetVideoTaskData() { $request = Yii::$app->request; if ($request->isPost) { try { $post = $request->post(); $res = Yii::$app->services->validate->validate($post, [ [['video_id', 'read_second', 'share_user_id'], 'integer'] ]); if (!$res) throw new Exception(Yii::$app->services->validate->getErrorMessage()); $event = new VideoRewardPush($this->mall_id); $post['mall_id'] = $this->mall_id; $post['user_id'] = $this->user_id; \Yii::$app->services->events->dispatch($event, $post, $event->listeners); return $this->success('操作成功'); } catch (\Exception $e) { $this->error($e->getMessage()); } } } }