maven多模块分离

This commit is contained in:
jy.hu
2014-10-27 21:10:38 +08:00
parent 4cee4678af
commit cde0f75069
169 changed files with 1308 additions and 798 deletions
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
+1
View File
@@ -0,0 +1 @@
/target/
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>weixin4j-base</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,4 @@
eclipse.preferences.version=1
encoding//src/main/java=UTF-8
encoding//src/test/java=UTF-8
encoding/<project>=UTF-8
@@ -0,0 +1,13 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7
@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
+4
View File
@@ -0,0 +1,4 @@
weixin4j-base
=============
tencent weixin base java sdk 微信开发基础工程
+104
View File
@@ -0,0 +1,104 @@
<?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>
<parent>
<groupId>com.foxinmy.weixin4j</groupId>
<artifactId>weixin4j</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>weixin4j-base</artifactId>
<name>weixin4j-base</name>
<url>https://github.com/foxinmy</url>
<dependencies>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>${xstream.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>${dom4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<exclusions>
<exclusion>
<artifactId>logback-core</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${jcl.over.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${log4j.over.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons.codec.version}</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,32 @@
package com.foxinmy.weixin4j.exception;
public class PayException extends Exception {
private static final long serialVersionUID = 7148145661883468514L;
private String errorCode;
private String errorMsg;
public PayException(String errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public PayException(String errorMsg) {
this.errorCode = "-1";
this.errorMsg = errorMsg;
}
public String getErrorCode() {
return this.errorCode;
}
public String getErrorMsg() {
return this.errorMsg;
}
@Override
public String getMessage() {
return String.format("%s,%s", getErrorMsg(), getErrorCode());
}
}
@@ -0,0 +1,44 @@
package com.foxinmy.weixin4j.exception;
/**
* 调用微信接口抛出的异常
*
* @className WeixinException
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
* @see
*/
public class WeixinException extends Exception {
private static final long serialVersionUID = 7148145661883468514L;
private int errorCode;
private String errorMsg;
public WeixinException(int errorCode) {
this.errorCode = errorCode;
}
public WeixinException(int errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public WeixinException(String errorMsg) {
this.errorMsg = errorMsg;
}
public int getErrorCode() {
return errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
@Override
public String getMessage() {
return this.errorCode + "," + this.errorMsg;
}
}
@@ -0,0 +1,78 @@
package com.foxinmy.weixin4j.http;
import java.io.Serializable;
/**
* 调用接口响应值
*
* @className BaseResult
* @author jy.hu
* @date 2014年9月24日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%85%A8%E5%B1%80%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E">全局返回码</a>
*/
public class BaseResult implements Serializable {
private static final long serialVersionUID = -6185313616955051150L;
private int errcode;
private String errmsg;
private String msgid;
private String text;
public BaseResult() {
}
public BaseResult(int errcode, String errmsg, String text) {
this.errcode = errcode;
this.errmsg = errmsg;
this.text = text;
}
public BaseResult(int errcode, String errmsg, String msgid, String text) {
this.errcode = errcode;
this.errmsg = errmsg;
this.msgid = msgid;
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
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 String getMsgid() {
return msgid;
}
public void setMsgid(String msgid) {
this.msgid = msgid;
}
@Override
public String toString() {
return "BaseResult [errcode=" + errcode + ", errmsg=" + errmsg
+ ", msgid=" + msgid + ", text=" + text + "]";
}
}
@@ -0,0 +1,195 @@
package com.foxinmy.weixin4j.http;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import com.foxinmy.weixin4j.exception.WeixinException;
/**
* 调用微信相关接口的HttpRequest,对于其他请求可能并不试用
*
* @className HttpRequest
* @author jy
* @date 2014年8月21日
* @since JDK 1.7
* @see
*/
public class HttpRequest {
private AbstractHttpClient client;
public HttpRequest() {
this(150, 100, 10000, 10000);
}
public HttpRequest(int maxConPerRoute, int maxTotal, int socketTimeout,
int connectionTimeout) {
PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
// 指定IP并发最大数
connectionManager.setDefaultMaxPerRoute(maxConPerRoute);
// socket最大创建数
connectionManager.setMaxTotal(maxTotal);
client = new DefaultHttpClient(connectionManager);
client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT,
socketTimeout);
client.getParams().setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);
client.getParams().setBooleanParameter(
CoreConnectionPNames.TCP_NODELAY, false);
client.getParams().setParameter(
CoreConnectionPNames.SOCKET_BUFFER_SIZE, 1024 * 1024);
client.getParams().setParameter(ClientPNames.COOKIE_POLICY,
CookiePolicy.IGNORE_COOKIES);
client.getParams().setParameter(
CoreProtocolPNames.HTTP_CONTENT_CHARSET, Consts.UTF_8);
client.getParams().setParameter(HttpHeaders.CONTENT_ENCODING,
Consts.UTF_8);
client.getParams().setParameter(HttpHeaders.ACCEPT_CHARSET,
Consts.UTF_8);
}
public Response get(String url) throws WeixinException {
return get(url, (Parameter[]) null);
}
public Response get(String url, Parameter... parameters)
throws WeixinException {
StringBuilder sb = new StringBuilder(url);
if (parameters != null && parameters.length > 0) {
if (url.indexOf("?") < 0) {
sb.append(String.format("?%s=%s", parameters[0].getName(),
parameters[0].getValue()));
}
for (int i = 0; i < parameters.length; i++) {
sb.append(parameters[i].toGetPara());
}
}
return doRequest(new HttpGet(sb.toString()));
}
public Response post(String url) throws WeixinException {
return post(url, (Parameter[]) null);
}
public Response post(String url, Parameter... parameters)
throws WeixinException {
HttpPost method = new HttpPost(url);
List<NameValuePair> params = new ArrayList<NameValuePair>();
for (Parameter parameter : parameters) {
params.add(parameter.toPostPara());
}
method.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));
return doRequest(method);
}
public Response post(String url, String body) throws WeixinException {
HttpPost method = new HttpPost(url);
method.setEntity(new StringEntity(body, ContentType.create(
ContentType.APPLICATION_JSON.getMimeType(), Consts.UTF_8)));
return doRequest(method);
}
public Response post(String url, byte[] bytes) throws WeixinException {
HttpPost method = new HttpPost(url);
method.setEntity(new ByteArrayEntity(bytes, ContentType.create(
ContentType.MULTIPART_FORM_DATA.getMimeType(), Consts.UTF_8)));
return doRequest(method);
}
public Response post(String url, File file) throws WeixinException {
HttpPost method = new HttpPost(url);
method.setEntity(new FileEntity(file, ContentType.create(
ContentType.APPLICATION_OCTET_STREAM.getMimeType(),
Consts.UTF_8)));
return doRequest(method);
}
public Response post(String url, PartParameter... paramters)
throws WeixinException {
HttpPost method = new HttpPost(url);
MultipartEntity entity = new MultipartEntity();
for (PartParameter paramter : paramters) {
entity.addPart(paramter.getName(), paramter.getContentBody());
}
method.setEntity(entity);
return doRequest(method);
}
protected Response doRequest(HttpRequestBase request)
throws WeixinException {
try {
HttpResponse httpResponse = client.execute(request);
StatusLine statusLine = httpResponse.getStatusLine();
HttpEntity httpEntity = httpResponse.getEntity();
int status = statusLine.getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new WeixinException(status, "request fail");
}
// 301或者302
if (status == HttpStatus.SC_MOVED_PERMANENTLY
|| status == HttpStatus.SC_MOVED_TEMPORARILY) {
throw new WeixinException(status, String.format(
"the page was redirected to %s",
httpResponse.getFirstHeader("location")));
}
byte[] data = EntityUtils.toByteArray(httpEntity);
Response response = new Response();
response.setBody(data);
response.setStatusCode(status);
response.setStatusText(statusLine.getReasonPhrase());
response.setStream(new ByteArrayInputStream(data));
response.setText(StringUtils.join(data));
Header contentType = httpResponse
.getFirstHeader(HttpHeaders.CONTENT_TYPE);
if (contentType.getValue().contains(
ContentType.APPLICATION_JSON.getMimeType())
|| contentType.getValue().contains(
ContentType.TEXT_PLAIN.getMimeType())) {
BaseResult result = response.getAsResult();
if (result.getErrcode() != 0) {
throw new WeixinException(result.getErrcode(),
result.getErrmsg());
}
}
EntityUtils.consume(httpEntity);
return response;
} catch (IOException e) {
throw new WeixinException(e.getMessage());
} finally {
request.releaseConnection();
}
}
}
@@ -0,0 +1,63 @@
package com.foxinmy.weixin4j.http;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
public class Parameter {
private final static String CHARSET = StandardCharsets.UTF_8.name();
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Parameter() {
}
public Parameter(String name, String value) {
this.name = name;
this.value = value;
}
public String toGetPara() {
try {
return String.format("&%s=%s", name, URLEncoder.encode(value, CHARSET));
} catch (UnsupportedEncodingException e) {
return String.format("&%s=%s", name, value);
}
}
public NameValuePair toPostPara() {
try {
return new BasicNameValuePair(name, URLEncoder.encode(value, CHARSET));
} catch (UnsupportedEncodingException e) {
return new BasicNameValuePair(name, value);
}
}
@Override
public String toString() {
return String.format("[Parameter name=%s, value=%s]", name, value);
}
}
@@ -0,0 +1,35 @@
package com.foxinmy.weixin4j.http;
import org.apache.http.entity.mime.content.ContentBody;
public class PartParameter {
private String name;
private ContentBody contentBody;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ContentBody getContentBody() {
return contentBody;
}
public void setContentBody(ContentBody contentBody) {
this.contentBody = contentBody;
}
public PartParameter(String name, ContentBody contentBody) {
super();
this.name = name;
this.contentBody = contentBody;
}
@Override
public String toString() {
return "PartParameter [name=" + name + ", contentBody=" + contentBody + "]";
}
}
@@ -0,0 +1 @@
基于HttpClient封装的针对微信公众平台、微信企业号API的HttpRequest
@@ -0,0 +1,124 @@
package com.foxinmy.weixin4j.http;
import java.io.InputStream;
import java.io.Serializable;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.thoughtworks.xstream.XStream;
public class Response {
private String text;
private int statusCode;
private String statusText;
private byte[] body;
private InputStream stream;
public Response() {
}
public Response(String text) {
this.text = text;
}
public String getAsString() {
return text;
}
public BaseResult getAsResult() {
return JSON.parseObject(text, BaseResult.class);
}
public JSONObject getAsJson() {
return JSON.parseObject(text);
}
@SuppressWarnings("unchecked")
public <T> T getAsObject(Class<? extends Serializable> clazz) {
return (T) JSON.parseObject(text, clazz);
}
public Object getAsXml() {
XStream xs = new XStream();
xs.autodetectAnnotations(true);
return xs.fromXML(text);
}
/**
* <a href=
* "http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%8F%A3%E9%A2%91%E7%8E%87%E9%99%90%E5%88%B6%E8%AF%B4%E6%98%8E"
* >全局返回码</a> {"errcode":45009,"errmsg":"api freq out of limit"}
*
* @return
* @throws DocumentException
*/
public BaseResult getBaseError() throws DocumentException {
BaseResult result = getAsResult();
if (result.getErrcode() != 0) {
SAXReader reader = new SAXReader();
Document doc = reader.read(Thread.currentThread()
.getContextClassLoader().getResourceAsStream("error.xml"));
Node node = doc.getRootElement().selectSingleNode(
String.format("error[@code='%d']", result.getErrcode()));
if (node != null) {
result.setText(node.getStringValue());
}
}
return result;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getStatusText() {
return statusText;
}
public void setStatusText(String statusText) {
this.statusText = statusText;
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
public InputStream getStream() {
return stream;
}
public void setStream(InputStream stream) {
this.stream = stream;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[Response text=").append(text);
sb.append(", statusCode=").append(statusCode);
sb.append(", statusText=").append(statusText).append("]");
return sb.toString();
}
}
@@ -0,0 +1,74 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token,正常情况下access_token有效期为7200秒,
* 重复获取将导致上次获取的access_token失效
*
* @className Token
* @author jy.hu
* @date 2014年4月5日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token">获取token</a>
*/
@XStreamAlias("app-token")
public class Token implements Serializable {
private static final long serialVersionUID = 1L;
@JSONField(name = "access_token")
private String accessToken;
@JSONField(name = "expires_in")
private int expiresIn;
private String openid;
private long time;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Token) {
return accessToken.equals(((Token) obj).getAccessToken());
}
return false;
}
@Override
public String toString() {
return "Token [accessToken=" + accessToken + ", expiresIn=" + expiresIn + ", openid=" + openid + ", time=" + time + "]";
}
}
@@ -0,0 +1,150 @@
package com.foxinmy.weixin4j.msg;
import java.io.Serializable;
import java.io.Writer;
import com.foxinmy.weixin4j.type.MessageType;
import com.foxinmy.weixin4j.util.ClassUtil;
import com.foxinmy.weixin4j.xml.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;
/**
* 普通消息基类
* <p>
* <font color="red">回复图片等多媒体消息时需要预先上传多媒体文件到微信服务器,
* 假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试</font>
* </p>
*
* @className BaseMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
*/
public class BaseMessage implements Serializable {
private static final long serialVersionUID = 7761192742840031607L;
private final static XStream xmlStream = new XStream();
private final static XStream jsonStream = new XStream(
new JsonHierarchicalStreamDriver() {
public HierarchicalStreamWriter createWriter(Writer writer) {
return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
}
});
@XStreamAlias("ToUserName")
private String toUserName; // 开发者微信号
@XStreamAlias("FromUserName")
private String fromUserName; // 发送方帐号(一个OpenID
@XStreamAlias("CreateTime")
private long createTime = System.currentTimeMillis(); // 消息创建时间 (整型)
@XStreamAlias("MsgType")
private MessageType msgType; // 消息类型
@XStreamAlias("MsgId")
private long msgId; // 消息ID
static {
Class<?>[] classes = ClassUtil.getClasses(
TextMessage.class.getPackage()).toArray(new Class[0]);
xmlStream.ignoreUnknownElements();
xmlStream.autodetectAnnotations(true);
xmlStream.processAnnotations(classes);
xmlStream.omitField(BaseMessage.class, "msgId");
jsonStream.setMode(XStream.NO_REFERENCES);
jsonStream.autodetectAnnotations(true);
jsonStream.processAnnotations(classes);
jsonStream.omitField(BaseMessage.class, "msgId");
}
public BaseMessage(MessageType msgType) {
this.msgType = msgType;
}
public BaseMessage(MessageType msgType, BaseMessage inMessage) {
this(msgType, inMessage.getFromUserName(), inMessage.getToUserName());
}
public BaseMessage(MessageType msgType, String toUserName,
String fromUserName) {
this.msgType = msgType;
this.toUserName = toUserName;
this.fromUserName = fromUserName;
}
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 long getCreateTime() {
return createTime;
}
public void setCreateTime(long createTime) {
this.createTime = createTime;
}
public MessageType getMsgType() {
return msgType;
}
public void setMsgType(MessageType msgType) {
this.msgType = msgType;
}
public long getMsgId() {
return msgId;
}
public void setMsgId(long msgId) {
this.msgId = msgId;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BaseMessage) {
return ((BaseMessage) obj).getMsgId() == msgId;
}
return false;
}
protected XStream getXStream() {
Class<? extends BaseMessage> targetClass = getMsgType()
.getMessageClass();
xmlStream.alias("xml", targetClass);
return xmlStream;
}
/**
* 消息对象转换为微信服务器接受的xml格式消息
*
* @return xml字符串
*/
public String toXml() {
return getXStream().toXML(this);
}
/**
* 消息对象转换为微信服务器接受的json格式字符串
*
* @return json字符串
*/
public String toJson() {
return jsonStream.toXML(this);
}
}
@@ -0,0 +1,49 @@
package com.foxinmy.weixin4j.msg;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 图片消息
*
* @className ImageMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF#.E5.9B.BE.E7.89.87.E6.B6.88.E6.81.AF">图片消息</a>
*/
public class ImageMessage extends BaseMessage {
private static final long serialVersionUID = 8430800898756567016L;
public ImageMessage() {
super(MessageType.image);
}
@XStreamAlias("PicUrl")
private String picUrl; // 图片链接
@XStreamAlias("MediaId")
private String mediaId; // 图片消息媒体id,可以调用多媒体文件下载接口拉取数据。
public String getPicUrl() {
return picUrl;
}
public String getMediaId() {
return mediaId;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ImageMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,picUrl=").append(picUrl);
sb.append(" ,mediaId=").append(mediaId);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,56 @@
package com.foxinmy.weixin4j.msg;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 链接消息
*
* @className LinkMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF#.E9.93.BE.E6.8E.A5.E6.B6.88.E6.81.AF">链接消息</a>
*/
public class LinkMessage extends BaseMessage {
private static final long serialVersionUID = 754952745115497030L;
public LinkMessage() {
super(MessageType.link);
}
@XStreamAlias("Title")
private String title; // 消息标题
@XStreamAlias("Description")
private String description; // 消息描述
@XStreamAlias("url")
private String url; // 消息链接
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getUrl() {
return url;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[LinkMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,title=").append(title);
sb.append(" ,description=").append(description);
sb.append(" ,url=").append(url);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,68 @@
package com.foxinmy.weixin4j.msg;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 地理位置消息
*
* @className LocationMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF#.E5.9C.B0.E7.90.86.E4.BD.8D.E7.BD.AE.E6.B6.88.E6.81.AF">地理位置消息</a>
*/
public class LocationMessage extends BaseMessage {
private static final long serialVersionUID = 2866021596599237334L;
public LocationMessage() {
super(MessageType.location);
}
@XStreamAlias("Location_X")
private double x; // 地理位置维度
@XStreamAlias("Location_Y")
private double y; // 地理位置经度
@XStreamAlias("Scale")
private double scale; // 地图缩放大小
@XStreamAlias("Label")
private String label; // 地理位置信息
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getScale() {
return scale;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[LocationMessage ,toUserName=")
.append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,location_X=").append(x);
sb.append(" ,location_Y=").append(y);
sb.append(" ,scale=").append(scale);
sb.append(" ,label=").append(label);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1 @@
普通消息
@@ -0,0 +1,55 @@
package com.foxinmy.weixin4j.msg;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 文本消息(接收|回复)
*
* @className TextMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF#.E6.96.87.E6.9C.AC.E6.B6.88.E6.81.AF">接收文本消息</a>
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E8%A2%AB%E5%8A%A8%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF#.E5.9B.9E.E5.A4.8D.E6.96.87.E6.9C.AC.E6.B6.88.E6.81.AF">回复文本消息</a>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
public class TextMessage extends BaseMessage {
private static final long serialVersionUID = -7018053906644190260L;
public TextMessage() {
super(MessageType.text);
}
public TextMessage(String content, BaseMessage inMessage) {
super(MessageType.text, inMessage);
this.content = content;
}
@XStreamAlias("Content")
private String content; // 消息内容
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[TextMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,content=").append(content);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,49 @@
package com.foxinmy.weixin4j.msg;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 视频消息
*
* @className VideoMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF#.E8.A7.86.E9.A2.91.E6.B6.88.E6.81.AF">视频消息</a>
*/
public class VideoMessage extends BaseMessage {
private static final long serialVersionUID = -1013075358679078381L;
public VideoMessage() {
super(MessageType.video);
}
@XStreamAlias("MediaId")
private String mediaId; // 视频消息媒体id,可以调用多媒体文件下载接口拉取数据。
@XStreamAlias("ThumbMediaId")
private String thumbMediaId; // 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
public String getMediaId() {
return mediaId;
}
public String getThumbMediaId() {
return thumbMediaId;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[VideoMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,mediaId=").append(mediaId);
sb.append(" ,thumbMediaId=").append(thumbMediaId);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,60 @@
package com.foxinmy.weixin4j.msg;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 语音消息
* <p>
* 开通语音识别功能,用户每次发送语音给公众号时,微信会在推送的语音消息XML数据包中,赋值到Recongnition字段.
* </p>
*
* @className VoiceMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF#.E8.AF.AD.E9.9F.B3.E6.B6.88.E6.81.AF">语音消息</a>
*/
public class VoiceMessage extends BaseMessage {
private static final long serialVersionUID = -7988380977182214003L;
public VoiceMessage() {
super(MessageType.voice);
}
@XStreamAlias("MediaId")
private String mediaId; // 语音消息媒体id,可以调用多媒体文件下载接口拉取数据。
@XStreamAlias("Format")
private String format; // 语音格式,如amrspeex等
@XStreamAlias("Recognition")
private String recognition; // 语音识别结果,UTF8编码
public String getRecognition() {
return recognition;
}
public String getMediaId() {
return mediaId;
}
public String getFormat() {
return format;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[VoiceMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,mediaId=").append(mediaId);
sb.append(" ,format=").append(format);
sb.append(" ,recognition=").append(recognition);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,47 @@
package com.foxinmy.weixin4j.msg.event;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 事件消息基类
* @className EventMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81">事件推送</a>
*/
public class EventMessage extends BaseMessage {
private static final long serialVersionUID = 7703667223814088865L;
public EventMessage(EventType eventType) {
super(MessageType.event);
this.eventType = eventType;
}
@XStreamAlias("Event")
private EventType eventType;
public EventType getEventType() {
return eventType;
}
public void setEventType(EventType eventType) {
this.eventType = eventType;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[EventMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,eventType=").append(eventType.name());
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,58 @@
package com.foxinmy.weixin4j.msg.event;
import com.foxinmy.weixin4j.type.EventType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 上报地理位置事件
*
* @className LocationEventMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81#.E4.B8.8A.E6.8A.A5.E5.9C.B0.E7.90.86.E4.BD.8D.E7.BD.AE.E4.BA.8B.E4.BB.B6">上报地理位置事件</a>
*/
public class LocationEventMessage extends EventMessage {
private static final long serialVersionUID = -2030716800669824861L;
public LocationEventMessage() {
super(EventType.location);
}
@XStreamAlias("Latitude")
private String latitude;// 地理位置纬度
@XStreamAlias("Longitude")
private String longitude;// 地理位置经度
@XStreamAlias("Precision")
private String precision;// 地理位置精度
public String getLatitude() {
return latitude;
}
public String getLongitude() {
return longitude;
}
public String getPrecision() {
return precision;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[LocationEventMessage ,toUserName=").append(
super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,eventType=").append(super.getEventType().name());
sb.append(" ,longitude=").append(longitude);
sb.append(" ,latitude=").append(latitude);
sb.append(" ,precision=").append(precision);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,72 @@
package com.foxinmy.weixin4j.msg.event;
import com.foxinmy.weixin4j.type.EventType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 群发消息事件推送
*
* @className MassEventMessage
* @author jy
* @date 2014年4月27日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AB%98%E7%BA%A7%E7%BE%A4%E5%8F%91%E6%8E%A5%E5%8F%A3#.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81.E7.BE.A4.E5.8F.91.E7.BB.93.E6.9E.9C">群发回调</a>
*/
public class MassEventMessage extends EventMessage {
private static final long serialVersionUID = -1660543255873723895L;
public MassEventMessage() {
super(EventType.massendjobfinish);
}
@XStreamAlias("Status")
private String status;
@XStreamAlias("TotalCount")
private int totalCount;
@XStreamAlias("FilterCount")
private int filterCount;
@XStreamAlias("SentCount")
private int sentCount;
@XStreamAlias("ErrorCount")
private int errorCount;
public String getStatus() {
return status;
}
public int getTotalCount() {
return totalCount;
}
public int getFilterCount() {
return filterCount;
}
public int getSentCount() {
return sentCount;
}
public int getErrorCount() {
return errorCount;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[MassEventMessage ,toUserName=").append(
super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,eventType=").append(super.getEventType().name());
sb.append(" ,status=").append(status);
sb.append(" ,totalCount=").append(totalCount);
sb.append(" ,filterCount=").append(filterCount);
sb.append(" ,sentCount=").append(sentCount);
sb.append(" ,errorCount=").append(errorCount);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1 @@
事件消息
@@ -0,0 +1,55 @@
package com.foxinmy.weixin4j.msg.event;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 扫描二维码事件
*
* @className ScanEventMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81#.E6.89.AB.E6.8F.8F.E5.B8.A6.E5.8F.82.E6.95.B0.E4.BA.8C.E7.BB.B4.E7.A0.81.E4.BA.8B.E4.BB.B6">扫描二维码事件</a>
*/
public class ScanEventMessage extends EventMessage {
public ScanEventMessage() {
super(null);
}
private static final long serialVersionUID = 8078674062833071562L;
private static final String PARA_PREFIX = "qrscene_";
@XStreamAlias("EventKey")
private String eventKey; // 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
@XStreamAlias("Ticket")
private String ticket; // 二维码的ticket,可用来换取二维码图片
public String getEventKey() {
return eventKey;
}
public String getTicket() {
return ticket;
}
public String getParameter() {
return eventKey.replace(PARA_PREFIX, "");
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ScanEventMessage ,toUserName=").append(
super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,eventType=").append(super.getEventType().name());
sb.append(" ,eventKey=").append(eventKey);
sb.append(" ,ticket=").append(ticket);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,30 @@
package com.foxinmy.weixin4j.msg.event;
/**
* 关注/取消关注事件
* <font color="red">包括直接关注与扫描关注</font>
* @className ScribeEventMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81#.E5.85.B3.E6.B3.A8.2F.E5.8F.96.E6.B6.88.E5.85.B3.E6.B3.A8.E4.BA.8B.E4.BB.B6">关注/取消关注事件</a>
*/
public class ScribeEventMessage extends ScanEventMessage {
private static final long serialVersionUID = -6846321620262204915L;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ScribeEventMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,eventType=").append(super.getEventType().name());
sb.append(" ,eventKey=").append(super.getEventKey());
sb.append(" ,ticket=").append(super.getTicket());
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,35 @@
package com.foxinmy.weixin4j.msg.event;
import com.foxinmy.weixin4j.type.EventType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 模板消息事件推送
*
* @className TemplatesendjobfinishMessage
* @author jy
* @date 2014年9月19日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%A8%A1%E6%9D%BF%E6%B6%88%E6%81%AF%E6%8E%A5%E5%8F%A3">模板消息事件推送</a>
*/
public class TemplatesendjobfinishMessage extends EventMessage {
private static final long serialVersionUID = -2903359365988594012L;
public TemplatesendjobfinishMessage() {
super(EventType.templatesendjobfinish);
}
@XStreamAlias("Status")
private String status; // 推送状态
public String getStatus() {
return status;
}
@Override
public String toString() {
return "TemplatesendjobfinishMessage [status=" + status + "]";
}
}
@@ -0,0 +1,45 @@
package com.foxinmy.weixin4j.msg.event.menu;
import com.foxinmy.weixin4j.msg.event.EventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 自定义菜单事件(view|click)
*
* @className MenuEventMessage
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81#.E7.82.B9.E5.87.BB.E8.8F.9C.E5.8D.95.E6.8B.89.E5.8F.96.E6.B6.88.E6.81.AF.E6.97.B6.E7.9A.84.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81">菜单事件</a>
*/
public class MenuEventMessage extends EventMessage {
private static final long serialVersionUID = -1049672447995366063L;
public MenuEventMessage() {
super(EventType.click);
}
@XStreamAlias("EventKey")
private String eventKey; // 事件KEY值,与自定义菜单接口中KEY值对应
public String getEventKey() {
return eventKey;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[MenuEventMessage ,toUserName=").append(
super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,eventType=").append(super.getEventType().name());
sb.append(" ,eventKey=").append(eventKey);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}
@@ -0,0 +1,74 @@
package com.foxinmy.weixin4j.msg.event.menu;
import com.foxinmy.weixin4j.type.EventType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 弹出地理位置选择器的事件推送
*
* @className MenuLocationEventMessage
* @author jy
* @date 2014年9月30日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81#location_select.EF.BC.9A.E5.BC.B9.E5.87.BA.E5.9C.B0.E7.90.86.E4.BD.8D.E7.BD.AE.E9.80.89.E6.8B.A9.E5.99.A8.E7.9A.84.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81">弹出地理位置选择事件推送</a>
*/
public class MenuLocationEventMessage extends MenuEventMessage {
private static final long serialVersionUID = 145223888272819563L;
public MenuLocationEventMessage() {
super.setEventType(EventType.location_select);
}
@XStreamAlias("SendLocationInfo")
private LocationInfo locationInfo;
public LocationInfo getLocationInfo() {
return locationInfo;
}
public static class LocationInfo {
@XStreamAlias("Location_X")
private double x; // 地理位置维度
@XStreamAlias("Location_Y")
private double y; // 地理位置经度
@XStreamAlias("Scale")
private double scale; // 地图缩放大小
@XStreamAlias("Label")
private String label; // 地理位置信息
@XStreamAlias("Poiname")
private String poiname;
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getScale() {
return scale;
}
public String getLabel() {
return label;
}
public String getPoiname() {
return poiname;
}
@Override
public String toString() {
return "LocationInfo [x=" + x + ", y=" + y + ", scale=" + scale
+ ", label=" + label + ", poiname=" + poiname + "]";
}
}
@Override
public String toString() {
return "MenuLocationEventMessage [locationInfo=" + locationInfo + "]";
}
}
@@ -0,0 +1,63 @@
package com.foxinmy.weixin4j.msg.event.menu;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 弹出拍照或者相册发图的事件推送(pic_sysphoto|pic_photo_or_album|pic_weixin)
*
* @className MenuPhotoEventMessage
* @author jy
* @date 2014年9月30日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81#pic_sysphoto.EF.BC.9A.E5.BC.B9.E5.87.BA.E7.B3.BB.E7.BB.9F.E6.8B.8D.E7.85.A7.E5.8F.91.E5.9B.BE.E7.9A.84.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81">弹出系统拍照发图的事件推送</a>
*/
public class MenuPhotoEventMessage extends MenuEventMessage {
private static final long serialVersionUID = 3142350663022709730L;
@XStreamAlias("SendPicsInfo")
private PictureInfo pictureInfo;
public PictureInfo getPictureInfo() {
return pictureInfo;
}
public static class PictureInfo {
@XStreamAlias("Count")
private int count;
@XStreamAlias("PicList")
private List<PictureItem> items;
public int getCount() {
return count;
}
public List<PictureItem> getItems() {
return items;
}
@Override
public String toString() {
return "PictureInfo [count=" + count + ", items=" + items + "]";
}
}
@XStreamAlias("item")
public static class PictureItem {
@XStreamAlias("PicMd5Sum")
private String md5;
@Override
public String toString() {
return "PictureItem [md5=" + md5 + "]";
}
}
@Override
public String toString() {
return "MenuPhotoEventMessage [pictureInfo=" + pictureInfo + "]";
}
}
@@ -0,0 +1,51 @@
package com.foxinmy.weixin4j.msg.event.menu;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 扫码推事件(scancode_push|scancode_waitmsg)
*
* @className MenuScanPushEventMessage
* @author jy
* @date 2014年9月30日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81#scancode_push.EF.BC.9A.E6.89.AB.E7.A0.81.E6.8E.A8.E4.BA.8B.E4.BB.B6.E7.9A.84.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81">扫码推事件的事件推送</a>
*/
public class MenuScanEventMessage extends MenuEventMessage {
private static final long serialVersionUID = 3142350663022709730L;
@XStreamAlias("ScanCodeInfo")
private ScanInfo scanInfo;
public ScanInfo getScanInfo() {
return scanInfo;
}
public static class ScanInfo {
@XStreamAlias("ScanType")
private String type;
@XStreamAlias("ScanResult")
private String result;
public String getType() {
return type;
}
public String getResult() {
return result;
}
@Override
public String toString() {
return "ScanInfo [type=" + type + ", result=" + result + "]";
}
}
@Override
public String toString() {
return "MenuScanPushEventMessage [scanInfo=" + scanInfo + "]";
}
}
@@ -0,0 +1 @@
底部菜单消息
@@ -0,0 +1,23 @@
package com.foxinmy.weixin4j.token;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 获取config.properties中的appid&appsecret信息
*
* @className AbstractTokenApi
* @author jy
* @date 2014年10月6日
* @since JDK 1.7
* @see
*/
public abstract class AbstractTokenApi implements TokenApi {
protected String getAppid() {
return ConfigUtil.getValue("app_id");
}
protected String getAppsecret() {
return ConfigUtil.getValue("app_secret");
}
}
@@ -0,0 +1,101 @@
package com.foxinmy.weixin4j.token;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import org.apache.commons.lang3.StringUtils;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.HttpRequest;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.xml.XStream;
/**
* 基于文件保存的Token获取类
*
* @className FileTokenApi
* @author jy.hu
* @date 2014年9月27日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token">获取token说明</a>
* @see com.foxinmy.weixin4j.model.Token
*/
public class FileTokenApi extends AbstractTokenApi {
private final HttpRequest request = new HttpRequest();
private final String appid;
private final String appsecret;
public FileTokenApi() {
this.appid = getAppid();
this.appsecret = getAppsecret();
}
public FileTokenApi(String appid, String appsecret) {
this.appid = appid;
this.appsecret = appsecret;
}
/**
* 获取token
* <p>
* 正常情况下返回{"access_token":"ACCESS_TOKEN","expires_in":7200},否则抛出异常.
* </p>
*
* @return token对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token">获取token说明</a>
* @see com.foxinmy.weixin4j.model.Token
*/
@Override
public Token getToken() throws WeixinException {
if (StringUtils.isBlank(appid) || StringUtils.isBlank(appsecret)) {
throw new IllegalArgumentException(
"appid or appsecret not be null!");
}
XStream xstream = new XStream();
xstream.autodetectAnnotations(true);
xstream.processAnnotations(Token.class);
File token_file = new File(String.format("%s/token_%s.xml",
ConfigUtil.getValue("token_path"), appid));
Token token = null;
Calendar ca = Calendar.getInstance();
long now_time = ca.getTimeInMillis();
try {
String api_token_uri = String.format(
ConfigUtil.getValue("api_token_uri"), appid, appsecret);
Response response = null;
if (token_file.exists()) {
token = (Token) xstream.fromXML(token_file);
long expise_time = token.getTime()
+ (token.getExpiresIn() * 1000) - 3;
if (expise_time > now_time) {
return token;
}
response = request.get(api_token_uri);
} else {
response = request.get(api_token_uri);
try {
token_file.createNewFile();
} catch (IOException e) {
token_file.getParentFile().mkdirs();
}
}
token = response.getAsObject(Token.class);
token.setTime(now_time);
token.setOpenid(appid);
xstream.toXML(token, new FileOutputStream(token_file));
} catch (IOException e) {
;
}
return token;
}
}
@@ -0,0 +1 @@
Token的实现
@@ -0,0 +1,86 @@
package com.foxinmy.weixin4j.token;
import org.apache.commons.lang3.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.HttpRequest;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 基于redis保存的Token获取类
*
* @className RedisTokenApi
* @author jy.hu
* @date 2014年9月27日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token">获取token说明</a>
* @see com.foxinmy.weixin4j.model.Token
*/
public class RedisTokenApi extends AbstractTokenApi {
private final HttpRequest request = new HttpRequest();
private final String appid;
private final String appsecret;
private JedisPool jedisPool;
public RedisTokenApi() {
this.appid = getAppid();
this.appsecret = getAppsecret();
}
public RedisTokenApi(String appid, String appsecret) {
this(appid, appsecret, "localhost", 6379);
}
public RedisTokenApi(String appid, String appsecret, String host, int port) {
this.appid = appid;
this.appsecret = appsecret;
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(50);
poolConfig.setMaxIdle(5);
poolConfig.setMaxWaitMillis(2000);
poolConfig.setTestOnBorrow(false);
poolConfig.setTestOnReturn(true);
this.jedisPool = new JedisPool(poolConfig, host, port);
}
@Override
public Token getToken() throws WeixinException {
if (StringUtils.isBlank(appid) || StringUtils.isBlank(appsecret)) {
throw new IllegalArgumentException(
"appid or appsecret not be null!");
}
Token token = null;
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String key = String.format("token:%s", appid);
String accessToken = jedis.get(key);
if (StringUtils.isBlank(accessToken)) {
String api_token_uri = String.format(
ConfigUtil.getValue("api_token_uri"), appid, appsecret);
token = request.get(api_token_uri).getAsObject(Token.class);
jedis.setex(key, token.getExpiresIn() - 3,
token.getAccessToken());
} else {
token = new Token();
token.setAccessToken(accessToken);
token.setExpiresIn(jedis.ttl(key).intValue());
}
token.setTime(System.currentTimeMillis());
token.setOpenid(appid);
} catch (Exception e) {
jedisPool.returnBrokenResource(jedis);
} finally {
jedisPool.returnResource(jedis);
}
return token;
}
}
@@ -0,0 +1,20 @@
package com.foxinmy.weixin4j.token;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.Token;
/**
* 获取Token接口
*
* @className TokenApi
* @author jy.hu
* @date 2014年9月27日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.model.Token
* @see com.foxinmy.weixin4j.token.AbstractTokenApi
* @see com.foxinmy.weixin4j.token.FileTokenApi
* @see com.foxinmy.weixin4j.token.RedisTokenApi
*/
public interface TokenApi {
public Token getToken() throws WeixinException;
}
@@ -0,0 +1,55 @@
package com.foxinmy.weixin4j.type;
/**
* 自定义菜单类型 <br/>
* <font color="red">scancode_push 到 location_selec
* 仅支持微信iPhone5.4.1以上版本,和Android5 .4以上版本的微信用户 旧版本微信用户点击后将没有回应
* 开发者也不能正常接收到事件推送。</font>
*
* @className ButtonType
* @author jy
* @date 2014年9月30日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%9B%E5%BB%BA%E6%8E%A5%E5%8F%A3">按钮类型</a>
*/
public enum ButtonType {
/**
* 点击推事件 用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event
* 的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
*/
click,
/**
* 跳转URL 用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。
*/
view,
/**
* 扫码推事件 用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,
* 开发者可以下发消息。
*/
scancode_push,
/**
* 扫码推事件且弹出“消息接收中”提示框用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,
* 然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。
*/
scancode_waitmsg,
/**
* 弹出系统拍照发图用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,
* 随后可能会收到开发者下发的消息。
*/
pic_sysphoto,
/**
* 弹出拍照或者相册发图用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。
*/
pic_photo_or_albu,
/**
* 弹出微信相册发图器用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,
* 随后可能会收到开发者下发的消息。
*/
pic_weixin,
/**
* 弹出地理位置选择器用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,
* 随后可能会收到开发者下发的消息。
*/
location_select;
}
@@ -0,0 +1,45 @@
package com.foxinmy.weixin4j.type;
import com.foxinmy.weixin4j.msg.event.EventMessage;
import com.foxinmy.weixin4j.msg.event.LocationEventMessage;
import com.foxinmy.weixin4j.msg.event.MassEventMessage;
import com.foxinmy.weixin4j.msg.event.ScanEventMessage;
import com.foxinmy.weixin4j.msg.event.ScribeEventMessage;
import com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage;
import com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage;
import com.foxinmy.weixin4j.msg.event.menu.MenuLocationEventMessage;
import com.foxinmy.weixin4j.msg.event.menu.MenuPhotoEventMessage;
import com.foxinmy.weixin4j.msg.event.menu.MenuScanEventMessage;
/**
* 事件类型
*
* @className EventType
* @author jy
* @date 2014年9月30日
* @since JDK 1.7
* @see
*/
public enum EventType {
subscribe(ScribeEventMessage.class), unsubscribe(ScribeEventMessage.class), scan(
ScanEventMessage.class), scancode_push(MenuScanEventMessage.class), view(
MenuEventMessage.class), scancode_waitmsg(
MenuScanEventMessage.class), pic_sysphoto(
MenuPhotoEventMessage.class), pic_photo_or_album(
MenuPhotoEventMessage.class), pic_weixin(
MenuPhotoEventMessage.class), location_select(
MenuLocationEventMessage.class), click(MenuEventMessage.class), location(
LocationEventMessage.class), massendjobfinish(
MassEventMessage.class), templatesendjobfinish(
TemplatesendjobfinishMessage.class);
private Class<? extends EventMessage> eventClass;
EventType(Class<? extends EventMessage> eventClass) {
this.eventClass = eventClass;
}
public Class<? extends EventMessage> getEventClass() {
return eventClass;
}
}
@@ -0,0 +1,40 @@
package com.foxinmy.weixin4j.type;
/**
* 上传的媒体类型<br/>
* 图片(image): 128K,支持JPG格式<br/>
* 语音(voice):256K,播放长度不超过60s,支持AMR\MP3格式<br/>
* 视频(video):1MB,支持MP4格式<br/>
* 缩略图(thumb):64KB,支持JPG格式<br/>
* <p>
* <font color='red'>媒体文件在后台保存时间为3天,即3天后media_id失效</font>
* <p/>
*
* @author jy.hu
* @date 2014年4月2日
* @since JDK 1.7
*/
public enum MediaType {
image("image/jpeg", "jpg"), voice("binary/octet-stream", "amr"), vedio(
"binary/octet-stream", "mp4"), thumb("image/jpeg", "jpg"), text(
"application/json", ""), music("binary/octet-stream", "mp3"), news(
"application/json", ""), mpnews("application/json", ""), mpvideo(
"binary/octet-stream", "");
MediaType(String contentType, String formatType) {
this.contentType = contentType;
this.formatType = formatType;
}
private String contentType;
public String getContentType() {
return contentType;
}
private String formatType;
public String getFormatType() {
return formatType;
}
}
@@ -0,0 +1,37 @@
package com.foxinmy.weixin4j.type;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.msg.ImageMessage;
import com.foxinmy.weixin4j.msg.LinkMessage;
import com.foxinmy.weixin4j.msg.LocationMessage;
import com.foxinmy.weixin4j.msg.TextMessage;
import com.foxinmy.weixin4j.msg.VideoMessage;
import com.foxinmy.weixin4j.msg.VoiceMessage;
import com.foxinmy.weixin4j.msg.event.EventMessage;
/**
*
* 消息类型
*
* @author jy.hu
*
*/
public enum MessageType {
text(TextMessage.class), image(ImageMessage.class), voice(
VoiceMessage.class), video(VideoMessage.class), location(
LocationMessage.class), link(LinkMessage.class), event(
EventMessage.class), signature(null);
private Class<? extends BaseMessage> messageClass;
MessageType(Class<? extends BaseMessage> messageClass) {
this.messageClass = messageClass;
}
public void setMessageClass(Class<? extends BaseMessage> messageClass) {
this.messageClass = messageClass;
}
public Class<? extends BaseMessage> getMessageClass() {
return messageClass;
}
}
@@ -0,0 +1 @@
消息类型、事件类型等枚举
@@ -0,0 +1,48 @@
package com.foxinmy.weixin4j.util;
import java.io.File;
import java.io.FilenameFilter;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
public class ClassUtil {
public static Set<Class<?>> getClasses(Package _package) {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
String subPath = _package.getName().replace(".", File.separator);
URL fullPath = classLoader.getResource(subPath);
File dir = new File(fullPath.getPath());
return findClasses(dir, _package.getName());
}
private static Set<Class<?>> findClasses(File dir, String packageName) {
Set<Class<?>> classes = new HashSet<Class<?>>();
File[] files = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file, String name) {
return file.isDirectory() || file.getName().endsWith(".class");
}
});
for (File file : files) {
if (file.isDirectory()) {
classes.addAll(findClasses(file,
packageName + "." + file.getName()));
} else {
try {
Class<?> clazz = Class.forName(packageName + "."
+ file.getName().replace(".class", ""));
if (clazz.isInterface()) {
continue;
}
classes.add(clazz);
} catch (ClassNotFoundException e) {
;
}
}
}
return classes;
}
}
@@ -0,0 +1,43 @@
package com.foxinmy.weixin4j.util;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ConfigUtil {
public ConfigUtil() {
}
private static Properties props = new Properties();
static {
try {
props.load(Thread.currentThread().getContextClassLoader()
.getResourceAsStream("weixin.properties"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getValue(String key) {
String url = props.getProperty(key);
Pattern p = Pattern.compile("(\\{[^\\}]*\\})");
Matcher m = p.matcher(url);
StringBuffer sb = new StringBuffer();
String sub = null;
while (m.find()) {
sub = m.group();
m.appendReplacement(sb,
getValue(sub.substring(1, sub.length() - 1)));
}
m.appendTail(sb);
return sb.toString();
}
public static void main(String[] args) {
System.out.println(getValue("api_token_uri"));
}
}
@@ -0,0 +1,67 @@
package com.foxinmy.weixin4j.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
/**
* IOUtil
*
* @className IOUtil
* @author jy.hu
* @date 2014年9月22日
* @since JDK 1.7
* @see org.apache.commons.io.IOUtils
*/
public class IOUtil {
private static final int EOF = -1;
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
public static byte[] toByteArray(Reader input) throws IOException {
return toByteArray(input, Charset.defaultCharset());
}
public static byte[] toByteArray(Reader input, Charset encoding) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
copy(input, output, encoding);
return output.toByteArray();
}
public static void copy(Reader input, OutputStream output, Charset encoding) throws IOException {
OutputStreamWriter out = new OutputStreamWriter(output, encoding);
copyLarge(input, out, new char[DEFAULT_BUFFER_SIZE]);
out.flush();
}
public static byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
copyLarge(input, output, new byte[DEFAULT_BUFFER_SIZE]);
return output.toByteArray();
}
private static long copyLarge(InputStream input, OutputStream output, byte[] buffer) throws IOException {
long count = 0;
int n = 0;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
private static long copyLarge(Reader input, Writer output, char[] buffer) throws IOException {
long count = 0;
int n = 0;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
}
@@ -0,0 +1,155 @@
package com.foxinmy.weixin4j.util;
import java.io.InputStream;
import java.util.Arrays;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
import com.foxinmy.weixin4j.xml.XStream;
public class MessageUtil {
private final static Logger log = LoggerFactory
.getLogger(MessageUtil.class);
/**
* 验证微信签名
*
* @param token
* 开发者填写的token
* @param echostr
* 随机字符串
* @param timestamp
* 时间戳
* @param nonce
* 随机数
* @param signature
* 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数
* @return 开发者通过检验signature对请求进行相关校验。若确认此次GET请求来自微信服务器
* 请原样返回echostr参数内容,则接入生效 成为开发者成功,否则接入失败
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97">接入指南</a>
*/
public static String signature(String token, String echostr,
String timestamp, String nonce, String signature) {
if (StringUtils.isBlank(token)) {
log.error("signature fail : token is null!");
return null;
}
if (StringUtils.isBlank(echostr) || StringUtils.isBlank(timestamp)
|| StringUtils.isBlank(nonce)) {
log.error("signature fail : invalid parameter!");
return null;
}
String _signature = null;
try {
String[] a = { token, timestamp, nonce };
Arrays.sort(a);
StringBuilder sb = new StringBuilder(3);
for (String str : a) {
sb.append(str);
}
_signature = DigestUtils.sha1Hex(sb.toString());
} catch (Exception e) {
log.error("signature error", e);
}
if (signature.equals(_signature)) {
return echostr;
} else {
log.error("signature fail : invalid signature!");
return null;
}
}
/**
* 获取对应的mapping key
*
* @param xmlMsg
* @return
* @throws DocumentException
* @see com.foxinmy.weixin4j.server.WeixinActionMapping
*/
public static String getMappingKey(String xmlMsg) throws DocumentException {
Document doc = DocumentHelper.parseText(xmlMsg);
String msgType = doc.selectSingleNode("/xml/MsgType").getStringValue();
if (msgType.equalsIgnoreCase(MessageType.event.name())) {
msgType += "_"
+ doc.selectSingleNode("/xml/Event").getStringValue();
}
return msgType;
}
/**
* xml消息转换为消息对象
*
* @param xmlMsg
* 消息字符串
* @return 消息对象
* @throws DocumentException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E9%AA%8C%E8%AF%81%E6%B6%88%E6%81%AF%E7%9C%9F%E5%AE%9E%E6%80%A7">验证消息的合法性</a>
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF">普通消息</a>
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81">事件触发</a>
* @see com.foxinmy.weixin4j.type.MessageType
* @see com.feican.weixin.msg.BaeMessage
* @see com.foxinmy.weixin4j.msg.TextMessage
* @see com.foxinmy.weixin4j.msg.ImageMessage
* @see com.foxinmy.weixin4j.msg.VoiceMessage
* @see com.foxinmy.weixin4j.msg.VideoMessage
* @see com.foxinmy.weixin4j.msg.LocationMessage
* @see com.foxinmy.weixin4j.msg.LinkMessage
* @see com.foxinmy.weixin4j.msg.event.ScribeEventMessage
* @see com.foxinmy.weixin4j.msg.event.ScanEventMessage
* @see com.foxinmy.weixin4j.msg.event.LocationEventMessage
* @see com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage
*/
public static BaseMessage xml2msg(String xmlMsg) throws DocumentException {
Document doc = DocumentHelper.parseText(xmlMsg);
String type = doc.selectSingleNode("/xml/MsgType").getStringValue();
if (StringUtils.isBlank(type)) {
return null;
}
MessageType messageType = MessageType.valueOf(type.toLowerCase());
Class<? extends BaseMessage> messageClass = messageType
.getMessageClass();
if (messageType == MessageType.event) {
type = doc.selectSingleNode("/xml/Event").getStringValue();
messageClass = EventType.valueOf(type.toLowerCase())
.getEventClass();
}
XStream xstream = new XStream();
xstream.ignoreUnknownElements();
xstream.autodetectAnnotations(true);
xstream.processAnnotations(messageClass);
xstream.alias("xml", messageClass);
return xstream.fromXML(xmlMsg, messageClass);
}
/**
* xml消息转换为消息对象
*
* @param inputStream
* 带消息字符串的输入流
* @return 消息对象
* @throws DocumentException
* @see {@link com.foxinmy.weixin4j.util.MessageUtil#xml2msg(String)}
*/
public static BaseMessage xml2msg(InputStream inputStream)
throws DocumentException {
SAXReader reader = new SAXReader();
Document doc = reader.read(inputStream);
return xml2msg(doc.asXML());
}
}
@@ -0,0 +1,76 @@
package com.foxinmy.weixin4j.util;
import java.util.Random;
import java.util.UUID;
/**
* 随机码工具类
* @className RandomUtil
* @author jy
* @date 2014年10月22日
* @since JDK 1.7
* @see
*/
public class RandomUtil {
private static final String ALLCHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String LETTERCHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String NUMBERCHAR = "0123456789";
// 返回一个定长的随机字符串(包含数字和大小写字母)
public static String generateString(int length) {
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
}
return sb.toString();
}
// 返回一个定长的随机纯数字字符串(只包含数字)
public static String generateStringByNumberChar(int length) {
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(NUMBERCHAR.charAt(random.nextInt(NUMBERCHAR.length())));
}
return sb.toString();
}
// 返回一个定长的随机纯字母字符串(只包含大小写字母)
public static String generateStringByLetterCharr(int length) {
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(LETTERCHAR.charAt(random.nextInt(LETTERCHAR.length())));
}
return sb.toString();
}
// 返回一个定长的随机纯大写字母字符串(只包含大小写字母)
public static String generateLowerString(int length) {
return generateStringByLetterCharr(length).toLowerCase();
}
// 返回一个定长的随机纯小写字母字符串(只包含大小写字母)
public static String generateUpperString(int length) {
return generateStringByLetterCharr(length).toUpperCase();
}
/**
* 随机获取UUID字符串(无中划线)
*
* @return UUID字符串
*/
public static String getUUID() {
String uuid = UUID.randomUUID().toString();
return uuid.substring(0, 8) + uuid.substring(9, 13)
+ uuid.substring(14, 18) + uuid.substring(19, 23)
+ uuid.substring(24);
}
public static void main(String[] args) {
System.out.println(System.nanoTime());
System.out.println(System.currentTimeMillis());
}
}
@@ -0,0 +1 @@
添加<![CDATA[]>支持
@@ -0,0 +1,44 @@
package com.foxinmy.weixin4j.xml;
import java.io.InputStream;
import java.io.Writer;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.Xpp3Driver;
public class XStream extends com.thoughtworks.xstream.XStream {
public XStream() {
super(new Xpp3Driver() {
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
@Override
protected void writeText(QuickWriter writer, String text) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
}
};
}
});
}
public XStream(HierarchicalStreamDriver hierarchicalStreamDriver) {
super(hierarchicalStreamDriver);
}
@SuppressWarnings("unchecked")
public <T> T fromXML(String xml, Class<T> t) {
return (T) super.fromXML(xml);
}
@SuppressWarnings("unchecked")
public <T> T fromXML(InputStream inputStream, Class<T> t) {
return (T) super.fromXML(inputStream);
}
}