getBookModel()->BookInfo($book_id); //容灾处理,如果计费出现问题在后台配置read_free=true则所有书籍都免费 if( Config::get("site.free") ){ return -1; } //此书设置了免费时长并且当前时间在免费时长内则免费阅读 if (intval($book_info['free_stime']) < time() && intval($book_info['free_etime']) > time()) { return -1; } //此书为增购限免 $user_id = UserService::instance()->getUserInfo()->id; $books = BuyMoreService::instance()->getFreeLimitCacheBook($user_id)->data; if (array_key_exists($book_id, $books)) { LogService::info('PAYMOREFREELIMIT:' . $book_id . ':' . date('Y-m-d H:i:s', $books[$book_id])); return -1; } $adminConfig = UserService::instance()->getRunTimeObject()->adminConfig; $default = ArrayHelper::array_get($adminConfig, 'book_chapter_charge_start'); $book = model('BookChargeSetting')->getInfo($adminConfig['admin_id'], $book_id); if ($book && $book['chapter_charge_start']) { return $book['chapter_charge_start'] - 1; } elseif ($default) { return $default - 1; } //如果本书籍设置了免费章节数,以本书籍的免费章节数为准 if( intval($book_info['free_chapter_num']) > 0 ){ return $book_info['free_chapter_num']; } //全局免费章节数 if (config('site.book_free_chapter_num') >= 0) { return intval(config('site.book_free_chapter_num')); } return -1; } /** * @param $book_id * @param $chapter_id * @return \app\main\model\object\ReturnObject */ public function getConsumeInfo($book_id, $chapter_id, $chapter_name = '') { try { $user_info = UserService::instance()->getUserInfo(); //vip用户,全部章节免费 if ($user_info->vip_endtime > time()) { // 针对VIP用户判断是否是CP书籍,免费不用做消费打点,直接返回 if (VipCpService::instance()->isFreeCpBookForVip($book_id, $user_info->id, $user_info->vip_endtime)) { return $this->setData($user_info->is_black)->getReturn(); } } $book_info = BookService::instance()->getBookModel()->BookInfo($book_id); //获取消费记录 $consume_map = ['user_id' => $user_info->id, 'book_id' => $book_id]; if (intval($book_info['billing_type']) == BookConstants::BOOK_BILLING_MODEL_BOOK && intval($book_info['price']) > 0) { $consume_map['type'] = BookConstants::BOOK_BILLING_MODEL_BOOK; } else { //按章收费 $consume_map['type'] = BookConstants::BOOK_BILLING_MODEL_CHAPTER; $consume_map['chapter_id'] = $chapter_id; } //当前章节已付过费 if ($data = $this->getConsumeModel()->setConnect($user_info->id)->where($consume_map)->find()) { return $this->setData($data['dd_kandian'] + $data['dd_free_kandian'])->getReturn(); } //全局每章节书币数 $kandian = $this->getChapterPrice($book_id); //增购-折扣 $disCache = CacheConstants::getBuyMoreDiscountCache($user_info->id); $discount = Redis::instance()->zRangeByScore($disCache, time(), '+inf', ['limit' => 1]); if ($discount) { $diss = explode(':', $discount[0]); $kandian = ceil($kandian * $diss[1]); LogService::info('PAYMOREDISCOUNT:' . $discount[0]); } //检查用户看点是否足够,不够时跳转支付页面 UserDdFlushService::instance()->checkUserFlushState($user_info->id); $totalKandian = FinancialService::instance()->getTotalKandianAndFreeKandian($user_info->id)->data; if ($totalKandian < $kandian) { return $this->setCode(ErrorCodeConstants::REDIRECT)->getReturn(); } //当前章节已进入消费流程 $cacheConsume = CacheConstants::getConsumeCacheChapter($user_info->id, $chapter_id); if (Redis::instance()->setnx($cacheConsume, 1)){ Redis::instance()->expire($cacheConsume, 2); } else { LogService::info('并发请求,消费退出'); return $this->setData(true)->getReturn(); } //消费书币 $consume_return = $this->payKandian($kandian, $user_info->id); if ($consume_return->code != ErrorCodeConstants::SUCCESS) { return $consume_return; } // region 用户画像打点到Nginx $dotdata = [ 'ctime' => time(), 'uid' => $user_info->id, 'openid' => $user_info->openid, 'unionid' => '', 'ip' => Ip::ip(), 'country' => Ip::country(Ip::ip()), 'province' => Ip::province(Ip::ip()), 'city' => Ip::city(Ip::ip()), 'pline' => empty(Config::get('site.theme')) ? 'xg' : Config::get('site.theme'), 'agent_id' => $user_info->agent_id ?? '', 'chid' => $user_info->channel_id ?? '', 'ua' => Ua::getUa(), 'cookie' => '', 'type' => 'consume', 'user' => [ 'gender' => $user_info->sex, 'is_subscribe' => $user_info->is_subscribe, 'vip_endtime' => $user_info->vip_endtime, 'subscribe_time' => $user_info->subscribe_time, 'is_pay' => $user_info->is_pay, 'referral_id' => $user_info->referral_id, 'ext' => $user_info->ext?json_decode($user_info->ext, true):'', ], 'content' => [ 'order_id' => '', 'book_id' => $book_id, 'book_sex' => $book_info['sex'], // 男频? 女频? 'book_category_id' => $book_info['book_category_id'], //书籍分类ID 'cz_consume' => $consume_return->data['kandian'], //消费充值看点 'zs_consume' => $consume_return->data['free_kandian'],//消费赠送看点 ] ]; curl_user_dot($dotdata); // endregion //判断此用户今天有没有在这本书扣过费,放入缓存 $book_today_is_consume = $this->checkIfConsume($book_id, $user_info->id); //插入消费记录 $consume_data = array_merge($consume_map, $consume_return->data, [ 'user_id' => $user_info->id, 'book_id' => $book_id, 'book_name' => $book_info['name'], 'chapter_id' => $chapter_id, 'chapter_name' => !empty($chapter_name) ? $chapter_name : $chapter_id, 'createtime' => time(), 'updatetime' => time(), 'extend1' => $book_id, 'extend2' => $chapter_id, ]); $this->getConsumeModel()->setConnect($user_info->id)->insert($consume_data); $freekandian = $consume_data['free_kandian'] ?? 0; $per_kandian = $consume_data['kandian'] ?? 0; $dd_free_kandian = $consume_data['dd_free_kandian'] ?? 0; $dd_per_kandian = $consume_data['dd_kandian'] ?? 0; //添加书籍消费统计,管理员 $this->bookConsumeCollect(0, $book_id, $book_today_is_consume, $kandian, $freekandian, $per_kandian); //添加书籍消费统计,渠道商 $this->bookConsumeCollect(UserService::instance()->getRunTimeObject()->adminId, $book_id, $book_today_is_consume, $kandian, $freekandian, $per_kandian); //支付成功 return $this->setData($dd_free_kandian + $dd_per_kandian)->getReturn(); } catch (\Exception $e) { LogService::exception($e); return $this->setCode(ErrorCodeConstants::EXCEPTION)->setData(false)->getReturn(); } } /** * 获取消费看点 * @param $kandian * @param $user_id * @return \app\main\model\object\ReturnObject */ public function payKandian($kandian, $user_id, $notes='') { //检查用户看点是否足够,不够时跳转支付页面 $user_total_free_kandian = $this->getTotalFreeKandian($user_id)->data; $consume_data = [ 'free_kandian' => 0, 'dd_free_kandian' => 0, 'kandian' => 0, 'dd_kandian' => 0, ]; $info = []; $order = []; //扣除用户看点 if ($user_total_free_kandian >= $kandian) { $data = $this->reduceFreeKandian($user_id, $kandian, [], $notes); if ($data->code != ErrorCodeConstants::SUCCESS) { return $data; } $consume_data['dd_free_kandian'] = $data->data[OrderContents::RECHARGE_DD_YES] ?? 0; $consume_data['free_kandian'] = $data->data[OrderContents::RECHARGE_DD_NO] ?? 0; while (!empty($data->data['json'])) { $recharge = array_pop($data->data['json']); $info[] = [ 'id' => $recharge['recharge_id'], 'kandian' => 0, 'free_kandian' => $recharge['free_kandian'], 'dd_kandian' => 0, 'dd_free_kandian' => $recharge['dd'] == 1 ? $recharge['free_kandian'] : 0, ]; if ($recharge['orders_id']) { $order[] = $recharge['orders_id']; } unset($recharge); } } else { if ($user_total_free_kandian) { $freedata = $this->reduceFreeKandian($user_id, $user_total_free_kandian, [], $notes); if ($freedata->code != ErrorCodeConstants::SUCCESS) { return $freedata; } else { $consume_data['dd_free_kandian'] = $freedata->data[OrderContents::RECHARGE_DD_YES] ?? 0; $consume_data['free_kandian'] = $freedata->data[OrderContents::RECHARGE_DD_NO] ?? 0; while (!empty($freedata->data['json'])) { $recharge = array_pop($freedata->data['json']); $info[] = [ 'id' => $recharge['recharge_id'], 'kandian' => 0, 'free_kandian' => $recharge['free_kandian'], 'dd_kandian' => 0, 'dd_free_kandian' => $recharge['dd'] == 1 ? $recharge['free_kandian'] : 0, ]; if ($recharge['orders_id']) { $order[] = $recharge['orders_id']; } unset($recharge); } } } //新的扣永久书币逻辑 UserDdFlushService::instance()->checkUserFlushState($user_id); $data = $this->reduceKandian($user_id, $kandian - $user_total_free_kandian, [], $notes); if ($data->code != ErrorCodeConstants::SUCCESS) { return $data; } $consume_data['dd_kandian'] = $data->data[OrderContents::RECHARGE_DD_YES] ?? 0; $consume_data['kandian'] = $data->data[OrderContents::RECHARGE_DD_NO] ?? 0; while (!empty($data->data['json'])) { $recharge = array_pop($data->data['json']); $info[] = [ 'id' => $recharge['recharge_id'], 'kandian' => $recharge['kandian'], 'free_kandian' => 0, 'dd_kandian' => $recharge['dd'] == 1 ? $recharge['kandian'] : 0, 'dd_free_kandian' => 0, ]; if ($recharge['orders_id']) { $order[] = $recharge['orders_id']; } unset($recharge); } } /* $consume_data['consume_info'] = json_encode([ 'recharge' => $info, 'orders_id' => array_unique($order), ]);*/ $consume_data['free_kandian'] += $consume_data['dd_free_kandian']; $consume_data['kandian'] += $consume_data['dd_kandian']; return $this->setData($consume_data)->getReturn(); } /** * 获取章节书币 * @param $book_id * @return int|mixed */ public function getChapterPrice($book_id) { $book_info = BookService::instance()->getBookModel()->BookInfo($book_id); //全局每章节书币数 $chapter_price = config('site.book_chapter_price'); if (intval($book_info['billing_type']) == BookConstants::BOOK_BILLING_MODEL_BOOK) { $chapter_price = intval($book_info['price']); } elseif ($book_info['billing_type'] == BookConstants::BOOK_BILLING_MODEL_CHAPTER) { $adminConfig = UserService::instance()->getRunTimeObject()->adminConfig; $default = ArrayHelper::array_get($adminConfig, 'book_chapter_price'); $book = model('BookChargeSetting')->getInfo($adminConfig['admin_id'], $book_id); if ($book && $book['chapter_kandian']) { $chapter_price = $book['chapter_kandian']; } elseif ($default) { $chapter_price = $default; } elseif(intval($book_info['price'])) { $chapter_price = intval($book_info['price']); } } return $chapter_price; } /** * 检测用户今天有没有在这本书扣过费 * @param $book_id * @return bool * @throws \Exception * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public function checkIfConsume($book_id, $user_id) { $book_today_is_consume = false; $everyDayKey = CacheConstants::getUserIsConsumeBookByDayKey($book_id, $user_id); if (Redis::instance()->exists($everyDayKey)) { $book_today_is_consume = true; } else { $beginToday = mktime(0, 0, 0, date('m'), date('d'), date('Y')); $consumeToday = $this->getConsumeModel()->where(['user_id' => $user_id, 'book_id' => $book_id])->order('id', 'desc')->find(); if (isset($consumeToday['updatetime']) && $consumeToday['updatetime'] > $beginToday) { Redis::instance()->setex($everyDayKey, 86400, '1'); $book_today_is_consume = true; } } return $book_today_is_consume; } /** * 书籍计费统计 * @param $channel_id * @param $book_id * @param $isConsumeToday * @param $count_kandian * @param $free_kandian * @param $recharge_kandian * @throws \Exception */ private function bookConsumeCollect($channel_id,$book_id,$isConsumeToday,$count_kandian,$free_kandian,$recharge_kandian){ $redis = Redis::instance(); //设置渠道籍列表 $collect_list_key = "BC-CL:".date("Ymd"); $redis->sadd($collect_list_key,$channel_id); $redis->expire($collect_list_key,86400*2); //设置渠道商书籍列表 $collect_channel_book_key = "BC-BL:{$channel_id}:".date("Ymd"); $redis->sadd($collect_channel_book_key,$book_id); $redis->expire($collect_channel_book_key,86400*2); //设置渠道商对应书籍数据 $collect_key = "BC:{$book_id}:{$channel_id}:".date("Ymd"); if($data = json_decode($redis->get($collect_key),true)){ if(!$isConsumeToday){ $data['spending_users'] = intval($data['spending_users'] ?? 0)+1; } $data['spending_num'] = intval($data['spending_num'] ?? 0)+1; $data['spending_count_kandian'] = intval($data['spending_count_kandian'] ?? 0)+$count_kandian; $data['spending_free_kandian'] = intval($data['spending_free_kandian'] ?? 0)+$free_kandian; $data['spending_recharge_kandian'] = intval($data['spending_recharge_kandian'] ?? 0)+$recharge_kandian; $redis->setex($collect_key,86400*2,json_encode($data)); }else{ $data['spending_users'] = 1; $data['spending_num'] = 1; $data['spending_count_kandian'] = $count_kandian; $data['spending_free_kandian'] = $free_kandian; $data['spending_recharge_kandian'] = $recharge_kandian; $redis->setex($collect_key,86400*2,json_encode($data)); } } /** * @param $userInfo * @param $bookInfo * @param $chapterInfo * @return \app\main\model\object\ReturnObject * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public function feeChapterForClient($userInfo, $bookInfo, $chapterInfo) { $bookId = $bookInfo['id']; $chapterId = $chapterInfo['id']; //容灾处理,如果计费出现问题在后台配置read_free=true则所有书籍都免费 if (Config::get("site.free")) { return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_FREE, 'state' => ClientApiConstants::CHAPTER_STATE_FINISH ])->getReturn(); } //此书设置了免费时长并且当前时间在免费时长内则免费阅读 if (intval($bookInfo['free_stime']) < time() && intval($bookInfo['free_etime']) > time()) { return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_LIMIT_FREE, 'state' => ClientApiConstants::CHAPTER_STATE_FINISH ])->getReturn(); } //如果本书籍设置了免费章节数,以本书籍的免费章节数为准 $chapterIdx = intval($bookInfo['free_chapter_num']); if ($chapterIdx > 0 && $chapterInfo['idx'] <= $chapterIdx) { return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_FREE, 'state' => ClientApiConstants::CHAPTER_STATE_FINISH ])->getReturn(); } //全局免费章节数 $siteBookFreeChapterNum = config('site.book_free_chapter_num'); if ($siteBookFreeChapterNum >= 0 && $chapterInfo['idx'] <= $siteBookFreeChapterNum) { return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_FREE, 'state' => ClientApiConstants::CHAPTER_STATE_FINISH ])->getReturn(); } if (empty($userInfo)) { return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_NORMAL, 'state' => ClientApiConstants::CHAPTER_STATE_NEED_LOGIN ])->getReturn(); } $userId = $userInfo['id']; //vip用户,全部章节免费 if ($userInfo['vip_endtime'] > time()) { return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_FREE, 'state' => ClientApiConstants::CHAPTER_STATE_FINISH ])->getReturn(); } //获取消费记录 $consumeMap = ['user_id' => $userId, 'book_id' => $bookId]; if (intval($bookInfo['billing_type']) == BookConstants::BOOK_BILLING_MODEL_BOOK && intval($bookInfo['price']) > 0) { $consumeMap['type'] = BookConstants::BOOK_BILLING_MODEL_BOOK; } else { //按章收费 $consumeMap['type'] = BookConstants::BOOK_BILLING_MODEL_CHAPTER; $consumeMap['chapter_id'] = $chapterId; } //当前章节已付过费 if ($this->getConsumeModel()->setConnect($userId)->where($consumeMap)->find()) { return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_CHARGED, 'state' => ClientApiConstants::CHAPTER_STATE_FINISH ])->getReturn(); } //全局每章节书币数 $chapterPrice = config('site.app_book_chapter_price'); if (intval($bookInfo['billing_type']) == BookConstants::BOOK_BILLING_MODEL_BOOK && intval($bookInfo['app_price']) > 0) { $chapterPrice = intval($bookInfo['app_price']); } elseif ($bookInfo['billing_type'] == BookConstants::BOOK_BILLING_MODEL_CHAPTER && intval($bookInfo['app_price']) > 0) { $chapterPrice = intval($bookInfo['app_price']); } //当前章节已进入消费流程 $cacheConsume = CacheConstants::getConsumeCacheChapter($userId, $chapterId); if (Redis::instance()->exists($cacheConsume)) { return $this->setData(true)->getReturn(); } $userTotalKandian = $this->getTotalKandianAndFreeKandian($userId)->data; if ($userTotalKandian < $chapterPrice) { return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_NORMAL, 'state' => ClientApiConstants::CHAPTER_STATE_BALANCE_NOT_ENOUGH, 'cost' => $chapterPrice, ])->getReturn(); } Redis::instance()->set($cacheConsume, 1, 2); //扣除用户看点 $consume_return = $this->payKandian($chapterPrice, $userId); if ($consume_return->code != ErrorCodeConstants::SUCCESS) { return $consume_return; } $userKandianData['kandian'] = $userInfo['kandian'] - $consume_return->data['kandian']; //减少用户书币 $userKandianUpdate = UserService::instance()->getUserModel()->setConnect($userId)->update($userKandianData, ['id' => $userId]); if (!$userKandianUpdate) { //todo 减用户书币失败 return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->getReturn(); } //更新redis $userKey = CacheConstants::getUserCacheKey($userId); if (Redis::instance()->exists($userKey)) { Redis::instance()->hmset($userKey, $userKandianData); Redis::instance()->expire($userKey, 86400); } //插入消费记录 //$consume_return->data['consume_info'] = isset($consume_return->data['consume_info'])?json_encode($consume_return->data['consume_info']):''; $consumeData = array_merge($consumeMap, $consume_return->data, [ 'user_id' => $userId, 'book_id' => $bookId, 'book_name' => $bookInfo['name'], 'chapter_id' => $chapterId, 'chapter_name' => $chapterInfo['name'], 'business_line' => 1, 'createtime' => time(), 'updatetime' => time(), ]); $this->getConsumeModel()->setConnect($userId)->insert($consumeData); $freeKandian = $consumeData['free_kandian'] ?? 0; $kandian = $consumeData['kandian'] ?? 0; $dotData = new DotBookCollectObject(); $dotData->channelId = $userInfo['channel_id']; $dotData->eventTime = Request::instance()->server('REQUEST_TIME'); $dotData->bookId = $bookId; $dotData->countKandian = $chapterPrice; $dotData->freeKandian = $freeKandian; $dotData->kandian = $kandian; $dotData->businessLine = PayConstants::BUSINESS_APP; $dot = BookDotService::instance()->generateBookData($dotData); BookDotService::instance()->dotBookInfo($dot); //支付成功 return $this->setData([ 'orderState' => ClientApiConstants::ORDER_CHAPTER_STATE_NORMAL, 'state' => ClientApiConstants::CHAPTER_STATE_FINISH, 'cost' => $chapterPrice, ])->getReturn(); } /** * 扣减免费书币 * @param $uid * @param $free_kandian * @param array $data * @return \app\main\model\object\ReturnObject */ public function reduceFreeKandian($uid, $free_kandian, $data = [], $notes = '') { $redis = Redis::instance(); $redisKey = CacheConstants::getFreeKandianUserRechargeListCacheKey($uid); $totalFree = $this->getTotalFreeKandian($uid)->data; if (!$totalFree) { return $this->setData($data)->getReturn(); } if ($zresult = $redis->zrangebyscore($redisKey, time(), '+inf', ['limit' => [0, 2]])) { if ($value = $redis->get($zresult[0])) { $recharge = json_decode($value, true); //充值KL字段缺失 if (!array_key_exists('dd', $recharge) || !array_key_exists('orders_id',$recharge)) { $setResult = $this->getFreeKandian($uid); if ($setResult->data) { return $this->reduceFreeKandian($uid, $free_kandian, $data, $notes); } else { return $this->setData($data)->getReturn(); } } //充值缓存可能会过期 } else { $setResult = $this->getFreeKandian($uid); if ($setResult->data) { return $this->reduceFreeKandian($uid, $free_kandian, $data, $notes); } else { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('免费书币扣减失败1')->getReturn(); } } } else { $setResult = $this->getFreeKandian($uid); if ($setResult->data) { return $this->reduceFreeKandian($uid, $free_kandian, $data, $notes); } else { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('免费书币扣减失败2')->getReturn(); } } $userKey = CacheConstants::getFreeKandianUserRechargeCacheKey($uid, $recharge['id']); if ($recharge['remain_free_kandian'] >= $free_kandian) { //如果此条记录够扣 $remain = $recharge['remain_free_kandian'] - $free_kandian; $updateArr = [ 'remain_free_kandian' => $remain, ]; /* if (!empty($notes)) { $notes .= '--扣除免费书币:'.$free_kandian; $updateArr['notes'] = ['exp', "concat(notes, '$notes')"]; }*/ $res = $this->getRechargeModel()->setConnect($uid)->update($updateArr, ['id' => $recharge['id']]); if ($res) { if ($remain > 0) { $rData = []; $rData['id'] = $recharge['id']; $rData['dd'] = $recharge['dd']; $rData['orders_id'] = $recharge['orders_id']; $rData['type'] = $recharge['type']; $rData['remain_free_kandian'] = $remain; $rData['free_endtime'] = $recharge['free_endtime']; $redis->del($userKey); $redis->setex($userKey, $recharge['free_endtime'] - time(), json_encode($rData, JSON_UNESCAPED_UNICODE)); } else { $redis->del($userKey); $redis->zrem($redisKey, $userKey); } $data = ArrayHelper::array_plus($data, [$recharge['dd'] => $free_kandian]); //为消费记录 准备数据 $data['json'][] = [ 'orders_id'=>$recharge['orders_id'], 'recharge_id'=>$recharge['id'], 'kandian' =>0, 'free_kandian'=>$free_kandian, 'dd' => $recharge['dd'], ]; return $this->setData($data)->getReturn(); } else { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('免费书币扣减失败3')->getReturn(); } } else { $updateArr = [ 'remain_free_kandian' => 0, ]; /* if (!empty($notes)) { $notes .= '--扣除免费书币:'.$recharge['remain_free_kandian']; $updateArr['notes'] = ['exp', "concat(notes, '$notes')"]; }*/ $res = $this->getRechargeModel()->setConnect($uid)->update($updateArr, ['id' => $recharge['id']]); if ($res) { //为消费记录 准备数据 $data['json'][] = [ 'orders_id' =>$recharge['orders_id'], 'recharge_id'=>$recharge['id'], 'kandian'=>0, 'free_kandian'=>$recharge['remain_free_kandian'], 'dd' => $recharge['dd'] ]; if (count($zresult) == 1) { $zresult = $redis->zrangebyscore($redisKey, '-inf', '+inf'); if ($zresult) { foreach ($zresult as $key) { Redis::instance()->del($key); } } $redis->del($redisKey); $data = ArrayHelper::array_plus($data, [$recharge['dd'] => $recharge['remain_free_kandian']]); return $this->setData($data)->getReturn(); } else { $redis->del($userKey); $redis->zrem($redisKey, $userKey); $data = ArrayHelper::array_plus($data, [$recharge['dd'] => $recharge['remain_free_kandian']]); return $this->reduceFreeKandian($uid, $free_kandian - $recharge['remain_free_kandian'], $data, $notes); } } else { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('免费书币扣减失败4')->getReturn(); } } } /** * 设置免费书币 * @param $uid * @return \app\main\model\object\ReturnObject */ public function getFreeKandian($uid) { $res = $this->getRechargeModel()->setConnect($uid) ->where('user_id', $uid) ->where('remain_free_kandian', '>', 0) ->where('free_endtime', '>', time()) ->order('free_endtime', 'desc') ->select(); $redisKey = CacheConstants::getFreeKandianUserRechargeListCacheKey($uid); $zresult = Redis::instance()->zrangebyscore($redisKey, '-inf', '+inf'); //得到未过期免费书币记录 Redis::instance()->del($redisKey); if ($zresult) { foreach ($zresult as $val) { Redis::instance()->del($val); } } $return = []; if ($res) { foreach ($res as $key => $val) { $rData = []; $rData['id'] = $val->id; $rData['remain_free_kandian'] = $val->remain_free_kandian; $rData['free_endtime'] = $val->free_endtime; $rData['dd'] = $val->dd; $rData['orders_id'] = $val->orders_id; $rData['type'] = $val->type; $rechargeCacheKey = CacheConstants::getFreeKandianUserRechargeCacheKey($uid, $val->id); Redis::instance()->setex($rechargeCacheKey, $val->free_endtime - time(), json_encode($rData, JSON_UNESCAPED_UNICODE)); Redis::instance()->zadd($redisKey, $val->free_endtime, $rechargeCacheKey); $return[] = $rData; LogService::info('AKDINFO:' . $rData['id'] . ':' . $rData['remain_free_kandian']); } Redis::instance()->expire($redisKey, 86400 * 5); return $this->setData($return)->getReturn(); } else { return $this->setData($return)->getReturn(); } } /** * 消费永久书币 * @param $uid * @param $kandian * @param $data * @return \app\main\model\object\ReturnObject */ public function reduceKandian($uid, $kandian, $data = [], $notes = '') { LogService::info("RDKDINFO:" . $kandian); $redis = Redis::instance(); $redisKey = CacheConstants::getKandianUserRechargeListCacheKey($uid); $total = $this->getTotalKandian($uid)->data; if (!$total) { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('永久书币不存在')->getReturn(); } if ($zresult = $redis->zrangebyscore($redisKey, '-inf', '+inf', ['limit' => [0, 2]])) { if ($value = $redis->get($zresult[0])) { $recharge = json_decode($value, true); } else { $setResult = $this->getKandian($uid); if ($setResult->data) { return $this->reduceKandian($uid, $kandian, $data, $notes); } else { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('永久书币扣减失败1')->getReturn(); } } } else { $setResult = $this->getKandian($uid); if ($setResult->data) { return $this->reduceKandian($uid, $kandian, $data, $notes); } else { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('永久书币扣减失败2')->getReturn(); } } $userKey = CacheConstants::getKandianUserRechargeCacheKey($uid, $recharge['id']); if ($recharge['remain_kandian'] >= $kandian) { //如果此条记录够扣 $remain = $recharge['remain_kandian'] - $kandian; $updateArr = [ 'remain_kandian' => $remain ]; /* if (!empty($notes)) { $notes .= '--扣除永久书币:'.$kandian; $updateArr['notes'] = ['exp', "concat(notes, '$notes')"]; }*/ $res = $this->getRechargeModel()->setConnect($uid)->update($updateArr, ['id' => $recharge['id']]); if ($remain > 0) { $rData = []; $rData['id'] = $recharge['id']; $rData['orders_id'] = $recharge['orders_id']; $rData['dd'] = $recharge['dd']; $rData['type'] = $recharge['type']; $rData['remain_kandian'] = $remain; $redis->del($userKey); $redis->set($userKey, json_encode($rData, JSON_UNESCAPED_UNICODE)); } else { $redis->del($userKey); $redis->zrem($redisKey, $userKey); } if ($res) { $data = ArrayHelper::array_plus($data, [$recharge['dd'] => $kandian]); //为消费记录准备数据 $data['json'][] = [ 'orders_id'=>$recharge['orders_id'], 'recharge_id'=>$recharge['id'], 'kandian'=>$kandian, 'free_kandian'=>0, 'dd' => $recharge['dd'], ]; return $this->setData($data)->getReturn(); } else { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('永久书币扣减失败3')->getReturn(); } } else { $updateArr = [ 'remain_kandian' => 0, ]; /* if (!empty($notes)) { $notes .= '--扣除永久书币:'.$recharge['remain_kandian']; $updateArr['notes'] = ['exp', "concat(notes, '$notes')"]; }*/ $res = $this->getRechargeModel()->setConnect($uid)->update($updateArr, ['id' => $recharge['id']]); if ($res) { //为消费记录准备数据 $data['json'][] = [ 'orders_id'=>$recharge['orders_id'], 'recharge_id'=>$recharge['id'], 'kandian'=>$recharge['remain_kandian'], 'free_kandian'=>0, 'dd' => $recharge['dd'] ]; if (count($zresult) == 1) { $zresult = $redis->zrangebyscore($redisKey, '-inf', '+inf'); if ($zresult) { foreach ($zresult as $key) { Redis::instance()->del($key); } } $redis->del($redisKey); $data = ArrayHelper::array_plus($data, [$recharge['dd'] => $recharge['remain_kandian']]); return $this->setData($data)->getReturn(); } else { $redis->del($userKey); $redis->zrem($redisKey, $userKey); $data = ArrayHelper::array_plus($data, [$recharge['dd'] => $recharge['remain_kandian']]); return $this->reduceKandian($uid, $kandian - $recharge['remain_kandian'], $data, $notes); } } else { return $this->setCode(ErrorCodeConstants::DB_ERROR_UPDATE)->setMsg('永久书币扣减失败4')->getReturn(); } } } /** * 缓存永久书币 * @param $uid * @return \app\main\model\object\ReturnObject */ public function getKandian($uid) { $res = $this->getRechargeModel()->setConnect($uid) ->where('user_id', $uid) ->where('remain_kandian', '>', 0) ->select(); $userKey = CacheConstants::getKandianUserRechargeListCacheKey($uid); $zresult = Redis::instance()->zrangebyscore($userKey, '-inf', '+inf'); Redis::instance()->del($userKey); if ($zresult) { foreach ($zresult as $val) { Redis::instance()->del($val); } } // dump($this->getRechargeModel()->setConnect($uid)->getLastSql());exit; $return = []; if ($res) { foreach ($res as $key => $val) { $rData = []; $rData['id'] = $val->id; $rData['orders_id'] = $val->orders_id; $rData['remain_kandian'] = $val->remain_kandian; $rData['dd'] = $val->dd; $rData['type'] = $val->type; $rechargeCache = CacheConstants::getKandianUserRechargeCacheKey($uid, $val->id); Redis::instance()->set($rechargeCache, json_encode($rData, JSON_UNESCAPED_UNICODE)); Redis::instance()->zadd($userKey, $val->id, $rechargeCache); Redis::instance()->expire($userKey, 86400 * 5); $return[] = $rData; } return $this->setData($return)->getReturn(); } else { return $this->setData($return)->getReturn(); } } /** * 得到用户总共的免费书币数 * @param $uid * @param $total * @return \app\main\model\object\ReturnObject */ public function getTotalFreeKandian($uid, $total = 1) { $totalKandian = 0; $redis = Redis::instance(); $redisKey = CacheConstants::getFreeKandianUserRechargeListCacheKey($uid); if ($zresult = $redis->zrangebyscore($redisKey, time(), '+inf')) { foreach ($zresult as $val) { if ($value = $redis->get($val)) { $recharge = json_decode($value, true); if (!array_key_exists('dd', $recharge)) { $this->getFreeKandian($uid); return $this->getTotalFreeKandian($uid); } if ($total) { LogService::info("KDINFO:" . $recharge['id'] . ':' . $recharge['remain_free_kandian']); $totalKandian += $recharge['remain_free_kandian']; } else { if ($recharge['dd'] == OrderContents::ORDER_DEDUCT_NO) { $totalKandian += $recharge['remain_free_kandian']; } } } else { $this->getFreeKandian($uid); return $this->getTotalFreeKandian($uid); } } } else { $res = $this->getFreeKandian($uid); if ($res->data) { return $this->getTotalFreeKandian($uid); } } LogService::info("freekandian:" . $totalKandian); return $this->setData($totalKandian)->getReturn(); } /** * 获取永久看点 * @param $uid * @param int $total * @return \app\main\model\object\ReturnObject */ public function getTotalKandian($uid, $total = 1) { $totalKandian = 0; $redis = Redis::instance(); $redisKey = CacheConstants::getKandianUserRechargeListCacheKey($uid); if ($zresult = $redis->zrangebyscore($redisKey, '-inf', '+inf')) { foreach ($zresult as $val) { if ($value = $redis->get($val)) { $recharge = json_decode($value, true); if (!array_key_exists('dd', $recharge) || !array_key_exists('orders_id', $recharge)) { $this->getKandian($uid); return $this->getTotalKandian($uid); } if ($total) { LogService::info("KDINFO:" . $recharge['id'] . ':' . $recharge['remain_kandian']); $totalKandian += $recharge['remain_kandian']; } else { if ($recharge['dd'] == OrderContents::ORDER_DEDUCT_NO) { $totalKandian += $recharge['remain_kandian']; } } } else { $this->getKandian($uid); return $this->getTotalKandian($uid); } } } else { $res = $this->getKandian($uid); if ($res->data) { return $this->getTotalKandian($uid); } } LogService::info('kandian:' . $totalKandian); return $this->setData($totalKandian)->getReturn(); } /** * @param $uid * @param $total int 1=总剩余 0=渠道剩余 * @return \app\main\model\object\ReturnObject */ public function getTotalRemainKandian($uid, $total = 1) { if ($total) { $rechargeTotal = model('recharge')->setConnect($uid)->where([ 'user_id' => $uid, ])->sum('kandian'); $consumeTotal = model('consume')->setConnect($uid)->where([ 'user_id' => $uid, ])->sum('kandian'); } else { $rechargeTotal = model('recharge')->setConnect($uid)->where([ 'user_id' => $uid, 'dd' => '0', ])->sum('kandian'); $consumeTotal = model('consume')->setConnect($uid)->where([ 'user_id' => $uid, ])->sum('kandian-dd_kandian'); } $remain = $rechargeTotal - $consumeTotal; if ($remain < 0) { $remain = 0; } return $this->setData($remain)->getReturn(); } /** * @param $uid * @param $total int 1=总剩余 0=渠道剩余 * @return \app\main\model\object\ReturnObject */ public function getTotalRemainFreeKandian($uid, $total = 1) { if ($total) { $rechargeTotal = model('recharge')->setConnect($uid)->where('free_endtime', 'gt', time())->where([ 'user_id' => $uid, ])->sum('free_kandian'); $consumeTotal = model('consume')->setConnect($uid)->where([ 'user_id' => $uid, ])->sum('free_kandian'); } else { $rechargeTotal = model('recharge')->setConnect($uid)->where('free_endtime', 'gt', time())->where([ 'user_id' => $uid, 'dd' => '0', ])->sum('free_kandian'); $consumeTotal = model('consume')->setConnect($uid)->where([ 'user_id' => $uid, ])->sum('free_kandian-dd_free_kandian'); } $remain = $rechargeTotal - $consumeTotal; if ($remain < 0) { $remain = 0; } return $this->setData($remain)->getReturn(); } /** * @param $uid * @param $total int 1=总剩余 0=渠道剩余 * @return \app\main\model\object\ReturnObject */ public function getTotalRemainOldFreeKandian($uid, $total = 1) { if ($total) { $remain = model('recharge')->setConnect($uid)->where('free_endtime', 'lt', time())->where([ 'user_id' => $uid, ])->sum('remain_free_kandian'); } else { $remain = model('recharge')->setConnect($uid)->where('free_endtime', 'lt', time())->where([ 'user_id' => $uid, 'dd' => '0', ])->sum('remain_free_kandian'); } return $this->setData($remain)->getReturn(); } /** * 修改用户书币 * @param $id * @param $type * @param $edit_type * @param int $kandian * @param int $free_kandian * @param string $notes * @return \app\main\model\object\ReturnObject */ public function modifyUserKandian($id, $type, $edit_type, $kandian = 0, $free_kandian = 0, $notes = '',$extend_type=0,$free_day=0) { $user = UserService::instance()->getUserModel()->getUserInfo($id); if ($user) { $init = []; //新增充值记录 $init['user_id'] = $id; $init['type'] = 3; $init['edit_type'] = $edit_type; $init['notes'] = $notes; if ($kandian > 0 || $free_kandian > 0) { $init['edit_type'] = 1; } $init['createtime'] = time(); UserDdFlushService::instance()->checkUserFlushState($id); if ($type == 1) { //增加书币 if ($kandian > 0) { $recharge_kandian = $init; //系统增加书币时,将永久书币改赠送书币(20天) if ($free_day){ $recharge_kandian['extend_type'] = $extend_type; $recharge_kandian['free_kandian'] = $kandian; $recharge_kandian['remain_free_kandian'] = $kandian; $recharge_kandian['free_endtime'] = $free_day * 86400 + time(); model('Recharge')->setConnect($id)->insertGetId($recharge_kandian); $userKey = CacheConstants::getFreeKandianUserRechargeListCacheKey($id); Redis::instance()->del($userKey); }else{ $recharge_kandian['kandian'] = $kandian; $recharge_kandian['remain_kandian'] = $kandian; $recharge_kandian['extend_type'] = $extend_type; model('Recharge')->setConnect($id)->insertGetId($recharge_kandian); $userKey = CacheConstants::getKandianUserRechargeListCacheKey($id); Redis::instance()->del($userKey); } } if ($free_kandian) { $recharge_free = $init; $recharge_free['free_kandian'] = $free_kandian; $recharge_free['remain_free_kandian'] = $free_kandian; $recharge_free['extend_type'] = $extend_type;//增加活動類型字段1,活動報名,2活動贈送书币 $recharge_free['free_endtime'] = intval(config('site.kandian_free_day')) * 86400 + time(); model('Recharge')->setConnect($id)->insertGetId($recharge_free); $userKey = CacheConstants::getFreeKandianUserRechargeListCacheKey($id); Redis::instance()->del($userKey); } } elseif ($type == 2) { //减少书币 if ($kandian > 0) { //永久书币 $res = FinancialService::instance()->reduceKandian($id, $kandian, [], $notes); if ($res->code != ErrorCodeConstants::SUCCESS) { return $res; } //添加扣减记录 $result = $res->data; if (isset($result['0'])) { //普通书币 $saveDate = []; $saveDate['user_id'] = $id; $saveDate['type'] = 3; $saveDate['edit_type'] = $edit_type; $saveDate['dd'] = 0; $saveDate['notes'] = $notes; $saveDate['kandian'] = -1*$result['0']; $saveDate['remain_kandian'] = 0; $saveDate['createtime'] = time(); model('Recharge')->setConnect($id)->insertGetId($saveDate); //插入充值记录 } if (isset($result['1'])) { //扣量书币 $saveDate = []; $saveDate['user_id'] = $id; $saveDate['type'] = 3; $saveDate['edit_type'] = $edit_type; $saveDate['dd'] = 1; $saveDate['notes'] = $notes; $saveDate['kandian'] = -1*$result['1']; $saveDate['remain_kandian'] = 0; $saveDate['createtime'] = time(); model('Recharge')->setConnect($id)->insertGetId($saveDate); //插入充值记录 } } if ($free_kandian) { //免费书币 //减少免费书币 $res = FinancialService::instance()->reduceFreeKandian($id, $free_kandian, [], $notes); if ($res->code != ErrorCodeConstants::SUCCESS) { return $res; } } } } else { //没有此用户 return $this->setCode(ErrorCodeConstants::DB_ERROR_SELECT)->setMsg('用户不存在')->getReturn(); } return $this->setData(true)->getReturn(); } public function reduceKandianCampaign( $userId, $match ){ try{ //更新老用户 remain_kandian; UserDdFlushService::instance()->checkUserFlushState($userId); //扣除永久书币 $res = $this->reduceKandian($userId,$match['kandian'],[],'消耗活动'); if ( $res->code == 0 ){ $info = $order = []; while (!empty($res->data['json'])) { $recharge = array_pop($res->data['json']); $info[] = [ 'id' => $recharge['recharge_id'], 'kandian' => $recharge['kandian'], 'free_kandian' => 0, 'dd_kandian' => $recharge['dd'] == 1 ? $recharge['kandian'] : 0, 'dd_free_kandian' => 0, ]; if ($recharge['orders_id']) { $order[] = $recharge['orders_id']; } unset($recharge); } // 增加消费记录 $campJson = json_encode([ 'campaign_id'=>(int)$match['campaign_id'], 'campaign_name'=>$match['activeName'], 'match_id'=> (int)$match['id'], 'match_name'=>$match['match_date'].$match['kandian'], ]); $consumeData = [ 'user_id' => (int)$userId, 'type' => 3, 'chapter_name'=>'', 'kandian'=>$match['kandian'], 'dd_kandian'=>$res->data[1]??0, 'createtime' => time(), 'updatetime' => time(), 'camp_info'=>$campJson, ]; $this->getConsumeModel()->setConnect($userId)->insert($consumeData); // region 用户画像打点到Nginx $user_info = UserService::instance()->getUserModel()->getUserInfo($userId, false); $ip_redis_key = CacheConstants::USER_IP_PREFIX . $userId; $ip_arr = json_decode(Redis::instance()->get($ip_redis_key), true, 1024); try{ $dotdata = [ 'ctime' => time(), 'uid' => $user_info['id'], 'openid' => $user_info['openid'], 'unionid' => '', 'ip' => $ip_arr['ip'] ?? '', 'country' => $ip_arr['country'] ?? '', 'province' => $ip_arr['province'] ?? '', 'city' => $ip_arr['city'] ?? '', 'pline' => empty(Config::get('site.theme')) ? 'xg' : Config::get('site.theme'), 'agent_id' => $user_info['agent_id'] ?? '', 'chid' => $user_info['channel_id'] ?? '', 'ua' => $ip_arr['ua'], 'cookie' => '', 'type' => 'consume', 'user' => [ 'gender' => $user_info['sex'] ?? '', 'is_subscribe' => $user_info['is_subscribe'] ?? '', 'vip_endtime' => $user_info['vip_endtime'] ?? '', 'subscribe_time' => $user_info['subscribe_time'] ?? '', 'is_pay' => $user_info['is_pay'] ?? '', 'referral_id' => $user_info['referral_id'] ?? '', 'ext' => $user_info['ext'] ? json_decode($user_info['ext'], true) : '', ], 'content' => [ 'order_id' => '', 'book_id' => '', 'book_sex' => '', // 男频? 女频? 'book_category_id' => '', //书籍分类ID 'cz_consume' => $match['kandian'], //消费充值看点 'zs_consume' => 0,//消费赠送看点 ] ]; curl_user_dot($dotdata); }catch (\Exception $exception){ LogService::error($exception->getMessage()); } // endregion return $this->setData(['code'=>0,])->getReturn(); } return $res; }catch (\Exception $e){ LogService::exception($e); return $this->setCode(ErrorCodeConstants::EXCEPTION)->setData(false)->getReturn(); } } /** * 获取免费看点+永久看点 * @param $uid * @param int $total * @return \app\main\model\object\ReturnObject */ public function getTotalKandianAndFreeKandian($uid, $total = 1) { $kandian = $this->getTotalKandian($uid, $total)->data; $free = $this->getTotalFreeKandian($uid, $total)->data; return $this->setData($kandian + $free)->getReturn(); } }