Fee.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. <?php
  2. /**
  3. * 前台控制器
  4. * 书籍
  5. */
  6. namespace app\index\controller;
  7. use app\common\library\Redis;
  8. use app\common\controller\Frontend;
  9. use think\Db;
  10. use think\Env;
  11. use think\Config;
  12. use app\common\library\Ssdb;
  13. class Fee extends Frontend
  14. {
  15. /**
  16. * @var Redis
  17. */
  18. protected $redis;
  19. public function _initialize()
  20. {
  21. $this->redis = Redis::instance();
  22. //parent::_initialize();
  23. }
  24. public function test(){
  25. //$ur = new UserRecentlyRead();
  26. //$ur->getOneReadRecord('U1B11000000039');
  27. //$res = $ur->getRecentlyRead(1);
  28. //$pp = $this->redis->hgetall('U1B11000000029');
  29. //dump($pp);
  30. // $thismodel = new UserRecentlyRead();
  31. // $read = $thismodel->getRecentlyRead();
  32. // dump($read);die;
  33. $book_id = 11000000039;
  34. //echo $this->redis->get('hello');die;
  35. //$chapters = model('Book')::getChapterList($book_id);
  36. $bookinfo = model('Book')->bookinfo($book_id);
  37. dump($bookinfo);die;
  38. // echo "<pre/>";
  39. // print_r($chapters);
  40. echo(microtime());
  41. $res = $this->bookfee(3,$bookinfo,'测试章节1',142,196511666,1);
  42. dump($res);
  43. echo(microtime());
  44. }
  45. /**
  46. * 点击下一章
  47. * @param $userId $this->user->id; 如果没有userid 就写到cookie里(最多5本)
  48. * @param $bookId url上传的
  49. * @param $chapter_id url上带的chapter_id ,cookie里的chaper_id 和 sid 优先级低(高于)
  50. * @param $chapter_name
  51. * @param $idx 第几章
  52. * @param int $kandian config('site.book_chapter_price')
  53. * @return array $returnArr['type'] = 4; // type:1扣书币成功 2.书币不足,3扣书币失败,4缺少参数,5获取书籍信息失败,6未登录,7,游客到了计费章节 $returnArr['isRecharge']=1,扣费,返回数组里没有isRecharge这个参数是免费的
  54. * @throws \Exception
  55. */
  56. public function bookfee($admin_id,$book,$chapter_name,$idx,$chapter_id,$user){
  57. $freeChapter = config('site.book_free_chapter_num'); //全局免费章节数
  58. if(intval($book['free_chapter_num'])>0){ //如果本书籍设置了免费章节数,以本书籍的免费章节数为准
  59. $freeChapter = $book['free_chapter_num'];
  60. }
  61. //维护书阅读数量
  62. $bookNumKey = 'B-N';
  63. $this->redis->hincrby($bookNumKey, $book['id'], 1);
  64. $bookKey = 'B:'.$book['id'];
  65. if($this->redis->exists($bookKey)){ //判断有没有这个key
  66. $this->redis->hincrby($bookKey,'read_num',1);
  67. }
  68. $kandian = config('site.book_chapter_price'); //全局每章节书币数
  69. $returnArr = [];
  70. if(!$book || !$admin_id || !$chapter_id || !$chapter_name || !$idx){ //必传参数
  71. $returnArr['error'] = 1;
  72. $returnArr['type'] = 4;
  73. $returnArr['msg'] = '缺少参数';
  74. return $returnArr;
  75. }
  76. if(!empty($user)) {
  77. $userId = $user['id'];
  78. }else{
  79. $userId = null;
  80. }
  81. if(Config::get("site.free")==1){ //容灾处理,如果计费出现问题在后台配置read_free=true则所有书籍都免费
  82. $this->recentlyRead($chapter_name,$chapter_id,$book,$userId); //记录最近阅读
  83. $returnArr['error'] = 0;
  84. $returnArr['type'] = 1;
  85. $returnArr['msg'] = '免费章节';
  86. return $returnArr;
  87. }
  88. if(intval($book['free_stime'])<time() && intval($book['free_etime'])>time()){ //此书设置了免费时长并且当前时间在免费时长内则免费阅读
  89. $this->recentlyRead($chapter_name,$chapter_id,$book,$userId); //记录最近阅读
  90. $returnArr['error'] = 0;
  91. $returnArr['type'] = 1;
  92. $returnArr['msg'] = '免费章节';
  93. return $returnArr;
  94. }
  95. if($idx > $freeChapter && empty($userId)){
  96. $returnArr['error'] = 1;
  97. $returnArr['type'] = 7;
  98. $returnArr['isRecharge'] = 1;
  99. $returnArr['msg'] = '游客到了计费章节';
  100. return $returnArr;
  101. }
  102. if($idx<=$freeChapter && empty($userId)){ //记录游客最近阅读
  103. $this->recentlyRead($chapter_name,$chapter_id,$book);
  104. $returnArr['error'] = 0;
  105. $returnArr['type'] = 1;
  106. $returnArr['msg'] = '免费章节';
  107. return $returnArr;
  108. }
  109. $bookId = $book['id']; //书籍id
  110. $consumeDB = model('Consume')->setConnect($userId);
  111. $book = model('Book')->bookinfo($bookId); //获取书籍信息
  112. if(!$book){
  113. $returnArr['error'] = 1;
  114. $returnArr['type'] = 5;
  115. $returnArr['msg'] = '获取书籍信息失败';
  116. return $returnArr;
  117. }
  118. $insertData = [];
  119. $insertData['user_id'] = $userId;
  120. $insertData['book_id'] = $bookId;
  121. $insertData['chapter_id'] = $chapter_id;
  122. $insertData['chapter_name'] = $chapter_name;
  123. $insertData['createtime'] = time();
  124. $insertData['updatetime'] = time();
  125. $insertConsume = []; //计费
  126. if($idx<=$freeChapter){ //免费章节
  127. $this->recentlyRead($chapter_name,$chapter_id,$book,$userId); //记录最近阅读
  128. $returnArr['error'] = 0;
  129. $returnArr['type'] = 1;
  130. $returnArr['msg'] = '免费章节';
  131. return $returnArr;
  132. }else{
  133. $returnArr['isRecharge'] = 1;
  134. }
  135. if($user['vip_endtime']>time()){ //vip用户,全部章节免费
  136. $this->recentlyRead($chapter_name,$chapter_id,$book,$userId); //记录最近阅读
  137. $returnArr['error'] = 0;
  138. $returnArr['type'] = 1;
  139. $returnArr['msg'] = '免费章节';
  140. return $returnArr;
  141. }
  142. //判断该本书是按本收费还是按章收费,是否自定义每章的价格
  143. $billing_type = $book['billing_type'];
  144. $price = $book['price'];
  145. $insertConsume['type'] = 1;
  146. $caseWhere = ['user_id'=>$userId,'book_id'=>$bookId,'type'=>1,'chapter_id'=>$chapter_id];
  147. if($billing_type == 2 && intval($price)>0){ //按本收费
  148. $chapter_id = null;
  149. $kandian = intval($price);
  150. $insertConsume['type'] = 2;
  151. $caseWhere = ['user_id'=>$userId,'book_id'=>$bookId,'type'=>2];
  152. }
  153. if($billing_type == 1 && intval($price)>0){ //自定义每章价格
  154. $kandian = intval($price);
  155. }
  156. //以下处理扣费章节
  157. $isConsume = $consumeDB->where($caseWhere)->find();
  158. if(!empty($isConsume)){
  159. $this->recentlyRead($chapter_name,$chapter_id,$book,$userId); //记录最近阅读
  160. $returnArr['error'] = 0;
  161. $returnArr['type'] = 1;
  162. $returnArr['msg'] = '已经付过费';
  163. return $returnArr;
  164. }
  165. //未扣费处理
  166. $fee = [];
  167. $feeBook = [];
  168. $feeBook['spending_count_kandian'] = $kandian;
  169. $feeBook['spending_recharge_kandian'] = 0;
  170. //书币是否够用
  171. $total_free_kandian = model('Recharge')->getTotalFreeKandian($userId);
  172. if($total_free_kandian + $user['kandian'] < $kandian){
  173. $returnArr['error'] = 1; // 0:成功,1:失败
  174. $returnArr['type'] = 2; // type:1扣书币成功 2.书币不足,3扣书币失败,4缺少参数
  175. $returnArr['msg'] = '抱歉,您的书币不足'; //提示信息
  176. return $returnArr;
  177. }
  178. if($total_free_kandian>=$kandian){
  179. $fee['free_kandian'] = $total_free_kandian-$kandian;
  180. $feeBook['spending_free_kandian'] = $kandian;
  181. }else{
  182. $fee['free_kandian'] = 0;
  183. $fee['kandian'] = $user['kandian']+$total_free_kandian-$kandian;
  184. $feeBook['spending_free_kandian'] = $total_free_kandian;
  185. $feeBook['spending_recharge_kandian'] = $kandian-$total_free_kandian;
  186. }
  187. if(isset($fee['kandian']) && $fee['kandian'] != $user['kandian']){
  188. $userRes = model('user')->setConnect($userId)->update($fee,['id'=>$userId]); //减少用户书币
  189. if(!$userRes){
  190. $returnArr['error'] = 1;
  191. $returnArr['type'] = 3;
  192. $returnArr['msg'] = '抱歉,操作失败';
  193. return $returnArr;
  194. }
  195. //更新redis
  196. $userKey = 'UN:'.$userId;
  197. if($this->redis->exists($userKey)){
  198. $this->redis->hmset($userKey,$fee);
  199. //up by wanghy 0407 用户信息过期时间由24小时修改为10小时
  200. $this->redis->expire($userKey, 36000);
  201. }
  202. }
  203. $this->recentlyRead($chapter_name,$chapter_id,$book,$userId); //记录最近阅读
  204. //减少免费书币
  205. if($total_free_kandian > 0){
  206. $rid = model('Recharge')->getLastFreeRecord($userId);
  207. model('Recharge')->reduceFreeKandian($userId,$kandian,$rid);
  208. }
  209. //user表操作结束,以下处理consume表和user_recently_read表
  210. $beginToday=mktime(0,0,0,date('m'),date('d'),date('Y'));
  211. //判断此用户今天有没有在这本书扣过费,放入缓存
  212. $everyDayKey = 'HC:'.$userId.':'.$bookId.':'.date("d");
  213. $isConsumeToday = false;
  214. if($this->redis->exists($everyDayKey)){
  215. $isConsumeToday = true;
  216. }else{
  217. $consumeToday = $consumeDB->where(['user_id'=>$userId,'book_id'=>$bookId])->order('id','desc')->find();
  218. if(isset($consumeToday['updatetime']) && $consumeToday['updatetime'] > $beginToday){
  219. $this->redis->setex($everyDayKey,86400,'1');
  220. $isConsumeToday = true;
  221. }
  222. }
  223. $insertConsume['user_id'] = $userId;
  224. $insertConsume['book_id'] = $bookId;
  225. $insertConsume['book_name'] = $book['name'];
  226. $insertConsume['chapter_id'] = $chapter_id;
  227. $insertConsume['chapter_name'] = $chapter_name;
  228. $insertConsume['kandian'] = $feeBook['spending_recharge_kandian'];
  229. $insertConsume['free_kandian'] = $feeBook['spending_free_kandian'];
  230. $insertConsume['createtime'] = time();
  231. $insertConsume['updatetime'] = time();
  232. $insertConsume['extend1'] = $bookId;
  233. $insertConsume['extend2'] = $chapter_id;
  234. //插入消费记录
  235. $consumeDB->insert($insertConsume);
  236. $feeBook['type'] = 1;
  237. $feeBookAll['type'] = 1;
  238. $feeBook['book_id'] = $bookId;
  239. $feeBookAll['book_id'] = $bookId;
  240. $feeBook['updatetime'] = time();
  241. $feeBookAll['updatetime'] = time();
  242. $feeBookAll['admin_id'] = 0;
  243. //0408 去掉用户喜好字段维护
  244. //记录用户喜好类目
  245. // if(!empty($user)){
  246. // $keyUBC = 'U-BC:' . $userId;
  247. // if ($this->redis->exists($keyUBC)) {
  248. // $user_cateIds = $this->redis->get($keyUBC);
  249. // if (!$user_cateIds) {
  250. // $user_cateIds = '';
  251. // }
  252. // } else {
  253. // $user_cateIds = $user['book_category_ids'];
  254. // }
  255. // $category_id = $book['book_category_id'];
  256. // $categoryString = '';
  257. // if (!empty($user_cateIds)) {
  258. // $categorys = explode(',', $user_cateIds);
  259. // if (!in_array($category_id, $categorys)) {
  260. // if (count($categorys) > 4) {
  261. // unset($categorys[0]);
  262. // foreach ($categorys as $key => $val) {
  263. // $categoryString .= $val . ',';
  264. // }
  265. // $categoryString .= $category_id;
  266. // } else {
  267. // $categoryString = $user_cateIds . ',' . $category_id;
  268. // }
  269. // } else {
  270. // $categoryString = $user_cateIds;
  271. // }
  272. // } else {
  273. // $categoryString = $category_id;
  274. // }
  275. // $this->redis->setex($keyUBC, 86400, $categoryString);
  276. // }
  277. //管理员数据汇总
  278. $this->bookConsumeCollect(0,$bookId,$isConsumeToday,$feeBook['spending_count_kandian'],$feeBook['spending_free_kandian'],$feeBook['spending_recharge_kandian']);
  279. //分销商,代理商插入小说汇总统计记录
  280. $this->bookConsumeCollect($admin_id,$bookId,$isConsumeToday,$feeBook['spending_count_kandian'],$feeBook['spending_free_kandian'],$feeBook['spending_recharge_kandian']);
  281. $returnArr['error'] = 0;
  282. $returnArr['type'] = 1;
  283. $returnArr['msg'] = '扣书币成功';
  284. return $returnArr;
  285. }
  286. /**
  287. * 书籍计费统计
  288. * @param $channel_id
  289. * @param $book_id
  290. * @param $isConsumeToday
  291. * @param $count_kandian
  292. * @param $free_kandian
  293. * @param $recharge_kandian
  294. * @throws \Exception
  295. */
  296. private function bookConsumeCollect($channel_id,$book_id,$isConsumeToday,$count_kandian,$free_kandian,$recharge_kandian){
  297. //设置渠道籍列表
  298. $collect_list_key = "BC-CL:".date("Ymd");
  299. $this->redis->sadd($collect_list_key,$channel_id);
  300. $this->redis->expire($collect_list_key,86400*2);
  301. //设置渠道商书籍列表
  302. $collect_channel_book_key = "BC-BL:{$channel_id}:".date("Ymd");
  303. $this->redis->sadd($collect_channel_book_key,$book_id);
  304. $this->redis->expire($collect_channel_book_key,86400*2);
  305. //设置渠道商对应书籍数据
  306. $collect_key = "BC:{$book_id}:{$channel_id}:".date("Ymd");
  307. if($data = json_decode($this->redis->get($collect_key),true)){
  308. if(!$isConsumeToday){
  309. $data['spending_users'] = intval($data['spending_users'] ?? 0)+1;
  310. }
  311. $data['spending_num'] = intval($data['spending_num'] ?? 0)+1;
  312. $data['spending_count_kandian'] = intval($data['spending_count_kandian'] ?? 0)+$count_kandian;
  313. $data['spending_free_kandian'] = intval($data['spending_free_kandian'] ?? 0)+$free_kandian;
  314. $data['spending_recharge_kandian'] = intval($data['spending_recharge_kandian'] ?? 0)+$recharge_kandian;
  315. $this->redis->setex($collect_key,86400*2,json_encode($data));
  316. }else{
  317. $data['spending_users'] = 1;
  318. $data['spending_num'] = 1;
  319. $data['spending_count_kandian'] = $count_kandian;
  320. $data['spending_free_kandian'] = $free_kandian;
  321. $data['spending_recharge_kandian'] = $recharge_kandian;
  322. $this->redis->setex($collect_key,86400*2,json_encode($data));
  323. }
  324. }
  325. /**
  326. * 最近阅读
  327. * @param $chapter_name
  328. * @param $chapter_id
  329. * @param $book array
  330. * @param null $userId
  331. * @return bool
  332. */
  333. public function recentlyRead($chapter_name,$chapter_id,$book,$userId=null){
  334. //记录最近阅读
  335. if(!$chapter_name || !$chapter_id || !$book){
  336. return false;
  337. }
  338. $bookId = $book['id'];
  339. $key = 'U-R:'.$userId; //最近阅读记录zset结构
  340. $hkey = 'U-B:'.$userId.':'.$bookId; //最近阅读阅读记录每条的数据:hash结构
  341. $RencentlyReadDB = model('UserRecentlyRead');
  342. $RencentlyReadDB = $RencentlyReadDB->setConnect($userId);
  343. if(empty($userId)){ //游客
  344. $chapterinfo = [];
  345. $chapterinfo['id'] = $chapter_id;
  346. $chapterinfo['name'] = $chapter_name;
  347. $RencentlyReadDB->recentCookie($book['id'], $chapterinfo); //阅读记录记入cookie 只记录5条数据
  348. return true;
  349. }
  350. $chapterInfo = [];
  351. $chapterInfo['id'] = $chapter_id;
  352. $chapterInfo['name'] = $chapter_name;
  353. $bookInfo = [];
  354. $bookInfo['image'] = $book['image'];
  355. $bookInfo['book_name'] = $book['name'];
  356. $bookInfo['last_chapter_name'] = $book['last_chapter_name'];
  357. $bookInfo['last_chapter_utime'] = $book['last_chapter_utime'];
  358. $bookInfo['state'] = $book['state'];
  359. $insertData = [];
  360. $insertData['user_id'] = $userId;
  361. $insertData['book_id'] = $bookId;
  362. $insertData['chapter_id'] = $chapter_id;
  363. $insertData['chapter_name'] = $chapter_name;
  364. $insertData['createtime'] = time();
  365. $insertData['updatetime'] = time();
  366. if(!$this->redis->exists($key)){ //如果redis里最近阅读记录为空拉取一下最近阅读记录(防灾)
  367. $RencentlyReadDB->getRecentlyRead(0, 10);
  368. }
  369. //判断redis里有没有记录
  370. $userRead = null;
  371. if($this->redis->exists($hkey) && $this->redis->exists($key)){
  372. $userRead = $this->redis->hgetall($hkey);
  373. }
  374. //如果redis里没有记录判断数据库里有没有记录
  375. if(empty($userRead)){
  376. $userRead = $RencentlyReadDB->where(['user_id'=>$userId,'book_id'=>$bookId])->find(); //是否阅读过此书
  377. }
  378. if (!empty($userRead)) { //更新
  379. $getId = $userRead['id'];
  380. $RencentlyReadDB->where(['id'=>$getId])->update(['chapter_id' => $chapter_id, 'updatetime' => time(),'chapter_name'=>$chapter_name,'flag'=>1]);
  381. if($getId){
  382. $insertData['id'] = $getId;
  383. $updateArr = array_merge($insertData,$bookInfo);
  384. $this->redis->hmset($hkey, $updateArr);
  385. if($this->redis->exists($key)) {
  386. $this->redis->zrem($key, $hkey);
  387. }
  388. $this->redis->zadd($key, $updateArr['updatetime'], $hkey);
  389. $this->redis->expire($key,43200); //设置失效时间
  390. $this->redis->expire($hkey,43200); //设置失效时间
  391. }
  392. } else {
  393. $RencentlyReadDB->insert($insertData);
  394. $id = $RencentlyReadDB->getLastInsID();
  395. $insertData['id'] = $id;
  396. $insertArr = array_merge($insertData, $bookInfo);
  397. $this->redis->hmset($hkey, $insertArr);
  398. $this->redis->expire($hkey,43200); //设置失效时间
  399. if($this->redis->exists($key)){
  400. $this->redis->zrem($key, $hkey);
  401. }
  402. if(!$this->redis->exists($key)){ //(如果redis里最近阅读记录为空拉取一下最近阅读记录防灾)
  403. $RencentlyReadDB->getRecentlyRead(time()+1, 10);
  404. }
  405. $this->redis->zadd($key, $insertArr['updatetime'], $hkey);
  406. $this->redis->expire($key,43200); //设置失效时间
  407. }
  408. }
  409. }