From 6abca790bce8af6336fb9f4aad26a0a45746677c Mon Sep 17 00:00:00 2001 From: koogua Date: Thu, 18 Feb 2021 21:20:23 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dfile=5Ftranscode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Console/Tasks/CleanLogTask.php | 13 ++ app/Console/Tasks/LiveTeacherNoticeTask.php | 81 +++++++ app/Console/Tasks/MonitorTask.php | 180 +++++++++++++++ .../Admin/Controllers/SettingController.php | 26 +++ app/Http/Admin/Controllers/TestController.php | 17 ++ app/Http/Admin/Services/AuthNode.php | 6 + .../Admin/Views/setting/dingtalk_robot.volt | 55 +++++ app/Library/Benchmark.php | 27 +++ app/Library/Helper.php | 18 ++ app/Models/Task.php | 3 +- .../DingTalk/Notice/ConsultCreate.php | 32 +++ .../DingTalk/Notice/CustomService.php | 36 +++ app/Services/DingTalk/Notice/LiveTeacher.php | 26 +++ app/Services/DingTalk/Notice/OrderFinish.php | 27 +++ app/Services/DingTalkNotice.php | 214 ++++++++++++++++++ .../20210215024511_data_202102151130.php | 42 ++++ scheduler.php | 3 + 17 files changed, 805 insertions(+), 1 deletion(-) create mode 100644 app/Console/Tasks/LiveTeacherNoticeTask.php create mode 100644 app/Console/Tasks/MonitorTask.php create mode 100644 app/Http/Admin/Views/setting/dingtalk_robot.volt create mode 100644 app/Library/Benchmark.php create mode 100644 app/Services/DingTalk/Notice/ConsultCreate.php create mode 100644 app/Services/DingTalk/Notice/CustomService.php create mode 100644 app/Services/DingTalk/Notice/LiveTeacher.php create mode 100644 app/Services/DingTalk/Notice/OrderFinish.php create mode 100644 app/Services/DingTalkNotice.php create mode 100644 db/migrations/20210215024511_data_202102151130.php diff --git a/app/Console/Tasks/CleanLogTask.php b/app/Console/Tasks/CleanLogTask.php index e198d702..581d6163 100644 --- a/app/Console/Tasks/CleanLogTask.php +++ b/app/Console/Tasks/CleanLogTask.php @@ -26,6 +26,7 @@ class CleanLogTask extends Task $this->cleanOrderLog(); $this->cleanRefundLog(); $this->cleanPointLog(); + $this->cleanDingTalkLog(); $this->cleanNoticeLog(); $this->cleanOtherLog(); } @@ -234,6 +235,18 @@ class CleanLogTask extends Task $this->whitelist[] = $type; } + /** + * 清理钉钉日志 + */ + protected function cleanDingTalkLog() + { + $type = 'dingtalk'; + + $this->cleanLog($type, 7); + + $this->whitelist[] = $type; + } + /** * 清理通知日志 */ diff --git a/app/Console/Tasks/LiveTeacherNoticeTask.php b/app/Console/Tasks/LiveTeacherNoticeTask.php new file mode 100644 index 00000000..49e25ed7 --- /dev/null +++ b/app/Console/Tasks/LiveTeacherNoticeTask.php @@ -0,0 +1,81 @@ +findLives(); + + if ($lives->count() == 0) return; + + $redis = $this->getRedis(); + + $keyName = $this->getCacheKeyName(); + + foreach ($lives as $live) { + $redis->sAdd($keyName, $live->chapter_id); + } + + $redis->expire($keyName, 86400); + } + + /** + * 消费讲师提醒 + */ + public function consumeAction() + { + $redis = $this->getRedis(); + + $keyName = $this->getCacheKeyName(); + + $chapterIds = $redis->sMembers($keyName); + + if (count($chapterIds) == 0) return; + + $chapterRepo = new ChapterRepo(); + + $notice = new LiveTeacherNotice(); + + foreach ($chapterIds as $chapterId) { + + $live = $chapterRepo->findChapterLive($chapterId); + + if ($live->start_time - time() < 30 * 60) { + + $notice->handle($live); + + $redis->sRem($keyName, $chapterId); + } + } + } + + /** + * @return ResultsetInterface|Resultset|ChapterLiveModel[] + */ + protected function findLives() + { + $today = strtotime(date('Ymd')); + + return ChapterLiveModel::query() + ->betweenWhere('start_time', $today, $today + 86400) + ->execute(); + } + + protected function getCacheKeyName() + { + return 'live_teacher_notice'; + } + +} \ No newline at end of file diff --git a/app/Console/Tasks/MonitorTask.php b/app/Console/Tasks/MonitorTask.php new file mode 100644 index 00000000..89481454 --- /dev/null +++ b/app/Console/Tasks/MonitorTask.php @@ -0,0 +1,180 @@ + $this->checkCPU(), + 'disk' => $this->checkDisk(), + 'mysql' => $this->checkMysql(), + 'redis' => $this->checkRedis(), + 'xunsearch' => $this->checkXunSearch(), + 'websocket' => $this->checkWebSocket(), + ]; + + foreach ($items as $key => $value) { + if (empty($value)) { + unset($items[$key]); + } + } + + if (empty($items)) return; + + $notice = new DingTalkNotice(); + + $content = implode("\n", $items); + + $notice->atTechSupport($content); + } + + protected function checkCPU() + { + $coreCount = $this->getCpuCoreCount(); + + $cpu = ServerInfo::cpu(); + + if ($cpu[1] > $coreCount / 2) { + return sprintf("cpu负载超过%s", $cpu[1]); + } + } + + protected function checkDisk() + { + $disk = ServerInfo::disk(); + + if ($disk['percent'] > 80) { + return sprintf("disk空间超过%s%%", $disk['percent']); + } + } + + protected function checkMysql() + { + try { + + $benchmark = new Benchmark(); + + $benchmark->start(); + + $user = UserModel::findFirst(); + + $benchmark->stop(); + + $elapsedTime = $benchmark->getElapsedTime(); + + if ($user === false) { + return sprintf("mysql查询失败"); + } + + if ($elapsedTime > 1) { + return sprintf("mysql查询响应超过%s秒", round($elapsedTime, 2)); + } + + } catch (\Exception $e) { + return sprintf("mysql可能存在异常"); + } + } + + protected function checkRedis() + { + try { + + $benchmark = new Benchmark(); + + $benchmark->start(); + + $site = $this->getSettings('site'); + + $benchmark->stop(); + + $elapsedTime = $benchmark->getElapsedTime(); + + if (empty($site)) { + return sprintf("redis查询失败"); + } + + if ($elapsedTime > 1) { + return sprintf("redis查询响应超过%s秒", round($elapsedTime, 2)); + } + + } catch (\Exception $e) { + return sprintf("redis可能存在异常"); + } + } + + protected function checkXunSearch() + { + try { + + $benchmark = new Benchmark(); + + $benchmark->start(); + + $searcher = new UserSearcher(); + + $user = $searcher->search('id:10000'); + + $benchmark->stop(); + + $elapsedTime = $benchmark->getElapsedTime(); + + if (empty($user)) { + return sprintf("xunsearch搜索失败"); + } + + if ($elapsedTime > 1) { + return sprintf("xunsearch搜索响应超过%s秒", round($elapsedTime, 2)); + } + + } catch (\Exception $e) { + return sprintf("xunsearch可能存在异常"); + } + } + + protected function checkWebSocket() + { + try { + + $benchmark = new Benchmark(); + + $config = $this->getConfig(); + + Gateway::$registerAddress = $config->path('websocket.register_address'); + + $benchmark->start(); + + Gateway::isUidOnline(10000); + + $benchmark->stop(); + + $elapsedTime = $benchmark->getElapsedTime(); + + if ($elapsedTime > 1) { + return sprintf("websocket响应超过%s秒", round($elapsedTime, 2)); + } + + } catch (\Exception $e) { + return sprintf("websocket可能存在异常"); + } + } + + protected function getCpuCoreCount() + { + $cpuInfo = file_get_contents('/proc/cpuinfo'); + + preg_match_all('/^processor/m', $cpuInfo, $matches); + + return count($matches[0]); + } + +} \ No newline at end of file diff --git a/app/Http/Admin/Controllers/SettingController.php b/app/Http/Admin/Controllers/SettingController.php index 76cff636..c7734972 100644 --- a/app/Http/Admin/Controllers/SettingController.php +++ b/app/Http/Admin/Controllers/SettingController.php @@ -378,4 +378,30 @@ class SettingController extends Controller } } + /** + * @Route("/dingtalk/robot", name="admin.setting.dingtalk_robot") + */ + public function dingtalkRobotAction() + { + $section = 'dingtalk.robot'; + + $settingService = new SettingService(); + + if ($this->request->isPost()) { + + $data = $this->request->getPost(); + + $settingService->updatePointSettings($section, $data); + + return $this->jsonSuccess(['msg' => '更新配置成功']); + + } else { + + $robot = $settingService->getSettings($section); + + $this->view->pick('setting/dingtalk_robot'); + $this->view->setVar('robot', $robot); + } + } + } diff --git a/app/Http/Admin/Controllers/TestController.php b/app/Http/Admin/Controllers/TestController.php index 4db114d2..d159935d 100644 --- a/app/Http/Admin/Controllers/TestController.php +++ b/app/Http/Admin/Controllers/TestController.php @@ -6,6 +6,7 @@ use App\Http\Admin\Services\AlipayTest as AlipayTestService; use App\Http\Admin\Services\Setting as SettingService; use App\Http\Admin\Services\WxpayTest as WxpayTestService; use App\Services\Captcha as CaptchaService; +use App\Services\DingTalkNotice as DingTalkNoticeService; use App\Services\Live as LiveService; use App\Services\Mail\Test as TestMailService; use App\Services\MyStorage as StorageService; @@ -250,4 +251,20 @@ class TestController extends Controller return $this->jsonSuccess(['status' => $status]); } + /** + * @Post("/dingtalk/robot", name="admin.test.dingtalk_robot") + */ + public function dingTalkRobotAction() + { + $noticeService = new DingTalkNoticeService(); + + $result = $noticeService->test(); + + if ($result) { + return $this->jsonSuccess(['msg' => '发送消息成功,请到钉钉确认']); + } else { + return $this->jsonError(['msg' => '发送消息失败,请检查配置']); + } + } + } \ No newline at end of file diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 20dedbb5..374aa629 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -800,6 +800,12 @@ class AuthNode extends Service 'type' => 'menu', 'route' => 'admin.setting.wechat_oa', ], + [ + 'id' => '5-1-15', + 'title' => '钉钉机器人', + 'type' => 'menu', + 'route' => 'admin.setting.dingtalk_robot', + ], [ 'id' => '5-1-14', 'title' => '积分设置', diff --git a/app/Http/Admin/Views/setting/dingtalk_robot.volt b/app/Http/Admin/Views/setting/dingtalk_robot.volt new file mode 100644 index 00000000..f243c6c0 --- /dev/null +++ b/app/Http/Admin/Views/setting/dingtalk_robot.volt @@ -0,0 +1,55 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + +
+
+ 钉钉机器人 +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+
+ 通知测试 +
+
+ +
+ + +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/app/Library/Benchmark.php b/app/Library/Benchmark.php new file mode 100644 index 00000000..e6943787 --- /dev/null +++ b/app/Library/Benchmark.php @@ -0,0 +1,27 @@ +startTime = microtime(true); + } + + public function stop() + { + $this->endTime = microtime(true); + } + + public function getElapsedTime() + { + return $this->endTime - $this->startTime; + } + +} \ No newline at end of file diff --git a/app/Library/Helper.php b/app/Library/Helper.php index af1d528a..47363e5c 100644 --- a/app/Library/Helper.php +++ b/app/Library/Helper.php @@ -35,6 +35,24 @@ function kg_substr($str, $start, $length, $suffix = '...') return $str == $result ? $str : $result . $suffix; } +/** + * 占位替换 + * + * @param string $str + * @param array $data + * @return string + */ +function kg_ph_replace($str, $data = []) +{ + if (empty($data)) return $str; + + foreach ($data as $key => $value) { + $str = str_replace('{' . $key . '}', $value, $str); + } + + return $str; +} + /** * uniqid封装 * diff --git a/app/Models/Task.php b/app/Models/Task.php index 58fb19fc..ad9f6953 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -14,10 +14,11 @@ class Task extends Model const TYPE_LUCKY_GIFT_DELIVER = 4; // 抽奖礼品派发 const TYPE_NOTICE_ACCOUNT_LOGIN = 11; // 帐号登录通知 - const TYPE_NOTICE_LIVE_BEGIN = 12; // 直播开始通知 + const TYPE_NOTICE_LIVE_BEGIN = 12; // 直播学员通知 const TYPE_NOTICE_ORDER_FINISH = 13; // 订单完成通知 const TYPE_NOTICE_REFUND_FINISH = 14; // 退款完成通知 const TYPE_NOTICE_CONSULT_REPLY = 15; // 咨询回复通知 + const TYPE_NOTICE_LIVE_TEACHER = 16; // 直播讲师通知 /** * 优先级 diff --git a/app/Services/DingTalk/Notice/ConsultCreate.php b/app/Services/DingTalk/Notice/ConsultCreate.php new file mode 100644 index 00000000..d88d3b24 --- /dev/null +++ b/app/Services/DingTalk/Notice/ConsultCreate.php @@ -0,0 +1,32 @@ +findById($consult->owner_id); + + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($consult->course_id); + + $content = kg_ph_replace("{user.name} 对课程:{course.title} 发起了咨询:\n{consult.question}", [ + 'user.name' => $user->name, + 'course.title' => $course->title, + 'consult.question' => $consult->question, + ]); + + $this->atCourseTeacher($course->id, $content); + } + +} \ No newline at end of file diff --git a/app/Services/DingTalk/Notice/CustomService.php b/app/Services/DingTalk/Notice/CustomService.php new file mode 100644 index 00000000..78b870ff --- /dev/null +++ b/app/Services/DingTalk/Notice/CustomService.php @@ -0,0 +1,36 @@ +sender_id}"; + + $cache = $this->getCache(); + + $content = $cache->get($keyName); + + if ($content) return; + + $cache->save($keyName, 1, 3600); + + $userRepo = new UserRepo(); + + $sender = $userRepo->findById($message->sender_id); + + $content = kg_ph_replace("{user} 通过在线客服给你发送了消息:{message}", [ + 'user' => $sender->name, + 'message' => $message->content, + ]); + + $this->atCustomService($content); + } + +} \ No newline at end of file diff --git a/app/Services/DingTalk/Notice/LiveTeacher.php b/app/Services/DingTalk/Notice/LiveTeacher.php new file mode 100644 index 00000000..1e4ce7f8 --- /dev/null +++ b/app/Services/DingTalk/Notice/LiveTeacher.php @@ -0,0 +1,26 @@ +findById($live->course_id); + + $content = kg_ph_replace("课程:{course.title} 计划于 {live.start_time} 开播,不要错过直播时间哦!", [ + 'course.title' => $course->title, + 'live.start_time' => date('Y-m-d H:i', $live->start_time), + ]); + + $this->atCourseTeacher($course->id, $content); + } + +} \ No newline at end of file diff --git a/app/Services/DingTalk/Notice/OrderFinish.php b/app/Services/DingTalk/Notice/OrderFinish.php new file mode 100644 index 00000000..cc360b57 --- /dev/null +++ b/app/Services/DingTalk/Notice/OrderFinish.php @@ -0,0 +1,27 @@ +findById($order->owner_id); + + $text = kg_ph_replace("开单啦,{user.name} 同学完成了订单!\n订单名称:{order.subject}\n订单金额:¥{order.amount}", [ + 'user.name' => $user->name, + 'order.subject' => $order->subject, + 'order.amount' => $order->amount, + ]); + + $this->send(['text' => $text]); + } + +} \ No newline at end of file diff --git a/app/Services/DingTalkNotice.php b/app/Services/DingTalkNotice.php new file mode 100644 index 00000000..fad0c1f0 --- /dev/null +++ b/app/Services/DingTalkNotice.php @@ -0,0 +1,214 @@ +settings = $this->getSettings('dingtalk.robot'); + + $this->logger = $this->getLogger('dingtalk'); + } + + /** + * 测试消息 + * + * @return bool + */ + public function test() + { + $params = [ + 'msgtype' => 'text', + 'text' => ['content' => '我是一条测试消息啦!'], + ]; + + return $this->send($params); + } + + /** + * 给技术人员发消息 + * + * @param string $content + * @return bool + */ + public function atTechSupport($content) + { + $atMobiles = $this->parseAtMobiles($this->settings['ts_mobiles']); + $atContent = $this->buildAtContent($content, $atMobiles); + + $params = [ + 'msgtype' => 'text', + 'text' => ['content' => $atContent], + 'at' => ['atMobiles' => $atMobiles], + ]; + + return $this->send($params); + } + + /** + * 给客服人员发消息 + * + * @param string $content + * @return bool + */ + public function atCustomService($content) + { + $atMobiles = $this->parseAtMobiles($this->settings['cs_mobiles']); + $atContent = $this->buildAtContent($content, $atMobiles); + + $params = [ + 'msgtype' => 'text', + 'text' => ['content' => $atContent], + 'at' => ['atMobiles' => $atMobiles], + ]; + + return $this->send($params); + } + + /** + * 给课程讲师发消息 + * + * @param int $courseId + * @param string $content + * @return bool + */ + public function atCourseTeacher($courseId, $content) + { + $courseRepo = new CourseRepo(); + + $course = $courseRepo->findById($courseId); + + $accountRepo = new AccountRepo(); + + $account = $accountRepo->findById($course->teacher_id); + + if (empty($account->phone)) { + return false; + } + + $atMobiles = $account->phone; + + $atContent = $this->buildAtContent($content, $atMobiles); + + $params = [ + 'msgtype' => 'text', + 'text' => ['content' => $atContent], + 'at' => ['atMobiles' => $atMobiles], + ]; + + return $this->send($params); + } + + /** + * 发送消息 + * + * @param array $params + * @return bool + */ + public function send($params) + { + if (isset($params['msgtype'])) { + $params['msgtype'] = 'text'; + } + + $webHook = "https://oapi.dingtalk.com/robot/send?access_token=%s×tamp=%s&sign=%s"; + + $appSecret = $this->settings['app_secret']; + $appToken = $this->settings['app_token']; + + $timestamp = time() * 1000; + $data = sprintf("%s\n%s", $timestamp, $appSecret); + $sign = urlencode(base64_encode(hash_hmac('sha256', $data, $appSecret, true))); + $postUrl = sprintf($webHook, $appToken, $timestamp, $sign); + + try { + + $client = new HttpClient(); + + $response = $client->post($postUrl, ['json' => $params]); + + $content = $response->getBody()->getContents(); + + $content = json_decode($content, true); + + $this->logger->debug('Send Message Request ' . kg_json_encode($params)); + + $this->logger->debug('Send Message Response ' . kg_json_encode($content)); + + $result = $content['errcode'] == 0; + + if ($result == false) { + $this->logger->error('Send Message Failed ' . kg_json_encode($content)); + } + + } catch (\Exception $e) { + + $this->logger->error('Send Message Exception ' . kg_json_encode([ + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'message' => $e->getMessage(), + ])); + + $result = false; + } + + return $result; + } + + /** + * @param string $mobiles + * @return array + */ + protected function parseAtMobiles($mobiles) + { + if (empty($mobiles)) { + return []; + } + + $mobiles = explode(',', $mobiles); + + return array_map(function ($mobile) { + return trim($mobile); + }, $mobiles); + } + + /** + * @param string $content + * @param array $mobiles + * @return string + */ + protected function buildAtContent($content, $mobiles = []) + { + if (count($mobiles) == 0) { + return $content; + } + + $result = ''; + + foreach ($mobiles as $mobile) { + $result .= sprintf('@%s ', $mobile); + } + + $result .= $content; + + return $result; + } + +} \ No newline at end of file diff --git a/db/migrations/20210215024511_data_202102151130.php b/db/migrations/20210215024511_data_202102151130.php new file mode 100644 index 00000000..f5fd6419 --- /dev/null +++ b/db/migrations/20210215024511_data_202102151130.php @@ -0,0 +1,42 @@ + 'dingtalk.robot', + 'item_key' => 'app_secret', + 'item_value' => '', + ], + [ + 'section' => 'dingtalk.robot', + 'item_key' => 'app_token', + 'item_value' => '', + ], + [ + 'section' => 'dingtalk.robot', + 'item_key' => 'ts_mobiles', + 'item_value' => '', + ], + [ + 'section' => 'dingtalk.robot', + 'item_key' => 'cs_mobiles', + 'item_value' => '', + ], + ]; + + $this->table('kg_setting')->insert($rows)->save(); + } + + public function down() + { + $this->getQueryBuilder() + ->delete('kg_setting') + ->where(['section' => 'dingtalk.robot']) + ->execute(); + } + +} diff --git a/scheduler.php b/scheduler.php index 666bb7e4..000900f3 100644 --- a/scheduler.php +++ b/scheduler.php @@ -25,6 +25,9 @@ $scheduler->php($script, $bin, ['--task' => 'vod_event', '--action' => 'main']) $scheduler->php($script, $bin, ['--task' => 'close_trade', '--action' => 'main']) ->at('*/13 * * * *'); +$scheduler->php($script, $bin, ['--task' => 'monitor', '--action' => 'main']) + ->at('*/12 * * * *'); + $scheduler->php($script, $bin, ['--task' => 'point_gift_deliver', '--action' => 'main']) ->at('*/11 * * * *'); From d798a5b609cad2d45a14c7abbe626e6006996044 Mon Sep 17 00:00:00 2001 From: koogua Date: Mon, 22 Feb 2021 11:31:21 +0800 Subject: [PATCH 2/2] =?UTF-8?q?dingtalk=E9=80=9A=E7=9F=A5=E9=98=B6?= =?UTF-8?q?=E6=AE=B5=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Console/Tasks/DeliverTask.php | 8 +-- app/Console/Tasks/NoticeTask.php | 54 ++++++++++++-- app/Console/Tasks/OptimizeTableTask.php | 71 +++++++++++++++++++ app/Console/Tasks/PointGiftDeliverTask.php | 11 ++- app/Console/Tasks/RefundTask.php | 11 +-- ...{MonitorTask.php => ServerMonitorTask.php} | 30 +++++--- ...ticeTask.php => TeacherLiveNoticeTask.php} | 24 +++---- .../Admin/Views/setting/dingtalk_robot.volt | 7 ++ app/Http/Home/Controllers/ImController.php | 4 +- app/Http/Home/Services/ImMessageTrait.php | 20 +++++- app/Models/Task.php | 21 +++++- app/Repos/ChapterLive.php | 25 +++++++ .../DingTalk/Notice/ConsultCreate.php | 37 +++++++++- .../DingTalk/Notice/CustomService.php | 41 ++++++++--- app/Services/DingTalk/Notice/LiveTeacher.php | 29 +++++++- app/Services/DingTalk/Notice/OrderFinish.php | 27 ------- app/Services/DingTalk/Notice/PointRedeem.php | 47 ++++++++++++ .../DingTalk/Notice/ServerMonitor.php | 36 ++++++++++ app/Services/Logic/Consult/ConsultCreate.php | 12 ++++ app/Services/Logic/Notice/AccountLogin.php | 1 + app/Services/Logic/Notice/ConsultReply.php | 1 + app/Services/Logic/Notice/LiveBegin.php | 1 + .../20210215024511_data_202102151130.php | 5 ++ .../20210215034511_schema_202102151230.php | 28 ++++++++ public/static/home/js/im.cs.js | 4 +- scheduler.php | 23 ++++-- 26 files changed, 475 insertions(+), 103 deletions(-) create mode 100644 app/Console/Tasks/OptimizeTableTask.php rename app/Console/Tasks/{MonitorTask.php => ServerMonitorTask.php} (84%) rename app/Console/Tasks/{LiveTeacherNoticeTask.php => TeacherLiveNoticeTask.php} (67%) delete mode 100644 app/Services/DingTalk/Notice/OrderFinish.php create mode 100644 app/Services/DingTalk/Notice/PointRedeem.php create mode 100644 app/Services/DingTalk/Notice/ServerMonitor.php create mode 100644 db/migrations/20210215034511_schema_202102151230.php diff --git a/app/Console/Tasks/DeliverTask.php b/app/Console/Tasks/DeliverTask.php index d41a22b7..e15007da 100644 --- a/app/Console/Tasks/DeliverTask.php +++ b/app/Console/Tasks/DeliverTask.php @@ -21,17 +21,13 @@ use Phalcon\Mvc\Model\ResultsetInterface; class DeliverTask extends Task { - const TRY_COUNT = 3; - public function mainAction() { $logger = $this->getLogger('order'); $tasks = $this->findTasks(30); - if ($tasks->count() == 0) { - return; - } + if ($tasks->count() == 0) return; $orderRepo = new OrderRepo(); @@ -84,7 +80,7 @@ class DeliverTask extends Task $task->try_count += 1; $task->priority += 1; - if ($task->try_count > self::TRY_COUNT) { + if ($task->try_count > $task->max_try_count) { $task->status = TaskModel::STATUS_FAILED; } diff --git a/app/Console/Tasks/NoticeTask.php b/app/Console/Tasks/NoticeTask.php index 7d17318d..17613ae7 100644 --- a/app/Console/Tasks/NoticeTask.php +++ b/app/Console/Tasks/NoticeTask.php @@ -3,6 +3,10 @@ namespace App\Console\Tasks; use App\Models\Task as TaskModel; +use App\Services\DingTalk\Notice\ConsultCreate as ConsultCreateNotice; +use App\Services\DingTalk\Notice\CustomService as CustomServiceNotice; +use App\Services\DingTalk\Notice\ServerMonitor as ServerMonitorNotice; +use App\Services\DingTalk\Notice\TeacherLive as TeacherLiveNotice; use App\Services\Logic\Notice\AccountLogin as AccountLoginNotice; use App\Services\Logic\Notice\ConsultReply as ConsultReplyNotice; use App\Services\Logic\Notice\LiveBegin as LiveBeginNotice; @@ -14,8 +18,6 @@ use Phalcon\Mvc\Model\ResultsetInterface; class NoticeTask extends Task { - const TRY_COUNT = 3; - public function mainAction() { $logger = $this->getLogger('notice'); @@ -46,6 +48,18 @@ class NoticeTask extends Task case TaskModel::TYPE_NOTICE_CONSULT_REPLY: $this->handleConsultReplyNotice($task); break; + case TaskModel::TYPE_NOTICE_CONSULT_CREATE: + $this->handleConsultCreateNotice($task); + break; + case TaskModel::TYPE_NOTICE_TEACHER_LIVE: + $this->handleTeacherLiveNotice($task); + break; + case TaskModel::TYPE_NOTICE_SERVER_MONITOR: + $this->handleServerMonitorNotice($task); + break; + case TaskModel::TYPE_NOTICE_CUSTOM_SERVICE: + $this->handleCustomServiceNotice($task); + break; } $task->status = TaskModel::STATUS_FINISHED; @@ -57,7 +71,7 @@ class NoticeTask extends Task $task->try_count += 1; $task->priority += 1; - if ($task->try_count > self::TRY_COUNT) { + if ($task->try_count >= $task->max_try_count) { $task->status = TaskModel::STATUS_FAILED; } @@ -108,11 +122,39 @@ class NoticeTask extends Task return $notice->handleTask($task); } + protected function handleConsultCreateNotice(TaskModel $task) + { + $notice = new ConsultCreateNotice(); + + return $notice->handleTask($task); + } + + protected function handleTeacherLiveNotice(TaskModel $task) + { + $notice = new TeacherLiveNotice(); + + return $notice->handleTask($task); + } + + protected function handleServerMonitorNotice(TaskModel $task) + { + $notice = new ServerMonitorNotice(); + + return $notice->handleTask($task); + } + + protected function handleCustomServiceNotice(TaskModel $task) + { + $notice = new CustomServiceNotice(); + + return $notice->handleTask($task); + } + /** * @param int $limit * @return ResultsetInterface|Resultset|TaskModel[] */ - protected function findTasks($limit = 100) + protected function findTasks($limit = 300) { $itemTypes = [ TaskModel::TYPE_NOTICE_ACCOUNT_LOGIN, @@ -120,6 +162,10 @@ class NoticeTask extends Task TaskModel::TYPE_NOTICE_ORDER_FINISH, TaskModel::TYPE_NOTICE_REFUND_FINISH, TaskModel::TYPE_NOTICE_CONSULT_REPLY, + TaskModel::TYPE_NOTICE_CONSULT_CREATE, + TaskModel::TYPE_NOTICE_TEACHER_LIVE, + TaskModel::TYPE_NOTICE_SERVER_MONITOR, + TaskModel::TYPE_NOTICE_CUSTOM_SERVICE, ]; $status = TaskModel::STATUS_PENDING; diff --git a/app/Console/Tasks/OptimizeTableTask.php b/app/Console/Tasks/OptimizeTableTask.php new file mode 100644 index 00000000..7143c0b7 --- /dev/null +++ b/app/Console/Tasks/OptimizeTableTask.php @@ -0,0 +1,71 @@ +optimizeImMessageTable(); + $this->optimizeLearningTable(); + $this->optimizeTaskTable(); + } + + protected function optimizeImMessageTable() + { + $count = ImMessageModel::count(); + + if ($count < 1000000) return; + + $messageModel = new ImMessageModel(); + + $tableName = $messageModel->getSource(); + + $this->db->delete($tableName, "create_time < :create_time", [ + 'create_time' => strtotime('-6 months'), + ]); + + $this->db->execute("OPTIMIZE TABLE {$tableName}"); + } + + protected function optimizeLearningTable() + { + $count = LearningModel::count(); + + if ($count < 1000000) return; + + $learningModel = new LearningModel(); + + $tableName = $learningModel->getSource(); + + $this->db->delete($tableName, "create_time < :create_time", [ + 'create_time' => strtotime('-6 months'), + ]); + + $this->db->execute("OPTIMIZE TABLE {$tableName}"); + } + + protected function optimizeTaskTable() + { + $count = TaskModel::count(); + + if ($count < 1000000) return; + + $taskModel = new TaskModel(); + + $tableName = $taskModel->getSource(); + + $this->db->delete($tableName, "create_time < :create_time AND status > :status", [ + 'create_time' => strtotime('-6 months'), + 'status' => TaskModel::STATUS_PENDING, + ]); + + $this->db->execute("OPTIMIZE TABLE {$tableName}"); + } + +} \ No newline at end of file diff --git a/app/Console/Tasks/PointGiftDeliverTask.php b/app/Console/Tasks/PointGiftDeliverTask.php index 0f8baac9..1af243e7 100644 --- a/app/Console/Tasks/PointGiftDeliverTask.php +++ b/app/Console/Tasks/PointGiftDeliverTask.php @@ -13,6 +13,7 @@ use App\Repos\ImGroup as ImGroupRepo; use App\Repos\ImGroupUser as ImGroupUserRepo; use App\Repos\PointGift as PointGiftRepo; use App\Repos\PointRedeem as PointRedeemRepo; +use App\Services\DingTalk\Notice\PointRedeem as PointRedeemNotice; use App\Services\Logic\Point\PointHistory as PointHistoryService; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; @@ -20,17 +21,13 @@ use Phalcon\Mvc\Model\ResultsetInterface; class PointGiftDeliverTask extends Task { - const TRY_COUNT = 3; - public function mainAction() { $logger = $this->getLogger('point'); $tasks = $this->findTasks(30); - if ($tasks->count() == 0) { - return; - } + if ($tasks->count() == 0) return; $redeemRepo = new PointRedeemRepo(); @@ -77,7 +74,7 @@ class PointGiftDeliverTask extends Task $task->try_count += 1; $task->priority += 1; - if ($task->try_count > self::TRY_COUNT) { + if ($task->try_count > $task->max_try_count) { $task->status = TaskModel::STATUS_FAILED; } @@ -167,7 +164,9 @@ class PointGiftDeliverTask extends Task protected function handleGoodsRedeem(PointRedeemModel $redeem) { + $notice = new PointRedeemNotice(); + $notice->createTask($redeem); } protected function handleCashRedeem(PointRedeemModel $redeem) diff --git a/app/Console/Tasks/RefundTask.php b/app/Console/Tasks/RefundTask.php index 4abc568e..996dde4b 100644 --- a/app/Console/Tasks/RefundTask.php +++ b/app/Console/Tasks/RefundTask.php @@ -20,20 +20,13 @@ use Phalcon\Mvc\Model\ResultsetInterface; class RefundTask extends Task { - /** - * 重试次数 - */ - const TRY_COUNT = 3; - public function mainAction() { $logger = $this->getLogger('refund'); $tasks = $this->findTasks(30); - if ($tasks->count() == 0) { - return; - } + if ($tasks->count() == 0) return; $tradeRepo = new TradeRepo(); $orderRepo = new OrderRepo(); @@ -103,7 +96,7 @@ class RefundTask extends Task $task->try_count += 1; $task->priority += 1; - if ($task->try_count > self::TRY_COUNT) { + if ($task->try_count > $task->max_try_count) { $task->status = TaskModel::STATUS_FAILED; } diff --git a/app/Console/Tasks/MonitorTask.php b/app/Console/Tasks/ServerMonitorTask.php similarity index 84% rename from app/Console/Tasks/MonitorTask.php rename to app/Console/Tasks/ServerMonitorTask.php index 89481454..7c2f4486 100644 --- a/app/Console/Tasks/MonitorTask.php +++ b/app/Console/Tasks/ServerMonitorTask.php @@ -5,15 +5,19 @@ namespace App\Console\Tasks; use App\Library\Benchmark; use App\Library\Utils\ServerInfo; use App\Models\User as UserModel; -use App\Services\DingTalkNotice; +use App\Services\DingTalk\Notice\ServerMonitor as ServerMonitorNotice; use App\Services\Search\UserSearcher; use GatewayClient\Gateway; -class MonitorTask extends Task +class ServerMonitorTask extends Task { public function mainAction() { + $robot = $this->getSettings('dingtalk.robot'); + + if ($robot['enabled'] == 0) return; + $items = [ 'cpu' => $this->checkCPU(), 'disk' => $this->checkDisk(), @@ -31,20 +35,20 @@ class MonitorTask extends Task if (empty($items)) return; - $notice = new DingTalkNotice(); - $content = implode("\n", $items); - $notice->atTechSupport($content); + $notice = new ServerMonitorNotice(); + + $notice->createTask($content); } protected function checkCPU() { - $coreCount = $this->getCpuCoreCount(); + $coreCount = $this->getCpuCount(); $cpu = ServerInfo::cpu(); - if ($cpu[1] > $coreCount / 2) { + if ($cpu[1] > $coreCount * 0.8) { return sprintf("cpu负载超过%s", $cpu[1]); } } @@ -168,13 +172,19 @@ class MonitorTask extends Task } } - protected function getCpuCoreCount() + protected function getCpuCount() { $cpuInfo = file_get_contents('/proc/cpuinfo'); - preg_match_all('/^processor/m', $cpuInfo, $matches); + preg_match("/^cpu cores\s:\s(\d+)/m", $cpuInfo, $matches); - return count($matches[0]); + $coreCount = intval($matches[1]); + + preg_match_all("/^processor/m", $cpuInfo, $matches); + + $processorCount = count($matches[0]); + + return $coreCount * $processorCount; } } \ No newline at end of file diff --git a/app/Console/Tasks/LiveTeacherNoticeTask.php b/app/Console/Tasks/TeacherLiveNoticeTask.php similarity index 67% rename from app/Console/Tasks/LiveTeacherNoticeTask.php rename to app/Console/Tasks/TeacherLiveNoticeTask.php index 49e25ed7..fac00d68 100644 --- a/app/Console/Tasks/LiveTeacherNoticeTask.php +++ b/app/Console/Tasks/TeacherLiveNoticeTask.php @@ -3,12 +3,12 @@ namespace App\Console\Tasks; use App\Models\ChapterLive as ChapterLiveModel; -use App\Repos\Chapter as ChapterRepo; -use App\Services\DingTalk\Notice\LiveTeacher as LiveTeacherNotice; +use App\Repos\ChapterLive as ChapterLiveRepo; +use App\Services\DingTalk\Notice\TeacherLive as TeacherLiveNotice; use Phalcon\Mvc\Model\Resultset; use Phalcon\Mvc\Model\ResultsetInterface; -class LiveTeacherNoticeTask extends Task +class TeacherLiveNoticeTask extends Task { /** @@ -40,23 +40,23 @@ class LiveTeacherNoticeTask extends Task $keyName = $this->getCacheKeyName(); - $chapterIds = $redis->sMembers($keyName); + $liveIds = $redis->sMembers($keyName); - if (count($chapterIds) == 0) return; + if (count($liveIds) == 0) return; - $chapterRepo = new ChapterRepo(); + $liveRepo = new ChapterLiveRepo(); - $notice = new LiveTeacherNotice(); + $notice = new TeacherLiveNotice(); - foreach ($chapterIds as $chapterId) { + foreach ($liveIds as $liveId) { - $live = $chapterRepo->findChapterLive($chapterId); + $live = $liveRepo->findById($liveId); if ($live->start_time - time() < 30 * 60) { - $notice->handle($live); + $notice->createTask($live); - $redis->sRem($keyName, $chapterId); + $redis->sRem($keyName, $liveId); } } } @@ -75,7 +75,7 @@ class LiveTeacherNoticeTask extends Task protected function getCacheKeyName() { - return 'live_teacher_notice'; + return 'teacher_live_notice_task'; } } \ No newline at end of file diff --git a/app/Http/Admin/Views/setting/dingtalk_robot.volt b/app/Http/Admin/Views/setting/dingtalk_robot.volt index f243c6c0..9d62e7f4 100644 --- a/app/Http/Admin/Views/setting/dingtalk_robot.volt +++ b/app/Http/Admin/Views/setting/dingtalk_robot.volt @@ -6,6 +6,13 @@
钉钉机器人
+
+ +
+ + +
+
diff --git a/app/Http/Home/Controllers/ImController.php b/app/Http/Home/Controllers/ImController.php index 7461363e..133ba5fa 100644 --- a/app/Http/Home/Controllers/ImController.php +++ b/app/Http/Home/Controllers/ImController.php @@ -205,14 +205,14 @@ class ImController extends Controller /** * @Post("/msg/cs/send", name="home.im.send_cs_msg") */ - public function sendCsMessageAction() + public function sendCustomMessageAction() { $from = $this->request->getPost('from', 'string'); $to = $this->request->getPost('to', 'string'); $service = new ImService(); - $service->sendCsMessage($from, $to); + $service->sendCustomMessage($from, $to); return $this->jsonSuccess(); } diff --git a/app/Http/Home/Services/ImMessageTrait.php b/app/Http/Home/Services/ImMessageTrait.php index 5a4842bb..07b33bd7 100644 --- a/app/Http/Home/Services/ImMessageTrait.php +++ b/app/Http/Home/Services/ImMessageTrait.php @@ -11,6 +11,7 @@ use App\Models\ImMessage as ImMessageModel; use App\Repos\ImFriendUser as ImFriendUserRepo; use App\Repos\ImMessage as ImMessageRepo; use App\Repos\ImUser as ImUserRepo; +use App\Services\DingTalk\Notice\CustomService as CustomServiceNotice; use App\Validators\ImFriendUser as ImFriendUserValidator; use App\Validators\ImGroup as ImGroupValidator; use App\Validators\ImGroupUser as ImGroupUserValidator; @@ -22,7 +23,7 @@ use GatewayClient\Gateway; * layim中普通聊天和自定义聊天中接收方用户名使用的字段不一样,也够坑爹的 * 普通聊天username,自定义聊天name */ -Trait ImMessageTrait +trait ImMessageTrait { public function getChatMessages() @@ -166,9 +167,11 @@ Trait ImMessageTrait } $this->eventsManager->fire('ImMessage:afterCreate', $this, $imMessage); + + return $imMessage; } - public function sendCsMessage($from, $to) + public function sendCustomMessage($from, $to) { $validator = new ImMessageValidator(); @@ -214,7 +217,11 @@ Trait ImMessageTrait unset($to['name']); - $this->sendChatMessage($from, $to); + $message = $this->sendChatMessage($from, $to); + + $this->handleCustomServiceNotice($message); + + return $message; } public function pullUnreadFriendMessages($id) @@ -334,4 +341,11 @@ Trait ImMessageTrait $group->update(); } + protected function handleCustomServiceNotice(ImMessageModel $message) + { + $notice = new CustomServiceNotice(); + + $notice->createTask($message); + } + } \ No newline at end of file diff --git a/app/Models/Task.php b/app/Models/Task.php index ad9f6953..10501f83 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -13,12 +13,24 @@ class Task extends Model const TYPE_POINT_GIFT_DELIVER = 3; // 积分礼品派发 const TYPE_LUCKY_GIFT_DELIVER = 4; // 抽奖礼品派发 + /** + * 针对外部用户 + */ const TYPE_NOTICE_ACCOUNT_LOGIN = 11; // 帐号登录通知 const TYPE_NOTICE_LIVE_BEGIN = 12; // 直播学员通知 const TYPE_NOTICE_ORDER_FINISH = 13; // 订单完成通知 const TYPE_NOTICE_REFUND_FINISH = 14; // 退款完成通知 const TYPE_NOTICE_CONSULT_REPLY = 15; // 咨询回复通知 - const TYPE_NOTICE_LIVE_TEACHER = 16; // 直播讲师通知 + + /** + * 针对内部人员 + */ + const TYPE_NOTICE_CONSULT_CREATE = 31; // 咨询创建通知 + const TYPE_NOTICE_TEACHER_LIVE = 32; // 直播讲师通知 + const TYPE_NOTICE_SERVER_MONITOR = 33; // 服务监控通知 + const TYPE_NOTICE_CUSTOM_SERVICE = 34; // 客服消息通知 + const TYPE_NOTICE_POINT_REDEEM = 35; // 积分兑换通知 + const TYPE_NOTICE_LUCKY_REDEEM = 36; // 抽奖兑换通知 /** * 优先级 @@ -84,6 +96,13 @@ class Task extends Model */ public $try_count = 0; + /** + * 最大重试次数 + * + * @var int + */ + public $max_try_count = 3; + /** * 创建时间 * diff --git a/app/Repos/ChapterLive.php b/app/Repos/ChapterLive.php index 6319a384..74d83587 100644 --- a/app/Repos/ChapterLive.php +++ b/app/Repos/ChapterLive.php @@ -5,6 +5,7 @@ namespace App\Repos; use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder; use App\Models\Chapter as ChapterModel; use App\Models\ChapterLive as ChapterLiveModel; +use Phalcon\Mvc\Model; class ChapterLive extends Repository { @@ -54,4 +55,28 @@ class ChapterLive extends Repository return $pager->paginate(); } + /** + * @param int $id + * @return ChapterLiveModel|Model|bool + */ + public function findById($id) + { + return ChapterLiveModel::findFirst([ + 'conditions' => 'id = :id:', + 'bind' => ['id' => $id], + ]); + } + + /** + * @param int $chapterId + * @return ChapterLiveModel|Model|bool + */ + public function findByChapterId($chapterId) + { + return ChapterLiveModel::findFirst([ + 'conditions' => 'chapter_id = :chapter_id:', + 'bind' => ['chapter_id' => $chapterId], + ]); + } + } diff --git a/app/Services/DingTalk/Notice/ConsultCreate.php b/app/Services/DingTalk/Notice/ConsultCreate.php index d88d3b24..7bb9abf3 100644 --- a/app/Services/DingTalk/Notice/ConsultCreate.php +++ b/app/Services/DingTalk/Notice/ConsultCreate.php @@ -3,6 +3,8 @@ namespace App\Services\DingTalk\Notice; use App\Models\Consult as ConsultModel; +use App\Models\Task as TaskModel; +use App\Repos\Consult as ConsultRepo; use App\Repos\Course as CourseRepo; use App\Repos\User as UserRepo; use App\Services\DingTalkNotice; @@ -10,8 +12,12 @@ use App\Services\DingTalkNotice; class ConsultCreate extends DingTalkNotice { - public function handle(ConsultModel $consult) + public function handleTask(TaskModel $task) { + $consultRepo = new ConsultRepo(); + + $consult = $consultRepo->findById($task->item_id); + $userRepo = new UserRepo(); $user = $userRepo->findById($consult->owner_id); @@ -26,7 +32,34 @@ class ConsultCreate extends DingTalkNotice 'consult.question' => $consult->question, ]); - $this->atCourseTeacher($course->id, $content); + return $this->atCourseTeacher($course->id, $content); + } + + public function createTask(ConsultModel $consult) + { + $keyName = "dingtalk_consult_create_notice:{$consult->owner_id}"; + + $cache = $this->getCache(); + + $content = $cache->get($keyName); + + if ($content) return; + + $cache->save($keyName, 1, 3600); + + $task = new TaskModel(); + + $itemInfo = [ + 'consult' => ['id' => $consult->id], + ]; + + $task->item_id = $consult->id; + $task->item_info = $itemInfo; + $task->item_type = TaskModel::TYPE_NOTICE_CONSULT_CREATE; + $task->priority = TaskModel::PRIORITY_LOW; + $task->status = TaskModel::STATUS_PENDING; + + $task->create(); } } \ No newline at end of file diff --git a/app/Services/DingTalk/Notice/CustomService.php b/app/Services/DingTalk/Notice/CustomService.php index 78b870ff..a57483cb 100644 --- a/app/Services/DingTalk/Notice/CustomService.php +++ b/app/Services/DingTalk/Notice/CustomService.php @@ -3,15 +3,35 @@ namespace App\Services\DingTalk\Notice; use App\Models\ImMessage as ImMessageModel; +use App\Models\Task as TaskModel; +use App\Repos\ImMessage as ImMessageRepo; use App\Repos\User as UserRepo; use App\Services\DingTalkNotice; class CustomService extends DingTalkNotice { - public function handle(ImMessageModel $message) + public function handleTask(TaskModel $task) { - $keyName = "dingtalk_cs_notice:{$message->sender_id}"; + $messageRepo = new ImMessageRepo(); + + $message = $messageRepo->findById($task->item_id); + + $userRepo = new UserRepo(); + + $sender = $userRepo->findById($message->sender_id); + + $content = kg_ph_replace("{user.name} 通过在线客服给你发送了消息:{message.content}", [ + 'user.name' => $sender->name, + 'message.content' => $message->content, + ]); + + return $this->atCustomService($content); + } + + public function createTask(ImMessageModel $message) + { + $keyName = "dingtalk_custom_service_notice:{$message->sender_id}"; $cache = $this->getCache(); @@ -21,16 +41,19 @@ class CustomService extends DingTalkNotice $cache->save($keyName, 1, 3600); - $userRepo = new UserRepo(); + $task = new TaskModel(); - $sender = $userRepo->findById($message->sender_id); + $itemInfo = [ + 'im_message' => ['id' => $message->id], + ]; - $content = kg_ph_replace("{user} 通过在线客服给你发送了消息:{message}", [ - 'user' => $sender->name, - 'message' => $message->content, - ]); + $task->item_id = $message->id; + $task->item_info = $itemInfo; + $task->item_type = TaskModel::TYPE_NOTICE_CUSTOM_SERVICE; + $task->priority = TaskModel::PRIORITY_MIDDLE; + $task->status = TaskModel::STATUS_PENDING; - $this->atCustomService($content); + $task->create(); } } \ No newline at end of file diff --git a/app/Services/DingTalk/Notice/LiveTeacher.php b/app/Services/DingTalk/Notice/LiveTeacher.php index 1e4ce7f8..db20b0cb 100644 --- a/app/Services/DingTalk/Notice/LiveTeacher.php +++ b/app/Services/DingTalk/Notice/LiveTeacher.php @@ -3,14 +3,20 @@ namespace App\Services\DingTalk\Notice; use App\Models\ChapterLive as ChapterLiveModel; +use App\Models\Task as TaskModel; +use App\Repos\ChapterLive as ChapterLiveRepo; use App\Repos\Course as CourseRepo; use App\Services\DingTalkNotice; -class LiveTeacher extends DingTalkNotice +class TeacherLive extends DingTalkNotice { - public function handle(ChapterLiveModel $live) + public function handleTask(TaskModel $task) { + $liveRepo = new ChapterLiveRepo(); + + $live = $liveRepo->findById($task->item_id); + $courseRepo = new CourseRepo(); $course = $courseRepo->findById($live->course_id); @@ -20,7 +26,24 @@ class LiveTeacher extends DingTalkNotice 'live.start_time' => date('Y-m-d H:i', $live->start_time), ]); - $this->atCourseTeacher($course->id, $content); + return $this->atCourseTeacher($course->id, $content); + } + + public function createTask(ChapterLiveModel $live) + { + $task = new TaskModel(); + + $itemInfo = [ + 'live' => ['id' => $live->id], + ]; + + $task->item_id = $live->id; + $task->item_info = $itemInfo; + $task->item_type = TaskModel::TYPE_NOTICE_TEACHER_LIVE; + $task->priority = TaskModel::PRIORITY_LOW; + $task->status = TaskModel::STATUS_PENDING; + + $task->create(); } } \ No newline at end of file diff --git a/app/Services/DingTalk/Notice/OrderFinish.php b/app/Services/DingTalk/Notice/OrderFinish.php deleted file mode 100644 index cc360b57..00000000 --- a/app/Services/DingTalk/Notice/OrderFinish.php +++ /dev/null @@ -1,27 +0,0 @@ -findById($order->owner_id); - - $text = kg_ph_replace("开单啦,{user.name} 同学完成了订单!\n订单名称:{order.subject}\n订单金额:¥{order.amount}", [ - 'user.name' => $user->name, - 'order.subject' => $order->subject, - 'order.amount' => $order->amount, - ]); - - $this->send(['text' => $text]); - } - -} \ No newline at end of file diff --git a/app/Services/DingTalk/Notice/PointRedeem.php b/app/Services/DingTalk/Notice/PointRedeem.php new file mode 100644 index 00000000..791db8b1 --- /dev/null +++ b/app/Services/DingTalk/Notice/PointRedeem.php @@ -0,0 +1,47 @@ +findById($task->item_id); + + $content = kg_ph_replace("{user.name} 兑换了商品 {gift.name},不要忘记发货哦!", [ + 'user.name' => $redeem->user_name, + 'gift.name' => $redeem->gift_name, + ]); + + return $this->atCustomService($content); + } + + public function createTask(PointRedeemModel $redeem) + { + if ($redeem->gift_type != PointGiftModel::TYPE_GOODS) return; + + $task = new TaskModel(); + + $itemInfo = [ + 'point_redeem' => ['id' => $redeem->id], + ]; + + $task->item_id = $redeem->id; + $task->item_info = $itemInfo; + $task->item_type = TaskModel::TYPE_NOTICE_POINT_REDEEM; + $task->priority = TaskModel::PRIORITY_MIDDLE; + $task->status = TaskModel::STATUS_PENDING; + + $task->create(); + } + +} \ No newline at end of file diff --git a/app/Services/DingTalk/Notice/ServerMonitor.php b/app/Services/DingTalk/Notice/ServerMonitor.php new file mode 100644 index 00000000..3901bafc --- /dev/null +++ b/app/Services/DingTalk/Notice/ServerMonitor.php @@ -0,0 +1,36 @@ +item_info['content']; + + return $notice->atTechSupport($content); + } + + public function createTask($content) + { + $task = new TaskModel(); + + $itemInfo = ['content' => $content]; + + $task->item_id = time(); + $task->item_info = $itemInfo; + $task->item_type = TaskModel::TYPE_NOTICE_SERVER_MONITOR; + $task->priority = TaskModel::PRIORITY_HIGH; + $task->status = TaskModel::STATUS_PENDING; + $task->max_try_count = 1; + + $task->create(); + } + +} \ No newline at end of file diff --git a/app/Services/Logic/Consult/ConsultCreate.php b/app/Services/Logic/Consult/ConsultCreate.php index 369fd19a..2ec411dd 100644 --- a/app/Services/Logic/Consult/ConsultCreate.php +++ b/app/Services/Logic/Consult/ConsultCreate.php @@ -6,6 +6,7 @@ use App\Models\Chapter as ChapterModel; use App\Models\Consult as ConsultModel; use App\Models\Course as CourseModel; use App\Models\User as UserModel; +use App\Services\DingTalk\Notice\ConsultCreate as ConsultCreateNotice; use App\Services\Logic\ChapterTrait; use App\Services\Logic\CourseTrait; use App\Services\Logic\Service; @@ -75,6 +76,8 @@ class ConsultCreate extends Service $this->incrUserDailyConsultCount($user); + $this->handleConsultCreateNotice($consult); + return $consult; } @@ -111,6 +114,8 @@ class ConsultCreate extends Service $this->incrUserDailyConsultCount($user); + $this->handleConsultCreateNotice($consult); + return $consult; } @@ -150,4 +155,11 @@ class ConsultCreate extends Service $this->eventsManager->fire('UserDailyCounter:incrConsultCount', $this, $user); } + protected function handleConsultCreateNotice(ConsultModel $consult) + { + $notice = new ConsultCreateNotice(); + + $notice->createTask($consult); + } + } diff --git a/app/Services/Logic/Notice/AccountLogin.php b/app/Services/Logic/Notice/AccountLogin.php index 6ae4b18f..9f4fdd06 100644 --- a/app/Services/Logic/Notice/AccountLogin.php +++ b/app/Services/Logic/Notice/AccountLogin.php @@ -54,6 +54,7 @@ class AccountLogin extends LogicService $task->item_type = TaskModel::TYPE_NOTICE_ACCOUNT_LOGIN; $task->priority = TaskModel::PRIORITY_LOW; $task->status = TaskModel::STATUS_PENDING; + $task->max_try_count = 1; $task->create(); } diff --git a/app/Services/Logic/Notice/ConsultReply.php b/app/Services/Logic/Notice/ConsultReply.php index 3b77f7c1..34cfaebc 100644 --- a/app/Services/Logic/Notice/ConsultReply.php +++ b/app/Services/Logic/Notice/ConsultReply.php @@ -86,6 +86,7 @@ class ConsultReply extends LogicService $task->item_type = TaskModel::TYPE_NOTICE_CONSULT_REPLY; $task->priority = TaskModel::PRIORITY_LOW; $task->status = TaskModel::STATUS_PENDING; + $task->max_try_count = 1; $task->create(); } diff --git a/app/Services/Logic/Notice/LiveBegin.php b/app/Services/Logic/Notice/LiveBegin.php index f125c56c..a0818d54 100644 --- a/app/Services/Logic/Notice/LiveBegin.php +++ b/app/Services/Logic/Notice/LiveBegin.php @@ -92,6 +92,7 @@ class LiveBegin extends LogicService $task->item_type = TaskModel::TYPE_NOTICE_LIVE_BEGIN; $task->priority = TaskModel::PRIORITY_LOW; $task->status = TaskModel::STATUS_PENDING; + $task->max_try_count = 1; $task->create(); } diff --git a/db/migrations/20210215024511_data_202102151130.php b/db/migrations/20210215024511_data_202102151130.php index f5fd6419..e2d0fde5 100644 --- a/db/migrations/20210215024511_data_202102151130.php +++ b/db/migrations/20210215024511_data_202102151130.php @@ -6,6 +6,11 @@ class Data202102151130 extends Phinx\Migration\AbstractMigration public function up() { $rows = [ + [ + 'section' => 'dingtalk.robot', + 'item_key' => 'enabled', + 'item_value' => '0', + ], [ 'section' => 'dingtalk.robot', 'item_key' => 'app_secret', diff --git a/db/migrations/20210215034511_schema_202102151230.php b/db/migrations/20210215034511_schema_202102151230.php new file mode 100644 index 00000000..77b94e12 --- /dev/null +++ b/db/migrations/20210215034511_schema_202102151230.php @@ -0,0 +1,28 @@ +table('kg_task') + ->addColumn('max_try_count', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'signed' => false, + 'comment' => '最大尝试数', + 'after' => 'try_count', + ])->save(); + } + + public function down() + { + $this->table('kg_task') + ->removeColumn('max_try_count') + ->save(); + } + +} diff --git a/public/static/home/js/im.cs.js b/public/static/home/js/im.cs.js index 06c7727d..f86c831f 100644 --- a/public/static/home/js/im.cs.js +++ b/public/static/home/js/im.cs.js @@ -64,7 +64,7 @@ layui.use(['jquery', 'layim'], function () { }); layim.on('sendMessage', function (res) { - sendCsMessage(res); + sendCustomMessage(res); }); showWelcomeMessage(csUser); @@ -77,7 +77,7 @@ layui.use(['jquery', 'layim'], function () { }); } - function sendCsMessage(res) { + function sendCustomMessage(res) { $.ajax({ type: 'POST', url: '/im/msg/cs/send', diff --git a/scheduler.php b/scheduler.php index 000900f3..253ad168 100644 --- a/scheduler.php +++ b/scheduler.php @@ -16,21 +16,24 @@ $scheduler->php($script, $bin, ['--task' => 'deliver', '--action' => 'main']) $scheduler->php($script, $bin, ['--task' => 'notice', '--action' => 'main']) ->at('*/3 * * * *'); -$scheduler->php($script, $bin, ['--task' => 'sync_learning', '--action' => 'main']) - ->at('*/7 * * * *'); - $scheduler->php($script, $bin, ['--task' => 'vod_event', '--action' => 'main']) ->at('*/5 * * * *'); -$scheduler->php($script, $bin, ['--task' => 'close_trade', '--action' => 'main']) - ->at('*/13 * * * *'); +$scheduler->php($script, $bin, ['--task' => 'sync_learning', '--action' => 'main']) + ->at('*/7 * * * *'); -$scheduler->php($script, $bin, ['--task' => 'monitor', '--action' => 'main']) - ->at('*/12 * * * *'); +$scheduler->php($script, $bin, ['--task' => 'teacher_live_notice', '--action' => 'consume']) + ->at('*/10 * * * *'); $scheduler->php($script, $bin, ['--task' => 'point_gift_deliver', '--action' => 'main']) ->at('*/11 * * * *'); +$scheduler->php($script, $bin, ['--task' => 'server_monitor', '--action' => 'main']) + ->at('*/12 * * * *'); + +$scheduler->php($script, $bin, ['--task' => 'close_trade', '--action' => 'main']) + ->at('*/13 * * * *'); + $scheduler->php($script, $bin, ['--task' => 'close_order', '--action' => 'main']) ->hourly(3); @@ -61,4 +64,10 @@ $scheduler->php($script, $bin, ['--task' => 'revoke_vip', '--action' => 'main']) $scheduler->php($script, $bin, ['--task' => 'sitemap', '--action' => 'main']) ->daily(4, 3); +$scheduler->php($script, $bin, ['--task' => 'teacher_live_notice', '--action' => 'provide']) + ->daily(4, 7); + +$scheduler->php($script, $bin, ['--task' => 'optimize_table', '--action' => 'main']) + ->weekly(6, 5, 3); + $scheduler->run(); \ No newline at end of file