xujunwei 3 years atrás
parent
commit
547bd88c08
23 changed files with 1710 additions and 0 deletions
  1. 11 0
      framework-common/pom.xml
  2. 219 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/InMsgParaser.java
  3. 124 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/OutMsgXmlBuilder.java
  4. 54 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InImageMsg.java
  5. 62 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InLinkMsg.java
  6. 72 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InLocationMsg.java
  7. 84 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InMsg.java
  8. 44 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InTextMsg.java
  9. 52 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InVideoMsg.java
  10. 54 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InVoiceMsg.java
  11. 62 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/event/InFollowEvent.java
  12. 65 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/event/InLocationEvent.java
  13. 54 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/event/InMenuEvent.java
  14. 72 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/event/InQrCodeEvent.java
  15. 62 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/speech_recognition/InSpeechRecognitionResults.java
  16. 79 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/News.java
  17. 50 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutImageMsg.java
  18. 87 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutMsg.java
  19. 111 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutMusicMsg.java
  20. 108 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutNewsMsg.java
  21. 47 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutTextMsg.java
  22. 76 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutVideoMsg.java
  23. 61 0
      framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutVoiceMsg.java

+ 11 - 0
framework-common/pom.xml

@@ -15,6 +15,17 @@
             <version>7.0.11</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>3.8</version>
+        </dependency>
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.19</version>
+        </dependency>
+
         <!-- 汉子转拼音 -->
         <dependency>
             <groupId>com.belerweb</groupId>

+ 219 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/InMsgParaser.java

@@ -0,0 +1,219 @@
+package com.mrxu.framework.common.weixin.msg;
+
+
+import com.mrxu.framework.common.util.StrFunc;
+import com.mrxu.framework.common.weixin.msg.in.*;
+import com.mrxu.framework.common.weixin.msg.in.event.InFollowEvent;
+import com.mrxu.framework.common.weixin.msg.in.event.InLocationEvent;
+import com.mrxu.framework.common.weixin.msg.in.event.InMenuEvent;
+import com.mrxu.framework.common.weixin.msg.in.event.InQrCodeEvent;
+import com.mrxu.framework.common.weixin.msg.in.speech_recognition.InSpeechRecognitionResults;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.Element;
+
+public class InMsgParaser {
+	
+	private InMsgParaser() {}
+	
+	/**
+	 * 从 xml 中解析出各类消息与事件
+	 */
+	public static InMsg parse(String xml) {
+		try {
+			return doParse(xml);
+		} catch (DocumentException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	/**
+	 * 消息类型
+	 * 1:text 文本消息
+	 * 2:image 图片消息
+	 * 3:voice 语音消息
+	 * 4:video 视频消息
+	 * 5:location 地址位置消息
+	 * 6:link 链接消息
+	 * 7:event 事件
+	 */
+	private static InMsg doParse(String xml) throws DocumentException {
+		Document doc = DocumentHelper.parseText(xml);
+        Element root = doc.getRootElement();
+        String toUserName = root.elementText("ToUserName");
+        String fromUserName = root.elementText("FromUserName");
+        Integer createTime = Integer.parseInt(root.elementText("CreateTime"));
+        String msgType = root.elementText("MsgType");
+        if ("text".equals(msgType))
+        	return parseInTextMsg(root, toUserName, fromUserName, createTime, msgType);
+        if ("image".equals(msgType))
+        	return parseInImageMsg(root, toUserName, fromUserName, createTime, msgType);
+        if ("voice".equals(msgType))
+        	return parseInVoiceMsgAndInSpeechRecognitionResults(root, toUserName, fromUserName, createTime, msgType);
+        if ("video".equals(msgType))
+        	return parseInVideoMsg(root, toUserName, fromUserName, createTime, msgType);
+        if ("location".equals(msgType))
+        	return parseInLocationMsg(root, toUserName, fromUserName, createTime, msgType);
+        if ("link".equals(msgType))
+        	return parseInLinkMsg(root, toUserName, fromUserName, createTime, msgType);
+        if ("event".equals(msgType))
+        	return parseInEvent(root, toUserName, fromUserName, createTime, msgType);
+        throw new RuntimeException("无法识别的消息类型,请查阅微信公众平台开发文档");
+	}
+	
+	private static InMsg parseInTextMsg(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
+		InTextMsg msg = new InTextMsg(toUserName, fromUserName, createTime, msgType);
+		msg.setContent(root.elementText("Content"));
+		msg.setMsgId(root.elementText("MsgId"));
+		return msg;
+	}
+	
+	private static InMsg parseInImageMsg(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
+		InImageMsg msg = new InImageMsg(toUserName, fromUserName, createTime, msgType);
+		msg.setPicUrl(root.elementText("PicUrl"));
+		msg.setMediaId(root.elementText("MediaId"));
+		msg.setMsgId(root.elementText("MsgId"));
+		return msg;
+	}
+	
+	private static InMsg parseInVoiceMsgAndInSpeechRecognitionResults(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
+		String recognition = root.elementText("Recognition");
+		if (StrFunc.isEmpty(recognition)) {
+			InVoiceMsg msg = new InVoiceMsg(toUserName, fromUserName, createTime, msgType);
+			msg.setMediaId(root.elementText("MediaId"));
+			msg.setFormat(root.elementText("Format"));
+			msg.setMsgId(root.elementText("MsgId"));
+			return msg;
+		}
+		else {
+			InSpeechRecognitionResults msg = new InSpeechRecognitionResults(toUserName, fromUserName, createTime, msgType);
+			msg.setMediaId(root.elementText("MediaId"));
+			msg.setFormat(root.elementText("Format"));
+			msg.setMsgId(root.elementText("MsgId"));
+			msg.setRecognition(recognition);			// 与 InVoiceMsg 唯一的不同之处
+			return msg;
+		}
+	}
+	
+	private static InMsg parseInVideoMsg(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
+		InVideoMsg msg = new InVideoMsg(toUserName, fromUserName, createTime, msgType);
+		msg.setMediaId(root.elementText("MediaId"));
+		msg.setThumbMediaId(root.elementText("ThumbMediaId"));
+		msg.setMsgId(root.elementText("MsgId"));
+		return msg;
+	}
+	
+	private static InMsg parseInLocationMsg(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
+		InLocationMsg msg = new InLocationMsg(toUserName, fromUserName, createTime, msgType);
+		msg.setLocation_X(root.elementText("Location_X"));
+		msg.setLocation_Y(root.elementText("Location_Y"));
+		msg.setScale(root.elementText("Scale"));
+		msg.setLabel(root.elementText("Label"));
+		msg.setMsgId(root.elementText("MsgId"));
+		return msg;
+	}
+	
+	private static InMsg parseInLinkMsg(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
+		InLinkMsg msg = new InLinkMsg(toUserName, fromUserName, createTime, msgType);
+		msg.setTitle(root.elementText("Title"));
+		msg.setDescription(root.elementText("Description"));
+		msg.setUrl(root.elementText("Url"));
+		msg.setMsgId(root.elementText("MsgId"));
+		return msg;
+	}
+	
+	// 解析四种事件
+	private static InMsg parseInEvent(Element root, String toUserName, String fromUserName, Integer createTime, String msgType) {
+		String event = root.elementText("Event");
+		String eventKey = root.elementText("EventKey");
+		
+		// 关注/取消关注事件(包括二维码扫描关注,二维码扫描关注事件与扫描带参数二维码事件是两回事)
+		if (("subscribe".equals(event) || "unsubscribe".equals(event)) && StrFunc.isEmpty(eventKey)) {
+			InFollowEvent e = new InFollowEvent(toUserName, fromUserName, createTime, msgType);
+			e.setEvent(event);
+			return e;
+		}
+		
+		// 扫描带参数二维码事件之一		1: 用户未关注时,进行关注后的事件推送
+		String ticket = root.elementText("Ticket");
+		if ("subscribe".equals(event) && StrFunc.isEmpty(eventKey) && eventKey.startsWith("qrscene_")) {
+			InQrCodeEvent e = new InQrCodeEvent(toUserName, fromUserName, createTime, msgType);
+			e.setEvent(event);
+			e.setEventKey(eventKey.substring(8));
+			e.setTicket(ticket);
+			return e;
+		}
+		// 扫描带参数二维码事件之二		2: 用户已关注时的事件推送
+		if ("SCAN".equals(event)) {
+			InQrCodeEvent e = new InQrCodeEvent(toUserName, fromUserName, createTime, msgType);
+			e.setEvent(event);
+			e.setEventKey(eventKey);
+			e.setTicket(ticket);
+			return e;
+		}
+		
+		// 上报地理位置事件
+		if ("LOCATION".equals(event)) {
+			InLocationEvent e = new InLocationEvent(toUserName, fromUserName, createTime, msgType);
+			e.setEvent(event);
+			e.setLatitude(root.elementText("Latitude"));
+			e.setLongitude(root.elementText("Longitude"));
+			e.setPrecision(root.elementText("Precision"));
+			return e;
+		}
+		
+		// 自定义菜单事件之一			1:点击菜单拉取消息时的事件推送
+		if ("CLICK".equals(event)) {
+			InMenuEvent e = new InMenuEvent(toUserName, fromUserName, createTime, msgType);
+			e.setEvent(event);
+			e.setEventKey(eventKey);
+			return e;
+		}
+		// 自定义菜单事件之二			2:点击菜单跳转链接时的事件推送
+		if ("VIEW".equals(event)) {
+			InMenuEvent e = new InMenuEvent(toUserName, fromUserName, createTime, msgType);
+			e.setEvent(event);
+			e.setEventKey(eventKey);
+			return e;
+		}
+		
+		throw new RuntimeException("无法识别的事件类型,请查阅微信公众平台开发文档");
+	}
+	
+	@SuppressWarnings("unused")
+	public static void main(String[] args) throws DocumentException {
+		String xml = 
+			"<xml>\n" +
+				"<ToUserName><![CDATA[James]]></ToUserName>\n" +
+				"<FromUserName><![CDATA[JFinal]]></FromUserName>\n" +
+				"<CreateTime>1348831860</CreateTime>\n" +
+				"<MsgType><![CDATA[text]]></MsgType>\n" +
+					"<Content><![CDATA[this is a test]]></Content>\n" +
+					"<MsgId>1234567890123456</MsgId>\n" +
+			"</xml>";
+		
+//		InTextMsg msg = (InTextMsg)parse(xml);
+//		System.out.println(msg.getToUserName());
+//		System.out.println(msg.getFromUserName());
+//		System.out.println(msg.getContent());
+		
+		
+		String xml_2 = 
+				"<xml>\n" +
+					"<ToUserName><![CDATA[James]]></ToUserName>\n" +
+					"<FromUserName><![CDATA[JFinal]]></FromUserName>\n" +
+					"<CreateTime>1348831860</CreateTime>\n" +
+					"<MsgType><![CDATA[text]]></MsgType>\n" +
+						"<Content><![CDATA[this is a test]]></Content>\n" +
+						"<MsgId>1234567890123456</MsgId>\n" +
+				"</xml>";
+		
+		Document doc = DocumentHelper.parseText(xml_2);
+        Element root = doc.getRootElement();
+        String value = root.elementText("abc");
+        System.out.println(value);
+	}
+}
+
+

+ 124 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/OutMsgXmlBuilder.java

@@ -0,0 +1,124 @@
+package com.mrxu.framework.common.weixin.msg;
+
+
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import com.mrxu.framework.common.weixin.msg.out.OutImageMsg;
+import com.mrxu.framework.common.weixin.msg.out.OutMsg;
+import com.mrxu.framework.common.weixin.msg.out.OutMusicMsg;
+import com.mrxu.framework.common.weixin.msg.out.OutNewsMsg;
+import com.mrxu.framework.common.weixin.msg.out.OutTextMsg;
+import com.mrxu.framework.common.weixin.msg.out.OutVideoMsg;
+import com.mrxu.framework.common.weixin.msg.out.OutVoiceMsg;
+
+import freemarker.cache.StringTemplateLoader;
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateExceptionHandler;
+
+/**
+ * 利用 FreeMarker 动态生成 OutMsg xml 内容 
+ */
+public class OutMsgXmlBuilder {
+	
+	private static String encoding = "utf-8";
+	private static Configuration config = initFreeMarkerConfiguration();
+	
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	public static String build(OutMsg outMsg) {
+		if (outMsg == null)
+			throw new IllegalArgumentException("参数 OutMsg 不能为 null");
+		
+		Map root = new HashMap();
+		// 供 OutMsg 里的 TEMPLATE 使用
+		root.put("__msg", outMsg);
+		
+		try {
+			Template template = config.getTemplate(outMsg.getClass().getSimpleName(), encoding);
+			StringWriter sw = new StringWriter();
+			template.process(root, sw);
+			return sw.toString();
+		} catch (freemarker.core.InvalidReferenceException e) {
+			throw new RuntimeException("可能是 " + outMsg.getClass().getSimpleName()+  " 对象中的某些属性未赋值,请仔细检查", e);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	private static Configuration initFreeMarkerConfiguration() {
+		Configuration config = new Configuration();
+		StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
+		initStringTemplateLoader(stringTemplateLoader);
+		config.setTemplateLoader(stringTemplateLoader);
+		
+		// 模板缓存更新时间,对于OutMsg xml 在类文件中的模板来说已有热加载保障了更新
+        config.setTemplateUpdateDelay(999999);
+        // - Set an error handler that prints errors so they are readable with
+        //   a HTML browser.
+        // config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER);
+        config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+        
+        // - Use beans wrapper (recommmended for most applications)
+        config.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
+        // - Set the default charset of the template files
+        config.setDefaultEncoding(encoding);		// config.setDefaultEncoding("ISO-8859-1");
+        // - Set the charset of the output. This is actually just a hint, that
+        //   templates may require for URL encoding and for generating META element
+        //   that uses http-equiv="Content-type".
+        config.setOutputEncoding(encoding);			// config.setOutputEncoding("UTF-8");
+        // - Set the default locale
+        config.setLocale(Locale.getDefault() /* Locale.CHINA */ );		// config.setLocale(Locale.US);
+        config.setLocalizedLookup(false);
+        
+        // 去掉int型输出时的逗号, 例如: 123,456
+        // config.setNumberFormat("#");		// config.setNumberFormat("0"); 也可以
+        config.setNumberFormat("#0.#####");
+        config.setDateFormat("yyyy-MM-dd");
+        config.setTimeFormat("HH:mm:ss");
+        config.setDateTimeFormat("yyyy-MM-dd HH:mm:ss");
+		return config;
+	}
+	
+	private static void initStringTemplateLoader(StringTemplateLoader loader) {
+		// text 文本消息
+		loader.putTemplate(OutTextMsg.class.getSimpleName(), OutTextMsg.TEMPLATE);
+		// news 图文消息
+		loader.putTemplate(OutNewsMsg.class.getSimpleName(), OutNewsMsg.TEMPLATE);
+		// image 图片消息
+		loader.putTemplate(OutImageMsg.class.getSimpleName(), OutImageMsg.TEMPLATE);
+		//voice 语音消息
+		loader.putTemplate(OutVoiceMsg.class.getSimpleName(), OutVoiceMsg.TEMPLATE);
+		// video 视频消息
+		loader.putTemplate(OutVideoMsg.class.getSimpleName(), OutVideoMsg.TEMPLATE);
+		// music 音乐消息
+		loader.putTemplate(OutMusicMsg.class.getSimpleName(), OutMusicMsg.TEMPLATE);
+	}
+	
+	public static void setEncoding(String encoding) {
+		OutMsgXmlBuilder.encoding = encoding;
+	}
+	
+	public static String getEncoding() {
+		return encoding;
+	}
+	
+	public static void main(String[] args) {
+		OutTextMsg msg = new OutTextMsg();
+		msg.setToUserName("to james");
+		msg.setFromUserName("from james");
+		msg.setCreateTime(msg.now());
+		msg.setContent("jfinal weixin 极速开发平台碉堡了");
+		String xml = OutMsgXmlBuilder.build(msg);
+		System.out.println(xml);
+	}
+}
+
+
+
+
+
+

+ 54 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InImageMsg.java

@@ -0,0 +1,54 @@
+package com.mrxu.framework.common.weixin.msg.in;
+
+
+/**
+	接收图片消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>1348831860</CreateTime>
+		<MsgType><![CDATA[image]]></MsgType>
+			<PicUrl><![CDATA[this is a url]]></PicUrl>
+			<MediaId><![CDATA[media_id]]></MediaId>
+			<MsgId>1234567890123456</MsgId>
+	</xml>
+*/
+public class InImageMsg extends InMsg {
+	
+	private String picUrl;
+	private String mediaId;
+	private String msgId;
+	
+	public InImageMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getPicUrl() {
+		return picUrl;
+	}
+	
+	public void setPicUrl(String picUrl) {
+		this.picUrl = picUrl;
+	}
+	
+	public String getMediaId() {
+		return mediaId;
+	}
+	
+	public void setMediaId(String mediaId) {
+		this.mediaId = mediaId;
+	}
+	
+	public String getMsgId() {
+		return msgId;
+	}
+	
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+}
+
+
+
+
+

+ 62 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InLinkMsg.java

@@ -0,0 +1,62 @@
+package com.mrxu.framework.common.weixin.msg.in;
+
+
+/**
+	接收链接消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>1351776360</CreateTime>
+		<MsgType><![CDATA[link]]></MsgType>
+			<Title><![CDATA[公众平台官网链接]]></Title>
+			<Description><![CDATA[公众平台官网链接]]></Description>
+			<Url><![CDATA[url]]></Url>
+			<MsgId>1234567890123456</MsgId>
+	</xml>
+*/
+public class InLinkMsg extends InMsg {
+	
+	private String title;
+	private String description;
+	private String url;
+	private String msgId;
+	
+	public InLinkMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getTitle() {
+		return title;
+	}
+	
+	public void setTitle(String title) {
+		this.title = title;
+	}
+	
+	public String getDescription() {
+		return description;
+	}
+	
+	public void setDescription(String description) {
+		this.description = description;
+	}
+	
+	public String getUrl() {
+		return url;
+	}
+	
+	public void setUrl(String url) {
+		this.url = url;
+	}
+	
+	public String getMsgId() {
+		return msgId;
+	}
+	
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+}
+
+
+

+ 72 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InLocationMsg.java

@@ -0,0 +1,72 @@
+package com.mrxu.framework.common.weixin.msg.in;
+
+
+/**
+	接收地理位置消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>1351776360</CreateTime>
+		<MsgType><![CDATA[location]]></MsgType>
+			<Location_X>23.134521</Location_X>
+			<Location_Y>113.358803</Location_Y>
+			<Scale>20</Scale>
+			<Label><![CDATA[位置信息]]></Label>
+			<MsgId>1234567890123456</MsgId>
+	</xml>
+*/
+public class InLocationMsg extends InMsg {
+	private String location_X;
+	private String location_Y;
+	private String scale;
+	private String label;
+	private String msgId;
+	
+	public InLocationMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getLocation_X() {
+		return location_X;
+	}
+	
+	public void setLocation_X(String location_X) {
+		this.location_X = location_X;
+	}
+	
+	public String getLocation_Y() {
+		return location_Y;
+	}
+	
+	public void setLocation_Y(String location_Y) {
+		this.location_Y = location_Y;
+	}
+	
+	public String getScale() {
+		return scale;
+	}
+	
+	public void setScale(String scale) {
+		this.scale = scale;
+	}
+	
+	public String getLabel() {
+		return label;
+	}
+	
+	public void setLabel(String label) {
+		this.label = label;
+	}
+	
+	public String getMsgId() {
+		return msgId;
+	}
+	
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+}
+
+
+
+

+ 84 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InMsg.java

@@ -0,0 +1,84 @@
+package com.mrxu.framework.common.weixin.msg.in;
+
+
+/**
+	接收消息,以下是接收文本消息的例子
+	接收文本消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName> 
+		<CreateTime>1348831860</CreateTime>
+		<MsgType><![CDATA[text]]></MsgType>
+			<Content><![CDATA[this is a test]]></Content>
+			<MsgId>1234567890123456</MsgId>
+	</xml>
+ */
+public abstract class InMsg {
+	
+	// 开发者微信号
+	protected String toUserName;
+	
+	// 发送方帐号(一个OpenID)
+	protected String fromUserName;
+	
+	// 消息创建时间 (整型)
+	protected Integer createTime;
+	
+	/**
+	 * 消息类型
+	 * 1:text 文本消息
+	 * 2:image 图片消息
+	 * 3:voice 语音消息
+	 * 4:video 视频消息
+	 * 5:location 地址位置消息
+	 * 6:link 链接消息
+	 * 7:event 事件
+	 */
+	protected String msgType;
+	
+	public InMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		this.toUserName = toUserName;
+		this.fromUserName = fromUserName;
+		this.createTime = createTime;
+		this.msgType = msgType;
+	}
+	
+	public String getToUserName() {
+		return toUserName;
+	}
+	
+	public void setToUserName(String toUserName) {
+		this.toUserName = toUserName;
+	}
+	
+	public String getFromUserName() {
+		return fromUserName;
+	}
+	
+	public void setFromUserName(String fromUserName) {
+		this.fromUserName = fromUserName;
+	}
+	
+	public Integer getCreateTime() {
+		return createTime;
+	}
+	
+	public void setCreateTime(Integer createTime) {
+		this.createTime = createTime;
+	}
+	
+	public String getMsgType() {
+		return msgType;
+	}
+	
+	public void setMsgType(String msgType) {
+		this.msgType = msgType;
+	}
+}
+
+
+
+
+
+
+

+ 44 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InTextMsg.java

@@ -0,0 +1,44 @@
+package com.mrxu.framework.common.weixin.msg.in;
+
+
+/**
+	接收文本消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName> 
+		<CreateTime>1348831860</CreateTime>
+		<MsgType><![CDATA[text]]></MsgType>
+			<Content><![CDATA[this is a test]]></Content>
+			<MsgId>1234567890123456</MsgId>
+	</xml>
+ */
+public class InTextMsg extends InMsg {
+	
+	private String content;
+	private String msgId;
+	
+	public InTextMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getContent() {
+		return content;
+	}
+	
+	public void setContent(String content) {
+		this.content = content;
+	}
+	
+	public String getMsgId() {
+		return msgId;
+	}
+	
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+	
+}
+
+
+
+

+ 52 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InVideoMsg.java

@@ -0,0 +1,52 @@
+package com.mrxu.framework.common.weixin.msg.in;
+
+
+/**
+	接收视频消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>1357290913</CreateTime>
+		<MsgType><![CDATA[video]]></MsgType>
+			<MediaId><![CDATA[media_id]]></MediaId>
+			<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>
+			<MsgId>1234567890123456</MsgId>
+	</xml>
+*/
+public class InVideoMsg extends InMsg {
+	
+	private String mediaId;
+	private String thumbMediaId;
+	private String msgId;
+	
+	public InVideoMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getMediaId() {
+		return mediaId;
+	}
+	
+	public void setMediaId(String mediaId) {
+		this.mediaId = mediaId;
+	}
+	
+	public String getThumbMediaId() {
+		return thumbMediaId;
+	}
+	
+	public void setThumbMediaId(String thumbMediaId) {
+		this.thumbMediaId = thumbMediaId;
+	}
+	
+	public String getMsgId() {
+		return msgId;
+	}
+	
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+}
+
+
+

+ 54 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/InVoiceMsg.java

@@ -0,0 +1,54 @@
+package com.mrxu.framework.common.weixin.msg.in;
+
+
+/**
+	接收语音消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>1357290913</CreateTime>
+		<MsgType><![CDATA[voice]]></MsgType>
+			<MediaId><![CDATA[media_id]]></MediaId>
+			<Format><![CDATA[Format]]></Format>
+			<MsgId>1234567890123456</MsgId>
+	</xml>
+*/
+public class InVoiceMsg extends InMsg {
+	
+	private String mediaId;
+	private String format;
+	private String msgId;
+	
+	public InVoiceMsg(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getMediaId() {
+		return mediaId;
+	}
+	
+	public void setMediaId(String mediaId) {
+		this.mediaId = mediaId;
+	}
+	
+	public String getFormat() {
+		return format;
+	}
+	
+	public void setFormat(String format) {
+		this.format = format;
+	}
+	
+	public String getMsgId() {
+		return msgId;
+	}
+	
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+}
+
+
+
+
+

+ 62 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/event/InFollowEvent.java

@@ -0,0 +1,62 @@
+package com.mrxu.framework.common.weixin.msg.in.event;
+
+
+
+
+/**
+	接收 关注/取消关注事件
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[FromUser]]></FromUserName>
+		<CreateTime>123456789</CreateTime>
+		<MsgType><![CDATA[event]]></MsgType>
+			<Event><![CDATA[subscribe]]></Event>
+	</xml>
+*/
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	关注实测数据结果: 比官方文档多出一个 EventKey 标记
+	<xml>
+		<ToUserName><![CDATA[gh_e21b62f685f4]]></ToUserName>
+		<FromUserName><![CDATA[o5Ljujn78A_S0uk_WvAM_fKFEXm4]]></FromUserName>
+		<CreateTime>1411785252</CreateTime>
+		<MsgType><![CDATA[event]]></MsgType>
+			<Event><![CDATA[subscribe]]></Event>
+			<EventKey><![CDATA[]]></EventKey>
+	</xml>
+	取消关注实测数据结果:比官方文档多出一个 EventKey 标记
+	<xml>
+		<ToUserName><![CDATA[gh_e21b62f685f4]]></ToUserName>
+		<FromUserName><![CDATA[o5Ljujn78A_S0uk_WvAM_fKFEXm4]]></FromUserName>
+		<CreateTime>1411785559</CreateTime>
+		<MsgType><![CDATA[event]]></MsgType>
+			<Event><![CDATA[unsubscribe]]></Event>
+			<EventKey><![CDATA[]]></EventKey>
+	</xml>
+*/
+public class InFollowEvent extends InMsg {
+	
+	// 订阅:subscribe
+	// 取消订阅:unsubscribe
+	private String event;
+	
+	public InFollowEvent(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getEvent() {
+		return event;
+	}
+	
+	public void setEvent(String event) {
+		this.event = event;
+	}
+}
+
+
+
+
+
+

+ 65 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/event/InLocationEvent.java

@@ -0,0 +1,65 @@
+package com.mrxu.framework.common.weixin.msg.in.event;
+
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	上报地理位置事件
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>123456789</CreateTime>
+		<MsgType><![CDATA[event]]></MsgType>
+			<Event><![CDATA[LOCATION]]></Event>
+			<Latitude>23.137466</Latitude>
+			<Longitude>113.352425</Longitude>
+			<Precision>119.385040</Precision>
+	</xml>
+ */
+public class InLocationEvent extends InMsg {
+	
+	private String event;
+	private String latitude;
+	private String longitude;
+	private String precision;
+	
+	public InLocationEvent(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getEvent() {
+		return event;
+	}
+	
+	public void setEvent(String event) {
+		this.event = event;
+	}
+	
+	public String getLatitude() {
+		return latitude;
+	}
+	
+	public void setLatitude(String latitude) {
+		this.latitude = latitude;
+	}
+	
+	public String getLongitude() {
+		return longitude;
+	}
+	
+	public void setLongitude(String longitude) {
+		this.longitude = longitude;
+	}
+	
+	public String getPrecision() {
+		return precision;
+	}
+	
+	public void setPrecision(String precision) {
+		this.precision = precision;
+	}
+}
+
+
+
+

+ 54 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/event/InMenuEvent.java

@@ -0,0 +1,54 @@
+package com.mrxu.framework.common.weixin.msg.in.event;
+
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	自定义菜单事件
+	1: 点击菜单拉取消息时的事件推送
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[FromUser]]></FromUserName>
+		<CreateTime>123456789</CreateTime>
+		<MsgType><![CDATA[event]]></MsgType>
+			<Event><![CDATA[CLICK]]></Event>
+			<EventKey><![CDATA[EVENTKEY]]></EventKey>
+	</xml>
+	
+	2: 点击菜单跳转链接时的事件推送
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[FromUser]]></FromUserName>
+		<CreateTime>123456789</CreateTime>
+		<MsgType><![CDATA[event]]></MsgType>
+			<Event><![CDATA[VIEW]]></Event>
+			<EventKey><![CDATA[www.qq.com]]></EventKey>
+	</xml>
+ */
+public class InMenuEvent extends InMsg {
+	private String event;
+	private String eventKey;
+	
+	public InMenuEvent(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getEvent() {
+		return event;
+	}
+	
+	public void setEvent(String event) {
+		this.event = event;
+	}
+	
+	public String getEventKey() {
+		return eventKey;
+	}
+	
+	public void setEventKey(String eventKey) {
+		this.eventKey = eventKey;
+	}
+}
+
+
+

+ 72 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/event/InQrCodeEvent.java

@@ -0,0 +1,72 @@
+package com.mrxu.framework.common.weixin.msg.in.event;
+
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	扫描带参数二维码事件
+	1. 用户未关注时,进行关注后的事件推送
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[FromUser]]></FromUserName>
+		<CreateTime>123456789</CreateTime>
+		<MsgType><![CDATA[event]]></MsgType>
+			<Event><![CDATA[subscribe]]></Event>
+			<EventKey><![CDATA[qrscene_123123]]></EventKey>
+			<Ticket><![CDATA[TICKET]]></Ticket>
+	</xml>
+	
+	2. 用户已关注时的事件推送
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[FromUser]]></FromUserName>
+		<CreateTime>123456789</CreateTime>
+		<MsgType><![CDATA[event]]></MsgType>
+			<Event><![CDATA[SCAN]]></Event>
+			<EventKey><![CDATA[SCENE_VALUE]]></EventKey>
+			<Ticket><![CDATA[TICKET]]></Ticket>
+	</xml>
+ */
+public class InQrCodeEvent extends InMsg {
+	
+	// 1. 用户未关注时,进行关注后的事件推送: subscribe
+	// 2. 用户已关注时的事件推送: SCAN
+	private String event;
+	
+	// 1. 用户未关注时,进行关注后的事件推送: qrscene_123123
+	// 2. 用户已关注时的事件推送: SCENE_VALUE
+	private String eventKey;
+	private String ticket;
+	
+	public InQrCodeEvent(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getEvent() {
+		return event;
+	}
+	
+	public void setEvent(String event) {
+		this.event = event;
+	}
+	
+	public String getEventKey() {
+		return eventKey;
+	}
+	
+	public void setEventKey(String eventKey) {
+		this.eventKey = eventKey;
+	}
+	
+	public String getTicket() {
+		return ticket;
+	}
+	
+	public void setTicket(String ticket) {
+		this.ticket = ticket;
+	}
+}
+
+
+
+

+ 62 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/in/speech_recognition/InSpeechRecognitionResults.java

@@ -0,0 +1,62 @@
+package com.mrxu.framework.common.weixin.msg.in.speech_recognition;
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	接收语音识别结果,与 InVoiceMsg 唯一的不同是多了一个 Recognition 标记
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>1357290913</CreateTime>
+		<MsgType><![CDATA[voice]]></MsgType>
+			<MediaId><![CDATA[media_id]]></MediaId>
+			<Format><![CDATA[Format]]></Format>
+			<Recognition><![CDATA[腾讯微信团队]]></Recognition>
+			<MsgId>1234567890123456</MsgId>
+	</xml>
+ */
+public class InSpeechRecognitionResults extends InMsg {
+	
+	private String mediaId;
+	private String format;
+	private String recognition;
+	private String msgId;
+	
+	public InSpeechRecognitionResults(String toUserName, String fromUserName, Integer createTime, String msgType) {
+		super(toUserName, fromUserName, createTime, msgType);
+	}
+	
+	public String getMediaId() {
+		return mediaId;
+	}
+	
+	public void setMediaId(String mediaId) {
+		this.mediaId = mediaId;
+	}
+	
+	public String getFormat() {
+		return format;
+	}
+	
+	public void setFormat(String format) {
+		this.format = format;
+	}
+	
+	public String getRecognition() {
+		return recognition;
+	}
+	
+	public void setRecognition(String recognition) {
+		this.recognition = recognition;
+	}
+	
+	public String getMsgId() {
+		return msgId;
+	}
+	
+	public void setMsgId(String msgId) {
+		this.msgId = msgId;
+	}
+}
+
+

+ 79 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/News.java

@@ -0,0 +1,79 @@
+package com.mrxu.framework.common.weixin.msg.out;
+
+
+/**
+	回复图文消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>12345678</CreateTime>
+		<MsgType><![CDATA[news]]></MsgType>
+			<ArticleCount>2</ArticleCount>
+			<Articles>
+				<item>
+					<Title><![CDATA[title1]]></Title> 
+					<Description><![CDATA[description1]]></Description>
+					<PicUrl><![CDATA[picurl]]></PicUrl>
+					<Url><![CDATA[url]]></Url>
+				</item>
+				
+				<item>
+					<Title><![CDATA[title]]></Title>
+					<Description><![CDATA[description]]></Description>
+					<PicUrl><![CDATA[picurl]]></PicUrl>
+					<Url><![CDATA[url]]></Url>
+				</item>
+			</Articles>
+	</xml>
+*/
+
+public class News {
+	
+	private String title;		// 不是必须
+	private String description;	// 不是必须
+	private String picUrl;		// 不是必须
+	private String url;			// 不是必须
+	
+	public News(String title, String description, String picUrl, String url) {
+		this.title = title;
+		this.description = description;
+		this.picUrl = picUrl;
+		this.url = url;
+	}
+	
+	public News() {
+		
+	}
+	
+	public String getTitle() {
+		return title;
+	}
+	
+	public void setTitle(String title) {
+		this.title = title;
+	}
+	
+	public String getDescription() {
+		return description;
+	}
+	
+	public void setDescription(String description) {
+		this.description = description;
+	}
+	
+	public String getPicUrl() {
+		return picUrl;
+	}
+	
+	public void setPicUrl(String picUrl) {
+		this.picUrl = picUrl;
+	}
+	
+	public String getUrl() {
+		return url;
+	}
+	
+	public void setUrl(String url) {
+		this.url = url;
+	}
+}

+ 50 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutImageMsg.java

@@ -0,0 +1,50 @@
+package com.mrxu.framework.common.weixin.msg.out;
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	回复图片消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>12345678</CreateTime>
+		<MsgType><![CDATA[image]]></MsgType>
+			<Image>
+				<MediaId><![CDATA[media_id]]></MediaId>
+			</Image>
+	</xml>
+ */
+public class OutImageMsg extends OutMsg {
+	public static final String TEMPLATE = 
+			"<xml>\n" +
+			"<ToUserName><![CDATA[${__msg.toUserName}]]></ToUserName>\n" +
+			"<FromUserName><![CDATA[${__msg.fromUserName}]]></FromUserName>\n" +
+			"<CreateTime>${__msg.createTime}</CreateTime>\n" +
+			"<MsgType><![CDATA[${__msg.msgType}]]></MsgType>\n" +
+				"<Image>\n" +
+					"<MediaId><![CDATA[${__msg.mediaId}]]></MediaId>\n" +
+				"</Image>\n" +
+			"</xml>";
+	
+	private String mediaId;
+	
+	public OutImageMsg() {
+		this.msgType = "image";
+	}
+	
+	public OutImageMsg(InMsg inMsg) {
+		super(inMsg);
+		this.msgType = "image";
+	}
+	
+	public String getMediaId() {
+		return mediaId;
+	}
+	
+	public void setMediaId(String mediaId) {
+		this.mediaId = mediaId;
+	}
+}
+
+
+

+ 87 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutMsg.java

@@ -0,0 +1,87 @@
+package com.mrxu.framework.common.weixin.msg.out;
+
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	回复文本消息
+	<xml>
+	<ToUserName><![CDATA[toUser]]></ToUserName>
+	<FromUserName><![CDATA[fromUser]]></FromUserName>
+	<CreateTime>12345678</CreateTime>
+	<MsgType><![CDATA[text]]></MsgType>
+	<Content><![CDATA[你好]]></Content>
+	</xml>
+
+ */
+public abstract class OutMsg {
+	
+	// 接收方帐号(收到的OpenID)
+	protected String toUserName;
+	
+	// 开发者微信号
+	protected String fromUserName;
+	
+	// 消息创建时间 (整型)
+	protected Integer createTime;
+	
+	/**
+	 * 被动响应消息类型
+	 * 1:text 文本消息
+	 * 2:image 图片消息
+	 * 3:voice 语音消息
+	 * 4:video 视频消息
+	 * 5:music 音乐消息
+	 * 6:news 图文消息
+	 */
+	protected String msgType;
+	
+	/**
+	 * 用接收到的消息初始化要发出去的消息,关键在于两者 toUserName 与 fromUserName 相反
+	 */
+	public OutMsg(InMsg inMsg) {
+		this.toUserName = inMsg.getFromUserName();
+		this.fromUserName = inMsg.getToUserName();
+		this.createTime = now();
+	}
+	
+	public OutMsg() {
+		
+	}
+	
+	public Integer now() {
+		return (int)(System.currentTimeMillis() / 1000);
+	}
+	
+	public String getToUserName() {
+		return toUserName;
+	}
+	
+	public void setToUserName(String toUserName) {
+		this.toUserName = toUserName;
+	}
+	
+	public String getFromUserName() {
+		return fromUserName;
+	}
+	
+	public void setFromUserName(String fromUserName) {
+		this.fromUserName = fromUserName;
+	}
+	
+	public Integer getCreateTime() {
+		return createTime;
+	}
+	
+	public void setCreateTime(Integer createTime) {
+		this.createTime = createTime;
+	}
+	
+	public String getMsgType() {
+		return msgType;
+	}
+	
+	public void setMsgType(String msgType) {
+		this.msgType = msgType;
+	}
+}

+ 111 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutMusicMsg.java

@@ -0,0 +1,111 @@
+package com.mrxu.framework.common.weixin.msg.out;
+
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	回复音乐消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>12345678</CreateTime>
+		<MsgType><![CDATA[music]]></MsgType>
+			<Music>
+				<Title><![CDATA[TITLE]]></Title>
+				<Description><![CDATA[DESCRIPTION]]></Description>
+				<MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
+				<HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
+				<ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
+			</Music>
+	</xml>
+*/
+public class OutMusicMsg extends OutMsg {
+	
+	public static final String TEMPLATE =
+		"<xml>\n" +
+			"<ToUserName><![CDATA[${__msg.toUserName}]]></ToUserName>\n" +
+			"<FromUserName><![CDATA[${__msg.fromUserName}]]></FromUserName>\n" +
+			"<CreateTime>${__msg.createTime}</CreateTime>\n" +
+			"<MsgType><![CDATA[${__msg.msgType}]]></MsgType>\n" +
+				"<Music>\n" +
+					"<Title><![CDATA[${(__msg.title)!}]]></Title>\n" +
+					"<Description><![CDATA[${(__msg.description)!}]]></Description>\n" +
+					"<MusicUrl><![CDATA[${(__msg.musicUrl)!}]]></MusicUrl>\n" +
+					"<HQMusicUrl><![CDATA[${(__msg.hqMusicUrl)!}]]></HQMusicUrl>\n" +
+					// 官司方文档错误,无此标记: "<ThumbMediaId><![CDATA[${__msg.thumbMediaId}]]></ThumbMediaId>\n" +
+					"<FuncFlag>${__msg.funcFlag}</FuncFlag>\n" +
+				"</Music>\n" +
+		"</xml>";
+			
+	private String title;		// 不是必须
+	private String description;	// 不是必须
+	private String musicUrl;	// 不是必须
+	private String hqMusicUrl;	// 不是必须
+	// private String thumbMediaId;	// 官方文档有误,无此属性
+	private String funcFlag = "0";
+	
+	public OutMusicMsg() {
+		this.msgType = "music";
+	}
+	
+	public OutMusicMsg(InMsg inMsg) {
+		super(inMsg);
+		this.msgType = "music";
+	}
+	
+	public String getTitle() {
+		return title;
+	}
+	
+	public void setTitle(String title) {
+		this.title = title;
+	}
+	
+	public String getDescription() {
+		return description;
+	}
+	
+	public void setDescription(String description) {
+		this.description = description;
+	}
+	
+	public String getMusicUrl() {
+		return musicUrl;
+	}
+	
+	public void setMusicUrl(String musicUrl) {
+		this.musicUrl = musicUrl;
+	}
+	
+	public String getHqMusicUrl() {
+		return hqMusicUrl;
+	}
+	
+	public void setHqMusicUrl(String hqMusicUrl) {
+		this.hqMusicUrl = hqMusicUrl;
+	}
+	
+	public String getFuncFlag() {
+		return funcFlag;
+	}
+	
+	// 设置为星标
+	public void setFuncFlag(boolean funcFlag) {
+		this.funcFlag = funcFlag ? "1" : "0";
+	}
+	
+	/* 官方文档有误,无此属性
+	public String getThumbMediaId() {
+		return thumbMediaId;
+	}
+	
+	public void setThumbMediaId(String thumbMediaId) {
+		this.thumbMediaId = thumbMediaId;
+	}*/
+}
+
+
+
+
+
+

+ 108 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutNewsMsg.java

@@ -0,0 +1,108 @@
+package com.mrxu.framework.common.weixin.msg.out;
+
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+	回复图文消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>12345678</CreateTime>
+		<MsgType><![CDATA[news]]></MsgType>
+			<ArticleCount>2</ArticleCount>
+			<Articles>
+				<item>
+					<Title><![CDATA[title1]]></Title> 
+					<Description><![CDATA[description1]]></Description>
+					<PicUrl><![CDATA[picurl]]></PicUrl>
+					<Url><![CDATA[url]]></Url>
+				</item>
+				
+				<item>
+					<Title><![CDATA[title]]></Title>
+					<Description><![CDATA[description]]></Description>
+					<PicUrl><![CDATA[picurl]]></PicUrl>
+					<Url><![CDATA[url]]></Url>
+				</item>
+			</Articles>
+	</xml> 
+ */
+public class OutNewsMsg extends OutMsg {
+	public static final String TEMPLATE =
+			"<xml>\n" +
+			"<ToUserName><![CDATA[${__msg.toUserName}]]></ToUserName>\n" +
+			"<FromUserName><![CDATA[${__msg.fromUserName}]]></FromUserName>\n" +
+			"<CreateTime>${__msg.createTime}</CreateTime>\n" +
+			"<MsgType><![CDATA[${__msg.msgType}]]></MsgType>\n" +
+				"<ArticleCount>${__msg.getArticleCount()}</ArticleCount>\n" +
+				"<Articles>\n" +
+					"<#list __msg.getArticles() as x>\n"+
+						"<item>\n" +
+							"<Title><![CDATA[${(x.title)!}]]></Title>\n" + 
+							"<Description><![CDATA[${(x.description)!}]]></Description>\n" +
+							"<PicUrl><![CDATA[${(x.picUrl)!}]]></PicUrl>\n" +
+							"<Url><![CDATA[${(x.url)!}]]></Url>\n" +
+						"</item>\n" +
+					"</#list>\n" +
+				"</Articles>\n" +
+			"</xml>";
+	
+	// private Integer articleCount;
+	private List<News> articles = new ArrayList<News>();
+	
+	public OutNewsMsg() {
+		this.msgType = "news";
+	}
+	
+	public OutNewsMsg(InMsg inMsg) {
+		super(inMsg);
+		this.msgType = "news";
+	}
+	
+	public Integer getArticleCount() {
+		// return articleCount;
+		return articles.size();
+	}
+	
+//	public void setArticleCount(Integer articleCount) {
+//		this.articleCount = articleCount;
+//	}
+	
+	public List<News> getArticles() {
+		return articles;
+	}
+	
+	public void setArticles(List<News> articles) {
+		if (articles != null)
+			this.articles = articles;
+	}
+	
+	public OutNewsMsg addNews(List<News> articles) {
+		if (articles != null)
+			this.articles.addAll(articles);
+		return this;
+	}
+	
+	public OutNewsMsg addNews(String title, String description, String picUrl, String url) {
+		this.articles.add(new News(title, description, picUrl, url));
+		return this;
+	}
+	
+	public OutNewsMsg addNews(News news) {
+		this.articles.add(news);
+		return this;
+	}
+}
+
+
+
+
+
+
+
+

+ 47 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutTextMsg.java

@@ -0,0 +1,47 @@
+package com.mrxu.framework.common.weixin.msg.out;
+
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	回复文本消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>12345678</CreateTime>
+		<MsgType><![CDATA[text]]></MsgType>
+			<Content><![CDATA[你好]]></Content>
+	</xml>
+ */
+public class OutTextMsg extends OutMsg {
+	public static final String TEMPLATE =
+			"<xml>\n" +
+			"<ToUserName><![CDATA[${__msg.toUserName}]]></ToUserName>\n" +
+			"<FromUserName><![CDATA[${__msg.fromUserName}]]></FromUserName>\n" +
+			"<CreateTime>${__msg.createTime}</CreateTime>\n" +
+			"<MsgType><![CDATA[${__msg.msgType}]]></MsgType>\n" +
+				"<Content><![CDATA[${__msg.content}]]></Content>\n" +
+			"</xml>";
+	
+	private String content;
+	
+	public OutTextMsg() {
+		this.msgType = "text";
+	}
+	
+	public OutTextMsg(InMsg inMsg) {
+		super(inMsg);
+		this.msgType = "text";
+	}
+	
+	public String getContent() {
+		return content;
+	}
+	
+	public OutTextMsg setContent(String content) {
+		this.content = content;
+		return this;
+	}
+}
+
+

+ 76 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutVideoMsg.java

@@ -0,0 +1,76 @@
+package com.mrxu.framework.common.weixin.msg.out;
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	回复视频消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>12345678</CreateTime>
+		<MsgType><![CDATA[video]]></MsgType>
+			<Video>
+				<MediaId><![CDATA[media_id]]></MediaId>
+				<Title><![CDATA[title]]></Title>
+				<Description><![CDATA[description]]></Description>
+			</Video>
+	</xml>
+ */
+public class OutVideoMsg extends OutMsg {
+	public static final String TEMPLATE =
+			"<xml>\n" +
+			"<ToUserName><![CDATA[${__msg.toUserName}]]></ToUserName>\n" +
+			"<FromUserName><![CDATA[${__msg.fromUserName}]]></FromUserName>\n" +
+			"<CreateTime>${__msg.createTime}</CreateTime>\n" +
+			"<MsgType><![CDATA[${__msg.msgType}]]></MsgType>\n" +
+				"<Video>\n" +
+					"<MediaId><![CDATA[${__msg.mediaId}]]></MediaId>\n" +
+					"<Title><![CDATA[${(__msg.title)!}]]></Title>\n" +
+					"<Description><![CDATA[${(__msg.description)!}]]></Description>\n" +
+				"</Video>\n" +
+			"</xml>";
+	
+	private String mediaId;
+	private String title;		// 不是必须
+	private String description;	// 不是必须
+	
+	public OutVideoMsg() {
+		this.msgType = "video";
+	}
+	
+	public OutVideoMsg(InMsg inMsg) {
+		super(inMsg);
+		this.msgType = "video";
+	}
+	
+	public String getMediaId() {
+		return mediaId;
+	}
+	
+	public void setMediaId(String mediaId) {
+		this.mediaId = mediaId;
+	}
+	
+	public String getTitle() {
+		return title;
+	}
+	
+	public void setTitle(String title) {
+		this.title = title;
+	}
+	
+	public String getDescription() {
+		return description;
+	}
+	
+	public void setDescription(String description) {
+		this.description = description;
+	}
+}
+
+
+
+
+
+
+

+ 61 - 0
framework-common/src/main/java/com/mrxu/framework/common/weixin/msg/out/OutVoiceMsg.java

@@ -0,0 +1,61 @@
+package com.mrxu.framework.common.weixin.msg.out;
+
+
+import com.mrxu.framework.common.weixin.msg.in.InMsg;
+
+/**
+	回复语音消息
+	<xml>
+		<ToUserName><![CDATA[toUser]]></ToUserName>
+		<FromUserName><![CDATA[fromUser]]></FromUserName>
+		<CreateTime>12345678</CreateTime>
+		<MsgType><![CDATA[voice]]></MsgType>
+			<Voice>
+				<MediaId><![CDATA[media_id]]></MediaId>
+			</Voice>
+	</xml>
+ */
+public class OutVoiceMsg extends OutMsg {
+	public static final String TEMPLATE =
+			"<xml>\n" +
+				"<ToUserName><![CDATA[${__msg.toUserName}]]></ToUserName>\n" +
+				"<FromUserName><![CDATA[${__msg.fromUserName}]]></FromUserName>\n" +
+				"<CreateTime>${__msg.createTime}</CreateTime>\n" +
+				"<MsgType><![CDATA[${__msg.msgType}]]></MsgType>\n" +
+					"<Voice>\n" +
+						"<MediaId><![CDATA[${__msg.mediaId}]]></MediaId>\n" +
+					"</Voice>\n" +
+			"</xml>";
+			
+	private String mediaId;
+	
+	public OutVoiceMsg() {
+		this.msgType = "voice";
+	}
+	
+	public OutVoiceMsg(InMsg inMsg) {
+		super(inMsg);
+		this.msgType = "voice";
+	}
+	
+	public String getMediaId() {
+		return mediaId;
+	}
+	
+	public void setMediaId(String mediaId) {
+		this.mediaId = mediaId;
+	}
+}
+
+
+
+
+
+
+
+
+
+
+
+
+