大重构:weixin4j精简为weixin4j-base、weixin4j-mp、weixin4j-qy、weixin4j-server四个子工程。

This commit is contained in:
jinyu
2015-04-27 21:00:33 +08:00
parent e89e988c62
commit 69c9bdffa4
349 changed files with 450 additions and 27394 deletions
+20 -102
View File
@@ -6,35 +6,28 @@ weixin4j-qy
功能列表
-------
* **weixin4j-qy-api**
+ PartyApi `部门管理API`
* PartyApi `部门管理API`
+ UserApi `成员管理API`
* UserApi `成员管理API`
+ TagApi `标签管理API`
* TagApi `标签管理API`
+ MediaApi `多媒体管理API`
* MediaApi `多媒体管理API`
+ MenuApi `菜单管理API`
* MenuApi `菜单管理API`
+ NotifyApi `消息发送API`
* NotifyApi `消息发送API`
+ AgentApi `应用设置API`
* AgentApi `应用设置API`
+ BatchApi `批量任务API`
* BatchApi `批量操作API`
* **weixin4j-qy-server**
如何使用
--------
1.API工程可以单独打包到其他项目中使用,需新增或拷贝`weixin.properties`文件到项目的`classpath`
+ `netty服务器` & `消息分发`
+ 回调连接`AES`加密、解密
项目说明
-------
1.`weixin4j-qy`包含「微信企业号」的API封装以及一个半成品的netty服务实现.
2.API的成功调用依赖于正确的appid等数据,创建(或者copy项目里面的)一个名为**weixin.properties**的资源文件放在自己工程中的classpath下.
weixin.properties说明
| 属性名 | 说明 |
| :---------- | :-------------- |
@@ -51,94 +44,19 @@ weixin4j-qy
token_path=/tmp/weixin/token
media_path=/tmp/weixin/media
3.在项目根目录执行`mvn package`命令后得到jar包,将`weixin4j-qy-full`包或者`weixin4j-base``weixin4j-qy-api`两个包引入到自己的工程内.
2.实例化一个`WeixinProxy`对象,调用API
WeixinProxy weixinProxy = new WeixinProxy();
// weixinProxy = new WeixinProxy(corpid,corpsecret);
// weixinProxy = new WeixinProxy(weixinAccount);
weixinProxy.getUser(userid);
4.如需使用netty服务,则可以在相应的action中实现自己的业务处理,打包后放到`正确的目录`下解压`weixin-qy-server-bin.zip`执行`sh startup.sh start`便可启动服务.
3.针对`token`存储有两种方案,`File存储`/`Redis存储`,当然也可自己实现`TokenHolder`,默认使用文件(xml)的方式保存token,如果环境中支持`redis`,建议使用`RedisTokenHolder`.
@ActionAnnotation(msgType = MessageType.text)
public class TextAction extends AbstractAction<TextMessage> {
@Override
public ResponseMessage execute(TextMessage inMessage) {
return new ResponseMessage(new Text("Hello World!"), inMessage);
}
}
WeixinProxy weixinProxy = new WeixinProxy(new RedisTokenHolder());
// weixinProxy = new WeixinProxy(new RedisTokenHolder(weixinAccount));
4.`mvn package`.
更新LOG
-------
* 2014-11-19
+ 得到`weixin4j-qy-api``weixin4j-qy-server`工程
+ **weixin4j-qy-api**: 新增部门管理接口
+ **weixin4j-qy-api**: 新增用户管理接口
+ **weixin4j-qy-api**: 新增标签管理接口
* 2014-11-23
+ **weixin4j-qy-api**: 新增`多媒体管理`接口
+ **weixin4j-qy-api**: 新增`发送消息`接口
+ **weixin4j-qy-api**: 新增`菜单管理`接口
* 2014-11-24
+ **weixin4j-qy-server**: 新增netty服务与消息分发
* 2014-12-28
+ **weixin4j-qy-api**: 增加用户进入应用的callback事件
+ **weixin4j-qy-api**: 增加批量获取用户详情的接口
+ **weixin4j-qy-api**: 新增获取微信服务器IP接口
+ **weixin4j-qy-server**: 调整回调模式下的首次验证的签名方式
* 2015-01-04
+ **weixin4j-qy-api**: 新增批量删除员工接口
* 2015-01-15
+ **weixin4j-qy-api**: 新增邀请成员关注接口
* 2015-03-08
+ **weixin4j-qy-api**: 新增根据code获取成员信息接口
* 2015-03-17
+ **weixin4j-qy-api**: 新增企业应用设置接口
+ **weixin4j-qy-server**: 新增deploy.xml远程部署ant脚本
* 2015-03-29
+ **weixin4j-qy-api**: 单行注释调整为多行文档注释
* 2015-04-01
+ **weixin4j-qy-api**: 新增[BatchApi](./weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/BatchApi.java)批量异步执行任务接口
+ **weixin4j-qy-api**: <font color="red">DepartApi命名为[PartyApi](./weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/PartyApi.java)</font>
* 2015-04-04
+ **weixin4j-qy-api**: [MediaApi](./weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/MediaApi.java)新增批量上传成员和部门接口
* 2015-04-09
+ **weixin4j-qy-api**: [AgentApi](./weixin4j-qy-api/src/main/java/com/foxinmy/weixin4j/qy/api/AgentApi.java)新增获取应用列表概况接口
* 2015-04-13
+ **weixin4j-qy-api**: 新增WeixinTokenCreator与WeixinJSTicketCreator类
[更新LOG](./CHANGE.md)
----------------------
+28 -6
View File
@@ -8,11 +8,33 @@
</parent>
<artifactId>weixin4j-qy</artifactId>
<name>weixin4j-qy</name>
<packaging>pom</packaging>
<url>https://github.com/foxinmy/weixin4j/tree/master/weixin4j-qy</url>
<description>微信企业号工具包</description>
<modules>
<module>weixin4j-qy-api</module>
<module>weixin4j-qy-server</module>
</modules>
<description>微信企业号API</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>weixin4j-qy-${project.version}</finalName>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>${jaxen.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,100 @@
package com.foxinmy.weixin4j.qy.event;
import java.io.Serializable;
import com.foxinmy.weixin4j.message.event.EventMessage;
import com.foxinmy.weixin4j.qy.type.EventType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 异步任务事件完成通知
*
* @className BatchjobresultMessage
* @author jy
* @date 2015年3月31日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6#.E5.BC.82.E6.AD.A5.E4.BB.BB.E5.8A.A1.E5.AE.8C.E6.88.90.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81">异步任务事件完成通知</a>
*/
public class BatchjobresultMessage extends EventMessage {
private static final long serialVersionUID = 8014540441322209657L;
public BatchjobresultMessage() {
super(EventType.batch_job_result.name());
}
/**
* 任务信息
*/
@XStreamAlias("BatchJob")
private BatchJob batchJob;
public BatchJob getBatchJob() {
return batchJob;
}
/**
* 任务信息
*
* @className BatchJob
* @author jy
* @date 2015年4月1日
* @since JDK 1.7
* @see
*/
public static class BatchJob implements Serializable {
private static final long serialVersionUID = -7520032656787156391L;
/**
* 异步任务id,最大长度为64字符
*/
@XStreamAlias("JobId")
private String jobId;
/**
* 操作类型,字符串,目前分别有: 1. sync_user(增量更新成员) 2. replace_user(全量覆盖成员) 3.
* invite_user(邀请成员关注) 4. replace_party(全量覆盖部门)
*
* @see com.foxinmy.weixin4j.qy.type.BatchType
*/
@XStreamAlias("JobType")
private String jobType;
/**
* 返回码
*/
@XStreamAlias("ErrCode")
private String ErrCode;
/**
* 对返回码的文本描述内容
*/
@XStreamAlias("ErrMsg")
private String errMsg;
public String getJobId() {
return jobId;
}
public String getJobType() {
return jobType;
}
public String getErrCode() {
return ErrCode;
}
public String getErrMsg() {
return errMsg;
}
@Override
public String toString() {
return "[jobId=" + jobId + ", jobType=" + jobType + ", ErrCode="
+ ErrCode + ", errMsg=" + errMsg + "]";
}
}
@Override
public String toString() {
return "BatchjobresultMessage [batchJob=" + batchJob + ", "
+ super.toString() + "]";
}
}
@@ -0,0 +1,41 @@
package com.foxinmy.weixin4j.qy.event;
import com.foxinmy.weixin4j.message.event.EventMessage;
import com.foxinmy.weixin4j.qy.type.EventType;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 用户进入应用的事件推送(企业号)本事件只有在应用的回调模式中打开上报开关时上报
*
* @className EnterAgentEventMessage
* @author jy
* @date 2014年12月28日
* @since JDK 1.7
* @see <a href=
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6#.E7.94.A8.E6.88.B7.E8.BF.9B.E5.85.A5.E5.BA.94.E7.94.A8.E7.9A.84.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81"
* >用户进入应用的事件推送</a>
*/
public class EnterAgentEventMessage extends EventMessage {
private static final long serialVersionUID = 7675732524832500820L;
public EnterAgentEventMessage() {
super(EventType.enter_agent.name());
}
/**
* 事件KEY值,与自定义菜单接口中KEY值对应
*/
@XStreamAlias("EventKey")
private String eventKey;
public String getEventKey() {
return eventKey;
}
@Override
public String toString() {
return "EnterAgentEventMessage [eventKey=" + eventKey + ", "
+ super.toString() + "]";
}
}
@@ -0,0 +1,37 @@
package com.foxinmy.weixin4j.qy.model;
import com.foxinmy.weixin4j.model.WeixinAccount;
/**
* 微信企业号信息
*
* @className WeixinQyAccount
* @author jy
* @date 2014年11月18日
* @since JDK 1.7
* @see <a href=
* "https://qy.weixin.qq.com/cgi-bin/home?lang=zh_CN&token=685923034#setting"
* >企业号设置</a>
*/
public class WeixinQyAccount extends WeixinAccount {
private static final long serialVersionUID = 3689999353867189585L;
public WeixinQyAccount() {
}
/**
*
* @param corpid
* 企业ID
* @param corpsecret
* 管理组的凭证密钥
*/
public WeixinQyAccount(String corpid, String corpsecret) {
super(corpid, corpsecret);
}
@Override
public String toString() {
return "WeixinQyAccount [" + super.toString() + "]";
}
}
-32
View File
@@ -1,32 +0,0 @@
# 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
# idea ignore
/.idea
*.iml
# maven ignore
target/*
# other ignore
*.log
*.tmp
Thumbs.db
/target/
.DS_Store
-113
View File
@@ -1,113 +0,0 @@
weixin4j-qy-api
===============
[微信企业号](http://qydev.weixin.qq.com/wiki/index.php)开发工具包
---------------------------------------------------------------
功能列表
-------
* PartyApi `部门管理API`
* UserApi `成员管理API`
* TagApi `标签管理API`
* MediaApi `多媒体管理API`
* MenuApi `菜单管理API`
* NotifyApi `消息发送API`
* AgentApi `应用设置API`
* BatchApi `批量操作API`
如何使用
--------
1.API工程可以单独打包到其他项目中使用,需新增或拷贝`weixin.properties`文件到项目的`classpath`
weixin.properties说明
| 属性名 | 说明 |
| :---------- | :-------------- |
| account | 微信企业号信息 `json格式` |
| token_path | 使用FileTokenHolder时token保存的物理路径 |
| media_path | 调用媒体接口时保存媒体文件的物理路径 |
示例(properties中换行用右斜杆\\)
account={"id":"corpid","secret":"corpsecret",\
"token":"企业号中应用在回调模式下的token",\
"encodingAesKey":"企业号中应用在回调模式下AES加密密钥"}
token_path=/tmp/weixin/token
media_path=/tmp/weixin/media
2.实例化一个`WeixinProxy`对象,调用API
WeixinProxy weixinProxy = new WeixinProxy();
// weixinProxy = new WeixinProxy(corpid,corpsecret);
// weixinProxy = new WeixinProxy(weixinAccount);
weixinProxy.getUser(userid);
3.针对`token`存储有两种方案,`File存储`/`Redis存储`,当然也可自己实现`TokenHolder`,默认使用文件(xml)的方式保存token,如果环境中支持`redis`,建议使用`RedisTokenHolder`.
WeixinProxy weixinProxy = new WeixinProxy(new RedisTokenHolder());
// weixinProxy = new WeixinProxy(new RedisTokenHolder(weixinAccount));
4.`mvn package`.
更新LOG
-------
* 2014-11-19
+ 新增`部门管理`接口
+ 新增`用户管理`接口
+ 新增`标签管理`接口
* 2014-12-28
+ 增加`批量获取用户详情`的接口
+ 新增`获取微信服务器IP`接口
* 2015-01-04
+ 新增批量删除员工接口
* 2015-01-15
+ 新增邀请成员关注接口
* 2015-03-08
+ 新增根据code获取成员信息接口
* 2015-03-17
+ 新增企业应用设置接口
* 2015-03-29
+ 单行注释调整为多行文档注释
* 2015-04-01
+ 新增[BatchApi](./src/main/java/com/foxinmy/weixin4j/qy/api/BatchApi.java)批量异步执行任务接口
+ <font color="red">DepartApi命名为[PartyApi](./src/main/java/com/foxinmy/weixin4j/qy/api/PartyApi.java)</font>
* 2015-04-04
+ [MediaApi](./src/main/java/com/foxinmy/weixin4j/qy/api/MediaApi.java)新增批量上传成员和部门接口
* 2015-04-09
+ [AgentApi](./src/main/java/com/foxinmy/weixin4j/qy/api/AgentApi.java)新增获取应用列表概况接口
* 2015-04-13
+ 新增WeixinTokenCreator与WeixinJSTicketCreator类
-40
View File
@@ -1,40 +0,0 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j-qy</artifactId>
<version>1.3</version>
</parent>
<artifactId>weixin4j-qy-api</artifactId>
<name>weixin4j-qy-api</name>
<url>https://github.com/foxinmy/weixin4j/tree/master/weixin4j-qy/weixin4j-qy-api</url>
<description>微信企业号API</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>weixin4j-qy-${project.version}</finalName>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>${jaxen.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
@@ -1,31 +0,0 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>full</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>target/classes</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>/**</include>
</includes>
<excludes>
<exclude>*.properties</exclude>
<exclude>*.xml</exclude>
</excludes>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<unpack>true</unpack>
<includes>
<include>com.foxinmy:weixin4j-base</include>
</includes>
</dependencySet>
</dependencySets>
</assembly>
@@ -1,807 +0,0 @@
package com.foxinmy.weixin4j.qy;
import java.io.File;
import java.io.IOException;
import java.util.List;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.qy.api.AgentApi;
import com.foxinmy.weixin4j.qy.api.BatchApi;
import com.foxinmy.weixin4j.qy.api.HelperApi;
import com.foxinmy.weixin4j.qy.api.MediaApi;
import com.foxinmy.weixin4j.qy.api.MenuApi;
import com.foxinmy.weixin4j.qy.api.NotifyApi;
import com.foxinmy.weixin4j.qy.api.PartyApi;
import com.foxinmy.weixin4j.qy.api.TagApi;
import com.foxinmy.weixin4j.qy.api.UserApi;
import com.foxinmy.weixin4j.qy.message.NotifyMessage;
import com.foxinmy.weixin4j.qy.model.AgentInfo;
import com.foxinmy.weixin4j.qy.model.AgentOverview;
import com.foxinmy.weixin4j.qy.model.AgentSetter;
import com.foxinmy.weixin4j.qy.model.BatchResult;
import com.foxinmy.weixin4j.qy.model.Callback;
import com.foxinmy.weixin4j.qy.model.IdParameter;
import com.foxinmy.weixin4j.qy.model.Party;
import com.foxinmy.weixin4j.qy.model.Tag;
import com.foxinmy.weixin4j.qy.model.User;
import com.foxinmy.weixin4j.qy.token.WeixinTokenCreator;
import com.foxinmy.weixin4j.qy.type.InviteType;
import com.foxinmy.weixin4j.qy.type.UserStatus;
import com.foxinmy.weixin4j.token.FileTokenHolder;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.MediaType;
/**
* 微信企业号接口实现
*
* @className WeixinProxy
* @author jy
* @date 2014年11月19日
* @since JDK 1.7
* @see <a href="http://qydev.weixin.qq.com/wiki/index.php">api文档</a>
*/
public class WeixinProxy {
private final MediaApi mediaApi;
private final MenuApi menuApi;
private final NotifyApi notifyApi;
private final PartyApi partyApi;
private final UserApi userApi;
private final TagApi tagApi;
private final HelperApi helperApi;
private final AgentApi agentApi;
private final BatchApi batchApi;
/**
* 默认采用文件存放Token信息
*/
public WeixinProxy() {
this(new FileTokenHolder(new WeixinTokenCreator()));
}
/**
* appid,appsecret
*
* @param corpid
* @param corpsecret
*/
public WeixinProxy(String corpid, String corpsecret) {
this(new FileTokenHolder(new WeixinTokenCreator(corpid, corpsecret)));
}
/**
* TokenHolder对象
*
* @see com.foxinmy.weixin4j.token.TokenHolder
* @param tokenHolder
*/
public WeixinProxy(TokenHolder tokenHolder) {
this.partyApi = new PartyApi(tokenHolder);
this.userApi = new UserApi(tokenHolder);
this.tagApi = new TagApi(tokenHolder);
this.helperApi = new HelperApi(tokenHolder);
this.agentApi = new AgentApi(tokenHolder);
this.batchApi = new BatchApi(tokenHolder);
this.notifyApi = new NotifyApi(tokenHolder);
this.menuApi = new MenuApi(tokenHolder);
this.mediaApi = new MediaApi(tokenHolder);
}
/**
* 发送消息(需要管理员对应用有使用权限,对收件人touser、toparty、totag有查看权限,否则本次调用失败)
* <p>
* 1) 发送人员列表存在错误的userid:执行发送,开发者需注意返回结果说明</br>
* 2)发送人员不在通讯录权限范围内:不执行发送任务,返回首个出错的userid</br>
* 3)发送人员不在应用可见范围内:不执行发送任务,返回首个出错的userid</br>
* </p>
*
* @param notify
* 客服消息对象
* @return
* 如果对应用或收件人、部门、标签任何一个无权限,则本次发送失败;如果收件人、部门或标签不存在,发送仍然执行,但返回无效的部分</br>
* { "errcode": 0, "errmsg": "ok", "invaliduser": "UserID1",
* "invalidparty":"PartyID1", "invalidtag":"TagID1" }
* @throws WeixinException
* @see com.foxinmy.weixin4j.qy.api.NotifyApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E">发送接口说明</a>
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E6%B6%88%E6%81%AF%E7%B1%BB%E5%9E%8B%E5%8F%8A%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F">发送格式说明</a>
* @see com.foxinmy.weixin4j.msg.model.Text
* @see com.foxinmy.weixin4j.msg.model.Image
* @see com.foxinmy.weixin4j.msg.model.Voice
* @see com.foxinmy.weixin4j.msg.model.Video
* @see com.foxinmy.weixin4j.msg.model.File
* @see com.foxinmy.weixin4j.msg.model.News
* @see com.foxinmy.weixin4j.msg.model.MpNews
* @see com.foxinmy.weixin4j.qy.message.NotifyMessage
*/
public JSONObject sendNotify(NotifyMessage notify) throws WeixinException {
return notifyApi.sendNotify(notify);
}
/**
* 自定义菜单(管理员须拥有应用的管理权限 并且应用必须设置在回调模式)
*
* @param btnList
* 菜单列表
* @param agentid
* 应用ID
* @throws WeixinException
* @see com.foxinmy.weixin4j.qy.api.MenuApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%88%9B%E5%BB%BA%E5%BA%94%E7%94%A8%E8%8F%9C%E5%8D%95">创建自定义菜单</a>
* @see com.foxinmy.weixin4j.model.Button
*/
public JsonResult createMenu(List<Button> btnList, int agentid)
throws WeixinException {
return menuApi.createMenu(btnList, agentid);
}
/**
* 查询菜单(管理员须拥有应用的管理权限 并且应用必须设置在回调模式。)
*
* @param agentid
* 应用ID
* @return 菜单集合
* @throws WeixinException
* @see com.foxinmy.weixin4j.qy.api.MenuApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E8%8F%9C%E5%8D%95%E5%88%97%E8%A1%A8">查询菜单</a>
* @see com.foxinmy.weixin4j.model.Button
*/
public List<Button> getMenu(int agentid) throws WeixinException {
return menuApi.getMenu(agentid);
}
/**
* 删除菜单(管理员须拥有应用的管理权限 并且应用必须设置在回调模式)
*
* @param agentid
* 应用ID
* @throws WeixinException
* @see com.foxinmy.weixin4j.qy.api.MenuApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%88%A0%E9%99%A4%E8%8F%9C%E5%8D%95">删除菜单</a>
* @return 处理结果
*/
public JsonResult deleteMenu(int agentid) throws WeixinException {
return menuApi.deleteMenu(agentid);
}
/**
* 上传媒体文件
*
* @param file
* 媒体对象
* @return 上传到微信服务器返回的媒体标识
* @see com.foxinmy.weixin4j.qy.api.MediaApi
* @see {@link com.foxinmy.weixin4j.qy.WeixinProxy.MediaApi#uploadMedia(File, MediaType)}
* @throws WeixinException
* @throws IOException
*/
public String uploadMedia(File file) throws WeixinException, IOException {
return mediaApi.uploadMedia(file);
}
/**
* 上传媒体文件
*
* @param file
* 文件对象
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @throws WeixinException
* @throws IOException
* @see com.foxinmy.weixin4j.qy.api.MediaApi
* @see com.foxinmy.weixin4j.type.MediaType
* @see {@link com.foxinmy.weixin4j.qy.WeixinProxy#uploadMedia(String, byte[],String)}
*/
public String uploadMedia(File file, MediaType mediaType)
throws WeixinException, IOException {
return mediaApi.uploadMedia(file, mediaType);
}
/**
* 上传媒体文件(完全公开。所有管理员均可调用,media_id可以共享)
* <p>
* 正常情况下返回{"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789},
* 否则抛出异常.
* </p>
*
* @param bytes
* 媒体数据包
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @see com.foxinmy.weixin4j.qy.api.MediaApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传媒体文件说明</a>
* @throws WeixinException
*/
public String uploadMedia(String fileName, byte[] bytes, String mediaType)
throws WeixinException {
return mediaApi.uploadMedia(fileName, bytes, mediaType);
}
/**
* 下载媒体文件(完全公开。所有管理员均可调用,media_id可以共享)
*
* @param mediaId
* 媒体ID
* @return 二进制数据包
* @see com.foxinmy.weixin4j.qy.api.MediaApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">获取媒体说明</a>
* @throws WeixinException
*/
public byte[] downloadMedia(String mediaId) throws WeixinException {
return mediaApi.downloadMedia(mediaId);
}
/**
* 下载媒体文件(完全公开。所有管理员均可调用,media_id可以共享)
* <p>
* 正常情况下返回表头如Content-Type: image/jpeg,否则抛出异常.
* </p>
*
* @param mediaId
* 存储在微信服务器上的媒体标识
* @param extension
* 媒体后缀名
* @return 写入硬盘后的文件对象
* @throws WeixinException
* @throws IOException
* @see com.foxinmy.weixin4j.qy.api.MediaApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">获取媒体说明</a>
* @see com.foxinmy.weixin4j.type.MediaType
* @see {@link com.foxinmy.weixin4j.WeixinProxy.MediaApi#downloadMedia(String)}
*/
public File downloadMedia(String mediaId, String extension)
throws WeixinException {
return mediaApi.downloadMedia(mediaId, extension);
}
/**
* 创建部门(根部门的parentid为1)
*
* @param party
* 部门对象
* @see com.foxinmy.weixin4j.qy.model.Party
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E5.88.9B.E5.BB.BA.E9.83.A8.E9.97.A8">创建部门说明</a>
* @see com.foxinmy.weixin4j.qy.api.PartyApi
* @return 部门ID
* @throws WeixinException
*/
public int createParty(Party party) throws WeixinException {
return partyApi.createParty(party);
}
/**
* 更新部门(如果非必须的字段未指定 则不更新该字段之前的设置值)
*
* @param party
* 部门对象
* @see com.foxinmy.weixin4j.qy.model.Party
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E6.9B.B4.E6.96.B0.E9.83.A8.E9.97.A8">更新部门说明</a>
* @see com.foxinmy.weixin4j.qy.api.PartyApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult updateParty(Party party) throws WeixinException {
return partyApi.updateParty(party);
}
/**
* 查询部门列表(以部门的order字段从小到大排列)
*
* @param partyId
* 部门ID。获取指定部门ID下的子部门
* @see com.foxinmy.weixin4j.qy.model.Party
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E5.88.97.E8.A1.A8">获取部门列表</a>
* @see com.foxinmy.weixin4j.qy.api.PartyApi
* @return 部门列表
* @throws WeixinException
*/
public List<Party> listParty(int partyId) throws WeixinException {
return partyApi.listParty(partyId);
}
/**
* 删除部门(不能删除根部门;不能删除含有子部门、成员的部门)
*
* @param partyId
* 部门ID
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E5.88.A0.E9.99.A4.E9.83.A8.E9.97.A8">删除部门说明</a>
* @see com.foxinmy.weixin4j.qy.api.PartyApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult deleteParty(int partyId) throws WeixinException {
return partyApi.deleteParty(partyId);
}
/**
* 批量上传部门
*
* @param parties
* 部门列表
* @see com.foxinmy.weixin4j.qy.api.MediaApi
* @see com.foxinmy.weixin4j.qy.api.BatchApi
* @see {@link com.foxinmy.weixin4j.qy.WeixinProxy#replaceparty(String,Callback)}
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E9.80.9A.E8.AE.AF.E5.BD.95.E6.9B.B4.E6.96.B0">批量任务</a>
* @return 上传后的mediaId
* @throws WeixinException
*/
public String batchUploadParties(List<Party> parties)
throws WeixinException {
return mediaApi.batchUploadParties(parties);
}
/**
* 创建成员
*
* @param user
* 成员对象
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E5.88.9B.E5.BB.BA.E6.88.90.E5.91.98">创建成员说明</a>
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult createUser(User user) throws WeixinException {
return userApi.createUser(user);
}
/**
* 更新用户(如果非必须的字段未指定 则不更新该字段之前的设置值)
*
* @param user
* 成员对象
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E6.9B.B4.E6.96.B0.E6.88.90.E5.91.98">更新成员说明</a>
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult updateUser(User user) throws WeixinException {
return userApi.updateUser(user);
}
/**
* 获取成员
*
* @param userid
* 成员唯一ID
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E8.8E.B7.E5.8F.96.E6.88.90.E5.91.98">获取成员说明</a>
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 成员对象
* @throws WeixinException
*/
public User getUser(String userid) throws WeixinException {
return userApi.getUser(userid);
}
/**
* code获取userid(管理员须拥有agent的使用权限;agentid必须和跳转链接时所在的企业应用ID相同。)
*
* @param code
* 通过员工授权获取到的code,每次员工授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期
* @param agentid
* 跳转链接时所在的企业应用ID
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 成员对象
* @see {@link com.foxinmy.weixin4j.qy.WeixinProxy#getUser(String)}
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BC%81%E4%B8%9A%E8%8E%B7%E5%8F%96code">企业获取code</a>
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E6%A0%B9%E6%8D%AEcode%E8%8E%B7%E5%8F%96%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF">根据code获取成员信息</a>
* @throws WeixinException
*/
public User getUserByCode(String code, int agentid) throws WeixinException {
return userApi.getUserByCode(code, agentid);
}
/**
* 获取部门成员
*
* @param partyId
* 部门ID 必须
* @param fetchChild
* 是否递归获取子部门下面的成员 非必须
* @param userStatus
* 成员状态 status可叠加 非必须
* @param findDetail
* 是否获取详细信息
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98">获取部门成员说明</a>
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 成员列表
* @throws WeixinException
*/
public List<User> listUser(int partyId, boolean fetchChild,
UserStatus userStatus, boolean findDetail) throws WeixinException {
return userApi.listUser(partyId, fetchChild, userStatus, findDetail);
}
/**
* 获取部门下所有状态成员(不进行递归)
*
* @param departId
* 部门ID
* @see {@link com.foxinmy.weixin4j.qy.WeixinProxy#listUser(int, boolean,UserStatus)}
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 成员列表
* @throws WeixinException
*/
public List<User> listUser(int departId) throws WeixinException {
return userApi.listUser(departId);
}
/**
* 删除成员
*
* @param userid
* 成员ID
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98">删除成员说明</a>
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult deleteUser(String userid) throws WeixinException {
return userApi.deleteUser(userid);
}
/**
* 批量删除成员
*
* @param userIds
* 成员列表
* @see <a href=
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98"
* >批量删除成员说明</a
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult batchDeleteUser(List<String> userIds)
throws WeixinException {
return userApi.batchDeleteUser(userIds);
}
/**
* 邀请成员关注(管理员须拥有该成员的查看权限)
*
* @param userId
* 成员ID
* @param tips
* 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 调用结果
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8">邀请成员关注说明</a>
* @throws WeixinException
*/
public InviteType inviteUser(String userId, String tips)
throws WeixinException {
return userApi.inviteUser(userId, tips);
}
/**
* 创建标签(创建的标签属于管理组;默认为未加锁状态)
*
* @param tagName
* 标签名称
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E5.88.9B.E5.BB.BA.E6.A0.87.E7.AD.BE">创建标签说明</a>
* @see com.foxinmy.weixin4j.qy.api.TagApi
* @return 标签ID
* @throws WeixinException
*/
public int createTag(String tagName) throws WeixinException {
return tagApi.createTag(tagName);
}
/**
* 更新标签(管理组必须是指定标签的创建者)
*
* @param tag
* 标签信息
* @see <a href=
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E6.9B.B4.E6.96.B0.E6.A0.87.E7.AD.BE.E5.90.8D.E5.AD.97"
* >更新标签说明</a>
* @see com.foxinmy.weixin4j.qy.model.Tag
* @see com.foxinmy.weixin4j.qy.api.TagApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult updateTag(Tag tag) throws WeixinException {
return tagApi.updateTag(tag);
}
/**
* 删除标签(管理组必须是指定标签的创建者 并且标签的成员列表为空)
*
* @param tagId
* 标签ID
* @return 处理结果
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E5.88.A0.E9.99.A4.E6.A0.87.E7.AD.BE">删除标签说明</a>
* @see com.foxinmy.weixin4j.qy.api.TagApi
* @throws WeixinException
*/
public JsonResult deleteTag(int tagId) throws WeixinException {
return tagApi.deleteTag(tagId);
}
/**
* 获取标签列表
*
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E8.8E.B7.E5.8F.96.E6.A0.87.E7.AD.BE.E5.88.97.E8.A1.A8">获取标签列表说明</a>
* @see com.foxinmy.weixin4j.qy.model.Tag
* @see com.foxinmy.weixin4j.qy.api.TagApi
* @return 标签列表
* @throws WeixinException
*/
public List<Tag> listTag() throws WeixinException {
return tagApi.listTag();
}
/**
* 获取标签成员(管理组须拥有“获取标签成员”的接口权限,标签须对管理组可见;返回列表仅包含管理组管辖范围的成员)
*
* @param tagId
* 标签ID
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E8.8E.B7.E5.8F.96.E6.A0.87.E7.AD.BE.E6.88.90.E5.91.98">获取标签成员说明</a>
* @see com.foxinmy.weixin4j.qy.api.TagApi
* @return 成员列表
* @throws WeixinException
*/
public List<User> getTagUsers(int tagId) throws WeixinException {
return tagApi.getTagUsers(tagId);
}
/**
* 新增标签成员(标签对管理组可见且未加锁,成员属于管理组管辖范围)<br>
* <font color="red">若部分userid非法,则在text中体现</font>
*
* @param tagId
* 标签ID
* @param userIds
* 成员ID
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E5.A2.9E.E5.8A.A0.E6.A0.87.E7.AD.BE.E6.88.90.E5.91.98">新增标签成员说明</a>
* @see com.foxinmy.weixin4j.qy.api.TagApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult addTagUsers(int tagId, List<String> userIds)
throws WeixinException {
return tagApi.addTagUsers(tagId, userIds);
}
/**
* 删除标签成员(标签对管理组可见且未加锁,成员属于管理组管辖范围)<br>
* <font color="red">若部分userid非法,则在text中体现</font>
*
* @param tagId
* 标签ID
* @param userIds
* 成员ID
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E5.88.A0.E9.99.A4.E6.A0.87.E7.AD.BE.E6.88.90.E5.91.98">删除标签成员说明</a>
* @see com.foxinmy.weixin4j.qy.api.TagApi
* @return 处理结果
* @throws WeixinException
*/
public JsonResult deleteTagUsers(int tagId, List<String> userIds)
throws WeixinException {
return tagApi.deleteTagUsers(tagId, userIds);
}
/**
* 获取微信服务器IP地址
*
* @return IP地址
* @see com.foxinmy.weixin4j.qy.api.HelperApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%9B%9E%E8%B0%83%E6%A8%A1%E5%BC%8F#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5">获取IP地址</a>
* @throws WeixinException
*/
public List<String> getcallbackip() throws WeixinException {
return helperApi.getcallbackip();
}
/**
* 获取企业号某个应用的基本信息,包括头像、昵称、帐号类型、认证类型、可见范围等信息
*
* @param agentid
* 授权方应用id
* @return 应用信息
* @see com.foxinmy.weixin4j.qy.model.AgentInfo
* @see com.foxinmy.weixin4j.qy.api.AgentApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E4%BC%81%E4%B8%9A%E5%8F%B7%E5%BA%94%E7%94%A8">企业号应用的信息</a>
* @throws WeixinException
*/
public AgentInfo getAgent(int agentid) throws WeixinException {
return agentApi.getAgent(agentid);
}
/**
* 设置企业应用的选项设置信息,如:地理位置上报等
*
* @param agentSet
* 设置参数
* @see com.foxinmy.weixin4j.qy.model.AgentSetter
* @see com.foxinmy.weixin4j.qy.api.AgentApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%AE%BE%E7%BD%AE%E4%BC%81%E4%B8%9A%E5%8F%B7%E5%BA%94%E7%94%A8">设置企业号信息</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult setAgent(AgentSetter agentSet) throws WeixinException {
return agentApi.setAgent(agentSet);
}
/**
* 获取应用概况列表
*
* @see com.foxinmy.weixin4j.qy.model.AgentOverview
* @see com.foxinmy.weixin4j.qy.api.AgentApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%E6%A6%82%E5%86%B5%E5%88%97%E8%A1%A8">获取应用概况</a>
* @return 应用概况列表
* @throws WeixinException
*/
public List<AgentOverview> listAgentOverview() throws WeixinException {
return agentApi.listAgentOverview();
}
/**
* 批量邀请成员关注
*
* @param parameter
* 成员ID,标签ID,部门ID
* @param callback
* 接收任务执行结果的回调地址等信息
* @param tips
* 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。
* @return 异步任务id,最大长度为64字符
* @see com.foxinmy.weixin4j.qy.model.IdParameter
* @see com.foxinmy.weixin4j.qy.model.Callback
* @see com.foxinmy.weixin4j.qy.api.BatchApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8">邀请成员关注</a>
* @throws WeixinException
*/
public String inviteuser(IdParameter parameter, Callback callback,
String tips) throws WeixinException {
return batchApi.inviteuser(parameter, callback, tips);
}
/**
* 批量更新成员,本接口以userid为主键,增量更新企业号通讯录成员。
* <p>
* 1.模板中的部门需填写部门ID,多个部门用分号分隔,部门ID必须为数字</br>
* 2.文件中存在、通讯录中也存在的成员,更新成员在文件中指定的字段值 </br> 3.文件中存在、通讯录中不存在的成员,执行添加操作</br>
* 4.通讯录中存在、文件中不存在的成员,保持不变</br>
* </p>
*
* @param mediaId
* 带user信息的cvs文件上传后的media_id
* @param callback
* 接收任务执行结果的回调地址等信息
* @return 异步任务id,最大长度为64字符
* @see com.foxinmy.weixin4j.qy.model.Callback
* @see com.foxinmy.weixin4j.qy.api.BatchApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E5.A2.9E.E9.87.8F.E6.9B.B4.E6.96.B0.E6.88.90.E5.91.98">批量更新成员</a>
* @throws WeixinException
*/
public String syncuser(String mediaId, Callback callback)
throws WeixinException {
return batchApi.syncuser(mediaId, callback);
}
/**
* 批量覆盖成员,本接口以userid为主键,全量覆盖企业号通讯录成员,任务完成后企业号通讯录成员与提交的文件完全保持一致。
* <p>
* 1.模板中的部门需填写部门ID,多个部门用分号分隔,部门ID必须为数字</br> 2.文件中存在、通讯录中也存在的成员,完全以文件为准</br>
* 3.文件中存在、通讯录中不存在的成员,执行添加操作</br>
* 4.通讯录中存在、文件中不存在的成员,执行删除操作。出于安全考虑,如果需要删除的成员多于50人,
* 且多于现有人数的20%以上,系统将中止导入并返回相应的错误码
* </p>
*
* @param mediaId
* 带userid信息的cvs文件上传后的media_id
* @param callback
* 接收任务执行结果的回调地址等信息
* @return 异步任务id,最大长度为64字符
* @see com.foxinmy.weixin4j.qy.model.Callback
* @see com.foxinmy.weixin4j.qy.api.BatchApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E5.85.A8.E9.87.8F.E8.A6.86.E7.9B.96.E6.88.90.E5.91.98">批量覆盖成员</a>
* @throws WeixinException
*/
public String replaceuser(String mediaId, Callback callback)
throws WeixinException {
return batchApi.replaceuser(mediaId, callback);
}
/**
* 批量上传成员
*
* @param users
* 成员列表
* @see com.foxinmy.weixin4j.qy.api.MediaApi
* @see com.foxinmy.weixin4j.qy.api.BatchApi
* @see {@link com.foxinmy.weixin4j.qy.WeixinProxy#syncuser(String,Callback)}
* @see {@link com.foxinmy.weixin4j.qy.WeixinProxy#replaceuser(String,Callback)}
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E9.80.9A.E8.AE.AF.E5.BD.95.E6.9B.B4.E6.96.B0">批量任务</a>
* @return 上传后的mediaId
* @throws WeixinException
*/
public String batchUploadUsers(List<User> users) throws WeixinException {
return mediaApi.batchUploadUsers(users);
}
/**
* 批量覆盖部门,本接口以partyid为键,全量覆盖企业号通讯录组织架构,任务完成后企业号通讯录组织架构与提交的文件完全保持一致。
* <p>
* 1.文件中存在、通讯录中也存在的部门,执行修改操作</br> 2.文件中存在、通讯录中不存在的部门,执行添加操作</br>
* 3.文件中不存在、通讯录中存在的部门,当部门为空时,执行删除操作</br>
* 4.CSV文件中,部门名称、部门ID、父部门ID为必填字段,部门ID必须为数字;排序为可选字段,置空或填0不修改排序
* </p>
*
* @param mediaId
* 带partyid信息的cvs文件上传后的media_id
* @param callback
* 接收任务执行结果的回调地址等信息
* @return 异步任务id,最大长度为64字符
* @see com.foxinmy.weixin4j.qy.model.Callback
* @see com.foxinmy.weixin4j.qy.api.BatchApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E5.85.A8.E9.87.8F.E8.A6.86.E7.9B.96.E9.83.A8.E9.97.A8">批量覆盖部门</a>
* @throws WeixinException
*/
public String replaceparty(String mediaId, Callback callback)
throws WeixinException {
return batchApi.replaceparty(mediaId, callback);
}
/**
* 获取异步任务执行的结果
*
* @param jobId
* 任务ID
* @return 效果信息
* @see com.foxinmy.weixin4j.qy.model.BatchResult
* @see com.foxinmy.weixin4j.qy.api.BatchApi
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E8.8E.B7.E5.8F.96.E5.BC.82.E6.AD.A5.E4.BB.BB.E5.8A.A1.E7.BB.93.E6.9E.9C">获取异步任务执行结果</a>
* @throws WeixinException
*/
public BatchResult getresult(String jobId) throws WeixinException {
return batchApi.getresult(jobId);
}
}
@@ -1,118 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.model.AgentInfo;
import com.foxinmy.weixin4j.qy.model.AgentOverview;
import com.foxinmy.weixin4j.qy.model.AgentSetter;
import com.foxinmy.weixin4j.qy.model.User;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 管理应用接口
*
* @className AgentApi
* @author jy
* @date 2015年3月16日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E4%BC%81%E4%B8%9A%E5%8F%B7%E5%BA%94%E7%94%A8">管理应用接口说明</a>
*/
public class AgentApi extends QyApi {
private final TokenHolder tokenHolder;
public AgentApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 获取企业号某个应用的基本信息,包括头像、昵称、帐号类型、认证类型、可见范围等信息
*
* @param agentid
* 授权方应用id
* @return 应用信息
* @see com.foxinmy.weixin4j.qy.model.AgentInfo
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E4%BC%81%E4%B8%9A%E5%8F%B7%E5%BA%94%E7%94%A8">企业号应用的信息</a>
* @throws WeixinException
*/
public AgentInfo getAgent(int agentid) throws WeixinException {
String agent_get_uri = getRequestUri("agent_get_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(agent_get_uri,
token.getAccessToken(), agentid));
JSONObject jsonObj = response.getAsJson();
AgentInfo agent = JSON.toJavaObject(jsonObj, AgentInfo.class);
agent.setAllowUsers(JSON.parseArray(
jsonObj.getJSONObject("allow_userinfos").getString("user"),
User.class));
agent.setAllowPartys(JSON.parseArray(
jsonObj.getJSONObject("allow_partys").getString("partyid"),
Integer.class));
agent.setAllowTags(JSON.parseArray(jsonObj.getJSONObject("allow_tags")
.getString("tagid"), Integer.class));
return agent;
}
/**
* 设置企业应用的选项设置信息,如:地理位置上报等
*
* @param agentSet
* 设置参数
* @see com.foxinmy.weixin4j.qy.model.AgentSetter
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%AE%BE%E7%BD%AE%E4%BC%81%E4%B8%9A%E5%8F%B7%E5%BA%94%E7%94%A8">设置企业号信息</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult setAgent(AgentSetter agentSet) throws WeixinException {
String agent_set_uri = getRequestUri("agent_set_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(agent_set_uri, token.getAccessToken()),
JSON.toJSONString(agentSet, typeFilter));
return response.getAsJsonResult();
}
private static ValueFilter typeFilter;
static {
typeFilter = new ValueFilter() {
@Override
public Object process(Object object, String name, Object value) {
if (value instanceof Boolean) {
return ((Boolean) value) ? 1 : 0;
}
if (value instanceof Enum) {
return ((Enum<?>) value).ordinal();
}
return value;
}
};
}
/**
* 获取应用概况列表
*
* @see com.foxinmy.weixin4j.qy.model.AgentOverview
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%E6%A6%82%E5%86%B5%E5%88%97%E8%A1%A8">获取应用概况</a>
* @return 应用概况列表
* @throws WeixinException
*/
public List<AgentOverview> listAgentOverview() throws WeixinException {
String agent_list_uri = getRequestUri("agent_list_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(agent_list_uri,
token.getAccessToken()));
return JSON.parseArray(response.getAsJson().getString("agentlist"),
AgentOverview.class);
}
}
@@ -1,172 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.model.BatchResult;
import com.foxinmy.weixin4j.qy.model.Callback;
import com.foxinmy.weixin4j.qy.model.IdParameter;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 批量异步任务API
* <p>
* 异步任务接口用于大批量数据的处理,提交后接口即返回,企业号会在后台继续执行任务。执行完成后,通过任务事件通知企业获取结果
* </p>
*
* @className BatchApi
* @author jy
* @date 2015年3月30日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3">批量任务</a>
*/
public class BatchApi extends QyApi {
private final TokenHolder tokenHolder;
public BatchApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 批量邀请成员关注
*
* @param parameter
* 成员ID,标签ID,部门ID
* @param callback
* 接收任务执行结果的回调地址等信息
* @param tips
* 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。
* @return 异步任务id,最大长度为64字符
* @see com.foxinmy.weixin4j.qy.model.IdParameter
* @see com.foxinmy.weixin4j.qy.model.Callback
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8">邀请成员关注</a>
* @throws WeixinException
*/
public String inviteuser(IdParameter parameter, Callback callback,
String tips) throws WeixinException {
String batch_inviteuser_uri = getRequestUri("batch_inviteuser_uri");
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.putAll(parameter.getParameter());
obj.put("callback", callback);
obj.put("invite_tips", tips);
Response response = request.post(
String.format(batch_inviteuser_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJson().getString("jobid");
}
/**
* 批量更新成员,本接口以userid为主键,增量更新企业号通讯录成员。
*
* <p>
* 1.模板中的部门需填写部门ID,多个部门用分号分隔,部门ID必须为数字</br>
* 2.文件中存在、通讯录中也存在的成员,更新成员在文件中指定的字段值 </br> 3.文件中存在、通讯录中不存在的成员,执行添加操作</br>
* 4.通讯录中存在、文件中不存在的成员,保持不变</br>
* </p>
*
* @param mediaId
* 带user信息的cvs文件上传后的media_id
* @param callback
* 接收任务执行结果的回调地址等信息
* @return 异步任务id,最大长度为64字符
* @see {@link com.foxinmy.weixin4j.qy.api.MediaApi#batchUploadUsers(List)}
* @see com.foxinmy.weixin4j.qy.model.Callback
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E5.A2.9E.E9.87.8F.E6.9B.B4.E6.96.B0.E6.88.90.E5.91.98">批量更新成员</a>
* @throws WeixinException
*/
public String syncuser(String mediaId, Callback callback)
throws WeixinException {
String batch_syncuser_uri = getRequestUri("batch_syncuser_uri");
return batch(batch_syncuser_uri, mediaId, callback);
}
private String batch(String batchUrl, String mediaId, Callback callback)
throws WeixinException {
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("media_id", mediaId);
obj.put("callback", callback);
Response response = request.post(
String.format(batchUrl, token.getAccessToken()),
obj.toJSONString());
return response.getAsJson().getString("jobid");
}
/**
* 批量覆盖成员,本接口以userid为主键,全量覆盖企业号通讯录成员,任务完成后企业号通讯录成员与提交的文件完全保持一致。
* <p>
* 1.模板中的部门需填写部门ID,多个部门用分号分隔,部门ID必须为数字</br> 2.文件中存在、通讯录中也存在的成员,完全以文件为准</br>
* 3.文件中存在、通讯录中不存在的成员,执行添加操作</br>
* 4.通讯录中存在、文件中不存在的成员,执行删除操作。出于安全考虑,如果需要删除的成员多于50人,
* 且多于现有人数的20%以上,系统将中止导入并返回相应的错误码
* </p>
*
* @param mediaId
* 带userid信息的cvs文件上传后的media_id
* @param callback
* 接收任务执行结果的回调地址等信息
* @return 异步任务id,最大长度为64字符
* @see {@link com.foxinmy.weixin4j.qy.api.MediaApi#batchUploadUsers(List)}
* @see com.foxinmy.weixin4j.qy.model.Callback
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E5.85.A8.E9.87.8F.E8.A6.86.E7.9B.96.E6.88.90.E5.91.98">批量覆盖成员</a>
* @throws WeixinException
*/
public String replaceuser(String mediaId, Callback callback)
throws WeixinException {
String batch_replaceuser_uri = getRequestUri("batch_replaceuser_uri");
return batch(batch_replaceuser_uri, mediaId, callback);
}
/**
* 批量覆盖部门,本接口以partyid为键,全量覆盖企业号通讯录组织架构,任务完成后企业号通讯录组织架构与提交的文件完全保持一致。
* <p>
* 1.文件中存在、通讯录中也存在的部门,执行修改操作</br> 2.文件中存在、通讯录中不存在的部门,执行添加操作</br>
* 3.文件中不存在、通讯录中存在的部门,当部门为空时,执行删除操作</br>
* 4.CSV文件中,部门名称、部门ID、父部门ID为必填字段,部门ID必须为数字;排序为可选字段,置空或填0不修改排序
* </p>
*
* @param mediaId
* 带partyid信息的cvs文件上传后的media_id
* @param callback
* 接收任务执行结果的回调地址等信息
* @return 异步任务id,最大长度为64字符
* @see {@link com.foxinmy.weixin4j.qy.api.MediaApi#batchUploadParties(List)}
* @see com.foxinmy.weixin4j.qy.model.Callback
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E5.85.A8.E9.87.8F.E8.A6.86.E7.9B.96.E9.83.A8.E9.97.A8">批量覆盖部门</a>
* @throws WeixinException
*/
public String replaceparty(String mediaId, Callback callback)
throws WeixinException {
String batch_replaceparty_uri = getRequestUri("batch_replaceparty_uri");
return batch(batch_replaceparty_uri, mediaId, callback);
}
/**
* 获取异步任务执行的结果
*
* @param jobId
* 任务ID
* @return 效果信息
* @see com.foxinmy.weixin4j.qy.model.BatchResult
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E8.8E.B7.E5.8F.96.E5.BC.82.E6.AD.A5.E4.BB.BB.E5.8A.A1.E7.BB.93.E6.9E.9C">获取异步任务执行结果</a>
* @throws WeixinException
*/
public BatchResult getresult(String jobId) throws WeixinException {
Token token = tokenHolder.getToken();
String batch_getresult_uri = getRequestUri("batch_getresult_uri");
Response response = request.get(String.format(batch_getresult_uri,
token.getAccessToken(), jobId));
return response.getAsObject(new TypeReference<BatchResult>() {
});
}
}
@@ -1,43 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 辅助API
*
* @className HelperApi
* @author jy
* @date 2014年12月28日
* @since JDK 1.7
* @see
*/
public class HelperApi extends QyApi {
private final TokenHolder tokenHolder;
public HelperApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 获取微信服务器IP地址
*
* @return IP地址
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%9B%9E%E8%B0%83%E6%A8%A1%E5%BC%8F#.E8.8E.B7.E5.8F.96.E5.BE.AE.E4.BF.A1.E6.9C.8D.E5.8A.A1.E5.99.A8.E7.9A.84ip.E6.AE.B5">获取IP地址</a>
* @throws WeixinException
*/
public List<String> getcallbackip() throws WeixinException {
String getcallbackip_uri = getRequestUri("getcallbackip_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(getcallbackip_uri,
token.getAccessToken()));
return JSON.parseArray(response.getAsJson().getString("ip_list"),
String.class);
}
}
@@ -1,252 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.mime.content.ByteArrayBody;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.PartParameter;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.model.Callback;
import com.foxinmy.weixin4j.qy.model.Party;
import com.foxinmy.weixin4j.qy.model.User;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.MediaType;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.util.FileUtil;
import com.foxinmy.weixin4j.util.IOUtil;
/**
* 媒体相关API
*
* @className MediaApi
* @author jy.hu
* @date 2014年9月25日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">管理多媒体文件</a>
* @see com.foxinmy.weixin4j.type.MediaType
*/
public class MediaApi extends QyApi {
private final TokenHolder tokenHolder;
public MediaApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 上传媒体文件
*
* @param file
* 媒体对象
* @return 上传到微信服务器返回的媒体标识
* @see {@link com.foxinmy.weixin4j.qy.api.MediaApi#uploadMedia(File, MediaType)}
* @throws WeixinException
* @throws IOException
*/
public String uploadMedia(File file) throws WeixinException, IOException {
String mediaTypeKey = IOUtil.getExtension(file.getName());
if (StringUtils.isBlank(mediaTypeKey)) {
mediaTypeKey = FileUtil.getFileType(file);
}
MediaType mediaType = MediaType.getMediaType(mediaTypeKey);
return uploadMedia(file, mediaType);
}
/**
* 上传媒体文件
*
* @param file
* 文件对象
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @throws WeixinException
* @throws IOException
* @see com.foxinmy.weixin4j.type.MediaType
* @see {@link com.foxinmy.weixin4j.qy.api.MediaApi#uploadMedia(String, byte[],String)}
*/
public String uploadMedia(File file, MediaType mediaType)
throws WeixinException, IOException {
byte[] datas = IOUtil.toByteArray(new FileInputStream(file));
return uploadMedia(file.getName(), datas, mediaType.name());
}
/**
* 上传媒体文件(完全公开。所有管理员均可调用,media_id可以共享)
* <p>
* 正常情况下返回{"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789},
* 否则抛出异常.
* </p>
*
* @param bytes
* 媒体数据包
* @param mediaType
* 媒体类型
* @return 上传到微信服务器返回的媒体标识
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">上传媒体文件说明</a>
* @throws WeixinException
*/
public String uploadMedia(String fileName, byte[] bytes, String mediaType)
throws WeixinException {
Token token = tokenHolder.getToken();
String file_upload_uri = getRequestUri("file_upload_uri");
Response response = request.post(String.format(file_upload_uri,
token.getAccessToken(), mediaType), new PartParameter("media",
new ByteArrayBody(bytes, fileName)));
return response.getAsJson().getString("media_id");
}
/**
* 下载媒体文件(完全公开。所有管理员均可调用,media_id可以共享)
* <p>
* 正常情况下返回表头如Content-Type: image/jpeg,否则抛出异常.
* </p>
*
* @param mediaId
* 存储在微信服务器上的媒体标识
* @param extension
* 媒体后缀名
* @return 写入硬盘后的文件对象
* @throws WeixinException
* @throws IOException
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">获取媒体说明</a>
* @see com.foxinmy.weixin4j.type.MediaType
* @see {@link com.foxinmy.weixin4j.qy.api.MediaApi#downloadMedia(String)}
*/
public File downloadMedia(String mediaId, String extension)
throws WeixinException {
String media_path = ConfigUtil.getValue("media_path");
File file = new File(media_path + File.separator + mediaId + "."
+ extension);
if (file.exists()) {
return file;
}
byte[] datas = downloadMedia(mediaId);
OutputStream os = null;
try {
boolean flag = file.createNewFile();
if (flag) {
os = new FileOutputStream(file);
os.write(datas);
} else {
throw new WeixinException(String.format("create file fail:%s",
file.getAbsolutePath()));
}
} catch (IOException e) {
throw new WeixinException(e.getMessage());
} finally {
try {
if (os != null) {
os.close();
}
} catch (IOException ignore) {
;
}
}
return file;
}
/**
* 下载媒体文件(完全公开。所有管理员均可调用,media_id可以共享)
*
* @param mediaId
* 媒体ID
* @return 二进制数据包
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6">获取媒体说明</a>
* @throws WeixinException
*/
public byte[] downloadMedia(String mediaId) throws WeixinException {
Token token = tokenHolder.getToken();
String file_download_uri = getRequestUri("file_download_uri");
Response response = request.get(String.format(file_download_uri,
token.getAccessToken(), mediaId));
return response.getBody();
}
/**
* 批量上传成员
*
* @param users
* 成员列表
* @see {@link com.foxinmy.weixin4j.qy.api.BatchApi#syncuser(String,Callback)}
* @see {@link com.foxinmy.weixin4j.qy.api.BatchApi#replaceuser(String,Callback)}
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E9.80.9A.E8.AE.AF.E5.BD.95.E6.9B.B4.E6.96.B0">批量任务</a>
* @return 上传后的mediaId
* @throws WeixinException
*/
public String batchUploadUsers(List<User> users) throws WeixinException {
return batchUpload("batch_syncuser.cvs", users);
}
/**
* 批量上传部门
*
* @param parties
* 部门列表
* @see {@link com.foxinmy.weixin4j.qy.api.BatchApi#replaceparty(String,Callback)}
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1%E6%8E%A5%E5%8F%A3#.E9.80.9A.E8.AE.AF.E5.BD.95.E6.9B.B4.E6.96.B0">批量任务</a>
* @return 上传后的mediaId
* @throws WeixinException
*/
public String batchUploadParties(List<Party> parties)
throws WeixinException {
return batchUpload("batch_replaceparty.cvs", parties);
}
private <T> String batchUpload(String batchName, List<T> models)
throws WeixinException {
JSONObject csvObj = JSON.parseObject(getConfigValue(batchName));
JSONArray columns = csvObj.getJSONArray("column");
StringWriter writer = new StringWriter();
writer.write(csvObj.getString("header"));
final Map<String, Object> column = new LinkedHashMap<String, Object>();
for (Object col : columns) {
column.put(col.toString(), "");
}
writer.write("\r\n");
for (T model : models) {
JSON.toJSONString(model, new PropertyFilter() {
@Override
public boolean apply(Object object, String name, Object value) {
if (column.containsKey(name)) {
column.put(name, value);
}
return true;
}
});
writer.write(StringUtils.join(column.values(), ","));
writer.write("\r\n");
}
String mediaId = uploadMedia(batchName,
writer.toString().getBytes(Consts.UTF_8), MediaType.file.name());
try {
writer.close();
} catch (IOException e) {
;
}
return mediaId;
}
}
@@ -1,121 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.NameFilter;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.type.ButtonType;
/**
* 菜单相关API
*
* @className MenuApi
* @author jy.hu
* @date 2014年9月25日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.model.Button
*/
public class MenuApi extends QyApi {
private final TokenHolder tokenHolder;
public MenuApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 自定义菜单(管理员须拥有应用的管理权限 并且应用必须设置在回调模式)
*
* @param btnList
* 菜单列表
* @param agentid
* 应用ID
* @throws WeixinException
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%88%9B%E5%BB%BA%E5%BA%94%E7%94%A8%E8%8F%9C%E5%8D%95">创建自定义菜单</a>
* @see com.foxinmy.weixin4j.model.Button
*/
public JsonResult createMenu(List<Button> btnList, int agentid)
throws WeixinException {
String menu_create_uri = getRequestUri("menu_create_uri");
Token token = tokenHolder.getToken();
JSONObject obj = new JSONObject();
obj.put("button", btnList);
Response response = request
.post(String.format(menu_create_uri, token.getAccessToken(),
agentid), JSON.toJSONString(obj, new NameFilter() {
@Override
public String process(Object object, String name,
Object value) {
if (object instanceof Button && name.equals("content")) {
if (((Button) object).getFormatType() == ButtonType.view) {
return "url";
} else {
return "key";
}
}
return name;
}
}));
return response.getAsJsonResult();
}
/**
* 查询菜单(管理员须拥有应用的管理权限 并且应用必须设置在回调模式。)
*
* @param agentid
* 应用ID
* @return 菜单集合
* @throws WeixinException
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E8%8F%9C%E5%8D%95%E5%88%97%E8%A1%A8">查询菜单</a>
* @see com.foxinmy.weixin4j.model.Button
*/
public List<Button> getMenu(int agentid) throws WeixinException {
String menu_get_uri = getRequestUri("menu_get_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(menu_get_uri,
token.getAccessToken(), agentid));
String text = JSON.toJSONString(
response.getAsJson().getJSONObject("menu")
.getJSONArray("button"), new NameFilter() {
@Override
public String process(Object object, String name,
Object value) {
if (name.equals("url") || name.equals("key")) {
return "content";
}
return name;
}
});
return JSON.parseArray(text, Button.class);
}
/**
* 删除菜单(管理员须拥有应用的管理权限 并且应用必须设置在回调模式)
*
* @param agentid
* 应用ID
* @throws WeixinException
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%88%A0%E9%99%A4%E8%8F%9C%E5%8D%95">删除菜单</a>
* @return 处理结果
*/
public JsonResult deleteMenu(int agentid) throws WeixinException {
String menu_delete_uri = getRequestUri("menu_delete_uri");
Token token = tokenHolder.getToken();
Response response = request.get(String.format(menu_delete_uri,
token.getAccessToken(), agentid));
return response.getAsJsonResult();
}
}
@@ -1,86 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.msg.model.Base;
import com.foxinmy.weixin4j.msg.model.Notifyable;
import com.foxinmy.weixin4j.qy.message.NotifyMessage;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 发送消息API
*
* @className NotifyApi
* @author jy.hu
* @date 2014年11月21日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E">发送接口说明</a>
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E6%B6%88%E6%81%AF%E7%B1%BB%E5%9E%8B%E5%8F%8A%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F">发送格式说明</a>
* @see com.foxinmy.weixin4j.msg.model.Text
* @see com.foxinmy.weixin4j.msg.model.Image
* @see com.foxinmy.weixin4j.msg.model.Voice
* @see com.foxinmy.weixin4j.msg.model.Video
* @see com.foxinmy.weixin4j.msg.model.File
* @see com.foxinmy.weixin4j.msg.model.News
* @see com.foxinmy.weixin4j.msg.model.MpNews
*/
public class NotifyApi extends QyApi {
private final TokenHolder tokenHolder;
public NotifyApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 发送消息(需要管理员对应用有使用权限,对收件人touser、toparty、totag有查看权限,否则本次调用失败)
* <p>
* 1) 发送人员列表存在错误的userid:执行发送,开发者需注意返回结果说明</br>
* 2)发送人员不在通讯录权限范围内:不执行发送任务,返回首个出错的userid</br>
* 3)发送人员不在应用可见范围内:不执行发送任务,返回首个出错的userid</br>
* </p>
*
* @param notify
* 客服消息对象
* @return
* 如果对应用或收件人、部门、标签任何一个无权限,则本次发送失败;如果收件人、部门或标签不存在,发送仍然执行,但返回无效的部分</br>
* { "errcode": 0, "errmsg": "ok", "invaliduser": "UserID1",
* "invalidparty":"PartyID1", "invalidtag":"TagID1" }
* @throws WeixinException
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E">发送接口说明</a>
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E6%B6%88%E6%81%AF%E7%B1%BB%E5%9E%8B%E5%8F%8A%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F">发送格式说明</a>
* @see com.foxinmy.weixin4j.msg.model.Text
* @see com.foxinmy.weixin4j.msg.model.Image
* @see com.foxinmy.weixin4j.msg.model.Voice
* @see com.foxinmy.weixin4j.msg.model.Video
* @see com.foxinmy.weixin4j.msg.model.File
* @see com.foxinmy.weixin4j.msg.model.News
* @see com.foxinmy.weixin4j.msg.model.MpNews
* @see com.foxinmy.weixin4j.qy.message.NotifyMessage
*/
public JSONObject sendNotify(NotifyMessage notify) throws WeixinException {
Base box = notify.getBox();
if (!(box instanceof Notifyable)) {
throw new WeixinException(String.format(
"%s not implement Notifyable", box.getClass()));
}
String msgtype = box.getMediaType().name();
JSONObject obj = (JSONObject) JSON.toJSON(notify);
obj.put("msgtype", msgtype);
obj.put(msgtype, box);
String message_send_uri = getRequestUri("message_send_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(message_send_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJson();
}
}
@@ -1,111 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.model.Party;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 部门API
*
* @className PartyApi
* @author jy
* @date 2014年11月18日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.qy.model.Party
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8">管理部门说明</a>
*/
public class PartyApi extends QyApi {
private final TokenHolder tokenHolder;
public PartyApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 创建部门(根部门的parentid为1)
*
* @param depart
* 部门对象
* @see com.foxinmy.weixin4j.qy.model.Party
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E5.88.9B.E5.BB.BA.E9.83.A8.E9.97.A8">创建部门说明</a>
* @return 部门ID
* @throws WeixinException
*/
public int createParty(Party depart) throws WeixinException {
String department_create_uri = getRequestUri("department_create_uri");
JSONObject obj = (JSONObject) JSON.toJSON(depart);
obj.remove("id");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(department_create_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJson().getIntValue("id");
}
/**
* 更新部门(如果非必须的字段未指定 则不更新该字段之前的设置值)
*
* @param depart
* 部门对象
* @see com.foxinmy.weixin4j.qy.model.Party
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E6.9B.B4.E6.96.B0.E9.83.A8.E9.97.A8">更新部门说明</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult updateParty(Party party) throws WeixinException {
String department_update_uri = getRequestUri("department_update_uri");
JSONObject obj = (JSONObject) JSON.toJSON(party);
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(department_update_uri, token.getAccessToken()),
obj.toJSONString());
return response.getAsJsonResult();
}
/**
* 查询部门列表(以部门的order字段从小到大排列)
*
* @param partId 部门ID。获取指定部门ID下的子部门
* @see com.foxinmy.weixin4j.qy.model.Party
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E5.88.97.E8.A1.A8">获取部门列表</a>
* @return 部门列表
* @throws WeixinException
*/
public List<Party> listParty(int partId) throws WeixinException {
String department_list_uri = getRequestUri("department_list_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(department_list_uri,
token.getAccessToken()));
return JSON.parseArray(response.getAsJson().getString("department"),
Party.class);
}
/**
* 删除部门(不能删除根部门;不能删除含有子部门、成员的部门)
*
* @param partId
* 部门ID
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8#.E5.88.A0.E9.99.A4.E9.83.A8.E9.97.A8">删除部门说明</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult deleteParty(int partId) throws WeixinException {
String department_delete_uri = getRequestUri("department_delete_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(department_delete_uri,
token.getAccessToken(), partId));
return response.getAsJsonResult();
}
}
@@ -1,28 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import java.util.ResourceBundle;
import com.foxinmy.weixin4j.api.BaseApi;
/**
* 微信企业号API
*
* @className QyApi
* @author jy.hu
* @date 2014年11月18日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.api.BaseApi
* @see <a href="http://qydev.weixin.qq.com/wiki/index.php">api文档</a>
*/
public class QyApi extends BaseApi {
private final static ResourceBundle weixinBundle;
static {
weixinBundle = ResourceBundle
.getBundle("com/foxinmy/weixin4j/qy/api/weixin");
}
@Override
protected ResourceBundle getWeixinBundle() {
return weixinBundle;
}
}
@@ -1,178 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.model.Tag;
import com.foxinmy.weixin4j.qy.model.User;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 标签API
*
* @className TagApi
* @author jy
* @date 2014年11月19日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE">管理标签</a>
*/
public class TagApi extends QyApi {
private final TokenHolder tokenHolder;
public TagApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 创建标签(创建的标签属于管理组;默认为未加锁状态)
*
* @param tagName
* 标签名称
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E5.88.9B.E5.BB.BA.E6.A0.87.E7.AD.BE">创建标签说明</a>
* @return 标签ID
* @throws WeixinException
*/
public int createTag(String tagName) throws WeixinException {
String tag_create_uri = getRequestUri("tag_create_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(tag_create_uri, token.getAccessToken()),
String.format("{\"tagname\":\"%s\"}", tagName));
return response.getAsJson().getIntValue("tagid");
}
/**
* 更新标签(管理组必须是指定标签的创建者)
*
* @param tag 标签信息
* @see <a href=
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E6.9B.B4.E6.96.B0.E6.A0.87.E7.AD.BE.E5.90.8D.E5.AD.97"
* >更新标签说明</a>
* @return 处理结果
* @see com.foxinmy.weixin4j.qy.model.Tag
* @throws WeixinException
*/
public JsonResult updateTag(Tag tag) throws WeixinException {
String tag_update_uri = getRequestUri("tag_update_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(tag_update_uri, token.getAccessToken()),
JSON.toJSONString(tag));
return response.getAsJsonResult();
}
/**
* 删除标签(管理组必须是指定标签的创建者 并且标签的成员列表为空)
*
* @param tagId
* 标签ID
* @return 处理结果
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E5.88.A0.E9.99.A4.E6.A0.87.E7.AD.BE">删除标签说明</a>
* @throws WeixinException
*/
public JsonResult deleteTag(int tagId) throws WeixinException {
String tag_delete_uri = getRequestUri("tag_delete_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(tag_delete_uri,
token.getAccessToken(), tagId));
return response.getAsJsonResult();
}
/**
* 获取标签列表
*
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E8.8E.B7.E5.8F.96.E6.A0.87.E7.AD.BE.E5.88.97.E8.A1.A8">获取标签列表说明</a>
* @return 标签列表
* @see com.foxinmy.weixin4j.qy.model.Tag
* @throws WeixinException
*/
public List<Tag> listTag() throws WeixinException {
String tag_list_uri = getRequestUri("tag_list_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(tag_list_uri,
token.getAccessToken()));
return JSON.parseArray(response.getAsJson().getString("taglist"),
Tag.class);
}
/**
* 获取标签成员(管理组须拥有“获取标签成员”的接口权限,标签须对管理组可见;返回列表仅包含管理组管辖范围的成员)
*
* @param tagId
* 标签ID
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E8.8E.B7.E5.8F.96.E6.A0.87.E7.AD.BE.E6.88.90.E5.91.98">获取标签成员说明</a>
* @return 成员列表
* @throws WeixinException
*/
public List<User> getTagUsers(int tagId) throws WeixinException {
String tag_get_user_uri = getRequestUri("tag_get_user_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(tag_get_user_uri,
token.getAccessToken(), tagId));
return JSON.parseArray(response.getAsJson().getString("userlist"),
User.class);
}
/**
* 新增标签成员(标签对管理组可见且未加锁,成员属于管理组管辖范围)<br>
* <font color="red">若部分userid非法,则在text中体现</font>
*
* @param tagId
* 标签ID
* @param userIds
* 成员ID
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E5.A2.9E.E5.8A.A0.E6.A0.87.E7.AD.BE.E6.88.90.E5.91.98">新增标签成员说明</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult addTagUsers(int tagId, List<String> userIds)
throws WeixinException {
String tag_add_user_uri = getRequestUri("tag_add_user_uri");
return excuteUsers(tag_add_user_uri, tagId, userIds);
}
/**
* 删除标签成员(标签对管理组可见且未加锁,成员属于管理组管辖范围)<br>
* <font color="red">若部分userid非法,则在text中体现</font>
*
* @param tagId
* 标签ID
* @param userIds
* 成员ID
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%A0%87%E7%AD%BE#.E5.88.A0.E9.99.A4.E6.A0.87.E7.AD.BE.E6.88.90.E5.91.98">删除标签成员说明</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult deleteTagUsers(int tagId, List<String> userIds)
throws WeixinException {
String tag_delete_user_uri = getRequestUri("tag_delete_user_uri");
return excuteUsers(tag_delete_user_uri, tagId, userIds);
}
private JsonResult excuteUsers(String uri, int tagId, List<String> userIds)
throws WeixinException {
JSONObject obj = new JSONObject();
obj.put("tagid", tagId);
obj.put("userlist", userIds);
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(uri, token.getAccessToken()), obj.toJSONString());
obj = response.getAsJson();
JsonResult result = JSON.toJavaObject(obj, JsonResult.class);
result.setText(obj.getString("invalidlist"));
return result;
}
}
@@ -1,280 +0,0 @@
package com.foxinmy.weixin4j.qy.api;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.qy.model.User;
import com.foxinmy.weixin4j.qy.type.InviteType;
import com.foxinmy.weixin4j.qy.type.UserStatus;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 成员API
*
* @className UserApi
* @author jy
* @date 2014年11月19日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98">管理成员说明</a>
*/
public class UserApi extends QyApi {
private final TokenHolder tokenHolder;
public UserApi(TokenHolder tokenHolder) {
this.tokenHolder = tokenHolder;
}
/**
* 创建成员
*
* @param user
* 成员对象
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E5.88.9B.E5.BB.BA.E6.88.90.E5.91.98">创建成员说明</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult createUser(User user) throws WeixinException {
String user_create_uri = getRequestUri("user_create_uri");
return excute(user_create_uri, user);
}
/**
* 更新用户(如果非必须的字段未指定 则不更新该字段之前的设置值)
*
* @param user
* 成员对象
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E6.9B.B4.E6.96.B0.E6.88.90.E5.91.98">更新成员说明</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult updateUser(User user) throws WeixinException {
String user_update_uri = getRequestUri("user_update_uri");
return excute(user_update_uri, user);
}
private JsonResult excute(String uri, User user) throws WeixinException {
JSONObject obj = (JSONObject) JSON.toJSON(user);
Object extattr = obj.remove("extattr");
if (extattr != null) {
JSONObject attrs = new JSONObject();
attrs.put("attrs", extattr);
obj.put("extattr", attrs);
}
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(uri, token.getAccessToken()), obj.toJSONString());
return response.getAsJsonResult();
}
/**
* 获取成员
*
* @param userid
* 成员唯一ID
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E8.8E.B7.E5.8F.96.E6.88.90.E5.91.98">获取成员说明</a>
* @return 成员对象
* @throws WeixinException
*/
public User getUser(String userid) throws WeixinException {
String user_get_uri = getRequestUri("user_get_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(user_get_uri,
token.getAccessToken(), userid));
JSONObject obj = response.getAsJson();
Object attrs = obj.getJSONObject("extattr").remove("attrs");
if (attrs != null) {
obj.put("extattr", attrs);
}
return JSON.toJavaObject(obj, User.class);
}
/**
* code获取userid(管理员须拥有agent的使用权限;agentid必须和跳转链接时所在的企业应用ID相同。)
*
* @param code
* 通过员工授权获取到的code,每次员工授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期
* @param agentid
* 跳转链接时所在的企业应用ID
* @see com.foxinmy.weixin4j.qy.api.UserApi
* @return 成员对象
* @see {@link com.foxinmy.weixin4j.qy.api.UserApi#getUser(String)}
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BC%81%E4%B8%9A%E8%8E%B7%E5%8F%96code">企业获取code</a>
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E6%A0%B9%E6%8D%AEcode%E8%8E%B7%E5%8F%96%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF">根据code获取成员信息</a>
* @throws WeixinException
*/
public User getUserByCode(String code, int agentid) throws WeixinException {
String user_getid_uri = getRequestUri("user_getid_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(user_getid_uri,
token.getAccessToken(), code, agentid));
return getUser(response.getAsJson().getString("UserId"));
}
/**
* 获取部门成员
*
* @param departId
* 部门ID 必须
* @param fetchChild
* 是否递归获取子部门下面的成员 非必须
* @param userStatus
* 成员状态 status可叠加 非必须
* @param findDetail
* 是否获取详细信息
* @see com.foxinmy.weixin4j.qy.model.User
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E8.8E.B7.E5.8F.96.E9.83.A8.E9.97.A8.E6.88.90.E5.91.98">获取部门成员说明</a>
* @return 成员列表
* @throws WeixinException
*/
public List<User> listUser(int departId, boolean fetchChild,
UserStatus userStatus, boolean findDetail) throws WeixinException {
String user_list_uri = findDetail ? getRequestUri("user_list_uri")
: getRequestUri("user_slist_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(user_list_uri,
token.getAccessToken(), departId, fetchChild ? 1 : 0,
userStatus.getVal()));
List<User> list = null;
if (findDetail) {
JSONArray arrays = response.getAsJson().getJSONArray("userlist");
list = new ArrayList<User>(arrays.size());
for (int i = 0; i < arrays.size(); i++) {
JSONObject obj = arrays.getJSONObject(i);
Object attrs = obj.getJSONObject("extattr").remove("attrs");
if (attrs != null) {
obj.put("extattr", attrs);
}
list.add(JSON.toJavaObject(obj, User.class));
}
} else {
list = JSON.parseArray(response.getAsJson().getString("userlist"),
User.class);
}
return list;
}
/**
* 获取部门下所有状态成员(不进行递归)
*
* @param departId
* 部门ID
* @see {@link com.foxinmy.weixin4j.qy.api.UserApi#listUser(int, boolean,UserStatus)}
* @return 成员列表
* @throws WeixinException
*/
public List<User> listUser(int departId) throws WeixinException {
return listUser(departId, false, UserStatus.BOTH, false);
}
/**
* 删除成员
*
* @param userid
* 成员ID
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98">删除成员说明</a>
* @return 处理结果
* @throws WeixinException
*/
public JsonResult deleteUser(String userid) throws WeixinException {
String user_delete_uri = getRequestUri("user_delete_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(user_delete_uri,
token.getAccessToken(), userid));
return response.getAsJsonResult();
}
/**
* 批量删除成员
*
* @param userIds
* 成员列表
* @see <a href=
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98"
* >批量删除成员说明</a
* @return 处理结果
* @throws WeixinException
*/
public JsonResult batchDeleteUser(List<String> userIds)
throws WeixinException {
JSONObject obj = new JSONObject();
obj.put("useridlist", userIds);
String user_delete_uri = getRequestUri("user_batchdelete_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(user_delete_uri,
token.getAccessToken(), obj.toJSONString()));
return response.getAsJsonResult();
}
/**
* 开启二次验证成功时调用(管理员须拥有userid对应员工的管理权限)
*
* @param userid
* 成员ID
* @return 调用结果
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%85%B3%E6%B3%A8%E4%B8%8E%E5%8F%96%E6%B6%88%E5%85%B3%E6%B3%A8">二次验证说明</a>
* @throws WeixinException
*/
public JsonResult authsucc(String userId) throws WeixinException {
String user_authsucc_uri = getRequestUri("user_authsucc_uri");
Token token = tokenHolder.getToken();
Response response = request.post(String.format(user_authsucc_uri,
token.getAccessToken(), userId));
return response.getAsJsonResult();
}
/**
* 邀请成员关注(管理员须拥有该成员的查看权限)
*
* @param userId
* 成员ID
* @param tips
* 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。
* @return 邀请类型
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98#.E9.82.80.E8.AF.B7.E6.88.90.E5.91.98.E5.85.B3.E6.B3.A8">邀请成员关注说明</a>
* @throws WeixinException
*/
public InviteType inviteUser(String userId, String tips)
throws WeixinException {
JSONObject obj = new JSONObject();
obj.put("userid", userId);
if (StringUtils.isBlank(tips)) {
obj.put("invite_tips", tips);
}
String invite_user_uri = getRequestUri("invite_user_uri");
Token token = tokenHolder.getToken();
Response response = request.post(
String.format(invite_user_uri, token.getAccessToken()),
obj.toJSONString());
int type = response.getAsJson().getIntValue("type");
if (type == 1) {
return InviteType.WEIXIN;
} else if (type == 2) {
return InviteType.EMAIL;
} else {
return null;
}
}
}
@@ -1,81 +0,0 @@
# ----------------------------------------------------------------------------
# api\u9996\u9875
# http://qydev.weixin.qq.com/wiki/index.php
# ----------------------------------------------------------------------------
api_base_url=https://qyapi.weixin.qq.com/cgi-bin
# \u521b\u5efa\u90e8\u95e8
department_create_uri={api_base_url}/department/create?access_token=%s
# \u66f4\u65b0\u90e8\u95e8
department_update_uri={api_base_url}/department/update?access_token=%s
# \u90e8\u95e8\u5217\u8868
department_list_uri={api_base_url}/department/list?access_token=%s
# \u5220\u9664\u90e8\u95e8
department_delete_uri={api_base_url}/department/delete?access_token=%s&id=%d
# \u521b\u5efa\u6210\u5458
user_create_uri={api_base_url}/user/create?access_token=%s
# \u66f4\u65b0\u6210\u5458\u4fe1\u606f
user_update_uri={api_base_url}/user/update?access_token=%s
# \u83b7\u53d6\u6210\u5458\u4fe1\u606f
user_get_uri={api_base_url}/user/get?access_token=%s&userid=%s
# code\u83b7\u53d6\u6210\u5458\u4fe1\u606f
user_getid_uri={api_base_url}/user/getuserinfo?access_token=%s&code=%s&agentid=%d
# \u83b7\u53d6\u90e8\u95e8\u6210\u5458
user_slist_uri={api_base_url}/user/simplelist?access_token=%s&department_id=%d&fetch_child=%d&status=%d
# \u83b7\u53d6\u90e8\u95e8\u6210\u5458(\u8be6\u60c5)
user_list_uri={api_base_url}/user/list?access_token=%s&department_id=%d&fetch_child=%d&status=%d
# \u5220\u9664\u6210\u5458
user_delete_uri={api_base_url}/user/delete?access_token=%s&userid=%s
# \u6279\u91cf\u5220\u9664\u6210\u5458
user_batchdelete_uri={api_base_url}/user/batchdelete?access_token=%s
# \u6210\u5458\u4e8c\u6b21\u9a8c\u8bc1\u6210\u529f\u65f6\u8c03\u7528
user_authsucc_uri={api_base_url}/user/authsucc?access_token=%s&userid=%s
# \u521b\u5efa\u6807\u7b7e
tag_create_uri={api_base_url}/tag/create?access_token=%s
# \u66f4\u65b0\u6807\u7b7e
tag_update_uri={api_base_url}/tag/update?access_token=%s
# \u5220\u9664\u6807\u7b7e
tag_delete_uri={api_base_url}/tag/delete?access_token=%s&tagid=%d
# \u83b7\u53d6\u6807\u7b7e
tag_list_uri={api_base_url}/tag/list?access_token=%s
# \u83b7\u53d6\u6807\u7b7e\u6210\u5458
tag_get_user_uri={api_base_url}/tag/get?access_token=%s&tagid=%d
# \u6dfb\u52a0\u6807\u7b7e\u6210\u5458
tag_add_user_uri={api_base_url}/tag/addtagusers?access_token=%s
# \u5220\u9664\u6807\u7b7e\u6210\u5458
tag_delete_user_uri={api_base_url}/tag/deltagusers?access_token=%s
# \u9080\u8bf7\u6210\u5458\u5173\u6ce8
invite_user_uri={api_base_url}/invite/send?access_token=%s
# \u4e0a\u4f20\u5a92\u4f53\u6587\u4ef6
file_upload_uri={api_base_url}/media/upload?access_token=%s&type=%s
# \u4e0b\u8f7d\u5a92\u4f53\u6587\u4ef6
file_download_uri={api_base_url}/media/get?access_token=%s&media_id=%s
# \u81ea\u5b9a\u4e49\u83dc\u5355
menu_create_uri={api_base_url}/menu/create?access_token=%s&agentid=%d
# \u5220\u9664\u83dc\u5355
menu_delete_uri={api_base_url}/menu/delete?access_token=%s&agentid=%d
# \u67e5\u8be2\u83dc\u5355
menu_get_uri={api_base_url}/menu/get?access_token=%s&agentid=%d
# \u53d1\u9001\u6d88\u606f
message_send_uri={api_base_url}/message/send?access_token=%s
# \u83b7\u53d6\u5fae\u4fe1IP\u5730\u5740
getcallbackip_uri={api_base_url}/getcallbackip?access_token=%s
# \u83b7\u53d6\u4f01\u4e1a\u53f7\u5e94\u7528\u4fe1\u606f
agent_get_uri={api_base_url}/agent/get?access_token=%s&agentid=%d
# \u8bbe\u7f6e\u4f01\u4e1a\u53f7\u5e94\u7528\u4fe1\u606f
agent_set_uri={api_base_url}/agent/set?access_token=%s
# \u83b7\u53d6\u5e94\u7528\u5217\u8868
agent_list_uri={api_base_url}/agent/list?access_token=%s
# \u6279\u91cf\u9080\u8bf7\u6210\u5458\u5173\u6ce8
batch_inviteuser_uri={api_base_url}/batch/inviteuser?access_token=%s
# \u6279\u91cf\u66f4\u65b0\u6210\u5458
batch_syncuser_uri={api_base_url}/batch/syncuser?access_token=%s
batch_syncuser.cvs={"header":"\u59d3\u540d,\u8d26\u53f7,\u5fae\u4fe1\u53f7,\u624b\u673a\u53f7,\u90ae\u7bb1,\u6240\u5728\u90e8\u95e8,\u804c\u4f4d","column":["name","userid","weixinid","mobile","email","department","position"]}
# \u6279\u91cf\u8986\u76d6\u6210\u5458
batch_replaceuser_uri={api_base_url}/batch/replaceuser?access_token=%s
# \u6279\u91cf\u8986\u76d6\u90e8\u95e8
batch_replaceparty_uri={api_base_url}/batch/replaceparty?access_token=%s
batch_replaceparty.cvs={"header":"\u90e8\u95e8\u540d\u79f0,\u90e8\u95e8ID,\u7236\u90e8\u95e8ID,\u6392\u5e8f","column":["name","id","parentid","order"]}
# \u83b7\u53d6\u5f02\u6b65\u4efb\u52a1\u6267\u884c\u7ed3\u679c
batch_getresult_uri={api_base_url}/batch/getresult?access_token=%s&jobid=%s
@@ -1,156 +0,0 @@
package com.foxinmy.weixin4j.qy.message;
import java.io.Serializable;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.msg.model.Base;
/**
* 发送消息对象
*
* @className NotifyMessage
* @author jy
* @date 2014年11月22日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.model.Text
* @see com.foxinmy.weixin4j.msg.model.Image
* @see com.foxinmy.weixin4j.msg.model.Voice
* @see com.foxinmy.weixin4j.msg.model.Video
* @see com.foxinmy.weixin4j.msg.model.File
* @see com.foxinmy.weixin4j.msg.model.News
* @see com.foxinmy.weixin4j.msg.model.MpNews
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E6%B6%88%E6%81%AF%E7%B1%BB%E5%9E%8B%E5%8F%8A%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F">消息说明</a>
*/
public class NotifyMessage implements Serializable {
private static final long serialVersionUID = 1219589414293000383L;
private static final String SEPARATOR = "|";
/**
* UserID列表(消息接收者,多个接收者用‘|’分隔)。特殊情况:指定为@all,则向关注该企业应用的全部成员发送
*/
private String touser;
/**
* PartyID列表,多个接受者用‘|’分隔。当touser为@all时忽略本参数
*/
private String toparty;
/**
* TagID列表,多个接受者用‘|’分隔。当touser为@all时忽略本参数
*/
private String totag;
/**
* 企业应用的id,整型。可在应用的设置页面查看
*/
private int agentid;
/**
* 表示是否是保密消息,0表示否,1表示是,默认0
*/
private int safe;
/**
* 消息对象
*/
@JSONField(serialize = false)
private Base box;
public NotifyMessage(Base box, int agentid) {
this(null, null, null, box, agentid, false);
this.touser = "@all";
}
public NotifyMessage(List<String> tousers, List<String> topartys,
List<String> totags, Base box, int agentid, boolean isSafe) {
if (tousers != null && !tousers.isEmpty()) {
this.touser = StringUtils.join(tousers, SEPARATOR);
}
if (topartys != null && !topartys.isEmpty()) {
this.toparty = StringUtils.join(topartys, SEPARATOR);
}
if (totags != null && !totags.isEmpty()) {
this.totag = StringUtils.join(totags, SEPARATOR);
}
this.agentid = agentid;
this.safe = isSafe ? 1 : 0;
this.box = box;
}
public String getTouser() {
return touser;
}
public void setTouser(String touser) {
this.touser = touser;
}
public void setTouser(List<String> tousers) {
if (tousers != null && !tousers.isEmpty()) {
this.touser = StringUtils.join(tousers, SEPARATOR);
}
}
public String getToparty() {
return toparty;
}
public void setToparty(String toparty) {
this.toparty = toparty;
}
public void setToparty(List<String> topartys) {
if (topartys != null && !topartys.isEmpty()) {
this.toparty = StringUtils.join(topartys, SEPARATOR);
}
}
public String getTotag() {
return totag;
}
public void setTotag(String totag) {
this.totag = totag;
}
public void setTotag(List<String> totags) {
if (totags != null && !totags.isEmpty()) {
this.totag = StringUtils.join(totags, SEPARATOR);
}
}
public int getAgentid() {
return agentid;
}
public void setAgentid(int agentid) {
this.agentid = agentid;
}
public int getSafe() {
return safe;
}
public void setSafe(int safe) {
this.safe = safe;
}
public void setSafe(boolean isSafe) {
this.safe = isSafe ? 1 : 0;
}
public Base getBox() {
return box;
}
public void setBox(Base box) {
this.box = box;
}
@Override
public String toString() {
return "NotifyMessage [touser=" + touser + ", toparty=" + toparty
+ ", totag=" + totag + ", agentid=" + agentid + ", safe="
+ safe + ", box=" + box + "]";
}
}
@@ -1,109 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 企业号应用的设置信息
*
* @className AgentInfo
* @author jy
* @date 2015年3月17日
* @since JDK 1.7
* @see
*/
public class AgentInfo extends AgentSetter {
private static final long serialVersionUID = -8975132919768696174L;
/**
* 企业应用方形头像
*/
@JSONField(name = "square_logo_url")
private String squareLogoUrl;
/**
* 企业应用圆形头像
*/
@JSONField(name = "round_logo_url")
private String roundLogoUrl;
/**
* 企业应用可见范围(人员),其中包括userid和关注状态state
*/
@JSONField(serialize = false)
private List<User> allowUsers;
/**
* 企业应用可见范围(部门)
*/
@JSONField(serialize = false)
private List<Integer> allowPartys;
/**
* 企业应用可见范围(标签)
*/
@JSONField(serialize = false)
private List<Integer> allowTags;
/**
* 企业应用是否被禁用
*/
private boolean close;
public AgentInfo() {
super(0);
}
public String getSquareLogoUrl() {
return squareLogoUrl;
}
public void setSquareLogoUrl(String squareLogoUrl) {
this.squareLogoUrl = squareLogoUrl;
}
public String getRoundLogoUrl() {
return roundLogoUrl;
}
public void setRoundLogoUrl(String roundLogoUrl) {
this.roundLogoUrl = roundLogoUrl;
}
public List<User> getAllowUsers() {
return allowUsers;
}
public void setAllowUsers(List<User> allowUsers) {
this.allowUsers = allowUsers;
}
public List<Integer> getAllowPartys() {
return allowPartys;
}
public void setAllowPartys(List<Integer> allowPartys) {
this.allowPartys = allowPartys;
}
public List<Integer> getAllowTags() {
return allowTags;
}
public void setAllowTags(List<Integer> allowTags) {
this.allowTags = allowTags;
}
public boolean isClose() {
return close;
}
public void setClose(boolean close) {
this.close = close;
}
@Override
public String toString() {
return "AgentInfo [squareLogoUrl=" + squareLogoUrl + ", roundLogoUrl="
+ roundLogoUrl + ", allowUsers=" + allowUsers
+ ", allowPartys=" + allowPartys + ", allowTags=" + allowTags
+ ", close=" + close + ", " + super.toString() + "]";
}
}
@@ -1,67 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 企业号应用的概况信息
* @className AgentOverview
* @author jy
* @date 2015年4月9日
* @since JDK 1.7
* @see
*/
public class AgentOverview implements Serializable {
private static final long serialVersionUID = 5459274811502476460L;
/**
* 企业应用的id
*/
private int agentid;
/**
* 企业应用名称
*/
private String name;
/**
* 企业应用方形头像
*/
@JSONField(name = "square_logo_url")
private String squareLogoUrl;
/**
* 企业应用圆形头像
*/
@JSONField(name = "round_logo_url")
private String roundLogoUrl;
public int getAgentid() {
return agentid;
}
public void setAgentid(int agentid) {
this.agentid = agentid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSquareLogoUrl() {
return squareLogoUrl;
}
public void setSquareLogoUrl(String squareLogoUrl) {
this.squareLogoUrl = squareLogoUrl;
}
public String getRoundLogoUrl() {
return roundLogoUrl;
}
public void setRoundLogoUrl(String roundLogoUrl) {
this.roundLogoUrl = roundLogoUrl;
}
@Override
public String toString() {
return "AgentOverview [agentid=" + agentid + ", name=" + name
+ ", squareLogoUrl=" + squareLogoUrl + ", roundLogoUrl="
+ roundLogoUrl + "]";
}
}
@@ -1,135 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.qy.type.ReportLocationType;
/**
* 设置企业号应用
*
* @className AgentSetter
* @author jy
* @date 2015年3月16日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E8%AE%BE%E7%BD%AE%E4%BC%81%E4%B8%9A%E5%8F%B7%E5%BA%94%E7%94%A8">设置企业号应用</a>
*/
public class AgentSetter implements Serializable {
private static final long serialVersionUID = 5420335232308079801L;
/**
* 企业应用的id
*/
private int agentid;
/**
* 企业应用是否打开地理位置上报
*/
@JSONField(name = "report_location_flag")
private ReportLocationType reportLocationType;
/**
* 企业应用头像的mediaid,通过多媒体接口上传图片获得mediaid,上传后会自动裁剪成方形和圆形两个头像
*/
@JSONField(name = "logo_mediaid")
private String logoMediaid;
/**
* 企业应用名称
*/
private String name;
/**
* 企业应用详情
*/
private String description;
/**
* 企业应用可信域名
*/
@JSONField(name = "redirect_domain")
private String redirectDomain;
/**
* 是否接收用户变更通知。0:不接收;1:接收
*/
@JSONField(name = "isreportuser")
private int isReportUser;
/**
* 是否上报用户进入应用事件。0:不接收;1:接收
*/
@JSONField(name = "isreportenter")
private int isReportEnter;
public AgentSetter(int agentid) {
this.agentid = agentid;
}
public int getAgentid() {
return agentid;
}
public void setAgentid(int agentid) {
this.agentid = agentid;
}
public ReportLocationType getReportLocationType() {
return reportLocationType;
}
public void setReportLocationType(ReportLocationType reportLocationType) {
this.reportLocationType = reportLocationType;
}
public String getLogoMediaid() {
return logoMediaid;
}
public void setLogoMediaid(String logoMediaid) {
this.logoMediaid = logoMediaid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getRedirectDomain() {
return redirectDomain;
}
public void setRedirectDomain(String redirectDomain) {
this.redirectDomain = redirectDomain;
}
public boolean getIsReportUser() {
return isReportUser == 1;
}
public void setIsReportUser(boolean isReportUser) {
this.isReportUser = isReportUser ? 1 : 0;
}
public boolean getIsReportEnter() {
return isReportEnter == 1;
}
public void setIsReportEnter(boolean isReportEnter) {
this.isReportEnter = isReportEnter ? 1 : 0;
}
@Override
public String toString() {
return "agentid=" + agentid + ", reportLocationType="
+ reportLocationType + ", logoMediaid=" + logoMediaid
+ ", name=" + name + ", description=" + description
+ ", redirectDomain=" + redirectDomain + ", isReportUser="
+ isReportUser + ", isReportEnter=" + isReportEnter;
}
}
@@ -1,99 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import com.alibaba.fastjson.JSONArray;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.qy.type.BatchStatus;
import com.foxinmy.weixin4j.qy.type.BatchType;
/**
* 异步任务执行结果
*
* @className BatchResult
* @author jy
* @date 2015年3月31日
* @since JDK 1.7
* @see
*/
public class BatchResult extends JsonResult {
private static final long serialVersionUID = 4985338631992208903L;
/**
* 任务状态
*/
private int status;
/**
* 任务类型
*/
private String type;
/**
* 任务运行总条数
*/
private int total;
/**
* 目前运行百分比,当任务完成时为100
*/
private int percentage;
/**
* 预估剩余时间(单位:分钟),当任务完成时为0
*/
private int remaintime;
/**
* 详细的处理结果 TODO
*/
private JSONArray result;
public BatchStatus getStatus() {
return BatchStatus.values()[status - 1];
}
public void setStatus(int status) {
this.status = status;
}
public BatchType getType() {
return BatchType.valueOf(type.toUpperCase());
}
public void setType(String type) {
this.type = type;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getPercentage() {
return percentage;
}
public void setPercentage(int percentage) {
this.percentage = percentage;
}
public int getRemaintime() {
return remaintime;
}
public void setRemaintime(int remaintime) {
this.remaintime = remaintime;
}
public JSONArray getResult() {
return result;
}
public void setResult(JSONArray result) {
this.result = result;
}
@Override
public String toString() {
return "BatchResult [status=" + getStatus() + ", type=" + getType() + ", total="
+ total + ", percentage=" + percentage + ", remaintime="
+ remaintime + ", result=" + result + "]";
}
}
@@ -1,52 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 调用某些接口时填入的回调信息
*
* @className Callback
* @author jy
* @date 2015年3月30日
* @since JDK 1.7
* @see
*/
public class Callback implements Serializable {
private static final long serialVersionUID = 8575808461248605317L;
/**
* 企业应用接收企业号推送请求的访问协议和地址,支持http或https协议
*/
private String url;
/**
* 用于生成签名
*/
private String token;
/**
* 用于消息体的加密,是AES密钥的Base64编码
*/
@JSONField(name = "encodingaeskey")
private String aesKey;
public Callback(String url, String token, String aesKey) {
this.url = url;
this.token = token;
this.aesKey = aesKey;
}
public String getUrl() {
return url;
}
public String getToken() {
return token;
}
public String getAesKey() {
return aesKey;
}
}
@@ -1,64 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
/**
* id参数集
*
* @className IdParameter
* @author jy
* @date 2015年3月30日
* @since JDK 1.7
* @see
*/
public class IdParameter implements Serializable {
private static final long serialVersionUID = -2689758682205591133L;
private Map<String, String> parameterMap;
public IdParameter() {
this.parameterMap = new HashMap<String, String>();
}
/**
* 成员ID列表,最多支持1000个
*
* @param userIds
* @return
*/
public IdParameter putUseIds(String... userIds) {
parameterMap.put("touser", StringUtils.join(userIds, '|'));
return this;
}
/**
* 部门ID列表,最多支持100个
*
* @param partyIds
* @return
*/
public IdParameter putUseIds(int... partyIds) {
parameterMap.put("toparty", StringUtils.join(partyIds, '|'));
return this;
}
/**
* 标签ID列表
*
* @param tagIds
* @return
*/
public IdParameter putTagIds(int... tagIds) {
parameterMap.put("totag", StringUtils.join(tagIds, '|'));
return this;
}
public Map<String, String> getParameter() {
return parameterMap;
}
}
@@ -1,48 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.io.Serializable;
/**
* name-value
* @className NameValue
* @author jy
* @date 2015年3月29日
* @since JDK 1.7
* @see
*/
public class NameValue implements Serializable {
private static final long serialVersionUID = -348620146718819093L;
private String name;
private String value;
public NameValue() {
}
public NameValue(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public void setName(String name) {
this.name = name;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return "NameValue [name=" + name + ", value=" + value + "]";
}
}
@@ -1,87 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.io.Serializable;
/**
* 部门对象
*
* @className Party
* @author jy
* @date 2014年11月18日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E9%83%A8%E9%97%A8">管理部门说明</a>
*/
public class Party implements Serializable {
private static final long serialVersionUID = -2567893218591084288L;
/**
* 部门ID
*/
private int id;
/**
* 部门名称。长度限制为1~64个字符
*/
private String name;
/**
* 父亲部门id。根部门id为1
*/
private int parentid;
/**
* 在父部门中的次序。从1开始,数字越大排序越靠后
*/
private int order;
public Party() {
}
public Party(String name) {
this(0, name, 1, 1);
}
public Party(int id, String name, int parentid, int order) {
this.id = id;
this.name = name;
this.parentid = parentid;
this.order = order;
}
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 getParentid() {
return parentid;
}
public void setParentid(int parentid) {
this.parentid = parentid;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public String toString() {
return "Party [id=" + id + ", name=" + name + ", parentid=" + parentid
+ ", order=" + order + "]";
}
}
@@ -1,60 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
/**
* 标签对象
*
* @className Tag
* @author jy
* @date 2014年11月24日
* @since JDK 1.7
* @see
*/
public class Tag implements Serializable {
private static final long serialVersionUID = 5204620476267654921L;
/**
* 标签ID
*/
@JSONField(name = "tagid")
private int id;
/**
* 标签名称
*/
@JSONField(name = "tagname")
private String name;
public Tag() {
}
public Tag(int id, String name) {
this.id = id;
this.name = name;
}
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;
}
@Override
public String toString() {
return "Tag [id=" + id + ", name=" + name + "]";
}
}
@@ -1,262 +0,0 @@
package com.foxinmy.weixin4j.qy.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import com.foxinmy.weixin4j.model.Gender;
import com.foxinmy.weixin4j.qy.type.UserStatus;
/**
* 部门成员对象
*
* @className User
* @author jy
* @date 2014年11月19日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E7%AE%A1%E7%90%86%E6%88%90%E5%91%98">管理成员说明</a>
*/
public class User implements Serializable {
private static final long serialVersionUID = 4747301605060801611L;
/**
* 必须 员工UserID。对应管理端的帐号,企业内必须唯一。长度为1~64个字符
*/
private String userid;
/**
* 必须 成员名称。长度为1~64个字符
*/
private String name;
/**
* 非必须 成员所属部门id列表。注意,每个部门的直属员工上限为1000个
*/
private List<Integer> department;
/**
* 非必须 职位信息。长度为0~64个字符
*/
private String position;
/**
* 非必须 手机号码。企业内必须唯一,mobile/weixinid/email三者不能同时为空
*/
private String mobile;
/**
* 非必须 性别。gender=0表示男,=1表示女。默认gender=0
*/
private int gender;
/**
* 非必须 办公电话。长度为0~64个字符
*/
private String tel;
/**
* 非必须 邮箱。长度为0~64个字符。企业内必须唯一
*/
private String email;
/**
* 非必须 微信号。企业内必须唯一
*/
private String weixinid;
/**
* 头像url。注:如果要获取小图将url最后的"/0"改成"/64"即可
*/
private String avatar;
/**
* 关注状态: 1=已关注,2=已冻结,4=未关注
*/
private int status;
/**
* 启用/禁用成员。1表示启用成员,0表示禁用成员
*/
private int enable;
/**
* 非必须 扩展属性。扩展属性需要在WEB管理端创建后才生效,否则忽略未知属性的赋值
*/
private List<NameValue> extattr;
public User() {
this.extattr = new ArrayList<NameValue>();
}
public User(String userid, String name) {
this(userid, name, null, null, null);
}
/**
* mobile/weixinid/email三者不能同时为空
*
* @param userid
* 用户ID
* @param name
* 用户昵称
* @param tel
* 号码
* @param email
* 邮箱
* @param weixinid
* 微信ID
*/
public User(String userid, String name, String tel, String email,
String weixinid) {
this.userid = userid;
this.name = name;
this.tel = tel;
this.email = email;
this.weixinid = weixinid;
this.extattr = new ArrayList<NameValue>();
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Integer> getDepartment() {
return department;
}
public void setDepartment(List<Integer> department) {
this.department = department;
}
public void setDepartment(Integer... department) {
this.department = Arrays.asList(department);
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public int getGender() {
return gender;
}
@JSONField(serialize = false)
public Gender getFormatGender() {
if (gender == 0) {
return Gender.male;
} else if (gender == 1) {
return Gender.female;
} else {
return Gender.unknown;
}
}
public void setGender(int gender) {
this.gender = gender;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getWeixinid() {
return weixinid;
}
public void setWeixinid(String weixinid) {
this.weixinid = weixinid;
}
@JSONField(serialize = false)
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
@JSONField(serialize = false, deserialize = false)
public UserStatus getFormatStatus() {
for (UserStatus userStatus : UserStatus.values()) {
if (userStatus.getVal() == status) {
return userStatus;
}
}
return null;
}
public void setStatus(int status) {
this.status = status;
}
public int getEnable() {
return enable;
}
@JSONField(serialize = false, deserialize = false)
public boolean getFormatEnable() {
return enable == 1;
}
public void setEnable(int enable) {
this.enable = enable;
}
public List<NameValue> getExtattr() {
return extattr;
}
public void setExtattr(List<NameValue> extattr) {
this.extattr = extattr;
}
public void setExtattr(NameValue... extattr) {
this.extattr = Arrays.asList(extattr);
}
public void pushExattr(String name, String value) {
pushExattr(new NameValue(name, value));
}
public void pushExattr(NameValue nameValue) {
extattr.add(nameValue);
}
@Override
public String toString() {
return "User [userid=" + userid + ", name=" + name + ", department="
+ department + ", position=" + position + ", mobile=" + mobile
+ ", gender=" + getFormatGender() + ", tel=" + tel + ", email="
+ email + ", weixinid=" + weixinid + ", avatar=" + avatar
+ ", status=" + getFormatStatus() + ", enable="
+ getFormatEnable() + ", extattr=" + extattr + "]";
}
}
@@ -1,55 +0,0 @@
package com.foxinmy.weixin4j.qy.token;
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.Consts;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* 微信企业号JSTICKET创建者
*
* @className WeixinJSTicketCreator
* @author jy
* @date 2015年1月10日
* @since JDK 1.7
* @see <a
* href="http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95">JS
* TICKET</a>
*/
public class WeixinJSTicketCreator implements TokenCreator {
private final String appid;
private final TokenHolder weixinTokenHolder;
private final HttpRequest request;
/**
* <font color="red">企业号的的access_token</font>
*
* @param weixinTokenHolder
*/
public WeixinJSTicketCreator(String appid, TokenHolder weixinTokenHolder) {
this.appid = appid;
this.weixinTokenHolder = weixinTokenHolder;
this.request = new HttpRequest();
}
@Override
public String getCacheKey() {
return String.format("qy_jsticket_%s", appid);
}
@Override
public Token createToken() throws WeixinException {
Response response = request.get(String.format(Consts.QY_JS_TICKET_URL,
weixinTokenHolder.getToken().getAccessToken()));
JSONObject result = response.getAsJson();
Token token = new Token(result.getString("ticket"));
token.setExpiresIn(result.getIntValue("expires_in"));
token.setTime(System.currentTimeMillis());
return token;
}
}
@@ -1,58 +0,0 @@
package com.foxinmy.weixin4j.qy.token;
import com.alibaba.fastjson.TypeReference;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.HttpRequest;
import com.foxinmy.weixin4j.http.Response;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.model.Token;
import com.foxinmy.weixin4j.model.WeixinQyAccount;
import com.foxinmy.weixin4j.token.TokenCreator;
import com.foxinmy.weixin4j.util.ConfigUtil;
/**
* 微信企业号TOKEN创建者
*
* @className WeixinTokenCreator
* @author jy
* @date 2015年1月10日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E5%8A%A8%E8%B0%83%E7%94%A8">微信企业号获取token说明</a>
* @see com.foxinmy.weixin4j.model.Token
*/
public class WeixinTokenCreator implements TokenCreator {
private final HttpRequest request;
private final String corpid;
private final String corpsecret;
public WeixinTokenCreator() {
WeixinQyAccount weixinAccount = ConfigUtil.getWeixinQyAccount();
this.corpid = weixinAccount.getId();
this.corpsecret = weixinAccount.getSecret();
this.request = new HttpRequest();
}
public WeixinTokenCreator(String corpid, String corpsecret) {
this.corpid = corpid;
this.corpsecret = corpsecret;
this.request = new HttpRequest();
}
@Override
public String getCacheKey() {
return String.format("qy_token_%s", corpid);
}
@Override
public Token createToken() throws WeixinException {
String tokenUrl = String.format(Consts.QY_ASSESS_TOKEN_URL, corpid,
corpsecret);
Response response = request.get(tokenUrl);
Token token = response.getAsObject(new TypeReference<Token>() {
});
token.setTime(System.currentTimeMillis());
return token;
}
}
@@ -1,34 +0,0 @@
package com.foxinmy.weixin4j.qy.type;
/**
* 异步任务的状态
*
* @className BatchStatus
* @author jy
* @date 2015年3月31日
* @since JDK 1.7
* @see
*/
public enum BatchStatus {
/**
* 开始
*/
START(1),
/**
* 进行中
*/
PROCESS(2),
/**
* 完成
*/
DONE(3);
private int code;
BatchStatus(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
@@ -1,39 +0,0 @@
package com.foxinmy.weixin4j.qy.type;
/**
* 异步任务的类型
*
* @className BatchType
* @author jy
* @date 2015年3月31日
* @since JDK 1.7
* @see
*/
public enum BatchType {
/**
* 增量更新成员
*/
SYNC_USER(1),
/**
* 全量覆盖成员
*/
REPLACE_USER(2),
/**
* 邀请成员关注
*/
INVITE_USER(3),
/**
* 全量覆盖部门
*/
REPLACE_PARTY(4);
private int code;
BatchType(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.type;
/**
* 邀请类型
*
* @className InviteType
* @author jy
* @date 2015年1月15日
* @since JDK 1.7
* @see
*/
public enum InviteType {
/**
* 微信邀请
*/
WEIXIN,
/**
* 邮件邀请
*/
EMAIL
}
@@ -1,25 +0,0 @@
package com.foxinmy.weixin4j.qy.type;
/**
* 上报策略
*
* @className ReportLocationType
* @author jy
* @date 2015年3月16日
* @since JDK 1.7
* @see
*/
public enum ReportLocationType {
/**
* 不上报
*/
NOT,
/**
* 进入回话上报
*/
DIALOG,
/**
* 持续上报
*/
PERSIST;
}
@@ -1,38 +0,0 @@
package com.foxinmy.weixin4j.qy.type;
/**
* 成员状态
*
* @className UserStatus
* @author jy
* @date 2014年11月19日
* @since JDK 1.7
* @see
*/
public enum UserStatus {
/**
* 0=全部
*/
BOTH(0),
/**
* 1=已关注
*/
FOLLOW(1),
/**
* 2=已冻结
*/
FROZEN(2),
/**
* 4=未关注
*/
UNFOLLOW(4);
private int val;
UserStatus(int val) {
this.val = val;
}
public int getVal() {
return val;
}
}
@@ -1,10 +0,0 @@
# \u6d4b\u8bd5\u4e4b\u7528 \u6b63\u5f0f\u73af\u5883\u4e0bcopy\u4e00\u4efd\u5230classpath
# \u4f01\u4e1a\u53f7\u4fe1\u606f
account={"id":"wxf10bce209c91d0e2","secret":"cW0OtP7-7YJ7jHKFZaJW2skJHE9bLHadxOswBdVdI2walRBSPfTSA6QqD_QXw8JZ",\
"token":"gp2eGT5mIpngr",\
"encodingAesKey":"BRYfV4zPFUJb3v3MySNBg1ERKE3vyyMRoScu76vFySv"}
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84
token_path=/tmp/weixin/token
# \u5a92\u4f53\u6587\u4ef6\u4fdd\u5b58\u8def\u5f84
media_path=/tmp/weixin/media
@@ -1,64 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
public class AesMsgTest extends MessagePush {
StringBuilder xmlSb = new StringBuilder();
@Test
public void testValidate() throws WeixinException, IOException {
String para = "?msg_signature=33f680b72cfd87a6f8f4d751575bcc31266853a9&timestamp=1416488666&nonce=1358224345&echostr=WZu9tT6KDuyhgOZ1aT%2FIoPNaUkA%2FsfVR%2F%2BDua5q84Kvhs6Fc1%2F6f2510%2BUuMByessdQWsoeak0lSIpaYOIlHrw%3D%3D";
xmlSb.delete(0, xmlSb.length());
String response = get(para);
Assert.assertEquals("2143641595566077626", response);
}
@Test
public void testType1() throws WeixinException, IOException {
String para = "?signature=6dd806a20a314723e78bc58742a1b98a7adfd151&timestamp=1415979366&nonce=1865915590";
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[gh_248c6f91d64f]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[oyFLst1bqtuTcxK-ojF8hOGtLQao]]></FromUserName>");
xmlSb.append("<CreateTime>1415979365</CreateTime>");
xmlSb.append("<MsgType><![CDATA[event]]></MsgType>");
xmlSb.append("<Event><![CDATA[CLICK]]></Event>");
xmlSb.append("<EventKey><![CDATA[CHECKIN]]></EventKey>");
xmlSb.append("</xml>");
String response = push(para, xmlSb.toString());
Assert.assertNotNull(response);
}
@Test
public void testType2() throws WeixinException, IOException {
String para = "?signature=ad05f836772d1cbba1ff2edb7be4b9c9fb3a43d5&timestamp=1415980001&nonce=1803216865&encrypt_type=raw&msg_signature=c0d38e9ca00548f7142627ec2908a4fe8481025e";
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[gh_248c6f91d64f]]></ToUserName>");
xmlSb.append("<FromUserName><![CDATA[oyFLst1bqtuTcxK-ojF8hOGtLQao]]></FromUserName>");
xmlSb.append("<CreateTime>1415980001</CreateTime>");
xmlSb.append("<MsgType><![CDATA[event]]></MsgType>");
xmlSb.append("<Event><![CDATA[CLICK]]></Event>");
xmlSb.append("<EventKey><![CDATA[CHECKIN]]></EventKey>");
xmlSb.append("</xml>");
String response = push(para, xmlSb.toString());
Assert.assertNotNull(response);
}
@Test
public void testType3() throws WeixinException, IOException {
String para = "?signature=ad05f836772d1cbba1ff2edb7be4b9c9fb3a43d5&timestamp=1415980001&nonce=1803216865&encrypt_type=aes&msg_signature=c0d38e9ca00548f7142627ec2908a4fe8481025e";
xmlSb.delete(0, xmlSb.length());
xmlSb.append("<xml>");
xmlSb.append("<ToUserName><![CDATA[gh_248c6f91d64f]]></ToUserName>");
xmlSb.append("<Encrypt><![CDATA[R6VQIWDR14XgSRLm25zc7V/WJYqK15gsUiMh0u/5GTMZME6jGtHkyfVN079ZPL065b+ZDq3TnoFKKtjtZlzcodY6Fm8+EujvtbTdVMMFSwdo8AwqVViAn09+DDfqPaNvbHUSiYsL3qlxArs1MH6APRUHFo7MU/piY1x2stJc8+kv28xtF+K8Aou0RuPO7PeQ18Zu/GkLnYNiI1E7UG31UYfOgVKcRjeE0PXa18iF5LBS8G/ce/l+/pH/DJWUBw5uXaqSOlo21tctlgLXu3bYUUkIu8tT49QwhHvRZILtO9icoyCNuTA7iTcHIdlAe1bD1S0ncmopIQCGmoU2/AXC2CCi6trONf3EPNKKKfDeQYHadnVZOg6kTX2cnYmHZLviYeLzjCKFSqSNkimoSKQ/Dcutpsq1D82NCwiExUZW4oo=]]></Encrypt>");
xmlSb.append("</xml>");
String response = push(para, xmlSb.toString());
Assert.assertNotNull(response);
}
}
@@ -1,56 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.qy.api.AgentApi;
import com.foxinmy.weixin4j.qy.model.AgentInfo;
import com.foxinmy.weixin4j.qy.model.AgentOverview;
import com.foxinmy.weixin4j.qy.model.AgentSetter;
import com.foxinmy.weixin4j.qy.type.ReportLocationType;
/**
* 应用API测试
*
* @className AgentTest
* @author jy
* @date 2015年03月17日
* @since JDK 1.7
* @see
*/
public class AgentTest extends TokenTest {
public AgentApi agentApi;
@Before
public void init() {
this.agentApi = new AgentApi(tokenHolder);
}
@Test
public void get() throws WeixinException {
AgentInfo agent = agentApi.getAgent(0);
Assert.assertTrue(agent != null);
System.err.println(agent);
}
@Test
public void set() throws WeixinException {
AgentSetter agentSet = new AgentSetter(1);
agentSet.setDescription("test");
agentSet.setRedirectDomain("test.com");
agentSet.setReportLocationType(ReportLocationType.DIALOG);
JsonResult result = agentApi.setAgent(agentSet);
Assert.assertTrue(result.getCode() == 0);
}
@Test
public void list() throws WeixinException {
List<AgentOverview> list = agentApi.listAgentOverview();
System.err.println(list);
}
}
@@ -1,61 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.qy.api.BatchApi;
import com.foxinmy.weixin4j.qy.api.MediaApi;
import com.foxinmy.weixin4j.qy.model.BatchResult;
import com.foxinmy.weixin4j.qy.model.Callback;
import com.foxinmy.weixin4j.qy.model.Party;
/**
* 异步任务测试
*
* @className BatchTest
* @author jy
* @date 2015年3月31日
* @since JDK 1.7
* @see
*/
public class BatchTest extends TokenTest {
public BatchApi batchApi;
public MediaApi mediaApi;
@Before
public void init() {
this.batchApi = new BatchApi(tokenHolder);
this.mediaApi = new MediaApi(tokenHolder);
}
@Test
public void syncuser() throws WeixinException {
String jobId = batchApi
.syncuser(
"1QFmZ8LE9dFxPPx8EH5Kfm3cqGXB0OuXY432ZpsfwMFTJjEDt7QI4GZB1UhYGOSYr",
new Callback("http://182.254.188.133:8090",
"gp2eGT5mIpngr",
"BRYfV4zPFUJb3v3MySNBg1ERKE3vyyMRoScu76vFySv"));
System.err.println(jobId);
}
@Test
public void replaceparty() throws WeixinException {
String mediaId = mediaApi.batchUploadParties(Arrays.asList(new Party(5,
"部门1", 1, 1), new Party(6, "部门2", 1, 1)));
String jobId = batchApi.replaceparty(mediaId, new Callback(
"http://182.254.188.133:8090", "gp2eGT5mIpngr",
"BRYfV4zPFUJb3v3MySNBg1ERKE3vyyMRoScu76vFySv"));
System.err.println(jobId);
}
@Test
public void getresult() throws WeixinException {
BatchResult result = batchApi
.getresult("PVucPBfEapLnvQZ1ru2Vdw3Dbl-jXs3AEQdS24cqmI0");
System.err.println(result);
}
}
@@ -1,35 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.qy.api.HelperApi;
/**
* 辅助API测试
*
* @className HelperTest
* @author jy
* @date 2014年12月28日
* @since JDK 1.7
* @see
*/
public class HelperTest extends TokenTest {
public HelperApi helperApi;
@Before
public void init() {
this.helperApi = new HelperApi(tokenHolder);
}
@Test
public void backip() throws WeixinException {
List<String> ips = helperApi.getcallbackip();
Assert.assertTrue(ips != null && !ips.isEmpty());
System.out.println(ips);
}
}
@@ -1,50 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.io.File;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.qy.api.MediaApi;
import com.foxinmy.weixin4j.type.MediaType;
/**
* 媒体上传下载测试
*
* @className MediaTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
* @see
*/
public class MediaTest extends TokenTest {
private MediaApi mediaApi;
@Before
public void init() {
mediaApi = new MediaApi(tokenHolder);
}
@Test
public void upload() throws IOException, WeixinException {
File file = new File("/tmp/test.docx");
String mediaId = mediaApi.uploadMedia(file, MediaType.file);
// 1-1gpykXsR8bhNvO13-ZvskptCBxQF1UE535jFdCF63N2inGRAqEb-psF6eppjIIl
// 1CF6sBgWWFGY9s4JCEet5ASszsTuyHpeN1f2LWXADveqBlKoxSgb3cO401NEM7dNY
Assert.assertNotNull(mediaId);
System.out.println(mediaId);
}
@Test
public void download() throws WeixinException, IOException {
File file = mediaApi
.downloadMedia(
"1CF6sBgWWFGY9s4JCEet5ASszsTuyHpeN1f2LWXADveqBlKoxSgb3cO401NEM7dNY",
"docx");
Assert.assertTrue(file.exists());
}
}
@@ -1,63 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.model.Button;
import com.foxinmy.weixin4j.qy.api.MenuApi;
import com.foxinmy.weixin4j.type.ButtonType;
/**
* 自定义菜单测试
*
* @className MenuTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
*/
public class MenuTest extends TokenTest {
private MenuApi menuApi;
private List<Button> btnList;
@Before
public void init() {
menuApi = new MenuApi(tokenHolder);
}
@Test
public void create() throws WeixinException {
btnList = new ArrayList<Button>();
Button b = new Button("click", "name", ButtonType.click);
btnList.add(b);
b = new Button("qq", "http://www.qq.com", ButtonType.view);
btnList.add(b);
b = new Button("photo", "photo", ButtonType.pic_photo_or_album);
btnList.add(b);
JsonResult result = menuApi.createMenu(btnList, 1);
Assert.assertEquals(0, result.getCode());
}
@Test
public void get() throws WeixinException {
btnList = menuApi.getMenu(1);
for (Button btn : btnList) {
System.out.println(btn);
}
Assert.assertEquals(2, btnList.size());
}
@Test
public void delete() throws WeixinException {
JsonResult result = menuApi.deleteMenu(1);
Assert.assertEquals(0, result.getCode());
}
}
@@ -1,68 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import com.foxinmy.weixin4j.exception.WeixinException;
public class MessagePush {
private final String server = "http://localhost:8090";
private final HttpClient httpClient;
private final HttpPost httpPost;
private final HttpGet httpGet;
public MessagePush() {
httpClient = new DefaultHttpClient();
httpPost = new HttpPost();
httpPost.setURI(URI.create(server));
httpGet = new HttpGet();
httpGet.setURI(URI.create(server));
}
public String get(String para) throws WeixinException, IOException {
httpGet.setURI(URI.create(server + para));
HttpResponse httpResponse = httpClient.execute(httpGet);
return entity(httpResponse);
}
public String push(String xml) throws WeixinException, IOException {
return push("", xml);
}
public String push(String para, String xml) throws WeixinException,
IOException {
httpPost.setURI(URI.create(server + para));
httpPost.setEntity(new StringEntity(xml, StandardCharsets.UTF_8));
HttpResponse httpResponse = httpClient.execute(httpPost);
return entity(httpResponse);
}
private String entity(HttpResponse httpResponse) throws WeixinException,
IOException {
StatusLine statusLine = httpResponse.getStatusLine();
int status = statusLine.getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new WeixinException(Integer.toString(status), "request fail");
}
if (status == HttpStatus.SC_MOVED_PERMANENTLY
|| status == HttpStatus.SC_MOVED_TEMPORARILY) {
throw new WeixinException(Integer.toString(status), "uri moved");
}
return EntityUtils.toString(httpResponse.getEntity(),
StandardCharsets.UTF_8);
}
}
@@ -1,93 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import com.alibaba.fastjson.JSONObject;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.msg.model.File;
import com.foxinmy.weixin4j.msg.model.Image;
import com.foxinmy.weixin4j.msg.model.MpNews;
import com.foxinmy.weixin4j.msg.model.News;
import com.foxinmy.weixin4j.msg.model.Text;
import com.foxinmy.weixin4j.msg.model.Video;
import com.foxinmy.weixin4j.msg.model.Voice;
import com.foxinmy.weixin4j.qy.api.NotifyApi;
import com.foxinmy.weixin4j.qy.message.NotifyMessage;
/**
* 客服消息测试
*
* @className MessageNotifyTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
* @see
*/
public class NotifyMsgTest extends TokenTest {
private NotifyApi notifyApi;
@Before
public void init() {
notifyApi = new NotifyApi(tokenHolder);
}
@Test
public void text() throws WeixinException {
NotifyMessage notify = new NotifyMessage(new Text("content"), 0);
System.out.println(notifyApi.sendNotify(notify));
}
@Test
public void image() throws WeixinException {
NotifyMessage notify = new NotifyMessage(new Image("123"), 0);
System.out.println(notifyApi.sendNotify(notify));
}
@Test
public void voice() throws WeixinException {
NotifyMessage notify = new NotifyMessage(new Voice("123"), 0);
System.out.println(notifyApi.sendNotify(notify));
}
@Test
public void video() throws WeixinException {
NotifyMessage notify = new NotifyMessage(new Video("123"), 0);
System.out.println(notifyApi.sendNotify(notify));
}
@Test
public void file() throws WeixinException {
File file = new File("file");
NotifyMessage notify = new NotifyMessage(file, 0);
System.out.println(notifyApi.sendNotify(notify));
}
@Test
public void news() throws WeixinException {
News news = new News();
NotifyMessage notify = new NotifyMessage(news, 0);
news.pushArticle("title1", "desc1", "picUrl1", "url1");
news.pushArticle("title2", "desc2", "picUrl2", "url2");
System.out.println(notifyApi.sendNotify(notify));
}
@Test
public void mpnews() throws WeixinException {
MpNews news = new MpNews();
NotifyMessage notify = new NotifyMessage(news, 0);
news.pushArticle("thumbMediaId1", "title1", "content1");
news.pushArticle("thumbMediaId2", "title1", "content2");
System.out.println(notifyApi.sendNotify(notify));
}
@Test
public void send1() throws WeixinException {
Text text = new Text("this is a text");
JSONObject result = notifyApi.sendNotify(new NotifyMessage(text, 1));
Assert.assertEquals(0, result.getIntValue("errcode"));
}
}
@@ -1,58 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.qy.api.PartyApi;
import com.foxinmy.weixin4j.qy.model.Party;
/**
* 部门API测试
*
* @className PartyTest
* @author jy
* @date 2014年11月18日
* @since JDK 1.7
* @see
*/
public class PartyTest extends TokenTest {
public PartyApi partyApi;
@Before
public void init() {
this.partyApi = new PartyApi(tokenHolder);
}
@Test
public void create() throws WeixinException {
Party Party = new Party("苦逼组");
int id = partyApi.createParty(Party);
Assert.assertTrue(id > 0);
}
@Test
public void update() throws WeixinException {
Party Party = new Party("苦逼组111");
Party.setId(2);
JsonResult result = partyApi.updateParty(Party);
Assert.assertEquals("updated", result.getDesc());
}
@Test
public void list() throws WeixinException {
List<Party> list = partyApi.listParty(1);
Assert.assertFalse(list.isEmpty());
System.out.println(list);
}
@Test
public void delete() throws WeixinException {
JsonResult result = partyApi.deleteParty(2);
Assert.assertEquals("deleted", result.getDesc());
}
}
@@ -1,77 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.util.Arrays;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.qy.api.TagApi;
import com.foxinmy.weixin4j.qy.model.Tag;
import com.foxinmy.weixin4j.qy.model.User;
/**
* 标签API测试
*
* @className TagTest
* @author jy
* @date 2014年11月18日
* @since JDK 1.7
* @see
*/
public class TagTest extends TokenTest {
public TagApi tagApi;
@Before
public void init() {
this.tagApi = new TagApi(tokenHolder);
}
@Test
public void create() throws WeixinException {
int tagId = tagApi.createTag("coder");
Assert.assertTrue(tagId > 0);
}
@Test
public void update() throws WeixinException {
JsonResult result = tagApi.updateTag(new Tag(1, "coder456"));
Assert.assertEquals("updated", result.getDesc());
}
@Test
public void getUsers() throws WeixinException {
List<User> listUser = tagApi.getTagUsers(1);
Assert.assertFalse(listUser.isEmpty());
System.out.println(listUser);
}
@Test
public void addUsers() throws WeixinException {
JsonResult result = tagApi.addTagUsers(1, Arrays.asList("jinyu"));
Assert.assertEquals("ok", result.getDesc());
}
@Test
public void deleteUsers() throws WeixinException {
JsonResult result = tagApi.deleteTagUsers(1, Arrays.asList("jinyu"));
Assert.assertEquals("ok", result.getDesc());
System.out.println(result);
}
@Test
public void list() throws WeixinException {
List<Tag> tags = tagApi.listTag();
Assert.assertFalse(tags.isEmpty());
System.out.println(tags);
}
@Test
public void delete() throws WeixinException {
JsonResult result = tagApi.deleteTag(3);
Assert.assertEquals("deleted", result.getDesc());
}
}
@@ -1,34 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.qy.token.WeixinTokenCreator;
import com.foxinmy.weixin4j.token.FileTokenHolder;
import com.foxinmy.weixin4j.token.TokenHolder;
/**
* token测试
*
* @className TokenTest
* @author jy.hu
* @date 2014年4月10日
* @since JDK 1.7
*/
public class TokenTest {
protected TokenHolder tokenHolder;
@Before
public void setUp() {
tokenHolder = new FileTokenHolder(
new WeixinTokenCreator());
}
@Test
public void test() throws WeixinException {
Assert.assertNotNull(tokenHolder.getToken());
}
}
@@ -1,92 +0,0 @@
package com.foxinmy.weixin4j.qy.test;
import java.util.Arrays;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.http.JsonResult;
import com.foxinmy.weixin4j.qy.api.MediaApi;
import com.foxinmy.weixin4j.qy.api.UserApi;
import com.foxinmy.weixin4j.qy.model.User;
import com.foxinmy.weixin4j.qy.type.UserStatus;
/**
* 用户API测试
*
* @className UserTest
* @author jy
* @date 2014年11月18日
* @since JDK 1.7
* @see
*/
public class UserTest extends TokenTest {
public UserApi userApi;
public MediaApi mediaApi;
@Before
public void init() {
this.userApi = new UserApi(tokenHolder);
this.mediaApi = new MediaApi(tokenHolder);
}
@Test
public void create() throws WeixinException {
User user = new User("u001", "jack");
user.setMobile("13500000000");
user.setDepartment(1);
user.pushExattr("爱好", "code");
JsonResult result = userApi.createUser(user);
Assert.assertEquals("created", result.getDesc());
}
@Test
public void batchUpload() throws WeixinException {
User user = new User("u001", "jack");
user.setMobile("13500000000");
user.setDepartment(1);
String mediaId = mediaApi.batchUploadUsers(Arrays.asList(user));
System.err.println(mediaId);
}
@Test
public void update() throws WeixinException {
User user = new User("u001", "ted");
user.setMobile("13500000000");
user.setDepartment(1);
user.pushExattr("爱好", "code");
JsonResult result = userApi.updateUser(user);
Assert.assertEquals("updated", result.getDesc());
}
@Test
public void get() throws WeixinException {
User user = userApi.getUser("u001");
Assert.assertTrue(user != null);
System.out.println(user);
}
@Test
public void list() throws WeixinException {
List<User> userList = userApi.listUser(1, true, UserStatus.BOTH, true);
Assert.assertFalse(userList.isEmpty());
System.out.println(userList);
userList = userApi.listUser(1);
Assert.assertFalse(userList.isEmpty());
System.out.println(userList);
}
@Test
public void delete() throws WeixinException {
JsonResult result = userApi.deleteUser("u001");
Assert.assertEquals("deleted", result.getDesc());
}
@Test
public void invite() throws WeixinException {
userApi.inviteUser("11", null);
}
}
-32
View File
@@ -1,32 +0,0 @@
# 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
# idea ignore
/.idea
*.iml
# maven ignore
target/*
# other ignore
*.log
*.tmp
Thumbs.db
/target/
.DS_Store
-78
View File
@@ -1,78 +0,0 @@
weixin4j-qy-server
==================
微信企业号netty服务
------------
功能列表
-------
* `netty构建服务器`
* `消息分发`
如何使用
--------
1.正确填写`weixin.properties`中的属性值
| 属性名 | 说明 |
| :---------- | :-------------- |
| account | 微信企业号信息 `json格式` |
| token_path | 使用FileTokenHolder时token保存的物理路径 |
| media_path | 调用媒体接口时保存媒体文件的物理路径 |
示例(properties中换行用右斜杆\\)
account={"id":"corpid","secret":"corpsecret",\
"token":"企业号中应用在回调模式下的token",\
"encodingAesKey":"企业号中应用在回调模式下AES加密密钥"}
token_path=/tmp/weixin/token
media_path=/tmp/weixin/media
2.在对应的action中实现自己的具体业务 如 TextAction 则表示收到文本消息
@Override
public ResponseMessage execute(TextMessage inMessage) {
return new ResponseMessage(new Text("Hello World!"), inMessage);
}
3.`mvn package`,得到一个zip的压缩包,解压(也可使用deploy.xml部署到远程服务器)到`启动目录`
4.启动netty服务(`com.foxinmy.weixin4j.mp.startup.WeixinQyServerBootstrap`)
sh startup.sh start
> 1.服务的启动脚本[startup.sh](./src/main/startup.sh)需要被注意到,有`JAVA_HOME`和`APP_HOME`两个参数.
> 2.其中`JAVA_HOME`参数值指的是java运行环境(jre|jdk)的安装根目录,如果与脚本中的值不一致,可以改更为实际的路径或者使用`ln -s /usr/local/java target`软链接命令创建期望的链接.
> 3.其中`APP_HOME`参数值指的是本服务的启动目录,此目录需要被正确事先创建好,同时[`deploy.xml`](./deploy.xml)远程部署命令也依赖于此.
> 4.Ant远程部署[`deploy.xml`](./deploy.xml)的正确执行需要[`jsch`](http://www.jcraft.com/jsch/)包的支持,下载jar包将其引入执行Ant命令时的`classpath`中.
> 5.一般来说*Action事件处理类中应该有自己的实际业务类(`service`)需要被注入,可以使用org.springframework.context.ApplicationContext#getBeansWithAnnotation(ActionAnnotation.class)函数获取Action集合后再来实现[`AbstractActionMapping`](https://github.com/foxinmy/weixin4j/blob/master/weixin4j-base/src/main/java/com/foxinmy/weixin4j/action/mapping/AbstractActionMapping.java).
更新LOG
-------
* 2014-11-19
+ 得到`weixin4j-qy-server`工程
* 2014-11-24
+ 新增netty服务与消息分发
* 2014-12-28
+ 增加用户进入应用的callback事件
+ 调整回调模式下的首次验证的签名方式
* 2015-03-25
+ 新增deploy.xml远程部署ant脚本
* 2015-04-01
+ 新增批量任务执行完成事件
-54
View File
@@ -1,54 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="qy-server" default="deploy" basedir=".">
<property name="app.home" value="." />
<property name="target.home" value="${app.home}/target" />
<property name="zip.name" value="weixin-qy-server-bin.zip" />
<property name="host" value="服务器IP" />
<property name="pwd" value="服务器口令" />
<property name="main.dir" value="服务部署的主目录,如/usr/local/weixin" />
<property name="sub.dir" value="服务部署的子目录,如weixin-qy-server" />
<target name="removeZip">
<echo>
${host}:删除[${zip.name}]...
</echo>
<sshexec host="${host}" username="root" password="${pwd}" trust="true" command="cd ${main.dir};pwd;rm -rf ${main.dir}/${zip.name};" />
</target>
<target name="uploadZip" depends="removeZip">
<echo>
${host}:上传[${zip.name}]...
</echo>
<scp file="${target.home}/${zip.name}" todir="root:${pwd}@${host}:${main.dir}" trust="true" />
</target>
<target name="shutdownService" depends="uploadZip">
<echo>
${host}:停止[${sub.dir}]...
</echo>
<sshexec host="${host}" username="root" password="${pwd}" trust="true" command="cd ${main.dir}/${sub.dir};pwd;sh startup.sh stop;rm -rf ${main.dir}/${sub.dir};" />
</target>
<target name="unzipProject" depends="shutdownService">
<echo>
${host}:解压${zip.name}...
</echo>
<sshexec host="${host}" username="root" password="${pwd}" trust="true" command="cd ${main.dir};pwd;unzip ${zip.name};" />
</target>
<target name="startupService" depends="unzipProject">
<echo>
${host}:启动服务...
</echo>
<sshexec host="${host}" username="root" password="${pwd}" trust="true" command="cd ${main.dir}/${sub.dir};pwd;sh startup.sh start;" />
</target>
<target name="build" depends="startupService">
<echo>
target.home = ${target.home}
file.zip = ${target.home}/${zip.name}
</echo>
</target>
<target name="deploy" depends="build">
</target>
</project>
-81
View File
@@ -1,81 +0,0 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j-qy</artifactId>
<version>1.3</version>
</parent>
<artifactId>weixin4j-qy-server</artifactId>
<name>weixin4j-qy-server</name>
<url>https://github.com/foxinmy/weixin4j/tree/master/weixin4j-qy/weixin4j-qy-server</url>
<description>微信企业号netty服务</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>weixin-qy-server</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>com.foxinmy</groupId>
<artifactId>weixin4j-qy-api</artifactId>
<version>${project.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>
</dependencies>
</project>
@@ -1,36 +0,0 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>bin</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>true</useProjectArtifact>
<outputDirectory>/lib</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>com/foxinmy/weixin4j/qy</directory>
<includes>
<include>**/*.md</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.sh</include>
<include>*.bat</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>/conf</outputDirectory>
</fileSet>
</fileSets>
</assembly>
@@ -1,20 +0,0 @@
package com.foxinmy.weixin4j.qy.action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.ImageMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 图片消息处理
*
* @className ImageAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.ImageMessage
*/
@ActionAnnotation(msgType = MessageType.image)
public class ImageAction extends DebugAction<ImageMessage> {
}
@@ -1,20 +0,0 @@
package com.foxinmy.weixin4j.qy.action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.LinkMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 链接消息处理
*
* @className LinkAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.LinkMessage
*/
@ActionAnnotation(msgType = MessageType.link)
public class LinkAction extends DebugAction<LinkMessage> {
}
@@ -1,20 +0,0 @@
package com.foxinmy.weixin4j.qy.action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.LocationMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 地理位置处理
*
* @className LocationAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.LocationMessage
*/
@ActionAnnotation(msgType = MessageType.location)
public class LocationAction extends DebugAction<LocationMessage> {
}
@@ -1 +0,0 @@
普通消息对应的Action
@@ -1,26 +0,0 @@
package com.foxinmy.weixin4j.qy.action;
import com.foxinmy.weixin4j.action.AbstractAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.TextMessage;
import com.foxinmy.weixin4j.msg.model.Text;
import com.foxinmy.weixin4j.response.ResponseMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 文字消息处理
*
* @className TextAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.TextMessage
*/
@ActionAnnotation(msgType = MessageType.text)
public class TextAction extends AbstractAction<TextMessage> {
@Override
public ResponseMessage execute(TextMessage inMessage) {
return new ResponseMessage(new Text("Hello World!"), inMessage);
}
}
@@ -1,20 +0,0 @@
package com.foxinmy.weixin4j.qy.action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.VideoMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 视频消息处理
*
* @className VideoAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.VideoMessage
*/
@ActionAnnotation(msgType = MessageType.video)
public class VideoAction extends DebugAction<VideoMessage> {
}
@@ -1,20 +0,0 @@
package com.foxinmy.weixin4j.qy.action;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.VoiceMessage;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 语音消息处理
*
* @className VoiceAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.VoiceMessage
*/
@ActionAnnotation(msgType = MessageType.voice)
public class VoiceAction extends DebugAction<VoiceMessage> {
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.BatchjobresultMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 异步任务完成事件
*
* @className BatchJobAction
* @author jy
* @date 2015年3月31日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.BatchjobresultMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.batch_job_result })
public class BatchJobAction extends DebugAction<BatchjobresultMessage> {
}
@@ -1,24 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.EnterAgentEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 用户进入应用的事件推送
*
* @className EnterAgentAction
* @author jy
* @date 2014年12月28日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.EnterAgentEventMessage
* @see<a href=
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E4%BA%8B%E4%BB%B6#.E7.94.A8.E6.88.B7.E8.BF.9B.E5.85.A5.E5.BA.94.E7.94.A8.E7.9A.84.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81"
* >用户进入应用的事件推送</a>
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.enter_agent })
public class EnterAgentAction extends DebugAction<EnterAgentEventMessage> {
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.LocationEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 上报地理位置后触发
*
* @className LocationAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.LocationEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.location })
public class LocationAction extends DebugAction<LocationEventMessage> {
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.MassEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 群发消息发送动作完成后触发
*
* @className MassSendAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.MassEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.masssendjobfinish })
public class MassSendAction extends DebugAction<MassEventMessage> {
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* click类型菜单点击时触发
*
* @className MenuClickAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.click })
public class MenuClickAction extends DebugAction<MenuEventMessage> {
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.menu.MenuLocationEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 点击菜单发送地理位置时触发
*
* @className MenuLocationAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuLocationEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.location_select })
public class MenuLocationAction extends DebugAction<MenuLocationEventMessage> {
}
@@ -1,23 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.menu.MenuPhotoEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 点击菜单发送图片时触发
*
* @className MenuPhotoAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuPhotoEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = {
EventType.pic_photo_or_album, EventType.pic_sysphoto,
EventType.pic_weixin })
public class MenuPhotoAction extends DebugAction<MenuPhotoEventMessage> {
}
@@ -1,22 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.menu.MenuScanEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 点击菜单扫描时触发
*
* @className MenuScanAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuScanEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.scancode_push,
EventType.scancode_waitmsg })
public class MenuScanAction extends DebugAction<MenuScanEventMessage> {
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* view类型菜单点击时触发
*
* @className MenuViewAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.menu.MenuEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.view })
public class MenuViewAction extends DebugAction<MenuEventMessage> {
}
@@ -1 +0,0 @@
事件消息对应的Action
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.ScanEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 扫描事件时触发
*
* @className ScanAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.ScanEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.scan })
public class ScanAction extends DebugAction<ScanEventMessage> {
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.ScribeEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 关注时触发
*
* @className SubscribeAction
* @author jy
* @date 2014年10月9日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.ScribeEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.subscribe })
public class SubscribeAction extends DebugAction<ScribeEventMessage> {
}
@@ -1,22 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 模板消息发送动作完成时触发
*
* @className TemplateSendAction
* @author jy
* @date 2014年10月10日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.TemplatesendjobfinishMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.templatesendjobfinish })
public class TemplateSendAction extends
DebugAction<TemplatesendjobfinishMessage> {
}
@@ -1,21 +0,0 @@
package com.foxinmy.weixin4j.qy.action.event;
import com.foxinmy.weixin4j.action.DebugAction;
import com.foxinmy.weixin4j.action.mapping.ActionAnnotation;
import com.foxinmy.weixin4j.msg.event.ScribeEventMessage;
import com.foxinmy.weixin4j.type.EventType;
import com.foxinmy.weixin4j.type.MessageType;
/**
* 取消关注时触发
*
* @className UnsubscribeAction
* @author jy
* @date 2014年10月10日
* @since JDK 1.7
* @see com.foxinmy.weixin4j.msg.event.ScribeEventMessage
*/
@ActionAnnotation(msgType = MessageType.event, eventType = { EventType.unsubscribe })
public class UnsubscribeAction extends DebugAction<ScribeEventMessage> {
}
@@ -1,5 +0,0 @@
WeixinMessageDecoder:对微信消息进行解码
WeixinMessageEncoder:对微信消息进行编码
WeixinServerHandler:微信请求处理类
@@ -1,83 +0,0 @@
package com.foxinmy.weixin4j.qy.server;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.foxinmy.weixin4j.model.WeixinQyAccount;
import com.foxinmy.weixin4j.response.HttpWeixinMessage;
import com.foxinmy.weixin4j.type.EncryptType;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.util.MessageUtil;
import com.foxinmy.weixin4j.xml.XmlStream;
/**
* 微信消息解码类
*
* @className WeixinMessageDecoder
* @author jy
* @date 2014年11月13日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%9B%9E%E8%B0%83%E6%A8%A1%E5%BC%8F">回调模式</a>
*/
public class WeixinMessageDecoder extends
MessageToMessageDecoder<FullHttpRequest> {
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req,
List<Object> out) throws Exception {
WeixinQyAccount qyAccount = ConfigUtil.getWeixinQyAccount();
String content = req.content().toString(Consts.UTF_8);
HttpWeixinMessage message = new HttpWeixinMessage();
message.setOriginalContent(content);
if (StringUtils.isNotBlank(content)) {
message = XmlStream.get(content, HttpWeixinMessage.class);
message.setOriginalContent(MessageUtil.aesDecrypt(
qyAccount.getId(), qyAccount.getEncodingAesKey(),
message.getEncryptContent()));
}
message.setMethod(req.getMethod().name());
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.getUri(),
true);
log.info("\n=================receive request=================");
log.info("{}", req.getMethod());
log.info("{}", req.getUri());
log.info("{}", content);
Map<String, List<String>> parameters = queryDecoder.parameters();
String msgSignature = parameters.containsKey("msg_signature") ? parameters
.get("msg_signature").get(0) : "";
message.setSignature(msgSignature);
String echoStr = parameters.containsKey("echostr") ? parameters.get(
"echostr").get(0) : "";
message.setEchoStr(echoStr);
String timeStamp = parameters.containsKey("timestamp") ? parameters
.get("timestamp").get(0) : "";
message.setTimeStamp(timeStamp);
String nonce = parameters.containsKey("nonce") ? parameters
.get("nonce").get(0) : "";
message.setNonce(nonce);
message.setToken(qyAccount.getToken());
message.setEncryptType(EncryptType.AES);
// 解密 echostr 20141228 added
if (message.getMethod().equals(HttpMethod.GET.name())
&& StringUtils.isNotBlank(echoStr)) {
message.setOriginalContent(MessageUtil.aesDecrypt(
qyAccount.getId(), qyAccount.getEncodingAesKey(), echoStr));
}
out.add(message);
}
}
@@ -1,69 +0,0 @@
package com.foxinmy.weixin4j.qy.server;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.foxinmy.weixin4j.model.WeixinQyAccount;
import com.foxinmy.weixin4j.qy.util.HttpUtil;
import com.foxinmy.weixin4j.response.ResponseMessage;
import com.foxinmy.weixin4j.util.ConfigUtil;
import com.foxinmy.weixin4j.util.DateUtil;
import com.foxinmy.weixin4j.util.MessageUtil;
import com.foxinmy.weixin4j.util.RandomUtil;
import com.foxinmy.weixin4j.xml.Map2ObjectConverter;
import com.foxinmy.weixin4j.xml.XmlStream;
import com.thoughtworks.xstream.core.ClassLoaderReference;
import com.thoughtworks.xstream.mapper.DefaultMapper;
/**
* 微信消息编码类
*
* @className WeixinMessageEncoder
* @author jy
* @date 2014年11月13日
* @since JDK 1.7
* @see <a
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8A%A0%E8%A7%A3%E5%AF%86%E6%96%B9%E6%A1%88%E7%9A%84%E8%AF%A6%E7%BB%86%E8%AF%B4%E6%98%8E">加密接入指引</a>
*/
public class WeixinMessageEncoder extends
MessageToMessageEncoder<ResponseMessage> {
private final Logger log = LoggerFactory.getLogger(getClass());
private final static XmlStream mapXstream = XmlStream.get();
static {
mapXstream.alias("xml", Map.class);
mapXstream.registerConverter(new Map2ObjectConverter(new DefaultMapper(
new ClassLoaderReference(XmlStream.class.getClassLoader()))));
}
@Override
protected void encode(ChannelHandlerContext ctx, ResponseMessage response,
List<Object> out) throws Exception {
WeixinQyAccount qyAccount = ConfigUtil.getWeixinQyAccount();
String xmlContent = response.toXml();
String nonce = RandomUtil.generateString(32);
String timestamp = DateUtil.timestamp2string();
String encrtypt = MessageUtil.aesEncrypt(qyAccount.getId(),
qyAccount.getEncodingAesKey(), xmlContent);
String msgSignature = MessageUtil.signature(qyAccount.getToken(),
nonce, timestamp, encrtypt);
Map<String, String> map = new HashMap<String, String>();
map.put("Encrypt", encrtypt);
map.put("MsgSignature", msgSignature);
map.put("TimeStamp", timestamp);
map.put("Nonce", nonce);
String content = mapXstream.toXML(map);
out.add(HttpUtil.createWeixinMessageResponse(
content, null));
log.info("\n=================aes encrtypt out=================");
log.info("{}", content);
}
}
@@ -1,99 +0,0 @@
package com.foxinmy.weixin4j.qy.server;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.apache.http.entity.ContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.foxinmy.weixin4j.action.WeixinAction;
import com.foxinmy.weixin4j.action.mapping.ActionMapping;
import com.foxinmy.weixin4j.qy.util.HttpUtil;
import com.foxinmy.weixin4j.response.HttpWeixinMessage;
import com.foxinmy.weixin4j.response.ResponseMessage;
import com.foxinmy.weixin4j.util.MessageUtil;
/**
* 微信被动消息处理类
*
* @className WeixinServerHandler
* @author jy
* @date 2014年11月16日
* @since JDK 1.7
* @see
*/
public class WeixinServerHandler extends
SimpleChannelInboundHandler<HttpWeixinMessage> {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ActionMapping actionMapping;
public WeixinServerHandler(ActionMapping actionMapping) {
this.actionMapping = actionMapping;
}
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
log.error("catch the exception:{}", cause);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx,
HttpWeixinMessage httpMessage) throws Exception {
log.info("\n=================message in=================\n{}",
httpMessage);
boolean isGet = httpMessage.getMethod().equals(HttpMethod.GET.name());
boolean validate = false;
if (isGet) {
validate = MessageUtil.signature(httpMessage.getToken(),
httpMessage.getTimeStamp(), httpMessage.getNonce(),
httpMessage.getEchoStr())
.equals(httpMessage.getSignature());
if (validate) {
ctx.write(HttpUtil.createWeixinMessageResponse(
httpMessage.getOriginalContent(),
ContentType.TEXT_PLAIN));
return;
}
} else {
validate = MessageUtil.signature(httpMessage.getToken(),
httpMessage.getTimeStamp(), httpMessage.getNonce(),
httpMessage.getEncryptContent()).equals(
httpMessage.getSignature());
}
if (!validate) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.FORBIDDEN));
return;
}
String xmlContent = httpMessage.getOriginalContent();
WeixinAction action = actionMapping.getAction(xmlContent);
if (action == null) {
ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.NOT_FOUND));
return;
}
ResponseMessage response = action.execute(xmlContent);
log.info("\n=================message out=================\n{}",
response);
if (response == null) {
ctx.write(HttpUtil.createWeixinMessageResponse("",
ContentType.TEXT_PLAIN));
return;
}
ctx.write(response);
}
}
@@ -1,30 +0,0 @@
package com.foxinmy.weixin4j.qy.server;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import com.foxinmy.weixin4j.action.mapping.ActionMapping;
import com.foxinmy.weixin4j.action.mapping.AnnotationActionMapping;
import com.foxinmy.weixin4j.qy.action.ImageAction;
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
private final ActionMapping actionMapping;
public WeixinServerInitializer() {
this.actionMapping = new AnnotationActionMapping(
ImageAction.class.getPackage());
}
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new WeixinMessageDecoder());
pipeline.addLast(new WeixinMessageEncoder());
pipeline.addLast(new WeixinServerHandler(actionMapping));
}
}
@@ -1,54 +0,0 @@
package com.foxinmy.weixin4j.qy.startup;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import java.util.ResourceBundle;
import com.foxinmy.weixin4j.qy.server.WeixinServerInitializer;
/**
* 微信服务netty启动程序
*
* @className WeixinServerBootstrap
* @author jy
* @date 2014年10月12日
* @since JDK 1.7
* @see
*/
public final class WeixinQyServerBootstrap {
private final static int port;
private final static int workerThreads;
static {
ResourceBundle netty = ResourceBundle.getBundle("netty");
port = Integer.parseInt(netty.getString("port"));
workerThreads = Integer.parseInt(netty.getString("workerThreads"));
}
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
try {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler())
.childHandler(new WeixinServerInitializer());
Channel ch = b.bind(port).sync().channel();
System.err.println("weixin server startup OK:" + port);
ch.closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
@@ -1,50 +0,0 @@
package com.foxinmy.weixin4j.qy.util;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaders.Names.DATE;
import static io.netty.handler.codec.http.HttpHeaders.Names.SERVER;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpResponse;
import java.util.Date;
import org.apache.http.Consts;
import org.apache.http.entity.ContentType;
/**
* HTTP工具类
*
* @className HttpUtil
* @author jy
* @date 2014年11月15日
* @since JDK 1.7
* @see
*/
public class HttpUtil {
public static HttpResponse createWeixinMessageResponse(String content,
ContentType contentType) {
if (contentType == null) {
contentType = ContentType.APPLICATION_XML;
}
FullHttpResponse httpResponse = new DefaultFullHttpResponse(HTTP_1_1,
OK, Unpooled.copiedBuffer(content, Consts.UTF_8));
httpResponse.headers().set(
CONTENT_TYPE,
String.format("%s;encoding=%s", contentType.getMimeType(),
Consts.UTF_8.displayName()));
httpResponse.headers().set(CONTENT_LENGTH, content.getBytes(Consts.UTF_8).length);
httpResponse.headers().set(CONNECTION, Values.KEEP_ALIVE);
httpResponse.headers().set(DATE, new Date());
httpResponse.headers().set(SERVER, "netty4");
return httpResponse;
}
}
@@ -1,63 +0,0 @@
<?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>
<charset>UTF-8</charset>
<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/qy/weixin.qy.%d{yyyy-MM-dd}.log
</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<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.weixin4j" level="INFO">
<appender-ref ref="async" />
</logger>
</configuration>
@@ -1,4 +0,0 @@
# netty\u76d1\u542c\u7aef\u53e3 \u7531\u4e8e\u5fae\u4fe1\u56de\u8c03url\u76ee\u524d\u53ea\u652f\u630180\u7aef\u53e3 \u53ef\u4ee5\u8003\u8651\u7528nginx\u8f6c\u53d1\u7684\u65b9\u5f0f\u6765\u89e3\u51b380\u7aef\u53e3\u88ab\u5360\u6709\u7684\u95ee\u9898
port=8090
# \u5de5\u4f5c\u8fdb\u7a0b\u6570
workerThreads=20
@@ -1,9 +0,0 @@
# \u4f01\u4e1a\u53f7\u4fe1\u606f
account={"id":"corpid","secret":"corpsecret",\
"token":"\u4f01\u4e1a\u53f7\u4e2d\u5e94\u7528\u5728\u56de\u8c03\u6a21\u5f0f\u4e0b\u7684token",\
"encodingAesKey":"\u4f01\u4e1a\u53f7\u4e2d\u5e94\u7528\u5728\u56de\u8c03\u6a21\u5f0f\u4e0bAES\u52a0\u5bc6\u5bc6\u94a5"}
# \u4f7f\u7528FileTokenHolder\u65f6token\u7684\u5b58\u653e\u8def\u5f84
token_path=/tmp/weixin/token
# \u5a92\u4f53\u6587\u4ef6\u4fdd\u5b58\u8def\u5f84
media_path=/tmp/weixin/media
@@ -1,142 +0,0 @@
ulimit -n 110000
#JDK home
JAVA_HOME="/usr/local/java/"
#executing user
RUNNING_USER=root
#Run home
APP_HOME="/usr/local/weixin/weixin-qy-server"
#main class
APP_MAINCLASS=com.foxinmy.weixin4j.qy.startup.WeixinQyServerBootstrap
#classpath
CLASSPATH=$APP_HOME/classes
for i in "$APP_HOME"/lib/*.jar; do
CLASSPATH="$CLASSPATH":"$i"
done
CLASSPATH="$CLASSPATH":"$APP_HOME"/conf
#jvm options
JAVA_OPTS="-Xms256m -Xmx512m -Djava.awt.headless=true -XX:MaxPermSize=128m -server -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 -XX:+DisableExplicitGC -Xnoclassgc -Xverify:none"
#psid
psid=0
checkpid() {
javaps=`$JAVA_HOME/bin/jps -l | grep $APP_MAINCLASS`
if [ -n "$javaps" ]; then
psid=`echo $javaps | awk '{print $1}'`
else
psid=0
fi
}
###################################
#startup
###################################
start() {
checkpid
if [ $psid -ne 0 ]; then
echo "====================================================="
echo "warn: $APP_MAINCLASS already started! (pid=$psid)"
echo "====================================================="
else
echo -n "Starting $APP_MAINCLASS ..."
# JAVA_CMD="nohup $JAVA_HOME/bin/java $JAVA_OPTS -classpath $CLASSPATH $APP_MAINCLASS >/dev/null 2>&1 &"
JAVA_CMD="$JAVA_HOME/bin/java $JAVA_OPTS -classpath $CLASSPATH $APP_MAINCLASS &"
su - $RUNNING_USER -c "$JAVA_CMD"
checkpid
if [ $psid -ne 0 ]; then
echo "(pid=$psid) [OK]"
else
echo "[Failed]"
fi
fi
}
###################################
#stop
###################################
stop() {
checkpid
if [ $psid -ne 0 ]; then
echo -n "Stopping $APP_MAINCLASS ...(pid=$psid) "
su - $RUNNING_USER -c "kill -9 $psid"
if [ $? -eq 0 ]; then
echo "[OK]"
else
echo "[Failed]"
fi
checkpid
if [ $psid -ne 0 ]; then
stop
fi
else
echo "====================================================="
echo "warn: $APP_MAINCLASS is not running"
echo "====================================================="
fi
}
###################################
#status
###################################
status() {
checkpid
if [ $psid -ne 0 ]; then
echo "$APP_MAINCLASS is running! (pid=$psid)"
else
echo "$APP_MAINCLASS is not running"
fi
}
###################################
#info
###################################
info() {
echo "System Information:"
echo "****************************"
echo `head -n 1 /etc/issue`
echo `uname -a`
echo
echo "JAVA_HOME=$JAVA_HOME"
echo `$JAVA_HOME/bin/java -version`
echo
echo "APP_HOME=$APP_HOME"
echo "APP_MAINCLASS=$APP_MAINCLASS"
echo "****************************"
}
###################################
#access only 1 argument:{start|stop|restart|status|info}
###################################
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart')
stop
start
;;
'status')
status
;;
'info')
info
;;
*)
echo "Usage: $0 {start|stop|restart|status|info}"
exit 1
esac
exit 0