MessageController.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. <?php
  2. namespace Modules\Api\Controllers;
  3. use App\Constants\AdConstrant;
  4. use App\Constants\CommonConstant;
  5. use App\Model\CommentModel;
  6. use App\Model\Mongo\MessageModel;
  7. use App\Model\Mongo\UserRelationModel;
  8. use App\Model\Mongo\VideoLikeModel;
  9. use App\Model\UserModel;
  10. use App\Model\VideoModel;
  11. use App\Repositories\ConfigRepoistory;
  12. use App\Repositories\MessageRepository;
  13. use App\Repositories\Utils\M3u8CacheRepository;
  14. use Carbon\Carbon;
  15. use Hyperf\Validation\Contract\ValidatorFactoryInterface;
  16. use Modules\Api\Services\VideoService;
  17. class MessageController extends BaseController
  18. {
  19. public function index()
  20. {
  21. $userCode = $this->getUserId();
  22. $inputData = $this->getJsonData();
  23. $configRepo = make(ConfigRepoistory::class);
  24. $videoService = make(VideoService::class);
  25. $adList = $videoService->getAdList(AdConstrant::MESSAGE_INDEX);
  26. $ad = null;
  27. if (!empty($adList[0])) {
  28. $link = M3u8CacheRepository::getLinkUrl($adList[0]['link'], ['user_code' => $userCode]);
  29. $ad = [
  30. 'position' => $adList[0]['position'],
  31. 'title' => $adList[0]['title'],
  32. 'cover' => !empty($adList[0]['cover']) ? M3u8CacheRepository::getImageUrl($adList[0]['cover']) : '',
  33. 'link' => $link,
  34. 'scheme' => $adList[0]['scheme'],
  35. 'type' => $adList[0]['type'],
  36. 'play' => !empty($adList[0]['play']) ? M3u8CacheRepository::getM3u8Url($adList[0]['play']) : '',
  37. 'resolution' => $adList[0]['resolution'] ?? '',
  38. ];
  39. if (!empty($ad['link'])) {
  40. $ad['link'] = M3u8CacheRepository::getLinkUrl($ad['link'], ['user_code' => $userCode]);
  41. }
  42. }
  43. $kefuUrl = $configRepo->get('server_link', 'sys', '');
  44. if (strripos($kefuUrl, '?')) {
  45. $kefuUrl .= '&user_code='. $userCode;
  46. } else {
  47. $kefuUrl .= '?user_code='. $userCode;
  48. }
  49. // 系统消息的第一条,包含统计
  50. $sysMsg = make(MessageRepository::class)->firstSys($userCode);
  51. $arrData = [
  52. 'ad' => $ad,
  53. 'kefu' => [
  54. 'url' => $kefuUrl,
  55. 'description' => '亲,有什么可以帮助您的?',
  56. ],
  57. 'sys' => $sysMsg,
  58. 'talk' => null, // TODO json
  59. ];
  60. // 私聊消息
  61. $shopsMsg = make(MessageRepository::class)->firstSys($userCode, 'user');
  62. $arrData['count_unread'] = $shopsMsg['count_unread'];
  63. return json_success($arrData);
  64. }
  65. /**
  66. * 系统消息分页.
  67. * @return mixed
  68. */
  69. public function sysPages()
  70. {
  71. $userCode = $this->getUserId();
  72. $inputData = $this->getJsonData();
  73. $previousDate = isset($inputData['previous_date']) ? trim($inputData['previous_date']) : '';
  74. if (empty($previousDate)) {
  75. $previousDate = Carbon::now()->toDateTimeString();
  76. }
  77. $limitNum = 20;
  78. $result = [
  79. 'previous_date' => $previousDate,
  80. 'data' => null
  81. ];
  82. $items = make(MessageRepository::class)->previousSysPages($userCode, $previousDate, $limitNum, true);
  83. if (!empty($items)) {
  84. $result['data'] = $items;
  85. }
  86. return json_success($result);
  87. }
  88. /**
  89. * 标记消息为已读.
  90. * @return mixed|void
  91. */
  92. public function doRead()
  93. {
  94. $userCode = $this->getUserId();
  95. $inputData = $this->getJsonData();
  96. $msgId = isset($inputData['msg_id']) ? trim($inputData['msg_id']) : '';
  97. if (empty($msgId)) {
  98. return json_fail('参数有误1');
  99. }
  100. if (!preg_match('/^[a-z0-9]{16,32}$/i', $msgId)) {
  101. return json_fail('参数有误2');
  102. }
  103. $result = make(MessageRepository::class)->doRead($msgId, $userCode);
  104. if (!$result) {
  105. return json_fail('不能正确操作已读');
  106. }
  107. return json_success(['result' => 'success']);
  108. }
  109. /**
  110. * 删除自己的消息.
  111. * @return mixed
  112. */
  113. public function deleteMyself()
  114. {
  115. $userCode = $this->getUserId();
  116. $inputData = $this->getJsonData();
  117. $msgIds = isset($inputData['msg_ids']) ? trim($inputData['msg_ids']) : '';
  118. $receiveUid = $userCode;
  119. if (empty($msgIds)) {
  120. return json_fail('缺少参数1');
  121. }
  122. $arrMsgIds = [];
  123. if (is_string($msgIds)) {
  124. if (stripos($msgIds, ',') !== false) {
  125. $arrMsgIds = explode(',', $msgIds);
  126. } else {
  127. $arrMsgIds[] = $msgIds;
  128. }
  129. } else {
  130. $arrMsgIds = $msgIds;
  131. }
  132. $result = make(MessageRepository::class)->slientDelete($arrMsgIds, $userCode);
  133. if (!$result) {
  134. return json_fail('删除消息有误.');
  135. }
  136. return json_success(['result' => 'success']);
  137. }
  138. /**
  139. * 被关注分页列表
  140. */
  141. public function followedPages()
  142. {
  143. $userCode = $this->getUserId();
  144. $inputData = $this->getJsonData();
  145. $validatorFactory = make(ValidatorFactoryInterface::class);
  146. $rules = [
  147. 'page' => 'nullable|int',
  148. 'page_size' => 'nullable|int',
  149. ];
  150. $validator = $validatorFactory->make($inputData, $rules);
  151. if ($validator->fails()) {
  152. // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
  153. return json_fail($validator->errors()->first());
  154. }
  155. $page = $inputData['page'] ?? 1;
  156. $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
  157. if ($pageSize > 100) {
  158. $pageSize = CommonConstant::PAGE_SIZE;
  159. }
  160. $page = format_page($page);
  161. $pageSize = intval($pageSize);
  162. $relationRepo = make(UserRelationModel::class);
  163. $filters = [
  164. 'to_user_id' => $userCode,
  165. ];
  166. $amount = $relationRepo->db()->countDocuments($filters);
  167. $skip = ($page - 1) * $pageSize;
  168. $options = [
  169. 'skip' => $skip,
  170. 'limit' => $pageSize,
  171. 'sort' => ['date' => -1],
  172. ];
  173. $temItems = $relationRepo->db()->find($filters, $options)->toArray();
  174. $data = [];
  175. $result = [
  176. 'current_page' => $page,
  177. 'per_page' => $pageSize,
  178. 'total' => $amount,
  179. 'data' => [],
  180. ];
  181. if (!empty($temItems)) {
  182. foreach ($temItems as $item) {
  183. $fromUserCode = $item['from_user_id'];
  184. $fromUser = UserModel::query()->where('code', $fromUserCode)->first(['id', 'code', 'nick', 'avatar']);
  185. if ($fromUser) {
  186. $fromUser = $fromUser->toArray();
  187. $fromUser['avatar'] = M3u8CacheRepository::getImageUrl($fromUser['avatar']);
  188. $fromUser['date'] = Carbon::parse($item['date'])->toDateTimeString();
  189. unset($fromUser['id']);
  190. $data[] = $fromUser;
  191. }
  192. }
  193. }
  194. $result['data'] = $data;
  195. return json_success($result);
  196. }
  197. /**
  198. * 被点赞视频分页列表
  199. */
  200. public function likedPages()
  201. {
  202. $userCode = $this->getUserId();
  203. $inputData = $this->getJsonData();
  204. $validatorFactory = make(ValidatorFactoryInterface::class);
  205. $rules = [
  206. 'page' => 'nullable|int',
  207. 'page_size' => 'nullable|int',
  208. ];
  209. $validator = $validatorFactory->make($inputData, $rules);
  210. if ($validator->fails()) {
  211. // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
  212. return json_fail($validator->errors()->first());
  213. }
  214. $page = $inputData['page'] ?? 1;
  215. $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
  216. if ($pageSize > 100) {
  217. $pageSize = CommonConstant::PAGE_SIZE;
  218. }
  219. $page = format_page($page);
  220. $pageSize = intval($pageSize);
  221. $relationRepo = make(VideoLikeModel::class);
  222. $filters = [
  223. 'uid' => $userCode,
  224. ];
  225. $amount = $relationRepo->db()->countDocuments($filters);
  226. $skip = ($page - 1) * $pageSize;
  227. $options = [
  228. 'skip' => $skip,
  229. 'limit' => $pageSize,
  230. 'sort' => ['date' => -1],
  231. ];
  232. $temItems = $relationRepo->db()->find($filters, $options)->toArray();
  233. $data = [];
  234. $result = [
  235. 'current_page' => $page,
  236. 'per_page' => $pageSize,
  237. 'total' => $amount,
  238. 'data' => [],
  239. ];
  240. $cacheVideo = [];
  241. if (!empty($temItems)) {
  242. $videoColumns = ['id', 'uid', 'title', 'cover'];
  243. foreach ($temItems as $item) {
  244. $fromUserCode = $item['uid'];
  245. $videoId = $item['vid'];
  246. if (empty($cacheVideo[$videoId])) {
  247. $videoInfo = VideoModel::query()->where(['status' => 1])->find($videoId, $videoColumns);
  248. if (empty($videoInfo)) {
  249. break;
  250. }
  251. $videoInfo = $videoInfo->toArray();
  252. $cacheVideo[$videoId] = $videoInfo;
  253. } else {
  254. $videoInfo = $cacheVideo[$videoId];
  255. }
  256. $fromUser = UserModel::query()->where('code', $fromUserCode)->first(['id', 'code', 'nick', 'avatar']);
  257. if ($fromUser) {
  258. $fromUser = $fromUser->toArray();
  259. $fromUser['avatar'] = M3u8CacheRepository::getImageUrl($fromUser['avatar']);
  260. $fromUser['date'] = Carbon::parse($item['date'])->toDateTimeString();
  261. $fromUser['video'] = [
  262. 'id' => $videoInfo['id'],
  263. 'title' => $videoInfo['title'],
  264. 'cover' => M3u8CacheRepository::getImageUrl($videoInfo['cover']),
  265. 'uid' => $videoInfo['uid'],
  266. ];
  267. unset($fromUser['id']);
  268. $data[] = $fromUser;
  269. }
  270. }
  271. }
  272. $result['data'] = $data;
  273. return json_success($result);
  274. }
  275. /**
  276. * 被评论分页列表
  277. */
  278. public function commentedPages()
  279. {
  280. $userCode = $this->getUserId();
  281. $inputData = $this->getJsonData();
  282. $validatorFactory = make(ValidatorFactoryInterface::class);
  283. $rules = [
  284. 'page' => 'nullable|int',
  285. 'page_size' => 'nullable|int',
  286. ];
  287. $validator = $validatorFactory->make($inputData, $rules);
  288. if ($validator->fails()) {
  289. // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
  290. return json_fail($validator->errors()->first());
  291. }
  292. $page = $inputData['page'] ?? 1;
  293. $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
  294. if ($pageSize > 100) {
  295. $pageSize = CommonConstant::PAGE_SIZE;
  296. }
  297. $page = format_page($page);
  298. $pageSize = intval($pageSize);
  299. if ($pageSize <= 0) {
  300. $pageSize = CommonConstant::PAGE_SIZE;
  301. }
  302. $columns = ['id', 'user_id', 'module', 'svalue', 'content', 'created_at'];
  303. $items = CommentModel::query()->where(['module_user_uid' => $userCode])->orderByDesc('created_at')->paginate($pageSize, $columns, 'page', $page);
  304. $result = $items->toArray();
  305. $cacheData = [
  306. 'video' => [],
  307. ];
  308. if (!empty($result['data'])) {
  309. $videoColumns = ['id', 'uid', 'title', 'cover'];
  310. foreach ($result['data'] as $k => &$v) {
  311. $fromUserCode = $v['user_id'];
  312. $moduleInfo = [];
  313. if ($v['module'] == 'video') {
  314. $videoId = $v['svalue'];
  315. if (empty($cacheData['video'][$videoId])) {
  316. $videoInfo = VideoModel::query()->where(['status' => 1])->find($videoId, $videoColumns);
  317. if (!empty($videoInfo)) {
  318. $videoInfo = $videoInfo->toArray();
  319. $cacheData['video'][$videoId] = $videoInfo;
  320. }
  321. } else {
  322. $videoInfo = $cacheData['video'][$videoId];
  323. }
  324. $moduleInfo = [
  325. 'module' => $v['module'],
  326. 'id' => !empty($videoInfo['id']) ? (string)$videoInfo['id'] : '', // 强语言,统一转换字符串.
  327. 'title' => !empty($videoInfo['title']) ? $videoInfo['title'] : '视频已下架',
  328. 'cover' => !empty($videoInfo['cover']) ? M3u8CacheRepository::getImageUrl($videoInfo['cover']) : '',
  329. 'uid' => !empty($videoInfo['uid']) ? $videoInfo['uid'] : '',
  330. ];
  331. }
  332. $fromUser = UserModel::query()->where('code', $fromUserCode)->first(['id', 'code', 'nick', 'avatar']);
  333. if ($fromUser) {
  334. $fromUser = $fromUser->toArray();
  335. $fromUser['avatar'] = M3u8CacheRepository::getImageUrl($fromUser['avatar']);
  336. $fromUser['date'] = $v['created_at'];
  337. $fromUser['module_info'] = $moduleInfo;
  338. unset($fromUser['id']);
  339. } else {
  340. $fromUser = null;
  341. }
  342. $result['data'][$k] = $fromUser;
  343. }
  344. }
  345. return json_success($result);
  346. }
  347. /**
  348. * 私聊用户列表
  349. *
  350. * @param ValidatorFactoryInterface $validatorFactory
  351. * @return mixed
  352. */
  353. public function privateChatLists(ValidatorFactoryInterface $validatorFactory)
  354. {
  355. $inputData = $this->getJsonData();
  356. $rules = [
  357. 'page' => 'required|integer',
  358. 'page_size' => 'required|integer',
  359. ];
  360. $validator = $validatorFactory->make($inputData, $rules);
  361. if ($validator->fails()) {
  362. // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
  363. return json_fail($validator->errors()->first());
  364. }
  365. $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
  366. if ($pageSize > 500) {
  367. $pageSize = CommonConstant::PAGE_SIZE;
  368. }
  369. $page = format_page($inputData['page'] ?? 1);
  370. $result = make(MessageModel::class)->db()->aggregate([
  371. [
  372. '$match' => [
  373. 'receive_uid' => $this->getUserId(),
  374. 'from_type' => 'user',
  375. ]
  376. ],
  377. [
  378. '$group' => [
  379. "_id" => [
  380. 'from_uid' => '$from_uid',
  381. ],
  382. 'struct_type' => [
  383. '$last' => '$struct_type',
  384. ],
  385. 'from_uid' => [
  386. '$last' => '$from_uid',
  387. ],
  388. 'from_nick' => [
  389. '$last' => '$from_nick',
  390. ],
  391. 'from_avatar' => [
  392. '$last' => '$from_avatar',
  393. ],
  394. 'content' => [
  395. '$last' => '$content',
  396. ],
  397. 'created_at' => [
  398. '$last' => '$created_at',
  399. ],
  400. ]
  401. ],
  402. [
  403. '$sort' => [
  404. "created_at" => -1,
  405. ]
  406. ],
  407. [
  408. '$limit' => $pageSize * $page,
  409. ],
  410. [
  411. '$skip' => ($page - 1) * $pageSize,
  412. ]
  413. ]);
  414. $res = $result->toArray();
  415. if (!empty($res)) {
  416. foreach ($res as &$val) {
  417. $content = json_decode($val['content'], true);
  418. $message = structureHandle($content['message'], $val['struct_type']);
  419. $val['message'] = json_encode($message);
  420. $val['created_at'] = mongoIsoDateToCarbonTime($val['created_at'])->toDateTimeString();
  421. $val['from_avatar'] = M3u8CacheRepository::getImageUrl($val['from_avatar']);
  422. $val['msg_type'] = $val['struct_type'];
  423. unset($val['_id'], $val['content'], $val['struct_type']);
  424. }
  425. }
  426. return json_success($res);
  427. }
  428. /**
  429. * 我的页面红点数量
  430. * @return mixed
  431. */
  432. public function myRed()
  433. {
  434. $result = [];
  435. $userCode = $this->getUserId();
  436. // 系统消息的第一条,包含统计
  437. $sysMsg = make(MessageRepository::class)->firstSys($userCode);
  438. $result['count_sys_unread'] = $sysMsg['count_unread'];
  439. // 私聊消息
  440. $shopsMsg = make(MessageRepository::class)->firstSys($userCode, 'user');
  441. $result['count_p2p_unread'] = $shopsMsg['count_unread'];
  442. return json_success($result);
  443. }
  444. }