TP6+swoole+easywechat使用

464 阅读2分钟

安装

安装think-swoole

composer require topthink/think-swoole

安装easywechat

composer require overtrue/wechat:~5.0 -vvv

前言

在结合think-swoole+easywechat扩展使用的时候,需要考虑curl兼容swoole携程问题,request兼容swoole框架,因为easywechat底层还是通过$_POST或者其他来获取请求参数。

还有就是好多的接口基本没有。需要自己写,因为这里安装的是5.0的版本。在6.0版本后easywecaht不在写操作接口的相关逻辑只提供了一些授权后的接口封装、请求封装、日志封装等等。个人还是觉得5.0版本够用了。然后就选择了5.0的版本来开发。5.0版本中例如,直播这块的接口逻辑需要自己写点、企业微信进群配置这些等等。

使用前配置

请在app/AppService.phpboot方法内增加配置默认请求类

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类里面事件类型

<?php

namespace app\listener\wechat;
use app\services\user\label\UserLabelServices;
use app\services\work\WorkClientServices;
use app\services\work\WorkDepartmentServices;
use app\services\work\WorkGroupChatServices;
use app\services\work\WorkMemberServices;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;

/** * 企业微信服务消息处理 
* Class WorkListener 
* @package app\listener\wechat 
*/
class WorkListener implements EventHandlerInterface
{    
    public function handle($payload = null)   
     {        
        $response = null;        
        switch ($payload['MsgType']) {            
            case 'event':                
            switch ($payload['Event']) {                    
                case 'change_contact'://通讯录事件                        
                    $this->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();

}