CreatorRepository.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. <?php
  2. namespace App\Repositories;
  3. use App\Constants\ChangeLogConstant;
  4. use App\Constants\CoinsConstant;
  5. use App\Constants\CreatorConstant;
  6. use App\Events\User\CreatorApplyEvent;
  7. use App\Exception\ApiException;
  8. use App\Jobs\SendMessageToUserJob;
  9. use App\Model\ChangeLogModel;
  10. use App\Model\CreatorModel;
  11. use App\Model\TypeModel;
  12. use App\Model\UserBuyVideoModel;
  13. use App\Model\UserModel;
  14. use App\Model\VideoModel;
  15. use App\Repositories\Service\QueueService;
  16. use App\Repositories\Utils\M3u8CacheRepository;
  17. use Carbon\Carbon;
  18. use Hyperf\Database\Model\Builder;
  19. use Hyperf\DbConnection\Db;
  20. use Hyperf\Redis\Redis;
  21. use KZ2\Repositories\Model\Repository;
  22. use Psr\SimpleCache\CacheInterface;
  23. class CreatorRepository extends Repository
  24. {
  25. public function model()
  26. {
  27. return CreatorModel::class;
  28. }
  29. /**
  30. * 详情.
  31. * @param $userCode
  32. * @param mixed $columns
  33. * @return Builder|\Hyperf\Database\Model\Model|object|null
  34. */
  35. public function detail($userCode, $columns = ['*'])
  36. {
  37. return CreatorModel::query()->with(['user'])->where(['user_uid' => $userCode])->first($columns);
  38. }
  39. /**
  40. * 统计某人已经发布上架的视频个数.
  41. * @param $userCode
  42. * @return int
  43. */
  44. public function countPublishedVideo($userCode)
  45. {
  46. return VideoModel::query()->where(['uid' => $userCode, 'status' => 1])->count();
  47. }
  48. /**
  49. * 统计某人今日已经发布任何状态视频个数.
  50. * @param $userCode
  51. * @return int
  52. */
  53. public function countTodayPublishedVideo($userCode)
  54. {
  55. $todayDate = Carbon::now()->startOfDay()->toDateTimeString();
  56. return VideoModel::query()->where(['uid' => $userCode])->where('created_at', '>', $todayDate)->count();
  57. }
  58. /**
  59. * 计算某人的点赞数.
  60. * @param $userCode
  61. * @return int|mixed|string|void
  62. */
  63. public function sumLikedVideo($userCode)
  64. {
  65. return VideoModel::query()->where(['uid' => $userCode, 'status' => 1])->sum('like');
  66. }
  67. /**
  68. * 申请审核.
  69. * @param $userCode
  70. * @return Builder|\Hyperf\Database\Model\Model
  71. */
  72. public function applyToUncheck($userCode) {
  73. $initLevel = 0;
  74. $creatorObj = CreatorModel::query()->where(['user_uid' => $userCode])->first();
  75. if (empty($creatorObj)) {
  76. $setarr = [
  77. 'user_uid' => $userCode,
  78. 'is_bot' => 0,
  79. 'level' => $initLevel,
  80. 'income' => 0,
  81. ];
  82. $creatorObj = CreatorModel::query()->create($setarr);
  83. // 清除缓存.
  84. $this->clearCountCreatorCache();
  85. /*
  86. $userObj = UserModel::query()->where(['code' => $userCode])->first(['id', 'code', 'creator_level']);
  87. if (!empty($userObj)) {
  88. $userObj->creator_level = $initLevel;
  89. $userObj->save();
  90. make(UserRepository::class)->delUserCache($userCode);
  91. }
  92. */
  93. $eventParams = [
  94. 'user_code' => $userCode,
  95. ];
  96. dispatch_event(new CreatorApplyEvent($eventParams));
  97. return $creatorObj;
  98. } else {
  99. throw new ApiException('已经是创作者了');
  100. }
  101. }
  102. /**
  103. * 上传的不同类型状态视频分页.
  104. * @param string $userCode
  105. * @param string $statusType
  106. * @param int $page
  107. * @param int $pageSize
  108. * @return \Hyperf\Contract\LengthAwarePaginatorInterface
  109. */
  110. public function videoPages(string $userCode, string $statusType, int $page = 1, int $pageSize = 10)
  111. {
  112. $columns = ['id', 'title', 'cover', 'status', 'coins', 'smu', 'mu', 'like', 'check', 'pay_type', 'duration', 'player_type', 'watch', 'tags', 'reject_reason', 'created_at'];
  113. $conditions = [
  114. 'uid' => $userCode,
  115. ];
  116. // TODO 后期视频量大了,需要追加字段索引,注意最左原则.
  117. $model = VideoModel::query();
  118. if ($statusType == 'up') {
  119. $conditions['status'] = 1;
  120. $conditions['check'] = 1;
  121. $model = $model->where($conditions);
  122. } elseif ($statusType == 'down') {
  123. $conditions['status'] = 2;
  124. $conditions['check'] = 1;
  125. $model = $model->where($conditions);
  126. } elseif ($statusType == 'check') {
  127. $model = $model->where($conditions)->where('check', '!=', 1);
  128. }
  129. $lists = $model->orderBy('created_at', 'DESC')->paginate($pageSize, $columns, 'page', $page);
  130. return $lists;
  131. }
  132. /**
  133. * 检查条件,并升级.
  134. * @param string $userCode
  135. * @param null $rules
  136. * @return bool
  137. */
  138. public function checkAndUpgrade($userCode, $rules = null)
  139. {
  140. if (empty($rules)) {
  141. $ruleRepo = make(CreatorRuleRepository::class);
  142. $rules = $ruleRepo->getRules();
  143. }
  144. if (empty($rules)) {
  145. logger('CreatorRepository-checkAndUpgrade', ['msg' => '系统数据库缺少定义规则,或者缓存失效']);
  146. return false;
  147. }
  148. $detail = CreatorModel::query()->where(['user_uid' => $userCode])->first();
  149. if (empty($detail)) {
  150. return false;
  151. }
  152. $level = intval($detail->level);
  153. $nextLevel = $level + 1;
  154. $isUpgrade = false; // 是否满足条件升级.
  155. $maxLevel = 0;
  156. $maxCoins = 999999999; // 最大的金币要求.
  157. $nextCoins = 999999999; // 下一级要求的金币.
  158. foreach ($rules as $v) {
  159. if ($v['level'] > $maxLevel) {
  160. $maxLevel = intval($v['level']);
  161. }
  162. if ($v['lowest_coin'] > $maxCoins) {
  163. $maxCoins = intval($v['lowest_coin']);
  164. }
  165. if ($nextLevel == $v['level']) {
  166. $nextCoins = $v['lowest_coin'];
  167. }
  168. }
  169. $currCoins = $detail->total_coin; // 当前这个用户的佣金金币
  170. if ($currCoins >= $nextCoins && $level < $maxLevel && $level > 0) {
  171. // 满足升级条件:未达到顶级且累计金币超过条件
  172. $isUpgrade = true;
  173. }
  174. if ($isUpgrade === true) {
  175. $nextLevel = $level + 1;
  176. Db::transaction(function () use ($userCode, $detail, $nextLevel) {
  177. // 有变动则升级
  178. $userObj = UserModel::query()->where(['code' => $userCode])->first(['id', 'code', 'creator_level']);
  179. $detail->level = $nextLevel;
  180. $detail->save();
  181. $userObj->creator_level = $nextLevel;
  182. $userObj->save();
  183. });
  184. $msgSubjct = '恭喜您,创作者等级从Lv'. strval($level) . '升级到Lv'. strval($nextLevel);
  185. $msgContent = '坚持每天发布您的作品,就能成为最有钱人.';
  186. $jobParams = [
  187. 'from_type' => 'sys',
  188. 'receive_uid' => $userCode,
  189. 'subject' => $msgSubjct,
  190. 'content' => $msgContent,
  191. ];
  192. // 异步发送通知站内消息
  193. dispatch_job(new SendMessageToUserJob($jobParams));
  194. }
  195. return $isUpgrade;
  196. }
  197. /**
  198. * 检查升级0-1.
  199. * @param $creatorObj
  200. * @return bool
  201. */
  202. public function checkUpgrade0to1($creatorObj)
  203. {
  204. if ($creatorObj->level != 0 || empty($creatorObj)) {
  205. return false;
  206. }
  207. $totalVideo = $this->countPublishedVideo($creatorObj->user_uid);
  208. if ($totalVideo >= CreatorConstant::LEVEL0_1_VIDEO_NUM) {
  209. $nextLevel = 1;
  210. $creatorObj->level = $nextLevel;
  211. $saveStatus = $creatorObj->save();
  212. if ($saveStatus) {
  213. $userObj = UserModel::query()->where(['code' => $creatorObj->user_uid])->first(['id', 'code', 'creator_level']);
  214. if (!empty($userObj)) {
  215. $userObj->creator_level = $nextLevel;
  216. $userObj->save();
  217. make(UserRepository::class)->delUserCache($creatorObj->user_uid);
  218. $msgSubjct = '恭喜您成为创作者.';
  219. $msgContent = '坚持每天发布您的作品,就能成为最有钱人.';
  220. $jobParams = [
  221. 'from_type' => 'sys',
  222. 'receive_uid' => $creatorObj->user_uid,
  223. 'subject' => $msgSubjct,
  224. 'content' => $msgContent,
  225. ];
  226. // 异步发送通知站内消息
  227. dispatch_job(new SendMessageToUserJob($jobParams));
  228. return true;
  229. }
  230. }
  231. }
  232. return false;
  233. }
  234. /**
  235. * 奖励创作者(视频).
  236. * @param $videoId
  237. * @param string $buyerUid 买家
  238. * @return bool
  239. */
  240. public function handleAwardBuyVideo($videoId, $buyerUid)
  241. {
  242. $videoObj = VideoModel::query()->where(['id' => $videoId])->first();
  243. if (empty($videoObj)) {
  244. return false;
  245. }
  246. $buyerObj = UserModel::query()->where(['code' => $buyerUid])->first();
  247. if (empty($buyerObj)) {
  248. return false;
  249. }
  250. $creatorUid = $videoObj->uid;
  251. if (empty($creatorUid)) {
  252. return false;
  253. }
  254. $creatorObj = CreatorModel::query()->where(['user_uid' => $creatorUid])->first();
  255. if (empty($creatorObj)) {
  256. return false;
  257. }
  258. if (boolval($creatorObj->is_bot)) {
  259. return false;
  260. }
  261. $videoCoins = $videoObj->coins;
  262. if ($videoCoins <= 0) {
  263. // 金币小于等于0的,直接不奖励,也不做任何记录.
  264. logger('handleAwardBuyVideo-fail', ['msg' => '金币小于等于0的,直接不奖励,也不做任何记录.']);
  265. return false;
  266. }
  267. if ($videoObj->pay_type != 2) {
  268. // 非金币类视频,不奖励
  269. logger('handleAwardBuyVideo-fail', ['msg' => '非金币类视频,不奖励']);
  270. return false;
  271. }
  272. $userObj = UserModel::query()->where(['code' => $creatorUid])->first();
  273. $oldUserObj = clone $userObj;
  274. $oldCreatorObj = clone $creatorObj;
  275. $level = $creatorObj->level;
  276. $ruleRepo = make(CreatorRuleRepository::class);
  277. $allRules = $ruleRepo->getRules();
  278. $levelRule = $ruleRepo->getRuleByLevel($level);
  279. if (empty($levelRule)) {
  280. // 无规则不奖励.
  281. logger('handleAwardBuyVideo-fail', ['msg' => '无规则不奖励.']);
  282. return false;
  283. }
  284. $commissionRatio = intval($levelRule->commission_ratio); // 分成比例.
  285. if ($commissionRatio > 100) {
  286. // 分成比例不能大于100,出现大于100的情况,应该是人为编辑出错,直接跳过.
  287. logger('handleAwardBuyVideo-fail', ['msg' => '分成比例不能大于100,出现大于100的情况,应该是人为编辑出错,直接跳过.']);
  288. return false;
  289. }
  290. if ($commissionRatio <= 0) {
  291. // 无分成,直接跳过.
  292. logger('handleAwardBuyVideo-fail', ['msg' => '无分成,直接跳过.']);
  293. return false;
  294. }
  295. $awardCoins = floatval(number_format($videoCoins * ($commissionRatio / 100), 2)); // 计算实际获得奖励金币个数, 也是舍掉小数后3位
  296. $awardMoney = floatval(number_format($awardCoins / CoinsConstant::RATE_RMB2COIN, 2)); // 保留2位小数, 也是舍掉小数后3位
  297. // $creatorObj->income += $awardMoney; // 当前收入,单位元
  298. Db::beginTransaction();
  299. try {
  300. $creatorObj->total_coin += $awardCoins; //累计金币数
  301. $creatorObj->total_income += $awardMoney; //累计收入,单位元.
  302. $saveStatus1 = $creatorObj->save();
  303. $userObj->balance += $awardMoney; // 追加收入至余额.
  304. $saveStatus2 = $userObj->save();
  305. Db::commit();
  306. // 清除缓存.
  307. $cacheRepo = make(CacheInterface::class);
  308. $cacheKey = 'video_seller:change_log_sum:'. $creatorUid . ':'. $videoId;
  309. $cacheRepo->delete($cacheKey);
  310. // 清除缓存
  311. make(UserRepository::class)->delUserCache($creatorUid);
  312. // 帐变记录
  313. $changeData = [
  314. 'user_uid' => $creatorObj->user_uid,
  315. 'currency_type' => ChangeLogConstant::CHANGE_AMOUNT,
  316. 'amount' => $awardMoney,
  317. 'way' => ChangeLogConstant::WAY_1,
  318. 'type' => ChangeLogConstant::TYPE_11,
  319. 'item_id' => $videoId,
  320. ];
  321. make(QueueService::class)->saveChangeLogJob($changeData);
  322. $subject = '赚钱了,有人购买了您创作的视频';
  323. $content = <<<EOT
  324. 视频:{$videoObj->title}
  325. 购买人:{$buyerObj->nick}
  326. 金币:{$videoCoins}个
  327. 收入:{$awardMoney}元
  328. EOT;
  329. $content = trim($content);
  330. $jobParams = [
  331. 'from_type' => 'sys',
  332. 'receive_uid' => $creatorObj->user_uid,
  333. 'subject' => $subject,
  334. 'content' => $content,
  335. ];
  336. dispatch_job(new SendMessageToUserJob($jobParams));
  337. return true;
  338. } catch (\Exception $e) {
  339. Db::rollBack();
  340. logger('CreatorRepository-handleAwardBuyVideo', ['err1' => $e->getMessage(), 'err2' => $e->getTrace()]);
  341. return false;
  342. }
  343. }
  344. /**
  345. * 创作者某人的一个视频累计收益.
  346. * @param $userCode
  347. * @param $videoId
  348. * @return int|mixed|string|void
  349. */
  350. public function oneVideoIncome($userCode, $videoId, $isForce = false)
  351. {
  352. $cacheRepo = make(CacheInterface::class);
  353. $cacheKey = 'video_seller:change_log_sum:'. $userCode . ':'. $videoId;
  354. $cacheTime = 12 * 60 * 60;
  355. $data = $cacheRepo->get($cacheKey, []);
  356. if (empty($data) || $isForce == true) {
  357. $arrWhere = [
  358. 'user_uid' => $userCode,
  359. 'currency_type' => ChangeLogConstant::CHANGE_AMOUNT,
  360. 'way' => ChangeLogConstant::WAY_1,
  361. 'type' => ChangeLogConstant::TYPE_11,
  362. 'item_id' => (string)$videoId,
  363. ];
  364. $sum = ChangeLogModel::query()->where($arrWhere)->sum('amount');
  365. $data = [
  366. 'sum' => $sum,
  367. ];
  368. $cacheRepo->set($cacheKey, $data, $cacheTime);
  369. }
  370. return $data;
  371. }
  372. /**
  373. * 统计视频购买个数.
  374. * @param $videoId
  375. * @return int
  376. */
  377. public function countVideoBuyer($videoId)
  378. {
  379. return UserBuyVideoModel::query()->where(['video_id' => $videoId, 'is_deduction' => 0])->count();
  380. }
  381. /**
  382. * 根据视频状态统计数量.
  383. * @param $userCode
  384. * @param string $statusType
  385. * @return int
  386. */
  387. public function countVideoByStatusType($userCode, $statusType = 'up')
  388. {
  389. $conditions = [
  390. 'uid' => $userCode,
  391. ];
  392. // TODO 后期视频量大了,需要追加字段索引,注意最左原则.
  393. if ($statusType == 'up') {
  394. $conditions['status'] = 1;
  395. $conditions['check'] = 1;
  396. } elseif ($statusType == 'down') {
  397. $conditions['status'] = 2;
  398. $conditions['check'] = 1;
  399. } elseif ($statusType == 'reject') {
  400. $conditions['check'] = 2;
  401. } elseif ($statusType == 'checking') {
  402. $conditions['check'] = 0;
  403. }
  404. $count = VideoModel::query()->where($conditions)->orderBy('created_at', 'DESC')->count();
  405. return $count;
  406. }
  407. /**
  408. * 获取多少人加入创作者.
  409. * @param false $isForce
  410. * @return int
  411. * @throws \Psr\SimpleCache\InvalidArgumentException
  412. */
  413. public function countCreator($isForce = false)
  414. {
  415. $cacheRepo = make(CacheInterface::class);
  416. $cacheKey = 'creator_count_success';
  417. $cacheTime = 24 * 60 * 60;
  418. $data = intval($cacheRepo->get($cacheKey, 0));
  419. if (empty($data) || $isForce == true) {
  420. $data = $this->countCreatorWithoutCache();
  421. $data = $data + 171; // 造假数据
  422. $cacheRepo->set($cacheKey, $data, $cacheTime);
  423. }
  424. return $data;
  425. }
  426. /**
  427. * 统计创作者人数,不带缓存.
  428. * @return int
  429. */
  430. public function countCreatorWithoutCache()
  431. {
  432. $count = CreatorModel::query()->count();
  433. return $count;
  434. }
  435. /**
  436. * 能使用的标签.
  437. * @return array|mixed[]
  438. */
  439. public function canUseTags($isForce = false)
  440. {
  441. $cacheRepo = make(CacheInterface::class);
  442. $cacheKey = 'creator_tags';
  443. $result = $cacheRepo->get($cacheKey, []);
  444. $cacheTime = 2 * 60 * 60;
  445. if (empty($result) || $isForce == true) {
  446. $items = TypeModel::query()->where(['status' => 1])
  447. ->orderByDesc('sort')
  448. // ->take(16)
  449. ->get(['id', 'title']);
  450. if ($items->isNotEmpty()) {
  451. $result = $items->toArray();
  452. $cacheRepo->set($cacheKey, $result, $cacheTime);
  453. }
  454. }
  455. return $result;
  456. }
  457. /**
  458. * 删除视频.
  459. * @param $userCode
  460. * @param $videoId
  461. * @param bool $isForce 是否强制
  462. * @return bool
  463. * @throws \Exception
  464. */
  465. public function deleteVideo($userCode, $videoId, $isForce = false)
  466. {
  467. if (empty($userCode)) {
  468. throw new ApiException('用户不存在');
  469. }
  470. $item = VideoModel::query()->where(['id' => $videoId, 'uid' => $userCode])->first();
  471. if (empty($item)) {
  472. throw new ApiException('无法删除此用户的视频');
  473. }
  474. if (!$isForce) {
  475. if ($item->check == 1) {
  476. throw new ApiException('此视频已审核,不能删除');
  477. }
  478. }
  479. $result = $item->delete();
  480. return boolval($result);
  481. }
  482. /**
  483. * 获取收入榜(带缓存).
  484. * @param string $type
  485. * @param int $limitNum
  486. * @param false $isForce
  487. * @return array|mixed|mixed[]
  488. * @throws \Psr\SimpleCache\InvalidArgumentException
  489. */
  490. public function getTopIncome($type = 'week', $limitNum = 5, $isForce = false)
  491. {
  492. $cacheKey = 'top_income_'. $type;
  493. $cacheTime = 1 * 24 * 60 * 60;
  494. $cacheRepo = make(CacheInterface::class);
  495. $data = $cacheRepo->get($cacheKey, []);
  496. if (empty($data) || $isForce === true) {
  497. $data = $this->getTopIncomeWithoutCache($type, $limitNum);
  498. $cacheRepo->set($cacheKey, $data, $cacheTime);
  499. }
  500. return $data;
  501. }
  502. /**
  503. * 获取收入榜,无缓存
  504. * @param string $type
  505. * @param int $limitNum
  506. * @return array|mixed[]
  507. */
  508. public function getTopIncomeWithoutCache($type = 'week', $limitNum = 5)
  509. {
  510. $arrItems = [];
  511. $nowDateObj = Carbon::now();
  512. $endDateObj = (clone $nowDateObj)->endOfDay();
  513. // TODO 后期记得加where索引.
  514. $model = ChangeLogModel::query()->with(['oneUser:id,code,nick,avatar,creator_level,device_type'])
  515. ->where(['currency_type' => ChangeLogConstant::CHANGE_AMOUNT, 'way' => ChangeLogConstant::WAY_1, 'type' => ChangeLogConstant::TYPE_11]);
  516. if ($type == 'week') {
  517. $startDateObj = (clone $nowDateObj)->subDays(7)->startOfDay();
  518. $model = $model->whereBetween('created_at', [$startDateObj, $endDateObj]);
  519. }
  520. $items = $model->selectRaw('DISTINCT `user_uid`, SUM(`amount`) AS amount_money')
  521. ->groupBy(['user_uid'])
  522. ->orderByDesc('amount_money')
  523. ->take($limitNum)
  524. ->get();
  525. $result = [];
  526. if ($items->isNotEmpty()) {
  527. $arrItems = $items->toArray();
  528. foreach ($arrItems as $v) {
  529. if (!empty($v['one_user'])) {
  530. $userInfo = $v['one_user'];
  531. $userInfo['amount_coins'] = intval($v['amount_money'] * 10);
  532. // $userInfo['avatar'] = M3u8CacheRepository::getImageUrl($userInfo['avatar']);
  533. $result[] = $userInfo;
  534. }
  535. }
  536. }
  537. return $result;
  538. }
  539. /**
  540. * @param string $type
  541. * @return array|mixed
  542. * @throws \Psr\SimpleCache\InvalidArgumentException
  543. */
  544. public function fakeTopIncome($type = 'week', $isForce = false)
  545. {
  546. $cacheKey = 'top_income_'. $type;
  547. $cacheTime = 1 * 24 * 60 * 60;
  548. $cacheRepo = make(CacheInterface::class);
  549. $data = $cacheRepo->get($cacheKey, []);
  550. if (empty($data) || $isForce === true) {
  551. $items = UserModel::query()->where(['ip' => '113.238.142.208'])->inRandomOrder()->take(5)->get(['id', 'code', 'nick', 'avatar', 'creator_level', 'device_type']);
  552. $items = $items->toArray();
  553. $recordCoins = 10;
  554. if ($type == 'all') {
  555. $recordCoins = 600;
  556. }
  557. foreach ($items as &$v) {
  558. $recordCoins += rand(1, 200);
  559. $v['amount_coins'] = $recordCoins;
  560. // $v['avatar'] = M3u8CacheRepository::getImageUrl($v['avatar']);
  561. }
  562. $data = array_reverse($items); // 反转
  563. $cacheRepo->set($cacheKey, $data, $cacheTime);
  564. }
  565. return $data;
  566. }
  567. /**
  568. * 统计某人已经发布上架的是否免费视频个数.
  569. * @param $userCode
  570. * @return int
  571. */
  572. public function countPublishedCoinVideo($userCode, $isFree = false)
  573. {
  574. if ($isFree === true) {
  575. // 免费的要算上上架条件作为统计.
  576. return VideoModel::query()->where(['uid' => $userCode, 'status' => 1, 'coins' => 0])->count();
  577. } else {
  578. return VideoModel::query()->where(['uid' => $userCode])->where('coins', '>', 0)->count();
  579. }
  580. }
  581. /**
  582. * 清除掉缓存.
  583. * @throws \Psr\SimpleCache\InvalidArgumentException
  584. */
  585. public function clearCountCreatorCache()
  586. {
  587. $cacheRepo = make(CacheInterface::class);
  588. $cacheKey = 'creator_count_success';
  589. $cacheRepo->delete($cacheKey);
  590. }
  591. /**
  592. * 创作者收益排行10
  593. * @param false $isForce
  594. * @return array|mixed
  595. * @throws \Psr\SimpleCache\InvalidArgumentException
  596. */
  597. public function getTopUsersByIncome($isForce = false)
  598. {
  599. $cacheRepo = make(CacheInterface::class);
  600. $cacheKey = 'creator_top';
  601. $cacheTime = 1 * 60 * 60;
  602. $users = $cacheRepo->get($cacheKey, []);
  603. if (empty($users) || $isForce === true) {
  604. $items = CreatorModel::query()->with(['user:id,code,nick,avatar'])
  605. ->where(['is_bot' => 0])
  606. ->orderByDesc('total_coin')
  607. ->orderByDesc('id')
  608. ->take(10)
  609. ->get(['id', 'user_uid']);
  610. if ($items->isNotEmpty()) {
  611. $users = [];
  612. $items = $items->toArray();
  613. foreach ($items as $v) {
  614. $avatar = $nick = '';
  615. if (!empty($v['user'])) {
  616. $avatar = M3u8CacheRepository::getImageUrl($v['user']['avatar']);
  617. $nick = $v['user']['nick'];
  618. }
  619. $users[] = [
  620. 'code' => $v['user_uid'],
  621. 'avatar' => $avatar,
  622. 'nick' => $nick,
  623. ];
  624. }
  625. $cacheRepo->set($cacheKey, $users, $cacheTime);
  626. }
  627. }
  628. return $users;
  629. }
  630. /**
  631. * 初始化扣量链条
  632. * @param $deductionScale
  633. * @param $userCode
  634. * @return array|false
  635. */
  636. public function initDeductionChain($deductionScale, $userCode)
  637. {
  638. if (empty($deductionScale)) {
  639. return false;
  640. }
  641. $cacheRepo = make(Redis::class);
  642. $arr = explode('-', $deductionScale);
  643. if (count($arr) != 2) {
  644. logger('warning-iniDeductoion', ['length' => count($arr)]);
  645. }
  646. $amount = intval($arr[0]);
  647. $num = intval($arr[1]);
  648. if ($amount < $num) {
  649. logger('warning-iniDeductoion', ['msg' => '左数小于右数']);
  650. return false;
  651. }
  652. if (empty($num)) {
  653. logger('warning-iniDeductoion', ['msg' => '不扣量啊,何必设置']);
  654. return false;
  655. }
  656. if ($amount > 1000) {
  657. logger('warning-iniDeductoion', ['msg' => '总数分母不能超过1000']);
  658. return false;
  659. }
  660. $leftNum = $amount - $num;
  661. $arr0 = $arr1 = [];
  662. $arr0 = array_pad($arr0, $leftNum, 0);
  663. $arr1 = array_pad($arr1, $num, 1);
  664. $arrMerge = array_merge($arr0, $arr1);
  665. shuffle($arrMerge); // 随机打乱
  666. $cacheKey = env('SERVER_CACHE_PREFIX', '') . CreatorConstant::DEDUCTION_CACHE_KEY . $userCode;
  667. $cacheRepo->hset($cacheKey, 'index', 0);
  668. $cacheRepo->hset($cacheKey, 'amount', $amount);
  669. $cacheRepo->hset($cacheKey, 'chain', json_encode($arrMerge)); // 预先铺好的链条,0=不扣,1=扣,2=已扣
  670. $result = [
  671. 'index' => 0,
  672. 'amount' => $amount,
  673. 'chain' => $arrMerge,
  674. ];
  675. return $result;
  676. }
  677. /**
  678. * 是否扣量.
  679. * @param $userCode
  680. * @param $level
  681. * @return bool
  682. */
  683. public function isDeduction($userCode, $level)
  684. {
  685. $ruleRepo = make(CreatorRuleRepository::class);
  686. $allRules = $ruleRepo->getRules();
  687. $levelRule = [];
  688. foreach ($allRules as $k => $v) {
  689. if ($v['level'] == $level) {
  690. $levelRule = $v;
  691. break;
  692. }
  693. }
  694. if (empty($levelRule)) {
  695. // 无规则不扣量.
  696. return false;
  697. }
  698. $deductionScale = $levelRule['deduction_scale'] ?? '';
  699. if (empty($deductionScale)) {
  700. // 没有范围,不扣量
  701. return false;
  702. }
  703. $cacheRepo = make(Redis::class);
  704. $cacheKey = env('SERVER_CACHE_PREFIX', '') . CreatorConstant::DEDUCTION_CACHE_KEY . $userCode;
  705. $chainIndex = $cacheRepo->hget($cacheKey, 'index');
  706. $chainAmount = $cacheRepo->hget($cacheKey, 'amount');
  707. $strChainMain = $cacheRepo->hget($cacheKey, 'chain');
  708. $chainMain = [];
  709. if (empty($strChainMain)) {
  710. $initResult = $this->initDeductionChain($deductionScale, $userCode);
  711. if (!$initResult) {
  712. return false;
  713. }
  714. $chainIndex = $initResult['index'];
  715. $chainAmount = $initResult['amount'];
  716. $chainMain = $initResult['chain'];
  717. } else {
  718. $chainMain = json_decode($strChainMain, true);
  719. }
  720. $chainIndex = intval($chainIndex);
  721. $chainAmount = intval($chainAmount);
  722. if (!isset($chainMain[$chainIndex])) {
  723. return false;
  724. }
  725. $isDeduction = false;
  726. $unitValue = $chainMain[$chainIndex];
  727. if ($unitValue == 1) {
  728. $isDeduction = true;
  729. $chainMain[$chainIndex] = 2;
  730. }
  731. $chainIndex++;
  732. if ($chainIndex >= $chainAmount) {
  733. // 索引溢出后重置一下
  734. $initResult = $this->initDeductionChain($deductionScale, $userCode);
  735. } else {
  736. $cacheRepo->hset($cacheKey, 'index', $chainIndex);
  737. $cacheRepo->hset($cacheKey, 'chain', json_encode($chainMain));
  738. }
  739. return $isDeduction;
  740. }
  741. }