| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- <?php
- /**
- * 视频服务.
- *
- * @author ningque
- * @date 2020-02-18 14:36:11
- */
- declare(strict_types=1);
- namespace Modules\Api\Services;
- use App\Constants\AdConstrant;
- use App\Constants\ChangeLogConstant;
- use App\Constants\CoinsConstant;
- use App\Constants\VideoConstant;
- use App\Exception\ApiException;
- use App\Jobs\AwardCreatorBuyVideoJob;
- use App\Jobs\VideoWatchedByBulkJob;
- use App\Model\AdModel;
- use App\Model\Mongo\VideoHistoryModel;
- use App\Model\Mongo\VideoLikeModel;
- use App\Model\TaskAwarkLogModel;
- use App\Model\UserBuyVideoModel;
- use App\Model\UserModel;
- use App\Model\VideoModel;
- use App\Repositories\CreatorRepository;
- use App\Repositories\RelationRepository;
- use App\Repositories\Service\QueueService;
- use App\Repositories\TaskAwarkLogRepository;
- use App\Repositories\UserRepository;
- use App\Repositories\Utils\M3u8CacheRepository;
- use Carbon\Carbon;
- use Hyperf\DbConnection\Db;
- use Modules\Manage\Controllers\AdController;
- use Psr\SimpleCache\CacheInterface;
- class VideoService
- {
- /**
- * 视频详情.
- * @param int
- * @return mixed
- */
- public function info($id, $userCode)
- {
- $relationRepo = make(RelationRepository::class);
- $user = make(UserRepository::class)->getUserInfoData($userCode);
- // $user = make(UserRepository::class)->getUserInfoFromCache($user_id);
- $columns = ['id','title', 'cover', 'like', 'uid', 'coins', 'pay_type', 'comment', 'mu', 'watch', 'resolution'];
- $ret = VideoModel::query()
- ->with(['type:type.id as type_id,type.title','user'])
- ->where('status', 1)
- ->find($id, $columns);
- if (!$ret) {
- throw new ApiException('没有此视频');
- }
- //鉴权
- $arrFlag = $this->videoAuth($user, $ret);
- /*$arrScreenshots = [];
- if (!empty($ret->screenshots)) {
- $arrTmp = explode(',', $ret->screenshots);
- foreach ($arrTmp as $v) {
- $arrScreenshots[] = M3u8CacheRepository::getImageUrl($v);
- }
- }
- $arrTags = [];
- if (!empty($ret->tags)) {
- $arrTmp = explode(',', $ret->tags);
- foreach ($arrTmp as $v) {
- $arrTags[] = $v;
- }
- }
- $ret->screenshots = $arrScreenshots;
- $ret->tags = $arrTags;
- $ret->cover = M3u8CacheRepository::getImageUrl($ret->cover);
- $ret->mu = M3u8CacheRepository::getVideoUrl($ret->mu);*/
- $result = $ret->toArray();
- $result['cover'] = M3u8CacheRepository::getImageUrl($result['cover']);
- if (!empty($result['user'])) {
- $result['user']['avatar'] = M3u8CacheRepository::getImageUrl($result['user']['avatar']);
- $result['user']['is_focus'] = $relationRepo->userIsFocusOtherUser($user['code'], $result['user']['code'], false);
- }
- $result['is_my_like'] = $relationRepo->userIsLikeVideo($user['code'], $result['id'], false);
- if (!empty($result['category'])) {
- foreach ($result['category'] as $ck => $t) {
- unset($result['category'][$ck]['pivot']);
- }
- }
- $result['auth_err'] = $arrFlag['err'];
- $result['video_surplus'] = $arrFlag['video_surplus'];
- $result['is_viewed'] = $arrFlag['is_viewed'];
- if ($result['auth_err']['key'] != 200) {
- // 非正常的,仅返回6秒预览m3u8地址.
- $m3u8Params1 = [
- 'url' => $result['mu'],
- 'is_preview' => true,
- ];
- $result['mu'] = M3u8CacheRepository::getVideoInnerUrl($m3u8Params1, $userCode, $id, 'video');
- } else {
- $result['mu'] = M3u8CacheRepository::getVideoInnerUrl($result['mu'], $userCode, $id, 'video');
- }
- $adItems = make(VideoService::class)->getAdList(AdConstrant::VIDEO_GUIDE);
- if (!empty($adItems)) {
- $ads = [];
- foreach ($adItems as $v) {
- $tmpLink = $v['link'];
- if (!empty($tmpLink)) {
- $tmpLink = M3u8CacheRepository::getLinkUrl($tmpLink, ['user_code' => $user->code]);
- }
- $ads[] = [
- 'title' => $v['title'],
- 'cover' => M3u8CacheRepository::getImageUrl($v['cover']),
- 'link' => $tmpLink,
- 'scheme' => $v['scheme'],
- ];
- }
- $result['ad'] = $ads;
- }
- $jobParams = [
- [
- 'date' => Carbon::now()->toDateString(),
- 'user_id' => $user['id'],
- 'user_code' => $user['code'],
- 'video_id' => $id,
- 'ad' => $ads
- ]
- ];
- // 异步处理.
- dispatch_job(new VideoWatchedByBulkJob($jobParams));
- return $result;
- }
- /**
- * 视频鉴权
- * @return array
- */
- public function videoAuth($user, $video)
- {
- $checkResult = [
- 'result' => false,
- 'video_surplus' => 0,
- 'is_viewed' => false,
- 'err' => [
- 'key' => 200,
- 'info' => '',
- ],
- ];
- //先看今天看过这部视频没有
- $data['date'] = Carbon::now()->toDateString();
- $data['user_id'] = $user['id'];
- $data['video_id'] = $video->id;
- if ($user['code'] === $video->uid && !empty($video->uid)) {
- // 原创者自己的视频,不限制.
- $count = 1;
- } else {
- $key = $data['date'] .'_'. $data['user_id'] .'_'. $data['video_id'];
- $_id = substr(md5($key), 8, 16);
- $data['_id'] = $_id;
- $count = make(VideoHistoryModel::class)->db()->countDocuments(
- ['_id' => $_id]
- );
- }
- // 今天一个人观看的次数统计.
- $realVideoViewedNum = make(VideoHistoryModel::class)->db()->countDocuments(
- ['uid' => $user['id'],'date' => $data['date']]
- );
- $todayTaskVideoNum = make(TaskAwarkLogRepository::class)->coutTodayOnce($user['code'], TaskAwarkLogModel::REWARD_TYPE_MOVIE_CONUT);
- // 今日剩余观影次数
- $videoSurplus = intval($user['max_view']) - $realVideoViewedNum + $todayTaskVideoNum;
- $oldSurplus = $videoSurplus;
- if ($user['is_vip'] == 'y') {
- $videoSurplus = 999999;
- }
- $isNegativeSurplus = false; // 是否是负数.
- if ($count) {
- // 观看过,依然允许他继续看.
- $checkResult['result'] = true;
- $checkResult['video_surplus'] = $videoSurplus;
- $checkResult['is_viewed'] = true;
- } else {
- $checkResult['result'] = true;
- //金币鉴权
- if ($video->coins > 0) {
- $buyNum = UserBuyVideoModel::query()->where(
- [
- 'user_id' => $user['id'],
- 'video_id' => $video->id,
- ]
- )->count();
- if (!$buyNum) {
- // 没购买过,无法观看.
- $checkResult['result'] = false;
- $checkResult['err'] = [
- 'key' => VideoConstant::VIDEO_COINS_NO,
- 'info' => VideoConstant::$messages[VideoConstant::VIDEO_COINS_NO],
- ];
- } else {
- // 不管是不是会员,超过每日观影次数没有,都记录.
- $this->addHistory($data);
- }
- } else {
- // 不需要金币,则检查剩余次数.
- if ($user['is_vip'] != 'y') {
- // 非VIP情况
- if ($videoSurplus > 0) {
- // 不管是不是会员,超过每日观影次数没有,都记录.
- $this->addHistory($data);
- $videoSurplus--;
- } else {
- $checkResult['result'] = false;
- $checkResult['err'] = [
- 'key' => VideoConstant::VIDEO_LIMIT_NO,
- 'info' => VideoConstant::$messages[VideoConstant::VIDEO_LIMIT_NO],
- ];
- }
- } else {
- $this->addHistory($data);
- }
- }
- }
- if ($videoSurplus < 0) {
- // 容错.
- $videoSurplus = 0;
- $isNegativeSurplus = true;
- }
- $checkResult['video_surplus'] = $videoSurplus;
- $userRepo = make(UserRepository::class);
- $userRepo->delUserCache($user['code']); // 清理这个用户的缓存.
- return $checkResult;
- }
- public function isLike($user_code, $video_id)
- {
- $key = $user_code .'_'. $video_id;
- $_id = substr(md5($key), 8, 16);
- $data = [
- '_id' => $_id
- ];
- $count = make(VideoLikeModel::class)->db()->countDocuments($data);
- if ($count) {
- $result = 1;
- } else {
- $result = 0;
- }
- return $result;
- }
- /**
- * 添加观影历史、更新观影次数
- * @return mixed
- */
- public function addHistory(array $data)
- {
- $result = make(VideoHistoryModel::class)->db()->insertOne(
- [
- '_id' => $data['_id'],
- 'date' => $data['date'],
- 'uid' => $data['user_id'],
- 'vid' => $data['video_id'],
- ]
- );
- return $result->getInsertedId();
- }
- /**
- * 返回播放地址(没有用).
- * @param string $m3u8
- * @return mixed
- */
- public function getPlayUrl($m3u8)
- {
- return M3u8CacheRepository::getVideoUrl($m3u8);
- }
- /**
- * 返回广告内容 .
- * @param string $position
- * @return array
- */
- public function getAdList($position)
- {
- $cacheKey = 'ad:' . $position;
- $ad = make(CacheInterface::class)->remember($cacheKey, 60 * 60, function () use ($position) {
- $items = AdModel::query()
- ->where('status', 1)
- ->where('position', '=', $position)
- ->orderBy('sort', 'DESC')
- ->get();
- if ($items->count()) {
- return $items->toArray();
- } else {
- return [];
- }
- });
- return $ad;
- // $now = Carbon::now()->toDateString();
- //
- // return collect($ad)->where('position', '=', $position)
- // ->where('start_time', '<=', $now)
- // ->where('end_time', '>=', $now)
- // ->all();
- }
- /**
- * 用户购买视频.
- * @param $userCode
- * @param $videoId
- * @return mixed
- */
- public function buyVideo($userCode, $videoId)
- {
- $userObj = UserModel::query()->where(['code' => $userCode])->first(); // 买家
- $buyNum = UserBuyVideoModel::query()->where(['user_id' => $userObj->id, 'video_id' => $videoId])->count();
- if ($buyNum) {
- throw new ApiException('您已经购买过,无需重复购买');
- }
- $videoObj = VideoModel::query()->where(['id' => $videoId])->first();
- if (empty($videoObj)) {
- throw new ApiException('视频不存在');
- }
- if ($videoObj->pay_type == 2) {
- if ($videoObj->coins < 1) {
- throw new ApiException('此视频不可使用金币购买');
- }
- } else {
- throw new ApiException('此视频不可使用金币购买');
- }
- if (intval($userObj->coins) < intval($videoObj->coins)) {
- throw new ApiException(sprintf('您金币不足%s,请先购买金币', $videoObj->coins), VideoConstant::VIDEO_COINS_NO);
- }
- // 视频创作者
- $creatorRepo = make(CreatorRepository::class);
- $creatorObj = $creatorRepo->detail($videoObj->uid);
- $isDeduction = 0;
- if (!empty($creatorObj->user_uid)) {
- $isDeduction = intval($creatorRepo->isDeduction($creatorObj->user_uid, $creatorObj->level)); // 是否扣量,默认不扣
- }
- Db::beginTransaction();
- $setarr = [
- 'user_id' => $userObj->id,
- 'video_id' => $videoObj->id,
- 'is_deduction' => $isDeduction,
- ];
- $buyObj = UserBuyVideoModel::query()->create($setarr);
- if (!$buyObj) {
- Db::rollBack();
- return '';
- }
- $userObj->coins = $userObj->coins - $videoObj->coins;
- if (!$userObj->save()) {
- Db::rollBack();
- return '';
- }
- Db::commit();
- $jobParams = [
- 'video_id' => $videoObj->id,
- 'buyer_uid' => $userObj->code,
- 'seller_uid' => !empty($creatorObj->user_uid) ? $creatorObj->user_uid : '',
- 'is_deduction' => $isDeduction,
- ];
- if (empty($isDeduction)) {
- // 异步奖励创作者.
- dispatch_job(new AwardCreatorBuyVideoJob($jobParams));
- }
- // 帐变记录
- $changeData = [
- 'user_uid' => $userObj->code,
- 'currency_type' => ChangeLogConstant::CHANGE_COIN,
- 'amount' => $videoObj->coins,
- 'way' => ChangeLogConstant::WAY_2,
- 'type' => ChangeLogConstant::TYPE_10,
- ];
- make(QueueService::class)->saveChangeLogJob($changeData);
- // 清除缓存
- make(UserRepository::class)->delUserCache($userObj->code);
- unset($userObj, $creatorObj); // 释放内存.
- return $videoObj;
- }
- }
|