首页 > 代码库 > 【JavaWeb】(10)微信公众号开发进阶

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

因为普通开发会有很多的权限限制,所以我们能够申请一个測试账号来开发体验一下微信公众号的其它接口功能。

申请測试号我就不介绍了。非常easy。申请成功后,还须要配置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. 自己定义菜单

接下来我们看一下怎样创建自己定义菜单。

对比文档首先创建基类button:

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,后面我们将用到。

翻译的工具方法非常easy:

	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)微信公众号开发进阶