diff --git a/CHANGELOG.md b/CHANGELOG.md
index 50174e64..f42c92f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,23 @@
+### [v1.2.2](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.2)(2020-12-24)
+
+#### 增加
+
+- 登录账户微信提醒
+- 购买成功微信提醒
+- 退款成功微信提醒
+- 开始直播微信提醒
+- 咨询回复微信提醒
+- 咨询回复短信提醒
+
+#### 修复
+
+- 创建章节,关联表数据没有生成
+- 创建群组,没有生成max_im_group_id缓存
+- 课程分类列表没有过滤掉帮助分类的内容
+- 创建角色字段routes MySQL text 类型报错
+- 低品质视频无法播放
+- 后台遗漏的权限
+
### [v1.2.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.1)(2020-12-10)
- 增加QQ,微信,微博第三方登录
- 代码优化以及问题修复
diff --git a/README.md b/README.md
index a6a49470..3a7b980b 100644
--- a/README.md
+++ b/README.md
@@ -6,9 +6,9 @@
酷瓜云课堂,依托腾讯云基础服务架构,采用C扩展框架Phalcon开发,GPL-2.0开源协议,致力开源网课系统,开源网校系统,开源在线教育系统。
-
-
-
+
+
+

#### 系统功能
@@ -86,3 +86,5 @@ Tips: 测试支付请用手机号注册一个新账户,以便接收订单通
- 系统定制
- 企业授权
+毫无保留的真开源不容易,如果对你有帮助,请给我们 **STAR** !!!
+
diff --git a/app/Builders/ConsultList.php b/app/Builders/ConsultList.php
index 44aa3f7e..55f27c0f 100644
--- a/app/Builders/ConsultList.php
+++ b/app/Builders/ConsultList.php
@@ -26,6 +26,7 @@ class ConsultList extends Builder
foreach ($consults as $key => $consult) {
$consults[$key]['owner'] = $users[$consult['owner_id']] ?? new \stdClass();
+ $consults[$key]['replier'] = $users[$consult['replier_id']] ?? new \stdClass();
}
return $consults;
@@ -67,7 +68,9 @@ class ConsultList extends Builder
public function getUsers(array $consults)
{
- $ids = kg_array_column($consults, 'owner_id');
+ $ownerIds = kg_array_column($consults, 'owner_id');
+ $replierIds = kg_array_column($consults, 'replier_id');
+ $ids = array_merge($ownerIds, $replierIds);
$userRepo = new UserRepo();
diff --git a/app/Console/Tasks/CleanLogTask.php b/app/Console/Tasks/CleanLogTask.php
index b4dd7ee9..d4d532c7 100644
--- a/app/Console/Tasks/CleanLogTask.php
+++ b/app/Console/Tasks/CleanLogTask.php
@@ -13,6 +13,7 @@ class CleanLogTask extends Task
$this->cleanSqlLog();
$this->cleanListenLog();
$this->cleanCaptchaLog();
+ $this->cleanWechatLog();
$this->cleanMailLog();
$this->cleanSmsLog();
$this->cleanVodLog();
@@ -22,6 +23,7 @@ class CleanLogTask extends Task
$this->cleanWxpayLog();
$this->cleanOrderLog();
$this->cleanRefundLog();
+ $this->cleanNoticeLog();
}
/**
@@ -112,6 +114,14 @@ class CleanLogTask extends Task
$this->cleanLog('mail', 7);
}
+ /**
+ * 清理微信服务日志
+ */
+ protected function cleanWechatLog()
+ {
+ $this->cleanLog('wechat', 7);
+ }
+
/**
* 清理阿里支付服务日志
*/
@@ -144,6 +154,14 @@ class CleanLogTask extends Task
$this->cleanLog('refund', 30);
}
+ /**
+ * 清理通知日志
+ */
+ protected function cleanNoticeLog()
+ {
+ $this->cleanLog('notice', 7);
+ }
+
/**
* 清理日志文件
*
diff --git a/app/Console/Tasks/DeliverTask.php b/app/Console/Tasks/DeliverTask.php
index 1895ce26..ad7c846b 100644
--- a/app/Console/Tasks/DeliverTask.php
+++ b/app/Console/Tasks/DeliverTask.php
@@ -12,7 +12,7 @@ use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Repos\Order as OrderRepo;
use App\Repos\User as UserRepo;
-use App\Services\Sms\Order as OrderSms;
+use App\Services\Logic\Notice\OrderFinish as OrderFinishNotice;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
@@ -26,7 +26,7 @@ class DeliverTask extends Task
{
$logger = $this->getLogger('order');
- $tasks = $this->findTasks();
+ $tasks = $this->findTasks(30);
if ($tasks->count() == 0) {
return;
@@ -84,7 +84,7 @@ class DeliverTask extends Task
}
if ($task->status == TaskModel::STATUS_FINISHED) {
- $this->handleOrderNotice($order);
+ $this->handleOrderFinishNotice($order);
} elseif ($task->status == TaskModel::STATUS_FAILED) {
$this->handleOrderRefund($order);
}
@@ -199,11 +199,11 @@ class DeliverTask extends Task
}
}
- protected function handleOrderNotice(OrderModel $order)
+ protected function handleOrderFinishNotice(OrderModel $order)
{
- $sms = new OrderSms();
+ $notice = new OrderFinishNotice();
- $sms->handle($order);
+ $notice->createTask($order);
}
protected function handleOrderRefund(OrderModel $order)
@@ -244,7 +244,7 @@ class DeliverTask extends Task
* @param int $limit
* @return ResultsetInterface|Resultset|TaskModel[]
*/
- protected function findTasks($limit = 100)
+ protected function findTasks($limit = 30)
{
$itemType = TaskModel::TYPE_DELIVER;
$status = TaskModel::STATUS_PENDING;
diff --git a/app/Console/Tasks/LiveNotifyTask.php b/app/Console/Tasks/LiveNotifyTask.php
deleted file mode 100644
index ab6e2241..00000000
--- a/app/Console/Tasks/LiveNotifyTask.php
+++ /dev/null
@@ -1,81 +0,0 @@
-getRedis();
-
- $service = new LiveNotifyService();
-
- $key = $service->getNotifyKey();
-
- $chapterIds = $redis->sMembers($key);
-
- if (!$chapterIds) return;
-
- $sentKey = $service->getSentNotifyKey();
-
- $sentChapterIds = $redis->sMembers($sentKey);
-
- foreach ($chapterIds as $chapterId) {
- if (!in_array($chapterId, $sentChapterIds)) {
- $this->sendNotification($chapterId);
- } else {
- $redis->sAdd($sentKey, $chapterId);
- }
- }
-
- if ($redis->sCard($sentKey) == 1) {
- $redis->expire($sentKey, 86400);
- }
- }
-
- protected function sendNotification($chapterId)
- {
- $chapterRepo = new ChapterRepo();
-
- $chapterLive = $chapterRepo->findChapterLive($chapterId);
-
- if (!$chapterLive) return;
-
- $targetUserIds = $this->findTargetUserIds($chapterLive->course_id);
-
- if (!$targetUserIds) return;
-
- $sms = new LiveSms();
-
- foreach ($targetUserIds as $userId) {
- $sms->handle($chapterId, $userId, $chapterLive->start_time);
- }
- }
-
- protected function findTargetUserIds($courseId)
- {
- $sourceTypes = [
- CourseUserModel::SOURCE_CHARGE,
- CourseUserModel::SOURCE_VIP,
- ];
-
- $rows = CourseUserModel::query()
- ->where('course_id = :course_id:', ['course_id' => $courseId])
- ->andWhere('role_type = :role_type:', ['role_type' => CourseUserModel::ROLE_STUDENT])
- ->inWhere('source_type', $sourceTypes)
- ->execute();
-
- if ($rows->count() == 0) {
- return [];
- }
-
- return kg_array_column($rows->toArray(), 'user_id');
- }
-
-}
diff --git a/app/Console/Tasks/MaintainTask.php b/app/Console/Tasks/MaintainTask.php
index 9ac60365..7f6d5898 100644
--- a/app/Console/Tasks/MaintainTask.php
+++ b/app/Console/Tasks/MaintainTask.php
@@ -19,7 +19,7 @@ class MaintainTask extends Task
* 重建首页课程缓存
*
* @param array $params
- * @command: php console.php maintain reset_index_course_cache
+ * @command: php console.php maintain rebuild_index_course_cache
*/
public function rebuildIndexCourseCacheAction($params)
{
diff --git a/app/Console/Tasks/NoticeTask.php b/app/Console/Tasks/NoticeTask.php
new file mode 100644
index 00000000..c4a385d4
--- /dev/null
+++ b/app/Console/Tasks/NoticeTask.php
@@ -0,0 +1,137 @@
+getLogger('notice');
+
+ $tasks = $this->findTasks(300);
+
+ if ($tasks->count() == 0) {
+ return;
+ }
+
+ foreach ($tasks as $task) {
+
+ try {
+
+ switch ($task->item_type) {
+ case TaskModel::TYPE_NOTICE_ACCOUNT_LOGIN:
+ $this->handleAccountLoginNotice($task);
+ break;
+ case TaskModel::TYPE_NOTICE_LIVE_BEGIN:
+ $this->handleLiveBeginNotice($task);
+ break;
+ case TaskModel::TYPE_NOTICE_ORDER_FINISH:
+ $this->handleOrderFinishNotice($task);
+ break;
+ case TaskModel::TYPE_NOTICE_REFUND_FINISH:
+ $this->handleRefundFinishNotice($task);
+ break;
+ case TaskModel::TYPE_NOTICE_CONSULT_REPLY:
+ $this->handleConsultReplyNotice($task);
+ break;
+ }
+
+ $task->status = TaskModel::STATUS_FINISHED;
+
+ $task->update();
+
+ } catch (\Exception $e) {
+
+ $task->try_count += 1;
+ $task->priority += 1;
+
+ if ($task->try_count > self::TRY_COUNT) {
+ $task->status = TaskModel::STATUS_FAILED;
+ }
+
+ $task->update();
+
+ $logger->info('Notice Process Exception ' . kg_json_encode([
+ 'code' => $e->getCode(),
+ 'message' => $e->getMessage(),
+ 'task' => $task->toArray(),
+ ]));
+ }
+ }
+ }
+
+ protected function handleAccountLoginNotice(TaskModel $task)
+ {
+ $notice = new AccountLoginNotice();
+
+ return $notice->handleTask($task);
+ }
+
+ protected function handleLiveBeginNotice(TaskModel $task)
+ {
+ $notice = new LiveBeginNotice();
+
+ return $notice->handleTask($task);
+ }
+
+ protected function handleOrderFinishNotice(TaskModel $task)
+ {
+ $notice = new OrderFinishNotice();
+
+ return $notice->handleTask($task);
+ }
+
+ protected function handleRefundFinishNotice(TaskModel $task)
+ {
+ $notice = new RefundFinishNotice();
+
+ return $notice->handleTask($task);
+ }
+
+ protected function handleConsultReplyNotice(TaskModel $task)
+ {
+ $notice = new ConsultReplyNotice();
+
+ return $notice->handleTask($task);
+ }
+
+ /**
+ * @param int $limit
+ * @return ResultsetInterface|Resultset|TaskModel[]
+ */
+ protected function findTasks($limit = 100)
+ {
+ $itemTypes = [
+ TaskModel::TYPE_NOTICE_ACCOUNT_LOGIN,
+ TaskModel::TYPE_NOTICE_LIVE_BEGIN,
+ TaskModel::TYPE_NOTICE_ORDER_FINISH,
+ TaskModel::TYPE_NOTICE_REFUND_FINISH,
+ TaskModel::TYPE_NOTICE_CONSULT_REPLY,
+ ];
+
+ $status = TaskModel::STATUS_PENDING;
+
+ $tryCount = self::TRY_COUNT;
+
+ return TaskModel::query()
+ ->inWhere('item_type', $itemTypes)
+ ->andWhere('status = :status:', ['status' => $status])
+ ->andWhere('try_count < :try_count:', ['try_count' => $tryCount + 1])
+ ->orderBy('priority ASC')
+ ->limit($limit)
+ ->execute();
+ }
+
+}
diff --git a/app/Console/Tasks/RefundTask.php b/app/Console/Tasks/RefundTask.php
index 0edf1a67..1697a4e1 100644
--- a/app/Console/Tasks/RefundTask.php
+++ b/app/Console/Tasks/RefundTask.php
@@ -11,9 +11,9 @@ use App\Repos\Order as OrderRepo;
use App\Repos\Refund as RefundRepo;
use App\Repos\Trade as TradeRepo;
use App\Repos\User as UserRepo;
+use App\Services\Logic\Notice\RefundFinish as RefundFinishNotice;
use App\Services\Pay\Alipay as AlipayService;
use App\Services\Pay\Wxpay as WxpayService;
-use App\Services\Sms\Refund as RefundSms;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
@@ -29,7 +29,7 @@ class RefundTask extends Task
{
$logger = $this->getLogger('refund');
- $tasks = $this->findTasks();
+ $tasks = $this->findTasks(30);
if ($tasks->count() == 0) {
return;
@@ -95,7 +95,7 @@ class RefundTask extends Task
$this->db->commit();
- $this->handleRefundNotice($refund);
+ $this->handleRefundFinishNotice($refund);
} catch (\Exception $e) {
@@ -259,7 +259,7 @@ class RefundTask extends Task
}
/**
- * 处理测试订单退款
+ * 处理赞赏订单退款
*
* @param OrderModel $order
*/
@@ -281,11 +281,11 @@ class RefundTask extends Task
/**
* @param RefundModel $refund
*/
- protected function handleRefundNotice(RefundModel $refund)
+ protected function handleRefundFinishNotice(RefundModel $refund)
{
- $sms = new RefundSms();
+ $notice = new RefundFinishNotice();
- $sms->handle($refund);
+ $notice->createTask($refund);
}
/**
diff --git a/app/Console/Tasks/Task.php b/app/Console/Tasks/Task.php
index 350cb5ec..75944d41 100644
--- a/app/Console/Tasks/Task.php
+++ b/app/Console/Tasks/Task.php
@@ -30,7 +30,7 @@ class Task extends \Phalcon\Cli\Task
return $appService->getRedis();
}
- public function getLogger($channel = null)
+ public function getLogger($channel = 'console')
{
$appService = new AppService();
diff --git a/app/Http/Admin/Controllers/SettingController.php b/app/Http/Admin/Controllers/SettingController.php
index 71e41fc0..309d6bd8 100644
--- a/app/Http/Admin/Controllers/SettingController.php
+++ b/app/Http/Admin/Controllers/SettingController.php
@@ -327,4 +327,29 @@ class SettingController extends Controller
}
}
+ /**
+ * @Route("/wechat", name="admin.setting.wechat")
+ */
+ public function wechatAction()
+ {
+ $settingService = new SettingService();
+
+ if ($this->request->isPost()) {
+
+ $section = $this->request->getPost('section', 'string');
+
+ $data = $this->request->getPost();
+
+ $settingService->updateWechatSettings($section, $data);
+
+ return $this->jsonSuccess(['msg' => '更新配置成功']);
+
+ } else {
+
+ $oa = $settingService->getWechatOASettings();
+
+ $this->view->setVar('oa', $oa);
+ }
+ }
+
}
diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php
index 427c9924..db2ec7d8 100644
--- a/app/Http/Admin/Services/AuthNode.php
+++ b/app/Http/Admin/Services/AuthNode.php
@@ -34,14 +34,14 @@ class AuthNode extends Service
'title' => '分类列表',
'type' => 'button',
'route' => 'admin.category.list',
- 'params' => ['type' => 'course'],
+ 'params' => ['type' => 1],
],
[
'id' => '1-2-2',
'title' => '添加分类',
'type' => 'button',
'route' => 'admin.category.add',
- 'params' => ['type' => 'course'],
+ 'params' => ['type' => 1],
],
[
'id' => '1-2-3',
@@ -90,7 +90,7 @@ class AuthNode extends Service
'id' => '1-1-5',
'title' => '删除课程',
'type' => 'button',
- 'route' => 'admin.course.edit',
+ 'route' => 'admin.course.delete',
],
[
'id' => '1-1-6',
@@ -529,6 +529,12 @@ class AuthNode extends Service
],
[
'id' => '3-2-3',
+ 'title' => '交易详情',
+ 'type' => 'button',
+ 'route' => 'admin.trade.show',
+ ],
+ [
+ 'id' => '3-2-4',
'title' => '交易退款',
'type' => 'button',
'route' => 'admin.trade.refund',
@@ -750,6 +756,12 @@ class AuthNode extends Service
'type' => 'menu',
'route' => 'admin.setting.oauth',
],
+ [
+ 'id' => '5-1-13',
+ 'title' => '微信公众平台',
+ 'type' => 'menu',
+ 'route' => 'admin.setting.wechat',
+ ],
],
],
],
diff --git a/app/Http/Admin/Services/Consult.php b/app/Http/Admin/Services/Consult.php
index 8625cf86..667b99a7 100644
--- a/app/Http/Admin/Services/Consult.php
+++ b/app/Http/Admin/Services/Consult.php
@@ -4,8 +4,10 @@ namespace App\Http\Admin\Services;
use App\Builders\ConsultList as ConsultListBuilder;
use App\Library\Paginator\Query as PagerQuery;
+use App\Models\Consult as ConsultModel;
use App\Repos\Consult as ConsultRepo;
use App\Repos\Course as CourseRepo;
+use App\Services\Logic\Notice\ConsultReply as ConsultReplyNotice;
use App\Validators\Consult as ConsultValidator;
class Consult extends Service
@@ -52,12 +54,18 @@ class Consult extends Service
$data = [];
+ $firstReply = false;
+
if (!empty($post['question'])) {
$data['question'] = $validator->checkQuestion($post['question']);
}
if (!empty($post['answer'])) {
$data['answer'] = $validator->checkAnswer($post['answer']);
+ $data['reply_time'] = time();
+ if ($consult->reply_time == 0) {
+ $firstReply = true;
+ }
}
if (isset($post['private'])) {
@@ -70,6 +78,10 @@ class Consult extends Service
$consult->update($data);
+ if ($firstReply) {
+ $this->handleReplyNotice($consult);
+ }
+
return $consult;
}
@@ -107,6 +119,13 @@ class Consult extends Service
$course->update();
}
+ protected function handleReplyNotice(ConsultModel $consult)
+ {
+ $notice = new ConsultReplyNotice();
+
+ $notice->createTask($consult);
+ }
+
protected function findOrFail($id)
{
$validator = new ConsultValidator();
diff --git a/app/Http/Admin/Services/Role.php b/app/Http/Admin/Services/Role.php
index 82fde39a..c9459b33 100644
--- a/app/Http/Admin/Services/Role.php
+++ b/app/Http/Admin/Services/Role.php
@@ -61,8 +61,11 @@ class Role extends Service
$data['name'] = $validator->checkName($post['name']);
$data['summary'] = $validator->checkSummary($post['summary']);
- $data['routes'] = $validator->checkRoutes($post['routes']);
- $data['routes'] = $this->handleRoutes($data['routes']);
+
+ if (isset($post['routes'])) {
+ $data['routes'] = $validator->checkRoutes($post['routes']);
+ $data['routes'] = $this->handleRoutes($data['routes']);
+ }
$role->update($data);
@@ -114,9 +117,9 @@ class Role extends Service
* @param array $routes
* @return array
*/
- protected function handleRoutes($routes)
+ protected function handleRoutes(array $routes)
{
- if (empty($routes)) {
+ if (count($routes) == 0) {
return [];
}
@@ -140,23 +143,24 @@ class Role extends Service
if (in_array('admin.course.list', $routes)) {
$list[] = 'admin.course.chapters';
$list[] = 'admin.chapter.lessons';
+ $list[] = 'admin.chapter.resources';
}
if (array_intersect(['admin.course.add', 'admin.course.edit'], $routes)) {
$list[] = 'admin.chapter.add';
$list[] = 'admin.chapter.edit';
+ $list[] = 'admin.chapter.create';
+ $list[] = 'admin.chapter.update';
$list[] = 'admin.chapter.content';
- }
-
- if (array_intersect(['admin.chapter.add', 'admin.chapter.edit'], $routes)) {
$list[] = 'admin.resource.create';
$list[] = 'admin.resource.update';
- $list[] = 'admin.resource.delete';
}
if (in_array('admin.course.delete', $routes)) {
$list[] = 'admin.chapter.delete';
$list[] = 'admin.chapter.restore';
+ $list[] = 'admin.resource.delete';
+ $list[] = 'admin.resource.restore';
}
if (in_array('admin.category.list', $routes)) {
@@ -172,6 +176,18 @@ class Role extends Service
$list[] = 'admin.category.list';
}
+ if (in_array('admin.order.show', $routes)) {
+ $list[] = 'admin.order.status_history';
+ }
+
+ if (in_array('admin.trade.show', $routes)) {
+ $list[] = 'admin.trade.status_history';
+ }
+
+ if (in_array('admin.refund.show', $routes)) {
+ $list[] = 'admin.refund.status_history';
+ }
+
$list = array_unique($list);
return array_values($list);
diff --git a/app/Http/Admin/Services/Session.php b/app/Http/Admin/Services/Session.php
index 2bff96d1..fae5222b 100644
--- a/app/Http/Admin/Services/Session.php
+++ b/app/Http/Admin/Services/Session.php
@@ -2,7 +2,9 @@
namespace App\Http\Admin\Services;
+use App\Models\User as UserModel;
use App\Services\Auth\Admin as AdminAuth;
+use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService;
use App\Validators\Account as AccountValidator;
use App\Validators\Captcha as CaptchaValidator;
@@ -45,6 +47,8 @@ class Session extends Service
$captchaValidator->checkCode($post['ticket'], $post['rand']);
}
+ $this->handleLoginNotice($user);
+
$this->auth->saveAuthInfo($user);
}
@@ -53,4 +57,11 @@ class Session extends Service
$this->auth->clearAuthInfo();
}
+ protected function handleLoginNotice(UserModel $user)
+ {
+ $service = new AccountLoginNoticeService();
+
+ $service->createTask($user);
+ }
+
}
diff --git a/app/Http/Admin/Services/Setting.php b/app/Http/Admin/Services/Setting.php
index 3d2130fd..3f2abe3e 100644
--- a/app/Http/Admin/Services/Setting.php
+++ b/app/Http/Admin/Services/Setting.php
@@ -57,6 +57,16 @@ class Setting extends Service
return $wxpay;
}
+ public function getWechatOASettings()
+ {
+ $oa = $this->getSettings('wechat.oa');
+
+ $oa['auth_url'] = $oa['auth_url'] ?: kg_full_url(['for' => 'home.wechat.oa.auth_callback']);
+ $oa['notify_url'] = $oa['notify_url'] ?: kg_full_url(['for' => 'home.wechat.oa.notify_callback']);
+
+ return $oa;
+ }
+
public function getVipSettings()
{
$vipRepo = new VipRepo();
@@ -144,7 +154,9 @@ class Setting extends Service
public function updateSmsSettings($section, $settings)
{
- $settings['template'] = kg_json_encode($settings['template']);
+ if (isset($settings['template'])) {
+ $settings['template'] = kg_json_encode($settings['template']);
+ }
$this->updateSettings($section, $settings);
}
@@ -160,4 +172,15 @@ class Setting extends Service
}
}
+ public function updateWechatSettings($section, $settings)
+ {
+ if ($section == 'wechat.oa') {
+ if (isset($settings['notice_template'])) {
+ $settings['notice_template'] = kg_json_encode($settings['notice_template']);
+ }
+ }
+
+ $this->updateSettings($section, $settings);
+ }
+
}
diff --git a/app/Http/Admin/Views/order/list.volt b/app/Http/Admin/Views/order/list.volt
index fb154b05..8b6a39b3 100644
--- a/app/Http/Admin/Views/order/list.volt
+++ b/app/Http/Admin/Views/order/list.volt
@@ -36,7 +36,7 @@
{% set show_url = url({'for':'admin.order.show','id':item.id}) %}
- 商品:{{ item.subject }} {{ item_type(item.item_type) }}
+ 商品:{{ item.subject }}
单号:{{ item.sn }}
|
diff --git a/app/Http/Admin/Views/setting/sms.volt b/app/Http/Admin/Views/setting/sms.volt
index dbddfc49..8601b0a2 100644
--- a/app/Http/Admin/Views/setting/sms.volt
+++ b/app/Http/Admin/Views/setting/sms.volt
@@ -53,21 +53,27 @@
|
订单通知 |
- |
- |
- 复制 |
+ |
+ |
+ 复制 |
退款通知 |
- |
- |
- 复制 |
+ |
+ |
+ 复制 |
- 直播通知 |
- |
- |
- 复制 |
+ 直播提醒 |
+ |
+ |
+ 复制 |
+
+
+ 回复通知 |
+ |
+ |
+ 复制 |
diff --git a/app/Http/Admin/Views/setting/wechat.volt b/app/Http/Admin/Views/setting/wechat.volt
new file mode 100644
index 00000000..a37d7091
--- /dev/null
+++ b/app/Http/Admin/Views/setting/wechat.volt
@@ -0,0 +1,16 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+
+
+
+
+ {{ partial('setting/wechat_oa') }}
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Http/Admin/Views/setting/wechat_oa.volt b/app/Http/Admin/Views/setting/wechat_oa.volt
new file mode 100644
index 00000000..efc70be5
--- /dev/null
+++ b/app/Http/Admin/Views/setting/wechat_oa.volt
@@ -0,0 +1,104 @@
+{% set notice_template = oa.notice_template|json_decode %}
+
+
+
+
\ No newline at end of file
diff --git a/app/Http/Api/Services/Account.php b/app/Http/Api/Services/Account.php
index baf537f8..9ce43137 100644
--- a/app/Http/Api/Services/Account.php
+++ b/app/Http/Api/Services/Account.php
@@ -2,9 +2,11 @@
namespace App\Http\Api\Services;
+use App\Models\User as UserModel;
use App\Repos\User as UserRepo;
use App\Services\Auth\Api as AuthService;
use App\Services\Logic\Account\Register as RegisterService;
+use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService;
use App\Validators\Account as AccountValidator;
class Account extends Service
@@ -50,6 +52,8 @@ class Account extends Service
$user = $validator->checkUserLogin($post['account'], $post['password']);
+ $this->handleLoginNotice($user);
+
return $this->auth->saveAuthInfo($user);
}
@@ -70,6 +74,8 @@ class Account extends Service
$user = $validator->checkVerifyLogin($post['account'], $post['verify_code']);
+ $this->handleLoginNotice($user);
+
return $this->auth->saveAuthInfo($user);
}
@@ -78,4 +84,11 @@ class Account extends Service
$this->auth->clearAuthInfo();
}
+ protected function handleLoginNotice(UserModel $user)
+ {
+ $service = new AccountLoginNoticeService();
+
+ $service->createTask($user);
+ }
+
}
diff --git a/app/Http/Home/Controllers/UserConsoleController.php b/app/Http/Home/Controllers/UserConsoleController.php
index 2b56c475..12ed2de5 100644
--- a/app/Http/Home/Controllers/UserConsoleController.php
+++ b/app/Http/Home/Controllers/UserConsoleController.php
@@ -2,6 +2,7 @@
namespace App\Http\Home\Controllers;
+use App\Repos\WechatSubscribe as WechatSubscribeRepo;
use App\Services\Logic\Account\OAuthProvider as OAuthProviderService;
use App\Services\Logic\User\Console\AccountInfo as AccountInfoService;
use App\Services\Logic\User\Console\ConnectDelete as ConnectDeleteService;
@@ -36,6 +37,15 @@ class UserConsoleController extends Controller
return true;
}
+ public function initialize()
+ {
+ parent::initialize();
+
+ $wechatOA = $this->getSettings('wechat.oa');
+
+ $this->view->setVar('wechat_oa', $wechatOA);
+ }
+
/**
* @Get("/", name="home.uc.index")
*/
@@ -201,6 +211,25 @@ class UserConsoleController extends Controller
$this->view->setVar('pager', $pager);
}
+ /**
+ * @Get("/subscribe", name="home.uc.subscribe")
+ */
+ public function subscribeAction()
+ {
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByUserId($this->authUser->id);
+
+ $subscribed = 0;
+
+ if ($subscribe) {
+ $subscribed = $subscribe->deleted == 0 ? 1 : 0;
+ }
+
+ $this->view->pick('user/console/subscribe');
+ $this->view->setVar('subscribed', $subscribed);
+ }
+
/**
* @Post("/profile/update", name="home.uc.update_profile")
*/
diff --git a/app/Http/Home/Controllers/WechatOfficialAccountController.php b/app/Http/Home/Controllers/WechatOfficialAccountController.php
new file mode 100644
index 00000000..223170e7
--- /dev/null
+++ b/app/Http/Home/Controllers/WechatOfficialAccountController.php
@@ -0,0 +1,76 @@
+getSubscribeStatus();
+
+ return $this->jsonSuccess(['status' => $status]);
+ }
+
+ /**
+ * @Get("/subscribe/qrcode", name="home.wechat.oa.sub_qrcode")
+ */
+ public function subscribeQrCodeAction()
+ {
+ $service = new WechatOAService();
+
+ $qrcode = $service->createSubscribeQrCode();
+
+ return $this->jsonSuccess(['qrcode' => $qrcode]);
+ }
+
+ /**
+ * @Get("/notify", name="home.wechat.oa.verify")
+ */
+ public function verifyAction()
+ {
+ $service = new WechatOAService();
+
+ $app = $service->getOfficialAccount();
+
+ $response = $app->server->serve();
+
+ $response->send();
+
+ exit;
+ }
+
+ /**
+ * @Post("/notify", name="home.wechat.oa.notify")
+ */
+ public function notifyAction()
+ {
+ $service = new WechatOAService();
+
+ $app = $service->getOfficialAccount();
+
+ $app->server->push(function ($message) use ($service) {
+ return $service->handleNotify($message);
+ });
+
+ $response = $app->server->serve();
+
+ $response->send();
+
+ exit;
+ }
+
+}
diff --git a/app/Http/Home/Services/Account.php b/app/Http/Home/Services/Account.php
index 38efb3b2..7f7fb06f 100644
--- a/app/Http/Home/Services/Account.php
+++ b/app/Http/Home/Services/Account.php
@@ -2,9 +2,11 @@
namespace App\Http\Home\Services;
+use App\Models\User as UserModel;
use App\Repos\User as UserRepo;
use App\Services\Auth\Home as AuthService;
use App\Services\Logic\Account\Register as RegisterService;
+use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService;
use App\Validators\Account as AccountValidator;
use App\Validators\Captcha as CaptchaValidator;
@@ -48,6 +50,8 @@ class Account extends Service
$validator->checkCode($post['ticket'], $post['rand']);
+ $this->handleLoginNotice($user);
+
$this->auth->saveAuthInfo($user);
}
@@ -59,6 +63,8 @@ class Account extends Service
$user = $validator->checkVerifyLogin($post['account'], $post['verify_code']);
+ $this->handleLoginNotice($user);
+
$this->auth->saveAuthInfo($user);
}
@@ -67,4 +73,11 @@ class Account extends Service
$this->auth->clearAuthInfo();
}
+ protected function handleLoginNotice(UserModel $user)
+ {
+ $service = new AccountLoginNoticeService();
+
+ $service->createTask($user);
+ }
+
}
diff --git a/app/Http/Home/Services/Connect.php b/app/Http/Home/Services/Connect.php
index 0cef8358..2e3a26f5 100644
--- a/app/Http/Home/Services/Connect.php
+++ b/app/Http/Home/Services/Connect.php
@@ -8,6 +8,7 @@ use App\Repos\Connect as ConnectRepo;
use App\Repos\User as UserRepo;
use App\Services\Auth\Home as AuthService;
use App\Services\Logic\Account\Register as RegisterService;
+use App\Services\Logic\Notice\AccountLogin as AccountLoginNoticeService;
use App\Services\OAuth\QQ as QQAuth;
use App\Services\OAuth\WeiBo as WeiBoAuth;
use App\Services\OAuth\WeiXin as WeiXinAuth;
@@ -32,6 +33,8 @@ class Connect extends Service
$this->handleConnectRelation($user, $openUser);
+ $this->handleLoginNotice($user);
+
$auth = $this->getAppAuth();
$auth->saveAuthInfo($user);
@@ -57,6 +60,8 @@ class Connect extends Service
$this->handleConnectRelation($user, $openUser);
+ $this->handleLoginNotice($user);
+
$auth = $this->getAppAuth();
$auth->saveAuthInfo($user);
@@ -75,6 +80,8 @@ class Connect extends Service
$user = $userRepo->findById($connect->user_id);
+ $this->handleLoginNotice($user);
+
$auth = $this->getAppAuth();
$auth->saveAuthInfo($user);
@@ -208,4 +215,11 @@ class Connect extends Service
}
}
+ protected function handleLoginNotice(UserModel $user)
+ {
+ $service = new AccountLoginNoticeService();
+
+ $service->createTask($user);
+ }
+
}
diff --git a/app/Http/Home/Services/WechatOfficialAccount.php b/app/Http/Home/Services/WechatOfficialAccount.php
new file mode 100644
index 00000000..ea19fd02
--- /dev/null
+++ b/app/Http/Home/Services/WechatOfficialAccount.php
@@ -0,0 +1,238 @@
+getOfficialAccount();
+ }
+
+ public function createSubscribeQrCode()
+ {
+ $user = $this->getLoginUser();
+
+ $app = $this->getOfficialAccount();
+
+ $result = $app->qrcode->temporary($user->id);
+
+ return $app->qrcode->url($result['ticket']);
+ }
+
+ public function getSubscribeStatus()
+ {
+ $user = $this->getLoginUser();
+
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByUserId($user->id);
+
+ $status = 0;
+
+ if ($subscribe) {
+ $status = $subscribe->deleted == 0 ? 1 : 0;
+ }
+
+ return $status;
+ }
+
+ public function handleNotify($message)
+ {
+ $service = new WechatService();
+
+ $service->logger->debug('Received Message ' . json_encode($message));
+
+ switch ($message['MsgType']) {
+ case 'event':
+ switch ($message['Event']) {
+ case 'subscribe':
+ return $this->handleSubscribeEvent($message);
+ break;
+ case 'unsubscribe':
+ return $this->handleUnsubscribeEvent($message);
+ break;
+ case 'SCAN':
+ return $this->handleScanEvent($message);
+ break;
+ case 'CLICK':
+ return $this->handleClickEvent($message);
+ break;
+ case 'VIEW':
+ return $this->handleViewEvent($message);
+ break;
+ case 'LOCATION':
+ return $this->handleLocationEvent($message);
+ break;
+ default:
+ return $this->emptyReplyMessage();
+ break;
+ }
+ break;
+ case 'text':
+ return $this->handleTextReply($message);
+ break;
+ case 'image':
+ return $this->handleImageReply($message);
+ break;
+ case 'voice':
+ return $this->handleVoiceReply($message);
+ break;
+ case 'video':
+ return $this->handleVideoReply($message);
+ break;
+ case 'shortvideo':
+ return $this->handleShortVideoReply($message);
+ break;
+ case 'location':
+ return $this->handleLocationReply($message);
+ break;
+ case 'link':
+ return $this->handleLinkReply($message);
+ break;
+ default:
+ return $this->emptyReplyMessage();
+ break;
+ }
+ }
+
+ protected function handleSubscribeEvent($message)
+ {
+ $openId = $message['FromUserName'] ?? '';
+ $eventKey = $message['EventKey'] ?? '';
+
+ if (!$eventKey) {
+ return $this->emptyReplyMessage();
+ }
+
+ $userId = str_replace('qrscene_', '', $eventKey);
+
+ $this->handleSubscribeRelation($userId, $openId);
+
+ return new TextMessage('开心呀,我们又多了一个小伙伴!');
+ }
+
+ protected function handleUnsubscribeEvent($message)
+ {
+ $openId = $message['FromUserName'] ?? '';
+
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByOpenId($openId);
+
+ if ($subscribe) {
+ $subscribe->deleted = 1;
+ $subscribe->update();
+ }
+
+ return new TextMessage('伤心呀,我们又少了一个小伙伴!');
+ }
+
+ protected function handleScanEvent($message)
+ {
+ /**
+ * 注意:当已关注过用户扫码时,"EventKey"没有带"qrscene_"前缀
+ */
+ $openId = $message['FromUserName'] ?? '';
+ $eventKey = $message['EventKey'] ?? '';
+ $userId = $eventKey;
+
+ $this->handleSubscribeRelation($userId, $openId);
+ }
+
+ protected function handleClickEvent($message)
+ {
+ $this->defaultReplyMessage();
+ }
+
+ protected function handleViewEvent($message)
+ {
+ $this->defaultReplyMessage();
+ }
+
+ protected function handleLocationEvent($message)
+ {
+ $this->defaultReplyMessage();
+ }
+
+ protected function handleTextReply($message)
+ {
+ return $this->defaultReplyMessage();
+ }
+
+ protected function handleImageReply($message)
+ {
+ return $this->defaultReplyMessage();
+ }
+
+ protected function handleVoiceReply($message)
+ {
+ return $this->defaultReplyMessage();
+ }
+
+ protected function handleVideoReply($message)
+ {
+ return $this->defaultReplyMessage();
+ }
+
+ protected function handleShortVideoReply($message)
+ {
+ return $this->defaultReplyMessage();
+ }
+
+ protected function handleLocationReply($message)
+ {
+ return $this->defaultReplyMessage();
+ }
+
+ protected function handleLinkReply($message)
+ {
+ return $this->defaultReplyMessage();
+ }
+
+ protected function emptyReplyMessage()
+ {
+ return new TextMessage('');
+ }
+
+ protected function defaultReplyMessage()
+ {
+ return new TextMessage('没有匹配的服务,如有需要请联系客服!');
+ }
+
+ protected function handleSubscribeRelation($userId, $openId)
+ {
+ $validator = new UserValidator();
+
+ $validator->checkUser($userId);
+
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByOpenId($openId);
+
+ if ($subscribe) {
+ if ($subscribe->deleted == 1) {
+ $subscribe->deleted = 0;
+ $subscribe->update();
+ }
+ } else {
+ $subscribe = $subscribeRepo->findSubscribe($userId, $openId);
+ if (!$subscribe) {
+ $subscribe = new WechatSubscribeModel();
+ $subscribe->user_id = $userId;
+ $subscribe->open_id = $openId;
+ $subscribe->create();
+ }
+ }
+ }
+
+}
diff --git a/app/Http/Home/Views/user/console/menu.volt b/app/Http/Home/Views/user/console/menu.volt
index cc8ed6ac..e670e951 100644
--- a/app/Http/Home/Views/user/console/menu.volt
+++ b/app/Http/Home/Views/user/console/menu.volt
@@ -52,6 +52,9 @@
\ No newline at end of file
diff --git a/app/Http/Home/Views/user/console/subscribe.volt b/app/Http/Home/Views/user/console/subscribe.volt
new file mode 100644
index 00000000..1fe6a2c7
--- /dev/null
+++ b/app/Http/Home/Views/user/console/subscribe.volt
@@ -0,0 +1,33 @@
+{% extends 'templates/main.volt' %}
+
+{% block content %}
+
+
+
+
+
+
+ 关注订阅
+
+
+ {% if subscribed == 0 %}
+
+
订阅官方公众号,接收重要通知!
+ {% else %}
+
你已经订阅官方公众号
+ {% endif %}
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block include_js %}
+
+ {{ js_include('home/js/user.console.subscribe.js') }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/Library/AppInfo.php b/app/Library/AppInfo.php
index e0ab2ed3..44cf5878 100644
--- a/app/Library/AppInfo.php
+++ b/app/Library/AppInfo.php
@@ -11,7 +11,7 @@ class AppInfo
protected $link = 'https://gitee.com/koogua';
- protected $version = '1.2.1';
+ protected $version = '1.2.2';
public function __get($name)
{
diff --git a/app/Models/Consult.php b/app/Models/Consult.php
index a790c650..31ea904d 100644
--- a/app/Models/Consult.php
+++ b/app/Models/Consult.php
@@ -36,12 +36,19 @@ class Consult extends Model
public $chapter_id;
/**
- * 用户编号
+ * 提主编号
*
* @var int
*/
public $owner_id;
+ /**
+ * 答主编号
+ *
+ * @var int
+ */
+ public $replier_id;
+
/**
* 提问
*
diff --git a/app/Models/Role.php b/app/Models/Role.php
index 827a368a..a30b573b 100644
--- a/app/Models/Role.php
+++ b/app/Models/Role.php
@@ -105,6 +105,8 @@ class Role extends Model
{
if (is_array($this->routes) && !empty($this->routes)) {
$this->routes = kg_json_encode($this->routes);
+ } else {
+ $this->routes = '';
}
$this->create_time = time();
diff --git a/app/Models/Task.php b/app/Models/Task.php
index 45890752..dd85bb59 100644
--- a/app/Models/Task.php
+++ b/app/Models/Task.php
@@ -11,6 +11,12 @@ class Task extends Model
const TYPE_DELIVER = 1; // 发货
const TYPE_REFUND = 2; // 退款
+ 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; // 咨询回复通知
+
/**
* 优先级
*/
diff --git a/app/Models/WechatSubscribe.php b/app/Models/WechatSubscribe.php
new file mode 100644
index 00000000..e1633caa
--- /dev/null
+++ b/app/Models/WechatSubscribe.php
@@ -0,0 +1,79 @@
+addBehavior(
+ new SoftDelete([
+ 'field' => 'deleted',
+ 'value' => 1,
+ ])
+ );
+ }
+
+ public function beforeCreate()
+ {
+ $this->create_time = time();
+ }
+
+ public function beforeUpdate()
+ {
+ $this->update_time = time();
+ }
+
+}
diff --git a/app/Repos/CourseUser.php b/app/Repos/CourseUser.php
index 12704ac5..01ece420 100644
--- a/app/Repos/CourseUser.php
+++ b/app/Repos/CourseUser.php
@@ -146,4 +146,16 @@ class CourseUser extends Repository
->execute();
}
+ /**
+ * @param int $courseId
+ * @return ResultsetInterface|Resultset|CourseUserModel[]
+ */
+ public function findByCourseId($courseId)
+ {
+ return CourseUserModel::query()
+ ->where('course_id = :course_id:', ['course_id' => $courseId])
+ ->andWhere('deleted = 0')
+ ->execute();
+ }
+
}
diff --git a/app/Repos/WechatSubscribe.php b/app/Repos/WechatSubscribe.php
new file mode 100644
index 00000000..2043d889
--- /dev/null
+++ b/app/Repos/WechatSubscribe.php
@@ -0,0 +1,57 @@
+ 'user_id= ?1 AND open_id = ?2',
+ 'bind' => [1 => $userId, 2 => $openId],
+ ]);
+ }
+
+ /**
+ * @param int $id
+ * @return WechatSubscribeModel|Model|bool
+ */
+ public function findById($id)
+ {
+ return WechatSubscribeModel::findFirst($id);
+ }
+
+ /**
+ * @param int $userId
+ * @return WechatSubscribeModel|Model|bool
+ */
+ public function findByUserId($userId)
+ {
+ return WechatSubscribeModel::findFirst([
+ 'conditions' => 'user_id = :user_id:',
+ 'bind' => ['user_id' => $userId],
+ ]);
+ }
+
+ /**
+ * @param string $openId
+ * @return WechatSubscribeModel|Model|bool
+ */
+ public function findByOpenId($openId)
+ {
+ return WechatSubscribeModel::findFirst([
+ 'conditions' => 'open_id = :open_id:',
+ 'bind' => ['open_id' => $openId],
+ ]);
+ }
+
+}
diff --git a/app/Services/Auth/Mobile.php b/app/Services/Auth/Mobile.php
deleted file mode 100644
index 5f94bcb4..00000000
--- a/app/Services/Auth/Mobile.php
+++ /dev/null
@@ -1,44 +0,0 @@
-getAuthKey();
-
- $authInfo = [
- 'id' => $user->id,
- 'name' => $user->name,
- ];
-
- $this->session->set($authKey, $authInfo);
- }
-
- public function clearAuthInfo()
- {
- $authKey = $this->getAuthKey();
-
- $this->session->remove($authKey);
- }
-
- public function getAuthInfo()
- {
- $authKey = $this->getAuthKey();
-
- $authInfo = $this->session->get($authKey);
-
- return $authInfo ?: null;
- }
-
- public function getAuthKey()
- {
- return 'mobile_auth_info';
- }
-
-}
diff --git a/app/Services/ChapterVod.php b/app/Services/ChapterVod.php
index fe711dc6..89d6d292 100644
--- a/app/Services/ChapterVod.php
+++ b/app/Services/ChapterVod.php
@@ -41,9 +41,12 @@ class ChapterVod extends Service
$vodTemplates = $this->getVodTemplates();
+ /**
+ * 腾讯云播放器只支持[od|hd|sd],遇到fd替换为od
+ */
foreach ($vodTemplates as $key => $template) {
if ($height >= $template['height']) {
- return $key;
+ return $key == 'fd' ? $default : $key;
}
}
diff --git a/app/Services/LiveNotify.php b/app/Services/LiveNotify.php
index ef1fd11d..edf7982d 100644
--- a/app/Services/LiveNotify.php
+++ b/app/Services/LiveNotify.php
@@ -5,6 +5,8 @@ namespace App\Services;
use App\Models\Chapter as ChapterModel;
use App\Models\ChapterLive as ChapterLiveModel;
use App\Repos\Chapter as ChapterRepo;
+use App\Repos\CourseUser as CourseUserRepo;
+use App\Services\Logic\Notice\LiveBegin as LiveBeginNotice;
class LiveNotify extends Service
{
@@ -42,16 +44,6 @@ class LiveNotify extends Service
return $result;
}
- public function getNotifyKey()
- {
- return 'live_notify';
- }
-
- public function getSentNotifyKey()
- {
- return 'live_notify_sent';
- }
-
/**
* 推流
*/
@@ -73,7 +65,7 @@ class LiveNotify extends Service
$chapterLive->update(['status' => ChapterLiveModel::STATUS_ACTIVE]);
- $this->sendBeginNotify($chapter);
+ $this->handleStreamBeginNotice($chapter);
return true;
}
@@ -126,15 +118,21 @@ class LiveNotify extends Service
}
- protected function sendBeginNotify(ChapterModel $chapter)
+ protected function handleStreamBeginNotice(ChapterModel $chapter)
{
- $redis = $this->getRedis();
+ $courseUserRepo = new CourseUserRepo();
- $key = $this->getNotifyKey();
+ $courseUsers = $courseUserRepo->findByCourseId($chapter->course_id);
- $redis->sAdd($key, $chapter->id);
+ if ($courseUsers->count() == 0) {
+ return;
+ }
- $redis->expire($key, 86400);
+ $notice = new LiveBeginNotice();
+
+ foreach ($courseUsers as $courseUser) {
+ $notice->createTask($chapter, $courseUser);
+ }
}
protected function getChapter($streamName)
diff --git a/app/Services/Logic/Consult/ConsultInfo.php b/app/Services/Logic/Consult/ConsultInfo.php
index 52dcf335..f8b5590b 100644
--- a/app/Services/Logic/Consult/ConsultInfo.php
+++ b/app/Services/Logic/Consult/ConsultInfo.php
@@ -37,6 +37,7 @@ class ConsultInfo extends Service
$result['course'] = $this->handleCourseInfo($consult);
$result['chapter'] = $this->handleChapterInfo($consult);
$result['owner'] = $this->handleOwnerInfo($consult);
+ $result['replier'] = $this->handleReplierInfo($consult);
return $result;
}
@@ -85,4 +86,19 @@ class ConsultInfo extends Service
];
}
+ protected function handleReplierInfo(ConsultModel $consult)
+ {
+ $userRepo = new UserRepo();
+
+ $replier = $userRepo->findById($consult->replier_id);
+
+ if (!$replier) return new \stdClass();
+
+ return [
+ 'id' => $replier->id,
+ 'name' => $replier->name,
+ 'avatar' => $replier->avatar,
+ ];
+ }
+
}
diff --git a/app/Services/Logic/Consult/ConsultReply.php b/app/Services/Logic/Consult/ConsultReply.php
index 1b0e457a..25e88371 100644
--- a/app/Services/Logic/Consult/ConsultReply.php
+++ b/app/Services/Logic/Consult/ConsultReply.php
@@ -2,11 +2,13 @@
namespace App\Services\Logic\Consult;
+use App\Models\Consult as ConsultModel;
use App\Services\Logic\ConsultTrait;
-use App\Services\Logic\Service;
+use App\Services\Logic\Notice\ConsultReply as ConsultReplyNotice;
+use App\Services\Logic\Service as LogicService;
use App\Validators\Consult as ConsultValidator;
-class ConsultReply extends Service
+class ConsultReply extends LogicService
{
use ConsultTrait;
@@ -25,12 +27,29 @@ class ConsultReply extends Service
$answer = $validator->checkAnswer($post['answer']);
- $consult->update([
- 'answer' => $answer,
- 'reply_time' => time(),
- ]);
+ $firstReply = false;
+
+ if ($consult->reply_time == 0) {
+ $firstReply = true;
+ }
+
+ $consult->replier_id = $user->id;
+ $consult->reply_time = time();
+ $consult->answer = $answer;
+ $consult->update();
+
+ if ($firstReply) {
+ $this->handleReplyNotice($consult);
+ }
return $consult;
}
+ protected function handleReplyNotice(ConsultModel $consult)
+ {
+ $notice = new ConsultReplyNotice();
+
+ $notice->createTask($consult);
+ }
+
}
diff --git a/app/Services/Logic/Notice/AccountLogin.php b/app/Services/Logic/Notice/AccountLogin.php
new file mode 100644
index 00000000..6ae4b18f
--- /dev/null
+++ b/app/Services/Logic/Notice/AccountLogin.php
@@ -0,0 +1,61 @@
+item_info;
+
+ $userId = $task->item_info['user']['id'];
+
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByUserId($userId);
+
+ if ($subscribe && $subscribe->deleted == 0) {
+
+ $notice = new WechatAccountLoginNotice();
+
+ return $notice->handle($subscribe, $params);
+ }
+ }
+
+ public function createTask(UserModel $user)
+ {
+ $task = new TaskModel();
+
+ $loginIp = $this->getClientIp();
+ $loginRegion = kg_ip2region($loginIp);
+
+ $itemInfo = [
+ 'user' => [
+ 'id' => $user->id,
+ 'name' => $user->name,
+ ],
+ 'login_ip' => $loginIp,
+ 'login_region' => $loginRegion,
+ 'login_time' => time(),
+ ];
+
+ $task->item_id = $user->id;
+ $task->item_info = $itemInfo;
+ $task->item_type = TaskModel::TYPE_NOTICE_ACCOUNT_LOGIN;
+ $task->priority = TaskModel::PRIORITY_LOW;
+ $task->status = TaskModel::STATUS_PENDING;
+
+ $task->create();
+ }
+
+}
diff --git a/app/Services/Logic/Notice/ConsultReply.php b/app/Services/Logic/Notice/ConsultReply.php
new file mode 100644
index 00000000..cdd3c34a
--- /dev/null
+++ b/app/Services/Logic/Notice/ConsultReply.php
@@ -0,0 +1,91 @@
+item_info['consult']['id'];
+
+ $consultRepo = new ConsultRepo();
+
+ $consult = $consultRepo->findById($consultId);
+
+ $courseRepo = new CourseRepo();
+
+ $course = $courseRepo->findById($consult->course_id);
+
+ $userRepo = new UserRepo();
+
+ $user = $userRepo->findById($consult->owner_id);
+
+ $replier = $userRepo->findById($consult->replier_id);
+
+ $params = [
+ 'user' => [
+ 'id' => $user->id,
+ 'name' => $user->name,
+ ],
+ 'replier' => [
+ 'id' => $replier->id,
+ 'name' => $replier->name,
+ ],
+ 'consult' => [
+ 'id' => $consult->id,
+ 'question' => $consult->question,
+ 'answer' => $consult->answer,
+ ],
+ 'course' => [
+ 'id' => $course->id,
+ 'title' => $course->title,
+ ],
+ ];
+
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByUserId($consult->owner_id);
+
+ if ($subscribe && $subscribe->deleted == 0) {
+
+ $notice = new WechatConsultReplyNotice();
+
+ return $notice->handle($subscribe, $params);
+
+ } else {
+
+ $notice = new SmsConsultReplyNotice();
+
+ return $notice->handle($user, $params);
+ }
+ }
+
+ public function createTask(ConsultModel $consult)
+ {
+ $task = new TaskModel();
+
+ $itemInfo = [
+ 'consult' => ['id' => $consult->id],
+ ];
+
+ $task->item_id = $consult->id;
+ $task->item_info = $itemInfo;
+ $task->item_type = TaskModel::TYPE_NOTICE_CONSULT_REPLY;
+ $task->priority = TaskModel::PRIORITY_LOW;
+ $task->status = TaskModel::STATUS_PENDING;
+
+ $task->create();
+ }
+
+}
diff --git a/app/Services/Logic/Notice/LiveBegin.php b/app/Services/Logic/Notice/LiveBegin.php
new file mode 100644
index 00000000..f125c56c
--- /dev/null
+++ b/app/Services/Logic/Notice/LiveBegin.php
@@ -0,0 +1,99 @@
+item_info['course_user'];
+ $chapterId = $task->item_info['chapter']['id'];
+
+ $courseRepo = new CourseRepo();
+
+ $course = $courseRepo->findById($courseUser['course_id']);
+
+ $userRepo = new UserRepo();
+
+ $user = $userRepo->findById($courseUser['user_id']);
+
+ $chapterRepo = new ChapterRepo();
+
+ $chapter = $chapterRepo->findById($chapterId);
+
+ $params = [
+ 'user' => [
+ 'id' => $user->id,
+ 'name' => $user->name,
+ ],
+ 'course' => [
+ 'id' => $course->id,
+ 'title' => $course->title,
+ ],
+ 'chapter' => [
+ 'id' => $chapter->id,
+ 'title' => $chapter->title,
+ ],
+ 'live' => [
+ 'start_time' => $chapter->attrs['start_time'],
+ 'end_time' => $chapter->attrs['end_time'],
+ ],
+ 'course_user' => $courseUser,
+ ];
+
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByUserId($user->id);
+
+ if ($subscribe && $subscribe->deleted == 0) {
+
+ $notice = new WechatLiveBeginNotice();
+
+ return $notice->handle($subscribe, $params);
+
+ } else {
+
+ $notice = new SmsLiveBeginNotice();
+
+ return $notice->handle($user, $params);
+ }
+ }
+
+ public function createTask(ChapterModel $chapter, CourseUserModel $courseUser)
+ {
+ $task = new TaskModel();
+
+ $itemInfo = [
+ 'course_user' => [
+ 'course_id' => $courseUser->course_id,
+ 'user_id' => $courseUser->user_id,
+ 'role_type' => $courseUser->role_type,
+ 'source_type' => $courseUser->role_type,
+ ],
+ 'chapter' => [
+ 'id' => $chapter->id,
+ ],
+ ];
+
+ $task->item_id = $chapter->id;
+ $task->item_info = $itemInfo;
+ $task->item_type = TaskModel::TYPE_NOTICE_LIVE_BEGIN;
+ $task->priority = TaskModel::PRIORITY_LOW;
+ $task->status = TaskModel::STATUS_PENDING;
+
+ $task->create();
+ }
+
+}
diff --git a/app/Services/Logic/Notice/OrderFinish.php b/app/Services/Logic/Notice/OrderFinish.php
new file mode 100644
index 00000000..5b70e132
--- /dev/null
+++ b/app/Services/Logic/Notice/OrderFinish.php
@@ -0,0 +1,76 @@
+item_info['order']['id'];
+
+ $orderRepo = new OrderRepo();
+
+ $order = $orderRepo->findById($orderId);
+
+ $userRepo = new UserRepo();
+
+ $user = $userRepo->findById($order->owner_id);
+
+ $params = [
+ 'user' => [
+ 'id' => $user->id,
+ 'name' => $user->name,
+ ],
+ 'order' => [
+ 'sn' => $order->sn,
+ 'subject' => $order->subject,
+ 'amount' => $order->amount,
+ ],
+ ];
+
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByUserId($order->owner_id);
+
+ if ($subscribe && $subscribe->deleted == 0) {
+
+ $notice = new WechatOrderFinishNotice();
+
+ return $notice->handle($subscribe, $params);
+
+ } else {
+
+ $notice = new SmsOrderFinishNotice();
+
+ return $notice->handle($user, $params);
+ }
+ }
+
+ public function createTask(OrderModel $order)
+ {
+ $task = new TaskModel();
+
+ $itemInfo = [
+ 'order' => ['id' => $order->id],
+ ];
+
+ $task->item_id = $order->id;
+ $task->item_info = $itemInfo;
+ $task->item_type = TaskModel::TYPE_NOTICE_ORDER_FINISH;
+ $task->priority = TaskModel::PRIORITY_HIGH;
+ $task->status = TaskModel::STATUS_PENDING;
+
+ $task->create();
+ }
+
+}
diff --git a/app/Services/Logic/Notice/RefundFinish.php b/app/Services/Logic/Notice/RefundFinish.php
new file mode 100644
index 00000000..468f5539
--- /dev/null
+++ b/app/Services/Logic/Notice/RefundFinish.php
@@ -0,0 +1,76 @@
+item_info['refund']['id'];
+
+ $refundRepo = new RefundRepo();
+
+ $refund = $refundRepo->findById($refundId);
+
+ $userRepo = new UserRepo();
+
+ $user = $userRepo->findById($refund->owner_id);
+
+ $params = [
+ 'user' => [
+ 'id' => $user->id,
+ 'name' => $user->name,
+ ],
+ 'order' => [
+ 'sn' => $refund->sn,
+ 'subject' => $refund->subject,
+ 'amount' => $refund->amount,
+ ],
+ ];
+
+ $subscribeRepo = new WechatSubscribeRepo();
+
+ $subscribe = $subscribeRepo->findByUserId($refund->owner_id);
+
+ if ($subscribe && $subscribe->deleted == 0) {
+
+ $notice = new WechatRefundFinishNotice();
+
+ return $notice->handle($subscribe, $params);
+
+ } else {
+
+ $notice = new SmsRefundFinishNotice();
+
+ return $notice->handle($user, $params);
+ }
+ }
+
+ public function createTask(RefundModel $refund)
+ {
+ $task = new TaskModel();
+
+ $itemInfo = [
+ 'refund' => ['id' => $refund->id],
+ ];
+
+ $task->item_id = $refund->id;
+ $task->item_info = $itemInfo;
+ $task->item_type = TaskModel::TYPE_NOTICE_ORDER_FINISH;
+ $task->priority = TaskModel::PRIORITY_MIDDLE;
+ $task->status = TaskModel::STATUS_PENDING;
+
+ $task->create();
+ }
+
+}
diff --git a/app/Services/Sms/Live.php b/app/Services/Sms/Live.php
deleted file mode 100644
index 356f40f1..00000000
--- a/app/Services/Sms/Live.php
+++ /dev/null
@@ -1,50 +0,0 @@
-findById($userId);
-
- if (empty($account->phone)) {
- return false;
- }
-
- $chapterRepo = new ChapterRepo();
-
- $chapter = $chapterRepo->findById($chapterId);
-
- $courseRepo = new CourseRepo();
-
- $course = $courseRepo->findById($chapter->course_id);
-
- $params = [
- $course->title,
- $chapter->title,
- $startTime,
- ];
-
- $templateId = $this->getTemplateId($this->templateCode);
-
- return $this->send($account->phone, $templateId, $params);
- }
-
-}
diff --git a/app/Services/Sms/Notice/ConsultReply.php b/app/Services/Sms/Notice/ConsultReply.php
new file mode 100644
index 00000000..85d21f21
--- /dev/null
+++ b/app/Services/Sms/Notice/ConsultReply.php
@@ -0,0 +1,37 @@
+findById($user->id);
+
+ if (!$account->phone) return null;
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ $params = [
+ $params['replier']['name'],
+ $params['course']['title'],
+ ];
+
+ return $this->send($account->phone, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/Sms/Notice/LiveBegin.php b/app/Services/Sms/Notice/LiveBegin.php
new file mode 100644
index 00000000..104b8f86
--- /dev/null
+++ b/app/Services/Sms/Notice/LiveBegin.php
@@ -0,0 +1,38 @@
+findById($user->id);
+
+ if (!$account->phone) return null;
+
+ $params = [
+ $params['course']['title'],
+ $params['chapter']['title'],
+ date('H:i', $params['live']['start_time']),
+ ];
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ return $this->send($account->phone, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/Sms/Notice/OrderFinish.php b/app/Services/Sms/Notice/OrderFinish.php
new file mode 100644
index 00000000..e67fd2a0
--- /dev/null
+++ b/app/Services/Sms/Notice/OrderFinish.php
@@ -0,0 +1,38 @@
+findById($user->id);
+
+ if (!$account->phone) return null;
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ $params = [
+ $params['order']['subject'],
+ $params['order']['sn'],
+ $params['order']['amount'],
+ ];
+
+ return $this->send($account->phone, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/Sms/Notice/RefundFinish.php b/app/Services/Sms/Notice/RefundFinish.php
new file mode 100644
index 00000000..f64d8908
--- /dev/null
+++ b/app/Services/Sms/Notice/RefundFinish.php
@@ -0,0 +1,38 @@
+findById($user->id);
+
+ if (!$account->phone) return null;
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ $params = [
+ $params['refund']['subject'],
+ $params['refund']['sn'],
+ $params['refund']['amount'],
+ ];
+
+ return $this->send($account->phone, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/Sms/Order.php b/app/Services/Sms/Order.php
deleted file mode 100644
index 64e4a6df..00000000
--- a/app/Services/Sms/Order.php
+++ /dev/null
@@ -1,39 +0,0 @@
-findById($order->owner_id);
-
- if (empty($account->phone)) {
- return false;
- }
-
- $templateId = $this->getTemplateId($this->templateCode);
-
- $params = [
- $order->subject,
- $order->sn,
- $order->amount,
- ];
-
- return $this->send($account->phone, $templateId, $params);
- }
-
-}
diff --git a/app/Services/Sms/Refund.php b/app/Services/Sms/Refund.php
deleted file mode 100644
index 5796f82b..00000000
--- a/app/Services/Sms/Refund.php
+++ /dev/null
@@ -1,39 +0,0 @@
-findById($refund->owner_id);
-
- if (empty($account->phone)) {
- return false;
- }
-
- $templateId = $this->getTemplateId($this->templateCode);
-
- $params = [
- $refund->subject,
- $refund->sn,
- $refund->amount,
- ];
-
- return $this->send($account->phone, $templateId, $params);
- }
-
-}
diff --git a/app/Services/Wechat.php b/app/Services/Wechat.php
new file mode 100644
index 00000000..ed106111
--- /dev/null
+++ b/app/Services/Wechat.php
@@ -0,0 +1,59 @@
+logger = $this->getLogger('wechat');
+ }
+
+ public function getOfficialAccount()
+ {
+ $settings = $this->getSettings('wechat.oa');
+
+ $config = [
+ 'app_id' => $settings['app_id'],
+ 'secret' => $settings['app_secret'],
+ 'token' => $settings['app_token'],
+ 'aes_key' => $settings['aes_key'],
+ 'log' => $this->getLogOptions(),
+ ];
+
+ return Factory::officialAccount($config);
+ }
+
+ protected function getLogOptions()
+ {
+ $config = $this->getConfig();
+
+ $default = $config->get('env') == ENV_DEV ? 'dev' : 'prod';
+
+ return [
+ 'default' => $default,
+ 'channels' => [
+ 'dev' => [
+ 'driver' => 'daily',
+ 'path' => log_path('wechat.log'),
+ 'level' => 'debug',
+ ],
+ 'prod' => [
+ 'driver' => 'daily',
+ 'path' => log_path('wechat.log'),
+ 'level' => 'info',
+ ],
+ ]
+ ];
+ }
+
+}
diff --git a/app/Services/Wechat/Notice/AccountLogin.php b/app/Services/Wechat/Notice/AccountLogin.php
new file mode 100644
index 00000000..b4e901e7
--- /dev/null
+++ b/app/Services/Wechat/Notice/AccountLogin.php
@@ -0,0 +1,43 @@
+ $first,
+ 'remark' => $remark,
+ 'keyword1' => $loginRegion,
+ 'keyword2' => $loginTime,
+ ];
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ return $this->send($subscribe->open_id, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/Wechat/Notice/ConsultReply.php b/app/Services/Wechat/Notice/ConsultReply.php
new file mode 100644
index 00000000..f33db7f9
--- /dev/null
+++ b/app/Services/Wechat/Notice/ConsultReply.php
@@ -0,0 +1,35 @@
+ $first,
+ 'remark' => $remark,
+ 'keyword1' => $params['course']['title'],
+ ];
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ return $this->send($subscribe->open_id, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/Wechat/Notice/LiveBegin.php b/app/Services/Wechat/Notice/LiveBegin.php
new file mode 100644
index 00000000..83077f4d
--- /dev/null
+++ b/app/Services/Wechat/Notice/LiveBegin.php
@@ -0,0 +1,36 @@
+ $first,
+ 'remark' => $remark,
+ 'keyword1' => $params['course']['title'],
+ 'keyword2' => $params['chapter']['title'],
+ 'keyword3' => date('Y-m-d H:i', $params['live']['start_time']),
+ ];
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ return $this->send($subscribe->open_id, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/Wechat/Notice/OrderFinish.php b/app/Services/Wechat/Notice/OrderFinish.php
new file mode 100644
index 00000000..c21ea2a9
--- /dev/null
+++ b/app/Services/Wechat/Notice/OrderFinish.php
@@ -0,0 +1,37 @@
+ $first,
+ 'remark' => $remark,
+ 'keyword1' => $params['order']['subject'],
+ 'keyword2' => $params['order']['sn'],
+ 'keyword3' => $params['order']['amount'],
+ ];
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ return $this->send($subscribe->open_id, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/Wechat/Notice/RefundFinish.php b/app/Services/Wechat/Notice/RefundFinish.php
new file mode 100644
index 00000000..6b2030fe
--- /dev/null
+++ b/app/Services/Wechat/Notice/RefundFinish.php
@@ -0,0 +1,36 @@
+ $first,
+ 'remark' => $remark,
+ 'keyword1' => $params['refund']['subject'],
+ 'keyword2' => $params['refund']['sn'],
+ 'keyword3' => $params['refund']['amount'],
+ ];
+
+ $templateId = $this->getTemplateId($this->templateCode);
+
+ return $this->send($subscribe->open_id, $templateId, $params);
+ }
+
+}
diff --git a/app/Services/WechatNotice.php b/app/Services/WechatNotice.php
new file mode 100644
index 00000000..18c85ba4
--- /dev/null
+++ b/app/Services/WechatNotice.php
@@ -0,0 +1,103 @@
+settings = $this->getSettings('wechat.oa');
+
+ $this->logger = $this->getLogger('wechat');
+ }
+
+ /**
+ * 发送模板消息
+ *
+ * @param string $openId
+ * @param string $templateId
+ * @param array $params
+ * @param string $url
+ * @param array $miniProgram
+ * @return bool
+ */
+ public function send($openId, $templateId, $params, $url = null, $miniProgram = [])
+ {
+ $service = new WechatService();
+
+ $app = $service->getOfficialAccount();
+
+ $content = [
+ 'touser' => $openId,
+ 'template_id' => $templateId,
+ 'data' => $this->formatParams($params),
+ ];
+
+ if ($url) {
+ $content['url'] = $url;
+ }
+
+ if ($miniProgram) {
+ $content['miniprogram'] = $miniProgram;
+ }
+
+ try {
+
+ $this->logger->debug('Send Template Message Request ' . kg_json_encode($content));
+
+ $response = $app->template_message->send($content);
+
+ $this->logger->debug('Send Template Message Response ' . kg_json_encode($response));
+
+ $result = $response['errcode'] == 0;
+
+ if ($result == false) {
+ $this->logger->error('Send Template Message Failed ' . kg_json_encode($response));
+ }
+
+ } catch (\Exception $e) {
+
+ $this->logger->error('Send Template Message Exception ' . kg_json_encode([
+ 'code' => $e->getCode(),
+ 'message' => $e->getMessage(),
+ ]));
+
+ $result = false;
+ }
+
+ return $result;
+ }
+
+ protected function formatParams($params)
+ {
+ if (!empty($params)) {
+ $params = array_map(function ($value) {
+ return strval($value);
+ }, $params);
+ }
+
+ return $params;
+ }
+
+ protected function getTemplateId($code)
+ {
+ $template = json_decode($this->settings['notice_template'], true);
+
+ return $template[$code] ?? null;
+ }
+
+}
diff --git a/composer.json b/composer.json
index bcd0e1cc..fe283e37 100644
--- a/composer.json
+++ b/composer.json
@@ -20,7 +20,8 @@
"aferrandini/phpqrcode": "1.0.1",
"xiaochong0302/ip2region": "^1.0",
"robmorgan/phinx": "^0.12",
- "lcobucci/jwt": "^3.3"
+ "lcobucci/jwt": "^3.3",
+ "overtrue/wechat": "^4.2"
},
"require-dev": {
"odan/phinx-migrations-generator": "^5.1",
diff --git a/composer.lock b/composer.lock
index 02109891..d6cb71b6 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,20 +1,20 @@
{
- "_readme": [
- "This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
- "This file is @generated automatically"
- ],
- "content-hash": "cce345a8509cd31ff8492310a2a2332a",
- "packages": [
- {
- "name": "aferrandini/phpqrcode",
- "version": "1.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/aferrandini/PHPQRCode.git",
- "reference": "3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46"
- },
- "dist": {
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "60ff0e1868be7414a1b31d397ced7fbd",
+ "packages": [
+ {
+ "name": "aferrandini/phpqrcode",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/aferrandini/PHPQRCode.git",
+ "reference": "3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46"
+ },
+ "dist": {
"type": "zip",
"url": "https://api.github.com/repos/aferrandini/PHPQRCode/zipball/3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46",
"reference": "3c1c0454d43710ab5bbe19a51ad4cb41c22e3d46",
@@ -25,27 +25,27 @@
"preferred": true
}
]
- },
- "require": {
- "php": ">=5.3.0"
- },
- "type": "library",
- "autoload": {
- "psr-0": {
- "PHPQRCode": "lib/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Ariel Ferrandini",
- "email": "arielferrandini@gmail.com",
- "homepage": "http://www.ferrandini.com/",
- "role": "Developer"
- }
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "PHPQRCode": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.jp/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ariel Ferrandini",
+ "email": "arielferrandini@gmail.com",
+ "homepage": "http://www.ferrandini.com/",
+ "role": "Developer"
+ }
],
"description": "PHPQRCode porting and changed for PHP 5.3 compatibility",
"homepage": "https://github.com/aferrandini/PHPQRCode",
@@ -344,26 +344,80 @@
],
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
- "keywords": [
- "annotations",
- "docblock",
- "lexer",
- "parser",
- "php"
- ],
- "time": "2019-07-30T19:33:28+00:00"
+ "keywords": [
+ "annotations",
+ "docblock",
+ "lexer",
+ "parser",
+ "php"
+ ],
+ "time": "2019-07-30T19:33:28+00:00"
},
+ {
+ "name": "easywechat-composer/easywechat-composer",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mingyoung/easywechat-composer.git",
+ "reference": "93cfce1ec842b9a5b1b0791a52afd18b833f114a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mingyoung/easywechat-composer/zipball/93cfce1ec842b9a5b1b0791a52afd18b833f114a",
+ "reference": "93cfce1ec842b9a5b1b0791a52afd18b833f114a",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "composer-plugin-api": "^1.0 || ^2.0",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "composer/composer": "^1.0 || ^2.0",
+ "phpunit/phpunit": "^6.5 || ^7.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "EasyWeChatComposer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "EasyWeChatComposer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
{
- "name": "egulias/email-validator",
- "version": "2.1.11",
- "source": {
- "type": "git",
- "url": "https://github.com/egulias/EmailValidator.git",
- "reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/92dd169c32f6f55ba570c309d83f5209cefb5e23",
+ "name": "张铭阳",
+ "email": "mingyoungcheung@gmail.com"
+ }
+ ],
+ "description": "The composer plugin for EasyWeChat",
+ "support": {
+ "issues": "https://github.com/mingyoung/easywechat-composer/issues",
+ "source": "https://github.com/mingyoung/easywechat-composer/tree/1.4.0"
+ },
+ "time": "2020-07-23T11:06:47+00:00"
+ },
+ {
+ "name": "egulias/email-validator",
+ "version": "2.1.11",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/egulias/EmailValidator.git",
+ "reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/92dd169c32f6f55ba570c309d83f5209cefb5e23",
"reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23",
"shasum": "",
"mirrors": [
@@ -1022,25 +1076,172 @@
"homepage": "https://github.com/mtdowling"
}
],
- "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
- "keywords": [
- "cron",
- "schedule"
- ],
- "abandoned": "dragonmantank/cron-expression",
- "time": "2017-01-23T04:29:33+00:00"
+ "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
+ "keywords": [
+ "cron",
+ "schedule"
+ ],
+ "abandoned": "dragonmantank/cron-expression",
+ "time": "2017-01-23T04:29:33+00:00"
},
+ {
+ "name": "overtrue/socialite",
+ "version": "2.0.22",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/overtrue/socialite.git",
+ "reference": "0ce3285293026a639de317a70b01eeef051e9962"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/overtrue/socialite/zipball/0ce3285293026a639de317a70b01eeef051e9962",
+ "reference": "0ce3285293026a639de317a70b01eeef051e9962",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "guzzlehttp/guzzle": "^5.0|^6.0|^7.0",
+ "php": ">=5.6",
+ "symfony/http-foundation": "^2.7|^3.0|^4.0|^5.0"
+ },
+ "conflict": {
+ "socialiteproviders/weixin": "*"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.2",
+ "phpunit/phpunit": "~6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Overtrue\\Socialite\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
{
- "name": "paragonie/random_compat",
- "version": "v9.99.99",
- "source": {
- "type": "git",
- "url": "https://github.com/paragonie/random_compat.git",
- "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
+ "name": "overtrue",
+ "email": "anzhengchao@gmail.com"
+ }
+ ],
+ "description": "A collection of OAuth 2 packages that extracts from laravel/socialite.",
+ "keywords": [
+ "login",
+ "oauth",
+ "qq",
+ "social",
+ "wechat",
+ "weibo"
+ ],
+ "support": {
+ "issues": "https://github.com/overtrue/socialite/issues",
+ "source": "https://github.com/overtrue/socialite/tree/2.0.22"
+ },
+ "funding": [
+ {
+ "url": "https://www.patreon.com/overtrue",
+ "type": "patreon"
+ }
+ ],
+ "time": "2020-11-12T23:23:15+00:00"
+ },
+ {
+ "name": "overtrue/wechat",
+ "version": "4.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/w7corp/easywechat.git",
+ "reference": "121607188e1cb1039a5ea0f49bcec011cb44dbdd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/w7corp/easywechat/zipball/121607188e1cb1039a5ea0f49bcec011cb44dbdd",
+ "reference": "121607188e1cb1039a5ea0f49bcec011cb44dbdd",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "easywechat-composer/easywechat-composer": "^1.1",
+ "ext-fileinfo": "*",
+ "ext-openssl": "*",
+ "ext-simplexml": "*",
+ "guzzlehttp/guzzle": "^6.2 || ^7.0",
+ "monolog/monolog": "^1.22 || ^2.0",
+ "overtrue/socialite": "~2.0",
+ "php": ">=7.2",
+ "pimple/pimple": "^3.0",
+ "psr/simple-cache": "^1.0",
+ "symfony/cache": "^3.3 || ^4.3 || ^5.0",
+ "symfony/event-dispatcher": "^4.3 || ^5.0",
+ "symfony/http-foundation": "^2.7 || ^3.0 || ^4.0 || ^5.0",
+ "symfony/psr-http-message-bridge": "^0.3 || ^1.0 || ^2.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.15",
+ "mikey179/vfsstream": "^1.6",
+ "mockery/mockery": "^1.2.3",
+ "phpstan/phpstan": "^0.12.0",
+ "phpunit/phpunit": "^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "EasyWeChat\\": "src/"
+ },
+ "files": [
+ "src/Kernel/Support/Helpers.php",
+ "src/Kernel/Helpers.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "overtrue",
+ "email": "anzhengchao@gmail.com"
+ }
+ ],
+ "description": "微信SDK",
+ "keywords": [
+ "easywechat",
+ "sdk",
+ "wechat",
+ "weixin",
+ "weixin-sdk"
+ ],
+ "support": {
+ "issues": "https://github.com/w7corp/easywechat/issues",
+ "source": "https://github.com/w7corp/easywechat/tree/4.3.3"
+ },
+ "time": "2020-12-07T08:20:11+00:00"
+ },
+ {
+ "name": "paragonie/random_compat",
+ "version": "v9.99.99",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/paragonie/random_compat.git",
+ "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
"reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
"shasum": "",
"mirrors": [
@@ -1208,26 +1409,85 @@
"homepage": "https://github.com/phalcon/incubator/graphs/contributors"
}
],
- "description": "Adapters, prototypes or functionality that can be potentially incorporated to the C-framework.",
- "homepage": "https://phalconphp.com",
- "keywords": [
- "framework",
- "incubator",
- "phalcon"
- ],
- "time": "2019-09-16T13:54:24+00:00"
+ "description": "Adapters, prototypes or functionality that can be potentially incorporated to the C-framework.",
+ "homepage": "https://phalconphp.com",
+ "keywords": [
+ "framework",
+ "incubator",
+ "phalcon"
+ ],
+ "time": "2019-09-16T13:54:24+00:00"
},
+ {
+ "name": "pimple/pimple",
+ "version": "v3.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/silexphp/Pimple.git",
+ "reference": "21e45061c3429b1e06233475cc0e1f6fc774d5b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/silexphp/Pimple/zipball/21e45061c3429b1e06233475cc0e1f6fc774d5b0",
+ "reference": "21e45061c3429b1e06233475cc0e1f6fc774d5b0",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/container": "^1.0"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Pimple": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
{
- "name": "psr/cache",
- "version": "1.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/php-fig/cache.git",
- "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "Pimple, a simple Dependency Injection Container",
+ "homepage": "https://pimple.symfony.com",
+ "keywords": [
+ "container",
+ "dependency injection"
+ ],
+ "support": {
+ "source": "https://github.com/silexphp/Pimple/tree/v3.3.1"
+ },
+ "time": "2020-11-24T20:35:42+00:00"
+ },
+ {
+ "name": "psr/cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
"reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
"shasum": "",
"mirrors": [
@@ -1457,27 +1717,27 @@
"php": ">=5.3.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Psr\\SimpleCache\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
- }
- ],
- "description": "Common interfaces for simple caching",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.jp/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
"keywords": [
"cache",
"caching",
@@ -1775,26 +2035,212 @@
"email": "fabien@symfony.com"
}
],
- "description": "Swiftmailer, free feature-rich PHP mailer",
- "homepage": "https://swiftmailer.symfony.com",
- "keywords": [
- "email",
- "mail",
- "mailer"
- ],
- "time": "2019-04-21T09:21:45+00:00"
+ "description": "Swiftmailer, free feature-rich PHP mailer",
+ "homepage": "https://swiftmailer.symfony.com",
+ "keywords": [
+ "email",
+ "mail",
+ "mailer"
+ ],
+ "time": "2019-04-21T09:21:45+00:00"
+ },
+ {
+ "name": "symfony/cache",
+ "version": "v5.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/cache.git",
+ "reference": "c15fd2b3dcf2bd7d5ee3265874870d6cc694306b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/c15fd2b3dcf2bd7d5ee3265874870d6cc694306b",
+ "reference": "c15fd2b3dcf2bd7d5ee3265874870d6cc694306b",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/cache": "~1.0",
+ "psr/log": "^1.1",
+ "symfony/cache-contracts": "^1.1.7|^2",
+ "symfony/polyfill-php80": "^1.15",
+ "symfony/service-contracts": "^1.1|^2",
+ "symfony/var-exporter": "^4.4|^5.0"
+ },
+ "conflict": {
+ "doctrine/dbal": "<2.10",
+ "symfony/dependency-injection": "<4.4",
+ "symfony/http-kernel": "<4.4",
+ "symfony/var-dumper": "<4.4"
+ },
+ "provide": {
+ "psr/cache-implementation": "1.0",
+ "psr/simple-cache-implementation": "1.0",
+ "symfony/cache-implementation": "1.0"
+ },
+ "require-dev": {
+ "cache/integration-tests": "dev-master",
+ "doctrine/cache": "^1.6",
+ "doctrine/dbal": "^2.10|^3.0",
+ "predis/predis": "^1.1",
+ "psr/simple-cache": "^1.0",
+ "symfony/config": "^4.4|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
+ "symfony/filesystem": "^4.4|^5.0",
+ "symfony/http-kernel": "^4.4|^5.0",
+ "symfony/messenger": "^4.4|^5.0",
+ "symfony/var-dumper": "^4.4|^5.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Cache\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
- "name": "symfony/config",
- "version": "v5.1.3",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/config.git",
- "reference": "cf63f0613a6c6918e96db39c07a43b01e19a0773"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/cf63f0613a6c6918e96db39c07a43b01e19a0773",
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Cache component with PSR-6, PSR-16, and tags",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "caching",
+ "psr6"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/cache/tree/v5.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-11-21T09:39:55+00:00"
+ },
+ {
+ "name": "symfony/cache-contracts",
+ "version": "v2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/cache-contracts.git",
+ "reference": "8034ca0b61d4dd967f3698aaa1da2507b631d0cb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/8034ca0b61d4dd967f3698aaa1da2507b631d0cb",
+ "reference": "8034ca0b61d4dd967f3698aaa1da2507b631d0cb",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/cache": "^1.0"
+ },
+ "suggest": {
+ "symfony/cache-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Cache\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to caching",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/cache-contracts/tree/v2.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-09-07T11:33:47+00:00"
+ },
+ {
+ "name": "symfony/config",
+ "version": "v5.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/config.git",
+ "reference": "cf63f0613a6c6918e96db39c07a43b01e19a0773"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/config/zipball/cf63f0613a6c6918e96db39c07a43b01e19a0773",
"reference": "cf63f0613a6c6918e96db39c07a43b01e19a0773",
"shasum": "",
"mirrors": [
@@ -1967,33 +2413,33 @@
],
"time": "2020-07-06T13:18:39+00:00"
},
- {
- "name": "symfony/deprecation-contracts",
- "version": "v2.1.3",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "5e20b83385a77593259c9f8beb2c43cd03b2ac14"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5e20b83385a77593259c9f8beb2c43cd03b2ac14",
- "reference": "5e20b83385a77593259c9f8beb2c43cd03b2ac14",
- "shasum": "",
- "mirrors": [
- {
- "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
- "preferred": true
- }
- ]
- },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+ "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.1-dev"
+ "dev-master": "2.2-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -2035,31 +2481,31 @@
"type": "tidelift"
}
],
- "time": "2020-06-06T08:49:21+00:00"
+ "time": "2020-09-07T11:33:47+00:00"
},
- {
- "name": "symfony/event-dispatcher",
- "version": "v4.3.4",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "429d0a1451d4c9c4abe1959b2986b88794b9b7d2"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/429d0a1451d4c9c4abe1959b2986b88794b9b7d2",
- "reference": "429d0a1451d4c9c4abe1959b2986b88794b9b7d2",
- "shasum": "",
- "mirrors": [
- {
- "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
- "preferred": true
- }
- ]
- },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v4.4.17",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "f029d6f21eac61ab23198e7aca40e7638e8c8924"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f029d6f21eac61ab23198e7aca40e7638e8c8924",
+ "reference": "f029d6f21eac61ab23198e7aca40e7638e8c8924",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
"require": {
- "php": "^7.1.3",
- "symfony/event-dispatcher-contracts": "^1.1"
+ "php": ">=7.1.3",
+ "symfony/event-dispatcher-contracts": "^1.1"
},
"conflict": {
"symfony/dependency-injection": "<3.4"
@@ -2069,24 +2515,20 @@
"symfony/event-dispatcher-implementation": "1.1"
},
"require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "~3.4|~4.0",
- "symfony/dependency-injection": "~3.4|~4.0",
- "symfony/expression-language": "~3.4|~4.0",
- "symfony/http-foundation": "^3.4|^4.0",
- "symfony/service-contracts": "^1.1",
- "symfony/stopwatch": "~3.4|~4.0"
+ "psr/log": "~1.0",
+ "symfony/config": "^3.4|^4.0|^5.0",
+ "symfony/dependency-injection": "^3.4|^4.0|^5.0",
+ "symfony/error-handler": "~3.4|~4.4",
+ "symfony/expression-language": "^3.4|^4.0|^5.0",
+ "symfony/http-foundation": "^3.4|^4.0|^5.0",
+ "symfony/service-contracts": "^1.1|^2",
+ "symfony/stopwatch": "^3.4|^4.0|^5.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.3-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\EventDispatcher\\": ""
@@ -2101,40 +2543,54 @@
],
"authors": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
],
- "description": "Symfony EventDispatcher Component",
- "homepage": "https://symfony.com",
- "time": "2019-08-26T08:55:16+00:00"
+ "description": "Symfony EventDispatcher Component",
+ "homepage": "https://symfony.com",
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
{
- "name": "symfony/event-dispatcher-contracts",
- "version": "v1.1.5",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/event-dispatcher-contracts.git",
- "reference": "c61766f4440ca687de1084a5c00b08e167a2575c"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c61766f4440ca687de1084a5c00b08e167a2575c",
- "reference": "c61766f4440ca687de1084a5c00b08e167a2575c",
- "shasum": "",
- "mirrors": [
- {
- "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
- "preferred": true
- }
- ]
- },
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-10-31T22:44:29+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v1.1.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+ "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
"require": {
- "php": "^7.1.3"
+ "php": ">=7.1.3"
},
"suggest": {
"psr/event-dispatcher": "",
@@ -2142,9 +2598,13 @@
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-master": "1.1-dev"
- }
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
},
"autoload": {
"psr-4": {
@@ -2165,18 +2625,32 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Generic abstractions related to dispatching event",
- "homepage": "https://symfony.com",
- "keywords": [
- "abstractions",
- "contracts",
- "decoupling",
- "interfaces",
- "interoperability",
- "standards"
- ],
- "time": "2019-06-20T06:46:26+00:00"
+ "description": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
},
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-07-06T13:19:58+00:00"
+ },
{
"name": "symfony/filesystem",
"version": "v5.1.3",
@@ -2247,26 +2721,26 @@
],
"time": "2020-05-30T20:35:19+00:00"
},
- {
- "name": "symfony/http-foundation",
- "version": "v5.1.6",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/http-foundation.git",
- "reference": "6cca6b2e4b69fc5bace160d14cf1ee5f71483db4"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6cca6b2e4b69fc5bace160d14cf1ee5f71483db4",
- "reference": "6cca6b2e4b69fc5bace160d14cf1ee5f71483db4",
- "shasum": "",
- "mirrors": [
- {
- "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
- "preferred": true
- }
- ]
- },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v5.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "e4576271ee99123aa59a40564c7b5405f0ebd1e6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e4576271ee99123aa59a40564c7b5405f0ebd1e6",
+ "reference": "e4576271ee99123aa59a40564c7b5405f0ebd1e6",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
"require": {
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1",
@@ -2283,11 +2757,6 @@
"symfony/mime": "To use the file extension guesser"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.1-dev"
- }
- },
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpFoundation\\": ""
@@ -2326,7 +2795,7 @@
"type": "tidelift"
}
],
- "time": "2020-09-13T05:01:27+00:00"
+ "time": "2020-11-27T06:13:25+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -2653,28 +3122,28 @@
],
"time": "2020-07-14T12:35:20+00:00"
},
- {
- "name": "symfony/polyfill-mbstring",
- "version": "v1.18.1",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
- "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
- "shasum": "",
- "mirrors": [
- {
- "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
- "preferred": true
- }
- ]
- },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.20.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "39d483bdf39be819deabf04ec872eb0b2410b531"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531",
+ "reference": "39d483bdf39be819deabf04ec872eb0b2410b531",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
"require": {
- "php": ">=5.3.3"
+ "php": ">=7.1"
},
"suggest": {
"ext-mbstring": "For best performance"
@@ -2682,7 +3151,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -2734,7 +3203,7 @@
"type": "tidelift"
}
],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2020-10-23T14:02:19+00:00"
},
{
"name": "symfony/polyfill-php70",
@@ -2980,33 +3449,33 @@
],
"time": "2020-07-14T12:35:20+00:00"
},
- {
- "name": "symfony/polyfill-php80",
- "version": "v1.18.1",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981",
- "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981",
- "shasum": "",
- "mirrors": [
- {
- "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
- "preferred": true
- }
- ]
- },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.20.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de",
+ "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
"require": {
- "php": ">=7.0.8"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -3055,37 +3524,125 @@
"url": "https://symfony.com/sponsor",
"type": "custom"
},
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
],
- "time": "2020-07-14T12:35:20+00:00"
+ "time": "2020-10-23T14:02:19+00:00"
+ },
+ {
+ "name": "symfony/psr-http-message-bridge",
+ "version": "v2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/psr-http-message-bridge.git",
+ "reference": "51a21cb3ba3927d4b4bf8f25cc55763351af5f2e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/51a21cb3ba3927d4b4bf8f25cc55763351af5f2e",
+ "reference": "51a21cb3ba3927d4b4bf8f25cc55763351af5f2e",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0",
+ "symfony/http-foundation": "^4.4 || ^5.0"
+ },
+ "require-dev": {
+ "nyholm/psr7": "^1.1",
+ "symfony/phpunit-bridge": "^4.4 || ^5.0"
+ },
+ "suggest": {
+ "nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
+ },
+ "type": "symfony-bridge",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Bridge\\PsrHttpMessage\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
},
{
- "name": "symfony/service-contracts",
- "version": "v2.1.3",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/service-contracts.git",
- "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/58c7475e5457c5492c26cc740cc0ad7464be9442",
- "reference": "58c7475e5457c5492c26cc740cc0ad7464be9442",
- "shasum": "",
- "mirrors": [
- {
- "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
- "preferred": true
- }
- ]
- },
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ }
+ ],
+ "description": "PSR HTTP message bridge",
+ "homepage": "http://symfony.com",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr-17",
+ "psr-7"
+ ],
+ "support": {
+ "issues": "https://github.com/symfony/psr-http-message-bridge/issues",
+ "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-09-29T08:17:46+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+ "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
"require": {
"php": ">=7.2.5",
"psr/container": "^1.0"
@@ -3096,7 +3653,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.1-dev"
+ "dev-master": "2.2-dev"
},
"thanks": {
"name": "symfony/contracts",
@@ -3137,26 +3694,105 @@
"url": "https://symfony.com/sponsor",
"type": "custom"
},
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
],
- "time": "2020-07-06T13:23:11+00:00"
+ "time": "2020-09-07T11:33:47+00:00"
+ },
+ {
+ "name": "symfony/var-exporter",
+ "version": "v5.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-exporter.git",
+ "reference": "fbc3507f23d263d75417e09a12d77c009f39676c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/fbc3507f23d263d75417e09a12d77c009f39676c",
+ "reference": "fbc3507f23d263d75417e09a12d77c009f39676c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "symfony/polyfill-php80": "^1.15"
+ },
+ "require-dev": {
+ "symfony/var-dumper": "^4.4.9|^5.0.9"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\VarExporter\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
},
{
- "name": "tencentcloud/tencentcloud-sdk-php",
- "version": "3.0.251",
- "source": {
- "type": "git",
- "url": "https://github.com/TencentCloud/tencentcloud-sdk-php.git",
- "reference": "a3b3054262e48776e8014d5e385a8932b0102f29"
- },
- "dist": {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A blend of var_export() + serialize() to turn any serializable data structure to plain PHP code",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "clone",
+ "construct",
+ "export",
+ "hydrate",
+ "instantiate",
+ "serialize"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-exporter/tree/v5.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-10-28T21:31:18+00:00"
+ },
+ {
+ "name": "tencentcloud/tencentcloud-sdk-php",
+ "version": "3.0.251",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/TencentCloud/tencentcloud-sdk-php.git",
+ "reference": "a3b3054262e48776e8014d5e385a8932b0102f29"
+ },
+ "dist": {
"type": "zip",
"url": "https://api.github.com/repos/TencentCloud/tencentcloud-sdk-php/zipball/a3b3054262e48776e8014d5e385a8932b0102f29",
"reference": "a3b3054262e48776e8014d5e385a8932b0102f29",
@@ -3630,27 +4266,27 @@
"reference": "65144f2b0fad32b182ccb062b1efc1b4edea5d44",
"shasum": "",
"mirrors": [
- {
- "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
- "preferred": true
- }
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
]
},
- "require": {
- "php": ">=5.3.0"
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "notification-url": "https://packagist.jp/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Phalcon Team",
+ "email": "team@phalconphp.com",
+ "homepage": "https://phalconphp.com/en/team"
},
- "type": "library",
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Phalcon Team",
- "email": "team@phalconphp.com",
- "homepage": "https://phalconphp.com/en/team"
- },
- {
+ {
"name": "Contributors",
"homepage": "https://github.com/phalcon/ide-stubs/graphs/contributors"
}
@@ -3741,5 +4377,5 @@
"ext-fileinfo": "*"
},
"platform-dev": [],
- "plugin-api-version": "1.1.0"
+ "plugin-api-version": "2.0.0"
}
diff --git a/db/migrations/20201205091213_create_connect_table.php b/db/migrations/20201205091213_create_connect_table.php
index 53041625..4e0419da 100644
--- a/db/migrations/20201205091213_create_connect_table.php
+++ b/db/migrations/20201205091213_create_connect_table.php
@@ -84,7 +84,7 @@ class CreateConnectTable extends Phinx\Migration\AbstractMigration
'after' => 'create_time',
])
->addIndex(['open_id', 'provider'], [
- 'name' => 'openid_provider',
+ 'name' => 'open_provider',
'unique' => false,
])
->create();
diff --git a/db/migrations/20201212102844_schema_202012121830.php b/db/migrations/20201212102844_schema_202012121830.php
new file mode 100644
index 00000000..90512438
--- /dev/null
+++ b/db/migrations/20201212102844_schema_202012121830.php
@@ -0,0 +1,99 @@
+table('kg_consult')
+ ->addColumn('replier_id', 'integer', [
+ 'null' => false,
+ 'default' => '0',
+ 'limit' => MysqlAdapter::INT_REGULAR,
+ 'comment' => '回复者编号',
+ 'after' => 'owner_id',
+ ])
+ ->save();
+ $this->table('kg_connect')
+ ->addColumn('union_id', 'string', [
+ 'null' => false,
+ 'default' => '',
+ 'limit' => 50,
+ 'collation' => 'utf8mb4_general_ci',
+ 'encoding' => 'utf8mb4',
+ 'comment' => 'union_id',
+ 'after' => 'user_id',
+ ])
+ ->addIndex(['union_id', 'provider'], [
+ 'name' => 'union_provider',
+ 'unique' => false,
+ ])
+ ->addIndex(['user_id'], [
+ 'name' => 'user_id',
+ 'unique' => false,
+ ])
+ ->save();
+ $this->table('kg_wechat_subscribe', [
+ 'id' => false,
+ 'primary_key' => ['id'],
+ 'engine' => 'InnoDB',
+ 'encoding' => 'utf8mb4',
+ 'collation' => 'utf8mb4_general_ci',
+ 'comment' => '',
+ 'row_format' => 'DYNAMIC',
+ ])
+ ->addColumn('id', 'integer', [
+ 'null' => false,
+ 'limit' => MysqlAdapter::INT_REGULAR,
+ 'identity' => 'enable',
+ 'comment' => '主键编号',
+ ])
+ ->addColumn('user_id', 'integer', [
+ 'null' => false,
+ 'default' => '0',
+ 'limit' => MysqlAdapter::INT_REGULAR,
+ 'comment' => '用户编号',
+ 'after' => 'id',
+ ])
+ ->addColumn('open_id', 'string', [
+ 'null' => false,
+ 'default' => '',
+ 'limit' => 50,
+ 'collation' => 'utf8mb4_general_ci',
+ 'encoding' => 'utf8mb4',
+ 'comment' => '开放ID',
+ 'after' => 'user_id',
+ ])
+ ->addColumn('deleted', 'integer', [
+ 'null' => false,
+ 'default' => '0',
+ 'limit' => MysqlAdapter::INT_REGULAR,
+ 'comment' => '删除标识',
+ 'after' => 'open_id',
+ ])
+ ->addColumn('create_time', 'integer', [
+ 'null' => false,
+ 'default' => '0',
+ 'limit' => MysqlAdapter::INT_REGULAR,
+ 'comment' => '创建时间',
+ 'after' => 'deleted',
+ ])
+ ->addColumn('update_time', 'integer', [
+ 'null' => false,
+ 'default' => '0',
+ 'limit' => MysqlAdapter::INT_REGULAR,
+ 'comment' => '更新时间',
+ 'after' => 'create_time',
+ ])
+ ->addIndex(['open_id'], [
+ 'name' => 'open_id',
+ 'unique' => false,
+ ])
+ ->addIndex(['user_id'], [
+ 'name' => 'user_id',
+ 'unique' => false,
+ ])
+ ->create();
+ }
+}
diff --git a/db/migrations/20201212112717_data_202012121830.php b/db/migrations/20201212112717_data_202012121830.php
new file mode 100644
index 00000000..88f41687
--- /dev/null
+++ b/db/migrations/20201212112717_data_202012121830.php
@@ -0,0 +1,81 @@
+ 'wechat.oa',
+ 'item_key' => 'enabled',
+ 'item_value' => '0',
+ ],
+ [
+ 'section' => 'wechat.oa',
+ 'item_key' => 'app_id',
+ 'item_value' => '',
+ ],
+ [
+ 'section' => 'wechat.oa',
+ 'item_key' => 'app_secret',
+ 'item_value' => '',
+ ],
+ [
+ 'section' => 'wechat.oa',
+ 'item_key' => 'app_token',
+ 'item_value' => '',
+ ],
+ [
+ 'section' => 'wechat.oa',
+ 'item_key' => 'aes_key',
+ 'item_value' => '',
+ ],
+ [
+ 'section' => 'wechat.oa',
+ 'item_key' => 'notify_url',
+ 'item_value' => '',
+ ],
+ [
+ 'section' => 'wechat.oa',
+ 'item_key' => 'notice_template',
+ 'item_value' => '{"account_login":"","order_finish":"","refund_finish":"","live_begin":"","consult_reply":""}',
+ ],
+ ];
+
+ $this->table('kg_setting')->insert($rows)->save();
+
+ $this->updateSmsTemplate();
+ }
+
+ public function down()
+ {
+ $this->getQueryBuilder()->delete('kg_setting')->where(['section' => 'wechat.oa'])->execute();
+ }
+
+ protected function updateSmsTemplate()
+ {
+ $table = 'kg_setting';
+
+ $where = ['section' => 'sms', 'item_key' => 'template'];
+
+ $setting = $this->getQueryBuilder()->select('*')->from($table)->where($where)->execute()->fetch('assoc');
+
+ $itemValue = json_decode($setting['item_value'], true);
+
+ $newItemValue = json_encode([
+ 'verify' => $itemValue['verify'],
+ 'order_finish' => $itemValue['order'],
+ 'refund_finish' => $itemValue['refund'],
+ 'live_begin' => $itemValue['live'],
+ 'consult_reply' => '',
+ ]);
+
+ $this->getQueryBuilder()->update($table)->where($where)->set('item_value', $newItemValue)->execute();
+ }
+
+}
\ No newline at end of file
diff --git a/public/static/home/css/common.css b/public/static/home/css/common.css
index bf8478c5..aa001338 100644
--- a/public/static/home/css/common.css
+++ b/public/static/home/css/common.css
@@ -1609,6 +1609,24 @@
margin: 0 10px;
}
+.my-subscribe {
+ margin-bottom: 15px;
+}
+
+.my-subscribe .qrcode {
+ margin: 30px auto;
+ width: 160px;
+ height: 160px;
+}
+
+.my-subscribe .tips {
+ text-align: center;
+}
+
+.my-subscribe .success {
+ color: green;
+}
+
.order-filter {
padding: 15px 20px;
}
diff --git a/public/static/home/js/user.console.subscribe.js b/public/static/home/js/user.console.subscribe.js
new file mode 100644
index 00000000..14dc9fd2
--- /dev/null
+++ b/public/static/home/js/user.console.subscribe.js
@@ -0,0 +1,29 @@
+layui.use(['jquery'], function () {
+
+ var $ = layui.jquery;
+ var subscribed = $('input[name=subscribed]').val();
+ var interval = null;
+
+ if (subscribed === '0') {
+ showQrCode();
+ interval = setInterval(function () {
+ queryStatus();
+ }, 5000);
+ }
+
+ function showQrCode() {
+ $.get('/wechat/oa/subscribe/qrcode', function (res) {
+ $('#sub-qrcode').html('
');
+ });
+ }
+
+ function queryStatus() {
+ $.get('/wechat/oa/subscribe/status', function (res) {
+ if (res.status === 1) {
+ clearInterval(interval);
+ $('#sub-tips').addClass('success').html('关注公众号成功');
+ }
+ });
+ }
+
+});
\ No newline at end of file
diff --git a/scheduler.php b/scheduler.php
index b5b27246..f363c2e7 100644
--- a/scheduler.php
+++ b/scheduler.php
@@ -13,8 +13,8 @@ $bin = '/usr/local/bin/php';
$scheduler->php($script, $bin, ['--task' => 'deliver', '--action' => 'main'])
->at('*/3 * * * *');
-$scheduler->php($script, $bin, ['--task' => 'live_notify', '--action' => 'main'])
- ->at('*/5 * * * *');
+$scheduler->php($script, $bin, ['--task' => 'notice', '--action' => 'main'])
+ ->at('*/3 * * * *');
$scheduler->php($script, $bin, ['--task' => 'sync_learning', '--action' => 'main'])
->at('*/7 * * * *');
@@ -49,9 +49,6 @@ $scheduler->php($script, $bin, ['--task' => 'unlock_user', '--action' => 'main']
$scheduler->php($script, $bin, ['--task' => 'revoke_vip', '--action' => 'main'])
->daily(3, 11);
-$scheduler->php($script, $bin, ['--task' => 'clean_token', '--action' => 'main'])
- ->daily(3, 17);
-
$scheduler->php($script, $bin, ['--task' => 'sitemap', '--action' => 'main'])
->daily(4, 3);