$courseId, 'uniacid' => UNIACID, 'validity_time' => ['>', time()] ]; if (!$isFreeCourse) { //课程当前非免费时,过滤掉通过【加入学习】获得的 get_type=free 的记录 $mustCondition['get_type'] = ['<>', 'free']; } //判断是否订阅(同时返回所有可能的命中记录,以便对 get_type=vip 做权益失效判断) $list = self::whereOr(function ($query) use ($mustCondition, $userInfo) { if( $userInfo['mobile']){ $query->where($mustCondition)->where('mobile', $userInfo['mobile']); }else{ $query->where($mustCondition)->where('mobile', '<>', null); } })->whereOr(function ($query) use ($mustCondition, $userInfo) { $query->where($mustCondition)->where(['user_id' => $userInfo['id']]); })->select(); if ($list) { //缓存 VIP 免费权益命中结果,避免多条记录重复计算 $vipFreeAccessChecked = false; $vipFreeAccess = false; foreach ($list as $item) { if ($item['get_type'] == 'vip') { //get_type=vip 的记录需当前用户 VIP 免费权益仍命中该课程才视为有效 if (!$vipFreeAccessChecked) { $vipFreeAccess = self::getVipFreeAccess($userId, $courseId); $vipFreeAccessChecked = true; } if (!$vipFreeAccess) { continue; } } return true; } } //判断是否已订阅该课程所属的专栏 $columnBindIds = ColumnBind::where([ 'uniacid' => UNIACID, 'course_id' => $courseId, 'status'=>1, 'type'=>1 //类型是课程才可用,目录不算 ])->field("column_id")->select(); $data = self::where([ 'user_id' => $userId, 'uniacid' => UNIACID, 'course_id' => ['in', array_column($columnBindIds, 'column_id')], 'validity_time' => ['>', time()] ])->find(); if ($data) { return true; } foreach ($columnBindIds as $column) { if (self::getSubscriptionAuth($userId, $column['column_id'])) { return true; } //判断是否在专栏内开启了试看 $isTry = ColumnBind::where([ 'uniacid' => UNIACID, 'course_id' => $courseId, 'column_id' => $column['column_id'], 'try' => 1 ])->field("try")->find(); if ($isTry) { return true; } } return false; } public function course() { return $this->belongsTo('\app\common\model\course\Course', 'course_id', 'id', [], 'LEFT')->setEagerlyType(0); } public function user() { return $this->belongsTo('\app\admin\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); } public function mobileuser() { return $this->belongsTo('\app\admin\model\User', 'mobile', 'mobile', [], 'LEFT')->setEagerlyType(0); } /** * 构建手机号搜索过滤参数 * 将 user.mobile 过滤条件从 filter/op 中提取并移除, * 以便在控制器中扩展为同时匹配 subscription.mobile 或 user.mobile * @param array $filter 过滤条件数组 * @param array $op 操作符数组 * @return array [手机号关键词, 修改后的filter, 修改后的op] */ public static function buildMobileFilterParams(array $filter, array $op) { $mobile = ''; if (isset($filter['user.mobile']) && $filter['user.mobile'] !== '') { $mobile = trim($filter['user.mobile']); unset($filter['user.mobile'], $op['user.mobile']); } return [$mobile, $filter, $op]; } /** * 获取课程列表 * @param $userId 分组ID * @param $params 分页信息 page页码 limit获取数量 * @return array */ public function getMyCourse($userId = 0, $params = []) { $userInfo = \app\common\model\User::getUserInfo($userId); if (!$userInfo) { return []; } $rows = self::with(['course']); $mustCondition = [ 'subscription.uniacid' => UNIACID, 'course.id'=>['<>', 'null'] ]; if ($params['type'] != 'all') { $mustCondition['course.type'] = $params['type']; } //判断是否订阅 //要同时查UserId与Mobile $list = $rows->whereOr(function ($query) use ($mustCondition, $userInfo) { if( $userInfo['mobile']){ $query->where($mustCondition)->where('mobile', $userInfo['mobile']); }else{ $query->where($mustCondition)->where('mobile', '<>', null); } // $query->where($mustCondition)->where('mobile', $userInfo['mobile'])->where('mobile', '<>', null); })->whereOr(function ($query) use ($mustCondition, $userInfo) { $query->where($mustCondition)->where(['user_id' => $userInfo['id']]); }) ->limit($params['limit'])->page($params['page']) ->order('createtime', 'desc') ->select(); $data = []; if (!empty($list)) { foreach ($list as $item) { //课程从免费变为收费/其他时,通过【加入学习】获得的订阅记录视为失效,不展示 if ($item['get_type'] == 'free' && !self::isFreeCourse($item['course_id'])) { continue; } //VIP 免费权益失效(卡过期/取消权益/范围调整)时,对应订阅记录视为失效,不展示 if ($item['get_type'] == 'vip' && !self::getVipFreeAccess($item['user_id'], $item['course_id'])) { continue; } $item->getRelation('course')->visible(['name', 'cover', 'type', 'id']); $data[] = $item; } } return $data; } /** * 获取有效时间 * @param array $courseInfo 课程信息 * @param int $historyTime 历史订阅的时间,如果是指定天数,那么在原有时长的基础上累加 * @param array|null $validityOverride 有效期覆盖配置(来自组合商品规格项),格式:['validity_type'=>int, 'validity_value'=>string] * @return int */ public function getValidityTime($courseInfo, $historyTime = 0, $validityOverride = null) { // 如果有覆盖配置(组合商品自定义有效期),优先使用 if ($validityOverride && !empty($validityOverride['validity_type'])) { $overrideType = intval($validityOverride['validity_type']); $overrideValue = $validityOverride['validity_value'] ?? ''; switch ($overrideType) { case 1: // 与商品原有效期一致,走课程自身逻辑 break; case 2: // 长期有效 return \app\common\constant\course\Subscription::VALIDITY_ETERNITY; case 3: // 固定天数 $days = intval($overrideValue); if ($historyTime && $historyTime > time()) { return $historyTime + ($days * 86400); } return time() + ($days * 86400); case 4: // 固定截止日期 return is_numeric($overrideValue) ? intval($overrideValue) : strtotime($overrideValue); } } // 默认使用课程自身的有效期配置 if ($courseInfo['validity_type'] == 'long') { $validityTime = \app\common\constant\course\Subscription::VALIDITY_ETERNITY; } elseif ($courseInfo['validity_type'] == 'fix') { $validityTime = $courseInfo['validity_fix_time']; } else { if ($historyTime && $historyTime > time()) { $validityTime = $historyTime + ($courseInfo['validity_diy_time'] * 86400); } else { $validityTime = time() + ($courseInfo['validity_diy_time'] * 86400); } } return $validityTime; } /** * 增加用户购买课程 * @param int $userId 用户ID * @param int $courseId 课程ID * @param string $getType 获取方式 * @param string $mobile 手机号 * @param array|null $validityOverride 有效期覆盖配置(来自组合商品规格项),格式:['validity_type'=>int, 'validity_value'=>string] * @return void */ public function setUserCourse($userId, $courseId, $getType = '',$mobile='', $validityOverride = null) { $courseInfo = CourseModel::where([ 'id' => $courseId, 'status' => 1, 'uniacid' => UNIACID ])->find(); if (!$courseInfo) { return false; } if (!$getType) { $getType = $courseInfo['pay_type']; } $row = [ 'user_id' => $userId, 'mobile'=>$mobile, 'course_id' => $courseId, 'uniacid' => UNIACID, 'get_type' => $getType, 'createtime' => time() ]; $subInfo = $this->getSubscriptionInfo($userId,$mobile,$courseId); if ($subInfo) { $row['validity_time'] = $this->getValidityTime($courseInfo, $subInfo['validity_time'], $validityOverride); $where = [ 'course_id' => $courseId ]; if($userId){ $where['user_id'] = $userId; }else{ $where['mobile'] = $mobile; } self::where($where)->update($row); } else { $row['validity_time'] = $this->getValidityTime($courseInfo, 0, $validityOverride); self::insert($row); } if ($row['validity_time'] < time()) { return false; } return true; } /** * 获取订阅信息 * @param $userId * @param $mobile * @param $courseId * @return mixed */ protected function getSubscriptionInfo($userId,$mobile,$courseId){ $subInfo = false; if($mobile){ //手机号订阅 //判断有没有订阅过 $subInfo = self::where([ 'mobile' => $mobile, 'course_id' => $courseId ])->find(); if(!$subInfo){ $userInfo = \app\common\model\User::where([ 'mobile'=>$mobile ])->find(); if($userInfo){ $userId = $userInfo['id']; //判断有没有订阅过 $subInfo = self::where([ 'user_id' => $userId, 'course_id' => $courseId ])->find(); } } }else{ //判断有没有订阅过 $subInfo = self::where([ 'user_id' => $userId, 'course_id' => $courseId ])->find(); } return $subInfo; } /** * 取消用户订阅项目 * @param $userId * @param $goodsId * @param $goodsType * @return void */ public static function cancelUserSubscription($userId, $goodsId, $goodsType,$extendData='') { switch ($goodsType) { case 'activity': \app\api\model\app\activity\UserTicket::where([ 'uniacid'=>UNIACID, 'user_id'=>$userId, 'activity_id'=>$goodsId, 'ticket_no'=>$extendData ])->update([ 'status'=>2 ]); break; case 'course': case 'column': self::where([ 'user_id'=>$userId, 'course_id'=>$goodsId, 'uniacid'=>UNIACID ])->update([ 'validity_time'=>time() ]); break; case 'exercises': \app\common\model\app\exam\ExercisesSubscribe::where([ 'user_id'=>$userId, 'exercises_id'=>$goodsId, 'uniacid'=>UNIACID ])->delete(); break; case 'vipcard': $extendData = explode("_",$extendData); if(!$extendData){ return false; } $cardUserData = \app\common\model\app\vip\CardUser::where([ 'user_id'=>$userId, 'card_id'=>$goodsId, 'uniacid'=>UNIACID ])->find(); if(!$cardUserData){ return false; } $vipTime = $extendData[1] * 86400; $endtime = ($cardUserData['end_time'] - $vipTime) > time() ? time() + ($cardUserData['end_time'] - $vipTime) : time(); \app\common\model\app\vip\CardUser::where([ 'id'=>$cardUserData['id'], 'uniacid'=>UNIACID ])->update([ 'updatetime'=>time(), 'end_time'=>$endtime ]); break; } return true; } }