最近已经是第四次开发微信公众号,这一次的开发任务是完成一个微信企业号的开发, 主要是利用微信企业号进行办公管理。而其中很重要的一个功能即为本地通讯录与微信服务器上的通讯录保持同步。
而基本的实现逻辑为:在本地数据库进行增、删、改操作以后,获得执行返回结果,如果本地操作成功,则调用微信企业号管理通讯录接口,实现对微信服务器上数据的同步操作。但是在这个基本的业务逻辑中,可能会出现微信服务器和本地数据库的数据不统一的情况,其中常见的场景为一下两种:
①本地执行数据增加操作以后,获得返回结果,正要执行微信同步操作时,网络异常,那么会出现本地数据未能同步到微信服务器上——本地存在微信服务器没有的数据。
②本地执行数据删除操作以后,获得返回结果,正要执行微信同步操作时,网络异常,那么会出现本地数据未能同步到微信服务器上——微信服务器存在本地没有的数据。
在没有通讯录异步更新功能之前,我们采取的方法,即是定期利用算法对本地数据与微信数据进行比较,执行相应同步操作,其中带来的系统消耗较大,如果数据量较大,常常出现一些差错,导致两边数据无法维护。
现在来说说微信公众号更新的异步任务接口(位置:管理通讯录—异步任务接口)。有两点说明:①主要用于本地通讯录与微信服务器通讯录的同步。②此接口为异步响应的接口,需要调用获取异步任务结果接口获取操作结果。由于是第三方微信公众号应用,则原则上用户只能在一端实现数据维护,更好的选择即是在第三方的微信应用上进行通讯录的操作,来保证两边的数据同步。
接口关键说明:
1.文件中存在、通讯录中也存在的部门,执行修改操作
2.文件中存在、通讯录中不存在的部门,执行添加操作
3.文件中不存在、通讯录中存在的部门,当部门下没有任何成员或子部门时,执行删除操作
4.CSV文件中,部门名称、部门ID、父部门ID为必填字段,部门ID必须为数字;排序为可选字段,置空或填0不修改排序
接口调用次序
通讯录更新接口提供三种更新方法:1) 增量更新成员 2)全量覆盖成员 3) 全量覆盖部门。如果企业要做到与企业号通讯录完全一致,可先调用全量覆盖部门接口,再调用全量覆盖成员接口,即可保持通讯录完全一致。
根据微信企业号的文档,我们需要实现以下四步操作:
①根据微信企业号文档规定的文档格式,将本地数据库的数据对应生成相应的CSV文件(此处贴出生成CSV文件的PHP示例代码)
/**
* 输出csv文件
* @param $data 上传数组
* @param $filetype 上传文件类型(batch_user/ batch_group)
*/
public function outputCsvFile($data, $fileTypeName) {
$filename = $fileTypeName.time();
$output_url = "source/weixin_upload_room/csv_upload/".$filename.'.csv';
$fp = fopen($output_url, 'w');
fwrite($fp,"\xEF\xBB\xBF");
foreach ($data as $row) {
fputcsv($fp, $row);
}
fclose($fp);
return $output_url;
}
②调用上传临时素材文件接口(
管理素材文件—上传临时素材文件)将生成的CSV文件上传到微信服务器。此处出现了本次开发的第一个难关,因为必须采用非表单上传文件,将指定路径的文件用PHP代码上传到服务器。查阅资料后发现可以使用php curl技术模拟表单上传文件,因此决定采取curl的方法,可是一直调试不成功。再后来发现curl的调用方法也是随着PHP版本的变化,发生了变化,调了两天都一直调不出结果,最后在google上找到了解决方法。
③调用全量覆盖部门接口,进行部门的更新。
④调用全量覆盖成员接口,进行成员的更新。
以下贴出全部的php
curl 模拟 https请求
/**
* 功能:php模拟发送https post请求
* @param $url 请求的链接
* @param $params get请求的所带的参数
* 参数格式为:$params = "a=b&c=d&e=f&g=" . urlencode('全仔');
*/
function https_post($url, $params){
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_HEADER, 0); // 不要http header 加快效率
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_POST, 1); // post 提交方式
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
全量覆盖部门
/**
* 企业号通讯录数据同步:全量覆盖部门
* 原理:将本地数据库与微信企业号全量覆盖。
* 接口限制:应说明清楚本功能,且限制调用次数。且本功能应和全量覆盖成员同步使用!
* @param 无
* @return array("errcode"=>"错误码","errmsg"=>"错误信息","id"=>"微信id") 操作成功错误码为0
*/
public function synchronous_area_to_weixin(){
$res = $this -> db -> get('area');
if($res -> num_rows()){
$area_array[] = array('部门名称','部门ID','父部门ID','排序');
foreach($res -> result_array() as $row){
$area_array[] = array($row['name'],$row['weixin_id'],$row['parent_id'],0);
}
}else{
return false;
}
$file_url = $this -> mytool -> outputCsvFile($area_array,'batch_group');
$access_token = $this -> session -> userdata("access_token");
//判断当前php版本是否支持curlfile
if (class_exists('\CURLFile')) {
$field = array('media' => new \CURLFile(realpath($file_url)));
} else {
$field = array('media' => '@' . realpath($file_url));
}
$media_res = $this->mytool->https_post('https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token='.$access_token['access_token'].'&type=file',$field);
$media_res_obj = json_decode($media_res);
if($media_res_obj -> media_id){
$batch_group = array('media_id' => urlencode($media_res_obj -> media_id));
$batch_group_json = urldecode(json_encode($batch_group));
$batch_group_res = $this->mytool->https_post('https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty?access_token='.$access_token['access_token'],$batch_group_json);
$batch_group_res_obj = json_decode($batch_group_res);
$errmsg = $this->myweixintranslation->tranalation($batch_group_res_obj->errcode);
$res_array = array("errcode" => $batch_group_res_obj->errcode,"errmsg" => $errmsg);
}else{
$errmsg = $this->myweixintranslation->tranalation($media_res_obj->errcode);
$res_array = array("errcode" => $media_res_obj->errcode,"errmsg" => $errmsg);
}
return $res_array;
}
全量覆盖成员
/**
* 企业号通讯录数据同步:全量覆盖成员
* 原理:将本地数据库与微信企业号全量覆盖。
* 接口限制:应说明清楚本功能,且限制调用次数。且本功能应和全量覆盖部门同步使用!
* @param 无
* @return array("errcode"=>"错误码","errmsg"=>"错误信息","id"=>"微信id") 操作成功错误码为0
*/
public function synchronous_distribution_member_to_weixin(){
$res = $this -> db -> get('distribution_member');
if($res -> num_rows()){
$area_array[] = array('姓名','帐号','微信号','手机号','邮箱','所在部门','职位');
foreach($res -> result_array() as $row){
$area_array[] = array($row['name'],$row['weixin_id'],'',$row['phone'],$row['email'],$row['area_id'],'');
}
}else{
return false;
}
$file_url = $this -> mytool -> outputCsvFile($area_array,'batch_user');
//判断当前php版本是否支持curlfile
if (class_exists('\CURLFile')) {
$field = array('media' => new \CURLFile(realpath($file_url)));
} else {
$field = array('media' => '@' . realpath($file_url));
}
$media_res = $this->mytool->https_post('https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token='.$access_token['access_token'].'&type=file',$field);
$media_res_obj = json_decode($media_res);
if($media_res_obj -> media_id){
$batch_user = array('media_id' => urlencode($media_res_obj -> media_id));
$batch_user_json = urldecode(json_encode($batch_user));
$batch_user_res = $this->mytool->https_post('https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser?access_token='.$access_token['access_token'],$batch_user_json);
$batch_user_res_obj = json_decode($batch_user_res);
$errmsg = $this->myweixintranslation->tranalation($batch_user_res_obj->errcode);
$res_array = array("errcode" => $batch_user_res_obj->errcode,"errmsg" => $errmsg);
}else{
$errmsg = $this->myweixintranslation->tranalation($media_res_obj->errcode);
$res_array = array("errcode" => $media_res_obj->errcode,"errmsg" => $errmsg);
}
return $res_array;
}
更多关于微信接入、微信请求、微信细节处理的方法可详细参见我的其他博客内容。