mirror of
				https://gitee.com/binary/weixin-java-tools.git
				synced 2025-11-01 03:25:35 +08:00 
			
		
		
		
	添加客服消息接口
This commit is contained in:
		
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,5 @@ | ||||
| *.class | ||||
| test-output | ||||
|  | ||||
| # Mobile Tools for Java (J2ME) | ||||
| .mtj.tmp/ | ||||
| @ -10,3 +11,9 @@ | ||||
|  | ||||
| # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml | ||||
| hs_err_pid* | ||||
|  | ||||
| target | ||||
| .project | ||||
| .classpath | ||||
|  | ||||
| src/test/resources/test-config.xml | ||||
|  | ||||
							
								
								
									
										66
									
								
								pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | ||||
| <?xml version="1.0"?> | ||||
| <project | ||||
| 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | ||||
| 	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||||
| 	<modelVersion>4.0.0</modelVersion> | ||||
| 	<groupId>chanjarster.weixin</groupId> | ||||
| 	<artifactId>weixin-java</artifactId> | ||||
| 	<version>1.0.0-SNAPSHOT</version> | ||||
| 	<name>WeiXin Java Toolset</name> | ||||
| 	<url>https://github.com/chanjarster/weixin-java</url> | ||||
| 	<properties> | ||||
| 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
| 		<downloadJavadocs>true</downloadJavadocs> | ||||
| 		<downloadSources>true</downloadSources> | ||||
| 	</properties> | ||||
| 	<dependencies> | ||||
| 		<dependency> | ||||
| 			<groupId>junit</groupId> | ||||
| 			<artifactId>junit</artifactId> | ||||
| 			<version>4.11</version> | ||||
| 			<scope>test</scope> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.apache.httpcomponents</groupId> | ||||
| 			<artifactId>fluent-hc</artifactId> | ||||
| 			<version>4.3.5</version> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>javax.xml.bind</groupId> | ||||
| 			<artifactId>jaxb-api</artifactId> | ||||
| 			<version>2.2.7</version> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>com.sun.xml.bind</groupId> | ||||
| 			<artifactId>jaxb-impl</artifactId> | ||||
| 			<version>2.2.7</version> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.apache.oltu.oauth2</groupId> | ||||
| 			<artifactId>org.apache.oltu.oauth2.client</artifactId> | ||||
| 			<version>1.0.0</version> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>com.google.code.gson</groupId> | ||||
| 			<artifactId>gson</artifactId> | ||||
| 			<version>2.2.2</version> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.testng</groupId> | ||||
| 			<artifactId>testng</artifactId> | ||||
| 			<version>6.8.7</version> | ||||
| 			<scope>test</scope> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.mockito</groupId> | ||||
| 			<artifactId>mockito-all</artifactId> | ||||
| 			<version>1.9.5</version> | ||||
| 			<scope>test</scope> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>org.apache.commons</groupId> | ||||
| 			<artifactId>commons-lang3</artifactId> | ||||
| 			<version>3.1</version> | ||||
| 		</dependency> | ||||
| 	</dependencies> | ||||
| </project> | ||||
| @ -0,0 +1,21 @@ | ||||
| package chanjarster.weixin.exception; | ||||
|  | ||||
| import chanjarster.weixin.in.WxError; | ||||
|  | ||||
| public class WxErrorException extends Exception { | ||||
|  | ||||
|   private static final long serialVersionUID = -6357149550353160810L; | ||||
|    | ||||
|   private WxError error; | ||||
|  | ||||
|   public WxErrorException(WxError error) { | ||||
|     super(error.toString()); | ||||
|     this.error = error; | ||||
|   } | ||||
|  | ||||
|   public WxError getError() { | ||||
|     return error; | ||||
|   } | ||||
|  | ||||
|   | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/main/java/chanjarster/weixin/in/WxAccessToken.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/java/chanjarster/weixin/in/WxAccessToken.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| package chanjarster.weixin.in; | ||||
|  | ||||
| import chanjarster.weixin.util.WxGsonBuilder; | ||||
|  | ||||
| public class WxAccessToken { | ||||
|  | ||||
|   private String access_token; | ||||
|    | ||||
|   private int expires_in; | ||||
|  | ||||
|   public String getAccess_token() { | ||||
|     return access_token; | ||||
|   } | ||||
|  | ||||
|   public void setAccess_token(String access_token) { | ||||
|     this.access_token = access_token; | ||||
|   } | ||||
|  | ||||
|   public int getExpires_in() { | ||||
|     return expires_in; | ||||
|   } | ||||
|  | ||||
|   public void setExpires_in(int expires_in) { | ||||
|     this.expires_in = expires_in; | ||||
|   } | ||||
|  | ||||
|   public static WxAccessToken fromJson(String json) { | ||||
|     return WxGsonBuilder.create().fromJson(json, WxAccessToken.class); | ||||
|   } | ||||
|    | ||||
| } | ||||
							
								
								
									
										37
									
								
								src/main/java/chanjarster/weixin/in/WxError.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/main/java/chanjarster/weixin/in/WxError.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| package chanjarster.weixin.in; | ||||
|  | ||||
| import chanjarster.weixin.util.WxGsonBuilder; | ||||
|  | ||||
| public class WxError { | ||||
|  | ||||
|   private int errcode; | ||||
|    | ||||
|   private String errmsg; | ||||
|  | ||||
|   public int getErrcode() { | ||||
|     return errcode; | ||||
|   } | ||||
|  | ||||
|   public void setErrcode(int errcode) { | ||||
|     this.errcode = errcode; | ||||
|   } | ||||
|  | ||||
|   public String getErrmsg() { | ||||
|     return errmsg; | ||||
|   } | ||||
|  | ||||
|   public void setErrmsg(String errmsg) { | ||||
|     this.errmsg = errmsg; | ||||
|   } | ||||
|  | ||||
|   public static WxError fromJson(String json) { | ||||
|     return WxGsonBuilder.create().fromJson(json, WxError.class); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String toString() { | ||||
|     return "{ errcode=" + errcode + ", errmsg=" + errmsg + "}"; | ||||
|   } | ||||
|    | ||||
|    | ||||
| } | ||||
							
								
								
									
										124
									
								
								src/main/java/chanjarster/weixin/out/WxCustomMessage.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/main/java/chanjarster/weixin/out/WxCustomMessage.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,124 @@ | ||||
| package chanjarster.weixin.out; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import chanjarster.weixin.util.WxGsonBuilder; | ||||
|  | ||||
| /** | ||||
|  * 回复给用户的客服消息 | ||||
|  * @author chanjarster | ||||
|  * | ||||
|  */ | ||||
| public class WxCustomMessage { | ||||
|  | ||||
|   protected String touser; | ||||
|   protected String msgtype; | ||||
|   protected String content; | ||||
|   protected String media_id; | ||||
|   protected String thumb_media_id; | ||||
|   protected String title; | ||||
|   protected String description; | ||||
|   protected String musicurl; | ||||
|   protected String hqmusicurl; | ||||
|   protected List<WxArticle> articles = new ArrayList<WxArticle>(); | ||||
|    | ||||
|   public String getTouser() { | ||||
|     return touser; | ||||
|   } | ||||
|   public void setTouser(String touser) { | ||||
|     this.touser = touser; | ||||
|   } | ||||
|   public String getMsgtype() { | ||||
|     return msgtype; | ||||
|   } | ||||
|   public void setMsgtype(String msgtype) { | ||||
|     this.msgtype = msgtype; | ||||
|   } | ||||
|   public String getContent() { | ||||
|     return content; | ||||
|   } | ||||
|   public void setContent(String content) { | ||||
|     this.content = content; | ||||
|   } | ||||
|   public String getMedia_id() { | ||||
|     return media_id; | ||||
|   } | ||||
|   public void setMedia_id(String media_id) { | ||||
|     this.media_id = media_id; | ||||
|   } | ||||
|   public String getThumb_media_id() { | ||||
|     return thumb_media_id; | ||||
|   } | ||||
|   public void setThumb_media_id(String thumb_media_id) { | ||||
|     this.thumb_media_id = thumb_media_id; | ||||
|   } | ||||
|   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 List<WxArticle> getArticles() { | ||||
|     return articles; | ||||
|   } | ||||
|   public void setArticles(List<WxArticle> articles) { | ||||
|     this.articles = articles; | ||||
|   } | ||||
|    | ||||
|   public String toJson() { | ||||
|     return WxGsonBuilder.INSTANCE.create().toJson(this); | ||||
|   } | ||||
|    | ||||
|   public static class WxArticle { | ||||
|      | ||||
|     protected String title; | ||||
|     protected String description; | ||||
|     protected String url; | ||||
|     protected String picurl; | ||||
|      | ||||
|     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 getPicurl() { | ||||
|       return picurl; | ||||
|     } | ||||
|     public void setPicurl(String picurl) { | ||||
|       this.picurl = picurl; | ||||
|     } | ||||
|      | ||||
|   } | ||||
| } | ||||
							
								
								
									
										74
									
								
								src/main/java/chanjarster/weixin/out/WxMenu.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/main/java/chanjarster/weixin/out/WxMenu.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| package chanjarster.weixin.out; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * 公众号菜单 | ||||
|  * @author chanjarster | ||||
|  * | ||||
|  */ | ||||
| public class WxMenu { | ||||
|  | ||||
|   protected List<WxMenuButton> button = new ArrayList<WxMenuButton>(); | ||||
|  | ||||
|   public List<WxMenuButton> getButton() { | ||||
|     return button; | ||||
|   } | ||||
|  | ||||
|   public void setButton(List<WxMenuButton> button) { | ||||
|     this.button = button; | ||||
|   } | ||||
|    | ||||
|   public static class WxMenuButton { | ||||
|  | ||||
|     protected String type; | ||||
|     protected String name; | ||||
|     protected String key; | ||||
|     protected String url; | ||||
|      | ||||
|     protected List<WxMenuButton> sub_button = new ArrayList<WxMenuButton>(); | ||||
|  | ||||
|     public String getType() { | ||||
|       return type; | ||||
|     } | ||||
|  | ||||
|     public void setType(String type) { | ||||
|       this.type = type; | ||||
|     } | ||||
|  | ||||
|     public String getName() { | ||||
|       return name; | ||||
|     } | ||||
|  | ||||
|     public void setName(String name) { | ||||
|       this.name = name; | ||||
|     } | ||||
|  | ||||
|     public String getKey() { | ||||
|       return key; | ||||
|     } | ||||
|  | ||||
|     public void setKey(String key) { | ||||
|       this.key = key; | ||||
|     } | ||||
|  | ||||
|     public String getUrl() { | ||||
|       return url; | ||||
|     } | ||||
|  | ||||
|     public void setUrl(String url) { | ||||
|       this.url = url; | ||||
|     } | ||||
|  | ||||
|     public List<WxMenuButton> getSub_button() { | ||||
|       return sub_button; | ||||
|     } | ||||
|  | ||||
|     public void setSub_button(List<WxMenuButton> sub_button) { | ||||
|       this.sub_button = sub_button; | ||||
|     } | ||||
|      | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										418
									
								
								src/main/java/chanjarster/weixin/out/WxUserMessage.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										418
									
								
								src/main/java/chanjarster/weixin/out/WxUserMessage.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,418 @@ | ||||
| package chanjarster.weixin.out; | ||||
|  | ||||
| import javax.xml.bind.JAXBException; | ||||
| import javax.xml.bind.annotation.XmlAccessType; | ||||
| import javax.xml.bind.annotation.XmlAccessorType; | ||||
| import javax.xml.bind.annotation.XmlElement; | ||||
| import javax.xml.bind.annotation.XmlRegistry; | ||||
| import javax.xml.bind.annotation.XmlRootElement; | ||||
| import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; | ||||
|  | ||||
| import chanjarster.weixin.util.AdapterCDATA; | ||||
| import chanjarster.weixin.util.XmlTransformer; | ||||
|  | ||||
| /** | ||||
|  * <pre> | ||||
|  * 微信推送过来的消息,同时也是同步回复给用户的消息 | ||||
|  * 相关字段的解释看微信开发者文档: | ||||
|  * http://mp.weixin.qq.com/wiki/index.php?title=接收普通消息 | ||||
|  * http://mp.weixin.qq.com/wiki/index.php?title=接收事件推送 | ||||
|  * http://mp.weixin.qq.com/wiki/index.php?title=接收语音识别结果 | ||||
|  * </pre> | ||||
|  * @author chanjarster | ||||
|  * | ||||
|  */ | ||||
| @XmlRootElement(name = "xml") | ||||
| @XmlAccessorType(XmlAccessType.FIELD) | ||||
| public class WxUserMessage { | ||||
|  | ||||
|   /////////////////////// | ||||
|   // 以下都是微信推送过来的消息的xml的element所对应的属性 | ||||
|   /////////////////////// | ||||
|    | ||||
|   @XmlElement(name="ToUserName") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String ToUserName; | ||||
|    | ||||
|   @XmlElement(name="FromUserName") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String FromUserName; | ||||
|    | ||||
|   @XmlElement(name="CreateTime") | ||||
|   private Long CreateTime; | ||||
|    | ||||
|   @XmlElement(name="MsgType") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String MsgType; | ||||
|    | ||||
|   @XmlElement(name="Content") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Content; | ||||
|  | ||||
|   @XmlElement(name="MsgId") | ||||
|   private Long MsgId; | ||||
|    | ||||
|   @XmlElement(name="PicUrl") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String PicUrl; | ||||
|    | ||||
|   @XmlElement(name="MediaId") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String MediaId; | ||||
|    | ||||
|   @XmlElement(name="Format") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Format; | ||||
|    | ||||
|   @XmlElement(name="ThumbMediaId") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String ThumbMediaId; | ||||
|    | ||||
|   @XmlElement(name="Location_X") | ||||
|   private Double Location_X; | ||||
|    | ||||
|   @XmlElement(name="Location_Y") | ||||
|   private Double Location_Y; | ||||
|    | ||||
|   @XmlElement(name="Scale") | ||||
|   private Double Scale; | ||||
|    | ||||
|   @XmlElement(name="Label") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Label; | ||||
|    | ||||
|   @XmlElement(name="Title") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Title; | ||||
|    | ||||
|   @XmlElement(name="Description") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Description; | ||||
|    | ||||
|   @XmlElement(name="Url") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Url; | ||||
|    | ||||
|   @XmlElement(name="Event") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Event; | ||||
|    | ||||
|   @XmlElement(name="EventKey") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String EventKey; | ||||
|    | ||||
|   @XmlElement(name="Ticket") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Ticket; | ||||
|    | ||||
|   @XmlElement(name="Latitude") | ||||
|   private Double Latitude; | ||||
|    | ||||
|   @XmlElement(name="Longitude") | ||||
|   private Double Longitude; | ||||
|    | ||||
|   @XmlElement(name="Precision") | ||||
|   private Double Precision; | ||||
|    | ||||
|   @XmlElement(name="Recognition") | ||||
|   @XmlJavaTypeAdapter(AdapterCDATA.class) | ||||
|   private String Recognition; | ||||
|    | ||||
|   public String getToUserName() { | ||||
|     return ToUserName; | ||||
|   } | ||||
|   public void setToUserName(String toUserName) { | ||||
|     ToUserName = toUserName; | ||||
|   } | ||||
|    | ||||
|   public Long getCreateTime() { | ||||
|     return CreateTime; | ||||
|   } | ||||
|   public void setCreateTime(Long createTime) { | ||||
|     CreateTime = createTime; | ||||
|   } | ||||
|    | ||||
|   public String getMsgType() { | ||||
|     return MsgType; | ||||
|   } | ||||
|   public void setMsgType(String msgType) { | ||||
|     MsgType = msgType; | ||||
|   } | ||||
|    | ||||
|   public String getContent() { | ||||
|     return Content; | ||||
|   } | ||||
|   public void setContent(String content) { | ||||
|     Content = content; | ||||
|   } | ||||
|    | ||||
|   public Long getMsgId() { | ||||
|     return MsgId; | ||||
|   } | ||||
|   public void setMsgId(Long msgId) { | ||||
|     MsgId = msgId; | ||||
|   } | ||||
|    | ||||
|   public String getPicUrl() { | ||||
|     return PicUrl; | ||||
|   } | ||||
|   public void setPicUrl(String picUrl) { | ||||
|     PicUrl = picUrl; | ||||
|   } | ||||
|    | ||||
|   public String getMediaId() { | ||||
|     return MediaId; | ||||
|   } | ||||
|   public void setMediaId(String mediaId) { | ||||
|     MediaId = mediaId; | ||||
|   } | ||||
|    | ||||
|   public String getFormat() { | ||||
|     return Format; | ||||
|   } | ||||
|   public void setFormat(String format) { | ||||
|     Format = format; | ||||
|   } | ||||
|    | ||||
|   public String getThumbMediaId() { | ||||
|     return ThumbMediaId; | ||||
|   } | ||||
|   public void setThumbMediaId(String thumbMediaId) { | ||||
|     ThumbMediaId = thumbMediaId; | ||||
|   } | ||||
|    | ||||
|   public Double getLocation_X() { | ||||
|     return Location_X; | ||||
|   } | ||||
|   public void setLocation_X(Double location_X) { | ||||
|     Location_X = location_X; | ||||
|   } | ||||
|    | ||||
|   public Double getLocation_Y() { | ||||
|     return Location_Y; | ||||
|   } | ||||
|   public void setLocation_Y(Double location_Y) { | ||||
|     Location_Y = location_Y; | ||||
|   } | ||||
|    | ||||
|   public Double getScale() { | ||||
|     return Scale; | ||||
|   } | ||||
|   public void setScale(Double scale) { | ||||
|     Scale = scale; | ||||
|   } | ||||
|   | ||||
|   public String getLabel() { | ||||
|     return Label; | ||||
|   } | ||||
|   public void setLabel(String label) { | ||||
|     Label = label; | ||||
|   } | ||||
|    | ||||
|   public String getTitle() { | ||||
|     return Title; | ||||
|   } | ||||
|   public void setTitle(String title) { | ||||
|     Title = title; | ||||
|   } | ||||
|    | ||||
|   public String getDescription() { | ||||
|     return Description; | ||||
|   } | ||||
|   public void setDescription(String description) { | ||||
|     Description = description; | ||||
|   } | ||||
|    | ||||
|   public String getUrl() { | ||||
|     return Url; | ||||
|   } | ||||
|   public void setUrl(String url) { | ||||
|     Url = url; | ||||
|   } | ||||
|    | ||||
|   public String getEvent() { | ||||
|     return Event; | ||||
|   } | ||||
|   public void setEvent(String event) { | ||||
|     Event = event; | ||||
|   } | ||||
|    | ||||
|   public String getEventKey() { | ||||
|     return EventKey; | ||||
|   } | ||||
|   public void setEventKey(String eventKey) { | ||||
|     EventKey = eventKey; | ||||
|   } | ||||
|    | ||||
|   public String getTicket() { | ||||
|     return Ticket; | ||||
|   } | ||||
|   public void setTicket(String ticket) { | ||||
|     Ticket = ticket; | ||||
|   } | ||||
|    | ||||
|   public Double getLatitude() { | ||||
|     return Latitude; | ||||
|   } | ||||
|   public void setLatitude(Double latitude) { | ||||
|     Latitude = latitude; | ||||
|   } | ||||
|    | ||||
|   public Double getLongitude() { | ||||
|     return Longitude; | ||||
|   } | ||||
|   public void setLongitude(Double longitude) { | ||||
|     Longitude = longitude; | ||||
|   } | ||||
|    | ||||
|   public Double getPrecision() { | ||||
|     return Precision; | ||||
|   } | ||||
|   public void setPrecision(Double precision) { | ||||
|     Precision = precision; | ||||
|   } | ||||
|    | ||||
|   public String getRecognition() { | ||||
|     return Recognition; | ||||
|   } | ||||
|   public void setRecognition(String recognition) { | ||||
|     Recognition = recognition; | ||||
|   } | ||||
|  | ||||
|   public String getFromUserName() { | ||||
|     return FromUserName; | ||||
|   } | ||||
|   public void setFromUserName(String fromUserName) { | ||||
|     FromUserName = fromUserName; | ||||
|   } | ||||
|    | ||||
|   public String toXml() { | ||||
|     try { | ||||
|       return XmlTransformer.toXml(WxUserMessage.class, this); | ||||
|     } catch (JAXBException e) { | ||||
|       e.printStackTrace(); | ||||
|     } | ||||
|     return ""; | ||||
|   } | ||||
|    | ||||
|   public static WxUserMessage fromXml(String xml) { | ||||
|     try { | ||||
|       return XmlTransformer.fromXml(WxUserMessage.class, xml); | ||||
|     } catch (JAXBException e) { | ||||
|       e.printStackTrace(); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|   @Override | ||||
|   public int hashCode() { | ||||
|     final int prime = 31; | ||||
|     int result = 1; | ||||
|     result = prime * result + ((Content == null) ? 0 : Content.hashCode()); | ||||
|     result = prime * result + ((CreateTime == null) ? 0 : CreateTime.hashCode()); | ||||
|     result = prime * result + ((Description == null) ? 0 : Description.hashCode()); | ||||
|     result = prime * result + ((Event == null) ? 0 : Event.hashCode()); | ||||
|     result = prime * result + ((EventKey == null) ? 0 : EventKey.hashCode()); | ||||
|     result = prime * result + ((Format == null) ? 0 : Format.hashCode()); | ||||
|     result = prime * result + ((FromUserName == null) ? 0 : FromUserName.hashCode()); | ||||
|     result = prime * result + ((Label == null) ? 0 : Label.hashCode()); | ||||
|     result = prime * result + ((Latitude == null) ? 0 : Latitude.hashCode()); | ||||
|     result = prime * result + ((Location_X == null) ? 0 : Location_X.hashCode()); | ||||
|     result = prime * result + ((Location_Y == null) ? 0 : Location_Y.hashCode()); | ||||
|     result = prime * result + ((Longitude == null) ? 0 : Longitude.hashCode()); | ||||
|     result = prime * result + ((MediaId == null) ? 0 : MediaId.hashCode()); | ||||
|     result = prime * result + ((MsgId == null) ? 0 : MsgId.hashCode()); | ||||
|     result = prime * result + ((MsgType == null) ? 0 : MsgType.hashCode()); | ||||
|     result = prime * result + ((PicUrl == null) ? 0 : PicUrl.hashCode()); | ||||
|     result = prime * result + ((Precision == null) ? 0 : Precision.hashCode()); | ||||
|     result = prime * result + ((Recognition == null) ? 0 : Recognition.hashCode()); | ||||
|     result = prime * result + ((Scale == null) ? 0 : Scale.hashCode()); | ||||
|     result = prime * result + ((ThumbMediaId == null) ? 0 : ThumbMediaId.hashCode()); | ||||
|     result = prime * result + ((Ticket == null) ? 0 : Ticket.hashCode()); | ||||
|     result = prime * result + ((Title == null) ? 0 : Title.hashCode()); | ||||
|     result = prime * result + ((ToUserName == null) ? 0 : ToUserName.hashCode()); | ||||
|     result = prime * result + ((Url == null) ? 0 : Url.hashCode()); | ||||
|     return result; | ||||
|   } | ||||
|   @Override | ||||
|   public boolean equals(Object obj) { | ||||
|     if (this == obj) return true; | ||||
|     if (obj == null) return false; | ||||
|     if (getClass() != obj.getClass()) return false; | ||||
|     WxUserMessage other = (WxUserMessage) obj; | ||||
|     if (Content == null) { | ||||
|       if (other.Content != null) return false; | ||||
|     } else if (!Content.equals(other.Content)) return false; | ||||
|     if (CreateTime == null) { | ||||
|       if (other.CreateTime != null) return false; | ||||
|     } else if (!CreateTime.equals(other.CreateTime)) return false; | ||||
|     if (Description == null) { | ||||
|       if (other.Description != null) return false; | ||||
|     } else if (!Description.equals(other.Description)) return false; | ||||
|     if (Event == null) { | ||||
|       if (other.Event != null) return false; | ||||
|     } else if (!Event.equals(other.Event)) return false; | ||||
|     if (EventKey == null) { | ||||
|       if (other.EventKey != null) return false; | ||||
|     } else if (!EventKey.equals(other.EventKey)) return false; | ||||
|     if (Format == null) { | ||||
|       if (other.Format != null) return false; | ||||
|     } else if (!Format.equals(other.Format)) return false; | ||||
|     if (FromUserName == null) { | ||||
|       if (other.FromUserName != null) return false; | ||||
|     } else if (!FromUserName.equals(other.FromUserName)) return false; | ||||
|     if (Label == null) { | ||||
|       if (other.Label != null) return false; | ||||
|     } else if (!Label.equals(other.Label)) return false; | ||||
|     if (Latitude == null) { | ||||
|       if (other.Latitude != null) return false; | ||||
|     } else if (!Latitude.equals(other.Latitude)) return false; | ||||
|     if (Location_X == null) { | ||||
|       if (other.Location_X != null) return false; | ||||
|     } else if (!Location_X.equals(other.Location_X)) return false; | ||||
|     if (Location_Y == null) { | ||||
|       if (other.Location_Y != null) return false; | ||||
|     } else if (!Location_Y.equals(other.Location_Y)) return false; | ||||
|     if (Longitude == null) { | ||||
|       if (other.Longitude != null) return false; | ||||
|     } else if (!Longitude.equals(other.Longitude)) return false; | ||||
|     if (MediaId == null) { | ||||
|       if (other.MediaId != null) return false; | ||||
|     } else if (!MediaId.equals(other.MediaId)) return false; | ||||
|     if (MsgId == null) { | ||||
|       if (other.MsgId != null) return false; | ||||
|     } else if (!MsgId.equals(other.MsgId)) return false; | ||||
|     if (MsgType == null) { | ||||
|       if (other.MsgType != null) return false; | ||||
|     } else if (!MsgType.equals(other.MsgType)) return false; | ||||
|     if (PicUrl == null) { | ||||
|       if (other.PicUrl != null) return false; | ||||
|     } else if (!PicUrl.equals(other.PicUrl)) return false; | ||||
|     if (Precision == null) { | ||||
|       if (other.Precision != null) return false; | ||||
|     } else if (!Precision.equals(other.Precision)) return false; | ||||
|     if (Recognition == null) { | ||||
|       if (other.Recognition != null) return false; | ||||
|     } else if (!Recognition.equals(other.Recognition)) return false; | ||||
|     if (Scale == null) { | ||||
|       if (other.Scale != null) return false; | ||||
|     } else if (!Scale.equals(other.Scale)) return false; | ||||
|     if (ThumbMediaId == null) { | ||||
|       if (other.ThumbMediaId != null) return false; | ||||
|     } else if (!ThumbMediaId.equals(other.ThumbMediaId)) return false; | ||||
|     if (Ticket == null) { | ||||
|       if (other.Ticket != null) return false; | ||||
|     } else if (!Ticket.equals(other.Ticket)) return false; | ||||
|     if (Title == null) { | ||||
|       if (other.Title != null) return false; | ||||
|     } else if (!Title.equals(other.Title)) return false; | ||||
|     if (ToUserName == null) { | ||||
|       if (other.ToUserName != null) return false; | ||||
|     } else if (!ToUserName.equals(other.ToUserName)) return false; | ||||
|     if (Url == null) { | ||||
|       if (other.Url != null) return false; | ||||
|     } else if (!Url.equals(other.Url)) return false; | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|    | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| public interface WxConfigProvider { | ||||
|  | ||||
|   public void updateAccessToken(String accessToken, Integer expiresIn); | ||||
|    | ||||
|   public String getAccessToken(); | ||||
|    | ||||
|   public String getAppId(); | ||||
|    | ||||
|   public String getSecret(); | ||||
|    | ||||
|   public String getToken(); | ||||
|    | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| import chanjarster.weixin.out.WxUserMessage; | ||||
|  | ||||
| /** | ||||
|  * 处理微信推送消息的处理器 | ||||
|  * @author chanjarster | ||||
|  * | ||||
|  */ | ||||
| public interface WxMessageHandler { | ||||
|  | ||||
|   public void handle(WxUserMessage wxMessage, Map<String, Object> context); | ||||
|    | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| import chanjarster.weixin.out.WxUserMessage; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * 微信消息拦截器,可以用来做一些验证 | ||||
|  * @author qianjia | ||||
|  * | ||||
|  */ | ||||
| public interface WxMessageInterceptor { | ||||
|  | ||||
|   /** | ||||
|    * 拦截微信消息 | ||||
|    * @param wxMessage | ||||
|    * @return  true代表OK,false代表不OK | ||||
|    */ | ||||
|   public boolean intercept(WxUserMessage wxMessage, Map<String, Object> context); | ||||
|    | ||||
| } | ||||
							
								
								
									
										192
									
								
								src/main/java/chanjarster/weixin/service/WxMessageRouter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								src/main/java/chanjarster/weixin/service/WxMessageRouter.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import chanjarster.weixin.out.WxUserMessage; | ||||
|  | ||||
| /** | ||||
|  * 微信消息路由器,通过代码化的配置,把来自微信的消息交给某个的handler处理 | ||||
|  * @author qianjia | ||||
|  * | ||||
|  */ | ||||
| public class WxMessageRouter { | ||||
|    | ||||
|   private List<Rule> rules = new ArrayList<Rule>(); | ||||
|  | ||||
|   /** | ||||
|    * 开始一个新的Route规则 | ||||
|    * @return | ||||
|    */ | ||||
|   public Rule start() { | ||||
|     return new Rule(this); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * 处理微信消息 | ||||
|    * @param wxMessage | ||||
|    */ | ||||
|   public void route(WxUserMessage wxMessage) { | ||||
|     for (Rule rule : rules) { | ||||
|       boolean doNext = rule.service(wxMessage); | ||||
|       if (!doNext) { | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   public static class Rule { | ||||
|      | ||||
|     private final WxMessageRouter routerBuilder; | ||||
|  | ||||
|     private String msgType; | ||||
|  | ||||
|     private String event; | ||||
|      | ||||
|     private String eventKey; | ||||
|      | ||||
|     private String content; | ||||
|      | ||||
|     private boolean forward = false; | ||||
|      | ||||
|     private List<WxMessageHandler> handlers = new ArrayList<WxMessageHandler>(); | ||||
|      | ||||
|     private List<WxMessageInterceptor> interceptors = new ArrayList<WxMessageInterceptor>(); | ||||
|      | ||||
|     protected Rule(WxMessageRouter routerBuilder) { | ||||
|       this.routerBuilder = routerBuilder; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 如果msgType等于某值 | ||||
|      * @param msgType | ||||
|      * @return | ||||
|      */ | ||||
|     public Rule msgType(String msgType) { | ||||
|       this.msgType = msgType; | ||||
|       return this; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 如果event等于某值 | ||||
|      * @param event | ||||
|      * @return | ||||
|      */ | ||||
|     public Rule event(String event) { | ||||
|       this.event = event; | ||||
|       return this; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 如果eventKey等于某值 | ||||
|      * @param eventKey | ||||
|      * @return | ||||
|      */ | ||||
|     public Rule eventKey(String eventKey) { | ||||
|       this.eventKey = eventKey; | ||||
|       return this; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 如果content等于某值 | ||||
|      * @param content | ||||
|      * @return | ||||
|      */ | ||||
|     public Rule content(String content) { | ||||
|       this.content = content; | ||||
|       return this; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 如果本规则命中,在执行完handler后,还会接着给后面的Rule执行 | ||||
|      * @return | ||||
|      */ | ||||
|     public Rule forward() { | ||||
|       this.forward = true; | ||||
|       return this; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 添加interceptor | ||||
|      * @param interceptor | ||||
|      * @param otherInterceptors | ||||
|      * @return | ||||
|      */ | ||||
|     public Rule interceptor(WxMessageInterceptor interceptor, WxMessageInterceptor... otherInterceptors) { | ||||
|       this.interceptors.add(interceptor); | ||||
|       if (otherInterceptors != null && otherInterceptors.length > 0) { | ||||
|         for (WxMessageInterceptor i : otherInterceptors) { | ||||
|           this.interceptors.add(i); | ||||
|         } | ||||
|       } | ||||
|       return this; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 添加handler | ||||
|      * @param handler | ||||
|      * @return | ||||
|      */ | ||||
|     public Rule handler(WxMessageHandler handler, WxMessageHandler... otherHandlers) { | ||||
|       this.handlers.add(handler); | ||||
|       if (otherHandlers != null && otherHandlers.length > 0) { | ||||
|         for (WxMessageHandler i : otherHandlers) { | ||||
|           this.handlers.add(i); | ||||
|         } | ||||
|       } | ||||
|       return this; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 规则结束 | ||||
|      * @return | ||||
|      */ | ||||
|     public WxMessageRouter end() { | ||||
|       this.routerBuilder.rules.add(this); | ||||
|       return this.routerBuilder; | ||||
|     } | ||||
|      | ||||
|     protected boolean test(WxUserMessage wxMessage) { | ||||
|       return  | ||||
|           (this.msgType == null || this.msgType.equals(wxMessage.getMsgType())) | ||||
|           && | ||||
|           (this.event == null || this.event.equals(wxMessage.getEvent())) | ||||
|           && | ||||
|           (this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey())) | ||||
|           && | ||||
|           (this.content == null || this.content.equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim())) | ||||
|       ; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * 处理微信推送过来的消息 | ||||
|      * @param wxMessage | ||||
|      * @return true 代表继续执行别的router,false 代表停止执行别的router | ||||
|      */ | ||||
|     protected boolean service(WxUserMessage wxMessage) { | ||||
|       // 如果不匹配本规则,那么接着执行后面的Rule | ||||
|       if (!test(wxMessage)) { | ||||
|         return true; | ||||
|       } | ||||
|        | ||||
|       Map<String, Object> context = new HashMap<String, Object>(); | ||||
|       // 如果拦截器不通过 | ||||
|       for (WxMessageInterceptor interceptor : this.interceptors) { | ||||
|         if (!interceptor.intercept(wxMessage, context)) { | ||||
|           return this.forward; | ||||
|         } | ||||
|       } | ||||
|        | ||||
|       // 交给handler处理 | ||||
|       for (WxMessageHandler interceptor : this.handlers) { | ||||
|         interceptor.handle(wxMessage, context); | ||||
|       } | ||||
|        | ||||
|       return this.forward; | ||||
|     } | ||||
|      | ||||
|   } | ||||
|    | ||||
| } | ||||
							
								
								
									
										14
									
								
								src/main/java/chanjarster/weixin/service/WxMsgType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/main/java/chanjarster/weixin/service/WxMsgType.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| public class WxMsgType { | ||||
|  | ||||
|   public static final String TEXT = "text"; | ||||
|   public static final String IMAGE = "image"; | ||||
|   public static final String VOICE = "voice"; | ||||
|   public static final String MUSIC = "music"; | ||||
|   public static final String VIDEO = "video"; | ||||
|   public static final String NEWS = "news"; | ||||
|   public static final String LOCATION = "location"; | ||||
|   public static final String LINK = "link"; | ||||
|  | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/main/java/chanjarster/weixin/service/WxService.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/main/java/chanjarster/weixin/service/WxService.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| import chanjarster.weixin.exception.WxErrorException; | ||||
| import chanjarster.weixin.out.WxCustomMessage; | ||||
| import chanjarster.weixin.out.WxMenu; | ||||
|  | ||||
| /** | ||||
|  * 微信相关的常量 | ||||
|  */ | ||||
| public interface WxService { | ||||
|    | ||||
|   public void refreshAccessToken() throws WxErrorException; | ||||
|    | ||||
|   public String sendCustomMessage(WxCustomMessage message) throws WxErrorException; | ||||
|    | ||||
|   public String createMenu(WxMenu menu) throws WxErrorException; | ||||
|    | ||||
|   public String deleteMenu() throws WxErrorException; | ||||
|    | ||||
|   public WxMenu getMenu() throws WxErrorException; | ||||
|  | ||||
|   public void setWxConfigProvider(WxConfigProvider wxConfigProvider); | ||||
| } | ||||
							
								
								
									
										179
									
								
								src/main/java/chanjarster/weixin/service/WxServiceImpl.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/main/java/chanjarster/weixin/service/WxServiceImpl.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,179 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.nio.charset.Charset; | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
|  | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.apache.http.client.ClientProtocolException; | ||||
| import org.apache.http.client.methods.CloseableHttpResponse; | ||||
| import org.apache.http.client.methods.HttpGet; | ||||
| import org.apache.http.client.methods.HttpPost; | ||||
| import org.apache.http.entity.StringEntity; | ||||
| import org.apache.http.impl.client.BasicResponseHandler; | ||||
| import org.apache.http.impl.client.CloseableHttpClient; | ||||
| import org.apache.http.impl.client.HttpClients; | ||||
|  | ||||
| import chanjarster.weixin.exception.WxErrorException; | ||||
| import chanjarster.weixin.in.WxAccessToken; | ||||
| import chanjarster.weixin.in.WxError; | ||||
| import chanjarster.weixin.out.WxCustomMessage; | ||||
| import chanjarster.weixin.out.WxMenu; | ||||
|  | ||||
| public class WxServiceImpl implements WxService { | ||||
|  | ||||
|   /** | ||||
|    * 全局的是否正在刷新Access Token的flag | ||||
|    * true: 正在刷新 | ||||
|    * false: 没有刷新 | ||||
|    */ | ||||
|   protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false); | ||||
|    | ||||
|   protected static final CloseableHttpClient httpclient = HttpClients.createDefault(); | ||||
|    | ||||
|   protected static final Charset UTF8 = Charset.forName("UTF-8"); | ||||
|    | ||||
|   protected WxConfigProvider wxConfigProvider; | ||||
|    | ||||
|   /** | ||||
|    * 获得access_token | ||||
|    * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token | ||||
|    * @return | ||||
|    * @throws WxErrorException  | ||||
|    */ | ||||
|   public void refreshAccessToken() throws WxErrorException { | ||||
|     if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) { | ||||
|       try { | ||||
|         String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" | ||||
|             + "&appid=" + wxConfigProvider.getAppId()  | ||||
|             + "&secret=" + wxConfigProvider.getSecret() | ||||
|             ; | ||||
|         try { | ||||
|           HttpGet httpGet = new HttpGet(url); | ||||
|           CloseableHttpResponse response = httpclient.execute(httpGet); | ||||
|           String resultContent = new BasicResponseHandler().handleResponse(response); | ||||
|           WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | ||||
|           wxConfigProvider.updateAccessToken(accessToken.getAccess_token(), accessToken.getExpires_in()); | ||||
|         } catch (ClientProtocolException e) { | ||||
|           throw new RuntimeException(e); | ||||
|         } catch (IOException e) { | ||||
|           throw new RuntimeException(e); | ||||
|         } | ||||
|       } finally { | ||||
|         GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false); | ||||
|       } | ||||
|     } else { | ||||
|       // 每隔100ms检查一下是否刷新完毕了 | ||||
|       while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) { | ||||
|         try { | ||||
|           Thread.sleep(100); | ||||
|         } catch (InterruptedException e) { | ||||
|         } | ||||
|       } | ||||
|       // 刷新完毕了,就没他什么事儿了 | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   /** | ||||
|    * 发送客服消息 | ||||
|    * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息 | ||||
|    * @param message | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
|   public String sendCustomMessage(WxCustomMessage message) throws WxErrorException { | ||||
|     String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | ||||
|     return post(url, message.toJson()); | ||||
|   } | ||||
|    | ||||
|   protected String post(String uri, String data) throws WxErrorException { | ||||
|     return execute("POST", uri, data); | ||||
|   } | ||||
|    | ||||
|   protected String get(String uri, String data) throws WxErrorException { | ||||
|     return execute("GET", uri, data); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 | ||||
|    * @param request | ||||
|    * @return 微信服务端返回的结果 | ||||
|    * @throws WxErrorException  | ||||
|    */ | ||||
|   protected String execute(String method, String uri, String data) throws WxErrorException { | ||||
|     if (StringUtils.isBlank(wxConfigProvider.getAccessToken())) { | ||||
|       refreshAccessToken(); | ||||
|     } | ||||
|     String accessToken = wxConfigProvider.getAccessToken(); | ||||
|      | ||||
|     String uriWithAccessToken = uri; | ||||
|     uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; | ||||
|      | ||||
|     try { | ||||
|       String resultContent = null; | ||||
|       if ("POST".equals(method)) { | ||||
|         HttpPost httpPost = new HttpPost(uriWithAccessToken); | ||||
|         StringEntity entity = new StringEntity(data, UTF8); | ||||
|         httpPost.setEntity(entity); | ||||
|         CloseableHttpResponse response = httpclient.execute(httpPost); | ||||
|         resultContent = new BasicResponseHandler().handleResponse(response); | ||||
|       } else if ("GET".equals(method)) { | ||||
|         HttpGet httpGet = new HttpGet(uriWithAccessToken); | ||||
|         CloseableHttpResponse response = httpclient.execute(httpGet); | ||||
|         resultContent = new BasicResponseHandler().handleResponse(response); | ||||
|       } | ||||
|        | ||||
|       WxError error = WxError.fromJson(resultContent); | ||||
|       /* | ||||
|        * 关于微信返回错误码 详情请看 http://mp.weixin.qq.com/wiki/index.php?title=全局返回码说明 | ||||
|        * 40001 微信图片不对 | ||||
|        * 42001 access_token超时 | ||||
|        */ | ||||
|       if (error.getErrcode() == 42001 || error.getErrcode() == 40001) { | ||||
|         refreshAccessToken(); | ||||
|         return execute(method, uri, data); | ||||
|       } | ||||
|       if (error.getErrcode() != 0) { | ||||
|         throw new WxErrorException(error); | ||||
|       } | ||||
|       return resultContent; | ||||
|     } catch (ClientProtocolException e) { | ||||
|       throw new RuntimeException(e); | ||||
|     } catch (IOException e) { | ||||
|       throw new RuntimeException(e); | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   /** | ||||
|    *  | ||||
|    * @param menu | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
|   public String createMenu(WxMenu menu) throws WxErrorException { | ||||
|     // TODO | ||||
|     return null; | ||||
|   } | ||||
|    | ||||
|   /** | ||||
|    *  | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
|   public String deleteMenu() throws WxErrorException { | ||||
|  // TODO | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    *  | ||||
|    * @return | ||||
|    * @throws WxErrorException | ||||
|    */ | ||||
|   public WxMenu getMenu() throws WxErrorException { | ||||
|     // TODO | ||||
|     return null; | ||||
|   } | ||||
|    | ||||
|   public void setWxConfigProvider(WxConfigProvider wxConfigProvider) { | ||||
|     this.wxConfigProvider = wxConfigProvider; | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										24
									
								
								src/main/java/chanjarster/weixin/util/AdapterCDATA.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/main/java/chanjarster/weixin/util/AdapterCDATA.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| package chanjarster.weixin.util; | ||||
|  | ||||
| import javax.xml.bind.annotation.adapters.XmlAdapter; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * http://stackoverflow.com/questions/14193944/jaxb-marshalling-unmarshalling-with-cdata | ||||
|  *  | ||||
|  * @author chanjarster | ||||
|  * | ||||
|  */ | ||||
| public class AdapterCDATA extends XmlAdapter<String, String> { | ||||
|  | ||||
|     @Override | ||||
|     public String marshal(String arg0) throws Exception { | ||||
|         return "<![CDATA[" + arg0 + "]]>"; | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public String unmarshal(String arg0) throws Exception { | ||||
|         return arg0; | ||||
|     } | ||||
|      | ||||
| } | ||||
| @ -0,0 +1,88 @@ | ||||
| /* | ||||
|  * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | ||||
|  * | ||||
|  * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | ||||
|  * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | ||||
|  * arose from modification of the original source, or other redistribution of this source | ||||
|  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | ||||
|  */ | ||||
| package chanjarster.weixin.util; | ||||
|  | ||||
| import java.lang.reflect.Type; | ||||
|  | ||||
| import chanjarster.weixin.out.WxCustomMessage; | ||||
| import chanjarster.weixin.out.WxCustomMessage.WxArticle; | ||||
| import chanjarster.weixin.service.WxMsgType; | ||||
|  | ||||
| import com.google.gson.JsonArray; | ||||
| import com.google.gson.JsonElement; | ||||
| import com.google.gson.JsonObject; | ||||
| import com.google.gson.JsonSerializationContext; | ||||
| import com.google.gson.JsonSerializer; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * @author qianjia | ||||
|  * | ||||
|  */ | ||||
| public class WxCustomMessageGsonAdapter implements JsonSerializer<WxCustomMessage> { | ||||
|  | ||||
|   public JsonElement serialize(WxCustomMessage message, Type typeOfSrc, JsonSerializationContext context) { | ||||
|     JsonObject messageJson = new JsonObject(); | ||||
|     messageJson.addProperty("touser", message.getTouser()); | ||||
|     messageJson.addProperty("msgtype", message.getMsgtype()); | ||||
|      | ||||
|     if (WxMsgType.TEXT.equals(message.getMsgtype())) { | ||||
|       JsonObject text = new JsonObject(); | ||||
|       text.addProperty("content", message.getContent()); | ||||
|       messageJson.add("text", text); | ||||
|     } | ||||
|  | ||||
|     if (WxMsgType.IMAGE.equals(message.getMsgtype())) { | ||||
|       JsonObject image = new JsonObject(); | ||||
|       image.addProperty("media_id", message.getMedia_id()); | ||||
|       messageJson.add("image", image); | ||||
|     } | ||||
|  | ||||
|     if (WxMsgType.VOICE.equals(message.getMsgtype())) { | ||||
|       JsonObject voice = new JsonObject(); | ||||
|       voice.addProperty("media_id", message.getMedia_id()); | ||||
|       messageJson.add("voice", voice); | ||||
|     } | ||||
|  | ||||
|     if (WxMsgType.VIDEO.equals(message.getMsgtype())) { | ||||
|       JsonObject video = new JsonObject(); | ||||
|       video.addProperty("media_id", message.getMedia_id()); | ||||
|       video.addProperty("thumb_media_id", message.getThumb_media_id()); | ||||
|       video.addProperty("title", message.getTitle()); | ||||
|       video.addProperty("description", message.getDescription()); | ||||
|       messageJson.add("video", video); | ||||
|     } | ||||
|  | ||||
|     if (WxMsgType.MUSIC.equals(message.getMsgtype())) { | ||||
|       JsonObject music = new JsonObject(); | ||||
|       music.addProperty("title", message.getTitle()); | ||||
|       music.addProperty("description", message.getDescription()); | ||||
|       music.addProperty("thumb_media_id", message.getThumb_media_id()); | ||||
|       music.addProperty("musicurl", message.getMusicurl()); | ||||
|       music.addProperty("hqmusicurl", message.getHqmusicurl()); | ||||
|       messageJson.add("music", music); | ||||
|     } | ||||
|      | ||||
|     if (WxMsgType.NEWS.equals(message.getMsgtype())) { | ||||
|       JsonArray articleJsonArray = new JsonArray(); | ||||
|       for (WxArticle article : message.getArticles()) { | ||||
|         JsonObject articleJson = new JsonObject(); | ||||
|         articleJson.addProperty("title", article.getTitle()); | ||||
|         articleJson.addProperty("description", article.getDescription()); | ||||
|         articleJson.addProperty("url", article.getUrl()); | ||||
|         articleJson.addProperty("picurl", article.getPicurl()); | ||||
|         articleJsonArray.add(articleJson); | ||||
|       } | ||||
|       messageJson.add("articles", articleJsonArray); | ||||
|     } | ||||
|      | ||||
|     return messageJson; | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										21
									
								
								src/main/java/chanjarster/weixin/util/WxGsonBuilder.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/main/java/chanjarster/weixin/util/WxGsonBuilder.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package chanjarster.weixin.util; | ||||
|  | ||||
| import chanjarster.weixin.out.WxCustomMessage; | ||||
|  | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.GsonBuilder; | ||||
|  | ||||
| public class WxGsonBuilder { | ||||
|  | ||||
|   public static final GsonBuilder INSTANCE = new GsonBuilder(); | ||||
|    | ||||
|   static { | ||||
|     INSTANCE.disableHtmlEscaping(); | ||||
|     INSTANCE.registerTypeAdapter(WxCustomMessage.class, new WxCustomMessageGsonAdapter()); | ||||
|   } | ||||
|    | ||||
|   public static Gson create() { | ||||
|     return INSTANCE.create(); | ||||
|   } | ||||
|    | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/main/java/chanjarster/weixin/util/WxMenuGsonAdapter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/main/java/chanjarster/weixin/util/WxMenuGsonAdapter.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| /* | ||||
|  * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved. | ||||
|  * | ||||
|  * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended | ||||
|  * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction | ||||
|  * arose from modification of the original source, or other redistribution of this source | ||||
|  * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD. | ||||
|  */ | ||||
| package chanjarster.weixin.util; | ||||
|  | ||||
| import java.lang.reflect.Type; | ||||
|  | ||||
| import chanjarster.weixin.out.WxMenu; | ||||
| import chanjarster.weixin.out.WxMenu.WxMenuButton; | ||||
|  | ||||
| import com.google.gson.JsonArray; | ||||
| import com.google.gson.JsonElement; | ||||
| import com.google.gson.JsonObject; | ||||
| import com.google.gson.JsonSerializationContext; | ||||
| import com.google.gson.JsonSerializer; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * @author qianjia | ||||
|  * | ||||
|  */ | ||||
| public class WxMenuGsonAdapter implements JsonSerializer<WxMenu> { | ||||
|  | ||||
|   public JsonElement serialize(WxMenu menu, Type typeOfSrc, JsonSerializationContext context) { | ||||
|     JsonObject json = new JsonObject(); | ||||
|  | ||||
|     JsonArray buttonArray = new JsonArray(); | ||||
|     for (WxMenuButton button : menu.getButton()) { | ||||
|       JsonObject buttonJson = serialize(button); | ||||
|       buttonArray.add(buttonJson); | ||||
|     } | ||||
|     json.add("button", buttonArray); | ||||
|      | ||||
|     return json; | ||||
|   } | ||||
|  | ||||
|   protected JsonObject serialize(WxMenuButton button) { | ||||
|     JsonObject buttonJson = new JsonObject(); | ||||
|     buttonJson.addProperty("name", button.getName()); | ||||
|     // TODO 其他字段 | ||||
|     if (button.getSub_button() == null || button.getSub_button().size() == 0) { | ||||
|       JsonArray buttonArray = new JsonArray(); | ||||
|       for (WxMenuButton sub_button : button.getSub_button()) { | ||||
|         buttonArray.add(serialize(sub_button)); | ||||
|       } | ||||
|       buttonJson.add("sub_button", buttonArray); | ||||
|     } | ||||
|     return buttonJson; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										67
									
								
								src/main/java/chanjarster/weixin/util/XmlTransformer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/main/java/chanjarster/weixin/util/XmlTransformer.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| package chanjarster.weixin.util; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.StringReader; | ||||
| import java.io.StringWriter; | ||||
| import java.io.Writer; | ||||
|  | ||||
| import javax.xml.bind.JAXBContext; | ||||
| import javax.xml.bind.JAXBException; | ||||
| import javax.xml.bind.Marshaller; | ||||
| import javax.xml.bind.Unmarshaller; | ||||
|  | ||||
| import com.sun.xml.bind.marshaller.CharacterEscapeHandler; | ||||
|  | ||||
| public class XmlTransformer { | ||||
|  | ||||
|   /** | ||||
|    * xml -> pojo | ||||
|    * @param clazz | ||||
|    * @param object | ||||
|    * @return | ||||
|    * @throws JAXBException  | ||||
|    */ | ||||
|   public static <T> T fromXml(Class<T> clazz, String xml) throws JAXBException { | ||||
|     JAXBContext context = JAXBContext.newInstance(clazz); | ||||
|     Unmarshaller um = context.createUnmarshaller(); | ||||
|     T object = (T) um.unmarshal(new StringReader(xml)); | ||||
|     return object; | ||||
|   } | ||||
|    | ||||
|   public static <T> T fromXml(Class<T> clazz, InputStream is) throws JAXBException { | ||||
|     JAXBContext context = JAXBContext.newInstance(clazz); | ||||
|     Unmarshaller um = context.createUnmarshaller(); | ||||
|     T object = (T) um.unmarshal(is); | ||||
|     return object; | ||||
|   } | ||||
|    | ||||
|   /** | ||||
|    * pojo -> xml | ||||
|    * @param clazz | ||||
|    * @return | ||||
|    * @throws JAXBException  | ||||
|    */ | ||||
|   public static <T> String toXml(Class<T> clazz, T object) throws JAXBException { | ||||
|     StringWriter stringWriter = new StringWriter(); | ||||
|     toXml(clazz, object, stringWriter); | ||||
|     return stringWriter.getBuffer().toString(); | ||||
|   } | ||||
|    | ||||
|   public static <T> void toXml(Class<T> clazz, T object, Writer writer) throws JAXBException { | ||||
|     JAXBContext context = JAXBContext.newInstance(clazz); | ||||
|     Marshaller m = context.createMarshaller(); | ||||
|     m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); | ||||
|     m.setProperty(CharacterEscapeHandler.class.getName(), characterUnescapeHandler); | ||||
|     m.marshal(object, writer); | ||||
|   } | ||||
|    | ||||
|   protected static CharacterEscapeHandler characterUnescapeHandler = new CharacterUnescapeHandler(); | ||||
|    | ||||
|   protected static class CharacterUnescapeHandler implements CharacterEscapeHandler { | ||||
|     public void escape(char[] ac, int i, int j, boolean flag, Writer writer) throws IOException { | ||||
|       writer.write(ac, i, j); | ||||
|     } | ||||
|   } | ||||
|    | ||||
| } | ||||
							
								
								
									
										38
									
								
								src/test/java/chanjarster/weixin/in/WxErrorTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/test/java/chanjarster/weixin/in/WxErrorTest.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| package chanjarster.weixin.in; | ||||
|  | ||||
| import org.testng.Assert; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| import chanjarster.weixin.in.WxError; | ||||
|  | ||||
| @Test | ||||
| public class WxErrorTest { | ||||
|  | ||||
|   public void testFromJson() { | ||||
|  | ||||
|     String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\" }"; | ||||
|     WxError wxError = WxError.fromJson(json); | ||||
|     Assert.assertTrue(wxError.getErrcode() == 40003); | ||||
|     Assert.assertEquals(wxError.getErrmsg(), "invalid openid"); | ||||
|  | ||||
|   } | ||||
|    | ||||
|   public void testFromBadJson1() { | ||||
|  | ||||
|     String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\", \"media_id\": \"12323423dsfafsf232f\" }"; | ||||
|     WxError wxError = WxError.fromJson(json); | ||||
|     Assert.assertTrue(wxError.getErrcode() == 40003); | ||||
|     Assert.assertEquals(wxError.getErrmsg(), "invalid openid"); | ||||
|  | ||||
|   } | ||||
|    | ||||
|   public void testFromBadJson2() { | ||||
|  | ||||
|     String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}"; | ||||
|     WxError wxError = WxError.fromJson(json); | ||||
|     Assert.assertTrue(wxError.getErrcode() == 0); | ||||
|     Assert.assertEquals(wxError.getErrmsg(), null); | ||||
|  | ||||
|   } | ||||
|    | ||||
| } | ||||
| @ -0,0 +1,83 @@ | ||||
| package chanjarster.weixin.out; | ||||
|  | ||||
| import org.testng.Assert; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| import chanjarster.weixin.out.WxCustomMessage; | ||||
| import chanjarster.weixin.out.WxCustomMessage.WxArticle; | ||||
| import chanjarster.weixin.service.WxMsgType; | ||||
| @Test | ||||
| public class WxCustomMessageTest { | ||||
|  | ||||
|   public void testTextReply() { | ||||
|     WxCustomMessage reply = new WxCustomMessage(); | ||||
|     reply.setTouser("OPENID"); | ||||
|     reply.setMsgtype(WxMsgType.TEXT); | ||||
|     reply.setContent("sfsfdsdf"); | ||||
|     Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); | ||||
|   } | ||||
|    | ||||
|   public void testImageReply() { | ||||
|     WxCustomMessage reply = new WxCustomMessage(); | ||||
|     reply.setTouser("OPENID"); | ||||
|     reply.setMsgtype(WxMsgType.IMAGE); | ||||
|     reply.setMedia_id("MEDIA_ID"); | ||||
|     Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); | ||||
|   } | ||||
|    | ||||
|   public void testVoiceReply() { | ||||
|     WxCustomMessage reply = new WxCustomMessage(); | ||||
|     reply.setTouser("OPENID"); | ||||
|     reply.setMsgtype(WxMsgType.VOICE); | ||||
|     reply.setMedia_id("MEDIA_ID"); | ||||
|     Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); | ||||
|   } | ||||
|    | ||||
|   public void testVideoReply() { | ||||
|     WxCustomMessage reply = new WxCustomMessage(); | ||||
|     reply.setTouser("OPENID"); | ||||
|     reply.setMsgtype(WxMsgType.VIDEO); | ||||
|     reply.setMedia_id("MEDIA_ID"); | ||||
|     reply.setThumb_media_id("MEDIA_ID"); | ||||
|     reply.setTitle("TITLE"); | ||||
|     reply.setDescription("DESCRIPTION"); | ||||
|     Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); | ||||
|   } | ||||
|    | ||||
|   public void testMusicReply() { | ||||
|     WxCustomMessage reply = new WxCustomMessage(); | ||||
|     reply.setTouser("OPENID"); | ||||
|     reply.setMsgtype(WxMsgType.MUSIC); | ||||
|     reply.setThumb_media_id("MEDIA_ID"); | ||||
|     reply.setDescription("DESCRIPTION"); | ||||
|     reply.setTitle("TITLE"); | ||||
|     reply.setMusicurl("MUSIC_URL"); | ||||
|     reply.setHqmusicurl("HQ_MUSIC_URL"); | ||||
|     Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"music\",\"music\":{\"title\":\"TITLE\",\"description\":\"DESCRIPTION\",\"thumb_media_id\":\"MEDIA_ID\",\"musicurl\":\"MUSIC_URL\",\"hqmusicurl\":\"HQ_MUSIC_URL\"}}"); | ||||
|   } | ||||
|    | ||||
|   public void testNewsReply() { | ||||
|     WxCustomMessage reply = new WxCustomMessage(); | ||||
|     reply.setTouser("OPENID"); | ||||
|     reply.setMsgtype(WxMsgType.NEWS); | ||||
|      | ||||
|     WxArticle article1 = new WxArticle(); | ||||
|     article1.setUrl("URL"); | ||||
|     article1.setPicurl("PIC_URL"); | ||||
|     article1.setDescription("Is Really A Happy Day"); | ||||
|     article1.setTitle("Happy Day"); | ||||
|     reply.getArticles().add(article1); | ||||
|      | ||||
|     WxArticle article2 = new WxArticle(); | ||||
|     article2.setUrl("URL"); | ||||
|     article2.setPicurl("PIC_URL"); | ||||
|     article2.setDescription("Is Really A Happy Day"); | ||||
|     article2.setTitle("Happy Day"); | ||||
|     reply.getArticles().add(article2); | ||||
|  | ||||
|      | ||||
|     System.out.println(reply.toJson()); | ||||
|     Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}"); | ||||
|   } | ||||
|    | ||||
| } | ||||
							
								
								
									
										119
									
								
								src/test/java/chanjarster/weixin/out/WxUserMessageTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/test/java/chanjarster/weixin/out/WxUserMessageTest.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | ||||
| package chanjarster.weixin.out; | ||||
|  | ||||
| import org.testng.Assert; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| import chanjarster.weixin.out.WxUserMessage; | ||||
| import chanjarster.weixin.service.WxMsgType; | ||||
|  | ||||
| @Test | ||||
| public class WxUserMessageTest { | ||||
|  | ||||
|   public void testFromXml() { | ||||
|  | ||||
|     String xml = "<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>" | ||||
|                 + "<PicUrl><![CDATA[this is a url]]></PicUrl>" | ||||
|                 + "<MediaId><![CDATA[media_id]]></MediaId>" | ||||
|                 + "<Format><![CDATA[Format]]></Format>" | ||||
|                 + "<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>" | ||||
|                 + "<Location_X>23.134521</Location_X>" | ||||
|                 + "<Location_Y>113.358803</Location_Y>" | ||||
|                 + "<Scale>20</Scale>" | ||||
|                 + "<Label><![CDATA[位置信息]]></Label>" | ||||
|                 + "<Description><![CDATA[公众平台官网链接]]></Description>" | ||||
|                 + "<Url><![CDATA[url]]></Url>" | ||||
|                 + "<Title><![CDATA[公众平台官网链接]]></Title>" | ||||
|                 + "<Event><![CDATA[subscribe]]></Event>" | ||||
|                 + "<EventKey><![CDATA[qrscene_123123]]></EventKey>" | ||||
|                 + "<Ticket><![CDATA[TICKET]]></Ticket>" | ||||
|                 + "<Latitude>23.137466</Latitude>" | ||||
|                 + "<Longitude>113.352425</Longitude>" | ||||
|                 + "<Precision>119.385040</Precision>" | ||||
|                 + "</xml>"; | ||||
|     WxUserMessage wxMessage = WxUserMessage.fromXml(xml); | ||||
|     Assert.assertEquals(wxMessage.getToUserName(), "toUser"); | ||||
|     Assert.assertEquals(wxMessage.getFromUserName(), "fromUser"); | ||||
|     Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860l)); | ||||
|     Assert.assertEquals(wxMessage.getMsgType(), WxMsgType.TEXT); | ||||
|     Assert.assertEquals(wxMessage.getContent(), "this is a test"); | ||||
|     Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l)); | ||||
|     Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); | ||||
|     Assert.assertEquals(wxMessage.getMediaId(), "media_id"); | ||||
|     Assert.assertEquals(wxMessage.getFormat(), "Format"); | ||||
|     Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); | ||||
|     Assert.assertEquals(wxMessage.getLocation_X(), new Double(23.134521d)); | ||||
|     Assert.assertEquals(wxMessage.getLocation_Y(), new Double(113.358803d)); | ||||
|     Assert.assertEquals(wxMessage.getScale(), new Double(20)); | ||||
|     Assert.assertEquals(wxMessage.getLabel(), "位置信息"); | ||||
|     Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); | ||||
|     Assert.assertEquals(wxMessage.getUrl(), "url"); | ||||
|     Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); | ||||
|     Assert.assertEquals(wxMessage.getEvent(), "subscribe"); | ||||
|     Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); | ||||
|     Assert.assertEquals(wxMessage.getTicket(), "TICKET"); | ||||
|     Assert.assertEquals(wxMessage.getLatitude(), new Double(23.137466)); | ||||
|     Assert.assertEquals(wxMessage.getLongitude(), new Double(113.352425)); | ||||
|     Assert.assertEquals(wxMessage.getPrecision(), new Double(119.385040)); | ||||
|   } | ||||
|    | ||||
|   public void testToXml() { | ||||
|     WxUserMessage wxMessage = new WxUserMessage(); | ||||
|     wxMessage.setToUserName("toUser"); | ||||
|     wxMessage.setFromUserName("fromUser"); | ||||
|     wxMessage.setCreateTime(new Long(1348831860l)); | ||||
|     wxMessage.setMsgType(WxMsgType.TEXT); | ||||
|     wxMessage.setContent("this is a test"); | ||||
|     wxMessage.setMsgId(new Long(1234567890123456l)); | ||||
|     wxMessage.setPicUrl("this is a url"); | ||||
|     wxMessage.setMediaId("media_id"); | ||||
|     wxMessage.setFormat("Format"); | ||||
|     wxMessage.setThumbMediaId("thumb_media_id"); | ||||
|     wxMessage.setLocation_X(new Double(23.134521d)); | ||||
|     wxMessage.setLocation_Y(new Double(113.358803d)); | ||||
|     wxMessage.setScale(new Double(20)); | ||||
|     wxMessage.setLabel("位置信息"); | ||||
|     wxMessage.setDescription("公众平台官网链接"); | ||||
|     wxMessage.setUrl("url"); | ||||
|     wxMessage.setTitle("公众平台官网链接"); | ||||
|     wxMessage.setEvent("subscribe"); | ||||
|     wxMessage.setEventKey("qrscene_123123"); | ||||
|     wxMessage.setTicket("TICKET"); | ||||
|     wxMessage.setLatitude(new Double(23.137466)); | ||||
|     wxMessage.setLongitude(new Double(113.352425)); | ||||
|     wxMessage.setPrecision(new Double(119.385040)); | ||||
|      | ||||
|     String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" | ||||
|         + "<xml>\n" | ||||
|         + "    <ToUserName><![CDATA[toUser]]></ToUserName>\n" | ||||
|         + "    <FromUserName><![CDATA[fromUser]]></FromUserName>\n" | ||||
|         + "    <CreateTime>1348831860</CreateTime>\n" | ||||
|         + "    <MsgType><![CDATA[text]]></MsgType>\n" | ||||
|         + "    <Content><![CDATA[this is a test]]></Content>\n" | ||||
|         + "    <MsgId>1234567890123456</MsgId>\n" | ||||
|         + "    <PicUrl><![CDATA[this is a url]]></PicUrl>\n" | ||||
|         + "    <MediaId><![CDATA[media_id]]></MediaId>\n" | ||||
|         + "    <Format><![CDATA[Format]]></Format>\n" | ||||
|         + "    <ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>\n" | ||||
|         + "    <Location_X>23.134521</Location_X>\n" | ||||
|         + "    <Location_Y>113.358803</Location_Y>\n" | ||||
|         + "    <Scale>20.0</Scale>\n" | ||||
|         + "    <Label><![CDATA[位置信息]]></Label>\n" | ||||
|         + "    <Title><![CDATA[公众平台官网链接]]></Title>\n" | ||||
|         + "    <Description><![CDATA[公众平台官网链接]]></Description>\n" | ||||
|         + "    <Url><![CDATA[url]]></Url>\n" | ||||
|         + "    <Event><![CDATA[subscribe]]></Event>\n" | ||||
|         + "    <EventKey><![CDATA[qrscene_123123]]></EventKey>\n" | ||||
|         + "    <Ticket><![CDATA[TICKET]]></Ticket>\n" | ||||
|         + "    <Latitude>23.137466</Latitude>\n" | ||||
|         + "    <Longitude>113.352425</Longitude>\n" | ||||
|         + "    <Precision>119.38504</Precision>\n" | ||||
|         + "</xml>\n"; | ||||
|     Assert.assertEquals(wxMessage.toXml(), xml); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,6 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| // TODO | ||||
| public class WxMessageRouterTest { | ||||
|  | ||||
| } | ||||
							
								
								
									
										125
									
								
								src/test/java/chanjarster/weixin/service/WxServiceTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/test/java/chanjarster/weixin/service/WxServiceTest.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,125 @@ | ||||
| package chanjarster.weixin.service; | ||||
|  | ||||
| import java.io.InputStream; | ||||
|  | ||||
| import javax.xml.bind.JAXBException; | ||||
| import javax.xml.bind.annotation.XmlAccessType; | ||||
| import javax.xml.bind.annotation.XmlAccessorType; | ||||
| import javax.xml.bind.annotation.XmlRootElement; | ||||
|  | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.testng.Assert; | ||||
| import org.testng.annotations.DataProvider; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| import chanjarster.weixin.exception.WxErrorException; | ||||
| import chanjarster.weixin.out.WxCustomMessage; | ||||
| import chanjarster.weixin.util.XmlTransformer; | ||||
|  | ||||
| public class WxServiceTest { | ||||
|  | ||||
|   @Test(dataProvider = "configs") | ||||
|   public void testRefreshAccessToken(WxConfigProvider config) throws WxErrorException { | ||||
|     String before = config.getAccessToken(); | ||||
|      | ||||
|     WxService wxService = new WxServiceImpl(); | ||||
|     wxService.setWxConfigProvider(config); | ||||
|     wxService.refreshAccessToken(); | ||||
|      | ||||
|     String after = config.getAccessToken(); | ||||
|      | ||||
|     Assert.assertNotEquals(before, after); | ||||
|     Assert.assertTrue(StringUtils.isNotBlank(after)); | ||||
|   } | ||||
|    | ||||
|   @Test(dataProvider = "configs") | ||||
|   public void sendCustomMessage(SimpleWxConfigProvider config) throws WxErrorException { | ||||
|     WxService wxService = new WxServiceImpl(); | ||||
|     wxService.setWxConfigProvider(config); | ||||
|      | ||||
|     WxCustomMessage message = new WxCustomMessage(); | ||||
|     message.setMsgtype(WxMsgType.TEXT); | ||||
|     message.setTouser(config.getOpenId()); | ||||
|     message.setContent("欢迎使用教务系统微信公众号\n下面\n<a href=\"http://192.168.1.249:9180/eams-rc/login.action\">Hello World</a>"); | ||||
|  | ||||
|     wxService.sendCustomMessage(message); | ||||
|   } | ||||
|   /** | ||||
|    * 返回新的access_token | ||||
|    * @return | ||||
|    * @throws JAXBException  | ||||
|    */ | ||||
|   @DataProvider(name = "configs") | ||||
|   public Object[][] getConfig() throws JAXBException { | ||||
|     /** | ||||
|      * 将 src/test/resources/test-config.sample.xml 改成 test-config.xml 并设置appId, secret, 一个过期的accessToken | ||||
|      */ | ||||
|     // 没有access_token | ||||
|     InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml"); | ||||
|     SimpleWxConfigProvider config1 = XmlTransformer.fromXml(SimpleWxConfigProvider.class, is1); | ||||
|     return new Object[][] { | ||||
|         new Object[] { | ||||
|             config1 | ||||
|         } | ||||
|     }; | ||||
|   } | ||||
|    | ||||
|   @XmlRootElement(name = "xml") | ||||
|   @XmlAccessorType(XmlAccessType.FIELD) | ||||
|   public static class SimpleWxConfigProvider implements WxConfigProvider { | ||||
|     private String appId; | ||||
|     private String secret; | ||||
|     private String accessToken = ""; | ||||
|     private Integer expiresIn; | ||||
|     private String token; | ||||
|     private String openId; | ||||
|     public void updateAccessToken(String accessToken, Integer expiresIn) { | ||||
|       this.accessToken = accessToken; | ||||
|       this.expiresIn = expiresIn; | ||||
|     } | ||||
|     public String getAccessToken() { | ||||
|       return accessToken; | ||||
|     } | ||||
|     public String getAppId() { | ||||
|       return appId; | ||||
|     } | ||||
|     public String getSecret() { | ||||
|       return secret; | ||||
|     } | ||||
|     public String getToken() { | ||||
|       return token; | ||||
|     } | ||||
|     public void setAppId(String appId) { | ||||
|       this.appId = appId; | ||||
|     } | ||||
|     public void setSecret(String secret) { | ||||
|       this.secret = secret; | ||||
|     } | ||||
|     public void setToken(String token) { | ||||
|       this.token = token; | ||||
|     } | ||||
|     public Integer getExpiresIn() { | ||||
|       return expiresIn; | ||||
|     } | ||||
|     public void setExpiresIn(Integer expiresIn) { | ||||
|       this.expiresIn = expiresIn; | ||||
|     } | ||||
|     public void setAccessToken(String accessToken) { | ||||
|       this.accessToken = accessToken; | ||||
|     } | ||||
|     public String getOpenId() { | ||||
|       return openId; | ||||
|     } | ||||
|     public void setOpenId(String openId) { | ||||
|       this.openId = openId; | ||||
|     } | ||||
|     @Override | ||||
|     public String toString() { | ||||
|       return "SimpleWxConfigProvider [appId=" + appId + ", secret=" + secret + ", accessToken=" + accessToken | ||||
|           + ", expiresIn=" + expiresIn + ", token=" + token + ", openId=" + openId + "]"; | ||||
|     } | ||||
|       | ||||
|   } | ||||
|    | ||||
|    | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/test/resources/test-config.sample.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/test/resources/test-config.sample.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| <xml> | ||||
|   <appId></appId> | ||||
|   <secret></secret> | ||||
|   <accessToken></accessToken> | ||||
|   <expiresIn></expiresIn> | ||||
|   <openId></openId> | ||||
| </xml> | ||||
		Reference in New Issue
	
	Block a user
	 Daniel Qian
					Daniel Qian