Index.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. <?php
  2. namespace app\index\controller;
  3. use app\admin\model\SinaNotice;
  4. use app\middleware\Log;
  5. use app\middleware\Safe;
  6. use app\service\WeiboService;
  7. use InvalidArgumentException;
  8. use ReflectionException;
  9. use think\admin\Controller;
  10. use think\admin\service\SystemService;
  11. use think\exception\FuncNotFoundException;
  12. use think\exception\ClassNotFoundException;
  13. use think\db\exception\DbException;
  14. use think\db\exception\ModelNotFoundException;
  15. use think\db\exception\DataNotFoundException;
  16. use think\facade\Cache;
  17. use think\facade\Db;
  18. use think\facade\Log as FacadeLog;
  19. use think\facade\Request;
  20. use \think\response\Json;
  21. /**
  22. * Class Index
  23. * @package app\index\controller
  24. */
  25. class Index extends Controller
  26. {
  27. public function index()
  28. {
  29. $this->redirect(sysuri('admin/login/index'));
  30. }
  31. /**
  32. * 检测登录 通过cookie中的SUB字段的内容,调用用户接口检测登录状态
  33. * @return mixed
  34. * @throws ClassNotFoundException
  35. * @throws ReflectionException
  36. */
  37. public function checkLogin()
  38. {
  39. FacadeLog::info("cookies:" . json_encode(Request::post()));
  40. $sub = "";
  41. if (!empty($_COOKIE['SUB'])) {
  42. $sub = $_COOKIE['SUB'];
  43. } else {
  44. $sub = Request::post('cookie');
  45. }
  46. $userInfoRes = (new WeiboService(0))->userinfo($sub);
  47. if (empty($userInfoRes) || $userInfoRes['code'] != 10000) {
  48. return $this->response(403, $userInfoRes['msg'] ?? '没有登录');
  49. }
  50. // 使用客户端信息生成token
  51. $token = md5($_SERVER['HTTP_USER_AGENT'] . $_SERVER['HTTP_ACCEPT_ENCODING'] . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . get_client_ip(0)) . $userInfoRes['data']['uid'];
  52. $user = $userInfoRes['data'];
  53. // 生成加密用的密钥和向量
  54. $cipher = "aes-256-gcm";
  55. $ivlen = openssl_cipher_iv_length($cipher);
  56. $iv = bin2hex(openssl_random_pseudo_bytes($ivlen));
  57. $aesKey = bin2hex(openssl_random_pseudo_bytes(32));
  58. $user = array_merge($user, [
  59. 'aes_key' => $aesKey,
  60. 'ase_iv' => $iv,
  61. ]);
  62. // 缓存用户信息1天
  63. Cache::set('u:' . $token, json_encode($user), 86400);
  64. return $this->successResponse([
  65. 'user' => $user,
  66. 'token' => $token,
  67. 'first' => Cache::get('u:f:' . $user['uid']) != 1,
  68. ]);
  69. }
  70. /**
  71. * 聚合页配置信息
  72. * @return mixed
  73. * @throws FuncNotFoundException
  74. * @throws ReflectionException
  75. * @throws InvalidArgumentException
  76. * @throws ClassNotFoundException
  77. */
  78. public function groupPageConfig()
  79. {
  80. return $this->successResponse(['config' => SystemService::instance()->getData('group:page:config')]);
  81. }
  82. /**
  83. * 聚合页通知获取
  84. * 查询最近20条通知
  85. * @return mixed
  86. * @throws DbException
  87. * @throws ModelNotFoundException
  88. * @throws DataNotFoundException
  89. */
  90. public function notices()
  91. {
  92. $rows = SinaNotice::limit(20)->order('id', 'desc')->select();
  93. return $this->successResponse([
  94. "lists" => $rows,
  95. ]);
  96. }
  97. /**
  98. * 首次弹窗
  99. * 发放邀请函微博
  100. * @return Json
  101. */
  102. public function sendInviteWeibo()
  103. {
  104. $config = SystemService::instance()->getData('group:page:config');
  105. $sendRes = (new WeiboService(Safe::$user['uid']))->status($config['firstDialog']['content']);
  106. if (empty($sendRes) || $sendRes['code'] != 10000) {
  107. return $this->response(403, $sendRes['msg'] ?? '发布失败');
  108. }
  109. Cache::set('u:f:' . Safe::$user['uid'], 1, 0);
  110. return $this->successResponse(null, '发布成功!');
  111. }
  112. /**
  113. * 首次弹窗用户未选择发送邀请函
  114. * 标记已经首次弹窗过了
  115. * @return mixed
  116. */
  117. public function setFirst()
  118. {
  119. Cache::set('u:f:' . Safe::$user['uid'], 1, 0);
  120. return $this->successResponse(null, '操作成功!');
  121. }
  122. /**
  123. * 获取活动规则
  124. * @return mixed
  125. * @throws FuncNotFoundException
  126. * @throws ReflectionException
  127. * @throws InvalidArgumentException
  128. * @throws ClassNotFoundException
  129. */
  130. public function getRule()
  131. {
  132. $config = SystemService::instance()->getData('activity:rule');
  133. return $this->successResponse($config);
  134. }
  135. /**
  136. * 获取品牌任务相关配置
  137. *
  138. * @return mixed
  139. * @throws FuncNotFoundException
  140. * @throws ClassNotFoundException
  141. * @throws ReflectionException
  142. * @throws DbException
  143. * @throws ModelNotFoundException
  144. * @throws DataNotFoundException
  145. */
  146. public function brand()
  147. {
  148. $conf = $this->getBrandConfigAndState();
  149. if ($conf instanceof Json) {
  150. return $conf;
  151. }
  152. return $this->successResponse($conf);
  153. }
  154. /**
  155. * 品牌任务关注接口
  156. * @return mixed
  157. * @throws FuncNotFoundException
  158. * @throws ClassNotFoundException
  159. * @throws ReflectionException
  160. * @throws DbException
  161. * @throws ModelNotFoundException
  162. * @throws DataNotFoundException
  163. */
  164. public function follow()
  165. {
  166. // 先获取品牌任务配置及状态
  167. $conf = $this->getBrandConfigAndState();
  168. if ($conf instanceof Json) {
  169. return $conf;
  170. }
  171. // 已经关注过了
  172. if ($conf['state']['follow_state'] == 1) {
  173. return $this->successResponse([
  174. 'result' => 2,
  175. 'brand' => $conf,
  176. ], '您已经关注过了');
  177. }
  178. // 调用接口关注
  179. $weiboRes = (new WeiboService(Safe::$user['uid']))->add($conf['config']['follow']['id']);
  180. if (empty($weiboRes) || $weiboRes['code'] != 10000) {
  181. return $this->response(600, $weiboRes['msg'] ?? '关注失败~');
  182. }
  183. $date = date('Y-m-d');
  184. $redisKey = $this->getTaskStateRedisKey(intval(Safe::$user['uid']));
  185. // 如果已经完成了另外两项,则标记品牌任务已经完成
  186. $finishState = $conf['state']['finish_state'];
  187. if ($conf['state']['view_state'] == 1 && $conf['state']['forward_state'] == 1) {
  188. $finishState = 1;
  189. }
  190. // 更新任务状态
  191. $nums = Db::table('user_task')->where('uid', Safe::$user['uid'])->where('date', $date)->update([
  192. 'follow_state' => 1,
  193. 'finish_state' => $finishState,
  194. ]);
  195. if ($nums) {
  196. $conf['state']['follow_state'] = 1;
  197. $conf['state']['finish_state'] = $finishState;
  198. Cache::set($redisKey, json_encode($conf['state']), 86400);
  199. return $this->successResponse([
  200. 'result' => 1,
  201. 'brand' => $conf,
  202. ], '关注成功');
  203. } else {
  204. return $this->response(601, '更新任务状态失败');
  205. }
  206. }
  207. /**
  208. * 品牌任务转发博文接口
  209. * @return mixed
  210. * @throws FuncNotFoundException
  211. * @throws ClassNotFoundException
  212. * @throws ReflectionException
  213. * @throws DbException
  214. * @throws ModelNotFoundException
  215. * @throws DataNotFoundException
  216. */
  217. public function forward()
  218. {
  219. // 先获取品牌任务配置及状态
  220. $conf = $this->getBrandConfigAndState();
  221. if ($conf instanceof Json) {
  222. return $conf;
  223. }
  224. // 已经转发过了
  225. if ($conf['state']['forward_state'] == 1) {
  226. return $this->successResponse([
  227. 'result' => 2,
  228. 'brand' => $conf,
  229. ], '您已经转发过了');
  230. }
  231. // 调用微博接口转发
  232. $weiboRes = (new WeiboService(Safe::$user['uid']))->repost($conf['config']['forward']['id'], $conf['config']['forward']['content'] ?? '');
  233. if (empty($weiboRes) || $weiboRes['code'] != 10000) {
  234. return $this->response(600, $weiboRes['msg'] ?? '转发失败~');
  235. }
  236. $date = date('Y-m-d');
  237. $redisKey = $this->getTaskStateRedisKey(intval(Safe::$user['uid']));
  238. // 如果已经完成了另外两项,则标记品牌任务已经完成
  239. $finishState = $conf['state']['finish_state'];
  240. if ($conf['state']['view_state'] == 1 && $conf['state']['follow_state'] == 1) {
  241. $finishState = 1;
  242. }
  243. // 更新任务状态
  244. $nums = Db::table('user_task')->where('uid', Safe::$user['uid'])->where('date', $date)->update([
  245. 'forward_state' => 1,
  246. 'finish_state' => $finishState,
  247. ]);
  248. if ($nums) {
  249. $conf['state']['forward_state'] = 1;
  250. $conf['state']['finish_state'] = $finishState;
  251. Cache::set($redisKey, json_encode($conf['state']), 86400);
  252. return $this->successResponse([
  253. 'result' => 1,
  254. 'brand' => $conf,
  255. ], '转发成功');
  256. } else {
  257. return $this->response(601, '更新任务状态失败');
  258. }
  259. }
  260. /**
  261. * 完成品牌任务后领取加票机会接口
  262. * @return mixed
  263. * @throws FuncNotFoundException
  264. * @throws ClassNotFoundException
  265. * @throws ReflectionException
  266. * @throws DbException
  267. * @throws ModelNotFoundException
  268. * @throws DataNotFoundException
  269. */
  270. public function taskFinishAddVotes()
  271. {
  272. // 先获取品牌任务配置及状态
  273. $conf = $this->getBrandConfigAndState();
  274. if ($conf instanceof Json) {
  275. return $conf;
  276. }
  277. if ($conf['state']['finish_add_votes'] == 1) {
  278. return $this->successResponse([
  279. 'result' => 2,
  280. 'brand' => $conf,
  281. ], '您今日已经领取过投票机会了~');
  282. }
  283. // 未完成品牌任务 领取失败
  284. if ($conf['state']['finish_state'] == 0) {
  285. return $this->response(603, '请先完成品牌任务,再来领取加票', [
  286. 'result' => 0,
  287. 'brand' => $conf,
  288. ]);
  289. }
  290. // 调用微博接口 给用户发券(加票)
  291. $weiboRes = (new WeiboService(Safe::$user['uid']))->setcoupons($conf['config']['task']['votes']);
  292. if (empty($weiboRes) || $weiboRes['code'] != 10000) {
  293. return $this->response(600, $weiboRes['msg'] ?? '加票失败~');
  294. }
  295. // 标记加票成功
  296. $date = date('Y-m-d');
  297. $redisKey = $this->getTaskStateRedisKey(intval(Safe::$user['uid']));
  298. $nums = Db::table('user_task')->where('uid', Safe::$user['uid'])->where('date', $date)->update([
  299. 'finish_add_votes' => 1,
  300. ]);
  301. if ($nums) {
  302. $conf['state']['finish_add_votes'] = 1;
  303. Cache::set($redisKey, json_encode($conf['state']), 86400);
  304. return $this->successResponse([
  305. 'result' => 1,
  306. 'brand' => $conf,
  307. ], '加票成功');
  308. } else {
  309. return $this->response(601, '更新任务状态失败');
  310. }
  311. }
  312. /**
  313. * 用户完成端外分享时调用接口加票
  314. * @return mixed
  315. * @throws FuncNotFoundException
  316. * @throws ClassNotFoundException
  317. * @throws ReflectionException
  318. * @throws DbException
  319. * @throws ModelNotFoundException
  320. * @throws DataNotFoundException
  321. */
  322. public function shareFinishAddVotes()
  323. {
  324. // 先获取品牌任务配置及状态
  325. $conf = $this->getBrandConfigAndState();
  326. if ($conf instanceof Json) {
  327. return $conf;
  328. }
  329. if ($conf['state']['share_add_votes'] == 1) {
  330. return $this->successResponse([
  331. 'result' => 2,
  332. 'brand' => $conf,
  333. ], '您今日已经领取过投票机会了~');
  334. }
  335. // 调用接口给用户发券(加票)
  336. $weiboRes = (new WeiboService(Safe::$user['uid']))->setcoupons($conf['config']['share']['votes']);
  337. if (empty($weiboRes) || $weiboRes['code'] != 10000) {
  338. return $this->response(600, $weiboRes['msg'] ?? '加票失败~');
  339. }
  340. // 标记状态
  341. $date = date('Y-m-d');
  342. $redisKey = $this->getTaskStateRedisKey(intval(Safe::$user['uid']));
  343. $nums = Db::table('user_task')->where('uid', Safe::$user['uid'])->where('date', $date)->update([
  344. 'share_add_votes' => 1,
  345. ]);
  346. if ($nums) {
  347. $conf['state']['share_add_votes'] = 1;
  348. Cache::set($redisKey, json_encode($conf['state']), 86400);
  349. return $this->successResponse([
  350. 'result' => 1,
  351. 'brand' => $conf,
  352. ], '加票成功');
  353. } else {
  354. return $this->response(601, '更新任务状态失败');
  355. }
  356. }
  357. /**
  358. * 标记用户已经浏览过主页了,用户点了去浏览时调用
  359. * @return mixed
  360. * @throws FuncNotFoundException
  361. * @throws ClassNotFoundException
  362. * @throws ReflectionException
  363. * @throws DbException
  364. * @throws ModelNotFoundException
  365. * @throws DataNotFoundException
  366. */
  367. public function setViewed()
  368. {
  369. // 先获取品牌任务配置及状态
  370. $conf = $this->getBrandConfigAndState();
  371. if ($conf instanceof Json) {
  372. return $conf;
  373. }
  374. if ($conf['state']['view_state'] == 1) {
  375. return $this->successResponse([
  376. 'result' => 2,
  377. 'brand' => $conf,
  378. ], '您已经浏览过了');
  379. }
  380. $date = date('Y-m-d');
  381. $redisKey = $this->getTaskStateRedisKey(intval(Safe::$user['uid']));
  382. // 如果已经完成了另外两项,则标记品牌任务已经完成
  383. $finishState = $conf['state']['finish_state'];
  384. if ($conf['state']['forward_state'] == 1 && $conf['state']['follow_state'] == 1) {
  385. $finishState = 1;
  386. }
  387. $nums = Db::table('user_task')->where('uid', Safe::$user['uid'])->where('date', $date)->update([
  388. 'view_state' => 1,
  389. 'finish_state' => $finishState,
  390. ]);
  391. if ($nums) {
  392. $conf['state']['view_state'] = 1;
  393. $conf['state']['finish_state'] = $finishState;
  394. Cache::set($redisKey, json_encode($conf['state']), 86400);
  395. return $this->successResponse([
  396. 'result' => 1,
  397. 'brand' => $conf,
  398. ], '浏览成功');
  399. } else {
  400. return $this->response(601, '更新任务状态失败');
  401. }
  402. }
  403. /**
  404. * 获取品牌任务redis 缓存键
  405. * @param int $uid
  406. * @return string
  407. */
  408. protected function getTaskStateRedisKey(int $uid): string
  409. {
  410. $dateForRedis = date('Ymd');
  411. return "t:{$dateForRedis}:" . $uid;
  412. }
  413. /**
  414. * 获取品牌任务配置及状态
  415. * @return \think\response\Json|array
  416. * @throws FuncNotFoundException
  417. * @throws ReflectionException
  418. * @throws InvalidArgumentException
  419. * @throws ClassNotFoundException
  420. * @throws DbException
  421. * @throws ModelNotFoundException
  422. * @throws DataNotFoundException
  423. */
  424. protected function getBrandConfigAndState()
  425. {
  426. $config = SystemService::instance()->getData('brand:task:config');
  427. $date = date('Y-m-d');
  428. $dateForRedis = date('Ymd');
  429. $redisKey = "t:{$dateForRedis}:" . Safe::$user['uid'];
  430. $state = Cache::get($redisKey);
  431. if (empty($state)) {
  432. // 缓存失败 查数据库
  433. $state = Db::table('user_task')->where('uid', Safe::$user['uid'])->where('date', $date)->find();
  434. if (empty($state)) {
  435. // 当天第一次,初始化数据
  436. $state = [
  437. // 品牌任务完成状态
  438. 'finish_state' => 0,
  439. // 关注子任务状态
  440. 'follow_state' => 0,
  441. // 转发子任务状态
  442. 'forward_state' => 0,
  443. // 查看浏览主页任务状态
  444. 'view_state' => 0,
  445. // 品牌任务完成后是否加票
  446. 'finish_add_votes' => 0,
  447. // 分享完成后是否加票
  448. 'share_add_votes' => 0,
  449. 'uid' => Safe::$user['uid'],
  450. 'date' => $date,
  451. ];
  452. if (0 == Db::table('user_task')->insert($state)) {
  453. return $this->response(5001, '系统错误,请稍后再试~');
  454. }
  455. }
  456. Cache::set($redisKey, json_encode($state), 86400);
  457. } else {
  458. $state = json_decode($state, true);
  459. }
  460. // 如果未关注状态,查询下已经实际已经关注了
  461. if ($state['follow_state'] == 0) {
  462. $weiboRes = (new WeiboService(Safe::$user['uid']))->friends($config['follow']['id']);
  463. if (isset($weiboRes['data'][$config['follow']['id']]) && $weiboRes['data'][$config['follow']['id']] == 1) {
  464. $nums = Db::table('user_task')->where('uid', Safe::$user['uid'])->where('date', $date)->update(['follow_state' => 1,]);
  465. if ($nums) {
  466. $state['follow_state'] = 1;
  467. Cache::set($redisKey, json_encode($state), 86400);
  468. }
  469. }
  470. }
  471. // 校准状态,可能存在异常情况关注,转发,浏览都完成了,但是总的品牌任务状态没标记成已完成
  472. if ($state['follow_state'] == 1 && $state['forward_state'] == 1 && $state['view_state'] == 1 && $state['finish_state'] != 1) {
  473. $nums = Db::table('user_task')->where('uid', Safe::$user['uid'])->where('date', $date)->update(['finish_state' => 1,]);
  474. if ($nums) {
  475. $state['finish_state'] = 1;
  476. Cache::set($redisKey, json_encode($state), 86400);
  477. }
  478. }
  479. return [
  480. 'config' => $config,
  481. 'state' => $state,
  482. ];
  483. }
  484. /**
  485. * @param $code
  486. * @param $msg
  487. * @param $data
  488. * @return Json
  489. */
  490. protected function response($code, $msg, $data = null)
  491. {
  492. return json(
  493. [
  494. 'code' => $code,
  495. 'message' => $msg,
  496. 'data' => $data,
  497. 'trackID' => Log::$logID,
  498. ],
  499. 200
  500. );
  501. }
  502. /**
  503. * @param $data
  504. * @param $msg
  505. * @return mixed
  506. */
  507. protected function successResponse($data, $msg = '操作成功')
  508. {
  509. return $this->response(0, $msg, $data);
  510. }
  511. }