微信客服聊天的开发
实现的目标
前端只要有人点击在线客服,进入聊天窗口,就发送后台设置的自动回复内容和图片。
后台可以把小程序里的客服列表,拉取出来,为每个客服设置不同的自动回复内容和图片。
环境:我们属于第三方开发者,后台是绑定多个小程序的。
步骤:
1. 进入微信小程序后台“客服”模块,找到设置“消息推送的文字”链接,打开滚动到中间,有一个“消息推送”,点击启用。然后设置服务器URL 等信息。
这是我路由中的设置。id是小程序详情表的id。
            /**
             * 小程序客服聊天
             */
            Route::any('{id}/chat','WechatChatController@Chat');                         //小程序客服聊天的消息推送
            Route::any('{id}/uploadTempMedia','WechatChatController@uploadTempMedia');   //小程序图片
服务器接收要验证并返回“echostr”的信息。PHP的话要把接收到echostr信息强制转成字符串一下,不然不通过。
//微信客服收到的消息
    public function Chat(Request $request,$id){
        $wc_id = $id;
        \Log::channel('api')->info('------------');
        \Log::channel('api')->info('requst:'.json_encode($request->all()));
        $FromUserName = $request->input("FromUserName");
        $MsgType = $request->input("MsgType");
        $Event = $request->input("Event");
        //&& $MsgType=='event' &&  $Event =='user_enter_tempsession'
        if ( !empty($FromUserName) )
        {
            //查找后台设置的客服
            $kf_info = MerchantConsult::getOneKfInfo($wc_id);
            \Log::channel('api')->info('查找客服配置:'.json_encode($kf_info));
            if (!empty($kf_info))
            {
                \Log::channel('api')->info('~~~~~~');
                MiniApp::send_text($wc_id,$FromUserName,$kf_info['msg']);
                \Log::channel('api')->info('~~~~~22~');
                $response = MiniApp::send_img($wc_id,$FromUserName,$kf_info['media_id']);
                \Log::channel('api')->info('------------'.$response.json_encode($response));
            }
        }
        $signature = $request->input("signature");
        $timestamp = $request->input("timestamp");
        $nonce = $request->input("nonce");
        $echostr = $request->input("echostr");
        return $this->checkSignature($signature,$timestamp,$nonce,$echostr);
    }
    //检验signature
    private function checkSignature($signature,$timestamp,$nonce,$echostr)
    {
        $token = 'tyunai';
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );
        \Log::channel('api')->info(json_encode($tmpArr).'  tmpStr: '.$tmpStr.'  signature: '.$signature);
        if ($tmpStr == $signature ) {
            return (string)$echostr;
        } else {
            return false;
        }
    }
2. 小程序后台设置成功之后。就看我的MiniApp里的封装吧。
现在我只封装了这些
MiniApp.php文件
/**
 * Created by PhpStorm.
 * User: 清行
 * Date: 2020/1/6
 * Time: 16:58
 */
namespace App\Common\Wechat\MiniApp;
class MiniApp
{
    use User,Message;
}
User.php
<?php
/**
 * Created by PhpStorm.
 * User: 清行
 * Date: 2020/1/6
 * Time: 17:00
 */
namespace App\Common\Wechat\MiniApp;
use App\Common\Wechat\ThirdParty\Common;
use App\Exceptions\ApiException;
use App\Models\WechatMiniApp;
trait User
{
    //登录凭证校验
    public static function getCode2Session($appid,$appSecret,$js_code)
    {
        $url = 'https://api.weixin.qq.com/sns/jscode2session?appid='.$appid.'&secret='.$appSecret.'&js_code='.$js_code.'&grant_type=authorization_code';
        return Common::wxGet($url);
    }
    //获取小程序全局唯一后台接口调用凭据access_token
    public static function getAccessToken($appid,$appSecret)
    {
        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$appid.'&secret='.$appSecret;
        $response = Common::wxGet($url);
        if (!empty($response['access_token'])){
            return $response['access_token'];
        }else{
            throw new ApiException([$response['errcode'],$response['errmsg']]);
        }
    }
    //
    public static function get_access_token($id)
    {
        $appBase = WechatMiniApp::getAppBase($id);
        return self::getAccessToken($appBase['app_id'],$appBase['app_secret']);
    }
}
Message.php
<?php
/**
 * Created by PhpStorm.
 * User: 清行
 * Date: 2020/2/17
 * Time: 18:22
 */
namespace App\Common\Wechat\MiniApp;
use App\Common\Wechat\ThirdParty\Common;
trait Message
{
    //发送文字类的内容
    public static function send_text($id,$touser,$text)
    {
        $access_token = self::get_access_token($id);
        $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$access_token;
        $params = [
            'touser' => $touser ,
            'msgtype' => 'text' ,
            'text' =>['content' => $text],
        ];
        $response = Common::wxPost($url,$params);
        return $response;
    }
    //发送图片格式的内容
    public static function send_img($id,$touser,$media_id)
    {
        $access_token = self::get_access_token($id);
        $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$access_token;
        $params = [
            'touser' => $touser ,
            'msgtype' => 'image' ,
            'image' =>['media_id' => $media_id],
        ];
        $response = Common::wxPost($url,$params);
        return $response;
    }
    //把媒体文件上传到微信服务器
    public static function uploadTempMedia($id,$path)
    {
        $access_token = self::get_access_token($id);
        $url = 'https://api.weixin.qq.com/cgi-bin/media/upload?access_token='.$access_token.'&type=image';
        return self::newHttpsPost($url,$path);
    }
    //发送图片
    public static function newHttpsPost($url ='' , $path = '' ){
        $curl = curl_init();
        if (class_exists('\CURLFile')){
            curl_setopt($curl, CURLOPT_SAFE_UPLOAD, true);
            $data = array('media' => new \CURLFile($path));//
        }
        else
        {
            curl_setopt($curl,CURLOPT_SAFE_UPLOAD,false);
            $data = array('media'=>'@'.$path);
        }
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POST, 1 );
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_USERAGENT,"TEST");
        $result = curl_exec($curl);
        $res=json_decode($result,true);
        return $res;
    }
    //获取客服列表
    public static function getKFList($id)
    {
        $access_token = self::get_access_token($id);
        $url = 'https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token='.$access_token;
        $response = Common::wxGet($url);
        return $response;
    }
}
这里还用到了我另外一个封装,那是我封装第三方post,get请求的类,也放出来吧
<?php
/**
 * Created by PhpStorm.
 * User: 清行
 * Date: 2019/11/11
 * Time: 15:11
 */
namespace App\Common\Wechat\ThirdParty;
use App\Common\Err\ApiErrDesc;
use App\Exceptions\ApiException;
use App\Common\Curl\Curl;
use Illuminate\Support\Facades\Log;
use SimpleSoftwareIO\QrCode\Facades\QrCode;
class Common
{
    /***
     * 微信的Get请求 参数会urlencode编码一次
     * @param $url           请求地址
     * @param array $params  请求参数
     * @return mixed
     */
    public static function wxGet($url,$params=array())
    {
        $response = self::getJosn($url,$params);
        return self::responseAction($url,$params,$response,'GET');
    }
    /***
     * 微信的post请求
     * @param $url    请求地址
     * @param $params 请求参数
     * @return mixed
     */
    public static function wxPost($url,$params)
    {
        $response = self::postJosn($url,$params);
        return self::responseAction($url,$params,$response,'POST');
    }
    /**
     * get请求
     * @param $url  请求地址
     * @return mixed
     */
    public static function get($url)
    {
        $curl = new Curl();
        $response = $curl->to($url)->asJson( true )->get();
        return $response;
    }
    /**
     * 微信简单的get请求不需要json成数组
     * @param $url  请求地址
     * @return mixed
     */
    public static function wxGetBase($url,$params=array())
    {
        $curl = new Curl();
        $response = $curl->to($url)->withData($params)->get();
        return self::responseAction($url,$params,$response,'GET');
    }
    /***
     * get请求 参数会改为json格式
     * @param $url    请求地址
     * @param $params 请求参数
     * @return mixed
     */
    public static function getJosn($url,$params=array())
    {
        $curl = new Curl();
        $response = $curl->to($url)->withData($params)->asJson( true )->get();
        return $response;
    }
    /***
     * post请求 参数会改为json格式
     * @param $url    请求地址
     * @param $params 请求参数
     * @return mixed
     */
    public static function postJosn($url,$params)
    {
        $curl = new Curl();
        $response = $curl->to($url)->withData( $params)->asJson( true )->post();
        return $response;
    }
    /**
     * 对请求的返回信息进行处理
     * @param $url    请求地址
     * @param $params 请求参数
     * @return mixed
     */
    public static function responseAction($url,$params,$response,$method='')
    {
        if (empty($response['errcode']))
        {
            Log::channel('wechat')->info($method.'请求成功:请求的URL是'.$url.PHP_EOL.'请求的参数是'.json_encode($params).PHP_EOL.'返回内容是'.json_encode($response));
        }else{
            Log::channel('wechat')->error($method.'请求失败:请求的URL是'.$url.PHP_EOL.'请求的参数是'.json_encode($params).PHP_EOL.'返回内容是'.json_encode($response));
            //抛出异常
            if (!empty($response['errcode']) && !empty($response['errmsg'])){
                throw new ApiException([$response['errcode'],$response['errmsg']]);
            }else{
                throw new ApiException(ApiErrDesc::ERR_UNKNOWN);
            }
        }
        return $response;
    }
    /**
     * 生成二维码
     * @param string $string    生成的二维码内容
     * @param int $size         大小
     * @return mixed
     */
    public static function generateQrcode($string='',$size=150)
    {
        return QrCode::size($size)->generate($string);
    }
}
我感觉我写的代码都是精华,干货。
3. 看完微信小程序接口的封装,就该看后台代码了。后台我用的Laravel-admin
<?php
namespace App\Admin\Controllers;
use App\Common\Wechat\MiniApp\MiniApp;
use App\Models\MerchantConsult;
use App\Traits\LaravelAdmin;
use Encore\Admin\Admin;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Show;
class MerchantConsultController extends AdminController
{
    /**
     * Title for current resource.
     *
     * @var string
     */
    use LaravelAdmin;
    protected $title = '客服列表';
    /**
     * Make a grid builder.
     *
     * @return Grid
     */
    protected function grid()
    {
        $grid = new Grid(new MerchantConsult());
        $grid->column('id', __('Id'));
        $this->showMerchantName($grid,'所属商户');
        $grid->column('kf_nick', __('客服名称'));
        $grid->column('img', __('客服二维码'));
        $grid->column('msg', __('客服自动回复消息'));
        $grid->column('status', __('是否启用'))->switch(config('adminCommon.switch_using'));
        $grid->column('kf_wx', __('客服微信号'));
        $grid->column('position_id', __('所在页面位置'))->using(config('adminCommon.kf_position_id'));
        return $grid;
    }
    /**
     * Make a show builder.
     *
     * @param mixed $id
     * @return Show
     */
    protected function detail($id)
    {
        $show = new Show(MerchantConsult::findOrFail($id));
        $show->field('id', __('Id'));
        $show->field('merchant_id', __('Merchant id'));
        $show->field('kf_nick', __('客服名称'));
        $show->field('img', __('客服二维码'))->image();
        $show->field('msg', __('客服自动回复消息'));
        $show->field('status', __('状态'))->using(config('adminCommon.using'));;
        $show->field('kf_id', __('客服ID'));
        $show->field('kf_wx', __('客服微信号'));
        $show->field('kf_headimgurl', __('客服微信头像'))->image();
        $show->field('kf_account', __('客服账号'));
        $show->field('position_id', __('所在页面位置'))->using(config('adminCommon.kf_position_id'));
        return $show;
    }
    /**
     * Make a form builder.
     *
     * @return Form
     */
    protected function form()
    {
        $form = new Form(new MerchantConsult());
        $this->showMerchantName($form,'所属属商户');
        $form->select('kf_id','选择客服');
        $form->image('img', __('客服二维码'))->move('images/merchant/store/consult')->uniqueName()->removable();
        $form->text('msg', __('客服自动回复消息'));
        $form->switch('status', __('是否启用'))->states(config('adminCommon.switch_using'))->default(0);
        $form->radio('position_id', __('所在页面位置'))->options(config('adminCommon.kf_position_id'));
        Admin::script('
            //改变商户时
            $(".merchant_id").on("change", function(){
                getList();
            });
            getList();
            function getList()
            {
                var merchant_id = $(".merchant_id").val();
                
                $.ajax({
                    method: \'get\',
                    url: "/admin/ajax/getWCKFList",
                    data: {
                        _token:LA.token,
                        q:merchant_id,
                    },
                    success: function (data) {
                        $(".kf_id").html("");
                        $.each( data, function(i, row){
                            $(".kf_id").append("<option value=\'"+row["id"]+"\'>"+row["text"]+"<\/option>");
                        });
                    }
                });
            }
            
        ',false);
        //保存前回调
        $form->saving(function (Form $form) {
            $this->getKfInfo($form);
        });
        //保存后回调
        $form->saved(function (Form $form) {
           if( is_file(storage_path('app/public/'.$form->model()->img))){
               $filedate = MiniApp::uploadTempMedia($form->model()->merchant_id,storage_path('app/public/'.$form->model()->img));
               if (!empty($filedate['media_id']))
               {
                   MerchantConsult::updateMediaId($form->model()->id,$filedate['media_id']);
               }
           }
        });
        return $form;
    }
    //根据kf_id获取客服列表的单项所有信息
    public function getKfInfo($form)
    {
        $kf_id = $form->kf_id;
        if (empty($kf_id)) {
            return $form;
        }
        $kf_list = MiniApp::getKFList($form->merchant_id);
        if (!empty($kf_list['kf_list']))
        {
            foreach ($kf_list['kf_list'] as $row )
            {
                if($row['kf_id'] == $form->kf_id)
                {
                    $form->model()->kf_nick = $row['kf_nick'] ;
                    $form->model()->kf_account = $row['kf_account'] ;
                    $form->model()->kf_headimgurl = $row['kf_headimgurl'] ;
                    $form->model()->kf_wx = $row['kf_wx'] ;
                    return $form;
                }
            }
        }
        return $form;
    }
}
4. 结束。这就是我的所有代码了。
感觉我封装的还不错的话,给我推荐个好工作吧。