【JavaWeb】(10)微信公众号开发进阶

由于普通开发会有许多的权限限制,所以我们可以申请一个测试账号来开发体验一下微信公众号的其他接口功能。申请测试号我就不介绍了,很简单。申请成功后,还需要配置Url地址和token,和我们普通公众账号填写的一致就可以了。

1. 图文消息

这里由于我们图文消息用到许多上一篇TextMessage中的一些属性,所以我们需要重构一下我们的代码,创建基类BaseMessage:

public class BaseMessage {

	private String ToUserName;
	private String FromUserName;
	private long CreateTime;
	private String MsgType;
	public String getToUserName() {
		return ToUserName;
	}
	public void setToUserName(String toUserName) {
		ToUserName = toUserName;
	}
	public String getFromUserName() {
		return FromUserName;
	}
	public void setFromUserName(String fromUserName) {
		FromUserName = fromUserName;
	}
	public long getCreateTime() {
		return CreateTime;
	}
	public void setCreateTime(long createTime) {
		CreateTime = createTime;
	}
	public String getMsgType() {
		return MsgType;
	}
	public void setMsgType(String msgType) {
		MsgType = msgType;
	}
}
然后将我们的TextMessage和NewsMessage都继承这个基类。在创建图文消息中的一条项目类News:

public class News {

	private String Title;
	private String Description;
	private String PicUrl;
	private String Url;
}
最后创建图文消息类:

public class NewsMessage extends BaseMessage {

	private int ArticleCount;
	private List<News> Articles;
}
每一个类都生成set和get方法。

增添我们的工具类MessageUtil:

        /**
	 * 将图文消息转换成xml
	 * 
	 * @param newsMessage
	 */
	public static String newsMessageToXml(NewsMessage newsMessage) {
		XStream xStream = new XStream();
		xStream.alias("xml", newsMessage.getClass());
		xStream.alias("item", new News().getClass());
		return xStream.toXML(newsMessage);
	}

	/**
	 * 图文消息的组装
	 * 
	 * @param toUserName
	 * @param fromUserName
	 * @return
	 */
	public static String initNewsMessage(String toUserName, String fromUserName) {
		String message = null;
		List<News> newsList = new ArrayList<News>();
		NewsMessage newsMessage = new NewsMessage();
		News news = new News();
		News news2 = new News();

		news.setTitle("克里斯托弗诺兰");
		news.setDescription("美国当代伟大的导演");
		news.setPicUrl("http://wechat.tunnel.mobi/WeChat/image/p1659233202.jpg");
		news.setUrl("http://movie.duuban.com");
		newsList.add(news);
		
		news2.setTitle("克里斯托弗诺兰");
		news2.setDescription("美国当代伟大的导演");
		news2.setPicUrl("http://wechat.tunnel.mobi/WeChat/image/p1659233202.jpg");
		news2.setUrl("http://movie.duuban.com");
		newsList.add(news2);

		newsMessage.setToUserName(fromUserName);
		newsMessage.setFromUserName(toUserName);
		newsMessage.setCreateTime(new Date().getTime());
		newsMessage.setMsgType(MESSAGE_NEWS);
		newsMessage.setArticles(newsList);
		newsMessage.setArticleCount(newsList.size());
		message = newsMessageToXml(newsMessage);
		return message;
	}
这里需要新增一个常量:

	public static final String MESSAGE_NEWS = "news";
最后在修改一下WeChatServlet类测试就可以了。


2. access_token的获取

access_token是公众号的全局唯一票据,公众号调用各接口时都需要使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,续订是刷新,重复获取将导致上次获取的access_token失效。

创建我们的工具类:

public class WeChatUtil {

	private static final String APPID = "wx893f724e92d10f25";
	private static final String APP_SECRET = "b2ba8f4bcceb544798ce25509910555f";

	private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";

	/**
	 * 使用get方式获得Json对象
	 * 
	 * @param url
	 * @return
	 */
	public static JSONObject doGetString(String url) {
		DefaultHttpClient httpClient = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet(url);
		JSONObject jsonObject = null;

		try {
			HttpResponse response = httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String result = EntityUtils.toString(entity, "UTF-8");
				jsonObject = JSONObject.fromObject(result);
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return jsonObject;
	}

	/**
	 * 使用post方式获得Json对象
	 * 
	 * @param url
	 * @return
	 */
	public static JSONObject doPostString(String url, String outString) {

		DefaultHttpClient httpClient = new DefaultHttpClient();
		HttpPost httpPost = new HttpPost(url);
		JSONObject jsonObject = null;

		try {

			httpPost.setEntity(new StringEntity(outString, "UTF-8"));
			HttpResponse response = httpClient.execute(httpPost);
			String result = EntityUtils.toString(response.getEntity(), "UTF-8");
			jsonObject = JSONObject.fromObject(result);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * 获取access_token
	 * 
	 * @return
	 */
	public static AccessToken getAccessToken() {
		AccessToken token = new AccessToken();
		String url = ACCESS_TOKEN_URL.replace("APPID", APPID).replace(
				"APPSECRET", APP_SECRET);
		JSONObject jsonObject = doGetString(url);
		if (jsonObject != null) {
			token.setToken(jsonObject.getString("access_token"));
			token.setExpiresIn(jsonObject.getInt("expires_in"));
		}
		return token;
	}
}
里面有get和post两种方式获取access_token的方法,这里需要导入几个jar包,后面的项目源码里面会有,可以下载。


3. 图片消息自动回复

这里我们首先需要获得一个OpenId,新增我们的工具类方法,使用上传图片的一个方法来获得一个media_id,上传文件的类比较长,这里就不贴出来了。上传的Url地址文档里面也有介绍,这个是上传地址:https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE

继续扩充我们的工具类MessageUtil:

        /**
	 * 将图片消息转换成xml
	 * 
	 * @param newsMessage
	 */
	public static String imageMessageToXml(ImageMessage imageMessage) {
		XStream xStream = new XStream();
		xStream.alias("xml", imageMessage.getClass());
		return xStream.toXML(imageMessage);
	}

	/**
	 * 组装图片消息
	 * 
	 * @param toUserName
	 * @param fromUserName
	 * @return
	 */
	public static String initImageMessage(String toUserName, String fromUserName) {
		String message = null;

		Image image = new Image();

		image.setMediaId("kk5p2z-NaHhQs1X6oZsMgWKzz4p8xWW1nVJAG4jx1NGM1mWAypJZ6gHlJPTLw243");
		ImageMessage imageMessage = new ImageMessage();
		imageMessage.setImage(image);
		imageMessage.setToUserName(fromUserName);
		imageMessage.setFromUserName(toUserName);
		imageMessage.setMsgType(MESSAGE_IMAGE);
		imageMessage.setCreateTime(new Date().getTime());
		message = imageMessageToXml(imageMessage);
		return message;
	}
其中的MediaId就是我们再上传的时候取得的,每一次上传取得的MediaId都是不同的,这里需要注意一下。


4. 音乐消息的回复

音乐消息的回复和图片消息的回复大体上一直,我们需要添加一首歌到项目中。

这里我们要发的音乐消息需要一个缩略图,和上传图片的方式基本一样,只不过在类型上修改为“thumb__media_id”,其它操作基本一样。


5. 自定义菜单

接下来我们看一下如何创建自定义菜单。

对照文档首先创建基类按钮:

public class Button {

	private String name;
	private String type;
	private Button[] sub_button;
}
然后创建ClickButton、ViewButton,ClickButton自己独有的属性:key、ViewButton独有的属性:url,最后创建Menu实体类,只有一个成员变量Button集合。

创建主菜单:

        /**
	 * 创建组装主菜单
	 * 
	 * @return
	 */
	public static Menu initMenu() {
		Menu menu = new Menu();

		// 主菜单一
		ClickButton button11 = new ClickButton();
		button11.setName("Click菜单");
		button11.setType("click");
		button11.setKey("11");

		// 主菜单二
		ViewButton button21 = new ViewButton();
		button21.setName("view菜单");
		button21.setType("view");
		button21.setUrl("http://movie.douban.com");

		// 菜单三子菜单一
		ClickButton button31 = new ClickButton();
		button31.setName("扫码事件");
		button31.setType("scancode_push");
		button31.setKey("31");

		// 菜单三子菜单二
		ClickButton button32 = new ClickButton();
		button32.setName("地理位置");
		button32.setType("location_select");
		button32.setKey("32");

		// 主菜单三
		Button button = new Button();
		button.setName("菜单");
		button.setSub_button(new Button[] { button31, button32 });

		menu.setButton(new Button[] { button11, button21, button });
		return menu;
	}
然后编写创建菜单的方法:

	/**
	 * 创建菜单
	 * @param token
	 * @param menu
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static int createMenu(String token, String menu)
			throws ClientProtocolException, IOException {
		int result = 0;
		String url = CREATE_MENU_URL.replace("ACCESS_TOKEN", token);
		JSONObject jsonObject = doPostString(url, menu);
		if (jsonObject != null) {
			result = jsonObject.getInt("errcode");
		}
		return result;
	}
这里需要添加一个URL的常量:

	private static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
编写测试代码:

			String menu = JSONObject.fromObject(WeChatUtil.initMenu())
					.toString();
			int result = WeChatUtil.createMenu(token.getToken(), menu);
			if (result == 0) {
				System.out.println("创建菜单成功");
			} else {

				System.out.println("创建菜单失败,错误码" + result);
			}
执行! 创建菜单成功!


6. 菜单的事件推送

修改我们的WeChatServlet类:

			} else if (MessageUtil.MESSAGE_EVENT.equals(msgType)) {
				String eventType = map.get("Event");
				if (MessageUtil.MESSAGE_SUBSCRIBE.equals(eventType)) { // 首次关注事件
					message = MessageUtil.initText(toUserName, fromUserName,
							MessageUtil.menuText());
				} else if (MessageUtil.MESSAGE_CLICK.equals(eventType)) { // 菜单一的点击事件
					message = MessageUtil.initText(toUserName, fromUserName,
							MessageUtil.menuText());
				} else if (MessageUtil.MESSAGE_VIEW.equals(eventType)) { // 菜单二的点击事件
					String url = map.get("EventKey");
					message = MessageUtil.initText(toUserName, fromUserName,
							url);
				} else if (MessageUtil.MESSAGE_SCANCODE.equals(eventType)) { // 菜单三的扫码事件
					String key = map.get("EventKey");
					message = MessageUtil.initText(toUserName, fromUserName,
							key);
				}
			} else if (MessageUtil.MESSAGE_LOCATION.equals(msgType)) { // 地理位置
				String label = map.get("Label");
				message = MessageUtil.initText(toUserName, fromUserName, label);
			}
其中,如果当前页面要跳转到另一个页面的时候,那么公众号是不会给我们在返回来的时候输出信息的。


7. 自定义菜单的查询与删除

	/**
	 * 菜单的查询
	 * 
	 * @param token
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static JSONObject queryMenu(String token)
			throws ClientProtocolException, IOException {
		String url = QUERY_MENU_URL.replace("ACCESS_TOKEN", token);
		JSONObject jsonObject = doGetString(url);
		return jsonObject;
	}

	/**
	 * 菜单的删除
	 * 
	 * @param token
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static int deleteMenu(String token)
			throws ClientProtocolException, IOException {
		String url = DELETE_MENU_URL.replace("ACCESS_TOKEN", token);
		JSONObject jsonObject = doGetString(url);
		int result = 0;
		if (jsonObject != null) {
			result = jsonObject.getInt("errcode");
		}
		return result;
	}
需要添加两个常量:

	private static final String QUERY_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
	private static final String DELETE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";
编写测试类即可。


8. 百度翻译

接下来增加一个翻译的应用。做这个功能首先需要在百度开发平台申请一个账号,并添加一个应用,记住自己的API Key和Secret Key,后面我们将用到。

翻译的工具方法很简单:

	public static String translate(String source)
			throws ClientProtocolException, IOException {
		String url = TRANS_URL.replace("KEYWORD",
				URLEncoder.encode(source, "UTF-8"));
		JSONObject jsonObject = doGetString(url);
		StringBuffer dst = new StringBuffer();
		List<Map> list = (List<Map>) jsonObject.get("trans_result");
		for (Map map : list) {
			dst.append(map.get("dst"));
		}
		return dst.toString();
	}

TRANS_URL是这个样子的:

	private static final String TRANS_URL = "http://openapi.baidu.com/public/2.0/bmt/translate?client_id=bqnAaRDLL6KGVjZByGeO8Ycq&q=KEYWORD&from=auto&to=auto";
client_id后面跟的是我们申请的API Key,from和to设置成auto可以让工具自己判断翻译中文或者英文。
然后在Servlet中调用即可:

					message = MessageUtil.initText(toUserName, fromUserName,
							WeChatUtil.translate(content));
大功告成!


版权声明:本文为博主原创文章,未经博主允许不得转载。

【JavaWeb】(10)微信公众号开发进阶

上一篇:vim 配置 C/C++/Rust/Python 开发环境


下一篇:HTTP/1.x的连接管理