1

上一篇文章:nodejs微信公众号开发——6.自定义菜单,实现了简单自定义菜单的功能,里面每个菜单的类型都是click的,当然它支持更多类型,如view,scancode_waitmsg,scancode_push,pic_sysphoto,location_select等,根据实际需求配置。本节主要介绍用户管理的内容 (项目github地:https://github.com/Panfen/wem... )

1. 用户管理介绍

公众号里面的用户可能来自四面八方,拥有这不同的职业、兴趣等类别,可以通过对用户有效的管理,实现精准服务、精准营销。用户管理的内容包含:

  • 用户分组管理
  • 设置用户备注名
  • 获取用户基本信息(UnionID机制)
  • 获取用户列表
  • 获取用户地理位置
  • 网页授权获取用户基本信息

2. 用户分组

使用这个接口,对公众平台的分组进行查询、创建、修改、删除等操作,也可以使用接口在需要时移动用户到某个分组。接口提供的功能包括:

  • 创建分组
  • 查询所有分组
  • 查询用户所在分组
  • 修改分组名
  • 移动用户分组
  • 批量移动用户分组
  • 删除分组

2.1 在api中定义url

var api = {
    ...
    groups:{
        create:prefix+'groups/create?',  //access_token=ACCESS_TOKEN  创建分组,POST请求
        get:prefix+'groups/get?',        //access_token=ACCESS_TOKE  查询所有分组,GET请求
        getId:prefix+'groups/getid?',    //access_token=ACCESS_TOKEN  查询用户所在分组,POST请求
        update:prefix+'groups/update?',  //access_token=ACCESS_TOKEN  修改分组名,POST请求
        membersUpdate:prefix+'groups/members/update?',  //access_token=ACCESS_TOKEN  移动用户分组,POST请求
        membersBatchupdate:prefix+'groups/members/batchupdate?', //access_token=ACCESS_TOKEN  批量移动用户分组,POST请求
        delete:prefix+'groups/delete?'   //access_token=ACCESS_TOKEN  删除分组,POST请求
    }
}

2.2 创建分组:createGroup

写到现在,应该对这种api的代码实现非常熟悉了,都是在Wechat的原型上增加方法:

Wechat.prototype.createGroup = function(name){
    var that = this;
    return new Promise(function(resolve,reject){
        that.fetchAccessToken().then(function(data){
            var url = api.groups.create + 'access_token=' + data.access_token;
            var opts = {
                group:{
                    name:name
                }
            };
            request({method:'POST',url:url,body:opts,json:true}).then(function(response){
                var _data = response.body;
                if(_data.group){
                    resolve(_data.group);
                }else{
                    throw new Error('create group failed: ' + _data.errmsg);
                }
            }).catch(function(err){
                reject(err);
            });
        });
    });
}

2.3 获取所有分组:getGroups

同样是简单的增加原型方法:

Wechat.prototype.getGroups = function(name){
    var that = this;
    return new Promise(function(resolve,reject){
        that.fetchAccessToken().then(function(data){
            var url = api.groups.get + 'access_token=' + data.access_token;
            request({url:url,json:true}).then(function(response){
                ...
            });
        });
    });
}  

图片描述

注: &#34"的十进制表示,因为是测试,我就不改了

2.4 删除分组:deleteGroups

接口提供功能是,发起一次请求根据一个ID删除一个分组,即一次删除一个分组。有些时候我们希望一次删除多个分组,我们可以自己封装支持批量删除的操作:

Wechat.prototype.deleteGroups = function(idArr){    
var that = this;
    that.fetchAccessToken().then(function(data){
        var queue = [];
        for(var i = 0; i < idArr.length; i++){
            queue.push(_deleteGroup(data.access_token,idArr[i]));
        }
        Promise.all(queue).then(function(data){
            console.log('data:' + data);
        }).catch(function(err){
            console.log(err)
        })
    });
}

这里的参数idArrid的数组,里面有一个或多个id值,通过Promise.all()的方法,来处理Promise的集合,_deleteGroup函数实现了为每一个id构建一个Promise:

var _deleteGroup = function(access_token,id){
    var url = api.groups.delete + 'access_token=' + access_token;
    var opts = {
        group:{
            id:id
        }
    };
    return new Promise(function(resolve,reject){
        request({method:'POST',url:url,body:opts,json:true}).then(function(response){
            ...
        });
    });
}

注:
这里已经实现了批量删除分组的功能,但是后台报了一个错:
TypeError: You may only yield a function, promise, generator, array, or object, but the following object was passed: "undefined"
由于不影响程序运行,主要暂时不知道怎么解决,先mark着。明白原因的大神请不吝赐教,不胜感激!

2.5 移动用户分组

鉴于移动用户分组和批量移动用户分组功能的相似性,将其实现在一个函数moveUsersToGroup里面:

Wechat.prototype.moveUsersToGroup = function(openid,to_groupid){
    var that = this;
    return new Promise(function(resolve,reject){
        that.fetchAccessToken().then(function(data){
            var url = '';
            var form = {}
            if(openid && !Array.isArray(openid)) {   //单个用户分组
                url = api.groups.membersUpdate + 'access_token=' + data.access_token;
                form = {
                    openid:openid,
                    to_groupid:to_groupid
                };
            }else if(Array.isArray(openid)){   //批量用户分组
                url = api.groups.membersBatchupdate + 'access_token=' + data.access_token;
                form = {
                    openid_list:openid,
                    to_groupid:to_groupid
                };
            }
            request({method:'POST',url:url,body:form,json:true}).then(function(response){
                ...
            )}
        });
    });
}

测试代码:
将当前用户移动到分组号为114的分组,

else if(content === '6'){
    var msg = yield wechatApi.moveUsersToGroup(message.FromUserName,114);
    var groups = yield wechatApi.getGroups();
    console.log('获取到如下分组:\n'+ JSON.stringify(groups));
}

图片描述

可以看到,该分组的count值变成1

3. 设置用户备注名

毫无技术难度的代码:

Wechat.prototype.updateUserRemark = function(openid,remark){
    var that = this;
    return new Promise(function(resolve,reject){
        that.fetchAccessToken().then(function(data){
            var url = api.user.updateUserRemark + 'access_token=' + data.access_token;
            var form = {
                openid:openid,
                remark:remark
            };
            request({method:'POST',url:url,body:form,json:true}).then(function(response){
                ...
            });
        });
    });
}

图片描述

4.获取用户基本信息

在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同)。公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称头像性别所在城市语言关注时间
获取用户信息的接口分为获取单个用户信息和批量获取用户信息,这个接口请求的地址,请求方式都不同。我们将两者实现在一个函数里面:

//获取单个或一批用户信息
Wechat.prototype.fetchUserInfo = function(open_id,lang){
    var that = this;
    var lang = lang || 'zh_CN';
    var url = '';
    var opts = {}
    return new Promise(function(resolve,reject){
        that.fetchAccessToken().then(function(data){

            if(open_id && !Array.isArray(open_id)){   //单个获取
                url = api.user.getUserInfo + 'access_token=' + data.access_token +'&openid='+ open_id +'&lang=' +lang;
                opts = {
                    url:url,
                    json:true
                }
            }else if(open_id && Array.isArray(open_id)){
                url = api.user.batchGetUserInfo + 'access_token=' + data.access_token;
                var user_list = [];
                for(var i=0;i<open_id.length;i++){
                    user_list.push({
                        openid:open_id[i],
                        lang:lang
                    });
                }
                opts = {
                    method:'POST',
                    url:url,
                    body:{
                        user_list:user_list
                    },
                    json:true
                }
            }
            request(opts).then(function(response){
                ...
            });
        });
    });
}

测试获取单个用户信息和批量获取信息(模拟):

else if(content === '8'){
    var data1 = yield wechatApi.fetchUserInfo(message.FromUserName);
    console.log(JSON.stringify(data1));
    var data2 = yield wechatApi.fetchUserInfo([message.FromUserName]);
    console.log(JSON.stringify(data2))
}

图片描述

5.获取用户列表

公众号可通过本接口来获取帐号的关注者列表,关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成。一次拉取调用最多拉取10000个关注者的OpenID,可以通过多次拉取的方式来满足需求。

Wechat.prototype.getUserOpenIds = function(next_openid){
    var that = this;
    return new Promise(function(resolve,reject){
        that.fetchAccessToken().then(function(data){
            var url = api.user.getUserOpenIds + 'access_token=' + data.access_token;
            if(next_openid) url += '&next_openid=' + next_openid;
            request({url:url,json:true}).then(function(response){
                ...
            });
        });
    });
}

测试一下吧:

var data1 = yield wechatApi.getUserOpenIds();
console.log(JSON.stringify(data1));
var data2 = yield wechatApi.getUserOpenIds(message.FromUserName);
console.log(JSON.stringify(data2));

图片描述

看到这里的结果是不是有点奇怪?按照文档说明,当next_openid不填写的时候,是从头开始拉取用户数据;填写next_openid时,时第一个拉取的OPENID。从结果看出,填写next_openid时,数据是从next_openid开始算起的。

其他函数不在这里继续介绍(都是重复性的代码T_T),可以在github上看到完成代码。

6. 参考资料

列举了参考的几份很有价值的资料,主要是加深都promisekoa框架的理解。


芒果屋里的猫
2.3k 声望363 粉丝

同步 -> 异步 -> 回调