root 4 yıl önce
ebeveyn
işleme
edcb89e9dc
100 değiştirilmiş dosya ile 11335 ekleme ve 5 silme
  1. 15 4
      .gitignore
  2. 1 1
      README.md
  3. 11 0
      apidoc.json
  4. 76 0
      app/Console/Commands/ActionTrigger/BatchWechatMaterial/DelWechatMaterial.php
  5. 75 0
      app/Console/Commands/ActionTrigger/BatchWechatMaterial/PreviewWechatMaterial.php
  6. 139 0
      app/Console/Commands/ActionTrigger/BatchWechatMaterial/SyncZsWechatMaterial.php
  7. 68 0
      app/Console/Commands/ActionTrigger/BatchWechatMaterial/testWechatMaterial.php
  8. 107 0
      app/Console/Commands/ActionTrigger/BatchWechatMaterial/testWechatMaterialMsgSend.php
  9. 59 0
      app/Console/Commands/BatchWechatMaterial/SendCustomWechatMaterial.php
  10. 88 0
      app/Console/Commands/BatchWechatMaterial/SyncWechatMaterialStatistics.php
  11. 107 0
      app/Console/Commands/GZH/FansStat.php
  12. 131 0
      app/Console/Commands/GZH/ForceUserProperty.php
  13. 33 0
      app/Console/Commands/Inspire.php
  14. 71 0
      app/Console/Commands/Recommend/UpdateHighQualityBooks.php
  15. 55 0
      app/Console/Commands/SendEmails.php
  16. 169 0
      app/Console/Commands/SmartPush/BookUpdatePush.php
  17. 107 0
      app/Console/Commands/SmartPush/CouponExpirePush.php
  18. 54 0
      app/Console/Commands/SmartPush/ForceSubscribeDelayMsg.php
  19. 229 0
      app/Console/Commands/SmartPush/KeepContinueRead.php
  20. 301 0
      app/Console/Commands/SmartPush/KeepContinueReadV2.php
  21. 312 0
      app/Console/Commands/SmartPush/KeepContinueReadV3.php
  22. 327 0
      app/Console/Commands/SmartPush/RfmPush.php
  23. 195 0
      app/Console/Commands/SmartPush/remindSign.php
  24. 115 0
      app/Console/Commands/Test3.php
  25. 76 0
      app/Console/Commands/Tool/ContinueReadTool.php
  26. 462 0
      app/Console/Commands/Wechat/AdReport.php
  27. 447 0
      app/Console/Commands/Wechat/AdReportTest.php
  28. 430 0
      app/Console/Commands/Wechat/GdtAdReport.php
  29. 166 0
      app/Console/Commands/Wechat/ReportRetry.php
  30. 166 0
      app/Console/Commands/test2.php
  31. 92 0
      app/Console/Commands/test_queue.php
  32. 124 0
      app/Console/Kernel.php
  33. 17 0
      app/Consts/Channel/ChannelConst.php
  34. 8 0
      app/Events/Event.php
  35. 51 0
      app/Exceptions/Handler.php
  36. 25 0
      app/Http/Controllers/AdminController.php
  37. 89 0
      app/Http/Controllers/Auth/AuthController.php
  38. 32 0
      app/Http/Controllers/Auth/PasswordController.php
  39. 46 0
      app/Http/Controllers/Channel/BaseController.php
  40. 13 0
      app/Http/Controllers/Controller.php
  41. 19 0
      app/Http/Controllers/HomeController.php
  42. 52 0
      app/Http/Controllers/Queue/Template/AddNewsTasksController.php
  43. 62 0
      app/Http/Controllers/Queue/Template/AddTemplateTasksController.php
  44. 62 0
      app/Http/Controllers/Queue/Template/AddTextsTasksController.php
  45. 27 0
      app/Http/Controllers/Queue/Template/QueuedController.php
  46. 25 0
      app/Http/Controllers/QueuedController.php
  47. 39 0
      app/Http/Controllers/Wechat/Api/MPverifysController.php
  48. 77 0
      app/Http/Controllers/Wechat/Api/TestApisController.php
  49. 295 0
      app/Http/Controllers/Wechat/Api/WechatInnerApisController.php
  50. 78 0
      app/Http/Controllers/Wechat/Api/WechatOpApisController.php
  51. 605 0
      app/Http/Controllers/Wechat/Api/WechatOuterApisController.php
  52. 68 0
      app/Http/Controllers/Wechat/Command/GzhBanAlertController.php
  53. 405 0
      app/Http/Controllers/Wechat/GzhMsg/GzhMsgsController.php
  54. 379 0
      app/Http/Controllers/Wechat/Material/MaterialsController.php
  55. 132 0
      app/Http/Controllers/Wechat/Menu/MenusController.php
  56. 112 0
      app/Http/Controllers/Wechat/Oauth/UserOauthsController.php
  57. 716 0
      app/Http/Controllers/Wechat/OfficialAccount/OfficialInteractiveEventController.php
  58. 20 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/ForceSubscribeUsersTransformer.php
  59. 34 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialAccountTransformer.php
  60. 28 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialImgtextUrlsTransformer.php
  61. 15 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialInteractiveEventTextTransformer.php
  62. 12 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialInteractiveEventTransformer.php
  63. 15 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialMenuTransformer.php
  64. 18 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/WechatPublicTemplatesTransformer.php
  65. 33 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/WechatTemplatesMsgTransformer.php
  66. 22 0
      app/Http/Controllers/Wechat/OfficialAccount/Transformers/WechatTemplatesTransformer.php
  67. 347 0
      app/Http/Controllers/Wechat/Pay/PaysController.php
  68. 35 0
      app/Http/Controllers/Wechat/Qrcode/QrcodesController.php
  69. 112 0
      app/Http/Controllers/Wechat/Staff/NewsWorksController.php
  70. 148 0
      app/Http/Controllers/Wechat/Staff/StaffsController.php
  71. 101 0
      app/Http/Controllers/Wechat/Staff/TextsWorksController.php
  72. 36 0
      app/Http/Controllers/Wechat/Statistic/NewsStatisticsController.php
  73. 47 0
      app/Http/Controllers/Wechat/Statistic/StatisticsController.php
  74. 355 0
      app/Http/Controllers/Wechat/Template/TemplateBasesController.php
  75. 106 0
      app/Http/Controllers/Wechat/Template/TemplateWorksController.php
  76. 63 0
      app/Http/Controllers/Wechat/Template/TemplatesController.php
  77. 87 0
      app/Http/Controllers/Wechat/ThirdWx/MsgsController.php
  78. 265 0
      app/Http/Controllers/Wechat/ThirdWx/OauthPlatformsController.php
  79. 26 0
      app/Http/Controllers/Wechat/ThirdWx/data_format.php
  80. 53 0
      app/Http/Controllers/Wechat/User/UserInfosController.php
  81. 87 0
      app/Http/Controllers/WechatBaseApisController.php
  82. 172 0
      app/Http/Controllers/WechatController.php
  83. 103 0
      app/Http/Controllers/WechatOpController.php
  84. 58 0
      app/Http/Kernel.php
  85. 30 0
      app/Http/Middleware/Authenticate.php
  86. 17 0
      app/Http/Middleware/EncryptCookies.php
  87. 26 0
      app/Http/Middleware/RedirectIfAuthenticated.php
  88. 17 0
      app/Http/Middleware/VerifyCsrfToken.php
  89. 18 0
      app/Http/Models/WechatGroup.php
  90. 25 0
      app/Http/Models/WechatGroupApi.php
  91. 86 0
      app/Http/Models/WechatGroupGzh.php
  92. 10 0
      app/Http/Requests/Request.php
  93. 113 0
      app/Http/routes.php
  94. 55 0
      app/Jobs/ActionTrigger.php
  95. 93 0
      app/Jobs/ForceUserProperty.php
  96. 21 0
      app/Jobs/Job.php
  97. 33 0
      app/Jobs/Queue.php
  98. 36 0
      app/Jobs/QueuedTest.php
  99. 115 0
      app/Jobs/SendBatchWechatMaterial.php
  100. 0 0
      app/Jobs/SendNews.php

+ 15 - 4
.gitignore

@@ -1,6 +1,17 @@
-# ---> Laravel
-/bootstrap/compiled.php
-.env.*.php
-.env.php
+mestead.yaml
+Homestead.json
+/idea
 .env
+/storage/app
+/storage/framework
+/storage/logs
+composer.lock
+/public/log/
+.buildpath
+.project
+.settings
+.DS_Store
+.idea
+.idea/
+
 

+ 1 - 1
README.md

@@ -1,2 +1,2 @@
-# wangdu_wechat
+# wangdu_modify_wechat
 

+ 11 - 0
apidoc.json

@@ -0,0 +1,11 @@
+{
+"name": "阅读云",
+"version": "1.0.0",
+"description": "接口文档",
+"title": "阅读云微信接口文档",
+"url": "http://ydy.wangluogudu.com",
+"template": {
+"withCompare": true,
+"withGenerator": true
+}
+}

+ 76 - 0
app/Console/Commands/ActionTrigger/BatchWechatMaterial/DelWechatMaterial.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace App\Console\Commands\ActionTrigger\BatchWechatMaterial;
+use App\Http\Controllers\WechatController;
+use Illuminate\Console\Command;
+use DB;
+
+/**
+ * 删除微信素材
+ *
+ */
+class DelWechatMaterial extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'ActionTrigger:del_zs_wechat_material  {data}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '删除微信素材';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        \Log::info('del_zs_wechat_material start:');
+        $data = $this->argument('data');
+        try{
+            $this->start($data['data']);
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+
+        \Log::info('del_zs_wechat_material end:');
+    }
+
+    public function start($data){
+
+
+        \Log::info('$data');\Log::info($data);
+        //检查参数
+        if(!($data['appid']&&$data['media_id'])){
+            \Log::info('params_empty');
+            return;
+        }
+        //微信删除素材接口
+        try{
+            $WechatController = new WechatController($data['appid']);
+            $res = $WechatController->app->material->delete($data['media_id']);
+            \Log::info($res);
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+
+        return;
+    }
+}

+ 75 - 0
app/Console/Commands/ActionTrigger/BatchWechatMaterial/PreviewWechatMaterial.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace App\Console\Commands\ActionTrigger\BatchWechatMaterial;
+use App\Http\Controllers\WechatController;
+use Illuminate\Console\Command;
+
+/**
+ * 预览微信素材
+ *
+ */
+class PreviewWechatMaterial extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'ActionTrigger:preview_zs_wechat_material  {data}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '预览微信素材';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        \Log::info('ActionTrigger::preview_wechat_material_start');
+        $data = $this->argument('data');
+        try{
+            $this->start($data['data']);
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+        \Log::info('ActionTrigger::preview_wechat_material_end:');
+    }
+
+    public function start($data){
+
+    	\Log::info('$data');\Log::info($data);
+        // 检查参数
+        if(!($data['appid']&&$data['media_id']&&$data['openid'])){
+            \Log::info('params_empty');
+            return;
+        }
+        //调用预览接口
+        try{
+            $WechatController = new WechatController($data['appid']);
+            $broadcast = $WechatController->app->broadcast;
+            $res = $broadcast->previewNews($data['media_id'], $data['openid']);
+            \Log::info('$res');\Log::info($res);
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+        return;
+
+    }
+  
+}

+ 139 - 0
app/Console/Commands/ActionTrigger/BatchWechatMaterial/SyncZsWechatMaterial.php

@@ -0,0 +1,139 @@
+<?php
+
+namespace App\Console\Commands\ActionTrigger\BatchWechatMaterial;
+
+use App\Modules\OfficialAccount\Services\CustomMsgService;
+use App\Modules\WechatMaterial\Models\BatchWechatMaterial;
+use App\Modules\WechatMaterial\Models\WechatMaterial;
+use App\Modules\WechatMaterial\Models\WechatMaterialSendMsg;
+use Illuminate\Console\Command;
+
+/**
+ * 同步微信素材
+ *
+ */
+class SyncZsWechatMaterial extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'ActionTrigger:sync_zs_wechat_material {data}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '同步微信素材';
+
+    private $_task = 'ActionTrigger:sync_zs_wechat_material ';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        // 获取数据
+        $reqData = $this->argument('data');
+        $data    = getProp($reqData, 'data', []);
+        \Log::info($this->_task . 'request_data');
+        \Log::info($data);
+
+        \Log::info($this->_task . '[start]');
+        $this->start($data);
+        \Log::info($this->_task . '[end]');
+    }
+
+    public function start($data)
+    {
+        // 获取参数
+        $appId     = getProp($data, 'appid');
+        $batchId   = getProp($data, 'batch_id', 0);
+        $channelId = getProp($data, 'distribution_channel_id', 0);
+        if (!$appId || !$batchId || !$channelId) {
+            return false;
+        }
+
+        // 获取msg信息,未查询到数据或者media_id已存在则不再继续往下执行
+        $msgInfo = WechatMaterialSendMsg::getMsgByAppBatchChannelId($appId, $batchId, $channelId);
+        \Log::info($this->_task . 'msgInfo');
+        \Log::info($msgInfo);
+        if (!$msgInfo) {
+            return false;
+        }
+
+        // 获取素材数据
+        $materials = WechatMaterial::getWeChatMaterials($batchId);
+        $materials = $materials ? $materials->toArray() : [];
+        \Log::info($this->_task . 'materials_ids');
+        \Log::info(array_column($materials, 'id'));
+        if (!$materials) {
+            return false;
+        }
+
+        // 筛选出内容中的所有图片
+        $contents = implode(',', array_column($materials, 'content'));
+        $images   = CustomMsgService::extract_content_images($contents);
+        \Log::info($this->_task . 'extract_content_image_urls');
+        \Log::info($images);
+
+        // 上传内容中的图片,替换内容中图片链接
+        $upImagesRes = CustomMsgService::multi_upload_material_imgs($appId, $images);
+        [$oldUrls, $newUrls] = [array_column($upImagesRes['urls'], 'old_url'), array_column($upImagesRes['urls'], 'new_url')];
+        $materials = CustomMsgService::replace_content_images($materials, $oldUrls, $newUrls);
+
+        // 批量上传缩略图
+        $thumbImages = array_column($materials, 'cover_url');
+        $upThumbsRes = CustomMsgService::multi_upload_material_imgs($appId, $thumbImages, 'thumb');
+
+        // 上传article
+        $upArticleRes = CustomMsgService::upload_articles($channelId, $msgInfo, $materials, $upThumbsRes['urls']);
+        $wxMediaId    = getProp($upArticleRes['data'], 'media_id');
+        if ($wxMediaId) {
+            // 更新media_id
+            WechatMaterialSendMsg::where('id', getProp($msgInfo, 'id'))->update([
+                'wechat_media_id' => $wxMediaId,
+                'upload_result'   => json_encode([
+                    'up_images'   => $upImagesRes['urls'],
+                    'up_thumbs'   => $upThumbsRes['urls'],
+                    'up_articles' => $upArticleRes
+                ]),
+                'status'          => 'push_wechat_material',
+                'updated_at'      => date('Y-m-d H:i:s')
+            ]);
+
+            // 更新batch同步状态
+            BatchWechatMaterial::where('id', $batchId)->update([
+                'status'     => 'push_wechat_material',
+                'updated_at' => date('Y-m-d H:i:s')
+            ]);
+        } else {
+            $msg = 'upload fail';
+            if ($upArticleRes && !$upArticleRes['code']) $msg = $upArticleRes['msg'];
+            if (getProp($upThumbsRes['error'], 'thumb')) $msg = getProp($upThumbsRes['error'], 'thumb');
+            if (getProp($upImagesRes['error'], 'common')) $msg = getProp($upImagesRes['error'], 'common');
+
+            // 更新上传结果
+            WechatMaterialSendMsg::where('id', getProp($msgInfo, 'id'))->update([
+                'upload_result' => $msg,
+                'status'        => 'fail_push_wechat_material',
+                'updated_at'    => date('Y-m-d H:i:s')
+            ]);
+        }
+    }
+
+}

+ 68 - 0
app/Console/Commands/ActionTrigger/BatchWechatMaterial/testWechatMaterial.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace App\Console\Commands\ActionTrigger\BatchWechatMaterial;
+use App\Http\Controllers\WechatController;
+use Illuminate\Console\Command;
+use DB;
+
+/**
+ * 删除微信素材
+ *
+ */
+class testWechatMaterial extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'get_wechat_material  {--appid=}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '删除微信素材';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $appid = $this->option('appid');
+
+        try{
+            $WechatController = new WechatController($appid);
+            $res = $WechatController->app->material->lists('news', 10, 20);
+            dump($res['item'][0]['media_id']);
+            dump($res['item'][1]['media_id']);
+            dump($res['item'][2]['media_id']);
+            dump($res['item'][3]['media_id']);
+            dump($res['item'][4]['media_id']);
+            dump($res['item'][5]['media_id']);
+            dump($res['item'][6]['media_id']);
+            dump($res['item'][7]['media_id']);
+            dump($res['item'][8]['media_id']);
+            dump($res['item'][9]['media_id']);
+//            \Log::info('$res');
+//            \Log::info($res);
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+
+        return;
+    }
+}

+ 107 - 0
app/Console/Commands/ActionTrigger/BatchWechatMaterial/testWechatMaterialMsgSend.php

@@ -0,0 +1,107 @@
+<?php
+
+namespace App\Console\Commands\ActionTrigger\BatchWechatMaterial;
+use App\Http\Controllers\WechatController;
+use Illuminate\Console\Command;
+use DB;
+
+/**
+ * 删除微信素材
+ *
+ */
+class testWechatMaterialMsgSend extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'testWechatMaterialMsgSend';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'testWechatMaterialMsgSend';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        \Log::info('SendBatchWechatMaterial start');
+        $param = [
+            'appid'=>'wxdbc486f1b4f6a8c3',
+            'media_id'=>'XYhErlq3w-hlmwqOB4O5So616GrRvIOD8MoBjI8IlSY',
+            'openids'=>[
+                '0'=>'oAcqg1LRHNKN2jaEkJ5v56HOwPEQ',
+                '1'=>'oq6ID0m4KrnpHwwZUh9BBmFsW_18',
+            ],
+            'task_id'=>2,
+        ];
+        $this->start($param);
+        \Log::info('SendBatchWechatMaterial end');
+
+    }
+
+    public function start($param)
+    {
+        \Log::info('$param');\Log::info($param);
+
+        //检查参数
+        if(!($param['appid']&&$param['media_id']&&$param['openids']&&$param['task_id'])){
+            \Log::info('params_empty');
+            return;
+        }
+        //服务号高级发送接口openid限定数量为2-10000之间
+        if(!is_array($param['openids'])||count($param['openids'])>10000||count($param['openids'])<2){
+            \Log::info('SendBatchWechatMaterial:over_open_num_limt');
+            return;
+        }
+
+        //调用服务号高级发送接口
+        try{
+            $WechatController = new WechatController($param['appid']);
+            $res = $WechatController->app->broadcast->sendNews($param['media_id'],$param['openids']);
+            \Log::info($res);
+            //发送失败
+            if($res['errcode']!=0){
+                \Log::info("SendBatchWechatMateria {$param['task_id']} errcode:{$res['errcode']} errmsg:{{$res['errcode']}}");
+                return;
+            }
+            //保存msg_id和msg_data_id
+            DB::connection('api_mysql')
+                ->table('wechat_material_send_msgs')
+                ->where('id',$param['task_id'])
+                ->update(['wechat_msg_id'=>$res['msg_id'],'wechat_msg_data_id'=>$res['msg_data_id']]);
+            //发送成功更新发送人数
+            DB::connection('api_mysql')
+                ->table('wechat_material_send_msgs')
+                ->where('id',$param['task_id'])
+                ->increment('send_user_num',count($param['openids']));
+            //若为最后一条消息则更新发送状态为发送完成
+            if($param['type']=='last_task'){
+                DB::connection('api_mysql')
+                    ->table('wechat_material_send_msgs')
+                    ->where('id',$param['task_id'])
+                    ->update(['status'=>'has_send','updated_at'=>date('Y-m-d H:i:s')]);
+            }
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+        return;
+    }
+}

+ 59 - 0
app/Console/Commands/BatchWechatMaterial/SendCustomWechatMaterial.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace App\Console\Commands\BatchWechatMaterial;
+
+use Illuminate\Console\Command;
+use App\Modules\WechatMaterial\Services\BatchWechatCustomSendService;
+use Redis;
+
+/**
+ * 发送微信素材客服消息
+ *
+ */
+class SendCustomWechatMaterial extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'BatchWechatMaterial:SendCustomWechatMaterial';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '发送微信素材客服消息';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->start();
+    }
+
+    public function start(){
+    	\Log::info('send_custom_wechat_material_start');
+    	switch_global_db('api_mysql','default');
+
+    	BatchWechatCustomSendService::toCheckAndSendBatchCustomMsg();
+
+    	switch_global_db('mysql','default');
+    	\Log::info('send_custom_wechat_material_end:');
+    }
+  
+}

+ 88 - 0
app/Console/Commands/BatchWechatMaterial/SyncWechatMaterialStatistics.php

@@ -0,0 +1,88 @@
+<?php
+
+namespace App\Console\Commands\BatchWechatMaterial;
+
+use App\Http\Controllers\Wechat\Statistic\NewsStatisticsController;
+use App\Http\Controllers\WechatController;
+use App\Modules\User\Services\UserService;
+use Illuminate\Console\Command;
+use App\Modules\WechatMaterial\Services\WechatMaterialSendMsgService;
+use Illuminate\Support\Facades\DB;
+use Redis;
+
+/**
+ * 同步微信素材统计信息
+ *
+ */
+class SyncWechatMaterialStatistics extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'sync_wechat_material_statistics';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '同步微信素材统计信息';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->start();
+    }
+
+    public function start()
+    {
+        myLog('SyncWechatMaterialStatistics')->notice('-------  查询群发消息送达数开始');
+
+
+        #region 短网址测试
+//        $url = $WechatController->app->url;
+//        $shortUrl = $url->shorten('https://m.ycsd.cn/');
+//        var_dump($shortUrl);
+        #endregion
+
+        #region 基本框架 为确保公众号数据已完成统计和处理,请于每天上午8点后查询公众号前一天的数据。
+//        $queryDay = date("Y-m-d",strtotime('-1 day', time()));
+//        WechatMaterialSendMsgService::updateFirstDayMaterialUV($queryDay);
+        #endregion
+
+        $WechatController = new WechatController('wxdbc486f1b4f6a8c3');
+        $stats            = $WechatController->app->stats;
+        $r                = $stats->articleSummary('2020-07-14', '2020-07-14');
+
+
+        var_dump($r);
+        var_dump(json_decode($r, true)['list']);
+        $list = json_decode($r, true)['list'];
+        foreach ($list as $item) {
+            if ($item['msgid'] != '') {
+                var_dump($item['msgid']);
+            }
+        }
+
+
+        myLog('SyncWechatMaterialStatistics')->notice('-------  查询群发消息送达数结束');
+    }
+
+
+}

+ 107 - 0
app/Console/Commands/GZH/FansStat.php

@@ -0,0 +1,107 @@
+<?php
+
+namespace App\Console\Commands\GZH;
+
+use Illuminate\Console\Command;
+use Redis;
+use WechatOP;
+use DB;
+use Log;
+
+class FansStat extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'gzh:fansStat';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '公众号后台获取粉丝数据';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $now_date = date('Y-m-d');
+        $now_time = date('Y-m-d H:i:s');
+        $yesterday = date('Y-m-d',strtotime('-1 day'));
+        print_r("====start gzh:fansStat deal {$yesterday}====".date('Y-m-d H:i:s')."\r\n");
+        Log::info("====start gzh:fansStat deal {$yesterday}====");
+
+        $official_accounts = DB::table('wangdu.official_accounts')->where('is_auth',1)->where('is_enabled',1)->get();
+        foreach($official_accounts as $official_account)
+        {
+            $official_account_id = $official_account->id;
+
+            if(DB::table('wangdu.official_account_stats')->where('official_account_id',$official_account_id)->where('cumulate_user_updated_at',$yesterday)->count()) continue;
+
+            $appid = $official_account->appid;
+            $redis_key = '[wechat_op.common.component_refresh_token.'.$appid.']';
+            $component_refresh_token = Redis::Get($redis_key);
+
+            $options = [
+                'app_id'    => $appid,
+                'secret'    => env('WECHAT_OP_SECRET'),   // 仅适用于 单独配置公众号
+                'token'     => env('WECHAT_OP_TOKEN'),   // 仅适用于 单独配置公众号
+                'aes_key'   => env('WECHAT_OP_AES_KEY'),   // 仅适用于 单独配置公众号
+                'auth_type' => 'COMPONENT', // COMPONENT 开放平台授权公众号,MANUAL 单独配置公众号
+                'component_refresh_token' => $component_refresh_token,   // 授权回调时获取的 authorizer_refresh_token,仅适用于 开放品台授权公众号
+                'cache'   => [
+                    'driver' => 'redis',   // redis, filesystem, laravel
+                    'dir' => storage_path('tmp') // 只有为filesystem时候这个目录才有效
+                ],
+            ];
+            try{
+                $app = WechatOP::app($options);
+
+                $stats = $app->stats;
+                $user_stats = $stats->userCumulate($yesterday,$yesterday);
+                
+                $cumulate_user_num = 0; // 初始化
+                foreach ($user_stats->list as $item)
+                {
+                    $cumulate_user_num = $item['cumulate_user'];
+                    $cumulate_user_updated_at = $item['ref_date'];
+                }
+                $updated_at = $created_at = $now_time;
+                //更新日统计todo
+
+                $stat = DB::table('wangdu.official_account_stats')->where('official_account_id',$official_account_id)->first();
+                //更新累计统计
+                if($stat)
+                {
+                    DB::table('wangdu.official_account_stats')->where('official_account_id',$official_account_id)->update(compact('cumulate_user_num','cumulate_user_updated_at','updated_at'));
+                }else{
+                    DB::table('wangdu.official_account_stats')->insert(compact('official_account_id','cumulate_user_num','cumulate_user_updated_at','updated_at','created_at'));
+                }
+
+
+            }catch (\Exception $e){
+
+                Log::info("appid:".$appid."  error:".$e->getMessage());
+            };
+
+        }
+        print_r("====end gzh:fansStat deal {$yesterday}====".date('Y-m-d H:i:s')."\r\n");
+        Log::info("====end gzh:fansStat deal {$yesterday}====".date('Y-m-d H:i:s'));
+    }
+}

+ 131 - 0
app/Console/Commands/GZH/ForceUserProperty.php

@@ -0,0 +1,131 @@
+<?php
+
+namespace App\Console\Commands\GZH;
+
+use Illuminate\Console\Command;
+use Redis;
+use WechatOP;
+use DB;
+use Log;
+
+class ForceUserProperty extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'gzh:force_user_property';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '公众号获取粉丝昵称';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $task_start_time = time();
+        print_r("====start gzh:force_user_property deal ====".date('Y-m-d H:i:s')."\r\n");
+        Log::info("====start gzh:fansStat deal ====");
+
+        $active_time = date('Y-m-d H:i:s', time() - 3600*52);
+        $update_user_num = 0;
+        $official_accounts = DB::connection('command_mysql')->select("select appid,count(1) from yueduyun.temp_force_subscribe_users t where last_interactive_time > '{$active_time}' and `is_subscribed` = 1 and not exists(select 1 from zhuishuyun_user.force_subscribe_user_properties where appid = t.appid and openid = t.openid) group by appid");
+        foreach($official_accounts as $official_account)
+        {
+            print_r("====appid====".$official_account->appid."\r\n");
+            $appid = $official_account->appid;
+            $redis_key = '[wechat_op.common.component_refresh_token.'.$appid.']';
+            $component_refresh_token = Redis::Get($redis_key);
+
+            $options = [
+                'app_id'    => $appid,
+                'secret'    => env('WECHAT_OP_SECRET'),   // 仅适用于 单独配置公众号
+                'token'     => env('WECHAT_OP_TOKEN'),   // 仅适用于 单独配置公众号
+                'aes_key'   => env('WECHAT_OP_AES_KEY'),   // 仅适用于 单独配置公众号
+                'auth_type' => 'COMPONENT', // COMPONENT 开放平台授权公众号,MANUAL 单独配置公众号
+                'component_refresh_token' => $component_refresh_token,   // 授权回调时获取的 authorizer_refresh_token,仅适用于 开放品台授权公众号
+                'cache'   => [
+                    'driver' => 'redis',   // redis, filesystem, laravel
+                    'dir' => storage_path('tmp') // 只有为filesystem时候这个目录才有效
+                ],
+            ];
+            $app = WechatOP::app($options);
+            $userService = $app->user;
+
+            $force_users = DB::connection('command_mysql')->select("select t.* from yueduyun.temp_force_subscribe_users t where appid = '{$appid}' and last_interactive_time > '{$active_time}' and `is_subscribed` = 1 and not exists(select 1 from zhuishuyun_user.force_subscribe_user_properties where appid = t.appid and openid=t.openid)");
+            $total = count($force_users);
+            $len = 100;//微信限制 最大100
+            $save_len = 400;
+            $openids = [];
+            $data = [];
+            foreach ($force_users as $k=>$_user)
+            {
+
+                $openids[] = $_user->openid;
+                if(count($openids) == $len || $k == $total - 1)//批量获取
+                {
+                    try{
+                        $users = $userService->batchGet($openids);
+                        $openids = [];
+                        foreach ($users->user_info_list as $item)
+                        {
+                            if(isset($item['nickname']))
+                            {
+                                $current_time = date('Y-m-d H:i:s');
+                                if($item['nickname'])
+                                {
+                                    $data[] = [
+                                        'openid'=>$item['openid'],
+                                        'appid'=>$appid,
+                                        'current_nickname'=>trim($item['nickname']),
+                                        'created_at'=>$current_time,
+                                        'updated_at'=>$current_time,
+                                    ];
+
+                                }
+                            }
+                        }
+
+                    }catch (\Exception $e){
+                        $error_msg = $e->getMessage();
+                        Log::error($appid .'failed '.$error_msg);
+
+                        if(strstr($error_msg,'component is not authorized by this account') || strstr($error_msg, 'api unauthorized'))
+                        {
+                            //未授权情况下不再采集
+                            break;
+                        }
+                    };
+                }
+                if(count($data) >= $save_len || $k == $total - 1)//批量存储
+                {
+                    $update_user_num += count($data);
+                    DB::connection('command_mysql')->table('zhuishuyun_user.force_subscribe_user_properties')->insert($data);
+                    $data = [];
+                }
+
+            }
+        }
+        $used_time = time() - $task_start_time;
+        print_r("====end gzh:force_user_property deal update user num {$update_user_num} used {$used_time} seconds====".date('Y-m-d H:i:s')."\r\n");
+        Log::info("====end gzh:force_user_property deal update user num {$update_user_num}  used {$used_time} seconds");
+    }
+}

+ 33 - 0
app/Console/Commands/Inspire.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+use Illuminate\Foundation\Inspiring;
+
+class Inspire extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'inspire';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Display an inspiring quote';
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->comment(PHP_EOL.Inspiring::quote().PHP_EOL);
+    }
+}

+ 71 - 0
app/Console/Commands/Recommend/UpdateHighQualityBooks.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace App\Console\Commands\Recommend;
+
+use Illuminate\Console\Command;
+use Redis;
+use Log;
+use App\Modules\Book\Models\BookConfig;
+
+class UpdateHighQualityBooks extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'UpdateHighQualityBooks';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '更新缓存优质书库';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        //统一在此脚本中维护缓存
+        print_r("======更新缓存优质书库 【任务执行开始】=====" . date("y-m-d H:i:s" . "\n"));
+        Log::info("======更新缓存优质书库 【任务执行开始】=====" . date("y-m-d H:i:s" . "\n"));
+
+        $channels = ['男频','女频'];
+        foreach ($channels as $channel_name)
+        {
+            if($channel_name == '女频') $key = 'female_high_reco_books';
+            if($channel_name == '男频') $key = 'male_high_reco_books';
+
+            //recommend_books
+            $books = BookConfig::getChannelHighRecommendBooks($channel_name);
+
+            $_high_reco_books = [];
+            foreach ($books as $book)
+            {
+                $encode_book = json_encode($book);
+                $_high_reco_books[$book->bid] = $encode_book;
+                $reco_books[$book->bid] = $encode_book;
+            }
+            Redis::del($key);
+            Redis::hmset($key,$_high_reco_books);
+        }
+        print_r("======更新缓存优质书库 【任务执行结束】=====" . date("y-m-d H:i:s" . "\n"));
+        Log::info("======更新缓存优质书库 【任务执行结束】=====" . date("y-m-d H:i:s" . "\n"));
+    }
+
+
+}

+ 55 - 0
app/Console/Commands/SendEmails.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\DripEmailer;
+use Illuminate\Console\Command;
+
+class SendEmails extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'email:send {user}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Send drip e-mails to a user';
+
+    /**
+     * The drip e-mail service.
+     *
+     * @var DripEmailer
+     */
+    protected $drip;
+
+    /**
+     * Create a new command instance.
+     *
+     * @param  DripEmailer  $drip
+     * @return void
+     */
+    public function __construct(DripEmailer $drip)
+    {
+        parent::__construct();
+
+        $this->drip = $drip;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+    	v('handlestart');
+//         $this->drip->send(User::find($this->argument('user')));
+    }
+}
+

+ 169 - 0
app/Console/Commands/SmartPush/BookUpdatePush.php

@@ -0,0 +1,169 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2020/9/16
+ * Time: 17:58
+ */
+
+namespace App\Console\Commands\SmartPush;
+
+use App\Jobs\SendTexts;
+use App\Modules\User\Services\ReadRecordService;
+use DB;
+use Redis;
+use Hashids;
+use Illuminate\Console\Command;
+
+class BookUpdatePush extends Command
+{
+
+    const BOOK_LAST_UPDATE_INFO = 'book:last_update'; //list
+
+    const BOOK_LAST_UPDATE_INFO_STATUS = 'book:last_update_status';
+
+    const CUSTOM_CATEGORY = 'book_update_push';
+
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'SmartPush:BookUpdatePush';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '图书更新推送';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+
+    private $switch = [];
+    private $default_status;
+
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->start();
+    }
+
+
+    private function start(){
+        /*$info = DB::connection('api_mysql')->table('custom_msg_switchs')->where('custom_category',self::CUSTOM_CATEGORY)->first();
+        if(!$info) return ;
+        $this->default_status = $info->default_switch_status;*/
+        $this->default_status = 0;
+        $update_status = Redis::get(self::BOOK_LAST_UPDATE_INFO_STATUS);
+        if($update_status == '1'){
+            Redis::set(self::BOOK_LAST_UPDATE_INFO_STATUS,2);
+            $this->getBookAndSend();
+        }
+    }
+
+    private function getBookAndSend(){
+        $length = Redis::llen(self::BOOK_LAST_UPDATE_INFO);
+        if($length <= 0) return [];
+        $count = ceil($length/100);
+        for ($i=0;$i <$count;$i++){
+            $start = $i*100;
+            $end = $start+99;
+            $result = Redis::Lrange(self::BOOK_LAST_UPDATE_INFO,$start,$end);
+            foreach ($result as $item){
+                $record = json_decode($item,1);
+                $bid = $record['bid'];
+                $book_name = $record['book_name'];
+                $chapter_name = $record['chapter_name'];
+                $this->sendByBook($bid,$book_name,$chapter_name);
+            }
+        }
+        Redis::del(self::BOOK_LAST_UPDATE_INFO);
+    }
+
+
+    private function sendByBook($bid,$book_name,$chapter_name){
+        $user_list = $this->userList($bid);
+        if(!$user_list) return ;
+        foreach ($user_list as $uid){
+            $user_force_subscribe_info = $this->userPushInfo($uid);
+            if(!$user_force_subscribe_info) continue;
+            $cid_time = ReadRecordService::getRecordByUidBid($uid,$bid);
+            if(!$cid_time) continue;
+            list($cid,$time) = explode('_',$cid_time);
+            if(!$cid) continue;
+            $link = 'https://site'.encodeDistributionChannelId($user_force_subscribe_info['distribution_channel_id']).'.zhuishuyun.com/reader?bid='.Hashids::encode($bid).'&cid='.$cid.'&fromtype=book_update_push';
+            $content_fromat = "小说更新提醒\r\n\r\n<a href='%s'>《%s》已更新到《%s》</a>\r\n\r\n精彩内容不容错过~\r\n";
+            $content = sprintf($content_fromat,$link,$book_name,$chapter_name);
+            $data = [
+                'openid'=>$user_force_subscribe_info['openid'],
+                'appid'=>$user_force_subscribe_info['appid'],
+                'type'=>'one_task',
+                'task_id'=>1,
+                'send_time'=>date('Y-m-d H:i:s'),
+                'content'=>$content
+            ];
+            $send_data=array(
+                'send_time'=>date("Y-m-d H:i:s"),
+                'data' => $data
+            );
+            $job = (new SendTexts($send_data))->onConnection('rabbitmq')->delay(0)->onQueue('send_texts_list');
+            dispatch($job);
+        }
+    }
+
+
+    private function userPushInfo($uid){
+        $user_force_subscribe_info = DB::connection('api_mysql')->table('temp_force_subscribe_users')
+            ->select('distribution_channel_id','openid','appid')
+            ->where('uid',$uid)
+            ->where('is_subscribed',1)
+            ->where('last_interactive_time','>',date('Y-m-d H:i:s',time()-86400*2))
+            ->first();
+        if(!$user_force_subscribe_info) return [];
+        if(!$this->switchStatus($user_force_subscribe_info->distribution_channel_id)) return [];
+        return [
+            'distribution_channel_id'=>$user_force_subscribe_info->distribution_channel_id,
+            'openid'=> $user_force_subscribe_info->openid,
+            'appid'=>$user_force_subscribe_info->appid
+        ];
+    }
+
+    private function userList($bid){
+        // [8426169,14128];
+        return DB::connection('api_mysql')->table('user_last_chapter_order')->where('bid',$bid)->select('uid')->pluck('uid');
+    }
+
+
+    private function switchStatus($distribution_channel_id){
+
+        return false;
+        if(isset($this->switch[$distribution_channel_id])){
+            return $this->switch[$distribution_channel_id] == 1;
+        }
+        $switch = DB::connection('api_mysql')->table('custom_msg_switchs_msgs')
+            ->where('distribution_channel_id',$distribution_channel_id)
+            ->where('custom_category',self::CUSTOM_CATEGORY)
+            ->select('status')
+            ->first();
+        if($switch){
+            $this->switch[$distribution_channel_id] = $switch->status;
+        }else{
+            $this->switch[$distribution_channel_id] = $this->default_status;
+        }
+        return $this->switch[$distribution_channel_id] == 1;
+    }
+}

+ 107 - 0
app/Console/Commands/SmartPush/CouponExpirePush.php

@@ -0,0 +1,107 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2020/9/9
+ * Time: 14:20
+ */
+
+namespace App\Console\Commands\SmartPush;
+
+use DB;
+use GuzzleHttp\Pool;
+use GuzzleHttp\Client;
+use Illuminate\Console\Command;
+use App\Http\Controllers\WechatController;
+use GuzzleHttp\Psr7\Request as GuzzleRequest;
+
+class CouponExpirePush extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'SmartPush:CouponExpirePush';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '优惠券到期推送';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->send();
+    }
+
+    private function send(){
+
+        $client = new Client();
+        $requests = $this->generate();
+        $pool = new Pool($client, $requests, [
+            'concurrency' => 25,
+            'fulfilled' => function ($response, $index) {
+            },
+            'rejected' => function ($reason, $index) {
+            },
+        ]);
+        $promise = $pool->promise();
+        $promise->wait();
+
+    }
+
+    private function  generate(){
+
+        $uids = DB::connection('api_mysql')->table('user_coupon')
+            ->where('use_status',1)
+            ->where('expire_time','>=',date('Y-m-d H:i:s',time()+86400))
+            ->where('expire_time','<=',date('Y-m-d H:i:s',time()+86400+3600))
+            ->select('uid')
+            ->get();
+        if(!$uids) return ;
+        foreach ($uids as $item){
+            $user =  DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                ->select('id','uid','distribution_channel_id','openid','appid')->where('uid',$item->uid)->first();
+            if(!$user) continue;
+            $user_info = DB::connection('api_mysql')->table('users')->where('id',$item->uid)->select('nickname')->first();
+            $nickname = '';
+            if($user_info && $user_info->nickname)$nickname = $user_info->nickname;
+            $content_format = "尊敬的会员:%s\r\n\r\n您有一张优惠券即将到期\r\n<a href='%s'>点击立即使用</a>";
+            $link = 'https://site'.encodeDistributionChannelId($user->distribution_channel_id).'.leyuee.com/pay?fromtype=coupon_expire_push';
+            $content = sprintf($content_format,$nickname,$link);
+            $accecc_token = $this->getAccessToken($user->appid);
+            if(!$accecc_token)continue;
+            $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accecc_token;
+            $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                'touser'=>$user->openid,
+                'msgtype'=>'text',
+                'text'=>['content'=>$content]
+            ],JSON_UNESCAPED_UNICODE));
+            yield $request;
+        }
+
+    }
+    private function getAccessToken($appid){
+        $WechatController = new WechatController($appid);
+        $accessToken = $WechatController->app->access_token; // EasyWeChat\Core\AccessToken 实例
+        $token = $accessToken->getToken(); // token 字符串
+        return $token;
+    }
+}

+ 54 - 0
app/Console/Commands/SmartPush/ForceSubscribeDelayMsg.php

@@ -0,0 +1,54 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2020/3/30
+ * Time: 10:18
+ */
+
+namespace App\Console\Commands\SmartPush;
+
+use App\Modules\OfficialAccount\Services\ForceSubscribeDelayMsgService;
+use Illuminate\Console\Command;
+
+class ForceSubscribeDelayMsg  extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'SmartPush:ForceSubscribeDelayMsg';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '关注延迟';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->send();
+    }
+
+    private function send()
+    {
+        ForceSubscribeDelayMsgService::getSendList(120);
+    }
+}

+ 229 - 0
app/Console/Commands/SmartPush/KeepContinueRead.php

@@ -0,0 +1,229 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2019/8/20
+ * Time: 14:00
+ */
+
+namespace App\Console\Commands\SmartPush;
+
+use Illuminate\Console\Command;
+use DB;
+use Redis;
+use App\Modules\Book\Services\BookConfigService;
+use GuzzleHttp\Client;
+use App\Http\Controllers\WechatController;
+use GuzzleHttp\Psr7\Request as GuzzleRequest;
+use Hashids;
+use GuzzleHttp\Pool;
+
+class KeepContinueRead extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'SmartPush:KeepContinueRead';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '持续阅读推送';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->send();
+    }
+
+    private  function generateRequest()
+    {
+        $switch_array = [];
+        $now_hour = date('G');
+        $temp = 0;
+        $continueReadUrlFormat = 'https://site%s.%s.com%s';
+        while (true){
+            $user =  DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                ->select('id','uid','distribution_channel_id','openid','appid')
+                ->where('id','>',$temp)
+                ->orderBy('id')
+                ->limit(1000)
+                ->get();
+            if(!$user) break;
+
+            foreach ($user as $item){
+                /*if(!in_array($item->distribution_channel_id,[5,8,123,146,155,160,211,255,256,691,695,722,4025,4053,4174,4236,4237,4241,4326,4334,4364,4426,4427,4487,4488,4556,4593,4742,4889,4891,5204,5611,6122,6123,6124,6460,6907,6929,6987])){
+                    continue;
+                }*/
+               
+		\Log::info( '---------------------------start--start-------------------------------------------' );
+		//\Log::info('$item->distribution_channel_id is :'.$item->distribution_channel_id  );
+		\Log::info( '$item->id is :'.$item->id );
+                if(!isset($switch_array[$item->distribution_channel_id])){
+                    $switch = DB::connection('api_mysql')->table('custom_msg_switchs_msgs')
+                        ->where('distribution_channel_id',$item->distribution_channel_id)
+                        ->where('custom_category','continue_read')
+                        ->select('status')
+                        ->first();
+                    if($switch){
+                        $switch_array[$item->distribution_channel_id] = $switch->status;
+                    }else{
+                        $switch_array[$item->distribution_channel_id] = 1;
+                    }
+                }
+
+                $switch_status = $switch_array[$item->distribution_channel_id];
+                if($switch_status == 2) continue;
+		//\Log::info( '$item->distribution_channel_id in is :'.$item->distribution_channel_id );
+
+                $read_info = $this->getFirstReadRecord($item->uid);
+		//\Log::info( $read_info );
+		\Log::info( 'uid is :'.$item->uid  );
+                if(!$read_info) continue;
+
+                //获取本次发送的时间间隔
+                $this_push_hour = Redis::hget('book_read:'.$item->uid,'next_push_hour');
+                //上次发送时间
+                $prev_send_info = DB::connection('api_mysql')->table('custom_push_keep_continue')->where('uid',$item->uid)->select('send_time')->orderBy('id','desc')->first();
+
+                $diff = $this_push_hour?$this_push_hour:8;
+                if($diff == 8){
+                    if((time()-$read_info['time']) > 3600*8){
+                        $send_hour = $now_hour;
+                    }else{
+                        $send_hour = -1;
+                    }
+                }else{
+                    if($prev_send_info){
+                        //$send_hour = date('G',strtotime($prev_send_info->send_time)+$diff*3600);
+                        if((time()-strtotime($prev_send_info->send_time)) >$diff*3600){
+                            $send_hour = $now_hour;
+                        }else{
+                            $send_hour = -1;
+                        }
+                    }else{
+                        $send_hour = $now_hour;
+                    }
+                }
+
+                if($send_hour != $now_hour ) continue;
+                $openid = $item->openid;
+                $appid = $item->appid;
+                $content_format = "您看的小说有更新\r\n\r\n<a href='%s'>《%s》剧情已更新,点击继续阅读</a> \r\n\r\n记得点击菜单栏签到领书币哦";
+                $content = sprintf($content_format,
+                    sprintf($continueReadUrlFormat,
+                        encodeDistributionChannelId($item->distribution_channel_id),
+                        env('CUSTOM_HOST'),
+                        $read_info['url']
+                    ),
+                    $read_info['book_name']
+                );
+                $accecc_token = $this->getAccessToken($appid);
+                if(!$accecc_token)continue;
+
+                $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accecc_token;
+                $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                    'touser'=>$openid,
+                    'msgtype'=>'text',
+                    'text'=>['content'=>$content]
+                ],JSON_UNESCAPED_UNICODE));
+
+                DB::connection('api_mysql')->table('custom_push_keep_continue')->insert([
+                    'uid'=>$item->uid,
+                    'time_diff'=>$diff,
+                    'send_time'=>date('Y-m-d H:i:s'),
+                    'day'=>date('Y-m-d'),
+                    'created_at'=>date('Y-m-d H:i:s'),
+                    'updated_at'=>date('Y-m-d H:i:s')
+                ]);
+                if($diff == 8){
+                    $next = 5;
+                }else{
+                    $next = 12;
+                }
+                Redis::hset('book_read:'.$item->uid,'next_push_hour',$next);
+		\Log::info( '----------------------------end-end-end-------------------------------------------' );
+                yield $request;
+            }
+            $temp = $item->id;
+        }
+    }
+
+
+    private  function getFirstReadRecord($uid){
+        //Redis::hget('book_base:' . $uid, 'last_read', "{$bid}_{$cid}_{$book_name}_{$chapter_name}_" . time());
+        $record = Redis::hget('book_read:' . $uid, 'last_read');
+        if($record){
+            $record_arr = explode('_',$record);
+            $bid = $record_arr[0];
+            $book_name = $this->bid2BookName($bid);
+            $bid = Hashids::encode($bid);
+            $cid = $record_arr[1];
+            $time = $record_arr[2];
+            $res = [
+                'url' => '/reader?bid='.$bid.'&cid='.$cid.'&fromtype=continue_read',
+                'book_name'=>$book_name,
+                'bid'=>$bid,
+                'time'=>$time
+            ];
+            return $res;
+        }
+        return [];
+    }
+
+    private  function bid2BookName($bid){
+        $book_name = null;
+        if(is_null($book_name)){
+            $book_key = 'wap:string:book:'.$bid;
+            $book_name = Redis::get($book_key);
+            Redis::EXPIRE($book_key,3600);
+            if(!$book_name){
+                $book_name = '';
+                $book_info = BookConfigService::getBookById($bid);
+                if($book_info && isset($book_info->book_name)){
+                    $book_name = $book_info->book_name;
+                }
+            }
+        }
+        return $book_name;
+    }
+
+    private function getAccessToken($appid){
+        $WechatController = new WechatController($appid);
+        $accessToken = $WechatController->app->access_token; // EasyWeChat\Core\AccessToken 实例
+        $token = $accessToken->getToken(); // token 字符串
+        return $token;
+    }
+
+    private function send(){
+        $client = new Client();
+        $requests = $this->generateRequest();
+        $pool = new Pool($client, $requests, [
+            'concurrency' => 25,
+            'fulfilled' => function ($response, $index) {
+            },
+            'rejected' => function ($reason, $index) {
+            },
+        ]);
+        $promise = $pool->promise();
+        $promise->wait();
+    }
+}

+ 301 - 0
app/Console/Commands/SmartPush/KeepContinueReadV2.php

@@ -0,0 +1,301 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2019/8/20
+ * Time: 14:00
+ */
+
+namespace App\Console\Commands\SmartPush;
+
+use Illuminate\Console\Command;
+use DB;
+use Redis;
+use GuzzleHttp\Client;
+use App\Http\Controllers\WechatController;
+use GuzzleHttp\Psr7\Request as GuzzleRequest;
+use GuzzleHttp\Pool;
+use Hashids;
+
+class KeepContinueReadV2 extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'SmartPush:KeepContinueReadV2';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '持续阅读推送';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->send();
+    }
+
+    private  function generateRequest()
+    {
+		$switch_array = [];
+        $now_hour = date('G');
+        $temp = 0;
+        $info = DB::connection('api_mysql')->table('custom_msg_switchs')->where('custom_category','continue_read')->first();
+        if(!$info) return ;
+        $default_status = $info->default_switch_status;
+        while (true){
+            $user =  DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                ->select('id','uid','distribution_channel_id','openid','appid')
+                ->where('id','>',$temp)
+                //->whereIn('id',[3032288,3042985])
+                ->where('last_interactive_time','>',date('Y-m-d H:i:s',time()-86400*2))
+                ->orderBy('id')
+                ->limit(1000)
+                ->get();
+            if(!$user) break;
+
+            foreach ($user as $item){
+                if(isInnerSites($item->distribution_channel_id)) continue;
+				//if(!in_array($item->distribution_channel_id,[70])) continue;
+				//\Log::info( '---------------------------start--start-------------------------------------------' );
+				//\Log::info('$item->distribution_channel_id is :'.$item->distribution_channel_id  );
+				//\Log::info( '$item->id is :'.$item->id );
+                if(!isset($switch_array[$item->distribution_channel_id])){
+                    $switch = DB::connection('api_mysql')->table('custom_msg_switchs_msgs')
+                        ->where('distribution_channel_id',$item->distribution_channel_id)
+                        ->where('custom_category','continue_read')
+                        ->select('status')
+                        ->first();
+                    if($switch){
+                        $switch_array[$item->distribution_channel_id] = $switch->status;
+                    }else{
+                        $switch_array[$item->distribution_channel_id] = $default_status;
+                    }
+                }
+			
+				$switch_status = $switch_array[$item->distribution_channel_id];
+                //if(!in_array($item->distribution_channel_id,[5,123,14,13,8])) continue;
+                if($switch_status != 1) continue;
+				//\Log::info( '$item->distribution_channel_id in is :'.$item->distribution_channel_id );
+				//$data = ['first'=>[],'seconds'=>[]];
+                $read_info = $this->getReadRecord($item->uid);
+                if(empty($read_info['first'])){
+                    continue;
+                }
+                //获取本次发送的时间间隔
+                $this_push_hour = Redis::hget('book_read:'.$item->uid,'next_push_hour');
+                //上次发送时间
+                $prev_send_info = DB::connection('api_mysql')->table('custom_push_keep_continue')->where('uid',$item->uid)->select('send_time')->orderBy('id','desc')->first();
+
+                $diff = $this_push_hour?$this_push_hour:8;
+                if($diff == 8){
+                    if((time()-$read_info['time']) > 3600*8){
+                        $send_hour = $now_hour;
+                    }else{
+                        $send_hour = -1;
+                    }
+                }else{
+                    if($prev_send_info){
+                        //$send_hour = date('G',strtotime($prev_send_info->send_time)+$diff*3600);
+                        if((time()-strtotime($prev_send_info->send_time)) >$diff*3600){
+                            $send_hour = $now_hour;
+                        }else{
+                            $send_hour = -1;
+                        }
+                    }else{
+                        $send_hour = $now_hour;
+                    }
+                }
+                $domain = sprintf('https://site%s.%s.com',encodeDistributionChannelId($item->distribution_channel_id),
+                    env('CUSTOM_HOST'));
+                if($send_hour != $now_hour ) continue;
+                $openid = $item->openid;
+                $appid = $item->appid;
+                $user_info = DB::connection('api_mysql')->table('users')->where('id',$item->uid)->select('nickname')->first();
+                $nickname = '读者';
+                if($user_info && $user_info->nickname)$nickname = $user_info->nickname;
+                //$content_format = "您看的小说有更新\r\n\r\n<a href='%s'>《%s》剧情已更新,点击继续阅读</a> \r\n\r\n记得点击菜单栏签到领书币哦";
+                $content_format = "@%s 为您推荐上次未看完的小说\r\n\r\n点击<a href='%s'>继续阅读</a>❤\r\n\r\n";
+                /*$content = sprintf($content_format,
+                    sprintf($continueReadUrlFormat,
+                        encodeDistributionChannelId($item->distribution_channel_id),
+                        env('CUSTOM_HOST'),
+                        $read_info['url']
+                    ),
+                    $read_info['book_name']
+                )*/;
+                $content = sprintf($content_format,$nickname,$domain.$read_info['first']['url']);
+                if(!empty($read_info['seconds'])){
+                    $content .=  "历史阅读记录:\r\n\r\n";
+                    foreach ($read_info['seconds'] as $record_item){
+                        $content .= sprintf(" 🌳 <a href='%s'>%s</a>\r\n",$domain.$record_item['url'],$record_item['book_name']);
+                    }
+                }
+                $content .= "\r\n为了方便下次阅读,请<a href='http://cdn-pro.18yuedu.com/h5/top.html'>置顶公众号</a>";
+                $access_token = $this->getAccessToken($appid);
+                if(!$access_token)continue;
+
+                $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$access_token;
+                $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                    'touser'=>$openid,
+                    'msgtype'=>'text',
+                    'text'=>['content'=>$content]
+                ],JSON_UNESCAPED_UNICODE));
+
+                DB::connection('api_mysql')->table('custom_push_keep_continue')->insert([
+                    'uid'=>$item->uid,
+                    'time_diff'=>$diff,
+                    'send_time'=>date('Y-m-d H:i:s'),
+                    'day'=>date('Y-m-d'),
+                    'created_at'=>date('Y-m-d H:i:s'),
+                    'updated_at'=>date('Y-m-d H:i:s')
+                ]);
+                if($diff == 8){
+                    $next = 24;
+                }else{
+                    $next = 24;
+                }
+                Redis::hset('book_read:'.$item->uid,'next_push_hour',$next);
+				//\Log::info( '----------------------------end-end-end-------------------------------------------' );
+                yield $request;
+            }
+            $temp = $item->id;
+        }
+    }
+
+
+    private  function getFirstReadRecord($uid){
+        //Redis::hget('book_base:' . $uid, 'last_read', "{$bid}_{$cid}_{$book_name}_{$chapter_name}_" . time());
+        $record = Redis::hget('book_read:' . $uid, 'last_read');
+        if($record){
+            $record_arr = explode('_',$record);
+            $bid = $record_arr[0];
+            $book_name = $this->bid2BookName($bid);
+			//$hash = new Hashids('D6M97LIvpp4qWuz3nKzqi6yYN4GAA61b',32,'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890');
+            $bid = $hash->encode($bid);
+            $cid = $record_arr[1];
+            $time = $record_arr[2];
+            $res = [
+                'url' => '/reader?bid='.$bid.'&cid='.$cid.'&fromtype=continue_read',
+                'book_name'=>$book_name,
+                'bid'=>$bid,
+                'time'=>$time
+            ];
+            return $res;
+        }
+        return [];
+    }
+
+    private function getReadRecord($uid){
+        //$hash = new Hashids('D6M97LIvpp4qWuz3nKzqi6yYN4GAA61b',32,'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890');
+        $records = Redis::hgetall('book_read:' . $uid);
+        $data = ['first'=>[],'seconds'=>[],'time'=>''];
+        foreach ($records as $k=>$item){
+            if($k == 'last_read'){
+                $record_arr = explode('_',$item);
+                $bid = $record_arr[0];
+                //$book_name = $this->bid2BookName($bid);
+                $bid = Hashids::encode($bid);
+                $cid = $record_arr[1];
+                $time = $record_arr[2];
+                $data['first'] = [
+                    'url' => '/reader?bid='.$bid.'&cid='.$cid.'&fromtype=continue_read',
+                    'book_name'=>'',
+                ];
+                $data['time'] = $time;
+                continue;
+            }
+            if(!is_numeric($k)) continue;
+            $record = explode('_', $item);
+            $latest_read_cid = $record[0];
+            $book_name = self::bid2BookName($k);
+            $latest_read_time = $record[count($record) - 1];
+            $data['seconds'][] =[
+                'url' => '/reader?bid='.Hashids::encode($k).'&cid='.$latest_read_cid.'&fromtype=continue_read',
+                'book_name'=>$book_name,
+                'time'=>$latest_read_time
+            ];
+        }
+        $temp = $data['seconds'];
+        if($temp){
+            usort($temp, function ($a, $b) {
+                if ($a['time'] >= $b['time']) return -1;
+                return 1;
+            });
+        }
+        $temp_res = [];
+        foreach ($temp as $k=>$it){
+            $temp_res[] = $it;
+            if($k>=2) break;
+        }
+        $data['seconds'] = $temp_res;
+        return $data;
+    }
+
+    private  function bid2BookName($bid){
+        $book_name = null;
+        if(is_null($book_name)){
+            $book_key = 'wap:string:book:'.$bid;
+            $book_name = Redis::get($book_key);
+            Redis::EXPIRE($book_key,3600);
+            if(!$book_name){
+                $book_name = '';
+				$book_info = DB::connection('api_mysql')->table('book_configs')->where('bid',$bid)->select('book_name')->first();
+                //$book_info = BookConfigService::getBookById($bid);
+                if($book_info && isset($book_info->book_name)){
+                    $book_name = $book_info->book_name;
+					Redis::setex($book_key,3600,$book_name);
+                }
+            }
+        }
+        return $book_name;
+    }
+
+    private function getAccessToken($appid){
+		try{
+			$WechatController = new WechatController($appid);
+			$accessToken = $WechatController->app->access_token; // EasyWeChat\Core\AccessToken 实例
+			$token = $accessToken->getToken(); // token 字符串
+			return $token;
+		}catch(\Exception $e){
+			\Log::error($e->getMessage());
+		}
+		return '';
+        
+    }
+
+    private function send(){
+        $client = new Client();
+        $requests = $this->generateRequest();
+        $pool = new Pool($client, $requests, [
+            'concurrency' => 5,
+            'fulfilled' => function ($response, $index) {
+
+            },
+            'rejected' => function ($reason, $index) {
+
+            },
+        ]);
+
+        $promise = $pool->promise();
+        $promise->wait();
+    }
+}

+ 312 - 0
app/Console/Commands/SmartPush/KeepContinueReadV3.php

@@ -0,0 +1,312 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2020/6/8
+ * Time: 17:41
+ */
+
+namespace App\Console\Commands\SmartPush;
+
+use Illuminate\Console\Command;
+use DB;
+use Redis;
+use GuzzleHttp\Client;
+use App\Http\Controllers\WechatController;
+use GuzzleHttp\Psr7\Request as GuzzleRequest;
+use GuzzleHttp\Pool;
+use Hashids;
+use Log;
+
+/*
+继续阅读智能推送时间逻辑:
+
+遍历阅读后离开阅读器的用户,若3小时后未再次访问阅读器,需推送一条继续阅读智能推送消息
+
+若再过5小时后扔未访问,需推送第二条继续阅读智能推送消息
+
+若再过5小时后扔未访问,需推送第三条继续阅读智能推送消息
+
+若再过12小时后扔未访问,需推送第四条继续阅读智能推送消息
+
+若再过12小时后扔未访问,需推送第五条继续阅读智能推送消息
+
+期间任何一个时间节点遍历时若被打断,则重新开始计算
+*/
+class KeepContinueReadV3 extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'SmartPush:KeepContinueReadV3';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '持续阅读推送';
+
+    private static $role = [0,3,5,5,12,12];
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $str = str_random(32);
+        Log::info('KeepContinueReadV3 start at :'.date('Y-m-d H:i:s').',flag is :'.$str);
+        $this->send();
+        Log::info('KeepContinueReadV3 end at :'.date('Y-m-d H:i:s').',flag is :'.$str);
+    }
+
+    private function send(){
+        $client = new Client();
+        $channel_id = $this->getAllSite();
+        if(!$channel_id)  return ;
+        $requests = $this->start($channel_id);
+        $pool = new Pool($client, $requests, [
+            'concurrency' => 5,
+            'fulfilled' => function ($response, $index) {
+            },
+            'rejected' => function ($reason, $index) {
+
+            },
+        ]);
+        $promise = $pool->promise();
+        $promise->wait();
+    }
+
+    private function getAllSite(){
+        $info = DB::connection('api_mysql')->table('custom_msg_switchs')->where('custom_category','continue_read')->first();
+        if(!$info) return [];
+        $default_status = $info->default_switch_status;
+        $site_list = explode(',',redisEnv('INNER_SITE'));
+        $on_distribution_channel_id = [];
+        foreach ($site_list as $distribution_channel_id){
+            $switch_info = DB::connection('api_mysql')->table('custom_msg_switchs_msgs')
+                ->where('distribution_channel_id',$distribution_channel_id)
+                ->where('custom_category','continue_read')
+                ->select('status')
+                ->first();
+            if($switch_info){
+                $switch = $switch_info->status;
+            }else{
+                $switch = $default_status;
+            }
+            if($switch != 1) continue;
+            $on_distribution_channel_id[] = $distribution_channel_id;
+        }
+        return $on_distribution_channel_id;
+    }
+
+    private function start($distribution_channel_ids){
+        $temp = 0;
+        //$sites = $this->channelIds();
+        while (true){
+            $user =  DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                ->select('id','uid','distribution_channel_id','openid','appid','last_interactive_time')
+                ->where('id','>',$temp)
+                ->whereIn('distribution_channel_id',$distribution_channel_ids)
+                ->orderBy('id')
+                ->limit(1000)
+                ->get();
+            if(!$user) break;
+            foreach ($user as $item){
+                $temp = $item->id;
+                if( (time() - strtotime($item->last_interactive_time)) > 2*86400 ) continue;
+                //if(!in_array($item->distribution_channel_id,$sites)) continue;
+                $read_info = $this->getReadRecord($item->uid);
+                if(empty($read_info['first']) || !isset($read_info['time']) || empty($read_info['time'])){
+                    continue;
+                }
+                $is_access = $this->isAccess($item->uid,$read_info['time'],$now_role,$is_first);
+                if(!$is_access) continue;
+                $access_token = $this->getAccessToken($item->appid);
+                if(!$access_token)continue;
+                $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$access_token;
+                $content = $this->content($item->uid,$item->distribution_channel_id,$read_info);
+                $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                    'touser'=>$item->openid,
+                    'msgtype'=>'text',
+                    'text'=>['content'=>$content]
+                ],JSON_UNESCAPED_UNICODE));
+                if($is_first){
+                    DB::connection('api_mysql')->table('custom_push_keep_continue_v2')->where('uid',$item->uid)->update([
+                        'times'=>$now_role,
+                        'updated_at'=>date('Y-m-d H:i:s')
+                    ]);
+                }else{
+                    DB::connection('api_mysql')->table('custom_push_keep_continue_v2')->insert([
+                        'uid'=>$item->uid,
+                        'times'=>$now_role,
+                        'created_at'=>date('Y-m-d H:i:s'),
+                        'updated_at'=>date('Y-m-d H:i:s')
+                    ]);
+                }
+                yield $request;
+            }
+        }
+    }
+
+    private function isAccess($uid,$last_read_time,&$now_role,&$is_first){
+        //获取用户上次发送的规则
+        $prev_send_info = DB::connection('api_mysql')->table('custom_push_keep_continue_v2')->where('uid',$uid)->select('updated_at','times')->orderBy('id','desc')->first();
+        //上次发送规则
+        $last_role = 0;
+        $is_first = false;
+        $last_send_time = 0;
+        if($prev_send_info){
+            $is_first = true;
+            $last_role = $prev_send_info->times;
+            $last_send_time = strtotime($prev_send_info->updated_at);
+        }
+        $now_role = $last_role+1;
+        if($now_role == 6) $now_role = 1;
+        //上次更新时间,也就是上次发送时间
+
+        //第一次发送是根据用户离开阅读器的时间,即当前时间和用户上次阅读的时间比较是否查过规定的时间
+        if($now_role == 1){
+            if((time() - $last_read_time) < self::$role[$now_role] * 3600){
+                //小于发送时间
+                return false;
+            }
+            return true;
+        }
+        //是否到达检测时间,即上次发送时间加上当前规则所规定的时间,差距在半个小时以内就算到达
+        if(abs( time()-($last_send_time+self::$role[$now_role] * 3600)) > 1800){
+            //没有到达时间
+            return false;
+        }
+        //用户阅读时间是否符合条件
+        $time_diff = 0;
+        foreach (self::$role as $k=>$t){
+            if($k <= $now_role){
+                $time_diff += $t;
+            }
+        }
+        if( (time() - $last_read_time) >= $time_diff*3600){
+            //到了时间还是没阅读,就发送
+            return true;
+        }else{
+            //时间节点遍历时若被打断,则重新开始计算
+            DB::connection('api_mysql')->table('custom_push_keep_continue_v2')->where('uid',$uid)->update([
+                'times'=>0,
+                'updated_at'=>date('Y-m-d H:i:s')
+            ]);
+            return false;
+        }
+    }
+
+    private function content($uid,$distribution_channel_id,$read_info){
+        $domain = sprintf('https://site%s.%s.com',encodeDistributionChannelId($distribution_channel_id),
+            env('CUSTOM_HOST'));
+        $user_info = DB::connection('api_mysql')->table('users')->where('id',$uid)->select('nickname')->first();
+        $nickname = '读者';
+        if($user_info && $user_info->nickname)$nickname = $user_info->nickname;
+        $content_format = "@%s 为您推荐上次未看完的小说\r\n\r\n点击<a href='%s'>继续阅读</a>❤\r\n\r\n";
+        $content = sprintf($content_format,$nickname,$domain.$read_info['first']['url']);
+        if(!empty($read_info['seconds'])){
+            $content .=  "历史阅读记录:\r\n\r\n";
+            foreach ($read_info['seconds'] as $record_item){
+                $content .= sprintf(" 🌳 <a href='%s'>%s</a>\r\n",$domain.$record_item['url'],$record_item['book_name']);
+            }
+        }
+        $content .= "\r\n为了方便下次阅读,请<a href='http://cdn-pro.18yuedu.com/h5/top.html'>置顶公众号</a>";
+        return $content;
+    }
+
+    private function getAccessToken($appid){
+        try{
+            $WechatController = new WechatController($appid);
+            $accessToken = $WechatController->app->access_token; // EasyWeChat\Core\AccessToken 实例
+            $token = $accessToken->getToken(); // token 字符串
+            return $token;
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+        return '';
+    }
+
+    private function getReadRecord($uid){
+        $records = Redis::hgetall('book_read:' . $uid);
+        $data = ['first'=>[],'seconds'=>[],'time'=>''];
+        foreach ($records as $k=>$item){
+            if($k == 'last_read'){
+                $record_arr = explode('_',$item);
+                $bid = $record_arr[0];
+                //$book_name = $this->bid2BookName($bid);
+                $bid = Hashids::encode($bid);
+                $cid = $record_arr[1];
+                $time = $record_arr[2];
+                $data['first'] = [
+                    'url' => '/reader?bid='.$bid.'&cid='.$cid.'&fromtype=continue_read_v2',
+                    'book_name'=>'',
+                ];
+                $data['time'] = $time;
+                continue;
+            }
+            if(!is_numeric($k)) continue;
+            $record = explode('_', $item);
+            $latest_read_cid = $record[0];
+            $book_name = self::bid2BookName($k);
+            $latest_read_time = $record[count($record) - 1];
+            $data['seconds'][] =[
+                'url' => '/reader?bid='.Hashids::encode($k).'&cid='.$latest_read_cid.'&fromtype=continue_read_v2',
+                'book_name'=>$book_name,
+                'time'=>$latest_read_time
+            ];
+        }
+        $temp = $data['seconds'];
+        if($temp){
+            usort($temp, function ($a, $b) {
+                if ($a['time'] >= $b['time']) return -1;
+                return 1;
+            });
+        }
+        $temp_res = [];
+        foreach ($temp as $k=>$it){
+            $temp_res[] = $it;
+            if($k>=2) break;
+        }
+        $data['seconds'] = $temp_res;
+        return $data;
+    }
+    private  function bid2BookName($bid){
+        $book_name = null;
+        if(is_null($book_name)){
+            $book_key = 'wap:string:book:'.$bid;
+            $book_name = Redis::get($book_key);
+            Redis::EXPIRE($book_key,3600);
+            if(!$book_name){
+                $book_name = '';
+                $book_info = DB::connection('api_mysql')->table('book_configs')->where('bid',$bid)->select('book_name')->first();
+                //$book_info = BookConfigService::getBookById($bid);
+                if($book_info && isset($book_info->book_name)){
+                    $book_name = $book_info->book_name;
+                    Redis::setex($book_key,3600,$book_name);
+                }
+            }
+        }
+        return $book_name;
+    }
+
+    private function channelIds(){
+        $str = '5,123,14,13,8';
+        return explode(',',$str);
+    }
+}

+ 327 - 0
app/Console/Commands/SmartPush/RfmPush.php

@@ -0,0 +1,327 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2020/8/13
+ * Time: 11:44
+ */
+
+namespace App\Console\Commands\SmartPush;
+
+use App\Modules\Order\Models\Order;
+use App\Modules\OfficialAccount\Models\ForceSubscribeUsers;
+use App\Modules\User\Services\UserService;
+use Illuminate\Console\Command;
+use DB;
+use Redis;
+use GuzzleHttp\Client;
+use App\Http\Controllers\WechatController;
+use GuzzleHttp\Psr7\Request as GuzzleRequest;
+use GuzzleHttp\Pool;
+
+class RfmPush extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'SmartPush:RfmPush {--type=} {--time=}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '签到提醒';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+
+    private $user;
+
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    private $Level = [
+        'new_user_activity_sm'=>['222','212','122','112'],
+        'new_user_activity_bm'=>['212','122','112'],
+        'new_user_activity_for_free'=>['000']
+    ];
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $type = $this->option('type');
+        $time = $this->option('time');
+        if(!$type || !$time) return ;
+        $client = new Client();
+        $this->getUser($time);
+        if(!$this->user) return ;
+        if($type == 'sm' || $type == 'bm'){
+            $requests = $this->newUserSmAndBm();
+            if($requests){
+                $pool = new Pool($client, $requests, ['concurrency' => 5,
+                    'fulfilled' => function ($response, $index) {},
+                    'rejected' => function ($reason, $index) {},
+                ]);
+                $promise = $pool->promise();
+                $promise->wait();
+            }
+        }
+
+        if($type == 'all'){
+            $requests =$this->newUserAll();
+            if($requests){
+                $pool = new Pool($client, $requests, ['concurrency' => 5,
+                    'fulfilled' => function ($response, $index) {},
+                    'rejected' => function ($reason, $index) {},
+                ]);
+                $promise = $pool->promise();
+                $promise->wait();
+            }
+        }
+        if($type == 'free-24'){
+            $requests =$this->newUserFree();
+            if($requests){
+                $pool = new Pool($client, $requests, ['concurrency' => 5,
+                    'fulfilled' => function ($response, $index) {},
+                    'rejected' => function ($reason, $index) {},
+                ]);
+                $promise = $pool->promise();
+                $promise->wait();
+            }
+        }
+        if($type == 'free-36'){
+            $requests =$this->newUserFreeSmall();
+            if($requests){
+                $pool = new Pool($client, $requests, ['concurrency' => 5,
+                    'fulfilled' => function ($response, $index) {},
+                    'rejected' => function ($reason, $index) {},
+                ]);
+                $promise = $pool->promise();
+                $promise->wait();
+            }
+        }
+    }
+
+
+    private function newUserFree(){
+        $user = $this->user;
+        foreach ($user as $item){
+            $charge = Order::where('uid',$item->uid)->where('status','PAID')->count();
+            if($charge) continue;
+            $force_subscribe_info = DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                ->where('uid',$item->uid)->where('is_subscribed',1)->select('appid','openid','distribution_channel_id')->first();
+            if(!$force_subscribe_info) continue;
+            $accecc_token = $this->getAccessToken($force_subscribe_info->appid);
+            if(!$accecc_token)continue;
+            $status = $this->isAccess($item->uid,'new_user_activity_for_free',$distribution_channel_id);
+            if($status){
+                $content = $this->newUserActivityBmContent($force_subscribe_info->distribution_channel_id);
+                $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accecc_token;
+                $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                    'touser'=>$force_subscribe_info->openid,
+                    'msgtype'=>'text',
+                    'text'=>['content'=>$content]
+                ],JSON_UNESCAPED_UNICODE));
+                yield $request;
+            }
+        }
+    }
+
+    private function newUserFreeSmall(){
+        $user = $this->user;
+        foreach ($user as $item){
+            $charge = Order::where('uid',$item->uid)->where('status','PAID')->count();
+            if($charge) continue;
+            $force_subscribe_info = DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                ->where('uid',$item->uid)->where('is_subscribed',1)->select('appid','openid','distribution_channel_id')->first();
+            if(!$force_subscribe_info) continue;
+            $accecc_token = $this->getAccessToken($force_subscribe_info->appid);
+            if(!$accecc_token)continue;
+            $status = $this->isAccess($item->uid,'new_user_activity_for_free',$distribution_channel_id);
+            if($status){
+                $content = $this->newUserAllContent($force_subscribe_info->distribution_channel_id);
+                $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accecc_token;
+                $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                    'touser'=>$force_subscribe_info->openid,
+                    'msgtype'=>'text',
+                    'text'=>['content'=>$content]
+                ],JSON_UNESCAPED_UNICODE));
+                yield $request;
+            }
+        }
+    }
+
+    //新用户大礼包-全量用户
+    private function newUserAll(){
+        $user = $this->user;
+        foreach ($user as $item){
+            $charge = Order::where('uid',$item->uid)->where('status','PAID')->count();
+            if($charge) continue;
+            $force_subscribe_info = DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                ->where('uid',$item->uid)->where('is_subscribed',1)->select('appid','openid','distribution_channel_id')->first();
+            if(!$force_subscribe_info) continue;
+            $accecc_token = $this->getAccessToken($force_subscribe_info->appid);
+            if(!$accecc_token)continue;
+            $content = $this->newUserAllContent($force_subscribe_info->distribution_channel_id);
+            $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accecc_token;
+            $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                'touser'=>$force_subscribe_info->openid,
+                'msgtype'=>'text',
+                'text'=>['content'=>$content]
+            ],JSON_UNESCAPED_UNICODE));
+            yield $request;
+        }
+    }
+
+    private function getUser($time){
+        $s_time = $time+0.5;
+        $b_time = $time-0.5;
+        $user = DB::connection('api_mysql')->table('rfm_ab_test_user')
+            ->where('created_at','>=',date('Y-m-d H:i:s',time()-$s_time*3600))
+            ->where('created_at','<=',date('Y-m-d H:i:s',time()-$b_time*3600))
+            ->where('type','B')
+            ->select('uid','created_at')
+            ->get();
+        if(!$user){
+            $this->user = null;
+            return ;
+        }
+        $this->user = $user;
+    }
+
+    //新用户大礼包-小M值用户   新用户大礼包-大M值用户
+    private function newUserSmAndBm(){
+        $user = $this->user;
+        foreach ($user as $item){
+            $charge = Order::where('uid',$item->uid)->where('status','PAID')->count();
+            if($charge) continue;
+            $force_subscribe_info = DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                ->where('uid',$item->uid)->where('is_subscribed',1)->select('appid','openid','distribution_channel_id')->first();
+            if(!$force_subscribe_info) continue;
+            $status = $this->isAccessSmAndBm($item->uid,$distribution_channel_id);
+            $accecc_token = $this->getAccessToken($force_subscribe_info->appid);
+            if(!$accecc_token)continue;
+            if($status['new_user_activity_sm']){
+                $content = $this->newUserActivitySmContent($force_subscribe_info->distribution_channel_id);
+                $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accecc_token;
+                $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                    'touser'=>$force_subscribe_info->openid,
+                    'msgtype'=>'text',
+                    'text'=>['content'=>$content]
+                ],JSON_UNESCAPED_UNICODE));
+                yield $request;
+            }
+            if($status['new_user_activity_bm']){
+                $content = $this->newUserActivityBmContent($force_subscribe_info->distribution_channel_id);
+                $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accecc_token;
+                $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                    'touser'=>$force_subscribe_info->openid,
+                    'msgtype'=>'text',
+                    'text'=>['content'=>$content]
+                ],JSON_UNESCAPED_UNICODE));
+                yield $request;
+            }
+        }
+    }
+
+
+    private function isAccessSmAndBm($uid,&$distribution_channel_id){
+        $sql = 'select id,created_at,distribution_channel_id from users WHERE  openid = (SELECT  openid FROM  users WHERE  id = '.$uid.')';
+        $result = DB::connection('api_mysql')->select($sql);
+        $return_status = [
+            'new_user_activity_sm'=>false,
+            'new_user_activity_bm'=>false
+        ];
+        foreach ($result as $item){
+            if($item->id == $uid) {
+                $distribution_channel_id = $item->distribution_channel_id;
+                continue;
+            }
+            if($return_status['new_user_activity_sm'] && $return_status['new_user_activity_bm']){
+                return $return_status;
+            }
+            $this_user_level = UserService::rfmLevel($item->id,strtotime($item->created_at));
+            if($this_user_level){
+                $this_user_level_text = implode('',$this_user_level);
+                if(in_array($this_user_level_text,$this->Level['new_user_activity_sm'])){
+                    $return_status['new_user_activity_sm'] = true;
+                }
+                if(in_array($this_user_level_text,$this->Level['new_user_activity_bm'])){
+                    $return_status['new_user_activity_bm'] = true;
+                }
+            }
+        }
+        return $return_status;
+    }
+
+    private function isAccess($uid,$k,&$distribution_channel_id){
+        $sql = 'select id,created_at,distribution_channel_id from users WHERE  openid = (SELECT  openid FROM  users WHERE  id = '.$uid.')';
+        $result = DB::connection('api_mysql')->select($sql);
+        foreach ($result as $item){
+            if($item->id == $uid) {
+                $distribution_channel_id = $item->distribution_channel_id;
+                continue;
+            }
+            $this_user_level = UserService::rfmLevel($item->id,strtotime($item->created_at));
+            if($this_user_level){
+                $this_user_level_text = implode('',$this_user_level);
+                if(in_array($this_user_level_text,$this->Level[$k])){
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+
+    private function newUserActivitySmContent($distribution_channel_id){
+        $text = "您的新用户专享礼包已送达\r\n\r\n充19.9得4000书币,充38得8800书币,只有一次机会哦!\r\n\r\n过期失效,不要错过!\r\n\r\n<a href='%s'>点击立即领取>></a>\r\n\r\n为方便下次阅读,请<a href='%s'>置顶公众号</a>";
+        $link_format = 'https://site%s.%s.com/activity/rfmNUSm?send_time='.time();
+        $link = sprintf($link_format,encodeDistributionChannelId($distribution_channel_id),env('CUSTOM_HOST'));
+        $top = 'https://help.zhuishuyun.com/top.html';
+        return sprintf($text,$link,$top);
+    }
+
+    private function newUserActivityBmContent($distribution_channel_id){
+        $text = "您的新用户专享礼包已送达\r\n\r\n <a href='%s'>优惠充值特惠</a> \r\n\r\n过期失效,不要错过!\r\n\r\n<a href='%s'>点击立即领取>></a>\r\n\r\n为方便下次阅读,请<a href='%s'>置顶公众号</a>";
+        $link_format = 'https://site%s.%s.com/sale/newUserSale?send_time='.time();
+        $link = sprintf($link_format,encodeDistributionChannelId($distribution_channel_id),env('CUSTOM_HOST'));
+        $top = 'https://help.zhuishuyun.com/top.html';
+        return sprintf($text,$link,$link,$top);
+    }
+
+    private function newUserAllContent($distribution_channel_id){
+        $text = "您的新用户专享礼包已送达\r\n\r\n充9.9得2000书币,只有一次机会哦!\r\n\r\n过期失效,不要错过!\r\n\r\n<a href='%s'>点击立即领取>></a>\r\n\r\n为方便下次阅读,请<a href='%s'>置顶公众号</a>";
+        $link_format = 'https://site%s.%s.com/sale/newUserActivity?send_time='.time();
+        $link = sprintf($link_format,encodeDistributionChannelId($distribution_channel_id),env('CUSTOM_HOST'));
+        $top = 'https://help.zhuishuyun.com/top.html';
+        return sprintf($text,$link,$top);
+    }
+
+    private function getAccessToken($appid){
+        try{
+            $WechatController = new WechatController($appid);
+            $accessToken = $WechatController->app->access_token; // EasyWeChat\Core\AccessToken 实例
+            $token = $accessToken->getToken(); // token 字符串
+            return $token;
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+        return '';
+    }
+
+
+
+}

+ 195 - 0
app/Console/Commands/SmartPush/remindSign.php

@@ -0,0 +1,195 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2019/10/23
+ * Time: 16:44
+ */
+
+namespace App\Console\Commands\SmartPush;
+
+use App\Modules\User\Services\UserSignService;
+use Illuminate\Console\Command;
+use DB;
+use Redis;
+use GuzzleHttp\Client;
+use App\Http\Controllers\WechatController;
+use GuzzleHttp\Psr7\Request as GuzzleRequest;
+use GuzzleHttp\Pool;
+
+class remindSign extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'SmartPush:remindSign {--start=} {--end=}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '签到提醒';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->send();
+    }
+
+    private  function generateRequest()
+    {
+        $switch_array = [];
+        $start = $this->option('start',0);
+        $end = $this->option('end');
+        $temp = $start;
+        $continueReadUrlFormat = 'https://site%s.%s.com/sign';
+        $info = DB::connection('api_mysql')->table('custom_msg_switchs')->where('custom_category','remind_sign')->first();
+        if(!$info) return ;
+        $default_status = $info->default_switch_status;
+        while (true){
+            if($end == -1){
+                $user =  DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                    ->select('id','uid','distribution_channel_id','openid','appid')
+                    ->where('id','>',$temp)
+                    ->where('last_interactive_time','>',date('Y-m-d H:i:s',time()-86400*2))
+                    ->orderBy('id')
+                    ->limit(5000)
+                    ->get();
+            }else{
+                $user =  DB::connection('api_mysql')->table('temp_force_subscribe_users')
+                    ->select('id','uid','distribution_channel_id','openid','appid')
+                    ->where('id','>',$temp)
+                    ->where('id','<=',$end)
+                    ->where('last_interactive_time','>',date('Y-m-d H:i:s',time()-86400*2))
+                    ->orderBy('id')
+                    ->limit(5000)
+                    ->get();
+            }
+
+            if(!$user) break;
+
+            foreach ($user as $item){
+                //if(!in_array($item->distribution_channel_id,[70])) continue;
+                //\Log::info( '---------------------------start--start-------------------------------------------' );
+                //\Log::info('$item->distribution_channel_id is :'.$item->distribution_channel_id  );
+                //\Log::info( 'remindSign $item->id is :'.$item->id );
+                if(!isset($switch_array[$item->distribution_channel_id])){
+                    $switch = DB::connection('api_mysql')->table('custom_msg_switchs_msgs')
+                        ->where('distribution_channel_id',$item->distribution_channel_id)
+                        ->where('custom_category','remind_sign')
+                        ->select('status')
+                        ->first();
+                    if($switch){
+                        $switch_array[$item->distribution_channel_id] = $switch->status;
+                    }else{
+                        $switch_array[$item->distribution_channel_id] = $default_status;
+                    }
+                }
+
+                $switch_status = $switch_array[$item->distribution_channel_id];
+                if($switch_status != 1) continue;
+                //\Log::info( 'remindSign $item->distribution_channel_id in is :'.$item->distribution_channel_id );
+
+                $is_sign = $this->isSign($item->uid);
+                if(!$item->uid)$item->uid = 0;
+                //\Log::info( $read_info );
+                //\Log::info( 'uid is :'.$item->uid  );
+                if($is_sign) continue;
+                $user_info = DB::connection('api_mysql')->table('users')->where('id',$item->uid)->select('nickname','openid','created_at')->first();
+                $nickname = '读者';
+                if($user_info && $user_info->nickname)$nickname = $user_info->nickname;
+
+                $openid = $item->openid;
+                $appid = $item->appid;
+                //$url = sprintf($continueReadUrlFormat,encodeDistributionChannelId($item->distribution_channel_id),env('CUSTOM_HOST'));
+                $url = 'weixin://bizmsgmenu?msgmenucontent=签到&msgmenuid=1';
+                $guide_status = false;
+                if($user_info){
+                    $guide_status = UserSignService::guideFans($item->uid,$item->distribution_channel_id,$user_info->openid,$user_info->created_at);
+                }
+                if($guide_status){
+                    $guide_link = 'https://site'.encodeDistributionChannelId($item->distribution_channel_id).'.leyuee.com/guidestrem?uid='.$item->uid.'&fee=500';
+                    $content_format = "亲爱的%s,您今日还未签到,本次签到最高可领取150书币哦\r\n\r\n❤<a href='%s'>点击此处签到领书币</a>\r\n\r\n<a href='%s'>免费领取500书币奖励>></a> \r\n\r\n为了方便下次阅读,请<a href='%s'>置顶公众号</a>";
+                    $content = sprintf($content_format,$nickname,$url,$guide_link,
+                        'https://help.zhuishuyun.com/top.html'
+                    );
+                }else{
+                    $content_format = "亲爱的%s,您今日还未签到,本次签到最高可领取150书币哦\r\n\r\n❤<a href='%s'>点击此处签到领书币</a>\r\n\r\n\r\n为了方便下次阅读,请<a href='%s'>置顶公众号</a>";
+                    $content = sprintf($content_format,
+                        $nickname,$url,
+                        'https://help.zhuishuyun.com/top.html'
+                    );
+                }
+
+                $accecc_token = $this->getAccessToken($appid);
+                if(!$accecc_token)continue;
+
+                $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accecc_token;
+                $request = new GuzzleRequest('post',$url,[],\GuzzleHttp\json_encode([
+                    'touser'=>$openid,
+                    'msgtype'=>'text',
+                    'text'=>['content'=>$content]
+                ],JSON_UNESCAPED_UNICODE));
+
+                //\Log::info( '----------------------------end-end-end-------------------------------------------' );
+                yield $request;
+            }
+            $temp = $item->id;
+
+        }
+    }
+
+
+    private function getAccessToken($appid){
+        try{
+            $WechatController = new WechatController($appid);
+            $accessToken = $WechatController->app->access_token; // EasyWeChat\Core\AccessToken 实例
+            $token = $accessToken->getToken(); // token 字符串
+            return $token;
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+        return '';
+    }
+
+    private function send(){
+        $client = new Client();
+        $requests = $this->generateRequest();
+        $pool = new Pool($client, $requests, [
+            'concurrency' => 5,
+            'fulfilled' => function ($response, $index) {
+
+            },
+            'rejected' => function ($reason, $index) {
+
+            },
+        ]);
+        $promise = $pool->promise();
+        $promise->wait();
+    }
+
+    private function isSign($uid){
+        $sign_day = Redis::hget('book_read:' . $uid,'sign_day');
+        if ($sign_day && $sign_day == date('Y-m-d')) {
+            return true;
+        }
+        return false;
+    }
+}

+ 115 - 0
app/Console/Commands/Test3.php

@@ -0,0 +1,115 @@
+<?php
+
+
+namespace App\Console\Commands;
+
+
+use App\Modules\OfficialAccount\Services\CustomMsgService;
+use App\Modules\WechatMaterial\Models\BatchWechatMaterial;
+use App\Modules\WechatMaterial\Models\WechatMaterial;
+use App\Modules\WechatMaterial\Models\WechatMaterialSendMsg;
+use Illuminate\Console\Command;
+
+class Test3 extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'test:3 {appId} {channelId} {batchId}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    private $_task = 'ActionTrigger:sync_zs_wechat_material ';
+
+    /**
+     * @return bool
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     */
+    public function handle()
+    {
+        [$appId, $batchId, $channelId] = [$this->argument('appId'), $this->argument('batchId'), $this->argument('channelId')];
+
+        // 获取参数
+        if (!$appId || !$batchId || !$channelId) {
+            return false;
+        }
+
+        // 获取msg信息,未查询到数据或者media_id已存在则不再继续往下执行
+        $msgInfo = WechatMaterialSendMsg::getMsgByAppBatchChannelId($appId, $batchId, $channelId);
+        \Log::info($this->_task . 'msgInfo');
+        \Log::info($msgInfo);
+        if (!$msgInfo) {
+            return false;
+        }
+
+        // 获取素材数据
+        $materials = WechatMaterial::getWeChatMaterials($batchId);
+        $materials = $materials ? $materials->toArray() : [];
+        \Log::info($this->_task . 'materials_ids');
+        \Log::info(array_column($materials, 'id'));
+        if (!$materials) {
+            return false;
+        }
+
+        // 筛选出内容中的所有图片
+        $contents = implode(',', array_column($materials, 'content'));
+        $images   = CustomMsgService::extract_content_images($contents);
+        \Log::info($this->_task . 'extract_content_image_urls');
+        \Log::info($images);
+
+        // 上传内容中的图片,替换内容中图片链接
+        $upImagesRes = CustomMsgService::multi_upload_material_imgs($appId, $images);
+        [$oldUrls, $newUrls] = [array_column($upImagesRes['urls'], 'old_url'), array_column($upImagesRes['urls'], 'new_url')];
+        $materials = CustomMsgService::replace_content_images($materials, $oldUrls, $newUrls);
+
+        dd($images, $upImagesRes, $oldUrls, $newUrls, $materials);
+
+        // 批量上传缩略图
+        $thumbImages = array_column($materials, 'cover_url');
+        $upThumbsRes = CustomMsgService::multi_upload_material_imgs($appId, $thumbImages, 'thumb');
+
+        // 上传article
+        $upArticleRes = CustomMsgService::upload_articles($channelId, $msgInfo, $materials, $upThumbsRes['urls']);
+        $wxMediaId    = getProp($upArticleRes['data'], 'media_id');
+        if ($wxMediaId) {
+            // 更新media_id
+            WechatMaterialSendMsg::where('id', getProp($msgInfo, 'id'))->update([
+                'wechat_media_id' => $wxMediaId,
+                'upload_result'   => json_encode([
+                    'up_images'   => $upImagesRes['urls'],
+                    'up_thumbs'   => $upThumbsRes['urls'],
+                    'up_articles' => $upArticleRes
+                ]),
+                'status'          => 'push_wechat_material',
+                'updated_at'      => date('Y-m-d H:i:s')
+            ]);
+
+            // 更新batch同步状态
+            BatchWechatMaterial::where('id', $batchId)->update([
+                'status'     => 'push_wechat_material',
+                'updated_at' => date('Y-m-d H:i:s')
+            ]);
+        } else {
+            $msg = 'upload fail';
+            if ($upArticleRes && !$upArticleRes['code']) $msg = $upArticleRes['msg'];
+            if (getProp($upThumbsRes['error'], 'thumb')) $msg = getProp($upThumbsRes['error'], 'thumb');
+            if (getProp($upImagesRes['error'], 'common')) $msg = getProp($upImagesRes['error'], 'common');
+
+            // 更新上传结果
+            WechatMaterialSendMsg::where('id', getProp($msgInfo, 'id'))->update([
+                'upload_result' => $msg,
+                'status'        => 'fail_push_wechat_material',
+                'updated_at'    => date('Y-m-d H:i:s')
+            ]);
+        }
+
+    }
+
+}

+ 76 - 0
app/Console/Commands/Tool/ContinueReadTool.php

@@ -0,0 +1,76 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2020/5/11
+ * Time: 10:51
+ */
+
+namespace pp\Console\Commands\ToolA;
+
+
+use Illuminate\Console\Command;
+
+class ContinueReadTool extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'Tool:ContinueReadTool';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '继续阅读推送表迁移';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+    }
+
+    //寻找custom_push_keep_continue表距离当前一天的记录id
+    private function findId(){
+        $id = 0;
+        while (true){
+            if($id){
+                $result = DB::connection('api_mysql')
+                    ->table('custom_push_keep_continue')
+                    ->where('id','<',$id)
+                    ->select('id','created_at')->orderBy('id','desc')->first();
+            }else{
+                $result = DB::connection('api_mysql')->table('custom_push_keep_continue')->select('id','created_at')->orderBy('id','desc')->first();
+            }
+            if($result){
+                foreach ($result as $item){
+                    $id = $item->id;
+                    if(strtotime($item->created_at) <= time()-86400*1.5){
+                        return $item->id;
+                    }
+                }
+            }else{
+                break;
+            }
+        }
+        return $id;
+    }
+
+
+}

+ 462 - 0
app/Console/Commands/Wechat/AdReport.php

@@ -0,0 +1,462 @@
+<?php
+
+namespace App\Console\Commands\Wechat;
+
+use GuzzleHttp\Client;
+use Illuminate\Console\Command;
+use App\Http\Controllers\WechatController;
+use Exception;
+use Log;
+use DB;
+use Redis;
+
+class AdReport extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'Wechat:WeixinAdReport';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    private $user_action_set_id = [];
+
+    private $assess_token = [];
+
+    private $error_msg;
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->start();
+    }
+    private function start(){
+        $data = $this->getDataFromDB();
+        if($data){
+            $this->getDataAndReport($data);
+        }
+    }
+
+    private function getDataFromDB(){
+        $data = [];
+        $start = date('Y-m-d');
+        if(date('H:i:s') < '02:00:00'){
+            $start = date('Y-m-d H:i:s',time()-3*3600);
+        }
+        $result = DB::connection('api_mysql')->table('distribution_channel_weixin_ad_report_orders')
+            ->where('report_status',0)
+            ->where('created_at','>=',$start)
+            ->select('order_id','appid','openid','price','report_status','created_at','report_type')
+            ->get();
+        if($result){
+            foreach ($result as $item){
+                $order_status = DB::connection('api_mysql')->table('orders')->where('id',$item->order_id)->select('status')->first();
+                if($order_status->status == 'UNPAID')continue;
+                $report_info = DB::table('wechat_advertise_report_record')->where('product_id',$item->order_id)->where('result_status','SUCCESS')->count();
+                if($report_info) {
+                    try{
+                        DB::connection('api_mysql')->table('distribution_channel_weixin_ad_report_orders')->where('order_id',$item->order_id)->update([
+                            'report_status'=>1,
+                            'updated_at'=>date('Y-m-d H:i:s')
+                        ]);
+                    }catch (\Exception $e){
+                        \Log::info($e);
+                    }
+
+                    continue;
+                }
+                $data[] = ['created_at' => $item->created_at, 'openid' => $item->openid,'report_type'=>$item->report_type,
+                    'id' => $item->order_id, 'appid' => $item->appid, 'price' => $item->price];
+            }
+        }
+        return $data;
+    }
+
+    private function getDataAndReport($order_result){
+        $result = $order_result;
+        $client = new Client();
+        foreach ($result as $item){
+            $report_type = $this->transferReportType($item['report_type'],$item['created_at'],$item['id']);
+            $item_array = $item;
+            $user_action_set_id = $this->getDataSurce($item_array['appid']);
+            $data = [
+                'user_action_set_id'=>$user_action_set_id,
+                'url'=>'',
+                'action_time'=>strtotime($item_array['created_at']),
+                'action_type'=>'COMPLETE_ORDER',
+                'openid'=>$item_array['openid'],
+                'appid'=>$item_array['appid'],
+                'product_id'=>$item_array['id'],
+                'value'=>$item_array['price']*100,
+                'source'=>'Biz',
+                'claim_type'=>0,
+                'object'=>$report_type
+            ];
+            $data['created_at'] = date('Y-m-d H:i:s');
+            $data['updated_at'] = date('Y-m-d H:i:s');
+            $data['result_status'] = 'FAIL';
+
+            $book_info = $this->getBookName($item_array['id']);
+            $product_id = '';
+            $product_name = '';
+            $data['bid'] = $book_info['en_bid'];
+            if($book_info['en_bid'] && $book_info['book_name']){
+                $product_id = $book_info['en_bid'];
+                $data['bid'] = $book_info['en_bid'];
+                $product_name = $book_info['book_name'].'-'.env('PROJECT_NAME');
+                $data['product_name'] = $product_name;
+                $data['url'] = env('GDT_REPORT_URL').'/detail?id='.$book_info['en_bid'];
+            }
+
+            $data['result'] = '';
+            if(!$user_action_set_id) {
+                $data['result_msg'] = $this->error_msg;
+                DB::table('wechat_advertise_report_record')->insert($data);
+                continue;
+            };
+            $request_data = [
+                'actions'=>[
+                    [
+                        'user_action_set_id'=>$data['user_action_set_id'],
+                        'url'=>$data['url'],
+                        'action_time'=>$data['action_time'],
+                        'action_type'=>$data['action_type'],
+                        'user_id'=>[
+                            'wechat_app_id'=>$data['appid'],
+                            'wechat_openid'=>$data['openid']
+                        ],
+                        'action_param'=>[
+                            'object'=>$report_type,
+                            'product_name'=>$product_name,
+                            'product_id'=>$product_id,
+                            'source'=>$data['source'],
+                            'wechat_app_id'=>'',
+                            'claim_type'=>$data['claim_type'],
+                            'value'=>$item_array['price']*100
+                        ]
+                    ]
+                ]
+            ];
+
+            $token = $this->getAccessToken($item_array['appid']);
+            if(!$token) continue;
+            $url = 'https://api.weixin.qq.com/marketing/user_actions/add?version=v1.0&access_token='.$token;
+            try{
+                $result = $client->request('post',$url,[
+                    'headers'=>['Content-Type'=>'application/json'],
+                    'body'=>\GuzzleHttp\json_encode($request_data)
+                ])->getBody()->getContents();
+                Log::info('user_actions/add result is: '.$result);
+                $result_array = \GuzzleHttp\json_decode($result,1);
+                if(isset($result_array['errcode']) && $result_array['errcode'] == 0){
+                    $data['result_status'] = 'SUCCESS';
+                    $data['result_msg'] = $result_array['errcode'];
+                    $data['result'] = $result;
+                }else{
+                    $data['result_status'] = 'FAIL';
+                    $data['result_msg'] = isset($result_array['errcode'])?$result_array['errcode']:'unknown';
+                    $data['result'] = $result;
+                }
+
+                DB::table('wechat_advertise_report_record')->insert($data);
+                try{
+                    if($data['result_status'] == 'SUCCESS'){
+                        DB::connection('api_mysql')->table('distribution_channel_weixin_ad_report_orders')->where('order_id',$item_array['id'])->update([
+                        'report_status'=>1,
+                        'updated_at'=>date('Y-m-d H:i:s')
+                    ]);
+                    }
+                }catch (\Exception $e){\Log::info($e);}
+
+            }catch (Exception $e){
+                Log::error($e);
+            }
+
+            try{
+                 $this->secondReport($client,$item,$user_action_set_id,$book_info['en_bid'],$book_info['book_name'],$token,$report_type);
+
+            }catch (\Exception $e){
+                \Log::info($e);
+            }
+        }
+
+    }
+
+    private function secondReport(Client $client,$data,$user_action_set_id,$bid='',$book_name='',$token='',$report_type){
+        $item_array = $data;
+        $data = [
+            'user_action_set_id'=>$user_action_set_id,
+            'url'=>'',
+            'action_time'=>strtotime($item_array['created_at']),
+            'action_type'=>'PURCHASE',
+            'openid'=>$item_array['openid'],
+            'appid'=>$item_array['appid'],
+            'product_id'=>$item_array['id'],
+            'value'=>$item_array['price']*100,
+            'source'=>'Biz',
+            'claim_type'=>0,
+            'object'=>$report_type
+        ];
+        $data['created_at'] = date('Y-m-d H:i:s');
+        $data['updated_at'] = date('Y-m-d H:i:s');
+        $data['result_status'] = 'FAIL';
+        $product_id = '';
+        $product_name = '';
+        $data['bid'] = '';
+        if($bid && $book_name){
+            $product_id = $bid;
+            $data['bid'] = $bid;
+            $product_name = $book_name.'-'.env('PROJECT_NAME');
+            $data['product_name'] = $product_name;
+            $data['url'] = env('GDT_REPORT_URL').'/detail?id='.$bid;
+        }
+        $data['result'] = '';
+        if(!$user_action_set_id) {
+            $data['result_msg'] = $this->error_msg;
+            DB::table('wechat_advertise_report_record')->insert($data);
+        };
+
+        $request_data = [
+            'actions'=>[
+                [
+                    'user_action_set_id'=>$data['user_action_set_id'],
+                    'url'=>$data['url'],
+                    'action_time'=>$data['action_time'],
+                    'action_type'=>$data['action_type'],
+                    'user_id'=>[
+                        'wechat_app_id'=>$data['appid'],
+                        'wechat_openid'=>$data['openid']
+                    ],
+                    'action_param'=>[
+                        'object'=>$report_type,
+                        'product_name'=>$product_name,
+                        'product_id'=>$product_id,
+                        'source'=>$data['source'],
+                        'wechat_app_id'=>'',
+                        'claim_type'=>$data['claim_type'],
+                        'value'=>$item_array['price']*100
+                    ]
+                ]
+            ]
+        ];
+
+        if(!$token) return '';
+        $url = 'https://api.weixin.qq.com/marketing/user_actions/add?version=v1.0&access_token='.$token;
+        try{
+            $result = $client->request('post',$url,[
+                'headers'=>['Content-Type'=>'application/json'],
+                'body'=>\GuzzleHttp\json_encode($request_data)
+            ])->getBody()->getContents();
+            Log::info('user_actions/add result is: '.$result);
+            $result_array = \GuzzleHttp\json_decode($result,1);
+            if(isset($result_array['errcode']) && $result_array['errcode'] == 0){
+                $data['result_status'] = 'SUCCESS';
+                $data['result_msg'] = $result_array['errcode'];
+                $data['result'] = $result;
+            }else{
+                $data['result_status'] = 'FAIL';
+                $data['result_msg'] = isset($result_array['errcode'])?$result_array['errcode']:'unknown';
+                $data['result'] = $result;
+            }
+
+            DB::table('wechat_advertise_report_record')->insert($data);
+            return $data['result_status'];    
+        }catch (Exception $e){
+            Log::error($e);
+        }
+        return '';
+    }
+
+    private function getDataSurce($appid,$type='WECHAT'){
+        if(!isset($this->user_action_set_id[$appid])){
+            $result = $this->getDataSourceFromDB($appid,$type);
+            if($result){
+                $this->user_action_set_id[$appid] = $result;
+                return $result;
+            }
+            $result = $this->createDataSource($appid,$type);
+            if($result){
+                $this->user_action_set_id[$appid] = $result;
+                return $result;
+            }
+            Log::info('appid is :'.$appid.', createDataSource fail');
+            return '';
+        }
+        return  $this->user_action_set_id[$appid];
+
+    }
+    private function getAccessToken($appid){
+        if(isset($this->assess_token[$appid])){
+            return $this->assess_token[$appid];
+        }
+        /*$WechatController = new WechatController($appid);
+        $accessToken = $WechatController->app->access_token; // EasyWeChat\Core\AccessToken 实例
+        $token = $accessToken->getToken(); // token 字符串
+        if($token){
+            $this->assess_token[$appid] = $token;
+            return $token;
+        }
+        Log::info('appid is :'.$appid.',request access_token fail');
+        return '';*/
+        $access_token = Redis::get('Wechat:access_token:appid:'.$appid);
+        if($access_token){
+            $this->assess_token[$appid] = $access_token;
+            return $this->assess_token[$appid];
+        }
+        /*$appsecret = 'f73d3be068d86417bf1a3fafefe4596a';
+        $url = sprintf(
+            'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s',
+            $appid,
+            $appsecret
+        );
+        $client = new Client();
+        $result = $client->request('get',$url)->getBody()->getContents();
+        Log::info('request access token ,result is:'.$result);
+        $result = \GuzzleHttp\json_decode($result,1);
+        if($result && isset($result['access_token'])){
+            Redis::setex('Wechat:access_token:appid:'.$appid,7000,$result['access_token']);
+            $this->assess_token[$appid] = $result['access_token'];
+            return $this->assess_token[$appid];
+        }*/
+        $this->error_msg = $appid.',access_token fail';
+        return '';
+    }
+
+    private function createDataSource(string $appid,string $type){
+        $token = $this->getAccessToken($appid);
+        if(!$token) return 0;
+        $url = 'https://api.weixin.qq.com/marketing/user_action_sets/add?version=v1.0&access_token='.$token;
+        $client = new Client();
+        $name = '支付下单';
+        $description = '支付下单,充值统计';
+        $body = json_encode([
+            'type'=>$type,
+            'wechat_app_id'=>$appid,
+            'name'=>$name,
+            'description'=>$description
+        ]);
+        try{
+            $result = $client->request('post',$url,[
+                'headers'=>['Content-Type'=>'application/json'],
+                'body'=>$body
+            ])->getBody()->getContents();
+            $result = \GuzzleHttp\json_decode($result,1);
+            if($result['errcode'] == 0 && isset($result['data']) && isset($result['data']['user_action_set_id'])){
+                $user_action_set_id = $result['data']['user_action_set_id'];
+                $this->saveDataSource($appid,$name,$type,$description,$user_action_set_id);
+                return $user_action_set_id;
+            }
+            if($result['errcode'] == 900351000){
+                $msg_array = explode(':',$result['errmsg']);
+                if(isset($msg_array[1])){
+                    $user_action_set_id = trim($msg_array[1]);
+                    $this->saveDataSource($appid,$name,$type,$description,$user_action_set_id);
+                    return $user_action_set_id;
+                }
+            }
+        }catch (Exception $e){
+            Log::error($e);
+        }
+        $this->error_msg = $appid.',createDataSource fail';
+        return 0;
+    }
+
+    private function getDataSourceFromDB($appid,$type):int
+    {
+        $result = DB::table('wechat_advertise_data_source')->where('appid', $appid)->where('type', $type)->select('user_action_set_id')->first();
+        if($result){
+            return $result->user_action_set_id;
+        }
+        return 0;
+    }
+
+    private function saveDataSource(string $appid,string $name,string $type,string $description,int $user_action_set_id){
+        DB::table('wechat_advertise_data_source')->insert([
+            'appid'=>$appid,
+            'name'=>$name,
+            'type'=>$type,
+            'description'=>$description,
+            'user_action_set_id'=>$user_action_set_id,
+            'created_at'=>date('Y-m-d H:i:s'),
+            'updated_at'=>date('Y-m-d H:i:s')
+        ]);
+    }
+
+
+    private function getBookName($order_id){
+        $result = ['bid'=>'','book_name'=>'','en_bid'=>''];
+        try{
+            $order_info = DB::connection('api_mysql')->table('orders')->select('from_bid')->where('id',$order_id)->first();
+            if($order_info && $order_info->from_bid){
+                $result['bid'] = $order_info->from_bid;
+                $result['en_bid'] = \Hashids::encode($order_info->from_bid);
+                $book_info = DB::connection('api_mysql')->table('book_configs')
+                    ->select('book_name','is_on_shelf')
+                    ->where('bid',$order_info->from_bid)->first();
+                if($book_info){
+                    if($book_info->is_on_shelf == 2){
+                        $result['book_name'] = $book_info->book_name;
+                    }
+                }
+                return $result;
+            }
+        }catch (\Exception $e){
+
+        }
+        return $result;
+    }
+    private function transferReportType($report_type,$order_time,$order_id){
+        $map = [
+            'one_day_first_pay'=>'read_1',
+            'one_day_all_pay'=>'read_2',
+            'current_day_register_first_pay'=>'read_3',
+            'current_day_register'=>'read_4',
+            'three_day_first_pay'=>'read_5',
+            'three_day_all_pay'=>'read_6'
+        ];
+        if(isset($map[$report_type])){
+            return $map[$report_type];
+        }
+        $order_info = DB::connection('api_mysql')->table('orders')->where('id',$order_id)->select('uid')->first();
+        if(!$order_info){
+            return '';
+        }
+        $uid = $order_info->uid;
+        $user_info = DB::connection('api_mysql')->table('users')->where('id',$uid)->select('created_at')->first();
+        if(!$user_info) return '';
+        $register_time = $user_info->created_at;
+        $register_timestamp = strtotime($register_time);
+        $order_timestamp = strtotime($order_time);
+        if(date('Y-m-d',$register_timestamp) == date('Y-m-d',$order_timestamp)){
+            return 'read_4';
+        }
+        if(strtotime($order_time) -  strtotime($register_time)  < 86400){
+            return 'read_2';
+        }
+        if(strtotime($order_time) -  strtotime($register_time)  < 3*86400){
+            return 'read_6';
+        }
+        return '';
+    }
+}

+ 447 - 0
app/Console/Commands/Wechat/AdReportTest.php

@@ -0,0 +1,447 @@
+<?php
+
+namespace App\Console\Commands\Wechat;
+
+use GuzzleHttp\Client;
+use Illuminate\Console\Command;
+use App\Http\Controllers\WechatController;
+use Exception;
+use Log;
+use DB;
+use Redis;
+
+class AdReportTest extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'Wechat:WeixinAdReportTest';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    private $user_action_set_id = [];
+
+    private $assess_token = [];
+
+    private $error_msg;
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        //
+        /*$data = [
+            'created_at'=>date('Y-m-d H:i:s'),
+            'openid'=>'onkhc1BlHQzKuV_5o0n9wxo9Cu8A',
+            'id'=>'20488494',
+            'appid'=>'wx81f3952367416341',
+            'price'=>0.01,
+        ];*/
+        //Redis::RPUSH('Wechat:WeixinAdReport',\GuzzleHttp\json_encode($data));
+
+        //echo \GuzzleHttp\json_encode($data);
+        $this->start();
+    }
+    private function start(){
+        /*$data = $this->getDataFromDB();
+        if($data){
+            $this->getDataAndReport($data);
+        }*/
+        $data = [
+            [
+                'created_at'=>date('Y-m-d H:i:s'),
+                'openid'=>'oTO7qvlsPvv6B2dtQM8h3Cb6CXjI',
+                'id'=>'26787982',
+                'appid'=>'wx422e5d5e11db16d9',
+                'price'=>0.01,
+            ]
+        ];
+        $this->getDataAndReport($data);
+    }
+
+    private function getDataFromDB(){
+        $data = [];
+        $start = date('Y-m-d');
+        if(date('H:i:s') < '02:00:00'){
+            $start = date('Y-m-d H:i:s',time()-3*3600);
+        }
+        $result = DB::connection('api_mysql')->table('distribution_channel_weixin_ad_report_orders')
+            ->where('report_status',0)
+            ->where('created_at','>=',$start)
+            ->select('order_id','appid','openid','price','report_status','created_at')
+            ->get();
+        if($result){
+            foreach ($result as $item){
+                $order_status = DB::connection('api_mysql')->table('orders')->where('id',$item->order_id)->select('status')->first();
+                if($order_status->status == 'UNPAID')continue;
+                $report_info = DB::table('wechat_advertise_report_record')->where('product_id',$item->order_id)->where('result_status','SUCCESS')->count();
+                if($report_info) {
+                    try{
+                        DB::connection('api_mysql')->table('distribution_channel_weixin_ad_report_orders')->where('order_id',$item->order_id)->update([
+                            'report_status'=>1,
+                            'updated_at'=>date('Y-m-d H:i:s')
+                        ]);
+                    }catch (\Exception $e){
+                        \Log::info($e);
+                    }
+
+                    continue;
+                }
+                $data[] = ['created_at' => $item->created_at, 'openid' => $item->openid,
+                    'id' => $item->order_id, 'appid' => $item->appid, 'price' => $item->price];
+            }
+        }
+        return $data;
+    }
+
+    private function getDataAndReport($order_result){
+        $result = $order_result;
+        /*$result = Redis::LRANGE('Wechat:WeixinAdReport',0,-1);
+        Redis::del('Wechat:WeixinAdReport');
+        Log::info($result);
+        if(!$result){
+            Log::info('Wechat:WeixinAdReport,empty list');
+        }*/
+        $client = new Client();
+        foreach ($result as $item){
+            //$item_array = \GuzzleHttp\json_decode($item,1);
+            $item_array = $item;
+            $user_action_set_id = $this->getDataSurce($item_array['appid']);
+            $data = [
+                'user_action_set_id'=>$user_action_set_id,
+                'url'=>'',
+                'action_time'=>strtotime($item_array['created_at']),
+                'action_type'=>'COMPLETE_ORDER',
+                'openid'=>$item_array['openid'],
+                'appid'=>$item_array['appid'],
+                'product_id'=>$item_array['id'],
+                'value'=>$item_array['price']*100,
+                'source'=>'Biz',
+                'claim_type'=>0
+            ];
+            $data['created_at'] = date('Y-m-d H:i:s');
+            $data['updated_at'] = date('Y-m-d H:i:s');
+            $data['result_status'] = 'FAIL';
+            $book_info = $this->getBookName($item_array['id']);
+            $product_id = '';
+            $product_name = '';
+            $data['bid'] = $book_info['en_bid'];
+            if($book_info['en_bid'] && $book_info['book_name']){
+                $product_id = $book_info['en_bid'];
+                $data['bid'] = $book_info['en_bid'];
+                $product_name = $book_info['book_name'].'-'.env('PROJECT_NAME');
+                $data['product_name'] = $product_name;
+            }
+
+            $data['result'] = '';
+            if(!$user_action_set_id) {
+                $data['result_msg'] = $this->error_msg;
+                DB::table('wechat_advertise_report_record')->insert($data);
+                continue;
+            };
+
+            $request_data = [
+                'actions'=>[
+                    [
+                        'user_action_set_id'=>$data['user_action_set_id'],
+                        'url'=>$data['url'],
+                        'action_time'=>$data['action_time'],
+                        'action_type'=>$data['action_type'],
+                        'user_id'=>[
+                            'wechat_app_id'=>$data['appid'],
+                            'wechat_openid'=>$data['openid']
+                        ],
+                        'action_param'=>[
+                            'object'=>'',
+                            'product_name'=>$product_name,
+                            'product_id'=>$product_id,
+                            'source'=>$data['source'],
+                            'wechat_app_id'=>'',
+                            'claim_type'=>$data['claim_type'],
+                            'value'=>$item_array['price']*100
+                        ]
+                    ]
+                ]
+            ];
+
+            $token = $this->getAccessToken($item_array['appid']);
+            if(!$token) continue;
+            $url = 'https://api.weixin.qq.com/marketing/user_actions/add?version=v1.0&access_token='.$token;
+            try{
+                $result = $client->request('post',$url,[
+                    'headers'=>['Content-Type'=>'application/json'],
+                    'body'=>\GuzzleHttp\json_encode($request_data)
+                ])->getBody()->getContents();
+                Log::info('user_actions/add result is: '.$result);
+                $result_array = \GuzzleHttp\json_decode($result,1);
+                if(isset($result_array['errcode']) && $result_array['errcode'] == 0){
+                    $data['result_status'] = 'SUCCESS';
+                    $data['result_msg'] = $result_array['errcode'];
+                    $data['result'] = $result;
+                }else{
+                    $data['result_status'] = 'FAIL';
+                    $data['result_msg'] = isset($result_array['errcode'])?$result_array['errcode']:'unknown';
+                    $data['result'] = $result;
+                }
+
+                DB::table('wechat_advertise_report_record')->insert($data);
+                try{
+                    if($data['result_status'] == 'SUCCESS'){
+                        DB::connection('api_mysql')->table('distribution_channel_weixin_ad_report_orders')->where('order_id',$item_array['id'])->update([
+                        'report_status'=>1,
+                        'updated_at'=>date('Y-m-d H:i:s')
+                    ]);
+                    }
+                }catch (\Exception $e){
+                    \Log::info($e);
+                }
+
+            }catch (Exception $e){
+                Log::error($e);
+            }
+            try{
+                $this->secondReport($client,$item,$user_action_set_id,$book_info['en_bid'],$book_info['book_name'],$token);
+            }catch (\Exception $e){
+                \Log::info($e);
+            }
+        }
+
+    }
+
+    private function secondReport(Client $client,$data,$user_action_set_id,$bid='',$book_name='',$token=''){
+        $item_array = $data;
+        $data = [
+            'user_action_set_id'=>$user_action_set_id,
+            'url'=>'',
+            'action_time'=>strtotime($item_array['created_at']),
+            'action_type'=>'PURCHASE',
+            'openid'=>$item_array['openid'],
+            'appid'=>$item_array['appid'],
+            'product_id'=>$item_array['id'],
+            'value'=>$item_array['price']*100,
+            'source'=>'Biz',
+            'claim_type'=>0
+        ];
+        $data['created_at'] = date('Y-m-d H:i:s');
+        $data['updated_at'] = date('Y-m-d H:i:s');
+        $data['result_status'] = 'FAIL';
+        $product_id = '';
+        $product_name = '';
+        $data['bid'] = '';
+        if($bid && $book_name){
+            $product_id = $bid;
+            $data['bid'] = $bid;
+            $product_name = $book_name.'-'.env('PROJECT_NAME');
+            $data['product_name'] = $product_name;
+        }
+        $data['result'] = '';
+        if(!$user_action_set_id) {
+            $data['result_msg'] = $this->error_msg;
+            DB::table('wechat_advertise_report_record')->insert($data);
+        };
+
+        $request_data = [
+            'actions'=>[
+                [
+                    'user_action_set_id'=>$data['user_action_set_id'],
+                    'url'=>$data['url'],
+                    'action_time'=>$data['action_time'],
+                    'action_type'=>$data['action_type'],
+                    'user_id'=>[
+                        'wechat_app_id'=>$data['appid'],
+                        'wechat_openid'=>$data['openid']
+                    ],
+                    'action_param'=>[
+                        'object'=>'',
+                        'product_name'=>$product_name,
+                        'product_id'=>$product_id,
+                        'source'=>$data['source'],
+                        'wechat_app_id'=>'',
+                        'claim_type'=>$data['claim_type'],
+                        'value'=>$item_array['price']*100
+                    ]
+                ]
+            ]
+        ];
+
+        if(!$token) return ;
+        $url = 'https://api.weixin.qq.com/marketing/user_actions/add?version=v1.0&access_token='.$token;
+        try{
+            $result = $client->request('post',$url,[
+                'headers'=>['Content-Type'=>'application/json'],
+                'body'=>\GuzzleHttp\json_encode($request_data)
+            ])->getBody()->getContents();
+            Log::info('user_actions/add result is: '.$result);
+            $result_array = \GuzzleHttp\json_decode($result,1);
+            if(isset($result_array['errcode']) && $result_array['errcode'] == 0){
+                $data['result_status'] = 'SUCCESS';
+                $data['result_msg'] = $result_array['errcode'];
+                $data['result'] = $result;
+            }else{
+                $data['result_status'] = 'FAIL';
+                $data['result_msg'] = isset($result_array['errcode'])?$result_array['errcode']:'unknown';
+                $data['result'] = $result;
+            }
+
+            DB::table('wechat_advertise_report_record')->insert($data);
+
+        }catch (Exception $e){
+            Log::error($e);
+        }
+    }
+
+    private function getDataSurce($appid,$type='WECHAT'){
+        if(!isset($this->user_action_set_id[$appid])){
+            $result = $this->getDataSourceFromDB($appid,$type);
+            if($result){
+                $this->user_action_set_id[$appid] = $result;
+                return $result;
+            }
+            $result = $this->createDataSource($appid,$type);
+            if($result){
+                $this->user_action_set_id[$appid] = $result;
+                return $result;
+            }
+            Log::info('appid is :'.$appid.', createDataSource fail');
+            return '';
+        }
+        return  $this->user_action_set_id[$appid];
+
+    }
+    private function getAccessToken($appid){
+        if(isset($this->assess_token[$appid])){
+            return $this->assess_token[$appid];
+        }
+        /*$WechatController = new WechatController($appid);
+        $accessToken = $WechatController->app->access_token; // EasyWeChat\Core\AccessToken 实例
+        $token = $accessToken->getToken(); // token 字符串
+        if($token){
+            $this->assess_token[$appid] = $token;
+            return $token;
+        }
+        Log::info('appid is :'.$appid.',request access_token fail');
+        return '';*/
+        $access_token = Redis::get('Wechat:access_token:appid:'.$appid);
+        if($access_token){
+            $this->assess_token[$appid] = $access_token;
+            return $this->assess_token[$appid];
+        }
+        /*$appsecret = 'f73d3be068d86417bf1a3fafefe4596a';
+        $url = sprintf(
+            'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s',
+            $appid,
+            $appsecret
+        );
+        $client = new Client();
+        $result = $client->request('get',$url)->getBody()->getContents();
+        Log::info('request access token ,result is:'.$result);
+        $result = \GuzzleHttp\json_decode($result,1);
+        if($result && isset($result['access_token'])){
+            Redis::setex('Wechat:access_token:appid:'.$appid,7000,$result['access_token']);
+            $this->assess_token[$appid] = $result['access_token'];
+            return $this->assess_token[$appid];
+        }*/
+        $this->error_msg = $appid.',access_token fail';
+        return '';
+    }
+
+    private function createDataSource(string $appid,string $type){
+        $token = $this->getAccessToken($appid);
+        if(!$token) return 0;
+        $url = 'https://api.weixin.qq.com/marketing/user_action_sets/add?version=v1.0&access_token='.$token;
+        $client = new Client();
+        $name = '支付下单';
+        $description = '支付下单,充值统计';
+        $body = json_encode([
+            'type'=>$type,
+            'wechat_app_id'=>$appid,
+            'name'=>$name,
+            'description'=>$description
+        ]);
+        try{
+            $result = $client->request('post',$url,[
+                'headers'=>['Content-Type'=>'application/json'],
+                'body'=>$body
+            ])->getBody()->getContents();
+            $result = \GuzzleHttp\json_decode($result,1);
+            if($result['errcode'] == 0 && isset($result['data']) && isset($result['data']['user_action_set_id'])){
+                $user_action_set_id = $result['data']['user_action_set_id'];
+                $this->saveDataSource($appid,$name,$type,$description,$user_action_set_id);
+                return $user_action_set_id;
+            }
+            if($result['errcode'] == 900351000){
+                $msg_array = explode(':',$result['errmsg']);
+                if(isset($msg_array[1])){
+                    $user_action_set_id = trim($msg_array[1]);
+                    $this->saveDataSource($appid,$name,$type,$description,$user_action_set_id);
+                    return $user_action_set_id;
+                }
+            }
+        }catch (Exception $e){
+            Log::error($e);
+        }
+        $this->error_msg = $appid.',createDataSource fail';
+        return 0;
+    }
+
+    private function getDataSourceFromDB($appid,$type):int
+    {
+        $result = DB::table('wechat_advertise_data_source')->where('appid', $appid)->where('type', $type)->select('user_action_set_id')->first();
+        if($result){
+            return $result->user_action_set_id;
+        }
+        return 0;
+    }
+
+    private function saveDataSource(string $appid,string $name,string $type,string $description,int $user_action_set_id){
+        DB::table('wechat_advertise_data_source')->insert([
+            'appid'=>$appid,
+            'name'=>$name,
+            'type'=>$type,
+            'description'=>$description,
+            'user_action_set_id'=>$user_action_set_id,
+            'created_at'=>date('Y-m-d H:i:s'),
+            'updated_at'=>date('Y-m-d H:i:s')
+        ]);
+    }
+
+
+    private function getBookName($order_id){
+        $result = ['bid'=>'','book_name'=>'','en_bid'=>''];
+        try{
+            $order_info = DB::connection('api_mysql')->table('orders')->select('from_bid')->where('id',$order_id)->first();
+            if($order_info && $order_info->from_bid){
+                $result['bid'] = $order_info->from_bid;
+                $result['en_bid'] = \Hashids::encode($order_info->from_bid);
+                $book_info = DB::connection('api_mysql')->table('book_configs')->select('book_name')->where('bid',$order_info->from_bid)->first();
+                if($book_info){
+                    $result['book_name'] = $book_info->book_name;
+                }
+                return $result;
+            }
+        }catch (\Exception $e){
+
+        }
+        return $result;
+    }
+}

+ 430 - 0
app/Console/Commands/Wechat/GdtAdReport.php

@@ -0,0 +1,430 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2020/4/28
+ * Time: 16:38
+ */
+
+namespace App\Console\Commands\Wechat;
+
+use Illuminate\Console\Command;
+use GuzzleHttp\Client;
+use Exception;
+use Log;
+use DB;
+use Redis;
+
+class GdtAdReport extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'Wechat:GdtAdReport {--type=}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    private $user_action_set_id = [];
+
+    private $assess_token = [];
+
+    private $error_msg;
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $option = $this->option('type');
+        if($option == 'order'){
+            $this->reportOrders();
+        }
+        if($option == 'register'){
+            $this->reportRegister();
+        }
+    }
+
+    private function reportRegister(){
+        $action_type = 'REGISTER';
+        $users = $this->getRegisterData($action_type);
+        if(!$users) return ;
+        $client = new Client();
+        foreach ($users  as $item){
+
+            $user_action_set_id = $this->getDataSurce($item['appid'],'WEB');
+            $action_time = strtotime($item['created_at']);
+            $data = [
+                'user_action_set_id'=>$user_action_set_id,
+                'url'=>'',
+                'action_time'=>$action_time,
+                'action_type'=>$action_type,
+                'openid'=>$item['openid'],
+                'appid'=>$item['appid'],
+                'product_id'=>$item['id'],
+                'value'=>0,
+                'created_at'=>date('Y-m-d H:i:s'),
+                'updated_at'=>date('Y-m-d H:i:s'),
+                'result_status'=>'FAIL',
+                'result_msg'=>'',
+                'result'=>'',
+            ];
+            $book_info = $this->getBookNameByBid($item['bid']);
+            $product_id = '';
+            $product_name = '';
+            $data['bid'] = $book_info['en_bid'];
+            if($book_info['en_bid'] && $book_info['book_name']){
+                $product_id = $book_info['en_bid'];
+                $data['bid'] = $book_info['en_bid'];
+                $product_name = $book_info['book_name'].'-'.env('PROJECT_NAME');
+                $data['product_name'] = $product_name;
+                $data['url'] = env('GDT_REPORT_URL').'/detail?id='.$book_info['en_bid'];
+            }
+            $token = $this->getAccessToken($item['appid']);
+            if(!$user_action_set_id || !$token) {
+                $data['result_msg'] = $this->error_msg;
+                DB::table('wechat_advertise_gdt_report_record')->insert($data);
+                continue;
+            };
+            $result = $this->request($client, $user_action_set_id, $action_time, $action_type, $item['appid'], $item['openid'], 0, $product_name, $product_id,$token,'');
+            $data['result_status'] = $result['result_status'];
+            $data['result_msg'] = $result['result_msg'];
+            $data['result'] = $result['result'];
+            try{
+                DB::table('wechat_advertise_gdt_report_record')->insert($data);
+                if($data['result_status'] == 'SUCCESS'){
+                    DB::connection('api_mysql')->table('distribution_channel_weixin_spread_users')->where('uid',$item['id'])->update([
+                        'report_status'=>1,
+                        'updated_at'=>date('Y-m-d H:i:s')
+                    ]);
+                }
+
+            }catch (\Exception $e){}
+            //second
+            $data['action_type'] = 'FOLLOW';
+            $result = $this->request($client, $user_action_set_id, $action_time, 'FOLLOW', $item['appid'], $item['openid'], 0, $product_name, $product_id,$token,'');
+            $data['result_status'] = $result['result_status'];
+            $data['result_msg'] = $result['result_msg'];
+            $data['result'] = $result['result'];
+            DB::table('wechat_advertise_gdt_report_record')->insert($data);
+
+        }
+    }
+
+    private function reportOrders(){
+        $action_type = 'COMPLETE_ORDER';
+        $orders = $this->getOrderData($action_type);
+        if(!$orders) return ;
+        $client = new Client();
+        foreach ($orders  as $item){
+            $report_type = $this->transferReportType($item['report_type'],$item['created_at'],$item['id']);
+
+            $user_action_set_id = $this->getDataSurce($item['appid'],'WEB');
+            $action_time = strtotime($item['created_at']);
+            $data = [
+                'user_action_set_id'=>$user_action_set_id,
+                'url'=>'',
+                'action_time'=>$action_time,
+                'action_type'=>$action_type,
+                'openid'=>$item['openid'],
+                'appid'=>$item['appid'],
+                'product_id'=>$item['id'],
+                'value'=>$item['price']*100,
+                'created_at'=>date('Y-m-d H:i:s'),
+                'updated_at'=>date('Y-m-d H:i:s'),
+                'result_status'=>'FAIL',
+                'result_msg'=>'',
+                'result'=>'',
+                'object'=>$report_type
+            ];
+            $book_info = $this->getBookNameFromOrer($item['id']);
+            $product_id = '';
+            $product_name = '';
+            $data['bid'] = $book_info['en_bid'];
+            if($book_info['en_bid'] && $book_info['book_name']){
+                $product_id = $book_info['en_bid'];
+                $data['bid'] = $book_info['en_bid'];
+                $product_name = $book_info['book_name'].'-'.env('PROJECT_NAME');
+                $data['product_name'] = $product_name;
+                $data['url'] = env('GDT_REPORT_URL').'/detail?id='.$book_info['en_bid'];
+            }
+            $token = $this->getAccessToken($item['appid']);
+            if(!$user_action_set_id || !$token) {
+                $data['result_msg'] = $this->error_msg;
+                DB::table('wechat_advertise_gdt_report_record')->insert($data);
+                continue;
+            };
+            $result = $this->request($client, $user_action_set_id, $action_time, $action_type, $item['appid'], $item['openid'], $item['price']*100, $product_name, $product_id,$token,$report_type);
+            $data['result_status'] = $result['result_status'];
+            $data['result_msg'] = $result['result_msg'];
+            $data['result'] = $result['result'];
+            try{
+                DB::table('wechat_advertise_gdt_report_record')->insert($data);
+                if($data['result_status'] == 'SUCCESS'){
+                    DB::connection('api_mysql')->table('distribution_channel_weixin_spread_orders')->where('order_id',$item['id'])->update([
+                        'report_status'=>1,
+                        'updated_at'=>date('Y-m-d H:i:s')
+                    ]);
+                }
+
+            }catch (\Exception $e){}
+            //second
+            $data['action_type'] = 'PURCHASE';
+            $result = $this->request($client, $user_action_set_id, $action_time, 'PURCHASE', $item['appid'], $item['openid'], $item['price']*100, $product_name, $product_id,$token,$report_type);
+            $data['result_status'] = $result['result_status'];
+            $data['result_msg'] = $result['result_msg'];
+            $data['result'] = $result['result'];
+            DB::table('wechat_advertise_gdt_report_record')->insert($data);
+            try{
+                if($data['result_status'] == 'SUCCESS'){
+                    DB::connection('api_mysql')->table('distribution_channel_weixin_spread_orders')->where('order_id',$item['id'])->update([
+                        'report_status'=>1,
+                        'updated_at'=>date('Y-m-d H:i:s')
+                    ]);
+                }
+            }catch (\Exception $e){}
+        }
+    }
+
+    private function getOrderData($action_type)
+    {
+        $data = [];
+        $start = date('Y-m-d');
+        if (date('H:i:s') < '02:00:00') {
+            $start = date('Y-m-d H:i:s', time() - 3 * 3600);
+        }
+        $result = DB::connection('api_mysql')->table('distribution_channel_weixin_spread_orders')
+            ->where('report_status', 0)
+            ->where('created_at', '>=', $start)
+            ->select('order_id', 'appid', 'openid', 'price', 'report_status', 'created_at','report_type')
+            ->get();
+        if ($result) {
+            foreach ($result as $item) {
+                $report_info = DB::table('wechat_advertise_gdt_report_record')
+                    ->where('product_id', $item->order_id)
+                    ->where('action_type', $action_type)
+                    ->where('result_status', 'SUCCESS')
+                    ->count();
+                if ($report_info) {
+                    try {
+                        DB::connection('api_mysql')->table('distribution_channel_weixin_spread_orders')->where('order_id', $item->order_id)->update([
+                            'report_status' => 1,
+                            'updated_at' => date('Y-m-d H:i:s')
+                        ]);
+                    } catch (\Exception $e) {
+                        \Log::info($e);
+                    }
+                    continue;
+                }
+                $data[] = ['created_at' => $item->created_at, 'openid' => $item->openid,'report_type'=>$item->report_type,
+                    'id' => $item->order_id, 'appid' => $item->appid, 'price' => $item->price];
+            }
+        }
+        return $data;
+    }
+
+    private function getRegisterData($action_type)
+    {
+        $data = [];
+        $start = date('Y-m-d');
+        if (date('H:i:s') < '02:00:00') {
+            $start = date('Y-m-d H:i:s', time() - 3 * 3600);
+        }
+        $result = DB::connection('api_mysql')->table('distribution_channel_weixin_spread_users')
+            ->where('report_status', 0)
+            ->where('created_at', '>=', $start)
+            ->select('uid', 'appid', 'openid', 'report_status', 'created_at', 'bid')
+            ->get();
+        if ($result) {
+            foreach ($result as $item) {
+                $report_info = DB::table('wechat_advertise_gdt_report_record')
+                    ->where('product_id', $item->uid)
+                    ->where('action_type', $action_type)
+                    ->where('result_status', 'SUCCESS')
+                    ->count();
+                if ($report_info) {
+                    continue;
+                }
+                $data[] = ['created_at' => $item->created_at, 'openid' => $item->openid,
+                    'id' => $item->uid, 'appid' => $item->appid, 'bid' => $item->bid];
+            }
+        }
+        return $data;
+    }
+
+    private function request(Client $client, $user_action_set_id, $action_time, $action_type, $appid, $openid, $value, $product_name, $product_id,$token,$report_type)
+    {
+        $data['result_status'] = 'FAIL';
+        $data['result_msg'] = '';
+        $data['result'] = '';
+        $url = 'https://api.weixin.qq.com/marketing/user_actions/add?version=v1.0&access_token=' . $token;
+        $request_data = [
+            'user_action_set_id' => $user_action_set_id,
+            'actions' => [
+                ['url' => '', 'action_time' => $action_time, 'action_type' => $action_type,
+                    'user_id' => [
+                        'wechat_app_id' => $appid,
+                        'wechat_openid' => $openid
+                    ],
+                    'action_param' => [
+                        'product_name' => $product_name,
+                        'product_id' => $product_id,
+                        'value' => $value,
+                        'object'=>$report_type
+                    ]
+                ]
+            ]
+        ];
+        try {
+            $result = $client->request('post', $url, [
+                'headers' => ['Content-Type' => 'application/json'],
+                'body' => \GuzzleHttp\json_encode($request_data)
+            ])->getBody()->getContents();
+            Log::info('gdt user_actions/add body is ' );
+            Log::info(\GuzzleHttp\json_encode($request_data));
+            Log::info('gdt user_actions/add result is: ' . $result);
+            $result_array = \GuzzleHttp\json_decode($result, 1);
+            if (isset($result_array['errcode']) && $result_array['errcode'] == 0) {
+                $data['result_status'] = 'SUCCESS';
+                $data['result_msg'] = $result_array['errcode'];
+                $data['result'] = $result;
+            } else {
+                $data['result_status'] = 'FAIL';
+                $data['result_msg'] = isset($result_array['errcode']) ? $result_array['errcode'] : 'unknown';
+                $data['result'] = $result;
+            }
+            return $data;
+        } catch (Exception $e) {
+            $data['result_status'] = 'FAIL';
+            $data['result_msg'] = $e->getMessage();
+            Log::error($e);
+            return $data;
+        }
+    }
+
+    private function getDataSurce($appid, $type = 'WECHAT')
+    {
+        if (!isset($this->user_action_set_id[$appid])) {
+            $result = $this->getDataSourceFromDB($appid, $type);
+            if ($result) {
+                $this->user_action_set_id[$appid] = $result;
+                return $result;
+            }
+            $this->error_msg = 'getDataSurce fail';
+            return '';
+        }
+        return $this->user_action_set_id[$appid];
+    }
+
+    private function getDataSourceFromDB($appid, $type)
+    {
+        $result = DB::table('wechat_advertise_data_source')->where('appid', $appid)->where('type', $type)->select('user_action_set_id')->first();
+        if ($result) {
+            return $result->user_action_set_id;
+        }
+        return 0;
+    }
+
+    private function getAccessToken($appid)
+    {
+        if (isset($this->assess_token[$appid])) {
+            return $this->assess_token[$appid];
+        }
+        $access_token = Redis::get('Wechat:access_token:appid:' . $appid);
+        if ($access_token) {
+            $this->assess_token[$appid] = $access_token;
+            return $this->assess_token[$appid];
+        }
+        $this->error_msg = $appid . ',access_token fail';
+        return '';
+    }
+
+    private function getBookNameFromOrer($order_id){
+        $result = ['bid'=>'','book_name'=>'','en_bid'=>''];
+        try{
+            $order_info = DB::connection('api_mysql')->table('orders')->select('from_bid')->where('id',$order_id)->first();
+            if($order_info && $order_info->from_bid){
+                $result['bid'] = $order_info->from_bid;
+                $result['en_bid'] = \Hashids::encode($order_info->from_bid);
+                $book_info = DB::connection('api_mysql')->table('book_configs')
+                    ->select('book_name','is_on_shelf')
+                    ->where('bid',$order_info->from_bid)->first();
+                if($book_info){
+                    if($book_info->is_on_shelf == 2){
+                        $result['book_name'] = $book_info->book_name;
+                    }
+                }
+                return $result;
+            }
+        }catch (\Exception $e){
+
+        }
+        return $result;
+    }
+
+    private function getBookNameByBid($bid){
+        $result = ['bid'=>'','book_name'=>'','en_bid'=>''];
+        if(!$bid) return $result;
+        $result['en_bid'] = \Hashids::encode($bid);
+        $result['bid'] = $bid;
+        $book_info = DB::connection('api_mysql')->table('book_configs')->select('book_name','is_on_shelf')->where('bid',$bid)->first();
+        if($book_info){
+            if($book_info->is_on_shelf == 2){
+                $result['book_name'] = $book_info->book_name;
+            }
+        }
+        return $result;
+    }
+
+    private function transferReportType($report_type,$order_time,$order_id){
+        $map = [
+            'one_day_first_pay'=>'read_1',
+            'one_day_all_pay'=>'read_2',
+            'current_day_register_first_pay'=>'read_3',
+            'current_day_register'=>'read_4',
+            'three_day_first_pay'=>'read_5',
+            'three_day_all_pay'=>'read_6'
+        ];
+        if(isset($map[$report_type])){
+            return $map[$report_type];
+        }
+        $order_info = DB::connection('api_mysql')->table('orders')->where('id',$order_id)->select('uid')->first();
+        if(!$order_info){
+            return '';
+        }
+        $uid = $order_info->uid;
+        $user_info = DB::connection('api_mysql')->table('users')->where('id',$uid)->select('created_at')->first();
+        if(!$user_info) return '';
+        $register_time = $user_info->created_at;
+        $register_timestamp = strtotime($register_time);
+        $order_timestamp = strtotime($order_time);
+        if(date('Y-m-d',$register_timestamp) == date('Y-m-d',$order_timestamp)){
+            return 'read_4';
+        }
+        if(strtotime($order_time) -  strtotime($register_time)  < 86400){
+            return 'read_2';
+        }
+        if(strtotime($order_time) -  strtotime($register_time)  < 3*86400){
+            return 'read_6';
+        }
+        return '';
+    }
+}

+ 166 - 0
app/Console/Commands/Wechat/ReportRetry.php

@@ -0,0 +1,166 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: z-yang
+ * Date: 2020/9/30
+ * Time: 17:09
+ */
+
+namespace App\Console\Commands\Wechat;
+
+use DB;
+use Log;
+use Redis;
+use Exception;
+use GuzzleHttp\Client;
+use Illuminate\Console\Command;
+
+class ReportRetry extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'Wechat:ReportRetry  {--start_time=} {--end_time=}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '异常从新上报';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    private $user_action_set_id = [];
+
+    private $assess_token = [];
+
+    private $error_msg;
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $start_time = $this->option('start_time');
+        if(!$start_time){
+            $start_time = date('Y-m-d 00:00:00');
+        }
+        $end_time = $this->option('end_time');
+        if(!$end_time){
+            $end_time = date('Y-m-d 23:59:59');
+        }
+        $this->startMp($start_time,$end_time);
+    }
+
+    //mp重试
+    private function startMp($start_time,$end_time){
+        $error_record = DB::table('wechat_advertise_report_record')
+            ->where('created_at','>=',$start_time)
+            ->where('created_at','<=',$end_time)
+            ->where('result_status','FAIL')
+            ->select('id','user_action_set_id','url','action_time','action_type','openid','appid','product_id',
+                'product_name','bid','value','source','claim_type','object')
+            ->get();
+        if(!$error_record) return ;
+        $client = new Client();
+        foreach ($error_record as $item){
+            $report_info = DB::table('wechat_advertise_report_record')
+                ->where('action_type',$item->action_type)
+                ->where('product_id',$item->product_id)
+                ->where('result_status','SUCCESS')->count();
+            if($report_info) {
+                continue;
+            }
+            $request_data = [
+                'actions'=>[
+                    [
+                        'user_action_set_id'=>$item->user_action_set_id,
+                        'url'=>$item->url,
+                        'action_time'=>$item->action_time,
+                        'action_type'=>$item->action_type,
+                        'user_id'=>[
+                            'wechat_app_id'=>$item->appid,
+                            'wechat_openid'=>$item->openid
+                        ],
+                        'action_param'=>[
+                            'object'=>$item->object,
+                            'product_name'=>$item->product_name,
+                            'product_id'=>$item->bid,
+                            'source'=>$item->source,
+                            'wechat_app_id'=>'',
+                            'claim_type'=>$item->claim_type,
+                            'value'=>$item->value
+                        ]
+                    ]
+                ]
+            ];
+            $token = $this->getAccessToken($item->appid);
+            $result = $this->sendReport($client,$request_data,$token);
+            if($result && $result['result_status'] == 'SUCCESS'){
+                $result['updated_at'] = date('Y-m-d H:i:s');
+                DB::table('wechat_advertise_report_record')->where('id',$item->id)->update($result);
+
+                DB::connection('api_mysql')->table('distribution_channel_weixin_ad_report_orders')->where('order_id',$item->product_id)
+                    ->update(['report_status'=>1]);
+            }
+        }
+
+    }
+
+
+    private function sendReport(Client $client,$request_data,$token){
+        if(!$token) return '';
+        $url = 'https://api.weixin.qq.com/marketing/user_actions/add?version=v1.0&access_token='.$token;
+        $data = [];
+        try{
+            $result = $client->request('post',$url,[
+                'headers'=>['Content-Type'=>'application/json'],
+                'body'=>\GuzzleHttp\json_encode($request_data)
+            ])->getBody()->getContents();
+            Log::info('user_actions/add result is: '.$result);
+            $result_array = \GuzzleHttp\json_decode($result,1);
+            if(isset($result_array['errcode']) && $result_array['errcode'] == 0){
+                $data['result_status'] = 'SUCCESS';
+                $data['result_msg'] = $result_array['errcode'];
+                $data['result'] = $result;
+            }else{
+                $data['result_status'] = 'FAIL';
+                $data['result_msg'] = isset($result_array['errcode'])?$result_array['errcode']:'unknown';
+                $data['result'] = $result;
+            }
+            return $data;
+        }catch (Exception $e){
+            Log::error($e);
+        }
+        return $data;
+    }
+
+
+    private function getAccessToken($appid){
+        if(isset($this->assess_token[$appid])){
+            return $this->assess_token[$appid];
+        }
+        $access_token = Redis::get('Wechat:access_token:appid:'.$appid);
+        if($access_token){
+            $this->assess_token[$appid] = $access_token;
+            return $this->assess_token[$appid];
+        }
+
+        $this->error_msg = $appid.',access_token fail';
+        return '';
+    }
+
+}

+ 166 - 0
app/Console/Commands/test2.php

@@ -0,0 +1,166 @@
+<?php
+
+namespace App\Console\Commands;
+
+use GuzzleHttp\Client;
+use Illuminate\Console\Command;
+use Redis;
+use WechatOP;
+use DB;
+
+class test2 extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'test:test3';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->appId='wx1037462ad78bf1f2';
+        $this->appSecret='d6bda51bfdb4cf6eb504c622fd72c210';
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->fix();
+    }
+
+    private function  https_request($url, $data = null, $timeout = 5000)
+    {
+        $curl = curl_init();
+        curl_setopt($curl, CURLOPT_URL, $url);
+        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
+        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
+        if (! empty($data)) {
+            curl_setopt($curl, CURLOPT_POST, 1);
+            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
+        }
+        if ($timeout) {
+            curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
+        }
+
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+        $output = curl_exec($curl);
+        if ($output === false) {
+            \Log::error('Curl error: ' . curl_error($curl).". url:".$url);
+        }
+
+        curl_close($curl);
+        return $output;
+    }
+
+    public function getToken($is_force = true )
+    {
+
+        $appId = $this->appId;
+        $appSecret = $this->appSecret;
+        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appId . '&secret=' . $appSecret;
+        $rs1 = $this->https_request($url);
+        \Log::info(($this->appId).'get_token-'.$rs1);
+        if ($rs1)
+        {
+            $rs = json_decode($rs1, true);
+            if (is_array($rs) && $rs['access_token'])
+            {
+                $access_token = $rs['access_token'];
+                $expires_in = intval($rs['expires_in'], 10);
+                if ($expires_in <= 0)
+                {
+                    $expires_in = 5;
+                } else if ($expires_in >= 7200)
+                {
+                    $expires_in = 7000;
+                }
+
+                $data = array();
+                $data['expire_time'] = time() + $expires_in;
+                $data['access_token'] = $access_token;
+
+                Redis::setex(($this->appId).'access_token', $expires_in, serialize($data));
+                return $access_token;
+            } else
+            {
+                $content = "get access token error:" . $rs1;
+                \Log::info(($this->appId).$content);
+            }
+        } else
+        {
+            $content = "get access token error:" . $rs1;
+            \Log::info(($this->appId).$content);
+        }
+
+        return false;
+    }
+
+    //修复crm用户昵称问题
+    public function fix()
+    {
+        
+        $uids = collect(DB::select("select distinct uid from yueduyun.user_bind_hk_welfare
+ where not exists(select 1 from zsy_crm.crm_users where uid = yueduyun.user_bind_hk_welfare.uid)"))->pluck('uid');//->map(function ($user){ return $user->id;});
+        $sub_users = DB::table('yueduyun.force_subscribe_users')->whereIn('uid',$uids)->where('is_subscribed',1)->get();
+        $time = date("Y-m-d H:i:s");
+        foreach($sub_users as $sub_user)
+        {
+            $appid = $sub_user->appid;
+            $openid = $sub_user->openid;
+            $redis_key = '[wechat_op.common.component_refresh_token.'.$appid.']';
+            $component_refresh_token = Redis::Get($redis_key);
+
+            $options = [
+                'app_id'    => $appid,
+                'secret'    => env('WECHAT_OP_SECRET'),   // 仅适用于 单独配置公众号
+                'token'     => env('WECHAT_OP_TOKEN'),   // 仅适用于 单独配置公众号
+                'aes_key'   => env('WECHAT_OP_AES_KEY'),   // 仅适用于 单独配置公众号
+                'auth_type' => 'COMPONENT', // COMPONENT 开放平台授权公众号,MANUAL 单独配置公众号
+                'component_refresh_token' => $component_refresh_token,   // 授权回调时获取的 authorizer_refresh_token,仅适用于 开放品台授权公众号
+                'oauth'     => [
+                    'scopes' => ['snsapi_base'], // 公众号授权用户方式 snsapi_base, snsapi_userinfo
+                    'callback' => '/oauth_callback',
+                ],
+                'cache'   => [
+                    'driver' => 'redis',   // redis, filesystem, laravel
+                    'dir' => storage_path('tmp') // 只有为filesystem时候这个目录才有效
+                ],
+            ];
+            try{
+                $app = WechatOP::app($options);
+
+                $user_data = $app->user->get($openid);
+                if(isset($user_data))
+                {
+                    //DB::table('yueduyun.users')->where('id',$sub_user->uid)->update(['nickname'=>$user_data->nickname]);
+                    DB::connection('command_mysql')->table('zsy_crm.crm_users')->insert(['uid'=>$sub_user->uid,'nickname'=>$user_data->nickname,'created_at'=>$time,'updated_at'=>$time]);
+                }
+
+                dump($user_data);
+            }catch (\Exception $e){
+                dump($e->getMessage());
+            };
+
+
+        }
+    }
+}
+

+ 92 - 0
app/Console/Commands/test_queue.php

@@ -0,0 +1,92 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+use PhpAmqpLib\Connection\AMQPStreamConnection;
+use PhpAmqpLib\Message\AMQPMessage;
+define('HOST', '120.55.25.252');
+define('PORT', 5672);
+define('USER', 'test');
+define('PASS', '6acbQWE13');
+define('VHOST', '/');
+//If this is enabled you can see AMQP output on the CLI
+define('AMQP_DEBUG', true);
+
+class test_queue extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'command:tqueue';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'tqueue';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        echo 'publish';
+    	$this->exchange = 'router';
+    	$this->queue = 'msg';
+    	$connection = new AMQPStreamConnection(HOST, PORT, USER, PASS, VHOST);
+    	$channel = $connection->channel();
+    	echo $channel;
+    	/*
+    	 The following code is the same both in the consumer and the producer.
+    	In this way we are sure we always have a queue to consume from and an
+    	exchange where to publish messages.
+    	*/
+    	/*
+    	 name: $queue
+    	passive: false
+    	durable: true // the queue will survive server restarts
+    	exclusive: false // the queue can be accessed in other channels
+    	auto_delete: false //the queue won't be deleted once the channel is closed.
+    	*/
+    	$channel->queue_declare($this->queue, false, true, false, false);
+    	/*
+    	 name: $exchange
+    	type: direct
+    	passive: false
+    	durable: true // the exchange will survive server restarts
+    	auto_delete: false //the exchange won't be deleted once the channel is closed.
+    	*/
+    	$channel->exchange_declare($exchange, 'direct', false, true, false);
+    	$channel->queue_bind($queue, $exchange);
+    	
+    	// $messageBody = implode(' ', array_slice($argv, 1));
+    	$messageBody = 'msg1';
+    	$options = getopt("f:hp:");
+    	var_dump('$options');
+    	var_dump($options);
+    	
+    	$message = new AMQPMessage($messageBody, array('content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));
+    	
+    	$channel->basic_publish($message, $exchange);
+    	$channel->close();
+    	$connection->close();
+        
+    }
+
+}

+ 124 - 0
app/Console/Kernel.php

@@ -0,0 +1,124 @@
+<?php
+
+namespace App\Console;
+
+use Illuminate\Console\Scheduling\Schedule;
+use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
+
+class Kernel extends ConsoleKernel
+{
+    /**
+     * The Artisan commands provided by your application.
+     *;
+     * @var array
+     */
+    protected $commands = [
+        Commands\Inspire::class,
+        Commands\test2::class,
+        Commands\Test3::class,
+        Commands\SmartPush\KeepContinueRead::class,
+        Commands\SmartPush\KeepContinueReadV2::class,
+        Commands\SmartPush\KeepContinueReadV3::class,
+        Commands\GZH\FansStat::class,
+        Commands\GZH\ForceUserProperty::class,
+        Commands\Wechat\AdReport::class,
+        Commands\Wechat\AdReportTest::class,
+        Commands\Wechat\GdtAdReport::class,
+        Commands\Wechat\ReportRetry::class,
+        Commands\SmartPush\remindSign::class,
+        Commands\Recommend\UpdateHighQualityBooks::class,
+        Commands\SmartPush\ForceSubscribeDelayMsg::class,
+        Commands\SmartPush\CouponExpirePush::class,
+        Commands\BatchWechatMaterial\SyncWechatMaterialStatistics::class,
+        Commands\SmartPush\RfmPush::class,
+        //更新推送
+        Commands\SmartPush\BookUpdatePush::class,
+
+        // 微信素材批量发送
+        Commands\BatchWechatMaterial\SendCustomWechatMaterial::class,
+        // 删除微信素材消息
+        Commands\ActionTrigger\BatchWechatMaterial\DelWechatMaterial::class,
+        // 预览微信素材消息
+        Commands\ActionTrigger\BatchWechatMaterial\PreviewWechatMaterial::class,
+        // 同步微信素材消息
+        Commands\ActionTrigger\BatchWechatMaterial\SyncZsWechatMaterial::class,
+        // 测试微信素材
+        Commands\ActionTrigger\BatchWechatMaterial\testWechatMaterial::class,
+        // 测试微信群发
+        Commands\ActionTrigger\BatchWechatMaterial\testWechatMaterialMsgSend::class,
+        
+        
+    ];
+
+    /**
+     * Define the application's command schedule.
+     *
+     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
+     * @return void
+     */
+    protected function schedule(Schedule $schedule)
+    {
+
+        $schedule->command('SmartPush:KeepContinueReadV3')->cron('02 7-23 * * *');
+
+        $schedule->command('SmartPush:KeepContinueReadV2')->cron('06 7-23 * * *');
+
+        //拉取公众号后台粉丝数据
+        $schedule->command('gzh:fansStat')->dailyAt('09:04');
+
+        //拉取公众号后台昵称
+        //$schedule->command('gzh:force_user_property')->everyFiveMinutes();
+
+        //微信广告上报
+        $schedule->command('Wechat:WeixinAdReport')->everyFiveMinutes();
+        //微信gdt上报
+        $schedule->command('Wechat:GdtAdReport --type=order')->everyFiveMinutes();
+        $schedule->command('Wechat:GdtAdReport --type=register')->everyFiveMinutes();
+
+        //$schedule->command('Wechat:ReportRetry')->hourly();
+
+        //未签到提醒
+        $schedule->command('SmartPush:remindSign --start=0 --end=70000000')->dailyAt('08:01');
+        $schedule->command('SmartPush:remindSign --start=70000000 --end=100000000')->dailyAt('08:03');
+        $schedule->command('SmartPush:remindSign --start=100000000 --end=118000000')->dailyAt('08:05');
+        $schedule->command('SmartPush:remindSign --start=118000000 --end=126000000')->dailyAt('08:07');
+        $schedule->command('SmartPush:remindSign --start=126000000 --end=132000000')->dailyAt('08:09');
+        $schedule->command('SmartPush:remindSign --start=132000000 --end=136000000')->dailyAt('08:11');
+        $schedule->command('SmartPush:remindSign --start=136000000 --end=139000000')->dailyAt('08:13');
+        $schedule->command('SmartPush:remindSign --start=139000000 --end=140700000')->dailyAt('08:14');
+        $schedule->command('SmartPush:remindSign --start=140700000 --end=141150000')->dailyAt('08:14');
+        $schedule->command('SmartPush:remindSign --start=141150000 --end=141270000')->dailyAt('08:15');
+        $schedule->command('SmartPush:remindSign --start=141270000 --end=141400000')->dailyAt('08:16');
+        $schedule->command('SmartPush:remindSign --start=141400000 --end=141550000')->dailyAt('08:17');
+        $schedule->command('SmartPush:remindSign --start=141550000 --end=141750000')->dailyAt('08:18');
+        $schedule->command('SmartPush:remindSign --start=141550000 --end=141680000')->dailyAt('08:19');
+        $schedule->command('SmartPush:remindSign --start=141680000 --end=-1')->dailyAt('08:21');
+
+
+        $schedule->command('UpdateHighQualityBooks')->hourly();
+        //优惠券过期提醒
+        $schedule->command('SmartPush:CouponExpirePush')->hourly();
+
+        //$schedule->command('SmartPush:ForceSubscribeDelayMsg')->everyFiveMinutes();
+        $schedule->command('SmartPush:ForceSubscribeDelayMsg')->cron('*/2 * * * *');
+
+
+        //微信批量素材
+        $schedule->command('BatchWechatMaterial:SendCustomWechatMaterial')->everyFiveMinutes();
+
+        //rfm 推送*******************************
+        //新用户大礼包-小M值用户
+        $schedule->command('SmartPush:RfmPush --type=sm  --time=4')->cron('*/59 * * * *');
+        //新用户大礼包-大M值用户
+        $schedule->command('SmartPush:RfmPush --type=bm  --time=23')->cron('*/58 * * * *');
+        //新用户大礼包-全量用户
+        $schedule->command('SmartPush:RfmPush --type=all  --time=23')->cron('*/57 * * * *');
+        //新用户大礼包-免费用户
+        $schedule->command('SmartPush:RfmPush --type=free-24  --time=24')->cron('*/56 * * * *');
+        //新用户大礼包-免费用户
+        $schedule->command('SmartPush:RfmPush --type=free-24  --time=36')->cron('*/55 * * * *');
+
+        //更新推送
+        $schedule->command('SmartPush:BookUpdatePush')->cron('*/6 * * * *');
+    }
+}

+ 17 - 0
app/Consts/Channel/ChannelConst.php

@@ -0,0 +1,17 @@
+<?php
+
+
+namespace App\Consts\Channel;
+
+
+class ChannelConst
+{
+    // 男频
+    const PROPERTY_MALE = 'male';
+
+    // 女频
+    const PROPERTY_FEMALE = 'female';
+
+    // 综合
+    const PROPERTY_DEFAULT = 'hybrid';
+}

+ 8 - 0
app/Events/Event.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace App\Events;
+
+abstract class Event
+{
+    //
+}

+ 51 - 0
app/Exceptions/Handler.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Exceptions;
+
+use Exception;
+use Illuminate\Auth\Access\AuthorizationException;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+use Illuminate\Foundation\Validation\ValidationException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+
+class Handler extends ExceptionHandler
+{
+    /**
+     * A list of the exception types that should not be reported.
+     *
+     * @var array
+     */
+    protected $dontReport = [
+        AuthorizationException::class,
+        HttpException::class,
+        ModelNotFoundException::class,
+        ValidationException::class,
+    ];
+
+    /**
+     * Report or log an exception.
+     *
+     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
+     *
+     * @param  \Exception  $e
+     * @return void
+     */
+    public function report(Exception $e)
+    {
+        return parent::report($e);
+    }
+
+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Exception  $e
+     * @return \Illuminate\Http\Response
+     */
+    public function render($request, Exception $e)
+    {
+        return parent::render($request, $e);
+    }
+}

+ 25 - 0
app/Http/Controllers/AdminController.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Session;
+
+class AdminController extends Controller
+{
+
+    /**
+     * Create a new controller instance.
+     *
+     * @return void
+     */
+    public function __construct(Request $request)
+    {
+    	 
+    	Session::put('user',$request->user());
+    	
+        $this->middleware('auth');
+    }
+
+}

+ 89 - 0
app/Http/Controllers/Auth/AuthController.php

@@ -0,0 +1,89 @@
+<?php
+
+namespace App\Http\Controllers\Auth;
+
+use Auth;
+use App\User;
+use Validator;
+use App\Http\Controllers\Controller;
+use Illuminate\Foundation\Auth\ThrottlesLogins;
+use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
+
+class AuthController extends Controller
+{
+    /*
+    |--------------------------------------------------------------------------
+    | Registration & Login Controller
+    |--------------------------------------------------------------------------
+    |
+    | This controller handles the registration of new users, as well as the
+    | authentication of existing users. By default, this controller uses
+    | a simple trait to add these behaviors. Why don't you explore it?
+    |
+    */
+
+    use AuthenticatesAndRegistersUsers, ThrottlesLogins;
+
+    /**
+     * Where to redirect users after login / registration.
+     *
+     * @var string
+     */
+    protected $redirectTo = '/admin_index';
+
+    /**
+     * Create a new authentication controller instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->middleware('guest', ['except' => 'logout']);
+    }
+
+    /**
+     * Get a validator for an incoming registration request.
+     *
+     * @param  array  $data
+     * @return \Illuminate\Contracts\Validation\Validator
+     */
+    protected function validator(array $data)
+    {
+        return Validator::make($data, [
+            'name' => 'required|max:255',
+            'email' => 'required|email|max:255|unique:users',
+            'password' => 'required|confirmed|min:6',
+        ]);
+    }
+
+    /**
+     * Create a new user instance after a valid registration.
+     *
+     * @param  array  $data
+     * @return User
+     */
+    protected function create(array $data)
+    {
+        return User::create([
+            'name' => $data['name'],
+            'email' => $data['email'],
+            'password' => bcrypt($data['password']),
+        ]);
+    }
+
+    protected function register()
+    {
+        return view('auth.register');
+    }
+
+    protected function add_user()
+    {
+        return view('auth.register');
+    }
+
+    protected function logout()
+    {
+        Auth::logout();
+        return redirect()->action('Auth\AuthController@showLoginForm');
+    }
+}

+ 32 - 0
app/Http/Controllers/Auth/PasswordController.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Controllers\Auth;
+
+use App\Http\Controllers\Controller;
+use Illuminate\Foundation\Auth\ResetsPasswords;
+
+class PasswordController extends Controller
+{
+    /*
+    |--------------------------------------------------------------------------
+    | Password Reset Controller
+    |--------------------------------------------------------------------------
+    |
+    | This controller is responsible for handling password reset requests
+    | and uses a simple trait to include this behavior. You're free to
+    | explore this trait and override any methods you wish to tweak.
+    |
+    */
+
+    use ResetsPasswords;
+
+    /**
+     * Create a new password controller instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        $this->middleware('guest');
+    }
+}

+ 46 - 0
app/Http/Controllers/Channel/BaseController.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace App\Http\Controllers\Channel;
+
+use Illuminate\Routing\Controller;
+
+class BaseController extends Controller
+{
+
+    //获取分销渠道ID
+    function getChannelId() {
+        if(empty(session('ydychannel'))) {
+            if(env('APP_ENV') =='local') return 1;
+        }
+
+        $distribution_channel = unserialize(session('ydychannel'));
+        return $distribution_channel->id;
+    }
+
+    //获取分销渠道名称
+    function getChannelName() {
+        if(empty(session('ydychannel'))) {
+            if(env('APP_ENV') =='local') return '测试';
+        }
+
+        $distribution_channel = unserialize(session('ydychannel'));
+        return $distribution_channel->distribution_channel_name;
+    }
+
+    //获取分销渠道域名
+    function getChannelDomain() {
+        if(empty(session('ydychannel'))) {
+            if(env('APP_ENV') =='local') return 'site1.aizhuishu.com';
+        }
+
+        $distribution_channel = unserialize(session('ydychannel'));
+        return "site{$distribution_channel->id}.leyuee.com";
+    }
+
+    //获取登陆用户ID
+    function getChannelUserId()
+    {
+        return session('ydyauth');
+    }
+
+}

+ 13 - 0
app/Http/Controllers/Controller.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Routing\Controller as BaseController;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+
+class Controller extends BaseController
+{
+    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+}

+ 19 - 0
app/Http/Controllers/HomeController.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Route;
+use App;
+use App\Http\Controllers\WechatController;
+use EasyWeChat\Foundation\Application;
+use Cookie;
+class HomeController extends WechatController
+{
+	public function __construct()
+	{
+		parent::__construct();
+	}
+    
+}

+ 52 - 0
app/Http/Controllers/Queue/Template/AddNewsTasksController.php

@@ -0,0 +1,52 @@
+<?php
+ 
+namespace App\Http\Controllers\Queue\Template;
+ 
+use Illuminate\Http\Request;
+use App\Http\Requests;
+use App\Jobs\SendNews;
+use App\Http\Controllers\Controller;
+use App\Http\Models\WechatTemplateSendInfo;
+use Carbon\Carbon;
+use App\Http\Controllers\Wechat\Staff\NewsWorksController;
+
+class AddNewsTasksController extends Controller
+{
+	public function add_news_task(){
+		$data = array();
+		$data['openid'] = 'oAcqg1LRHNKN2jaEkJ5v56HOwPEQ';
+		$data['appid'] = 'wxdbc486f1b4f6a8c3';
+		$data['news_content'] = '[[{"title":"标题啊"},{"description":"描述哦"},{"url":"http://www.baidu.com"},{"image":"https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=314110819,2721402218&fm=173&s=91B3C23586024F4D0235ECFB0300C036&w=550&h=550&img.JPEG"}],[{"title":"标题2啊"},{"description":"描述哦"},{"url":"http://www.baidu.com"},{"image":"https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=314110819,2721402218&fm=173&s=91B3C23586024F4D0235ECFB0300C036&w=550&h=550&img.JPEG"}]]';
+		$data['type'] = 'last_task';// last_task,one_task
+		$data['task_id'] = 3;
+		$data['send_time'] = date("Y-m-d H:i:s");
+		$send_data=array(
+			'send_time'=>date("Y-m-d H:i:s"),
+			'data' => $data
+		);
+		
+		v('add_news_task_data:');v($send_data);
+		$delay = 0;
+
+// 		v('delay:'.$delay);die();
+		$job = (new SendNews($send_data))->onConnection('rabbitmq')->delay($delay)->onQueue('send_news_list');
+		
+		dispatch($job);
+
+	}
+	
+	public function test_send_news(){
+		$data = array();
+		$data['openid'] = 'oAcqg1LRHNKN2jaEkJ5v56HOwPEQ';
+		$data['appid'] = 'wxdbc486f1b4f6a8c3';
+		$data['news_content'] = '[[{"title":"标题啊"},{"description":"描述哦"},{"url":"http://www.baidu.com"},{"image":"https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=314110819,2721402218&fm=173&s=91B3C23586024F4D0235ECFB0300C036&w=550&h=550&img.JPEG"}],[{"title":"标题2啊"},{"description":"描述哦"},{"url":"http://www.baidu.com"},{"image":"https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=314110819,2721402218&fm=173&s=91B3C23586024F4D0235ECFB0300C036&w=550&h=550&img.JPEG"}]]';
+		$data['type'] = 'last_task';// last_task,one_task
+		$data['task_id'] = 3;
+		$data['send_time'] = date("Y-m-d H:i:s");
+
+		v('test_send_template_data:');v($data);
+		$TemplateWork = new NewsWorksController($data);
+		$TemplateWork->do_work();
+	}
+
+}

+ 62 - 0
app/Http/Controllers/Queue/Template/AddTemplateTasksController.php

@@ -0,0 +1,62 @@
+<?php
+ 
+namespace App\Http\Controllers\Queue\Template;
+ 
+use Illuminate\Http\Request;
+use App\Http\Requests;
+use App\Jobs\SendTemplate;
+use App\Http\Controllers\Controller;
+use App\Http\Models\WechatTemplateSendInfo;
+use Carbon\Carbon;
+use App\Http\Controllers\Wechat\Template\TemplateWorksController;
+
+class AddTemplateTasksController extends Controller
+{
+	public function add_template_task(){
+		
+		$data = array();
+		$data['openid'] = 'oAcqg1LRHNKN2jaEkJ5v56HOwPEQ';
+		$data['appid'] = 'wxdbc486f1b4f6a8c3';
+		$data['template_id'] = 'AHR4NcI3TA6lCBDNumxKYct8FOQgjJbmKWDBaLSl5N8';
+		$data['template_content'] = '[{"first":["到期2时间","#336699"]},{"keyword1":["昵称啊","#336699"]},{"keyword2":["woca","#336699"]},{"remark":["nima","#336699"]}]';
+		$data['type'] = 'last_task';// last_task,one_task
+		$data['url'] = 'www.baidu.com';
+		$data['task_id'] = 3;
+		$data['send_time'] = date("Y-m-d H:i:s");
+		$send_data=array(
+			'send_time'=>date("Y-m-d H:i:s"),
+			'data' => $data
+		);
+		
+		v('add_template_task_data:');v($send_data);
+		$delay = 0;
+
+// 		v('delay:'.$delay);die();
+		$job = (new SendTemplate($send_data))->onConnection('rabbitmq')->delay($delay)->onQueue('send_template_list');
+		
+// 		$name = $job->getName();
+		v('name');v($job);
+// 		die();
+		dispatch($job);
+
+	}
+	
+	public function test_send_template(){
+		$data = array();
+// 		$data['openid'] = 'oTsZb0cLT7kg3RnxGkfabuRapxMA';
+// 		$data['appid'] = 'wx03d107cd1ee728c2';
+		$data['openid'] = 'oAcqg1LRHNKN2jaEkJ5v56HOwPEQ';
+		$data['appid'] = 'wxdbc486f1b4f6a8c3';
+		$data['template_id'] = 'AHR4NcI3TA6lCBDNumxKYct8FOQgjJbmKWDBaLSl5N8';
+		$data['template_content'] = '[{"first":["到期2时间","#336699"]},{"keyword1":["昵称啊","#336699"]},{"keyword2":["woca","#336699"]},{"remark":["nima","#336699"]}]';
+		$data['type'] = 'last_task';// last_task,one_task
+		$data['url'] = 'www.baidu.com';
+		$data['task_id'] = 3;
+		$data['send_time'] = date('Y-m-d H:i:s');
+
+		v('test_send_template_data:');v($data);
+		$TemplateWork = new TemplateWorksController($data);
+		$TemplateWork->do_work();
+	}
+
+}

+ 62 - 0
app/Http/Controllers/Queue/Template/AddTextsTasksController.php

@@ -0,0 +1,62 @@
+<?php
+ 
+namespace App\Http\Controllers\Queue\Template;
+ 
+use Illuminate\Http\Request;
+use App\Http\Requests;
+use App\Jobs\SendTexts;
+use App\Http\Controllers\Controller;
+use App\Http\Models\WechatTemplateSendInfo;
+use Carbon\Carbon;
+use App\Http\Controllers\Wechat\Staff\TextsWorksController;
+
+class AddTextsTasksController extends Controller
+{
+	public function add_texts_task(){
+		$data = array();
+		$data['openid'] = 'oMF1twu7TSSFcV2snbAmvZ5t96D4';
+		$data['appid'] = 'wxb2aaa55b088f12e9';
+		
+// 		$data['openid'] = 'oAcqg1LRHNKN2jaEkJ5v56HOwPEQ';
+// 		$data['appid'] = 'wxdbc486f1b4f6a8c3';
+		
+		$data['content'] = <<<EOT
+		你好啊3323,<a href="www.baidu.com">woca</a>
+woha
+woca
+	  nima
+EOT;
+// 		$data['content'] = json_encode($data['content']);
+		$data['type'] = 'last_task';// last_task,one_task
+		$data['task_id'] = 1;
+		$data['send_time'] = date("Y-m-d H:i:s");
+		$send_data=array(
+			'send_time'=>date("Y-m-d H:i:s"),
+			'data' => $data
+		);
+		
+		v('add_texts_task_data:');v($send_data);
+		$delay = 0;
+
+// 		v('delay:'.$delay);die();
+		$job = (new SendTexts($send_data))->onConnection('rabbitmq')->delay($delay)->onQueue('send_texts_list');
+		
+		dispatch($job);
+
+	}
+	
+	public function test_send_texts(){
+		$data = array();
+		$data['openid'] = 'oAcqg1LRHNKN2jaEkJ5v56HOwPEQ';
+		$data['appid'] = 'wxdbc486f1b4f6a8c3';
+		$data['content'] = '你好,<a href="www.baidu.com">woca</a>\n\nwoha';
+		$data['type'] = 'last_task';// last_task,one_task
+		$data['task_id'] = 3;
+		$data['send_time'] = date("Y-m-d H:i:s");
+
+		v('test_send_template_data:');v($data);
+		$TemplateWork = new TextsWorksController($data);
+		$TemplateWork->do_work();
+	}
+
+}

+ 27 - 0
app/Http/Controllers/Queue/Template/QueuedController.php

@@ -0,0 +1,27 @@
+<?php
+ 
+namespace App\Http\Controllers\Queue\Template;
+ 
+use Illuminate\Http\Request;
+use App\Http\Models\WechatTemplateSendInfo;
+use App\Http\Controllers\Controller;
+
+use App\Http\Requests;
+// use App\Jobs\QueuedTest;
+use App\Jobs\SendTemplate;
+
+class QueuedController extends Controller
+{
+    public function Test(){
+    	$data = WechatTemplateSendInfo::get_wechat_template_send_info(1);
+    	$send_data=array(
+    			'send_time'=>date("Y-m-d H:i:s"),
+    			'data' => $data
+    	);
+//         $data=array(
+//             'time'=>time()
+//         );
+        $job = (new SendTemplate($send_data))->onConnection('rabbitmq')->onQueue('laravel_queue3');
+        dispatch($job);
+    }
+}

+ 25 - 0
app/Http/Controllers/QueuedController.php

@@ -0,0 +1,25 @@
+<?php
+ 
+namespace App\Http\Controllers;
+ 
+use Illuminate\Http\Request;
+use App\Http\Models\WechatTemplateSendInfo;
+
+use App\Http\Requests;
+use App\Jobs\QueuedTest;
+ 
+class QueuedController extends Controller
+{
+    public function Test(){
+    	$data = WechatTemplateSendInfo::get_wechat_template_send_info(1);
+    	$send_data=array(
+    			'send_time'=>date("Y-m-d H:i:s"),
+    			'data' => $data
+    	);
+//         $data=array(
+//             'time'=>time()
+//         );
+        $job = (new QueuedTest($send_data))->onConnection('rabbitmq')->onQueue('laravel_queue');
+        dispatch($job);
+    }
+}

+ 39 - 0
app/Http/Controllers/Wechat/Api/MPverifysController.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Api;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use DB;
+
+/**
+ * 微信验证
+ * @author zhoulingjie
+ *
+ */
+class MPverifysController extends Controller
+{
+	/**
+	 * 从oss上读取文件的内容输出
+	 * @param Request $request
+	 */
+   function mp_verify(Request $request){
+   	    $file_name = '';
+		preg_match('/wechat\/(.*)?.txt/i',$_SERVER['REQUEST_URI'],$data);
+		isset($data[1]) && !empty($data[1]) && $file_name=$data[1];
+		
+		$file_url = 'http://yueduyun-wechat.oss-cn-hangzhou-internal.aliyuncs.com/mp_verify/'.$file_name.'.txt';
+		
+    	$contents = @file_get_contents($file_url);
+    	v('$file_name:'.$file_name.' $file_url:'.$file_url.' $contents:'.json_encode($contents));
+    	if(empty($contents)){
+    		echo 'invalid file_url';
+    	}else{
+    		echo $contents;
+    	}
+    	exit();
+   	   
+   }
+}

+ 77 - 0
app/Http/Controllers/Wechat/Api/TestApisController.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Api;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use DB;
+use Illuminate\Support\Facades\Redis;
+use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Facades\Input;
+use Illuminate\Support\Facades\Validator;
+use Illuminate\Support\Facades\File;
+use App\Http\Controllers\WechatController;
+use App\Http\Controllers\Wechat\Api\WechatInnerApisController;
+
+
+/**
+ * 阅读云相关api接口,和微信无关
+ * @author zhoulingjie
+ *
+ */
+class TestApisController extends WechatController
+{
+   public $WechatApi;
+   function __construct(){
+	   	v('TestApisController_construct');
+	   	$this->gzh_app_id = $_REQUEST['gzh_app_id'];
+	   	
+	   	parent::__construct($this->gzh_app_id);
+	   	// 方便扩展
+	   	$this->param = array();
+	   	v('$this->param:');v($this->param);
+	   	$this->param['app'] = $this->app;
+	   	$this->param['gzh_app_id'] = $this->gzh_app_id;
+	   	$this->param['openid'] = '';
+	   	$this->param['group_api'] = $this->group_api;
+	   	$this->param['group'] = $this->group;
+	   	$this->WechatApi = new WechatInnerApisController($this->param);
+   }
+   
+    /**
+     * 测试连接远程db
+     */
+    static public function test_connect_db()
+    {
+    	 v('test_connect_db');
+    	 DB::connection()->enableQuerylog();
+    	 $datas = DB::select('select * from users limit 2');
+    	 v(DB::getQuerylog());
+    	 v('$datas');v($datas);
+    }
+    
+    /**
+     * 测试连接远程redis
+     */
+    static public function test_connect_redis()
+    {
+    	v('test_connect_redis');
+    	Redis::Set('zhoulj_test','zhoulj_test5');
+    	$redis_res = Redis::Get('zhoulj_test');
+    	v('redis_res');v($redis_res);
+    }
+    
+    /**
+     * 测试搜索接口
+     */
+    public function test_search()
+    {
+    	v('test_search');
+    	$result = $this->WechatApi->search_event_content('text','我');
+    	v('$result');v($result);
+    }
+ 
+}

+ 295 - 0
app/Http/Controllers/Wechat/Api/WechatInnerApisController.php

@@ -0,0 +1,295 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Api;
+
+use App\Http\Requests;
+use App\Modules\OfficialAccount\Services\WechatTemplateService;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use App\Libs\Classes\WxSign;
+use App\Http\Controllers\WechatBaseApisController;
+use Illuminate\Support\Facades\Redis;
+
+/**
+ * 微信内部中间件相关api接口
+ * @author zhoulingjie
+ *
+ */
+class WechatInnerApisController extends WechatBaseApisController
+{
+	 public function __construct($_param)
+	 {
+	 	parent::__construct($_param);
+	 }
+	 
+	 /**
+	  * 得到事件的推送内容
+	  * 
+	  */
+	 public function get_event_content($event,$content,$gzh_app_id,$openid)
+	 {
+ 	    $data = array();
+ 		$data['event'] = strtolower($event);
+ 		$data['content'] = $content;
+ 		$data['appid'] = $gzh_app_id;
+ 		$data['openid'] = $openid;
+ 		return $this->do_api_post($data,'get_event_content');
+	 }
+	 
+	 /**
+	  * 得到强关用户信息
+	  */
+	 function get_force_wx_user($gzh_app_id,$openid)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	$data['openid'] = $openid;
+	 	return $this->do_api_post($data,'get_force_wx_user');
+	 }
+	 
+	 /**
+	  * 得到默认登录用户信息
+	  */
+	 function get_login_wx_user($gzh_app_id,$union_id)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	$data['unionid'] = $union_id;
+	 	return $this->do_api_post($data,'get_login_wx_user');
+	 }
+	 
+	 /**
+	  * 得到公众号信息
+	  */
+	 function get_official_account($gzh_app_id)
+	 {
+	 	// 从redis取
+	 	$redis_offcial_account_key = env('redis_offcial_account_key');
+	 	if(!empty($redis_offcial_account_key)){
+	 		$redis_offcial_account_key = $redis_offcial_account_key.$gzh_app_id;
+// 	 		v('get_official_account_from_redis:'.$gzh_app_id.' redis_offcial_account_key:'.$redis_offcial_account_key);
+	 		$official_account = Redis::hGet($redis_offcial_account_key,'official_account_info');
+	 		$official_account = objectToArray(json_decode($official_account));
+// 	 		v('official_account:'.json_encode($official_account));
+	 		if(empty($official_account)){
+	 			v('get_official_account_from_redis_api:'.$gzh_app_id);
+	 			$data = array();
+	 			$data['appid'] = $gzh_app_id;
+	 			return $this->do_api_post($data,'get_official_account');
+	 		}
+	 		return $official_account;
+	 	}else{
+	 		v('get_official_account_from_api:'.$gzh_app_id);
+		 	$data = array();
+		 	$data['appid'] = $gzh_app_id;
+		 	return $this->do_api_post($data,'get_official_account');
+	 	}
+
+	 }
+	 
+	 /**
+	  * 得到基础模板消息列表
+	  */
+	 function get_wechat_public_templates($gzh_app_id)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	return $this->do_api_post($data,'get_wechat_public_templates');
+	 }
+	 
+	 /**
+	  * 得到公众号对应的模板消息
+	  */
+	 function get_template_official_info($gzh_app_id,$common_template_id)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	$data['common_template_id'] = $common_template_id;
+	 	return $this->do_api_post($data,'get_template_official_info');
+	 }
+	 
+	 
+	 /**
+	  * 得到公众号对应的菜单设置列表
+	  */
+	 function get_official_account_menus($gzh_app_id)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	return $this->do_api_post($data,'get_official_account_menus');
+	 }
+	 
+	 /**
+	  * 和强关公众号交互时间更新
+	  */
+	 function update_user_interaction_time($gzh_app_id,$openid,$distribution_channel_id)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	$data['openid'] = $openid;
+	 	$data['distribution_channel_id'] = $distribution_channel_id;
+	 	$data['last_interactive_time'] = date('Y-m-d H:i:s');
+	 	return $this->do_api_post($data,'update_user_interaction_time');
+	 }
+	 
+	 /**
+	  * 更新模板消息任务状态
+	  */
+	 function update_template_task_status($task_id,$status,$msg)
+	 {
+	 	// 从redis取
+	 	$redis_task_key = env('redis_task_key');
+	 	if(!empty($redis_task_key)){
+	 		$redis_task_key = $redis_task_key.$task_id;
+// 	 		v('update_template_task_status_from_redis:'.$task_id.' redis_task_key:'.$redis_task_key);
+	 		$template_task = Redis::hGet($redis_task_key,'wechat_msg');
+	 		$template_task = objectToArray(json_decode($template_task));
+// 	 		v('update_template_task_status_template_task:'.json_encode($template_task));
+	 		$task_status = isset($template_task['status']) ? $template_task['status']:'';
+	 		v('update_template_task_status_task_id:'.$task_id.' $task_status:'.$task_status);
+	 		if($task_status == 1 || $task_status == 8){
+	 			v('update_template_task_status_exe_innner:'.$task_id);
+	 			$data = array();
+	 			$data['id'] = $task_id;
+	 			$data['status'] = $status;
+	 			$data['msg'] = $msg;
+                WechatTemplateService::updateWechatTemplateStatus($data);
+	 			//return $this->do_api_post($data,'update_template_task_status');
+	 		}else{
+	 			v('update_template_task_status_ignore:'.$task_id.' status:'.$task_status);
+	 		}
+	 	
+	 		return false;
+	 	}else{
+ 			v('update_template_task_status_inner:'.$task_id);
+ 			$data = array();
+ 			$data['id'] = $task_id;
+ 			$data['status'] = $status;
+ 			$data['msg'] = $msg;
+ 			return $this->do_api_post($data,'update_template_task_status');
+	 	}
+
+	 }
+	 
+	 /**
+	  * 关注
+	  */
+	 function subscribe_wx_user($gzh_app_id,$openid)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	$data['openid'] = $openid;
+	 	return $this->do_api_post($data,'subscribe_wx_user');
+	 }
+	 
+	 /**
+	  * 取消关注
+	  */
+	 function unsubscribe_wx_user($gzh_app_id,$openid)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	$data['openid'] = $openid;
+	 	return $this->do_api_post($data,'unsubscribe_wx_user');
+	 }
+	 
+	 /**
+	 * 更新用户uid
+	 */
+	 function update_force_wx_user($openid,$uid)
+	 {
+	 	$data = array();
+	 	$data['uid'] = $uid;
+	 	$data['openid'] = $openid;
+	 	return $this->do_api_post($data,'update_force_wx_user');
+	 }
+	 
+	 /**
+	  * 取消授权
+	  */
+	 function cancel_oauth_official_account($gzh_app_id)
+	 {
+	 	$data = array();
+	 	$data['appid'] = $gzh_app_id;
+	 	return $this->do_api_post($data,'cancel_oauth_official_account');
+	 }
+	 
+	 /**
+	  * 保存默认登录用户信息
+	  */
+	 function save_login_wx_user($data)
+	 {
+	 	return $this->do_api_post($data,'save_login_wx_user');
+	 }
+	 
+	 /**
+	  * 保存强关登录用户信息
+	  */
+	 function save_force_wx_user($data)
+	 {
+	 	return $this->do_api_post($data,'save_force_wx_user');
+	 }
+	 
+	 /**
+	  * 保存公众号信息
+	  */
+	 function save_official_account($data)
+	 {
+	 	return $this->do_api_post($data,'save_official_account');
+	 }
+	 
+	 /**
+	  * 保存公众号对应模板消息
+	  */
+	 function save_template_official_info($data)
+	 {
+	 	return $this->do_api_post($data,'save_template_official_info');
+	 }
+	 
+	 /**
+	  * 更新公众号对应模板消息
+	  */
+	 function update_template_official_info($data)
+	 {
+	 	return $this->do_api_post($data,'update_template_official_info');
+	 }
+
+	 /**
+	  * 得到模板消息任务
+	  */
+	 function get_template_task($task_id)
+	 {
+	 	// 从redis取
+	 	$redis_task_key = env('redis_task_key');
+	 	if(!empty($redis_task_key)){
+	 		$redis_task_key = $redis_task_key.$task_id;
+	 		$template_task = Redis::hGet($redis_task_key,'wechat_msg');
+	 		$template_task = objectToArray(json_decode($template_task));
+	 		if(empty($template_task)){
+	 			v('get_template_task_from_redis_api:'.$task_id);
+	 			$data = array();
+	 			$data['id'] = $task_id;
+	 			return $this->do_api_post($data,'get_template_task');
+	 		}
+	 		return $template_task;
+	 	}else{
+	 		$data = array();
+	 		$data['id'] = $task_id;
+	 		return $this->do_api_post($data,'get_template_task');
+	 	}
+
+	 }
+	 
+	 /**
+	  * 得到素材强关的映射关系(distribution_channel_id,openid => uid)
+	  */
+	 function get_material_force_subscribe_mapping($distribution_channel_id,$openid)
+	 {
+	 	$data = array();
+	 	$data['distribution_channel_id'] = $distribution_channel_id;
+	 	$data['openid'] = $openid;
+	 	return $this->do_api_post($data,'get_material_force_subscribe_mapping');
+	 }
+	 
+}

+ 78 - 0
app/Http/Controllers/Wechat/Api/WechatOpApisController.php

@@ -0,0 +1,78 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Api;
+
+use App\Http\Controllers\WechatOpController;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use WechatOP;
+use GuzzleHttp\Client;
+use EasyWeChat\OpenPlatform\Api\BaseApi;
+
+/**
+ * 三方相关api接口
+ * @author zhoulingjie
+ *
+ */
+class WechatOpApisController extends WechatOpController
+{
+	
+	 public function __construct(Request $request)
+	 {
+		parent::__construct();
+	 }
+	
+	 /**
+	  * 获取三方平台绑定的公众号列表
+	  * @param Request $request
+	  * 
+	  http://zydy/api/get_authorizer_list?component_appid=wxceb2aacdce248393&component_access_token=17_hTuiGp6BoLl3OuC8qtJHocQOM2UjClJFnp-htHjKcXmTxNsl06M2c7SG7cJHWOgYwxPgfpeRIA-302-Rl_TxdV2X4YcDHKXbT-6Qk7oUlaGrF7e3TWxZgsT1p39XYUUWXfXUob7rIS6qjayrQTYhADAKNS
+	  */
+	 function get_authorizer_list(Request $request){
+	 	$result = array('code'=>1,'msg'=>'','data'=>'');
+	 	$component_appid = $request->get('component_appid');
+	 	$component_access_token = $request->get('component_access_token');
+	 	v('get_authorizer_list_start,component_appid:'.$component_appid.' $component_access_token:'.$component_access_token);
+	 	if(empty($component_appid)){
+	 		$result['code'] = 0;
+	 		$result['msg'] = 'invalid param';
+	 		json_echo($result);
+	 	}
+	 
+	 	$check_result = $this->check_sign_params($request);
+	 	if($check_result['code'] == 0){
+	 		$result['code'] = 0;
+	 		$result['msg'] = $check_result['msg'];
+// 	 		json_echo($result);
+	 	}
+	 	
+// 	 	$authorizer_list = $this->openPlatform->getAuthorizerInfo('wxceb2aacdce248393');
+
+	 	$authors = [];
+
+	 	for($i=1;$i<4;$i++){
+	 		$client = new Client();
+	 		$form_params = [
+		 		"component_appid"=>$component_appid,
+		 		"count"=>500,
+		 		"offset"=>($i-1)*500,
+	 		];
+	 		v($form_params);
+	 		$authorizer_list = $client->request("post","https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_list?component_access_token=".$component_access_token,
+	 				['json'=>$form_params,'connect_timeout' => 3]
+	 		)->getBody()->getContents();
+	 		$authorizer_list = json_decode($authorizer_list,true);
+	 		v('$authorizer_list:'.$i);v($authorizer_list);
+	 		if(!empty($authorizer_list['list'])){
+	 			foreach($authorizer_list['list'] as $authorizer){
+	 				$authors[] = $authorizer['authorizer_appid'];
+	 			}
+	 		}
+	 	}
+	 	
+	 	$result['data'] = $authors;
+	 	json_echo($result);
+	 }
+    
+}

+ 605 - 0
app/Http/Controllers/Wechat/Api/WechatOuterApisController.php

@@ -0,0 +1,605 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Api;
+
+use App\Http\Controllers\WechatController;
+use App\Http\Controllers\Wechat\Qrcode\QrcodesController;
+use App\Http\Controllers\Wechat\Material\MaterialsController;
+use App\Http\Controllers\Wechat\Template\TemplateBasesController;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use App\Http\Controllers\Wechat\Staff\StaffsController;
+
+/**
+ * 对外相关api接口,和微信token有关
+ * @author zhoulingjie
+ *
+ */
+class WechatOuterApisController extends WechatController
+{
+
+    public function __construct(Request $request)
+    {
+        // TODO 加密,检测gzh_app_id合法性
+        $this->gzh_app_id = $request->get('gzh_app_id');
+        parent::__construct($this->gzh_app_id);
+        // 方便扩展
+        $param               = array();
+        $param['app']        = $this->app;
+        $param['WechatApi']  = isset($this->WechatApi) ? $this->WechatApi : null;
+        $param['gzh_app_id'] = $this->gzh_app_id;
+        $this->Qrcode        = new QrcodesController($param);
+        $this->Material      = new MaterialsController($param);
+        $this->Template      = new TemplateBasesController($param);
+        $this->Staff         = new StaffsController($this->param);
+        $this->User          = $this->app->user;
+
+    }
+
+    /**
+     * http://zydy/api/get_qrcode_url?gzh_app_id=wx03d107cd1ee728c2&scene_id=999&timestamp=1511503861&sign=74b21f32c094f8d343c9743c196f6a32
+     * 获取带参二维码
+     */
+    public function get_qrcode_url(Request $request)
+    {
+        $result     = array('code' => 1, 'msg' => '', 'data' => '');
+        $sceneId    = $request->get('scene_id');
+        $gzh_app_id = $request->get('gzh_app_id');
+        v($request->all());
+        if (empty($sceneId) || empty($gzh_app_id)) {
+            $result['code'] = 0;
+            $result['msg']  = 'invalid param';
+            json_echo($result);
+        }
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+
+        $qrcode_url = $this->Qrcode->create_qrcode('temporary', $sceneId);
+        //     	v($chapters);
+        if (!empty($qrcode_url)) {
+            $result['data'] = $qrcode_url;
+        } else {
+            $result['code'] = 0;
+            $result['msg']  = 'create qrcode url fail';
+        }
+
+        json_echo($result);
+    }
+
+    /**
+     * http://zydy/api/upload_gzh_img?gzh_app_id=wxdbc486f1b4f6a8c3&img_path=http%3A%2F%2Fauth.aizhuishu.com%2Fadmin%2Fimgs%2Fcommon%2Fcontact%2Fcontact_customer.png&timestamp=1511503861&sign=74b21f32c094f8d343c9743c196f6a32
+     * http://zydy/api/upload_gzh_img?gzh_app_id=wxdbc486f1b4f6a8c3&group_nick=zhuishuyun&timestamp=1513600581&sign=38693a228ea65fc894c15bc6d76e7287
+     * http://zydy/api/upload_gzh_img?gzh_app_id=wxdbc486f1b4f6a8c3&is_default=0&img_url=https%3A%2F%2Fimages.csdn.net%2F20171218%2FVCG41103923818.jpg&group_nick=zhuishuyun&timestamp=1513600581&sign=38693a228ea65fc894c15bc6d76e7287
+     * 上传公众号图片
+     */
+    public function upload_gzh_img(Request $request)
+    {
+        $result     = array('code' => 1, 'msg' => '', 'data' => array('media_id' => '', 'url' => ''));
+        $group_nick = $request->get('group_nick');
+        $gzh_app_id = $request->get('gzh_app_id');
+        $is_default = $request->has('is_default') ? $request->get('is_default') : 1;
+        $img_url    = $request->has('img_url') ? $request->get('img_url') : '';
+
+        v($request->all());
+        if (empty($group_nick) || empty($gzh_app_id)) {
+            $result['code'] = 0;
+            $result['msg']  = 'upload_gzh_img invalid param';
+            json_echo($result);
+        }
+        v('upload_gzh_img:' . $gzh_app_id);
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+
+        if (!$is_default) {
+            $img_url = DownImage($img_url, public_path('admin/imgs/contact_customer/contact_customer_' . $gzh_app_id . '.png'));
+        } else {
+            $img_url = public_path('admin/imgs/common/contact/contact_customer_' . $group_nick . '.png');
+        }
+        $upload_result = $this->Material->upload_contact_cusomer_img($group_nick, $img_url);
+        v('$upload_result');
+        v($upload_result);
+
+        if (!empty($upload_result)) {
+            $result['data']['media_id'] = $upload_result->media_id;
+            $result['data']['url']      = $upload_result->url;
+        } else {
+            $result['code'] = 0;
+            $result['msg']  = 'upload img fail';
+        }
+        v('gzh_appid:' . $gzh_app_id . ' result:' . json_encode($result));
+
+        json_echo($result);
+    }
+
+    /**
+     * http://zydy/api/upload_material_img?gzh_app_id=wxdbc486f1b4f6a8c3&img_url=https%3A%2F%2Fimages.csdn.net%2F20171218%2FVCG41103923818.jpg&group_nick=zhuishuyun&timestamp=1514453795&sign=35d5a601b9d45d57b648f8574b946274
+     * 上传公众号素材图片
+     */
+    public function upload_material_img(Request $request)
+    {
+        $result       = array('code' => 1, 'msg' => '', 'data' => array('media_id' => '', 'url' => ''));
+        $group_nick   = $request->get('group_nick');
+        $gzh_app_id   = $request->get('gzh_app_id');
+        $image_type   = $request->has('image_type') ? $request->get('image_type') : 'common';
+        $img_url      = $request->has('img_url') ? $request->get('img_url') : '';
+        $real_img_url = urldecode($img_url);
+
+        v($request->all());
+        if (empty($group_nick) || empty($gzh_app_id)) {
+            $result['code'] = 0;
+            $result['msg']  = 'upload_gzh_img invalid param';
+            json_echo($result);
+        }
+        v('upload_gzh_img:' . $gzh_app_id);
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+        $path = public_path('admin/imgs/material/temp_' . $gzh_app_id . '_' . md5($img_url) . '.png');
+        v('DownImage:real_img_url:' . $real_img_url . ' path:' . $path);
+        DownImage($real_img_url, $path);
+
+        if ($image_type == 'common') {
+            $upload_result = $this->Material->upload_material_img($group_nick, $path);
+        } else {
+            $upload_result = $this->Material->upload_thumb_material_img($group_nick, $path);
+        }
+
+        if (!empty($upload_result)) {
+            $result['data']['media_id'] = $upload_result->media_id;
+            $result['data']['url']      = $upload_result->url;
+        } else {
+            $result['code'] = 0;
+            $result['msg']  = $this->Material->getErrorMsg('upload_materials', 'upload img fail');
+        }
+
+        json_echo($result);
+    }
+
+    /**
+     * http://zydy/api/upload_gzh_article?show_cover_pic=0&author=%E8%B6%85%E5%93%A5&digest=%E5%8F%AA%E6%98%AF%E4%B8%AA%E6%91%98%E8%A6%81&content=%E5%8F%AA%E6%98%AF%E4%B8%AA%E5%86%85%E5%AE%B9%E8%80%8C%E5%B7%B2&title=testtest&thumb_media_id=XYhErlq3w-hlmwqOB4O5SlIHHHRE3jetv2k1y1iwemE&gzh_app_id=wxdbc486f1b4f6a8c3&group_nick=zhuishuyun&timestamp=1514454260&sign=9d708b61806fe5dacb7f632803463da1
+     * 上传公众号图文素材
+     */
+    public function upload_gzh_article(Request $request)
+    {
+        $result             = array('code' => 1, 'msg' => '', 'data' => array('chapter_media_id' => '', 'chapter_url' => ''));
+        $group_nick         = $request->get('group_nick');
+        $gzh_app_id         = $request->get('gzh_app_id');
+        $title              = $request->has('title') ? $request->get('title') : '';
+        $thumb_media_id     = $request->has('thumb_media_id') ? $request->get('thumb_media_id') : '';
+        $show_cover_pic     = $request->has('show_cover_pic') ? $request->get('show_cover_pic') : '';
+        $author             = $request->has('author') ? $request->get('author') : '';
+        $digest             = $request->has('digest') ? $request->get('digest') : '';
+        $content            = $request->has('content') ? $request->get('content') : '';
+        $content_source_url = $request->has('content_source_url') ? $request->get('content_source_url') : '';
+        v($request->all());
+        if (empty($group_nick) || empty($gzh_app_id) || empty($thumb_media_id) || empty($content)) {
+            $result['code'] = 0;
+            $result['msg']  = 'upload_gzh_article invalid param';
+            json_echo($result);
+        }
+        v('upload_gzh_img:' . $gzh_app_id);
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+
+        $article                       = array();
+        $article['thumb_media_id']     = $thumb_media_id;//'XYhErlq3w-hlmwqOB4O5SqFAgM6yWcg0u9ttENsjSIg';
+        $article['title']              = $title;
+        $article['show_cover_pic']     = $show_cover_pic;
+        $article['author']             = $author;
+        $article['digest']             = $digest;
+        $article['content']            = $content;
+        $article['content_source_url'] = $content_source_url;
+        $upload_result                 = $this->Material->upload_article($article);
+
+        if (!empty($upload_result)) {
+            $chapter_resource = $this->Material->get_material_resource($upload_result->media_id);
+            if (!empty($chapter_resource)) {
+                $result['data']['chapter_url']      = isset($chapter_resource['news_item'][0]['url']) ? $chapter_resource['news_item'][0]['url'] : '';
+                $result['data']['chapter_media_id'] = isset($chapter_resource['news_item'][0]['thumb_media_id']) ? $chapter_resource['news_item'][0]['thumb_media_id'] : '';
+            } else {
+                $result['code'] = 0;
+                $result['msg']  = 'get_material_resource fail';
+            }
+        } else {
+            $result['code'] = 0;
+            $result['msg']  = 'upload img fail';
+        }
+        v($result);
+
+        json_echo($result);
+    }
+
+    /**
+     * http://zydy/api/upload_gzh_articles?show_cover_pic=0&author=%E8%B6%85%E5%93%A5&digest=%E5%8F%AA%E6%98%AF%E4%B8%AA%E6%91%98%E8%A6%81&content=%E5%8F%AA%E6%98%AF%E4%B8%AA%E5%86%85%E5%AE%B9%E8%80%8C%E5%B7%B2&title=testtest&thumb_media_id=XYhErlq3w-hlmwqOB4O5SlIHHHRE3jetv2k1y1iwemE&gzh_app_id=wxdbc486f1b4f6a8c3&group_nick=zhuishuyun&timestamp=1514454260&sign=9d708b61806fe5dacb7f632803463da1
+     * 上传公众号图文素材
+     */
+    public function upload_gzh_articles(Request $request)
+    {
+        $result     = array('code' => 1, 'msg' => '', 'data' => array('chapter_media_id' => '', 'chapter_url' => ''));
+        $group_nick = $request->get('group_nick');
+        $gzh_app_id = $request->get('gzh_app_id');
+        $articles   = $request->has('articles') ? $request->get('articles') : '';
+        v($request->all());
+        if (empty($group_nick) || empty($gzh_app_id) || empty($articles)) {
+            $result['code'] = 0;
+            $result['msg']  = 'upload_gzh_articles invalid param';
+            json_echo($result);
+        }
+        v('upload_gzh_articles:' . $gzh_app_id);
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+
+//     	$articles = json_decode($articles);
+        $upload_result = $this->Material->upload_articles($gzh_app_id, $articles);
+
+        if (!empty($upload_result)) {
+            $result['data']   = $upload_result;
+            $chapter_resource = $this->Material->get_material_resource($upload_result->media_id);
+        } else {
+            $result['code'] = 0;
+            $result['msg']  = $this->Material->getErrorMsg('upload_articles', 'upload articles fail');
+        }
+        v($result);
+
+        json_echo($result);
+    }
+
+
+    /**
+     * 维维读书 wx03d107cd1ee728c2 17
+     * 纳兰阅读 wx392b782e2a082eb2 16
+     * 已读攻毒 wx62c7e9290d51eeba 23
+     * http://zydy/oauth/add_public_template?gzh_app_id=wxd52de911dfef6d92&common_template_id=&timestamp=1511509543&sign=98dccc7b29e3c05e4b4367f488ff1966
+     * http://zsyauth.aizhuishu.com/oauth/auto_set_menu_and_template_test?authorizer_appid=wxdb365b8f2fc8aeb0&timestamp=1511509543&sign=98dccc7b29e3c05e4b4367f488ff1966
+     */
+    function add_public_template(Request $request)
+    {
+        $result             = array('code' => 1, 'msg' => '', 'data' => '');
+        $gzh_app_id         = $request->get('gzh_app_id');
+        $common_template_id = $request->get('common_template_id');
+        v('add_public_template_start,gzh_appid:' . $gzh_app_id . ' common_template_id:' . $common_template_id);
+        if (empty($gzh_app_id) || empty($common_template_id)) {
+            $result['code'] = 0;
+            $result['msg']  = 'invalid param';
+            json_echo($result);
+        }
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+        $this->Template->add_one_tamplate($common_template_id);
+        v('add_public_template_end,gzh_app_id:' . $gzh_app_id . ' common_template_id:' . $common_template_id);
+    }
+
+    /**
+     * 嘉言小说 wxdbc486f1b4f6a8c3
+     * http://zydy/api/get_full_official_account_users?gzh_app_id=wxdbc486f1b4f6a8c3&next_openid=timestamp=1511509543&sign=98dccc7b29e3c05e4b4367f488ff1966
+     */
+    function get_full_official_account_users(Request $request)
+    {
+        $result      = array('code' => 1, 'msg' => '', 'data' => array('total' => '', 'count' => '', 'data' => '', 'next_openid' => ''));
+        $gzh_app_id  = $request->get('gzh_app_id');
+        $next_openid = $request->get('next_openid');
+        v('get_full_official_account_users_start,gzh_appid:' . $gzh_app_id . ' next_openid:' . $next_openid);
+        if (empty($gzh_app_id)) {
+            $result['code'] = 0;
+            $result['msg']  = 'invalid param';
+            json_echo($result);
+        }
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+        $users = $this->User->lists($next_openid);
+        v($users);
+        $result['data']['total']       = isset($users->total) ? $users->total : '';
+        $result['data']['count']       = isset($users->count) ? $users->count : '';
+        $result['data']['data']        = isset($users->data) ? $users->data : '';
+        $result['data']['next_openid'] = isset($users->next_openid) ? $users->next_openid : '';
+        v('get_full_official_account_users_end,gzh_app_id:' . $gzh_app_id . ' next_openid:' . $next_openid);
+        json_echo($result);
+    }
+
+    /**
+     * 维护公众号模板消息(被封,删除等异常情况)
+     * http://zydy/api/check_official_account_templates?gzh_app_id=wxdbc486f1b4f6a8c3&timestamp=1523440738&sign=1d9640c40ef0bbb19b5352f60372c300
+     */
+    function check_official_account_templates(Request $request)
+    {
+        $result     = array('code' => 1, 'msg' => '', 'data' => '');
+        $gzh_app_id = $request->get('gzh_app_id');
+        v('check_official_account_templates_start,gzh_appid:' . $gzh_app_id);
+        if (empty($gzh_app_id)) {
+            $result['code'] = 0;
+            $result['msg']  = 'invalid param';
+            json_echo($result);
+        }
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+        // 微信后台的模板
+        $wx_backend_templates = $this->Template->get_private_templates();
+        $wx_template_ids      = array();
+        if (isset($wx_backend_templates->template_list) && !empty($wx_backend_templates->template_list)) {
+            foreach ($wx_backend_templates->template_list as $one_template) {
+                $wx_template_ids[] = $one_template['template_id'];
+            }
+        }
+        v('wx_template_ids:' . json_encode($wx_template_ids));
+
+        // 我们目前的基础模板
+        $base_templates = $this->WechatApi->get_wechat_public_templates($gzh_app_id);
+//     	v('base_templates');v($base_templates);
+        if ($base_templates) {
+            foreach ($base_templates as $template) {
+                $this->Template->check_one_tamplate($template['common_template_id'], $wx_template_ids);
+            }
+        }
+
+        v('maintain_official_account_templates_end,gzh_app_id:' . $gzh_app_id);
+    }
+
+    /**
+     * 删除菜单
+     * http://zydy/api/del_menu?gzh_app_id=wxdbc486f1b4f6a8c3&timestamp=1523615257&sign=056ea8390b3d5a1a6d2444288c1a0251
+     */
+    function del_menu(Request $request)
+    {
+        $result     = array('code' => 1, 'msg' => '', 'data' => '');
+        $gzh_app_id = $request->get('gzh_app_id');
+        v('del_menu_start,gzh_appid:' . $gzh_app_id);
+        if (empty($gzh_app_id)) {
+            $result['code'] = 0;
+            $result['msg']  = 'invalid param';
+            json_echo($result);
+        }
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+        $menu         = $this->app->menu;
+        $current_menu = $menu->current();
+        v('$current_menu');
+        v($current_menu);
+
+        $del_menu = $menu->destroy();
+        v('$del_menu');
+        v($del_menu);
+
+        $current_menu = $menu->current();
+        v('$after_current_menu');
+
+        v('del_menu_end,gzh_app_id:' . $gzh_app_id);
+    }
+
+    /**
+     * 得到用户信息
+     * http://zydy/api/get_userinfo?gzh_app_id=wxdbc486f1b4f6a8c3&openid=oAcqg1LRHNKN2jaEkJ5v56HOwPEQ&timestamp=1524130007&sign=e7ceefdf35ae7eee15e00404fe89c66c
+     */
+    function get_userinfo(Request $request)
+    {
+        $result     = array('code' => 1, 'msg' => '', 'data' => '');
+        $gzh_app_id = $request->get('gzh_app_id');
+        $openid     = $request->get('openid');
+        v('get_userinfo,gzh_appid:' . $gzh_app_id . ' openid:' . $openid);
+        if (empty($gzh_app_id) || empty($openid)) {
+            $result['code'] = 0;
+            $result['msg']  = 'invalid param';
+            json_echo($result);
+        }
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+        $user     = $this->app->user;
+        $userinfo = $user->get($openid);
+        v('userinfo');
+        v($userinfo);
+
+        $result['data'] = $userinfo;
+        json_echo($result);
+    }
+
+    /**
+     * 得到用户信息
+     * http://zydy/api/get_short_url?gzh_app_id=wxdbc486f1b4f6a8c3&url=www.baidu.com&timestamp=1524130007&sign=e7ceefdf35ae7eee15e00404fe89c66c
+     */
+    function get_short_url(Request $request)
+    {
+        $result     = array('code' => 1, 'msg' => '', 'data' => '');
+        $gzh_app_id = $request->get('gzh_app_id');
+        $url        = $request->get('url');
+        $url        = urldecode($url);
+        v('get_short_url,gzh_appid:' . $gzh_app_id . ' $url:' . $url);
+        if (empty($gzh_app_id) || empty($url)) {
+            $result['code'] = 0;
+            $result['msg']  = 'invalid param';
+            json_echo($result);
+        }
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+
+        try {
+            v('get_short_url_start:' . $gzh_app_id);
+            $app_short_url = $this->app->url;
+            $short_result  = $app_short_url->shorten($url);
+            v('short_result:' . $gzh_app_id);
+            v($short_result);
+            if (isset($short_result->errcode) && $short_result->errcode == '0'
+                && isset($short_result->errmsg) && $short_result->errmsg == 'ok') {
+                $result['data'] = $short_result->short_url;
+            } else {
+                $result['code'] = 0;
+                $result['msg']  = $short_result->errmsg;
+            }
+        } catch (\Exception $e) {
+            $error_msg = $e->getMessage();
+            v('get_short_url_ept:' . $error_msg);
+            if (strpos($error_msg, 'api forbidden') !== false) {
+                $result['code'] = 0;
+                $result['msg']  = '公众号微信接口被封,请等待解封!';
+            } elseif (strpos($error_msg, 'user limited') !== false) {
+                $result['code'] = 0;
+                $result['msg']  = '公众号微信接口被限制,请等待解除!';
+            }
+        }
+
+        json_echo($result);
+    }
+
+    /**
+     * 检查公众号模板消息(被封,删除等异常情况)
+     * http://zydy/api/check_template_status?gzh_app_id=wxdbc486f1b4f6a8c3&timestamp=1523440738&sign=1d9640c40ef0bbb19b5352f60372c300
+     */
+    function check_template_status(Request $request)
+    {
+        $result             = array('code' => 1, 'msg' => '', 'data' => '');
+        $gzh_app_id         = $request->get('gzh_app_id');
+        $common_template_id = $request->get('common_template_id');
+        v('check_template_status_start,gzh_appid:' . $gzh_app_id . ' common_template_id:' . $common_template_id);
+        if (empty($gzh_app_id) || empty($common_template_id)) {
+            $result['code'] = 0;
+            $result['msg']  = 'invalid param';
+            json_echo($result);
+        }
+
+        $check_result = $this->check_sign_params($request);
+        if ($check_result['code'] == 0) {
+            $result['code'] = 0;
+            $result['msg']  = $check_result['msg'];
+            json_echo($result);
+        }
+
+        $error_msgs = [
+            '1' => '模板修复成功',
+            '2' => '模板正常',
+            '3' => '微信模板修复失败!',
+            '4' => '微信模板接口被封,请等待解封!',
+            '5' => '微信后台模板数量达到上限,请删除部分模板后再操作',
+            '6' => '微信模板被封,请等待解封!',
+            '7' => '模板被微信官方取消失效了!',
+            '8' => '号被限制了权限',
+            '9' => '接口未授权',
+            '10' => '分类不正确!',
+            '11' => '号的模板功能被处罚限制中!',
+        ];
+
+        // 一、先检查分类
+        $industry = $this->Template->get_industry();
+        if ($industry == '4') {
+            $result['msg'] = $error_msgs[$industry];
+            json_echo($result);
+        }
+        $primary_industry_first_class    = $industry->primary_industry['first_class'];
+        $primary_industry_second_class   = $industry->primary_industry['second_class'];
+        $secondary_industry_first_class  = $industry->secondary_industry['first_class'];
+        $secondary_industry_second_class = $industry->secondary_industry['second_class'];
+        v('$primary_industry_first_class:' . $primary_industry_first_class . ' $primary_industry_second_class:' . $primary_industry_second_class . ' $secondary_industry_first_class:' . $secondary_industry_first_class . ' $secondary_industry_second_class:' . $secondary_industry_second_class);
+        
+        $extra_error_msg = '';
+        // 存在部分分类异常情况,所以分类错误允许往下走,还能抢救一部分模板消息
+        if (!($primary_industry_first_class == 'IT科技' && $primary_industry_second_class == 'IT软件与服务'
+            && $secondary_industry_first_class == '文体娱乐' && $secondary_industry_second_class == '文化|传媒')) {
+            v('check_template_status_set_template_dustry:' . $gzh_app_id);
+            $set_res = $this->Template->set_template_dustry_res(2, 37);
+            if ($set_res == 2) {
+                v('modify_dustry_ept:' . $gzh_app_id);
+//                 $result['msg'] = '模板分类错误,请修改分类至(IT科技->IT软件与服务、文体娱乐->文化|传媒),如不能修改请等待,分类1个月才能修改一次';
+//                 json_echo($result);
+                
+                $extra_error_msg = '请注意:模板分类错误,请修改分类至(IT科技->IT软件与服务、文体娱乐->文化|传媒),如不能修改请等待,分类1个月才能修改一次';
+                
+            } else {
+                v('modify_dustry_ok:' . $gzh_app_id);
+            }
+        }
+
+        // 二、分类正确,检查某一个模板,微信后台是否存在,我们系统后台是否存在
+
+        // 微信后台的模板
+        $wx_backend_templates = $this->Template->get_private_templates();
+        $wx_template_ids      = array();
+        if (isset($wx_backend_templates->template_list) && !empty($wx_backend_templates->template_list)) {
+            foreach ($wx_backend_templates->template_list as $one_template) {
+                $wx_template_ids[] = $one_template['template_id'];
+            }
+        }
+        v('wx_template_ids:' . json_encode($wx_template_ids));
+
+        $check_res     = $this->Template->check_one_tamplate_res($common_template_id, $wx_template_ids);
+        $result['msg'] = $error_msgs[$check_res].' '.$extra_error_msg;
+
+        v('check_template_status_end,gzh_app_id:' . $gzh_app_id);
+        json_echo($result);
+    }
+
+    public function get_gzh_statistics(Request $request)
+    {
+        $from_date  = $request->input('from_date', date('Y-m-d', strtotime('-7 day')));
+        $to_date    = $request->input('to_date', date('Y-m-d', strtotime('-1 day')));
+        $gzh_app_id = $this->gzh_app_id;
+        if (empty($gzh_app_id)) {
+            return ['code' => 304, 'msg' => 'appid empty'];
+        }
+        $result = array('code' => 1, 'msg' => '', 'data' => array());
+        try {
+            $result['data']['user_cumulate'] = $this->app->stats->userCumulate($from_date, $to_date);//获取累计用户数据, 最大时间跨度:7;
+            $result['data']['user_summary']  = $this->app->stats->userSummary($from_date, $to_date);//获取用户增减数据, 最大时间跨度:7;
+            v($result);
+        } // 加上\ 全局抓取
+        catch (\Exception $e) {
+            $result['code'] = 0;
+            $result['msg']  = $e->getMessage();
+            v('get_gzh_statistics_ept:' . $gzh_app_id . ' info:' . $e->getMessage());
+        }
+
+        return $result;
+    }
+}

+ 68 - 0
app/Http/Controllers/Wechat/Command/GzhBanAlertController.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Command;
+
+use App\Http\Controllers\WechatController;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+
+/**
+ * 公众号被封禁检测
+ * @author zhoulingjie
+ *
+ */
+class GzhBanAlertController extends WechatController
+{
+	
+	 public function __construct(Request $request)
+	 {
+	    // TODO 加密,检测gzh_app_id合法性
+	    $this->gzh_app_id =$request->get('gzh_app_id');
+		parent::__construct($this->gzh_app_id);
+		// 方便扩展
+		$param = array();
+		$param['app'] = $this->app;
+		$param['WechatApi'] = isset($this->WechatApi)?$this->WechatApi:null;
+		$param['gzh_app_id'] = $this->gzh_app_id;
+		$this->Menu = $this->app->menu;
+	 }
+	
+    /**
+       http://zydy/api/check_gzh_ban?gzh_app_id=wxdbc486f1b4f6a8c3&api_id=4&from=zhoulj    
+       检测公众号是否被封:目前通过菜单
+     */
+    public function check_gzh_ban(Request $request)
+    {
+    	$result = array('code'=>1,'msg'=>'','data'=>'');
+        $gzh_app_id = $request->get('gzh_app_id');
+        $from = $request->get('from');
+    	if(empty($gzh_app_id)){
+    		$result['code'] = 0;
+    		$result['msg'] = 'invalid param';
+    		json_echo($result);
+    	}
+    	
+    	if($from != 'zhoulj'){
+    		$check_result = $this->check_sign_params($request);
+    		if($check_result['code'] == 0){
+    			$result['code'] = 0;
+    			$result['msg'] = $check_result['msg'];
+    			json_echo($result);
+    		}
+    	}
+
+    	$res = '';
+        $menus = [];
+        try{
+            $menus = $this->Menu->current();
+        }catch (\Exception $e){}
+    	$is_menu_open = isset($menus->is_menu_open)?$menus->is_menu_open:1;// 获取不到默认成功?
+
+    	$result['data'] = $is_menu_open;
+
+    	json_echo($result);
+    }
+    
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 405 - 0
app/Http/Controllers/Wechat/GzhMsg/GzhMsgsController.php


+ 379 - 0
app/Http/Controllers/Wechat/Material/MaterialsController.php

@@ -0,0 +1,379 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Material;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use EasyWeChat\Foundation\Application;
+use SimpleSoftwareIO\QrCode\Facades\QrCode;
+use EasyWeChat\Message\Image;
+use EasyWeChat\Message\Text;
+use EasyWeChat\Message\Article;
+use GuzzleHttp\Client;
+
+/**
+ * 素材类,包括图片,图文
+ *
+ */
+class MaterialsController
+{
+    private $_errorMsg = [];
+
+    public function __construct($_param)
+    {
+        $this->param = $_param;
+        $this->app   = $_param['app'];
+        // 永久素材
+        $this->material = $this->app->material;
+        // 临时素材
+        $this->temporary = $this->app->material_temporary;
+        // 客服消息
+        $this->staff = $this->app->staff;
+    }
+
+    /**
+     * 上传客服二维码永久素材
+     */
+    public function upload_contact_cusomer_img($group_nick, $path)
+    {
+        v('upload_contact_cusomer_img:' . $group_nick . ' path:' . $path);
+
+        $result = $this->upload_materials($path);
+        v('upload_contact_cusomer_img_result');
+        v($result);
+        return $result;
+    }
+
+    /**
+     * 上传永久素材
+     */
+    public function upload_material_img($group_nick, $path)
+    {
+        v('upload_material_img:' . $group_nick . ' path:' . $path);
+
+        $result = $this->upload_materials($path);
+        v('upload_material_img_result');
+        v($result);
+        return $result;
+    }
+
+    /**
+     * 上传永久缩略图素材
+     */
+    public function upload_thumb_material_img($group_nick, $path)
+    {
+        v('upload_thumb_material_img:' . $group_nick . ' path:' . $path);
+
+        $result = $this->upload_thumb_materials($path);
+        v('upload_thumb_material_img_result');
+        v($result);
+        return $result;
+    }
+
+    /**
+     * 上传永久素材
+     * 可能有网络问题,最多尝试3次
+     */
+    public function upload_materials($path, $try_time = 0)
+    {
+        v('upload_materials:path:' . $path . ' try_time:' . $try_time);
+        $result = '';
+        if ($try_time > 2) {
+            return false;
+        }
+
+        try {
+            return $this->material->uploadImage($path);
+        } catch (\Exception $e) {
+            $this->_errorMsg['upload_materials'] = $e->getMessage();
+            v('upload_material_img_except:' . $e->getMessage());
+            return $this->upload_materials($path, $try_time + 1);
+        }
+
+    }
+
+    /**
+     * 上传永久缩量图素材
+     * 可能有网络问题,最多尝试3次
+     */
+    public function upload_thumb_materials($path, $try_time = 0)
+    {
+        v('upload_thumb_materials:path:' . $path . ' try_time:' . $try_time);
+        $result = '';
+        if ($try_time > 2) {
+            return false;
+        }
+
+        try {
+            return $this->material->uploadThumb($path);
+        } catch (\Exception $e) {
+            v('upload_material_img_except:' . $e->getMessage());
+            return $this->upload_thumb_materials($path, $try_time + 1);
+        }
+
+    }
+
+    /**
+     * 获取永久素材
+     */
+    public function get_materials(Request $request)
+    {
+        $result = $this->material->lists('image', $offset = 0, $count = 5);
+        v($result);
+    }
+
+    /**
+     * 获取永久素材列表
+     */
+    public function get_material_img_list()
+    {
+        $result = $this->material->lists('image', 0, 10);
+        v($result);
+        return $result;
+    }
+
+    /**
+     * 获取永久图文素材
+     */
+    public function get_material_resource($media_id)
+    {
+        $result = $this->material->get($media_id);
+        v($result);
+        return $result;
+    }
+
+    /**
+     * 检查素材是否存在了,如果存在则返回素材实体,不存在则上传
+     */
+    public function check_material_img_exist($name)
+    {
+        v('origin_name:' . $name);
+        $img_list = $this->material->lists('image', 0, 100);
+        foreach ($img_list->item as $img) {
+            v($img);
+            v('material_name:' . $img['name']);
+            if ($img['name'] == $name) {
+                return $img;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 得到素材实体
+     */
+    public function get_material_img($name)
+    {
+        $mateial_img = $this->check_material_img_exist($name);
+        if (empty($mateial_img)) {
+            // 可能会网络问题
+            try {
+                $update_img = $this->upload_materials($path);
+            } catch (\Exception $e) {
+                v('upload_material_img_except:' . $e->getMessage());
+            }
+
+            return $update_img;
+        } else {
+            return $mateial_img;
+        }
+    }
+
+    /**
+     * 推送图片消息
+     * 48小时外:response out of time limit or subscription is canceled hint: [kSi1ga0292ge30]
+     */
+    public function send_image($media_id, $openid)
+    {
+        v('send_image_media_id:' . $media_id . ' openid:' . $openid);
+        try {
+            $message = new Image(['media_id' => $media_id]);
+            $result  = $this->staff->message($message)->to($openid)->send();
+            v('send_image_result:');
+            v($result);
+        } catch (\Exception $e) {
+            v('send_image_except:' . $openid . ' info:' . $e->getMessage());
+        }
+    }
+
+    /**
+     * 上传临时素材
+     */
+    public function upload_temporary_materials($path)
+    {
+        v('upload_temporary_materials:' . $path);
+//     	$path = "/imgs/common/luqu_notice.png";
+        $upload_result = $this->temporary->uploadImage($path);
+        v('upload_temporary_materials_result:');
+        v($upload_result);
+        $media_id = isset($upload_result->media_id) ? $upload_result->media_id : '';
+        v('media_id:' . $media_id);
+        return $media_id;
+    }
+
+    /**
+     * 上传临时素材
+     */
+    public function upload_temporary_thumb_materials($path)
+    {
+        v('upload_temporary_materials:' . $path);
+        //     	$path = "/imgs/common/luqu_notice.png";
+        $upload_result = $this->temporary->uploadThumb($path);
+        v('upload_temporary_materials_result:');
+        v($upload_result);
+        $media_id = isset($upload_result->media_id) ? $upload_result->media_id : '';
+        v('media_id:' . $media_id);
+        return $media_id;
+    }
+
+    /**
+     * 获取临时素材
+     */
+    public function get_temporary_single_material($mediaId)
+    {
+        v('get_temporary_single_material:' . $mediaId);
+        $content = $this->temporary->getStream($mediaId);
+        v('get_temporary_single_material_content:' . $content);
+        file_put_contents(public_path("/admin/imgs/download/luqu_notice.png"));
+        return empty($content) ? false : true;
+    }
+
+    /**
+     * 上传单篇图文
+     */
+    public function upload_article($data)
+    {
+        // 上传单篇图文
+        $article = new Article([
+            'title'              => $data['title'],
+            'thumb_media_id'     => $data['thumb_media_id'],
+            "show_cover_pic"     => $data['show_cover_pic'],
+            "author"             => $data['author'],
+            "digest"             => $data['digest'],
+            "content"            => $data['content'],
+            "content_source_url" => $data['content_source_url'],
+            //...
+        ]);
+        v('$article');
+        v($article);
+        $result = $this->material->uploadArticle($article);
+        v('upload_article_result:' . json_encode($result));
+
+        return $result;
+    }
+
+
+    /**
+     * 上传临时素材
+     */
+    public function upload_test($appid, $path)
+    {
+        v('upload_test');
+        $upload_result = $this->material->uploadArticalMediaImage($path);
+        v('upload_article_result:' . json_encode($upload_result));
+        die();
+
+        $component_access_token = app('wechat_op')->access_token->getToken();
+        v('$component_access_token:' . $component_access_token);
+        // 先上传缩量图
+        try {
+            $client      = new Client();
+            $form_params = [
+                "media" => 'https://cdn-novel.iycdm.com/document_covers/2018111201/1383.jpg?x-oss-process=image/resize,w_900/format,jpg',
+//     		   "media"=>$path
+            ];
+            v($form_params);
+            $upload_res = $client->request("post", "https://api.weixin.qq.com/cgi-bin/media/add_material?type=thumb&access_token=" . $component_access_token,
+                ['json' => $form_params, 'connect_timeout' => 3]
+            )->getBody()->getContents();
+            $upload_res = json_decode($upload_res, true);
+            v('$upload_res:' . $appid);
+            v($upload_res);
+
+        } catch (\Exception $e) {
+            v('upload_test_ept_appid:' . $appid . ' ept:' . $e->getMessage());
+        }
+
+        return $upload_res;
+    }
+
+    /**
+     * 上传多篇图文,框架没方法自己写。
+     */
+    public function upload_articles($appid, $articles)
+    {
+        $articles = objectToArray(json_decode($articles));
+        v('articles');
+        v($articles);
+
+        $component_access_token = app('wechat_op')->access_token->getToken();
+        v('$component_access_token:' . $component_access_token);
+        $last_articles = [];
+        $result        = [];
+        try {
+            foreach ($articles as $article) {
+                $last_articles[] = new Article([
+                    'title'              => $article['title'],
+                    'thumb_media_id'     => $article['thumb_media_id'],
+                    "show_cover_pic"     => $article['show_cover_pic'],
+                    "author"             => $article['author'],
+                    "digest"             => $article['digest'],
+                    "content"            => $article['content'],
+                    "content_source_url" => $article['content_source_url'],
+                    //...
+                ]);
+
+                // 测试是否只有1本书可以上传?
+//     			$last_articles = new Article([
+//     					'title' => $article['title'],
+//     					'thumb_media_id' => $article['thumb_media_id'],
+//     					"show_cover_pic" => $article['show_cover_pic'],
+//     					"author" => $article['author'],
+//     					"digest" => $article['digest'],
+//     					"content" => $article['content'],
+//     					"content_source_url" =>$article['content_source_url'],
+//     					//...
+//     			]);break;
+            }
+            v('upload_article_before:' . json_encode($last_articles));
+            $result = $this->material->uploadArticle($last_articles);
+            v('upload_article_result:' . json_encode($result));
+            $media_id = isset($result->media_id) ? $result->media_id : '';
+            v('upload_article_media_id:' . $media_id);
+        } catch (\Exception $e) {
+            $this->_errorMsg['upload_articles'] = $e->getMessage();
+            v('upload_articles_ept:' . $e->getMessage());
+        }
+
+        return $result;
+
+//     	$upload_res = '';
+//     	try{
+//     			$client = new Client();
+//     			$form_params = [
+// 	    			"touser"=>'oAcqg1LRHNKN2jaEkJ5v56HOwPEQ',
+// 	    			"mpnews"=>json_encode(['media_id'=>$media_id]),
+// 	    			"msgtype"=>"mpnews",
+// 	    			"send_ignore_reprint"=>0
+//     			];
+//     			v($form_params);
+//     			$upload_res = $client->request("post","https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=".$component_access_token,
+//     					['json'=>$form_params,'connect_timeout' => 3]
+//     			)->getBody()->getContents();
+//     			$upload_res = json_decode($upload_res,true);
+//     			v('$upload_res:'.$appid);v($upload_res);
+
+//     	}catch(\Exception $e){
+//     			v('upload_articles_ept_appid:'.$appid.' ept:'.$e->getMessage());
+//     	}
+
+//     	v('upload_articles_result:'.json_encode($upload_res));
+//     	return $upload_res;
+    }
+
+
+    public function getErrorMsg($type, $msg = '')
+    {
+        return getProp($this->_errorMsg, $type, $msg);
+    }
+}

+ 132 - 0
app/Http/Controllers/Wechat/Menu/MenusController.php

@@ -0,0 +1,132 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Menu;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use Cookie;
+
+/**
+ * 菜单管理
+ * TODO:自动添加公众号默认菜单
+ * @author zhoulingjie
+ *
+ */
+class MenusController
+{
+
+    public function __construct($_param)
+    {
+    	$this->param = $_param;
+		$this->app = $_param['app'];
+		$this->WechatApi = $_param['WechatApi'];
+    }
+    
+    /**
+     * menu/set_menu
+     * @param Request $request
+     */
+    public function set_menu()
+    {
+        $add_menus = $this->WechatApi->get_official_account_menus($this->param['gzh_app_id']);
+
+        v('set_menu_start');
+        if(empty($add_menus)){
+            v('set_menu_invalid_param');
+            return false;
+        }
+        $menu = $this->app->menu;
+
+        v('set_menu:');
+        v($add_menus);
+        try{
+            // 先删再加,解决菜单缓存问题
+            $del_menu = $menu->destroy();
+            v('del_menu');v($del_menu);
+            $add_menu = $menu->add($add_menus);
+            v('add_menu');v($add_menu);
+
+            //过滤iOS 充值入口
+            $ios_menus = [];
+            foreach ($add_menus as $k=>$_menu)
+            {
+                if(isset($_menu['sub_button']))
+                {
+                    $name = $_menu['name'];
+                    $sub_button = [];
+                    foreach($_menu['sub_button'] as $j=>$_sub_menu)
+                    {
+                        if(!(isset($_sub_menu['url']) && strstr($_sub_menu['url'],'/pay'))) $sub_button[] = $_sub_menu;
+                    }
+                    if($sub_button)  $ios_menus[] = compact('name','sub_button');
+                }else{
+                    if(!(isset($_menu['url']) && strstr($_menu['url'],'/pay'))) $ios_menus[] = $_menu;
+                }
+            }
+            $res = $menu->add($ios_menus,['client_platform_type'=>"1"]);
+            v('add_menu');v($res);
+        }catch(\Exception $e){
+            v('set_menu_ept:'.$e->getMessage());
+        }
+
+        v('set_menu_end');
+    }
+    
+    /**
+     * menu/set_menu
+     * @param Request $request
+     */
+    public function set_menu2()
+    {
+    	v('set_menu_start');
+    	$menu = $this->app->menu;
+    	$base_url = 'http://site'.$this->param['distribution_channel_id'].'.'.$this->param['distribution_menu_host'];
+    	 
+    	$buttons =
+        [
+	    	[
+		    	"type" => "view",
+		    	"name" => "继续阅读",
+		    	"url"  => $base_url.'continue',
+	    	],
+	    	[
+		    	"type" => "view",
+		    	"name" => "书城首页",
+		    	"url"  => $base_url,
+	    	],
+	    	[
+		    	"name"       => "用户中心",
+		    	"sub_button" => 
+		    	  [
+			    	[
+				    	"type" => "view",
+				    	"name" => "个人中心",
+				    	"url"  => $base_url.'user',
+			    	],
+			    	[
+				    	"type" => "view",
+				    	"name" => "阅读记录",
+				    	"url"  => $base_url.'bookshelf',
+			    	],
+			    	[
+				    	"type" => "view",
+				    	"name" => "我要充值",
+				    	"url"  => $base_url.'paycenter',
+			    	],
+			    	[
+				    	"type" => "click",
+				    	"name" => "联系客服",
+				    	"key"  => 'contact_customer',
+			    	],
+			      ],
+		      ],
+    	];
+    	v('set_menu:');
+    	v($buttons);
+    	v($menu->add($buttons));
+    	v('set_menu_end');
+    }
+    
+}

+ 112 - 0
app/Http/Controllers/Wechat/Oauth/UserOauthsController.php

@@ -0,0 +1,112 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Oauth;
+
+use App\Http\Controllers\WechatController;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use WechatOP;
+use App\Http\Controllers\Wechat\Api\WechatInnerApisController;
+
+/**
+ * 授权完,回传openid到redirect_url去
+ * @author zhoulingjie
+ *
+ */
+class UserOauthsController extends WechatController
+{
+    public function __construct()
+    {    	
+    	// TODO 加密
+    	$this->gzh_app_id = $_REQUEST['gzh_app_id'];
+		parent::__construct($this->gzh_app_id);
+    }
+    
+    /**
+     * 本平台作为第三方授权
+     * 授权完后传openid到目标站点设置(cookie不能跨域)
+     * 
+     */
+    public function oauth_callback(Request $request)
+    {
+    	v('oauth_callback_start');
+    	$user = array();
+    	try{
+	    	if(env('DEVELOP_MODE') == 'local'){
+		    	$user['openid'] = 'oKXfNs_4SvNpMZWOzNdJklWFEn8Y';
+	    	}else{
+		        // 获取 OAuth 授权结果用户信息
+	    		v('oauth_callback_get_user');
+				$user = $this->app->oauth->setRequest($request)->user();
+				v('simple_user_data:');v($user);
+				$user = $user->original;  
+				
+				$data = array();
+				$data['appid'] =$this->gzh_app_id;
+				$data['distribution_channel_id'] = isset($this->official_account['distribution_channel_id'])?$this->official_account['distribution_channel_id']:null;
+				$data['openid'] = $user['openid'];
+				// 如果unionid没有取到,则自定义1个
+				$my_unionid = $data['openid'];
+				v('my_unionid:'.$my_unionid);
+				$user['unionid'] = $data['unionid'] = isset($user['unionid'])?$user['unionid']:$my_unionid;
+				// 去重在后台判断,支付号不需要保存用户
+				if(isset($this->official_account['official_account_type']) && $this->official_account['official_account_type'] =='third_platform_pay'){
+					v('oauth_third_platform_pay:'.$data['openid']);
+				}else{
+					// 性能考虑,在h5那边保存用户
+					v('to_save_data');v($data);
+// 					$this->WechatApi->save_login_wx_user($data);
+				}
+				
+	    	}
+    	}
+    	catch(\Exception $e){
+    		v('oauth_callback_ept:'.$e->getMessage());
+    	}
+    	
+    	$redirect_url = $request->get('redirect_url');
+    	v('oauth_callback_direct_url0:'.$redirect_url);
+    	$openid = isset($user['openid'])?$user['openid']:'';
+    	$unionid = isset($user['unionid'])?$user['unionid']:'';
+    	if(strpos($redirect_url,'zsypay') > -1){
+//     		v('has zsypay');
+    	}else{
+    		$redirect_url = urldecode($redirect_url);
+    	}
+    	if(strpos($redirect_url,'?') > -1){
+    		$redirect_url = $redirect_url.'&openid='.$openid.'&unionid='.$unionid;
+    	}else{
+    		$redirect_url = $redirect_url.'?openid='.$openid.'&unionid='.$unionid;
+    	}
+    	
+    	v('oauth_callback_direct_url:'.$redirect_url);
+
+        return redirect($redirect_url);
+    }
+   
+    /**
+     * ydy.wangluogudu.com/user_oauth?gzh_app_id=wx6916de1267c67d50&redirect_url=http%3a%2f%2fydy.wangluogudu.com%2foauth%2fshow_openid%3fgzh_app_id%3dwx6916de1267c67d50
+     * 获取用户信息
+     */
+    public function user_oauth(Request $request)
+    {
+    	v('third_check_user_auth_start');
+    	$params = array();
+    	$params['gzh_app_id'] = $request->get("gzh_app_id");
+    	$params['redirect_url'] = $request->get("redirect_url");
+    	$this->options['oauth']['callback'] .= '?'.http_build_query($params);
+    	// options改变了,这个app对象必须重新赋值
+    	$this->app = WechatOP::app($this->options);
+    	v($this->options['oauth']['callback']);
+    	return $this->app->oauth->setRequest($request)->redirect();
+    }
+    
+    public function show_openid(Request $request)
+    {
+    	echo '您的openid:'.$request->get('openid');
+    	die();
+    }
+
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 716 - 0
app/Http/Controllers/Wechat/OfficialAccount/OfficialInteractiveEventController.php


+ 20 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/ForceSubscribeUsersTransformer.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+class ForceSubscribeUsersTransformer
+{
+    public function transform($forceSubscribeUsers){
+        return [
+            'uid'       =>  $forceSubscribeUsers->uid,
+            'official_account_id'       =>  $forceSubscribeUsers->official_account_id,
+            'distribution_channel_id'       =>  $forceSubscribeUsers->distribution_channel_id,
+            'appid'       =>  $forceSubscribeUsers->appid,
+            'openid'   =>  $forceSubscribeUsers->openid,
+            'is_subscribed'   => $forceSubscribeUsers->is_subscribed,
+            'subscribe_time' => $forceSubscribeUsers->subscribe_time,
+            'unsubscribe_time' => $forceSubscribeUsers->unsubscribe_time,
+            'last_interactive_time' => $forceSubscribeUsers->last_interactive_time,
+        ];
+    }
+}

+ 34 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialAccountTransformer.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+class OfficialAccountTransformer
+{
+    public function transform($officialAccount){
+        return [
+            'id'       =>  isset($officialAccount->id) ? $officialAccount->id : "",
+            'name'       =>  isset($officialAccount->name) ? $officialAccount->name : "",
+            'nickname'       =>  isset($officialAccount->nickname) ? $officialAccount->nickname : "",
+            'alias'       =>  isset($officialAccount->alias) ? $officialAccount->alias : "",
+            'head_img'       =>  isset($officialAccount->head_img) ? $officialAccount->head_img : "",
+            'appid'   =>  isset($officialAccount->appid) ? $officialAccount->appid : "",
+            'appsecret'   => isset($officialAccount->appsecret) ? $officialAccount->appsecret : "",
+            'verify_txt' => isset($officialAccount->verify_txt) ? $officialAccount->verify_txt : "",
+            'is_auth' => isset($officialAccount->is_auth) ? $officialAccount->is_auth : 1,
+            'service_type_info' => isset($officialAccount->service_type_info) ? $officialAccount->service_type_info : "",
+            'subscribe_top_num' => isset($officialAccount->subscribe_top_num) ? $officialAccount->subscribe_top_num : 0,
+            'subscribe_day_maximum' => isset($officialAccount->subscribe_day_maximum) ? $officialAccount->subscribe_day_maximum : 0,
+            'distribution_channel_id' => isset($officialAccount->distribution_channel_id) ? $officialAccount->distribution_channel_id : "",
+            'qrcode_url' => isset($officialAccount->qrcode_url) ? $officialAccount->qrcode_url : "",
+            'principal_name' => isset($officialAccount->principal_name) ? $officialAccount->principal_name : "",
+            'func_info' => isset($officialAccount->func_info) ? $officialAccount->func_info : "",
+            'authorizer_refresh_token' => isset($officialAccount->authorizer_refresh_token) ? $officialAccount->authorizer_refresh_token : "",
+            'cancel_auth_time' => isset($officialAccount->cancel_auth_time) ? $officialAccount->cancel_auth_time : "",
+            'official_account_type' => isset($officialAccount->official_account_type) ? $officialAccount->official_account_type : "",
+            'verify_type_info' => isset($officialAccount->verify_type_info) ? $officialAccount->verify_type_info : "",
+            'is_enabled' => isset($officialAccount->is_enabled) ? $officialAccount->is_enabled : 1,
+            'todayForceSubscribeUsers' => isset($officialAccount->todayForceSubscribeUsers) ? $officialAccount->todayForceSubscribeUsers : 0,
+            'allForceSubscribeUsers' => isset($officialAccount->allForceSubscribeUsers) ? $officialAccount->allForceSubscribeUsers : 0,
+        ];
+    }
+}

+ 28 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialImgtextUrlsTransformer.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+
+class OfficialImgtextUrlsTransformer
+{
+    public function transform($officialImgtextUrls){
+        return [
+            'id'       =>  $officialImgtextUrls->id,
+            'content_source_url'       =>  $officialImgtextUrls->content_source_url,
+            'img_url'       =>  $officialImgtextUrls->img_url,
+            'created_at'       =>  $officialImgtextUrls->created_at,
+            'book_name'       =>  $officialImgtextUrls->book_name,
+            'chapter_sequence'       =>  $officialImgtextUrls->chapter_sequence,
+            'title'       =>  $officialImgtextUrls->title,
+            'send_orders_id'       =>  $officialImgtextUrls->send_orders_id,
+            'send_orders_name'       =>  $officialImgtextUrls->send_orders_name,
+            'send_orders_url'       =>  $officialImgtextUrls->send_orders_url,
+            // 'content'       =>  $officialImgtextUrls->content,
+            // 'author'       =>  $officialImgtextUrls->author,
+            // 'digest'       =>  $officialImgtextUrls->digest,
+            // 'gzh_app_id'       =>  $officialImgtextUrls->gzh_app_id,
+            // 'thumb_media_id'       =>  $officialImgtextUrls->thumb_media_id,
+            // 'distribution_channel_id'       =>  $officialImgtextUrls->distribution_channel_id,
+        ];
+    }
+}

+ 15 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialInteractiveEventTextTransformer.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+class OfficialInteractiveEventTextTransformer
+{
+    public function transform($officialInteractiveTextEvent){
+        return [
+            'title'       =>  $officialInteractiveTextEvent->text->book_name,
+            'description'       =>  $officialInteractiveTextEvent->text->intro,
+            'url'       =>  $officialInteractiveTextEvent->text->url,
+            'image'       =>  $officialInteractiveTextEvent->text->cover,
+        ];
+    }
+}

+ 12 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialInteractiveEventTransformer.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+class OfficialInteractiveEventTransformer
+{
+    public function transform($officialInteractiveEvent){
+        return [
+            'text'       =>  $officialInteractiveEvent->text,
+        ];
+    }
+}

+ 15 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/OfficialMenuTransformer.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+class OfficialMenuTransformer
+{
+    public function transform($officialMenu){
+        return [
+            'type'       =>  $officialMenu->type,
+            'name'       =>  $officialMenu->name,
+            'url'       =>  $officialMenu->url,
+            'sub_button'       =>  $officialMenu->sub_button,
+        ];
+    }
+}

+ 18 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/WechatPublicTemplatesTransformer.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+class WechatPublicTemplatesTransformer
+{
+    public function transform($wechatPublicTemplates){
+        return [
+            'common_template_id'       =>  $wechatPublicTemplates->common_template_id,
+            'title'       =>  $wechatPublicTemplates->title,
+            'primary_industry'       =>  $wechatPublicTemplates->primary_industry,
+            'deputy_industry'       =>  $wechatPublicTemplates->deputy_industry,
+            'content'       =>  $wechatPublicTemplates->content,
+            'example'       =>  $wechatPublicTemplates->example,
+            
+        ];
+    }
+}

+ 33 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/WechatTemplatesMsgTransformer.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+class WechatTemplatesMsgTransformer
+{
+    public function transform($wechatTemplatesMsg){
+        return [
+            'id'       =>  $wechatTemplatesMsg->id,
+            'template_id'       =>  $wechatTemplatesMsg->template_id,
+            'name'       =>  $wechatTemplatesMsg->name,
+            'send_time'       =>  $wechatTemplatesMsg->send_time,
+            'template_content'       =>  $wechatTemplatesMsg->template_content,
+            'redirect_url'       =>  $wechatTemplatesMsg->redirect_url,
+            'appid'       =>  $wechatTemplatesMsg->appid,
+            'status'       =>  $wechatTemplatesMsg->status,
+            'remark'       =>  $wechatTemplatesMsg->remark,
+            'distribution_channel_id'       =>  $wechatTemplatesMsg->distribution_channel_id,
+            'subscribe_time'       =>  $wechatTemplatesMsg->subscribe_time,
+            'sex'       =>  $wechatTemplatesMsg->sex,
+            'balance'       =>  $wechatTemplatesMsg->balance,
+            'order_type'       =>  $wechatTemplatesMsg->order_type,
+            'category_id'       =>  $wechatTemplatesMsg->category_id,
+            'example'       => $wechatTemplatesMsg->wechatTemplates->wechatPublicTemplates->example,
+            'common_template_id'       => $wechatTemplatesMsg->wechatTemplates->wechatPublicTemplates->common_template_id,
+            'title'       => $wechatTemplatesMsg->wechatTemplates->wechatPublicTemplates->title,
+            'primary_industry'       => $wechatTemplatesMsg->wechatTemplates->wechatPublicTemplates->primary_industry,
+            'deputy_industry'       => $wechatTemplatesMsg->wechatTemplates->wechatPublicTemplates->deputy_industry,
+            'content'       => $wechatTemplatesMsg->wechatTemplates->wechatPublicTemplates->content,
+            'example'       => $wechatTemplatesMsg->wechatTemplates->wechatPublicTemplates->example,
+        ];
+    }
+}

+ 22 - 0
app/Http/Controllers/Wechat/OfficialAccount/Transformers/WechatTemplatesTransformer.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\OfficialAccount\Transformers;
+
+class WechatTemplatesTransformer
+{
+    public function transform($wechatTemplates){
+        return [
+            'appid'       =>  $wechatTemplates->appid,
+            'common_template_id'       =>  $wechatTemplates->common_template_id,
+            'template_id'       =>  $wechatTemplates->template_id,
+            // 'name'       =>  $wechatTemplates->WechatTemplateMsg->name,
+            // 'send_time'       =>  $wechatTemplates->WechatTemplateMsg->send_time,
+            // 'template_content'       =>  $wechatTemplates->WechatTemplateMsg->template_content,
+            // 'redirect_url'       =>  $wechatTemplates->WechatTemplateMsg->redirect_url,
+            // 'send_appid'       =>  $wechatTemplates->WechatTemplateMsg->send_appid,
+            // 'status'       =>  $wechatTemplates->WechatTemplateMsg->status,
+            // 'remark'       =>  $wechatTemplates->WechatTemplateMsg->remark,
+            // 'distribution_channel_id'       =>  $wechatTemplates->WechatTemplateMsg->distribution_channel_id,
+        ];
+    }
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 347 - 0
app/Http/Controllers/Wechat/Pay/PaysController.php


+ 35 - 0
app/Http/Controllers/Wechat/Qrcode/QrcodesController.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Qrcode;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use EasyWeChat\Foundation\Application;
+
+class QrcodesController
+{
+    public function __construct($_param)
+    {
+    	$this->param = $_param;
+		$this->app = $_param['app'];
+    }
+
+    public function create_qrcode($type='temporary',$sceneId)
+    {
+    	v('create_qrcode:'.$sceneId);
+    	$expire_seconds = 30*24*3600;// 过期时间
+    	if($type == 'temporary'){
+    		$result = $this->app->qrcode->temporary($sceneId, $expire_seconds);
+    	}else{
+    		$result = $this->app->qrcode->forever($sceneId);
+    	}
+    	
+		$ticket = $result->ticket;
+		$expireSeconds = $result->expire_seconds; // 有效秒数
+		$url = $this->app->qrcode->url($ticket);// 二维码地址
+		v('sceneId:'.$sceneId.' url:'.$url);
+
+		return $url;
+    }
+   
+}

+ 112 - 0
app/Http/Controllers/Wechat/Staff/NewsWorksController.php

@@ -0,0 +1,112 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Staff;
+
+use App\Http\Models\OfficialAccount;
+
+use App\Http\Requests;
+use App\Http\Controllers\WechatController;
+use App\Http\Controllers\Wechat\Staff\StaffsController;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use App\Http\Models\WechatUser;
+use App\Http\Models\WechatTemplateSendInfo;
+use App\Http\Models\WechatTemplateTask;
+use App\Http\Models\WechatTemplateOfficialInfo;
+use Redis;
+
+
+/**
+ * 图文模板消息定时发送类
+ * @author zhoulingjie
+ * cat /var/log/supervisord-logs/send_news_list* | grep '{"send_time":"2018-04-08 18' | wc -l
+ * cat /var/log/supervisord-logs/send* | grep '"receive_time":"2018-02-01 09' | wc -l
+ * cat /var/log/supervisord-logs/send* | grep '"receive_time":"2018-02-01 09:01' | wc -l
+ *
+ */
+class NewsWorksController extends WechatController
+{
+    
+    public function __construct($template_info)
+    {
+    	$this->template_info = $template_info;
+//     	v('$template_info');v($this->template_info);
+    	$this->gzh_app_id = isset($this->template_info['appid'])?$this->template_info['appid']:'';
+    	if(!empty($this->gzh_app_id)){
+    		parent::__construct($this->gzh_app_id);
+    		$this->Staff = new StaffsController($this->param);
+    	}else{
+    		v('template_work_appid is null');
+    	}
+    	$this->error_msg = '';
+    }
+    
+    /**
+     * 执行模板消息
+     */
+    public function do_work()
+    {
+    	if(empty($this->gzh_app_id)) return false;
+    	
+    	// 检查表数据
+    	$openid = $this->template_info['openid'];
+    	v('do_work_start_news:'.$openid);
+    	
+    	$appid = $this->template_info['appid'];
+    	$task_id = $this->template_info['task_id'];
+    	$type = $this->template_info['type'];
+    	$news_content = $this->template_info['news_content'];
+    	if(empty($openid) || empty($task_id) || empty($news_content)){
+    		v('invalid_null_task_id:'.$task_id.' openid:'.$openid);
+    		return false;
+    	}
+    	
+    	$template_task = $this->WechatApi->get_template_task($task_id);
+    	$status = isset($template_task['status']) ? $template_task['status']:'';
+    	if($status == 4){
+    		v('task_id_close:'.$task_id);
+    		return false;
+    	}
+    
+    	$this->send_one_template($openid,$news_content,$task_id);
+    	
+    	if($type == 'last_task' && $status != 3){
+    		//v('update_news_task_status:'.$task_id);
+    		$status = 3;
+    		$this->WechatApi->update_template_task_status($task_id, $status, $this->error_msg);
+    	}
+
+    }
+    
+    function send_one_template($openid,$datas,$task_id=''){
+    	try{
+    		$datas = objectToArray(json_decode($datas));
+    		$send_data = array();
+    		foreach($datas  as $no=> $data){
+    			foreach($data  as $_data){
+    				foreach($_data as $key=>$one_data){
+    					$send_data[$no][$key] = $one_data;
+    				}
+    			}
+    		}
+
+    		$to_send_data = array();
+    		$to_send_data['text'] = $send_data;
+            $result = '';
+    		$result = $this->Staff->batch_send_wechat_content($openid, $to_send_data);
+
+            if($task_id && is_object($result) && isset($result->errmsg) && $result->errmsg == 'ok')
+            {
+                Redis::sadd('cus_ok:'.$task_id,$openid);//记录真正发送成功数
+            }
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		v('send_openid_ept:oKtP5w5SeNDNPmN6qN-4llpVUPbw'.$openid.' info:'.$e->getMessage());
+    		$this->error_msg = $e->getMessage();
+    	}
+    }
+  
+    
+}

+ 148 - 0
app/Http/Controllers/Wechat/Staff/StaffsController.php

@@ -0,0 +1,148 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Staff;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use EasyWeChat\Message\News;
+use EasyWeChat\Message\Text;
+use EasyWeChat\Message\Image;
+
+/**
+ * 客服消息管理
+ * @author zhoulingjie
+ *
+ */
+class StaffsController
+{
+    
+    public function __construct($_param)
+    {
+		$this->app = $_param['app'];
+    }
+    
+    /**
+     * 批量消息推送
+     * 注意:text消息要被动回复出去
+     * @param Request $request
+     */
+    public function batch_send_wechat_content($openid,$to_send_datas,$reply_type='staff')
+    {
+    	v('batch_send_wechat_content_start:'.$openid);
+    	if(empty($to_send_datas) || empty($openid)) {
+    		v('batch_send_wechat_content_invalid_param:'.$openid);
+    		return '';
+    	}
+    	
+    	$result = '';
+    	foreach($to_send_datas as $send_type=>$to_send_data){
+    		if($send_type == 'image'){
+    			$this->send_wechat_content_image($openid, $to_send_data);
+    		}
+    		
+    		if($send_type == 'text'){
+    			$result = $this->send_wechat_content_text($openid, $to_send_data,$reply_type);
+    		}
+    	}
+
+    	return $result;
+    }
+    
+    /**
+     * 图片消息推送-->image
+     * @param Request $request
+     */
+    public function send_wechat_content_image($openid,$send_datas)
+    {
+    	$medis_id = isset($send_datas['media_id'])?$send_datas['media_id']:'';
+    	v('send_wechat_content_image_start:'.$openid.' medis_id:'.$medis_id);
+    	v($send_datas);
+    	if(empty($medis_id) || empty($openid) || empty($send_datas)) {
+    		v('send_wechat_content_image_invalid_param:'.$openid);
+    		return '';
+    	}
+    	v('before_send_datas');
+//     	v($send_datas);
+    
+    	$last_send_data = new Image(['media_id' => $medis_id]);
+    	 
+    	try{
+    		v('send_to_openid:'.$openid);
+    		if(!empty($last_send_data)){
+    			$result = $this->app->staff->message($last_send_data)->to($openid)->send();
+    			v($result);
+    		}
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		v('send_wechat_content_image_ept:'.$openid.' info:'.$e->getMessage());
+    	}
+    
+    	v('send_wechat_content_image_end:'.$openid);
+    	return '';
+    }
+    
+    /**
+     * 多图文消息推送-->text
+     * @param Request $request
+     */
+    public function send_wechat_content_text($openid,$send_datas,$reply_type='staff')
+    {
+//     	v('send_wechat_content_text_start:'.$openid);
+    	if(empty($openid) || empty($send_datas)) {
+    		v('send_wechat_content_text_invalid_param:'.$openid);
+    		return '';
+    	}
+//     	v('before_send_datas_text');
+//     	v($send_datas);
+
+    	$last_send_data = null;
+    	if(is_array($send_datas)){
+    		$news_tpls = array();
+    		$num = 0;
+    		foreach($send_datas  as $send_data){
+    			$news = new News([
+    					'title'       => $send_data['title'],
+    					'description' => $send_data['description'],
+    					'url'         => $send_data['url'],
+    					'image'       => $send_data['image'],
+    					// ...
+    					]);
+    			$news_tpls[] = $news;
+    			$num++;
+    			if($num >= 8) break;
+    		}
+    		$last_send_data = $news_tpls;
+//     		v('$news_tpls');v($news_tpls);
+    	}else{
+//     		v('$text');v($send_datas);
+    		$last_send_data = $send_datas;
+    	}
+    
+    	try{
+//     		v('send_to_openid:'.$openid);
+    		if(!empty($last_send_data)){
+    			// 直接返回
+    			if($reply_type == 'direct_return'){
+    				v('direct_return:'.$openid);
+    				return $last_send_data;
+    			}else{
+    				$result = $this->app->staff->message($last_send_data)->to($openid)->send();
+    				return $result;
+    			}
+                //v('staff:send:result:');v($result);
+
+    		}
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		v('send_wechat_content_text_ept:'.$openid.' info:'.$e->getMessage());
+    	}
+    	 
+//     	v('send_wechat_content_text_end:'.$openid);
+    	return '';
+    }
+   
+}

+ 101 - 0
app/Http/Controllers/Wechat/Staff/TextsWorksController.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Staff;
+
+use App\Http\Models\OfficialAccount;
+
+use App\Http\Requests;
+use App\Http\Controllers\WechatController;
+use App\Http\Controllers\Wechat\Staff\StaffsController;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use App\Http\Models\WechatUser;
+use App\Http\Models\WechatTemplateSendInfo;
+use App\Http\Models\WechatTemplateTask;
+use App\Http\Models\WechatTemplateOfficialInfo;
+use Redis;
+
+
+/**
+ * 自定义消息定时发送类
+ * @author zhoulingjie
+ *
+ */
+class TextsWorksController extends WechatController
+{
+    
+    public function __construct($template_info)
+    {
+    	$this->template_info = $template_info;
+//     	v('$template_info');v($this->template_info);
+    	$this->gzh_app_id = isset($this->template_info['appid'])?$this->template_info['appid']:'';
+    	if(!empty($this->gzh_app_id)){
+    		parent::__construct($this->gzh_app_id);
+    		$this->Staff = new StaffsController($this->param);
+    	}else{
+    		v('template_work_appid is null');
+    	}
+    	$this->error_msg = '';
+    }
+    
+    /**
+     * 执行模板消息
+     */
+    public function do_work()
+    {
+    	if(empty($this->gzh_app_id)) return false;
+    	v('do_work_start_text');
+    	
+    	// 检查表数据
+    	$openid = $this->template_info['openid'];
+    	$appid = $this->template_info['appid'];
+    	$task_id = $this->template_info['task_id'];
+    	$type = $this->template_info['type'];
+    	$content = $this->template_info['content'];
+    	if(empty($openid) || empty($task_id) || empty($content)){
+    		v('invalid_null_task_id:'.$task_id.' openid:'.$openid);
+    		return false;
+    	}
+    	
+    	$template_task = $this->WechatApi->get_template_task($task_id);
+    	$status = isset($template_task['status']) ? $template_task['status']:'';
+    	v('task_id:'.$task_id.' status:'.$status);
+    	if($status == 4){
+    		v('task_id_close:'.$task_id);
+    		return false;
+    	}
+    
+    	$this->send_one_template($openid,$content,$task_id);
+    	
+    	if($type == 'last_task' && $status != 3){
+    		v('update_text_task_status:'.$task_id);
+    		$status = 3;
+    		$this->WechatApi->update_template_task_status($task_id, $status, $this->error_msg);
+    	}
+
+    	v('update_text_task_end:'.$task_id);
+
+    }
+    
+    function send_one_template($openid,$content,$task_id=''){
+    	try{
+    		v('send_text_to_openid:'.$openid);
+//     		v('content:');v($content);
+
+    		$result = $this->param['app']->staff->message($content)->to($openid)->send();
+            if($task_id && is_object($result) && isset($result->errcode) && $result->errcode == 0)
+            {
+                Redis::sadd('cus_ok:'.$task_id,$openid);//记录真正发送成功数
+            }
+//     		v('send_text_result');v($result);
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		v('send_openid_ept:'.$openid.' info:'.$e->getMessage());
+    		$this->error_msg = $e->getMessage();
+    	}
+    }
+  
+    
+}

+ 36 - 0
app/Http/Controllers/Wechat/Statistic/NewsStatisticsController.php

@@ -0,0 +1,36 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Hardy
+ * Date: 2020/7/14
+ * Time: 15:28
+ */
+
+
+namespace App\Http\Controllers\Wechat\Statistic;
+
+
+use App\Http\Controllers\WechatController;
+
+class NewsStatisticsController extends WechatController
+{
+    public function __construct($_param)
+    {
+        $this->$_param =$_param;
+        $this->gzh_app_id = isset($this->template_info['appid'])?$this->$_param['appid']:'';
+        if(!empty($this->gzh_app_id)){
+            parent::__construct($this->gzh_app_id);
+        }else{
+            v('appid is null');
+        }
+        $this->error_msg = '';
+    }
+
+    public function test()
+    {
+        $url = $this->app->url;
+        $shortUrl = $url->shorten('https://m.ycsd.cn/');
+        dd($shortUrl);
+    }
+
+}

+ 47 - 0
app/Http/Controllers/Wechat/Statistic/StatisticsController.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Statistic;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use EasyWeChat\Message\News;
+
+/**
+ * 公众号统计信息
+ * @author zhoulingjie
+ *
+ */
+class StatisticsController
+{
+    
+    public function __construct($_param)
+    {
+    	$this->param = $_param;
+		$this->app = $_param['app'];
+    }
+    
+    /**
+     * 得到公众号统计信息
+     * @param Request $request
+     */
+    public function get_gzh_statistics($from_date,$to_date,$gzh_app_id)
+    {
+    	$result = array('code'=>1,'msg'=>'','data'=>array());
+    	try{
+    		$result['data']['user_cumulate'] = $this->app->stats->userCumulate($from_date, $to_date);//获取累计用户数据, 最大时间跨度:7;
+    		$result['data']['user_summary'] = $this->app->stats->userSummary($from_date, $to_date);//获取用户增减数据, 最大时间跨度:7;
+    		v($result);
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		$result['code'] = 0;
+    		$result['msg'] = $e->getMessage();
+    		v('get_gzh_statistics_ept:'.$gzh_app_id.' info:'.$e->getMessage());
+    	}
+    	
+    	return $result;
+    }
+    
+}

+ 355 - 0
app/Http/Controllers/Wechat/Template/TemplateBasesController.php

@@ -0,0 +1,355 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Template;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use App\Http\Models\WechatTemplateBaseInfo;
+use App\Http\Models\WechatTemplateOfficialInfo;
+
+/**
+ * 模板消息基础表,用于自动创建公众号模板行业和模板消息
+ * @author zhoulingjie
+ *
+ */
+class TemplateBasesController
+{
+    
+    public function __construct($_param)
+    {
+//     	v('$this->param');v($_param);
+    	$this->param = $_param;
+		$this->app = $_param['app'];
+		$this->WechatApi = $_param['WechatApi'];
+    }
+    
+    /**
+     * 自动开通模板行业、添加对应模板消息
+     * @param Request $request
+     */
+    public function auto_set_dustry_templates()
+    {
+    	v('auto_set_dustry_templates_before');
+//     	$this->get_industry();
+//     	$this->get_private_templates();
+    	
+    	// TODO 从group取,设置行业
+    	v('industry_first_id:'.$this->param['group']['industry_first_id'].' industry_second_id:'.$this->param['group']['industry_second_id']);
+    	$this->set_template_dustry($this->param['group']['industry_first_id'],$this->param['group']['industry_second_id']);
+    	// 设置模板消息
+    	$this->set_template_notices();
+    	
+    	v('auto_set_dustry_templates_after');
+    }
+    
+    /**
+     * 设置行业(传id) 默认 2,37
+     * 
+IT科技	互联网/电子商务	1
+IT科技	IT软件与服务	2
+IT科技	IT硬件与设备	3
+IT科技	电子技术	4
+IT科技	通信与运营商	5
+IT科技	网络游戏	6
+金融业	银行	7
+金融业	基金|理财|信托	8
+金融业	保险	9
+餐饮	餐饮	10
+酒店旅游	酒店	11
+酒店旅游	旅游	12
+运输与仓储	快递	13
+运输与仓储	物流	14
+运输与仓储	仓储	15
+教育	培训	16
+教育	院校	17
+政府与公共事业	学术科研	18
+政府与公共事业	交警	19
+政府与公共事业	博物馆	20
+政府与公共事业	公共事业|非盈利机构	21
+医药护理	医药医疗	22
+医药护理	护理美容	23
+医药护理	保健与卫生	24
+交通工具	汽车相关	25
+交通工具	摩托车相关	26
+交通工具	火车相关	27
+交通工具	飞机相关	28
+房地产	建筑	29
+房地产	物业	30
+消费品	消费品	31
+商业服务	法律	32
+商业服务	会展	33
+商业服务	中介服务	34
+商业服务	认证	35
+商业服务	审计	36
+文体娱乐	传媒	37
+文体娱乐	体育	38
+文体娱乐	娱乐休闲	39
+印刷	印刷	40
+其它	其它	41
+     */
+    public function set_template_dustry($industryId1=2,$industryId2=37)
+    {
+    	if(empty($industryId1)) $industryId1 = 2;
+    	if(empty($industryId2)) $industryId2 = 37;
+    	v('set_template_dustry_start,industryId1:'.$industryId1.' industryId2:'.$industryId2);
+    
+    	//修改账号所属行业
+    	try{
+    		$set_dustry_res = $this->app->notice->setIndustry($industryId1, $industryId2);
+    		v('set_dustry_res');v($set_dustry_res);
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		v('set_template_dustry_ept:'.$e->getMessage());
+    	}
+    	
+    	v('set_template_dustry_end');
+    }
+   
+    public function set_template_dustry_res($industryId1=2,$industryId2=37)
+    {
+    	if(empty($industryId1)) $industryId1 = 2;
+    	if(empty($industryId2)) $industryId2 = 37;
+    	v('set_template_dustry_res_start,industryId1:'.$industryId1.' industryId2:'.$industryId2);
+    
+    	//修改账号所属行业
+    	try{
+    		$set_dustry_res = $this->app->notice->setIndustry($industryId1, $industryId2);
+    		v('set_dustry_res');v($set_dustry_res);
+    		return 1;
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		$error_msg = $e->getMessage();
+    		v('set_template_dustry_res_ept:'.$error_msg);
+    		return 2;
+    	}
+    	 
+    }
+    
+    /**
+     * 得到行业列表
+     */
+    public function get_industry()
+    {
+    	try{
+    		v('get_industry_start');
+    		$industrys = $this->app->notice->getIndustry();
+    		v('industrys');v($industrys);
+    		return $industrys;
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		$error_msg = $e->getMessage();
+    		v('get_industry_ept:appid:'.$this->param['gzh_app_id'].' ept:'.$error_msg);
+    		if(strpos($error_msg,'api forbidden') !== false){
+    			return 4;
+    		}
+    		return '';
+    	}
+    }
+    
+    /**
+     * 得到已添加的模板消息列表
+     */
+    public function get_private_templates()
+    {
+    	try{
+    		v('get_private_templates_start');
+    		$private_templates = $this->app->notice->getPrivateTemplates();
+    		v('private_templates');v($private_templates);
+            return $private_templates;
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		v('get_private_templates_ept:'.$e->getMessage());
+    	}
+    }
+    
+    /**
+     * 删除指定模板消息
+     */
+    public function delete_private_template($templateId)
+    {
+    	v('delete_private_template:'.$templateId);
+    	$del_res = $this->app->notice->deletePrivateTemplate($templateId);
+    	v('del_res');v($del_res);
+    }
+    
+    
+    /**
+     * 注意:不同公众号,相同模板消息的template_id不一样
+     * 设置模板消息id
+     *  TM00202 帐户资金变动提醒
+     TM00155 会员充值通知
+     OPENTM217772013 任务到期通知
+     TM405959659 会员卡升级通知
+     */
+    public function set_template_notices()
+    {
+    	v('set_template_notice_start');
+    	$templates = $this->WechatApi->get_wechat_public_templates($this->param['gzh_app_id']);
+    	v('$templates');v($templates);
+    	if($templates && is_array($templates) && count($templates) > 0)
+        {
+            foreach($templates as $template){
+                $this->add_one_tamplate($template['common_template_id']);
+            }
+        }
+    	v('set_template_notice_end');
+    }
+    
+    function add_one_tamplate($common_template_id){
+    	if(!empty($common_template_id)){
+    		// 添加模板并获取模板ID
+    		try{
+    				// 同一个公众号可以添加多次模板消息,所以拿common作为标准
+    				// 数据库已存在不要再添加 FIXME:如果是号主自己后台删除了模板消息就有bug,需要从已有的模板消息做检测
+    				$origin_wechat_template_official_info = $this->WechatApi->get_template_official_info($this->param['gzh_app_id'], $common_template_id);
+    				v('origin_wechat_template_official_info');v($origin_wechat_template_official_info);
+    				if(empty($origin_wechat_template_official_info)){
+    					$add_res = $this->app->notice->addTemplate($common_template_id);
+    					v('add_res_'.$common_template_id.':');v($add_res);
+    					if($add_res->errcode==0 && $add_res->errmsg=='ok'){
+    						$insert_data = array();
+    						$insert_data['appid'] = $this->param['gzh_app_id'];
+    						$insert_data['template_id'] = $add_res->template_id;
+    						$insert_data['common_template_id'] = $common_template_id;
+    						$this->WechatApi->save_template_official_info($insert_data);
+    					}
+    				}
+    		}
+    		// 加上\ 全局抓取
+    		catch(\Exception $e){
+    			v('add_template_ept:'.$e->getMessage());
+    		}
+    	}
+    }
+    
+    /**
+     * 深度检测模板消息
+     * @param unknown_type $common_template_id
+     */
+    function check_one_tamplate($common_template_id,$wx_template_ids){
+    	if(!empty($common_template_id)){
+    		// 添加模板并获取模板ID
+    		try{
+    			// 同一个公众号可以添加多次模板消息,所以拿common作为标准
+    			$origin_wechat_template_official_info = $this->WechatApi->get_template_official_info($this->param['gzh_app_id'], $common_template_id);
+    			v('origin_wechat_template_official_info');v($origin_wechat_template_official_info);
+    			if(empty($origin_wechat_template_official_info)){
+    				$add_res = $this->app->notice->addTemplate($common_template_id);
+    				v('add_res_'.$common_template_id.':'.$this->param['gzh_app_id']);v($add_res);
+    				if($add_res->errcode==0 && $add_res->errmsg=='ok'){
+    					$insert_data = array();
+    					$insert_data['appid'] = $this->param['gzh_app_id'];
+    					$insert_data['template_id'] = $add_res->template_id;
+    					$insert_data['common_template_id'] = $common_template_id;
+    					$this->WechatApi->save_template_official_info($insert_data);
+    					return true;
+    				}
+    			}
+    			// 有的话,检测下微信后台存不存在
+    			else{
+    				if(!in_array($origin_wechat_template_official_info['template_id'],$wx_template_ids)){
+    					v('template_not_exist_wx_backend:'.$common_template_id.' appid:'.$this->param['gzh_app_id']);
+    					// 不存在则执行更新操作,先添加再更新
+    					$add_res = $this->app->notice->addTemplate($common_template_id);
+    					v('update_add_res_'.$common_template_id.':appid:'.$this->param['gzh_app_id']);v($add_res);
+    					if($add_res->errcode==0 && $add_res->errmsg=='ok'){
+    						$update_data = array();
+    						$update_data['appid'] = $this->param['gzh_app_id'];
+    						$update_data['template_id'] = $add_res->template_id;
+    						$update_data['common_template_id'] = $common_template_id;
+    						$this->WechatApi->update_template_official_info($update_data);
+    					}
+    				}else{
+    					v('template_exist_wx_backend:'.$common_template_id.' origin_template_id:'.$origin_wechat_template_official_info['template_id'].' appid:'.$this->param['gzh_app_id']);
+    				}
+    				return true;
+    			}
+    		}
+    		// 加上\ 全局抓取
+    		catch(\Exception $e){
+    			v('check_template_ept:appid:'.$this->param['gzh_app_id'].' ept:'.$e->getMessage());
+    			return false;
+    		}
+    	}
+    	
+    	return true;
+    }
+    
+    /**
+     * 深度检测模板消息-返回结果
+     * @param unknown_type $common_template_id
+     */
+    function check_one_tamplate_res($common_template_id,$wx_template_ids){
+    		// 添加模板并获取模板ID
+    		try{
+    			// 同一个公众号可以添加多次模板消息,所以拿common作为标准
+    			$origin_wechat_template_official_info = $this->WechatApi->get_template_official_info($this->param['gzh_app_id'], $common_template_id);
+    			v('origin_wechat_template_official_info');v($origin_wechat_template_official_info);
+    			if(empty($origin_wechat_template_official_info)){
+    				$add_res = $this->app->notice->addTemplate($common_template_id);
+    				v('check_one_tamplate_res_add_res_'.$common_template_id.':'.$this->param['gzh_app_id']);v($add_res);
+    				if($add_res->errcode==0 && $add_res->errmsg=='ok'){
+    					$insert_data = array();
+    					$insert_data['appid'] = $this->param['gzh_app_id'];
+    					$insert_data['template_id'] = $add_res->template_id;
+    					$insert_data['common_template_id'] = $common_template_id;
+    					$this->WechatApi->save_template_official_info($insert_data);
+    				}
+    				return 1;
+    			}
+    			// 有的话,检测下微信后台存不存在
+    			else{
+    				if(!in_array($origin_wechat_template_official_info['template_id'],$wx_template_ids)){
+    					v('template_not_exist_wx_backend:'.$common_template_id.' appid:'.$this->param['gzh_app_id']);
+    					// 不存在则执行更新操作,先添加再更新
+    					$add_res = $this->app->notice->addTemplate($common_template_id);
+    					v('update_add_res_'.$common_template_id.':appid:'.$this->param['gzh_app_id']);v($add_res);
+    					if($add_res->errcode==0 && $add_res->errmsg=='ok'){
+    						$update_data = array();
+    						$update_data['appid'] = $this->param['gzh_app_id'];
+    						$update_data['template_id'] = $add_res->template_id;
+    						$update_data['common_template_id'] = $common_template_id;
+    						$this->WechatApi->update_template_official_info($update_data);
+    					}
+    					return 1;
+    				}else{
+    					v('template_exist_wx_backend:'.$common_template_id.' origin_template_id:'.$origin_wechat_template_official_info['template_id'].' appid:'.$this->param['gzh_app_id']);
+    					return 2;
+    				}
+    			}
+    		}
+    		// 加上\ 全局抓取
+    		catch(\Exception $e){
+    			$error_msg = $e->getMessage();
+    			v('check_template_ept:appid:'.$this->param['gzh_app_id'].' ept:'.$error_msg);
+    			if(strpos($error_msg,'api forbidden') !== false){
+    				return 4;
+    			}elseif(strpos($error_msg,'template num exceeds limit') !== false){
+    				return 5;
+    			}elseif(strpos($error_msg,'invalid template_id') !== false){
+    				return 6;
+    			}elseif(strpos($error_msg,'disabled template') !== false){
+    			    return 7;
+    			}elseif(strpos($error_msg,'user limited') !== false){
+    			    return 8;
+    			}elseif(strpos($error_msg,'api unauthorized') !== false){
+    			    return 9;
+    			}elseif(strpos($error_msg,'invalid industry') !== false){
+    			    return 10;
+    			}elseif(strpos($error_msg,'for punishment') !== false){
+    			    return 11;
+    			}
+    			
+    			else{
+    				return 3;
+    			}
+    		}
+    }
+    
+}

+ 106 - 0
app/Http/Controllers/Wechat/Template/TemplateWorksController.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Template;
+
+use App\Http\Models\OfficialAccount;
+
+use App\Http\Requests;
+use App\Http\Controllers\WechatController;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use App\Http\Models\WechatUser;
+use App\Http\Models\WechatTemplateSendInfo;
+use App\Http\Models\WechatTemplateTask;
+use App\Http\Models\WechatTemplateOfficialInfo;
+
+
+/**
+ * 模板消息定时发送类
+ * @author zhoulingjie
+ *
+ */
+class TemplateWorksController extends WechatController
+{
+    
+    public function __construct($template_info)
+    {
+    	$this->template_info = $template_info;
+//     	v('$template_info');v($this->template_info);
+    	$this->gzh_app_id = isset($this->template_info['appid'])?$this->template_info['appid']:'';
+    	if(!empty($this->gzh_app_id)){
+    		parent::__construct($this->gzh_app_id);
+    	}else{
+    		v('template_work_appid is null');
+    	}
+    	$this->error_msg = '';
+    }
+    
+    /**
+     * 执行模板消息
+     */
+    public function do_work()
+    {
+    	if(empty($this->gzh_app_id)) return false;
+    	v('do_work_start2');
+    	
+    	// 检查表数据
+    	$openid = $this->template_info['openid'];
+    	$appid = $this->template_info['appid'];
+    	$url = $this->template_info['url'];
+    	$task_id = $this->template_info['task_id'];
+    	$type = $this->template_info['type'];
+    	$template_id = $this->template_info['template_id'];
+    	$template_content = $this->template_info['template_content'];
+    	if(empty($openid) || empty($task_id) || empty($template_id)){
+    		v('invalid_null_task_id:'.$task_id.' openid:'.$openid);
+    		return false;
+    	}
+    	
+    	v('template_start:openid:'.$openid);
+    	
+    	$template_task = $this->WechatApi->get_template_task($task_id);
+    	$status = isset($template_task['status']) ? $template_task['status']:'';
+//     	v('task_id:'.$task_id.' status:'.$status);
+    	if($status == 4){
+    		v('task_id_close:'.$task_id);
+    		return false;
+    	}
+    
+    	$this->send_one_template($openid,$template_id,$url,$template_content);
+    	
+    	if($type == 'last_task' && $status != 3){
+    		v('update_tamplate_task_status:'.$task_id);
+    		$status = 3;
+    		$this->WechatApi->update_template_task_status($task_id, $status, $this->error_msg);
+    	}
+    	
+    	// TODO  休眠200ms,外面的调度相当于for循环,直接不停止cpu会飙高,sleep的方式有待考证
+    	usleep(200000);
+    	v('update_tamplate_task_end:'.$task_id);
+
+    }
+    
+    function send_one_template($openid,$templateId,$url,$data){
+    	try{
+    		v('send_to_openid:'.$openid.' templateId:'.$templateId.' url:'.$url);
+    		$data = objectToArray(json_decode($data));
+    		$send_data = array();
+    		foreach($data  as $_data){
+    			foreach($_data as $key=>$one_data){
+    				$send_data[$key] = $one_data;
+    			}
+    		}
+//     		v($send_data);
+    		$result = $this->app->notice->uses($templateId)->withUrl($url)->andData($send_data)->andReceiver($openid)->send();
+//     		v('send_one_template_res:'.$openid.' res:'.json_encode($result));
+    	}
+    	// 加上\ 全局抓取
+    	catch(\Exception $e){
+    		v('send_openid_ept:'.$openid.' info:'.$e->getMessage());
+    		$this->error_msg = $e->getMessage();
+    	}
+    }
+  
+    
+}

+ 63 - 0
app/Http/Controllers/Wechat/Template/TemplatesController.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\Template;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\OpenPlatform\Guard;
+use EasyWeChat\Foundation\Application;
+
+/**
+ * 模板消息管理
+ * TODO:不同公众号可能模板消息ID不一样,要求能自动添加和搜索模板消息
+ * @author zhoulingjie
+ *
+ */
+class TemplatesController
+{
+    
+    public function __construct($_app)
+    {
+		$this->app = $_app;
+    }
+    
+    /**
+     * 会员充值通知	
+     * @param Request $request
+     */
+    public function send_vip_charge_notice_tpl($open_id)
+    {
+    	v('send_vip_charge_notice_tpl_start');
+    	
+    	$open_ids = array(
+    			$open_id,
+    	);
+    
+    	$templateId = 'IFNH4uRZdjVu259VATikWB8YOoRuWktjSiHQaGejQfw';
+    
+    	$data = array(
+    			"first"  => "您好,您已成功进行会员卡充值。",
+    			"accountType"  => "账号",
+    			"account"  => $open_id,
+    			"amount"  => array(200, "#FE2E2E"),
+    			"result"  => "充值成功",
+    			"remark" => "阅读云模板消息测试",
+    	);
+    	v($data);
+    
+    	foreach($open_ids as $open_id){
+    		try{
+    			v('send_to_openid:'.$open_id);
+    			$result = $this->app->notice->uses($templateId)->andData($data)->andReceiver($open_id)->send();
+    			v($result);
+    		}
+    		// 加上\ 全局抓取
+    		catch(\Exception $e){
+    			v('send_openid_ept:'.$open_id.' info:'.$e->getMessage());
+    		}
+    	}
+    
+    }
+    
+}

+ 87 - 0
app/Http/Controllers/Wechat/ThirdWx/MsgsController.php

@@ -0,0 +1,87 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\ThirdWx;
+
+use App\Http\Controllers\WechatController;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+use WechatOP;
+use Cookie;
+
+/**
+ * 维护第三方平台的基础配置
+ * @author zhoulingjie
+ *
+ */
+class MsgsController extends WechatController
+{
+    
+    /**
+     * 接收微信的第三方平台信息推送
+     * grep -nr component_verify_ticket storage/*
+     * @param Request $request
+     */
+    function receive_component_verify_ticket(Request $request){
+    	v('receive_tickt_start');
+    	v($request->all());
+    	$server = WechatOP::server();
+
+    	v('start_setmessage');
+        if(env('DEVELOP_MODE') == 'local'){
+     		v('local_test');
+     		$message = $this->get_fake_data();
+     		$res = $this->deal_callback($message);
+     	}else{
+     		v('online_test');
+    		$server->setMessageHandler(function($message) {
+     			$res = $this->deal_callback($message);
+     		});
+     	}
+    	
+ 		v('receive_tickt_end');
+ 		return $server->serve();
+    }
+    
+    function deal_callback($message){
+    	v('thirdwx_deal_callback');v($message);
+    	v('InfoType:'.$message->get('InfoType'));
+    	switch ($message->get('InfoType')) {
+    		case "component_verify_ticket":
+    			v('save_ticket:'.$message->get('ComponentVerifyTicket'));
+    			// 保存开放平台 ticket
+    			$save_ticket_res = app('wechat_op')->saveTicket($message->get('ComponentVerifyTicket'));
+    			v('save_ticket_res');v($save_ticket_res);
+    			break;
+    		case "unauthorized":
+    			// 公众号取消授权记录
+    			$appid = $message->get('AuthorizerAppid');
+    			v('gzh_unauthorized:'.$appid);
+    			parent::__construct($appid);
+    			$this->WechatApi->cancel_oauth_official_account($appid);
+    			break;
+    		case "authorized":
+    			// 公众号授权
+    			v('gzh_authorized');
+    			break;
+    		default:
+    			break;
+    	}
+    }
+    
+//     {"\u0000*\u0000items":{
+//     	"AppId":"wx46d3ea76723d890c","CreateTime":"1506602969","InfoType":"component_verify_ticket","ComponentVerifyTicket":"ticket@@@MX6T2u23fgQ6BWYyU5dteplNwP_Nu-HEjoVhzmjSIKeTNu8dTVUfdCjdJqVGX4uF5ODSJKJMRqH1blDFUYgpSw"}
+//     }
+    function get_fake_data(){
+    	$message = new \stdClass();
+    	$test = 'ticket';
+    	if($test == 'ticket'){
+    		$message->InfoType = 'EVENT_UNAUTHORIZED';
+    		$message->AuthorizationCode = '200';
+    	}
+    	 
+    	return $message;
+    }
+
+}

+ 265 - 0
app/Http/Controllers/Wechat/ThirdWx/OauthPlatformsController.php

@@ -0,0 +1,265 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\ThirdWx;
+
+use App\Http\Models\WechatGroupGzh;
+
+use App\Http\Controllers\WechatController;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use WechatOP;
+use Illuminate\Support\Facades\Redis;
+use App\Http\Controllers\Wechat\Menu\MenusController;
+use App\Http\Controllers\Wechat\Template\TemplateBasesController;
+
+/**
+ * 授权第三方平台获取公众号权限
+ * @author zhoulingjie
+ *
+ */
+class OauthPlatformsController extends WechatController
+{
+    
+    public function __construct()
+    {
+		parent::__construct();
+    }
+    
+    /**
+     * 公众号授权第三方平台并跳转
+     http://zydy/oauth/oauth_platform?distribution_channel_id=1&official_account_type=force_subscribe&group_nick=aizhuishu&redirect_url=http%3a%2f%2fchannel.aizhuishu.com%2fservice-setting%3ftab%3d3&timestamp=1513240244&sign=577684945fd6f2dc0295b7dc3f5cf60c
+    * @param Request $request
+     */
+    function oauth_platform(Request $request){
+    	v('oauth_platform_start');
+    	$result = array('code'=>1,'msg'=>'','data'=>'');
+    	$official_account_type = $request->get('official_account_type');
+    	$distribution_channel_id = $request->get('distribution_channel_id');
+    	$origin_redirect_url = $request->get('redirect_url');
+    	$group_nick = $request->get('group_nick');
+    	$redirect_url = urlencode($origin_redirect_url);
+    	v($request->all());
+    	if(empty($official_account_type) || empty($distribution_channel_id) || empty($redirect_url) || empty($group_nick)){
+    		$result['code'] = 0;
+    		$result['msg'] = 'invalid param';
+    		json_echo($result);
+    	}
+    	 
+    	$check_result = $this->check_sign_params($request);
+    	if($check_result['code'] == 0){
+    		$result['code'] = 0;
+    		$result['msg'] = $check_result['msg'];
+    		json_echo($result);
+    	}
+    	$_param = $official_account_type.'*'.$distribution_channel_id.'*'.$redirect_url.'*'.$group_nick;
+    	$this->oauth_platform_callback_url = $this->oauth_platform_callback_base_url.$_param.'/oauth_platform_callback';
+    	v('oauth_platform_callback_url:'.$this->oauth_platform_callback_url);
+    	try{
+    		$response = WechatOP::oauth()->redirect($this->oauth_platform_callback_url);
+    		v('$response');
+    		
+    		$target_url = $response->getTargetUrl();
+    		v('target_url');v($target_url);
+    	}catch(\Exception $e){
+    		v('oauth_callback_ept:'.$e->getMessage());
+    		$origin_redirect_url .= "&distribution_channel_id=".$distribution_channel_id."&success=0&msg=授权异常:".urlencode($e->getMessage());
+    		v('ept_redirect_url:'.$origin_redirect_url);
+    		// 跳转回去
+    		$this->redirect_url($origin_redirect_url);
+    		exit();
+    	}
+    	
+    	v('oauth_platform_end');
+    	// 注意:直接跳转会有bug
+    	echo "
+    	<html>
+    	<head>
+    	<script LANGUAGE='JavaScript'> 
+		window.location= '".$target_url."';
+		</script>
+		</head>
+		<body>
+		<br><a href='".$target_url."'>若没有自动跳转,点击本链接跳转到授权页</a><br>
+		</body>
+		</html>  
+    	";
+    }
+    
+    /**
+     * 授权结束回调给平台调用凭据
+     * @param Request $request
+     */
+    function oauth_platform_callback(Request $request){
+    	v('oauth_platform_callback');v($request->all());
+    	$official_account_type = $distribution_channel_id = $group_nick = '';
+    	// 提取授权公众号的基础回调参数,回调格式domain/oauth/official_account_type*distribution_channel_id/oauth_platform_callback
+    	preg_match('/oauth\/(.*)?\/oauth_platform_callback/i',$_SERVER['REQUEST_URI'],$data);
+    	if(isset($data[1]) && !empty($data[1])){
+    		$_param =$data[1];
+    		$_param_arr = explode('*',$_param);
+    		$official_account_type = $_param_arr[0];
+    		$distribution_channel_id = $_param_arr[1];
+    		$redirect_url = $_param_arr[2];
+    		$group_nick = $_param_arr[3];
+    		$redirect_url = urldecode($redirect_url);
+    		if(strpos($redirect_url,'?') === false){
+    			$redirect_url .= '?';
+    		}
+    	}
+    	
+    	v('oauth_platform_callback:official_account_type:'.$official_account_type.' distribution_channel_id:'.$distribution_channel_id.' redirect_url:'.$redirect_url);
+//     	die('end');
+    	
+    	$authInfo = WechatOP::oauth()->user();
+    	$authInfo = objectToArray($authInfo);
+    	// 取第一个val
+    	$authInfo = current($authInfo);
+    	v('authInfo');v($authInfo);
+    	// 授权信息
+    	$authorization_info = $authInfo['authorization_info'];
+    	v('authorization_info');v($authorization_info);
+    	
+    	$appid = isset($authorization_info['authorizer_appid'])?$authorization_info['authorizer_appid']:'';
+    	
+    	$is_service_auth =  isset($authInfo['authorizer_info']['service_type_info']['id']) && 
+    			$authInfo['authorizer_info']['service_type_info']['id'] == 2 && 
+    			isset($authInfo['authorizer_info']['verify_type_info']['id']) &&
+    			$authInfo['authorizer_info']['verify_type_info']['id'] == 0;
+    	
+    	$is_white_appids = false;
+    	$white_appids = Redis::smembers('auth_white_appids');
+    	v('white_appids');v($white_appids);
+    	if(!empty($appid) && in_array($appid,$white_appids)){
+    		$is_white_appids = true;
+    	}
+    	
+    	v('oauth_platform_callback:appid:'.$appid.' is_service_auth:'.$is_service_auth.' is_white_appids:'.$is_white_appids);
+    	
+    	// 只有认证微信服务号才支持,或者白名单列表
+    	if($is_service_auth || $is_white_appids)
+    	{
+    		// 保存token信息
+    		$authorizer_appid = $authorization_info['authorizer_appid'];
+    		$authorizer_refresh_token = $authorization_info['authorizer_refresh_token'];
+    		$redis_key = '[wechat_op.common.component_refresh_token.'.$authorizer_appid.']';
+    		$redis_val = $authorizer_refresh_token;
+    		v('oauth_platform_callback:redis_key:'.$redis_key.' redis_val:'.$redis_val);
+    		Redis::Set($redis_key, $authorizer_refresh_token);
+    		$redis_res = Redis::Get($redis_key);
+    		v('redis_res:'.$redis_res);
+    		
+    		// 重要:要先保存映射表
+    		$insert_group = array();
+    		$insert_group['group_nick'] = $group_nick;
+    		$insert_group['appid'] = $authorizer_appid;
+    		WechatGroupGzh::save_group_gzh($insert_group);
+    		
+    		// 初始化接口
+    		parent::__construct($authorizer_appid);
+    		
+    		$origin_distribution_channel_id = isset($this->official_account['distribution_channel_id'])?$this->official_account['distribution_channel_id']:'';
+    		$origin_appid = isset($this->official_account['appid'])?$this->official_account['appid']:'';
+    		v('check_official_account,distribution_channel_id:'.$distribution_channel_id.' origin_distribution_channel_id:'.$origin_distribution_channel_id.' origin_appid:'.$origin_appid);
+            
+    		// 空账号或者同一个账号
+            if(empty($origin_appid) || ($distribution_channel_id == $origin_distribution_channel_id)){
+            	v('third_oauth_save_or_update:'.$authorizer_appid.' distribution_channel_id:'.$distribution_channel_id);
+            	// 保存商户公众号信息
+            	$authorizer_info = $authInfo['authorizer_info'];
+            	v('$authorizer_info');v($authorizer_info);
+            	$insert_offical_account = array();
+            	$insert_offical_account['nickname'] = isset($authorizer_info['nick_name'])?$authorizer_info['nick_name']:'';
+            	$insert_offical_account['head_img'] = isset($authorizer_info['head_img'])?$authorizer_info['head_img']:'';
+            	$insert_offical_account['service_type_info'] = isset($authorizer_info['service_type_info']['id'])?$authorizer_info['service_type_info']['id']:null;
+            	$insert_offical_account['verify_type_info'] = isset($authorizer_info['verify_type_info']['id'])?$authorizer_info['verify_type_info']['id']:null;
+            	$insert_offical_account['name'] = isset($authorizer_info['user_name'])?$authorizer_info['user_name']:'';
+            	$insert_offical_account['alias'] = isset($authorizer_info['alias'])?$authorizer_info['alias']:'';
+            	$insert_offical_account['qrcode_url'] = isset($authorizer_info['qrcode_url'])?$authorizer_info['qrcode_url']:'';
+            	$insert_offical_account['principal_name'] = isset($authorizer_info['principal_name'])?$authorizer_info['principal_name']:'';
+            	$insert_offical_account['authorizer_refresh_token'] = $authorizer_refresh_token;
+            	$insert_offical_account['appid'] = $authorizer_appid;
+            	$insert_offical_account['func_info'] = json_encode($authorization_info['func_info']);
+            	$insert_offical_account['is_auth'] = 1;
+            	$insert_offical_account['official_account_type'] = $official_account_type;
+            	$insert_offical_account['distribution_channel_id'] = $distribution_channel_id;
+            	v('authorizer_info_insert_data:');v($insert_offical_account);
+            	$this->WechatApi->save_official_account($insert_offical_account);
+            		
+            	// 自动生成菜单和模板消息,如果默认是登录或者支付公众号,则不生成菜单
+            	// TODO check下是否自动生成
+            	if(!in_array($official_account_type,array('third_platform_default_login','third_platform_pay'))){
+            		v('auto_set_menu:'.$authorizer_appid);
+            		$this->auto_set_menu_and_template($authorizer_appid,'all');
+            	}else{
+            		v('not_set_menu:'.$authorizer_appid);
+            	}
+            	
+            	$redirect_url .= "&distribution_channel_id=".$distribution_channel_id."&authorizer_appid=".$authorizer_appid."&success=1";
+            }
+            // 已存在的
+            else{
+            	v('third_oauth_direct_return:'.$authorizer_appid.' distribution_channel_id:'.$distribution_channel_id);
+            	$redirect_url .= "&distribution_channel_id=".$distribution_channel_id."&success=0&msg=该服务号已经被渠道号:".$origin_distribution_channel_id." 授权,不能再次授权!";
+            }
+    		
+    	}else{
+    		$redirect_url .= "&distribution_channel_id=".$distribution_channel_id."&success=0&msg=只支持认证的服务号!";
+    	}
+    	v('redirect_url:'.$redirect_url);
+    	
+    	// 跳转回去
+    	$this->redirect_url($redirect_url);
+    }
+    
+    function auto_set_menu_and_template($authorizer_appid='',$set_type='all'){
+    	if(empty($authorizer_appid)){
+    		v('auto_set_menu_and_template_param_null:'.$authorizer_appid);
+    		return false;
+    	}
+    	
+    	parent::__construct($authorizer_appid);
+    	 
+    	if($set_type == 'all'){
+    		$menu = new MenusController($this->param);
+    		$menu->set_menu();
+    		 
+    		$template = new TemplateBasesController($this->param);
+    		$template->auto_set_dustry_templates();
+    	}elseif($set_type == 'menu'){
+    		$menu = new MenusController($this->param);
+    		$menu->set_menu();
+    	}elseif($set_type == 'template'){
+    		$template = new TemplateBasesController($this->param);
+    		$template->auto_set_dustry_templates();
+    	}else{
+    		v('auto_set_menu_and_template_invalid_set_type:'.$authorizer_appid);
+    	}
+
+    }
+    
+    /**
+     * 嘉言小说 wxdbc486f1b4f6a8c3
+     http://zydy/oauth/auto_set_menu_and_template_test?set_type=menu&authorizer_appid=wxdbc486f1b4f6a8c3&timestamp=1511509543&sign=98dccc7b29e3c05e4b4367f488ff1966
+     */
+    function auto_set_menu_and_template_test(Request $request){
+    	
+    	$result = array('code'=>1,'msg'=>'','data'=>'');
+    	$authorizer_appid = $request->get('authorizer_appid');
+    	$set_type = $request->get('set_type');
+    	if(empty($authorizer_appid) || empty($set_type)){
+    		$result['code'] = 0;
+    		$result['msg'] = 'invalid param';
+    		json_echo($result);
+    	}
+    	
+        $check_result = $this->check_sign_params($request);
+    	if($check_result['code'] == 0){
+    		$result['code'] = 0;
+    		$result['msg'] = $check_result['msg'];
+    		json_echo($result);
+    	}
+    	$this->auto_set_menu_and_template($authorizer_appid,$set_type);
+    }
+    
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 26 - 0
app/Http/Controllers/Wechat/ThirdWx/data_format.php


+ 53 - 0
app/Http/Controllers/Wechat/User/UserInfosController.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Http\Controllers\Wechat\User;
+
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use EasyWeChat\Foundation\Application;
+
+/**
+ * 用户信息
+ * @author zhoulingjie
+ *
+ */
+class UserInfosController
+{
+
+    public function __construct($_param)
+    {
+    	$this->param = $_param;
+		$this->app = $_param['app'];
+		$this->official_account = $this->param['official_account'];
+    }
+   
+    public function get_oauth_user_info($openid,$force_subscribe_sceneId){
+    	try{
+    		$user_data = $this->app->user->get($openid);
+    		v('get_oauth_user_info_user_data:');v($user_data);
+    		$data = array();
+    		$data['appid'] = isset($this->official_account['appid'])?$this->official_account['appid']:'';
+    		$data['official_account_id'] = isset($this->official_account['id'])?$this->official_account['id']:'';
+    		$data['distribution_channel_id'] = isset($this->official_account['distribution_channel_id'])?$this->official_account['distribution_channel_id']:'';
+    		$data['nick_name'] = isset($user_data['nickname'])?$user_data['nickname']:'';
+    		$data['openid'] = isset($user_data['openid'])?$user_data['openid']:'';
+    		$data['sex'] = isset($user_data['sex'])?$user_data['sex']:'';
+    		$data['city'] = isset($user_data['city'])?$user_data['city']:'';
+    		$data['country'] = isset($user_data['country'])?$user_data['country']:'';
+    		$data['uid'] = $force_subscribe_sceneId;
+    		$data['unionid'] = isset($user_data['unionid'])?$user_data['unionid']:'';
+    		$data['province'] = isset($user_data['province'])?$user_data['province']:'';
+    		$data['head_img'] = isset($user_data['headimgurl'])?$user_data['headimgurl']:'';
+    		$data['is_subscribed'] = isset($user_data['subscribe'])?$user_data['subscribe']:0;
+    		$data['subscribe_time'] = isset($user_data['subscribe_time'])?$user_data['subscribe_time']:'';
+
+    	}catch(\Exception $e){
+    		v('get_oauth_user_info_ept:'.$e->getMessage());
+    	}
+    	v('get_oauth_user_info:');v($data);
+    	return $data;
+    	
+    }
+    
+}

+ 87 - 0
app/Http/Controllers/WechatBaseApisController.php

@@ -0,0 +1,87 @@
+<?php
+
+namespace App\Http\Controllers;
+use App\Http\Controllers\Controller;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use App\Libs\Classes\WxSign;
+use App\Modules\WechatInner\Services\WechatInnerService;
+
+class WechatBaseApisController extends Controller
+{
+	public function __construct($_param){
+		$this->param = $_param;
+		$this->WxSign = new WxSign();
+	}
+	
+	/**
+	 * api请求封装
+	 *
+	 */
+	public function do_api_post($data,$api_event)
+	{
+		$api_event = strtolower($api_event);
+		
+		$inner_api_events = [
+			'get_force_wx_user',
+			'update_user_interaction_time',
+			'get_event_content',
+// 			'get_material_force_subscribe_mapping'
+		];
+		// 部分接口直接查
+		if(in_array($api_event,$inner_api_events)){
+			$event_datas =  WechatInnerService::get_event_data($data,$api_event);
+			return $event_datas;
+		}
+		
+		v('do_api_post_start:'.$api_event);
+		try{
+			$search_url = $this->param['group']['domain'].$this->param['group_api'][$api_event]['api_url'];
+			$post_data  =  $this->WxSign->get_sign_arr($data,$this->param['group']['secret_key']);
+			$result = $this->do_api_post_base($search_url,$post_data);
+		}
+		catch(\Exception $e){
+			v('do_api_post_ept:'.$e->getMessage());
+			$result = null;
+		}
+	
+		return $result;
+	}
+	
+	/**
+	 * api请求接口基础
+	 *
+	 */
+	public function do_api_post_base($search_url,$post_data)
+	{
+		$post_results = null;
+		try{
+			v('do_api_post_base_start, search_url:'.$search_url);
+			v($post_data);
+			$post_results = hpost($search_url,$post_data);
+			if(isset($post_results['code']) && $post_results['code'] == 0){
+				$post_results = $post_results['data'];
+			}else{
+				$code = isset($post_results['code'])?$post_results['code']:'';
+				$msg = isset($post_results['msg'])?$post_results['msg']:'';
+				v('post_error:code'.$code.' msg:'.$msg);
+				$post_results = null;
+			}
+		}
+		catch(\Exception $e){
+			v('do_api_post_base_ept:'.$e->getMessage());
+			
+		}
+
+		// 	 	$result = array();
+		// 	 	$one_book1 = array('title'=>'搜索结果','description'=>'描述1','url'=>'www.baidu.com','image'=>'https://mmbiz.qpic.cn/mmbiz_jpg/U3h620I4Tu1wicDy9Lv9Il4xDOibuxsPl5cyhz2sR0FrSKzmtKcYSC3HKHHBIIWUzWPXX3rcXqw1eT69hicBrjl8Q/640.jpeg?tp=webp&wxfrom=5&wx_lazy=1');
+		// 	 	$one_book2 = array('title'=>'搜索的图文消息','description'=>'描述2','url'=>'www.baidu.com','image'=>'https://mmbiz.qpic.cn/mmbiz_jpg/U3h620I4Tu1wicDy9Lv9Il4xDOibuxsPl5cyhz2sR0FrSKzmtKcYSC3HKHHBIIWUzWPXX3rcXqw1eT69hicBrjl8Q/640.jpeg?tp=webp&wxfrom=5&wx_lazy=1');
+		// 	 	$one_book3 = array('title'=>'阅读云搜索图文消息','description'=>'阅读云搜索图文消息','url'=>'www.baidu.com','image'=>'https://gitee.com/uploads/88/1043488_yuelooll.png?1495765918');
+	
+		// 	 	$result[] = $one_book1;
+		// 	 	$result[] = $one_book2;
+		// 	 	$result[] = $one_book3;
+// 		v('post_results');v($post_results);
+		return $post_results;
+	}
+}

+ 172 - 0
app/Http/Controllers/WechatController.php

@@ -0,0 +1,172 @@
+<?php
+
+namespace App\Http\Controllers;
+use App\Http\Controllers\Controller;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use App\Libs\Classes\BLogger;
+use App\Libs\Classes\WxSign;
+use EasyWeChat\Foundation\Application;
+use WechatOP;
+use Illuminate\Support\Facades\Redis;
+use App\Http\Models\WechatGroupGzh;
+use App\Http\Controllers\Wechat\Api\WechatInnerApisController;
+use Doctrine\Common\Cache\PredisCache;
+// use Doctrine\Common\Cache\RedisCache;
+
+class WechatController extends Controller
+{
+	
+	public $wx_log_path;
+	public $options;
+	public $host;
+	public $auth_host;
+	public $third_host;
+	public $WxSign;
+	public $token;
+	public $gzh_name;
+	public $gzh_app_id;
+	public $Redis;// redis的连接对象
+	public $WechatApi;
+	public $param;
+	public $official_account;
+// 	public $wechat_group_gzh;
+
+	public function __construct($gzh_app_id='') {
+		// 方便扩展
+		$this->param = array();
+		$this->gzh_app_id = $gzh_app_id;
+		$this->param['gzh_app_id'] = $this->gzh_app_id;
+		$this->param['openid'] = '';
+		// check公众号,不等于微信测试号
+		if(!empty($this->gzh_app_id) && $this->gzh_app_id != 'wx570bc396a51b8ff8'){
+			$this->wechat_group_gzh = WechatGroupGzh::get_wechat_group_gzh($this->gzh_app_id);
+			if(!empty($this->wechat_group_gzh)) {
+				$this->param['group_api'] = $this->wechat_group_gzh['group_api'];
+				$this->param['group'] = $this->wechat_group_gzh['group'];
+			}else{
+				v('invalid gzh_app_id:'.$gzh_app_id);
+				// 			json_echo('invalid gzh_app_id');
+			}
+		}
+			
+		$this->wx_log_path = '/var/www/ydy_wechat/storage/logs/easywechat.log';
+		$this->domain = env('DOMAIN');//'aizhuishu.com';
+		$this->auth_host = $this->host = env('ONLINE_AUTH_HOST');
+		if(env('DEVELOP_MODE') == 'online'){
+			$this->Redis = Redis::connection();// 默认的正式库的redis
+		}elseif(env('DEVELOP_MODE') == 'online_test'){
+			$this->Redis = Redis::connection();// 默认env的redis
+		}elseif(env('DEVELOP_MODE') == 'test'){
+			$this->Redis = Redis::connection('test_redis');// 测试库的redis
+		}elseif(env('DEVELOP_MODE') == 'local'){
+			$this->wx_log_path = '/Applications/MAMP/htdocs/ydy_wechat/storage/logs/easywechat.log';
+			$this->Redis = Redis::connection('test_redis');// 测试库的redis
+		}
+// 		v('$this->Redis');v($this->Redis);
+// 		v('develop:'.env('DEVELOP_MODE').' domain:'.$this->domain);
+
+		$this->WxSign = new WxSign();
+		$this->oauth_platform_callback_base_url = $this->auth_host.'oauth/';// 第三方授权微信回跳地址
+		
+		$this->token = env('WECHAT_OP_TOKEN');
+		
+		if(!empty($this->gzh_app_id)){
+			// token部分的redis都用正式库的
+			// 只在授权时刻提供,丢失后需要用户重新授权
+			$redis_key = '[wechat_op.common.component_refresh_token.'.$this->gzh_app_id.']';
+			$component_refresh_token = Redis::Get($redis_key);
+// 			v('redis_key:'.$redis_key.' component_refresh_token:'.$component_refresh_token);
+			
+			$this->options = [
+				'app_id'    => $this->gzh_app_id,
+				'secret'    => env('WECHAT_OP_SECRET'),   // 仅适用于 单独配置公众号
+				'token'     => env('WECHAT_OP_TOKEN'),   // 仅适用于 单独配置公众号
+				'aes_key'   => env('WECHAT_OP_AES_KEY'),   // 仅适用于 单独配置公众号
+				'auth_type' => 'COMPONENT', // COMPONENT 开放平台授权公众号,MANUAL 单独配置公众号
+				'component_refresh_token' => $component_refresh_token,   // 授权回调时获取的 authorizer_refresh_token,仅适用于 开放品台授权公众号
+				'oauth'     => [
+				   'scopes' => ['snsapi_base'], // 公众号授权用户方式 snsapi_base, snsapi_userinfo
+				   'callback' => '/oauth_callback',
+				],
+				'cache'   => [
+					'driver' => 'redis',   // redis, filesystem, laravel
+					'dir' => storage_path('tmp') // 只有为filesystem时候这个目录才有效
+				],
+			];
+			// EasyWechat\Foundation\Application 对象
+			$this->app = WechatOP::app($this->options);
+			
+			$this->param['app'] = $this->app;
+
+			$this->WechatApi = new WechatInnerApisController($this->param);
+			$this->official_account = $this->WechatApi->get_official_account($this->gzh_app_id);
+			$this->param['official_account'] = $this->official_account;
+			$this->param['WechatApi'] = $this->WechatApi;
+		}
+
+		$this->set_init_param();
+		v('post:');v($_POST);
+	}
+		
+	// 初始化post,get变量,兼容两种
+	public function set_init_param(){
+		if(empty($_POST)) {
+			$_POST = $_REQUEST;
+		}
+		if(empty($_REQUEST)) {
+			$_REQUEST = $_POST;
+		}
+
+		if(isset($_POST['_url'])) {
+			unset($_POST['_url']);
+		}
+		if(isset($_REQUEST['_url'])) {
+			unset($_REQUEST['_url']);
+		}
+	}	
+	
+	public function redirect_url($response_url){
+//		v('redirect_url in:'.$response_url);
+		header("Location:".$response_url);
+        exit;
+	}
+	
+	/**
+	 * 检查签名和时间戳
+	 * @param unknown_type $request
+	 */
+	public function check_sign_params($request){
+		$result = array('code'=>1,'msg'=>'','data'=>'');
+		$timestamp = !empty($request->get('timestamp'))?$request->get('timestamp'):'';
+		$sign = !empty($request->get('sign'))?$request->get('sign'):'';
+		v('check_sign_params:$timestamp:'.$timestamp.' $sign:'.$sign);
+		if(empty($timestamp) || empty($sign)){
+			$result['code'] = 0;
+			$result['msg'] = 'invalid param';
+			return $result;
+		}
+		
+		$timestamp = $request->get('timestamp');
+		$sign = $request->get('sign');
+		$now = microtime(true);
+		$diff = $now - $timestamp;
+		v('timestamp:'.$timestamp.' now:'.$now.' diff:'.$diff);
+		// 时间戳8小时内
+		if($diff > 28*3600){
+			$result['code'] = 0;
+			$result['msg'] = 'invalid time';
+			return $result;
+		}
+
+		$my_sign = $this->WxSign->get_sign($request->all());
+		v('check_sign_params sign:'.$sign.' my_sign:'.$my_sign.' info:'.json_encode($request->all()));
+		if($sign != $my_sign){
+			$result['code'] = 0;
+			$result['msg'] = 'invalid sign';
+			return $result;
+		}
+		return $result;
+	}
+	
+}

+ 103 - 0
app/Http/Controllers/WechatOpController.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace App\Http\Controllers;
+use App\Http\Controllers\Controller;
+use App\Http\Requests;
+use Illuminate\Http\Request;
+use App\Libs\Classes\BLogger;
+use App\Libs\Classes\WxSign;
+use EasyWeChat\Foundation\Application;
+use WechatOP;
+use Illuminate\Support\Facades\Redis;
+use Doctrine\Common\Cache\PredisCache;
+
+class WechatOpController extends Controller
+{
+	
+	public $openPlatform;
+	public $WxSign;
+// 	public $wechat_group_gzh;
+
+	public function __construct($gzh_app_id='') {
+		v('start_wechat_op_construct');
+		$options = [
+		// ...
+		'open_platform' => [
+			'app_id'   => env('WECHAT_OP_APPID'),
+			'secret'   => env('WECHAT_OP_SECRET'),
+			'token'    => env('WECHAT_OP_TOKEN'),
+			'aes_key'  => env('WECHAT_OP_AES_KEY'),
+			],
+		// ...
+		];
+		v('$options');v($options);
+		$app = new Application($options);
+		$this->openPlatform = $app->open_platform;
+		v('end_wechat_op_construct');
+		
+		$this->WxSign = new WxSign();
+		$this->set_init_param();
+		v('post:');v($_POST);
+	}
+		
+	// 初始化post,get变量,兼容两种
+	public function set_init_param(){
+		if(empty($_POST)) {
+			$_POST = $_REQUEST;
+		}
+		if(empty($_REQUEST)) {
+			$_REQUEST = $_POST;
+		}
+
+		if(isset($_POST['_url'])) {
+			unset($_POST['_url']);
+		}
+		if(isset($_REQUEST['_url'])) {
+			unset($_REQUEST['_url']);
+		}
+	}	
+	
+	public function redirect_url($response_url){
+//		v('redirect_url in:'.$response_url);
+		header("Location:".$response_url);
+        exit;
+	}
+	
+	/**
+	 * 检查签名和时间戳
+	 * @param unknown_type $request
+	 */
+	public function check_sign_params($request){
+		$result = array('code'=>1,'msg'=>'','data'=>'');
+		$timestamp = !empty($request->get('timestamp'))?$request->get('timestamp'):'';
+		$sign = !empty($request->get('sign'))?$request->get('sign'):'';
+		v('check_sign_params:$timestamp:'.$timestamp.' $sign:'.$sign);
+		if(empty($timestamp) || empty($sign)){
+			$result['code'] = 0;
+			$result['msg'] = 'invalid param';
+			return $result;
+		}
+		
+		$timestamp = $request->get('timestamp');
+		$sign = $request->get('sign');
+		$now = microtime(true);
+		$diff = $now - $timestamp;
+		v('timestamp:'.$timestamp.' now:'.$now.' diff:'.$diff);
+		// 时间戳8小时内
+		if($diff > 28*3600){
+			$result['code'] = 0;
+			$result['msg'] = 'invalid time';
+			return $result;
+		}
+
+		$my_sign = $this->WxSign->get_sign($request->all());
+		v('check_sign_params sign:'.$sign.' my_sign:'.$my_sign.' info:'.json_encode($request->all()));
+		if($sign != $my_sign){
+			$result['code'] = 0;
+			$result['msg'] = 'invalid sign';
+			return $result;
+		}
+		return $result;
+	}
+	
+}

+ 58 - 0
app/Http/Kernel.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace App\Http;
+
+use Illuminate\Foundation\Http\Kernel as HttpKernel;
+
+class Kernel extends HttpKernel
+{
+    /**
+     * The application's global HTTP middleware stack.
+     *
+     * These middleware are run during every request to your application.
+     *
+     * @var array
+     */
+    protected $middleware = [
+        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
+    ];
+
+    /**
+     * The application's route middleware groups.
+     *
+     * @var array
+     */
+    protected $middlewareGroups = [
+        'web' => [
+            \App\Http\Middleware\EncryptCookies::class,
+            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+            \Illuminate\Session\Middleware\StartSession::class,
+            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+//            \App\Http\Middleware\VerifyCsrfToken::class,
+        ],
+        'channel' => [
+            \App\Http\Middleware\EncryptCookies::class,
+            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+            \Illuminate\Session\Middleware\StartSession::class,
+            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+//            \App\Http\Middleware\VerifyCsrfToken::class,
+        ],
+        'api' => [
+            'throttle:60,1',
+        ],
+    ];
+
+    /**
+     * The application's route middleware.
+     *
+     * These middleware may be assigned to groups or used individually.
+     *
+     * @var array
+     */
+    protected $routeMiddleware = [
+        'auth' => \App\Http\Middleware\Authenticate::class,
+        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
+        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+    ];
+}

+ 30 - 0
app/Http/Middleware/Authenticate.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Support\Facades\Auth;
+
+class Authenticate
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @param  string|null  $guard
+     * @return mixed
+     */
+    public function handle($request, Closure $next, $guard = null)
+    {
+        if (Auth::guard($guard)->guest()) {
+            if ($request->ajax()) {
+                return response('Unauthorized.', 401);
+            } else {
+                return redirect()->guest('login');
+            }
+        }
+
+        return $next($request);
+    }
+}

+ 17 - 0
app/Http/Middleware/EncryptCookies.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter;
+
+class EncryptCookies extends BaseEncrypter
+{
+    /**
+     * The names of the cookies that should not be encrypted.
+     *
+     * @var array
+     */
+    protected $except = [
+        //
+    ];
+}

+ 26 - 0
app/Http/Middleware/RedirectIfAuthenticated.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Support\Facades\Auth;
+
+class RedirectIfAuthenticated
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @param  string|null  $guard
+     * @return mixed
+     */
+    public function handle($request, Closure $next, $guard = null)
+    {
+        if (Auth::guard($guard)->check()) {
+            return redirect('/');
+        }
+
+        return $next($request);
+    }
+}

+ 17 - 0
app/Http/Middleware/VerifyCsrfToken.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
+
+class VerifyCsrfToken extends BaseVerifier
+{
+    /**
+     * The URIs that should be excluded from CSRF verification.
+     *
+     * @var array
+     */
+    protected $except = [
+        //
+    ];
+}

+ 18 - 0
app/Http/Models/WechatGroup.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Http\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use DB;
+
+class WechatGroup extends Model
+{
+    protected $table = 'wechat_groups';
+    protected $guarded = ['id'];
+    
+    static function get_group($group_nick){
+    	$result = self::where('group_nick',$group_nick)->first()->toarray();
+    	return $result;
+    }
+    
+}

+ 25 - 0
app/Http/Models/WechatGroupApi.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Http\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use DB;
+
+class WechatGroupApi extends Model
+{
+    protected $table = 'wechat_group_apis';
+    protected $guarded = ['id'];
+
+    static function get_group_api($group_nick){
+    	$group_apis = self::where('group_nick',$group_nick)->get()->toarray();
+    	$result = array();
+    	if(!empty($group_apis)){
+    		foreach($group_apis as $group_api){
+    		   $result[$group_api['api_nick']] = $group_api;
+    		}
+    	}
+    	
+    	return $result;
+    }
+    
+}

+ 86 - 0
app/Http/Models/WechatGroupGzh.php

@@ -0,0 +1,86 @@
+<?php
+
+namespace App\Http\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use DB;
+use Illuminate\Support\Facades\Redis;
+
+class WechatGroupGzh extends Model
+{
+    protected $table = 'wechat_group_gzhs';
+    protected $guarded = ['id'];
+   
+    static function getAllList()
+    {
+    	$all = self::all();
+
+    	return $all;
+    }
+    
+    static function save_group_gzh($insert_data){
+    	$origin_group_gzh = self::get_group_gzh($insert_data['appid'],$insert_data['group_nick']);
+//     	v('$origin_group_gzh');v($origin_group_gzh);
+    	if(empty($origin_group_gzh)){
+    		v('group_gzh_not_exist:'.$insert_data['appid']);
+    		$data = array();
+    		$data['group_nick'] = isset($insert_data['group_nick'])?$insert_data['group_nick']:'';
+    		$data['appid'] = isset($insert_data['appid'])?$insert_data['appid']:'';
+    		$data['status'] = 1;
+    		v('save_data');v($data);
+    		$result = self::firstOrCreate($data);
+    		return $result;
+    	}else{
+    		v('group_gzh_exist:'.$insert_data['appid']);
+    	}
+    }
+    
+    static function get_group_gzh($appid,$group_nick){
+    	$group_gzh = self::where('appid',$appid)->where('group_nick',$group_nick)->first();
+    	return $group_gzh;
+    }
+    
+    static function get_wechat_group_gzh($appid){
+        // 从redis取
+    	$redis_group_gzh = Redis::get('wechat_group_gzh:appid:'.$appid);
+    	$group_gzh = '';
+    	if(!empty($redis_group_gzh)){
+    		$group_gzh = json_decode($redis_group_gzh);
+    	}else{
+    		$group_gzh = self::where('appid',$appid)->where('status',1)->first();
+    		v('redis_group_gzh_set:'.$appid);
+    		// 重要判断,否则新号有bug
+    		if(!empty($group_gzh)){
+    			Redis::set('wechat_group_gzh:appid:'.$appid,json_encode($group_gzh));
+    		}
+    	}
+    	$group_nick = isset($group_gzh->group_nick)?$group_gzh->group_nick:'';
+    	$result = array('group'=>null,'group_api'=>null);
+    	if(!empty($group_gzh)){
+    		$group = Redis::get('wechat_group:group_nick:'.$group_nick);
+    		if(empty($group)){
+    			$group = WechatGroup::get_group($group_nick);
+    			Redis::set('wechat_group:group_nick:'.$group_nick,json_encode($group));
+    			v('redis_group_set:'.json_encode($group));
+    		}else{
+    			$group = objectToArray(json_decode($group));
+    		}
+    		
+    		$group_api = Redis::get('wechat_group_api:group_nick:'.$group_nick);
+    		if(empty($group_api)){
+    			$group_api = WechatGroupApi::get_group_api($group_nick);
+    			Redis::set('wechat_group_api:group_nick:'.$group_nick,json_encode($group_api));
+    			v('redis_group_api_set:'.json_encode($group_api));
+    		}else{
+    			$group_api = objectToArray(json_decode($group_api));
+    		}
+    		
+    		$result['group'] = $group;
+    		$result['group_api'] = $group_api;
+    	}
+//     	$result['group']['domain'] = 'http://api.zsy.com/api/';
+//     	v('$get_wechat_group_gzh');v($result);
+    	return $result;
+    }
+    
+}

+ 10 - 0
app/Http/Requests/Request.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+abstract class Request extends FormRequest
+{
+    //
+}

+ 113 - 0
app/Http/routes.php

@@ -0,0 +1,113 @@
+<?php
+
+// 第三方平台维护
+Route::group(['namespace'=>'Wechat\ThirdWx'],function(){
+	Route::any('wx/receive_ticket', 'MsgsController@receive_component_verify_ticket');
+	
+	Route::any('oauth/oauth_platform', 'OauthPlatformsController@oauth_platform');
+// 	Route::any('oauth/oauth_platform_callback', 'OauthPlatformsController@oauth_platform_callback');
+	Route::any('oauth/{param}/oauth_platform_callback', 'OauthPlatformsController@oauth_platform_callback')->where('param', '(.*\*.*)?');
+	
+	Route::any('oauth/auto_set_menu_and_template_test', 'OauthPlatformsController@auto_set_menu_and_template_test');
+	
+});
+
+// 菜单设置
+Route::group(['namespace'=>'Wechat\Menu'],function(){
+	Route::any('menu/set_menu', 'MenusController@set_menu');
+});
+
+// 模板消息设置
+Route::group(['namespace'=>'Wechat\Template'],function(){
+	Route::any('template/auto_set_dustry_templates', 'TemplateBasesController@auto_set_dustry_templates');
+	Route::any('template/get_industry', 'TemplateBasesController@get_industry');
+	Route::any('template/get_private_templates', 'TemplateBasesController@get_private_templates');
+	Route::any('template/delete_private_template', 'TemplateBasesController@delete_private_template');
+	Route::any('template/set_template_notices', 'TemplateBasesController@set_template_notices');
+});
+
+// 公众号消息回调
+Route::group(['namespace'=>'Wechat\GzhMsg'],function(){
+    // 	http://domain/$APPID$/callback
+	Route::any('{appid}/callback','GzhMsgsController@index')->where('appid', '(wx.*)?');
+	Route::any('{appid}/test_unit_func','GzhMsgsController@test_unit_func')->where('appid', '(wx.*)?');
+	
+});
+
+// 网页授权
+Route::group(['namespace'=>'Wechat\Oauth'],function(){
+	Route::any('wechat/user_oauth','UserOauthsController@user_oauth');
+	Route::any('user_oauth','UserOauthsController@user_oauth');
+	Route::any('oauth_callback','UserOauthsController@oauth_callback');
+	Route::any('oauth/show_openid','UserOauthsController@show_openid');
+});
+
+// 微信支付
+Route::group(['namespace'=>'Wechat\Pay'],function(){
+	Route::any('pay/create_wxpay_order','PaysController@create_wxpay_order');
+	Route::any('pay/notify','PaysController@notify');
+	Route::any('create_wxpay_order','PaysController@create_wxpay_order');
+	Route::any('pay/index','PaysController@index');
+});
+
+// 数据统计
+Route::group(['namespace'=>'Wechat\Statistic'],function(){
+});
+
+// 对外接口(被其他项目调用)
+Route::group(['namespace'=>'Wechat\Api'],function(){
+	Route::any('api/get_qrcode_url','WechatOuterApisController@get_qrcode_url');
+	Route::any('api/upload_gzh_img','WechatOuterApisController@upload_gzh_img');
+	Route::any('api/upload_gzh_article','WechatOuterApisController@upload_gzh_article');
+	Route::any('api/upload_gzh_articles','WechatOuterApisController@upload_gzh_articles');
+	Route::any('api/upload_material_img','WechatOuterApisController@upload_material_img');
+	Route::any('api/add_public_template','WechatOuterApisController@add_public_template');
+	Route::any('api/get_full_official_account_users','WechatOuterApisController@get_full_official_account_users');
+	Route::any('api/check_official_account_templates','WechatOuterApisController@check_official_account_templates');
+	Route::any('api/del_menu','WechatOuterApisController@del_menu');
+	Route::any('api/get_userinfo','WechatOuterApisController@get_userinfo');
+	Route::any('api/get_short_url','WechatOuterApisController@get_short_url');
+	Route::any('api/check_template_status','WechatOuterApisController@check_template_status');
+	
+	Route::any('api/get_authorizer_list','WechatOpApisController@get_authorizer_list');
+	
+	//获取用户统计
+    Route::any('api/get_gzh_statistics','WechatOuterApisController@get_gzh_statistics');
+
+    //test
+	Route::any('api/test_connect_db','TestApisController@test_connect_db');
+	Route::any('api/test_connect_redis','TestApisController@test_connect_redis');
+	Route::any('api/test_search','TestApisController@test_search');
+	
+});
+
+// 定时调度接口
+Route::group(['namespace'=>'Wechat\Command'],function(){
+	Route::any('api/check_gzh_ban','GzhBanAlertController@check_gzh_ban');
+
+});
+
+
+// 消息队列
+Route::group(['namespace'=>'Queue\Template'],function(){
+	Route::any('queue/add_template_task','AddTemplateTasksController@add_template_task');
+	Route::any('queue/test_send_template','AddTemplateTasksController@test_send_template');
+	
+	Route::any('queue/add_news_task','AddNewsTasksController@add_news_task');
+	Route::any('queue/test_send_news','AddNewsTasksController@test_send_news');
+	
+	Route::any('queue/add_texts_task','AddTextsTasksController@add_texts_task');
+	Route::any('queue/test_send_texts','AddTextsTasksController@test_send_texts');
+	
+	Route::get('queue/test','QueuedController@Test');
+	
+});
+
+Route::get('/test','QueuedController@Test');
+
+// 微信验证文件MP_verify_llzpVQoJadKcUCRR.txt
+// Route::redirect('/here', '/there', 301);
+Route::any('wechat/MP_verify{param}.txt', 'Wechat\Api\MPverifysController@mp_verify')->where('param', '(.*)?');
+Route::any('MP_verify{param}.txt', 'Wechat\Api\MPverifysController@mp_verify')->where('param', '(.*)?');
+
+

+ 55 - 0
app/Jobs/ActionTrigger.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Jobs\Job;
+use Artisan;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * 动作触发器,兼容所有扔队列需要异步处理的请求
+ * @author zhoulingjie
+ *
+ */
+class ActionTrigger implements ShouldQueue
+{
+    use DispatchesJobs, InteractsWithQueue, Queueable, SerializesModels;
+
+    protected $data;
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($data)
+    {
+        //
+        \Log::info('action_trigger_init');
+        $this->data = $data;
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+    	$data =  $this->data;
+    	$data = isset($data['data'])?$data['data']:null;
+    	\Log::info('ActionTrigger_start');\Log::info($data);
+    	echo json_encode($this->data);
+        try {
+        	\Log::info('ActionTrigger:'.$data['action_type'].' start');
+
+            Artisan::call('ActionTrigger:'.$data['action_type'],['data'=>$data]);
+
+        } catch (\Exception $e) {
+            \Log::info('action_trigger_ept:'.$e->getMessage());
+        }
+    }
+}

+ 93 - 0
app/Jobs/ForceUserProperty.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Jobs\Job;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use DB;
+use Redis;
+use WechatOP;
+use Log;
+
+class ForceUserProperty extends Job implements ShouldQueue
+{
+    use InteractsWithQueue, SerializesModels;
+    private $data;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($data)
+    {
+        $data['send_time']=date("Y-m-d H:i:s");
+        $this->data = $data;
+    }
+ 
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        try{
+            $appid = $this->data['appid'];
+            $openid = $this->data['openid'];
+            $current_time = date('Y-m-d H:i:s');
+            $user_data = DB::connection('zhuishuyun_user')->table('force_subscribe_user_properties')->where('appid',$appid)->where('openid',$openid)->first();
+
+            if(!$user_data)
+            {
+                $redis_key = '[wechat_op.common.component_refresh_token.'.$appid.']';
+                $component_refresh_token = Redis::Get($redis_key);
+
+                $options = [
+                    'app_id'    => $appid,
+                    'secret'    => env('WECHAT_OP_SECRET'),   // 仅适用于 单独配置公众号
+                    'token'     => env('WECHAT_OP_TOKEN'),   // 仅适用于 单独配置公众号
+                    'aes_key'   => env('WECHAT_OP_AES_KEY'),   // 仅适用于 单独配置公众号
+                    'auth_type' => 'COMPONENT', // COMPONENT 开放平台授权公众号,MANUAL 单独配置公众号
+                    'component_refresh_token' => $component_refresh_token,   // 授权回调时获取的 authorizer_refresh_token,仅适用于 开放品台授权公众号
+                    'cache'   => [
+                        'driver' => 'redis',   // redis, filesystem, laravel
+                        'dir' => storage_path('tmp') // 只有为filesystem时候这个目录才有效
+                    ],
+                ];
+                $app = WechatOP::app($options);
+                $info = $app->user->get($openid);
+                $nickname = $info->nickname;
+                $sex = $info->sex;
+                $data = [
+                    'openid'=>$openid,
+                    'appid'=>$appid,
+                    'current_nickname'=>trim($nickname),
+                    'sex'=>$sex,
+                    'created_at'=>$current_time,
+                    'updated_at'=>$current_time,
+                ];
+                DB::connection('zhuishuyun_user')->table('force_subscribe_user_properties')->insert($data);
+            }
+        }
+        catch(\Exception $e){
+        	v('handle_ept: info:'.$e->getMessage());
+        }
+
+    }
+    
+    /**
+     * The job failed to process.
+     *
+     * @param  Exception  $exception
+     * @return void
+     */
+    public function failed(Exception $exception)
+    {
+    	// Send user notification of failure, etc...
+    	v('sendNews_ept:'.$exception->getMessage().' data:'.json_encode($this->data));
+    }
+    
+}

+ 21 - 0
app/Jobs/Job.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Jobs;
+
+use Illuminate\Bus\Queueable;
+
+abstract class Job
+{
+    /*
+    |--------------------------------------------------------------------------
+    | Queueable Jobs
+    |--------------------------------------------------------------------------
+    |
+    | This job base class provides a central location to place any logic that
+    | is shared across all of your jobs. The trait included with the class
+    | provides access to the "onQueue" and "delay" queue helper methods.
+    |
+    */
+
+    use Queueable;
+}

+ 33 - 0
app/Jobs/Queue.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Jobs\Job;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Contracts\Queue\ShouldQueue;
+
+class Queue extends Job implements ShouldQueue
+{
+    use InteractsWithQueue, SerializesModels;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        //
+    }
+}

+ 36 - 0
app/Jobs/QueuedTest.php

@@ -0,0 +1,36 @@
+<?php
+ 
+namespace App\Jobs;
+ 
+use App\Jobs\Job;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Contracts\Queue\ShouldQueue;
+ 
+class QueuedTest extends Job implements ShouldQueue
+{
+    use InteractsWithQueue, SerializesModels;
+    private $data;
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($data)
+    {
+        //
+        $data['date']=date("Y-m-d H:i:s");
+        $this->data = $data;
+    }
+ 
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        //
+        echo json_encode($this->data);
+    }
+}

+ 115 - 0
app/Jobs/SendBatchWechatMaterial.php

@@ -0,0 +1,115 @@
+<?php
+
+namespace App\Jobs;
+use App\Http\Controllers\WechatController;
+use App\Jobs\Job;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Redis;
+use DB;
+class SendBatchWechatMaterial extends Job implements ShouldQueue
+{
+    use InteractsWithQueue, SerializesModels;
+    private $data;
+
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($data)
+    {
+        $this->data = $data;
+    }
+ 
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        \Log::info('SendBatchWechatMaterial start');
+        $param = $this->data;
+        \Log::info($param);
+
+        try{
+            $param = $param['data'];
+            $this->start($param);
+        }catch(\Exception $e){
+            \Log::error($e->getMessage());
+        }
+
+
+        \Log::info('SendBatchWechatMaterial end');
+
+    }
+
+    public function start($param)
+    {
+        \Log::info('$param2');\Log::info($param);
+
+        //检查参数
+        if(!($param['appid']&&$param['media_id']&&$param['openids']&&$param['task_id'])){
+            \Log::info('params_empty');
+            return;
+        }
+        
+        // 只有1个用户,则加一个凑数
+        if(count($param['openids']) == 1){
+            $param['openids'][] = 'test';
+        }
+        
+        //服务号高级发送接口openid限定数量为2-10000之间
+        if(!is_array($param['openids'])||count($param['openids'])>10000||count($param['openids'])<2){
+            \Log::info('SendBatchWechatMaterial:over_open_num_limt');
+            return;
+        }
+
+        //调用服务号高级发送接口
+        try{
+            $WechatController = new WechatController($param['appid']);
+            $res = $WechatController->app->broadcast->sendNews($param['media_id'],$param['openids']);
+            \Log::info('sendNews_res:');\Log::info($res);
+            //发送失败
+            if($res['errcode']!=0){
+                \Log::info("SendBatchWechatMateria {$param['task_id']} errcode:{$res['errcode']} errmsg:{{$res['errcode']}}");
+                return;
+            }
+            //保存msg_id和msg_data_id
+            DB::connection('api_mysql')
+                ->table('wechat_material_send_msgs')
+                ->where('id',$param['task_id'])
+                ->update(['wechat_msg_id'=>$res['msg_id'],'wechat_msg_data_id'=>$res['msg_data_id']]);
+            //发送成功更新发送人数
+            DB::connection('api_mysql')
+                ->table('wechat_material_send_msgs')
+                ->where('id',$param['task_id'])
+                ->increment('receive_user_num',count($param['openids']));
+            //若为最后一条消息则更新发送状态为发送完成
+            if($param['type']=='last_task'){
+                DB::connection('api_mysql')
+                    ->table('wechat_material_send_msgs')
+                    ->where('id',$param['task_id'])
+                    ->update(['status'=>'has_send','updated_at'=>date('Y-m-d H:i:s')]);
+            }
+        }catch(\Exception $e){
+            \Log::error('sendNews_ept:'.$e->getMessage());
+        }
+        return;
+    }
+    
+    /**
+     * The job failed to process.
+     *
+     * @param  Exception  $exception
+     * @return void
+     */
+    public function failed(Exception $exception)
+    {
+    	// Send user notification of failure, etc...
+    	v('SendBatchWechatMaterial_ept:'.$exception->getMessage().' data:'.json_encode($this->data));
+    }
+    
+}

+ 0 - 0
app/Jobs/SendNews.php


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor