论坛首页 移动开发技术论坛

微信公众平台开发实战(08) 基于地理信息的服务(LBS)

浏览 2418 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2014-10-24  

实现查找附近功能;

1)发送地理位置;

点击窗口底部的“+”按钮,选择“位置”,点“发送”

2)指定关键词搜索;

格式:附近+关键词\n例如:附近ATM、附近KTV、附近厕所

 

目录结构

  • 项目结构图
  • 增加和修改相关源代码
    1. 百度地点类
    2. 用户位置类
    3. 用户位置访问类
    4. 百度地图工具类
    5. 地图网页文件
    6. 核心Service

    7. 消息处理工具类
    8. 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/

 先提交位置信息,然后输入“附近咖啡厅”,返回附近咖啡厅的图文信息。


 
 

 

参考文档

  1.  

 

完整源代码

  • 大小: 27.9 KB
  • 大小: 55.6 KB
  • 大小: 144.8 KB
论坛首页 移动开发技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics