实现查找附近功能;
1)发送地理位置;
点击窗口底部的“+”按钮,选择“位置”,点“发送”
2)指定关键词搜索;
格式:附近+关键词\n例如:附近ATM、附近KTV、附近厕所
目录结构
- 项目结构图
- 增加和修改相关源代码
- 百度地点类
- 用户位置类
- 用户位置访问类
- 百度地图工具类
- 地图网页文件
-
核心Service
- 消息处理工具类
- Maven项目文件
- 上传本地代码到GitHub
- 上传工程WAR档至SAE
- 微信客户端测试
- 参考文档
- 完整项目源代码
项目结构图
源代码文件说明
序号 | 文件名 | 说明 | 操作 |
1 | BaiduPlace.java | 百度地点类 | 新增 |
2 | UserLocation.java | 用户位置类 | 新增 |
3 | UserLocationDao.java | 用户位置访问类 | 新增 |
4 | BaiduMapUtil.java | 百度地图工具类 | 新增 |
5 | route.jsp | 导航页面 | 新增 |
6 | CoreService.java | 核心服务类,新增处理各种输入,返回不同图文信息 | 更新 |
7 | MessageUtil.java | 新增生成图文信息的方法 | 更新 |
8 | pom.xml | 新增json和Base64等jar | 更新 |
增加和修改相关源代码
百度地点类
BaiduPlace.java
package com.coderdream.model; /** * 地址信息 * */ public class BaiduPlace implements Comparable<BaiduPlace> { /** * 名称 */ private String name; /** * 详细地址 */ private String address; /** * 经度 */ private String lng; /** * 纬度 */ private String lat; /** * 联系电话 */ private String telephone; /** * 距离 */ private int distance; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getLng() { return lng; } public void setLng(String lng) { this.lng = lng; } public String getLat() { return lat; } public void setLat(String lat) { this.lat = lat; } public String getTelephone() { return telephone; } public void setTelephone(String telephone) { this.telephone = telephone; } public int getDistance() { return distance; } public void setDistance(int distance) { this.distance = distance; } public int compareTo(BaiduPlace baiduPlace) { return this.distance - baiduPlace.getDistance(); } }
用户位置类
UserLocation.java
package com.coderdream.model; /** * 用户地理位置 */ public class UserLocation { private String openId; /** * 经度 */ private String lng; /** * 纬度 */ private String lat; /** * 百度地图经度 */ private String bd09Lng; /** * 百度地图纬度 */ private String bd09Lat; public String getOpenId() { return openId; } public void setOpenId(String openId) { this.openId = openId; } public String getLng() { return lng; } public void setLng(String lng) { this.lng = lng; } public String getLat() { return lat; } public void setLat(String lat) { this.lat = lat; } public String getBd09Lng() { return bd09Lng; } public void setBd09Lng(String bd09Lng) { this.bd09Lng = bd09Lng; } public String getBd09Lat() { return bd09Lat; } public void setBd09Lat(String bd09Lat) { this.bd09Lat = bd09Lat; } }
用户位置访问类
UserLocationDao.java
package com.coderdream.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.coderdream.model.UserLocation; import com.coderdream.util.DBUtil; /** * */ public class UserLocationDao { /** * 保存用户地理位置 * * @param request * 请求对象 * @param openId * 用户的OpenID * @param lng * 用户发送的经度 * @param lat * 用户发送的纬度 * @param bd09_lng * 经过百度坐标转换后的经度 * @param bd09_lat * 经过百度坐标转换后的纬度 */ public int saveUserLocation(String openId, String lng, String lat, String bd09_lng, String bd09_lat) { String sql = "insert into user_location(open_id, lng, lat, bd09_lng, bd09_lat) values (?, ?, ?, ?, ?)"; int count = 0; Connection con = null; PreparedStatement ps = null; try { con = DBUtil.getConnection(); ps = con.prepareStatement(sql); ps.setString(1, openId); ps.setString(2, lng); ps.setString(3, lat); ps.setString(4, bd09_lng); ps.setString(5, bd09_lat); count = ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { // 释放资源 DBUtil.close(ps); DBUtil.close(con); } return count; } /** * 获取用户最后一次发送的地理位置 * * @param request * 请求对象 * @param openId * 用户的OpenID * @return UserLocation */ public UserLocation getLastLocation(String openId) { UserLocation userLocation = null; String sql = "select open_id, lng, lat, bd09_lng, bd09_lat from user_location where open_id=? order by id desc limit 0,1"; Connection con = null; PreparedStatement pre = null; ResultSet rs = null; try { con = DBUtil.getConnection(); pre = con.prepareStatement(sql); pre.setString(1, openId); rs = pre.executeQuery(); if (rs.next()) { userLocation = new UserLocation(); userLocation.setOpenId(rs.getString("open_id")); userLocation.setLng(rs.getString("lng")); userLocation.setLat(rs.getString("lat")); userLocation.setBd09Lng(rs.getString("bd09_lng")); userLocation.setBd09Lat(rs.getString("bd09_lat")); } // 释放资源 rs.close(); } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(rs); DBUtil.close(pre); DBUtil.close(con); } return userLocation; } }
百度地图工具类
BaiduMapUtil.java
package com.coderdream.util; import it.sauronsoftware.base64.Base64; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.List; import net.sf.json.JSONObject; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import com.coderdream.model.Article; import com.coderdream.model.BaiduPlace; import com.coderdream.model.UserLocation; /** * 百度地图操作类 javabase64-1.3.1.jar * */ public class BaiduMapUtil { /** * 圆形区域检索 * * @param query * 检索关键词 * @param lng * 经度 * @param lat * 纬度 * @return List<BaiduPlace> * @throws UnsupportedEncodingException */ public static List<BaiduPlace> searchPlace(String query, String lng, String lat) throws Exception { // 拼装请求地址 String requestUrl = "http://api.map.baidu.com/place/v2/search?&query=QUERY&location=LAT,LNG&radius=2000&output=xml&scope=2&page_size=10&page_num=0&ak=CA21bdecc75efc1664af5a195c30bb4e"; requestUrl = requestUrl.replace("QUERY", URLEncoder.encode(query, "UTF-8")); requestUrl = requestUrl.replace("LAT", lat); requestUrl = requestUrl.replace("LNG", lng); // 调用Place API圆形区域检索 String respXml = httpRequest(requestUrl); // 解析返回的xml List<BaiduPlace> placeList = parsePlaceXml(respXml); return placeList; } /** * 发送http请求 * * @param requestUrl * 请求地址 * @return String */ public static String httpRequest(String requestUrl) { StringBuffer buffer = new StringBuffer(); try { URL url = new URL(requestUrl); HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection(); httpUrlConn.setDoInput(true); httpUrlConn.setRequestMethod("GET"); httpUrlConn.connect(); // 将返回的输入流转换成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 释放资源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); } catch (Exception e) { e.printStackTrace(); } return buffer.toString(); } /** * 根据百度地图返回的流解析出地址信息 * * @param inputStream * 输入流 * @return List<BaiduPlace> */ @SuppressWarnings("unchecked") private static List<BaiduPlace> parsePlaceXml(String xml) { List<BaiduPlace> placeList = null; try { Document document = DocumentHelper.parseText(xml); // 得到xml根元素 Element root = document.getRootElement(); // 从根元素获取<results> Element resultsElement = root.element("results"); // 从<results>中获取<result>集合 List<Element> resultElementList = resultsElement.elements("result"); // 判断<result>集合的大小 if (resultElementList.size() > 0) { placeList = new ArrayList<BaiduPlace>(); // POI名称 Element nameElement = null; // POI地址信息 Element addressElement = null; // POI经纬度坐标 Element locationElement = null; // POI电话信息 Element telephoneElement = null; // POI扩展信息 Element detailInfoElement = null; // 距离中心点的距离 Element distanceElement = null; // 遍历<result>集合 for (Element resultElement : resultElementList) { nameElement = resultElement.element("name"); addressElement = resultElement.element("address"); locationElement = resultElement.element("location"); telephoneElement = resultElement.element("telephone"); detailInfoElement = resultElement.element("detail_info"); BaiduPlace place = new BaiduPlace(); place.setName(nameElement.getText()); place.setAddress(addressElement.getText()); place.setLng(locationElement.element("lng").getText()); place.setLat(locationElement.element("lat").getText()); // 当<telephone>元素存在时获取电话号码 if (null != telephoneElement) place.setTelephone(telephoneElement.getText()); // 当<detail_info>元素存在时获取<distance> if (null != detailInfoElement) { distanceElement = detailInfoElement.element("distance"); if (null != distanceElement) place.setDistance(Integer.parseInt(distanceElement.getText())); } placeList.add(place); } // 按距离由近及远排序 Collections.sort(placeList); } } catch (Exception e) { e.printStackTrace(); } return placeList; } /** * 根据Place组装图文列表 * * @param placeList * @param bd09Lng * 经度 * @param bd09Lat * 纬度 * @return List<Article> */ public static List<Article> makeArticleList(List<BaiduPlace> placeList, String bd09Lng, String bd09Lat) { // 项目的根路径 String basePath = "http://1.wxquan.sinaapp.com/"; List<Article> list = new ArrayList<Article>(); BaiduPlace place = null; for (int i = 0; i < placeList.size(); i++) { place = placeList.get(i); Article article = new Article(); article.setTitle(place.getName() + "\n距离约" + place.getDistance() + "米"); // P1表示用户发送的位置(坐标转换后),p2表示当前POI所在位置 article.setUrl(String.format(basePath + "route.jsp?p1=%s,%s&p2=%s,%s", bd09Lng, bd09Lat, place.getLng(), place.getLat())); // 将首条图文的图片设置为大图 if (i == 0) article.setPicUrl(basePath + "images/poisearch.png"); else article.setPicUrl(basePath + "images/navi.png"); list.add(article); } return list; } /** * 将微信定位的坐标转换成百度坐标(GCJ-02 -> Baidu) * * @param lng * 经度 * @param lat * 纬度 * @return UserLocation */ public static UserLocation convertCoord(String lng, String lat) { // 百度坐标转换接口 String convertUrl = "http://api.map.baidu.com/ag/coord/convert?from=2&to=4&x={x}&y={y}"; convertUrl = convertUrl.replace("{x}", lng); convertUrl = convertUrl.replace("{y}", lat); UserLocation location = new UserLocation(); try { String jsonCoord = httpRequest(convertUrl); JSONObject jsonObject = JSONObject.fromObject(jsonCoord); // 对转换后的坐标进行Base64解码 location.setBd09Lng(Base64.decode(jsonObject.getString("x"), "UTF-8")); location.setBd09Lat(Base64.decode(jsonObject.getString("y"), "UTF-8")); } catch (Exception e) { location = null; e.printStackTrace(); } return location; } }
导航页面
route.jsp
<%@ page language="java" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>步行导航</title> <meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=0;"> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <style type="text/css"> body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;} </style> <script type="text/javascript" src="http://api.map.baidu.com/api?v=1.5&ak=CA21bdecc75efc1664af5a195c30bb4e"></script> </head> <% String p1 = request.getParameter("p1"); String p2 = request.getParameter("p2"); %> <body> <div id="allmap"></div> </body> <script type="text/javascript"> // 创建起点、终点的经纬度坐标点 var p1 = new BMap.Point(<%=p1%>); var p2 = new BMap.Point(<%=p2%>); // 创建地图、设置中心坐标和默认缩放级别 var map = new BMap.Map("allmap"); map.centerAndZoom(new BMap.Point((p1.lng+p2.lng)/2, (p1.lat+p2.lat)/2), 17); // 右下角添加缩放按钮 map.addControl(new BMap.NavigationControl({anchor: BMAP_ANCHOR_BOTTOM_RIGHT, type: BMAP_NAVIGATION_CONTROL_ZOOM})); // 步行导航检索 var walking = new BMap.WalkingRoute(map, {renderOptions:{map: map, autoViewport: true}}); walking.search(p1, p2); </script> </html>
核心服务类
更新代码后,能实现查找附近功能;
1)发送地理位置;
点击窗口底部的“+”按钮,选择“位置”,点“发送”
2)指定关键词搜索;
格式:附近+关键词\n例如:附近ATM、附近KTV、附近厕所。
CoreService.java
package com.coderdream.service; import java.io.InputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import com.coderdream.bean.Logging; import com.coderdream.dao.LoggingDao; import com.coderdream.dao.UserLocationDao; import com.coderdream.model.Article; import com.coderdream.model.BaiduPlace; import com.coderdream.model.Music; import com.coderdream.model.MusicMessage; import com.coderdream.model.NewsMessage; import com.coderdream.model.TextMessage; import com.coderdream.model.UserLocation; import com.coderdream.util.BaiduMapUtil; import com.coderdream.util.MessageUtil; /** * 核心服务类 */ public class CoreService { public static String TAG = "CoreService"; private Logger logger = Logger.getLogger(CoreService.class); private LoggingDao loggingDao = new LoggingDao(this.getClass().getName()); private UserLocationDao userLocationDao = new UserLocationDao(); /** * 处理微信发来的请求 * * @param request * @return xml */ public String processRequest(InputStream inputStream) { logger.debug(TAG + " #1# processRequest"); SimpleDateFormat f_timestamp = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS"); Logging logging = new Logging(f_timestamp.format(Calendar.getInstance().getTime()), "DEBUG", TAG, "#1# processRequest"); loggingDao.addLogging(logging); // xml格式的消息数据 String respXml = null; // 默认返回的文本消息内容 String respContent = "未知的消息类型!"; try { // 调用parseXml方法解析请求消息 Map<String, String> requestMap = MessageUtil.parseXml(inputStream); // 发送方帐号 String fromUserName = requestMap.get("FromUserName"); // 开发者微信号 String toUserName = requestMap.get("ToUserName"); // 消息类型 String msgType = requestMap.get("MsgType"); String logStr = "#2# fromUserName: " + fromUserName + ", toUserName: " + toUserName + ", msgType: " + msgType; logger.debug(TAG + logStr); logging = new Logging(f_timestamp.format(Calendar.getInstance().getTime()), "DEBUG", TAG, logStr); loggingDao.addLogging(logging); // 回复文本消息 TextMessage textMessage = new TextMessage(); textMessage.setToUserName(fromUserName); textMessage.setFromUserName(toUserName); textMessage.setCreateTime(new Date().getTime()); textMessage.setMsgType(MessageUtil.MESSAGE_TYPE_TEXT); logStr = "#3# textMessage: " + textMessage.toString(); logger.debug(TAG + logStr); logging = new Logging(f_timestamp.format(Calendar.getInstance().getTime()), "DEBUG", TAG, logStr); loggingDao.addLogging(logging); // 文本消息 if (msgType.equals(MessageUtil.MESSAGE_TYPE_TEXT)) { // respContent = "您发送的是文本消息!"; // 接收用户发送的文本消息内容 String content = requestMap.get("Content").trim(); // 创建图文消息 NewsMessage newsMessage = new NewsMessage(); newsMessage.setToUserName(fromUserName); newsMessage.setFromUserName(toUserName); newsMessage.setCreateTime(new Date().getTime()); newsMessage.setMsgType(MessageUtil.MESSAGE_TYPE_NEWS); newsMessage.setFuncFlag(0); List<Article> articleList = new ArrayList<Article>(); // 翻译 if (content.startsWith("翻译")) { String keyWord = content.replaceAll("^翻译", "").trim(); if ("".equals(keyWord)) { textMessage.setContent(getTranslateUsage()); } else { textMessage.setContent(BaiduTranslateService.translate(keyWord)); } respContent = textMessage.getContent(); } // 如果以“歌曲”2个字开头 else if (content.startsWith("歌曲")) { // 将歌曲2个字及歌曲后面的+、空格、-等特殊符号去掉 String keyWord = content.replaceAll("^歌曲[\\+ ~!@#%^-_=]?", ""); // 如果歌曲名称为空 if ("".equals(keyWord)) { logging = new Logging(f_timestamp.format(Calendar.getInstance().getTime()), "DEBUG", TAG, "#歌曲名称为空#"); loggingDao.addLogging(logging); respContent = getMusicUsage(); } else { String[] kwArr = keyWord.split("@"); // 歌曲名称 String musicTitle = kwArr[0]; // 演唱者默认为空 String musicAuthor = ""; if (2 == kwArr.length) { musicAuthor = kwArr[1]; } // 搜索音乐 Music music = BaiduMusicService.searchMusic(musicTitle, musicAuthor); // 未搜索到音乐 if (null == music) { respContent = "对不起,没有找到你想听的歌曲<" + musicTitle + ">。"; logging = new Logging(f_timestamp.format(Calendar.getInstance().getTime()), "DEBUG", TAG, "#未搜索到音乐 respContent# " + respContent); loggingDao.addLogging(logging); } else { // 音乐消息 logger.info("找到 " + musicTitle + " 了!!!"); MusicMessage musicMessage = new MusicMessage(); musicMessage.setToUserName(fromUserName); musicMessage.setFromUserName(toUserName); musicMessage.setCreateTime(new Date().getTime()); musicMessage.setMsgType(MessageUtil.MESSAGE_TYPE_MUSIC); musicMessage.setMusic(music); newsMessage.setFuncFlag(0); respXml = MessageUtil.messageToXml(musicMessage); logging = new Logging(f_timestamp.format(Calendar.getInstance().getTime()), "DEBUG", TAG, "#return respXml# " + respXml); loggingDao.addLogging(logging); return respXml; } } } // 如果以“历史”2个字开头 else if (content.startsWith("历史")) { // 将歌曲2个字及歌曲后面的+、空格、-等特殊符号去掉 String dayStr = content.substring(2); // 如果只输入历史两个字,在输出当天的历史 if (null == dayStr || "".equals(dayStr.trim())) { DateFormat df = new SimpleDateFormat("MMdd"); dayStr = df.format(Calendar.getInstance().getTime()); } respContent = TodayInHistoryService.getTodayInHistoryInfoFromDB(dayStr); } // 周边搜索 else if (content.startsWith("附近")) { String keyWord = content.replaceAll("附近", "").trim(); // 获取用户最后一次发送的地理位置 UserLocation location = userLocationDao.getLastLocation(fromUserName); // 未获取到 if (null == location) { respContent = getLocationUsage(); } else { // 根据转换后(纠偏)的坐标搜索周边POI List<BaiduPlace> placeList = BaiduMapUtil.searchPlace(keyWord, location.getBd09Lng(), location.getBd09Lat()); // 未搜索到POI if (null == placeList || 0 == placeList.size()) { respContent = String.format("/难过,您发送的位置附近未搜索到“%s”信息!", keyWord); } else { articleList = BaiduMapUtil.makeArticleList(placeList, location.getBd09Lng(), location.getBd09Lat()); // 回复图文消息 newsMessage = new NewsMessage(); newsMessage.setToUserName(fromUserName); newsMessage.setFromUserName(toUserName); newsMessage.setCreateTime(new Date().getTime()); newsMessage.setMsgType(MessageUtil.MESSAGE_TYPE_NEWS); newsMessage.setArticles(articleList); newsMessage.setArticleCount(articleList.size()); return MessageUtil.messageToXml(newsMessage); } } } // 单图文消息 else if ("1".equals(content)) { Article article = new Article(); article.setTitle("微信公众帐号开发教程Java版"); article.setDescription("柳峰,80后,微信公众帐号开发经验4个月。为帮助初学者入门,特推出此系列教程,也希望借此机会认识更多同行!"); article.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article.setUrl("http://blog.csdn.net/lyq8479?toUserName=" + fromUserName + "&toUserName=" + toUserName); articleList.add(article); // 设置图文消息个数 newsMessage.setArticleCount(articleList.size()); // 设置图文消息包含的图文集合 newsMessage.setArticles(articleList); // 将图文消息对象转换成xml字符串 return MessageUtil.messageToXml(newsMessage); } // 单图文消息---不含图片 else if ("2".equals(content)) { Article article = new Article(); article.setTitle("微信公众帐号开发教程Java版"); // 图文消息中可以使用QQ表情、符号表情 article.setDescription("柳峰,80后," // + emoji(0x1F6B9) + ",微信公众帐号开发经验4个月。为帮助初学者入门,特推出此系列连载教程,也希望借此机会认识更多同行!\n\n目前已推出教程共12篇,包括接口配置、消息封装、框架搭建、QQ表情发送、符号表情发送等。\n\n后期还计划推出一些实用功能的开发讲解,例如:天气预报、周边搜索、聊天功能等。"); // 将图片置为空 article.setPicUrl(""); article.setUrl("http://blog.csdn.net/lyq8479?toUserName=" + fromUserName + "&toUserName=" + toUserName); articleList.add(article); newsMessage.setArticleCount(articleList.size()); newsMessage.setArticles(articleList); return MessageUtil.messageToXml(newsMessage); } // 多图文消息 else if ("3".equals(content)) { Article article1 = new Article(); article1.setTitle("微信公众帐号开发教程\n引言"); article1.setDescription(""); article1.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article1.setUrl("http://blog.csdn.net/lyq8479/article/details/8937622?toUserName=" + fromUserName + "&toUserName=" + toUserName); Article article2 = new Article(); article2.setTitle("第2篇\n微信公众帐号的类型"); article2.setDescription(""); article2.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article2.setUrl("http://blog.csdn.net/lyq8479/article/details/8941577?toUserName=" + fromUserName + "&toUserName=" + toUserName); Article article3 = new Article(); article3.setTitle("关注页面"); article3.setDescription("关注页面"); article3.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article3.setUrl("http://wxquan.sinaapp.com/follow.jsp?toUserName=" + fromUserName + "&toUserName=" + toUserName); articleList.add(article1); articleList.add(article2); articleList.add(article3); newsMessage.setArticleCount(articleList.size()); newsMessage.setArticles(articleList); return MessageUtil.messageToXml(newsMessage); } // 多图文消息---首条消息不含图片 else if ("4".equals(content)) { Article article1 = new Article(); article1.setTitle("微信公众帐号开发教程Java版"); article1.setDescription(""); // 将图片置为空 article1.setPicUrl(""); article1.setUrl("http://blog.csdn.net/lyq8479"); Article article2 = new Article(); article2.setTitle("第4篇\n消息及消息处理工具的封装"); article2.setDescription(""); article2.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article2.setUrl("http://blog.csdn.net/lyq8479/article/details/8949088?toUserName=" + fromUserName + "&toUserName=" + toUserName); Article article3 = new Article(); article3.setTitle("第5篇\n各种消息的接收与响应"); article3.setDescription(""); article3.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article3.setUrl("http://blog.csdn.net/lyq8479/article/details/8952173?toUserName=" + fromUserName + "&toUserName=" + toUserName); Article article4 = new Article(); article4.setTitle("第6篇\n文本消息的内容长度限制揭秘"); article4.setDescription(""); article4.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article4.setUrl("http://blog.csdn.net/lyq8479/article/details/8967824?toUserName=" + fromUserName + "&toUserName=" + toUserName); articleList.add(article1); articleList.add(article2); articleList.add(article3); articleList.add(article4); newsMessage.setArticleCount(articleList.size()); newsMessage.setArticles(articleList); return MessageUtil.messageToXml(newsMessage); } // 多图文消息---最后一条消息不含图片 else if ("5".equals(content)) { Article article1 = new Article(); article1.setTitle("第7篇\n文本消息中换行符的使用"); article1.setDescription(""); article1.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article1.setUrl("http://blog.csdn.net/lyq8479/article/details/9141467?toUserName=" + fromUserName + "&toUserName=" + toUserName); Article article2 = new Article(); article2.setTitle("第8篇\n文本消息中使用网页超链接"); article2.setDescription(""); article2.setPicUrl("http://wxquan.sinaapp.com/meal.jpg"); article2.setUrl("http://blog.csdn.net/lyq8479/article/details/9157455?toUserName=" + fromUserName + "&toUserName=" + toUserName); Article article3 = new Article(); article3.setTitle("如果觉得文章对你有所帮助,请通过博客留言或关注微信公众帐号xiaoqrobot来支持柳峰!"); article3.setDescription(""); // 将图片置为空 article3.setPicUrl(""); article3.setUrl("http://blog.csdn.net/lyq8479"); articleList.add(article1); articleList.add(article2); articleList.add(article3); newsMessage.setArticleCount(articleList.size()); newsMessage.setArticles(articleList); return MessageUtil.messageToXml(newsMessage); } // 其他,弹出帮助信息 else { respContent = getUsage(); } } // 图片消息 else if (msgType.equals(MessageUtil.MESSAGE_TYPE_IMAGE)) { respContent = "您发送的是图片消息!"; } // 语音消息 else if (msgType.equals(MessageUtil.MESSAGE_TYPE_VOICE)) { respContent = "您发送的是语音消息!"; } // 视频消息 else if (msgType.equals(MessageUtil.MESSAGE_TYPE_VIDEO)) { respContent = "您发送的是视频消息!"; } // 地理位置消息 else if (msgType.equals(MessageUtil.MESSAGE_TYPE_LOCATION)) { respContent = "您发送的是地理位置消息!"; // 用户发送的经纬度 String lng = requestMap.get("Location_Y"); String lat = requestMap.get("Location_X"); // 坐标转换后的经纬度 String bd09Lng = null; String bd09Lat = null; // 调用接口转换坐标 UserLocation userLocation = BaiduMapUtil.convertCoord(lng, lat); if (null != userLocation) { bd09Lng = userLocation.getBd09Lng(); bd09Lat = userLocation.getBd09Lat(); } logger.info("lng= " + lng + "; lat= " + lat); logger.info("bd09Lng= " + bd09Lng + "; bd09Lat= " + bd09Lat); // 保存用户地理位置 int count = userLocationDao.saveUserLocation(fromUserName, lng, lat, bd09Lng, bd09Lat); loggingDao.debug("fromUserName" + fromUserName + "lng= " + lng + "; lat= " + lat + "bd09Lng= " + bd09Lng + "; bd09Lat= " + bd09Lat + "count" + count); StringBuffer buffer = new StringBuffer(); buffer.append("[愉快]").append("成功接收您的位置!").append("\n\n"); buffer.append("您可以输入搜索关键词获取周边信息了,例如:").append("\n"); buffer.append(" 附近ATM").append("\n"); buffer.append(" 附近KTV").append("\n"); buffer.append(" 附近厕所").append("\n"); buffer.append("必须以“附近”两个字开头!"); respContent = buffer.toString(); } // 链接消息 else if (msgType.equals(MessageUtil.MESSAGE_TYPE_LINK)) { respContent = "您发送的是链接消息!"; } // 事件推送 else if (msgType.equals(MessageUtil.MESSAGE_TYPE_EVENT)) { // 事件类型 String eventType = requestMap.get("Event"); // 关注 if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) { respContent = "谢谢您的关注!/n"; respContent += getSubscribeMsg(); } // 取消关注 else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) { // TODO 取消订阅后用户不会再收到公众账号发送的消息,因此不需要回复 } // 扫描带参数二维码 else if (eventType.equals(MessageUtil.EVENT_TYPE_SCAN)) { // TODO 处理扫描带参数二维码事件 } // 上报地理位置 else if (eventType.equals(MessageUtil.EVENT_TYPE_LOCATION)) { // TODO 处理上报地理位置事件 } // 自定义菜单 else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) { // TODO 处理菜单点击事件 } } logStr = "#4# respContent: " + respContent; logger.debug(TAG + logStr); logging = new Logging(f_timestamp.format(Calendar.getInstance().getTime()), "DEBUG", TAG, logStr); loggingDao.addLogging(logging); // 设置文本消息的内容 textMessage.setContent(respContent); // 将文本消息对象转换成xml respXml = MessageUtil.messageToXml(textMessage); logStr = "#5# respXml: " + respXml; logger.debug(TAG + logStr); logging = new Logging(f_timestamp.format(Calendar.getInstance().getTime()), "DEBUG", TAG, logStr); loggingDao.addLogging(logging); } catch (Exception e) { e.printStackTrace(); } return respXml; } /** * 翻译使用指南 * * @return */ public static String getTranslateUsage() { StringBuffer buffer = new StringBuffer(); // buffer.append(XiaoqUtil.emoji(0xe148)).append("Q译通使用指南").append("\n\n"); buffer.append("Q译通为用户提供专业的多语言翻译服务,目前支持以下翻译方向:").append("\n"); buffer.append(" 中 -> 英").append("\n"); buffer.append(" 英 -> 中").append("\n"); buffer.append(" 日 -> 中").append("\n\n"); buffer.append("使用示例:").append("\n"); buffer.append(" 翻译我是中国人").append("\n"); buffer.append(" 翻译dream").append("\n"); buffer.append(" 翻译さようなら").append("\n\n"); buffer.append("回复“?”显示主菜单"); return buffer.toString(); } /** * 歌曲点播使用指南 * * @return */ public static String getMusicUsage() { StringBuffer buffer = new StringBuffer(); buffer.append("歌曲点播操作指南").append("\n\n"); buffer.append("回复:歌曲+歌名").append("\n"); buffer.append("例如:歌曲存在").append("\n"); buffer.append("或者:歌曲存在@汪峰").append("\n\n"); buffer.append("回复“?”显示主菜单"); return buffer.toString(); } /** * 歌曲点播使用指南 * * @return */ public static String getUsage() { StringBuffer buffer = new StringBuffer(); buffer.append("历史年月(历史0403)").append("\n"); buffer.append("歌曲歌名(歌曲)").append("\n"); buffer.append("翻译词语(翻译明天)-支持中英日语").append("\n"); buffer.append("回复“?”显示主菜单"); return buffer.toString(); } /** * 关注提示语 * * @return */ public static String getSubscribeMsg() { StringBuffer buffer = new StringBuffer(); buffer.append("您是否有过出门在外四处找ATM或厕所的经历?").append("\n\n"); buffer.append("您是否有过出差在外搜寻美食或娱乐场所的经历?").append("\n\n"); buffer.append("周边搜索为您的出行保驾护航,为您提供专业的周边生活指南,回复“附近”开始体验吧!"); return buffer.toString(); } /** * 使用说明 * * @return */ public static String getLocationUsage() { StringBuffer buffer = new StringBuffer(); buffer.append("周边搜索使用说明").append("\n\n"); buffer.append("1)发送地理位置").append("\n"); buffer.append("点击窗口底部的“+”按钮,选择“位置”,点“发送”").append("\n\n"); buffer.append("2)指定关键词搜索").append("\n"); buffer.append("格式:附近+关键词\n例如:附近ATM、附近KTV、附近厕所"); return buffer.toString(); } }
消息处理工具类
MessageUtil.java
package com.coderdream.util; import java.io.InputStream; import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.coderdream.model.Article; import com.coderdream.model.MusicMessage; import com.coderdream.model.NewsMessage; import com.coderdream.model.TextMessage; 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; /** * 消息处理工具类 * */ public class MessageUtil { public static String TAG = "MessageUtil"; private static Logger logger = Logger.getLogger(MessageUtil.class); /** * 请求消息类型:文本 */ public static final String MESSAGE_TYPE_TEXT = "text"; /** * 请求消息类型:图片 */ public static final String MESSAGE_TYPE_IMAGE = "image"; /** * 请求消息类型:语音 */ public static final String MESSAGE_TYPE_VOICE = "voice"; /** * 请求消息类型:视频 */ public static final String MESSAGE_TYPE_VIDEO = "video"; /** * 请求消息类型:地理位置 */ public static final String MESSAGE_TYPE_LOCATION = "location"; /** * 请求消息类型:链接 */ public static final String MESSAGE_TYPE_LINK = "link"; /** * 请求消息类型:事件推送 */ public static final String MESSAGE_TYPE_EVENT = "event"; /** * 事件类型:subscribe(订阅) */ public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; /** * 事件类型:unsubscribe(取消订阅) */ public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; /** * 事件类型:scan(用户已关注时的扫描带参数二维码) */ public static final String EVENT_TYPE_SCAN = "scan"; /** * 事件类型:LOCATION(上报地理位置) */ public static final String EVENT_TYPE_LOCATION = "LOCATION"; /** * 事件类型:CLICK(自定义菜单) */ public static final String EVENT_TYPE_CLICK = "CLICK"; /** * 响应消息类型:图文 */ public static final String MESSAGE_TYPE_NEWS = "news"; /** * 响应消息类型:音乐 */ public static final String MESSAGE_TYPE_MUSIC = "music"; /** * 解析微信发来的请求(XML) * * @param request * @return Map<String, String> * @throws Exception */ @SuppressWarnings("unchecked") public static Map<String, String> parseXml(InputStream inputStream) throws Exception { // 将解析结果存储在HashMap中 Map<String, String> map = new HashMap<String, String>(); logger.debug(TAG + " begin"); // 从request中取得输入流 // InputStream inputStream = request.getInputStream(); // 读取输入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); logger.debug(TAG + " read inputStream"); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子节点 List<Element> elementList = root.elements(); // 遍历所有子节点 for (Element e : elementList) { map.put(e.getName(), e.getText()); logger.debug(TAG + " ###### log4j debug" + e.getName() + " : " + e.getText()); } // 释放资源 inputStream.close(); inputStream = null; return map; } /** * 扩展xstream使其支持CDATA */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 boolean cdata = true; @SuppressWarnings("rawtypes") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); /** * 文本消息对象转换成xml * * @param textMessage * 文本消息对象 * @return xml */ public static String messageToXml(TextMessage textMessage) { xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } /** * 图文消息对象转换成xml * * @param newsMessage * 图文消息对象 * @return xml */ public static String messageToXml(NewsMessage newsMessage) { xstream.alias("xml", newsMessage.getClass()); xstream.alias("item", new Article().getClass()); return xstream.toXML(newsMessage); } /** * 音乐消息对象转换成xml * * @param musicMessage * 音乐消息对象 * @return xml */ public static String messageToXml(MusicMessage musicMessage) { xstream.alias("xml", musicMessage.getClass()); String musicString = xstream.toXML(musicMessage); return musicString; } }
Maven工程配置文件
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.coderdream</groupId> <artifactId>wxquan</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>wxquan Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <junit.version>4.11</junit.version> <servlet.api.version>2.5</servlet.api.version> <jsp.api.version>2.1</jsp.api.version> <slf4j.version>1.7.5</slf4j.version> <dom4j.version>1.6.1</dom4j.version> <xstream.version>1.4.7</xstream.version> <mysql.version>5.1.17</mysql.version> <dbunit.version>2.4.9</dbunit.version> <gson.version>2.2.4</gson.version> <json.version>2.4</json.version> <javabase.version>1.3.1</javabase.version> </properties> <dependencies> <!-- 测试的时候用到,打包的时候没有 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- 编译的时候用到,打包的时候没有 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>${servlet.api.version}</version> <scope>provided</scope> </dependency> <!-- 编译的时候用到,打包的时候没有 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>${jsp.api.version}</version> <scope>provided</scope> </dependency> <!-- 日志包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- 打包的时候剔除 xml-apis-1.0.b2.jar SAE中不支持 --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>${dom4j.version}</version> <exclusions> <exclusion> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> </exclusion> </exclusions> </dependency> <!-- 打包的时候剔除 xml-apis-1.0.b2.jar SAE中不支持 --> <!-- 这个jar必须用1.4.7的高版本,否则SAE不支持 --> <!-- 详细原因:http://blog.csdn.net/lyq8479/article/details/38878543 --> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>${xstream.version}</version> </dependency> <!-- 编译的时候用到,打包的时候没有,SAE已包含此jar --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> <scope>provided</scope> </dependency> <!-- 测试的时候用到,打包的后WAR里没有 --> <dependency> <groupId>org.dbunit</groupId> <artifactId>dbunit</artifactId> <version>${dbunit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>${gson.version}</version> </dependency> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>${json.version}</version> </dependency> <dependency> <groupId>it.sauronsoftware</groupId> <artifactId>javabase64</artifactId> <version>${javabase.version}</version> </dependency> </dependencies> <build> <finalName>wxquan</finalName> </build> </project>
上传本地代码到GitHub
将新增和修改过的代码上传到GitHub
上传工程WAR档至SAE
将eclipse中的工程导出为wxquan.war档,上传到SAE中,更新已有的版本。
微信客户端测试
登录微信网页版:https://wx.qq.com/
先提交位置信息,然后输入“附近咖啡厅”,返回附近咖啡厅的图文信息。
参考文档
完整源代码