CourseService.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2017~2021 LARAVEL研发中心
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://www.laravel.cn
  8. // +----------------------------------------------------------------------
  9. // | Author: laravel开发员 <laravel.qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace App\Services\Api;
  12. use App\Models\ExamAccessLogModel;
  13. use App\Models\VideoCategoryModel;
  14. use App\Models\VideoCoursesModel;
  15. use App\Models\VideoLearnLogModel;
  16. use App\Models\VideoModel;
  17. use App\Services\BaseService;
  18. use App\Services\ConfigService;
  19. use App\Services\RedisService;
  20. /**
  21. * 视频课服务-服务类
  22. * @author laravel开发员
  23. * @since 2020/11/11
  24. * @package App\Services\Api
  25. */
  26. class CourseService extends BaseService
  27. {
  28. // 静态对象
  29. protected static $instance = null;
  30. /**
  31. * 构造函数
  32. * @author laravel开发员
  33. * @since 2020/11/11
  34. */
  35. public function __construct()
  36. {
  37. $this->model = new VideoCoursesModel();
  38. }
  39. /**
  40. * 静态入口
  41. */
  42. public static function make()
  43. {
  44. if (!self::$instance) {
  45. self::$instance = new static();
  46. }
  47. return self::$instance;
  48. }
  49. /**
  50. * 获取
  51. * @param $type
  52. * @param int $num
  53. * @return array|mixed
  54. */
  55. public function getListByType($type, $num = 0)
  56. {
  57. $num = $num? $num : \App\Services\ConfigService::make()->getConfigByCode('show_course_num', 4);
  58. $cacheKey = "caches:videos:type_list_{$type}_{$num}";
  59. $datas = RedisService::get($cacheKey);
  60. if($datas){
  61. return $datas;
  62. }
  63. $datas = VideoCoursesModel::from('videos_courses as a')
  64. ->leftJoin('videos as b',function($join) use($type){
  65. $join->on('a.video_id','=','b.id')->where('b.type', $type);
  66. })
  67. ->where(['b.status'=>1,'b.mark'=>1,'a.status'=>1,'a.mark'=>1])
  68. ->select(['a.id','a.course_name','a.poster','b.type','a.video_id','a.course_url','a.fee','a.description','a.is_recommend','a.status'])
  69. ->orderBy('a.is_recommend','desc')
  70. ->orderBy('a.create_time','desc')
  71. ->limit($num)
  72. ->get();
  73. $datas = $datas? $datas->toArray() : [];
  74. if($datas){
  75. foreach ($datas as &$item){
  76. $item['poster'] = $item['poster'] ? get_image_url($item['poster']) : '';
  77. $item['poster_error'] = 0;
  78. }
  79. RedisService::set($cacheKey, $datas, rand(3600, 7200));
  80. }
  81. return $datas;
  82. }
  83. /**
  84. * 获取视频集
  85. * @param int
  86. * @return array|mixed
  87. */
  88. public function getVideoList($params,$pageSize)
  89. {
  90. $page = isset($params['page'])? $params['page'] : 1;
  91. $type = isset($params['type'])? $params['type'] : 0;
  92. $sc = isset($params['sc'])? $params['sc'] : 0;
  93. $cacheKey = "caches:videos:list_by_all:{$page}_{$pageSize}_{$type}".md5(json_encode($params));
  94. $datas = RedisService::get($cacheKey);
  95. // 视频课访问次数统计
  96. if(empty($sc)){
  97. ExamAccessLogModel::saveLog(date('Y-m-d'), $type, 20);
  98. }
  99. if($datas){
  100. return $datas;
  101. }
  102. $list = VideoModel::where(['type'=>$type,'status'=>1,'mark'=>1])
  103. ->select(['id','video_name','poster','type','description','is_recommend','status'])
  104. ->orderBy('sort','desc')
  105. ->orderBy('create_time','desc')
  106. ->paginate($pageSize > 0 ? $pageSize : 9999999);
  107. $list = $list? $list->toArray() :[];
  108. $rows = isset($list['data'])? $list['data'] : [];
  109. $datas = [
  110. 'pageSize'=> $pageSize,
  111. 'total'=> isset($list['total'])? $list['total'] : 0,
  112. 'list'=> $rows
  113. ];
  114. if($rows){
  115. RedisService::set($cacheKey, $datas, rand(300,600));
  116. }
  117. return $datas;
  118. }
  119. /**
  120. * 获取分类下视频课
  121. * @param int
  122. * @return array|mixed
  123. */
  124. public function getListByCate($params)
  125. {
  126. $type = isset($params['type'])? $params['type'] : 0;
  127. $sc = isset($params['sc'])? $params['sc'] : 0;
  128. $cacheKey = "caches:videos:list_by_cate:{$type}_".md5(json_encode($params));
  129. $datas = RedisService::get($cacheKey);
  130. // 视频课访问次数统计
  131. if(empty($sc)){
  132. ExamAccessLogModel::saveLog(date('Y-m-d'), $type, 20);
  133. }
  134. if($datas){
  135. return $datas;
  136. }
  137. //
  138. $datas = VideoCategoryModel::with(['courses'=>function($query) use($params){
  139. $kw = isset($params['keyword'])? trim($params['keyword']) : '';
  140. if($kw){
  141. $query->where(function($query) use($kw){
  142. $query->where('video_name',"like","%{$kw}%")
  143. ->orwhere('description',"like","%{$kw}%");
  144. });
  145. }
  146. $type = isset($params['type'])? intval($params['type']) : 0;
  147. if($type>0){
  148. $query->where('type',$type);
  149. }
  150. }])->distinct()
  151. ->from('videos_categorys as a')
  152. ->leftJoin('videos as b',function($join) use($params){
  153. $type = isset($params['type'])? intval($params['type']) : 0;
  154. $join->on('a.id','=','b.category_id')->where('b.type', $type);
  155. })
  156. ->where('b.id','>', 0)
  157. ->where(['a.status'=>1,'a.mark'=>1])
  158. ->select(['a.id','a.name','a.sort','a.icon'])
  159. ->orderBy('a.sort','desc')
  160. ->get();
  161. $datas = $datas? $datas->toArray() : [];
  162. if($datas){
  163. foreach ($datas as &$item){
  164. $item['icon'] = $item['icon']? get_image_url($item['icon']) : '';
  165. }
  166. RedisService::set($cacheKey, $datas, rand(300,600));
  167. }
  168. return $datas;
  169. }
  170. /**
  171. * 获取视频集下课程列表
  172. * @param int
  173. * @return array|mixed
  174. */
  175. public function getListByGroup($groupId,$params,$pageSize)
  176. {
  177. $page = isset($params['page'])? $params['page'] : 1;
  178. $type = isset($params['type'])? $params['type'] : 0;
  179. $cid = isset($params['cid'])? $params['cid'] : 0;
  180. $sc = isset($params['sc'])? $params['sc'] : 0;
  181. $cacheKey = "caches:videos:list_by_group:{$groupId}_{$page}_{$pageSize}_{$type}".md5(json_encode($params));
  182. $datas = RedisService::get($cacheKey);
  183. // 视频课访问次数统计
  184. if(empty($sc)){
  185. ExamAccessLogModel::saveLog(date('Y-m-d'), $type, 20);
  186. }
  187. if($datas){
  188. return $datas;
  189. }
  190. $list = $this->model->leftJoin('videos as b','b.id','=','videos_courses.video_id')
  191. ->with(['vip'])
  192. ->where(['videos_courses.video_id'=> $groupId,'videos_courses.status'=>1,'videos_courses.mark'=>1,'b.status'=>1,'b.mark'=>1])
  193. ->where(function($query) use($params){
  194. $kw = isset($params['keyword'])? trim($params['keyword']) : '';
  195. if($kw){
  196. $query->where('videos_courses.course_name',"like","%{$kw}%")
  197. ->orwhere('videos_courses.description',"like","%{$kw}%");
  198. }
  199. })
  200. ->where(function($query) use($cid){
  201. if($cid){
  202. $query->whereNotIn('videos_courses.id', [$cid]);
  203. }
  204. })
  205. ->select(['videos_courses.id','videos_courses.video_id','videos_courses.course_name','videos_courses.course_url','videos_courses.fee','videos_courses.poster','videos_courses.description','videos_courses.sort'])
  206. ->withCount(['courses','learns'])
  207. ->orderBy('videos_courses.sort','desc')
  208. ->orderBy('videos_courses.id','asc')
  209. ->paginate($pageSize > 0 ? $pageSize : 9999999);
  210. $list = $list? $list->toArray() :[];
  211. if($list){
  212. foreach ($list['data'] as &$item){
  213. }
  214. }
  215. $rows = isset($list['data'])? $list['data'] : [];
  216. $datas = [
  217. 'pageSize'=> $pageSize,
  218. 'total'=> isset($list['total'])? $list['total'] : 0,
  219. 'list'=> $rows
  220. ];
  221. if($rows){
  222. RedisService::set($cacheKey, $datas, rand(300,600));
  223. }
  224. return $datas;
  225. }
  226. /**
  227. * 视频课详情
  228. * @param $userId 用户
  229. * @param $id 课程ID
  230. * @return array|mixed
  231. */
  232. public function getInfo($userId, $id)
  233. {
  234. $cacheKey = "caches:videos:info_{$userId}_{$id}";
  235. $data = RedisService::get($cacheKey, $cacheKey);
  236. if($data){
  237. return $data;
  238. }
  239. $data = $this->model->from('videos_courses as videos_courses')
  240. ->leftJoin('videos as b','b.id','=','videos_courses.video_id')
  241. ->leftJoin('videos_learn_logs as c',function($join) use($userId){
  242. $join->on('c.course_id','=','videos_courses.id')
  243. ->where(['c.user_id'=>$userId,'c.status'=>1,'c.mark'=>1]);
  244. })
  245. ->with(['vip'])
  246. ->where(['videos_courses.id'=>$id,'videos_courses.status'=>1,'videos_courses.mark'=>1])
  247. ->select(['videos_courses.id','videos_courses.video_id','videos_courses.course_name','videos_courses.course_url','videos_courses.fee','videos_courses.poster','videos_courses.description','videos_courses.sort','c.id as learn_id','b.type'])
  248. ->first();
  249. $data = $data? $data->toArray() : [];
  250. if($data){
  251. // 验证付费视频是否有播放权限
  252. $fee = isset($data['fee'])? $data['fee'] : 0;
  253. $data['learn_id'] = !empty($data['learn_id'])? $data['learn_id'] : 0;
  254. $buyVipData = isset($data['vip'])? $data['vip'] : [];
  255. $data['buy_vip'] = $buyVipData? 1 : 0;
  256. $data['can_play'] = $buyVipData || $fee<=0? 1 : 0;
  257. $data['preview_time'] = ConfigService::make()->getConfigByCode('course_play_preview_time', 3);
  258. $data['courses_count'] = $this->model->where(['video_id'=> $data['video_id'],'status'=>1,'mark'=>1])->count('id');
  259. $data['learns_count'] = VideoLearnLogModel::where(['video_id'=> $data['video_id'],'status'=>1,'mark'=>1])->count('id');
  260. // 播放
  261. RedisService::set($cacheKey, $data, rand(5, 10));
  262. }
  263. $this->model->where(['id'=> $id])->increment('views', 1);
  264. return $data;
  265. }
  266. /**
  267. * 课程学习记录
  268. * @param $userId 用户
  269. * @param $id 课程ID
  270. * @return array|false|mixed
  271. */
  272. public function learn($userId, $id)
  273. {
  274. $cacheKey = "caches:videos:learn_{$userId}_{$id}";
  275. $data = RedisService::get($cacheKey, $cacheKey);
  276. if($data){
  277. $this->error = '课程已学习过';
  278. return $data;
  279. }
  280. $info = $this->model->where(['id'=> $id,'mark'=>1])->select(['id','video_id'])->first();
  281. $videoId = isset($info['video_id'])? $info['video_id'] : 0;
  282. if(empty($info)){
  283. $this->error = '课程不存在';
  284. return false;
  285. }
  286. if($videoId<=0){
  287. $this->error = '课程集参数错误';
  288. return false;
  289. }
  290. // 是否已有学习记录
  291. if($learnInfo = VideoLearnLogModel::where(['user_id'=>$userId,'course_id'=>$id,'video_id'=>$videoId])->select(['id','user_id','course_id','video_id'])->first()){
  292. RedisService::set($cacheKey, $learnInfo->toArray(), rand(300, 600));
  293. $this->error = '课程已学习过';
  294. return false;
  295. }
  296. $data = ['user_id'=> $userId,'course_id'=>$id,'video_id'=>$videoId,'create_time'=>time(),'status'=>1,'mark'=>1];
  297. $data['id'] = VideoLearnLogModel::insertGetId($data);
  298. RedisService::set($cacheKey, $data, rand(300, 600));
  299. $this->error = '课程学习完成';
  300. return false;
  301. }
  302. }