From 032a73096135d05a770936fa2f725bc96890917d Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Tue, 22 Sep 2020 19:57:44 +0800 Subject: [PATCH 01/44] =?UTF-8?q?=E5=90=88=E5=B9=B6=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Controllers/Controller.php | 11 +++++++++++ app/Http/Admin/Services/Setting.php | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/app/Http/Admin/Controllers/Controller.php b/app/Http/Admin/Controllers/Controller.php index b38fabe5..ed8264ee 100644 --- a/app/Http/Admin/Controllers/Controller.php +++ b/app/Http/Admin/Controllers/Controller.php @@ -21,6 +21,17 @@ class Controller extends \Phalcon\Mvc\Controller public function beforeExecuteRoute(Dispatcher $dispatcher) { + /** + * demo分支拒绝数据提交 + */ + if ($this->isNotSafeRequest()) { + $dispatcher->forward([ + 'controller' => 'public', + 'action' => 'forbidden', + ]); + return false; + } + if ($this->isNotSafeRequest()) { $this->checkHttpReferer(); $this->checkCsrfToken(); diff --git a/app/Http/Admin/Services/Setting.php b/app/Http/Admin/Services/Setting.php index d8c97936..2a19326b 100644 --- a/app/Http/Admin/Services/Setting.php +++ b/app/Http/Admin/Services/Setting.php @@ -57,8 +57,16 @@ class Setting extends Service $result = []; + /** + * demo分支过滤敏感数据 + */ if ($items->count() > 0) { foreach ($items as $item) { + $case1 = preg_match('/(id|auth|key|secret|password|pwd)$/', $item->item_key); + $case2 = $this->dispatcher->getControllerName() == 'setting'; + if ($case1 && $case2) { + $item->item_value = '***'; + } $result[$item->item_key] = $item->item_value; } } From 6679214808f741cd373ed5ee80e37f2666c3a8e4 Mon Sep 17 00:00:00 2001 From: koogua Date: Sat, 26 Sep 2020 18:59:02 +0800 Subject: [PATCH 02/44] =?UTF-8?q?!1=20=E7=B2=BE=E7=AE=80=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=20*=20=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=20*=20=E9=87=8D=E6=96=B0=E6=8F=90=E4=BA=A4LICENSE=20*=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=89=8D=E5=8F=B0=E6=94=AF=E4=BB=98=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8B=A5=E5=B9=B2=E5=91=BD=E4=BB=A4=E8=A1=8C?= =?UTF-8?q?=E5=BF=AB=E6=8D=B7=E6=96=B9=E6=B3=95=20*=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=96=87=E4=BB=B6=20LICENSE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +-- app/Console/Tasks/SyncCourseIndexTask.php | 2 -- app/Console/Tasks/SyncGroupIndexTask.php | 2 -- app/Console/Tasks/SyncLearningTask.php | 2 -- app/Console/Tasks/SyncUserIndexTask.php | 2 -- app/Console/Tasks/UpgradeTask.php | 4 +-- app/Http/Admin/Services/WxpayTest.php | 13 +++++-- app/Http/Admin/Views/course/add.volt | 2 +- app/Http/Admin/Views/course/list.volt | 2 +- app/Http/Admin/Views/course/search.volt | 2 +- app/Http/Admin/Views/setting/pay_wxpay.volt | 2 +- app/Http/Home/Controllers/Controller.php | 18 +++++----- app/Http/Home/Controllers/OrderController.php | 5 ++- app/Http/Home/Services/Trade.php | 13 ++++++- app/Http/Home/Views/order/info.volt | 4 +-- app/Http/Home/Views/order/pay.volt | 9 ++--- app/Http/Home/Views/partials/header.volt | 2 +- .../Home/Views/teacher/console/consults.volt | 2 +- .../Home/Views/teacher/console/courses.volt | 2 +- .../Home/Views/teacher/console/lives.volt | 2 +- app/Http/Home/Views/user/console/account.volt | 2 +- .../Home/Views/user/console/consults.volt | 2 +- app/Http/Home/Views/user/console/courses.volt | 2 +- .../Home/Views/user/console/favorites.volt | 2 +- app/Http/Home/Views/user/console/friends.volt | 2 +- app/Http/Home/Views/user/console/groups.volt | 2 +- app/Http/Home/Views/user/console/orders.volt | 2 +- app/Http/Home/Views/user/console/profile.volt | 2 +- app/Http/Home/Views/user/console/refunds.volt | 2 +- app/Http/Home/Views/user/console/reviews.volt | 2 +- app/Models/Audit.php | 5 +++ app/Models/Course.php | 2 +- app/Providers/Cookie.php | 7 ++++ app/Providers/Escaper.php | 4 +-- app/Services/Pay/Alipay.php | 1 + app/Validators/Category.php | 28 ++++++++------- app/Validators/Chapter.php | 34 ++++++++++++------- app/Validators/Course.php | 28 ++++++++------- app/Validators/Help.php | 26 +++++++------- app/Validators/ImGroup.php | 28 ++++++++------- app/Validators/Package.php | 28 ++++++++------- app/Validators/Page.php | 28 ++++++++------- app/Validators/Topic.php | 28 ++++++++------- app/Validators/User.php | 28 ++++++++------- public/static/home/css/common.css | 23 ++----------- public/static/home/js/order.pay.js | 5 ++- 46 files changed, 237 insertions(+), 180 deletions(-) diff --git a/README.md b/README.md index 22a6034c..abfadbd8 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ #### 使用协议 -虽然尝试了解过开源协议,但是理解的模棱两可,干脆用自己的协议吧,算不算开源我也不很清楚。 +虽然尝试了解过开源协议,但是理解的模棱两可,干脆用自己的协议吧。 1. 本系统属于强业务类型,非通用类库框架,不适合再次衍生发布。 2. 在保留我们版权标识的前提下,用户可以修改以满足自己的需求,可以用于商业用途。 @@ -74,7 +74,7 @@ #### 有商业服务吗? -生存是一个问题,生存才能发展,我们为用户提供的服务包括: +生存才能发展,我们目前提供的服务包括: - 系统安装 - 系统定制 diff --git a/app/Console/Tasks/SyncCourseIndexTask.php b/app/Console/Tasks/SyncCourseIndexTask.php index 1578f036..ae1f7076 100644 --- a/app/Console/Tasks/SyncCourseIndexTask.php +++ b/app/Console/Tasks/SyncCourseIndexTask.php @@ -12,8 +12,6 @@ class SyncCourseIndexTask extends Task public function mainAction() { - $cache = $this->getCache(); - $redis = $this->getRedis(); $key = $this->getSyncKey(); diff --git a/app/Console/Tasks/SyncGroupIndexTask.php b/app/Console/Tasks/SyncGroupIndexTask.php index d48223de..a233c1a5 100644 --- a/app/Console/Tasks/SyncGroupIndexTask.php +++ b/app/Console/Tasks/SyncGroupIndexTask.php @@ -12,8 +12,6 @@ class SyncGroupIndexTask extends Task public function mainAction() { - $cache = $this->getCache(); - $redis = $this->getRedis(); $key = $this->getSyncKey(); diff --git a/app/Console/Tasks/SyncLearningTask.php b/app/Console/Tasks/SyncLearningTask.php index 8adbc36e..af1f41aa 100644 --- a/app/Console/Tasks/SyncLearningTask.php +++ b/app/Console/Tasks/SyncLearningTask.php @@ -16,8 +16,6 @@ class SyncLearningTask extends Task public function mainAction() { - $cache = $this->getCache(); - $redis = $this->getRedis(); $sync = new LearningSync(); diff --git a/app/Console/Tasks/SyncUserIndexTask.php b/app/Console/Tasks/SyncUserIndexTask.php index 7e55f3da..f792a465 100644 --- a/app/Console/Tasks/SyncUserIndexTask.php +++ b/app/Console/Tasks/SyncUserIndexTask.php @@ -12,8 +12,6 @@ class SyncUserIndexTask extends Task public function mainAction() { - $cache = $this->getCache(); - $redis = $this->getRedis(); $key = $this->getSyncKey(); diff --git a/app/Console/Tasks/UpgradeTask.php b/app/Console/Tasks/UpgradeTask.php index a8600332..13bb53fb 100644 --- a/app/Console/Tasks/UpgradeTask.php +++ b/app/Console/Tasks/UpgradeTask.php @@ -43,7 +43,7 @@ class UpgradeTask extends Task public function resetAnnotationAction() { $config = $this->getConfig(); - $cache = $this->getCache(); + $redis = $this->getRedis(); $dbIndex = $config->path('annotation.db'); @@ -74,7 +74,7 @@ class UpgradeTask extends Task public function resetMetadataAction() { $config = $this->getConfig(); - $cache = $this->getCache(); + $redis = $this->getRedis(); $dbIndex = $config->path('metadata.db'); diff --git a/app/Http/Admin/Services/WxpayTest.php b/app/Http/Admin/Services/WxpayTest.php index b6094d6e..053d3e1f 100644 --- a/app/Http/Admin/Services/WxpayTest.php +++ b/app/Http/Admin/Services/WxpayTest.php @@ -14,9 +14,18 @@ class WxpayTest extends PayTest { $wxpayService = new WxpayService(); - $qrcode = $wxpayService->scan($trade); + $code = $wxpayService->scan($trade); - return $qrcode ?: false; + $codeUrl = null; + + if ($code) { + $codeUrl = $this->url->get( + ['for' => 'home.qrcode'], + ['text' => urlencode($code)] + ); + } + + return $codeUrl ?: false; } public function status($tradeNo) diff --git a/app/Http/Admin/Views/course/add.volt b/app/Http/Admin/Views/course/add.volt index 66a731bf..a90cd2e7 100644 --- a/app/Http/Admin/Views/course/add.volt +++ b/app/Http/Admin/Views/course/add.volt @@ -11,7 +11,7 @@
- +
diff --git a/app/Http/Admin/Views/course/list.volt b/app/Http/Admin/Views/course/list.volt index 9580510d..450a3641 100644 --- a/app/Http/Admin/Views/course/list.volt +++ b/app/Http/Admin/Views/course/list.volt @@ -8,7 +8,7 @@ {% elseif value == 2 %} 直播 {% elseif value == 3 %} - 图文 + 专栏 {% endif %} {%- endmacro %} diff --git a/app/Http/Admin/Views/course/search.volt b/app/Http/Admin/Views/course/search.volt index 06be131d..9a8d0935 100644 --- a/app/Http/Admin/Views/course/search.volt +++ b/app/Http/Admin/Views/course/search.volt @@ -35,7 +35,7 @@
- +
diff --git a/app/Http/Admin/Views/setting/pay_wxpay.volt b/app/Http/Admin/Views/setting/pay_wxpay.volt index 14e7bf66..beb2c4f9 100644 --- a/app/Http/Admin/Views/setting/pay_wxpay.volt +++ b/app/Http/Admin/Views/setting/pay_wxpay.volt @@ -19,7 +19,7 @@
- +
diff --git a/app/Http/Home/Controllers/Controller.php b/app/Http/Home/Controllers/Controller.php index 78b8e630..30819779 100644 --- a/app/Http/Home/Controllers/Controller.php +++ b/app/Http/Home/Controllers/Controller.php @@ -3,7 +3,6 @@ namespace App\Http\Home\Controllers; use App\Caches\NavTreeList as NavCache; -use App\Caches\Setting as SettingCache; use App\Library\AppInfo as AppInfo; use App\Library\Seo as Seo; use App\Models\User as UserModel; @@ -116,9 +115,7 @@ class Controller extends \Phalcon\Mvc\Controller protected function getSiteInfo() { - $appService = new AppService(); - - return $appService->getSettings('site'); + return $this->getSettings('site'); } protected function getAppInfo() @@ -128,8 +125,6 @@ class Controller extends \Phalcon\Mvc\Controller protected function getImInfo() { - $cache = new SettingCache(); - $websocket = $this->getConfig()->get('websocket'); /** @@ -142,8 +137,8 @@ class Controller extends \Phalcon\Mvc\Controller } return [ - 'main' => $cache->get('im.main'), - 'cs' => $cache->get('im.cs'), + 'main' => $this->getSettings('im.main'), + 'cs' => $this->getSettings('im.cs'), 'ws' => $websocket, ]; } @@ -155,4 +150,11 @@ class Controller extends \Phalcon\Mvc\Controller return $appService->getConfig(); } + protected function getSettings($section) + { + $appService = new AppService(); + + return $appService->getSettings($section); + } + } diff --git a/app/Http/Home/Controllers/OrderController.php b/app/Http/Home/Controllers/OrderController.php index 9f4550cb..591c8187 100644 --- a/app/Http/Home/Controllers/OrderController.php +++ b/app/Http/Home/Controllers/OrderController.php @@ -67,7 +67,10 @@ class OrderController extends Controller $order = $service->handle(); - $location = $this->url->get(['for' => 'home.order.pay'], ['sn' => $order->sn]); + $location = $this->url->get( + ['for' => 'home.order.pay'], + ['sn' => $order->sn] + ); return $this->jsonSuccess(['location' => $location]); } diff --git a/app/Http/Home/Services/Trade.php b/app/Http/Home/Services/Trade.php index 50e176dd..629fc986 100644 --- a/app/Http/Home/Services/Trade.php +++ b/app/Http/Home/Services/Trade.php @@ -72,9 +72,20 @@ class Trade extends Service protected function getWxpayQrCode(TradeModel $trade) { + $qrCode = null; + $service = new WxpayService(); - return $service->scan($trade); + $text = $service->scan($trade); + + if ($text) { + $qrCode = $this->url->get( + ['for' => 'home.qrcode'], + ['text' => urlencode($text)] + ); + } + + return $qrCode; } } diff --git a/app/Http/Home/Views/order/info.volt b/app/Http/Home/Views/order/info.volt index 33b78cd5..c4472b16 100644 --- a/app/Http/Home/Views/order/info.volt +++ b/app/Http/Home/Views/order/info.volt @@ -21,10 +21,10 @@
- {% if order.status == 'pending' %} + {% if order.status == 1 %} 立即支付 {% endif %} - {% if (order.item_type in ['course','package']) and (order.status == 'finished') %} + {% if (order.item_type in [1,2]) and (order.status == 3) %} 申请退款 {% endif %}
diff --git a/app/Http/Home/Views/order/pay.volt b/app/Http/Home/Views/order/pay.volt index 5b0cf144..16bca937 100644 --- a/app/Http/Home/Views/order/pay.volt +++ b/app/Http/Home/Views/order/pay.volt @@ -2,6 +2,9 @@ {% block content %} + {% set create_url = url({'for':'home.trade.create'}) %} + {% set status_url = url({'for':'home.trade.status'}) %} +
- {% set create_url = url({'for':'home.trade.create'}) %} - {% set status_url = url({'for':'home.trade.status'}) %} - - + {{ image('home/img/alipay.png') }} + {{ image('home/img/wxpay.png') }}
",success:function(e,i){s.bigimg=e.find(".layui-layer-phimg"),s.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),s.event(e),t.tab&&t.tab(u[d],e),"function"==typeof y&&y(e)},end:function(){s.end=!0,i(document).off("keyup",s.keyup)}},t))},function(){r.close(s.loadi),r.msg("当前图片地址异常
是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){u.length>1&&s.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),l.html=i("html"),r.open=function(e){var t=new s(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.$),e.layer=r,t("layer",r)})):"function"==typeof define&&define.amd?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='
  • "+(i.title||"unnaming")+"
  • ";return s[0]?s.before(r):n.append(r),o.append('
    '+(i.content||"")+"
    "),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a('');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(''),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(''),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+"")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+"")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+""),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)});layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,n=layui.hint(),o=layui.device(),a={config:{},set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,r,e,t)}},l=function(){var e=this;return{upload:function(t){e.upload.call(e,t)},reload:function(t){e.reload.call(e,t)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var i=this;i.config=t.extend({},i.config,a.config,e),i.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",acceptMime:"",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var i=this,e=i.config;e.elem=t(e.elem),e.bindAction=t(e.bindAction),i.file(),i.events()},p.prototype.file=function(){var e=this,i=e.config,n=e.elemFile=t(['"].join("")),a=i.elem.next();(a.hasClass(u)||a.hasClass(c))&&a.remove(),o.ie&&o.ie<10&&i.elem.wrap('
    '),e.isFile()?(e.elemFile=i.elem,i.field=i.elem[0].name):i.elem.after(n),o.ie&&o.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,i=e.config,n=t(''),o=t(['
    ',"
    "].join(""));t("#"+f)[0]||t("body").append(n),i.elem.next().hasClass(c)||(e.elemFile.wrap(o),i.elem.next("."+c).append(function(){var e=[];return layui.each(i.data,function(t,i){i="function"==typeof i?i():i,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return i.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var t=this;window.FileReader&&layui.each(t.chooseFiles,function(t,i){var n=new FileReader;n.readAsDataURL(i),n.onload=function(){e&&e(t,i,this.result)}})},p.prototype.upload=function(e,i){var n,a=this,l=a.config,r=a.elemFile[0],u=function(){var i=0,n=0,o=e||a.files||a.chooseFiles||r.files,u=function(){l.multiple&&i+n===a.fileLength&&"function"==typeof l.allDone&&l.allDone({total:a.fileLength,successful:i,aborted:n})};layui.each(o,function(e,o){var r=new FormData;r.append(l.field,o),layui.each(l.data,function(e,t){t="function"==typeof t?t():t,r.append(e,t)});var c={url:l.url,type:"post",data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(t){i++,d(e,t),u()},error:function(){n++,a.msg("请求上传接口出现异常"),m(e),u()}};"function"==typeof l.progress&&(c.xhr=function(){var e=t.ajaxSettings.xhr();return e.upload.addEventListener("progress",function(e){if(e.lengthComputable){var t=Math.floor(e.loaded/e.total*100);l.progress(t,l.item[0],e)}}),e}),t.ajax(c)})},c=function(){var e=t("#"+f);a.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var t,i=e.contents().find("body");try{t=i.text()}catch(n){a.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}t&&(clearInterval(p.timer),i.html(""),d(0,t))},30)},d=function(e,t){if(a.elemFile.next("."+s).remove(),r.value="","object"!=typeof t)try{t=JSON.parse(t)}catch(i){return t={},a.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(t,e||0,function(e){a.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){a.upload(e)})},h=l.exts,v=function(){var t=[];return layui.each(e||a.chooseFiles,function(e,i){t.push(i.name)}),t}(),g={preview:function(e){a.preview(e)},upload:function(e,t){var i={};i[e]=t,a.upload(i)},pushFile:function(){return a.files=a.files||{},layui.each(a.chooseFiles,function(e,t){a.files[e]=t}),a.files},resetFile:function(e,t,i){var n=new File([t],i);a.files=a.files||{},a.files[e]=n}},y=function(){if("choose"!==i&&!l.auto||(l.choose&&l.choose(g),"choose"!==i))return l.before&&l.before(g),o.ie?o.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return a.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return a.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return a.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,t){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(t))||(n=!0)}),n)return a.msg("选择的图片中包含不支持的格式"),r.value=""}if(a.fileLength=function(){var t=0,i=e||a.files||a.chooseFiles||r.files;return layui.each(i,function(){t++}),t}(),l.number&&a.fileLength>l.number)return a.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(o.ie&&o.ie<10)){var F;if(layui.each(a.chooseFiles,function(e,t){if(t.size>1024*l.size){var i=l.size/1024;i=i>=1?i.toFixed(2)+"MB":l.size+"KB",r.value="",F=i}}),F)return a.msg("文件不能超过"+F)}y()}},p.prototype.reload=function(e){e=e||{},delete e.elem,delete e.bindAction;var i=this,e=i.config=t.extend({},i.config,a.config,e),n=e.elem.next();n.attr({name:e.name,accept:e.acceptMime,multiple:e.multiple})},p.prototype.events=function(){var e=this,i=e.config,a=function(t){e.chooseFiles={},layui.each(t,function(t,i){var n=(new Date).getTime();e.chooseFiles[n+"-"+t]=i})},l=function(t,n){var o=e.elemFile,a=t.length>1?t.length+"个文件":(t[0]||{}).name||o[0].value.match(/[^\/\\]+\..+/g)||[]||"";o.next().hasClass(s)&&o.next().remove(),e.upload(null,"choose"),e.isFile()||i.choose||o.after(''+a+"")};i.elem.off("upload.start").on("upload.start",function(){var o=t(this),a=o.attr("lay-data");if(a)try{a=new Function("return "+a)(),e.config=t.extend({},i,a)}catch(l){n.error("Resource element property lay-data configuration item has a syntax error: "+a)}e.config.item=o,e.elemFile[0].click()}),o.ie&&o.ie<10||i.elem.off("upload.over").on("upload.over",function(){var e=t(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=t(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,o){var r=t(this),u=o.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),a(u),i.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var t=this.files||[];a(t),i.auto?e.upload():l(t)}),i.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),i.elem.data("haveEvents")||(e.elemFile.on("change",function(){t(this).trigger("upload.change")}),i.elem.on("click",function(){e.isFile()||t(this).trigger("upload.start")}),i.drag&&i.elem.on("dragover",function(e){e.preventDefault(),t(this).trigger("upload.over")}).on("dragleave",function(e){t(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),t(this).trigger("upload.drop",e)}),i.bindAction.on("click",function(){t(this).trigger("upload.action")}),i.elem.data("haveEvents",!0))},a.render=function(e){var t=new p(e);return l.call(t)},e(r,a)});layui.define("jquery",function(e){"use strict";var i=layui.jquery,t={config:{},index:layui.slider?layui.slider.index+1e4:0,set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,n,e,i)}},a=function(){var e=this,i=e.config;return{setValue:function(i,t){return e.slide("set",i,t||0)},config:i}},n="slider",l="layui-disabled",s="layui-slider",r="layui-slider-bar",o="layui-slider-wrap",u="layui-slider-wrap-btn",d="layui-slider-tips",v="layui-slider-input",c="layui-slider-input-txt",m="layui-slider-input-btn",p="layui-slider-hover",f=function(e){var a=this;a.index=++t.index,a.config=i.extend({},a.config,t.config,e),a.render()};f.prototype.config={type:"default",min:0,max:100,value:0,step:1,showstep:!1,tips:!0,input:!1,range:!1,height:200,disabled:!1,theme:"#009688"},f.prototype.render=function(){var e=this,t=e.config;if(t.step<1&&(t.step=1),t.maxt.min?a:t.min,t.value[1]=n>t.min?n:t.min,t.value[0]=t.value[0]>t.max?t.max:t.value[0],t.value[1]=t.value[1]>t.max?t.max:t.value[1];var r=Math.floor((t.value[0]-t.min)/(t.max-t.min)*100),v=Math.floor((t.value[1]-t.min)/(t.max-t.min)*100),m=v-r+"%";r+="%",v+="%"}else{"object"==typeof t.value&&(t.value=Math.min.apply(null,t.value)),t.valuet.max&&(t.value=t.max);var m=Math.floor((t.value-t.min)/(t.max-t.min)*100)+"%"}var p=t.disabled?"#c2c2c2":t.theme,f='
    '+(t.tips?'
    ':"")+'
    '+(t.range?'
    ':"")+"
    ",h=i(t.elem),y=h.next("."+s);if(y[0]&&y.remove(),e.elemTemp=i(f),t.range?(e.elemTemp.find("."+o).eq(0).data("value",t.value[0]),e.elemTemp.find("."+o).eq(1).data("value",t.value[1])):e.elemTemp.find("."+o).data("value",t.value),h.html(e.elemTemp),"vertical"===t.type&&e.elemTemp.height(t.height+"px"),t.showstep){for(var g=(t.max-t.min)/t.step,b="",x=1;x
    ')}e.elemTemp.append(b)}if(t.input&&!t.range){var w=i('
    ');h.css("position","relative"),h.append(w),h.find("."+c).children("input").val(t.value),"vertical"===t.type?w.css({left:0,top:-48}):e.elemTemp.css("margin-right",w.outerWidth()+15)}t.disabled?(e.elemTemp.addClass(l),e.elemTemp.find("."+u).addClass(l)):e.slide(),e.elemTemp.find("."+u).on("mouseover",function(){var a="vertical"===t.type?t.height:e.elemTemp[0].offsetWidth,n=e.elemTemp.find("."+o),l="vertical"===t.type?a-i(this).parent()[0].offsetTop-n.height():i(this).parent()[0].offsetLeft,s=l/a*100,r=i(this).parent().data("value"),u=t.setTips?t.setTips(r):r;e.elemTemp.find("."+d).html(u),"vertical"===t.type?e.elemTemp.find("."+d).css({bottom:s+"%","margin-bottom":"20px",display:"inline-block"}):e.elemTemp.find("."+d).css({left:s+"%",display:"inline-block"})}).on("mouseout",function(){e.elemTemp.find("."+d).css("display","none")})},f.prototype.slide=function(e,t,a){var n=this,l=n.config,s=n.elemTemp,f=function(){return"vertical"===l.type?l.height:s[0].offsetWidth},h=s.find("."+o),y=s.next("."+v),g=y.children("."+c).children("input").val(),b=100/((l.max-l.min)/Math.ceil(l.step)),x=function(e,i){e=Math.ceil(e)*b>100?Math.ceil(e)*b:Math.round(e)*b,e=e>100?100:e,h.eq(i).css("vertical"===l.type?"bottom":"left",e+"%");var t=T(h[0].offsetLeft),a=l.range?T(h[1].offsetLeft):0;"vertical"===l.type?(s.find("."+d).css({bottom:e+"%","margin-bottom":"20px"}),t=T(f()-h[0].offsetTop-h.height()),a=l.range?T(f()-h[1].offsetTop-h.height()):0):s.find("."+d).css("left",e+"%"),t=t>100?100:t,a=a>100?100:a;var n=Math.min(t,a),o=Math.abs(t-a);"vertical"===l.type?s.find("."+r).css({height:o+"%",bottom:n+"%"}):s.find("."+r).css({width:o+"%",left:n+"%"});var u=l.min+Math.round((l.max-l.min)*e/100);if(g=u,y.children("."+c).children("input").val(g),h.eq(i).data("value",u),u=l.setTips?l.setTips(u):u,s.find("."+d).html(u),l.range){var v=[h.eq(0).data("value"),h.eq(1).data("value")];v[0]>v[1]&&v.reverse()}l.change&&l.change(l.range?v:u)},T=function(e){var i=e/f()*100/b,t=Math.round(i)*b;return e==f()&&(t=Math.ceil(i)*b),t},w=i(['
    f()&&(r=f());var o=r/f()*100/b;x(o,e),t.addClass(p),s.find("."+d).show(),i.preventDefault()},o=function(){t.removeClass(p),s.find("."+d).hide()};M(r,o)})}),s.on("click",function(e){var t=i("."+u);if(!t.is(event.target)&&0===t.has(event.target).length&&t.length){var a,n="vertical"===l.type?f()-e.clientY+i(this).offset().top:e.clientX-i(this).offset().left;n<0&&(n=0),n>f()&&(n=f());var s=n/f()*100/b;a=l.range?"vertical"===l.type?Math.abs(n-parseInt(i(h[0]).css("bottom")))>Math.abs(n-parseInt(i(h[1]).css("bottom")))?1:0:Math.abs(n-h[0].offsetLeft)>Math.abs(n-h[1].offsetLeft)?1:0:0,x(s,a),e.preventDefault()}}),y.hover(function(){var e=i(this);e.children("."+m).fadeIn("fast")},function(){var e=i(this);e.children("."+m).fadeOut("fast")}),y.children("."+m).children("i").each(function(e){i(this).on("click",function(){g=1==e?g-l.stepl.max?l.max:Number(g)+l.step;var i=(g-l.min)/(l.max-l.min)*100/b;x(i,0)})});var q=function(){var e=this.value;e=isNaN(e)?0:e,e=el.max?l.max:e,this.value=e;var i=(e-l.min)/(l.max-l.min)*100/b;x(i,0)};y.children("."+c).children("input").on("keydown",function(e){13===e.keyCode&&(e.preventDefault(),q.call(this))}).on("change",q)},f.prototype.events=function(){var e=this;e.config},t.render=function(e){var i=new f(e);return a.call(i)},e(n,t)});layui.define("jquery",function(e){"use strict";var i=layui.jquery,o={config:{},index:layui.colorpicker?layui.colorpicker.index+1e4:0,set:function(e){var o=this;return o.config=i.extend({},o.config,e),o},on:function(e,i){return layui.onevent.call(this,"colorpicker",e,i)}},r=function(){var e=this,i=e.config;return{config:i}},t="colorpicker",n="layui-show",l="layui-colorpicker",c=".layui-colorpicker-main",a="layui-icon-down",s="layui-icon-close",f="layui-colorpicker-trigger-span",d="layui-colorpicker-trigger-i",u="layui-colorpicker-side",p="layui-colorpicker-side-slider",g="layui-colorpicker-basis",v="layui-colorpicker-alpha-bgcolor",h="layui-colorpicker-alpha-slider",m="layui-colorpicker-basis-cursor",b="layui-colorpicker-main-input",k=function(e){var i={h:0,s:0,b:0},o=Math.min(e.r,e.g,e.b),r=Math.max(e.r,e.g,e.b),t=r-o;return i.b=r,i.s=0!=r?255*t/r:0,0!=i.s?e.r==r?i.h=(e.g-e.b)/t:e.g==r?i.h=2+(e.b-e.r)/t:i.h=4+(e.r-e.g)/t:i.h=-1,r==o&&(i.h=0),i.h*=60,i.h<0&&(i.h+=360),i.s*=100/255,i.b*=100/255,i},y=function(e){var e=e.indexOf("#")>-1?e.substring(1):e;if(3==e.length){var i=e.split("");e=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]}e=parseInt(e,16);var o={r:e>>16,g:(65280&e)>>8,b:255&e};return k(o)},x=function(e){var i={},o=e.h,r=255*e.s/100,t=255*e.b/100;if(0==r)i.r=i.g=i.b=t;else{var n=t,l=(255-r)*t/255,c=(n-l)*(o%60)/60;360==o&&(o=0),o<60?(i.r=n,i.b=l,i.g=l+c):o<120?(i.g=n,i.b=l,i.r=n-c):o<180?(i.g=n,i.r=l,i.b=l+c):o<240?(i.b=n,i.r=l,i.g=n-c):o<300?(i.b=n,i.g=l,i.r=l+c):o<360?(i.r=n,i.g=l,i.b=n-c):(i.r=0,i.g=0,i.b=0)}return{r:Math.round(i.r),g:Math.round(i.g),b:Math.round(i.b)}},C=function(e){var o=x(e),r=[o.r.toString(16),o.g.toString(16),o.b.toString(16)];return i.each(r,function(e,i){1==i.length&&(r[e]="0"+i)}),r.join("")},P=function(e){var i=/[0-9]{1,3}/g,o=e.match(i)||[];return{r:o[0],g:o[1],b:o[2]}},B=i(window),w=i(document),D=function(e){var r=this;r.index=++o.index,r.config=i.extend({},r.config,o.config,e),r.render()};D.prototype.config={color:"",size:null,alpha:!1,format:"hex",predefine:!1,colors:["#009688","#5FB878","#1E9FFF","#FF5722","#FFB800","#01AAED","#999","#c00","#ff8c00","#ffd700","#90ee90","#00ced1","#1e90ff","#c71585","rgb(0, 186, 189)","rgb(255, 120, 0)","rgb(250, 212, 0)","#393D49","rgba(0,0,0,.5)","rgba(255, 69, 0, 0.68)","rgba(144, 240, 144, 0.5)","rgba(31, 147, 255, 0.73)"]},D.prototype.render=function(){var e=this,o=e.config,r=i(['
    ',"",'3&&(o.alpha&&"rgb"==o.format||(e="#"+C(k(P(o.color))))),"background: "+e):e}()+'">','',"","","
    "].join("")),t=i(o.elem);o.size&&r.addClass("layui-colorpicker-"+o.size),t.addClass("layui-inline").html(e.elemColorBox=r),e.color=e.elemColorBox.find("."+f)[0].style.background,e.events()},D.prototype.renderPicker=function(){var e=this,o=e.config,r=e.elemColorBox[0],t=e.elemPicker=i(['
    ','
    ','
    ','
    ','
    ','
    ',"
    ",'
    ','
    ',"
    ","
    ",'
    ','
    ','
    ',"
    ","
    ",function(){if(o.predefine){var e=['
    '];return layui.each(o.colors,function(i,o){e.push(['
    ','
    ',"
    "].join(""))}),e.push("
    "),e.join("")}return""}(),'
    ','
    ','',"
    ",'
    ','','',"","
    "].join(""));e.elemColorBox.find("."+f)[0];i(c)[0]&&i(c).data("index")==e.index?e.removePicker(D.thisElemInd):(e.removePicker(D.thisElemInd),i("body").append(t)),D.thisElemInd=e.index,D.thisColor=r.style.background,e.position(),e.pickerEvents()},D.prototype.removePicker=function(e){var o=this;o.config;return i("#layui-colorpicker"+(e||o.index)).remove(),o},D.prototype.position=function(){var e=this,i=e.config,o=e.bindElem||e.elemColorBox[0],r=e.elemPicker[0],t=o.getBoundingClientRect(),n=r.offsetWidth,l=r.offsetHeight,c=function(e){return e=e?"scrollLeft":"scrollTop",document.body[e]|document.documentElement[e]},a=function(e){return document.documentElement[e?"clientWidth":"clientHeight"]},s=5,f=t.left,d=t.bottom;f-=(n-o.offsetWidth)/2,d+=s,f+n+s>a("width")?f=a("width")-n-s:fa()&&(d=t.top>l?t.top-l:a()-l,d-=2*s),i.position&&(r.style.position=i.position),r.style.left=f+("fixed"===i.position?0:c(1))+"px",r.style.top=d+("fixed"===i.position?0:c())+"px"},D.prototype.val=function(){var e=this,i=(e.config,e.elemColorBox.find("."+f)),o=e.elemPicker.find("."+b),r=i[0],t=r.style.backgroundColor;if(t){var n=k(P(t)),l=i.attr("lay-type");if(e.select(n.h,n.s,n.b),"torgb"===l&&o.find("input").val(t),"rgba"===l){var c=P(t);if(3==(t.match(/[0-9]{1,3}/g)||[]).length)o.find("input").val("rgba("+c.r+", "+c.g+", "+c.b+", 1)"),e.elemPicker.find("."+h).css("left",280);else{o.find("input").val(t);var a=280*t.slice(t.lastIndexOf(",")+1,t.length-1);e.elemPicker.find("."+h).css("left",a)}e.elemPicker.find("."+v)[0].style.background="linear-gradient(to right, rgba("+c.r+", "+c.g+", "+c.b+", 0), rgb("+c.r+", "+c.g+", "+c.b+"))"}}else e.select(0,100,100),o.find("input").val(""),e.elemPicker.find("."+v)[0].style.background="",e.elemPicker.find("."+h).css("left",280)},D.prototype.side=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f),t=r.attr("lay-type"),n=e.elemPicker.find("."+u),l=e.elemPicker.find("."+p),c=e.elemPicker.find("."+g),y=e.elemPicker.find("."+m),C=e.elemPicker.find("."+v),w=e.elemPicker.find("."+h),D=l[0].offsetTop/180*360,E=100-(y[0].offsetTop+3)/180*100,H=(y[0].offsetLeft+3)/260*100,W=Math.round(w[0].offsetLeft/280*100)/100,j=e.elemColorBox.find("."+d),F=e.elemPicker.find(".layui-colorpicker-pre").children("div"),L=function(i,n,l,c){e.select(i,n,l);var f=x({h:i,s:n,b:l});if(j.addClass(a).removeClass(s),r[0].style.background="rgb("+f.r+", "+f.g+", "+f.b+")","torgb"===t&&e.elemPicker.find("."+b).find("input").val("rgb("+f.r+", "+f.g+", "+f.b+")"),"rgba"===t){var d=0;d=280*c,w.css("left",d),e.elemPicker.find("."+b).find("input").val("rgba("+f.r+", "+f.g+", "+f.b+", "+c+")"),r[0].style.background="rgba("+f.r+", "+f.g+", "+f.b+", "+c+")",C[0].style.background="linear-gradient(to right, rgba("+f.r+", "+f.g+", "+f.b+", 0), rgb("+f.r+", "+f.g+", "+f.b+"))"}o.change&&o.change(e.elemPicker.find("."+b).find("input").val())},M=i(['
    t&&(r=t);var l=r/180*360;D=l,L(l,H,E,W),e.preventDefault()};Y(r),e.preventDefault()}),n.on("click",function(e){var o=e.clientY-i(this).offset().top;o<0&&(o=0),o>this.offsetHeight&&(o=this.offsetHeight);var r=o/180*360;D=r,L(r,H,E,W),e.preventDefault()}),y.on("mousedown",function(e){var i=this.offsetTop,o=this.offsetLeft,r=e.clientY,t=e.clientX,n=function(e){var n=i+(e.clientY-r),l=o+(e.clientX-t),a=c[0].offsetHeight-3,s=c[0].offsetWidth-3;n<-3&&(n=-3),n>a&&(n=a),l<-3&&(l=-3),l>s&&(l=s);var f=(l+3)/260*100,d=100-(n+3)/180*100;E=d,H=f,L(D,f,d,W),e.preventDefault()};layui.stope(e),Y(n),e.preventDefault()}),c.on("mousedown",function(e){var o=e.clientY-i(this).offset().top-3+B.scrollTop(),r=e.clientX-i(this).offset().left-3+B.scrollLeft();o<-3&&(o=-3),o>this.offsetHeight-3&&(o=this.offsetHeight-3),r<-3&&(r=-3),r>this.offsetWidth-3&&(r=this.offsetWidth-3);var t=(r+3)/260*100,n=100-(o+3)/180*100;E=n,H=t,L(D,t,n,W),e.preventDefault(),y.trigger(e,"mousedown")}),w.on("mousedown",function(e){var i=this.offsetLeft,o=e.clientX,r=function(e){var r=i+(e.clientX-o),t=C[0].offsetWidth;r<0&&(r=0),r>t&&(r=t);var n=Math.round(r/280*100)/100;W=n,L(D,H,E,n),e.preventDefault()};Y(r),e.preventDefault()}),C.on("click",function(e){var o=e.clientX-i(this).offset().left;o<0&&(o=0),o>this.offsetWidth&&(o=this.offsetWidth);var r=Math.round(o/280*100)/100;W=r,L(D,H,E,r),e.preventDefault()}),F.each(function(){i(this).on("click",function(){i(this).parent(".layui-colorpicker-pre").addClass("selected").siblings().removeClass("selected");var e,o=this.style.backgroundColor,r=k(P(o)),t=o.slice(o.lastIndexOf(",")+1,o.length-1);D=r.h,H=r.s,E=r.b,3==(o.match(/[0-9]{1,3}/g)||[]).length&&(t=1),W=t,e=280*t,L(r.h,r.s,r.b,t)})})},D.prototype.select=function(e,i,o,r){var t=this,n=(t.config,C({h:e,s:100,b:100})),l=C({h:e,s:i,b:o}),c=e/360*180,a=180-o/100*180-3,s=i/100*260-3;t.elemPicker.find("."+p).css("top",c),t.elemPicker.find("."+g)[0].style.background="#"+n,t.elemPicker.find("."+m).css({top:a,left:s}),"change"!==r&&t.elemPicker.find("."+b).find("input").val("#"+l)},D.prototype.pickerEvents=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f),t=e.elemPicker.find("."+b+" input"),n={clear:function(i){r[0].style.background="",e.elemColorBox.find("."+d).removeClass(a).addClass(s),e.color="",o.done&&o.done(""),e.removePicker()},confirm:function(i,n){var l=t.val(),c=l,f={};if(l.indexOf(",")>-1){if(f=k(P(l)),e.select(f.h,f.s,f.b),r[0].style.background=c="#"+C(f),(l.match(/[0-9]{1,3}/g)||[]).length>3&&"rgba"===r.attr("lay-type")){var u=280*l.slice(l.lastIndexOf(",")+1,l.length-1);e.elemPicker.find("."+h).css("left",u),r[0].style.background=l,c=l}}else f=y(l),r[0].style.background=c="#"+C(f),e.elemColorBox.find("."+d).removeClass(s).addClass(a);return"change"===n?(e.select(f.h,f.s,f.b,n),void(o.change&&o.change(c))):(e.color=l,o.done&&o.done(l),void e.removePicker())}};e.elemPicker.on("click","*[colorpicker-events]",function(){var e=i(this),o=e.attr("colorpicker-events");n[o]&&n[o].call(this,e)}),t.on("keyup",function(e){var o=i(this);n.confirm.call(this,o,13===e.keyCode?null:"change")})},D.prototype.events=function(){var e=this,o=e.config,r=e.elemColorBox.find("."+f);e.elemColorBox.on("click",function(){e.renderPicker(),i(c)[0]&&(e.val(),e.side())}),o.elem[0]&&!e.elemColorBox[0].eventHandler&&(w.on("click",function(o){if(!i(o.target).hasClass(l)&&!i(o.target).parents("."+l)[0]&&!i(o.target).hasClass(c.replace(/\./g,""))&&!i(o.target).parents(c)[0]&&e.elemPicker){if(e.color){var t=k(P(e.color));e.select(t.h,t.s,t.b)}else e.elemColorBox.find("."+d).removeClass(a).addClass(s);r[0].style.background=e.color||"",e.removePicker()}}),B.on("resize",function(){return!(!e.elemPicker||!i(c)[0])&&void e.position()}),e.elemColorBox[0].eventHandler=!0)},o.render=function(e){var i=new D(e);return r.call(i)},e(t,o)});layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,a=layui.hint(),n=layui.device(),l="form",r=".layui-form",s="layui-this",o="layui-hide",c="layui-disabled",u=function(){this.config={verify:{required:[/[\S]+/,"必填项不能为空"],phone:[/^1\d{10}$/,"请输入正确的手机号"],email:[/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,"邮箱格式不正确"],url:[/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,"链接格式不正确"],number:function(e){if(!e||isNaN(e))return"只能填写数字"},date:[/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,"日期格式不正确"],identity:[/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,"请输入正确的身份证号"]}}};u.prototype.set=function(e){var i=this;return t.extend(!0,i.config,e),i},u.prototype.verify=function(e){var i=this;return t.extend(!0,i.config.verify,e),i},u.prototype.on=function(e,t){return layui.onevent.call(this,l,e,t)},u.prototype.val=function(e,i){var a=this,n=t(r+'[lay-filter="'+e+'"]');return n.each(function(e,a){var n=t(this);layui.each(i,function(e,t){var i,a=n.find('[name="'+e+'"]');a[0]&&(i=a[0].type,"checkbox"===i?a[0].checked=t:"radio"===i?a.each(function(){this.value==t&&(this.checked=!0)}):a.val(t))})}),f.render(null,e),a.getValue(e)},u.prototype.getValue=function(e,i){i=i||t(r+'[lay-filter="'+e+'"]').eq(0);var a={},n={},l=i.find("input,select,textarea");return layui.each(l,function(e,t){if(t.name=(t.name||"").replace(/^\s*|\s*&/,""),t.name){if(/^.*\[\]$/.test(t.name)){var i=t.name.match(/^(.*)\[\]$/g)[0];a[i]=0|a[i],t.name=t.name.replace(/^(.*)\[\]$/,"$1["+a[i]++ +"]")}/^checkbox|radio$/.test(t.type)&&!t.checked||(n[t.name]=t.value)}}),n},u.prototype.render=function(e,i){var n=this,u=t(r+function(){return i?'[lay-filter="'+i+'"]':""}()),d={select:function(){var e,i="请选择",a="layui-form-select",n="layui-select-title",r="layui-select-none",d="",f=u.find("select"),v=function(i,l){t(i.target).parent().hasClass(n)&&!l||(t("."+a).removeClass(a+"ed "+a+"up"),e&&d&&e.val(d)),e=null},y=function(i,u,f){var y,p=t(this),m=i.find("."+n),k=m.find("input"),g=i.find("dl"),x=g.children("dd"),b=this.selectedIndex;if(!u){var C=function(){var e=i.offset().top+i.outerHeight()+5-h.scrollTop(),t=g.outerHeight();b=p[0].selectedIndex,i.addClass(a+"ed"),x.removeClass(o),y=null,x.eq(b).addClass(s).siblings().removeClass(s),e+t>h.height()&&e>=t&&i.addClass(a+"up"),T()},w=function(e){i.removeClass(a+"ed "+a+"up"),k.blur(),y=null,e||$(k.val(),function(e){var i=p[0].selectedIndex;e&&(d=t(p[0].options[i]).html(),0===i&&d===k.attr("placeholder")&&(d=""),k.val(d||""))})},T=function(){var e=g.children("dd."+s);if(e[0]){var t=e.position().top,i=g.height(),a=e.height();t>i&&g.scrollTop(t+g.scrollTop()-i+a-5),t<0&&g.scrollTop(t+g.scrollTop()-5)}};m.on("click",function(e){i.hasClass(a+"ed")?w():(v(e,!0),C()),g.find("."+r).remove()}),m.find(".layui-edge").on("click",function(){k.focus()}),k.on("keyup",function(e){var t=e.keyCode;9===t&&C()}).on("keydown",function(e){var t=e.keyCode;9===t&&w();var i=function(t,a){var n,l;e.preventDefault();var r=function(){var e=g.children("dd."+s);if(g.children("dd."+o)[0]&&"next"===t){var i=g.children("dd:not(."+o+",."+c+")"),n=i.eq(0).index();if(n>=0&&n无匹配项

    '):g.find("."+r).remove()},"keyup"),""===t&&g.find("."+r).remove(),void T())};f&&k.on("keyup",q).on("blur",function(i){var a=p[0].selectedIndex;e=k,d=t(p[0].options[a]).html(),0===a&&d===k.attr("placeholder")&&(d=""),setTimeout(function(){$(k.val(),function(e){d||k.val("")},"blur")},200)}),x.on("click",function(){var e=t(this),a=e.attr("lay-value"),n=p.attr("lay-filter");return!e.hasClass(c)&&(e.hasClass("layui-select-tips")?k.val(""):(k.val(e.text()),e.addClass(s)),e.siblings().removeClass(s),p.val(a).removeClass("layui-form-danger"),layui.event.call(this,l,"select("+n+")",{elem:p[0],value:a,othis:i}),w(!0),!1)}),i.find("dl>dt").on("click",function(e){return!1}),t(document).off("click",v).on("click",v)}};f.each(function(e,l){var r=t(this),o=r.next("."+a),u=this.disabled,d=l.value,f=t(l.options[l.selectedIndex]),v=l.options[0];if("string"==typeof r.attr("lay-ignore"))return r.show();var h="string"==typeof r.attr("lay-search"),p=v?v.value?i:v.innerHTML||i:i,m=t(['
    ','
    ','','
    ','
    ',function(e){var t=[];return layui.each(e,function(e,a){0!==e||a.value?"optgroup"===a.tagName.toLowerCase()?t.push("
    "+a.label+"
    "):t.push('
    '+a.innerHTML+"
    "):t.push('
    '+(a.innerHTML||i)+"
    ")}),0===t.length&&t.push('
    没有选项
    '),t.join("")}(r.find("*"))+"
    ","
    "].join(""));o[0]&&o.remove(),r.after(m),y.call(this,m,u,h)})},checkbox:function(){var e={checkbox:["layui-form-checkbox","layui-form-checked","checkbox"],_switch:["layui-form-switch","layui-form-onswitch","switch"]},i=u.find("input[type=checkbox]"),a=function(e,i){var a=t(this);e.on("click",function(){var t=a.attr("lay-filter"),n=(a.attr("lay-text")||"").split("|");a[0].disabled||(a[0].checked?(a[0].checked=!1,e.removeClass(i[1]).find("em").text(n[1])):(a[0].checked=!0,e.addClass(i[1]).find("em").text(n[0])),layui.event.call(a[0],l,i[2]+"("+t+")",{elem:a[0],value:a[0].value,othis:e}))})};i.each(function(i,n){var l=t(this),r=l.attr("lay-skin"),s=(l.attr("lay-text")||"").split("|"),o=this.disabled;"switch"===r&&(r="_"+r);var u=e[r]||e.checkbox;if("string"==typeof l.attr("lay-ignore"))return l.show();var d=l.next("."+u[0]),f=t(['
    ",function(){var e=n.title.replace(/\s/g,""),t={checkbox:[e?""+n.title+"":"",''].join(""),_switch:""+((n.checked?s[0]:s[1])||"")+""};return t[r]||t.checkbox}(),"
    "].join(""));d[0]&&d.remove(),l.after(f),a.call(this,f,u)})},radio:function(){var e="layui-form-radio",i=["",""],a=u.find("input[type=radio]"),n=function(a){var n=t(this),s="layui-anim-scaleSpring";a.on("click",function(){var o=n[0].name,c=n.parents(r),u=n.attr("lay-filter"),d=c.find("input[name="+o.replace(/(\.|#|\[|\])/g,"\\$1")+"]");n[0].disabled||(layui.each(d,function(){var a=t(this).next("."+e);this.checked=!1,a.removeClass(e+"ed"),a.find(".layui-icon").removeClass(s).html(i[1])}),n[0].checked=!0,a.addClass(e+"ed"),a.find(".layui-icon").addClass(s).html(i[0]),layui.event.call(n[0],l,"radio("+u+")",{elem:n[0],value:n[0].value,othis:a}))})};a.each(function(a,l){var r=t(this),s=r.next("."+e),o=this.disabled;if("string"==typeof r.attr("lay-ignore"))return r.show();s[0]&&s.remove();var u=t(['
    ',''+i[l.checked?0:1]+"","
    "+function(){var e=l.title||"";return"string"==typeof r.next().attr("lay-radio")&&(e=r.next().html(),r.next().remove()),e}()+"
    ","
    "].join(""));r.after(u),n.call(this,u)})}};return e?d[e]?d[e]():a.error("不支持的"+e+"表单渲染"):layui.each(d,function(e,t){t()}),n};var d=function(){var e=null,a=f.config.verify,s="layui-form-danger",o={},c=t(this),u=c.parents(r),d=u.find("*[lay-verify]"),v=c.parents("form")[0],h=c.attr("lay-filter");return layui.each(d,function(l,r){var o=t(this),c=o.attr("lay-verify").split("|"),u=o.attr("lay-verType"),d=o.val();if(o.removeClass(s),layui.each(c,function(t,l){var c,f="",v="function"==typeof a[l];if(a[l]){var c=v?f=a[l](d,r):!a[l][0].test(d);if(f=f||a[l][1],"required"===l&&(f=o.attr("lay-reqText")||f),c)return"tips"===u?i.tips(f,function(){return"string"==typeof o.attr("lay-ignore")||"select"!==r.tagName.toLowerCase()&&!/^checkbox|radio$/.test(r.type)?o:o.next()}(),{tips:1}):"alert"===u?i.alert(f,{title:"提示",shadeClose:!0}):i.msg(f,{icon:5,shift:6}),n.android||n.ios||setTimeout(function(){r.focus()},7),o.addClass(s),e=!0}}),e)return e}),!e&&(o=f.getValue(null,u),layui.event.call(this,l,"submit("+h+")",{elem:this,form:v,field:o}))},f=new u,v=t(document),h=t(window);f.render(),v.on("reset",r,function(){var e=t(this).attr("lay-filter");setTimeout(function(){f.render(null,e)},50)}),v.on("submit",r,d).on("click","*[lay-submit]",d),e(l,f)});layui.define("form",function(e){"use strict";var i=layui.$,a=layui.form,n=layui.layer,t="tree",r={config:{},index:layui[t]?layui[t].index+1e4:0,set:function(e){var a=this;return a.config=i.extend({},a.config,e),a},on:function(e,i){return layui.onevent.call(this,t,e,i)}},l=function(){var e=this,i=e.config,a=i.id||e.index;return l.that[a]=e,l.config[a]=i,{config:i,reload:function(i){e.reload.call(e,i)},getChecked:function(){return e.getChecked.call(e)},setChecked:function(i){return e.setChecked.call(e,i)}}},c="layui-hide",d="layui-disabled",s="layui-tree-set",o="layui-tree-iconClick",h="layui-icon-addition",u="layui-icon-subtraction",p="layui-tree-entry",f="layui-tree-main",y="layui-tree-txt",v="layui-tree-pack",C="layui-tree-spread",k="layui-tree-setLineShort",m="layui-tree-showLine",x="layui-tree-lineExtend",b=function(e){var a=this;a.index=++r.index,a.config=i.extend({},a.config,r.config,e),a.render()};b.prototype.config={data:[],showCheckbox:!1,showLine:!0,accordion:!1,onlyIconControl:!1,isJump:!1,edit:!1,text:{defaultNodeName:"未命名",none:"无数据"}},b.prototype.reload=function(e){var a=this;layui.each(e,function(e,i){i.constructor===Array&&delete a.config[e]}),a.config=i.extend(!0,{},a.config,e),a.render()},b.prototype.render=function(){var e=this,a=e.config;e.checkids=[];var n=i('
    ');e.tree(n);var t=a.elem=i(a.elem);if(t[0]){if(e.key=a.id||e.index,e.elem=n,e.elemNone=i('
    '+a.text.none+"
    "),t.html(e.elem),0==e.elem.find(".layui-tree-set").length)return e.elem.append(e.elemNone);a.showCheckbox&&e.renderForm("checkbox"),e.elem.find(".layui-tree-set").each(function(){var e=i(this);e.parent(".layui-tree-pack")[0]||e.addClass("layui-tree-setHide"),!e.next()[0]&&e.parents(".layui-tree-pack").eq(1).hasClass("layui-tree-lineExtend")&&e.addClass(k),e.next()[0]||e.parents(".layui-tree-set").eq(0).next()[0]||e.addClass(k)}),e.events()}},b.prototype.renderForm=function(e){a.render(e,"LAY-tree-"+this.index)},b.prototype.tree=function(e,a){var n=this,t=n.config,r=a||t.data;layui.each(r,function(a,r){var l=r.children&&r.children.length>0,o=i('
    '),h=i(['
    ','
    ','
    ',function(){return t.showLine?l?'':'':''}(),function(){return t.showCheckbox?'':""}(),function(){return t.isJump&&r.href?''+(r.title||r.label||t.text.defaultNodeName)+"":''+(r.title||r.label||t.text.defaultNodeName)+""}(),"
    ",function(){if(!t.edit)return"";var e={add:'',update:'',del:''},i=['
    '];return t.edit===!0&&(t.edit=["update","del"]),"object"==typeof t.edit?(layui.each(t.edit,function(a,n){i.push(e[n]||"")}),i.join("")+"
    "):void 0}(),"
    "].join(""));l&&(h.append(o),n.tree(o,r.children)),e.append(h),h.prev("."+s)[0]&&h.prev().children(".layui-tree-pack").addClass("layui-tree-showLine"),l||h.parent(".layui-tree-pack").addClass("layui-tree-lineExtend"),n.spread(h,r),t.showCheckbox&&(r.checked&&n.checkids.push(r.id),n.checkClick(h,r)),t.edit&&n.operate(h,r)})},b.prototype.spread=function(e,a){var n=this,t=n.config,r=e.children("."+p),l=r.children("."+f),c=r.find("."+o),k=r.find("."+y),m=t.onlyIconControl?c:l,x="";m.on("click",function(i){var a=e.children("."+v),n=m.children(".layui-icon")[0]?m.children(".layui-icon"):m.find(".layui-tree-icon").children(".layui-icon");if(a[0]){if(e.hasClass(C))e.removeClass(C),a.slideUp(200),n.removeClass(u).addClass(h);else if(e.addClass(C),a.slideDown(200),n.addClass(u).removeClass(h),t.accordion){var r=e.siblings("."+s);r.removeClass(C),r.children("."+v).slideUp(200),r.find(".layui-tree-icon").children(".layui-icon").removeClass(u).addClass(h)}}else x="normal"}),k.on("click",function(){var n=i(this);n.hasClass(d)||(x=e.hasClass(C)?t.onlyIconControl?"open":"close":t.onlyIconControl?"close":"open",t.click&&t.click({elem:e,state:x,data:a}))})},b.prototype.setCheckbox=function(e,i,a){var n=this,t=(n.config,a.prop("checked"));if(!a.prop("disabled")){if("object"==typeof i.children||e.find("."+v)[0]){var r=e.find("."+v).find('input[same="layuiTreeCheck"]');r.each(function(){this.disabled||(this.checked=t)})}var l=function(e){if(e.parents("."+s)[0]){var i,a=e.parent("."+v),n=a.parent(),r=a.prev().find('input[same="layuiTreeCheck"]');t?r.prop("checked",t):(a.find('input[same="layuiTreeCheck"]').each(function(){this.checked&&(i=!0)}),i||r.prop("checked",!1)),l(n)}};l(e),n.renderForm("checkbox")}},b.prototype.checkClick=function(e,a){var n=this,t=n.config,r=e.children("."+p),l=r.children("."+f);l.on("click",'input[same="layuiTreeCheck"]+',function(r){layui.stope(r);var l=i(this).prev(),c=l.prop("checked");l.prop("disabled")||(n.setCheckbox(e,a,l),t.oncheck&&t.oncheck({elem:e,checked:c,data:a}))})},b.prototype.operate=function(e,a){var t=this,r=t.config,l=e.children("."+p),d=l.children("."+f);l.children(".layui-tree-btnGroup").on("click",".layui-icon",function(l){layui.stope(l);var f=i(this).data("type"),b=e.children("."+v),g={data:a,type:f,elem:e};if("add"==f){b[0]||(r.showLine?(d.find("."+o).addClass("layui-tree-icon"),d.find("."+o).children(".layui-icon").addClass(h).removeClass("layui-icon-file")):d.find(".layui-tree-iconArrow").removeClass(c),e.append('
    '));var w=r.operate&&r.operate(g),N={};if(N.title=r.text.defaultNodeName,N.id=w,t.tree(e.children("."+v),[N]),r.showLine)if(b[0])b.hasClass(x)||b.addClass(x),e.find("."+v).each(function(){i(this).children("."+s).last().addClass(k)}),b.children("."+s).last().prev().hasClass(k)?b.children("."+s).last().prev().removeClass(k):b.children("."+s).last().removeClass(k),!e.parent("."+v)[0]&&e.next()[0]&&b.children("."+s).last().removeClass(k);else{var T=e.siblings("."+s),L=1,A=e.parent("."+v);layui.each(T,function(e,a){i(a).children("."+v)[0]||(L=0)}),1==L?(T.children("."+v).addClass(m),T.children("."+v).children("."+s).removeClass(k),e.children("."+v).addClass(m),A.removeClass(x),A.children("."+s).last().children("."+v).children("."+s).last().addClass(k)):e.children("."+v).children("."+s).addClass(k)}if(!r.showCheckbox)return;if(d.find('input[same="layuiTreeCheck"]')[0].checked){var I=e.children("."+v).children("."+s).last();I.find('input[same="layuiTreeCheck"]')[0].checked=!0}t.renderForm("checkbox")}else if("update"==f){var F=d.children("."+y).html();d.children("."+y).html(""),d.append(''),d.children(".layui-tree-editInput").val(F).focus();var j=function(e){var i=e.val().trim();i=i?i:r.text.defaultNodeName,e.remove(),d.children("."+y).html(i),g.data.title=i,r.operate&&r.operate(g)};d.children(".layui-tree-editInput").blur(function(){j(i(this))}),d.children(".layui-tree-editInput").on("keydown",function(e){13===e.keyCode&&(e.preventDefault(),j(i(this)))})}else n.confirm('确认删除该节点 "'+(a.title||"")+'" 吗?',function(a){if(r.operate&&r.operate(g),g.status="remove",n.close(a),!e.prev("."+s)[0]&&!e.next("."+s)[0]&&!e.parent("."+v)[0])return e.remove(),void t.elem.append(t.elemNone);if(e.siblings("."+s).children("."+p)[0]){if(r.showCheckbox){var l=function(e){if(e.parents("."+s)[0]){var a=e.siblings("."+s).children("."+p),n=e.parent("."+v).prev(),r=n.find('input[same="layuiTreeCheck"]')[0],c=1,d=0;0==r.checked&&(a.each(function(e,a){var n=i(a).find('input[same="layuiTreeCheck"]')[0];0!=n.checked||n.disabled||(c=0),n.disabled||(d=1)}),1==c&&1==d&&(r.checked=!0,t.renderForm("checkbox"),l(n.parent("."+s))))}};l(e)}if(r.showLine){var d=e.siblings("."+s),h=1,f=e.parent("."+v);layui.each(d,function(e,a){i(a).children("."+v)[0]||(h=0)}),1==h?(b[0]||(f.removeClass(x),d.children("."+v).addClass(m),d.children("."+v).children("."+s).removeClass(k)),e.next()[0]?f.children("."+s).last().children("."+v).children("."+s).last().addClass(k):e.prev().children("."+v).children("."+s).last().addClass(k),e.next()[0]||e.parents("."+s)[1]||e.parents("."+s).eq(0).next()[0]||e.prev("."+s).addClass(k)):!e.next()[0]&&e.hasClass(k)&&e.prev().addClass(k)}}else{var y=e.parent("."+v).prev();if(r.showLine){y.find("."+o).removeClass("layui-tree-icon"),y.find("."+o).children(".layui-icon").removeClass(u).addClass("layui-icon-file");var w=y.parents("."+v).eq(0);w.addClass(x),w.children("."+s).each(function(){i(this).children("."+v).children("."+s).last().addClass(k)})}else y.find(".layui-tree-iconArrow").addClass(c);e.parents("."+s).eq(0).removeClass(C),e.parent("."+v).remove()}e.remove()})})},b.prototype.events=function(){var e=this,a=e.config;e.elem.find(".layui-tree-checkedFirst");e.setChecked(e.checkids),e.elem.find(".layui-tree-search").on("keyup",function(){var n=i(this),t=n.val(),r=n.nextAll(),l=[];r.find("."+y).each(function(){var e=i(this).parents("."+p);if(i(this).html().indexOf(t)!=-1){l.push(i(this).parent());var a=function(e){e.addClass("layui-tree-searchShow"),e.parent("."+v)[0]&&a(e.parent("."+v).parent("."+s))};a(e.parent("."+s))}}),r.find("."+p).each(function(){var e=i(this).parent("."+s);e.hasClass("layui-tree-searchShow")||e.addClass(c)}),0==r.find(".layui-tree-searchShow").length&&e.elem.append(e.elemNone),a.onsearch&&a.onsearch({elem:l})}),e.elem.find(".layui-tree-search").on("keydown",function(){i(this).nextAll().find("."+p).each(function(){var e=i(this).parent("."+s);e.removeClass("layui-tree-searchShow "+c)}),i(".layui-tree-emptyText")[0]&&i(".layui-tree-emptyText").remove()})},b.prototype.getChecked=function(){var e=this,a=e.config,n=[],t=[];e.elem.find(".layui-form-checked").each(function(){n.push(i(this).prev()[0].value)});var r=function(e,a){layui.each(e,function(e,t){layui.each(n,function(e,n){if(t.id==n){var l=i.extend({},t);return delete l.children,a.push(l),t.children&&(l.children=[],r(t.children,l.children)),!0}})})};return r(i.extend({},a.data),t),t},b.prototype.setChecked=function(e){var a=this;a.config;a.elem.find("."+s).each(function(a,n){var t=i(this).data("id"),r=i(n).children("."+p).find('input[same="layuiTreeCheck"]'),l=r.next();if("number"==typeof e){if(t==e)return r[0].checked||l.click(),!1}else"object"==typeof e&&layui.each(e,function(e,i){if(i==t&&!r[0].checked)return l.click(),!0})})},l.that={},l.config={},r.reload=function(e,i){var a=l.that[e];return a.reload(i),l.call(a)},r.getChecked=function(e){var i=l.that[e];return i.getChecked()},r.setChecked=function(e,i){var a=l.that[e];return a.setChecked(i)},r.render=function(e){var i=new b(e);return l.call(i)},e(t,r)});layui.define(["laytpl","form"],function(e){"use strict";var a=layui.$,t=layui.laytpl,n=layui.form,i="transfer",l={config:{},index:layui[i]?layui[i].index+1e4:0,set:function(e){var t=this;return t.config=a.extend({},t.config,e),t},on:function(e,a){return layui.onevent.call(this,i,e,a)}},r=function(){var e=this,a=e.config,t=a.id||e.index;return r.that[t]=e,r.config[t]=a,{config:a,reload:function(a){e.reload.call(e,a)},getData:function(){return e.getData.call(e)}}},c="layui-hide",o="layui-btn-disabled",d="layui-none",s="layui-transfer-box",u="layui-transfer-header",h="layui-transfer-search",f="layui-transfer-active",y="layui-transfer-data",p=function(e){return e=e||{},['
    ','
    ','","
    ","{{# if(d.data.showSearch){ }}",'","{{# } }}",'
      ',"
      "].join("")},v=['
      ',p({index:0,checkAllName:"layTransferLeftCheckAll"}),'
      ','",'","
      ",p({index:1,checkAllName:"layTransferRightCheckAll"}),"
      "].join(""),x=function(e){var t=this;t.index=++l.index,t.config=a.extend({},t.config,l.config,e),t.render()};x.prototype.config={title:["列表一","列表二"],width:200,height:360,data:[],value:[],showSearch:!1,id:"",text:{none:"无数据",searchNone:"无匹配数据"}},x.prototype.reload=function(e){var t=this;layui.each(e,function(e,a){a.constructor===Array&&delete t.config[e]}),t.config=a.extend(!0,{},t.config,e),t.render()},x.prototype.render=function(){var e=this,n=e.config,i=e.elem=a(t(v).render({data:n,index:e.index})),l=n.elem=a(n.elem);l[0]&&(n.data=n.data||[],n.value=n.value||[],e.key=n.id||e.index,l.html(e.elem),e.layBox=e.elem.find("."+s),e.layHeader=e.elem.find("."+u),e.laySearch=e.elem.find("."+h),e.layData=i.find("."+y),e.layBtn=i.find("."+f+" .layui-btn"),e.layBox.css({width:n.width,height:n.height}),e.layData.css({height:function(){return n.height-e.layHeader.outerHeight()-e.laySearch.outerHeight()-2}()}),e.renderData(),e.events())},x.prototype.renderData=function(){var e=this,a=(e.config,[{checkName:"layTransferLeftCheck",views:[]},{checkName:"layTransferRightCheck",views:[]}]);e.parseData(function(e){var t=e.selected?1:0,n=["
    • ",'',"
    • "].join("");a[t].views.push(n),delete e.selected}),e.layData.eq(0).html(a[0].views.join("")),e.layData.eq(1).html(a[1].views.join("")),e.renderCheckBtn()},x.prototype.renderForm=function(e){n.render(e,"LAY-transfer-"+this.index)},x.prototype.renderCheckBtn=function(e){var t=this,n=t.config;e=e||{},t.layBox.each(function(i){var l=a(this),r=l.find("."+y),d=l.find("."+u).find('input[type="checkbox"]'),s=r.find('input[type="checkbox"]'),h=0,f=!1;if(s.each(function(){var e=a(this).data("hide");(this.checked||this.disabled||e)&&h++,this.checked&&!e&&(f=!0)}),d.prop("checked",f&&h===s.length),t.layBtn.eq(i)[f?"removeClass":"addClass"](o),!e.stopNone){var p=r.children("li:not(."+c+")").length;t.noneView(r,p?"":n.text.none)}}),t.renderForm("checkbox")},x.prototype.noneView=function(e,t){var n=a('

      '+(t||"")+"

      ");e.find("."+d)[0]&&e.find("."+d).remove(),t.replace(/\s/g,"")&&e.append(n)},x.prototype.setValue=function(){var e=this,t=e.config,n=[];return e.layBox.eq(1).find("."+y+' input[type="checkbox"]').each(function(){var e=a(this).data("hide");e||n.push(this.value)}),t.value=n,e},x.prototype.parseData=function(e){var t=this,n=t.config,i=[];return layui.each(n.data,function(t,l){l=("function"==typeof n.parseData?n.parseData(l):l)||l,i.push(l=a.extend({},l)),layui.each(n.value,function(e,a){a==l.value&&(l.selected=!0)}),e&&e(l)}),n.data=i,t},x.prototype.getData=function(e){var a=this,t=a.config,n=[];return a.setValue(),layui.each(e||t.value,function(e,a){layui.each(t.data,function(e,t){delete t.selected,a==t.value&&n.push(t)})}),n},x.prototype.events=function(){var e=this,t=e.config;e.elem.on("click",'input[lay-filter="layTransferCheckbox"]+',function(){var t=a(this).prev(),n=t[0].checked,i=t.parents("."+s).eq(0).find("."+y);t[0].disabled||("all"===t.attr("lay-type")&&i.find('input[type="checkbox"]').each(function(){this.disabled||(this.checked=n)}),e.renderCheckBtn({stopNone:!0}))}),e.layBtn.on("click",function(){var n=a(this),i=n.data("index"),l=e.layBox.eq(i),r=[];if(!n.hasClass(o)){e.layBox.eq(i).each(function(t){var n=a(this),i=n.find("."+y);i.children("li").each(function(){var t=a(this),n=t.find('input[type="checkbox"]'),i=n.data("hide");n[0].checked&&!i&&(n[0].checked=!1,l.siblings("."+s).find("."+y).append(t.clone()),t.remove(),r.push(n[0].value)),e.setValue()})}),e.renderCheckBtn();var c=l.siblings("."+s).find("."+h+" input");""===c.val()||c.trigger("keyup"),t.onchange&&t.onchange(e.getData(r),i)}}),e.laySearch.find("input").on("keyup",function(){var n=this.value,i=a(this).parents("."+h).eq(0).siblings("."+y),l=i.children("li");l.each(function(){var e=a(this),t=e.find('input[type="checkbox"]'),i=t[0].title.indexOf(n)!==-1;e[i?"removeClass":"addClass"](c),t.data("hide",!i)}),e.renderCheckBtn();var r=l.length===i.children("li."+c).length;e.noneView(i,r?t.text.searchNone:"")})},r.that={},r.config={},l.reload=function(e,a){var t=r.that[e];return t.reload(a),r.call(t)},l.getData=function(e){var a=r.that[e];return a.getData()},l.render=function(e){var a=new x(e);return r.call(a)},e(i,l)});layui.define(["laytpl","laypage","layer","form","util"],function(e){"use strict";var t=layui.$,i=layui.laytpl,a=layui.laypage,l=layui.layer,n=layui.form,o=(layui.util,layui.hint()),r=layui.device(),d={config:{checkName:"LAY_CHECKED",indexName:"LAY_TABLE_INDEX"},cache:{},index:layui.table?layui.table.index+1e4:0,set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,y,e,t)}},c=function(){var e=this,t=e.config,i=t.id||t.index;return i&&(c.that[i]=e,c.config[i]=t),{config:t,reload:function(t){e.reload.call(e,t)},setColsWidth:function(){e.setColsWidth.call(e)},resize:function(){e.resize.call(e)}}},s=function(e){var t=c.config[e];return t||o.error("The ID option was not found in the table instance"),t||null},u=function(e,a,l,n){var o=e.templet?function(){return"function"==typeof e.templet?e.templet(l):i(t(e.templet).html()||String(a)).render(l)}():a;return n?t("
      "+o+"
      ").text():o},y="table",h=".layui-table",f="layui-hide",p="layui-none",v="layui-table-view",m=".layui-table-tool",g=".layui-table-box",b=".layui-table-init",x=".layui-table-header",k=".layui-table-body",C=".layui-table-main",w=".layui-table-fixed",T=".layui-table-fixed-l",A=".layui-table-fixed-r",L=".layui-table-total",N=".layui-table-page",S=".layui-table-sort",R="layui-table-edit",W="layui-table-hover",_=function(e){var t='{{#if(item2.colspan){}} colspan="{{item2.colspan}}"{{#} if(item2.rowspan){}} rowspan="{{item2.rowspan}}"{{#}}}';return e=e||{},['',"","{{# layui.each(d.data.cols, function(i1, item1){ }}","","{{# layui.each(item1, function(i2, item2){ }}",'{{# if(item2.fixed && item2.fixed !== "right"){ left = true; } }}','{{# if(item2.fixed === "right"){ right = true; } }}',function(){return e.fixed&&"right"!==e.fixed?'{{# if(item2.fixed && item2.fixed !== "right"){ }}':"right"===e.fixed?'{{# if(item2.fixed === "right"){ }}':""}(),"{{# var isSort = !(item2.colGroup) && item2.sort; }}",'",e.fixed?"{{# }; }}":"","{{# }); }}","","{{# }); }}","","
      ','
      ','{{# if(item2.type === "checkbox"){ }}','',"{{# } else { }}",'{{item2.title||""}}',"{{# if(isSort){ }}",'',"{{# } }}","{{# } }}","
      ","
      "].join("")},E=['',"","
      "].join(""),z=['
      ',"{{# if(d.data.toolbar){ }}",'
      ','
      ','
      ',"
      ","{{# } }}",'
      ',"{{# if(d.data.loading){ }}",'
      ','',"
      ","{{# } }}","{{# var left, right; }}",'
      ',_(),"
      ",'
      ',E,"
      ","{{# if(left){ }}",'
      ','
      ',_({fixed:!0}),"
      ",'
      ',E,"
      ","
      ","{{# }; }}","{{# if(right){ }}",'
      ','
      ',_({fixed:"right"}),'
      ',"
      ",'
      ',E,"
      ","
      ","{{# }; }}","
      ","{{# if(d.data.totalRow){ }}",'
      ','','',"
      ","
      ","{{# } }}","{{# if(d.data.page){ }}",'
      ','
      ',"
      ","{{# } }}","","
      "].join(""),H=t(window),j=t(document),F=function(e){var i=this;i.index=++d.index,i.config=t.extend({},i.config,d.config,e),i.render()};F.prototype.config={limit:10,loading:!0,cellMinWidth:60,defaultToolbar:["filter","exports","print"],autoSort:!0,text:{none:"无数据"}},F.prototype.render=function(){var e=this,a=e.config;if(a.elem=t(a.elem),a.where=a.where||{},a.id=a.id||a.elem.attr("id")||e.index,a.request=t.extend({pageName:"page",limitName:"limit"},a.request),a.response=t.extend({statusName:"code",statusCode:0,msgName:"msg",dataName:"data",totalRowName:"totalRow",countName:"count"},a.response),"object"==typeof a.page&&(a.limit=a.page.limit||a.limit,a.limits=a.page.limits||a.limits,e.page=a.page.curr=a.page.curr||1,delete a.page.elem,delete a.page.jump),!a.elem[0])return e;a.height&&/^full-\d+$/.test(a.height)&&(e.fullHeightGap=a.height.split("-")[1],a.height=H.height()-e.fullHeightGap),e.setInit();var l=a.elem,n=l.next("."+v),o=e.elem=t(i(z).render({VIEW_CLASS:v,data:a,index:e.index}));if(a.index=e.index,e.key=a.id||a.index,n[0]&&n.remove(),l.after(o),e.layTool=o.find(m),e.layBox=o.find(g),e.layHeader=o.find(x),e.layMain=o.find(C),e.layBody=o.find(k),e.layFixed=o.find(w),e.layFixLeft=o.find(T),e.layFixRight=o.find(A),e.layTotal=o.find(L),e.layPage=o.find(N),e.renderToolbar(),e.fullSize(),a.cols.length>1){var r=e.layFixed.find(x).find("th");r.height(e.layHeader.height()-1-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom")))}e.pullData(e.page),e.events()},F.prototype.initOpts=function(e){var t=this,i=(t.config,{checkbox:48,radio:48,space:15,numbers:40});e.checkbox&&(e.type="checkbox"),e.space&&(e.type="space"),e.type||(e.type="normal"),"normal"!==e.type&&(e.unresize=!0,e.width=e.width||i[e.type])},F.prototype.setInit=function(e){var t=this,i=t.config;return i.clientWidth=i.width||function(){var e=function(t){var a,l;t=t||i.elem.parent(),a=t.width();try{l="none"===t.css("display")}catch(n){}return!t[0]||a&&!l?a:e(t.parent())};return e()}(),"width"===e?i.clientWidth:void layui.each(i.cols,function(e,a){layui.each(a,function(l,n){if(!n)return void a.splice(l,1);if(n.key=e+"-"+l,n.hide=n.hide||!1,n.colGroup||n.colspan>1){var o=0;layui.each(i.cols[e+1],function(t,i){i.HAS_PARENT||o>1&&o==n.colspan||(i.HAS_PARENT=!0,i.parentKey=e+"-"+l,o+=parseInt(i.colspan>1?i.colspan:1))}),n.colGroup=!0}t.initOpts(n)})})},F.prototype.renderToolbar=function(){var e=this,a=e.config,l=['
      ','
      ','
      '].join(""),n=e.layTool.find(".layui-table-tool-temp");if("default"===a.toolbar)n.html(l);else if("string"==typeof a.toolbar){var o=t(a.toolbar).html()||"";o&&n.html(i(o).render(a))}var r={filter:{title:"筛选列",layEvent:"LAYTABLE_COLS",icon:"layui-icon-cols"},exports:{title:"导出",layEvent:"LAYTABLE_EXPORT",icon:"layui-icon-export"},print:{title:"打印",layEvent:"LAYTABLE_PRINT",icon:"layui-icon-print"}},d=[];"object"==typeof a.defaultToolbar&&layui.each(a.defaultToolbar,function(e,t){var i="string"==typeof t?r[t]:t;i&&d.push('
      ')}),e.layTool.find(".layui-table-tool-self").html(d.join(""))},F.prototype.setParentCol=function(e,t){var i=this,a=i.config,l=i.layHeader.find('th[data-key="'+a.index+"-"+t+'"]'),n=parseInt(l.attr("colspan"))||0;if(l[0]){var o=t.split("-"),r=a.cols[o[0]][o[1]];e?n--:n++,l.attr("colspan",n),l[n<1?"addClass":"removeClass"](f),r.colspan=n,r.hide=n<1;var d=l.data("parentkey");d&&i.setParentCol(e,d)}},F.prototype.setColsPatch=function(){var e=this,t=e.config;layui.each(t.cols,function(t,i){layui.each(i,function(t,i){i.hide&&e.setParentCol(i.hide,i.parentKey)})})},F.prototype.setColsWidth=function(){var e=this,t=e.config,i=0,a=0,l=0,n=0,o=e.setInit("width");e.eachCols(function(e,t){t.hide||i++}),o=o-function(){return"line"===t.skin||"nob"===t.skin?2:i+1}()-e.getScrollWidth(e.layMain[0])-1;var r=function(e){layui.each(t.cols,function(i,r){layui.each(r,function(i,d){var c=0,s=d.minWidth||t.cellMinWidth;return d?void(d.colGroup||d.hide||(e?l&&ln&&a&&(l=(o-n)/a)};r(),r(!0),e.autoColNums=a,e.eachCols(function(i,a){var n=a.minWidth||t.cellMinWidth;a.colGroup||a.hide||(0===a.width?e.getCssRule(t.index+"-"+a.key,function(e){e.style.width=Math.floor(l>=n?l:n)+"px"}):/\d+%$/.test(a.width)&&e.getCssRule(t.index+"-"+a.key,function(e){e.style.width=Math.floor(parseFloat(a.width)/100*o)+"px"}))});var d=e.layMain.width()-e.getScrollWidth(e.layMain[0])-e.layMain.children("table").outerWidth();if(e.autoColNums&&d>=-i&&d<=i){var c=function(t){var i;return t=t||e.layHeader.eq(0).find("thead th:last-child"),i=t.data("field"),!i&&t.prev()[0]?c(t.prev()):t},s=c(),u=s.data("key");e.getCssRule(u,function(t){var i=t.style.width||s.outerWidth();t.style.width=parseFloat(i)+d+"px",e.layMain.height()-e.layMain.prop("clientHeight")>0&&(t.style.width=parseFloat(t.style.width)-1+"px")})}e.loading(!0)},F.prototype.resize=function(){var e=this;e.fullSize(),e.setColsWidth(),e.scrollPatch()},F.prototype.reload=function(e){var i=this;e=e||{},delete i.haveInit,e.data&&e.data.constructor===Array&&delete i.config.data,i.config=t.extend(!0,{},i.config,e),i.render()},F.prototype.errorView=function(e){var i=this,a=i.layMain.find("."+p),l=t('
      '+(e||"Error")+"
      ");a[0]&&(i.layNone.remove(),a.remove()),i.layFixed.addClass(f),i.layMain.find("tbody").html(""),i.layMain.append(i.layNone=l),d.cache[i.key]=[]},F.prototype.page=1,F.prototype.pullData=function(e){var i=this,a=i.config,l=a.request,n=a.response,o=function(){"object"==typeof a.initSort&&i.sort(a.initSort.field,a.initSort.type)};if(i.startTime=(new Date).getTime(),a.url){var r={};r[l.pageName]=e,r[l.limitName]=a.limit;var d=t.extend(r,a.where);a.contentType&&0==a.contentType.indexOf("application/json")&&(d=JSON.stringify(d)),i.loading(),t.ajax({type:a.method||"get",url:a.url,contentType:a.contentType,data:d,dataType:"json",headers:a.headers||{},success:function(t){"function"==typeof a.parseData&&(t=a.parseData(t)||t),t[n.statusName]!=n.statusCode?(i.renderForm(),i.errorView(t[n.msgName]||'返回的数据不符合规范,正确的成功状态码应为:"'+n.statusName+'": '+n.statusCode)):(i.renderData(t,e,t[n.countName]),o(),a.time=(new Date).getTime()-i.startTime+" ms"),i.setColsWidth(),"function"==typeof a.done&&a.done(t,e,t[n.countName])},error:function(e,t){i.errorView("数据接口请求异常:"+t),i.renderForm(),i.setColsWidth()}})}else if(a.data&&a.data.constructor===Array){var c={},s=e*a.limit-a.limit;c[n.dataName]=a.data.concat().splice(s,a.limit),c[n.countName]=a.data.length,"object"==typeof a.totalRow&&(c[n.totalRowName]=t.extend({},a.totalRow)),i.renderData(c,e,c[n.countName]),o(),i.setColsWidth(),"function"==typeof a.done&&a.done(c,e,c[n.countName])}},F.prototype.eachCols=function(e){var t=this;return d.eachCols(null,e,t.config.cols),t},F.prototype.renderData=function(e,n,o,r){var c=this,s=c.config,y=e[s.response.dataName]||[],h=e[s.response.totalRowName],v=[],m=[],g=[],b=function(){var e;return!r&&c.sortKey?c.sort(c.sortKey.field,c.sortKey.sort,!0):(layui.each(y,function(a,l){var o=[],y=[],h=[],p=a+s.limit*(n-1)+1;0!==l.length&&(r||(l[d.config.indexName]=a),c.eachCols(function(n,r){var c=r.field||n,v=s.index+"-"+r.key,m=l[c];if(void 0!==m&&null!==m||(m=""),!r.colGroup){var g=['','
      '+function(){var n=t.extend(!0,{LAY_INDEX:p},l),o=d.config.checkName;switch(r.type){case"checkbox":return'";case"radio":return n[o]&&(e=a),'';case"numbers":return p}return r.toolbar?i(t(r.toolbar).html()||"").render(n):u(r,m,n)}(),"
      "].join("");o.push(g),r.fixed&&"right"!==r.fixed&&y.push(g),"right"===r.fixed&&h.push(g)}}),v.push(''+o.join("")+""),m.push(''+y.join("")+""),g.push(''+h.join("")+""))}),c.layBody.scrollTop(0),c.layMain.find("."+p).remove(),c.layMain.find("tbody").html(v.join("")),c.layFixLeft.find("tbody").html(m.join("")),c.layFixRight.find("tbody").html(g.join("")),c.renderForm(),"number"==typeof e&&c.setThisRowChecked(e),c.syncCheckAll(),c.haveInit?c.scrollPatch():setTimeout(function(){c.scrollPatch()},50),c.haveInit=!0,l.close(c.tipsIndex),s.HAS_SET_COLS_PATCH||c.setColsPatch(),void(s.HAS_SET_COLS_PATCH=!0))};return d.cache[c.key]=y,c.layPage[0==o||0===y.length&&1==n?"addClass":"removeClass"](f),r?b():0===y.length?(c.renderForm(),c.errorView(s.text.none)):(c.layFixed.removeClass(f),b(),c.renderTotal(y,h),void(s.page&&(s.page=t.extend({elem:"layui-table-page"+s.index,count:o,limit:s.limit,limits:s.limits||[10,20,30,40,50,60,70,80,90],groups:3,layout:["prev","page","next","skip","count","limit"],prev:'',next:'',jump:function(e,t){t||(c.page=e.curr,s.limit=e.limit,c.pullData(e.curr))}},s.page),s.page.count=o,a.render(s.page))))},F.prototype.renderTotal=function(e,t){var i=this,a=i.config,l={};if(a.totalRow){layui.each(e,function(e,t){0!==t.length&&i.eachCols(function(e,i){var a=i.field||e,n=t[a];i.totalRow&&(l[a]=(l[a]||0)+(parseFloat(n)||0))})}),i.dataTotal={};var n=[];i.eachCols(function(e,o){var r=o.field||e,d=function(){var e=o.totalRowText||"",i=parseFloat(l[r]).toFixed(2),a={};return a[r]=i,i=u(o,i,a),t?t[o.field]||e:o.totalRow?i||e:e}(),c=['','
      '+d,"
      "].join("");o.field&&(i.dataTotal[r]=d),n.push(c)}),i.layTotal.find("tbody").html(""+n.join("")+"")}},F.prototype.getColElem=function(e,t){var i=this,a=i.config;return e.eq(0).find(".laytable-cell-"+(a.index+"-"+t)+":eq(0)")},F.prototype.renderForm=function(e){n.render(e,"LAY-table-"+this.index)},F.prototype.setThisRowChecked=function(e){var t=this,i=(t.config,"layui-table-click"),a=t.layBody.find('tr[data-index="'+e+'"]');a.addClass(i).siblings("tr").removeClass(i)},F.prototype.sort=function(e,i,a,l){var n,r,c=this,s={},u=c.config,h=u.elem.attr("lay-filter"),f=d.cache[c.key];"string"==typeof e&&c.layHeader.find("th").each(function(i,a){var l=t(this),o=l.data("field");if(o===e)return e=l,n=o,!1});try{var n=n||e.data("field"),p=e.data("key");if(c.sortKey&&!a&&n===c.sortKey.field&&i===c.sortKey.sort)return;var v=c.layHeader.find("th .laytable-cell-"+p).find(S);c.layHeader.find("th").find(S).removeAttr("lay-sort"),v.attr("lay-sort",i||null),c.layFixed.find("th")}catch(m){return o.error("Table modules: Did not match to field")}c.sortKey={field:n,sort:i},u.autoSort&&("asc"===i?r=layui.sort(f,n):"desc"===i?r=layui.sort(f,n,!0):(r=layui.sort(f,d.config.indexName),delete c.sortKey)),s[u.response.dataName]=r||f,c.renderData(s,c.page,c.count,!0),l&&layui.event.call(e,y,"sort("+h+")",{field:n,type:i})},F.prototype.loading=function(e){var i=this,a=i.config;a.loading&&(e?(i.layInit&&i.layInit.remove(),delete i.layInit,i.layBox.find(b).remove()):(i.layInit=t(['
      ','',"
      "].join("")),i.layBox.append(i.layInit)))},F.prototype.setCheckData=function(e,t){var i=this,a=i.config,l=d.cache[i.key];l[e]&&l[e].constructor!==Array&&(l[e][a.checkName]=t)},F.prototype.syncCheckAll=function(){var e=this,t=e.config,i=e.layHeader.find('input[name="layTableCheckbox"]'),a=function(i){return e.eachCols(function(e,a){"checkbox"===a.type&&(a[t.checkName]=i)}),i};i[0]&&(d.checkStatus(e.key).isAll?(i[0].checked||(i.prop("checked",!0),e.renderForm("checkbox")),a(!0)):(i[0].checked&&(i.prop("checked",!1),e.renderForm("checkbox")),a(!1)))},F.prototype.getCssRule=function(e,t){var i=this,a=i.elem.find("style")[0],l=a.sheet||a.styleSheet||{},n=l.cssRules||l.rules;layui.each(n,function(i,a){if(a.selectorText===".laytable-cell-"+e)return t(a),!0})},F.prototype.fullSize=function(){var e,t=this,i=t.config,a=i.height;t.fullHeightGap&&(a=H.height()-t.fullHeightGap,a<135&&(a=135),t.elem.css("height",a)),a&&(e=parseFloat(a)-(t.layHeader.outerHeight()||38),i.toolbar&&(e-=t.layTool.outerHeight()||50),i.totalRow&&(e-=t.layTotal.outerHeight()||40),i.page&&(e-=t.layPage.outerHeight()||41),t.layMain.css("height",e-2))},F.prototype.getScrollWidth=function(e){var t=0;return e?t=e.offsetWidth-e.clientWidth:(e=document.createElement("div"),e.style.width="100px",e.style.height="100px",e.style.overflowY="scroll",document.body.appendChild(e),t=e.offsetWidth-e.clientWidth,document.body.removeChild(e)),t},F.prototype.scrollPatch=function(){var e=this,i=e.layMain.children("table"),a=e.layMain.width()-e.layMain.prop("clientWidth"),l=e.layMain.height()-e.layMain.prop("clientHeight"),n=(e.getScrollWidth(e.layMain[0]),i.outerWidth()-e.layMain.width()),o=function(e){if(a&&l){if(e=e.eq(0),!e.find(".layui-table-patch")[0]){var i=t('
      ');i.find("div").css({width:a}),e.find("tr").append(i)}}else e.find(".layui-table-patch").remove()};o(e.layHeader),o(e.layTotal);var r=e.layMain.height(),d=r-l;e.layFixed.find(k).css("height",i.height()>=d?d:"auto"),e.layFixRight[n>0?"removeClass":"addClass"](f),e.layFixRight.css("right",a-1)},F.prototype.events=function(){var e,i=this,a=i.config,o=t("body"),c={},s=i.layHeader.find("th"),h=".layui-table-cell",p=a.elem.attr("lay-filter");i.layTool.on("click","*[lay-event]",function(e){var o=t(this),c=o.attr("lay-event"),s=function(e){var l=t(e.list),n=t('
        ');n.html(l),a.height&&n.css("max-height",a.height-(i.layTool.outerHeight()||50)),o.find(".layui-table-tool-panel")[0]||o.append(n),i.renderForm(),n.on("click",function(e){layui.stope(e)}),e.done&&e.done(n,l)};switch(layui.stope(e),j.trigger("table.tool.panel.remove"),l.close(i.tipsIndex),c){case"LAYTABLE_COLS":s({list:function(){var e=[];return i.eachCols(function(t,i){i.field&&"normal"==i.type&&e.push('
      • ')}),e.join("")}(),done:function(){n.on("checkbox(LAY_TABLE_TOOL_COLS)",function(e){var l=t(e.elem),n=this.checked,o=l.data("key"),r=l.data("parentkey");layui.each(a.cols,function(e,t){layui.each(t,function(t,l){if(e+"-"+t===o){var d=l.hide;l.hide=!n,i.elem.find('*[data-key="'+a.index+"-"+o+'"]')[n?"removeClass":"addClass"](f),d!=l.hide&&i.setParentCol(!n,r),i.resize()}})})})}});break;case"LAYTABLE_EXPORT":r.ie?l.tips("导出功能不支持 IE,请用 Chrome 等高级浏览器导出",this,{tips:3}):s({list:function(){return['
      • 导出到 Csv 文件
      • ','
      • 导出到 Excel 文件
      • '].join("")}(),done:function(e,l){l.on("click",function(){var e=t(this).data("type");d.exportFile.call(i,a.id,null,e)})}});break;case"LAYTABLE_PRINT":var u=window.open("打印窗口","_blank"),h=[""].join(""),v=t(i.layHeader.html());v.append(i.layMain.find("table").html()),v.append(i.layTotal.find("table").html()),v.find("th.layui-table-patch").remove(),v.find(".layui-table-col-special").remove(),u.document.write(h+v.prop("outerHTML")),u.document.close(),u.print(),u.close()}layui.event.call(this,y,"toolbar("+p+")",t.extend({event:c,config:a},{}))}),s.on("mousemove",function(e){var i=t(this),a=i.offset().left,l=e.clientX-a;i.data("unresize")||c.resizeStart||(c.allowResize=i.width()-l<=10,o.css("cursor",c.allowResize?"col-resize":""))}).on("mouseleave",function(){t(this);c.resizeStart||o.css("cursor","")}).on("mousedown",function(e){var l=t(this);if(c.allowResize){var n=l.data("key");e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],i.getCssRule(n,function(e){var t=e.style.width||l.outerWidth();c.rule=e,c.ruleWidth=parseFloat(t),c.minWidth=l.data("minwidth")||a.cellMinWidth})}}),j.on("mousemove",function(t){if(c.resizeStart){if(t.preventDefault(),c.rule){var a=c.ruleWidth+t.clientX-c.offset[0];a');return n[0].value=i.data("content")||l.text(),i.find("."+R)[0]||i.append(n),n.focus(),void layui.stope(e)}}).on("mouseenter","td",function(){b.call(this)}).on("mouseleave","td",function(){b.call(this,"hide")});var g="layui-table-grid-down",b=function(e){var i=t(this),a=i.children(h);if(!i.data("off"))if(e)i.find(".layui-table-grid-down").remove();else if(a.prop("scrollWidth")>a.outerWidth()){if(a.find("."+g)[0])return;i.append('
        ')}};i.layBody.on("click","."+g,function(e){var n=t(this),o=n.parent(),d=o.children(h);i.tipsIndex=l.tips(['
        ',d.html(),"
        ",''].join(""),d[0],{tips:[3,""],time:-1,anim:-1,maxWidth:r.ios||r.android?300:i.elem.width()/2,isOutAnim:!1,skin:"layui-table-tips",success:function(e,t){e.find(".layui-table-tips-c").on("click",function(){l.close(t)})}}),layui.stope(e)}),i.layBody.on("click","*[lay-event]",function(){var e=t(this),a=e.parents("tr").eq(0).data("index");layui.event.call(this,y,"tool("+p+")",v.call(this,{event:e.attr("lay-event")})),i.setThisRowChecked(a)}),i.layMain.on("scroll",function(){var e=t(this),a=e.scrollLeft(),n=e.scrollTop();i.layHeader.scrollLeft(a),i.layTotal.scrollLeft(a),i.layFixed.find(k).scrollTop(n),l.close(i.tipsIndex)}),H.on("resize",function(){i.resize()})},function(){j.on("click",function(){j.trigger("table.remove.tool.panel")}),j.on("table.remove.tool.panel",function(){t(".layui-table-tool-panel").remove()})}(),d.init=function(e,i){i=i||{};var a=this,l=t(e?'table[lay-filter="'+e+'"]':h+"[lay-data]"),n="Table element property lay-data configuration item has a syntax error: ";return l.each(function(){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){o.error(n+l)}var c=[],s=t.extend({elem:this,cols:[],data:[],skin:a.attr("lay-skin"),size:a.attr("lay-size"),even:"string"==typeof a.attr("lay-even")},d.config,i,l);e&&a.hide(),a.find("thead>tr").each(function(e){s.cols[e]=[],t(this).children().each(function(i){var a=t(this),l=a.attr("lay-data");try{l=new Function("return "+l)()}catch(r){return o.error(n+l)}var d=t.extend({title:a.text(),colspan:a.attr("colspan")||0,rowspan:a.attr("rowspan")||0},l);d.colspan<2&&c.push(d),s.cols[e].push(d)})}),a.find("tbody>tr").each(function(e){var i=t(this),a={};i.children("td").each(function(e,i){var l=t(this),n=l.data("field");if(n)return a[n]=l.html()}),layui.each(c,function(e,t){var l=i.children("td").eq(e);a[t.field]=l.html()}),s.data[e]=a}),d.render(s)}),a},c.that={},c.config={},d.eachCols=function(e,i,a){var l=c.config[e]||{},n=[],o=0;a=t.extend(!0,[],a||l.cols),layui.each(a,function(e,t){layui.each(t,function(t,i){if(i.colGroup){var l=0;o++,i.CHILD_COLS=[],layui.each(a[e+1],function(e,t){t.PARENT_COL_INDEX||l>1&&l==i.colspan||(t.PARENT_COL_INDEX=o,i.CHILD_COLS.push(t),l+=parseInt(t.colspan>1?t.colspan:1))})}i.PARENT_COL_INDEX||n.push(i)})});var r=function(e){layui.each(e||n,function(e,t){return t.CHILD_COLS?r(t.CHILD_COLS):void("function"==typeof i&&i(e,t))})};r()},d.checkStatus=function(e){var t=0,i=0,a=[],l=d.cache[e]||[];return layui.each(l,function(e,l){return l.constructor===Array?void i++:void(l[d.config.checkName]&&(t++,a.push(d.clearCacheKey(l))))}),{data:a,isAll:!!l.length&&t===l.length-i}},d.exportFile=function(e,t,i){var a=this;t=t||d.clearCacheKey(d.cache[e]),i=i||"csv";var l=c.config[e]||{},n={csv:"text/csv",xls:"application/vnd.ms-excel"}[i],s=document.createElement("a");return r.ie?o.error("IE_NOT_SUPPORT_EXPORTS"):(s.href="data:"+n+";charset=utf-8,\ufeff"+encodeURIComponent(function(){var i=[],l=[],n=[];return layui.each(t,function(t,a){var n=[];"object"==typeof e?(layui.each(e,function(e,a){0==t&&i.push(a||"")}),layui.each(d.clearCacheKey(a),function(e,t){n.push('"'+(t||"")+'"')})):d.eachCols(e,function(e,l){if(l.field&&"normal"==l.type&&!l.hide){var o=a[l.field];void 0!==o&&null!==o||(o=""),0==t&&i.push(l.title||""),n.push('"'+u(l,o,a,"text")+'"')}}),l.push(n.join(","))}),layui.each(a.dataTotal,function(e,t){n.push(t)}),i.join(",")+"\r\n"+l.join("\r\n")+"\r\n"+n.join(",")}()),s.download=(l.title||"table_"+(l.index||""))+"."+i,document.body.appendChild(s),s.click(),void document.body.removeChild(s))},d.resize=function(e){if(e){var t=s(e);if(!t)return;c.that[e].resize()}else layui.each(c.that,function(){this.resize()})},d.reload=function(e,t){var i=s(e);if(i){var a=c.that[e];return a.reload(t),c.call(a)}},d.render=function(e){var t=new F(e);return c.call(t)},d.clearCacheKey=function(e){return e=t.extend({},e),delete e[d.config.checkName],delete e[d.config.indexName],e},d.init(),e(y,d)});layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(clearInterval(e.timer),e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
          ',function(){var i=[];return layui.each(e.elemItem,function(e){i.push("")}),i.join("")}(),"
        "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a",u=1;u<=i.length;u++){var r='
      • ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+'
      • ":n+=r}n+=""+(i.text?''+i.value+"星":"")+"";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)});layui.define("jquery",function(e){"use strict";var t=layui.$,i={fixbar:function(e){var i,n,a="layui-fixbar",o="layui-fixbar-top",r=t(document),l=t("body");e=t.extend({showHeight:200},e),e.bar1=e.bar1===!0?"":e.bar1,e.bar2=e.bar2===!0?"":e.bar2,e.bgcolor=e.bgcolor?"background-color:"+e.bgcolor:"";var c=[e.bar1,e.bar2,""],u=t(['
          ',e.bar1?'
        • '+c[0]+"
        • ":"",e.bar2?'
        • '+c[1]+"
        • ":"",'
        • '+c[2]+"
        • ","
        "].join("")),g=u.find("."+o),s=function(){var t=r.scrollTop();t>=e.showHeight?i||(g.show(),i=1):i&&(g.hide(),i=0)};t("."+a)[0]||("object"==typeof e.css&&u.css(e.css),l.append(u),s(),u.find("li").on("click",function(){var i=t(this),n=i.attr("lay-type");"top"===n&&t("html,body").animate({scrollTop:0},200),e.click&&e.click.call(this,n)}),r.on("scroll",function(){clearTimeout(n),n=setTimeout(function(){s()},100)}))},countdown:function(e,t,i){var n=this,a="function"==typeof t,o=new Date(e).getTime(),r=new Date(!t||a?(new Date).getTime():t).getTime(),l=o-r,c=[Math.floor(l/864e5),Math.floor(l/36e5)%24,Math.floor(l/6e4)%60,Math.floor(l/1e3)%60];a&&(i=t);var u=setTimeout(function(){n.countdown(e,r+1e3,i)},1e3);return i&&i(l>0?c:[0,0,0,0],t,u),l<=0&&clearTimeout(u),u},timeAgo:function(e,t){var i=this,n=[[],[]],a=(new Date).getTime()-new Date(e).getTime();return a>26784e5?(a=new Date(e),n[0][0]=i.digit(a.getFullYear(),4),n[0][1]=i.digit(a.getMonth()+1),n[0][2]=i.digit(a.getDate()),t||(n[1][0]=i.digit(a.getHours()),n[1][1]=i.digit(a.getMinutes()),n[1][2]=i.digit(a.getSeconds())),n[0].join("-")+" "+n[1].join(":")):a>=864e5?(a/1e3/60/60/24|0)+"天前":a>=36e5?(a/1e3/60/60|0)+"小时前":a>=18e4?(a/1e3/60|0)+"分钟前":a<0?"未来":"刚刚"},digit:function(e,t){var i="";e=String(e),t=t||2;for(var n=e.length;n/g,">").replace(/'/g,"'").replace(/"/g,""")},event:function(e,n,a){var o=t("body");return a=a||"click",n=i.event[e]=t.extend(!0,i.event[e],n)||{},i.event.UTIL_EVENT_CALLBACK=i.event.UTIL_EVENT_CALLBACK||{},o.off(a,"*["+e+"]",i.event.UTIL_EVENT_CALLBACK[e]),i.event.UTIL_EVENT_CALLBACK[e]=function(){var i=t(this),a=i.attr(e);"function"==typeof n[a]&&n[a].call(this,i)},o.on(a,"*["+e+"]",i.event.UTIL_EVENT_CALLBACK[e]),n}};!function(e,t,i){"$:nomunge";function n(){a=t[l](function(){o.each(function(){var t=e(this),i=t.width(),n=t.height(),a=e.data(this,u);(i!==a.w||n!==a.h)&&t.trigger(c,[a.w=i,a.h=n])}),n()},r[g])}var a,o=e([]),r=e.resize=e.extend(e.resize,{}),l="setTimeout",c="resize",u=c+"-special-event",g="delay",s="throttleWindow";r[g]=250,r[s]=!0,e.event.special[c]={setup:function(){if(!r[s]&&this[l])return!1;var t=e(this);o=o.add(t),e.data(this,u,{w:t.width(),h:t.height()}),1===o.length&&n()},teardown:function(){if(!r[s]&&this[l])return!1;var t=e(this);o=o.not(t),t.removeData(u),o.length||clearTimeout(a)},add:function(t){function n(t,n,o){var r=e(this),l=e.data(this,u)||{};l.w=n!==i?n:r.width(),l.h=o!==i?o:r.height(),a.apply(this,arguments)}if(!r[s]&&this[l])return!1;var a;return e.isFunction(t)?(a=t,n):(a=t.handler,void(t.handler=n))}}}(t,window),e("util",i)});layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t='';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),!i&&f.width()&&(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)});layui.define(["layer","form"],function(t){"use strict";var e=layui.$,i=layui.layer,a=layui.form,l=(layui.hint(),layui.device()),n="layedit",o="layui-show",r="layui-disabled",c=function(){var t=this;t.index=0,t.config={tool:["strong","italic","underline","del","|","left","center","right","|","link","unlink","face","image"],hideTool:[],height:280}};c.prototype.set=function(t){var i=this;return e.extend(!0,i.config,t),i},c.prototype.on=function(t,e){return layui.onevent(n,t,e)},c.prototype.build=function(t,i){i=i||{};var a=this,n=a.config,r="layui-layedit",c=e("string"==typeof t?"#"+t:t),u="LAY_layedit_"+ ++a.index,d=c.next("."+r),y=e.extend({},n,i),f=function(){var t=[],e={};return layui.each(y.hideTool,function(t,i){e[i]=!0}),layui.each(y.tool,function(i,a){C[a]&&!e[a]&&t.push(C[a])}),t.join("")}(),m=e(['
        ','
        '+f+"
        ",'
        ','',"
        ","
        "].join(""));return l.ie&&l.ie<8?c.removeClass("layui-hide").addClass(o):(d[0]&&d.remove(),s.call(a,m,c[0],y),c.addClass("layui-hide").after(m),a.index)},c.prototype.getContent=function(t){var e=u(t);if(e[0])return d(e[0].document.body.innerHTML)},c.prototype.getText=function(t){var i=u(t);if(i[0])return e(i[0].document.body).text()},c.prototype.setContent=function(t,i,a){var l=u(t);l[0]&&(a?e(l[0].document.body).append(i):e(l[0].document.body).html(i),layedit.sync(t))},c.prototype.sync=function(t){var i=u(t);if(i[0]){var a=e("#"+i[1].attr("textarea"));a.val(d(i[0].document.body.innerHTML))}},c.prototype.getSelection=function(t){var e=u(t);if(e[0]){var i=m(e[0].document);return document.selection?i.text:i.toString()}};var s=function(t,i,a){var l=this,n=t.find("iframe");n.css({height:a.height}).on("load",function(){var o=n.contents(),r=n.prop("contentWindow"),c=o.find("head"),s=e([""].join("")),u=o.find("body");c.append(s),u.attr("contenteditable","true").css({"min-height":a.height}).html(i.value||""),y.apply(l,[r,n,i,a]),g.call(l,r,t,a)})},u=function(t){var i=e("#LAY_layedit_"+t),a=i.prop("contentWindow");return[a,i]},d=function(t){return 8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),t},y=function(t,a,n,o){var r=t.document,c=e(r.body);c.on("keydown",function(t){var e=t.keyCode;if(13===e){var a=m(r),l=p(a),n=l.parentNode;if("pre"===n.tagName.toLowerCase()){if(t.shiftKey)return;return i.msg("请暂时用shift+enter"),!1}r.execCommand("formatBlock",!1,"

        ")}}),e(n).parents("form").on("submit",function(){var t=c.html();8==l.ie&&(t=t.replace(/<.+>/g,function(t){return t.toLowerCase()})),n.value=t}),c.on("paste",function(e){r.execCommand("formatBlock",!1,"

        "),setTimeout(function(){f.call(t,c),n.value=c.html()},100)})},f=function(t){var i=this;i.document;t.find("*[style]").each(function(){var t=this.style.textAlign;this.removeAttribute("style"),e(this).css({"text-align":t||""})}),t.find("table").addClass("layui-table"),t.find("script,link").remove()},m=function(t){return t.selection?t.selection.createRange():t.getSelection().getRangeAt(0)},p=function(t){return t.endContainer||t.parentElement().childNodes[0]},v=function(t,i,a){var l=this.document,n=document.createElement(t);for(var o in i)n.setAttribute(o,i[o]);if(n.removeAttribute("text"),l.selection){var r=a.text||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.pasteHTML(e(n).prop("outerHTML")),a.select()}else{var r=a.toString()||i.text;if("a"===t&&!r)return;r&&(n.innerHTML=r),a.deleteContents(),a.insertNode(n)}},h=function(t,i){var a=this.document,l="layedit-tool-active",n=p(m(a)),o=function(e){return t.find(".layedit-tool-"+e)};i&&i[i.hasClass(l)?"removeClass":"addClass"](l),t.find(">i").removeClass(l),o("unlink").addClass(r),e(n).parents().each(function(){var t=this.tagName.toLowerCase(),e=this.style.textAlign;"b"!==t&&"strong"!==t||o("b").addClass(l),"i"!==t&&"em"!==t||o("i").addClass(l),"u"===t&&o("u").addClass(l),"strike"===t&&o("d").addClass(l),"p"===t&&("center"===e?o("center").addClass(l):"right"===e?o("right").addClass(l):o("left").addClass(l)),"a"===t&&(o("link").addClass(l),o("unlink").removeClass(r))})},g=function(t,a,l){var n=t.document,o=e(n.body),c={link:function(i){var a=p(i),l=e(a).parent();b.call(o,{href:l.attr("href"),target:l.attr("target")},function(e){var a=l[0];"A"===a.tagName?a.href=e.url:v.call(t,"a",{target:e.target,href:e.url,text:e.url},i)})},unlink:function(t){n.execCommand("unlink")},face:function(e){x.call(this,function(i){v.call(t,"img",{src:i.src,alt:i.alt},e)})},image:function(a){var n=this;layui.use("upload",function(o){var r=l.uploadImage||{};o.render({url:r.url,method:r.type,elem:e(n).find("input")[0],done:function(e){0==e.code?(e.data=e.data||{},v.call(t,"img",{src:e.data.src,alt:e.data.title},a)):i.msg(e.msg||"上传失败")}})})},code:function(e){k.call(o,function(i){v.call(t,"pre",{text:i.code,"lay-lang":i.lang},e)})},help:function(){i.open({type:2,title:"帮助",area:["600px","380px"],shadeClose:!0,shade:.1,skin:"layui-layer-msg",content:["http://www.layui.com/about/layedit/help.html","no"]})}},s=a.find(".layui-layedit-tool"),u=function(){var i=e(this),a=i.attr("layedit-event"),l=i.attr("lay-command");if(!i.hasClass(r)){o.focus();var u=m(n);u.commonAncestorContainer;l?(n.execCommand(l),/justifyLeft|justifyCenter|justifyRight/.test(l)&&n.execCommand("formatBlock",!1,"

        "),setTimeout(function(){o.focus()},10)):c[a]&&c[a].call(this,u),h.call(t,s,i)}},d=/image/;s.find(">i").on("mousedown",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)||u.call(this)}).on("click",function(){var t=e(this),i=t.attr("layedit-event");d.test(i)&&u.call(this)}),o.on("click",function(){h.call(t,s),i.close(x.index)})},b=function(t,e){var l=this,n=i.open({type:1,id:"LAY_layedit_link",area:"350px",shade:.05,shadeClose:!0,moveType:1,title:"超链接",skin:"layui-layer-msg",content:['

          ','
        • ','','
          ','',"
          ","
        • ",'
        • ','','
          ','",'","
          ","
        • ",'
        • ','','',"
        • ","
        "].join(""),success:function(t,n){var o="submit(layedit-link-yes)";a.render("radio"),t.find(".layui-btn-primary").on("click",function(){i.close(n),l.focus()}),a.on(o,function(t){i.close(b.index),e&&e(t.field)})}});b.index=n},x=function(t){var a=function(){var t=["[微笑]","[嘻嘻]","[哈哈]","[可爱]","[可怜]","[挖鼻]","[吃惊]","[害羞]","[挤眼]","[闭嘴]","[鄙视]","[爱你]","[泪]","[偷笑]","[亲亲]","[生病]","[太开心]","[白眼]","[右哼哼]","[左哼哼]","[嘘]","[衰]","[委屈]","[吐]","[哈欠]","[抱抱]","[怒]","[疑问]","[馋嘴]","[拜拜]","[思考]","[汗]","[困]","[睡]","[钱]","[失望]","[酷]","[色]","[哼]","[鼓掌]","[晕]","[悲伤]","[抓狂]","[黑线]","[阴险]","[怒骂]","[互粉]","[心]","[伤心]","[猪头]","[熊猫]","[兔子]","[ok]","[耶]","[good]","[NO]","[赞]","[来]","[弱]","[草泥马]","[神马]","[囧]","[浮云]","[给力]","[围观]","[威武]","[奥特曼]","[礼物]","[钟]","[话筒]","[蜡烛]","[蛋糕]"],e={};return layui.each(t,function(t,i){e[i]=layui.cache.dir+"images/face/"+t+".gif"}),e}();return x.hide=x.hide||function(t){"face"!==e(t.target).attr("layedit-event")&&i.close(x.index)},x.index=i.tips(function(){var t=[];return layui.each(a,function(e,i){t.push('
      • '+e+'
      • ')}),'
          '+t.join("")+"
        "}(),this,{tips:1,time:0,skin:"layui-box layui-util-face",maxWidth:500,success:function(l,n){l.css({marginTop:-4,marginLeft:-10}).find(".layui-clear>li").on("click",function(){t&&t({src:a[this.title],alt:this.title}),i.close(n)}),e(document).off("click",x.hide).on("click",x.hide)}})},k=function(t){var e=this,l=i.open({type:1,id:"LAY_layedit_code",area:"550px",shade:.05,shadeClose:!0,moveType:1,title:"插入代码",skin:"layui-layer-msg",content:['
          ','
        • ','','
          ','","
          ","
        • ",'
        • ','','
          ','',"
          ","
        • ",'
        • ','','',"
        • ","
        "].join(""),success:function(l,n){var o="submit(layedit-code-yes)";a.render("select"),l.find(".layui-btn-primary").on("click",function(){i.close(n),e.focus()}),a.on(o,function(e){i.close(k.index),t&&t(e.field)})}});k.index=l},C={html:'',strong:'',italic:'',underline:'',del:'',"|":'',left:'',center:'',right:'',link:'',unlink:'',face:'',image:'',code:'',help:''},w=new c;t(n,w)});layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html('
        1. '+o.replace(/[\r\t\n]+/g,"
        2. ")+"
        "),c.find(">.layui-code-h3")[0]||c.prepend('

        '+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code':"")+"

        ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss"); \ No newline at end of file From cff2d5564e32f3329fd863feb7ee58aeb2631243 Mon Sep 17 00:00:00 2001 From: koogua Date: Thu, 8 Oct 2020 18:41:07 +0800 Subject: [PATCH 05/44] =?UTF-8?q?!5=20=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B71.1.0=20*=20=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7=20*=20=E5=AE=8C=E5=96=84=E5=90=8E=E5=8F=B0=E4=BB=8A?= =?UTF-8?q?=E6=97=A5=E7=BB=9F=E8=AE=A1,=E5=A2=9E=E5=8A=A0=E6=9D=83?= =?UTF-8?q?=E9=99=90=E7=99=BD=E5=90=8D=E5=8D=95=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E9=A6=96=E9=A1=B5=E8=8F=9C=E5=8D=95=EF=BC=8C?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=90=8E=E5=8F=B0=E7=99=BB=E5=BD=95=E9=A1=B5?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=20*=20Merge=20branch=20'koogua/I1XFCF'=20of?= =?UTF-8?q?=20https://gitee.com/koogua/course-tencen=E2=80=A6=20*=20?= =?UTF-8?q?=E5=89=8D=E5=8F=B0=E5=AD=A6=E4=B9=A0=E8=B5=84=E6=96=99=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=AE=8C=E6=88=90=20*=20!2=20=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E8=BF=90=E8=90=A5=E7=BB=9F=E8=AE=A1=E5=90=88=E5=B9=B6=20*=20?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E5=AD=A6=E4=B9=A0=E8=B5=84=E6=96=99=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Library/AppInfo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Library/AppInfo.php b/app/Library/AppInfo.php index 9bf2de1c..d2198fa6 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.0.0'; + protected $version = '1.1.0'; public function __get($name) { From de2a71415e8f9801b3ef8dcbf061ed5b650c94e8 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Tue, 13 Oct 2020 17:58:11 +0800 Subject: [PATCH 06/44] =?UTF-8?q?demo=E5=90=8E=E5=8F=B0=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Views/templates/main.volt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/Http/Admin/Views/templates/main.volt b/app/Http/Admin/Views/templates/main.volt index 3c1345f1..8803b2a0 100644 --- a/app/Http/Admin/Views/templates/main.volt +++ b/app/Http/Admin/Views/templates/main.volt @@ -22,5 +22,12 @@ {% block include_js %}{% endblock %} {% block inline_js %}{% endblock %} + +{% set site = setting('site') %} + +{% if site['analytics_enabled'] == 1 %} + {{ site['analytics_script'] }} +{% endif %} + \ No newline at end of file From e03dcb89fa4514443b35d8faa4a5742891e43d05 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Mon, 23 Nov 2020 21:50:37 +0800 Subject: [PATCH 07/44] =?UTF-8?q?=E5=88=A0=E9=99=A4=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E7=9A=84signature=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Admin/Controllers/UploadController.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/Http/Admin/Controllers/UploadController.php b/app/Http/Admin/Controllers/UploadController.php index 712c0af9..878b784a 100644 --- a/app/Http/Admin/Controllers/UploadController.php +++ b/app/Http/Admin/Controllers/UploadController.php @@ -91,22 +91,4 @@ class UploadController extends Controller return $this->jsonSuccess($data); } - /** - * @Get("/sign", name="admin.upload.sign") - */ - public function signatureAction() - { - $service = new StorageService(); - - $token = $service->getFederationToken(); - - $data = [ - 'credentials' => $token->getCredentials(), - 'expiredTime' => $token->getExpiredTime(), - 'startTime' => time(), - ]; - - return $this->jsonSuccess($data); - } - } From a2a8e5c810453414d6cfeda994dfdaf1f460c796 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Mon, 23 Nov 2020 21:55:58 +0800 Subject: [PATCH 08/44] =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E6=96=AD=E7=82=B9=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Home/Controllers/IndexController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Http/Home/Controllers/IndexController.php b/app/Http/Home/Controllers/IndexController.php index 82430cfd..ea099b71 100644 --- a/app/Http/Home/Controllers/IndexController.php +++ b/app/Http/Home/Controllers/IndexController.php @@ -44,8 +44,6 @@ class IndexController extends Controller { $service = new IndexService(); - dd($service->getLives()); - $this->view->pick('index/full'); $this->view->setVar('lives', $service->getLives()); $this->view->setVar('slides', $service->getSlides()); From 4cae5ece118626c9e71498407b8d6542ce1746f6 Mon Sep 17 00:00:00 2001 From: koogua Date: Fri, 27 Nov 2020 09:38:54 +0800 Subject: [PATCH 09/44] =?UTF-8?q?!16=20v1.2.0=E9=98=B6=E6=AE=B5=E6=80=A7?= =?UTF-8?q?=E5=90=88=E5=B9=B6=20*=20=E6=9B=B4=E6=8D=A2README.md=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E7=AE=80=E4=B9=A6=E5=9B=BE=E5=BA=8A=E4=B8=BAgitee?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=20*=20=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=99=A8=E5=9B=BE=E7=89=87=E4=B8=8A=E4=BC=A0=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=BA=AB=E4=BB=BD?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=EF=BC=8Cmarkdown=E5=86=85=E5=AE=B9=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=20*=20=E7=A7=BB=E9=99=A4Mobile=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8DAPI=E8=AF=B7=E6=B1=82=E7=AB=A0?= =?UTF-8?q?=E8=8A=82=E4=BF=A1=E6=81=AF=E6=9D=83=E9=99=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 5 -- README.md | 52 +++++++++++-------- app/Http/Admin/Views/setting/storage.volt | 4 ++ .../Api/Controllers/ChapterController.php | 6 +++ app/Http/Api/Services/Account.php | 4 -- .../Home/Controllers/PublicController.php | 26 ---------- .../Home/Controllers/UploadController.php | 11 +++- app/Http/Home/Views/chapter/catalog.volt | 3 +- app/Http/Home/Views/chapter/live/active.volt | 1 - app/Http/Home/Views/chapter/live/forbid.volt | 1 - .../Home/Views/chapter/live/inactive.volt | 1 - app/Http/Home/Views/chapter/read.volt | 1 - app/Http/Home/Views/chapter/vod.volt | 1 - app/Http/Home/Views/course/show_teacher.volt | 2 +- app/Http/Home/Views/user/show.volt | 2 +- app/Http/Mobile/Controllers/Controller.php | 20 ------- .../Mobile/Controllers/IndexController.php | 40 -------------- .../Mobile/Controllers/PublicController.php | 14 ----- app/Http/Mobile/Module.php | 31 ----------- app/Http/Mobile/Services/Login.php | 29 ----------- app/Http/Mobile/Services/Logout.php | 18 ------- app/Http/Mobile/Services/Service.php | 8 --- app/Library/Helper.php | 36 +++++++++++++ app/Models/Slide.php | 2 +- app/Services/Logic/Consult/ConsultCreate.php | 1 + app/Services/Logic/Consult/ConsultDelete.php | 18 ++++--- app/Services/Logic/Course/BasicInfo.php | 2 + app/Services/Logic/Help/HelpInfo.php | 4 ++ app/Services/Logic/Page/PageInfo.php | 2 + app/Services/Logic/Review/ReviewCreate.php | 6 +-- app/Services/Logic/Review/ReviewDelete.php | 6 +-- app/Services/Logic/Review/ReviewUpdate.php | 7 +-- app/Traits/Auth.php | 2 +- app/Validators/Course.php | 2 +- app/Validators/ImGroup.php | 2 +- app/Validators/Slide.php | 13 +---- app/Validators/User.php | 2 +- app/Validators/Validator.php | 4 +- bootstrap/HttpKernel.php | 4 -- config/routes.php | 9 ---- public/static/admin/js/vditor.js | 2 +- 41 files changed, 128 insertions(+), 276 deletions(-) delete mode 100644 LICENSE delete mode 100644 app/Http/Mobile/Controllers/Controller.php delete mode 100644 app/Http/Mobile/Controllers/IndexController.php delete mode 100644 app/Http/Mobile/Controllers/PublicController.php delete mode 100644 app/Http/Mobile/Module.php delete mode 100644 app/Http/Mobile/Services/Login.php delete mode 100644 app/Http/Mobile/Services/Logout.php delete mode 100644 app/Http/Mobile/Services/Service.php diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 1afa4e89..00000000 --- a/LICENSE +++ /dev/null @@ -1,5 +0,0 @@ -使用协议 - -1. 本系统属于强业务类型,非通用类库框架,不适合再次衍生发布。 -2. 在保留我们版权标识的前提下,用户可以修改以满足自己的需求,可以用于商业用途。 -3. 有限社区支持,用户对自己的行为负责。 \ No newline at end of file diff --git a/README.md b/README.md index abfadbd8..1d264fff 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,45 @@ ## 酷瓜云课堂 +![酷瓜云网课GPL协议开源](https://images.gitee.com/uploads/images/2020/1127/092621_3805cf8f_23592.png) + #### 项目介绍 -酷瓜云课堂,依托腾讯云基础服务架构,采用 C 扩展框架 Phalcon 开发,致力网络教育软件。 +酷瓜云课堂,依托腾讯云基础服务架构,采用C扩展框架Phalcon开发,GPL-2.0开源协议,致力开源网课系统,开源网校系统,开源在线教育系统。 + +![](https://img.shields.io/static/v1?label=release&message=1.2.0&color=blue) +![](https://img.shields.io/static/v1?label=stars&message=101&color=blue) +![](https://img.shields.io/static/v1?label=forks&message=40&color=blue) +![](https://img.shields.io/static/v1?label=license&message=GPL-2.0&color=blue) #### 系统功能 实现了点播、直播、专栏、会员、微聊等,是一个完整的产品,具体功能我也不想写一大堆,自己体验吧! -- [前台演示](https://ctc.koogua.com) -- [后台演示](https://ctc.koogua.com/admin) - -帐号:100015@163.com / 123456 (前后台通用) - 友情提示: - 系统配置低(1核 1G 1M 跑多个容器),切莫压测 - 课程数据来源于网络(无实质内容),切莫购买 - 管理后台已禁止数据提交,私密配置已过滤 +演示帐号:**13507083515 / 123456** (前后台通用) + +桌面端演示: + +- [前台演示](https://ctc.koogua.com) +- [后台演示](https://ctc.koogua.com/admin) + +移动端演示: + +![移动端二维码](https://images.gitee.com/uploads/images/2020/1127/093203_265221a2_23592.png) + +支付流程演示: + +- [MySQL提升课程全面讲解MySQL架构设计(0.01元)](https://ctc.koogua.com/order/confirm?item_id=1390&item_type=1) +- [Nginx入门到实践Nginx中间件(0.01元)](https://ctc.koogua.com/order/confirm?item_id=1286&item_type=1) +- [数据库与中间件的基础必修课(0.02元)](https://ctc.koogua.com/order/confirm?item_id=80&item_type=2) + +Tips: 测试支付请用手机号注册一个新账户,以便接收订单通知,以及避免课程无法购买 + #### 项目组件 - 后台框架:[phalcon 3.4.5](https://phalcon.io) @@ -27,14 +48,6 @@ - 即时通讯:[workerman 3.5.22](https://workerman.net) - 基础依赖:[php7.3](https://php.net), [mysql5.7](https://mysql.com), [redis5.0](https://redis.io) -#### 使用协议 - -虽然尝试了解过开源协议,但是理解的模棱两可,干脆用自己的协议吧。 - -1. 本系统属于强业务类型,非通用类库框架,不适合再次衍生发布。 -2. 在保留我们版权标识的前提下,用户可以修改以满足自己的需求,可以用于商业用途。 -3. 有限社区支持,用户对自己的行为负责。 - #### 安装指南 - [运行环境搭建](https://gitee.com/koogua/course-tencent-cloud-docker) @@ -43,20 +56,13 @@ #### 开发计划 - 桌面端:进行中 -- 移动端:待启动 +- 移动端:进行中 - 小程序:待启动 #### 意见反馈 - [在线反馈](https://gitee.com/koogua/course-tencent-cloud/issues)(推荐) -- QQ邮箱: 76632555@qq.com -- QQ群组: 787363898 - -#### 加入我们 - -这是一个创业项目,个人能力和精力有限,要兼顾产品规划以及开发,还要处理很多琐碎事情。目前在南山科技园某个众创空间,希望有 **深圳前端同学** 加入我们。 - -联系邮箱:76632555@qq.com +- QQ交流群: 787363898 #### 通过这个项目能学到什么? diff --git a/app/Http/Admin/Views/setting/storage.volt b/app/Http/Admin/Views/setting/storage.volt index 34ab1e62..9b1d2d6e 100644 --- a/app/Http/Admin/Views/setting/storage.volt +++ b/app/Http/Admin/Views/setting/storage.volt @@ -55,6 +55,10 @@ cover_270 mageMogr2/thumbnail/270x/interlace/0 + + content_800 + mageMogr2/thumbnail/800x/interlace/0 + slide_1100 imageMogr2/thumbnail/1100x/interlace/0 diff --git a/app/Http/Api/Controllers/ChapterController.php b/app/Http/Api/Controllers/ChapterController.php index 1897b9d1..d990ac62 100644 --- a/app/Http/Api/Controllers/ChapterController.php +++ b/app/Http/Api/Controllers/ChapterController.php @@ -47,6 +47,12 @@ class ChapterController extends Controller $chapter = $service->handle($id); + $owned = $chapter['me']['owned'] ?? false; + + if (!$owned) { + return $this->jsonError(['msg' => '没有访问章节权限']); + } + return $this->jsonSuccess(['chapter' => $chapter]); } diff --git a/app/Http/Api/Services/Account.php b/app/Http/Api/Services/Account.php index 96d09ce6..baf537f8 100644 --- a/app/Http/Api/Services/Account.php +++ b/app/Http/Api/Services/Account.php @@ -50,10 +50,6 @@ class Account extends Service $user = $validator->checkUserLogin($post['account'], $post['password']); - //$validator = new CaptchaValidator(); - - //$validator->checkCode($post['ticket'], $post['rand']); - return $this->auth->saveAuthInfo($user); } diff --git a/app/Http/Home/Controllers/PublicController.php b/app/Http/Home/Controllers/PublicController.php index 63eb1e37..21e28b1e 100644 --- a/app/Http/Home/Controllers/PublicController.php +++ b/app/Http/Home/Controllers/PublicController.php @@ -10,7 +10,6 @@ use App\Services\Pay\Wxpay as WxpayService; use App\Services\Storage as StorageService; use App\Traits\Response as ResponseTrait; use App\Traits\Security as SecurityTrait; -use Phalcon\Text; use PHPQRCode\QRcode; class PublicController extends \Phalcon\Mvc\Controller @@ -44,31 +43,6 @@ class PublicController extends \Phalcon\Mvc\Controller } } - /** - * @Get("/img/{id:[0-9]+}", name="home.img") - */ - public function imageAction($id) - { - $repo = new UploadRepo(); - - $file = $repo->findById($id); - - if ($file && Text::startsWith($file->mime, 'image')) { - - $service = new StorageService(); - - $location = $service->getImageUrl($file->path); - - $this->response->redirect($location); - - } else { - - $this->response->setStatusCode(404); - - return $this->response; - } - } - /** * @Get("/qrcode", name="home.qrcode") */ diff --git a/app/Http/Home/Controllers/UploadController.php b/app/Http/Home/Controllers/UploadController.php index e7877cf0..351f25cb 100644 --- a/app/Http/Home/Controllers/UploadController.php +++ b/app/Http/Home/Controllers/UploadController.php @@ -1,9 +1,9 @@ getAuthUser(); + + $validator = new AppValidator(); + + $validator->checkAuthUser($authUser->id); + } + /** * @Post("/avatar/img", name="home.upload.avatar_img") */ diff --git a/app/Http/Home/Views/chapter/catalog.volt b/app/Http/Home/Views/chapter/catalog.volt index 71105da9..9625d784 100644 --- a/app/Http/Home/Views/chapter/catalog.volt +++ b/app/Http/Home/Views/chapter/catalog.volt @@ -7,11 +7,10 @@
        \ No newline at end of file diff --git a/app/Http/Home/Views/connect/bind_register.volt b/app/Http/Home/Views/connect/bind_register.volt index 7c1f151d..87208750 100644 --- a/app/Http/Home/Views/connect/bind_register.volt +++ b/app/Http/Home/Views/connect/bind_register.volt @@ -23,6 +23,7 @@ + diff --git a/app/Http/Home/Views/order/pay.volt b/app/Http/Home/Views/order/pay.volt index 16bca937..5e3a0a88 100644 --- a/app/Http/Home/Views/order/pay.volt +++ b/app/Http/Home/Views/order/pay.volt @@ -17,8 +17,12 @@ 支付金额:{{ '¥%0.2f'|format(order.amount) }}
        - {{ image('home/img/alipay.png') }} - {{ image('home/img/wxpay.png') }} + {% if pay_provider.alipay.enabled == 1 %} + {{ image('home/img/alipay.png') }} + {% endif %} + {% if pay_provider.wxpay.enabled == 1 %} + {{ image('home/img/wxpay.png') }} + {% endif %}
        diff --git a/app/Services/Logic/Account/OAuthProvider.php b/app/Services/Logic/Account/OAuthProvider.php new file mode 100644 index 00000000..79a9cd2d --- /dev/null +++ b/app/Services/Logic/Account/OAuthProvider.php @@ -0,0 +1,23 @@ +getSettings('oauth.weixin'); + $weibo = $this->getSettings('oauth.weibo'); + $qq = $this->getSettings('oauth.qq'); + + return [ + 'weixin' => ['enabled' => $weixin['enabled']], + 'weibo' => ['enabled' => $weibo['enabled']], + 'qq' => ['enabled' => $qq['enabled']], + ]; + } + +} diff --git a/app/Services/Logic/Order/PayProvider.php b/app/Services/Logic/Order/PayProvider.php new file mode 100644 index 00000000..a8271845 --- /dev/null +++ b/app/Services/Logic/Order/PayProvider.php @@ -0,0 +1,21 @@ +getSettings('pay.alipay'); + $wxpay = $this->getSettings('pay.wxpay'); + + return [ + 'alipay' => ['enabled' => $alipay['enabled']], + 'wxpay' => ['enabled' => $wxpay['enabled']], + ]; + } + +} diff --git a/app/Library/OAuth.php b/app/Services/OAuth.php similarity index 89% rename from app/Library/OAuth.php rename to app/Services/OAuth.php index 1543c937..43aeefb0 100644 --- a/app/Library/OAuth.php +++ b/app/Services/OAuth.php @@ -1,18 +1,37 @@ $this->clientId, 'redirect_uri' => $this->redirectUri, 'state' => $this->getState(), - 'scope' => 'get_user_info', 'response_type' => 'code', + 'scope' => 'get_user_info', ]; return self::AUTHORIZE_URL . '?' . http_build_query($params); diff --git a/app/Library/OAuth/WeiBo.php b/app/Services/OAuth/WeiBo.php similarity index 93% rename from app/Library/OAuth/WeiBo.php rename to app/Services/OAuth/WeiBo.php index 5e47cc0b..a8c7bb48 100644 --- a/app/Library/OAuth/WeiBo.php +++ b/app/Services/OAuth/WeiBo.php @@ -1,8 +1,8 @@ openId; + $userInfo['id'] = $data['id']; $userInfo['name'] = $data['name']; - $userInfo['avatar'] = $data['avatar_large']; + $userInfo['avatar'] = $data['profile_image_url']; return $userInfo; } diff --git a/app/Library/OAuth/WeiXin.php b/app/Services/OAuth/WeiXin.php similarity index 97% rename from app/Library/OAuth/WeiXin.php rename to app/Services/OAuth/WeiXin.php index 278b8d10..df371797 100644 --- a/app/Library/OAuth/WeiXin.php +++ b/app/Services/OAuth/WeiXin.php @@ -1,8 +1,8 @@ $this->clientId, 'redirect_uri' => $this->redirectUri, 'state' => $this->getState(), - 'scope' => 'snsapi_login', 'response_type' => 'code', + 'scope' => 'snsapi_login', ]; return self::AUTHORIZE_URL . '?' . http_build_query($params); From dd99631f6ea6b78d590b4431431af0b45e38f6eb Mon Sep 17 00:00:00 2001 From: koogua Date: Mon, 7 Dec 2020 11:02:13 +0800 Subject: [PATCH 28/44] =?UTF-8?q?!33=20=E5=BC=80=E6=94=BE=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=98=B6=E6=AE=B5=E6=80=A7=E5=90=88=E5=B9=B6=20*=20Me?= =?UTF-8?q?rge=20remote-tracking=20branch=20'gitee/xiaochong0302/I280IZ'?= =?UTF-8?q?=20into=20xiaocho=E2=80=A6=20*=20=E5=88=9D=E6=AD=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E5=BC=80=E6=94=BE=E7=99=BB=E5=BD=95=EF=BC=8C=E5=BE=85?= =?UTF-8?q?=E7=BA=BF=E4=B8=8A=E6=B5=8B=E8=AF=957=20*=20Merge=20branch=20'd?= =?UTF-8?q?emo'=20of=20gitee.com:koogua/course-tencent-cloud=20into=20xiao?= =?UTF-8?q?=E2=80=A6=20*=20=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90=E5=BC=80?= =?UTF-8?q?=E6=94=BE=E7=99=BB=E5=BD=95=EF=BC=8C=E5=BE=85=E7=BA=BF=E4=B8=8A?= =?UTF-8?q?=E6=B5=8B=E8=AF=956=20*=20!30=20=E5=BC=80=E6=94=BE=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E7=BA=BF=E4=B8=8A=E6=B5=8B=E8=AF=955=20*=20!29=20?= =?UTF-8?q?=E5=BC=80=E6=94=BE=E7=99=BB=E5=BD=95=E7=BA=BF=E4=B8=8A=E6=B5=8B?= =?UTF-8?q?=E8=AF=955=20*=20=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90=E5=BC=80?= =?UTF-8?q?=E6=94=BE=E7=99=BB=E5=BD=95=EF=BC=8C=E5=BE=85=E7=BA=BF=E4=B8=8A?= =?UTF-8?q?=E6=B5=8B=E8=AF=955=20*=20!28=20=E5=BC=80=E6=94=BE=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E7=BA=BF=E4=B8=8A=E6=B5=8B=E8=AF=954=20*=20=E5=88=9D?= =?UTF-8?q?=E6=AD=A5=E5=AE=8C=E6=88=90=E5=BC=80=E6=94=BE=E7=99=BB=E5=BD=95?= =?UTF-8?q?=EF=BC=8C=E5=BE=85=E7=BA=BF=E4=B8=8A=E6=B5=8B=E8=AF=954=20*=20!?= =?UTF-8?q?27=20=E5=BC=80=E6=94=BE=E7=99=BB=E5=BD=95=E7=BA=BF=E4=B8=8A?= =?UTF-8?q?=E6=B5=8B=E8=AF=953=20*=20=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E5=BC=80=E6=94=BE=E7=99=BB=E5=BD=95=EF=BC=8C=E5=BE=85=E7=BA=BF?= =?UTF-8?q?=E4=B8=8A=E6=B5=8B=E8=AF=953=20*=20!26=20=E5=BC=80=E6=94=BE?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E7=BA=BF=E4=B8=8A=E6=B5=8B=E8=AF=952=20*=20?= =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90=E5=BC=80=E6=94=BE=E7=99=BB?= =?UTF-8?q?=E5=BD=95=EF=BC=8C=E5=BE=85=E7=BA=BF=E4=B8=8A=E6=B5=8B=E8=AF=95?= =?UTF-8?q?2=20*=20!25=20=E5=BC=80=E6=94=BE=E7=99=BB=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E4=B8=8A=E6=B5=8B=E8=AF=95=20*=20=E5=88=9D=E6=AD=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E5=BC=80=E6=94=BE=E7=99=BB=E5=BD=95=EF=BC=8C=E5=BE=85?= =?UTF-8?q?=E7=BA=BF=E4=B8=8A=E6=B5=8B=E8=AF=95=20*=20!22=20=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E6=9B=B4=E6=96=B0h5=E6=94=AF=E4=BB=98=20*=20Merge=20r?= =?UTF-8?q?emote-tracking=20branch=20'remotes/gitee/develop'=20into=20demo?= =?UTF-8?q?=20*=20!20=20=E9=AA=8C=E8=AF=81=E6=9B=B4=E6=96=B0h5=E6=94=AF?= =?UTF-8?q?=E4=BB=98=20*=20Merge=20branch=20'develop'=20of=20https://gitee?= =?UTF-8?q?.com/koogua/course-tencent-clou=E2=80=A6=20*=20!16=20v1.2.0?= =?UTF-8?q?=E9=98=B6=E6=AE=B5=E6=80=A7=E5=90=88=E5=B9=B6=20*=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E8=B0=83=E8=AF=95=E6=96=AD=E7=82=B9=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=20*=20=E5=88=A0=E9=99=A4=E9=87=8D=E5=A4=8D=E7=9A=84signature?= =?UTF-8?q?=E6=96=B9=E6=B3=95=20*=20Merge=20branch=20'develop'=20of=20http?= =?UTF-8?q?s://gitee.com/koogua/course-tencent-clou=E2=80=A6=20*=20demo?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E5=A2=9E=E5=8A=A0=E7=BB=9F=E8=AE=A1=20*=20!5?= =?UTF-8?q?=20=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC=E5=8F=B71.1.0=20*=20!4?= =?UTF-8?q?=20v1.1.0=E7=89=88=E6=9C=ACdevelop->demo=20*=20Merge=20branch?= =?UTF-8?q?=20'develop'=20into=20demo=20*=201.=E5=A2=9E=E5=8A=A0changelog.?= =?UTF-8?q?md=20*=20Merge=20branch=20'develop'=20into=20demo=20*=20Merge?= =?UTF-8?q?=20branch=20'develop'=20into=20demo=20*=20Merge=20branch=20'dev?= =?UTF-8?q?elop'=20into=20demo=20*=20!1=20=E7=B2=BE=E7=AE=80=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=20*=20Merge=20branch=20'develop'=20?= =?UTF-8?q?into=20demo=20*=20=E5=90=88=E5=B9=B6=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Controllers/Controller.php | 11 + .../Admin/Controllers/SettingController.php | 29 +++ app/Http/Admin/Services/AuthNode.php | 6 + app/Http/Admin/Services/Setting.php | 35 +++ app/Http/Admin/Views/setting/oauth.volt | 24 ++ app/Http/Admin/Views/setting/oauth_qq.volt | 35 +++ app/Http/Admin/Views/setting/oauth_weibo.volt | 35 +++ .../Admin/Views/setting/oauth_weixin.volt | 35 +++ app/Http/Admin/Views/templates/main.volt | 7 + .../Home/Controllers/AccountController.php | 6 + .../Home/Controllers/ConnectController.php | 139 ++++++++++++ app/Http/Home/Controllers/OrderController.php | 6 + .../Controllers/UserConsoleController.php | 34 ++- app/Http/Home/Services/Connect.php | 208 ++++++++++++++++++ app/Http/Home/Views/account/login.volt | 11 + app/Http/Home/Views/connect/bind.volt | 34 +++ app/Http/Home/Views/connect/bind_login.volt | 21 ++ .../Home/Views/connect/bind_register.volt | 32 +++ app/Http/Home/Views/order/pay.volt | 8 +- .../Home/Views/user/console/account_info.volt | 56 +++++ app/Library/OAuth.php | 53 ----- app/Models/Connect.php | 104 +++++++++ app/Repos/Connect.php | 62 ++++++ app/Services/Logic/Account/OAuthProvider.php | 23 ++ app/Services/Logic/Order/PayProvider.php | 21 ++ .../Logic/User/Console/ConnectDelete.php | 26 +++ .../Logic/User/Console/ConnectList.php | 45 ++++ app/Services/OAuth.php | 107 +++++++++ app/{Library => Services}/OAuth/QQ.php | 43 ++-- app/{Library => Services}/OAuth/WeiBo.php | 32 +-- app/{Library => Services}/OAuth/WeiXin.php | 29 ++- app/Validators/Connect.php | 41 ++++ .../20201205091213_create_connect_table.php | 92 ++++++++ ...201205112717_insert_oauth_setting_data.php | 85 +++++++ public/static/home/css/common.css | 52 ++++- 35 files changed, 1475 insertions(+), 112 deletions(-) create mode 100644 app/Http/Admin/Views/setting/oauth.volt create mode 100644 app/Http/Admin/Views/setting/oauth_qq.volt create mode 100644 app/Http/Admin/Views/setting/oauth_weibo.volt create mode 100644 app/Http/Admin/Views/setting/oauth_weixin.volt create mode 100644 app/Http/Home/Controllers/ConnectController.php create mode 100644 app/Http/Home/Services/Connect.php create mode 100644 app/Http/Home/Views/connect/bind.volt create mode 100644 app/Http/Home/Views/connect/bind_login.volt create mode 100644 app/Http/Home/Views/connect/bind_register.volt delete mode 100644 app/Library/OAuth.php create mode 100644 app/Models/Connect.php create mode 100644 app/Repos/Connect.php create mode 100644 app/Services/Logic/Account/OAuthProvider.php create mode 100644 app/Services/Logic/Order/PayProvider.php create mode 100644 app/Services/Logic/User/Console/ConnectDelete.php create mode 100644 app/Services/Logic/User/Console/ConnectList.php create mode 100644 app/Services/OAuth.php rename app/{Library => Services}/OAuth/QQ.php (75%) rename app/{Library => Services}/OAuth/WeiBo.php (73%) rename app/{Library => Services}/OAuth/WeiXin.php (76%) create mode 100644 app/Validators/Connect.php create mode 100644 db/migrations/20201205091213_create_connect_table.php create mode 100644 db/migrations/20201205112717_insert_oauth_setting_data.php diff --git a/app/Http/Admin/Controllers/Controller.php b/app/Http/Admin/Controllers/Controller.php index 058bfa77..25899d50 100644 --- a/app/Http/Admin/Controllers/Controller.php +++ b/app/Http/Admin/Controllers/Controller.php @@ -21,6 +21,17 @@ class Controller extends \Phalcon\Mvc\Controller public function beforeExecuteRoute(Dispatcher $dispatcher) { + /** + * demo分支拒绝数据提交 + */ + if ($this->isNotSafeRequest()) { + $dispatcher->forward([ + 'controller' => 'public', + 'action' => 'forbidden', + ]); + return false; + } + if ($this->isNotSafeRequest()) { $this->checkHttpReferer(); $this->checkCsrfToken(); diff --git a/app/Http/Admin/Controllers/SettingController.php b/app/Http/Admin/Controllers/SettingController.php index 24c70aa3..71e41fc0 100644 --- a/app/Http/Admin/Controllers/SettingController.php +++ b/app/Http/Admin/Controllers/SettingController.php @@ -298,4 +298,33 @@ class SettingController extends Controller } } + /** + * @Route("/oauth", name="admin.setting.oauth") + */ + public function oauthAction() + { + $settingService = new SettingService(); + + if ($this->request->isPost()) { + + $section = $this->request->getPost('section', 'string'); + + $data = $this->request->getPost(); + + $settingService->updateSettings($section, $data); + + return $this->jsonSuccess(['msg' => '更新配置成功']); + + } else { + + $qqAuth = $settingService->getQQAuthSettings(); + $weixinAuth = $settingService->getWeixinAuthSettings(); + $weiboAuth = $settingService->getWeiboAuthSettings(); + + $this->view->setVar('qq_auth', $qqAuth); + $this->view->setVar('weixin_auth', $weixinAuth); + $this->view->setVar('weibo_auth', $weiboAuth); + } + } + } diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 43fb64fd..427c9924 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -744,6 +744,12 @@ class AuthNode extends Service 'type' => 'menu', 'route' => 'admin.setting.im', ], + [ + 'id' => '5-1-12', + 'title' => '开放登录', + 'type' => 'menu', + 'route' => 'admin.setting.oauth', + ], ], ], ], diff --git a/app/Http/Admin/Services/Setting.php b/app/Http/Admin/Services/Setting.php index b75f2f77..c04014ed 100644 --- a/app/Http/Admin/Services/Setting.php +++ b/app/Http/Admin/Services/Setting.php @@ -9,6 +9,33 @@ use App\Repos\Vip as VipRepo; class Setting extends Service { + public function getQQAuthSettings() + { + $oauth = $this->getSettings('oauth.qq'); + + $oauth['redirect_uri'] = $oauth['redirect_uri'] ?: kg_full_url(['for' => 'home.oauth.qq_callback']); + + return $oauth; + } + + public function getWeixinAuthSettings() + { + $oauth = $this->getSettings('oauth.weixin'); + + $oauth['redirect_uri'] = $oauth['redirect_uri'] ?: kg_full_url(['for' => 'home.oauth.weixin_callback']); + + return $oauth; + } + + public function getWeiboAuthSettings() + { + $oauth = $this->getSettings('oauth.weibo'); + + $oauth['redirect_uri'] = $oauth['redirect_uri'] ?: kg_full_url(['for' => 'home.oauth.weibo_callback']); + + return $oauth; + } + public function getAlipaySettings() { $alipay = $this->getSettings('pay.alipay'); @@ -59,8 +86,16 @@ class Setting extends Service $result = []; + /** + * demo分支过滤敏感数据 + */ if ($items->count() > 0) { foreach ($items as $item) { + $case1 = preg_match('/(id|auth|key|secret|password|pwd)$/', $item->item_key); + $case2 = $this->dispatcher->getControllerName() == 'setting'; + if ($case1 && $case2) { + $item->item_value = '***'; + } $result[$item->item_key] = $item->item_value; } } diff --git a/app/Http/Admin/Views/setting/oauth.volt b/app/Http/Admin/Views/setting/oauth.volt new file mode 100644 index 00000000..15b714b7 --- /dev/null +++ b/app/Http/Admin/Views/setting/oauth.volt @@ -0,0 +1,24 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + +
        +
          +
        • QQ登录
        • +
        • 微信登录
        • +
        • 新浪微博
        • +
        +
        +
        + {{ partial('setting/oauth_qq') }} +
        +
        + {{ partial('setting/oauth_weixin') }} +
        +
        + {{ partial('setting/oauth_weibo') }} +
        +
        +
        + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Admin/Views/setting/oauth_qq.volt b/app/Http/Admin/Views/setting/oauth_qq.volt new file mode 100644 index 00000000..2f0d2f9a --- /dev/null +++ b/app/Http/Admin/Views/setting/oauth_qq.volt @@ -0,0 +1,35 @@ +
        +
        + +
        + + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + + + +
        +
        +
        \ No newline at end of file diff --git a/app/Http/Admin/Views/setting/oauth_weibo.volt b/app/Http/Admin/Views/setting/oauth_weibo.volt new file mode 100644 index 00000000..c7ca6bae --- /dev/null +++ b/app/Http/Admin/Views/setting/oauth_weibo.volt @@ -0,0 +1,35 @@ +
        +
        + +
        + + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + + + +
        +
        +
        \ No newline at end of file diff --git a/app/Http/Admin/Views/setting/oauth_weixin.volt b/app/Http/Admin/Views/setting/oauth_weixin.volt new file mode 100644 index 00000000..4c08c355 --- /dev/null +++ b/app/Http/Admin/Views/setting/oauth_weixin.volt @@ -0,0 +1,35 @@ +
        +
        + +
        + + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + + + +
        +
        +
        \ No newline at end of file diff --git a/app/Http/Admin/Views/templates/main.volt b/app/Http/Admin/Views/templates/main.volt index 3c1345f1..8803b2a0 100644 --- a/app/Http/Admin/Views/templates/main.volt +++ b/app/Http/Admin/Views/templates/main.volt @@ -22,5 +22,12 @@ {% block include_js %}{% endblock %} {% block inline_js %}{% endblock %} + +{% set site = setting('site') %} + +{% if site['analytics_enabled'] == 1 %} + {{ site['analytics_script'] }} +{% endif %} + \ No newline at end of file diff --git a/app/Http/Home/Controllers/AccountController.php b/app/Http/Home/Controllers/AccountController.php index 8dc2d285..888deac1 100644 --- a/app/Http/Home/Controllers/AccountController.php +++ b/app/Http/Home/Controllers/AccountController.php @@ -4,6 +4,7 @@ namespace App\Http\Home\Controllers; use App\Http\Home\Services\Account as AccountService; use App\Services\Logic\Account\EmailUpdate as EmailUpdateService; +use App\Services\Logic\Account\OAuthProvider as OAuthProviderService; use App\Services\Logic\Account\PasswordReset as PasswordResetService; use App\Services\Logic\Account\PasswordUpdate as PasswordUpdateService; use App\Services\Logic\Account\PhoneUpdate as PhoneUpdateService; @@ -62,8 +63,13 @@ class AccountController extends Controller $captcha = $service->getSettings('captcha'); + $service = new OAuthProviderService(); + + $oauthProvider = $service->handle(); + $returnUrl = $this->request->getHTTPReferer(); + $this->view->setVar('oauth_provider', $oauthProvider); $this->view->setVar('return_url', $returnUrl); $this->view->setVar('captcha', $captcha); } diff --git a/app/Http/Home/Controllers/ConnectController.php b/app/Http/Home/Controllers/ConnectController.php new file mode 100644 index 00000000..49da1270 --- /dev/null +++ b/app/Http/Home/Controllers/ConnectController.php @@ -0,0 +1,139 @@ +getAuthorizeUrl(ConnectModel::PROVIDER_QQ); + + return $this->response->redirect($url, true); + } + + /** + * @Get("/weixin", name="home.oauth.weixin") + */ + public function weixinAction() + { + $service = new ConnectService(); + + $url = $service->getAuthorizeUrl(ConnectModel::PROVIDER_WEIXIN); + + return $this->response->redirect($url, true); + } + + /** + * @Get("/weibo", name="home.oauth.weibo") + */ + public function weiboAction() + { + $service = new ConnectService(); + + $url = $service->getAuthorizeUrl(ConnectModel::PROVIDER_WEIBO); + + return $this->response->redirect($url, true); + } + + /** + * @Get("/qq/callback", name="home.oauth.qq_callback") + */ + public function qqCallbackAction() + { + $this->handleCallback(ConnectModel::PROVIDER_QQ); + } + + /** + * @Get("/weixin/callback", name="home.oauth.weixin_callback") + */ + public function weixinCallbackAction() + { + $this->handleCallback(ConnectModel::PROVIDER_WEIXIN); + } + + /** + * @Get("/weibo/callback", name="home.oauth.weibo_callback") + */ + public function weiboCallbackAction() + { + $this->handleCallback(ConnectModel::PROVIDER_WEIBO); + } + + /** + * @Get("/weibo/refuse", name="home.oauth.weibo_refuse") + */ + public function weiboRefuseAction() + { + return $this->response->redirect(['for' => 'home.account.login']); + } + + /** + * @Post("/bind/login", name="home.oauth.bind_login") + */ + public function bindLoginAction() + { + $service = new ConnectService(); + + $service->bindLogin(); + + $location = $this->url->get(['for' => 'home.uc.account']); + + return $this->jsonSuccess(['location' => $location]); + } + + /** + * @Post("/bind/register", name="home.oauth.bind_register") + */ + public function bindRegisterAction() + { + $service = new ConnectService(); + + $service->bindRegister(); + + $location = $this->url->get(['for' => 'home.uc.account']); + + return $this->jsonSuccess(['location' => $location]); + } + + protected function handleCallback($provider) + { + $code = $this->request->getQuery('code'); + $state = $this->request->getQuery('state'); + + $service = new ConnectService(); + + $openUser = $service->getOpenUserInfo($code, $state, $provider); + + $connect = $service->getConnectRelation($openUser['id'], $provider); + + if ($connect && $connect->deleted == 0) { + if ($this->authUser->id > 0) { + $service->bindUser($openUser, $provider); + return $this->response->redirect(['for' => 'home.uc.account']); + } else { + $service->authLogin($connect); + return $this->response->redirect(['for' => 'home.index']); + } + } + + $captcha = $service->getSettings('captcha'); + + $this->view->pick('connect/bind'); + $this->view->setVar('captcha', $captcha); + $this->view->setVar('provider', $provider); + $this->view->setVar('open_user', $openUser); + } + +} diff --git a/app/Http/Home/Controllers/OrderController.php b/app/Http/Home/Controllers/OrderController.php index acf797a8..6cd6e310 100644 --- a/app/Http/Home/Controllers/OrderController.php +++ b/app/Http/Home/Controllers/OrderController.php @@ -7,6 +7,7 @@ use App\Services\Logic\Order\OrderCancel as OrderCancelService; use App\Services\Logic\Order\OrderConfirm as OrderConfirmService; use App\Services\Logic\Order\OrderCreate as OrderCreateService; use App\Services\Logic\Order\OrderInfo as OrderInfoService; +use App\Services\Logic\Order\PayProvider as PayProviderService; use Phalcon\Mvc\Dispatcher; use Phalcon\Mvc\View; @@ -82,6 +83,10 @@ class OrderController extends Controller { $sn = $this->request->getQuery('sn', 'string'); + $service = new PayProviderService(); + + $payProvider = $service->handle(); + $service = new OrderInfoService(); $order = $service->handle($sn); @@ -90,6 +95,7 @@ class OrderController extends Controller $this->response->redirect(['for' => 'home.uc.orders']); } + $this->view->setVar('pay_provider', $payProvider); $this->view->setVar('order', $order); } diff --git a/app/Http/Home/Controllers/UserConsoleController.php b/app/Http/Home/Controllers/UserConsoleController.php index 5e159e0e..2b56c475 100644 --- a/app/Http/Home/Controllers/UserConsoleController.php +++ b/app/Http/Home/Controllers/UserConsoleController.php @@ -2,7 +2,10 @@ namespace App\Http\Home\Controllers; +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; +use App\Services\Logic\User\Console\ConnectList as ConnectListService; use App\Services\Logic\User\Console\ConsultList as ConsultListService; use App\Services\Logic\User\Console\CourseList as CourseListService; use App\Services\Logic\User\Console\FavoriteList as FavoriteListService; @@ -59,13 +62,21 @@ class UserConsoleController extends Controller */ public function accountAction() { + $type = $this->request->getQuery('type', 'string', 'info'); + $service = new AccountInfoService(); $captcha = $service->getSettings('captcha'); $account = $service->handle(); - $type = $this->request->getQuery('type', 'string', 'info'); + $service = new OAuthProviderService(); + + $oauthProvider = $service->handle(); + + $service = new ConnectListService(); + + $connects = $service->handle(); if ($type == 'info') { $this->view->pick('user/console/account_info'); @@ -77,6 +88,8 @@ class UserConsoleController extends Controller $this->view->pick('user/console/account_password'); } + $this->view->setVar('oauth_provider', $oauthProvider); + $this->view->setVar('connects', $connects); $this->view->setVar('captcha', $captcha); $this->view->setVar('account', $account); } @@ -207,4 +220,23 @@ class UserConsoleController extends Controller return $this->jsonSuccess($content); } + /** + * @Post("/connect/{id:[0-9]+}/delete", name="home.uc.unconnect") + */ + public function deleteConnectAction($id) + { + $service = new ConnectDeleteService(); + + $service->handle($id); + + $location = $this->url->get(['for' => 'home.uc.account']); + + $content = [ + 'location' => $location, + 'msg' => '解除登录绑定成功', + ]; + + return $this->jsonSuccess($content); + } + } diff --git a/app/Http/Home/Services/Connect.php b/app/Http/Home/Services/Connect.php new file mode 100644 index 00000000..15816c1f --- /dev/null +++ b/app/Http/Home/Services/Connect.php @@ -0,0 +1,208 @@ +request->getPost(); + + $auth = $this->getConnectAuth($post['provider']); + + $auth->checkState($post['state']); + + $validator = new AccountValidator(); + + $user = $validator->checkUserLogin($post['account'], $post['password']); + + $openUser = json_decode($post['open_user'], true); + + $this->handleConnectRelation($user, $openUser, $post['provider']); + + $auth = $this->getAppAuth(); + + $auth->saveAuthInfo($user); + } + + public function bindRegister() + { + $post = $this->request->getPost(); + + $auth = $this->getConnectAuth($post['provider']); + + $auth->checkState($post['state']); + + $openUser = json_decode($post['open_user'], true); + + $registerService = new RegisterService(); + + $account = $registerService->handle(); + + $userRepo = new UserRepo(); + + $user = $userRepo->findById($account->id); + + $this->handleConnectRelation($user, $openUser, $post['provider']); + + $auth = $this->getAppAuth(); + + $auth->saveAuthInfo($user); + } + + public function bindUser($openUser, $provider) + { + $user = $this->getLoginUser(); + + $this->handleConnectRelation($user, $openUser, $provider); + } + + public function authLogin(ConnectModel $connect) + { + $userRepo = new UserRepo(); + + $user = $userRepo->findById($connect->user_id); + + $auth = $this->getAppAuth(); + + $auth->saveAuthInfo($user); + } + + public function getAuthorizeUrl($provider) + { + $auth = $this->getConnectAuth($provider); + + return $auth->getAuthorizeUrl(); + } + + public function getOpenUserInfo($code, $state, $provider) + { + $auth = $this->getConnectAuth($provider); + + $auth->checkState($state); + + $token = $auth->getAccessToken($code); + + $openId = $auth->getOpenId($token); + + return $auth->getUserInfo($token, $openId); + } + + public function getConnectRelation($openId, $provider) + { + $connectRepo = new ConnectRepo(); + + return $connectRepo->findByOpenId($openId, $provider); + } + + public function getConnectAuth($provider) + { + $auth = null; + + switch ($provider) { + case ConnectModel::PROVIDER_QQ: + $auth = $this->getQQAuth(); + break; + case ConnectModel::PROVIDER_WEIXIN: + $auth = $this->getWeiXinAuth(); + break; + case ConnectModel::PROVIDER_WEIBO: + $auth = $this->getWeiBoAuth(); + break; + } + + if (!$auth) { + throw new \Exception('Invalid OAuth Provider'); + } + + return $auth; + } + + protected function getQQAuth() + { + $settings = $this->getSettings('oauth.qq'); + + return new QQAuth( + $settings['client_id'], + $settings['client_secret'], + $settings['redirect_uri'] + ); + } + + protected function getWeiXinAuth() + { + $settings = $this->getSettings('oauth.weixin'); + + return new WeiXinAuth( + $settings['client_id'], + $settings['client_secret'], + $settings['redirect_uri'] + ); + } + + protected function getWeiBoAuth() + { + $settings = $this->getSettings('oauth.weibo'); + + return new WeiBoAuth( + $settings['client_id'], + $settings['client_secret'], + $settings['redirect_uri'] + ); + } + + protected function getAppAuth() + { + /** + * @var $auth AuthService + */ + $auth = $this->getDI()->get('auth'); + + return $auth; + } + + protected function handleConnectRelation(UserModel $user, array $openUser, $provider) + { + $connectRepo = new ConnectRepo(); + + $connect = $connectRepo->findByOpenId($openUser['id'], $provider); + + if ($connect) { + + if (time() - $connect->update_time > 86400) { + $connect->open_name = $openUser['name']; + $connect->open_avatar = $openUser['avatar']; + } + + if ($connect->deleted == 1) { + $connect->deleted = 0; + $connect->update(); + } + + } else { + + $connect = new ConnectModel(); + + $connect->user_id = $user->id; + $connect->open_id = $openUser['id']; + $connect->open_name = $openUser['name']; + $connect->open_avatar = $openUser['avatar']; + $connect->provider = $provider; + + $connect->create(); + } + } + +} diff --git a/app/Http/Home/Views/account/login.volt b/app/Http/Home/Views/account/login.volt index edd461b6..34641c28 100644 --- a/app/Http/Home/Views/account/login.volt +++ b/app/Http/Home/Views/account/login.volt @@ -27,6 +27,17 @@ · 忘记密码
        +
        + {% if oauth_provider.qq.enabled == 1 %} + + {% endif %} + {% if oauth_provider.weixin.enabled == 1 %} + + {% endif %} + {% if oauth_provider.weibo.enabled == 1 %} + + {% endif %} +
        {% endblock %} diff --git a/app/Http/Home/Views/connect/bind.volt b/app/Http/Home/Views/connect/bind.volt new file mode 100644 index 00000000..ecb9d499 --- /dev/null +++ b/app/Http/Home/Views/connect/bind.volt @@ -0,0 +1,34 @@ +{% extends 'templates/main.volt' %} + +{% block content %} + + + + + +{% endblock %} + +{% block include_js %} + + {{ js_include('https://ssl.captcha.qq.com/TCaptcha.js',false) }} + {{ js_include('home/js/captcha.verify.js') }} + +{% endblock %} \ No newline at end of file diff --git a/app/Http/Home/Views/connect/bind_login.volt b/app/Http/Home/Views/connect/bind_login.volt new file mode 100644 index 00000000..abec6bfd --- /dev/null +++ b/app/Http/Home/Views/connect/bind_login.volt @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/app/Http/Home/Views/connect/bind_register.volt b/app/Http/Home/Views/connect/bind_register.volt new file mode 100644 index 00000000..87208750 --- /dev/null +++ b/app/Http/Home/Views/connect/bind_register.volt @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/app/Http/Home/Views/order/pay.volt b/app/Http/Home/Views/order/pay.volt index 16bca937..5e3a0a88 100644 --- a/app/Http/Home/Views/order/pay.volt +++ b/app/Http/Home/Views/order/pay.volt @@ -17,8 +17,12 @@ 支付金额:{{ '¥%0.2f'|format(order.amount) }}
        - {{ image('home/img/alipay.png') }} - {{ image('home/img/wxpay.png') }} + {% if pay_provider.alipay.enabled == 1 %} + {{ image('home/img/alipay.png') }} + {% endif %} + {% if pay_provider.wxpay.enabled == 1 %} + {{ image('home/img/wxpay.png') }} + {% endif %}
        +
        + 开放登录 +
        + {% if connects %} +
        已经绑定的第三方帐号
        +
        + + + + + + + + + {% for connect in connects %} + {% set url = url({'for':'home.uc.unconnect','id':connect.id}) %} + + + + + + + + {% endfor %} +
        序号提供方用户信息创建日期操作
        {{ loop.index }}{{ connect_provider(connect) }}{{ connect_user(connect) }}{{ date('Y-m-d H:i',connect.create_time) }}解除绑定
        +
        + {% endif %} +
        支持绑定的第三方帐号
        +
        + {% if oauth_provider.qq.enabled == 1 %} + + {% endif %} + {% if oauth_provider.qq.enabled == 1 %} + + {% endif %} + {% if oauth_provider.qq.enabled == 1 %} + + {% endif %} +
        diff --git a/app/Library/OAuth.php b/app/Library/OAuth.php deleted file mode 100644 index 49879bc6..00000000 --- a/app/Library/OAuth.php +++ /dev/null @@ -1,53 +0,0 @@ -appId = $appId; - $this->appSecret = $appSecret; - $this->redirectUri = $redirectUri; - } - - public function httpGet($uri, $params = [], $headers = []) - { - $client = new HttpClient(); - - $options = ['query' => $params, 'headers' => $headers]; - - $response = $client->get($uri, $options); - - return $response->getBody(); - } - - public function httpPost($uri, $params = [], $headers = []) - { - $client = new HttpClient(); - - $options = ['query' => $params, 'headers' => $headers]; - - $response = $client->post($uri, $options); - - return $response->getBody(); - } - - abstract public function getAuthorizeUrl(); - - abstract public function getAccessToken($code); - - abstract public function getOpenId($accessToken); - - abstract public function getUserInfo($accessToken, $openId); - -} diff --git a/app/Models/Connect.php b/app/Models/Connect.php new file mode 100644 index 00000000..4266e805 --- /dev/null +++ b/app/Models/Connect.php @@ -0,0 +1,104 @@ +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/Connect.php b/app/Repos/Connect.php new file mode 100644 index 00000000..e32c88a1 --- /dev/null +++ b/app/Repos/Connect.php @@ -0,0 +1,62 @@ +where('1 = 1'); + + if (isset($where['user_id'])) { + $query->andWhere('user_id = :user_id:', ['user_id' => $where['user_id']]); + } + + if (isset($where['provider'])) { + $query->andWhere('provider = :provider:', ['provider' => $where['provider']]); + } + + if (isset($where['deleted'])) { + $query->andWhere('deleted = :deleted:', ['deleted' => $where['deleted']]); + } + + $query->orderBy('id DESC'); + + return $query->execute(); + } + + /** + * @param int $id + * @return ConnectModel|Model|bool + */ + public function findById($id) + { + return ConnectModel::findFirst($id); + } + + /** + * @param string $openId + * @param int $provider + * @return ConnectModel|Model|bool + */ + public function findByOpenId($openId, $provider) + { + return ConnectModel::findFirst([ + 'conditions' => 'open_id = ?1 and provider = ?2', + 'bind' => [1 => $openId, 2 => $provider], + ]); + } + +} diff --git a/app/Services/Logic/Account/OAuthProvider.php b/app/Services/Logic/Account/OAuthProvider.php new file mode 100644 index 00000000..79a9cd2d --- /dev/null +++ b/app/Services/Logic/Account/OAuthProvider.php @@ -0,0 +1,23 @@ +getSettings('oauth.weixin'); + $weibo = $this->getSettings('oauth.weibo'); + $qq = $this->getSettings('oauth.qq'); + + return [ + 'weixin' => ['enabled' => $weixin['enabled']], + 'weibo' => ['enabled' => $weibo['enabled']], + 'qq' => ['enabled' => $qq['enabled']], + ]; + } + +} diff --git a/app/Services/Logic/Order/PayProvider.php b/app/Services/Logic/Order/PayProvider.php new file mode 100644 index 00000000..a8271845 --- /dev/null +++ b/app/Services/Logic/Order/PayProvider.php @@ -0,0 +1,21 @@ +getSettings('pay.alipay'); + $wxpay = $this->getSettings('pay.wxpay'); + + return [ + 'alipay' => ['enabled' => $alipay['enabled']], + 'wxpay' => ['enabled' => $wxpay['enabled']], + ]; + } + +} diff --git a/app/Services/Logic/User/Console/ConnectDelete.php b/app/Services/Logic/User/Console/ConnectDelete.php new file mode 100644 index 00000000..27d60489 --- /dev/null +++ b/app/Services/Logic/User/Console/ConnectDelete.php @@ -0,0 +1,26 @@ +getLoginUser(); + + $validator = new ConnectValidator(); + + $connect = $validator->checkConnect($id); + + $validator->checkOwner($user->id, $connect->user_id); + + $connect->deleted = 1; + + $connect->update(); + } + +} diff --git a/app/Services/Logic/User/Console/ConnectList.php b/app/Services/Logic/User/Console/ConnectList.php new file mode 100644 index 00000000..7c930e89 --- /dev/null +++ b/app/Services/Logic/User/Console/ConnectList.php @@ -0,0 +1,45 @@ +getLoginUser(); + + $params = [ + 'user_id' => $user->id, + 'deleted' => 0, + ]; + + $connectRepo = new ConnectRepo(); + + $connects = $connectRepo->findAll($params); + + if ($connects->count() == 0) { + return []; + } + + $items = []; + + foreach ($connects as $connect) { + $items[] = [ + 'id' => $connect->id, + 'open_id' => $connect->open_id, + 'open_name' => $connect->open_name, + 'open_avatar' => $connect->open_avatar, + 'provider' => $connect->provider, + 'create_time' => $connect->create_time, + 'update_time' => $connect->update_time, + ]; + } + + return $items; + } + +} diff --git a/app/Services/OAuth.php b/app/Services/OAuth.php new file mode 100644 index 00000000..43aeefb0 --- /dev/null +++ b/app/Services/OAuth.php @@ -0,0 +1,107 @@ +clientId = $clientId; + $this->clientSecret = $clientSecret; + $this->redirectUri = $redirectUri; + } + + public function httpGet($uri, $params = [], $headers = []) + { + $client = new HttpClient(); + + $options = ['query' => $params, 'headers' => $headers]; + + $response = $client->get($uri, $options); + + return $response->getBody(); + } + + public function httpPost($uri, $params = [], $headers = []) + { + $client = new HttpClient(); + + $options = ['query' => $params, 'headers' => $headers]; + + $response = $client->post($uri, $options); + + return $response->getBody(); + } + + public function getState() + { + /** + * @var $crypt Crypt + */ + $crypt = Di::getDefault()->get('crypt'); + + return $crypt->encryptBase64(rand(1000, 9999)); + } + + public function checkState($state) + { + /** + * 注意事项: + * callback中的state参数并未做encode处理,参数中含有"+" + * 获取参数的时候却自动做了decode处理,"+"变成了空格 + */ + $state = str_replace(' ', '+', $state); + + /** + * @var $crypt Crypt + */ + $crypt = Di::getDefault()->get('crypt'); + + $value = $crypt->decryptBase64($state); + + if ($value < 1000 || $value > 9999) { + throw new \Exception('Invalid OAuth State Value'); + } + + return true; + } + + abstract public function getAuthorizeUrl(); + + abstract public function getAccessToken($code); + + abstract public function getOpenId($accessToken); + + abstract public function getUserInfo($accessToken, $openId); + +} diff --git a/app/Library/OAuth/QQ.php b/app/Services/OAuth/QQ.php similarity index 75% rename from app/Library/OAuth/QQ.php rename to app/Services/OAuth/QQ.php index 3515a0d9..c166b8e5 100644 --- a/app/Library/OAuth/QQ.php +++ b/app/Services/OAuth/QQ.php @@ -1,8 +1,8 @@ $this->appId, + 'client_id' => $this->clientId, 'redirect_uri' => $this->redirectUri, + 'state' => $this->getState(), 'response_type' => 'code', - 'scope' => '', + 'scope' => 'get_user_info', ]; return self::AUTHORIZE_URL . '?' . http_build_query($params); @@ -28,11 +29,10 @@ class QQ extends OAuth { $params = [ 'code' => $code, - 'client_id' => $this->appId, - 'client_secret' => $this->appSecret, + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, 'redirect_uri' => $this->redirectUri, 'grant_type' => 'authorization_code', - 'state' => 'ok', ]; $response = $this->httpPost(self::ACCESS_TOKEN_URL, $params); @@ -56,14 +56,14 @@ class QQ extends OAuth public function getUserInfo($accessToken, $openId) { $params = [ + 'oauth_consumer_key' => $this->clientId, 'access_token' => $accessToken, 'openid' => $openId, - 'oauth_consumer_key' => $this->appId, ]; $response = $this->httpGet(self::USER_INFO_URL, $params); - - $this->parseUserInfo($response); + + return $this->parseUserInfo($response); } protected function parseAccessToken($response) @@ -81,15 +81,15 @@ class QQ extends OAuth protected function parseOpenId($response) { - $result = $match = []; + $result = $matches = []; if (!empty($response)) { - preg_match('/callback\(\s+(.*?)\s+\)/i', $response, $match); - $result = json_decode($match[1], true); + preg_match('/callback\(\s+(.*?)\s+\)/i', $response, $matches); + $result = json_decode($matches[1], true); } if (!isset($result['openid'])) { - throw new \Exception("Fetch Openid Failed:{$response}"); + throw new \Exception("Fetch OpenId Failed:{$response}"); } return $result['openid']; @@ -98,16 +98,15 @@ class QQ extends OAuth protected function parseUserInfo($response) { $data = json_decode($response, true); - - if ($data['ret'] != 0) { - throw new \Exception("Fetch User Info Failed:{$data['msg']}"); + + if (isset($data['ret']) && $data['ret'] != 0) { + throw new \Exception("Fetch User Info Failed:{$response}"); } - - $userInfo['type'] = 'QQ'; + + $userInfo['id'] = $this->openId; $userInfo['name'] = $data['nickname']; - $userInfo['nick'] = $data['nickname']; - $userInfo['head'] = $data['figureurl_2']; - + $userInfo['avatar'] = $data['figureurl']; + return $userInfo; } diff --git a/app/Library/OAuth/WeiBo.php b/app/Services/OAuth/WeiBo.php similarity index 73% rename from app/Library/OAuth/WeiBo.php rename to app/Services/OAuth/WeiBo.php index b7bc79ac..a8c7bb48 100644 --- a/app/Library/OAuth/WeiBo.php +++ b/app/Services/OAuth/WeiBo.php @@ -1,21 +1,22 @@ $this->appId, + 'client_id' => $this->clientId, 'redirect_uri' => $this->redirectUri, + 'state' => $this->getState(), 'response_type' => 'code', ]; @@ -26,8 +27,8 @@ class WeiBo extends OAuth { $params = [ 'code' => $code, - 'client_id' => $this->appId, - 'client_secret' => $this->appSecret, + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, 'redirect_uri' => $this->redirectUri, 'grant_type' => 'authorization_code', ]; @@ -59,8 +60,8 @@ class WeiBo extends OAuth private function parseAccessToken($response) { $data = json_decode($response, true); - - if (!isset($data['access_token']) || isset($data['uid'])) { + + if (!isset($data['access_token']) || !isset($data['uid'])) { throw new \Exception("Fetch Access Token Failed:{$response}"); } @@ -72,16 +73,15 @@ class WeiBo extends OAuth private function parseUserInfo($response) { $data = json_decode($response, true); - - if ($data['error_code'] != 0) { - throw new \Exception("Fetch User Info Failed:{$data['error']}"); + + if (isset($data['error_code']) && $data['error_code'] != 0) { + throw new \Exception("Fetch User Info Failed:{$response}"); } - - $userInfo['type'] = 'WEIBO'; + + $userInfo['id'] = $data['id']; $userInfo['name'] = $data['name']; - $userInfo['nick'] = $data['screen_name']; - $userInfo['head'] = $data['avatar_large']; - + $userInfo['avatar'] = $data['profile_image_url']; + return $userInfo; } diff --git a/app/Library/OAuth/WeiXin.php b/app/Services/OAuth/WeiXin.php similarity index 76% rename from app/Library/OAuth/WeiXin.php rename to app/Services/OAuth/WeiXin.php index 4d741276..df371797 100644 --- a/app/Library/OAuth/WeiXin.php +++ b/app/Services/OAuth/WeiXin.php @@ -1,8 +1,8 @@ $this->appId, + 'appid' => $this->clientId, 'redirect_uri' => $this->redirectUri, + 'state' => $this->getState(), 'response_type' => 'code', 'scope' => 'snsapi_login', - 'state' => 'dev', ]; return self::AUTHORIZE_URL . '?' . http_build_query($params); @@ -28,8 +28,8 @@ class WeiXin extends OAuth { $params = [ 'code' => $code, - 'appid' => $this->appId, - 'secret' => $this->appSecret, + 'appid' => $this->clientId, + 'secret' => $this->clientSecret, 'grant_type' => 'authorization_code', ]; @@ -62,7 +62,7 @@ class WeiXin extends OAuth $data = json_decode($response, true); if (isset($data['errcode']) && $data['errcode'] != 0) { - throw new \Exception("Fetch Access Token Failed:{$data['errmsg']}"); + throw new \Exception("Fetch Access Token Failed:{$response}"); } $this->openId = $data['openid']; @@ -73,16 +73,15 @@ class WeiXin extends OAuth private function parseUserInfo($response) { $data = json_decode($response, true); - + if (isset($data['errcode']) && $data['errcode'] != 0) { - throw new \Exception("Fetch User Info Failed:{$data['errmsg']}"); + throw new \Exception("Fetch User Info Failed:{$response}"); } - - $userInfo['type'] = 'WEIXIN'; - $userInfo['name'] = $data['name']; - $userInfo['nick'] = $data['screen_name']; - $userInfo['head'] = $data['avatar_large']; - + + $userInfo['id'] = $data['openid']; + $userInfo['name'] = $data['nickname']; + $userInfo['avatar'] = $data['headimgurl']; + return $userInfo; } diff --git a/app/Validators/Connect.php b/app/Validators/Connect.php new file mode 100644 index 00000000..cdcf4410 --- /dev/null +++ b/app/Validators/Connect.php @@ -0,0 +1,41 @@ +checkConnectById($id); + } + + public function checkConnectById($id) + { + $connectRepo = new ConnectRepo(); + + $connect = $connectRepo->findById($id); + + if (!$connect) { + throw new BadRequestException('connect.not_found'); + } + + return $connect; + } + + public function checkConnectByOpenId($openId, $provider) + { + $connectRepo = new ConnectRepo(); + + $connect = $connectRepo->findByOpenId($openId, $provider); + + if (!$connect) { + throw new BadRequestException('connect.not_found'); + } + + return $connect; + } +} diff --git a/db/migrations/20201205091213_create_connect_table.php b/db/migrations/20201205091213_create_connect_table.php new file mode 100644 index 00000000..53041625 --- /dev/null +++ b/db/migrations/20201205091213_create_connect_table.php @@ -0,0 +1,92 @@ +table('kg_connect', [ + '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('open_name', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 30, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '开放名称', + 'after' => 'open_id', + ]) + ->addColumn('open_avatar', 'string', [ + 'null' => false, + 'default' => '', + 'limit' => 150, + 'collation' => 'utf8mb4_general_ci', + 'encoding' => 'utf8mb4', + 'comment' => '开放头像', + 'after' => 'open_name', + ]) + ->addColumn('provider', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '提供方', + 'after' => 'open_avatar', + ]) + ->addColumn('deleted', 'integer', [ + 'null' => false, + 'default' => '0', + 'limit' => MysqlAdapter::INT_REGULAR, + 'comment' => '删除标识', + 'after' => 'provider', + ]) + ->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', 'provider'], [ + 'name' => 'openid_provider', + 'unique' => false, + ]) + ->create(); + } +} diff --git a/db/migrations/20201205112717_insert_oauth_setting_data.php b/db/migrations/20201205112717_insert_oauth_setting_data.php new file mode 100644 index 00000000..40cfe6f2 --- /dev/null +++ b/db/migrations/20201205112717_insert_oauth_setting_data.php @@ -0,0 +1,85 @@ + 'oauth.qq', + 'item_key' => 'enabled', + 'item_value' => '0', + ], + [ + 'section' => 'oauth.qq', + 'item_key' => 'client_id', + 'item_value' => '', + ], + [ + 'section' => 'oauth.qq', + 'item_key' => 'client_secret', + 'item_value' => '', + ], + [ + 'section' => 'oauth.qq', + 'item_key' => 'redirect_uri', + 'item_value' => '', + ], + [ + 'section' => 'oauth.weixin', + 'item_key' => 'enabled', + 'item_value' => '0', + ], + [ + 'section' => 'oauth.weixin', + 'item_key' => 'client_id', + 'item_value' => '', + ], + [ + 'section' => 'oauth.weixin', + 'item_key' => 'client_secret', + 'item_value' => '', + ], + [ + 'section' => 'oauth.weixin', + 'item_key' => 'redirect_uri', + 'item_value' => '', + ], + [ + 'section' => 'oauth.weibo', + 'item_key' => 'enabled', + 'item_value' => '0', + ], + [ + 'section' => 'oauth.weibo', + 'item_key' => 'client_id', + 'item_value' => '', + ], + [ + 'section' => 'oauth.weibo', + 'item_key' => 'client_secret', + 'item_value' => '', + ], + [ + 'section' => 'oauth.weibo', + 'item_key' => 'redirect_uri', + 'item_value' => '', + ], + ]; + + $this->table('kg_setting')->insert($rows)->save(); + } + + public function down() + { + $this->execute("DELETE FROM kg_setting WHERE section = 'oauth.qq'"); + $this->execute("DELETE FROM kg_setting WHERE section = 'oauth.weixin'"); + $this->execute("DELETE FROM kg_setting WHERE section = 'oauth.weibo'"); + } + +} \ No newline at end of file diff --git a/public/static/home/css/common.css b/public/static/home/css/common.css index a78ec345..bf8478c5 100644 --- a/public/static/home/css/common.css +++ b/public/static/home/css/common.css @@ -1188,7 +1188,7 @@ } .login-wrap .link { - margin-bottom: 30px; + margin-bottom: 20px; font-size: 12px; text-align: center; } @@ -1202,6 +1202,26 @@ color: #999; } +.login-wrap .oauth { + text-align: center; +} + +.login-wrap .oauth a { + margin: 0 10px; +} + +.login-qq { + color: dodgerblue; +} + +.login-wechat { + color: green; +} + +.login-weibo { + color: red; +} + .user-profile { position: relative; } @@ -1537,9 +1557,12 @@ margin-right: 0; } +.security-item-list { + margin-top: -20px; +} + .security-item { - padding: 15px; - line-height: 50px; + line-height: 80px; border-bottom: 1px dashed #ccc; } @@ -1563,6 +1586,29 @@ float: right; } +.connect-list { + margin-bottom: 20px; +} + +.open-avatar img { + width: 16px; + height: 16px; + border-radius: 100%; +} + +.connect-tips { + color: #666; + margin-bottom: 20px; +} + +.oauth-list { + margin-bottom: 20px; +} + +.oauth-list a { + margin: 0 10px; +} + .order-filter { padding: 15px 20px; } From 841593b5a7df2c746bd03dae41fa5473edc68b97 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Tue, 8 Dec 2020 17:10:07 +0800 Subject: [PATCH 29/44] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E7=99=BB=E5=BD=95=EF=BC=8C=E4=BF=AE=E5=A4=8D=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E5=AF=86=E7=A0=81=E5=8A=A0=E5=AF=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Controllers/Controller.php | 11 ------- app/Http/Admin/Services/Setting.php | 8 ----- app/Http/Admin/Views/templates/main.volt | 6 ---- .../Home/Controllers/ConnectController.php | 5 ++-- app/Http/Home/Services/Connect.php | 25 +++++++++------- .../Home/Views/user/console/account_info.volt | 2 +- app/Http/Home/Views/user/show.volt | 6 ++-- app/Services/Logic/Account/Register.php | 5 ++++ app/Services/OAuth/QQ.php | 30 ++++++++++--------- app/Services/OAuth/WeiBo.php | 18 ++++++----- app/Services/OAuth/WeiXin.php | 20 +++++++------ 11 files changed, 62 insertions(+), 74 deletions(-) diff --git a/app/Http/Admin/Controllers/Controller.php b/app/Http/Admin/Controllers/Controller.php index 25899d50..058bfa77 100644 --- a/app/Http/Admin/Controllers/Controller.php +++ b/app/Http/Admin/Controllers/Controller.php @@ -21,17 +21,6 @@ class Controller extends \Phalcon\Mvc\Controller public function beforeExecuteRoute(Dispatcher $dispatcher) { - /** - * demo分支拒绝数据提交 - */ - if ($this->isNotSafeRequest()) { - $dispatcher->forward([ - 'controller' => 'public', - 'action' => 'forbidden', - ]); - return false; - } - if ($this->isNotSafeRequest()) { $this->checkHttpReferer(); $this->checkCsrfToken(); diff --git a/app/Http/Admin/Services/Setting.php b/app/Http/Admin/Services/Setting.php index c04014ed..45c11719 100644 --- a/app/Http/Admin/Services/Setting.php +++ b/app/Http/Admin/Services/Setting.php @@ -86,16 +86,8 @@ class Setting extends Service $result = []; - /** - * demo分支过滤敏感数据 - */ if ($items->count() > 0) { foreach ($items as $item) { - $case1 = preg_match('/(id|auth|key|secret|password|pwd)$/', $item->item_key); - $case2 = $this->dispatcher->getControllerName() == 'setting'; - if ($case1 && $case2) { - $item->item_value = '***'; - } $result[$item->item_key] = $item->item_value; } } diff --git a/app/Http/Admin/Views/templates/main.volt b/app/Http/Admin/Views/templates/main.volt index 8803b2a0..6b70ff60 100644 --- a/app/Http/Admin/Views/templates/main.volt +++ b/app/Http/Admin/Views/templates/main.volt @@ -23,11 +23,5 @@ {% block include_js %}{% endblock %} {% block inline_js %}{% endblock %} -{% set site = setting('site') %} - -{% if site['analytics_enabled'] == 1 %} - {{ site['analytics_script'] }} -{% endif %} - \ No newline at end of file diff --git a/app/Http/Home/Controllers/ConnectController.php b/app/Http/Home/Controllers/ConnectController.php index 49da1270..bfe468ee 100644 --- a/app/Http/Home/Controllers/ConnectController.php +++ b/app/Http/Home/Controllers/ConnectController.php @@ -115,12 +115,11 @@ class ConnectController extends Controller $service = new ConnectService(); $openUser = $service->getOpenUserInfo($code, $state, $provider); - - $connect = $service->getConnectRelation($openUser['id'], $provider); + $connect = $service->getConnectRelation($openUser['id'], $openUser['provider']); if ($connect && $connect->deleted == 0) { if ($this->authUser->id > 0) { - $service->bindUser($openUser, $provider); + $service->bindUser($openUser); return $this->response->redirect(['for' => 'home.uc.account']); } else { $service->authLogin($connect); diff --git a/app/Http/Home/Services/Connect.php b/app/Http/Home/Services/Connect.php index 15816c1f..0cef8358 100644 --- a/app/Http/Home/Services/Connect.php +++ b/app/Http/Home/Services/Connect.php @@ -30,7 +30,7 @@ class Connect extends Service $openUser = json_decode($post['open_user'], true); - $this->handleConnectRelation($user, $openUser, $post['provider']); + $this->handleConnectRelation($user, $openUser); $auth = $this->getAppAuth(); @@ -55,18 +55,18 @@ class Connect extends Service $user = $userRepo->findById($account->id); - $this->handleConnectRelation($user, $openUser, $post['provider']); + $this->handleConnectRelation($user, $openUser); $auth = $this->getAppAuth(); $auth->saveAuthInfo($user); } - public function bindUser($openUser, $provider) + public function bindUser(array $openUser) { $user = $this->getLoginUser(); - $this->handleConnectRelation($user, $openUser, $provider); + $this->handleConnectRelation($user, $openUser); } public function authLogin(ConnectModel $connect) @@ -173,24 +173,27 @@ class Connect extends Service return $auth; } - protected function handleConnectRelation(UserModel $user, array $openUser, $provider) + protected function handleConnectRelation(UserModel $user, array $openUser) { $connectRepo = new ConnectRepo(); - $connect = $connectRepo->findByOpenId($openUser['id'], $provider); + $connect = $connectRepo->findByOpenId($openUser['id'], $openUser['provider']); if ($connect) { - if (time() - $connect->update_time > 86400) { - $connect->open_name = $openUser['name']; - $connect->open_avatar = $openUser['avatar']; + $connect->open_name = $openUser['name']; + $connect->open_avatar = $openUser['avatar']; + + if ($connect->user_id != $user->id) { + $connect->user_id = $user->id; } if ($connect->deleted == 1) { $connect->deleted = 0; - $connect->update(); } + $connect->update(); + } else { $connect = new ConnectModel(); @@ -199,7 +202,7 @@ class Connect extends Service $connect->open_id = $openUser['id']; $connect->open_name = $openUser['name']; $connect->open_avatar = $openUser['avatar']; - $connect->provider = $provider; + $connect->provider = $openUser['provider']; $connect->create(); } diff --git a/app/Http/Home/Views/user/console/account_info.volt b/app/Http/Home/Views/user/console/account_info.volt index 5b6b3e22..89a6f9b6 100644 --- a/app/Http/Home/Views/user/console/account_info.volt +++ b/app/Http/Home/Views/user/console/account_info.volt @@ -51,7 +51,7 @@
        邮箱绑定 - {% if account.phone %} + {% if account.email %} 已绑定邮箱:{{ account.email|anonymous }} 修改 {% else %} diff --git a/app/Http/Home/Views/user/show.volt b/app/Http/Home/Views/user/show.volt index 80f762f0..f8c616e4 100644 --- a/app/Http/Home/Views/user/show.volt +++ b/app/Http/Home/Views/user/show.volt @@ -7,6 +7,8 @@ {% set full_user_url = full_url({'for':'home.user.show','id':user.id}) %} {% set qrcode_url = url({'for':'home.qrcode'},{'text':full_user_url}) %} + {% set user.area = user.area ? user.area : '火星' %} + {% set user.about = user.about ? user.about : '这个家伙很懒,什么都没留下!' %} - {% if user.about %} -
        {{ user.about }}
        - {% endif %} +
        {{ user.about }}
        {% set show_tab_courses = user.course_count > 0 %} diff --git a/app/Services/Logic/Account/Register.php b/app/Services/Logic/Account/Register.php index 1d5a79f8..ade1df09 100644 --- a/app/Services/Logic/Account/Register.php +++ b/app/Services/Logic/Account/Register.php @@ -2,6 +2,7 @@ namespace App\Services\Logic\Account; +use App\Library\Utils\Password as PasswordUtil; use App\Library\Validators\Common as CommonValidator; use App\Models\Account as AccountModel; use App\Models\ImUser as ImUserModel; @@ -42,6 +43,10 @@ class Register extends Service $data['password'] = $accountValidator->checkPassword($post['password']); + $data['salt'] = PasswordUtil::salt(); + + $data['password'] = PasswordUtil::hash($data['password'], $data['salt']); + try { $this->db->begin(); diff --git a/app/Services/OAuth/QQ.php b/app/Services/OAuth/QQ.php index c166b8e5..2c59f144 100644 --- a/app/Services/OAuth/QQ.php +++ b/app/Services/OAuth/QQ.php @@ -2,6 +2,7 @@ namespace App\Services\OAuth; +use App\Models\Connect as ConnectModel; use App\Services\OAuth; class QQ extends OAuth @@ -21,7 +22,7 @@ class QQ extends OAuth 'response_type' => 'code', 'scope' => 'get_user_info', ]; - + return self::AUTHORIZE_URL . '?' . http_build_query($params); } @@ -34,22 +35,22 @@ class QQ extends OAuth 'redirect_uri' => $this->redirectUri, 'grant_type' => 'authorization_code', ]; - + $response = $this->httpPost(self::ACCESS_TOKEN_URL, $params); - + $this->accessToken = $this->parseAccessToken($response); - + return $this->accessToken; } public function getOpenId($accessToken) { $params = ['access_token' => $accessToken]; - + $response = $this->httpGet(self::OPENID_URL, $params); - + $this->openId = $this->parseOpenId($response); - + return $this->openId; } @@ -60,7 +61,7 @@ class QQ extends OAuth 'access_token' => $accessToken, 'openid' => $openId, ]; - + $response = $this->httpGet(self::USER_INFO_URL, $params); return $this->parseUserInfo($response); @@ -69,29 +70,29 @@ class QQ extends OAuth protected function parseAccessToken($response) { $result = []; - + parse_str($response, $result); - + if (!isset($result['access_token'])) { throw new \Exception("Fetch Access Token Failed:{$response}"); } - + return $result['access_token']; } protected function parseOpenId($response) { $result = $matches = []; - + if (!empty($response)) { preg_match('/callback\(\s+(.*?)\s+\)/i', $response, $matches); $result = json_decode($matches[1], true); } - + if (!isset($result['openid'])) { throw new \Exception("Fetch OpenId Failed:{$response}"); } - + return $result['openid']; } @@ -106,6 +107,7 @@ class QQ extends OAuth $userInfo['id'] = $this->openId; $userInfo['name'] = $data['nickname']; $userInfo['avatar'] = $data['figureurl']; + $userInfo['provider'] = ConnectModel::PROVIDER_QQ; return $userInfo; } diff --git a/app/Services/OAuth/WeiBo.php b/app/Services/OAuth/WeiBo.php index a8c7bb48..646c950b 100644 --- a/app/Services/OAuth/WeiBo.php +++ b/app/Services/OAuth/WeiBo.php @@ -2,6 +2,7 @@ namespace App\Services\OAuth; +use App\Models\Connect as ConnectModel; use App\Services\OAuth; class WeiBo extends OAuth @@ -19,7 +20,7 @@ class WeiBo extends OAuth 'state' => $this->getState(), 'response_type' => 'code', ]; - + return self::AUTHORIZE_URL . '?' . http_build_query($params); } @@ -32,11 +33,11 @@ class WeiBo extends OAuth 'redirect_uri' => $this->redirectUri, 'grant_type' => 'authorization_code', ]; - + $response = $this->httpPost(self::ACCESS_TOKEN_URL, $params); - + $this->accessToken = $this->parseAccessToken($response); - + return $this->accessToken; } @@ -51,9 +52,9 @@ class WeiBo extends OAuth 'access_token' => $accessToken, 'uid' => $openId, ]; - + $response = $this->httpGet(self::USER_INFO_URL, $params); - + return $this->parseUserInfo($response); } @@ -64,9 +65,9 @@ class WeiBo extends OAuth if (!isset($data['access_token']) || !isset($data['uid'])) { throw new \Exception("Fetch Access Token Failed:{$response}"); } - + $this->openId = $data['uid']; - + return $data['access_token']; } @@ -81,6 +82,7 @@ class WeiBo extends OAuth $userInfo['id'] = $data['id']; $userInfo['name'] = $data['name']; $userInfo['avatar'] = $data['profile_image_url']; + $userInfo['provider'] = ConnectModel::PROVIDER_WEIBO; return $userInfo; } diff --git a/app/Services/OAuth/WeiXin.php b/app/Services/OAuth/WeiXin.php index df371797..85d7edd2 100644 --- a/app/Services/OAuth/WeiXin.php +++ b/app/Services/OAuth/WeiXin.php @@ -2,6 +2,7 @@ namespace App\Services\OAuth; +use App\Models\Connect as ConnectModel; use App\Services\OAuth; class WeiXin extends OAuth @@ -20,7 +21,7 @@ class WeiXin extends OAuth 'response_type' => 'code', 'scope' => 'snsapi_login', ]; - + return self::AUTHORIZE_URL . '?' . http_build_query($params); } @@ -32,11 +33,11 @@ class WeiXin extends OAuth 'secret' => $this->clientSecret, 'grant_type' => 'authorization_code', ]; - + $response = $this->httpPost(self::ACCESS_TOKEN_URL, $params); - + $this->accessToken = $this->parseAccessToken($response); - + return $this->accessToken; } @@ -51,22 +52,22 @@ class WeiXin extends OAuth 'access_token' => $accessToken, 'openid' => $openId, ]; - + $response = $this->httpGet(self::USER_INFO_URL, $params); - + return $this->parseUserInfo($response); } private function parseAccessToken($response) { $data = json_decode($response, true); - + if (isset($data['errcode']) && $data['errcode'] != 0) { throw new \Exception("Fetch Access Token Failed:{$response}"); } - + $this->openId = $data['openid']; - + return $data['access_token']; } @@ -81,6 +82,7 @@ class WeiXin extends OAuth $userInfo['id'] = $data['openid']; $userInfo['name'] = $data['nickname']; $userInfo['avatar'] = $data['headimgurl']; + $userInfo['provider'] = ConnectModel::PROVIDER_WEIXIN; return $userInfo; } From 3854e1d4581b2bca14a30190072fad22ce0aa1b2 Mon Sep 17 00:00:00 2001 From: koogua Date: Wed, 9 Dec 2020 15:31:42 +0800 Subject: [PATCH 30/44] =?UTF-8?q?!39=20=E4=BF=AE=E5=A4=8D=E8=AF=BE?= =?UTF-8?q?=E7=A8=8B=E5=88=86=E7=B1=BB=E6=9C=AA=E8=BF=87=E6=BB=A4=20*=20?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E8=AF=BE=E7=A8=8B=E5=88=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Services/Course.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Http/Admin/Services/Course.php b/app/Http/Admin/Services/Course.php index 9dba1d7c..b0f4bfdc 100644 --- a/app/Http/Admin/Services/Course.php +++ b/app/Http/Admin/Services/Course.php @@ -8,6 +8,7 @@ use App\Caches\CourseCategoryList as CourseCategoryListCache; use App\Caches\CourseRelatedList as CourseRelatedListCache; use App\Caches\CourseTeacherList as CourseTeacherListCache; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Category as CategoryModel; use App\Models\Course as CourseModel; use App\Models\CourseCategory as CourseCategoryModel; use App\Models\CourseRating as CourseRatingModel; @@ -224,7 +225,10 @@ class Course extends Service { $categoryRepo = new CategoryRepo(); - $allCategories = $categoryRepo->findAll(['deleted' => 0]); + $allCategories = $categoryRepo->findAll([ + 'type' => CategoryModel::TYPE_COURSE, + 'deleted' => 0, + ]); if ($allCategories->count() == 0) { return []; From 9d8c147d38e0f6075aa56af10b3cdabbbaf85067 Mon Sep 17 00:00:00 2001 From: koogua Date: Wed, 9 Dec 2020 17:50:08 +0800 Subject: [PATCH 31/44] =?UTF-8?q?!41=20=E4=BF=AE=E5=A4=8D=E8=AF=BE?= =?UTF-8?q?=E7=A8=8B=E5=88=86=E7=B1=BB=E6=9C=AA=E8=BF=87=E6=BB=A42=20*=20?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E8=AF=BE=E7=A8=8B=E5=88=86=E7=B1=BB=20*=20?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E8=AF=BE=E7=A8=8B=E5=88=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Services/Course.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Http/Admin/Services/Course.php b/app/Http/Admin/Services/Course.php index b0f4bfdc..7f644d0d 100644 --- a/app/Http/Admin/Services/Course.php +++ b/app/Http/Admin/Services/Course.php @@ -251,8 +251,11 @@ class Course extends Service $list = []; + /** + * 没有二级分类的不显示 + */ foreach ($allCategories as $category) { - if ($category->level == 1) { + if ($category->level == 1 && $category->child_count > 0) { $list[$category->id] = [ 'name' => $category->name, 'value' => $category->id, From addafb0b44601e37789b54c7f493facd3e79a348 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Wed, 9 Dec 2020 17:56:53 +0800 Subject: [PATCH 32/44] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E7=99=BB=E5=BD=95=EF=BC=8C=E8=AE=A1=E5=88=92=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E8=B7=AF=E5=BE=84=EF=BC=8C=E5=91=A8=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ README.md | 6 +++--- app/Http/Admin/Services/Setting.php | 1 + app/Http/Admin/Views/setting/oauth_qq.volt | 2 +- app/Http/Admin/Views/setting/oauth_weibo.volt | 8 +++++++- app/Http/Admin/Views/setting/oauth_weixin.volt | 2 +- app/Library/AppInfo.php | 2 +- .../20201205112717_insert_oauth_setting_data.php | 5 +++++ scheduler.php | 2 +- 9 files changed, 24 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af6041a4..50174e64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### [v1.2.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.1)(2020-12-10) +- 增加QQ,微信,微博第三方登录 +- 代码优化以及问题修复 + ### [v1.2.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.0)(2020-11-25) - 增加客户端api - 代码优化以及问题修复 diff --git a/README.md b/README.md index 1d264fff..a6a49470 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ 酷瓜云课堂,依托腾讯云基础服务架构,采用C扩展框架Phalcon开发,GPL-2.0开源协议,致力开源网课系统,开源网校系统,开源在线教育系统。 -![](https://img.shields.io/static/v1?label=release&message=1.2.0&color=blue) -![](https://img.shields.io/static/v1?label=stars&message=101&color=blue) -![](https://img.shields.io/static/v1?label=forks&message=40&color=blue) +![](https://img.shields.io/static/v1?label=release&message=1.2.1&color=blue) +![](https://img.shields.io/static/v1?label=stars&message=112&color=blue) +![](https://img.shields.io/static/v1?label=forks&message=41&color=blue) ![](https://img.shields.io/static/v1?label=license&message=GPL-2.0&color=blue) #### 系统功能 diff --git a/app/Http/Admin/Services/Setting.php b/app/Http/Admin/Services/Setting.php index 45c11719..3d2130fd 100644 --- a/app/Http/Admin/Services/Setting.php +++ b/app/Http/Admin/Services/Setting.php @@ -32,6 +32,7 @@ class Setting extends Service $oauth = $this->getSettings('oauth.weibo'); $oauth['redirect_uri'] = $oauth['redirect_uri'] ?: kg_full_url(['for' => 'home.oauth.weibo_callback']); + $oauth['refuse_uri'] = $oauth['refuse_uri'] ?: kg_full_url(['for' => 'home.oauth.weibo_refuse']); return $oauth; } diff --git a/app/Http/Admin/Views/setting/oauth_qq.volt b/app/Http/Admin/Views/setting/oauth_qq.volt index 2f0d2f9a..bcf13f40 100644 --- a/app/Http/Admin/Views/setting/oauth_qq.volt +++ b/app/Http/Admin/Views/setting/oauth_qq.volt @@ -19,7 +19,7 @@
        - +
        diff --git a/app/Http/Admin/Views/setting/oauth_weibo.volt b/app/Http/Admin/Views/setting/oauth_weibo.volt index c7ca6bae..5b5a7a73 100644 --- a/app/Http/Admin/Views/setting/oauth_weibo.volt +++ b/app/Http/Admin/Views/setting/oauth_weibo.volt @@ -19,11 +19,17 @@
        - +
        +
        + +
        + +
        +
        diff --git a/app/Http/Admin/Views/setting/oauth_weixin.volt b/app/Http/Admin/Views/setting/oauth_weixin.volt index 4c08c355..2adfc78f 100644 --- a/app/Http/Admin/Views/setting/oauth_weixin.volt +++ b/app/Http/Admin/Views/setting/oauth_weixin.volt @@ -19,7 +19,7 @@
        - +
        diff --git a/app/Library/AppInfo.php b/app/Library/AppInfo.php index 569370b3..e0ab2ed3 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.0'; + protected $version = '1.2.1'; public function __get($name) { diff --git a/db/migrations/20201205112717_insert_oauth_setting_data.php b/db/migrations/20201205112717_insert_oauth_setting_data.php index 40cfe6f2..a7e4cf86 100644 --- a/db/migrations/20201205112717_insert_oauth_setting_data.php +++ b/db/migrations/20201205112717_insert_oauth_setting_data.php @@ -70,6 +70,11 @@ final class InsertOauthSettingData extends AbstractMigration 'item_key' => 'redirect_uri', 'item_value' => '', ], + [ + 'section' => 'oauth.weibo', + 'item_key' => 'refuse_uri', + 'item_value' => '', + ], ]; $this->table('kg_setting')->insert($rows)->save(); diff --git a/scheduler.php b/scheduler.php index b038b608..b5b27246 100644 --- a/scheduler.php +++ b/scheduler.php @@ -8,7 +8,7 @@ $scheduler = new Scheduler(); $script = __DIR__ . '/console.php'; -$bin = '/usr/bin/php'; +$bin = '/usr/local/bin/php'; $scheduler->php($script, $bin, ['--task' => 'deliver', '--action' => 'main']) ->at('*/3 * * * *'); From edf1074becfa929960cf5ed816a22fd7205f82d8 Mon Sep 17 00:00:00 2001 From: koogua Date: Wed, 9 Dec 2020 18:04:25 +0800 Subject: [PATCH 33/44] =?UTF-8?q?!43=20v1.2.0=E9=98=B6=E6=AE=B5=E6=80=A7?= =?UTF-8?q?=E5=90=88=E5=B9=B6=20*=20Merge=20branches=20'develop'=20and=20'?= =?UTF-8?q?master'=20of=20https://gitee.com/koogua/cour=E2=80=A6=20*=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BC=80=E5=8F=91=E7=99=BB=E5=BD=95=EF=BC=8C?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E4=BB=BB=E5=8A=A1=E6=89=A7=E8=A1=8C=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=EF=BC=8C=E5=91=A8=E6=9C=9F=20*=20!41=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=AF=BE=E7=A8=8B=E5=88=86=E7=B1=BB=E6=9C=AA=E8=BF=87?= =?UTF-8?q?=E6=BB=A42=20*=20!40=20=E4=BF=AE=E5=A4=8D=E8=AF=BE=E7=A8=8B?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E6=9C=AA=E8=BF=87=E6=BB=A42=20*=20!39=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=BE=E7=A8=8B=E5=88=86=E7=B1=BB=E6=9C=AA?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=20*=20!38=20=E4=BF=AE=E5=A4=8D=E8=AF=BE?= =?UTF-8?q?=E7=A8=8B=E5=88=86=E7=B1=BB=E6=9C=AA=E8=BF=87=E6=BB=A4=20*=20Me?= =?UTF-8?q?rge=20branch=20'master'=20of=20https://gitee.com/koogua/course-?= =?UTF-8?q?tencent-cloud=E2=80=A6=20*=20!36=20=E4=BF=AE=E5=A4=8D=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=AF=BE=E7=A8=8B=E5=90=8E=E8=BF=9B=E5=85=A5=E5=88=97?= =?UTF-8?q?=E8=A1=A8500=E9=94=99=E8=AF=AF=20*=20!35=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=85=BE=E8=AE=AF=E4=BA=91=E5=9B=9E=E8=B0=83=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E6=94=B9=E5=8F=98=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF=20*=20!34=20=E4=BF=AE=E5=A4=8D=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=AF=BE=E6=97=B6=E7=9B=B8=E5=85=B3=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E8=A1=A8=E6=95=B0=E6=8D=AE=E6=9C=AA=E7=94=9F=E6=88=90=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20*=20!23=20=E4=BF=AE=E5=A4=8D=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=AF=BE=E6=97=B6=E5=90=8E=E8=BF=9B=E5=85=A5=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E9=A1=B5=E9=9D=A2500=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ README.md | 6 +++--- app/Http/Admin/Services/Course.php | 11 +++++++++-- app/Http/Admin/Services/Setting.php | 1 + app/Http/Admin/Views/setting/oauth_qq.volt | 2 +- app/Http/Admin/Views/setting/oauth_weibo.volt | 8 +++++++- app/Http/Admin/Views/setting/oauth_weixin.volt | 2 +- app/Library/AppInfo.php | 2 +- .../20201205112717_insert_oauth_setting_data.php | 5 +++++ scheduler.php | 2 +- 10 files changed, 33 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af6041a4..50174e64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### [v1.2.1](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.1)(2020-12-10) +- 增加QQ,微信,微博第三方登录 +- 代码优化以及问题修复 + ### [v1.2.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.2.0)(2020-11-25) - 增加客户端api - 代码优化以及问题修复 diff --git a/README.md b/README.md index 1d264fff..a6a49470 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ 酷瓜云课堂,依托腾讯云基础服务架构,采用C扩展框架Phalcon开发,GPL-2.0开源协议,致力开源网课系统,开源网校系统,开源在线教育系统。 -![](https://img.shields.io/static/v1?label=release&message=1.2.0&color=blue) -![](https://img.shields.io/static/v1?label=stars&message=101&color=blue) -![](https://img.shields.io/static/v1?label=forks&message=40&color=blue) +![](https://img.shields.io/static/v1?label=release&message=1.2.1&color=blue) +![](https://img.shields.io/static/v1?label=stars&message=112&color=blue) +![](https://img.shields.io/static/v1?label=forks&message=41&color=blue) ![](https://img.shields.io/static/v1?label=license&message=GPL-2.0&color=blue) #### 系统功能 diff --git a/app/Http/Admin/Services/Course.php b/app/Http/Admin/Services/Course.php index 9dba1d7c..7f644d0d 100644 --- a/app/Http/Admin/Services/Course.php +++ b/app/Http/Admin/Services/Course.php @@ -8,6 +8,7 @@ use App\Caches\CourseCategoryList as CourseCategoryListCache; use App\Caches\CourseRelatedList as CourseRelatedListCache; use App\Caches\CourseTeacherList as CourseTeacherListCache; use App\Library\Paginator\Query as PagerQuery; +use App\Models\Category as CategoryModel; use App\Models\Course as CourseModel; use App\Models\CourseCategory as CourseCategoryModel; use App\Models\CourseRating as CourseRatingModel; @@ -224,7 +225,10 @@ class Course extends Service { $categoryRepo = new CategoryRepo(); - $allCategories = $categoryRepo->findAll(['deleted' => 0]); + $allCategories = $categoryRepo->findAll([ + 'type' => CategoryModel::TYPE_COURSE, + 'deleted' => 0, + ]); if ($allCategories->count() == 0) { return []; @@ -247,8 +251,11 @@ class Course extends Service $list = []; + /** + * 没有二级分类的不显示 + */ foreach ($allCategories as $category) { - if ($category->level == 1) { + if ($category->level == 1 && $category->child_count > 0) { $list[$category->id] = [ 'name' => $category->name, 'value' => $category->id, diff --git a/app/Http/Admin/Services/Setting.php b/app/Http/Admin/Services/Setting.php index c04014ed..d6d46164 100644 --- a/app/Http/Admin/Services/Setting.php +++ b/app/Http/Admin/Services/Setting.php @@ -32,6 +32,7 @@ class Setting extends Service $oauth = $this->getSettings('oauth.weibo'); $oauth['redirect_uri'] = $oauth['redirect_uri'] ?: kg_full_url(['for' => 'home.oauth.weibo_callback']); + $oauth['refuse_uri'] = $oauth['refuse_uri'] ?: kg_full_url(['for' => 'home.oauth.weibo_refuse']); return $oauth; } diff --git a/app/Http/Admin/Views/setting/oauth_qq.volt b/app/Http/Admin/Views/setting/oauth_qq.volt index 2f0d2f9a..bcf13f40 100644 --- a/app/Http/Admin/Views/setting/oauth_qq.volt +++ b/app/Http/Admin/Views/setting/oauth_qq.volt @@ -19,7 +19,7 @@
        - +
        diff --git a/app/Http/Admin/Views/setting/oauth_weibo.volt b/app/Http/Admin/Views/setting/oauth_weibo.volt index c7ca6bae..5b5a7a73 100644 --- a/app/Http/Admin/Views/setting/oauth_weibo.volt +++ b/app/Http/Admin/Views/setting/oauth_weibo.volt @@ -19,11 +19,17 @@
        - +
        +
        + +
        + +
        +
        diff --git a/app/Http/Admin/Views/setting/oauth_weixin.volt b/app/Http/Admin/Views/setting/oauth_weixin.volt index 4c08c355..2adfc78f 100644 --- a/app/Http/Admin/Views/setting/oauth_weixin.volt +++ b/app/Http/Admin/Views/setting/oauth_weixin.volt @@ -19,7 +19,7 @@
        - +
        diff --git a/app/Library/AppInfo.php b/app/Library/AppInfo.php index 569370b3..e0ab2ed3 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.0'; + protected $version = '1.2.1'; public function __get($name) { diff --git a/db/migrations/20201205112717_insert_oauth_setting_data.php b/db/migrations/20201205112717_insert_oauth_setting_data.php index 40cfe6f2..a7e4cf86 100644 --- a/db/migrations/20201205112717_insert_oauth_setting_data.php +++ b/db/migrations/20201205112717_insert_oauth_setting_data.php @@ -70,6 +70,11 @@ final class InsertOauthSettingData extends AbstractMigration 'item_key' => 'redirect_uri', 'item_value' => '', ], + [ + 'section' => 'oauth.weibo', + 'item_key' => 'refuse_uri', + 'item_value' => '', + ], ]; $this->table('kg_setting')->insert($rows)->save(); diff --git a/scheduler.php b/scheduler.php index b038b608..b5b27246 100644 --- a/scheduler.php +++ b/scheduler.php @@ -8,7 +8,7 @@ $scheduler = new Scheduler(); $script = __DIR__ . '/console.php'; -$bin = '/usr/bin/php'; +$bin = '/usr/local/bin/php'; $scheduler->php($script, $bin, ['--task' => 'deliver', '--action' => 'main']) ->at('*/3 * * * *'); From 6455f881812229db284adc49ec808e91299e7ea0 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Wed, 9 Dec 2020 19:29:34 +0800 Subject: [PATCH 34/44] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Home/Controllers/ConnectController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Home/Controllers/ConnectController.php b/app/Http/Home/Controllers/ConnectController.php index bfe468ee..10c11e4c 100644 --- a/app/Http/Home/Controllers/ConnectController.php +++ b/app/Http/Home/Controllers/ConnectController.php @@ -117,7 +117,7 @@ class ConnectController extends Controller $openUser = $service->getOpenUserInfo($code, $state, $provider); $connect = $service->getConnectRelation($openUser['id'], $openUser['provider']); - if ($connect && $connect->deleted == 0) { + if ($connect) { if ($this->authUser->id > 0) { $service->bindUser($openUser); return $this->response->redirect(['for' => 'home.uc.account']); From 29274830172ac7eb63d5f4cbcaa8c5c18d84ad9e Mon Sep 17 00:00:00 2001 From: koogua Date: Wed, 9 Dec 2020 19:31:42 +0800 Subject: [PATCH 35/44] =?UTF-8?q?!44=20v1.2.1=E9=98=B6=E6=AE=B5=E6=80=A7?= =?UTF-8?q?=E5=90=88=E5=B9=B6=20*=20=E4=BC=98=E5=8C=96=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91=20*=20Merge=20branches=20'?= =?UTF-8?q?develop'=20and=20'master'=20of=20https://gitee.com/koogua/cour?= =?UTF-8?q?=E2=80=A6=20*=20=E4=BC=98=E5=8C=96=E5=BC=80=E5=8F=91=E7=99=BB?= =?UTF-8?q?=E5=BD=95=EF=BC=8C=E8=AE=A1=E5=88=92=E4=BB=BB=E5=8A=A1=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E8=B7=AF=E5=BE=84=EF=BC=8C=E5=91=A8=E6=9C=9F=20*=20!4?= =?UTF-8?q?1=20=E4=BF=AE=E5=A4=8D=E8=AF=BE=E7=A8=8B=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E6=9C=AA=E8=BF=87=E6=BB=A42=20*=20!40=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E8=AF=BE=E7=A8=8B=E5=88=86=E7=B1=BB=E6=9C=AA=E8=BF=87=E6=BB=A4?= =?UTF-8?q?2=20*=20!39=20=E4=BF=AE=E5=A4=8D=E8=AF=BE=E7=A8=8B=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E6=9C=AA=E8=BF=87=E6=BB=A4=20*=20!38=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=AF=BE=E7=A8=8B=E5=88=86=E7=B1=BB=E6=9C=AA=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=20*=20Merge=20branch=20'master'=20of=20https://gitee.?= =?UTF-8?q?com/koogua/course-tencent-cloud=E2=80=A6=20*=20!36=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=B7=BB=E5=8A=A0=E8=AF=BE=E7=A8=8B=E5=90=8E=E8=BF=9B?= =?UTF-8?q?=E5=85=A5=E5=88=97=E8=A1=A8500=E9=94=99=E8=AF=AF=20*=20!35=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=85=BE=E8=AE=AF=E4=BA=91=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84=E6=94=B9=E5=8F=98=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=9A=84=E9=94=99=E8=AF=AF=20*=20!34=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=88=9B=E5=BB=BA=E8=AF=BE=E6=97=B6=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E8=A1=A8=E6=95=B0=E6=8D=AE=E6=9C=AA=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=9A=84=E9=97=AE=E9=A2=98=20*=20!23=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=B7=BB=E5=8A=A0=E8=AF=BE=E6=97=B6=E5=90=8E=E8=BF=9B?= =?UTF-8?q?=E5=85=A5=E7=BC=96=E8=BE=91=E9=A1=B5=E9=9D=A2500=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Home/Controllers/ConnectController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Home/Controllers/ConnectController.php b/app/Http/Home/Controllers/ConnectController.php index bfe468ee..10c11e4c 100644 --- a/app/Http/Home/Controllers/ConnectController.php +++ b/app/Http/Home/Controllers/ConnectController.php @@ -117,7 +117,7 @@ class ConnectController extends Controller $openUser = $service->getOpenUserInfo($code, $state, $provider); $connect = $service->getConnectRelation($openUser['id'], $openUser['provider']); - if ($connect && $connect->deleted == 0) { + if ($connect) { if ($this->authUser->id > 0) { $service->bindUser($openUser); return $this->response->redirect(['for' => 'home.uc.account']); From bee4daf6723a6ee595505ee37226051df379d669 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Thu, 10 Dec 2020 15:41:07 +0800 Subject: [PATCH 36/44] =?UTF-8?q?=E6=9B=B4=E6=94=B9QQ=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E8=8E=B7=E5=8F=96ACCESS=5FTOKEN=E7=9A=84=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Home/Controllers/ConnectController.php | 8 +++++--- app/Services/OAuth/QQ.php | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Http/Home/Controllers/ConnectController.php b/app/Http/Home/Controllers/ConnectController.php index 10c11e4c..708e3e81 100644 --- a/app/Http/Home/Controllers/ConnectController.php +++ b/app/Http/Home/Controllers/ConnectController.php @@ -117,11 +117,13 @@ class ConnectController extends Controller $openUser = $service->getOpenUserInfo($code, $state, $provider); $connect = $service->getConnectRelation($openUser['id'], $openUser['provider']); - if ($connect) { - if ($this->authUser->id > 0) { + if ($this->authUser->id > 0) { + if ($openUser) { $service->bindUser($openUser); return $this->response->redirect(['for' => 'home.uc.account']); - } else { + } + } else { + if ($connect && $connect->deleted == 0) { $service->authLogin($connect); return $this->response->redirect(['for' => 'home.index']); } diff --git a/app/Services/OAuth/QQ.php b/app/Services/OAuth/QQ.php index 2c59f144..4e2ba114 100644 --- a/app/Services/OAuth/QQ.php +++ b/app/Services/OAuth/QQ.php @@ -36,7 +36,7 @@ class QQ extends OAuth 'grant_type' => 'authorization_code', ]; - $response = $this->httpPost(self::ACCESS_TOKEN_URL, $params); + $response = $this->httpGet(self::ACCESS_TOKEN_URL, $params); $this->accessToken = $this->parseAccessToken($response); From 069145ed5012719a8993d01dd3855d323bb2236b Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Fri, 11 Dec 2020 20:50:44 +0800 Subject: [PATCH 37/44] =?UTF-8?q?=E6=91=98=E9=99=A4demo=E5=88=86=E6=94=AF?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=9A=84=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Controllers/Controller.php | 11 ----------- app/Http/Admin/Services/Setting.php | 8 -------- app/Http/Admin/Views/templates/main.volt | 6 ------ 3 files changed, 25 deletions(-) diff --git a/app/Http/Admin/Controllers/Controller.php b/app/Http/Admin/Controllers/Controller.php index 25899d50..058bfa77 100644 --- a/app/Http/Admin/Controllers/Controller.php +++ b/app/Http/Admin/Controllers/Controller.php @@ -21,17 +21,6 @@ class Controller extends \Phalcon\Mvc\Controller public function beforeExecuteRoute(Dispatcher $dispatcher) { - /** - * demo分支拒绝数据提交 - */ - if ($this->isNotSafeRequest()) { - $dispatcher->forward([ - 'controller' => 'public', - 'action' => 'forbidden', - ]); - return false; - } - if ($this->isNotSafeRequest()) { $this->checkHttpReferer(); $this->checkCsrfToken(); diff --git a/app/Http/Admin/Services/Setting.php b/app/Http/Admin/Services/Setting.php index d6d46164..3d2130fd 100644 --- a/app/Http/Admin/Services/Setting.php +++ b/app/Http/Admin/Services/Setting.php @@ -87,16 +87,8 @@ class Setting extends Service $result = []; - /** - * demo分支过滤敏感数据 - */ if ($items->count() > 0) { foreach ($items as $item) { - $case1 = preg_match('/(id|auth|key|secret|password|pwd)$/', $item->item_key); - $case2 = $this->dispatcher->getControllerName() == 'setting'; - if ($case1 && $case2) { - $item->item_value = '***'; - } $result[$item->item_key] = $item->item_value; } } diff --git a/app/Http/Admin/Views/templates/main.volt b/app/Http/Admin/Views/templates/main.volt index 8803b2a0..6b70ff60 100644 --- a/app/Http/Admin/Views/templates/main.volt +++ b/app/Http/Admin/Views/templates/main.volt @@ -23,11 +23,5 @@ {% block include_js %}{% endblock %} {% block inline_js %}{% endblock %} -{% set site = setting('site') %} - -{% if site['analytics_enabled'] == 1 %} - {{ site['analytics_script'] }} -{% endif %} - \ No newline at end of file From d60d748c2874944de07db398ed344c7390c6bd45 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Sat, 12 Dec 2020 17:01:49 +0800 Subject: [PATCH 38/44] =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=9F=BA=E6=9C=AC=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Admin/Controllers/SettingController.php | 25 + app/Http/Admin/Services/AuthNode.php | 6 + app/Http/Admin/Services/Setting.php | 10 + app/Http/Admin/Views/setting/wechat.volt | 16 + app/Http/Admin/Views/setting/wechat_oa.volt | 53 + .../WeChatOfficialAccountController.php | 40 + .../Home/Services/WeChatOfficialAccount.php | 17 + app/Models/WeChatSubscribe.php | 79 + app/Services/WeChat.php | 59 + composer.json | 3 +- composer.lock | 1364 ++++++++++++----- 11 files changed, 1307 insertions(+), 365 deletions(-) create mode 100644 app/Http/Admin/Views/setting/wechat.volt create mode 100644 app/Http/Admin/Views/setting/wechat_oa.volt create mode 100644 app/Http/Home/Controllers/WeChatOfficialAccountController.php create mode 100644 app/Http/Home/Services/WeChatOfficialAccount.php create mode 100644 app/Models/WeChatSubscribe.php create mode 100644 app/Services/WeChat.php diff --git a/app/Http/Admin/Controllers/SettingController.php b/app/Http/Admin/Controllers/SettingController.php index 71e41fc0..7452c717 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->updateSettings($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..52d6bf1e 100644 --- a/app/Http/Admin/Services/AuthNode.php +++ b/app/Http/Admin/Services/AuthNode.php @@ -750,6 +750,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/Setting.php b/app/Http/Admin/Services/Setting.php index 3d2130fd..3b744ad8 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(); 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..c430775b --- /dev/null +++ b/app/Http/Admin/Views/setting/wechat_oa.volt @@ -0,0 +1,53 @@ +
        +
        + +
        + + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + +
        +
        +
        + +
        + + + +
        +
        +
        \ No newline at end of file diff --git a/app/Http/Home/Controllers/WeChatOfficialAccountController.php b/app/Http/Home/Controllers/WeChatOfficialAccountController.php new file mode 100644 index 00000000..e62bddc9 --- /dev/null +++ b/app/Http/Home/Controllers/WeChatOfficialAccountController.php @@ -0,0 +1,40 @@ +getOfficialAccount(); + + $response = $app->server->serve(); + + $response->send(); + + exit; + } + + /** + * @Post("/notify", name="home.wechat.oa.notify") + */ + public function notifyAction() + { + + } + +} diff --git a/app/Http/Home/Services/WeChatOfficialAccount.php b/app/Http/Home/Services/WeChatOfficialAccount.php new file mode 100644 index 00000000..a6173e16 --- /dev/null +++ b/app/Http/Home/Services/WeChatOfficialAccount.php @@ -0,0 +1,17 @@ +getOfficialAccount(); + } + +} diff --git a/app/Models/WeChatSubscribe.php b/app/Models/WeChatSubscribe.php new file mode 100644 index 00000000..f1ce2201 --- /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/Services/WeChat.php b/app/Services/WeChat.php new file mode 100644 index 00000000..55f5ba9d --- /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/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" } From 6f955bff842a06de1bf548defbba244a0942ff86 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Mon, 14 Dec 2020 16:16:25 +0800 Subject: [PATCH 39/44] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E7=94=9F=E6=88=90MaxImGroupId=E7=9A=84=E9=97=AE=E9=A2=98,?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=BE=E7=A8=8B=E9=BB=98=E8=AE=A4=E7=BE=A4?= =?UTF-8?q?=E4=B8=BB=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Services/Course.php | 43 ++++++++++++++++++++++++++++++ app/Models/Course.php | 11 -------- app/Models/ImGroup.php | 8 ++++++ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/app/Http/Admin/Services/Course.php b/app/Http/Admin/Services/Course.php index 7f644d0d..10eaf27e 100644 --- a/app/Http/Admin/Services/Course.php +++ b/app/Http/Admin/Services/Course.php @@ -15,12 +15,14 @@ use App\Models\CourseRating as CourseRatingModel; use App\Models\CourseRelated as CourseRelatedModel; use App\Models\CourseUser as CourseUserModel; use App\Models\ImGroup as ImGroupModel; +use App\Models\ImGroupUser as ImGroupUserModel; use App\Repos\Category as CategoryRepo; use App\Repos\Chapter as ChapterRepo; use App\Repos\Course as CourseRepo; use App\Repos\CourseCategory as CourseCategoryRepo; use App\Repos\CourseRelated as CourseRelatedRepo; use App\Repos\CourseUser as CourseUserRepo; +use App\Repos\ImGroup as ImGroupRepo; use App\Repos\User as UserRepo; use App\Services\Sync\CourseIndex as CourseIndexSync; use App\Validators\Course as CourseValidator; @@ -94,6 +96,7 @@ class Course extends Service $imGroup = new ImGroupModel(); + $imGroup->type = ImGroupModel::TYPE_COURSE; $imGroup->course_id = $course->id; $imGroup->name = $course->title; $imGroup->about = $course->summary; @@ -190,6 +193,8 @@ class Course extends Service $course->update($data); + $this->updateImGroup($course); + return $course; } @@ -199,6 +204,12 @@ class Course extends Service $course->deleted = 1; $course->update(); + $groupRepo = new ImGroupRepo(); + + $group = $groupRepo->findByCourseId($course->id); + $group->deleted = 1; + $group->update(); + return $course; } @@ -208,6 +219,12 @@ class Course extends Service $course->deleted = 0; $course->update(); + $groupRepo = new ImGroupRepo(); + + $group = $groupRepo->findByCourseId($course->id); + $group->deleted = 0; + $group->update(); + return $course; } @@ -545,6 +562,32 @@ class Course extends Service $cache->rebuild($course->id); } + protected function updateImGroup(CourseModel $course) + { + $groupRepo = new ImGroupRepo(); + + $group = $groupRepo->findByCourseId($course->id); + + $data = []; + + if ($course->title != $group->name) { + $data['name'] = $course->title; + } + + if ($course->teacher_id > 0 && $group->owner_id == 0) { + + $groupUser = new ImGroupUserModel(); + $groupUser->group_id = $group->id; + $groupUser->user_id = $course->teacher_id; + $groupUser->create(); + + $data['owner_id'] = $course->teacher_id; + $data['user_count'] = $group->user_count + 1; + } + + $group->update($data); + } + protected function handleCourses($pager) { if ($pager->total_items > 0) { diff --git a/app/Models/Course.php b/app/Models/Course.php index 324c1f1f..aaa22404 100644 --- a/app/Models/Course.php +++ b/app/Models/Course.php @@ -326,17 +326,6 @@ class Course extends Model $cache->rebuild(); } - public function afterUpdate() - { - /** - * 群组名称和课程标题保持一致 - */ - if ($this->hasUpdated('title')) { - $imGroup = ImGroup::findFirst(['course_id' => $this->id]); - $imGroup->update(['name' => $this->title]); - } - } - public function afterFetch() { $this->market_price = (float)$this->market_price; diff --git a/app/Models/ImGroup.php b/app/Models/ImGroup.php index a8c42ea1..ec899cd0 100644 --- a/app/Models/ImGroup.php +++ b/app/Models/ImGroup.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Caches\MaxImGroupId as MaxImGroupIdCache; use App\Services\Sync\GroupIndex as GroupIndexSync; use Phalcon\Mvc\Model\Behavior\SoftDelete; use Phalcon\Text; @@ -153,6 +154,13 @@ class ImGroup extends Model $this->update_time = time(); } + public function afterCreate() + { + $cache = new MaxImGroupIdCache(); + + $cache->rebuild(); + } + public function afterFetch() { if (!Text::startsWith($this->avatar, 'http')) { From 4a46a42cba35d529fdff7d94e290e7444717e2c1 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Sat, 19 Dec 2020 15:38:57 +0800 Subject: [PATCH 40/44] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=A7=92=E8=89=B2route?= =?UTF-8?q?s=E5=AD=97=E6=AE=B5=E5=A1=AB=E5=85=85=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 99cfb9bc9ba31f4312d74b0b5ca2216b49c517c9) --- app/Http/Admin/Services/Role.php | 11 +++++++---- app/Models/Role.php | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/Http/Admin/Services/Role.php b/app/Http/Admin/Services/Role.php index 82fde39a..86bade39 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 []; } 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(); From 30fb586d0b1b6a980d2ceb8a43c9757621a8f0a6 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Sun, 20 Dec 2020 17:19:30 +0800 Subject: [PATCH 41/44] =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7=E4=BA=92?= =?UTF-8?q?=E5=8A=A8=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Builders/ConsultList.php | 5 +- app/Console/Tasks/CleanLogTask.php | 18 ++ app/Console/Tasks/DeliverTask.php | 10 +- app/Console/Tasks/LiveNotifyTask.php | 81 ------ app/Console/Tasks/MaintainTask.php | 2 +- app/Console/Tasks/NoticeTask.php | 137 ++++++++++ app/Console/Tasks/RefundTask.php | 10 +- app/Console/Tasks/Task.php | 2 +- .../Admin/Controllers/SettingController.php | 4 +- app/Http/Admin/Services/Consult.php | 19 ++ app/Http/Admin/Services/Session.php | 11 + app/Http/Admin/Services/Setting.php | 17 +- app/Http/Admin/Views/order/list.volt | 2 +- app/Http/Admin/Views/setting/sms.volt | 26 +- app/Http/Admin/Views/setting/wechat_oa.volt | 69 +++++- app/Http/Api/Services/Account.php | 13 + .../Controllers/UserConsoleController.php | 29 +++ .../WeChatOfficialAccountController.php | 40 --- .../WechatOfficialAccountController.php | 76 ++++++ app/Http/Home/Services/Account.php | 13 + .../Home/Services/WeChatOfficialAccount.php | 17 -- .../Home/Services/WechatOfficialAccount.php | 233 ++++++++++++++++++ app/Http/Home/Views/user/console/menu.volt | 3 + .../Home/Views/user/console/subscribe.volt | 33 +++ app/Models/Consult.php | 9 +- app/Models/Task.php | 6 + ...eChatSubscribe.php => WechatSubscribe.php} | 2 +- app/Repos/CourseUser.php | 12 + app/Repos/WechatSubscribe.php | 57 +++++ app/Services/Auth/Mobile.php | 44 ---- app/Services/LiveNotify.php | 30 ++- app/Services/Logic/Consult/ConsultInfo.php | 16 ++ app/Services/Logic/Consult/ConsultReply.php | 31 ++- app/Services/Logic/Notice/AccountLogin.php | 61 +++++ app/Services/Logic/Notice/ConsultReply.php | 91 +++++++ app/Services/Logic/Notice/LiveBegin.php | 99 ++++++++ app/Services/Logic/Notice/OrderFinish.php | 76 ++++++ app/Services/Logic/Notice/RefundFinish.php | 76 ++++++ app/Services/Sms/Live.php | 50 ---- app/Services/Sms/Notice/ConsultReply.php | 37 +++ app/Services/Sms/Notice/LiveBegin.php | 38 +++ app/Services/Sms/Notice/OrderFinish.php | 38 +++ app/Services/Sms/Notice/RefundFinish.php | 38 +++ app/Services/Sms/Order.php | 39 --- app/Services/Sms/Refund.php | 39 --- app/Services/{WeChat.php => Wechat.php} | 4 +- app/Services/Wechat/Notice/AccountLogin.php | 35 +++ app/Services/Wechat/Notice/ConsultReply.php | 37 +++ app/Services/Wechat/Notice/LiveBegin.php | 36 +++ app/Services/Wechat/Notice/OrderFinish.php | 37 +++ app/Services/Wechat/Notice/RefundFinish.php | 36 +++ app/Services/WechatNotice.php | 103 ++++++++ .../20201205091213_create_connect_table.php | 2 +- .../20201212102844_schema_202012121830.php | 99 ++++++++ .../20201212112717_data_202012121830.php | 81 ++++++ public/static/home/css/common.css | 18 ++ .../static/home/js/user.console.subscribe.js | 29 +++ scheduler.php | 4 +- 58 files changed, 1904 insertions(+), 376 deletions(-) delete mode 100644 app/Console/Tasks/LiveNotifyTask.php create mode 100644 app/Console/Tasks/NoticeTask.php delete mode 100644 app/Http/Home/Controllers/WeChatOfficialAccountController.php create mode 100644 app/Http/Home/Controllers/WechatOfficialAccountController.php delete mode 100644 app/Http/Home/Services/WeChatOfficialAccount.php create mode 100644 app/Http/Home/Services/WechatOfficialAccount.php create mode 100644 app/Http/Home/Views/user/console/subscribe.volt rename app/Models/{WeChatSubscribe.php => WechatSubscribe.php} (96%) create mode 100644 app/Repos/WechatSubscribe.php delete mode 100644 app/Services/Auth/Mobile.php create mode 100644 app/Services/Logic/Notice/AccountLogin.php create mode 100644 app/Services/Logic/Notice/ConsultReply.php create mode 100644 app/Services/Logic/Notice/LiveBegin.php create mode 100644 app/Services/Logic/Notice/OrderFinish.php create mode 100644 app/Services/Logic/Notice/RefundFinish.php delete mode 100644 app/Services/Sms/Live.php create mode 100644 app/Services/Sms/Notice/ConsultReply.php create mode 100644 app/Services/Sms/Notice/LiveBegin.php create mode 100644 app/Services/Sms/Notice/OrderFinish.php create mode 100644 app/Services/Sms/Notice/RefundFinish.php delete mode 100644 app/Services/Sms/Order.php delete mode 100644 app/Services/Sms/Refund.php rename app/Services/{WeChat.php => Wechat.php} (96%) create mode 100644 app/Services/Wechat/Notice/AccountLogin.php create mode 100644 app/Services/Wechat/Notice/ConsultReply.php create mode 100644 app/Services/Wechat/Notice/LiveBegin.php create mode 100644 app/Services/Wechat/Notice/OrderFinish.php create mode 100644 app/Services/Wechat/Notice/RefundFinish.php create mode 100644 app/Services/WechatNotice.php create mode 100644 db/migrations/20201212102844_schema_202012121830.php create mode 100644 db/migrations/20201212112717_data_202012121830.php create mode 100644 public/static/home/js/user.console.subscribe.js 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..560c8449 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; @@ -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) 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..4c69fcf6 --- /dev/null +++ b/app/Console/Tasks/NoticeTask.php @@ -0,0 +1,137 @@ +getLogger('notice'); + + $tasks = $this->findTasks(500); + + 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..ad69ec3c 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; @@ -95,7 +95,7 @@ class RefundTask extends Task $this->db->commit(); - $this->handleRefundNotice($refund); + $this->handleRefundFinishNotice($refund); } catch (\Exception $e) { @@ -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 7452c717..309d6bd8 100644 --- a/app/Http/Admin/Controllers/SettingController.php +++ b/app/Http/Admin/Controllers/SettingController.php @@ -340,13 +340,13 @@ class SettingController extends Controller $data = $this->request->getPost(); - $settingService->updateSettings($section, $data); + $settingService->updateWechatSettings($section, $data); return $this->jsonSuccess(['msg' => '更新配置成功']); } else { - $oa = $settingService->getWeChatOASettings(); + $oa = $settingService->getWechatOASettings(); $this->view->setVar('oa', $oa); } 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/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 3b744ad8..3f2abe3e 100644 --- a/app/Http/Admin/Services/Setting.php +++ b/app/Http/Admin/Services/Setting.php @@ -57,7 +57,7 @@ class Setting extends Service return $wxpay; } - public function getWeChatOASettings() + public function getWechatOASettings() { $oa = $this->getSettings('wechat.oa'); @@ -154,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); } @@ -170,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_oa.volt b/app/Http/Admin/Views/setting/wechat_oa.volt index c430775b..efc70be5 100644 --- a/app/Http/Admin/Views/setting/wechat_oa.volt +++ b/app/Http/Admin/Views/setting/wechat_oa.volt @@ -1,6 +1,8 @@ +{% set notice_template = oa.notice_template|json_decode %} +
        - +
        @@ -31,13 +33,7 @@
        - -
        - -
        -
        -
        - +
        @@ -47,7 +43,62 @@
        - + +
        +
        +
        +
        + 模板配置 +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        名称模板编号
        登录提醒
        订单通知
        退款通知
        直播提醒
        回复通知
        +
        +
        + +
        + + +
        \ 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 deleted file mode 100644 index e62bddc9..00000000 --- a/app/Http/Home/Controllers/WeChatOfficialAccountController.php +++ /dev/null @@ -1,40 +0,0 @@ -getOfficialAccount(); - - $response = $app->server->serve(); - - $response->send(); - - exit; - } - - /** - * @Post("/notify", name="home.wechat.oa.notify") - */ - public function notifyAction() - { - - } - -} 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/WeChatOfficialAccount.php b/app/Http/Home/Services/WeChatOfficialAccount.php deleted file mode 100644 index a6173e16..00000000 --- a/app/Http/Home/Services/WeChatOfficialAccount.php +++ /dev/null @@ -1,17 +0,0 @@ -getOfficialAccount(); - } - -} diff --git a/app/Http/Home/Services/WechatOfficialAccount.php b/app/Http/Home/Services/WechatOfficialAccount.php new file mode 100644 index 00000000..b205ccee --- /dev/null +++ b/app/Http/Home/Services/WechatOfficialAccount.php @@ -0,0 +1,233 @@ +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->info('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 $message['Event']; + 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) + { + } + + protected function handleViewEvent($message) + { + } + + protected function handleLocationEvent($message) + { + } + + 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 %} + +
        +
        {{ partial('user/console/menu') }}
        +
        +
        +
        + 关注订阅 +
        +
        + {% 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/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/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 similarity index 96% rename from app/Models/WeChatSubscribe.php rename to app/Models/WechatSubscribe.php index f1ce2201..e1633caa 100644 --- a/app/Models/WeChatSubscribe.php +++ b/app/Models/WechatSubscribe.php @@ -4,7 +4,7 @@ namespace App\Models; use Phalcon\Mvc\Model\Behavior\SoftDelete; -class WeChatSubscribe extends Model +class WechatSubscribe extends Model { /** 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/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 similarity index 96% rename from app/Services/WeChat.php rename to app/Services/Wechat.php index 55f5ba9d..ed106111 100644 --- a/app/Services/WeChat.php +++ b/app/Services/Wechat.php @@ -5,13 +5,13 @@ namespace App\Services; use EasyWeChat\Factory; use Phalcon\Logger\Adapter\File as FileLogger; -class WeChat extends Service +class Wechat extends Service { /** * @var FileLogger */ - protected $logger; + public $logger; public function __construct() { diff --git a/app/Services/Wechat/Notice/AccountLogin.php b/app/Services/Wechat/Notice/AccountLogin.php new file mode 100644 index 00000000..f05b9191 --- /dev/null +++ b/app/Services/Wechat/Notice/AccountLogin.php @@ -0,0 +1,35 @@ + $first, + 'remark' => $remark, + 'keyword1' => $params['login_region'], + 'keyword2' => date('Y-m-d H:i', $params['login_time']), + ]; + + $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..3fd1202a --- /dev/null +++ b/app/Services/Wechat/Notice/ConsultReply.php @@ -0,0 +1,37 @@ +open_id; + + $templateId = $this->getTemplateId($this->templateCode); + + $first = sprintf('%s 回复了你的咨询!', $params['replier']['name']); + + $remark = '如果还有其它疑问,请和我们保持联系哦!'; + + $params = [ + 'first' => $first, + 'remark' => $remark, + 'keyword1' => $params['course']['title'], + ]; + + return $this->send($openId, $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/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..78fc9f8f 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 * * * *'); From b090f3778e6874d9813e8384c1e8b404a0e912b5 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Mon, 21 Dec 2020 16:03:25 +0800 Subject: [PATCH 42/44] =?UTF-8?q?=E7=AC=AC=E4=B8=89=E6=96=B9=E5=BC=80?= =?UTF-8?q?=E6=94=BE=E7=99=BB=E5=BD=95=E5=A2=9E=E5=8A=A0=E5=85=AC=E4=BC=97?= =?UTF-8?q?=E5=8F=B7=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Console/Tasks/DeliverTask.php | 4 ++-- app/Console/Tasks/NoticeTask.php | 2 +- app/Console/Tasks/RefundTask.php | 4 ++-- app/Http/Home/Services/Connect.php | 14 ++++++++++++++ app/Http/Home/Services/WechatOfficialAccount.php | 14 ++++++++------ app/Services/Wechat/Notice/AccountLogin.php | 12 ++++++++++-- scheduler.php | 3 --- 7 files changed, 37 insertions(+), 16 deletions(-) diff --git a/app/Console/Tasks/DeliverTask.php b/app/Console/Tasks/DeliverTask.php index 560c8449..ad7c846b 100644 --- a/app/Console/Tasks/DeliverTask.php +++ b/app/Console/Tasks/DeliverTask.php @@ -26,7 +26,7 @@ class DeliverTask extends Task { $logger = $this->getLogger('order'); - $tasks = $this->findTasks(); + $tasks = $this->findTasks(30); if ($tasks->count() == 0) { return; @@ -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/NoticeTask.php b/app/Console/Tasks/NoticeTask.php index 4c69fcf6..c4a385d4 100644 --- a/app/Console/Tasks/NoticeTask.php +++ b/app/Console/Tasks/NoticeTask.php @@ -20,7 +20,7 @@ class NoticeTask extends Task { $logger = $this->getLogger('notice'); - $tasks = $this->findTasks(500); + $tasks = $this->findTasks(300); if ($tasks->count() == 0) { return; diff --git a/app/Console/Tasks/RefundTask.php b/app/Console/Tasks/RefundTask.php index ad69ec3c..1697a4e1 100644 --- a/app/Console/Tasks/RefundTask.php +++ b/app/Console/Tasks/RefundTask.php @@ -29,7 +29,7 @@ class RefundTask extends Task { $logger = $this->getLogger('refund'); - $tasks = $this->findTasks(); + $tasks = $this->findTasks(30); if ($tasks->count() == 0) { return; @@ -259,7 +259,7 @@ class RefundTask extends Task } /** - * 处理测试订单退款 + * 处理赞赏订单退款 * * @param OrderModel $order */ 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 index b205ccee..43514f80 100644 --- a/app/Http/Home/Services/WechatOfficialAccount.php +++ b/app/Http/Home/Services/WechatOfficialAccount.php @@ -50,7 +50,7 @@ class WechatOfficialAccount extends Service { $service = new WechatService(); - $service->logger->info('Received Message:' . json_encode($message)); + $service->logger->debug('Received Message ' . json_encode($message)); switch ($message['MsgType']) { case 'event': @@ -74,7 +74,7 @@ class WechatOfficialAccount extends Service return $this->handleLocationEvent($message); break; default: - return $message['Event']; + return $this->emptyReplyMessage(); break; } break; @@ -118,7 +118,7 @@ class WechatOfficialAccount extends Service $this->handleSubscribeRelation($userId, $openId); - return new TextMessage("欢迎您的光临!"); + return new TextMessage('开心呀,我们又多了一个小伙伴!'); } protected function handleUnsubscribeEvent($message) @@ -134,7 +134,7 @@ class WechatOfficialAccount extends Service $subscribe->update(); } - return new TextMessage("我们又少了一个可爱的小伙伴!"); + return new TextMessage('伤心呀,我们又少了一个小伙伴!'); } protected function handleScanEvent($message) @@ -198,20 +198,22 @@ class WechatOfficialAccount extends Service protected function emptyReplyMessage() { - return new TextMessage(""); + return new TextMessage(''); } protected function defaultReplyMessage() { - return new TextMessage("没有匹配的服务,如有需要请联系客服!"); + return new TextMessage('没有匹配的服务,如有需要请联系客服!'); } protected function handleSubscribeRelation($userId, $openId) { $validator = new UserValidator(); + $validator->checkUser($userId); $subscribeRepo = new WechatSubscribeRepo(); + $subscribe = $subscribeRepo->findByOpenId($openId); if ($subscribe) { diff --git a/app/Services/Wechat/Notice/AccountLogin.php b/app/Services/Wechat/Notice/AccountLogin.php index f05b9191..b4e901e7 100644 --- a/app/Services/Wechat/Notice/AccountLogin.php +++ b/app/Services/Wechat/Notice/AccountLogin.php @@ -20,11 +20,19 @@ class AccountLogin extends WechatNotice $first = '你好,登录系统成功!'; $remark = '如果非本人操作,请立即修改密码哦!'; + $loginRegion = implode('/', [ + $params['login_region']['country'], + $params['login_region']['province'], + $params['login_region']['city'], + ]); + + $loginTime = date('Y-m-d H:i:s', $params['login_time']); + $params = [ 'first' => $first, 'remark' => $remark, - 'keyword1' => $params['login_region'], - 'keyword2' => date('Y-m-d H:i', $params['login_time']), + 'keyword1' => $loginRegion, + 'keyword2' => $loginTime, ]; $templateId = $this->getTemplateId($this->templateCode); diff --git a/scheduler.php b/scheduler.php index 78fc9f8f..f363c2e7 100644 --- a/scheduler.php +++ b/scheduler.php @@ -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); From 8af866ab642be40addee3297f30b50432cfaa156 Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Mon, 21 Dec 2020 20:15:33 +0800 Subject: [PATCH 43/44] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E6=9D=83=E9=99=90=E5=92=8C=E4=BD=8E=E5=93=81=E8=B4=A8=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=97=A0=E6=B3=95=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Admin/Services/AuthNode.php | 12 +++++++++--- app/Http/Admin/Services/Role.php | 21 +++++++++++++++++---- app/Services/ChapterVod.php | 5 ++++- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app/Http/Admin/Services/AuthNode.php b/app/Http/Admin/Services/AuthNode.php index 52d6bf1e..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', diff --git a/app/Http/Admin/Services/Role.php b/app/Http/Admin/Services/Role.php index 86bade39..c9459b33 100644 --- a/app/Http/Admin/Services/Role.php +++ b/app/Http/Admin/Services/Role.php @@ -143,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)) { @@ -175,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/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; } } From 61218a22c3a984003feb84780fea1d0af6f0de2d Mon Sep 17 00:00:00 2001 From: xiaochong0302 Date: Wed, 23 Dec 2020 11:44:21 +0800 Subject: [PATCH 44/44] =?UTF-8?q?=E5=A2=9E=E5=8A=A0v1.2.2=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 20 +++++++++++++++++++ README.md | 8 +++++--- .../Home/Services/WechatOfficialAccount.php | 5 ++++- app/Library/AppInfo.php | 2 +- app/Services/Wechat/Notice/ConsultReply.php | 8 +++----- 5 files changed, 33 insertions(+), 10 deletions(-) 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开源协议,致力开源网课系统,开源网校系统,开源在线教育系统。 -![](https://img.shields.io/static/v1?label=release&message=1.2.1&color=blue) -![](https://img.shields.io/static/v1?label=stars&message=112&color=blue) -![](https://img.shields.io/static/v1?label=forks&message=41&color=blue) +![](https://img.shields.io/static/v1?label=release&message=1.2.2&color=blue) +![](https://img.shields.io/static/v1?label=stars&message=136&color=blue) +![](https://img.shields.io/static/v1?label=forks&message=50&color=blue) ![](https://img.shields.io/static/v1?label=license&message=GPL-2.0&color=blue) #### 系统功能 @@ -86,3 +86,5 @@ Tips: 测试支付请用手机号注册一个新账户,以便接收订单通 - 系统定制 - 企业授权 +毫无保留的真开源不容易,如果对你有帮助,请给我们 **STAR** !!! + diff --git a/app/Http/Home/Services/WechatOfficialAccount.php b/app/Http/Home/Services/WechatOfficialAccount.php index 43514f80..ea19fd02 100644 --- a/app/Http/Home/Services/WechatOfficialAccount.php +++ b/app/Http/Home/Services/WechatOfficialAccount.php @@ -6,7 +6,7 @@ use App\Models\WechatSubscribe as WechatSubscribeModel; use App\Repos\WechatSubscribe as WechatSubscribeRepo; use App\Services\Wechat as WechatService; use App\Validators\User as UserValidator; -use EasyWechat\Kernel\Messages\Text as TextMessage; +use EasyWeChat\Kernel\Messages\Text as TextMessage; class WechatOfficialAccount extends Service { @@ -151,14 +151,17 @@ class WechatOfficialAccount extends Service protected function handleClickEvent($message) { + $this->defaultReplyMessage(); } protected function handleViewEvent($message) { + $this->defaultReplyMessage(); } protected function handleLocationEvent($message) { + $this->defaultReplyMessage(); } protected function handleTextReply($message) 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/Services/Wechat/Notice/ConsultReply.php b/app/Services/Wechat/Notice/ConsultReply.php index 3fd1202a..f33db7f9 100644 --- a/app/Services/Wechat/Notice/ConsultReply.php +++ b/app/Services/Wechat/Notice/ConsultReply.php @@ -17,10 +17,6 @@ class ConsultReply extends WechatNotice */ public function handle(WechatSubscribeModel $subscribe, array $params) { - $openId = $subscribe->open_id; - - $templateId = $this->getTemplateId($this->templateCode); - $first = sprintf('%s 回复了你的咨询!', $params['replier']['name']); $remark = '如果还有其它疑问,请和我们保持联系哦!'; @@ -31,7 +27,9 @@ class ConsultReply extends WechatNotice 'keyword1' => $params['course']['title'], ]; - return $this->send($openId, $templateId, $params); + $templateId = $this->getTemplateId($this->templateCode); + + return $this->send($subscribe->open_id, $templateId, $params); } }