微信客服聊天的开发
实现的目标
前端只要有人点击在线客服,进入聊天窗口,就发送后台设置的自动回复内容和图片。
后台可以把小程序里的客服列表,拉取出来,为每个客服设置不同的自动回复内容和图片。
环境:我们属于第三方开发者,后台是绑定多个小程序的。
步骤:
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. 结束。这就是我的所有代码了。
感觉我封装的还不错的话,给我推荐个好工作吧。