From d1cc1b6c44ed1deb16ddd21d8194eebb924c51ef Mon Sep 17 00:00:00 2001 From: Chen XinYong Date: Wed, 24 Jul 2019 16:24:28 +0800 Subject: [PATCH] first commit --- .gitignore | 7 ++ LICENSE | 20 +++ README.md | 92 ++++++++++++++ composer.json | 34 +++++ ..._07_23_141906_create_wechat_menu_table.php | 47 +++++++ routes/web.php | 7 ++ .../Controllers/AdminWechatMenuController.php | 116 ++++++++++++++++++ .../AdminWechatReplyController.php | 97 +++++++++++++++ src/Models/AdminWechatMenu.php | 43 +++++++ src/Models/AdminWechatReply.php | 28 +++++ src/Tools/ApplyMenu.php | 99 +++++++++++++++ src/WechatManager.php | 42 +++++++ src/WechatManagerServiceProvider.php | 26 ++++ 13 files changed, 658 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 composer.json create mode 100644 database/migrations/2019_07_23_141906_create_wechat_menu_table.php create mode 100644 routes/web.php create mode 100644 src/Http/Controllers/AdminWechatMenuController.php create mode 100644 src/Http/Controllers/AdminWechatReplyController.php create mode 100644 src/Models/AdminWechatMenu.php create mode 100644 src/Models/AdminWechatReply.php create mode 100644 src/Tools/ApplyMenu.php create mode 100644 src/WechatManager.php create mode 100644 src/WechatManagerServiceProvider.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d4b362 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +phpunit.phar +/vendor +composer.phar +composer.lock +*.project +.idea/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..229071a --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jens Segers + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b3531f --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +laravel-admin 微信公众号管理插件 +====== + +目前实现了简单的自定义菜单和自定义关键字回复功能 + +#### 安装 +`composer require phpcxy/wechat-manager` + +#### 发布资源 +``` +php artisan vendor:publish --tag=wechat-manager-migrations +``` + +#### 数据迁移 + +``` +php artisan migrate +``` + +#### 发布菜单 +``` +php artisan admin:import wechat-manager +``` + +#### 配置微信 +在.env写入以下配置 +``` +WECHAT_OFFICIAL_ACCOUNT_APPID=公众号appid +WECHAT_OFFICIAL_ACCOUNT_SECRET=公众号app secret +WECHAT_OFFICIAL_ACCOUNT_TOKEN=token +WECHAT_OFFICIAL_ACCOUNT_AES_KEY= +``` + +#### 使用说明 + +0. 使用[laravel-wechat](https://github.com/overtrue/laravel-wechat)调用微信SDK,具体使用请查看文档 + +1. 菜单使用了[model-tree](http://laravel-admin.org/docs/zh/model-tree)来管理,编辑好菜单后点击`发布菜单`按钮进行发布 + +2. 获取指定关键字的回复 +``` +$text = WechatManager::getReply('keyword'); +``` +即可返回该关键字设置的回复信息,如果该关键字有多个回复则会随机获取一个返回。 + +使用laravel-wechat的话,可以在微信消息服务端那里这样使用 +``` + +/** + * 接收微信消息和事件 + * @param Request $request + * @return + */ +public function server(Request $request) +{ + $wechat = app('wechat.official_account'); + $wechat->server->push(function($message) { + + $type = $message['MsgType']; + + switch ($type) { + case 'text': + $content = $message['Content']; + $reply = WechatManager::getReply($content); + if ($reply) { + return $reply; + } + break; + case 'event': + // 菜单的点击回复使用了CLICK事件,所以需要在事件这里获取下回复内容 + if ($message['Event'] === 'CLICK') { + $reply = WechatManager::getReply($message['EventKey'], 'menu'); + if ($reply) { + return $reply; + } + } + + break; + + default: + return 'hello world'; + break; + + } + + }); + + return $this->wechat->server->serve(); +} + +``` + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..6b566dc --- /dev/null +++ b/composer.json @@ -0,0 +1,34 @@ +{ + "name": "phpcxy/wechat-manager", + "description": "wechat manager extension for laravel-admin", + "type": "library", + "keywords": ["laravel-admin", "extension", "wechat"], + "homepage": "https://github.com/phpcxy/wechat-manager", + "license": "MIT", + "authors": [ + { + "name": "phpcxy", + "email": "phpcxy@gmail.com" + } + ], + "require": { + "php": ">=7.0.0", + "encore/laravel-admin": "^1.7.3", + "overtrue/laravel-wechat": "~4.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0" + }, + "autoload": { + "psr-4": { + "Phpcxy\\WechatManager\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Phpcxy\\WechatManager\\WechatManagerServiceProvider" + ] + } + } +} diff --git a/database/migrations/2019_07_23_141906_create_wechat_menu_table.php b/database/migrations/2019_07_23_141906_create_wechat_menu_table.php new file mode 100644 index 0000000..30a7d4c --- /dev/null +++ b/database/migrations/2019_07_23_141906_create_wechat_menu_table.php @@ -0,0 +1,47 @@ +increments('id'); + $table->string('title'); + $table->unsignedInteger('parent_id')->default(0); + $table->unsignedInteger('order')->default(0); + $table->string('type', 16)->nullable(); + $table->string('key', 16)->nullable(); + $table->text('value')->nullable(); + $table->timestamps(); + }); + + Schema::create('admin_wechat_reply', function (Blueprint $table) { + $table->increments('id'); + $table->string('key'); + $table->text('value'); + $table->string('source', 16); + $table->string('type', 16); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('admin_wechat_menu'); + Schema::dropIfExists('admin_wechat_reply'); + } +} diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..6207574 --- /dev/null +++ b/routes/web.php @@ -0,0 +1,7 @@ +types = [ + 'view' => '链接', + 'text' => '文字', + 'click' => '事件' + ]; + } + + public function index(Content $content) + { + $content->header('微信自定义菜单'); + $content->body(AdminWechatMenu::tree(function($tree) { + $tree->tools(function (Tools $tools) { + $tools->add(new ApplyMenu()); + }); + + $tree->branch(function ($branch) { + return "{$branch['title']} {$this->types[$branch['type']]}"; + }); + })); + + return $content; + } + + /** + * Redirect to edit page. + * + * @param int $id + * + * @return \Illuminate\Http\RedirectResponse + */ + public function show($id) + { + return redirect()->route('menu.edit', ['id' => $id]); + } + + /** + * Make a form builder. + * + * @return Form + */ + protected function form() + { + $form = new Form(new AdminWechatMenu); + $form->text('title', __('名称'))->rules('required'); + $form->select('parent_id', '父级菜单')->options(AdminWechatMenu::selectOptions()) + ->rules('required'); + + $form->select('type', '类型')->options($this->types)->help("如果是一级菜单下还有二级菜单,无需选择") + ->rules(function($form) { + if (request()->get('parent_id') > 0) { + return 'required'; + } + }); + + $form->textarea('value', '内容')->help("如果是一级菜单下还有二级菜单,无需填写") + ->rules(function($form) { + if (request()->get('type')) { + return 'required'; + } + }); + + // 表单回调 + $form->saved(function (Form $form) { + if ($form->type == 'text') { + if (!$form->model()->key) { + $form->model()->key = 'M_' . Str::random(6); + $form->model()->save(); + } + + $reply = AdminWechatReply::where('key', $form->model()->key)->first(); + if ($reply) { + $reply->value = $form->value; + $reply->save(); + } else { + AdminWechatReply::create([ + 'key' => $form->model()->key, + 'source' => 'menu', + 'type' => 'text', + 'value' => $form->value + ]); + } + } + }); + + return $form; + } +} \ No newline at end of file diff --git a/src/Http/Controllers/AdminWechatReplyController.php b/src/Http/Controllers/AdminWechatReplyController.php new file mode 100644 index 0000000..28379a2 --- /dev/null +++ b/src/Http/Controllers/AdminWechatReplyController.php @@ -0,0 +1,97 @@ +types = [ + 'text' => '文字' + ]; + + $this->source = [ + 'menu' => '自定义菜单', + 'reply' => '关键字回复', + 'welcome' => '欢迎语' + ]; + } + + protected function grid() + { + $grid = new Grid(new AdminWechatReply); + + $grid->model()->where('source', '!=', 'menu'); + + $grid->column('id', __('ID')); + $grid->column('key', __('关键字')); + + $source = $this->source; + $grid->column('source', __('来源'))->display(function($value) use ($source) { + return $source[$value]; + })->label('primary'); + + $types = $this->types; + $grid->column('type', __('类型'))->display(function($type) use ($types) { + return $types[$type]; + })->label('danger'); + + return $grid; + } + + + /** + * Redirect to edit page. + * + * @param int $id + * + * @return \Illuminate\Http\RedirectResponse + */ + public function show($id) + { + return redirect()->route('reply.edit', ['id' => $id]); + } + + /** + * Make a form builder. + * + * @return Form + */ + protected function form() + { + $form = new Form(new AdminWechatReply); + + // 表单内不创建menu来源 + $source = $this->source; + Arr::forget($source, 'menu'); + $form->select('source', '来源')->options($source)->rules('required'); + + $form->text('key', '关键字')->rules('required_unless:source,welcome'); + $form->textarea('value', '回复的内容')->rules('required'); + $form->hidden('type')->default('text')->rules('required'); + + return $form; + } +} \ No newline at end of file diff --git a/src/Models/AdminWechatMenu.php b/src/Models/AdminWechatMenu.php new file mode 100644 index 0000000..969f5bc --- /dev/null +++ b/src/Models/AdminWechatMenu.php @@ -0,0 +1,43 @@ +type === 'text') { +// $res = AdminWechatReply::where('key', $value)->first(); +// return $res->value; +// } else { +// return $value; +// } +// } +} diff --git a/src/Models/AdminWechatReply.php b/src/Models/AdminWechatReply.php new file mode 100644 index 0000000..80031cb --- /dev/null +++ b/src/Models/AdminWechatReply.php @@ -0,0 +1,28 @@ +toTree(); + + $menuArray = []; + foreach ($menus as $menu) { + $array = []; + // 一级菜单没有设置内容和子菜单就报错 + if ($menu['parent_id'] === 0 && (empty($menu['type']) && count($menu['children']) === 0)) { + $menuArray = []; + break; + } + + $array['name'] = $menu['title']; + + // 子菜单的处理 + if (count($menu['children']) > 0) { + foreach ($menu['children'] as $child) { + + // 子菜单下还有子菜单 异常退出 + if (count($child['children']) > 0) { + $menuArray = []; + break 2; + } + + $sub = []; + $sub['name'] = $child['title']; + + $this->generateMenuKeyValue($child, $sub); + + $array['sub_button'][] = $sub; + } + } else { + $array['type'] = $menu['type']; + $this->generateMenuKeyValue($menu, $array); + } + + $menuArray[] = $array; + } + + if (count($menuArray) == 0) { + return $this->response()->error("发布失败,请检查自定义菜单的结构"); + } + + $app = app('wechat.official_account'); + + $ret = $app->menu->create($menuArray); + if ($ret['errcode'] === 0) { + return $this->response()->success('发布成功'); + } else { + return $this->response()->error("发布失败:{$ret['errmsg']}"); + } + } + + public function html() + { + return <<发布菜单 +HTML; + } + + public function dialog() + { + $this->confirm('确定发布菜单吗?'); + } + + /** + * @param $menu + * @param array $array + * @return void + */ + private function generateMenuKeyValue($menu, array &$array) + { + if ($menu['type'] === 'view') { + $array['url'] = $menu['value']; + $array['type'] = $menu['type']; + } elseif ($menu['type'] === 'text') { + $array['key'] = $menu['key']; + $array['type'] = 'click'; + } else { + $array['key'] = $menu['value']; + $array['type'] = $menu['type']; + } + } +} \ No newline at end of file diff --git a/src/WechatManager.php b/src/WechatManager.php new file mode 100644 index 0000000..951bcdb --- /dev/null +++ b/src/WechatManager.php @@ -0,0 +1,42 @@ +where('source', $source)->where('key', $content)->get(); + + if ($replies->count() > 0) { + return $replies->random()->value; + } + } + +} \ No newline at end of file diff --git a/src/WechatManagerServiceProvider.php b/src/WechatManagerServiceProvider.php new file mode 100644 index 0000000..ee6d85e --- /dev/null +++ b/src/WechatManagerServiceProvider.php @@ -0,0 +1,26 @@ +app->runningInConsole() && $migrations = $extension->migrations()) { + $this->publishes([$migrations => database_path('migrations')], 'wechat-manager-migrations'); + } + + $this->app->booted(function () { + WechatManager::routes(__DIR__.'/../routes/web.php'); + }); + } +} \ No newline at end of file