// +---------------------------------------------------------------------- namespace App\Services; use AlibabaCloud\Tea\Exception\TeaUnableRetryError; use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi; use App\Models\AccountLogModel; use App\Models\LiveGiftModel; use App\Models\LiveModel; use App\Models\MemberModel; use App\Models\RewardOrderModel; use App\Models\VideoCollectModel; use App\Services\Api\MemberCollectService; use App\Services\Api\MessageService; use App\Services\Api\TaskService; use App\Services\Api\VideoCollectService; use Darabonba\OpenApi\Models\Config; use AlibabaCloud\SDK\Dysmsapi\V20170525\Models\SendSmsRequest; use AlibabaCloud\Tea\Utils\Utils\RuntimeOptions; use Illuminate\Support\Facades\DB; /** * 在线直播服务管理-服务类 * @author laravel开发员 * @since 2020/11/11 * Class LiveService * @package App\Services */ class LiveService extends BaseService { // 静态对象 protected static $instance = null; /** * 构造函数 * @author laravel开发员 * @since 2020/11/11 * ConfigService constructor. */ public function __construct() { $this->model = new LiveModel(); } /** * 静态入口 * @return SmsService|static|null */ public static function make() { if (!self::$instance) { self::$instance = new static(); } return self::$instance; } /** * 列表数据 * @param $params * @param int $pageSize * @return array */ public function getDataList($params, $pageSize = 18, $field = '', $userId=0) { $where = ['a.mark' => 1,'a.status'=>1,'b.mark'=>1]; $field = $field? $field : 'lev_a.*'; $order = 'rand()'; $model = $this->model->with(['member'])->from('live as a') ->leftJoin('member as b', 'b.id', '=', 'a.user_id') ->where($where) ->where(function ($query) use ($params) { $type = isset($params['type']) ? $params['type'] : 0; if ($type > 0) { $query->where('a.type', $type); } $categoryId = isset($params['category_id']) ? $params['category_id'] : 0; if ($categoryId > 0) { $query->where('a.category_id', $categoryId); } $uid = isset($params['user_id']) ? $params['user_id'] : 0; if ($uid > 0) { $query->where('a.user_id', $uid); } }) ->where(function ($query) use ($params) { $keyword = isset($params['kw']) ? $params['kw'] : ''; if ($keyword) { $query->where('a.title', 'like', "%{$keyword}%") ->orWhere('a.description', 'like', "%{$keyword}%") ->orWhere('b.nickname', 'like', "%{$keyword}%"); } })->where(function ($query) use ($userId) { if ($userId) { $query->where('a.visible_users', '=', '') ->orWhere('a.visible_users', 'like',"%{$userId},%") ->orWhere('a.manage_users', 'like',"%{$userId},%"); } }); // 推荐的数据 $countModel = clone $model; $total = $countModel->where(function($query) use($params, $userId){ // 推荐视频数据 $isRecommend = isset($params['is_recommend']) ? $params['is_recommend'] : 0; if ($isRecommend > 0) { $recommendData = VideoCollectService::make()->getRecommendData($userId); $uids = isset($recommendData['uids'])? $recommendData['uids'] : []; // 按用户推荐 $category = isset($recommendData['category'])? $recommendData['category'] : []; // 按标签推荐 if($uids){ $query->orWhere(function($query) use($uids){ $query->whereIn('a.user_id', $uids); }); } if($category){ $query->orWhere(function($query) use($category){ $query->whereIn('a.category_id', $category); }); } } })->count('a.id'); if($total > 0){ // 关联推荐数据 $list = $countModel->selectRaw($field) ->orderByRaw($order) ->paginate($pageSize > 0 ? $pageSize : 9999999); }else{ // 默认推荐数据 $list = $model->selectRaw($field) ->orderByRaw($order) ->paginate($pageSize > 0 ? $pageSize : 9999999); } $list = $list ? $list->toArray() : []; if ($list && $list['data']) { foreach ($list['data'] as &$item) { $item['time_text'] = isset($item['create_time']) ? dateFormat($item['create_time'], 'Y-m-d H:i') : ''; $member = isset($item['member'])? $item['member'] : []; if($member){ $member['avatar'] = isset($member['avatar']) && $member['avatar']? get_image_url($member['avatar']) : get_image_url('/images/member/logo.png'); } $item['like_num'] = isset($item['like_num']) && $item['like_num']? intval($item['like_num']) : 0; $item['views'] = isset($item['views']) && $item['views']? intval($item['views']) : 0; $item['member'] = $member; } } return [ 'pageSize' => $pageSize, 'total' => isset($list['total']) ? $list['total'] : 0, 'list' => isset($list['data']) ? $list['data'] : [] ]; } /** * 详情 * @param $id * @param $userId * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null */ public function getInfo($id, $userId) { $info = $this->model->with(['member'])->where(['id'=> $id,'mark'=>1])->first(); $info = $info? $info->toArray() : []; if($info && isset($info['member'])){ if(isset($info['member']['avatar'])){ $info['member']['avatar'] = $info['member']['avatar']? $info['member']['avatar'] : '/images/member/logo.png'; $info['member']['avatar'] = get_image_url($info['member']['avatar']); } if($info['user_id'] == $userId){ $info['is_follow'] = 1; }else{ $checkFollow = MemberCollectService::make()->checkCollect($userId, $info['user_id'], 1); $info['is_follow'] = $checkFollow? 1 : 0; $info['member']['is_follow'] = $checkFollow? 1 : 0; } // 用户资料 $liveUserId = isset($info['user_id'])? $info['user_id'] : 0; $info['member']['fans'] = (int)MemberCollectService::make()->getCount($liveUserId, 1,1); // 粉丝数量(被关注) $info['member']['follow'] = (int)MemberCollectService::make()->getCount($liveUserId, 1,2); // 关注数量 // 观看权限 $info['view_limit'] = 0; if($info['status'] == 2){ $createTime = $info['create_time']? strtotime($info['create_time']) : time(); $time = $info['end_time']? intval($info['end_time'] - $createTime) : intval(time() - $createTime); $info['end_time'] = $info['end_time']? $info['end_time'] : time(); $info['live_hour'] = $time > 3600 ? intval($time/3600) : 0; $info['live_minute'] = $time%3600? intval($time%3600/60) : 0; $info['time_text'] = $createTime<(time() - 86400)? date('m-d H:i', $createTime) : date('H:i', $createTime); $info['end_time_text'] = $info['end_time']<(time() - 86400)? date('m-d H:i', $info['end_time']) : date('H:i', $info['end_time']); $info['fans_num'] = MemberCollectService::make()->getViewFansCountByType($info['user_id'], $id, 2); $info['new_fans'] = MemberCollectService::make()->getNewFansCount($info['user_id'], $id,2, $createTime); } // 浏览历史 if(!VideoCollectService::make()->getCollectCacheInfo($userId, $id, 1, 2)){ $data = [ 'user_id'=> $userId, 'type'=> 1, 'source_type'=> 2, 'collect_id'=> $id, 'collect_uid'=> isset($info['user_id'])? $info['user_id'] : 0, 'tags'=> isset($info['tags'])? $info['tags'] : '', 'create_time'=> time(), 'status'=> 1, ]; VideoCollectModel::insert($data); RedisService::clear("caches:videos:recommend:{$userId}_2"); RedisService::clear("caches:member:fans:{$userId}_{$id}_2"); RedisService::clear("caches:live:users_{$id}_{$userId}"); } // 更新播放量 if(!RedisService::get("caches:live:player:{$userId}_{$id}")){ $this->model->where(['id'=> $id])->update(['views'=>DB::raw('views + 1'),'update_time'=>time()]); RedisService::set("caches:live:player:{$userId}_{$id}", ['user_id'=> $userId,'id'=>$id], rand(600, 1800)); $info['views'] += 1; // 观看直播任务 TaskService::make()->updateTask($userId,1, $id,1); } // 结束直播 $createTime = $info['create_time']? strtotime($info['create_time']) : time(); if($createTime <= time() - 1.5 * 86400 && $info['status'] == 1){ $this->model->where(['id'=> $id])->update(['status'=>2,'end_time'=>time(),'update_time'=>time()]); $info['status'] = 2; } } return $info; } /** * 获取直播间在线观众数据 * @param $params * @param $pageSize * @param int $userId * @return array|mixed */ public function getUserList($params, $pageSize=200, $userId=0) { $liveId = isset($params['live_id'])? $params['live_id'] : 0; $cachekey = "caches:live:users_{$liveId}_{$userId}"; $datas = RedisService::get($cachekey); if($datas || RedisService::exists($cachekey)){ return $datas? $datas : [ 'current' => ['user_id'=> $userId,'rank'=>0,'reward_total'=>0], 'rank_limit'=> 200, 'total' => 0, 'list' => [] ]; } $rankNum = ConfigService::make()->getConfigByCode('live_show_rank', 200); $rankNum = $rankNum>20 && $rankNum <= 300? $rankNum : 200; $model = MemberModel::from('member as a') ->leftJoin('video_collect as b',function($join) use($liveId){ $join->on('a.id','=','b.user_id') ->where(['b.type'=>1,'b.collect_id'=> $liveId,'b.source_type'=>2,'b.is_online'=>1,'b.status'=>1,'b.mark'=>1]); }) ->where(['a.status'=>1,'a.mark'=>1]) ->where('b.id','>', 0) ->select(['a.id','a.nickname','a.avatar','a.member_level','a.gender','a.intro','a.status','b.create_time','b.reward_total','b.collect_id as live_id']); $countModel = clone $model; $total = $countModel->distinct('a.id')->count('a.id'); $datas = $model->groupBy('a.id') ->orderBy('b.reward_total','desc') ->orderBy('b.create_time','asc') ->limit($rankNum)->get(); $datas = $datas? $datas->toArray() : []; $rankData = ['user_id'=> $userId,'rank'=>0,'reward_total'=>0]; if ($datas) { foreach ($datas as $k=> &$item) { $item['time_text'] = isset($item['create_time']) ? dateFormat($item['create_time'], 'Y-m-d H:i') : ''; $item['avatar'] = isset($item['avatar']) && $item['avatar']? get_image_url($item['avatar']) : get_image_url('/images/member/logo.png'); $item['reward_total'] = isset($item['reward_total']) && $item['reward_total']? format_num(intval($item['reward_total'])) : 0; if($item['id'] == $userId){ $rankData['rank'] = ($k+1)>$rankNum? 0 : ($k+1); $rankData['reward_total'] = $item['reward_total']? format_num(intval($item['reward_total'])) : 0; } } // 用户排名在200名外的数据 if($rankData['rank'] <= 0 && $rankData['reward_total']<=0 && count($datas) > $rankNum){ $rewardTotal = VideoCollectModel::where(['user_id'=> $userId,'type'=>1,'collect_id'=> $liveId,'source_type'=>2,'status'=>1,'mark'=>1]) ->value('reward_total'); $rankData['reward_total'] = $rewardTotal? format_num(intval($rewardTotal)) : 0; } $totalText = $total>=10000 && $total<=100000? intval($total/10000).'万' : intval($total); RedisService::set($cachekey, ['current' => $rankData,'rank_limit'=> $rankNum,'total'=> $total,'total_text'=>$totalText,'list'=> $datas], rand(3,5)); } return [ 'current' => $rankData, 'rank_limit'=> $rankNum, 'total' => $total, 'total_text' => $total>=10000 && $total<=100000? intval($total/10000).'万' : intval($total), 'list' => $datas ]; } /** * 获取直播间礼物列表 * @param $params * @param $pageSize * @param int $userId * @return array|mixed */ public function getGiftList($params, $pageSize=30, $userId=0) { $page = request()->post('page', 1); $liveId = isset($params['live_id'])? $params['live_id'] : 0; $cachekey = "caches:live:gift_{$liveId}_{$userId}_{$page}"; $datas = RedisService::get($cachekey); if($datas || RedisService::exists($cachekey)){ return $datas? $datas : [ 'pageSize'=> $pageSize, 'total' => 0, 'list' => [] ]; } $list = LiveGiftModel::from('live_gift as a') ->where(['a.status'=>1,'a.mark'=>1]) ->select(['a.id','a.name','a.code','a.money','a.icon','a.file_url','a.sort']) ->orderBy('a.sort','desc') ->orderBy('a.create_time','asc') ->paginate($pageSize > 0 ? $pageSize : 9999999); $list = $list ? $list->toArray() : []; if ($list && $list['data']) { foreach ($list['data'] as &$item) { $item['icon'] = isset($item['icon']) && $item['icon']? get_image_url($item['icon']) : get_image_url('/images/gift/gift.png'); $item['file_url'] = isset($item['file_url']) && $item['file_url']? get_image_url($item['file_url']) : ''; } RedisService::set($cachekey, [ 'pageSize' => $pageSize, 'total' => isset($list['total']) ? $list['total'] : 0, 'list' => isset($list['data']) ? $list['data'] : [] ], rand(5,10)); } return [ 'pageSize' => $pageSize, 'total' => isset($list['total']) ? $list['total'] : 0, 'list' => isset($list['data']) ? $list['data'] : [] ]; } /** * 更新播放浏览历史 * @param $userId 用户ID * @param $id 视频ID * @return false */ public function updatePlay($userId, $id) { // 浏览历史 if(!VideoCollectService::make()->getCollectCacheInfo($userId, $id, 1,2)){ $info = $this->model->from('live as a') ->where(['a.id'=> $id,'a.mark'=>1]) ->select(['a.id','a.category','a.user_id']) ->first(); if(empty($info)){ return false; } $data = [ 'user_id'=> $userId, 'type'=> 1, 'source_type'=> 2, 'collect_id'=> $id, 'category_id'=> isset($info['category'])? $info['category'] : 0, 'collect_uid'=> isset($info['user_id'])? $info['user_id'] : 0, 'create_time'=> time(), 'status'=> 1, ]; VideoCollectModel::insert($data); RedisService::set("caches:videos:collect:temp_{$userId}_{$id}_1_2", $data, rand(10,30)); RedisService::clear("caches:videos:recommend:{$userId}_1_2"); RedisService::clear("caches:member:fans:{$userId}_{$id}_2"); RedisService::clear("caches:live:users_{$id}_{$userId}"); } // 更新播放量 if(!RedisService::get("caches:player:live:{$userId}_{$id}")){ $this->model->where(['id'=> $id])->update(['views'=>DB::raw('views + 1'),'update_time'=>time()]); RedisService::set("caches:player:live:{$userId}_{$id}", ['user_id'=> $userId,'id'=>$id], rand(6*3600, 86400)); // 观看直播任务 TaskService::make()->updateTask($userId,1, $id, 1); } $this->error = 1010; return true; } /** * 缓存数据 * @param $id * @param int $status * @return array|mixed */ public function getCacheInfo($id, $status=0) { $cacheKey = "caches:live:info:{$id}_{$status}"; $info = RedisService::get($cacheKey); if($info){ return $info; } $where = ['id' => $id, 'mark' => 1]; if($status){ $where['status'] = $status; } $info = $this->model->where($where) ->select(['id','user_id','create_time','end_time','views','reward_num','reward_total','like_num']) ->first(); $info = $info? $info->toArray() : []; if($info){ RedisService::set($cacheKey, $info, rand(3, 5)); } return $info; } /** * 状态设置 * @return bool */ public function status() { $id = request()->post('id', 0); $status = request()->post('status', 2); if ($id && !$info = $this->getCacheInfo($id)) { $this->error = 2981; return false; } $updateData = ['status'=>$status, 'update_time'=> time()]; if($status == 2){ $updateData['end_time'] = time(); } if($this->model->where(['id'=> $id,'mark'=>1])->update($updateData)){ $this->error = 1002; if($status == 2){ $createTime = $info['create_time']? strtotime($info['create_time']) : time(); $time = intval(time() - $createTime); $info['status'] = $status; $info['end_time'] = time(); $info['live_hour'] = $time > 3600 ? intval($time/3600) : 0; $info['live_minute'] = $time%3600? intval($time%3600/60) : 0; $info['time_text'] = $createTime<(time() - 86400)? date('m-d H:i', $createTime) : date('H:i', $createTime); $info['end_time_text'] = $info['end_time']<(time() - 86400)? date('m-d H:i', $info['end_time']) : date('H:i', $info['end_time']); $info['fans_num'] = MemberCollectService::make()->getViewFansCountByType($info['user_id'], $id, 2); $info['new_fans'] = MemberCollectService::make()->getNewFansCount($info['user_id'], $id,2, $createTime); } return $info; } $this->error = 1003; return true; } /** * 点赞 * @return bool */ public function like($userId, $params) { $liveId = isset($params['live_id'])? intval($params['live_id']) : 0; $num = isset($params['num'])? intval($params['num']) : 0; if($liveId<=0 || $num<=0){ return false; } $info = $this->getCacheInfo($liveId); if(empty($info)){ return false; } if($this->model->where(['id'=> $liveId])->update(['like_num'=>DB::raw("like_num + {$num}"),'update_time'=>time()])){ $this->error = 1017; return ['id'=>$liveId,'like_num'=>intval($info['like_num'] + $num),'nun'=>$num]; } return false; } /** * 获取直播推流/拉流地址 * @param $streamName * @param string $appName * @param string $playType * @param int $expireTime * @return array */ public function getLiveUrl($streamName, $appName = 'xlapp', $playType = 'rtmp', $expireTime = 1440) { $cachekey = "caches:live:urls:{$streamName}_{$appName}_{$playType}"; $datas = RedisService::get($cachekey); if($datas){ return $datas; } $playUrls = []; //未开启鉴权Key的情况下 $pushDomain = ConfigService::make()->getConfigByCode('live_push_url'); $playDomain = ConfigService::make()->getConfigByCode('live_play_url'); $pushKey = ConfigService::make()->getConfigByCode('push_url_key'); $playKey = ConfigService::make()->getConfigByCode('play_url_key'); $timeStamp = time() + $expireTime * 60; if ($pushKey == '') { $pushUrl = 'rtmp://' . $pushDomain . '/' . $appName . '/' . $streamName; } else { $sstring = '/' . $appName . '/' . $streamName . '-' . $timeStamp . '-0-0-' . $pushKey; $md5hash = md5($sstring); $pushUrl = 'rtmp://' . $pushDomain . '/' . $appName . '/' . $streamName . '?auth_key=' . $timeStamp . '-0-0-' . $md5hash; } if ($playKey == '') { $playUrls['rtmp'] = 'rtmp://' . $playDomain . '/' . $appName . '/' . $streamName; $playUrls['flv'] = 'http://' . $playDomain . '/' . $appName . '/' . $streamName . '.flv'; $playUrls['hls'] = 'http://' . $playDomain . '/' . $appName . '/' . $streamName . '.m3u8'; } else { $rtmpSstring = '/' . $appName . '/' . $streamName . '-' . $timeStamp . '-0-0-' . $playKey; $rtmpMd5hash = md5($rtmpSstring); $playUrls['rtmp'] = 'rtmp://' . $playDomain . '/' . $appName . '/' . $streamName . '?auth_key=' . $timeStamp . '-0-0-' . $rtmpMd5hash; $flvSstring = '/' . $appName . '/' . $streamName . '.flv-' . $timeStamp . '-0-0-' . $playKey; $flvMd5hash = md5($flvSstring); $playUrls['flv'] = 'http://' . $playDomain . '/' . $appName . '/' . $streamName . '.flv?auth_key=' . $timeStamp . '-0-0-' . $flvMd5hash; $hlsSstring = '/' . $appName . '/' . $streamName . '.m3u8-' . $timeStamp . '-0-0-' . $playKey; $hlsMd5hash = md5($hlsSstring); $playUrls['hls'] = 'http://' . $playDomain . '/' . $appName . '/' . $streamName . '.m3u8?auth_key=' . $timeStamp . '-0-0-' . $hlsMd5hash; } $datas = [ 'push_url' => $pushUrl, 'play_url' => isset($playUrls[$playType]) ? $playUrls[$playType] : '', 'play_urls' => $playUrls, ]; RedisService::set($cachekey, $datas, 1800); return $datas; } /** * 创建直播间 * @param $userId 直逼用户 * @param $params * @return array|false */ public function create($userId, $params) { $liveLevel = ConfigService::make()->getConfigByCode('live_open_level'); $liveLevel = $liveLevel > 0 ? $liveLevel : 0; $userInfo = MemberModel::where(['id' => $userId, 'mark' => 1])->select(['id', 'nickname', 'member_level', 'status'])->first(); $status = isset($userInfo['status']) ? $userInfo['status'] : 0; $nickname = isset($userInfo['nickname']) ? $userInfo['nickname'] : ''; $memberLevel = isset($userInfo['member_level']) ? $userInfo['member_level'] : 0; if (empty($userInfo) || $status != 1) { $this->error = 2024; return false; } if($memberLevel < $liveLevel){ $this->error = 2040; return false; } // 验证是否有开播中断播的继续播 $data = [ 'type' => isset($params['type']) ? intval($params['type']) : 1, 'user_id' => $userId, 'title' => isset($params['title']) && $params['title'] ? trim($params['title']) : $nickname.'正在直播', 'description' => isset($params['description']) && $params['description'] ? trim($params['description']) : lang('我正在直播,快来看看吧'), 'category' => isset($params['category']) ? intval($params['category']) : 0, 'visible_type' => isset($params['visible_type']) ? intval($params['visible_type']) : 0, 'visible_users' => isset($params['visible_users']) ? trim($params['visible_users']) : '', 'chat_type' => isset($params['chat_type']) ? intval($params['chat_type']) : 0, 'chat_status' => isset($params['chat_status']) ? intval($params['chat_status']) : 1, 'pay_status' => isset($params['pay_status']) ? intval($params['pay_status']) : 1, 'open_area' => isset($params['open_area']) ? intval($params['open_area']) : 1, 'view_allow' => isset($params['view_allow']) ? intval($params['view_allow']) : 1, 'push_url' => isset($params['push_url']) ? trim($params['push_url']) : '', 'play_url' => isset($params['play_url']) ? trim($params['play_url']) : '', 'update_time'=> time(), 'status' =>1, 'mark'=>1, ]; if($liveId = $this->model->where(['user_id'=> $userId,'status'=>1,'mark'=>1])->orderBy('create_time','desc')->value('id')){ if(!$this->model->where(['user_id'=> $userId,'status'=>1,'mark'=>1])->update($data)){ $this->error = 2042; return false; } }else{ $data['create_time'] = time(); $this->model->where(['user_id'=> $userId,'mark'=>1])->update(['status'=>2,'update_time'=>time()]); if(!$liveId = $this->model->insertGetId($data)){ $this->error = 2042; return false; } } $data['id'] = $liveId; $data['member'] = MemberModel::where(['id'=> $userId])->select(['id','nickname','avatar','status'])->first(); $data['member'] = $data['member']? $data['member'] : []; $data['member']['is_follow'] = 1; if(isset($data['member']['avatar'])){ $data['member']['avatar'] = $data['member']['avatar']? $data['member']['avatar'] : '/images/member/logo.png'; $data['member']['avatar'] = get_image_url($data['member']['avatar']); } $this->error = 2041; return $data; } /** * 直播打赏 * @param $userId 打赏用户ID * @param $params 参数 * @return bool */ public function reward($userId, $params) { $liveId = isset($params['live_id'])? intval($params['live_id']) : 0; $giftId = isset($params['gift_id'])? intval($params['gift_id']) : 0; $liveInfo = $this->model->with(['member'])->where(['id'=> $liveId,'mark'=>1]) ->select(['id','user_id','category','pay_status','reward_num','status']) ->first(); $status = isset($liveInfo['status'])? $liveInfo['status'] : 0; $payStatus = isset($liveInfo['pay_status'])? $liveInfo['pay_status'] : 0; $liveUserId = isset($liveInfo['user_id'])? $liveInfo['user_id'] : 0; $liveMember = isset($liveInfo['member'])? $liveInfo['member'] : []; $liveUserUsdt = isset($liveMember['usdt'])? $liveMember['usdt'] : 0; $bonusUserId = isset($liveMember['parent_id'])? $liveMember['parent_id'] : 0; if(empty($liveInfo) || empty($liveMember) || $status != 1){ $this->error = 2301; return false; } if($payStatus!=1){ $this->error = 2302; return false; } $giftInfo = LiveGiftModel::where(['id'=> $giftId,'status'=>1,'mark'=>1]) ->select(['id','name','money']) ->first(); $money = isset($giftInfo['money'])? intval($giftInfo['money']) : 0; if(empty($giftInfo) || $money<=0){ $this->error = 2303; return false; } // 用户信息 $userInfo = MemberModel::where(['id'=> $userId,'mark'=>1]) ->select(['id','nickname','parent_id','point_id','balance','status']) ->first(); $balance = isset($userInfo['balance'])? floatval($userInfo['balance']) : 0; if($balance < $money){ $this->error = 2304; return false; } // 直播推荐人(经纪人) $bounsUserInfo = []; $bonusUserLevel = 0; $bonusUserUsdt = 0; if($bonusUserId){ $bounsUserInfo = MemberModel::where(['id'=> $bonusUserId,'status'=>1,'mark'=>1]) ->select(['id','nickname','parent_id','point_id','member_level','usdt','balance','status']) ->first(); $bonusUserLevel = isset($bounsUserInfo['member_level'])? $bounsUserInfo['member_level'] : 0; $bonusUserUsdt = isset($bounsUserInfo['usdt'])? $bounsUserInfo['usdt'] : 0; } // TODO 打赏订单 $orderNo = get_order_num('RW'); $awardLevel = ConfigService::make()->getConfigByCode('live_award_level',5); // 推荐奖励等级 $awardLevel = $awardLevel>=1 && $awardLevel<=30? $awardLevel : 5; $awardRate = ConfigService::make()->getConfigByCode('award_rewad_rate',10); // 推荐奖励比例% $awardRate = $awardRate>=0 && $awardLevel<100? $awardRate : 10; $xdPrice = ConfigService::make()->getConfigByCode('xd_price',100); // 星豆价格 $xdPrice = $xdPrice>0.01 && $xdPrice <=10000? $xdPrice : 100; $usdt = moneyFormat($money/$xdPrice,2); // 推荐佣金 $bonusUsdt = $awardRate? moneyFormat($usdt * $awardRate/100, 4) : 0; DB::beginTransaction(); $order = [ 'live_id'=> $liveId, 'user_id'=> $userId, 'live_user_id'=> $liveUserId, 'gift_id'=> $giftId, 'order_no'=> $orderNo, 'type'=> 1, 'money'=> $money, 'usdt'=> $usdt, 'bonus_rate'=> $awardRate? $awardRate : 0, 'bonus_usdt'=> $bonusUserId>0? $bonusUsdt : 0, 'bonus_uid'=> $bonusUserId, 'bonus_status'=> $bonusUserLevel >= $awardLevel? 1 : 2, 'create_time'=> time(), 'status'=> 1, 'mark'=>1 ]; if(!$orderId = RewardOrderModel::insertGetId($order)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'创建订单失败'], 600); return false; } // 扣除打赏用户余额 $updateData = ['balance'=>DB::raw("balance - {$money}"),'update_time'=>time()]; if(!MemberModel::where(['id'=> $userId,'mark'=>1])->update($updateData)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'账户扣款失败'], 600); return false; } // 明细 $log = [ 'user_id'=> $userId, 'source_id'=> $liveId, 'source_order_no'=> $orderNo, 'user_type'=> 1, 'type'=>1, 'coin_type'=> 2, 'money'=> -$usdt, 'date'=> date('Y-m-d'), 'actual_money'=> -$money, 'balance'=> $balance, 'create_time'=> time(), 'remark'=> '礼物打赏消费', 'status'=> 1, 'mark'=> 1, ]; if(!AccountLogModel::insertGetId($log)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'账户明细处理失败'], 600); return false; } // TODO 推荐奖励(等级需要超过) $realUsdt = $usdt; $dateTime = date('Y-m-d H:i:s'); $liveName = isset($liveMember['nickname']) && $liveMember['nickname']? $liveMember['nickname'] : $liveUserId; $userNickname = isset($userInfo['nickname']) && $userInfo['nickname']? $userInfo['nickname'] : $userId; if($bonusUserId>0 && $bonusUserLevel >= $awardLevel && $bonusUsdt>0){ // 账户变动 $updateData = ['usdt'=>DB::raw("usdt + {$bonusUsdt}"),'update_time'=>time()]; if(!MemberModel::where(['id'=> $bonusUserId,'mark'=>1])->update($updateData)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'update'=>$updateData,'usdt'=>$bonusUsdt,'error'=>'主播推荐奖励处理失败'], 600); return false; } // 明细 $log = [ 'user_id'=> $bonusUserId, 'source_id'=> $liveId, 'source_order_no'=> $orderNo, 'user_type'=> 1, 'type'=> 10, 'coin_type'=> 1, 'money'=> $bonusUsdt, 'date'=> date('Y-m-d'), 'actual_money'=> $bonusUsdt, 'balance'=> $bonusUserUsdt, 'create_time'=> time(), 'remark'=> '打赏推荐奖励', 'status'=> 1, 'mark'=> 1, ]; if(!AccountLogModel::insertGetId($log)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'主播推荐奖励明细处理失败'], 600); return false; } // 消息 MessageService::make()->pushMessage($userId, lang('打赏推荐奖励通知'), lang('打赏推荐奖励通知内容',['time'=> $dateTime,'usdt'=>$bonusUsdt,'live_name'=>$liveName]),3); $realUsdt = moneyFormat($usdt - $bonusUsdt, 4); } // 被打赏主播账户USDT账户进账 $updateData = ['usdt'=>DB::raw("usdt + {$realUsdt}"),'update_time'=>time()]; if(!MemberModel::where(['id'=> $liveUserId,'mark'=>1])->update($updateData)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'打赏进账处理失败'], 600); return false; } // 明细 $log = [ 'user_id'=> $liveUserId, 'source_id'=> $liveId, 'source_order_no'=> $orderNo, 'user_type'=> 1, 'type'=> 1, 'coin_type'=> 1, 'money'=> $realUsdt, 'date'=> date('Y-m-d'), 'actual_money'=> $realUsdt, 'balance'=> $liveUserUsdt, 'create_time'=> time(), 'remark'=> '礼物打赏收入', 'status'=> 1, 'mark'=> 1, ]; if(!AccountLogModel::insertGetId($log)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'打赏进账明细处理失败'], 600); return false; } // TODO 浏览用户更新打赏统计 if(!VideoCollectService::make()->getCollectCacheInfo($userId, $liveId, 1,2)){ $data = [ 'user_id'=> $userId, 'type'=> 1, 'source_type'=> 2, 'collect_id'=> $liveId, 'reward_total'=> $money, 'category_id'=> isset($liveInfo['category'])? $liveInfo['category'] : 0, 'collect_uid'=> isset($liveInfo['user_id'])? $liveInfo['user_id'] : 0, 'create_time'=> time(), 'status'=> 1, ]; if(!VideoCollectModel::insertGetId($data)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'更新打赏统计失败'], 600); return false; } RedisService::set("caches:videos:collect:temp_{$userId}_{$liveId}_1_2", $data, rand(10,30)); RedisService::clear("caches:videos:recommend:{$userId}_1_2"); RedisService::clear("caches:member:fans:{$userId}_{$liveId}_2"); RedisService::clear("caches:live:users_{$liveId}_{$userId}"); } // 更新 else if (!VideoCollectModel::where(['user_id'=> $userId,'type'=>1,'source_type'=>2, 'collect_id'=>$liveId])->update(['reward_total'=> DB::raw("reward_total + {$money}"),'update_time'=>time()])){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'更新打赏统计失败1'], 600); return false; } // TODO 直播间打赏统计 $updateData = ['reward_total'=>DB::raw("reward_total + {$money}"),'update_time'=>time()]; if(!$this->checkUserReward($userId, $liveId) || $liveInfo['reward_num']<=0){ // 打赏人数统计 $updateData['reward_num'] = DB::raw("reward_num + 1"); } if(!$this->model->where(['id'=> $liveId])->update($updateData)){ DB::rollBack(); $this->error = 2305; RedisService::set("caches:live:reward:error_{$userId}", ['data'=> $order,'error'=>'更新打赏统计失败2'], 600); return false; } DB::commit(); // TODO 推送站内消息 MessageService::make()->pushMessage($userId, lang('打赏消费通知'), lang('打赏消费通知内容',['time'=> $dateTime,'money'=>$money,'live_name'=>$liveName]),3); MessageService::make()->pushMessage($liveUserId, lang('打赏通知'), lang('打赏通知内容',['time'=> $dateTime,'money'=>$money,'nickname'=>$userNickname]),3); // TODO 打赏直播任务 TaskService::make()->updateTask($userId,7, $liveId); $this->error = 2306; return ['id'=>$orderId]; } /** * 验证是否打赏过 * @param $userId 打赏用户ID * @param $liveId 直播间ID * @return array|mixed */ public function checkUserReward($userId, $liveId) { $cacheKey = "caches:live:reward:{$userId}_{$liveId}"; $data = RedisService::get($cacheKey); if($data || RedisService::exists($cacheKey)){ return $data; } $data = RewardOrderModel::where(['user_id'=> $userId,'live_id'=> $liveId])->value('id'); if($data){ RedisService::set($cacheKey, $data, rand(3600, 7200)); } return $data; } }