first commit

This commit is contained in:
jy.hu 2014-05-05 23:09:34 +08:00
parent 4bb2115805
commit 6786d7888e
61 changed files with 5302 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
ass
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# eclipse ignore
*.settings/*
/.project
/.classpath
/.tomcatplugin
# maven ignore
target/*
# other ignore
*.log
*.tmp
Thumbs.db

189
pom.xml Normal file
View File

@ -0,0 +1,189 @@
<?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>com.foxinmy.weixin</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>weixin4j-sdk</artifactId>
<name>weixin4j-sdk</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
<version>3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>${xstream.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons.codec.version}</version>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>${commons.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>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>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>${jaxen.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>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.7.3</version>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<id>releases</id>
<name>Internal Releases</name>
<url>http://42.62.64.62:8081/nexus/content/repositories/releases/
</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>Internal Snapshots</name>
<url>http://42.62.64.62:8081/nexus/content/repositories/snapshots/
</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
<id>public</id>
<name>Public Repositories</name>
<url>http://42.62.64.62:8081/nexus/content/groups/public/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>public</id>
<name>Public Repositories</name>
<url>http://42.62.64.62:8081/nexus/content/groups/public/</url>
</pluginRepository>
</pluginRepositories>
<properties>
<junit.version>4.8.2</junit.version>
<dom4j.version>1.6.1</dom4j.version>
<logback.version>1.0.9</logback.version>
<jcl.over.version>1.7.6</jcl.over.version>
<log4j.over.version>1.7.6</log4j.over.version>
<xstream.version>1.4.7</xstream.version>
<commons.httpclient.version>3.1</commons.httpclient.version>
<commons.codec.version>1.9</commons.codec.version>
<fastjson.version>1.1.9</fastjson.version>
<jaxen.version>1.1.4</jaxen.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<maven.test.skip>true</maven.test.skip>
</properties>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,912 @@
package com.foxinmy.weixin4j;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
import org.apache.commons.httpclient.methods.multipart.FilePart;
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.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.HttpRequest;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.Following;
import com.foxinmy.weixin4j.model.Group;
import com.foxinmy.weixin4j.model.MpArticle;
import com.foxinmy.weixin4j.model.QRParameter;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.User;
import com.foxinmy.weixin4j.model.UserToken;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.msg.notify.BaseNotify;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MediaType;
import com.foxinmy.weixin4j.type.MessageType;
import com.foxinmy.weixin4j.util.WeixinConfig;
import com.foxinmy.weixin4j.util.WeixinUtil;
import com.foxinmy.weixin4j.xml.XStream;
/**
* 微信服务实现
*
* @className WeixinServiceImpl
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php">api文档</a>
*/
public class WeixinProxy {
private final Logger log = LoggerFactory.getLogger(getClass());
private HttpRequest request = new HttpRequest();
/**
* 验证微信签名
*
* @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 String signature(String echostr, String timestamp, String nonce,
String signature) {
String app_token = WeixinConfig.getValue("app_token");
if (WeixinUtil.isBlank(app_token)) {
log.error("signature fail : token is null!");
return null;
}
if (WeixinUtil.isBlank(echostr) || WeixinUtil.isBlank(timestamp)
|| WeixinUtil.isBlank(nonce)) {
log.error("signature fail : invalid parameter!");
return null;
}
String _signature = null;
try {
String[] a = { app_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;
}
}
/**
* xml消息转换为消息对象
* <p>
* 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次
* </p>
*
* @param xml
* 消息字符串
* @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.in.ImageMessage
* @see com.foxinmy.weixin4j.msg.in.VoiceMessage
* @see com.foxinmy.weixin4j.msg.in.VideoMessage
* @see com.foxinmy.weixin4j.msg.in.LocationMessage
* @see com.foxinmy.weixin4j.msg.in.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.MenuEventMessage
*/
public BaseMessage xml2msg(String xmlStr) throws DocumentException {
if (WeixinUtil.isBlank(xmlStr))
return null;
Document doc = DocumentHelper.parseText(xmlStr);
String type = doc.selectSingleNode("/xml/MsgType").getStringValue();
if (WeixinUtil.isBlank(type)) {
return null;
}
XStream xstream = new XStream();
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.alias("xml", messageClass);
xstream.ignoreUnknownElements();
xstream.autodetectAnnotations(true);
xstream.processAnnotations(messageClass);
return xstream.fromXML(doc.asXML(), messageClass);
}
/**
* xml消息转换为消息对象
*
* @param inputStream
* 带消息字符串的输入流
* @return 消息对象
* @throws DocumentException
* @see {@link com.foxinmy.weixin4j.WeixinProxy#xml2msg(String)}
*/
public BaseMessage xml2msg(InputStream inputStream)
throws DocumentException {
SAXReader reader = new SAXReader();
Document doc = reader.read(inputStream);
return xml2msg(doc.asXML());
}
/**
* 获取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
*/
public Token getToken() throws WeixinException {
XStream xstream = new XStream();
xstream.autodetectAnnotations(true);
xstream.processAnnotations(Token.class);
String appOpenId = WeixinConfig.getValue("app_openId");
String token_path = WeixinConfig.getValue("token_path");
File token_file = new File(String.format("%s/%s_token.xml", token_path,
appOpenId));
Token token = null;
Calendar ca = Calendar.getInstance();
long now_time = ca.getTimeInMillis();
try {
if (token_file.exists()) {
token = (Token) xstream.fromXML(token_file);
long expise_time = token.getTime()
+ (token.getExpires_in() * 1000) - 5;
if (expise_time > now_time) {
return token;
}
} else {
try {
token_file.createNewFile();
} catch (IOException e) {
token_file.getParentFile().mkdirs();
}
}
token_path = WeixinConfig.getValue("api_token_uri");
Response response = request.get(token_path);
token = response.getAsObject(Token.class);
token.setTime(now_time);
token.setOpenid(appOpenId);
xstream.toXML(token, new FileOutputStream(token_file));
} catch (IOException e) {
log.error("获取token出错", e);
}
return token;
}
/**
* 获取token
*
* @param code
* 用户授权后返回的code
* @return token对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E4.BA.8C.E6.AD.A5.EF.BC.9A.E9.80.9A.E8.BF.87code.E6.8D.A2.E5.8F.96.E7.BD.91.E9.A1.B5.E6.8E.88.E6.9D.83access_token">获取用户token</a>
* @see com.foxinmy.weixin4j.model.UserToken
*/
public UserToken getAccessToken(String code) throws WeixinException {
String user_token_uri = WeixinConfig.getValue("sns_user_token_uri");
Response response = request.get(String.format(user_token_uri, code));
// 暂时不保存用户token
return response.getAsObject(UserToken.class);
}
/**
* 获取用户信息
*
* @param token
* 授权票据
* @return 用户对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E5.9B.9B.E6.AD.A5.EF.BC.9A.E6.8B.89.E5.8F.96.E7.94.A8.E6.88.B7.E4.BF.A1.E6.81.AF.28.E9.9C.80scope.E4.B8.BA_snsapi_userinfo.29">拉取用户信息</a>
* @see com.foxinmy.weixin4j.model.User
* @see com.foxinmy.weixin4j.model.UserToken
* {@link com.foxinmy.weixin4j.WeixinProxy#getAccessToken(String)}
*/
public User getUser(UserToken token) throws WeixinException {
String user_info_uri = WeixinConfig.getValue("sns_user_info_uri");
Response response = request.get(String.format(user_info_uri,
token.getAccess_token(), token.getOpenid()));
return response.getAsObject(User.class);
}
/**
* 获取用户信息
* <p>
* 在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID加密后的微信号,每个用户对每个公众号的OpenID是唯一的,对于不同公众号,
* 同一用户的openid不同,公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称头像性别所在城市语言和关注时间
* </p>
*
* @param openId
* 用户对应的ID
* @return 用户对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF">获取用户信息</a>
* @see com.foxinmy.weixin4j.model.User
*/
public User getUser(String openId) throws WeixinException {
String user_info_uri = WeixinConfig.getValue("api_user_info_uri");
Token token = getToken();
Response response = request.get(String.format(user_info_uri,
token.getAccess_token(), openId));
return response.getAsObject(User.class);
}
/**
* 生成带参数的二维码
*
* @param parameter
* @return byte数据包
* @throws WeixinException
* @see {@link com.foxinmy.weixin4j.WeixinProxy#getQR(QRParameter)}
*/
public byte[] getQRData(QRParameter parameter) throws WeixinException {
Token token = getToken();
String qr_uri = WeixinConfig.getValue("qr_ticket_uri");
Response response = null;
try {
response = request.post(String.format(qr_uri,
token.getAccess_token()),
new StringRequestEntity(parameter.toJson(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String ticket = response.getAsJson().getString("ticket");
qr_uri = WeixinConfig.getValue("qr_image_uri");
response = request.get(String.format(qr_uri, ticket));
return response.getBody();
}
/**
* 生成带参数的二维码
* <p>
* 二维码分为临时跟永久两种,扫描时触发推送带参数事件
* </p>
*
* @param parameter
* 二维码参数
* @return 硬盘存储的文件对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%94%9F%E6%88%90%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84%E4%BA%8C%E7%BB%B4%E7%A0%81">二维码</a>
* @see com.foxinmy.weixin4j.model.QRParameter
*/
public File getQR(QRParameter parameter) throws WeixinException {
String qr_path = WeixinConfig.getValue("qr_path");
String filename = String
.format("%s_%d_%d.jpg", parameter.getQrType().name(),
parameter.getScene_id(), parameter.getExpire_seconds());
File file = new File(qr_path + File.separator + filename);
if (file.exists()) {
return file;
}
FileOutputStream out = null;
try {
try {
file.createNewFile();
} catch (IOException e) {
file.getParentFile().mkdirs();
file.createNewFile();
}
out = new FileOutputStream(file);
byte[] b = getQRData(parameter);
out.write(b);
} catch (IOException e) {
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
}
}
return file;
}
/**
* 上传媒体文件
* <p>
* 正常情况下返回{"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789},
* 否则抛出异常.
* </p>
*
* @param file
* 文件对象
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传下载说明</a>
* @see com.foxinmy.weixin4j.type.MediaType
*/
public String uploadMedia(File file, MediaType mediaType)
throws WeixinException {
byte[] b = null;
ByteArrayOutputStream out = null;
FileInputStream in = null;
try {
b = new byte[4096];
out = new ByteArrayOutputStream();
in = new FileInputStream(file);
int len = -1;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
b = out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
return uploadMedia(file.getName(), b, mediaType);
}
/**
* 上传媒体文件
*
* @param bytes
* 媒体数据包
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @throws WeixinException
* @see {@link com.foxinmy.weixin4j.WeixinProxy#uploadMedia(File, MediaType)}
*/
public String uploadMedia(String fileName, byte[] bytes, MediaType mediaType)
throws WeixinException {
Token token = getToken();
String file_upload_uri = WeixinConfig.getValue("file_upload_uri");
Response response = null;
response = request.post(String.format(file_upload_uri,
token.getAccess_token(), mediaType.name()), new FilePart(
"media", new ByteArrayPartSource(fileName, bytes), null,
"UTF-8"));
return response.getAsJson().getString("media_id");
}
/**
* 下载媒体文件
* <p>
* 正常情况下返回表头如Content-Type: image/jpeg,否则抛出异常.
* </p>
*
* @param mediaId
* 存储在微信服务器上的媒体标识
* @param mediaType
* 媒体类型
* @return 写入硬盘后的文件对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传下载说明</a>
* @see com.foxinmy.weixin4j.type.MediaType
*/
public File downloadMedia(String mediaId, MediaType mediaType)
throws WeixinException {
String media_path = WeixinConfig.getValue("media_path");
String filename = mediaId + mediaType.getFormatType();
File file = new File(media_path + File.separator + filename);
if (file.exists()) {
return file;
}
FileOutputStream out = null;
try {
try {
file.createNewFile();
} catch (IOException e) {
file.getParentFile().mkdirs();
file.createNewFile();
}
out = new FileOutputStream(file);
byte[] b = downloadMediaData(mediaId, mediaType);
out.write(b);
} catch (IOException e) {
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
}
}
return file;
}
/**
* 下载媒体文件
*
* @param mediaId
* @param mediaType
* @return 二进制数据包
* @throws WeixinException
* @see {@link com.foxinmy.weixin4j.WeixinProxy#downloadMedia(String, MediaType)}
*/
public byte[] downloadMediaData(String mediaId, MediaType mediaType)
throws WeixinException {
Token token = getToken();
String file_download_uri = WeixinConfig.getValue("file_download_uri");
Response response = request.get(String.format(file_download_uri,
token.getAccess_token(), mediaId));
return response.getBody();
}
/**
* 发送客服消息(在48小时内不限制发送次数)
*
* @param notify
* 客服消息对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF">发送客服消息</a>
*/
public void sendNotify(BaseNotify notify) throws WeixinException {
String custom_notify_uri = WeixinConfig.getValue("custom_notify_uri");
Token token = getToken();
try {
request.post(String.format(custom_notify_uri,
token.getAccess_token(), notify.getTouser()),
new StringRequestEntity(notify.toJson(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 创建分组
*
* @param name
* 组名称
* @return group对象
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E5.88.9B.E5.BB.BA.E5.88.86.E7.BB.84">创建分组</a>
* @see com.foxinmy.weixin4j.model.Group
* @see com.foxinmy.weixin4j.model.Group#toCreateJson()
*/
public Group createGroup(String name) throws WeixinException {
String group_create_uri = WeixinConfig.getValue("group_create_uri");
Token token = getToken();
Response response = null;
Group group = new Group(name);
try {
response = request.post(String.format(group_create_uri,
token.getAccess_token()),
new StringRequestEntity(group.toCreateJson(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
JSONObject obj = response.getAsJson();
return JSON.parseObject(obj.getString("group"), Group.class);
}
/**
* 查询所有分组
*
* @return 组集合
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E6.9F.A5.E8.AF.A2.E6.89.80.E6.9C.89.E5.88.86.E7.BB.84">查询所有分组</a>
* @see com.foxinmy.weixin4j.model.Group
*/
public List<Group> getGroups() throws WeixinException {
String group_get_uri = WeixinConfig.getValue("group_get_uri");
Token token = getToken();
Response response = request.get(String.format(group_get_uri,
token.getAccess_token()));
JSONObject obj = response.getAsJson();
return JSON.parseArray(obj.getString("groups"), Group.class);
}
/**
* 查询用户所在分组
*
* @param openId
* 用户对应的ID
* @return 组ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E6.9F.A5.E8.AF.A2.E7.94.A8.E6.88.B7.E6.89.80.E5.9C.A8.E5.88.86.E7.BB.84">查询用户所在分组</a>
* @see com.foxinmy.weixin4j.model.Group
*/
public int findGroupByOpenId(String openId) throws WeixinException {
String group_getid_uri = WeixinConfig.getValue("group_getid_uri");
Token token = getToken();
Response response = null;
try {
response = request.post(
String.format(group_getid_uri, token.getAccess_token()),
new StringRequestEntity(String.format(
"{\"openid\":\"%s\"}", openId), "application/json",
"UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return response.getAsJson().getIntValue("groupid");
}
/**
* 修改分组名
*
* @param groupId
* 组ID
* @param name
* 组名称
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E4.BF.AE.E6.94.B9.E5.88.86.E7.BB.84.E5.90.8D">修改分组名</a>
* @see com.foxinmy.weixin4j.model.Group
* @see com.foxinmy.weixin4j.model.Group#toModifyJson()
*/
public void modifyGroup(int groupId, String name) throws WeixinException {
String group_modify_uri = WeixinConfig.getValue("group_modify_uri");
Token token = getToken();
Group group = new Group(groupId, name);
try {
request.post(String.format(group_modify_uri,
token.getAccess_token()),
new StringRequestEntity(group.toModifyJson(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 移动分组
*
* @param openId
* 用户对应的ID
* @param groupId
* 组ID
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3#.E7.A7.BB.E5.8A.A8.E7.94.A8.E6.88.B7.E5.88.86.E7.BB.84">移动分组</a>
* @see com.foxinmy.weixin4j.model.Group
*/
public void moveGroup(String openId, int groupId) throws WeixinException {
String group_move_uri = WeixinConfig.getValue("group_move_uri");
Token token = getToken();
try {
request.post(
String.format(group_move_uri, token.getAccess_token()),
new StringRequestEntity(String.format(
"{\"openid\":\"%s\",\"to_groupid\":%d}", openId,
groupId), "application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 获取用户一定数量(10000)的关注者列表
*
* @param nextOpenId
* 下一次拉取数据的openid
* @return 关注信息
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8">获取关注者列表</a>
* @see com.foxinmy.weixin4j.model.Following
*/
public Following getFollowing(String nextOpenId) throws WeixinException {
String fllowing_uri = WeixinConfig.getValue("following_uri");
Token token = getToken();
Response response = request.get(String.format(fllowing_uri,
token.getAccess_token(), nextOpenId == null ? "" : nextOpenId));
Following following = response.getAsObject(Following.class);
if (following.getCount() > 0) {
List<String> openIds = JSON.parseArray(following.getDataJson()
.getString("openid"), String.class);
List<User> userList = new ArrayList<User>();
for (String openId : openIds) {
userList.add(getUser(openId));
}
following.setUserList(userList);
}
return following;
}
/**
* 获取用户全部的关注者列表
* <p>
* 当公众号关注者数量超过10000时,可通过填写next_openid的值,从而多次拉取列表的方式来满足需求,
* 将上一次调用得到的返回中的next_openid值,作为下一次调用中的next_openid值
* </p>
*
* @return 用户对象集合
* @throws WeixinException
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8">获取关注者列表</a>
* @see com.foxinmy.weixin4j.model.Following
* @see com.foxinmy.weixin4j.WeixinProxy#getFollowing(String)
*/
public List<User> getAllFollowing() throws WeixinException {
List<User> userList = new ArrayList<User>();
String nextOpenId = null;
Following f = null;
for (;;) {
f = getFollowing(nextOpenId);
if (f.getCount() == 0) {
break;
}
userList.addAll(f.getUserList());
nextOpenId = f.getNextOpenId();
}
return userList;
}
/**
* 自定义菜单
*
* @param btnList
* @throws WeixinException
* @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>
* @see com.foxinmy.weixin4j.model.Button
*/
public void createMenu(List<Button> btnList) throws WeixinException {
String menu_create_uri = WeixinConfig.getValue("menu_create_uri");
Token token = getToken();
try {
JSONObject obj = new JSONObject();
obj.put("button", btnList);
request.post(
String.format(menu_create_uri, token.getAccess_token()),
new StringRequestEntity(obj.toJSONString(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 查询菜单
*
* @return 菜单集合
* @throws WeixinException
* @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%E6%9F%A5%E8%AF%A2%E6%8E%A5%E5%8F%A3">查询菜单</a>
* @see com.foxinmy.weixin4j.model.Button
*/
public List<Button> getMenu() throws WeixinException {
String menu_get_uri = WeixinConfig.getValue("menu_get_uri");
Token token = getToken();
Response response = request.get(String.format(menu_get_uri,
token.getAccess_token()));
String text = response.getAsJson().getJSONObject("menu")
.getString("button");
return JSON.parseArray(text, Button.class);
}
/**
* 删除菜单
*
* @throws WeixinException
* @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%A0%E9%99%A4%E6%8E%A5%E5%8F%A3">删除菜单</a>
* @see com.foxinmy.weixin4j.model.Button
*/
public void deleteMenu() throws WeixinException {
String menu_delete_uri = WeixinConfig.getValue("menu_delete_uri");
Token token = getToken();
request.get(String.format(menu_delete_uri, token.getAccess_token()));
}
/**
* 上传图文消息,一个图文消息支持1到10条图文
*
* @param articles
* 图片消息
* @return 媒体ID
* @throws WeixinException
* @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">高级群发</a>
* @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.B8.8A.E4.BC.A0.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF.E7.B4.A0.E6.9D.90">上传图文消息</a>
* @see com.foxinmy.weixin4j.model.MpArticle
*/
public String uploadNews(List<MpArticle> articles) throws WeixinException {
String news_upload_uri = WeixinConfig.getValue("news_upload_uri");
Token token = getToken();
Response response = null;
try {
JSONObject obj = new JSONObject();
obj.put("articles", articles);
response = request.post(String.format(news_upload_uri,
token.getAccess_token()),
new StringRequestEntity(obj.toJSONString(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return response.getAsJson().getString("media_id");
}
/**
* 分组群发
* <p>
* 在返回成功时,意味着群发任务提交成功,并不意味着此时群发已经结束,所以,仍有可能在后续的发送过程中出现异常情况导致用户未收到消息,
* 如消息有时会进行审核服务器不稳定等,此外,群发任务一般需要较长的时间才能全部发送完毕
* </p>
*
* @param articles
* 图文消息列表
* @param groupId
* 分组ID
* @return 发送出去的消息ID
* @throws WeixinException
* @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#.E6.A0.B9.E6.8D.AE.E5.88.86.E7.BB.84.E8.BF.9B.E8.A1.8C.E7.BE.A4.E5.8F.91">分组群发</a>
* @see com.foxinmy.weixin4j.model.MpArticle
* @see com.foxinmy.weixin4j.model.Group
* @see {@link com.foxinmy.weixin4j.WeixinProxy#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.WeixinProxy#uploadNews(List)}
*/
public String massNewsByGroup(List<MpArticle> articles, String groupId)
throws WeixinException {
String mediaId = uploadNews(articles);
JSONObject obj = new JSONObject();
obj.put("filter", new JSONObject().put("group_id", groupId));
obj.put("mpnews", new JSONObject().put("media_id", mediaId));
obj.put("msgtype", "mpnews");
String mass_group_uri = WeixinConfig.getValue("mass_group_uri");
Token token = getToken();
Response response = null;
try {
response = request.post(String.format(mass_group_uri,
token.getAccess_token()),
new StringRequestEntity(obj.toJSONString(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return response.getAsJson().getString("msg_id");
}
/**
* openId群发
*
* @param articles
* 图文消息列表
* @param openIds
* 目标ID列表
* @return 发送出去的消息ID
* @throws WeixinException
* @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#.E6.A0.B9.E6.8D.AEOpenID.E5.88.97.E8.A1.A8.E7.BE.A4.E5.8F.91">openId群发</a>
* @see com.foxinmy.weixin4j.model.MpArticle
* @see com.foxinmy.weixin4j.model.User
* @see {@link com.foxinmy.weixin4j.WeixinProxy#uploadMedia(File, MediaType)}
* @see {@link com.foxinmy.weixin4j.WeixinProxy#uploadNews(List)}
*/
public String massNewsByOpenIds(List<MpArticle> articles, String... openIds)
throws WeixinException {
String mediaId = uploadNews(articles);
JSONObject obj = new JSONObject();
obj.put("touser", openIds);
obj.put("mpnews", new JSONObject().put("media_id", mediaId));
obj.put("msgtype", "mpnews");
String mass_openid_uri = WeixinConfig.getValue("mass_openid_uri");
Token token = getToken();
Response response = null;
try {
response = request.post(String.format(mass_openid_uri,
token.getAccess_token()),
new StringRequestEntity(obj.toJSONString(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return response.getAsJson().getString("msg_id");
}
/**
* 删除群发消息
* <p>
* 请注意,只有已经发送成功的消息才能删除删除消息只是将消息的图文详情页失效,已经收到的用户还是能在其本地看到消息卡片
* </p>
*
* @param msgid
* 发送出去的消息ID
* @throws WeixinException
* @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#.E5.88.A0.E9.99.A4.E7.BE.A4.E5.8F.91">删除群发</a>
* @see {@link com.foxinmy.weixin4j.WeixinProxy#massNewsByGroup(List, String)}
* @see {@link com.foxinmy.weixin4j.WeixinProxy#massNewsByOpenIds(List, String...)
*/
public void deleteMassNews(String msgid) throws WeixinException {
JSONObject obj = new JSONObject();
obj.put("msgid", msgid);
String mass_delete_uri = WeixinConfig.getValue("mass_delete_uri");
Token token = getToken();
try {
request.post(
String.format(mass_delete_uri, token.getAccess_token()),
new StringRequestEntity(obj.toJSONString(),
"application/json", "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,38 @@
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 this.errorCode;
}
public String getErrorMsg() {
return this.errorMsg;
}
}

View File

@ -0,0 +1,133 @@
package com.foxinmy.weixin4j.http;
import java.io.IOException;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
public class HttpRequest {
private final String CONTENT_CHARSET = "UTF-8";
private final String ERROR_CODE_KEY = "errcode";
private final String ERROR_MSG_KEY = "errmsg";
private HttpClient client;
public HttpRequest() {
this(150, 30000, 30000, 1024 * 1024);
}
public HttpRequest(int maxConPerHost, int conTimeOutMs, int soTimeOutMs, int maxSize) {
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
connectionManager = new MultiThreadedHttpConnectionManager();
HttpConnectionManagerParams params = connectionManager.getParams();
params.setDefaultMaxConnectionsPerHost(maxConPerHost);
params.setConnectionTimeout(conTimeOutMs);
params.setSoTimeout(soTimeOutMs);
HttpClientParams clientParams = new HttpClientParams();
clientParams.setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
client = new HttpClient(clientParams, connectionManager);
client.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, CONTENT_CHARSET);
}
public Response get(String url) throws WeixinException {
Parameter[] empty = null;
return get(url, empty);
}
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 GetMethod(sb.toString()));
}
public Response post(String url) throws WeixinException {
Parameter[] empty = null;
return post(url, empty);
}
public Response post(String url, Parameter... parameters) throws WeixinException {
PostMethod method = new PostMethod(url);
for (Parameter parameter : parameters) {
method.addParameter(parameter.toPostPara());
}
return doRequest(method);
}
public Response post(String url, RequestEntity entity) throws WeixinException {
PostMethod method = new PostMethod(url);
method.setRequestEntity(entity);
return doRequest(method);
}
public Response post(String url, Part... parts) throws WeixinException {
PostMethod method = new PostMethod(url);
method.getParams().setContentCharset(CONTENT_CHARSET);
MultipartRequestEntity entity = new MultipartRequestEntity(parts, method.getParams());
method.setRequestEntity(entity);
return doRequest(method);
}
protected Response doRequest(HttpMethod method) throws WeixinException {
try {
method.getParams().setContentCharset(CONTENT_CHARSET);
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false));
int status = client.executeMethod(method);
if (status != HttpStatus.SC_OK) {
throw new WeixinException(status, getCause(method.getStatusCode()));
}
// HttpClient对于要求接受后继服务的请求象POST和PUT等不能自动处理转发
// 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", method.getResponseHeader("location")));
}
Response response = new Response(method.getResponseBodyAsString());
response.setBody(method.getResponseBody());
response.setStatusCode(status);
response.setStatusText(method.getStatusText());
response.setStream(method.getResponseBodyAsStream());
Header contentType = method.getResponseHeader("Content-Type");
if (contentType.getValue().indexOf("application/json") >= 0 || contentType.getValue().indexOf("text/plain") >= 0) {
JSONObject jsonObj = response.getAsJson();
if (jsonObj.containsKey(ERROR_CODE_KEY) && jsonObj.getIntValue(ERROR_CODE_KEY) != 0) {
throw new WeixinException(jsonObj.getIntValue(ERROR_CODE_KEY), jsonObj.getString(ERROR_MSG_KEY));
}
}
return response;
} catch (IOException e) {
throw new WeixinException(e.getMessage());
} finally {
method.releaseConnection();
}
}
private String getCause(int statusCode) {
return "error---------------" + statusCode;
}
}

View File

@ -0,0 +1,58 @@
package com.foxinmy.weixin4j.http;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import org.apache.commons.httpclient.NameValuePair;
public class Parameter {
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", URLEncoder.encode(name, "UTF-8"), URLEncoder.encode(value, "UTF-8"));
} catch (UnsupportedEncodingException e) {
return String.format("&%s=%s", name, value);
}
}
public NameValuePair toPostPara() {
try {
return new NameValuePair(URLEncoder.encode(name, "UTF-8"), URLEncoder.encode(value, "UTF-8"));
} catch (UnsupportedEncodingException e) {
return new NameValuePair(name, value);
}
}
@Override
public String toString() {
return String.format("[Parameter name=%s, value=%s]", name,value);
}
}

View File

@ -0,0 +1,122 @@
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.foxinmy.weixin4j.msg.ErrorMessage;
import com.thoughtworks.xstream.XStream;
public class Response {
private final String ERROR_CODE_KEY = "errcode";
private final String ERROR_MSG_KEY = "errmsg";
private String text;
private int statusCode;
private String statusText;
private byte[] body;
private InputStream stream;
public Response(String text) {
this.text = text;
}
public String getAsString() {
return text;
}
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 ErrorMessage getErrorMsg() throws DocumentException {
JSONObject jsonObj = getAsJson();
if (jsonObj.containsKey(ERROR_CODE_KEY)) {
SAXReader reader = new SAXReader();
Document doc = reader.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("error_code.xml"));
Node node = doc.getRootElement().selectSingleNode(String.format("error[@code='%d']", jsonObj.getInteger(ERROR_CODE_KEY)));
if (node != null) {
return new ErrorMessage(jsonObj.getInteger(ERROR_CODE_KEY), jsonObj.getString(ERROR_MSG_KEY), node.getStringValue());
}
return new ErrorMessage(jsonObj.getInteger(ERROR_CODE_KEY), "unknown error", "未知错误");
}
return new ErrorMessage(0, "request success", "");
}
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();
}
}

View File

@ -0,0 +1,89 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
/**
* 网页授权结果
* @className AuthResult
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF">网页授权获取用户基本资料</a>
*/
public class AuthResult implements Serializable {
private static final long serialVersionUID = 654855396163854805L;
/**
* 网页授权获取code
* @className AuthScope
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%BD%91%E9%A1%B5%E6%8E%88%E6%9D%83%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF#.E7.AC.AC.E4.B8.80.E6.AD.A5.EF.BC.9A.E7.94.A8.E6.88.B7.E5.90.8C.E6.84.8F.E6.8E.88.E6.9D.83.EF.BC.8C.E8.8E.B7.E5.8F.96code">获取code</a>
*/
public enum AuthScope {
BASE("snsapi_base"), USERINFO("snsapi_userinfo");
private String name;
AuthScope(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public enum AuthCode {
OK, FAILED, REDIRECT
}
private AuthCode authCode;
private String location;
private UserToken accessToken;
public AuthResult(String location) {
this.location = location;
this.authCode = AuthCode.REDIRECT;
}
public AuthResult(UserToken accessToken) {
this.authCode = AuthCode.OK;
this.accessToken = accessToken;
}
public AuthCode getAuthCode() {
return authCode;
}
public void setAuthCode(AuthCode authCode) {
this.authCode = authCode;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public UserToken getAccessToken() {
return accessToken;
}
public void setAccessToken(UserToken accessToken) {
this.accessToken = accessToken;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[AuthResult authCode=").append(authCode);
sb.append(", location=").append(location);
sb.append(", accessToken=").append(accessToken).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,123 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 菜单按钮
* <p>目前自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单,一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以"..."代替
* 请注意,创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来,建议测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果</p>
* @className Button
* @author jy.hu
* @date 2014年4月5日
* @since JDK 1.7
*/
public class Button implements Serializable {
private static final long serialVersionUID = -6422234732203854866L;
private String name;
private BtnType type;
private String key;
private String url;
@JSONField(name = "sub_button")
private List<Button> subs;
public Button() {
}
public Button(String name) {
this.name = name;
}
public Button(String name, String url) {
this.name = name;
this.url = url;
this.type = BtnType.view;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BtnType getType() {
return type;
}
public void setType(BtnType type) {
this.type = type;
}
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<Button> getSubs() {
return subs;
}
public void setSubs(List<Button> subs) {
this.subs = subs;
}
public void pushSub(Button btn) {
if (this.subs == null) {
this.subs = new ArrayList<Button>();
}
this.subs.add(btn);
}
/**
* 按钮类型
* <p>click
* 用户点击click类型按钮后微信服务器会通过消息接口推送消息类型为event 的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互</p>
* <p>view
* 用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url值(即网页链接),达到打开网页的目的,建议与网页授权获取用户基本信息接口结合,获得用户的登入个人信息</p>
* @className BtnType
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
*/
public enum BtnType {
click, view
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[Button name=").append(name);
sb.append(" ,type=").append(type);
sb.append(" ,key=").append(key);
sb.append(" ,url=").append(url);
if (subs != null && !subs.isEmpty()) {
sb.append("{");
for (Button sub : subs) {
sb.append(sub.toString());
}
sb.append("}");
}
sb.append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,86 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 关注信息
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a href=
* "http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8"
* >关注信息</a>
*/
public class Following implements Serializable {
private static final long serialVersionUID = 1917454368271027134L;
private int total;
private int count;
@JSONField(name = "data")
private JSONObject dataJson;
@JSONField(name = "next_openid")
private String nextOpenId;
private List<User> userList;
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public JSONObject getDataJson() {
return dataJson;
}
public void setDataJson(JSONObject dataJson) {
this.dataJson = dataJson;
}
public String getNextOpenId() {
return nextOpenId;
}
public void setNextOpenId(String nextOpenId) {
this.nextOpenId = nextOpenId;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[Following total=").append(total);
sb.append(", count=").append(count);
if (userList != null && !userList.isEmpty()) {
sb.append(", users={");
for (User u : userList) {
sb.append(u.toString());
}
sb.append("}");
}
sb.append(", nextOpenId=").append(nextOpenId).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,96 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
import com.foxinmy.weixin4j.xml.XStream;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
/**
* 分组
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3">分组</a>
*/
public class Group implements Serializable {
private static final long serialVersionUID = 6979565973974005954L;
private int id;
private String name;
private int count;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCount() {
return count;
}
public Group(int id, String name) {
this.id = id;
this.name = name;
}
public Group(String name) {
this.name = name;
}
public Group() {
}
/**
* 返回创建分组所需的json格式字符串
* @return {"group": {"id": 107, "name": "test"}}
*/
public String toCreateJson() {
XStream xstream = new XStream(new JsonHierarchicalStreamDriver());
xstream.omitField(this.getClass(), "id");
xstream.omitField(this.getClass(), "count");
xstream.alias("group", this.getClass());
xstream.autodetectAnnotations(true);
xstream.processAnnotations(this.getClass());
return xstream.toXML(this);
}
/**
* 返回修改分组所需的json格式字符串
* @return
*/
public String toModifyJson() {
XStream xstream = new XStream(new JsonHierarchicalStreamDriver());
xstream.omitField(this.getClass(), "count");
xstream.alias("group", this.getClass());
xstream.autodetectAnnotations(true);
xstream.processAnnotations(this.getClass());
return xstream.toXML(this);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Group) {
return id == ((Group) obj).getId();
}
return super.equals(obj);
}
@Override
public String toString() {
return String.format("[Group id=%d ,name=%s ,count=%d]", id, name, count);
}
}

View File

@ -0,0 +1,75 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 群发图文消息
*
* @author jy.hu
* @date 2014年4月26日
* @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.B8.8A.E4.BC.A0.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF.E7.B4.A0.E6.9D.90">群发消息描述</a>
*/
public class MpArticle implements Serializable {
private static final long serialVersionUID = 5583479943661639234L;
@JSONField(name = "thumb_media_id")
private String thumbMediaId; // 图文消息缩略图的media_id可以在基础支持-上传多媒体文件接口中获得 非空
private String author;// 图文消息的作者 可为空
private String title;// 图文消息的标题 非空
@JSONField(name = "content_source_url")
private String url;// 在图文消息页面点击阅读原文后的页面 可为空
private String content;// 图文消息页面的内容支持HTML标签 非空
private String digest;// 图文消息的描述 可为空
public String getThumbMediaId() {
return thumbMediaId;
}
public void setThumbMediaId(String thumbMediaId) {
this.thumbMediaId = thumbMediaId;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getDigest() {
return digest;
}
public void setDigest(String digest) {
this.digest = digest;
}
public MpArticle(String thumbMediaId, String title,String content) {
this.thumbMediaId = thumbMediaId;
this.title = title;
this.content = content;
}
public MpArticle() {
}
}

View File

@ -0,0 +1,90 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
* 二维码参数对象
* <p>目前有2种类型的二维码,分别是临时二维码和永久二维码,前者有过期时间,最大为1800秒,但能够生成较多数量,后者无过期时间,数量较少(目前参数只支持1--100000)</p>
* @className QRParameter
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E7%94%9F%E6%88%90%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84%E4%BA%8C%E7%BB%B4%E7%A0%81">生成带参数的二维码</a>
*/
public class QRParameter implements Serializable {
private static final long serialVersionUID = 6611187606558274253L;
public enum QRType {
QR_SCENE, // 临时
QR_LIMIT_SCENE; // 永久
}
private int expire_seconds; // 该二维码有效时间以秒为单位 最大不超过1800
@XStreamAlias("action_name")
private QRType qrType; // 二维码类型QR_SCENE为临时,QR_LIMIT_SCENE为永久
@XStreamOmitField
private int scene_id; // 场景值ID临时二维码时为32位非0整型永久二维码时最大值为100000目前参数只支持1--100000
public int getExpire_seconds() {
return expire_seconds;
}
public void setExpire_seconds(int expire_seconds) {
this.expire_seconds = expire_seconds;
}
public QRType getQrType() {
return qrType;
}
public void setQrType(QRType qrType) {
this.qrType = qrType;
}
public int getScene_id() {
return scene_id;
}
public void setScene_id(int scene_id) {
this.scene_id = scene_id;
}
public QRParameter(int expire_seconds, QRType qrType, int scene_id) {
this.expire_seconds = expire_seconds;
this.qrType = qrType;
this.scene_id = scene_id;
}
public QRParameter(QRType action_name, int scene_id) {
this(0, action_name, scene_id);
}
public String toJson() {
/*
* XStream xstream = new XStream(new JsonHierarchicalStreamDriver() {
* public HierarchicalStreamWriter createWriter(Writer writer) { return
* new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE); } });
* xstream.setMode(XStream.NO_REFERENCES);
* xstream.autodetectAnnotations(true); if (this.qrType ==
* QRType.QR_LIMIT_SCENE) { xstream.omitField(QRParameter.class,
* "expire_seconds"); } return xstream.toXML(this);
*/
StringBuilder jsonBuilder = new StringBuilder("{");
jsonBuilder.append("\"action_name\":\"").append(qrType.name()).append("\"");
if (this.qrType == QRType.QR_SCENE) {
jsonBuilder.append(",\"expire_seconds\":").append(expire_seconds);
}
jsonBuilder.append(",\"action_info\":").append(String.format("{\"scene\": {\"scene_id\": %d}}", scene_id));
jsonBuilder.append("}");
return jsonBuilder.toString();
}
@Override
public String toString() {
return String.format("[QRParameter action_name=%s, expire_seconds=%d, scene_id=%d]", getQrType().name(), getExpire_seconds(), getScene_id());
}
}

View File

@ -0,0 +1,75 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
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;
private String access_token;
private int expires_in;
private String openid;
private long time;
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 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 access_token.equals(((Token) obj).getAccess_token());
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[Token access_token=").append(access_token);
sb.append(", expires_in=").append(expires_in);
sb.append(", openid=").append(openid);
sb.append(", time=").append(time).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,205 @@
package com.foxinmy.weixin4j.model;
import java.io.Serializable;
import com.foxinmy.weixin4j.util.WeixinUtil;
/**
* 用户对象
* <p>当用户与公众号有交互时,可通过openid获取信息</p>
* @author jy.hu
* @date 2014年4月8日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF">获取用户基本资料</a>
*/
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String openid; // 用户的唯一标识
private String nickname; // 用户昵称
private int sex; // 用户的性别值为1时是男性值为2时是女性值为0时是未知
private String province; // 用户个人资料填写的省份
private String city; // 普通用户个人资料填写的城市
private String country; // 国家如中国为CN
private String headimgurl; // 用户头像最后一个数值代表正方形头像大小有0466496132数值可选0代表640*640正方形头像用户没有头像时该项为空
private String privilege; // 用户特权信息json 数组如微信沃卡用户为chinaunicom
private int subscribe; // 是否关注
private long subscribe_time; // 关注时间
private Lang language; // 使用语言
// 国家地区语言版本
public enum Lang {
zh_CN("简体"), zh_TW("繁体"), en("英语");
private String desc;
Lang(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
// 用户性别 值为1时是男性值为2时是女性值为0时是未知
public enum Gender {
male(1), female(2), unknown(0);
private int sex;
Gender(int sex) {
this.sex = sex;
}
public int getInt() {
return sex;
}
}
// 有0466496132数值可选0代表640*640正方形头像
public enum Size {
small(46), middle1(64), middle2(96), big(132);
private int size;
Size(int size) {
this.size = size;
}
public int getInt() {
return size;
}
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getSex() {
return sex;
}
public Gender getGender(){
if(sex == 1){
return Gender.male;
}else if(sex == 2){
return Gender.female;
}else{
return Gender.unknown;
}
}
public void setSex(int sex) {
this.sex = sex;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getHeadimgurl() {
return headimgurl;
}
public String getHeadimgurl(Size size) {
if (!WeixinUtil.isBlank(headimgurl)) {
StringBuilder sb = new StringBuilder(headimgurl);
return sb.replace(0, (headimgurl.length() - 1), size.getInt() + "").toString();
}
return "";
}
public void setHeadimgurl(String headimgurl) {
this.headimgurl = headimgurl;
}
public String getPrivilege() {
return privilege;
}
public void setPrivilege(String privilege) {
this.privilege = privilege;
}
public int getSubscribe() {
return subscribe;
}
public void setSubscribe(int subscribe) {
this.subscribe = subscribe;
}
public Lang getLanguage() {
return language;
}
public void setLanguage(Lang language) {
this.language = language;
}
public long getSubscribe_time() {
return subscribe_time;
}
public void setSubscribe_time(long subscribe_time) {
this.subscribe_time = subscribe_time;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof User) {
return openid.equals(((User) obj).getOpenid());
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[User openid=").append(openid);
sb.append(", nickname=").append(nickname);
sb.append(", sex=").append(sex);
sb.append(", province=").append(province);
sb.append(", city=").append(city);
sb.append(", country=").append(country);
sb.append(", headimgurl=").append(headimgurl);
sb.append(", privilege=").append(privilege);
sb.append(", language=").append(language);
sb.append(", subscribe_time=").append(subscribe_time);
sb.append(", subscribe=").append(subscribe).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,47 @@
package com.foxinmy.weixin4j.model;
/**
* 用户token 一般通过授权页面获得
* @className UserToken
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.model.AuthResult
* @see com.foxinmy.weixin4j.model.AuthResult.AuthScope
*/
public class UserToken extends Token {
private static final long serialVersionUID = 1L;
private String refresh_token;
private String scope;
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[UserToken access_token=").append(super.getAccess_token());
sb.append(", expires_in=").append(super.getExpires_in());
sb.append(", openid=").append(super.getOpenid());
sb.append(", refresh_token=").append(refresh_token);
sb.append(", scope=").append(scope);
sb.append(", time=").append(super.getTime()).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,126 @@
package com.foxinmy.weixin4j.msg;
import java.io.Serializable;
import java.io.Writer;
import com.foxinmy.weixin4j.type.MessageType;
import com.foxinmy.weixin4j.util.WeixinConfig;
import com.foxinmy.weixin4j.util.WeixinUtil;
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;
@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
public BaseMessage(MessageType msgType) {
this.msgType = msgType;
}
public String getToUserName() {
return toUserName;
}
public void setToUserName(String toUserName) {
this.toUserName = toUserName;
}
public String getFromUserName() {
return fromUserName;
}
public void setFromUserName(String fromUserName) {
this.fromUserName = fromUserName;
}
public 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;
}
/**
* 消息对象转换为微信服务器接受的xml格式消息
* @return xml字符串
*/
public String toXml() {
XStream xstream = new XStream();
xstream.alias("xml", getMsgType().getMessageClass());
xstream.autodetectAnnotations(true);
xstream.processAnnotations(getMsgType().getMessageClass());
xstream.omitField(BaseMessage.class, "msgId");
if (WeixinUtil.isBlank(fromUserName)) {
setFromUserName(WeixinConfig.getValue("app_openId"));
}
return xstream.toXML(this);
}
/**
* 消息对象转换为微信服务器接受的json格式字符串
* @return json字符串
*/
public String toJson() {
XStream xstream = new XStream(new JsonHierarchicalStreamDriver() {
public HierarchicalStreamWriter createWriter(Writer writer) {
return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
}
});
xstream.setMode(XStream.NO_REFERENCES);
xstream.autodetectAnnotations(true);
xstream.processAnnotations(getMsgType().getMessageClass());
xstream.omitField(BaseMessage.class, "msgId");
if (WeixinUtil.isBlank(fromUserName)) {
setFromUserName(WeixinConfig.getValue("app_openId"));
}
return xstream.toXML(this);
}
}

View File

@ -0,0 +1,54 @@
package com.foxinmy.weixin4j.msg;
import java.io.Serializable;
public class ErrorMessage implements Serializable {
private static final long serialVersionUID = 2539772564331987708L;
private int code;
private String msg;
private String text;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public ErrorMessage(int code, String msg, String text) {
this.code = code;
this.msg = msg;
this.text = text;
}
public ErrorMessage() {
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ErrorMessage code=").append(code);
sb.append(", msg=").append(msg);
sb.append(", text=").append(text).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,53 @@
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 toUserName, String content) {
super(MessageType.text);
setToUserName(toUserName);
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();
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,70 @@
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>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
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 void setLatitude(String latitude) {
this.latitude = latitude;
}
public String getLongitude() {
return longitude;
}
public void setLongitude(String longitude) {
this.longitude = longitude;
}
public String getPrecision() {
return precision;
}
public void setPrecision(String precision) {
this.precision = precision;
}
@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();
}
}

View File

@ -0,0 +1,82 @@
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.MASSSENDJOBFINISH);
}
@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 void setStatus(String status) {
this.status = status;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public int getFilterCount() {
return filterCount;
}
public void setFilterCount(int filterCount) {
this.filterCount = filterCount;
}
public int getSentCount() {
return sentCount;
}
public void setSentCount(int sentCount) {
this.sentCount = sentCount;
}
public int getErrorCount() {
return errorCount;
}
public void setErrorCount(int errorCount) {
this.errorCount = 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();
}
}

View File

@ -0,0 +1,47 @@
package com.foxinmy.weixin4j.msg.event;
import com.foxinmy.weixin4j.type.EventType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 自定义菜单事件
* @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=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6%E6%8E%A8%E9%80%81#.E8.87.AA.E5.AE.9A.E4.B9.89.E8.8F.9C.E5.8D.95.E4.BA.8B.E4.BB.B6">自定义菜单事件</a>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
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;
}
public void setEventKey(String eventKey) {
this.eventKey = 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();
}
}

View File

@ -0,0 +1,62 @@
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>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
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 void setEventKey(String eventKey) {
this.eventKey = eventKey;
}
public String getTicket() {
return ticket;
}
public void setTicket(String ticket) {
this.ticket = 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();
}
}

View File

@ -0,0 +1,32 @@
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>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
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();
}
}

View File

@ -0,0 +1,58 @@
package com.foxinmy.weixin4j.msg.in;
import com.foxinmy.weixin4j.msg.BaseMessage;
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>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
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 void setPicUrl(String picUrl) {
this.picUrl = picUrl;
}
public String getMediaId() {
return mediaId;
}
public void setMediaId(String mediaId) {
this.mediaId = 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();
}
}

View File

@ -0,0 +1,69 @@
package com.foxinmy.weixin4j.msg.in;
import com.foxinmy.weixin4j.msg.BaseMessage;
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>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
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 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;
}
@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();
}
}

View File

@ -0,0 +1,80 @@
package com.foxinmy.weixin4j.msg.in;
import com.foxinmy.weixin4j.msg.BaseMessage;
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>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
public class LocationMessage extends BaseMessage {
private static final long serialVersionUID = 2866021596599237334L;
public LocationMessage() {
super(MessageType.location);
}
@XStreamAlias("Location_X")
private String location_X; // 地理位置维度
@XStreamAlias("Location_Y")
private String location_Y; // 地理位置经度
@XStreamAlias("Scale")
private String scale; // 地图缩放大小
@XStreamAlias("Label")
private String label; // 地理位置信息
public String getLocation_X() {
return location_X;
}
public void setLocation_X(String location_X) {
this.location_X = location_X;
}
public String getLocation_Y() {
return location_Y;
}
public void setLocation_Y(String location_Y) {
this.location_Y = location_Y;
}
public String getScale() {
return scale;
}
public void setScale(String scale) {
this.scale = scale;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@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(location_X);
sb.append(" ,location_Y=").append(location_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();
}
}

View File

@ -0,0 +1,58 @@
package com.foxinmy.weixin4j.msg.in;
import com.foxinmy.weixin4j.msg.BaseMessage;
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>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
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 void setMediaId(String mediaId) {
this.mediaId = mediaId;
}
public String getThumbMediaId() {
return thumbMediaId;
}
public void setThumbMediaId(String thumbMediaId) {
this.thumbMediaId = 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();
}
}

View File

@ -0,0 +1,71 @@
package com.foxinmy.weixin4j.msg.in;
import com.foxinmy.weixin4j.msg.BaseMessage;
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>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
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 void setRecognition(String recognition) {
this.recognition = recognition;
}
public String getMediaId() {
return mediaId;
}
public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
@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();
}
}

View File

@ -0,0 +1,105 @@
package com.foxinmy.weixin4j.msg.notify;
import java.util.LinkedList;
import java.util.List;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/**
* 客服图文消息
* @className ArticleNotify
* @author jy.hu
* @date 2014年4月6日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF">客服图文消息</a>
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify#toJson()
*/
public class ArticleNotify extends BaseNotify {
private static final int MAX_ARTICLE_COUNT = 10;
private static final long serialVersionUID = 1740696901128709998L;
public ArticleNotify() {
super(MessageType.news);
}
public ArticleNotify(String touser) {
super(touser, MessageType.news);
}
@XStreamAlias("news")
private News news;
@XStreamOmitField
private int count; // 图文消息个数限制为10条以内
public void pushArticle(String title, String desc, String picUrl, String url) {
if ((count + 1) > MAX_ARTICLE_COUNT) {
return;
}
if (this.news == null) {
this.news = new News();
}
this.news.pushArticle(title, desc, picUrl, url);
count++;
}
private static class News {
@XStreamAlias("articles")
private List<$> articles;
public News() {
this.articles = new LinkedList<$>();
}
public void pushArticle(String title, String desc, String picUrl, String url) {
this.articles.add(new $(title, desc, picUrl, url));
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for ($ item : articles) {
sb.append("{title=").append(item.title);
sb.append(" ,description=").append(item.desc);
sb.append(" ,picUrl=").append(item.picUrl);
sb.append(" ,url=").append(item.url).append("}");
}
return sb.toString();
}
}
private static class $ {
private String title; // 图文消息标题
@XStreamAlias("description")
private String desc; // 图文消息描述
@XStreamAlias("picurl")
private String picUrl; // 图片链接支持JPGPNG格式较好的效果为大图360*200小图200*200
@XStreamAlias("url")
private String url; // 点击图文消息跳转链接
public $(String title, String desc, String picUrl, String url) {
this.title = title;
this.desc = desc;
this.picUrl = picUrl;
this.url = url;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("(article ,title=").append(title);
sb.append(" ,description=").append(desc);
sb.append(" ,picUrl=").append(picUrl);
sb.append(" ,url=").append(url).append(")");
return sb.toString();
}
}
@Override
public String toString() {
return String.format("[ArticleNotify touser=%s ,msgtype=%s ,articles=%s]", super.getTouser(), super.getMsgtype().name(), this.news.toString());
}
}

View File

@ -0,0 +1,71 @@
package com.foxinmy.weixin4j.msg.notify;
import java.io.Serializable;
import java.io.Writer;
import com.foxinmy.weixin4j.type.MessageType;
import com.foxinmy.weixin4j.xml.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;
/**
* 客服消息基类(48小时内不限制发送次数)
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF">发送客服消息</a>
*/
public class BaseNotify implements Serializable {
private static final long serialVersionUID = 7190233634431087729L;
private String touser;
private MessageType msgtype;
public BaseNotify(MessageType msgtype) {
this.msgtype = msgtype;
}
public BaseNotify(String touser, MessageType msgtype) {
this.touser = touser;
this.msgtype = msgtype;
}
public String getTouser() {
return touser;
}
public void setTouser(String touser) {
this.touser = touser;
}
public MessageType getMsgtype() {
return msgtype;
}
public void setMsgtype(MessageType msgtype) {
this.msgtype = msgtype;
}
/**
* 客服消息json化
* @return {"touser": "to","msgtype": "text","text": {"content": "123"}}
*/
public String toJson() {
XStream xstream = new XStream(new JsonHierarchicalStreamDriver() {
public HierarchicalStreamWriter createWriter(Writer writer) {
return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
}
});
xstream.setMode(XStream.NO_REFERENCES);
xstream.autodetectAnnotations(true);
xstream.processAnnotations(this.getClass());
return xstream.toXML(this);
}
@Override
public String toString() {
return String.format("[BaseNotify touser=%s ,msgtype=%s]", touser, msgtype.name());
}
}

View File

@ -0,0 +1,49 @@
package com.foxinmy.weixin4j.msg.notify;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 客服图片消息
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E5.9B.BE.E7.89.87.E6.B6.88.E6.81.AF">客服图片消息</a>
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify#toJson()
*/
public class ImageNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public ImageNotify() {
super(MessageType.image);
}
public ImageNotify(String mediaId) {
super(MessageType.image);
this.image = new $(mediaId);
}
public ImageNotify(String mediaId, String touser) {
super(touser, MessageType.image);
this.image = new $(mediaId);
}
@XStreamAlias("image")
private $ image;
private static class $ {
@XStreamAlias("media_id")
private String mediaId;
public $(String mediaId) {
this.mediaId = mediaId;
}
}
@Override
public String toString() {
return String.format("[ImageNotify touser=%s ,msgtype=%s ,mediaId=%s]", super.getTouser(), super.getMsgtype().name(), this.image.mediaId);
}
}

View File

@ -0,0 +1,67 @@
package com.foxinmy.weixin4j.msg.notify;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 客服音乐消息
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E9.9F.B3.E4.B9.90.E6.B6.88.E6.81.AF">客服音乐消息</a>
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify#toJson()
*/
public class MusicNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public MusicNotify() {
super(MessageType.music);
}
public MusicNotify(String touser) {
super(touser, MessageType.music);
}
public void pushMusic(String title, String desc, String url, String hqUrl, String mediaId) {
this.music = new $();
this.music.thumbMediaId = mediaId;
this.music.desc = desc;
this.music.hqMusicUrl = hqUrl;
this.music.musicUrl = url;
this.music.title = title;
}
@XStreamAlias("music")
private $ music;
private static class $ {
private String title;
@XStreamAlias("description")
private String desc;
@XStreamAlias("musicurl")
private String musicUrl;
@XStreamAlias("hqmusicurl")
private String hqMusicUrl;
@XStreamAlias("thumb_media_id")
private String thumbMediaId;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("(music ,title=").append(title);
sb.append(" ,description=").append(desc);
sb.append(" ,musicURL=").append(musicUrl);
sb.append(" ,hqMusicUrl=").append(hqMusicUrl);
sb.append(" ,thumbMediaId=").append(thumbMediaId).append(")");
return sb.toString();
}
}
@Override
public String toString() {
return String.format("[VideoNotify touser=%s ,msgtype=%s ,music=%s]", super.getTouser(), super.getMsgtype().name(), this.music.toString());
}
}

View File

@ -0,0 +1,48 @@
package com.foxinmy.weixin4j.msg.notify;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 客服文本消息
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E6.96.87.E6.9C.AC.E6.B6.88.E6.81.AF">客服文本消息</a>
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify#toJson()
*/
public class TextNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public TextNotify() {
super(MessageType.text);
}
public TextNotify(String content) {
super(MessageType.text);
this.text = new $(content);
}
public TextNotify(String content, String touser) {
super(touser, MessageType.text);
this.text = new $(content);
}
@XStreamAlias("text")
private $ text;
private static class $ {
@XStreamAlias("content")
private String content;
public $(String content) {
this.content = content;
}
}
@Override
public String toString() {
return String.format("[TextNotify touser=%s ,msgtype=%s ,content=%s]", super.getTouser(), super.getMsgtype().name(), this.text.content);
}
}

View File

@ -0,0 +1,58 @@
package com.foxinmy.weixin4j.msg.notify;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 客服视频消息
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E8.A7.86.E9.A2.91.E6.B6.88.E6.81.AF">客服视频消息</a>
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify#toJson()
*/
public class VideoNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public VideoNotify() {
super(MessageType.video);
}
public VideoNotify(String touser) {
super(touser, MessageType.video);
}
public void pushVideo(String mediaId, String title, String desc) {
this.video = new $(mediaId, title, desc);
}
@XStreamAlias("video")
private $ video;
private static class $ {
@XStreamAlias("media_id")
private String mediaId;
private String title;
@XStreamAlias("description")
private String desc;
public $(String mediaId, String title, String desc) {
this.mediaId = mediaId;
this.title = title;
this.desc = desc;
}
@Override
public String toString() {
return String.format("(video mediaId=%s ,title=%s ,description=%s)", mediaId, title, desc);
}
}
@Override
public String toString() {
return String.format("[VideoNotify touser=%s ,msgtype=%s ,video=%s]", super.getTouser(), super.getMsgtype().name(), this.video.toString());
}
}

View File

@ -0,0 +1,49 @@
package com.foxinmy.weixin4j.msg.notify;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 客服语音消息
* @author jy.hu
* @date 2014年4月4日
* @since JDK 1.7
* @see <a href="http://mp.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E5%AE%A2%E6%9C%8D%E6%B6%88%E6%81%AF#.E5.8F.91.E9.80.81.E8.AF.AD.E9.9F.B3.E6.B6.88.E6.81.AF">客服语音消息</a>
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify
* @see com.foxinmy.weixin4j.msg.notify.BaseNotify#toJson()
*/
public class VoiceNotify extends BaseNotify {
private static final long serialVersionUID = -7698823863398518425L;
public VoiceNotify() {
super(MessageType.voice);
}
public VoiceNotify(String mediaId) {
super(MessageType.voice);
this.voice = new $(mediaId);
}
public VoiceNotify(String mediaId, String touser) {
super(touser, MessageType.voice);
this.voice = new $(mediaId);
}
@XStreamAlias("voice")
private $ voice;
private static class $ {
@XStreamAlias("media_id")
private String mediaId;
public $(String mediaId) {
this.mediaId = mediaId;
}
}
@Override
public String toString() {
return String.format("[VoiceNotify touser=%s ,msgtype=%s ,mediaId=%s]", super.getTouser(), super.getMsgtype().name(), this.voice.mediaId);
}
}

View File

@ -0,0 +1,93 @@
package com.foxinmy.weixin4j.msg.out;
import java.util.LinkedList;
import java.util.List;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复图文消息
* @className ArticleMessage
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @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.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF">回复图文消息</a>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
public class ArticleMessage extends BaseMessage {
private static final int MAX_ARTICLE_COUNT = 10;
private static final long serialVersionUID = -7331603018352309317L;
public ArticleMessage() {
super(MessageType.news);
}
public ArticleMessage(String toUserName) {
super(MessageType.news);
super.setToUserName(toUserName);
}
@XStreamAlias("ArticleCount")
private int count; // 图文消息个数限制为10条以内
@XStreamAlias("Articles")
private List<Article> articles;
public List<Article> getArticles() {
return this.articles;
}
public void pushArticle(String title, String desc, String picUrl, String url) {
if ((count + 1) > MAX_ARTICLE_COUNT) {
return;
}
if (this.articles == null) {
this.articles = new LinkedList<ArticleMessage.Article>();
}
this.articles.add(new Article(title, desc, picUrl, url));
count ++;
}
@XStreamAlias("item")
private static class Article {
@XStreamAlias("Title")
private String title; // 图文消息标题
@XStreamAlias("Description")
private String desc; // 图文消息描述
@XStreamAlias("PicUrl")
private String picUrl; // 图片链接支持JPGPNG格式较好的效果为大图360*200小图200*200
@XStreamAlias("Url")
private String url; // 点击图文消息跳转链接
public Article(String title, String desc, String picUrl, String url) {
this.title = title;
this.desc = desc;
this.picUrl = picUrl;
this.url = url;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("(Article ,title=").append(title);
sb.append(" ,description=").append(desc);
sb.append(" ,picUrl=").append(picUrl);
sb.append(" ,url=").append(url).append(")");
return sb.toString();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[ArticleMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,articles=").append(getArticles().toString());
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,49 @@
package com.foxinmy.weixin4j.msg.out;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复图片消息
* @className ImageMessage
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @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.E5.9B.BE.E7.89.87.E6.B6.88.E6.81.AF">回复图片消息</a>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
public class ImageMessage extends BaseMessage {
private static final long serialVersionUID = 6998255203997554731L;
public ImageMessage() {
super(MessageType.image);
super.getMsgType().setMessageClass(ImageMessage.class);
}
@XStreamAlias("Image")
private Image image;
public void pushMediaId(String mediaId) {
this.image = new Image();
this.image.mediaId = mediaId;
}
private static class Image {
@XStreamAlias("MediaId")
private String 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(" ,mediaId=").append(this.image.mediaId);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,76 @@
package com.foxinmy.weixin4j.msg.out;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 回复音乐消息
* @className MusicMessage
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @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.E9.9F.B3.E4.B9.90.E6.B6.88.E6.81.AF">回复音乐消息</a>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
public class MusicMessage extends BaseMessage {
private static final long serialVersionUID = 4384403772658796395L;
public MusicMessage() {
super(MessageType.music);
}
@XStreamAlias("Music")
private Music music;
public Music getMusic() {
return this.music;
}
public void pushMusic(String title, String desc, String url, String hqUrl, String mediaId) {
this.music = new Music();
this.music.thumbMediaId = mediaId;
this.music.desc = desc;
this.music.hqMusicUrl = hqUrl;
this.music.MusicURL = url;
this.music.title = title;
}
private static class Music {
@XStreamAlias("Title")
private String title; // 音乐标题
@XStreamAlias("Description")
private String desc; // 音乐描述
@XStreamAlias("MusicURL")
private String MusicURL; // 音乐链接
@XStreamAlias("HQMusicUrl")
private String hqMusicUrl; // 高质量音乐链接WIFI环境优先使用该链接播放音乐
@XStreamAlias("ThumbMediaId")
private String thumbMediaId; // 缩略图的媒体id通过上传多媒体文件得到的id
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("(Music ,title=").append(title);
sb.append(" ,description=").append(desc);
sb.append(" ,musicURL=").append(MusicURL);
sb.append(" ,hqMusicUrl=").append(hqMusicUrl);
sb.append(" ,thumbMediaId=").append(thumbMediaId).append(")");
return sb.toString();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[MusicMessage ,toUserName=").append(super.getToUserName());
sb.append(" ,fromUserName=").append(super.getFromUserName());
sb.append(" ,msgType=").append(super.getMsgType().name());
sb.append(" ,music=").append(getMusic().toString());
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,69 @@
package com.foxinmy.weixin4j.msg.out;
import com.foxinmy.weixin4j.msg.BaseMessage;
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=%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.E8.A7.86.E9.A2.91.E6.B6.88.E6.81.AF">回复视频消息</a>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
public class VideoMessage extends BaseMessage {
private static final long serialVersionUID = -1013075358679078381L;
public VideoMessage() {
super(MessageType.video);
super.getMsgType().setMessageClass(VideoMessage.class);
}
@XStreamAlias("Video")
private Video video;
public Video getVideo() {
return this.video;
}
public void pushVideo(String mediaId, String title, String desc) {
this.video = new Video();
this.video.desc = desc;
this.video.mediaId = mediaId;
this.video.title = title;
}
private static class Video {
@XStreamAlias("MediaId")
private String mediaId;
@XStreamAlias("Title")
private String title;
@XStreamAlias("Description")
private String desc;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("(Video mediaId=").append(mediaId);
sb.append(" ,title=").append(title);
sb.append(" ,desc=").append(desc).append(")");
return super.toString();
}
}
@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(" ,video=").append(getVideo().toString());
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,55 @@
package com.foxinmy.weixin4j.msg.out;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.type.MessageType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 语音消息
* @className VoiceMessage
* @author jy.hu
* @date 2014年3月23日
* @since JDK 1.7
* @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.E8.AF.AD.E9.9F.B3.E6.B6.88.E6.81.AF">回复语音消息</a>
* @see com.foxinmy.weixin4j.msg.BaseMessage
* @see com.foxinmy.weixin4j.msg.BaseMessage#toXml()
*/
public class VoiceMessage extends BaseMessage {
private static final long serialVersionUID = -7944926238652243793L;
public VoiceMessage() {
super(MessageType.voice);
super.getMsgType().setMessageClass(VoiceMessage.class);
}
@XStreamAlias("Voice")
private Voice voice;
public Voice getVocie() {
return voice;
}
public void pushMediaId(String mediaId){
this.voice = new Voice();
this.voice.setMediaId(mediaId);
}
private static class Voice {
@XStreamAlias("MediaId")
private String mediaId;
private void setMediaId(String mediaId) {
this.mediaId = mediaId;
}
}
@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(getVocie().mediaId);
sb.append(" ,createTime=").append(super.getCreateTime());
sb.append(" ,msgId=").append(super.getMsgId()).append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,30 @@
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.MenuEventMessage;
import com.foxinmy.weixin4j.msg.event.ScanEventMessage;
import com.foxinmy.weixin4j.msg.event.ScribeEventMessage;
/**
*
* 事件类型
* @author jy.hu
*
*/
public enum EventType {
subscribe(ScribeEventMessage.class), unsubscribe(ScribeEventMessage.class),
scan(ScanEventMessage.class),click(MenuEventMessage.class),view(MenuEventMessage.class),
location(LocationEventMessage.class),MASSSENDJOBFINISH(MassEventMessage.class);
private Class<? extends EventMessage> eventClass;
EventType(Class<? extends EventMessage> eventClass) {
this.eventClass = eventClass;
}
public Class<? extends EventMessage> getEventClass() {
return eventClass;
}
}

View File

@ -0,0 +1,32 @@
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");
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;
}
}

View File

@ -0,0 +1,39 @@
package com.foxinmy.weixin4j.type;
import com.foxinmy.weixin4j.msg.BaseMessage;
import com.foxinmy.weixin4j.msg.TextMessage;
import com.foxinmy.weixin4j.msg.event.EventMessage;
import com.foxinmy.weixin4j.msg.in.ImageMessage;
import com.foxinmy.weixin4j.msg.in.LinkMessage;
import com.foxinmy.weixin4j.msg.in.LocationMessage;
import com.foxinmy.weixin4j.msg.in.VideoMessage;
import com.foxinmy.weixin4j.msg.in.VoiceMessage;
import com.foxinmy.weixin4j.msg.out.ArticleMessage;
import com.foxinmy.weixin4j.msg.out.MusicMessage;
/**
*
* 消息类型
*
* @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),
// 发送的消息类型
music(MusicMessage.class), news(ArticleMessage.class);
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;
}
}

View File

@ -0,0 +1,41 @@
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 WeixinConfig {
public WeixinConfig() {
}
private static Properties props = new Properties();
static {
try {
props.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.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"));
}
}

View File

@ -0,0 +1,8 @@
package com.foxinmy.weixin4j.util;
public class WeixinUtil {
public static boolean isBlank(String str) {
return str == null || str.isEmpty();
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,70 @@
# app\u4fe1\u606f
app_id=wx4ab8f8de58159a57
app_secret=1d4eb0f4bf556aaed539f30ed05ca795
app_openId=gh_22b350df957b
app_token=carsonliu13450438112
# ----------------------------------------------------------------------------
# api\u9996\u9875
# http://mp.weixin.qq.com/wiki/index.php
# \u63a5\u53e3\u8c03\u7528\u8bf4\u660e
# 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
# ----------------------------------------------------------------------------
api_base_url=https://api.weixin.qq.com/cgi-bin
mp_base_url=https://mp.weixin.qq.com/cgi-bin
file_base_url=http://file.api.weixin.qq.com/cgi-bin
# \u7f51\u9875\u6388\u6743\u83b7\u53d6\u7528\u6237\u4fe1\u606f
user_auth_uri=https://open.weixin.qq.com/connect/oauth2/authorize?appid={app_id}&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect
sns_user_token_uri=https://api.weixin.qq.com/sns/oauth2/access_token?appid={app_id}&secret={app_secret}&code=%s&grant_type=authorization_code
sns_user_info_uri=https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN
# \u76f4\u63a5\u83b7\u53d6\u7528\u6237\u4fe1\u606f
api_user_info_uri={api_base_url}/user/info?access_token=%s&openid=%s&lang=zh_CN
# \u83b7\u53d6\u6211\u7684token
api_token_uri={api_base_url}/token?grant_type=client_credential&appid={app_id}&secret={app_secret}
# \u83b7\u53d6\u4e8c\u7ef4\u7801
qr_ticket_uri={api_base_url}/qrcode/create?access_token=%s
qr_image_uri={mp_base_url}/showqrcode?ticket=%s
# \u4e0a\u4f20\u5a92\u4f53\u6587\u4ef6
file_upload_uri={file_base_url}/media/upload?access_token=%s&type=%s
file_download_uri={file_base_url}/media/get?access_token=%s&media_id=%s
# \u53d1\u9001\u5ba2\u670d\u6d88\u606f
custom_notify_uri={api_base_url}/message/custom/send?access_token=%s
# \u521b\u5efa\u5206\u7ec4
group_create_uri={api_base_url}/groups/create?access_token=%s
# \u67e5\u8be2\u5206\u7ec4
group_get_uri={api_base_url}/groups/get?access_token=%s
# \u67e5\u8be2\u7528\u6237\u6240\u5728\u5206\u7ec4
group_getid_uri={api_base_url}/groups/getid?access_token=%s
# \u4fee\u6539\u5206\u7ec4\u540d
group_modify_uri={api_base_url}/groups/update?access_token=%s
# \u79fb\u52a8\u7528\u6237\u5206\u7ec4
group_move_uri={api_base_url}/groups/members/update?access_token=%s
# \u83b7\u53d6\u5173\u6ce8\u7740
following_uri={api_base_url}/user/get?access_token=%s&next_openid=%s
# \u81ea\u5b9a\u4e49\u83dc\u5355
menu_create_uri={api_base_url}/menu/create?access_token=%s
# \u67e5\u8be2\u83dc\u5355
menu_get_uri={api_base_url}/menu/get?access_token=%s
# \u5220\u9664\u83dc\u5355
menu_delete_uri={api_base_url}/menu/delete?access_token=%s
# \u4e0a\u4f20\u6d88\u606f
news_upload_uri={api_base_url}/media/uploadnews?access_token=%s
# \u5206\u7ec4\u7fa4\u53d1
mass_group_uri={api_base_url}/message/mass/sendall?access_token=%s
# openId\u7fa4\u53d1
mass_openid_uri={api_base_url}/message/mass/send?access_token=%s
# \u5220\u9664\u7fa4\u53d1
mass_delete_uri={api_base_url}/message/mass/delete?access_token=%s
# token\u5b58\u653e\u8def\u5f84
token_path=/tmp/weixin/token
# \u4e8c\u7ef4\u7801\u4fdd\u5b58\u8def\u5f84
qr_path=/tmp/weixin/qr
# \u5a92\u4f53\u6587\u4ef6\u4fdd\u5b58\u8def\u5f84
media_path=/tmp/weixin/media

View File

@ -0,0 +1,85 @@
<?xml version="1.0"?>
<!-- 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 -->
<errors>
<error code="-1">系统繁忙</error>
<error code="40001">获取access_token时AppSecret错误或者access_token无效</error>
<error code="40002">不合法的凭证类型</error>
<error code="40003">不合法的OpenID</error>
<error code="40004">不合法的媒体文件类型</error>
<error code="40005">不合法的文件类型</error>
<error code="40006">不合法的文件大小</error>
<error code="40007">不合法的媒体文件id</error>
<error code="40008">不合法的消息类型</error>
<error code="40009">不合法的图片文件大小</error>
<error code="40010">不合法的语音文件大小</error>
<error code="40011">不合法的视频文件大小</error>
<error code="40012">不合法的缩略图文件大小</error>
<error code="40013">不合法的APPID</error>
<error code="40014">不合法的access_token</error>
<error code="40015">不合法的菜单类型</error>
<error code="40016">不合法的按钮个数</error>
<error code="40017">不合法的按钮个数</error>
<error code="40018">不合法的按钮名字长度</error>
<error code="40019">不合法的按钮KEY长度</error>
<error code="40020">不合法的按钮URL长度</error>
<error code="40021">不合法的菜单版本号</error>
<error code="40022">不合法的子菜单级数</error>
<error code="40023">不合法的子菜单按钮个数</error>
<error code="40024">不合法的子菜单按钮类型</error>
<error code="40025">不合法的子菜单按钮名字长度</error>
<error code="40026">不合法的子菜单按钮KEY长度</error>
<error code="40027">不合法的子菜单按钮URL长度</error>
<error code="40028">不合法的自定义菜单使用用户</error>
<error code="40029">不合法的oauth_code</error>
<error code="40030">不合法的refresh_token</error>
<error code="40031">不合法的openid列表</error>
<error code="40032">不合法的openid列表长度</error>
<error code="40033">不合法的请求字符,不能包含\uxxxx格式的字符</error>
<error code="40035">不合法的参数</error>
<error code="40038">不合法的请求格式</error>
<error code="40039">不合法的URL长度</error>
<error code="40050">不合法的分组id</error>
<error code="40051">分组名字不合法</error>
<error code="41001">缺少access_token参数</error>
<error code="41002">缺少appid参数</error>
<error code="41003">缺少refresh_token参数</error>
<error code="41004">缺少secret参数</error>
<error code="41005">缺少多媒体文件数据</error>
<error code="41006">缺少media_id参数</error>
<error code="41007">缺少子菜单数据</error>
<error code="41008">缺少oauth code</error>
<error code="41009">缺少openid</error>
<error code="42001">access_token超时</error>
<error code="42002">refresh_token超时</error>
<error code="42003">oauth_code超时</error>
<error code="43001">需要GET请求</error>
<error code="43002">需要POST请求</error>
<error code="43003">需要HTTPS请求</error>
<error code="43004">需要接收者关注</error>
<error code="43005">需要好友关系</error>
<error code="44001">多媒体文件为空</error>
<error code="44002">POST的数据包为空</error>
<error code="44003">图文消息内容为空</error>
<error code="44004">文本消息内容为空</error>
<error code="45001">多媒体文件大小超过限制</error>
<error code="45002">消息内容超过限制</error>
<error code="45003">标题字段超过限制</error>
<error code="45004">描述字段超过限制</error>
<error code="45005">链接字段超过限制</error>
<error code="45006">图片链接字段超过限制</error>
<error code="45007">语音播放时间超过限制</error>
<error code="45008">图文消息超过限制</error>
<error code="45009">接口调用超过限制</error>
<error code="45010">创建菜单个数超过限制</error>
<error code="45015">回复时间超过限制</error>
<error code="45016">系统分组,不允许修改</error>
<error code="45017">分组名字过长</error>
<error code="45018">分组数量超过上限</error>
<error code="46001">不存在媒体数据</error>
<error code="46002">不存在的菜单版本</error>
<error code="46003">不存在的菜单数据</error>
<error code="46004">不存在的用户</error>
<error code="47001">解析JSON/XML内容错误</error>
<error code="48001">api功能未授权</error>
<error code="50001">用户未授权该api</error>
</errors>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- For assistance related to logback-translator or configuration -->
<!-- files in general, please contact the logback user mailing list -->
<!-- at http://www.qos.ch/mailman/listinfo/logback-user -->
<!-- -->
<!-- For professional support please see -->
<!-- http://www.qos.ch/shop/products/professionalSupport -->
<!-- -->
<configuration>
<!-- 控制台输出日志 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - %msg%n
</pattern>
</encoder>
</appender>
<!-- 文件输出指定项目日志 -->
<appender name="file"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender -->
<!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy -->
<!--for further documentation -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/tmp/weixin/log/%d{yyyy-MM-dd}.log
</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<!-- 异步输出指定项目日志 -->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file" />
</appender>
<logger name="org.apache" level="INFO">
<appender-ref ref="stdout" />
</logger>
<logger name="org.springframework" level="INFO">
<appender-ref ref="stdout" />
</logger>
<logger name="com.foxinmy.weixin" level="INFO">
<appender-ref ref="async" />
</logger>
</configuration>

View File

@ -0,0 +1,71 @@
package com.foxinmy.weixin4j.test;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.Button.BtnType;
/**
* 自定义菜单测试
* @className ButtonTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
*/
public class ButtonTest {
private WeixinProxy weixinProxy;
@Before
public void init() {
weixinProxy = new WeixinProxy();
}
@Test
public void create() {
List<Button> btnList = new ArrayList<Button>();
Button b = new Button("test");
b.setType(BtnType.click);
b.setKey("123");
btnList.add(b);
b = new Button("news","http://xxx");
btnList.add(b);
try {
weixinProxy.createMenu(btnList);
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void get() {
try {
List<Button> btnList = weixinProxy.getMenu();
for (Button btn : btnList) {
System.out.println(btn.toString());
}
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void delete() {
try {
weixinProxy.deleteMenu();
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
}

View File

@ -0,0 +1,78 @@
package com.foxinmy.weixin4j.test;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.Group;
/**
* 用户分组测试
* @className GroupTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
*/
public class GroupTest {
private WeixinProxy weixinProxy;
@Before
public void init() {
weixinProxy = new WeixinProxy();
}
@Test
public void create() {
try {
System.out.println(weixinProxy.createGroup("test"));
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void get() {
try {
List<Group> groups = weixinProxy.getGroups();
for (Group group : groups) {
System.out.println(group);
}
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void getid() {
try {
System.out.println(weixinProxy.findGroupByOpenId("owGBft_vbBbOaQOmpEUE4xDLeRSU"));
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void modify() {
try {
weixinProxy.modifyGroup(100, "test1");
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void move() {
try {
weixinProxy.moveGroup("owGBft_vbBbOaQOmpEUE4xDLeRSU",100);
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
}

View File

@ -0,0 +1,48 @@
package com.foxinmy.weixin4j.test;
import java.io.File;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.type.MediaType;
/**
* 媒体上传下载测试
* @className MediaTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
* @see
*/
public class MediaTest {
private WeixinProxy weixinProxy;
@Before
public void init() {
weixinProxy = new WeixinProxy();
}
@Test
public void upload() {
File file = new File("D:\\test.jpg");
try {
System.out.println(weixinProxy.uploadMedia(file, MediaType.image));
//usAxLEJxFXAjXVlLu_BSw0GvS2YEEoGeelRBybNZBGvMsdxiIcD1F9yBWYd9jVZx
} catch (WeixinException e) {
System.out.println(e.getErrorMsg());
}
}
@Test
public void download() {
try {
System.out.println(weixinProxy.downloadMedia("usAxLEJxFXAjXVlLu_BSw0GvS2YEEoGeelRBybNZBGvMsdxiIcD1F9yBWYd9jVZx", MediaType.image));
} catch (WeixinException e) {
System.out.println(e.getErrorMsg());
}
}
}

View File

@ -0,0 +1,270 @@
package com.foxinmy.weixin4j.test;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
/**
* 接收消息格式测试
* @author jy.hu
* @date 2014年3月24日
* @since JDK 1.7
*/
public class MessageInTest {
private StringBuilder xmlSb = new StringBuilder();
private WeixinProxy weixinProxy;
@Before
public void init(){
weixinProxy = new WeixinProxy();
}
/***************** event message *********************/
@Test
public void scribe() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[event]]></MsgType>");
xmlSb.append("<Event><![CDATA[subscribe]]></Event>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void scan() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[event]]></MsgType>");
xmlSb.append("<Event><![CDATA[SCAN]]></Event>");
xmlSb.append("<EventKey><![CDATA[SCENE_VALUE]]></EventKey>");
xmlSb.append("<Ticket><![CDATA[TICKET]]></Ticket>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void scan_scribe() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml><ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[event]]></MsgType>");
xmlSb.append("<Event><![CDATA[subscribe]]></Event>");
xmlSb.append("<EventKey><![CDATA[qrscene_123123]]></EventKey>");
xmlSb.append("<Ticket><![CDATA[TICKET]]></Ticket>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void location1() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[fromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[event]]></MsgType>");
xmlSb.append("<Event><![CDATA[LOCATION]]></Event>");
xmlSb.append("<Latitude>23.137466</Latitude>");
xmlSb.append("<Longitude>113.352425</Longitude>");
xmlSb.append("<Precision>119.385040</Precision>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void menu_click() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[event]]></MsgType>");
xmlSb.append("<Event><![CDATA[CLICK]]></Event>");
xmlSb.append("<EventKey><![CDATA[EVENTKEY]]></EventKey>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void menu_link() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[event]]></MsgType>");
xmlSb.append("<Event><![CDATA[VIEW]]></Event>");
xmlSb.append("<EventKey><![CDATA[www.qq.com]]></EventKey>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
/***************** common message *********************/
@Test
public void text() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[fromUser]]></FromUserName>");
xmlSb.append("<CreateTime>1348831860</CreateTime>");
xmlSb.append("<MsgType><![CDATA[text]]></MsgType>");
xmlSb.append("<Content><![CDATA[this is a test]]></Content>");
xmlSb.append("<MsgId>1234567890123456</MsgId>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void image() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[image]]></MsgType>");
xmlSb.append("<PicUrl><![CDATA[this is a url]]></PicUrl>");
xmlSb.append("<MediaId><![CDATA[media_id]]></MediaId>");
xmlSb.append("<MsgId>1234567890123456</MsgId>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void voice() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[voice]]></MsgType>");
xmlSb.append("<MediaId><![CDATA[media_id]]></MediaId>");
xmlSb.append("<Format><![CDATA[Format]]></Format>");
xmlSb.append("<MsgId>1234567890123456</MsgId>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void re_voice() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[voice]]></MsgType>");
xmlSb.append("<MediaId><![CDATA[media_id]]></MediaId>");
xmlSb.append("<Format><![CDATA[Format]]></Format>");
xmlSb.append("<Recognition><![CDATA[腾讯微信团队]]></Recognition>");
xmlSb.append("<MsgId>1234567890123456</MsgId>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void video() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[video]]></MsgType>");
xmlSb.append("<MediaId><![CDATA[media_id]]></MediaId>");
xmlSb.append("<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>");
xmlSb.append("<MsgId>1234567890123456</MsgId>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void location2() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[location]]></MsgType>");
xmlSb.append("<Location_X>23.134521</Location_X>");
xmlSb.append("<Location_Y>113.358803</Location_Y>");
xmlSb.append("<Scale>20</Scale>");
xmlSb.append("<Label><![CDATA[位置信息]]></Label>");
xmlSb.append("<MsgId>1234567890123456</MsgId>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void link() {
try {
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[toUser]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[FromUser]]></FromUserName>");
xmlSb.append("<CreateTime>123456789</CreateTime>");
xmlSb.append("<MsgType><![CDATA[link]]></MsgType>");
xmlSb.append("<Title><![CDATA[公众平台官网链接]]></Title>");
xmlSb.append("<Description><![CDATA[公众平台官网链接]]></Description>");
xmlSb.append("<Url><![CDATA[url]]></Url>");
xmlSb.append("<MsgId>1234567890123456</MsgId>");
xmlSb.append("</xml>");
System.out.println(weixinProxy.xml2msg(xmlSb.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,89 @@
package com.foxinmy.weixin4j.test;
import java.io.File;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.msg.notify.ArticleNotify;
import com.foxinmy.weixin4j.msg.notify.BaseNotify;
import com.foxinmy.weixin4j.msg.notify.ImageNotify;
import com.foxinmy.weixin4j.msg.notify.MusicNotify;
import com.foxinmy.weixin4j.msg.notify.TextNotify;
import com.foxinmy.weixin4j.msg.notify.VideoNotify;
import com.foxinmy.weixin4j.msg.notify.VoiceNotify;
import com.foxinmy.weixin4j.type.MediaType;
/**
* 客服消息测试
* @className MessageNotifyTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
* @see
*/
public class MessageNotifyTest {
private WeixinProxy weixinProxy;
@Before
public void init() {
weixinProxy = new WeixinProxy();
}
@Test
public void text() {
TextNotify notify = new TextNotify("123", "to");
System.out.println(notify.toJson());
}
@Test
public void image() {
ImageNotify notify = new ImageNotify("123", "to");
System.out.println(notify.toJson());
}
@Test
public void voice() {
VoiceNotify notify = new VoiceNotify("123", "to");
System.out.println(notify.toJson());
}
@Test
public void video() {
VideoNotify notify = new VideoNotify("to");
notify.pushVideo("123", "title", "desc");
System.out.println(notify.toJson());
}
@Test
public void music() {
MusicNotify notify = new MusicNotify("to");
notify.pushMusic("title", "desc", "url", "hqUrl", "mediaId");
System.out.println(notify.toJson());
}
@Test
public void article() {
ArticleNotify notify = new ArticleNotify("to");
notify.pushArticle("title1", "desc1", "picUrl1", "url1");
notify.pushArticle("title2", "desc2", "picUrl2", "url2");
System.out.println(notify.toJson());
}
@Test
public void send() {
BaseNotify t = new TextNotify("哈哈哈哈", "owGBft_vbBbOaQOmpEUE4xDLeRSU");
try {
weixinProxy.sendNotify(t);
String mediaId = weixinProxy.uploadMedia(new File("D:\\test.jpg"), MediaType.image);
t = new ImageNotify(mediaId, "owGBft_vbBbOaQOmpEUE4xDLeRSU");
weixinProxy.sendNotify(t);
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
}

View File

@ -0,0 +1,74 @@
package com.foxinmy.weixin4j.test;
import org.junit.Test;
import com.foxinmy.weixin4j.msg.TextMessage;
import com.foxinmy.weixin4j.msg.out.ArticleMessage;
import com.foxinmy.weixin4j.msg.out.ImageMessage;
import com.foxinmy.weixin4j.msg.out.MusicMessage;
import com.foxinmy.weixin4j.msg.out.VideoMessage;
import com.foxinmy.weixin4j.msg.out.VoiceMessage;
/**
* 发送消息格式测试
*
* @author jy.hu
* @date 2014年3月24日
* @since JDK 1.7
*/
public class MessageOutTest {
@Test
public void text() {
TextMessage message = new TextMessage();
message.setFromUserName("fromUser");
message.setToUserName("toUser");
message.setContent("text message");
System.out.println(message.toXml());
}
@Test
public void image() {
ImageMessage message = new ImageMessage();
message.setFromUserName("fromUser");
message.setToUserName("toUser");
message.pushMediaId("mediaId");
System.out.println(message.toXml());
}
@Test
public void voice() {
VoiceMessage message = new VoiceMessage();
message.setFromUserName("fromUser");
message.setToUserName("toUser");
message.pushMediaId("mediaId");
System.out.println(message.toXml());
}
@Test
public void video() {
VideoMessage message = new VideoMessage();
message.setFromUserName("fromUser");
message.setToUserName("toUser");
message.pushVideo("mediaId", "title", "title");
System.out.println(message.toXml());
}
@Test
public void music() {
MusicMessage message = new MusicMessage();
message.setFromUserName("fromUser");
message.setToUserName("toUser");
message.pushMusic("title", "desc", "url", "hqurl", "mediaId");
System.out.println(message.toXml());
}
@Test
public void article() {
ArticleMessage message = new ArticleMessage();
message.setFromUserName("fromUser");
message.setToUserName("toUser");
message.pushArticle("title1", "desc1", "picUrl1", "url1");
message.pushArticle("title2", "desc2", "picUrl2", "url2");
System.out.println(message.toXml());
}
}

View File

@ -0,0 +1,67 @@
package com.foxinmy.weixin4j.test;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.MpArticle;
import com.foxinmy.weixin4j.type.MediaType;
/**
* 群发消息
*
* @className MpNewsTest
* @author jy
* @date 2014年4月27日
* @since JDK 1.7
* @see
*/
public class MpNewsTest {
private WeixinProxy weixinProxy;
@Before
public void init() {
this.weixinProxy = new WeixinProxy();
}
@Test
public void massByGroup(){
List<MpArticle> articles = new ArrayList<MpArticle>();
try {
String thumbMediaId = weixinProxy.uploadMedia(new File("/tmp/test.jpg"), MediaType.image);
articles.add(new MpArticle(thumbMediaId, "title", "content"));
weixinProxy.massNewsByGroup(articles, "0");
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void massByOpenIds(){
List<MpArticle> articles = new ArrayList<MpArticle>();
try {
String thumbMediaId = weixinProxy.uploadMedia(new File("/tmp/test.jpg"), MediaType.image);
articles.add(new MpArticle(thumbMediaId, "title", "content"));
weixinProxy.massNewsByOpenIds(articles, "owGBft_vbBbOaQOmpEUE4xDLeRSU");
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void deleteMass(){
try {
weixinProxy.deleteMassNews("34182");
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
}

View File

@ -0,0 +1,48 @@
package com.foxinmy.weixin4j.test;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.QRParameter;
import com.foxinmy.weixin4j.model.QRParameter.QRType;
/**
* 二维码相关测试
* @className QRTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
*/
public class QRTest {
private WeixinProxy weixinProxy;
@Before
public void init() {
weixinProxy = new WeixinProxy();
}
@Test
public void temp_qr() {
QRParameter qr = new QRParameter(1200, QRType.QR_SCENE, 1);
try {
System.out.println(qr.toJson());
weixinProxy.getQR(qr);
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void forever_qr() {
QRParameter qr = new QRParameter(QRType.QR_LIMIT_SCENE, 1);
try {
System.out.println(qr.toJson());
weixinProxy.getQR(qr);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,32 @@
package com.foxinmy.weixin4j.test;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
import com.foxinmy.weixin4j.exception.WeixinException;
/**
* token相关测试
* @className TokenTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
*/
public class TokenTest {
private WeixinProxy weixinProxy;
@Before
public void init() {
weixinProxy = new WeixinProxy();
}
@Test
public void getToken() {
try {
System.out.println(weixinProxy.getToken());
} catch (WeixinException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,50 @@
package com.foxinmy.weixin4j.test;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.WeixinProxy;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.model.User;
/**
* 用户相关测试
* @className UserTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
*/
public class UserTest {
private WeixinProxy weixinProxy;
@Before
public void init() {
weixinProxy = new WeixinProxy();
}
@Test
public void getUser() {
try {
System.out.println(weixinProxy.getUser("owGBft_vbBbOaQOmpEUE4xDLeRSU"));
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
@Test
public void following() {
try {
System.out.println(weixinProxy.getFollowing(null));
List<User> userList = weixinProxy.getAllFollowing();
for (User user : userList) {
System.out.println(user);
}
} catch (WeixinException e) {
System.out.println(e.getErrorCode());
System.out.println(e.getErrorMsg());
}
}
}