2018-04-26 17:16:04 ssh456 阅读数 295
  • 微信公众平台开发4-消息管理-微信开发php

    微信公众平台开发之消息管理是子恒老师《微信公众平台开发》视频教程的第4部。详细讲解了用php开发微信,对微信公众平台中的消息管理开发。内容包含微信关键字回复,多图文消息回复开发,接收图片消息,视频,小视频消息等等。欢迎反馈,微信/QQ:68183131

    6334 人正在学习 去看看 秦子恒

//回复图文
	public function addNews(){
		$mp=$this->mp;
		$keyword=I('post.keyword');
		$title=I('post.title');
		$description=I('post.content');
		$url=I('post.url');
		//相对路径->绝对路径
		$file=realpath('.'.$url);
		$link=I('post.content_source_url');

		$accessToken=getAccess_token();
		include APP_PATH . 'LaneWeChat/lanewechat.php';
		//上传永久图片api
		$api="https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=$accessToken&type=image";
		$data['media']='@' . $file;
		$ret=Curl::callWebServer($api,$data,'post',true,false);
		    if (isset($ret['url'])) {
				$mp=$this->mp;
				$arr['title']=$title;
				$arr['url']=$link;
				$arr['picurl']=$ret['url'];
				$arr['description']=$description;
				$ret=M('mp_reply_news')->add($arr);
				if($ret){
					$data['mpid']=$mp['id'];
			        $data['reply_id']=$ret;
			        $data['type']='news';
			     	$data['keyword']=$keyword;
			        $rule=M('mp_rule');
			     	$ret=$rule->add($data);
				}
			    if ($ret) {
	     	    	$this->ajaxReturn(array('status'=>0,'msg'=>"添加成功!"));
		     	} else {
		     	    $this->ajaxReturn(array('msg'=>$ret));
	     	    }

	    }else{
			$ret['fail']='本地上传失败';
          $this->ajaxReturn(array('status'=>1,'msg'=>$ret));
          exit;
		}
	}

2015-10-10 15:24:51 u010592604 阅读数 2939
  • 微信公众平台开发4-消息管理-微信开发php

    微信公众平台开发之消息管理是子恒老师《微信公众平台开发》视频教程的第4部。详细讲解了用php开发微信,对微信公众平台中的消息管理开发。内容包含微信关键字回复,多图文消息回复开发,接收图片消息,视频,小视频消息等等。欢迎反馈,微信/QQ:68183131

    6334 人正在学习 去看看 秦子恒

这里接上一篇:服务器配置

既然我们选用了开发者中心,那微信公众平台的自动回复就不得行了,那就自己开发吧。

这就要在服务器配置的URL里面处理了。


一、自动回复处理代码

@RequestMapping(value="/api",method=RequestMethod.POST)
	public void initPostAPI(HttpServletRequest request, HttpServletResponse response)
	{
		String Event = "";
		String MsgType = "";
		String Content = "";
		String strJson = new String();
		 // 将请求、响应的编码均设置为UTF-8(防止中文乱码)  
       try {
			request.setCharacterEncoding("UTF-8");
			response.setCharacterEncoding("UTF-8");  
		} catch (UnsupportedEncodingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
// 调用核心业务类接收消息、处理消息
       User user = new User();

        String respMessage = "";//反馈信息
        Map<String, String> map = new HashMap<String, String>();  
        try {
			map = parseXml(request);
	} catch (Exception e) {
		e.printStackTrace();
	}
        
        Event = map.get("Event");
        MsgType = map.get("MsgType");
        Content = map.get("Content");
        
        System.out.println("Content = " + Content);
        System.out.println("MsgType = " + MsgType);
        System.out.println("Event = " + Event);
        System.out.println("EventKey = " + map.get("EventKey"));
        if(Event == null){//Event为null 则为用户输入信息被动回复
        	//自动回复用户信息
        	if(MsgType.equals("text")){
        		strJson = sendMess(map,strJson, Content);
        	}
       }
            SendUserMess.sendMess(strJson);
	}


二、这是回复信息内容的处理(文本信息,图文信息)

public String sendMess(Map map,String strJson,String Content){
		
		//根据用户输入的关键字查询对应响应
		AutoReply autoReply = new AutoReply();
		autoReply.setKeyName(Content);
		List<AutoReply> list = this.replyService.findByKeyname(autoReply);
		System.out.println("list size = "+list.size());
		
		//向用户回复消息
		if(list.size() != 0){
			autoReply = list.get(0);
			System.out.println("KeyValue-----"+autoReply.getKeyValue()+"--");
			
			if(!autoReply.getKeyValue().equals("")){
				strJson = "{\"touser\" :\""+map.get("FromUserName")+"\",";
		        strJson += "\"msgtype\":\"text\",";
		        strJson += "\"text\":{";
		        strJson += "\"content\":\""+autoReply.getKeyValue()+"\"";
		        strJson += "}}";
			}else if(!autoReply.getGraphic().equals("")){
				String newsinfo = "";
				String[] strs = autoReply.getGraphic().split(",");
				strJson = "{\"touser\" :\""+map.get("FromUserName")+"\",";
		        strJson += "\"msgtype\":\"news\",";
		        strJson += "\"news\":";
				String news = "{\"articles\": [";
				System.out.println(news);
				
				for(String str:strs){
					Graphic graphic = graphicService.loadByPK(Integer.valueOf(str));
					String url = graphic.getPicurl();
					//url = saveUrl + url.substring(24);
					
					newsinfo += "{\"title\":\""+graphic.getTitle()+"\"," +
										"\"description\":\""+graphic.getDescription()+"\",\"url\":\""+graphic.getUrl()+"\"," +
										"\"picurl\":\""+url+"\""+
										"},";
				}
				
				news = news + newsinfo.substring(0, (newsinfo.length()-1));
				news = news + "]}";
				strJson += news;
				strJson += "}";
				System.out.println("news-------"+news);
			}
		}else{//如果在数据库中找不到对应回复用户的信息则回复默认消息
			autoReply.setKeyName("未知");
			List<AutoReply> list_ = this.replyService.findByKeyname(autoReply);
			autoReply = list_.get(0);
			
			/*StringBuffer content = new StringBuffer();
			content.append("感谢您!").append("\n");
			content.append("再次感谢您").append("\n");
			content.append("再次再次感谢您");*/
			//拼装需要发送的消息
			strJson = "{\"touser\" :\""+map.get("FromUserName")+"\",";
	        strJson += "\"msgtype\":\"text\",";
	        strJson += "\"text\":{";
	        strJson += "\"content\":\""+autoReply.getKeyValue()+"\"";
	        strJson += "}}";
		}
		return strJson;
	}

三、核心代码 SendUserMess

public class SendUserMess {
	public static void sendMess(String jsonParam){
		String openIdUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

		String result = null;
		result = String.format(openIdUrl, urlEnodeUTF8(Const.AppId),
				urlEnodeUTF8(Const.AppSecrect));

		try {
			System.out.println("Staring");
			// 创建HttpClient实例
			DefaultHttpClient  httpClient = new DefaultHttpClient ();
			// 创建Get方法实例
			HttpGet method = new HttpGet(result);
			HttpResponse re = httpClient.execute(method);
			String resData = EntityUtils.toString(re.getEntity());
			method.releaseConnection();
			System.out.println("get weixinnumber ..." + resData);
			JSONObject obj = JSONObject.fromObject(resData);
			if (obj != null) {
				String token = obj.getString("access_token");
				System.out.println("token = " + token);
                //获取ticket
				String sendMessUrl = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=%s";
				result = String.format(sendMessUrl, token);
				
				httpClient = new DefaultHttpClient();
				
				/*JSONObject jsonParam = new JSONObject();
				jsonParam.put("touser", ""+openId+"");
				jsonParam.put("msgtype", "text");
				jsonParam.put("text", "{'content': '"+respMessage+"'}");*/
				
				System.out.println(jsonParam);
				
				StringEntity entity = new StringEntity(jsonParam,"utf-8");//解决中文乱码问题    
                entity.setContentEncoding("UTF-8");    
                entity.setContentType("application/json");    
				
				// 创建Get方法实例
				HttpPost me = new HttpPost(result);
				me.setEntity(entity);
				HttpResponse hr = httpClient.execute(me);
				String data = EntityUtils.toString(hr.getEntity());
				me.releaseConnection();

				JSONObject userInfo = JSONObject.fromObject(data);
				System.out.println("get backMessage ..." + data);
				
			}
		} catch (Exception ex) {
			System.out.println("error");
			System.out.println(ex.getMessage());
		}
	}
	
	private static String urlEnodeUTF8(String str){
        String result = str;
        try {
            result = URLEncoder.encode(str,"UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


这里我用的不是自动回复的哪个接口, 而是用的“发送客服消息”的接口,一样可以达到效果。也可以试下哪个自动回复的接口,反正大体都一样

2019-06-29 20:23:39 m0_37605129 阅读数 391
  • 微信公众平台开发4-消息管理-微信开发php

    微信公众平台开发之消息管理是子恒老师《微信公众平台开发》视频教程的第4部。详细讲解了用php开发微信,对微信公众平台中的消息管理开发。内容包含微信关键字回复,多图文消息回复开发,接收图片消息,视频,小视频消息等等。欢迎反馈,微信/QQ:68183131

    6334 人正在学习 去看看 秦子恒

前提:第一次发布文章,如有写的不好的地方请指正或代码上可以优化的地方也请各路大神多多指教。顺带想吐槽下微信开发文档真的太恶心了,第一次接触下来让人头晕眼花,通过查阅各种资料后理清了思路,在此写上总结。

开发流程

  1. 服务器(url)接口配置
  2. 发送文字(根据微信公众号文档“接收事件推送”事件接口编写代码)
  3. 生成二维码(账号管理下的“生成带参数的二维码”事件接口编写代码)
  4. 根据UI的图组合微信头像、微信名字、公众号二维码并调用素材管理下的“新增临时素材”接口上传图片
  5. 发送图片(调用消息管理下的客服消息下的客服接口-发消息(图片)接口)
  6. 进行裂变(调用“扫描带参数二维码事件”)(此步骤还未开发完,需根据业务需求变动)

详细流程,附上代码:

第一步服务器(url)接口配置

服务器(url)接口配置,此步骤就是微信授权接口的过程,如果域名都不改变,微信只会校验一次。此请求微信文档中说明了是get请求。
/**
     * 微信消息接收和token验证
     * @param request
     * @param response
     */
    @RequestMapping(value = "/weChatToken", method = RequestMethod.GET)
    public void weChatToken(HttpServletRequest request, HttpServletResponse response){
        boolean isGet = request.getMethod().toLowerCase().equals("get");
        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 (signature != null && WeChatUtil.checkSignature(signature, timestamp, nonce)) {
                try {
                    PrintWriter print = response.getWriter();
                    print.write(echostr);
                    print.flush();
                } catch (IOException e) {
                    log.error("微信消息接收和token验证异常",e);
                }
            }
        }
    }

第二步发送文字

我这边的需求是发送的文字中要带上微信名称,所以还需要调用微信的获取accessToken接口跟获取用户信息接口。获取用户信息接口中需要openId,在用户点击关注微信公众号时,微信会自动发送过来。微信为什么会知道咱们的接口呢,因为咱们已经在第一步授权了接口,微信会以默认post请求的方式调用相同的路由,所以我们还得写一个post请求的接口。注:finally中是发送图片的动作。调用事件中运用了抽象工厂的设计模式,此处还有待优化。

2.1 接口详解

/**
     * 微信公众号 -->
     *   消息管理 -->
     *     接收普通消息、接收事件推送、被动回复用户消息
     *
     * @param request
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/weChatToken", method = RequestMethod.POST)
    public String responseEvent(HttpServletRequest request){
        WeChatMsgService weChatMsgService = null;
        Map<String, String> requestMessageMap = null;
        try {
            // 获取到请求参数
            requestMessageMap = XMLUtil.xmlToMap(request);
            log.info("获取到参数为:{}",requestMessageMap);

            // 判断请求参数中 MsgType的值或判断Event值
            String returnMessage = "";
            if (WeChatUtil.REQ_MESSAGE_TYPE_EVENT.equals(requestMessageMap.get("MsgType"))) {
                // 如果后期有取消订阅事件,此处方法可以去除if
                if (WeChatUtil.EVENT_TYPE_SUBSCRIBE.equals(requestMessageMap.get("Event"))) {
                    weChatMsgService = getType(requestMessageMap.get("Event"));
                    returnMessage = weChatMsgService.getReturnMessage(requestMessageMap);
                }
            }

            return returnMessage;
        } catch (Exception e) {
            log.error("微信公众号自动回复异常",e);
        }finally {
            if (WeChatUtil.EVENT_TYPE_SUBSCRIBE.equals(requestMessageMap.get("Event"))) {
                weChatMsgService = weChatCustomerServiceImgMessageServiceImpl;
                weChatMsgService.getReturnMessage(requestMessageMap);
            }

        }

        return "";
    }

2.1.2 使用设计模式调用不同的业务逻辑(default有待修改)
此处注入了业务层
@Autowired
private WeChatMsgService weChatTextMessageServiceImpl;

/**
     *  根据类型选择所需的业务处理逻辑
     * @param type
     */
    private WeChatMsgService getType(String type){
        switch (type) {
            case WeChatUtil.EVENT_TYPE_SUBSCRIBE:
                return weChatTextMessageServiceImpl;
            default:
                return null;
        }
    }

2.1.3 调用业务层组合返回的信息(微信文档要求返回的信息为xml的形式)

@Service
@Slf4j
public class WeChatTextMessageServiceImpl implements WeChatMsgService {

    @Autowired
    private WeiXinService weiXinService;

    @Override
    public String getReturnMessage(Map<String,String> requestMessageMap) {

        // 获取access_token
        WeiXinAccessTokenDto accessToken = weiXinService.getAccessToken();
        //获取用户信息
        WeiXinUserInfoDto userInfo = null;
        if (accessToken.getAccess_token() != null) {
            userInfo = weiXinService.getUnionID(accessToken.getAccess_token(), requestMessageMap.get("FromUserName"));
        }

        TextMessageResponseVo textMessageResponseVo = new TextMessageResponseVo();
        textMessageResponseVo.setFromUserName(requestMessageMap.get("ToUserName"));
        textMessageResponseVo.setToUserName(requestMessageMap.get("FromUserName"));
        textMessageResponseVo.setMsgType("text");
        textMessageResponseVo.setCreateTime(System.currentTimeMillis());
        textMessageResponseVo.setContent("欢迎" + userInfo.getNickname() + "11111");

        String result = WeChatUtil.messageToXml(textMessageResponseVo);
        return result;
    }

}

2.2 调用微信获取accessToken 的接口(业务层涉及到其他逻辑,所以就不全部粘出来了)

@Override
    public WeiXinAccessTokenDto getAccessToken() {
        Map<String,String> reqParams = new HashMap<>();
        reqParams.put("appid","appid");
        reqParams.put("secret","secret");
        reqParams.put("grant_type","client_credential");

        String result = HttpUtil.sendGetJoint(accessTokenUrl, reqParams);
        // 将返回的字符串转为对象
        WeiXinAccessTokenDto weiXinAccessTokenDto = JSON.parseObject(result, WeiXinAccessTokenDto.class);
        return weiXinAccessTokenDto;
    }

2.3 通过微信接口获取用户信息(微信获取用户信息的接口有三种,此处只举例一种)

@Override
    public WeiXinUserInfoDto getUnionID(String accessToken, String openId) {
        Map<String,String> reqParams = new HashMap<>();
        reqParams.put("lang","zh_CN");
        reqParams.put("access_token",accessToken);
        reqParams.put("openid",openId);

        String result = HttpUtil.sendGetJoint(unionIDUrl, reqParams);
        log.info("获取微信用户信息为:{}",result);
        // 将返回的字符串转为对象
        WeiXinUserInfoDto weiXinUserInfoDto = JSON.parseObject(result, WeiXinUserInfoDto.class);
        return weiXinUserInfoDto;
    }

2.4 返回信息实体类(此处用了Swagger,不用的童鞋可以删除注解。还要引入lombok)

/**
 * @author: hugh_lin
 * @date: 2019/06/26 18:53
 * @Description: 微信公众号返回消息公共实体类
 * @Modifed:
 **/
@Getter
@Setter
public class BaseWeChatResponseVo {

    @ApiModelProperty("开发者微信号")
    private String ToUserName;

    @ApiModelProperty("发送方帐号(一个OpenID)")
    private String FromUserName;

    @ApiModelProperty("消息创建时间 (整型)")
    private Long CreateTime;

    @ApiModelProperty("消息类型,event或text、image、voice、video、music、news")
    private String MsgType;

}

/**
 * @author: hugh_lin
 * @date: 2019/06/26 18:58
 * @Description: 微信公众号返回消息实体
 * @Modifed:
 **/
@Getter
@Setter
public class TextMessageResponseVo extends BaseWeChatResponseVo{

    @ApiModelProperty("文本消息内容")
    private String Content;

}

2.5 微信工具类

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.youchengjvhe.weishangketang.model.vo.wechat.TextMessageResponseVo;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Formatter;

/**
 * @author: hugh_lin
 * @date: 2019/06/26 15:45
 * @Description: 微信消息处理工具类
 * @Modifed:
 **/
@Slf4j
public class WeChatUtil {

    //请求消息的类型------------------------------------------
    /**
     *  文本
     */
    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_VOICE = "voice";

    /**
     *  视频
     */
    public static final String REQ_MESSAGE_TYPE_VIDEO = "video";

    /**
     *  小视频
     */
    public static final String REQ_MESSAGE_TYPE_SHORTVIDEO = "shortvideo";

    /**
     *  地理位置
     */
    public static final String REQ_MESSAGE_TYPE_LOCATION = "location";

    /**
     *  链接
     */
    public static final String REQ_MESSAGE_TYPE_LINK = "link";

    /**
     *  事件推送
     */
    public static final String REQ_MESSAGE_TYPE_EVENT = "event";

    /**
     *  事件类型--订阅
     */
    public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";

    /**
     *  取消订阅
     */
    public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";

    /**
     *  已关注用户扫描带参数二维码
     */
    public static final String EVENT_TYPE_SCAN = "SCAN";

    /**
     *  上报地理位置
     */
    public  static final String EVENT_TYPE_LOCATION = "LOCATION";

    /**
     *  自定义菜单事件
     */
    public static final String EVENT_TYPE_CLICK = "CLICK";

    //响应消息类型
    /**
     *  文本
     */
    public static final String RESP_MESSAGE_TYPE_TEXT = "text";

    /**
     *  图片
     */
    public static final String RESP_MESSAGE_TYPE_IMAGE = "image";

    /**
     *  语音
     */
    public static final String RESP_MESSAGE_TYPE_VOICE = "voice";

    /**
     *  视频
     */
    public static final String RESP_MESSAGE_TYPE_VIDEO = "video";

    /**
     *  音乐
     */
    public static final String RESP_MESSAGE_TYPE_MUSIC = "music";

    /**
     *  图文
     */
    public static final String RESP_MESSAGE_TYPE_ARTICLE = "news";

    //---------------------------------------------------------------------

    /**
     *  与测试账号接口配置信息中的Token要一致
     *  测试token,微信公众号内可自行配置
     */
    private static String token = "******";

    /**
     * 验证签名
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] arr = new String[] { token, timestamp, nonce };
        // 将token、timestamp、nonce三个参数进行字典序排序
        Arrays.sort(arr);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        MessageDigest md = null;
        String tmpStr = null;

        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToHex(digest );
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
        return tmpStr != null ? tmpStr.equals(signature) : false;
    }

    /**
     * 十六进制字节数组转为字符串
     * @param hash
     * @return
     */
    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     * 扩展xstream使其支持CDATA
     */
    private static XStream xstream = new XStream(new XppDriver() {
        @Override
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                // 对所有xml节点的转换都增加CDATA标记
                boolean cdata = true;

                @Override
                @SuppressWarnings("unchecked")
                public void startNode(String name, Class clazz) {
                    super.startNode(name, clazz);
                }

                @Override
                protected void writeText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    } else {
                        writer.write(text);
                    }
                }
            };
        }
    });

    /**
     * 文本消息转换成xml
     */
    public static String messageToXml(TextMessageResponseVo textMessageResponseVo) {
        xstream.alias("xml", textMessageResponseVo.getClass());
        return xstream.toXML(textMessageResponseVo);
    }

    /**
     * 链接url下载图片
     * @param urlList
     * @param path
     */
    public static void downloadPicture(String urlList,String path) {
        URL url = null;
        try {
            url = new URL(urlList);
            DataInputStream dataInputStream = new DataInputStream(url.openStream());

            FileOutputStream fileOutputStream = new FileOutputStream(new File(path));
            ByteArrayOutputStream output = new ByteArrayOutputStream();

            byte[] buffer = new byte[1024];
            int length;

            while ((length = dataInputStream.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }
            fileOutputStream.write(output.toByteArray());
            dataInputStream.close();
            fileOutputStream.close();
        } catch (Exception e) {
            log.error("根据url下载图片异常",e);
        }
    }
}

第三步组合图片与文字信息并返回

3、4、5步骤是在一个代码块中,所以此处进行详细分解。注:调用方式已在第二步说明

3.1 整体方法(一些注入的路径,可以自行查阅微信公众号文档)

/**
 * @author: hugh_lin
 * @date: 2019/06/28 16:37
 * @Description: 公众号客服消息(回复图片)
 * @Modifed:
 **/
@Service
public class WeChatCustomerServiceImgMessageServiceImpl implements WeChatMsgService {

    /**
     * 图片请求路径
     */
    @Value("${test.imgFileUrl}")
    private String imgFileUrl;
    /**
     * 图片生成路径
     */
    @Value("${test.imgFilePath}")
    private String imgFilePath;
    @Value("${weixin.getDownloadQrCodeUrl}")
    private String downloadQrCodeUrl;

    @Autowired
    private WeiXinService weiXinService;

    @Override
    public String getReturnMessage(Map<String, String> requestMessageMap) {

        // 获取accessToken
        WeiXinAccessTokenDto accessToken = weiXinService.getAccessToken();
        //获取用户信息
        WeiXinUserInfoDto userInfo = null;
        if (accessToken.getAccess_token() != null) {
            userInfo = weiXinService.getUnionID(accessToken.getAccess_token(), requestMessageMap.get("FromUserName"));
        }

        // 生成微信公众号二维码
        WeChatQrCodeDto qrCode = weiXinService.getQrCode(accessToken.getAccess_token(), userInfo.getOpenid());

        /* 下载二维码图片 */
        // 1.生成下载地址
        StringBuffer qrCodeUrl = new StringBuffer();
        qrCodeUrl.append(downloadQrCodeUrl);
        qrCodeUrl.append("?ticket=");
        qrCodeUrl.append(qrCode.getTicket());
        // 2.生成文件名并生成下载到本地的路径
        StringBuffer imgPath = new StringBuffer();
        imgPath.append("D:\\");
        imgPath.append(UUID.randomUUID());
        imgPath.append(".jpg");
        WeChatUtil.downloadPicture(qrCodeUrl.toString(),imgPath.toString());

        // 合成图片
        ImageUtil.generateCompositeImages("D:\\aaa.jpg",userInfo.getHeadimgurl(),imgPath.toString(),userInfo.getNickname());

        // 上传到微信公众号素材库,将数据转成json格式并取出需要的数据
        String weChatMedia = weiXinService.getWeChatMediaId(accessToken.getAccess_token(), imgPath.toString(), "image");
        JSONObject media = JSONObject.parseObject(weChatMedia);

        // 发送客服消息
        weiXinService.sendCustomerServiceMessage(accessToken.getAccess_token(),userInfo.getOpenid(),"image","media_id",media.getString("media_id"));

        return null;
    }
}

3.2 生成公众号二维码(此处为什么要调用微信接口生成呢,因为如果进行裂变的话,需要调用微信扫描二维码事件,所以更方便)

@Override
    public WeChatQrCodeDto getQrCode(String accessToken, String content) {

        // 拼接url
        StringBuffer sb = new StringBuffer();
        sb.append(qrCodeUrl);
        sb.append("?access_token=");
        sb.append(accessToken);

        // 生成json数据进行请求
        JSONObject sceneStrJson = new JSONObject();
        sceneStrJson.put("scene_str",content);
        JSONObject sceneJson = new JSONObject();
        sceneJson.put("scene",sceneStrJson);

        JSONObject reqJson = new JSONObject();
        reqJson.put("action_name","QR_LIMIT_STR_SCENE");
        reqJson.put("action_info",sceneJson);

        String result = HttpUtil.sendPost(sb.toString(), reqJson.toString());
        log.info("获取微信公众号二维码信息为:{}",result);
        // 将返回的字符串转为对象
        WeChatQrCodeDto weChatQrCodeDto = JSON.parseObject(result, WeChatQrCodeDto.class);
        return weChatQrCodeDto;
    }

3.3下载二维码图片到本地(因为要组合图片,需要用到。如何获取微信开发文档中有提及,此处就不多说了。此处调用的是WeChatUtil,在第二步中已写上)

3.4合成图片(因为调用了Graphics画图工具,组合的图片大小及位置可自行根据UI图进行调整)

/**
 * @author: hugh_lin
 * @date: 2019/06/28 09:52
 * @Description: 图片工具类
 * @Modifed:
 **/
@Slf4j
public class ImageUtil {

    /**
     * 生成组合图片(此处根据需求需要组成三张图片)
     * @param args
     *         args[0] backgroundImage 背景图路径
     *         args[1] weChatAvatar 微信头像路径
     *         args[2] qrCode 二维码路径
     *         args[3] 微信名
     */
    public static void generateCompositeImages(String... args){
        try {
            //创建初始图片
            BufferedImage img = new BufferedImage(1242, 2208, BufferedImage.TYPE_INT_RGB);
            //读取本地图片
            BufferedImage backgroundImage = ImageIO.read(new File(args[0]));
            //读取微信头像图片
            BufferedImage weChatAvatar = ImageIO.read(new URL(args[1]));
            //读取本地图片
            BufferedImage qrCode = ImageIO.read(new File(args[2]));

            //开启画图
            Graphics g = img.getGraphics();

            //将微信头像跟二维码设置为圆角(根据需求变更)
            weChatAvatar = setClip(weChatAvatar,100);
            qrCode = setClip(qrCode, 100);

            // 绘制缩小后的图
            g.drawImage(backgroundImage.getScaledInstance(1242,2208, Image.SCALE_DEFAULT), 0, 0, null);
            g.drawImage(weChatAvatar.getScaledInstance(85, 85, Image.SCALE_DEFAULT), 93, 35, null);
            g.drawImage(qrCode.getScaledInstance(370, 370, Image.SCALE_DEFAULT), 203, 1800, null);

            g.setColor(Color.black);
            g.setFont(new Font("微软雅黑", Font.PLAIN, 24));
            //绘制文字
            g.drawString(args[3], 214, 58);

            g.dispose();
            ImageIO.write(img, "jpg", new File(args[2]));

        } catch (Exception e) {
            log.error("生成组合图片异常",e);
        }
    }

    /**
     * 图片设置圆角
     * @param srcImage
     * @return
     * @throws IOException
     */
    private static BufferedImage setRadius(BufferedImage srcImage) throws IOException{
        int radius = (srcImage.getWidth() + srcImage.getHeight()) / 6;
        return setRadius(srcImage, radius, 2, 5);
    }

    /**
     * 图片设置圆角
     * @param srcImage
     * @param radius
     * @param border
     * @param padding
     * @return
     * @throws IOException
     */
    private static BufferedImage setRadius(BufferedImage srcImage, int radius, int border, int padding) throws IOException{
        int width = srcImage.getWidth();
        int height = srcImage.getHeight();
        int canvasWidth = width + padding * 2;
        int canvasHeight = height + padding * 2;

        BufferedImage image = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D gs = image.createGraphics();
        gs.setComposite(AlphaComposite.Src);
        gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        gs.setColor(Color.WHITE);
        gs.fill(new RoundRectangle2D.Float(0, 0, canvasWidth, canvasHeight, radius, radius));
        gs.setComposite(AlphaComposite.SrcAtop);
        gs.drawImage(setClip(srcImage, radius), padding, padding, null);
        if(border !=0){
            gs.setColor(Color.GRAY);
            gs.setStroke(new BasicStroke(border));
            gs.drawRoundRect(padding, padding, canvasWidth - 2 * padding, canvasHeight - 2 * padding, radius, radius);
        }
        gs.dispose();
        return image;
    }

    /**
     * 图片切圆角
     * @param srcImage
     * @param radius
     * @return
     */
    private static BufferedImage setClip(BufferedImage srcImage, int radius){
        int width = srcImage.getWidth();
        int height = srcImage.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D gs = image.createGraphics();

        gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        gs.setClip(new RoundRectangle2D.Double(0, 0, width, height, radius, radius));
        gs.drawImage(srcImage, 0, 0, null);
        gs.dispose();
        return image;
    }

}

3.5上传到微信公众号素材库(因为调用发送客服图片信息接口时需要用到参数,所以只能先上传到素材库)

 @Override
    public String getWeChatMediaId(String accessToken, String imgPath, String type) {
        try {
            File file=new File(imgPath);
            if (!file.exists() || !file.isFile()) {
                return null;
            }

            // 拼接请求地址
            StringBuffer sb = new StringBuffer();
            sb.append(mediaUrl);
            sb.append("?access_token=");
            sb.append(accessToken);
            sb.append("&type=");
            sb.append(type);

            String result = HttpUtil.weChatFodderPost(sb.toString(), file);

            return result;
        } catch (Exception e) {
            log.error("素材流关闭异常",e);
        }

        return null;
    }

3.6发送客服图片消息

/**
     *  客服接口-发消息(此方法目前只支持:
     *                               发送文本消息、发送图片消息、发送语音消息、发送视频消息、发送音乐消息)
     * @param args
     * args[0] accessToken
     * args[1] OPENID
     * args[2] msgtype
     * args[3] content、media_id.....
     * args[4] 内容
     */
@Override
    public void sendCustomerServiceMessage(String... args) {
        // 拼接url
        StringBuffer sb = new StringBuffer();
        sb.append(customerServiceUrl);
        sb.append("?access_token=");
        sb.append(args[0]);

        // 生成json数据进行请求
        JSONObject contentJson = new JSONObject();
        contentJson.put(args[3],args[4]);

        JSONObject reqJson = new JSONObject();
        reqJson.put("touser",args[1]);
        reqJson.put("msgtype",args[2]);
        reqJson.put(args[2],contentJson);

        HttpUtil.sendPost(sb.toString(), reqJson.toString());

    }

最后,遇到个bug,因为需求是先返回文字再返回图片。测试的时候,偶然发生了先返回图片再返回文字的情况。从我的认知角度讲,会先执行finally块,再执行return,也是先返回return的消息,再返回finally中的消息。遇到此问题是否由网络延迟或等原因造成的还有待考究。

最后附上个人微信:a373011739 ,谢谢各位大佬的观看,鞠躬。

2017-03-07 23:25:04 diandianxiyu 阅读数 20589
  • 微信公众平台开发4-消息管理-微信开发php

    微信公众平台开发之消息管理是子恒老师《微信公众平台开发》视频教程的第4部。详细讲解了用php开发微信,对微信公众平台中的消息管理开发。内容包含微信关键字回复,多图文消息回复开发,接收图片消息,视频,小视频消息等等。欢迎反馈,微信/QQ:68183131

    6334 人正在学习 去看看 秦子恒

需求

在公众号内的输入任意文字,文字相关的第三方网站链接

实现

简单后台设置

我们在微信公众平台上,是可以进行简单的自定义消息回复的。

这里写图片描述

这里的显然不能满足我们的需求。

配置服务器

配置域名

配置测试服务器

略。

和一般我们配置项目没有区别,所以不再赘述。

本次我们的测试域名是 http://***.***.**/***.php

验证Token

在微信的公众后台的开发 / 基本配置 / 填写服务器配置

我们需要配置服务器的地址,在启用配置之前,需要经过微信的token验证。

本文使用的实例代码为PHP.

官方文档给出了验证流程图

这里写图片描述

下面为PHP实现的代码。

//1 获取加密参数

$token = "***";

$signature = $_GET['signature'];

$timestamp  =$_GET['timestamp'];

$nonce = $_GET['nonce'];

$echostr = $_GET['echostr'];

//2 排序数组
$list = [
    'token'=>$token,
    'timestamp'=>$timestamp,
    'nonce'=>$nonce,
];

sort($list);
//拼接字符串

$str ="";
foreach ($list as $value){
    $str .= $value;
}

//加密
$encode_str = sha1($str);
if($encode_str == $signature){
    echo  $echostr;

}

接收消息

在官方文档中,有一个对应的事例代码。

https://wximg.gtimg.com/shake_tv/mpwiki/cryptoDemo.zip

第一步,接收普通消息

这里我们采用明文的方式接收数据。

下面采用官方的接口调试工具。

这里写图片描述

由于是被动调用接口,所以我们只需要从消息中得到最后的用户id以及消息文字就行了。

$msg = file_get_contents("php://input");

$xml = new DOMDocument();

$xml->loadXML($msg);

$from_user_name = $xml->getElementsByTagName("FromUserName")->item(0)->nodeValue;

$msg_type = $xml->getElementsByTagName("MsgType")->item(0)->nodeValue;

$content = $xml->getElementsByTagName("Content")->item(0)->nodeValue;

echo  $from_user_name."  说的类型是".$msg_type.",  内容是".$content;

发送消息

当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。严格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。

也就是说,直接返回对应的xml就行。

下面的简单实现代码,直接返回给用户自己说的话。

$msg = file_get_contents("php://input");

$xml = new DOMDocument();

$xml->loadXML($msg);

$from_user_name = $xml->getElementsByTagName("FromUserName")->item(0)->nodeValue;

$to_user_name = $xml->getElementsByTagName("ToUserName")->item(0)->nodeValue;

$msg_type = $xml->getElementsByTagName("MsgType")->item(0)->nodeValue;

$content = $xml->getElementsByTagName("Content")->item(0)->nodeValue;

$time = time();

$str = "<xml>
<ToUserName><![CDATA[".$from_user_name."]]></ToUserName>
<FromUserName><![CDATA[".$to_user_name."]]></FromUserName>
<CreateTime>".$time."</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[".$content."]]></Content>
</xml>";

echo $str;

下面是最终实现的效果

这里写图片描述

返回超链接

//前后省略,跟上面的一样

$str = "<xml>
<ToUserName><![CDATA[".$from_user_name."]]></ToUserName>
<FromUserName><![CDATA[".$to_user_name."]]></FromUserName>
<CreateTime>".$time."</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[".$content."]]></Title> 
<Description><![CDATA[我是文字描述]]></Description>
<PicUrl><![CDATA[http://avatar.csdn.net/C/6/6/1_diandianxiyu.jpg]]></PicUrl>
<Url><![CDATA[http://blog.coderfix.cn]]></Url>
</item>
<item>
</xml>";

实现效果

这里写图片描述

总结

微信的文档还是十分详细的,我们能通过微信实现很多好玩的功能。

参考资料

2017-09-20 21:43:58 coco2d_x2014 阅读数 3129
  • 微信公众平台开发4-消息管理-微信开发php

    微信公众平台开发之消息管理是子恒老师《微信公众平台开发》视频教程的第4部。详细讲解了用php开发微信,对微信公众平台中的消息管理开发。内容包含微信关键字回复,多图文消息回复开发,接收图片消息,视频,小视频消息等等。欢迎反馈,微信/QQ:68183131

    6334 人正在学习 去看看 秦子恒
没有更多推荐了,返回首页