前言
在结合think-swoole+easywechat扩展使用的时候,需要考虑curl兼容swoole携程问题,request兼容swoole框架,因为easywechat底层还是通过$_POST或者其他来获取请求参数。
还有就是好多的接口基本没有。需要自己写,因为这里安装的是5.0的版本。在6.0版本后easywecaht不在写操作接口的相关逻辑只提供了一些授权后的接口封装、请求封装、日志封装等等。个人还是觉得5.0版本够用了。然后就选择了5.0的版本来开发。5.0版本中例如,直播这块的接口逻辑需要自己写点、企业微信进群配置这些等等。下面我们就看详细的实例教程:
安装
安装think-swoole
composer require topthink/think-swoole
安装easywechat
composer require overtrue/wechat:~5.0 -vvv
使用前配置
请在app/AppService.php的boot方法内增加配置默认请求类
use Yurun\Util\Swoole\Guzzle\SwooleHandler; DefaultHandler::setDefaultHandler(SwooleHandler::class);
例如这里实例化一个企业微信相关的
use EasyWeChat\Work\Application;
use Yurun\Util\Swoole\Guzzle\SwooleHandler;
$type = 'user';
$config=[
'corp_id'=>'',
'token'=>'',
];//实例化企业微信
$application[$type] = Factory::work($config);
//这里是为了兼容swoole的curl携程
$application[$type]['guzzle_handler'] = SwooleHandler::class;
$request = request();
//在swoole模式运行下,需要从think\request下获取请求信息,这一步十分重要
$application[$type]->rebind('request', new Request($request->get(), $request->post(), [], [], [], $request->server(), $request->getContent()));$this->application[$type]->register(new ServiceProvider());
使用前这些配置都需要增加的。这里也可以封装成类来进行调用
为什么要用type来区分实例化类型:
因为在企业微信下会有多种服务实例化对象,例如客户、自建应用、通讯录都会产生不同的实例化对象
构建企业微信服务首先改造下刚才实例化的方式:
use crmeb\services\wechat\groupChat\ServiceProvider;
use Yurun\Util\Swoole\Guzzle\SwooleHandler;use EasyWeChat\Work\Application;
use Symfony\Component\HttpFoundation\Request;
class Work extends BaseApplication
{
/**
* @var WorkConfig
*/
protected $config;
/**
* @var Application[]
*/
protected $application = [];
/**
* @var string */
protected $configHandler;
/**
* @var string[]
*/
protected static $property = [
'groupChat' => 'external_contact',
'groupChatWelcome' => 'external_contact_message_template'
];
/**
* Work constructor.
*/
public function __construct()
{
/** @var WorkConfig config */
$this->config = app()->make(WorkConfig::class);
$this->debug = DefaultConfig::value('logger');
}
/**
* 设置获取配置
* @param string $handler
* @return $this
*/
public function setConfigHandler(string $handler)
{
$this->configHandler = $handler;
return $this;
}
/**
* @return Work
*/
public static function instance()
{
return app()->make(static::class);
}
/**
* 获取实例化句柄
* @param string $type
* @return Application
*/
public function application(string $type = WorkConfig::TYPE_USER){
$config = $this->config->all();
$config = array_merge($config, $this->config->setHandler($this->configHandler)->getAppConfig($type));
if (!isset($this->application[$type])) {
$this->application[$type] = Factory::work($config);
$this->application[$type]['guzzle_handler'] = SwooleHandler::class;
$request = request();
$this->application[$type]->rebind('request', new Request($request->get(), $request->post(), [], [], [], $request->server(), $request->getContent()));
$this->application[$type]->register(new ServiceProvider());
}
return $this->application[$type];
}
}
企业微信服务
这里说明下,swoole里面尽量少用静态方法,而这里使用了的原因是,使用了app->make()实例化了当前类。
use think\Response;
/**
* 服务端
* @return Response
* @throws BadRequestException
* @throws InvalidArgumentException
* @throws InvalidConfigException
* @throws \ReflectionException
*/
public static function serve(): Response
{
$make = self::instance();
$make->application()->server->push($make->pushMessageHandler);
$response = $make->application()->server->serve();
return response($response->getContent());
}
首先设置pushMessageHandler类,打开app/AppService.php。在register方法中注册服务器相应事件类 ,例如app\listener\wechat\WorkListener 类引入
use crmeb\services\wechat\config\HttpCommonConfig;
use crmeb\services\wechat\config\LogCommonConfig;
use crmeb\services\wechat\config\WorkConfig;
use app\services\work\WorkConfigServices;
public function register()
{
//实例化企业微信配置
$this->app->bind(WorkConfig::class, function () {
return (new WorkConfig(new LogCommonConfig(), $this->app->make(HttpCommonConfig::class)))
->setHandler(WorkConfigServices::class);
});
//企业微信
$this->app->bind(Work::class, function () {
return (new Work)->setPushMessageHandler(WorkListener::class)
->setConfigHandler(WorkConfigServices::class);
});
}
一定要配置企业微信服务器相应的事件类WorkListener这里列举出WorkListener类里面事件类型
changeContactEvent($payload);
break;
case 'change_external_chat'://客户群事件
$this->changeExternalChatEvent($payload);
break;
case 'change_external_contact'://客户事件
$this->externalContactEvent($payload);
break;
case 'change_external_tag'://客户标签事件
$this->changeExternalTagEvent($payload);
break;
case 'batch_job_result'://异步任务完成通知
$this->batchJobResultEvent($payload);
break;
}
break;
case 'text'://文本消息
break;
case 'image'://图片消息
break;
case 'voice'://语音消息
break;
case 'video'://视频消息
break;
case 'news'://图文消息
break;
case 'update_button'://模板卡片更新消息
break;
case 'update_template_card'://更新点击用户的整张卡片
break;
}
return $response;
}
public function batchJobResultEvent(array $payload)
{
switch ($payload['JobType']) {
case 'sync_user'://增量更新成员
break;
case 'replace_user'://全量覆盖成员
break;
case 'invite_user'://邀请成员关注
break;
case 'replace_party'://全量覆盖部门
break;
}
}
/**
* 企业微信通讯录事件
* @param array $payload
* @return null
*/
public function changeContactEvent(array $payload)
{
$response = null;
try {
switch ($payload['ChangeType']) {
case 'create_user'://新增成员事件
break;
case 'update_user'://更新成员事件
break;
case 'delete_user'://删除成员事件
break;
case 'create_party'://新增部门事件
break;
case 'update_party'://更新部门事件
break;
case 'delete_party'://删除部门事件
break;
case 'update_tag'://标签成员变更事件
break;
}
} catch (\Throwable $e) {
\think\facade\Log::error([
'message' => '企业微信通讯录事件发生错误:' . $e->getMessage(),
'payload' => $payload,
'file' => $e->getFile(),
'line' => $e->getLine()
]);
}
return $response;
}
/**
* 客户事件
* @param array $payload
* @return |null
*/
public function externalContactEvent(array $payload)
{
$response = null;
try {
switch ($payload['ChangeType']) {
case 'add_external_contact'://添加企业客户事件
/** @var WorkClientServices $make */
$make = app()->make(WorkClientServices::class);
$make->createClient($payload);
break;
case 'edit_external_contact'://编辑企业客户事件
/** @var WorkClientServices $make */
$make = app()->make(WorkClientServices::class);
$make->updateClient($payload);
break;
case 'del_external_contact':
/** @var WorkClientServices $make */
$make = app()->make(WorkClientServices::class);
$make->deleteClient($payload);
break;
case 'add_half_external_contact'://外部联系人免验证添加成员事件
break;
case 'del_follow_user'://删除跟进成员事件
/** @var WorkClientServices $make */
$make = app()->make(WorkClientServices::class);
$make->deleteFollowClient($payload);
break;
case 'transfer_fail'://客户接替失败事件
break;
}
} catch (\Throwable $e) {
\think\facade\Log::error([
'message' => '客户事件发生错误:' . $e->getMessage(),
'payload' => $payload,
'file' => $e->getFile(),
'line' => $e->getLine()
]);
}
return $response;
}
/**
* 客户群事件
* @param array $payload
*/
public function changeExternalChatEvent(array $payload)
{
try {
switch ($payload['ChangeType']) {
case 'create'://客户群创建事件
break;
case 'update'://客户群变更事件
break;
case 'dismiss'://客户群解散事件
break;
}
} catch (\Throwable $e) {
\think\facade\Log::error([
'message' => $e->getMessage(),
'payload' => $payload,
'file' => $e->getFile(),
'line' => $e->getLine()
]);
}
}
/**
* 客户标签事件
* @param array $payload
*/
public function changeExternalTagEvent(array $payload)
{
switch ($payload['ChangeType']) {
case 'create'://企业客户标签创建事件
break;
case 'update'://企业客户标签变更事件
break;
case 'delete'://企业客户标签删除事件
break;
case 'shuffle'://企业客户标签重排事件
break;
}
}
}
下面提供了完整的类
BaseApplication类
use crmeb\services\wechat\contract\BaseApplicationInterface;
/**
* Class BaseApplication
* @package crmeb\services\wechat
*/
abstract class BaseApplication implements BaseApplicationInterface
{
//app端
const APP = 'app';
//h5端、公众端
const WEB = 'web';
//小程序端
const MINI = 'mini';
//开发平台
const OPEN = 'open';
//pc端
const PC = 'pc';
/**
* 访问端
* @var string
*/
protected $accessEnd;
/**
* @var array
*/
protected static $property = [];
/**
* @var string
*/
protected $pushMessageHandler;
/**
* Debug
* @var bool
*/
protected $debug = true;
/**
* 设置消息处理类
* @param string $handler
* @return $this
*/
public function setPushMessageHandler(string $handler)
{
$this->pushMessageHandler = $handler;
return $this;
}
/**
* 设置访问端
* @param string $accessEnd
* @return $this
*/
public function setAccessEnd(string $accessEnd) {
if (in_array($accessEnd, [self::APP, self::WEB, self::MINI])) {
$this->accessEnd = $accessEnd;
}
return $this;
}
/**
* 自动获取访问端
* @param \think\Request $request
* @return string
*/
public function getAuthAccessEnd(\think\Request $request)
{
if (!$this->accessEnd) {
try {
if ($request->isApp()) {
$this->accessEnd = self::APP;
} else if ($request->isPc()) {
$this->accessEnd = self::PC;
} else if ($request->isWechat() || $request->isH5()) {
$this->accessEnd = self::WEB;
} else if ($request->isRoutine()) {
$this->accessEnd = self::MINI;
} else {
$this->accessEnd = self::WEB;
}
} catch (\Throwable $e) {
$this->accessEnd = self::WEB;
}
}
return $this->accessEnd;
}
/**
* 记录错误日志
* @param \Throwable $e
*/
protected static function error(\Throwable $e)
{
static::instance()->debug && \think\facade\Log::error([
'error' => $e->getMessage(),
'line' => $e->getLine(),
' file' => $e->getFile()
]);
}
/**
* 请求日志
* @param string $message
* @param $request
* @param $response
*/
protected static function logger(string $message, $request, $response)
{
$debug = static::instance()->debug;
if ($debug)
{
\think\facade\Log::info([
'message' => $message,
'request' => json_encode($request),
'response' => json_encode($response)
]);
}
}
/**
* @param $name
* @param $arguments
* @return mixed
*/
public static function __callStatic($name, $arguments)
{
if (in_array($name, array_keys(static::$property))) {
$name = static::$property[$name];
return static::instance()->application()->{$name};
}
throw new WechatException('方法不存在');
}
}
BaseApplicationInterface接口类
namespace crmeb\services\wechat\contract;
/**
* Interface BaseApplicationInterface
* @package crmeb\services\wechat\contract
*/
interface BaseApplicationInterface
{
/**
* @return mixed
*/
public static function instance();
/**
* @return mixed
*/
public function application();
}
最后
如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star:http://github.crmeb.net/u/defu不胜感激 !
免费获取源码地址:http://www.crmeb.com
PHP学习手册:https://doc.crmeb.com
技术交流论坛:https://q.crmeb.com