合并开发分支,发布正式版

This commit is contained in:
Binary Wang
2019-05-18 17:07:45 +08:00
committed by GitHub
194 changed files with 6446 additions and 1140 deletions

View File

@@ -7,11 +7,11 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>3.3.0</version>
<version>3.4.0</version>
</parent>
<artifactId>weixin-java-cp</artifactId>
<name>WxJava - CP</name>
<name>WxJava - CP Java SDK</name>
<description>微信企业号/企业微信 Java SDK</description>
<dependencies>

View File

@@ -84,6 +84,11 @@ public class WxCpConsts {
*/
public static final String LOCATION_SELECT = "location_select";
/**
* 任务卡片事件推送.
*/
public static final String TASKCARD_CLICK = "taskcard_click";
}
/**
@@ -126,4 +131,46 @@ public class WxCpConsts {
public static final String UPDATE_TAG = "update_tag";
}
/**
* 应用推送消息的消息类型.
*/
public static class AppChatMsgType {
/**
* 文本消息.
*/
public static final String TEXT = "text";
/**
* 图片消息.
*/
public static final String IMAGE = "image";
/**
* 语音消息.
*/
public static final String VOICE = "voice";
/**
* 视频消息.
*/
public static final String VIDEO = "video";
/**
* 发送文件CP专用.
*/
public static final String FILE = "file";
/**
* 文本卡片消息CP专用.
*/
public static final String TEXTCARD = "textcard";
/**
* 图文消息(点击跳转到外链).
*/
public static final String NEWS = "news";
/**
* 图文消息(点击跳转到图文消息页面).
*/
public static final String MPNEWS = "mpnews";
/**
* markdown消息.
*/
public static final String MARKDOWN = "markdown";
}
}

View File

@@ -3,46 +3,75 @@ package me.chanjar.weixin.cp.api;
import java.util.List;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage;
import me.chanjar.weixin.cp.bean.WxCpChat;
/**
* 群聊服务
* 群聊服务.
*
* @author gaigeshen
*/
public interface WxCpChatService {
String APPCHAT_CREATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/create";
String APPCHAT_UPDATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/update";
String APPCHAT_GET_CHATID = "https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid=";
/**
* 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群
* 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群.
*
* @param name 群聊名最多50个utf8字符超过将截断
* @param owner 指定群主的id。如果不指定系统会随机从userlist中选一人作为群主
* @param users 群成员id列表。至少2人至多500人
* @param name 群聊名最多50个utf8字符超过将截断
* @param owner 指定群主的id。如果不指定系统会随机从userlist中选一人作为群主
* @param users 群成员id列表。至少2人至多500人
* @param chatId 群聊的唯一标志不能与已有的群重复字符串类型最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填系统会随机生成群id
* @return 创建群聊会话的结果,群聊的唯一标志
* @return 创建群聊会话chatId
* @throws WxErrorException 发生异常
*/
String chatCreate(String name, String owner, List<String> users, String chatId) throws WxErrorException;
/**
* 修改群聊会话
*
* @param chatId 群聊id
* @param name 新的群聊名。若不需更新请忽略此参数null or empty。最多50个utf8字符超过将截断
* @param owner 新群主的id。若不需更新请忽略此参数null or empty
* @param usersToAdd 添加成员的id列表若不需要更新则传递空对象或者空集合
* chatCreate 同名方法
*/
String create(String name, String owner, List<String> users, String chatId) throws WxErrorException;
/**
* 修改群聊会话.
*
* @param chatId 群聊id
* @param name 新的群聊名。若不需更新请忽略此参数null or empty。最多50个utf8字符超过将截断
* @param owner 新群主的id。若不需更新请忽略此参数null or empty
* @param usersToAdd 添加成员的id列表若不需要更新则传递空对象或者空集合
* @param usersToDelete 踢出成员的id列表若不需要更新则传递空对象或者空集合
* @throws WxErrorException 发生异常
*/
void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException;
/**
* 获取群聊会话
*
* chatUpdate 同名方法
*/
void update(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException;
/**
* 获取群聊会话.
*
* @param chatId 群聊编号
* @return 群聊会话
* @throws WxErrorException 发生异常
*/
WxCpChat chatGet(String chatId) throws WxErrorException;
/**
* chatGet 同名方法
*/
WxCpChat get(String chatId) throws WxErrorException;
/**
* 应用支持推送文本、图片、视频、文件、图文等类型.
* 请求方式: POSTHTTPS
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/appchat/send?access_token=ACCESS_TOKEN
* 文档地址https://work.weixin.qq.com/api/doc#90000/90135/90248
*
* @param message 要发送的消息内容对象
*/
void sendMsg(WxCpAppChatMessage message) throws WxErrorException;
}

View File

@@ -26,7 +26,7 @@ public interface WxCpDepartmentService {
* @return 部门id
* @throws WxErrorException 异常
*/
Integer create(WxCpDepart depart) throws WxErrorException;
Long create(WxCpDepart depart) throws WxErrorException;
/**
* <pre>

View File

@@ -0,0 +1,66 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult;
import me.chanjar.weixin.cp.bean.WxCpCheckinData;
import me.chanjar.weixin.cp.bean.WxCpCheckinOption;
import me.chanjar.weixin.cp.bean.WxCpDialRecord;
import java.util.Date;
import java.util.List;
/**
* @author Element
* @Package me.chanjar.weixin.cp.api
* @date 2019-04-06 10:52
* @Description: <pre>
* 企业微信OA相关接口
*
* </pre>
*/
public interface WxCpOAService {
/**
* <pre>
* 获取打卡数据
* API doc : https://work.weixin.qq.com/api/doc#90000/90135/90262
* </pre>
*
* @param openCheckinDataType 打卡类型。1上下班打卡2外出打卡3全部打卡
* @param starttime 获取打卡记录的开始时间
* @param endtime 获取打卡记录的结束时间
* @param userIdList 需要获取打卡记录的用户列表
*/
List<WxCpCheckinData> getCheckinData(Integer openCheckinDataType, Date starttime, Date endtime, List<String> userIdList) throws WxErrorException;
/**
* <pre>
* 获取打卡规则
* API doc : https://work.weixin.qq.com/api/doc#90000/90135/90263
* </pre>
*
* @param datetime 需要获取规则的当天日期
* @param userIdList 需要获取打卡规则的用户列表
* @return
* @throws WxErrorException
*/
List<WxCpCheckinOption> getCheckinOption(Date datetime, List<String> userIdList) throws WxErrorException;
/**
* <pre>
* 获取审批数据
* 通过本接口来获取公司一段时间内的审批记录。一次拉取调用最多拉取10000个审批记录可以通过多次拉取的方式来满足需求但调用频率不可超过600次/分。
* API doc : https://work.weixin.qq.com/api/doc#90000/90135/91530
* </pre>
*
* @param starttime 获取审批记录的开始时间
* @param endtime 获取审批记录的结束时间
* @param nextSpnum 第一个拉取的审批单号,不填从该时间段的第一个审批单拉取
* @return
* @throws WxErrorException
*/
WxCpApprovalDataResult getApprovalData(Date starttime, Date endtime, Long nextSpnum) throws WxErrorException;
List<WxCpDialRecord> getDialRecord(Date starttime, Date endtime, Integer offset, Integer limit) throws WxErrorException;
}

View File

@@ -7,6 +7,7 @@ import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
@@ -16,6 +17,15 @@ import me.chanjar.weixin.cp.config.WxCpConfigStorage;
* @author chanjaster
*/
public interface WxCpService {
String GET_JSAPI_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket";
String GET_AGENT_CONFIG_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?&type=agent_config";
String MESSAGE_SEND = "https://qyapi.weixin.qq.com/cgi-bin/message/send";
String GET_CALLBACK_IP = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip";
String BATCH_REPLACE_PARTY = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty";
String BATCH_REPLACE_USER = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser";
String BATCH_GET_RESULT = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=";
String JSCODE_TO_SESSION_URL = "https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session";
/**
* <pre>
* 验证推送过来的消息的正确性
@@ -68,6 +78,33 @@ public interface WxCpService {
*/
String getJsapiTicket(boolean forceRefresh) throws WxErrorException;
/**
* 获得jsapi_ticket,不强制刷新jsapi_ticket
* 应用的jsapi_ticket用于计算agentConfig参见“通过agentConfig注入应用的权限”的签名签名计算方法与上述介绍的config的签名算法完全相同但需要注意以下区别
*
* 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。
* 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。
* @see #getJsapiTicket(boolean)
*/
String getAgentJsapiTicket() throws WxErrorException;
/**
* <pre>
* 获取应用的jsapi_ticket
* 应用的jsapi_ticket用于计算agentConfig参见“通过agentConfig注入应用的权限”的签名签名计算方法与上述介绍的config的签名算法完全相同但需要注意以下区别
*
* 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。
* 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。
*
* 获得时会检查jsapiToken是否过期如果过期了那么就刷新一下否则就什么都不干
*
* 详情请见https://work.weixin.qq.com/api/doc#10029/%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%E7%9A%84jsapi_ticket
* </pre>
*
* @param forceRefresh 强制刷新
*/
String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException;
/**
* <pre>
* 创建调用jsapi时所需要的签名
@@ -89,6 +126,13 @@ public interface WxCpService {
*/
WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException;
/**
* 小程序登录凭证校验
*
* @param jsCode 登录时获取的 code
*/
WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException;
/**
* <pre>
* 获取微信服务器的ip段
@@ -165,6 +209,13 @@ public interface WxCpService {
*/
WxSession getSession(String id, boolean create);
/**
* 获取WxSessionManager 对象
*
* @return WxSessionManager
*/
WxSessionManager getSessionManager();
/**
* <pre>
* 设置WxSessionManager只有当需要使用个性化的WxSessionManager的时候才需要调用此方法
@@ -250,8 +301,17 @@ public interface WxCpService {
*/
WxCpChatService getChatService();
/**
* 获取任务卡片服务
*
* @return 任务卡片服务
*/
WxCpTaskCardService getTaskCardService();
WxCpAgentService getAgentService();
WxCpOAService getOAService();
/**
* http请求对象
*/

View File

@@ -0,0 +1,30 @@
package me.chanjar.weixin.cp.api;
import me.chanjar.weixin.common.error.WxErrorException;
import java.util.List;
/**
* <pre>
* 任务卡片管理接口.
* Created by Jeff on 2019-05-16.
* </pre>
*
* @author <a href="https://github.com/domainname">Jeff</a>
* @date 2019-05-16
*/
public interface WxCpTaskCardService {
/**
* <pre>
* 更新任务卡片消息状态
* 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/91579
*
* 注意: 这个方法使用WxCpConfigStorage里的agentId
* </pre>
*
* @param userIds 企业的成员ID列表
* @param taskId 任务卡片ID
* @param clickedKey 已点击按钮的Key
*/
void update(List<String> userIds, String taskId, String clickedKey) throws WxErrorException;
}

View File

@@ -1,16 +1,10 @@
package me.chanjar.weixin.cp.api.impl;
import java.io.File;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
@@ -24,20 +18,23 @@ import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
import me.chanjar.weixin.cp.api.WxCpAgentService;
import me.chanjar.weixin.cp.api.WxCpChatService;
import me.chanjar.weixin.cp.api.WxCpDepartmentService;
import me.chanjar.weixin.cp.api.WxCpMediaService;
import me.chanjar.weixin.cp.api.WxCpMenuService;
import me.chanjar.weixin.cp.api.WxCpOAuth2Service;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpTagService;
import me.chanjar.weixin.cp.api.WxCpUserService;
import me.chanjar.weixin.cp.api.*;
import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, RequestHttp<H, P> {
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author chanjarster
*/
public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestHttp<H, P> {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
private WxCpUserService userService = new WxCpUserServiceImpl(this);
@@ -48,6 +45,8 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ
private WxCpOAuth2Service oauth2Service = new WxCpOAuth2ServiceImpl(this);
private WxCpTagService tagService = new WxCpTagServiceImpl(this);
private WxCpAgentService agentService = new WxCpAgentServiceImpl(this);
private WxCpOAService oaService = new WxCpOAServiceImpl(this);
private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this);
/**
* 全局的是否正在刷新access token的锁
@@ -59,14 +58,19 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ
*/
protected final Object globalJsapiTicketRefreshLock = new Object();
/**
* 全局的是否正在刷新agent的jsapi_ticket的锁
*/
protected final Object globalAgentJsapiTicketRefreshLock = new Object();
protected WxCpConfigStorage configStorage;
private WxSessionManager sessionManager = new StandardSessionManager();
protected WxSessionManager sessionManager = new StandardSessionManager();
/**
* 临时文件目录
*/
protected File tmpDirFile;
private File tmpDirFile;
private int retrySleepMillis = 1000;
private int maxRetryTimes = 5;
@@ -86,6 +90,30 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ
return getAccessToken(false);
}
@Override
public String getAgentJsapiTicket() throws WxErrorException {
return this.getAgentJsapiTicket(false);
}
@Override
public String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException {
if (forceRefresh) {
this.configStorage.expireAgentJsapiTicket();
}
if (this.configStorage.isAgentJsapiTicketExpired()) {
synchronized (this.globalAgentJsapiTicketRefreshLock) {
if (this.configStorage.isAgentJsapiTicketExpired()) {
String responseContent = this.get(WxCpService.GET_AGENT_CONFIG_TICKET, null);
JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
this.configStorage.updateAgentJsapiTicket(jsonObject.get("ticket").getAsString(),
jsonObject.get("expires_in").getAsInt());
}
}
}
return this.configStorage.getAgentJsapiTicket();
}
@Override
public String getJsapiTicket() throws WxErrorException {
@@ -97,20 +125,18 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ
if (forceRefresh) {
this.configStorage.expireJsapiTicket();
}
if (this.configStorage.isJsapiTicketExpired()) {
synchronized (this.globalJsapiTicketRefreshLock) {
if (this.configStorage.isJsapiTicketExpired()) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket";
String responseContent = execute(SimpleGetRequestExecutor.create(this), url, null);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
this.configStorage.updateJsapiTicket(jsapiTicket,
expiresInSeconds);
String responseContent = this.get(WxCpService.GET_JSAPI_TICKET, null);
JsonObject tmpJsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
this.configStorage.updateJsapiTicket(tmpJsonObject.get("ticket").getAsString(),
tmpJsonObject.get("expires_in").getAsInt());
}
}
}
return this.configStorage.getJsapiTicket();
}
@@ -139,18 +165,27 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ
@Override
public WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send";
Integer agentId = message.getAgentId();
if(null == agentId){
if (null == agentId) {
message.setAgentId(this.getWxCpConfigStorage().getAgentId());
}
return WxCpMessageSendResult.fromJson(this.post(url, message.toJson()));
return WxCpMessageSendResult.fromJson(this.post(WxCpService.MESSAGE_SEND, message.toJson()));
}
@Override
public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException {
Map<String, String> params = new HashMap<>(2);
params.put("js_code", jsCode);
params.put("grant_type", "authorization_code");
String result = this.get(JSCODE_TO_SESSION_URL, Joiner.on("&").withKeyValueSeparator("=").join(params));
return WxCpMaJsCode2SessionResult.fromJson(result);
}
@Override
public String[] getCallbackIp() throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/getcallbackip";
String responseContent = get(url, null);
String responseContent = get(WxCpService.GET_CALLBACK_IP, null);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
JsonArray jsonArray = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray();
String[] ips = new String[jsonArray.size()];
@@ -171,7 +206,7 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ
}
/**
* 向微信端发送请求在这里执行的策略是当发生access_token过期时才去刷新然后重新执行请求而不是全局定时请求
* 向微信端发送请求在这里执行的策略是当发生access_token过期时才去刷新然后重新执行请求而不是全局定时请求.
*/
@Override
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
@@ -285,25 +320,28 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ
this.sessionManager = sessionManager;
}
@Override
public WxSessionManager getSessionManager() {
return this.sessionManager;
}
@Override
public String replaceParty(String mediaId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceparty";
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("media_id", mediaId);
return post(url, jsonObject.toString());
return post(WxCpService.BATCH_REPLACE_PARTY, jsonObject.toString());
}
@Override
public String replaceUser(String mediaId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/replaceuser";
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("media_id", mediaId);
return post(url, jsonObject.toString());
return post(WxCpService.BATCH_REPLACE_USER, jsonObject.toString());
}
@Override
public String getTaskResult(String joinId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/getresult?jobid=" + joinId;
String url = WxCpService.BATCH_GET_RESULT + joinId;
return get(url, null);
}
@@ -350,6 +388,16 @@ public abstract class WxCpServiceAbstractImpl<H, P> implements WxCpService, Requ
return chatService;
}
@Override
public WxCpOAService getOAService() {
return oaService;
}
@Override
public WxCpTaskCardService getTaskCardService() {
return taskCardService;
}
@Override
public RequestHttp<?, ?> getRequestHttp() {
return this;

View File

@@ -1,36 +1,35 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.cp.api.WxCpChatService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage;
import me.chanjar.weixin.cp.bean.WxCpChat;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.cp.api.WxCpChatService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpChat;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
/**
* 群聊服务实现
* 群聊服务实现.
*
* @author gaigeshen
*/
public class WxCpChatServiceImpl implements WxCpChatService {
public class WxCpChatServiceImpl implements WxCpChatService {
private static final JsonParser JSON_PARSER = new JsonParser();
private final WxCpService cpService;
private final WxCpService internalService;
/**
* 创建群聊服务实现的实例
*
* @param internalService 企业微信的服务
* 创建群聊服务实现的实例.
*
* @param cpService 企业微信的服务
*/
public WxCpChatServiceImpl(WxCpService internalService) {
this.internalService = internalService;
WxCpChatServiceImpl(WxCpService cpService) {
this.cpService = cpService;
}
@Override
@@ -48,12 +47,18 @@ public class WxCpChatServiceImpl implements WxCpChatService {
if (StringUtils.isNotBlank(chatId)) {
data.put("chatid", chatId);
}
String result = internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/create", WxGsonBuilder.create().toJson(data));
String result = this.cpService.post(APPCHAT_CREATE, WxGsonBuilder.create().toJson(data));
return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString();
}
@Override
public void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException {
public String create(String name, String owner, List<String> users, String chatId) throws WxErrorException {
return chatCreate(name, owner, users, chatId);
}
@Override
public void chatUpdate(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete)
throws WxErrorException {
Map<String, Object> data = new HashMap<>(5);
if (StringUtils.isNotBlank(chatId)) {
data.put("chatid", chatId);
@@ -70,14 +75,30 @@ public class WxCpChatServiceImpl implements WxCpChatService {
if (usersToDelete != null && !usersToDelete.isEmpty()) {
data.put("del_user_list", usersToDelete);
}
internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/update", WxGsonBuilder.create().toJson(data));
this.cpService.post(APPCHAT_UPDATE, WxGsonBuilder.create().toJson(data));
}
@Override
public void update(String chatId, String name, String owner, List<String> usersToAdd, List<String> usersToDelete) throws WxErrorException {
chatUpdate(chatId, name, owner, usersToAdd, usersToDelete);
}
@Override
public WxCpChat chatGet(String chatId) throws WxErrorException {
String result = internalService.get("https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid=" + chatId, null);
return WxCpGsonBuilder.create().fromJson(
new JsonParser().parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class);
String result = this.cpService.get(APPCHAT_GET_CHATID + chatId, null);
return WxCpGsonBuilder.create()
.fromJson(JSON_PARSER.parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class);
}
@Override
public WxCpChat get(String chatId) throws WxErrorException {
return chatGet(chatId);
}
@Override
public void sendMsg(WxCpAppChatMessage message) throws WxErrorException {
this.cpService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/send", message.toJson());
}
}

View File

@@ -28,11 +28,11 @@ public class WxCpDepartmentServiceImpl implements WxCpDepartmentService {
}
@Override
public Integer create(WxCpDepart depart) throws WxErrorException {
public Long create(WxCpDepart depart) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create";
String responseContent = this.mainService.post(url, depart.toJson());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id"));
return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("id"));
}
@Override

View File

@@ -0,0 +1,165 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpOAService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpApprovalDataResult;
import me.chanjar.weixin.cp.bean.WxCpCheckinData;
import me.chanjar.weixin.cp.bean.WxCpCheckinOption;
import me.chanjar.weixin.cp.bean.WxCpDialRecord;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.Date;
import java.util.List;
/**
* @author Element
* @Package me.chanjar.weixin.cp.api.impl
* @date 2019-04-06 11:20
* @Description: TODO
*/
public class WxCpOAServiceImpl implements WxCpOAService {
private WxCpService mainService;
public WxCpOAServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
@Override
public List<WxCpCheckinData> getCheckinData(Integer openCheckinDataType, Date starttime, Date endtime, List<String> userIdList) throws WxErrorException {
if (starttime == null || endtime == null) {
throw new RuntimeException("starttime and endtime can't be null");
}
if (userIdList == null || userIdList.size() > 100) {
throw new RuntimeException("用户列表不能为空不超过100个若用户超过100个请分批获取");
}
long endtimestamp = endtime.getTime() / 1000L;
long starttimestamp = starttime.getTime() / 1000L;
if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) {
throw new RuntimeException("获取记录时间跨度不超过一个月");
}
String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata";
JsonObject jsonObject = new JsonObject();
JsonArray jsonArray = new JsonArray();
jsonObject.addProperty("opencheckindatatype", openCheckinDataType);
jsonObject.addProperty("starttime", starttimestamp);
jsonObject.addProperty("endtime", endtimestamp);
for (String userid : userIdList) {
jsonArray.add(userid);
}
jsonObject.add("useridlist", jsonArray);
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
.fromJson(
tmpJsonElement.getAsJsonObject().get("checkindata"),
new TypeToken<List<WxCpCheckinData>>() {
}.getType()
);
}
@Override
public List<WxCpCheckinOption> getCheckinOption(Date datetime, List<String> userIdList) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckinoption";
if (datetime == null) {
throw new RuntimeException("datetime can't be null");
}
if (userIdList == null || userIdList.size() > 100) {
throw new RuntimeException("用户列表不能为空不超过100个若用户超过100个请分批获取");
}
JsonArray jsonArray = new JsonArray();
for (String userid : userIdList) {
jsonArray.add(userid);
}
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("datetime", datetime.getTime() / 1000L);
jsonObject.add("useridlist", jsonArray);
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
.fromJson(
tmpJsonElement.getAsJsonObject().get("info"),
new TypeToken<List<WxCpCheckinOption>>() {
}.getType()
);
}
@Override
public WxCpApprovalDataResult getApprovalData(Date starttime, Date endtime, Long nextSpnum) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/corp/getapprovaldata";
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("starttime", starttime.getTime() / 1000L);
jsonObject.addProperty("endtime", endtime.getTime() / 1000L);
if (nextSpnum != null) {
jsonObject.addProperty("next_spnum", nextSpnum);
}
String responseContent = this.mainService.post(url, jsonObject.toString());
return WxCpGsonBuilder.create().fromJson(responseContent, WxCpApprovalDataResult.class);
}
@Override
public List<WxCpDialRecord> getDialRecord(Date starttime, Date endtime, Integer offset, Integer limit) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/dial/get_dial_record";
JsonObject jsonObject = new JsonObject();
if (offset == null) {
offset = 0;
}
if (limit == null || limit <= 0) {
limit = 100;
}
jsonObject.addProperty("offset", offset);
jsonObject.addProperty("limit", limit);
if (starttime != null && endtime != null) {
long endtimestamp = endtime.getTime() / 1000L;
long starttimestamp = starttime.getTime() / 1000L;
if (endtimestamp - starttimestamp < 0 || endtimestamp - starttimestamp >= 30 * 24 * 60 * 60) {
throw new RuntimeException("受限于网络传输起止时间的最大跨度为30天如超过30天则以结束时间为基准向前取30天进行查询");
}
jsonObject.addProperty("start_time", starttimestamp);
jsonObject.addProperty("end_time", endtimestamp);
}
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
.fromJson(
tmpJsonElement.getAsJsonObject().get("record"),
new TypeToken<List<WxCpDialRecord>>() {
}.getType()
);
}
}

View File

@@ -8,6 +8,7 @@ import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
import me.chanjar.weixin.cp.api.WxCpOAService;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
@@ -18,7 +19,7 @@ import org.apache.http.impl.client.CloseableHttpClient;
import java.io.IOException;
public class WxCpServiceApacheHttpClientImpl extends WxCpServiceAbstractImpl<CloseableHttpClient, HttpHost> {
public class WxCpServiceApacheHttpClientImpl extends BaseWxCpServiceImpl<CloseableHttpClient, HttpHost> {
protected CloseableHttpClient httpClient;
protected HttpHost httpProxy;
@@ -39,37 +40,37 @@ public class WxCpServiceApacheHttpClientImpl extends WxCpServiceAbstractImpl<Clo
@Override
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
if (this.configStorage.isAccessTokenExpired() || forceRefresh) {
synchronized (this.globalAccessTokenRefreshLock) {
if (this.configStorage.isAccessTokenExpired()) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();
try {
HttpGet httpGet = new HttpGet(url);
if (this.httpProxy != null) {
RequestConfig config = RequestConfig.custom()
.setProxy(this.httpProxy).build();
httpGet.setConfig(config);
}
String resultContent = null;
try (CloseableHttpClient httpclient = getRequestHttpClient();
CloseableHttpResponse response = httpclient.execute(httpGet)) {
resultContent = new BasicResponseHandler().handleResponse(response);
} finally {
httpGet.releaseConnection();
}
WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.configStorage.updateAccessToken(
accessToken.getAccessToken(), accessToken.getExpiresIn());
} catch (IOException e) {
throw new RuntimeException(e);
}
if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) {
return this.configStorage.getAccessToken();
}
synchronized (this.globalAccessTokenRefreshLock) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();
try {
HttpGet httpGet = new HttpGet(url);
if (this.httpProxy != null) {
RequestConfig config = RequestConfig.custom()
.setProxy(this.httpProxy).build();
httpGet.setConfig(config);
}
String resultContent;
try (CloseableHttpClient httpclient = getRequestHttpClient();
CloseableHttpResponse response = httpclient.execute(httpGet)) {
resultContent = new BasicResponseHandler().handleResponse(response);
} finally {
httpGet.releaseConnection();
}
WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return this.configStorage.getAccessToken();

View File

@@ -8,7 +8,7 @@ import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
public class WxCpServiceJoddHttpImpl extends WxCpServiceAbstractImpl<HttpConnectionProvider, ProxyInfo> {
public class WxCpServiceJoddHttpImpl extends BaseWxCpServiceImpl<HttpConnectionProvider, ProxyInfo> {
protected HttpConnectionProvider httpClient;
protected ProxyInfo httpProxy;
@@ -30,30 +30,29 @@ public class WxCpServiceJoddHttpImpl extends WxCpServiceAbstractImpl<HttpConnect
@Override
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
if (this.configStorage.isAccessTokenExpired() || forceRefresh) {
synchronized (this.globalAccessTokenRefreshLock) {
if (this.configStorage.isAccessTokenExpired()) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();
if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) {
return this.configStorage.getAccessToken();
}
HttpRequest request = HttpRequest.get(url);
if (this.httpProxy != null) {
httpClient.useProxy(this.httpProxy);
}
request.withConnectionProvider(httpClient);
HttpResponse response = request.send();
synchronized (this.globalAccessTokenRefreshLock) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();
String resultContent = response.bodyText();
WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.configStorage.updateAccessToken(
accessToken.getAccessToken(), accessToken.getExpiresIn());
}
HttpRequest request = HttpRequest.get(url);
if (this.httpProxy != null) {
httpClient.useProxy(this.httpProxy);
}
request.withConnectionProvider(httpClient);
HttpResponse response = request.send();
String resultContent = response.bodyText();
WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.configStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
}
return this.configStorage.getAccessToken();
}

View File

@@ -11,7 +11,7 @@ import okhttp3.*;
import java.io.IOException;
public class WxCpServiceOkHttpImpl extends WxCpServiceAbstractImpl<OkHttpClient, OkHttpProxyInfo> {
public class WxCpServiceOkHttpImpl extends BaseWxCpServiceImpl<OkHttpClient, OkHttpProxyInfo> {
protected OkHttpClient httpClient;
protected OkHttpProxyInfo httpProxy;
@@ -33,34 +33,33 @@ public class WxCpServiceOkHttpImpl extends WxCpServiceAbstractImpl<OkHttpClient,
@Override
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
this.log.debug("WxCpServiceOkHttpImpl is running");
if (this.configStorage.isAccessTokenExpired() || forceRefresh) {
synchronized (this.globalAccessTokenRefreshLock) {
if (this.configStorage.isAccessTokenExpired()) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();
//得到httpClient
OkHttpClient client = getRequestHttpClient();
//请求的request
Request request = new Request.Builder().url(url).get().build();
String resultContent = null;
try {
Response response = client.newCall(request).execute();
resultContent = response.body().string();
} catch (IOException e) {
this.log.error(e.getMessage(), e);
}
if (!this.configStorage.isAccessTokenExpired() && !forceRefresh) {
return this.configStorage.getAccessToken();
}
WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.configStorage.updateAccessToken(accessToken.getAccessToken(),
accessToken.getExpiresIn());
synchronized (this.globalAccessTokenRefreshLock) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
+ "&corpid=" + this.configStorage.getCorpId()
+ "&corpsecret=" + this.configStorage.getCorpSecret();
//得到httpClient
OkHttpClient client = getRequestHttpClient();
//请求的request
Request request = new Request.Builder().url(url).get().build();
String resultContent = null;
try {
Response response = client.newCall(request).execute();
resultContent = response.body().string();
} catch (IOException e) {
this.log.error(e.getMessage(), e);
}
}
WxError error = WxError.fromJson(resultContent, WxType.CP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
this.configStorage.updateAccessToken(accessToken.getAccessToken(),
accessToken.getExpiresIn());
}
return this.configStorage.getAccessToken();
}

View File

@@ -0,0 +1,39 @@
package me.chanjar.weixin.cp.api.impl;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpTaskCardService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <pre>
* 任务卡片管理接口.
* Created by Jeff on 2019-05-16.
* </pre>
*
* @author <a href="https://github.com/domainname">Jeff</a>
* @date 2019-05-16
*/
@RequiredArgsConstructor
public class WxCpTaskCardServiceImpl implements WxCpTaskCardService {
private final WxCpService mainService;
@Override
public void update(List<String> userIds, String taskId, String clickedKey) throws WxErrorException {
Integer agentId = this.mainService.getWxCpConfigStorage().getAgentId();
Map<String, Object> data = new HashMap<>(4);
data.put("userids", userIds);
data.put("agentid", agentId);
data.put("task_id", taskId);
data.put("clicked_key", clickedKey);
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/update_taskcard";
this.mainService.post(url, WxGsonBuilder.create().toJson(data));
}
}

View File

@@ -0,0 +1,163 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import java.io.Serializable;
import java.util.List;
/**
* <pre>
* 应用推送消息
* Created by Binary Wang on 2019/1/26.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WxCpAppChatMessage implements Serializable {
private static final long serialVersionUID = -5469013416372240229L;
private String msgType;
private String content;
private String chatId;
private String mediaId;
private String title;
private String description;
private Boolean safe;
private String url;
private String btnTxt;
private List<NewArticle> articles;
private List<MpnewsArticle> mpnewsArticles;
/**
* 构建文本消息.
*/
public static WxCpAppChatMessage buildTextMsg(String chatId, String content, boolean safe) {
final WxCpAppChatMessage message = new WxCpAppChatMessage();
message.setMsgType(AppChatMsgType.TEXT);
message.setContent(content);
message.setChatId(chatId);
message.setSafe(safe);
return message;
}
/**
* 生成json字符串.
*/
public String toJson() {
JsonObject messageJson = new JsonObject();
messageJson.addProperty("msgtype", this.getMsgType());
messageJson.addProperty("chatid", this.getChatId());
if (this.getSafe() != null && this.getSafe()) {
messageJson.addProperty("safe", 1);
}
this.handleMsgType(messageJson);
return messageJson.toString();
}
private void handleMsgType(JsonObject messageJson) {
switch (this.getMsgType()) {
case AppChatMsgType.TEXT: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("text", text);
break;
}
case AppChatMsgType.MARKDOWN: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("markdown", text);
break;
}
case AppChatMsgType.TEXTCARD: {
JsonObject text = new JsonObject();
text.addProperty("title", this.getTitle());
text.addProperty("description", this.getDescription());
text.addProperty("url", this.getUrl());
text.addProperty("btntxt", this.getBtnTxt());
messageJson.add("textcard", text);
break;
}
case AppChatMsgType.IMAGE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("image", image);
break;
}
case AppChatMsgType.FILE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("file", image);
break;
}
case AppChatMsgType.VOICE: {
JsonObject voice = new JsonObject();
voice.addProperty("media_id", this.getMediaId());
messageJson.add("voice", voice);
break;
}
case AppChatMsgType.VIDEO: {
JsonObject video = new JsonObject();
video.addProperty("media_id", this.getMediaId());
video.addProperty("title", this.getTitle());
video.addProperty("description", this.getDescription());
messageJson.add("video", video);
break;
}
case AppChatMsgType.NEWS: {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray();
for (NewArticle article : this.getArticles()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("title", article.getTitle());
articleJson.addProperty("description", article.getDescription());
articleJson.addProperty("url", article.getUrl());
articleJson.addProperty("picurl", article.getPicUrl());
articleJsonArray.add(articleJson);
}
newsJsonObject.add("articles", articleJsonArray);
messageJson.add("news", newsJsonObject);
break;
}
case AppChatMsgType.MPNEWS: {
JsonObject newsJsonObject = new JsonObject();
if (this.getMediaId() != null) {
newsJsonObject.addProperty("media_id", this.getMediaId());
} else {
JsonArray articleJsonArray = new JsonArray();
for (MpnewsArticle article : this.getMpnewsArticles()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("title", article.getTitle());
articleJson.addProperty("thumb_media_id", article.getThumbMediaId());
articleJson.addProperty("author", article.getAuthor());
articleJson.addProperty("content_source_url", article.getContentSourceUrl());
articleJson.addProperty("content", article.getContent());
articleJson.addProperty("digest", article.getDigest());
articleJsonArray.add(articleJson);
}
newsJsonObject.add("articles", articleJsonArray);
}
messageJson.add("mpnews", newsJsonObject);
break;
}
default: {
//do nothing
}
}
}
}

View File

@@ -0,0 +1,69 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* @author Element
* @Package me.chanjar.weixin.cp.bean
* @date 2019-04-06 14:36
* @Description: 企业微信 OA 审批数据
*/
@Data
public class WxCpApprovalDataResult implements Serializable {
private static final long serialVersionUID = -1046940445840716590L;
@SerializedName("errcode")
private Integer errCode;
@SerializedName("errmsg")
private String errMsg;
private Integer count;
private Integer total;
@SerializedName("next_spnum")
private Long nextSpnum;
private WxCpApprovalData[] data;
@Data
public static class WxCpApprovalData implements Serializable{
private static final long serialVersionUID = -3051785319608491640L;
private String spname;
@SerializedName("apply_name")
private String applyName;
@SerializedName("apply_org")
private String applyOrg;
@SerializedName("approval_name")
private String[] approvalName;
@SerializedName("notify_name")
private String[] notifyName;
@SerializedName("sp_status")
private Integer spStatus;
@SerializedName("sp_num")
private Long spNum;
@SerializedName("apply_time")
private Long applyTime;
@SerializedName("apply_user_id")
private String applyUserId;
@SerializedName("comm")
private Map<String,String> comm;
}
}

View File

@@ -0,0 +1,51 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author Element
* @Package me.chanjar.weixin.cp.bean
* @date 2019-04-06 11:01
* @Description: 企业微信打卡数据
*/
@Data
public class WxCpCheckinData implements Serializable {
private static final long serialVersionUID = 1915820330847799605L;
@SerializedName("userid")
private String userId;
@SerializedName("groupname")
private String groupName;
@SerializedName("checkin_type")
private String checkinType;
@SerializedName("exception_type")
private String exceptionType;
@SerializedName("checkin_time")
private Long checkinTime;
@SerializedName("location_title")
private String locationTitle;
@SerializedName("location_detail")
private String locationDetail;
@SerializedName("wifiname")
private String wifiName;
@SerializedName("wifimac")
private String wifiMAC;
private String notes;
@SerializedName("mediaids")
private List<String> mediaIds;
}

View File

@@ -0,0 +1,151 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author Element
* @Package me.chanjar.weixin.cp.bean
* @date 2019-04-06 13:22
* @Description: 企业微信打卡规则
*/
@Data
public class WxCpCheckinOption implements Serializable {
private static final long serialVersionUID = -1964233697990417482L;
@SerializedName("userid")
private String userId;
private Group group;
@Data
public static class CheckinDate implements Serializable {
private static final long serialVersionUID = -5601722383347110974L;
private List<Integer> workdays;
@SerializedName("checkintime")
private CheckinTime[] checkinTime;
@SerializedName("flex_time")
private Long flexTime;
@SerializedName("noneed_offwork")
private Boolean noneedOffwork;
@SerializedName("limit_aheadtime")
private Long limitAheadtime;
}
@Data
public static class CheckinTime implements Serializable {
private static final long serialVersionUID = -8579954143265336276L;
@SerializedName("work_sec")
private Long workSec;
@SerializedName("off_work_sec")
private Long offWorkSec;
@SerializedName("remind_work_sec")
private Long remindWorkSec;
@SerializedName("remind_off_work_sec")
private Long remindOffWorkSec;
}
@Data
public static class Group implements Serializable {
private static final long serialVersionUID = -5888406969613403044L;
@SerializedName("groupid")
private Long id;
@SerializedName("groupname")
private String name;
@SerializedName("grouptype")
private Integer type;
@SerializedName("checkindate")
private List<CheckinDate> checkinDate;
@SerializedName("spe_workdays")
private List<SpeDay> speWorkdays;
@SerializedName("spe_offdays")
private List<SpeDay> speOffdays;
@SerializedName("sync_holidays")
private Boolean syncHolidays;
@SerializedName("need_photo")
private Boolean needPhoto;
@SerializedName("note_can_use_local_pic")
private Boolean note_can_use_local_pic;
@SerializedName("allow_checkin_offworkday")
private Boolean allow_checkin_offworkday;
@SerializedName("allow_apply_offworkday")
private Boolean allow_apply_offworkday;
@SerializedName("wifimac_infos")
private List<WifiMACInfo> wifiMACInfos;
@SerializedName("loc_infos")
private List<LocInfo> locInfos;
}
@Data
public static class WifiMACInfo implements Serializable{
private static final long serialVersionUID = -4657809185716627368L;
@SerializedName("wifiname")
private String name;
@SerializedName("wifimac")
private String mac;
}
@Data
public static class LocInfo implements Serializable{
private static final long serialVersionUID = -618965280668099608L;
private Long lat;
private Long lng;
@SerializedName("loc_title")
private String title;
@SerializedName("loc_detail")
private String detail;
private Long distance;
}
@Data
public static class SpeDay implements Serializable{
private static final long serialVersionUID = -3538818921359212748L;
private Long timestamp;
private String notes;
@SerializedName("checkintime")
private List<CheckinTime> checkinTime;
}
}

View File

@@ -0,0 +1,73 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author Element
* @Package me.chanjar.weixin.cp.bean
* @date 2019-04-06 15:38
* @Description: 公费电话拨打记录
*/
@Data
public class WxCpDialRecord implements Serializable {
private static final long serialVersionUID = 4178886812949929116L;
@SerializedName("call_time")
private Long callTime;
/**
* 总通话时长,单位为分钟
*/
@SerializedName("total_duration")
private Integer totalDuration;
/**
* 通话类型1-单人通话 2-多人通话
*/
@SerializedName("call_type")
private Integer callType;
private Caller caller;
private List<Callee> callee;
/**
* 主叫信息
*/
@Data
public static class Caller implements Serializable{
private static final long serialVersionUID = 4792200404338145607L;
@SerializedName("userid")
private String userId;
private Integer duration;
}
/**
* 被叫信息
*/
@Data
public static class Callee implements Serializable{
private static final long serialVersionUID = 2390963671336179550L;
/**
* 被叫用户的userid当被叫用户为企业内用户时返回
*/
@SerializedName("userid")
private String userId;
/**
* 被叫用户的号码,当被叫用户为外部用户时返回
*/
private String phone;
private Integer duration;
}
}

View File

@@ -37,7 +37,7 @@ public class WxCpInviteResult implements Serializable {
private String errMsg;
@SerializedName("invaliduser")
private String invalidUsers;
private String[] invalidUsers;
@SerializedName("invalidparty")
private String[] invalidParties;
@@ -45,16 +45,4 @@ public class WxCpInviteResult implements Serializable {
@SerializedName("invalidtag")
private String[] invalidTags;
public List<String> getInvalidUserList() {
return this.content2List(this.invalidUsers);
}
private List<String> content2List(String content) {
if (StringUtils.isBlank(content)) {
return Collections.emptyList();
}
return Splitter.on("|").splitToList(content);
}
}

View File

@@ -0,0 +1,33 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
/**
* <pre>
* 小程序登录凭证校验
* 文档地址https://work.weixin.qq.com/api/doc#90000/90136/90289/wx.qy.login
* </pre>
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Data
public class WxCpMaJsCode2SessionResult implements Serializable {
private static final long serialVersionUID = 6229609023682814765L;
@SerializedName("session_key")
private String sessionKey;
@SerializedName("userid")
private String userId;
@SerializedName("corpid")
private String corpId;
public static WxCpMaJsCode2SessionResult fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpMaJsCode2SessionResult.class);
}
}

View File

@@ -1,23 +1,19 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.Data;
import me.chanjar.weixin.common.api.WxConsts.KefuMsgType;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import me.chanjar.weixin.cp.bean.messagebuilder.*;
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import me.chanjar.weixin.cp.bean.messagebuilder.FileBuilder;
import me.chanjar.weixin.cp.bean.messagebuilder.ImageBuilder;
import me.chanjar.weixin.cp.bean.messagebuilder.MpnewsBuilder;
import me.chanjar.weixin.cp.bean.messagebuilder.NewsBuilder;
import me.chanjar.weixin.cp.bean.messagebuilder.TextBuilder;
import me.chanjar.weixin.cp.bean.messagebuilder.TextCardBuilder;
import me.chanjar.weixin.cp.bean.messagebuilder.VideoBuilder;
import me.chanjar.weixin.cp.bean.messagebuilder.VoiceBuilder;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
/**
* 消息.
*
@@ -45,6 +41,12 @@ public class WxCpMessage implements Serializable {
private List<NewArticle> articles = new ArrayList<>();
private List<MpnewsArticle> mpnewsArticles = new ArrayList<>();
/**
* 任务卡片特有的属性
*/
private String taskId;
private List<TaskCardButton> taskButtons = new ArrayList<>();
/**
* 获得文本消息builder.
*/
@@ -94,6 +96,13 @@ public class WxCpMessage implements Serializable {
return new MpnewsBuilder();
}
/**
* 获得markdown消息builder.
*/
public static MarkdownMsgBuilder MARKDOWN() {
return new MarkdownMsgBuilder();
}
/**
* 获得文件消息builder.
*/
@@ -101,17 +110,26 @@ public class WxCpMessage implements Serializable {
return new FileBuilder();
}
/**
* 获得任务卡片消息builder.
*/
public static TaskCardBuilder TASKCARD() {
return new TaskCardBuilder();
}
/**
* <pre>
* 请使用
* {@link WxConsts.KefuMsgType#TEXT}
* {@link WxConsts.KefuMsgType#IMAGE}
* {@link WxConsts.KefuMsgType#VOICE}
* {@link WxConsts.KefuMsgType#MUSIC}
* {@link WxConsts.KefuMsgType#VIDEO}
* {@link WxConsts.KefuMsgType#NEWS}
* {@link WxConsts.KefuMsgType#MPNEWS}
* {@link KefuMsgType#TEXT}
* {@link KefuMsgType#IMAGE}
* {@link KefuMsgType#VOICE}
* {@link KefuMsgType#MUSIC}
* {@link KefuMsgType#VIDEO}
* {@link KefuMsgType#NEWS}
* {@link KefuMsgType#MPNEWS}
* {@link KefuMsgType#MARKDOWN}
* {@link KefuMsgType#TASKCARD}
* </pre>
*
* @param msgType 消息类型
@@ -121,7 +139,162 @@ public class WxCpMessage implements Serializable {
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
JsonObject messageJson = new JsonObject();
if (this.getAgentId() != null) {
messageJson.addProperty("agentid", this.getAgentId());
}
if (StringUtils.isNotBlank(this.getToUser())) {
messageJson.addProperty("touser", this.getToUser());
}
messageJson.addProperty("msgtype", this.getMsgType());
if (StringUtils.isNotBlank(this.getToParty())) {
messageJson.addProperty("toparty", this.getToParty());
}
if (StringUtils.isNotBlank(this.getToTag())) {
messageJson.addProperty("totag", this.getToTag());
}
this.handleMsgType(messageJson);
if (StringUtils.isNotBlank(this.getSafe())) {
messageJson.addProperty("safe", this.getSafe());
}
return messageJson.toString();
}
private void handleMsgType(JsonObject messageJson) {
switch (this.getMsgType()) {
case KefuMsgType.TEXT: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("text", text);
break;
}
case KefuMsgType.MARKDOWN: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("markdown", text);
break;
}
case KefuMsgType.TEXTCARD: {
JsonObject text = new JsonObject();
text.addProperty("title", this.getTitle());
text.addProperty("description", this.getDescription());
text.addProperty("url", this.getUrl());
text.addProperty("btntxt", this.getBtnTxt());
messageJson.add("textcard", text);
break;
}
case KefuMsgType.IMAGE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("image", image);
break;
}
case KefuMsgType.FILE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("file", image);
break;
}
case KefuMsgType.VOICE: {
JsonObject voice = new JsonObject();
voice.addProperty("media_id", this.getMediaId());
messageJson.add("voice", voice);
break;
}
case KefuMsgType.VIDEO: {
JsonObject video = new JsonObject();
video.addProperty("media_id", this.getMediaId());
video.addProperty("thumb_media_id", this.getThumbMediaId());
video.addProperty("title", this.getTitle());
video.addProperty("description", this.getDescription());
messageJson.add("video", video);
break;
}
case KefuMsgType.NEWS: {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray();
for (NewArticle article : this.getArticles()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("title", article.getTitle());
articleJson.addProperty("description", article.getDescription());
articleJson.addProperty("url", article.getUrl());
articleJson.addProperty("picurl", article.getPicUrl());
articleJsonArray.add(articleJson);
}
newsJsonObject.add("articles", articleJsonArray);
messageJson.add("news", newsJsonObject);
break;
}
case KefuMsgType.MPNEWS: {
JsonObject newsJsonObject = new JsonObject();
if (this.getMediaId() != null) {
newsJsonObject.addProperty("media_id", this.getMediaId());
} else {
JsonArray articleJsonArray = new JsonArray();
for (MpnewsArticle article : this.getMpnewsArticles()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("title", article.getTitle());
articleJson.addProperty("thumb_media_id", article.getThumbMediaId());
articleJson.addProperty("author", article.getAuthor());
articleJson.addProperty("content_source_url", article.getContentSourceUrl());
articleJson.addProperty("content", article.getContent());
articleJson.addProperty("digest", article.getDigest());
articleJson.addProperty("show_cover_pic", article.getShowCoverPic());
articleJsonArray.add(articleJson);
}
newsJsonObject.add("articles", articleJsonArray);
}
messageJson.add("mpnews", newsJsonObject);
break;
}
case KefuMsgType.TASKCARD: {
JsonObject text = new JsonObject();
text.addProperty("title", this.getTitle());
text.addProperty("description", this.getDescription());
if (StringUtils.isNotBlank(this.getUrl())) {
text.addProperty("url", this.getUrl());
}
text.addProperty("task_id", this.getTaskId());
JsonArray buttonJsonArray = new JsonArray();
for (TaskCardButton button : this.getTaskButtons()) {
JsonObject buttonJson = new JsonObject();
buttonJson.addProperty("key", button.getKey());
buttonJson.addProperty("name", button.getName());
if (StringUtils.isNotBlank(button.getReplaceName())) {
buttonJson.addProperty("replace_name", button.getReplaceName());
}
if (StringUtils.isNotBlank(button.getColor())) {
buttonJson.addProperty("color", button.getColor());
}
if (button.getBold() != null) {
buttonJson.addProperty("is_bold", button.getBold());
}
buttonJsonArray.add(buttonJson);
}
text.add("btn", buttonJsonArray);
messageJson.add("taskcard", text);
break;
}
default: {
// do nothing
}
}
}
}

View File

@@ -0,0 +1,42 @@
package me.chanjar.weixin.cp.bean;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.Serializable;
import java.util.List;
/**
* <pre>
* 更新任务卡片消息状态的返回类
* 参考文档https://work.weixin.qq.com/api/doc#90000/90135/91579
* Created by Jeff on 2019-05-16.
* </pre>
*
* @author <a href="https://github.com/domainname">Jeff</a>
* @date 2019-05-16
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WxCpTaskCardUpdateResult implements Serializable {
@SerializedName("errcode")
private Integer errcode;
@SerializedName("errmsg")
private String errmsg;
/**
* 用户列表
*/
@SerializedName("invaliduser")
private List<String> invalidUsers;
public static WxCpTaskCardUpdateResult fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpTaskCardUpdateResult.class);
}
}

View File

@@ -20,7 +20,8 @@ public class WxCpUser implements Serializable {
private static final long serialVersionUID = -5696099236344075582L;
private String userId;
private String name;
private Integer[] departIds;
private Long[] departIds;
private Integer[] orders;
private String position;
private String mobile;
private Gender gender;

View File

@@ -1,24 +1,26 @@
package me.chanjar.weixin.cp.bean;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.util.XmlUtils;
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
import java.util.Map;
/**
* <pre>
@@ -36,6 +38,11 @@ import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
public class WxCpXmlMessage implements Serializable {
private static final long serialVersionUID = -1042994982179476410L;
/**
* 使用dom4j解析的存放所有xml属性和值的map.
*/
private Map<String, Object> allFieldsMap;
///////////////////////
// 以下都是微信推送过来的消息的xml的element所对应的属性
///////////////////////
@@ -149,6 +156,10 @@ public class WxCpXmlMessage implements Serializable {
@XStreamConverter(value = XStreamCDataConverter.class)
private String recognition;
@XStreamAlias("TaskId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String taskId;
/**
* 通讯录变更事件.
* 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType
@@ -240,6 +251,13 @@ public class WxCpXmlMessage implements Serializable {
@XStreamConverter(value = XStreamCDataConverter.class)
private String telephone;
/**
* 地址.
*/
@XStreamAlias("Address")
@XStreamConverter(value = XStreamCDataConverter.class)
private String address;
/**
* 扩展属性.
*/
@@ -320,17 +338,20 @@ public class WxCpXmlMessage implements Serializable {
*/
@XStreamAlias("TotalCount")
private Integer totalCount;
/**
* 过滤.
* 过滤是指特定地区、性别的过滤、用户设置拒收的过滤用户接收已超4条的过滤准备发送的粉丝数原则上filterCount = sentCount + errorCount
*/
@XStreamAlias("FilterCount")
private Integer filterCount;
/**
* 发送成功的粉丝数.
*/
@XStreamAlias("SentCount")
private Integer sentCount;
/**
* 发送失败的粉丝数.
*/
@@ -349,7 +370,9 @@ public class WxCpXmlMessage implements Serializable {
protected static WxCpXmlMessage fromXml(String xml) {
//修改微信变态的消息内容格式,方便解析
xml = xml.replace("</PicList><PicList>", "");
return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml);
final WxCpXmlMessage xmlMessage = XStreamTransformer.fromXml(WxCpXmlMessage.class, xml);
xmlMessage.setAllFieldsMap(XmlUtils.xml2Map(xml));
return xmlMessage;
}
protected static WxCpXmlMessage fromXml(InputStream is) {
@@ -402,9 +425,11 @@ public class WxCpXmlMessage implements Serializable {
@Data
public static class ExtAttr {
@XStreamAlias("Item")
@XStreamImplicit(itemFieldName = "Item")
protected final List<Item> items = new ArrayList<>();
@XStreamAlias("Item")
@Data
public static class Item {
@XStreamAlias("Name")

View File

@@ -1,9 +1,12 @@
package me.chanjar.weixin.cp.bean.article;
import lombok.Data;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <pre>
* Created by BinaryWang on 2017/3/27.
@@ -12,6 +15,9 @@ import java.io.Serializable;
* @author Binary Wang
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class NewArticle implements Serializable {
private static final long serialVersionUID = 4087852055781140659L;

View File

@@ -0,0 +1,32 @@
package me.chanjar.weixin.cp.bean.messagebuilder;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.bean.WxCpMessage;
/**
* <pre>
* markdown类型的消息builder
* Created by Binary Wang on 2019/1/20.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class MarkdownMsgBuilder extends BaseBuilder<MarkdownMsgBuilder> {
private String content;
public MarkdownMsgBuilder() {
this.msgType = WxConsts.KefuMsgType.MARKDOWN;
}
public MarkdownMsgBuilder content(String content) {
this.content = content;
return this;
}
@Override
public WxCpMessage build() {
WxCpMessage m = super.build();
m.setContent(this.content);
return m;
}
}

View File

@@ -0,0 +1,68 @@
package me.chanjar.weixin.cp.bean.messagebuilder;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
import java.util.List;
/**
* <pre>
* 任务卡片消息Builder
* 用法: WxCustomMessage m = WxCustomMessage.TASKCARD().title(...)....toUser(...).build();
* </pre>
*
* @author <a href="https://github.com/domainname">Jeff</a>
* @date 2019-05-16
*/
public class TaskCardBuilder extends BaseBuilder<TaskCardBuilder> {
private String title;
private String description;
private String url;
private String taskId;
/**
* 按钮个数为1~2个
*/
private List<TaskCardButton> buttons;
public TaskCardBuilder() {
this.msgType = WxConsts.KefuMsgType.TASKCARD;
}
public TaskCardBuilder title(String title) {
this.title = title;
return this;
}
public TaskCardBuilder description(String description) {
this.description = description;
return this;
}
public TaskCardBuilder url(String url) {
this.url = url;
return this;
}
public TaskCardBuilder taskId(String taskId) {
this.taskId = taskId;
return this;
}
public TaskCardBuilder buttons(List<TaskCardButton> buttons) {
this.buttons = buttons;
return this;
}
@Override
public WxCpMessage build() {
WxCpMessage m = super.build();
m.setSafe(null);
m.setTitle(this.title);
m.setDescription(this.description);
m.setUrl(this.url);
m.setTaskId(this.taskId);
m.setTaskButtons(this.buttons);
return m;
}
}

View File

@@ -0,0 +1,23 @@
package me.chanjar.weixin.cp.bean.taskcard;
import lombok.Builder;
import lombok.Data;
/**
* <pre>
* 任务卡片按钮
* Created by Jeff on 2019-05-16.
* </pre>
*
* @author <a href="https://github.com/domainname">Jeff</a>
* @date 2019-05-16
*/
@Data
@Builder
public class TaskCardButton {
private String key;
private String name;
private String replaceName;
private String color;
private Boolean bold;
}

View File

@@ -36,11 +36,23 @@ public interface WxCpConfigStorage {
/**
* 应该是线程安全的
*
* @param jsapiTicket
*/
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
String getAgentJsapiTicket();
boolean isAgentJsapiTicketExpired();
/**
* 强制将jsapi ticket过期掉
*/
void expireAgentJsapiTicket();
/**
* 应该是线程安全的
*/
void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds);
String getCorpId();
String getCorpSecret();

View File

@@ -1,11 +1,11 @@
package me.chanjar.weixin.cp.config;
import java.io.File;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.File;
/**
* 基于内存的微信配置provider在实际生产环境中应该将这些配置持久化
*
@@ -32,6 +32,9 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
protected volatile String jsapiTicket;
protected volatile long jsapiTicketExpiresTime;
protected volatile String agentJsapiTicket;
protected volatile long agentJsapiTicketExpiresTime;
protected volatile File tmpDirFile;
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
@@ -95,6 +98,28 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
}
@Override
public String getAgentJsapiTicket() {
return this.agentJsapiTicket;
}
@Override
public boolean isAgentJsapiTicketExpired() {
return System.currentTimeMillis() > this.agentJsapiTicketExpiresTime;
}
@Override
public void expireAgentJsapiTicket() {
this.agentJsapiTicketExpiresTime = 0;
}
@Override
public void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds) {
this.agentJsapiTicket = jsapiTicket;
// 预留200秒的时间
this.agentJsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
}
@Override
public void expireJsapiTicket() {
this.jsapiTicketExpiresTime = 0;

View File

@@ -9,26 +9,21 @@ import redis.clients.jedis.JedisPoolConfig;
import java.io.File;
/**
* Jedis client implementor for wechat config storage.
* <pre>
* 使用说明:本实现仅供参考,并不完整
* 使用说明:本实现仅供参考,并不完整.
* 比如为减少项目依赖未加入redis分布式锁的实现如有需要请自行实现。
* </pre>
*
* @author gaigeshen
*/
public class WxCpJedisConfigStorage implements WxCpConfigStorage {
/**
* Redis keys here
*/
private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN";
private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME";
private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET";
private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME";
/**
* Redis clients pool
*/
private static final String AGENT_JSAPI_TICKET_KEY = "WX_CP_AGENT_%s_JSAPI_TICKET";
private static final String AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY = "WX_CP_AGENT_%s_JSAPI_TICKET_EXPIRES_TIME";
private final JedisPool jedisPool;
private volatile String corpId;
private volatile String corpSecret;
@@ -46,7 +41,7 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
public WxCpJedisConfigStorage(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
public WxCpJedisConfigStorage(String host, int port) {
jedisPool = new JedisPool(host, port);
}
@@ -83,8 +78,7 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
if (expiresTimeStr != null) {
Long expiresTime = Long.parseLong(expiresTimeStr);
return System.currentTimeMillis() > expiresTime;
return System.currentTimeMillis() > Long.parseLong(expiresTimeStr);
}
return true;
@@ -123,17 +117,15 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
@Override
public boolean isJsapiTicketExpired() {
try (Jedis jedis = this.jedisPool.getResource()) {
String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY);
if (expiresTimeStr != null) {
Long expiresTime = Long.parseLong(expiresTimeStr);
long expiresTime = Long.parseLong(expiresTimeStr);
return System.currentTimeMillis() > expiresTime;
}
return true;
}
}
@@ -146,16 +138,51 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
@Override
public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.set(JS_API_TICKET_KEY, jsapiTicket);
jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY,
(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + ""));
}
}
@Override
public String getAgentJsapiTicket() {
try (Jedis jedis = this.jedisPool.getResource()) {
return jedis.get(String.format(AGENT_JSAPI_TICKET_KEY, agentId));
}
}
@Override
public boolean isAgentJsapiTicketExpired() {
try (Jedis jedis = this.jedisPool.getResource()) {
String expiresTimeStr = jedis.get(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId));
if (expiresTimeStr != null) {
return System.currentTimeMillis() > Long.parseLong(expiresTimeStr);
}
return true;
}
}
@Override
public void expireAgentJsapiTicket() {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.set(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId), "0");
}
}
@Override
public void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds) {
try (Jedis jedis = this.jedisPool.getResource()) {
jedis.set(String.format(AGENT_JSAPI_TICKET_KEY, agentId), jsapiTicket);
jedis.set(String.format(AGENT_JSAPI_TICKET_EXPIRES_TIME_KEY, agentId),
(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + ""));
}
}
@Override
public String getCorpId() {
return this.corpId;

View File

@@ -73,7 +73,7 @@ public class WxCpMessageRouter {
this.wxCpService = wxCpService;
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker();
this.sessionManager = new StandardSessionManager();
this.sessionManager = wxCpService.getSessionManager();
this.exceptionHandler = new LogExceptionHandler();
}

View File

@@ -7,7 +7,6 @@ import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.util.json.WxErrorAdapter;
import me.chanjar.weixin.cp.bean.WxCpChat;
import me.chanjar.weixin.cp.bean.WxCpDepart;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.WxCpTag;
import me.chanjar.weixin.cp.bean.WxCpUser;
@@ -20,7 +19,6 @@ public class WxCpGsonBuilder {
static {
INSTANCE.disableHtmlEscaping();
INSTANCE.registerTypeAdapter(WxCpMessage.class, new WxCpMessageGsonAdapter());
INSTANCE.registerTypeAdapter(WxCpChat.class, new WxCpChatGsonAdapter());
INSTANCE.registerTypeAdapter(WxCpDepart.class, new WxCpDepartGsonAdapter());
INSTANCE.registerTypeAdapter(WxCpUser.class, new WxCpUserGsonAdapter());

View File

@@ -1,127 +0,0 @@
/*
* KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
*
* This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
* only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
* arose from modification of the original source, or other redistribution of this source
* is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
*/
package me.chanjar.weixin.cp.util.json;
import com.google.gson.*;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Type;
/**
* @author Daniel Qian
*/
public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> {
@Override
public JsonElement serialize(WxCpMessage message, Type typeOfSrc, JsonSerializationContext context) {
JsonObject messageJson = new JsonObject();
messageJson.addProperty("agentid", message.getAgentId());
if (StringUtils.isNotBlank(message.getToUser())) {
messageJson.addProperty("touser", message.getToUser());
}
messageJson.addProperty("msgtype", message.getMsgType());
if (StringUtils.isNotBlank(message.getToParty())) {
messageJson.addProperty("toparty", message.getToParty());
}
if (StringUtils.isNotBlank(message.getToTag())) {
messageJson.addProperty("totag", message.getToTag());
}
if (WxConsts.KefuMsgType.TEXT.equals(message.getMsgType())) {
JsonObject text = new JsonObject();
text.addProperty("content", message.getContent());
messageJson.add("text", text);
}
if (WxConsts.KefuMsgType.TEXTCARD.equals(message.getMsgType())) {
JsonObject text = new JsonObject();
text.addProperty("title", message.getTitle());
text.addProperty("description", message.getDescription());
text.addProperty("url", message.getUrl());
text.addProperty("btntxt", message.getBtnTxt());
messageJson.add("textcard", text);
}
if (WxConsts.KefuMsgType.IMAGE.equals(message.getMsgType())) {
JsonObject image = new JsonObject();
image.addProperty("media_id", message.getMediaId());
messageJson.add("image", image);
}
if (WxConsts.KefuMsgType.FILE.equals(message.getMsgType())) {
JsonObject image = new JsonObject();
image.addProperty("media_id", message.getMediaId());
messageJson.add("file", image);
}
if (WxConsts.KefuMsgType.VOICE.equals(message.getMsgType())) {
JsonObject voice = new JsonObject();
voice.addProperty("media_id", message.getMediaId());
messageJson.add("voice", voice);
}
if (StringUtils.isNotBlank(message.getSafe())) {
messageJson.addProperty("safe", message.getSafe());
}
if (WxConsts.KefuMsgType.VIDEO.equals(message.getMsgType())) {
JsonObject video = new JsonObject();
video.addProperty("media_id", message.getMediaId());
video.addProperty("thumb_media_id", message.getThumbMediaId());
video.addProperty("title", message.getTitle());
video.addProperty("description", message.getDescription());
messageJson.add("video", video);
}
if (WxConsts.KefuMsgType.NEWS.equals(message.getMsgType())) {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray();
for (NewArticle article : message.getArticles()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("title", article.getTitle());
articleJson.addProperty("description", article.getDescription());
articleJson.addProperty("url", article.getUrl());
articleJson.addProperty("picurl", article.getPicUrl());
articleJsonArray.add(articleJson);
}
newsJsonObject.add("articles", articleJsonArray);
messageJson.add("news", newsJsonObject);
}
if (WxConsts.KefuMsgType.MPNEWS.equals(message.getMsgType())) {
JsonObject newsJsonObject = new JsonObject();
if (message.getMediaId() != null) {
newsJsonObject.addProperty("media_id", message.getMediaId());
} else {
JsonArray articleJsonArray = new JsonArray();
for (MpnewsArticle article : message.getMpnewsArticles()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("title", article.getTitle());
articleJson.addProperty("thumb_media_id", article.getThumbMediaId());
articleJson.addProperty("author", article.getAuthor());
articleJson.addProperty("content_source_url", article.getContentSourceUrl());
articleJson.addProperty("content", article.getContent());
articleJson.addProperty("digest", article.getDigest());
articleJson.addProperty("show_cover_pic", article.getShowCoverPic());
articleJsonArray.add(articleJson);
}
newsJsonObject.add("articles", articleJsonArray);
}
messageJson.add("mpnews", newsJsonObject);
}
return messageJson;
}
}

View File

@@ -6,6 +6,7 @@
* arose from modification of the original source, or other redistribution of this source
* is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
*/
package me.chanjar.weixin.cp.util.json;
import java.lang.reflect.Type;
@@ -24,11 +25,14 @@ import me.chanjar.weixin.cp.bean.Gender;
import me.chanjar.weixin.cp.bean.WxCpUser;
/**
* cp user gson adapter.
*
* @author Daniel Qian
*/
public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSerializer<WxCpUser> {
private static final String EXTERNAL_PROFILE = "external_profile";
private static final String EXTERNAL_ATTR = "external_attr";
private static final String EXTATTR = "extattr";
@Override
public WxCpUser deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
@@ -37,14 +41,24 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri
if (o.get("department") != null) {
JsonArray departJsonArray = o.get("department").getAsJsonArray();
Integer[] departIds = new Integer[departJsonArray.size()];
Long[] departIds = new Long[departJsonArray.size()];
int i = 0;
for (JsonElement jsonElement : departJsonArray) {
departIds[i++] = jsonElement.getAsInt();
departIds[i++] = jsonElement.getAsLong();
}
user.setDepartIds(departIds);
}
if (o.get("order") != null) {
JsonArray departJsonArray = o.get("order").getAsJsonArray();
Integer[] orders = new Integer[departJsonArray.size()];
int i = 0;
for (JsonElement jsonElement : departJsonArray) {
orders[i++] = jsonElement.getAsInt();
}
user.setOrders(orders);
}
user.setUserId(GsonHelper.getString(o, "userid"));
user.setName(GsonHelper.getString(o, "name"));
user.setPosition(GsonHelper.getString(o, "position"));
@@ -62,64 +76,73 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri
user.setQrCode(GsonHelper.getString(o, "qr_code"));
user.setToInvite(GsonHelper.getBoolean(o, "to_invite"));
if (GsonHelper.isNotNull(o.get("extattr"))) {
JsonArray attrJsonElements = o.get("extattr").getAsJsonObject().get("attrs").getAsJsonArray();
for (JsonElement attrJsonElement : attrJsonElements) {
WxCpUser.Attr attr = new WxCpUser.Attr(
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"),
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value")
);
user.getExtAttrs().add(attr);
}
if (GsonHelper.isNotNull(o.get(EXTATTR))) {
this.buildExtraAttrs(o, user);
}
if (GsonHelper.isNotNull(o.get(EXTERNAL_PROFILE))) {
JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray();
for (JsonElement element : attrJsonElements) {
final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type");
final String name = GsonHelper.getString(element.getAsJsonObject(), "name");
this.buildExternalAttrs(o, user);
}
switch (type) {
case 0: {
user.getExternalAttrs()
.add(WxCpUser.ExternalAttribute.builder()
.type(type)
.name(name)
.value(GsonHelper.getString(element.getAsJsonObject().get("text").getAsJsonObject(), "value"))
.build()
);
break;
}
case 1: {
final JsonObject web = element.getAsJsonObject().get("web").getAsJsonObject();
user.getExternalAttrs()
.add(WxCpUser.ExternalAttribute.builder()
.type(type)
.name(name)
.url(GsonHelper.getString(web, "url"))
.title(GsonHelper.getString(web, "title"))
.build()
);
break;
}
case 2: {
final JsonObject miniprogram = element.getAsJsonObject().get("miniprogram").getAsJsonObject();
user.getExternalAttrs()
.add(WxCpUser.ExternalAttribute.builder()
.type(type)
.name(name)
.appid(GsonHelper.getString(miniprogram, "appid"))
.pagePath(GsonHelper.getString(miniprogram, "pagepath"))
.title(GsonHelper.getString(miniprogram, "title"))
.build()
);
break;
}
default://ignored
return user;
}
private void buildExtraAttrs(JsonObject o, WxCpUser user) {
JsonArray attrJsonElements = o.get(EXTATTR).getAsJsonObject().get("attrs").getAsJsonArray();
for (JsonElement attrJsonElement : attrJsonElements) {
WxCpUser.Attr attr = new WxCpUser.Attr(
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "name"),
GsonHelper.getString(attrJsonElement.getAsJsonObject(), "value")
);
user.getExtAttrs().add(attr);
}
}
private void buildExternalAttrs(JsonObject o, WxCpUser user) {
JsonArray attrJsonElements = o.get(EXTERNAL_PROFILE).getAsJsonObject().get(EXTERNAL_ATTR).getAsJsonArray();
for (JsonElement element : attrJsonElements) {
final Integer type = GsonHelper.getInteger(element.getAsJsonObject(), "type");
final String name = GsonHelper.getString(element.getAsJsonObject(), "name");
switch (type) {
case 0: {
user.getExternalAttrs()
.add(WxCpUser.ExternalAttribute.builder()
.type(type)
.name(name)
.value(GsonHelper.getString(element.getAsJsonObject().get("text").getAsJsonObject(), "value"))
.build()
);
break;
}
case 1: {
final JsonObject web = element.getAsJsonObject().get("web").getAsJsonObject();
user.getExternalAttrs()
.add(WxCpUser.ExternalAttribute.builder()
.type(type)
.name(name)
.url(GsonHelper.getString(web, "url"))
.title(GsonHelper.getString(web, "title"))
.build()
);
break;
}
case 2: {
final JsonObject miniprogram = element.getAsJsonObject().get("miniprogram").getAsJsonObject();
user.getExternalAttrs()
.add(WxCpUser.ExternalAttribute.builder()
.type(type)
.name(name)
.appid(GsonHelper.getString(miniprogram, "appid"))
.pagePath(GsonHelper.getString(miniprogram, "pagepath"))
.title(GsonHelper.getString(miniprogram, "title"))
.build()
);
break;
}
default://ignored
}
}
return user;
}
@Override
@@ -133,11 +156,20 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri
}
if (user.getDepartIds() != null) {
JsonArray jsonArray = new JsonArray();
for (Integer departId : user.getDepartIds()) {
for (Long departId : user.getDepartIds()) {
jsonArray.add(new JsonPrimitive(departId));
}
o.add("department", jsonArray);
}
if (user.getOrders() != null) {
JsonArray jsonArray = new JsonArray();
for (Integer order : user.getOrders()) {
jsonArray.add(new JsonPrimitive(order));
}
o.add("order", jsonArray);
}
if (user.getPosition() != null) {
o.addProperty("position", user.getPosition());
}
@@ -191,14 +223,14 @@ public class WxCpUserGsonAdapter implements JsonDeserializer<WxCpUser>, JsonSeri
}
JsonObject attrsJson = new JsonObject();
attrsJson.add("attrs", attrsJsonArray);
o.add("extattr", attrsJson);
o.add(EXTATTR, attrsJson);
}
if (user.getExternalAttrs().size() > 0) {
JsonArray attrsJsonArray = new JsonArray();
for (WxCpUser.ExternalAttribute attr : user.getExternalAttrs()) {
JsonObject attrJson = new JsonObject();
attrJson.addProperty("type",attr.getType());
attrJson.addProperty("type", attr.getType());
attrJson.addProperty("name", attr.getName());
switch (attr.getType()) {
case 0: {

View File

@@ -1,11 +1,12 @@
package me.chanjar.weixin.cp.api;
import org.testng.annotations.*;
import com.google.inject.Inject;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
import org.testng.annotations.*;
import static org.testng.Assert.*;
@@ -14,7 +15,7 @@ import static org.testng.Assert.*;
* @author Daniel Qian
*
*/
@Test(groups = "customMessageAPI")
@Test
@Guice(modules = ApiTestModule.class)
public class WxCpMessageAPITest {
@@ -59,4 +60,51 @@ public class WxCpMessageAPITest {
System.out.println(messageSendResult.getInvalidUserList());
System.out.println(messageSendResult.getInvalidTagList());
}
@Test
public void testSendMessage_markdown() throws WxErrorException {
WxCpMessage message = WxCpMessage
.MARKDOWN()
.toUser(configStorage.getUserId())
.content("您的会议室已经预定,稍后会同步到`邮箱` \n" +
" >**事项详情** \n" +
" >事 项:<font color=\\\"info\\\">开会</font> \n" +
" >组织者:@miglioguan \n" +
" >参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" +
" > \n" +
" >会议室:<font color=\\\"info\\\">广州TIT 1楼 301</font> \n" +
" >日 期:<font color=\\\"warning\\\">2018年5月18日</font> \n" +
" >时 间:<font color=\\\"comment\\\">上午9:00-11:00</font> \n" +
" > \n" +
" >请准时参加会议。 \n" +
" > \n" +
" >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)")
.build();
WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message);
assertNotNull(messageSendResult);
System.out.println(messageSendResult);
System.out.println(messageSendResult.getInvalidPartyList());
System.out.println(messageSendResult.getInvalidUserList());
System.out.println(messageSendResult.getInvalidTagList());
}
@Test
public void testSendMessage_textCard() throws WxErrorException {
WxCpMessage message = WxCpMessage
.TEXTCARD()
.toUser(configStorage.getUserId())
.btnTxt("更多")
.description( "<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台领奖码xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>")
.url("URL")
.title("领奖通知")
.build();
WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message);
assertNotNull(messageSendResult);
System.out.println(messageSendResult);
System.out.println(messageSendResult.getInvalidPartyList());
System.out.println(messageSendResult.getInvalidUserList());
System.out.println(messageSendResult.getInvalidTagList());
}
}

View File

@@ -0,0 +1,36 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpService;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.testng.Assert.*;
/**
* <pre>
* Created by BinaryWang on 2019/3/31.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Test
@Guice(modules = ApiTestModule.class)
public class BaseWxCpServiceImplTest {
@Inject
protected WxCpService wxService;
@Test
public void testGetAgentJsapiTicket() throws WxErrorException {
assertThat(this.wxService.getAgentJsapiTicket()).isNotEmpty();
assertThat(this.wxService.getAgentJsapiTicket(true)).isNotEmpty();
}
@Test
public void testJsCode2Session() throws WxErrorException {
assertThat(this.wxService.jsCode2Session("111")).isNotNull();
}
}

View File

@@ -2,15 +2,21 @@ package me.chanjar.weixin.cp.api.impl;
import java.util.Arrays;
import me.chanjar.weixin.cp.bean.WxCpChat;
import org.testng.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import org.testng.*;
import org.testng.annotations.*;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpAppChatMessage;
import me.chanjar.weixin.cp.bean.WxCpChat;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 测试群聊服务
@@ -19,28 +25,134 @@ import me.chanjar.weixin.cp.api.WxCpService;
*/
@Guice(modules = ApiTestModule.class)
public class WxCpChatServiceImplTest {
private String chatId;
private String userId;
@Inject
private WxCpService wxCpService;
@Test
public void create() throws Exception {
wxCpService.getChatService().chatCreate("测试群聊", "gaige_shen", Arrays.asList("gaige_shen", "ZhangXiaoMing"), "mychatid");
private WxCpService cpService;
@BeforeTest
public void init() {
this.chatId = "mychatid";
this.userId = ((ApiTestModule.WxXmlCpInMemoryConfigStorage) this.cpService.getWxCpConfigStorage()).getUserId();
}
@Test
public void get() throws Exception {
WxCpChat chat = wxCpService.getChatService().chatGet("mychatid");
public void testChatCreate() throws Exception {
final String result = cpService.getChatService().chatCreate("测试群聊", userId,
Arrays.asList(userId, userId), chatId);
assertThat(result).isNotEmpty();
assertThat(result).isEqualTo(chatId);
}
@Test
public void testChatGet() throws Exception {
WxCpChat chat = this.cpService.getChatService().chatGet(chatId);
System.out.println(chat);
Assert.assertEquals(chat.getName(), "测试群聊");
}
@Test
public void update() throws Exception {
wxCpService.getChatService().chatUpdate("mychatid", "", "", Arrays.asList("ZhengWuYao"), null);
WxCpChat chat = wxCpService.getChatService().chatGet("mychatid");
public void testChatUpdate() throws Exception {
this.cpService.getChatService().chatUpdate(chatId, "", "", Arrays.asList("ZhengWuYao"), null);
WxCpChat chat = this.cpService.getChatService().chatGet(chatId);
System.out.println(chat);
Assert.assertEquals(chat.getUsers().size(), 3);
}
@DataProvider
public Object[][] messages() {
return new Object[][]{
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.TEXT)
.chatId(chatId)
.content("你的快递已到\n请携带工卡前往邮件中心领取")
.build()
},
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.IMAGE)
.chatId(chatId)
.mediaId("3_xWGPXZhpOKZrlRISWrjhPrDUZqZ-jIEVzxd56jLuqM")
.build()
},
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.VOICE)
.chatId(chatId)
.mediaId("3X5t6HkdN1hUgB7OzrdRnc8v0yI0CqlAxFxnCkS3msTnTLanpYrV4esLv4foZVnlf")
.build()
},
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.VIDEO)
.chatId(chatId)
.mediaId("3otWyy_acbID8fyltmCOW5hGVD8oa0_p0za5jhukxKTUDoGT71lqTvtQAWoycXpQf")
.title("aaaa")
.description("ddddd")
.build()
},
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.FILE)
.chatId(chatId)
.mediaId("34AyVyDdndVhB4Z2tT-_FYKZ7Xqrr47LPC11GHH4oy7o")
.build()
},
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.TEXTCARD)
.chatId(chatId)
.btnTxt("更多")
.title("领奖通知")
.url("https://zhidao.baidu.com/question/2073647112026042748.html")
.description("<div class=\"gray\">2016年9月26日</div> <div class=\"normal\"> 恭喜你抽中iPhone 7一台领奖码:520258</div><div class=\"highlight\">请于2016年10月10日前联系行 政同事领取</div>")
.build()
},
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.NEWS)
.chatId(chatId)
.articles(Lists.newArrayList(NewArticle.builder()
.title("领奖通知")
.url("https://zhidao.baidu.com/question/2073647112026042748.html")
.description("今年中秋节公司有豪礼相送")
.picUrl("http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png")
.build()
))
.build()
},
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.MPNEWS)
.chatId(chatId)
.mpnewsArticles(Lists.newArrayList(MpnewsArticle.newBuilder()
.title("地球一小时")
.thumbMediaId("3_xWGPXZhpOKZrlRISWrjhPrDUZqZ-jIEVzxd56jLuqM")
.author("Author")
.contentSourceUrl("https://work.weixin.qq.com")
.content("3月24日20:30-21:30 \n办公区将关闭照明一小时请各部门同事相互转告")
.digest("3月24日20:30-21:30 \n办公区将关闭照明一小时")
.build()
))
.build()
},
{WxCpAppChatMessage.builder()
.msgType(AppChatMsgType.MARKDOWN)
.chatId(chatId)
.content("您的会议室已经预定,稍后会同步到`邮箱` \n" +
" >**事项详情** \n" +
" >事 项:<font color=\\\"info\\\">开会</font> \n" +
" >组织者:@miglioguan \n" +
" >参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n" +
" > \n" +
" >会议室:<font color=\\\"info\\\">广州TIT 1楼 301</font> \n" +
" >日 期:<font color=\\\"warning\\\">2018年5月18日</font> \n" +
" >时 间:<font color=\\\"comment\\\">上午9:00-11:00</font> \n" +
" > \n" +
" >请准时参加会议。 \n" +
" > \n" +
" >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)")
.build()
},
};
}
@Test(dataProvider = "messages")
public void testSendMsg(WxCpAppChatMessage message) throws WxErrorException {
this.cpService.getChatService().sendMsg(message);
}
}

View File

@@ -31,7 +31,7 @@ public class WxCpDepartmentServiceImplTest {
cpDepart.setName("子部门" + System.currentTimeMillis());
cpDepart.setParentId(1L);
cpDepart.setOrder(1L);
Integer departId = this.wxCpService.getDepartmentService().create(cpDepart);
Long departId = this.wxCpService.getDepartmentService().create(cpDepart);
System.out.println(departId);
}

View File

@@ -0,0 +1,49 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpCheckinData;
import me.chanjar.weixin.cp.bean.WxCpCheckinOption;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Element
* @date 2019-04-20 13:46
*/
@Guice(modules = ApiTestModule.class)
public class WxCpOAServiceImplTest {
@Inject
protected WxCpService wxService;
@Test
public void testGetCheckinData() throws ParseException, WxErrorException {
Date startTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-04-11");
Date endTime = DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2019-05-10");
List<WxCpCheckinData> results = wxService.getOAService()
.getCheckinData(1, startTime, endTime, Lists.newArrayList("binary"));
assertThat(results).isNotNull();
}
@Test
public void testGetCheckinOption() throws WxErrorException {
Date now = new Date();
List<WxCpCheckinOption> results = wxService.getOAService()
.getCheckinOption(now, Lists.newArrayList("binary"));
assertThat(results).isNotNull();
}
}

View File

@@ -0,0 +1,65 @@
package me.chanjar.weixin.cp.api.impl;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import java.util.Arrays;
import static org.testng.Assert.assertNotNull;
/**
* 测试任务卡片服务
*
* @author <a href="https://github.com/domainname">Jeff</a>
* @date 2019-05-16
*/
@Guice(modules = ApiTestModule.class)
public class WxCpTaskCardServiceImplTest {
@Inject
private WxCpService wxCpService;
@Test
public void testSendTaskCard() throws WxErrorException {
TaskCardButton btn1 = TaskCardButton.builder()
.key("key1")
.name("同意")
.replaceName("已同意")
.bold(true)
.build();
TaskCardButton btn2 = TaskCardButton.builder()
.key("key2")
.name("拒绝")
.replaceName("已拒绝")
.color("red")
.build();
WxCpMessage message = WxCpMessage.TASKCARD()
.toUser("jeff|mr.t")
.title("有一个待审批的请求")
.description("申请:购买图书\n金额100 元")
.taskId("task_1")
.url("http://www.qq.com")
.buttons(Arrays.asList(btn1, btn2))
.build();
WxCpMessageSendResult messageSendResult = this.wxCpService.messageSend(message);
assertNotNull(messageSendResult);
System.out.println(messageSendResult);
System.out.println(messageSendResult.getInvalidPartyList());
System.out.println(messageSendResult.getInvalidUserList());
System.out.println(messageSendResult.getInvalidTagList());
}
@Test
public void testUpdate() throws Exception {
wxCpService.getTaskCardService().update(Arrays.asList("jeff", "mr.t"), "task_1", "key1");
}
}

View File

@@ -42,7 +42,7 @@ public class WxCpUserServiceImplTest {
WxCpUser user = new WxCpUser();
user.setUserId(userId);
user.setName("Some Woman");
user.setDepartIds(new Integer[]{2});
user.setDepartIds(new Long[]{2L});
user.setEmail("none@none.com");
user.setGender(Gender.FEMALE);
user.setMobile("13560084979");

View File

@@ -1,7 +1,7 @@
package me.chanjar.weixin.cp.bean;
import org.testng.Assert;
import org.testng.annotations.Test;
import org.testng.*;
import org.testng.annotations.*;
/**
* Created by huansinho on 2018/4/13.
@@ -10,7 +10,13 @@ import org.testng.annotations.Test;
public class WxCpAgentTest {
public void testDeserialize() {
String json = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\",\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\",\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}, {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]},\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]},\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0,\"isreportenter\": 0,\"home_url\": \"\"}";
String json = "{\"errcode\": 0,\"errmsg\": \"ok\",\"agentid\": 9,\"name\": \"测试应用\"," +
"\"square_logo_url\": \"http://wx.qlogo.cn/mmhead/alksjf;lasdjf;lasjfuodiuj3rj2o34j/0\"," +
"\"description\": \"这是一个企业号应用\",\"allow_userinfos\": {\"user\": [{\"userid\": \"0009854\"}," +
" {\"userid\": \"1723\"}, {\"userid\": \"5625\"}]},\"allow_partys\": {\"partyid\": [42762742]}," +
"\"allow_tags\": {\"tagid\": [23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7]}," +
"\"close\": 0,\"redirect_domain\": \"weixin.com.cn\",\"report_location_flag\": 0," +
"\"isreportenter\": 0,\"home_url\": \"\"}";
WxCpAgent wxCpAgent = WxCpAgent.fromJson(json);
@@ -18,7 +24,8 @@ public class WxCpAgentTest {
Assert.assertEquals(new Integer[]{42762742}, wxCpAgent.getAllowParties().getPartyIds().toArray());
Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7}, wxCpAgent.getAllowTags().getTagIds().toArray());
Assert.assertEquals(new Integer[]{23, 22, 35, 19, 32, 125, 133, 46, 150, 38, 183, 9, 7},
wxCpAgent.getAllowTags().getTagIds().toArray());
}

View File

@@ -2,10 +2,12 @@ package me.chanjar.weixin.cp.bean;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
import org.testng.annotations.Test;
import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
import static org.testng.Assert.assertEquals;
@Test
public class WxCpMessageTest {
@@ -19,12 +21,16 @@ public class WxCpMessageTest {
public void testTextCardBuild() {
WxCpMessage reply = WxCpMessage.TEXTCARD().toUser("OPENID")
.title("领奖通知")
.description("<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台领奖码xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>")
.description("<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台" +
"领奖码xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>")
.url("http://www.qq.com")
.btnTxt("更多")
.build();
assertThat(reply.toJson())
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"textcard\",\"textcard\":{\"title\":\"领奖通知\",\"description\":\"<div class=\\\"gray\\\">2016年9月26日</div> <div class=\\\"normal\\\">恭喜你抽中iPhone 7一台领奖码xxxx</div><div class=\\\"highlight\\\">请于2016年10月10日前联系行政同事领取</div>\",\"url\":\"http://www.qq.com\",\"btntxt\":\"更多\"},\"safe\":\"0\"}");
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"textcard\",\"textcard\":{\"title\":\"领奖通知\"," +
"\"description\":\"<div class=\\\"gray\\\">2016年9月26日</div> <div class=\\\"normal\\\">" +
"恭喜你抽中iPhone 7一台领奖码xxxx</div><div class=\\\"highlight\\\">请于2016年10月10日前联系行政同事领取</div>\"," +
"\"url\":\"http://www.qq.com\",\"btntxt\":\"更多\"},\"safe\":\"0\"}");
}
public void testImageBuild() {
@@ -40,9 +46,11 @@ public class WxCpMessageTest {
}
public void testVideoBuild() {
WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build();
WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID")
.description("DESCRIPTION").build();
assertThat(reply.toJson())
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"safe\":\"0\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}");
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\"," +
"\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"},\"safe\":\"0\"}");
}
public void testNewsBuild() {
@@ -61,7 +69,10 @@ public class WxCpMessageTest {
WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build();
assertThat(reply.toJson())
.isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"safe\":\"0\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}");
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":" +
"[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}," +
"{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}," +
"\"safe\":\"0\"}");
}
public void testMpnewsBuild_with_articles() {
@@ -88,14 +99,45 @@ public class WxCpMessageTest {
WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1, article2).build();
assertThat(reply.toJson())
.isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"safe\":\"0\",\"mpnews\":{\"articles\":[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"},{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}}");
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"articles\":" +
"[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," +
"\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}" +
",{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," +
"\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}," +
"\"safe\":\"0\"}");
}
public void testMpnewsBuild_with_media_id() {
WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").mediaId("mmm").build();
assertThat(reply.toJson())
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"safe\":\"0\",\"mpnews\":{\"media_id\":\"mmm\"}}");
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"media_id\":\"mmm\"},\"safe\":\"0\"}");
}
public void testTaskCardBuilder() {
TaskCardButton button1 = TaskCardButton.builder()
.key("yes")
.name("批准")
.replaceName("已批准")
.color("blue")
.bold(true)
.build();
TaskCardButton button2 = TaskCardButton.builder()
.key("yes")
.name("拒绝")
.replaceName("已拒绝")
.color("red")
.bold(false)
.build();
WxCpMessage reply = WxCpMessage.TASKCARD().toUser("OPENID")
.title("任务卡片")
.description("有一条待处理任务")
.url("http://www.qq.com")
.taskId("task_123")
.buttons(Arrays.asList(button1, button2))
.build();
assertThat(reply.toJson())
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"taskcard\",\"taskcard\":{\"title\":\"任务卡片\",\"description\":\"有一条待处理任务\",\"url\":\"http://www.qq.com\",\"task_id\":\"task_123\",\"btn\":[{\"key\":\"yes\",\"name\":\"批准\",\"replace_name\":\"已批准\",\"color\":\"blue\",\"is_bold\":true},{\"key\":\"yes\",\"name\":\"拒绝\",\"replace_name\":\"已拒绝\",\"color\":\"red\",\"is_bold\":false}]}}");
}
}

View File

@@ -1,9 +1,11 @@
package me.chanjar.weixin.cp.bean;
import me.chanjar.weixin.common.api.WxConsts;
import org.testng.annotations.*;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import static me.chanjar.weixin.cp.WxCpConsts.EventType.TASKCARD_CLICK;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@Test
public class WxCpXmlMessageTest {
@@ -117,4 +119,58 @@ public class WxCpXmlMessageTest {
assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "aef52ae501537e552725c5d7f99c1741");
assertEquals(wxMessage.getSendPicsInfo().getPicList().get(1).getPicMd5Sum(), "c4564632a4fab91378c39bea6aad6f9e");
}
public void testExtAttr() {
String xml = "<xml>" +
" <ToUserName><![CDATA[w56c9fe3d50ad1ea2]]></ToUserName>" +
" <FromUserName><![CDATA[sys]]></FromUserName>" +
" <CreateTime>1557241961</CreateTime>" +
" <MsgType><![CDATA[event]]></MsgType>" +
" <Event><![CDATA[change_contact]]></Event>" +
" <ChangeType><![CDATA[update_user]]></ChangeType>" +
" <UserID><![CDATA[zhangsan]]></UserID>" +
" <ExtAttr>" +
" <Item><Name><![CDATA[爱好]]></Name><Value><![CDATA[111]]></Value><Text><Value><![CDATA[111]]></Value></Text></Item>" +
" <Item><Name><![CDATA[入职时间]]></Name><Value><![CDATA[11111]]></Value><Text><Value><![CDATA[11111]]></Value></Text></Item>" +
" <Item><Name><![CDATA[城市]]></Name><Value><![CDATA[11111]]></Value><Text><Value><![CDATA[11111]]></Value></Text></Item>" +
" </ExtAttr>" +
" <Address><![CDATA[11111]]></Address>" +
"</xml>";
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml);
assertEquals(wxMessage.getToUserName(), "w56c9fe3d50ad1ea2");
assertEquals(wxMessage.getFromUserName(), "sys");
assertEquals(wxMessage.getCreateTime(), new Long(1557241961));
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT);
assertEquals(wxMessage.getEvent(), "change_contact");
assertEquals(wxMessage.getChangeType(), "update_user");
assertEquals(wxMessage.getUserId(), "zhangsan");
assertNotNull(wxMessage.getExtAttrs());
assertNotNull(wxMessage.getExtAttrs().getItems());
assertEquals(wxMessage.getExtAttrs().getItems().size(), 3);
assertEquals(wxMessage.getExtAttrs().getItems().get(0).getName(), "爱好");
}
public void testTaskCardEvent() {
String xml = "<xml>" +
"<ToUserName><![CDATA[toUser]]></ToUserName>" +
"<FromUserName><![CDATA[FromUser]]></FromUserName>" +
"<CreateTime>123456789</CreateTime>" +
"<MsgType><![CDATA[event]]></MsgType>" +
"<Event><![CDATA[taskcard_click]]></Event>" +
"<EventKey><![CDATA[key111]]></EventKey>" +
"<TaskId><![CDATA[taskid111]]></TaskId >" +
"<AgentID>1</AgentID>" +
"</xml>";
WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml);
assertEquals(wxMessage.getToUserName(), "toUser");
assertEquals(wxMessage.getFromUserName(), "FromUser");
assertEquals(wxMessage.getCreateTime(), Long.valueOf(123456789L));
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT);
assertEquals(wxMessage.getAgentId(), Integer.valueOf(1));
assertEquals(wxMessage.getEvent(), TASKCARD_CLICK);
assertEquals(wxMessage.getEventKey(), "key111");
assertEquals(wxMessage.getTaskId(), "taskid111");
}
}

View File

@@ -1,28 +1,23 @@
package me.chanjar.weixin.cp.demo;
import java.io.InputStream;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.ToString;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.cp.config.WxCpInMemoryConfigStorage;
import java.io.InputStream;
/**
* @author Daniel Qian
*/
@XStreamAlias("xml")
class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage {
@ToString
public class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage {
public static WxCpDemoInMemoryConfigStorage fromXml(InputStream is) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(WxCpDemoInMemoryConfigStorage.class);
return (WxCpDemoInMemoryConfigStorage) xstream.fromXML(is);
}
@Override
public String toString() {
return "SimpleWxConfigProvider [appidOrCorpid=" + this.corpId + ", corpSecret=" + this.corpSecret + ", accessToken=" + this.accessToken
+ ", expiresTime=" + this.expiresTime + ", token=" + this.token + ", aesKey=" + this.aesKey + "]";
}
}

View File

@@ -76,6 +76,13 @@ public class WxCpUserGsonAdapterTest {
final WxCpUser user = WxCpUser.fromJson(userJson);
assertThat(user).isNotNull();
assertThat(user.getOrders()).isNotEmpty();
assertThat(user.getOrders().length).isEqualTo(2);
assertThat(user.getOrders()[0]).isEqualTo(1);
assertThat(user.getOrders()[1]).isEqualTo(2);
assertThat(user.getExternalAttrs()).isNotEmpty();
final WxCpUser.ExternalAttribute externalAttr1 = user.getExternalAttrs().get(0);
@@ -100,6 +107,7 @@ public class WxCpUserGsonAdapterTest {
@Test
public void testSerialize() {
WxCpUser user = new WxCpUser();
user.setOrders(new Integer[]{1, 2});
user.addExternalAttr(WxCpUser.ExternalAttribute.builder()
.type(0)
.name("文本名称")
@@ -119,6 +127,10 @@ public class WxCpUserGsonAdapterTest {
.title("my miniprogram")
.build());
assertThat(user.toJson()).isEqualTo("{\"external_profile\":{\"external_attr\":[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}},{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}},{\"type\":2,\"name\":\"测试app\",\"miniprogram\":{\"appid\":\"wx8bd80126147df384\",\"pagepath\":\"/index\",\"title\":\"my miniprogram\"}}]}}");
assertThat(user.toJson()).isEqualTo("{\"order\":[1,2],\"external_profile\":{\"external_attr\":" +
"[{\"type\":0,\"name\":\"文本名称\",\"text\":{\"value\":\"文本\"}}," +
"{\"type\":1,\"name\":\"网页名称\",\"web\":{\"url\":\"http://www.test.com\",\"title\":\"标题\"}}," +
"{\"type\":2,\"name\":\"测试app\"," +
"\"miniprogram\":{\"appid\":\"wx8bd80126147df384\",\"pagepath\":\"/index\",\"title\":\"my miniprogram\"}}]}}");
}
}