From 401ec0793a072a8bdb1c0a36b2dd6d29735f079c Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Sun, 5 Jul 2020 19:23:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=9B=B4=E6=92=AD=E8=81=8A?= =?UTF-8?q?=E5=A4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Web/Controllers/AlipayController.php | 29 --- app/Http/Web/Controllers/LiveController.php | 14 ++ app/Http/Web/Controllers/PublicController.php | 42 ++++ app/Http/Web/Controllers/WxpayController.php | 29 --- app/Http/Web/Services/Live.php | 13 +- app/Http/Web/Views/chapter/live_chats.volt | 2 +- app/Http/Web/Views/chapter/show_live.volt | 16 +- app/Http/Web/Views/index/index.volt | 15 +- app/Http/Web/Views/templates/full.volt | 1 + app/Http/Web/Views/templates/layer.volt | 1 + app/Library/CsrfToken.php | 12 +- app/Services/Frontend/Chapter/ChapterInfo.php | 45 +++-- app/Services/Live.php | 186 +++++++++++++++++- config/config.default.php | 4 +- public/static/web/css/common.css | 21 +- public/static/web/js/index.js | 13 ++ public/static/web/js/live.im.js | 2 +- 17 files changed, 326 insertions(+), 119 deletions(-) delete mode 100644 app/Http/Web/Controllers/AlipayController.php delete mode 100644 app/Http/Web/Controllers/WxpayController.php create mode 100644 public/static/web/js/index.js diff --git a/app/Http/Web/Controllers/AlipayController.php b/app/Http/Web/Controllers/AlipayController.php deleted file mode 100644 index 3ea96d33..00000000 --- a/app/Http/Web/Controllers/AlipayController.php +++ /dev/null @@ -1,29 +0,0 @@ -notify(); - - if (!$response) exit; - - $response->send(); - - exit; - } - -} diff --git a/app/Http/Web/Controllers/LiveController.php b/app/Http/Web/Controllers/LiveController.php index d4c88e56..d20f09b7 100644 --- a/app/Http/Web/Controllers/LiveController.php +++ b/app/Http/Web/Controllers/LiveController.php @@ -14,6 +14,20 @@ class LiveController extends Controller use ResponseTrait; + /** + * @Get("/{id:[0-9]+}/preview", name="web.live.preview") + */ + public function previewAction($id) + { + $service = new LiveService(); + + $stats = $service->getStats($id); + + $this->view->setRenderLevel(View::LEVEL_ACTION_VIEW); + $this->view->pick('chapter/live_stats'); + $this->view->setVar('stats', $stats); + } + /** * @Get("/{id:[0-9]+}/chats", name="web.live.chats") */ diff --git a/app/Http/Web/Controllers/PublicController.php b/app/Http/Web/Controllers/PublicController.php index 989e44b6..3c245685 100644 --- a/app/Http/Web/Controllers/PublicController.php +++ b/app/Http/Web/Controllers/PublicController.php @@ -4,6 +4,8 @@ namespace App\Http\Web\Controllers; use App\Library\CsrfToken as CsrfTokenService; use App\Models\ContentImage as ContentImageModel; +use App\Services\Pay\Alipay as AlipayService; +use App\Services\Pay\Wxpay as WxpayService; use App\Services\Storage as StorageService; use App\Traits\Response as ResponseTrait; use App\Traits\Security as SecurityTrait; @@ -67,4 +69,44 @@ class PublicController extends \Phalcon\Mvc\Controller return $this->jsonSuccess(['token' => $token]); } + /** + * @Post("/alipay/notify", name="web.alipay_notify") + */ + public function alipayNotifyAction() + { + $alipayService = new AlipayService(); + + $response = $alipayService->notify(); + + if (!$response) exit; + + $response->send(); + + exit; + } + + /** + * @Post("/wxpay/notify", name="web.wxpay_notify") + */ + public function wxpayNotifyAction() + { + $wxpayService = new WxpayService(); + + $response = $wxpayService->notify(); + + if (!$response) exit; + + $response->send(); + + exit; + } + + /** + * @Post("/live/notify", name="web.live_notify") + */ + public function liveNotifyAction() + { + + } + } diff --git a/app/Http/Web/Controllers/WxpayController.php b/app/Http/Web/Controllers/WxpayController.php deleted file mode 100644 index bc6274ab..00000000 --- a/app/Http/Web/Controllers/WxpayController.php +++ /dev/null @@ -1,29 +0,0 @@ -notify(); - - if (!$response) exit; - - $response->send(); - - exit; - } - -} diff --git a/app/Http/Web/Services/Live.php b/app/Http/Web/Services/Live.php index 19cc4e4f..51981116 100644 --- a/app/Http/Web/Services/Live.php +++ b/app/Http/Web/Services/Live.php @@ -17,7 +17,9 @@ class Live extends Service $key = $this->getRedisListKey($id); - $items = $redis->lRange($key, 0, 10); + $redis->expire($key, 3 * 3600); + + $items = $redis->lRange($key, 0, 15); $result = []; @@ -88,7 +90,7 @@ class Live extends Service $content = $this->request->getPost('content', ['trim', 'striptags']); - $content = kg_substr($content, 0, 150); + $content = kg_substr($content, 0, 80); Gateway::$registerAddress = $this->getRegisterAddress(); @@ -111,9 +113,14 @@ class Live extends Service Gateway::sendToGroup($groupName, $encodeMessage, $clientId); $redis = $this->getRedis(); + $key = $this->getRedisListKey($id); + $redis->lPush($key, $encodeMessage); - $redis->lTrim($key, 0, 10); + + if ($redis->lLen($key) % 20 == 0) { + $redis->lTrim($key, 0, 15); + } return $message; } diff --git a/app/Http/Web/Views/chapter/live_chats.volt b/app/Http/Web/Views/chapter/live_chats.volt index 47bb3675..38362395 100644 --- a/app/Http/Web/Views/chapter/live_chats.volt +++ b/app/Http/Web/Views/chapter/live_chats.volt @@ -1,6 +1,6 @@ {% for chat in chats %}
- {% if chat.user.vip == 0 %} + {% if chat.user.vip == 1 %} {% endif %} {{ chat.user.name }} diff --git a/app/Http/Web/Views/chapter/show_live.volt b/app/Http/Web/Views/chapter/show_live.volt index 03ef0ef5..116e214a 100644 --- a/app/Http/Web/Views/chapter/show_live.volt +++ b/app/Http/Web/Views/chapter/show_live.volt @@ -31,12 +31,16 @@
-
-
- - -
-
+ {% if auth_user.id > 0 %} +
+
+ + +
+
+ {% else %} + + {% endif %}
diff --git a/app/Http/Web/Views/index/index.volt b/app/Http/Web/Views/index/index.volt index 985f432d..ca7dae70 100644 --- a/app/Http/Web/Views/index/index.volt +++ b/app/Http/Web/Views/index/index.volt @@ -68,19 +68,8 @@ {% endblock %} -{% block inline_js %} +{% block include_js %} - + {{ js_include('web/js/index.js') }} {% endblock %} \ No newline at end of file diff --git a/app/Http/Web/Views/templates/full.volt b/app/Http/Web/Views/templates/full.volt index 6d717396..7166528b 100644 --- a/app/Http/Web/Views/templates/full.volt +++ b/app/Http/Web/Views/templates/full.volt @@ -2,6 +2,7 @@ + diff --git a/app/Http/Web/Views/templates/layer.volt b/app/Http/Web/Views/templates/layer.volt index b9b1c962..e15ecd90 100644 --- a/app/Http/Web/Views/templates/layer.volt +++ b/app/Http/Web/Views/templates/layer.volt @@ -2,6 +2,7 @@ + {{ icon_link('favicon.ico') }} diff --git a/app/Library/CsrfToken.php b/app/Library/CsrfToken.php index 099450cb..2ef2a861 100644 --- a/app/Library/CsrfToken.php +++ b/app/Library/CsrfToken.php @@ -34,15 +34,21 @@ class CsrfToken public function checkToken($token) { + if (!$token) return false; + $text = $this->crypt->decryptBase64($token); - list($time, $fixed, $random) = explode($this->delimiter, $text); + $params = explode($this->delimiter, $text); - if ($time != intval($time) || $fixed != $this->fixed || strlen($random) != 8) { + if (!isset($params[0]) || !isset($params[1]) || !isset($params[2])) { return false; } - if (time() - $time > $this->lifetime) { + if ($params[0] != intval($params[0]) || $params[1] != $this->fixed || strlen($params[2]) != 8) { + return false; + } + + if (time() - $params[0] > $this->lifetime) { return false; } diff --git a/app/Services/Frontend/Chapter/ChapterInfo.php b/app/Services/Frontend/Chapter/ChapterInfo.php index deb7ee77..e0fe0601 100644 --- a/app/Services/Frontend/Chapter/ChapterInfo.php +++ b/app/Services/Frontend/Chapter/ChapterInfo.php @@ -130,11 +130,6 @@ class ChapterInfo extends FrontendService $playUrls = $service->getPlayUrls($chapter->id); - /** - * @var array $attrs - */ - $attrs = $chapter->attrs; - return [ 'id' => $chapter->id, 'title' => $chapter->title, @@ -150,22 +145,36 @@ class ChapterInfo extends FrontendService protected function formatChapterLive(ChapterModel $chapter) { - $liveService = new LiveService(); + $service = new LiveService(); - $playUrls = $liveService->getPullUrls("chapter_{$chapter->id}"); + $streamName = $this->getLiveStreamName($chapter->id); - /** - * @var array $attrs - */ - $attrs = $chapter->attrs; + $chapterRepo = new ChapterRepo(); + + $live = $chapterRepo->findChapterLive($chapter->id); + + $playUrls = []; + + if ($live->start_time - time() > 1800) { + $status = 'pending'; + } elseif (time() - $live->end_time > 1800) { + $status = 'finished'; + } else { + $status = $service->getStreamState($streamName); + } + + if ($status == 'active') { + $playUrls = $service->getPullUrls($streamName); + } return [ 'id' => $chapter->id, 'title' => $chapter->title, 'summary' => $chapter->summary, 'model' => $chapter->model, - 'start_time' => $attrs['start_time'], - 'end_time' => $attrs['end_time'], + 'status' => $status, + 'start_time' => $live->start_time, + 'end_time' => $live->end_time, 'play_urls' => $playUrls, 'user_count' => $chapter->user_count, 'agree_count' => $chapter->agree_count, @@ -180,11 +189,6 @@ class ChapterInfo extends FrontendService $read = $chapterRepo->findChapterRead($chapter->id); - /** - * @var array $attrs - */ - $attrs = $chapter->attrs; - return [ 'id' => $chapter->id, 'title' => $chapter->title, @@ -259,4 +263,9 @@ class ChapterInfo extends FrontendService $this->eventsManager->fire('chapterCounter:incrUserCount', $this, $chapter); } + protected function getLiveStreamName($id) + { + return "chapter_{$id}"; + } + } diff --git a/app/Services/Live.php b/app/Services/Live.php index 2f81bb92..038ecaa6 100644 --- a/app/Services/Live.php +++ b/app/Services/Live.php @@ -2,32 +2,191 @@ namespace App\Services; +use Phalcon\Logger\Adapter\File as FileLogger; +use TencentCloud\Common\Credential; +use TencentCloud\Common\Exception\TencentCloudSDKException; +use TencentCloud\Common\Profile\ClientProfile; +use TencentCloud\Common\Profile\HttpProfile; +use TencentCloud\Live\V20180801\LiveClient; +use TencentCloud\Live\V20180801\Models\DescribeLiveStreamStateRequest; +use TencentCloud\Live\V20180801\Models\ForbidLiveStreamRequest; +use TencentCloud\Live\V20180801\Models\ResumeLiveStreamRequest; + class Live extends Service { + const END_POINT = 'live.tencentcloudapi.com'; + /** * @var array */ protected $settings; + /** + * @var LiveClient + */ + protected $client; + + /** + * @var FileLogger + */ + protected $logger; + public function __construct() { $this->settings = $this->getSectionSettings('live'); + + $this->logger = $this->getLogger('live'); + + $this->client = $this->getLiveClient(); + } + + /** + * 获取流的状态 + * + * @param string $streamName + * @param string $appName + * @return string|bool + */ + public function getStreamState($streamName, $appName = 'live') + { + try { + + $request = new DescribeLiveStreamStateRequest(); + + $params = json_encode([ + 'DomainName' => $this->settings['push_domain'], + 'AppName' => $appName ?: 'live', + 'StreamName' => $streamName, + ]); + + $request->fromJsonString($params); + + $this->logger->debug('Describe Live Stream State Request ' . $params); + + $response = $this->client->DescribeLiveStreamState($request); + + $this->logger->debug('Describe Live Stream State Response ' . $response->toJsonString()); + + $result = $response->StreamState; + + } catch (TencentCloudSDKException $e) { + + $this->logger->error('Describe Live Stream State Exception ' . kg_json_encode([ + 'code' => $e->getErrorCode(), + 'message' => $e->getMessage(), + 'requestId' => $e->getRequestId(), + ])); + + $result = false; + } + + return $result; + } + + /** + * 禁推直播推流 + * + * @param string $streamName + * @param string $appName + * @param string $reason + * @return array|bool + */ + public function forbidStream($streamName, $appName = 'live', $reason = '') + { + try { + + $request = new ForbidLiveStreamRequest(); + + $params = json_encode([ + 'DomainName' => $this->settings['push_domain'], + 'AppName' => $appName ?: 'live', + 'StreamName' => $streamName, + 'Reason' => $reason, + ]); + + $request->fromJsonString($params); + + $this->logger->debug('Forbid Live Stream Request ' . $params); + + $response = $this->client->ForbidLiveStream($request); + + $this->logger->debug('Forbid Live Stream Response ' . $response->toJsonString()); + + $result = json_decode($response->toJsonString(), true); + + } catch (TencentCloudSDKException $e) { + + $this->logger->error('Forbid Live Stream Exception ' . kg_json_encode([ + 'code' => $e->getErrorCode(), + 'message' => $e->getMessage(), + 'requestId' => $e->getRequestId(), + ])); + + $result = false; + } + + return $result; + } + + /** + * 恢复直播推流 + * + * @param string $streamName + * @param string $appName + * @return array|bool + */ + public function resumeStream($streamName, $appName = 'live') + { + try { + + $request = new ResumeLiveStreamRequest(); + + $params = json_encode([ + 'DomainName' => $this->settings['push_domain'], + 'AppName' => $appName ?: 'live', + 'StreamName' => $streamName, + ]); + + $request->fromJsonString($params); + + $this->logger->debug('Resume Live Stream Request ' . $params); + + $response = $this->client->ResumeLiveStream($request); + + $this->logger->debug('Resume Live Stream Response ' . $response->toJsonString()); + + $result = json_decode($response->toJsonString(), true); + + } catch (TencentCloudSDKException $e) { + + $this->logger->error('Resume Live Stream Exception ' . kg_json_encode([ + 'code' => $e->getErrorCode(), + 'message' => $e->getMessage(), + 'requestId' => $e->getRequestId(), + ])); + + $result = false; + } + + return $result; } /** * 获取推流地址 * * @param string $streamName + * @param string $appName * @return string */ - function getPushUrl($streamName) + function getPushUrl($streamName, $appName = 'live') { + $appName = $appName ?: 'live'; + $authEnabled = $this->settings['push_auth_enabled']; $authKey = $this->settings['push_auth_key']; $expireTime = $this->settings['push_auth_delta'] + time(); $domain = $this->settings['push_domain']; - $appName = 'live'; $authParams = $this->getAuthParams($streamName, $authKey, $expireTime); @@ -46,6 +205,8 @@ class Live extends Service */ public function getPullUrls($streamName, $appName = 'live') { + $appName = $appName ?: 'live'; + $protocol = $this->settings['pull_protocol']; $domain = $this->settings['pull_domain']; $authEnabled = $this->settings['pull_auth_enabled']; @@ -116,4 +277,25 @@ class Live extends Service ]); } + protected function getLiveClient() + { + $secret = $this->getSectionSettings('secret'); + + $secretId = $secret['secret_id']; + $secretKey = $secret['secret_key']; + $region = ''; + + $credential = new Credential($secretId, $secretKey); + + $httpProfile = new HttpProfile(); + + $httpProfile->setEndpoint(self::END_POINT); + + $clientProfile = new ClientProfile(); + + $clientProfile->setHttpProfile($httpProfile); + + return new LiveClient($credential, $region, $clientProfile); + } + } diff --git a/config/config.default.php b/config/config.default.php index 70ad4f74..890d6181 100644 --- a/config/config.default.php +++ b/config/config.default.php @@ -123,12 +123,12 @@ $config['throttle']['lifetime'] = 60; $config['throttle']['rate_limit'] = 60; /** - * 客户端连接地址 + * 客户端连接地址(外部可访问的ip或域名) */ $config['websocket']['url'] = 'ws://127.0.0.1:8282'; /** - * gateway和worker注册地址 + * gateway和worker注册地址(内部访问) */ $config['websocket']['register_address'] = '127.0.0.1:1238'; diff --git a/public/static/web/css/common.css b/public/static/web/css/common.css index da2cb3da..b058d74a 100644 --- a/public/static/web/css/common.css +++ b/public/static/web/css/common.css @@ -183,6 +183,7 @@ .index-course-list .course-card .info { border: 1px solid #eee; + padding-bottom: 18px; } .index-carousel { @@ -803,6 +804,14 @@ color: #666; } +.chat-login-tips { + border-top: 1px solid #f2f2f2; + padding-top: 10px; + text-align: center; + font-size: 12px; + color: #999; +} + .chat-msg-form .layui-input { height: 32px; font-size: 12px; @@ -822,18 +831,6 @@ margin-right: 10px; } -.live-user-card { - margin-bottom: 10px; - line-height: 25px; -} - -.chat-login-tips { - padding-top: 20px; - text-align: center; - font-size: 12px; - color: #999; -} - .chapter-bg { background-color: #f2f2f2; } diff --git a/public/static/web/js/index.js b/public/static/web/js/index.js new file mode 100644 index 00000000..becd6b1f --- /dev/null +++ b/public/static/web/js/index.js @@ -0,0 +1,13 @@ +layui.use(['carousel', 'flow'], function () { + + var carousel = layui.carousel; + var flow = layui.flow; + + carousel.render({ + elem: '#carousel', + width: '100%', + height: '270px' + }); + + flow.lazyimg(); +}); \ No newline at end of file diff --git a/public/static/web/js/live.im.js b/public/static/web/js/live.im.js index 6acc035b..57bd40b4 100644 --- a/public/static/web/js/live.im.js +++ b/public/static/web/js/live.im.js @@ -65,7 +65,7 @@ layui.use(['jquery', 'form', 'helper'], function () { function showNewMessage(res) { var html = '
'; - if (res.user.vip === 0) { + if (res.user.vip === 1) { html += ''; } html += '' + res.user.name + ':';