GVKun编程网logo

unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)

22

本篇文章给大家谈谈unicloud云函数开发微信客服消息自动回复图片消息,以及完整步骤的知识点,同时本文还将给你拓展4.微信公众号开发:自动回复用户消息、JAVA实现springMVC方式的微信接入、

本篇文章给大家谈谈unicloud云函数开发微信客服消息自动回复图片消息,以及完整步骤的知识点,同时本文还将给你拓展4.微信公众号开发:自动回复用户消息、JAVA实现 springMVC方式的微信接入、实现消息自动回复实例、Java开发微信公众号接收和被动回复普通消息、Java调用微信客服消息实现发货通知的方法详解等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)

unicloud云函数开发微信客服消息自动回复图片消息(完整步骤)

最近搞得小程序开通流量主之后,想着给公众号引流一下,就要从小程序能够去关注公众号咯。但是小程序的机制大家懂的,不能直接跳转到公众号哦,那就只能找客服回复一个二维码图片咯!!!

配置一下

unicloud配置
  1. 先在项目uniCloud下面的云函数文件夹中右键添加云函数

  1. 在弹出层中新建一个名称为 contact 的云函数(名称随便取都行)

  1. 创建好之后,在contact云函数文件夹上右键上传云函数

  1. 云函数代码如下:

  1. 暂时不写云函数代码,先去unicloud后台配置小程序需要的URL数据

  1. 在右侧目录找到云函数 - 函数列表之后,找到contact云函数,点击详情按钮

  1. 找到云函数URL化模块,点击编辑

  1. 在输入框中输入 /contact 注意:此处必须用/开头,名称可以随便取

  1. 点击确定之后,在点击PATH后面的复制路径按钮

此时,uniCloud配置完成,下面开始配置小程序

小程序配置
  1. 微信公众平台登录自己的小程序账号
  2. 在左侧菜单栏找到开发 - 开发管理

  1. 进入开发管理之后切换tab到开发设置

  1. 找到消息推送,点击启用

  1. 跳转到消息推送配置页面之后,按照图片方式进行配置

此时点击提交,会提示你Token校验失败,请检查确认,不要慌,只是云函数里面没有写而已。这个时候就可以去写云函数的代码了~~

开始写云函数

如果对消息来源要求不高,或者不考虑安全性,可以直接在云函数中返回 event.querystringparameters.echostr

云函数代码如下:

'use strict';
exports.main = async (event, context) => {
  //event为客户端上传的参数
  console.log('event : ', event)
  
  //返回数据给客户端
  return event.querystringparameters.echostr;
};

校验安全性也很简单的

1. 将token、timestamp、nonce三个参数进行字典序排序

2. 将三个参数字符串拼接成一个字符串进行sha1加密

3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

云函数代码如下:

'use strict';
//npm install sha1
const sha1 = require("sha1");
const token = "123qweASD";

function verifyMSGSender(params){
  const mysignature = sha1([token,params.timestamp,params.nonce].sort().join(""));
  
  console.log(mysignature===params.signature?"校验通过":"校验失败");
  
  if(mysignature===params.signature)return true;
  else return false;
}

exports.main = async (event, context) => {
  //event为客户端上传的参数
  if(!verifyMSGSender(event.querystringparameters))return "来源校验失败";
  //校验通过后,下面这行返回echostr的代码注释掉
  else return event.querystringparameters.echostr;
  
  //返回数据给客户端
  return;
};

注意:token需要和消息配置页面的token保持一致哦~

此时就可以去消息配置里面点击保存按钮啦!!!

配置完成后是这样的

校验通过之后,代码里面的 echostr 代码就可以注释掉了

//校验通过后,下面这行返回echostr的代码注释掉
else return event.querystringparameters.echostr;

此时给客服发消息之后,在云函数日志里面可以看到返回结果了

但是这个结果是一个字符串的,我们需要把字符串转为json格式

// 解析json格式字符串
const receiveMsg = JSON.parse(event.body);

数据包返回格式

根据用户发送的消息,会有以下格式

{// 文本消息返回数据包

  "ToUserName": "toUser",

  "FromUserName": "fromUser",//客服消息发起者的openid

  "CreateTime": 1482048670,

  "MsgType": "text",//该条消息类型

  "Content": "this is a test",

  "Msgid": 1234567890123456

}
{// 图片消息返回数据包

  "ToUserName": "toUser",

  "FromUserName": "fromUser",//客服消息发起者的openid

  "CreateTime": 1482048670,

  "MsgType": "image",//该条消息类型

  "PicUrl": "this is a url",

  "MediaId": "media_id",//微信媒体资源id

  "Msgid": 1234567890123456

}
// 小程序卡片消息button按钮配置
<button 
  size="mini" 
  type="primary" 
  :plain="true" 
  open-type="contact" 
  :session-from="" 
  :show-message-card="true" 
  :send-message-title="title">
  查看
 </button>
 
 {// 小程序卡片消息返回数据包

  "ToUserName": "toUser",

  "FromUserName": "fromUser",//客服消息发起者的openid

  "CreateTime": 1482048670,

  "MsgType": "miniprogrampage",//该条消息类型

  "Msgid": 1234567890123456,

  "Title":"title",//小程序卡片标题

  "AppId":"appid",//小程序appid

  "PagePath":"path",//小程序卡片跳转页面

  "ThumbUrl":"",//小程序卡片缩略图

  "ThumbMediaId":""

}
{// 客服消息进入会话事件

  "ToUserName": "toUser",

  "FromUserName": "fromUser",//客服消息发起者的openid

  "CreateTime": 1482048670,

  "MsgType": "event",//该条消息类型

  "Event": "user_enter_tempsession",

  "SessionFrom": "sessionFrom"//开发者在客服会话按钮设置的 session-from 属性

}

回复客服消息

微信客服消息文档

先获取access_token

微信获取access_token文档

注意:APPID和APPSECRET配置在微信公众平台开发管理里面获取

const tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + APPID + '&secret=' + APPSECRET;
// uniCloud.httpclient 发起请求
const res1 = await uniCloud.httpclient.request(tokenUrl,
{
	method: 'GET',
	dataType:"json"
});
//返回数据给客户端
const access_token = res1.data.access_token;
开始回复消息
const access_token = res1.data.access_token
const res2 = await uniCloud.httpclient.request("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="+access_token,{
    method:"POST",
    headers:{
      "Content-Type":"application/json"
    },
    dataType:"json",
    data:{
      touser:touser,//接收此消息用户的openid
      msgtype:"text",//此消息的类型
      text:{
        content:"回复的文本内容",
      }
    }
});

回复消息的种类有很多,text文本消息,img图片消息,link链接消息,miniprogrampage小程序卡片消息。下面只说回复图片消息(这个在大部分教程里面都没写过,其他的可以自行在掘金上搜索)

在做图片消息自动回复之前,根据微信文档描述,需要现将图片上传到临时文件服务器,而且图片保存时间有效期只有三天

上传图片信息

微信客服上传临时图片文档

在请求参数中可以看到,我们需要传一个media的参数,而且是FormData类型的,但是我们不会在小程序上添加一个input框来用作上传图片,所以需要借助nodeJS的form-data模块

注意:在微信小程序中,不能直接在代码中写 new FormData() ,需要自行安装模块

  1. 首先将需要的图片上传到unicloud云存储中,获取到对应的URL

const img_url = 'https://云存储路径.jpg'

  1. 将图片链接转为Buffer
const url = await uniCloud.httpclient.request(img_url)
let buff = new Buffer(url.data);
  1. 将buff传入formData
// 此处的FormData需要安装到当前云函数文件夹中
// npm install form-data
let form = new FormData()
// 将 media 参数、buff信息、formdata中需要包含的filename、图片信息打包
form.append('media', buff, {
	filename: `${Date.Now()}.jpg`,
	contentType: 'image/jpeg'
})
  1. 请求上传客服临时文件接口,将formdata信息上传,获取到media_id
// 请求微信服务器API,将formdata信息上传,获取到media_id
const imgRes = await uniCloud.httpclient.request("https://api.weixin.qq.com/cgi-bin/media/upload?access_token=" + access_token + '&type=image', {
	method: "POST",
	headers: form.getHeaders(),
	dataType: 'json',
	content: form.getBuffer()
});
return imgRes.data.media_id
  1. 判断用户发送的消息,然后回复图片

注意:我这里判断的是用户发送 2 之后,回复消息

// 判断用户发送的消息内容
if(receiveMsg.Content === '2'){
	// 调用获取media_id的方法
	// 因为方法是一个promise,所以调用的时候需要加上 await 前缀
	let media_id = await uploadTempImg(url, access_token)
	if(media_id){
		// 发送消息
		const res2 = await uniCloud.httpclient.request("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + access_token, {
			method: "POST",
			headers: {
				"Content-Type": "application/json"
			},
			dataType: "json",
			data: {
				touser: receiveMsg.FromUserName, //接收此消息用户的openid
				msgtype: "image", //此消息的类型
				image: {
					media_id: media_id
				}
			}
                });
	}
}
自动回复的小机器人就出现了

完整代码

'use strict';
// 需要先执行初始化 npm init -y 生成packages.json文件后
// 再执行安装 npm install sha1
// 安装在当前目录哦。。不是全局安装的
const sha1 = require("sha1");
const FormData = require("form-data");
const token = "123qweASD";
const db = uniCloud.database();

function verifyMSGSender(params) {
	const mysignature = sha1([token, params.timestamp, params.nonce].sort().join(""));

	console.log(mysignature === params.signature ? "校验通过" : "校验失败");

	if (mysignature === params.signature) return true;
	else return false;
}

async function uploadTempImg(img_url, access_token){
	// 现将图片链接转为buffer
	const url = await uniCloud.httpclient.request(img_url)
	let buff = new Buffer(url.data);
	// 此处的FormData需要安装到当前云函数文件夹中
	// npm install form-data
	let form = new FormData()
	// 将 media 参数、buff信息、formdata中需要包含的filename、图片信息打包
	form.append('media', buff, {
		filename: `${Date.Now()}.jpg`,
		contentType: 'image/jpeg'
	})
	// 请求微信服务器API,将formdata信息上传,获取到media_id
	const imgRes = await uniCloud.httpclient.request(
		"https://api.weixin.qq.com/cgi-bin/media/upload?access_token=" + access_token + '&type=image', {
			method: "POST",
			headers: form.getHeaders(),
			dataType: 'json',
			content: form.getBuffer()
		});
		return imgRes.data.media_id
}

exports.main = async (event, context) => {
	// 获取config中配置的appid和appSecret
	let appId = await db.collection('wx_config').where({
		'key': 'wxId'
	}).get()
	let appSecret = await db.collection('wx_config').where({
		'key': 'wxSecret'
	}).get()
	const APPID = appId.data[0].val
	const APPSECRET = appSecret.data[0].val
	const tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + APPID + '&secret=' + APPSECRET;
	// uniCloud.httpclient 发起请求
	const res1 = await uniCloud.httpclient.request(tokenUrl,
	{
		method: 'GET',
		dataType:"json"
	});
	//返回数据给客户端
	const access_token = res1.data.access_token;

	//event为客户端上传的参数
	if (!verifyMSGSender(event.querystringparameters)) return "来源校验失败";
	//校验通过后,下面这行返回echostr的代码注释掉
	// else return event.querystringparameters.echostr;

	// 解析json格式字符串
	const receiveMsg = JSON.parse(event.body);
	const url = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-a8a16cef-f2b9-4644-b216-3b95bcb12602/eed2e97a-be28-4e6d-beb3-ca338d195858.jpg'
	// 判断用户发送的消息内容
	if(receiveMsg.Content === '2'){
		// 调用获取media_id的方法
		// 因为方法是一个promise,所以调用的时候需要加上 await 前缀
		let media_id = await uploadTempImg(url, access_token)
		if(media_id){
			// 发送消息
			const res2 = await uniCloud.httpclient.request(
				"https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + access_token, {
					method: "POST",
					headers: {
						"Content-Type": "application/json"
					},
					dataType: "json",
					data: {
						touser: receiveMsg.FromUserName, //接收此消息用户的openid
						msgtype: "image", //此消息的类型
						image: {
							media_id: media_id
						}
					}
				});
		}
	}

	//返回数据给客户端
	return;
};

4.微信公众号开发:自动回复用户消息

4.微信公众号开发:自动回复用户消息

在上一篇文章里 我们配置好和微信交互的URL里 我们就能接收到微信用户发送的消息了 我只需要判断消息的类型或者其他处理其他的业务逻辑 比如你可以根据他发的关键字来处理 他发订单号 你收到后可以去数据库查询 然后在反馈给他

这里我们就简单回复好了 比如他发送什么消息 我们就返回什么消息

首先是我封装的微信类

<?php
/**
 * 微信公众平台操作类
 */
class WeChat
{
    private $_appid;
    private $_appsecret;
    //微信公众平台请求开发者的服务器需要token
    private $_token;

    //标识qrcodeticket的类型,是永久还是临时
    const QRCODE_TYPE_TEMP = 1;
    const QRCODE_TYPE_TEMP_STR = 2;
    const QRCODE_TYPE_LIMIT = 3;
    const QRCODE_TYPE_LIMIT_STR = 4;

    /**
     * 构造函数
     * @param string $id     appid
     * @param string $secret app秘钥
     */
    public function __construct($id,$secret,$token){
        $this->_appid=$id;
        $this->_appsecret=$secret;
        $this->_token=$token;
    }

    /**
     * 用于第一次验证URl合法性
     */
    public function firstValid(){
        //校验签名的合法性
        if($this->_checkSignature()){
            //签名合法,告知微信服务器
            echo $_GET[''echostr''];
        }
    }
    /**
     * 验证签名
     * @return [type] [description]
     */
    private function _checkSignature(){
        //获取由微信服务器发过来的数据
        $signature = $_GET[''signature''];
        $timestamp = $_GET[''timestamp''];
        $nonce = $_GET[''nonce''];
        //开始验证数据
        $tmp_arr =  array($this->_token,$timestamp,$nonce);
        sort($tmp_arr,SORT_STRING);
        $tmp_str = implode($tmp_arr);
        $tmp_str = sha1($tmp_str);
        //对比数据
        if ($signature == $tmp_str) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 消息类型判断
     * @return array
     */
    public function responseMsg()
    {
        //因为很多都设置了register_globals禁止,不能用$GLOBALS["HTTP_RAW_POST_DATA"] 改用file_get_contents("php://input")即可
        $postStr = file_get_contents("php://input");
        if (!empty($postStr)){
            $postObj = simplexml_load_string($postStr, ''SimpleXMLElement'', LIBXML_NOCDATA);
            $RX_TYPE = trim($postObj->MsgType);
            //用户发送的消息类型判断
            switch ($RX_TYPE)
            {
                case "text":    //文本消息
                    return array(''type''=>''text'',''msg''=>''文本'',''obj''=>$postObj);
                    break;
                case "image":   //图片消息
                    return array(''type''=>''image'',''msg''=>''图片'',''obj''=>$postObj);
                    break;

                case "voice":   //语音消息
                    return array(''type''=>''voice'',''msg''=>''语音'',''obj''=>$postObj);
                    break;
                case "video":   //视频消息
                    return array(''type''=>''video'',''msg''=>''视频'',''obj''=>$postObj);
                    break;
                case "location"://位置消息
                    return array(''type''=>''location'',''msg''=>''位置'',''obj''=>$postObj);
                    break;
                case "link":    //链接消息
                    return array(''type''=>''link'',''msg''=>''链接'',''obj''=>$postObj);
                    break;
                default:
                return array(''type''=>''unknow msg type'',''msg''=>''未知'',''obj''=>$postObj);
                    break;
            }
        }else {
            echo "";
            exit;
        }
    }


    /**
     * 获取 access_tonken值
     * @param string $token_file 用来存储的文件
     * @return access_token
     */
    public function getAccessToken($token_file=''./access_token''){
        //处理是否过期问题,将access_token存储到文件
        $life_time = 7200;
        if (file_exists($token_file) && time() - filemtime($token_file) < $life_time) {
            // 存在有效的access_token 直接返回文件内容
            return file_get_contents($token_file);
        }
        //接口URL
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$this->_appid."&secret=".$this->_appsecret;
        //发送GET请求
        $result = $this->_request(''get'',$url);
        if (!$result) {
            return false;
        }
        //处理数据
        $result_obj = json_decode($result);
        //写入到文件
        file_put_contents($token_file, $result_obj->access_token);
        return $result_obj->access_token;
    }

    /**
     * 获取Ticket
     * @param string $content 二维码内容
     * @param int $type 二维码类型 1 临时整形 2临时字符串 3永久整形 4永久字符串
     * @param int $expire 有效时间
     * @return ticket
     */
    public function getQRCodeTicket($content,$type=2,$expire=604800){
        $access_token = $this->getAccessToken();
        $url = ''https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=''.$access_token;
        $type_list = array(
                self::QRCODE_TYPE_TEMP => ''QR_SCENE'',
                self::QRCODE_TYPE_TEMP_STR => ''QR_STR_SCENE'',
                self::QRCODE_TYPE_LIMIT=>''QR_LIMIT_SCENE'',
                self::QRCODE_TYPE_LIMIT_STR=>''QR_LIMIT_STR_SCENE''
            );
        $action_name = $type_list[$type];
        //post发送的数据
        switch ($type){
            case self::QRCODE_TYPE_TEMP:
                $data_arr[''expire_seconds'']=$expire;
                $data_arr[''action_name''] = $action_name;
                $data_arr[''action_info''][''scene''][''scene_id''] = $content;
                break;
            case self::QRCODE_TYPE_TEMP_STR:
                $data_arr[''expire_seconds'']=$expire;
                $data_arr[''action_name''] = $action_name;
                $data_arr[''action_info''][''scene''][''scene_str''] = $content;
                break;
            case self::QRCODE_TYPE_LIMIT:
                $data_arr[''action_name''] = $action_name;
                $data_arr[''action_info''][''scene''][''scene_id''] = $content;
                break;
            case self::QRCODE_TYPE_LIMIT_STR:
                $data_arr[''action_name''] = $action_name;
                $data_arr[''action_info''][''scene''][''scene_str''] = $content;
                break;
        }
        $data = json_encode($data_arr);
        $result = $this->_request(''post'',$url,$data);
        if(!$result){
            return false;
        }
        $result_obj = json_decode($result);
        return $result_obj->ticket;
    }

    /**
      * 根据ticket获取二维码
      * @param int|string $content qrcode内容标识
      * @param [type] $file 存储为文件的地址,如果null直接输出
      * @param integer $type 类型
      * @param integer $expire 如果是临时,标识有效期
      * @return  [type]     
     */
    public function getQRCode($content,$file=NULL,$type=2,$expire=604800){
        //获取ticket
        $ticket = $this->getQRCodeTicket($content,$type=2,$expire=604800);
        $url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=$ticket";
        //发送,取得图片数据
        $result = $this->_request(''get'',$url);
        if($file){
            file_put_contents($file,$result);
        }else{
            header(''Content-Type:image/jpeg'');
            echo $result;
        }        
    }    


    /**
     * 封装发送http请求
     * @param string $method 请求方式 get/post
     * @param string $url 请求目标的url
     * @param array $data post发送的数据
     * @param bool $ssl 是否为https协议
     * @return 响应主体
     */
    private function _request($method=''get'',$url,$data=array(),$ssl=true){
        //curl完成,先开启curl模块
        //初始化一个curl资源
        $curl = curl_init();
        //设置curl选项
        curl_setopt($curl,CURLOPT_URL,$url);//url
        //请求的代理信息
        $user_agent = isset($_SERVER[''HTTP_USER_AGENT''])?$_SERVER[''HTTP_USER_AGENT'']: ''Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0 FirePHP/0.7.4'';
        curl_setopt($curl,CURLOPT_USERAGENT,$user_agent);
        //referer头,请求来源        
        curl_setopt($curl,CURLOPT_AUTOREFERER,true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 30);//设置超时时间
        //SSL相关
        if($ssl){
            //禁用后,curl将终止从服务端进行验证;
            curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);
            //检查服务器SSL证书是否存在一个公用名
            curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,2);
        }
        //判断请求方式post还是get
        if(strtolower($method)==''post'') {
            /**************处理post相关选项******************/
            //是否为post请求 ,处理请求数据
            curl_setopt($curl,CURLOPT_POST,true);
            curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
        }
        //是否处理响应头
        curl_setopt($curl,CURLOPT_HEADER,false);
        //是否返回响应结果
        curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
        
        //发出请求
        $response = curl_exec($curl);
        if (false === $response) {
            echo ''<br>'', curl_error($curl), ''<br>'';
            return false;
        }
        //关闭curl
        curl_close($curl);
        return $response;
    }

}

然后是index.php能接收

<?php

header(''Content-type:text/html;charset=utf8'');
require ''./WeChat.class.php'';

$appid="231321321321";//这里请填你真实的参数值
$appsecret="sdfsdfwerwedsfsdf";//这里请填你真实的参数值
$token="2131";//这里请填你真实的参数值

$wx=new WeChat($appid,$appsecret,$token);

//验证url
// $wx->firstValid();

//判断消息类型
$result=$wx->responseMsg();

if($result[''type'']==''text''){
    //文本消息
    transmitText($result[''obj''], ''你发送的是文本,内容为:''.$result[''obj'']->Content);
}




/*
 * 接收文本消息
 */
function receiveText($object)
{
    $content = "你发送的是文本,内容为:".$object->Content;
    $result = $this->transmitText($object, $content);
    return $result;
}

/*
 * 回复文本消息
 */
function transmitText($object, $content)
{
    $textTpl = "<xml>
        <ToUserName><![CDATA[%s]]></ToUserName>
        <FromUserName><![CDATA[%s]]></FromUserName>
        <CreateTime>%s</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[%s]]></Content>
        </xml>";
    $result = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
    file_put_contents(''./test.txt'',$result);
    echo $result;die;
    // return $result;
}

效果如下

 

JAVA实现 springMVC方式的微信接入、实现消息自动回复实例

JAVA实现 springMVC方式的微信接入、实现消息自动回复实例

前段时间小忙了一阵,微信公众号的开发,从零开始看文档,踩了不少坑,也算是熬过来了,最近考虑做一些总结,方便以后再开发的时候回顾,也给正在做相关项目的同学做个参考。

1.思路

微信接入:用户消息和开发者需要的事件推送都会通过微信方服务器发起一个请求,转发到你在公众平台配置的服务器URL地址,微信方将带上signature,timestamp,nonce,echostr四个参数,我们自己服务器通过拼接公众平台配置的token,以及传上来的timestamp,nonce进行SHA1加密后匹配signature,返回ture说明接入成功。

消息回复:当用户给公众号发送消息时,微信服务器会将用户消息以xml格式通过POST请求到我们配置好的服务器对应的接口,而我们要做的事情就是根据消息类型等做相应的逻辑处理,并将最后的返回结果也通过xml格式return给微信服务器,微信方再传达给用户的这样一个过程。 

1.公众平台配置

2.Controller

@Controller
@RequestMapping("/wechat")
publicclass WechatController {
  @Value("${DNBX_TOKEN}")
  private String DNBX_TOKEN;
  
  private static final Logger LOGGER = LoggerFactory.getLogger(WechatController.class);
  
  @Resource
  WechatService wechatService;
  
  /**
   * 微信接入
   * @param wc
   * @return
   * @throws IOException 
   */
  @RequestMapping(value="/connect",method = {RequestMethod.GET,RequestMethod.POST})
  @ResponseBody
  publicvoid connectWeixin(HttpServletRequest request,HttpServletResponse response) throws IOException{
    // 将请求、响应的编码均设置为UTF-8(防止中文乱码) 
    request.setCharacterEncoding("UTF-8"); //微信服务器POST消息时用的是UTF-8编码,在接收时也要用同样的编码,否则中文会乱码;
    response.setCharacterEncoding("UTF-8"); //在响应消息(回复消息给用户)时,也将编码方式设置为UTF-8,原理同上;boolean isGet = request.getmethod().toLowerCase().equals("get"); 
   
    PrintWriter out = response.getWriter();
     
    try {
      if (isGet) {
        String signature = request.getParameter("signature");// 微信加密签名 
        String timestamp = request.getParameter("timestamp");// 时间戳 
        String nonce = request.getParameter("nonce");// 随机数 
        String echostr = request.getParameter("echostr");//随机字符串 
        
        // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 if (SignUtil.checkSignature(DNBX_TOKEN,signature,timestamp,nonce)) { 
          LOGGER.info("Connect the weixin server is successful.");
          response.getWriter().write(echostr); 
        } else { 
          LOGGER.error("Failed to verify the signature!"); 
        }
      }else{
        String respMessage = "异常消息!";
        
        try {
          respMessage = wechatService.weixinPost(request);
          out.write(respMessage);
          LOGGER.info("The request completed successfully");
          LOGGER.info("to weixin server "+respMessage);
        } catch (Exception e) {
          LOGGER.error("Failed to convert the message from weixin!"); 
        }
        
      }
    } catch (Exception e) {
      LOGGER.error("Connect the weixin server is error.");
    }finally{
      out.close();
    }
  }
}

3.签名验证 checkSignature

从上面的controller我们可以看到,我封装了一个工具类SignUtil,调用了里面的一个叫checkSignature,传入了四个值,DNBX_TOKEN,nonce。这个过程非常重要,其实我们可以理解为将微信传过来的值进行一个加解密的过程,很多大型的项目所有的接口为保证安全性都会有这样一个验证的过程。DNBX_TOKEN我们在微信公众平台配置的一个token字符串,主意保密哦!其他三个都是微信服务器发送get请求传过来的参数,我们进行一层sha1加密:

public class SignUtil { 
 
  /** 
   * 验证签名 
   * 
   * @param token 微信服务器token,在env.properties文件中配置的和在开发者中心配置的必须一致 
   * @param signature 微信服务器传过来sha1加密的证书签名
   * @param timestamp 时间戳
   * @param nonce 随机数 
   * @return 
   */ 
  public static boolean checkSignature(String token,String signature,String timestamp,String nonce) { 
    String[] arr = new String[] { token,nonce }; 
    // 将token、timestamp、nonce三个参数进行字典序排序 
    Arrays.sort(arr); 
    
    // 将三个参数字符串拼接成一个字符串进行sha1加密 
    String tmpStr = SHA1.encode(arr[0] + arr[1] + arr[2]); 
    
    // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信 
    return tmpStr != null ? tmpStr.equals(signature.toupperCase()) : false; 
  } 
  
}

SHA1:

/** 
 * 微信公众平台(JAVA) SDK 
 * 
 * SHA1算法
 * 
 * @author helijun 2016/06/15 19:49
 */ 
public final class SHA1 { 
 
  private static final char[] HEX_DIGITS = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; 
 
  /** 
   * Takes the raw bytes from the digest and formats them correct. 
   * 
   * @param bytes the raw bytes from the digest. 
   * @return the formatted bytes. 
   */ 
  private static String getFormattedText(byte[] bytes) { 
    int len = bytes.length; 
    StringBuilder buf = new StringBuilder(len * 2); 
    // 把密文转换成十六进制的字符串形式 
    for (int j = 0; j < len; j++) { 
      buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); 
      buf.append(HEX_DIGITS[bytes[j] & 0x0f]); 
    } 
    return buf.toString(); 
  } 
 
  public static String encode(String str) { 
    if (str == null) { 
      return null; 
    } 
    try { 
      MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); 
      messageDigest.update(str.getBytes()); 
      return getFormattedText(messageDigest.digest()); 
    } catch (Exception e) { 
      throw new RuntimeException(e); 
    } 
  } 
}

当你在公众平台提交保存,并且看到绿色的提示“接入成功”之后,恭喜你已经完成微信接入。这个过程需要细心一点,注意加密算法里的大小写,如果接入不成功,大多数情况都是加密算法的问题,多检查检查。

4. 实现消息自动回复service

/**
   * 处理微信发来的请求
   * 
   * @param request
   * @return
   */
  public String weixinPost(HttpServletRequest request) {
    String respMessage = null;
    try {

      // xml请求解析
      Map<String,String> requestMap = MessageUtil.xmlToMap(request);

      // 发送方帐号(open_id)
      String fromUserName = requestMap.get("FromUserName");
      // 公众帐号
      String toUserName = requestMap.get("ToUserName");
      // 消息类型
      String msgType = requestMap.get("MsgType");
      // 消息内容
      String content = requestMap.get("Content");
      
      LOGGER.info("FromUserName is:" + fromUserName + ",ToUserName is:" + toUserName + ",MsgType is:" + msgType);

      // 文本消息
      if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
        //这里根据关键字执行相应的逻辑,只有你想不到的,没有做不到的
        if(content.equals("xxx")){
          
        }
        
        //自动回复
        TextMessage text = new TextMessage();
        text.setContent("the text is" + content);
        text.setToUserName(fromUserName);
        text.setFromUserName(toUserName);
        text.setCreateTime(new Date().getTime() + "");
        text.setMsgType(msgType);
        
        respMessage = MessageUtil.textMessagetoXml(text);
        
      } /*else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {// 事件推送
        String eventType = requestMap.get("Event");// 事件类型
        
        if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {// 订阅
          respContent = "欢迎关注xxx公众号!";
          return MessageResponse.getTextMessage(fromUserName,toUserName,respContent);
        } else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {// 自定义菜单点击事件
          String eventKey = requestMap.get("EventKey");// 事件KEY值,与创建自定义菜单时指定的KEY值对应
          logger.info("eventKey is:" +eventKey);
          return xxx;
        }
      }
      //开启微信声音识别测试 2015-3-30
      else if(msgType.equals("voice"))
      {
        String recvMessage = requestMap.get("Recognition");
        //respContent = "收到的语音解析结果:"+recvMessage;
        if(recvMessage!=null){
          respContent = TulingApiProcess.getTulingResult(recvMessage);
        }else{
          respContent = "您说的太模糊了,能不能重新说下呢?";
        }
        return MessageResponse.getTextMessage(fromUserName,respContent); 
      }
      //拍照功能
      else if(msgType.equals("pic_sysphoto"))
      {
        
      }
      else
      {
        return MessageResponse.getTextMessage(fromUserName,"返回为空"); 
      }*/
      // 事件推送
      else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
        String eventType = requestMap.get("Event");// 事件类型
        // 订阅
        if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
          
          TextMessage text = new TextMessage();
          text.setContent("欢迎关注,xxx");
          text.setToUserName(fromUserName);
          text.setFromUserName(toUserName);
          text.setCreateTime(new Date().getTime() + "");
          text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
          
          respMessage = MessageUtil.textMessagetoXml(text);
        } 
        // Todo 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息
        else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {// 取消订阅
          
          
        } 
        // 自定义菜单点击事件
        else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
          String eventKey = requestMap.get("EventKey");// 事件KEY值,与创建自定义菜单时指定的KEY值对应
          if (eventKey.equals("customer_telephone")) {
            TextMessage text = new TextMessage();
            text.setContent("0755-86671980");
            text.setToUserName(fromUserName);
            text.setFromUserName(toUserName);
            text.setCreateTime(new Date().getTime() + "");
            text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
            
            respMessage = MessageUtil.textMessagetoXml(text);
          }
        }
      }
    }
    catch (Exception e) {
      Logger.error("error......")
    }
    return respMessage;
  }

先贴代码如上,大多都有注释,读一遍基本语义也懂了不需要多解释。

 有一个地方格外需要注意:

上面标红的fromUserName和toUserName刚好相反,这也是坑之一,还记得我当时调了很久,明明都没有问题就是不通,最后把这两个一换消息就收到了!其实回过头想也对,返回给微信服务器这时本身角色就变了,所以发送和接收方也肯定是相反的。

5.MessageUtil

public class MessageUtil {
  
  /** 
   * 返回消息类型:文本 
   */ 
  public static final String RESP_MESSAGE_TYPE_TEXT = "text"; 
 
  /** 
   * 返回消息类型:音乐 
   */ 
  public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; 
 
  /** 
   * 返回消息类型:图文 
   */ 
  public static final String RESP_MESSAGE_TYPE_NEWS = "news"; 
 
  /** 
   * 请求消息类型:文本 
   */ 
  public static final String REQ_MESSAGE_TYPE_TEXT = "text"; 
 
  /** 
   * 请求消息类型:图片 
   */ 
  public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; 
 
  /** 
   * 请求消息类型:链接 
   */ 
  public static final String REQ_MESSAGE_TYPE_LINK = "link"; 
 
  /** 
   * 请求消息类型:地理位置 
   */ 
  public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; 
 
  /** 
   * 请求消息类型:音频 
   */ 
  public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; 
 
  /** 
   * 请求消息类型:推送 
   */ 
  public static final String REQ_MESSAGE_TYPE_EVENT = "event"; 
 
  /** 
   * 事件类型:subscribe(订阅) 
   */ 
  public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; 
 
  /** 
   * 事件类型:unsubscribe(取消订阅) 
   */ 
  public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; 
 
  /** 
   * 事件类型:CLICK(自定义菜单点击事件) 
   */ 
  public static final String EVENT_TYPE_CLICK = "CLICK"; 
}

这里为了程序可读性、扩展性更好一点,我做了一些封装,定义了几个常量,以及将微信传过来的一些参数封装成java bean持久化对象,核心代码如上。重点讲下xml和map之间的转换

其实这个问题要归咎于微信是用xml通讯,而我们平时一般是用json,所以可能短时间内会有点不适应

1.引入jar包

<!-- 解析xml -->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    
    <dependency>
      <groupId>com.thoughtworks.xstream</groupId>
      <artifactId>xstream</artifactId>
      <version>1.4.9</version>
    </dependency>

2.xml转map集合对象

/**
   * xml转换为map
   * @param request
   * @return
   * @throws IOException
   */
  @SuppressWarnings("unchecked")
  public static Map<String,String> xmlToMap(HttpServletRequest request) throws IOException{
    Map<String,String> map = new HashMap<String,String>();
    SAXReader reader = new SAXReader();
    
    InputStream ins = null;
    try {
      ins = request.getInputStream();
    } catch (IOException e1) {
      e1.printstacktrace();
    }
    Document doc = null;
    try {
      doc = reader.read(ins);
      Element root = doc.getRootElement();
      
      List<Element> list = root.elements();
      
      for (Element e : list) {
        map.put(e.getName(),e.getText());
      }
      
      return map;
    } catch (DocumentException e1) {
      e1.printstacktrace();
    }finally{
      ins.close();
    }
    
    return null;
  }

3.文本消息对象转换成xml

/** 
   * 文本消息对象转换成xml 
   * 
   * @param textMessage 文本消息对象 
   * @return xml 
   */ 
  public static String textMessagetoXml(TextMessage textMessage){
    XStream xstream = new XStream();
    xstream.alias("xml",textMessage.getClass());
    return xstream.toXML(textMessage);
  }

到此为止已经大功告成了,这个时候可以在公众号里尝试发送“测试”,你会收到微信回复的“the text is 测试”,这也是上面代码里做的回复处理,当然你也可以发挥你的想象用他做所有你想做的事了,比如回复1查天气,2查违章等等....

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

Java开发微信公众号接收和被动回复普通消息

Java开发微信公众号接收和被动回复普通消息

这篇文章主要介绍了Java开发微信公众号接收和被动回复普通消息的相关资料,需要的朋友可以参考下

上篇说完了如何接入微信公众号,本文说一下微信公众号的最基本功能:普通消息的接收和回复。说到普通消息,那么什么是微信公众号所定义的普通消息呢,微信开发者文档中提到的接收的普通消息包括如下几类

1.文本消息

2.图片消息

3.语音消息

4.视频消息

5.小视频消息

6.地理位置消息

7.链接消息(被动回复的消息)

被动回复的普通消息包括:

1.回复文本消息

2.回复图片消息

3.回复语音消息

4.回复视频消息

5.回复音乐消息

6.回复图文消息

其实接收消息和被动回复消息这两个动作是不分家的,这本来就是一个交互场景,一般情况就是公众号通过分析接收到的消息,会给出对应的回复。当然也不能排除一些特殊业务了。

如何接收消息

要接收的这7中消息的xml格式这里就不列出了,请到官方文档查看,有具体的格式定义和属性说明。格式很简单,基本共有属性包括ToUserName、FromUserName、CreateTime、MsgType、Msgid,并且每种类型有自己特殊的属性。

看到这里,其实就很明白了,接收消息的过程其实就是获取post请求的这个xml,然后对这个xml进行分析的过程。post请求的入口还是之前提到的微信公众号接入的那个地址,整个公众号的所有请求都会走这个入口,只是接入时是get请求,其它情况下是post请求。处理xml这里用了dom4j,xml处理代码如下,在servlet的post方法中调用parseXml方法即可:

public static Map parseXml(HttpServletRequest request) throws Exception { // 将解析结果存储在HashMap中 Map map = new HashMap(); // 从request中取得输入流 InputStream inputStream = request.getInputStream(); /* * 读取request的body内容 此方法会导致流读取问题 Premature end of file. nested exception: * Premature end of file String requestBody = * inputStream2String(inputStream); System.out.println(requestBody); */ // 读取输入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子节点 List elementList = root.elements(); // 遍历所有子节点 for (Element e : elementList) map.put(e.getName(), e.getText()); // 释放资源 inputStream.close(); inputStream = null; return map; } private static String inputStream2String(InputStream is) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int i = -1; while ((i = is.read()) != -1) { baos.write(i); } return baos.toString(); }

如何被动回复消息

下面我基于这样一个逻辑来演示构造回复的消息,接收到文本消息"文本",回复文本消息;接收到“图片”,回复图片消息;接收到“语音”,回复语音消息;接收到“视频”,回复视频消息;接收到“音乐”,回复音乐消息;接收到“图文”,回复图文消息。

以回复文本消息作为说明:

消息创建时间(整形)

前两个属性可以从接收的消息中获取,接收的消息格式如下:

13488318601234567890123456

其中接收消息格式中的ToUserName便是回复消息的FromUserName,接收消息格式中的FromUserName便是回复消息的ToUserName。

CreateTime为消息发送的时间戳。MsgType为消息类型,文本为text。Content为消息内容。

具体每一种类型消息的回复,就是构造此种类型的xml格式内容,格式大同小异,只是音乐、视频、语音、图文格式相对于文本消息构造的xml内容稍微复杂一点。具体可参考官方文档。这里不做赘述,相信各位一看便明白。

Java调用微信客服消息实现发货通知的方法详解

Java调用微信客服消息实现发货通知的方法详解

这篇文章主要介绍了Java调用微信客服消息实现发货通知的方法,结合实例形式详细分析了java针对微信接口调用的原理、调用方法与相关注意事项,需要的朋友可以参考下

本文实例讲述了Java调用微信客服消息实现发货通知的方法。分享给大家供大家参考,具体如下:

微信文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547&token=&lang=zh_CN

个人说明:这是一个样例,微信客户消息有很多种,我现在用的是公众号发送消息。样子如下图。

说明:下面开始代码部分了。

1.首先看微信文档。这里才是我们需要的

这里是说发消息要POST请求这个接口:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN

但是这个接口后面需要带一个参数ACCESS_TOKEN。

下面先获取ACCESS_TOKEN。

//这里的WeixinUtil.getAccess_token()方法,放在下面。 String aToken = WeixinUtil.getAccess_token("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+你的appId+"&secret="+你的appSecret+""); System.out.println("这里是aToken"+aToken); String[] tokenOne = aToken.split(":"); String[] token = tokenOne[1].split(","); char [] stringArr = token[0].tochararray(); String token3 = "" ; for(int i=1;i

获取到一个ACCESS_TOKEN,然后就可以加入到微信请求中

//这里就是一个微信请求,首先用String放着 String tokenurl = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="+token3; //首先确定是发送文字消息,还是图文消息,这里是手写的json数据. //发送文字消息,无连接 String json = "{"touser":"这里是Openid","msgtype":"text","text":{"content":"Hello World"}}"; //图文消息,有链接连接 String jsonpic = "{"touser":""+这里是Openid+"","+ ""msgtype":"news","news":{"articles":["+ "{"title":"HelloWorld","url":"要跳转的链接"}]}}"; System.out.println("这里是json"+jsonpic); //请求方法,然后放回OK 成功,否则错误。这里这个请求方法在下边 String xmlStr = HttpKit.post(tokenurl,jsonpic); System.out.println("这里是xmlStr"+xmlStr);

说明:WeixinUtil.getAccess_token()方法。我放整个类了。改包名,只需要导入两个包

package com.uitrs.weixin; import java.net.HttpURLConnection; import java.net.URL; public class WeixinUtil { //传入URL public static String getAccess_token(String url) { String accesstoken = null; try { URL urlGet = new URL(url); HttpURLConnection http = (HttpURLConnection) urlGet .openConnection(); http.setRequestMethod("GET"); // 必须是get方式请求 http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); http.setDoOutput(true); http.setDoInput(true); System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒 System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒 http.connect(); InputStream is = http.getInputStream(); int size = is.available(); byte[] jsonBytes = new byte[size]; is.read(jsonBytes); accesstoken = new String(jsonBytes, "UTF-8"); System.out.println(accesstoken); is.close(); } catch (Exception e) { e.printstacktrace(); } return accesstoken; } }

说明:HttpKit.post();方法,我放整个类了。这个类我用的是导入

import com.jfinal.kit.HttpKit;

这个包到了jfinal的包。出自下面三个包当中,具体我也不清楚了

1.jfinal-2.2.jar (应该是这个)

2.jfinal-2.2-bin-with-src.jar

3.jfinal-weixin-1.7-bin-with-src.jar

更多关于java算法相关内容感兴趣的读者可查看本站专题:《Java字符与字符串操作技巧总结》、《Java数组操作技巧总结》、《Java数学运算技巧总结》、《Java编码操作技巧总结》和《Java数据结构与算法教程》

希望本文所述对大家java程序设计有所帮助。

关于unicloud云函数开发微信客服消息自动回复图片消息完整步骤的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于4.微信公众号开发:自动回复用户消息、JAVA实现 springMVC方式的微信接入、实现消息自动回复实例、Java开发微信公众号接收和被动回复普通消息、Java调用微信客服消息实现发货通知的方法详解的相关知识,请在本站寻找。

本文标签: