实现查找附近功能;
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/
先提交位置信息,然后输入“附近咖啡厅”,返回附近咖啡厅的图文信息。
参考文档
完整源代码
相关推荐
微信开发实例 微信公众平台开发教程-深入浅出微信公众平台实战开发(微网站、LBS云、Api接口调用、服务号高级接口)1.微信接口前期准备。由北风网提供,微信开发实例微信公众平台基础篇: 1)微信公众平台简介:开发...
微信公众号平台发开书籍与实例文档 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
微信公众平台应用开发实战-完整扫描版 pdf 版本。 微信公众平台应用开发实战-完整扫描版 pdf 文档。 微信公众平台应用开发实战-完整扫描版.pdf 全书一共9章,在逻辑上分为四大部分:第一部分(第1章)介绍了微信公众...
第二部分(第2、3章)介绍了进行微信公众平台开发所需的准备工作及将应用接入微信公众平台的方法;第三部分(第4、5章)详细介绍了微信公众平台已开放的API,通过实战项目对开发框架进行了系统讲解,读者甚至可以...
深入浅出微信公众平台实战开发(微网站、LBS云、Api接口调用、服务号高级接口) 入门到精通 详细讲解微信公众开发
刘捷版微信公众平台企业应用开发实战 Sample源码以及相关SDK
本书是微信公众平台应用开发方面的书籍,作者是腾讯公司的软件开发工程师,而且是微信公众平台应用开发的先驱者之一。本书全面介绍了微信公众平台应用开发所需的各项技术,系统解读了微信公众平台开放的API和各项...
《微信公众平台应用开发实战》是微信公众平台应用开发领域的经典著作,作者是腾讯公司的资深软件开发工程师,而且是微信公众平台应用开发的先驱者之一。本书全面介绍了微信公众平台应用开发所需的各项技术,系统解读...
对微信公众平台开发有兴趣的同学 对ASP.NET MVC开发有兴趣的同学 有志进入开发行业的所有同学 课程特色: 最前沿ASP.NET MVC 5、Entity Framework 6、微信公众平台5.2 最全6大功能19个接口用法大揭秘 零基础快速...
ASP.NET MVC 5 微信公众平台整合开发实战第三部
《企业微信公众平台开发实战:再小的个体也有自己的品牌》方倍工作室,2015年,配套源码。
微信公众平台应用开发实战-完整扫描版 PDF扫描版,放心下载。
《微信公众平台开发实战》(php版),手打完美书签,方便导航入阅读
本书是微信公众平台应用开发领域的经典著作,作者是腾讯公司的资深软件开发工程师,而且是微信公众平台应用开发的先驱者之一。本书全面介绍了微信公众平台应用开发所需的各项技术,系统解读了微信公众平台开放的API...
微信公众平台开发入门教程
PHP微信公众平台开发,内容丰富,涵盖了多个PHP微信公众平台开发的方面,适合于初学者来学习借鉴。
第二部分(第2、3章)介绍了进行微信公众平台开发所需的准备工作及将应用接入微信公众平台的方法;第三部分(第4、5章)详细介绍了微信公众平台已开放的API,通过实战项目对开发框架进行了系统讲解,读者甚至可以...