Loading

企业微信客服工具开发&与系统关联

前期准备

  1. 微信客服绑定小程序。ps:小程序需绑定开放平台,小程序绑定公众号,公众号同时绑定开放平台
  2. 创建企业微信应用。并记下AgentId和Secret
  3. 准备一个已备案的域名(建议用自己的,我试过买的代理域名,基本无法使用,已被人使用)
  4. 配置网页授权及JS-SDK
  5. 设置应用主页
  6. 关于企业微信客户端调试,请看企业微信Windows版本调试

服务端

  1. 接口1:通过企业ID+企业微信客服里的Secret获取token

    https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=企业ID&corpsecret=微信客服的Secret
    
  2. 接口2:通过企业ID+企业微信应用里的Secret获取app_token

    https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=企业ID&corpsecret=企业微信应用的Secret
    
  3. 接口3:通过external_userid获取用户信息

    POST:https://qyapi.weixin.qq.com/cgi-bin/kf/customer/batchget?access_token=token
    
    {
    	"external_userid_list": [
    		"external_userid"
    	],
    	"need_enter_session_context": 0
    }
    

客服工具栏功能开发(可以是小程序或h5)

我采用的是vue2+vue router+vant开发

  1. 在public目录下的index.html文件新增必要的企业微信JS-SDK

    下面js用于调用企业微信功能
    <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
    下面js用于调用js-sdk的API接口
    <script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js" type="text/javascript"></script>
    
  2. 通过接口1获取token。携带token初始化wx(用于调用企业微信功能)

       wxRequest({
                url: "/cgi-bin/get_jsapi_ticket", //这一步放服务器管理
                params: {
                    access_token: token
                }
            }).then(res => {
                let noncestr = randomString(17);
                let timestamp = Date.parse(new Date()) / 1000;
                let url = window.location.href.split('#')[0];
                let params = {
                    "jsapi_ticket": res.data.ticket,
                    "noncestr": noncestr,
                    "timestamp": timestamp,
                    "url": url
                };
    
                let signstr = "";
                for (let item in params) {
                    signstr += item + "=" + params[item] + "&"
                }
                signstr = signstr.substr(0, signstr.length - 1);
    
                wx.config({
                    beta: true,
                    debug: false, // 开启调试模式,调用的值会在企业微信客户端alert出来
                    appId: appId, // 企业ID
                    timestamp: timestamp, //签名的时间戳
                    nonceStr: noncestr, // 生成签名的随机字符串
                    signature: sha1(signstr), // 签名,见附录-JS-SDK使用权限签名算法
                    jsApiList: ["selectEnterpriseContact"] //需要使用的JS接口列表,凡是要调用的接口都需要传进来
                });
    
                wx.ready(function () {
                    console.log('config:success');
                    resolve(wx);
                })
    
                wx.error(res => {
                    console.log("config:error;" + res);
                    reject(res);
                })
            }).catch(err => {
                reject(err);
            })
    
  3. 通过接口2获取app_token。携带app_token初始化appwx。初始化之前wx必须初始化成功,否则该接口报错(用于调用js-sdk的API接口)

     wxRequest({
                url: "/cgi-bin/ticket/get",//这一步放服务器管理
                params: {
                    access_token: app_token,
                    type: "agent_config"
                }
            }).then(res => {
                let noncestr = randomString(17);
                let timestamp = Date.parse(new Date()) / 1000;
                let url = window.location.href.split('#')[0];
                let params = {
                    "jsapi_ticket": res.data.ticket,
                    "noncestr": noncestr,
                    "timestamp": timestamp,
                    "url": url
                };
    
                let signstr = "";
                for (let item in params) {
                    signstr += item + "=" + params[item] + "&"
                }
                signstr = signstr.substr(0, signstr.length - 1);
    
                wx.agentConfig({
                    beta: true, 
                    debug: false, // 开启调试模式,跟上面一样
                    corpid: corpid, // 企业ID
                    agentid: agentid,
                    timestamp: timestamp, // 时间戳
                    nonceStr: noncestr, // 签名的随机字符串
                    signature: sha1(signstr), // 签名,见附录-JS-SDK使用权限签名算法
                    jsApiList: ["selectEnterpriseContact", "getCurExternalContact", "getContext"] // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
                });
    
                wx.ready(function () {
                    console.log('getAgentConfig:success');
                    resolve(wx);
                })
    
                wx.error(res => {
                    console.log("getAgentConfig:error;" + res);
                    reject(res);
                })
            }).catch(err => {
                console.log(err)
                reject(err);
            })
    
  4. 检测客服进入页面是是否从特定的入口进入,只有特定的入口进入才会获取到外部user_id

    /**
     * 获取进入H5页面的入口环境
     * 调用该接口可以判断用户是从哪个入口打开页面,从而决定是否可以调用客户联系相关的接口
     * @param {*} weixin 
     * @returns 
     */
    export function getContext(weixin) {
        return new Promise((resolve, reject) => {
            weixin.invoke(
                "getContext", {
            }, function (res) {
                resolve(res);
            }
            );
        })
    }
    
    
      getContext(this.wxglobal.appwx)
            .then((res) => {
              if (res.err_msg == "getContext:ok") {
                this.entry = res.entry;
                console.log("入口:" + res.entry);
              } else {
                //错误处理
                console.error(res);
              }
            })
            .catch((err) => {
              console.error(err);
            });
    
  5. 获取用户userid。这里要注意getuserId接口,是用的appwx而不是wx

    /**
     * 获取外部客户信息
     * @param {*} weixin 
     * @returns 
     */
    export function getuserId(weixin) {
        return new Promise((resolve, reject) => {
            weixin.invoke(
                "getCurExternalContact", {
            }, function (res) {
                resolve(res);
            }
            );
        })
    }
     getuserId() {
          getuserId(this.wxglobal.appwx)
            .then((res) => {
              if (res.err_msg == "getCurExternalContact:ok") {
                let userId = res.userId; //返回当前外部联系人userId
         
              } else {
                //错误处理
                alert("获取用户信息失败");
              }
            })
            .catch((err) => {
              console.error(err);
            });
        },
    
  6. 获取到userid后,通过userid请求接口3获取用户信息拿取到unionid

    {
    	"errcode": 0,
    	"errmsg": "ok",
    	"customer_list": [
    		{
    			"external_userid": "external_userid",
    			"nickname": "微信昵称",
    			"avatar": "微信头像",
    			"gender": 1,//性别
    			"unionid": "unionid"//最关键就是这个
    		}
    	],
    	"invalid_external_userid": []
    }
    
  7. 当获取到unionid就能通过该字段关联自家数据库,查询该微信用户在系统下的账号信息

前提:微信账号绑定到系统账号。一搬扫码登录之前都会叫用户绑定到系统账号上,这个时候就把获取到的unionid绑定上去,这样就能实现企业微信客服工具栏与系统的关联

手机调试:在企业微信app打开手机开启debug模式

wx和appwx初始化一些方法,网上找的,不想写

//生成随机字符串
function randomString(len) {
    len = len || 32;
    var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
    var maxPos = $chars.length;
    var pwd = '';
    for (let i = 0; i < len; i++) {
        pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
    }
    return pwd;
}


// 字符串加密成 hex 字符串
function sha1(s) {
    var data = new Uint8Array(encodeUTF8(s))
    var i, j, t;
    var l = ((data.length + 8) >>> 6 << 4) + 16,
        s = new Uint8Array(l << 2);
    s.set(new Uint8Array(data.buffer)), s = new Uint32Array(s.buffer);
    for (t = new DataView(s.buffer), i = 0; i < l; i++) s[i] = t.getUint32(i << 2);
    s[data.length >> 2] |= 0x80 << (24 - (data.length & 3) * 8);
    s[l - 1] = data.length << 3;
    var w = [],
        f = [
            function () {
                return m[1] & m[2] | ~m[1] & m[3];
            },
            function () {
                return m[1] ^ m[2] ^ m[3];
            },
            function () {
                return m[1] & m[2] | m[1] & m[3] | m[2] & m[3];
            },
            function () {
                return m[1] ^ m[2] ^ m[3];
            }
        ],
        rol = function (n, c) {
            return n << c | n >>> (32 - c);
        },
        k = [1518500249, 1859775393, -1894007588, -899497514],
        m = [1732584193, -271733879, null, null, -1009589776];
    m[2] = ~m[0], m[3] = ~m[1];
    for (i = 0; i < s.length; i += 16) {
        var o = m.slice(0);
        for (j = 0; j < 80; j++)
            w[j] = j < 16 ? s[i + j] : rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1),
                t = rol(m[0], 5) + f[j / 20 | 0]() + m[4] + w[j] + k[j / 20 | 0] | 0,
                m[1] = rol(m[1], 30), m.pop(), m.unshift(t);
        for (j = 0; j < 5; j++) m[j] = m[j] + o[j] | 0;
    };
    t = new DataView(new Uint32Array(m).buffer);
    for (var i = 0; i < 5; i++) m[i] = t.getUint32(i << 2);

    var hex = Array.prototype.map.call(new Uint8Array(new Uint32Array(m).buffer), function (e) {
        return (e < 16 ? "0" : "") + e.toString(16);
    }).join("");
    return hex;
}


function encodeUTF8(s) {
    var i, r = [],
        c, x;
    for (i = 0; i < s.length; i++)
        if ((c = s.charCodeAt(i)) < 0x80) r.push(c);
        else if (c < 0x800) r.push(0xC0 + (c >> 6 & 0x1F), 0x80 + (c & 0x3F));
        else {
            if ((x = c ^ 0xD800) >> 10 == 0) //对四字节UTF-16转换为Unicode
                c = (x << 10) + (s.charCodeAt(++i) ^ 0xDC00) + 0x10000,
                    r.push(0xF0 + (c >> 18 & 0x7), 0x80 + (c >> 12 & 0x3F));
            else r.push(0xE0 + (c >> 12 & 0xF));
            r.push(0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
        };
    return r;
}
posted @ 2022-07-20 23:31  Sadness_sa  阅读(908)  评论(0)    收藏  举报