微信公众号客服 简单实现

实现语言  java

实现的前提:已经实现了公众号的其他一些基本功能,比如访问菜单。

1、微信公众号中添加客服功能 

2、添加客服 

 3、接口编写。

3-1、 我找了好久才找到。微信公众号接口文档中--自定义菜单中,创建一个类型为click的菜单(如下图1,2)

图1

这里会去访问 创建客服会话的- 新版客服功能 - 创建会话接口,微信文档中都有(https://api.weixin.qq.com/customservice/kfsession/create?access_token=ACCESS_TOKEN)

然后点击立即咨询 客服连接成功 回复消息。

图2

{    
          "type":"click",
          "name":"立即咨询",
          "key":"kf"
      },

kf是自定义的。 

 

*  1、click:点击推事件用户点击click类型按钮后,微信会会向你的接口工程发送消息(你自己接口工程的地址是公众号配置中的验证地址 如图3)消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;

这个button 会跟关注和取消关注一样去发送给后端服务器请求,就是配置的地址。 (公众号中配置的地址位置如下)

图3

 

3-2 、java代码  ,

controller

/**
 * 处理消息请求(包括关注和取消关注) 包好所有微信发给服务器的通知
 *
 * @param msg
 * @return
 */
@RequestMapping(value = "validate", method = RequestMethod.POST, produces = {MediaType.TEXT_XML_VALUE})
@ResponseBody
public Object handleMesg(@RequestBody RecieveMsg msg) throws Exception {
    Object out = weiXinService.handleMessage(msg);
    System.out.println(String.valueOf(msg));
    return out;
}

 

 

 

service 

/**
 * 取消和关注操作 推送  也有客服按钮请求的操作(kf为自定义)
 *  
 * @param msg
 * @return
 */
public Object handleMessage(RecieveMsg msg) {

    if(Objects.equals(msg.getEvent(),"CLICK") &&Objects.equals(msg.getEventKey(),"kf")){
        String kf = kfseesion(msg.getFromUserName());
        PushMsg out = new PushMsg();
        out.setFromUserName(msg.getToUserName());
        out.setToUserName(msg.getFromUserName());
        out.setMsgType("text");
        out.setCreateTime(System.currentTimeMillis());
        out.setContent(kf);
        return out;
    }


    String url = "/handleValidate";
    JSONObject obj = HttpClientUtils.httpRequest(weiXinBean.getHttpUrl() + url, "POST", JSONUtils.beanToJson(msg));
    PushMsg out = new PushMsg();
    out.setContent(String.valueOf(obj.get("content")).replace("APPID",weiXinBean.getAppId()).replace("REDIRECT_URL",weiXinBean.getYuMing()));
    out.setFromUserName(String.valueOf(obj.get("fromUserName")));
    out.setMsgType(String.valueOf(obj.get("msgType")));
    out.setToUserName(String.valueOf(obj.get("toUserName")));
    out.setCreateTime(obj.getLong("createTime"));
    return out;
}

 

 

/**
 * 功能描述:创建会话
 * @param:
 * @return:
 * @author: syl
 * @date: 2019/2/23 0023 下午 4:02
 */
public String kfseesion(String openId) {
    String accessToken = Tools.getAccessToken(getAccessTokenUrl());
    String urlToken = WxContstants.kfSessionUrl.replace("ACCESS_TOKEN",accessToken);

    JSONObject kfInfo = getonlinekflist();
    if(!StringUtils.isEmpty(kfInfo.get("kf_online_list"))){
        JSONArray kfOnlineList = kfInfo.getJSONArray("kf_online_list");
        if(kfOnlineList.size()==0){
            return "服务人员未在线";
        }
        Random random = new Random();
        int i = random.nextInt(kfOnlineList.size());
        JSONObject pj = new JSONObject();
        JSONObject item = (JSONObject) kfOnlineList.get(i);
        pj.put("kf_account",String.valueOf(item.get("kf_account")));
        //pj.put("kf_account","kf2002@gh_02e32b1d4ea2");
        pj.put("openid",openId);
        JSONObject jsonObject = HttpClientUtils.httpRequest(urlToken,"POST", String.valueOf(pj));
        Integer errcode =  jsonObject.getInteger("errcode");
        String errmsg =  jsonObject.getString("errmsg");
        if (Objects.equals(0, errcode)) {
            return "客服连接成功,请问有什么可以帮助您";
        } else if(Objects.equals(65415, errcode)) {
            return "服务人员未在线";
        }else{
            return errmsg;
        }
    }else{
        return "服务人员未在线";
    }

}

/**
 * 功能描述:获取在线客服列表 
 * @param: 
 * @return: 
 * @author: syl
 * @date: 2019/8/13 0013 上午 10:31
 */
public JSONObject getonlinekflist() {
    String accessToken = Tools.getAccessToken(getAccessTokenUrl());
    String urlToken = WxContstants.getonlinekflist.replace("ACCESS_TOKEN",accessToken);
    JSONObject jsonObject = HttpClientUtils.httpRequest(urlToken, "GET", "");
    return jsonObject;
}

 

 

 

工具类

RecieveMsg.java 

package com.navitek.maternal.apiweb.bean;

import lombok.Getter;
import lombok.Setter;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * @author 26968
 */
@Setter
@Getter
@XmlRootElement(name="xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class RecieveMsg {


    /**
     * 开发者微信号
     */
    protected String FromUserName;
    /**
     * 发送方帐号(一个OpenID)
     */
    protected String ToUserName;
    /**
     *  消息创建时间
      */

    protected Long CreateTime;
    /**
     * 消息类型
     * text 文本消息
     * image 图片消息
     * voice 语音消息
     * video 视频消息
     * music 音乐消息
     */
    protected String MsgType;
    /**
     * 消息id
     */
    protected Long MsgId;
    /**
     * 文本内容
     */
    private String Content;
    /**
     * 图片链接地址 有系统生成
     */
    private String PicUrl;
    /**
     * 多媒体
     */
    private String MediaId;

    /**
     * 事件
     */
    private  String Event;


    /**
     * 菜单的KEY值
     */
    private String EventKey;

    /**
     *
     */
    private String Ticket;


}

WxContstants.java




import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author 26968
 */
public class WxContstants {


    //保存用户验证码
    public static ConcurrentMap<String, Map<String, Object>> VERIFY_CODE = new ConcurrentHashMap<String, Map<String, Object>>();

    //验证码超时时间
    public static long maxTime = 300;


    /**
     * 菜单类型
     */
    public static String VIEW = "view";




    /**
     * 网页授权后根据code 获取用access_token
     */
    public static final String WEB_ACCESS_TOKEN = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

    /**
     * 获取用户基本信息接口
     */
    public static final String GET_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";


    /**
     * 暂时存储accesstoken
     */
    public static final Map<String, Long> currentHashMap = new HashMap<>();

    /**
     * 放token值
     */
    public static final Map<String, Object> TOKEN_MAP = new HashMap<>();


    public static final String ACCESS_TOKEN = "access_token";

    /**
     * 消息类型 (文本类型)
     */
    public static final String TEXT = "text";

    /**
     * image类型
     */
    public static final String IMAGE = "image";
    /**
     * event 事件类型
     */
    public static final String Event = "event";

    /**
     * subscribe 关注事件
     */
    public static final String SUBSCRIBE = "subscribe";

    /**
     * unsubscribe 取消关注事件
     */
    public static final String UNSUBSCRIBE = "unsubscribe";

    /**
     * 菜单点击事件
     */
    public static final String CLICK = "CLICK";

    /**
     * news 图文消息
     */
    public static final String NEWS = "news";

    /**
     * 获取access_token的Url
     */
    public static final String ACCESSTOKENURL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";


    /**
     * 创建自定义菜单接口
     */
    public static final String ButtonUrl = " https://api.weixin.qq.com/cgi-bin/menu/create?access_token=";

    /**
     * 删除菜单
     */
    public static final String DELETEBUTTON = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=";

    /**
     * 网页授权url
     */
    public static final String AUTHORIZE = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT&response_type=code&scope=snsapi_userinfo&state=22#wechat_redirect";

    /**
     * 客服相关 新增  获取 删除 绑定
     */


    public static final String addKfUrl = " https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN";
    public static final String getKfListUrl = " https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN";
    public static final String getonlinekflist = " https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token=ACCESS_TOKEN";
    public static final String delKfUrl = "https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT";
    public static final String bbKfUrl = " https://api.weixin.qq.com/customservice/kfaccount/inviteworker?access_token=ACCESS_TOKEN";
    public static final String kfSessionUrl = " https://api.weixin.qq.com/customservice/kfsession/create?access_token=ACCESS_TOKEN";
    public static final String actSessionUrl = " https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN";
    public static final String templateSend = " https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";


}

tools.java



import com.alibaba.fastjson.JSONObject;
import com.navitek.maternalweb.common.WxContstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

/**
 * @author 26968
 */
@Slf4j
public class Tools {


    /**
     * 将 map转化为xml格式
     *
     * @param params
     * @return
     */
    public static String toXml(Map<String, String> params) {
        StringBuilder xml = new StringBuilder();
        xml.append("<xml>");
        Iterator iterator = params.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry) iterator.next();
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if (!StringUtils.isEmpty(value)) {
                xml.append("<").append(key).append(">");
                xml.append((String) entry.getValue());
                xml.append("</").append(key).append(">");
            }
        }

        xml.append("</xml>");
        return xml.toString();
    }

    /**
     * 将xml格式的字符串转化为 map
     *
     * @param xmlStr
     * @return
     */
    public static Map<String, String> xmlToMap(String xmlStr) {
        XmlHelper xmlHelper = XmlHelper.of(xmlStr);
        return xmlHelper.toMap();
    }


    public static String getRealIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return ip;
    }

    public static String getRealIpV2(HttpServletRequest request) {
        String accessIP = request.getHeader("x-forwarded-for");
        return null == accessIP ? request.getRemoteAddr() : accessIP;
    }


    /**
     * 获取access_Token
     *
     * @return
     */
    public static String getAccessToken(String ACCESSTOKENURL) {

        /**
         * @Todo 暂时存在静态 map中
         */
        String accessToken = null;
        //先判断时间
        Long tokenTime = StringUtils.isEmpty((Long) WxContstants.currentHashMap.get(WxContstants.ACCESS_TOKEN)) ? 0:(Long) WxContstants.currentHashMap.get(WxContstants.ACCESS_TOKEN);
        //如果当前时间大于设置的时间则进行重新获取accessToken
        if (Instant.now().getEpochSecond() > tokenTime) {
            JSONObject result = HttpClientUtils.httpRequest(ACCESSTOKENURL, "GET", "");
            if (StringUtils.isEmpty(result.get(WxContstants.ACCESS_TOKEN))){
                log.info("调取微信接口获取access_token失败=="+result);
            }
            accessToken = String.valueOf(result.get(WxContstants.ACCESS_TOKEN));
            WxContstants.TOKEN_MAP.put(WxContstants.ACCESS_TOKEN,accessToken);
            WxContstants.currentHashMap.put(WxContstants.ACCESS_TOKEN,Instant.now().getEpochSecond()+6000);
        }
        accessToken = String.valueOf(WxContstants.TOKEN_MAP.get(WxContstants.ACCESS_TOKEN));
        return accessToken;
    }




    public static String getRandCode(){
        Random ne=new Random();
        return String.valueOf(ne.nextInt(9999-1000+1)+1000);
    }

    public static String getUUID(){
        UUID uuid=UUID.randomUUID();
        String str = uuid.toString();
        String uuidStr=str.replace("-", "");
        return uuidStr;
    }


}

 

4、效果。这样就可以实现简单的客服功能了,客服必须在线。 不在线就回复代码中写的反馈信息。

(如图4 ,5)

图4

 

图5

 

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

somdip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值