Files
weixin-java-tools/src/main/java/chanjarster/weixin/api/WxServiceImpl.java

235 lines
8.8 KiB
Java
Raw Normal View History

2014-08-21 21:19:24 +08:00
package chanjarster.weixin.api;
2014-08-18 16:48:20 +08:00
2014-08-23 12:04:29 +08:00
import java.io.File;
2014-08-18 16:48:20 +08:00
import java.io.IOException;
2014-08-23 12:04:29 +08:00
import java.io.InputStream;
2014-08-22 14:52:11 +08:00
import java.security.MessageDigest;
import java.util.Arrays;
2014-08-23 12:04:29 +08:00
import java.util.UUID;
2014-08-18 16:48:20 +08:00
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
2014-08-21 21:19:24 +08:00
import chanjarster.weixin.bean.WxAccessToken;
import chanjarster.weixin.bean.WxCustomMessage;
2014-08-25 11:01:09 +08:00
import chanjarster.weixin.bean.WxMassMessage;
import chanjarster.weixin.bean.WxMassNews;
import chanjarster.weixin.bean.WxMassVideo;
2014-08-21 21:19:24 +08:00
import chanjarster.weixin.bean.WxMenu;
2014-08-23 12:04:29 +08:00
import chanjarster.weixin.bean.result.WxError;
2014-08-25 11:01:09 +08:00
import chanjarster.weixin.bean.result.WxMassMaterialUploadResult;
import chanjarster.weixin.bean.result.WxMassSendResult;
import chanjarster.weixin.bean.result.WxMediaUploadResult;
2014-08-18 16:48:20 +08:00
import chanjarster.weixin.exception.WxErrorException;
2014-08-24 14:18:31 +08:00
import chanjarster.weixin.util.fs.FileUtil;
import chanjarster.weixin.util.http.MediaDownloadRequestExecutor;
import chanjarster.weixin.util.http.MediaUploadRequestExecutor;
import chanjarster.weixin.util.http.RequestExecutor;
import chanjarster.weixin.util.http.SimpleGetRequestExecutor;
import chanjarster.weixin.util.http.SimplePostRequestExecutor;
2014-08-18 16:48:20 +08:00
public class WxServiceImpl implements WxService {
/**
* 全局的是否正在刷新Access Token的flag
* true: 正在刷新
* false: 没有刷新
*/
protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false);
protected static final CloseableHttpClient httpclient = HttpClients.createDefault();
2014-08-22 16:08:30 +08:00
protected WxConfigStorage wxConfigStorage;
2014-08-18 16:48:20 +08:00
2014-08-22 14:52:11 +08:00
public boolean checkSignature(String timestamp, String nonce, String signature) {
try {
2014-08-22 16:08:30 +08:00
String token = wxConfigStorage.getToken();
2014-08-22 14:52:11 +08:00
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
String[] arr = new String[] { token, timestamp, nonce };
Arrays.sort(arr);
StringBuilder sb = new StringBuilder();
for(String a : arr) {
sb.append(a);
}
sha1.update(sb.toString().getBytes());
byte[] output = sha1.digest();
return bytesToHex(output).equals(signature);
} catch (Exception e) {
return false;
}
}
2014-08-24 14:18:31 +08:00
protected String bytesToHex(byte[] b) {
2014-08-22 14:52:11 +08:00
char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
StringBuffer buf = new StringBuffer();
for (int j = 0; j < b.length; j++) {
buf.append(hexDigit[(b[j] >> 4) & 0x0f]);
buf.append(hexDigit[b[j] & 0x0f]);
}
return buf.toString();
}
2014-08-18 16:48:20 +08:00
public void refreshAccessToken() throws WxErrorException {
if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) {
try {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
2014-08-22 16:08:30 +08:00
+ "&appid=" + wxConfigStorage.getAppId()
+ "&secret=" + wxConfigStorage.getSecret()
2014-08-18 16:48:20 +08:00
;
try {
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpGet);
String resultContent = new BasicResponseHandler().handleResponse(response);
2014-08-21 22:13:13 +08:00
WxError error = WxError.fromJson(resultContent);
if (error.getErrcode() != 0) {
throw new WxErrorException(error);
}
2014-08-18 16:48:20 +08:00
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
2014-08-22 16:08:30 +08:00
wxConfigStorage.updateAccessToken(accessToken.getAccess_token(), accessToken.getExpires_in());
2014-08-18 16:48:20 +08:00
} catch (ClientProtocolException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
} finally {
GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false);
}
} else {
// 每隔100ms检查一下是否刷新完毕了
while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
// 刷新完毕了,就没他什么事儿了
}
}
2014-08-24 14:18:31 +08:00
public void sendCustomMessage(WxCustomMessage message) throws WxErrorException {
2014-08-18 16:48:20 +08:00
String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
2014-08-24 14:18:31 +08:00
execute(new SimplePostRequestExecutor(), url, message.toJson());
2014-08-18 16:48:20 +08:00
}
2014-08-24 14:18:31 +08:00
public void createMenu(WxMenu menu) throws WxErrorException {
2014-08-21 22:13:13 +08:00
String url = "https://api.weixin.qq.com/cgi-bin/menu/create";
2014-08-24 14:18:31 +08:00
execute(new SimplePostRequestExecutor(), url, menu.toJson());
2014-08-21 22:13:13 +08:00
}
2014-08-24 14:18:31 +08:00
public void deleteMenu() throws WxErrorException {
2014-08-21 22:13:13 +08:00
String url = "https://api.weixin.qq.com/cgi-bin/menu/delete";
2014-08-24 14:18:31 +08:00
execute(new SimpleGetRequestExecutor(), url, null);
2014-08-21 22:13:13 +08:00
}
public WxMenu getMenu() throws WxErrorException {
String url = "https://api.weixin.qq.com/cgi-bin/menu/get";
try {
2014-08-24 14:18:31 +08:00
String resultContent = execute(new SimpleGetRequestExecutor(), url, null);
2014-08-21 22:13:13 +08:00
return WxMenu.fromJson(resultContent);
} catch (WxErrorException e) {
// 46003 不存在的菜单数据
if (e.getError().getErrcode() == 46003) {
return null;
}
throw e;
}
}
2014-08-25 11:01:09 +08:00
public WxMediaUploadResult uploadMedia(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException {
2014-08-24 14:18:31 +08:00
return uploadMedia(mediaType,FileUtil.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType));
2014-08-23 12:04:29 +08:00
}
2014-08-25 11:01:09 +08:00
public WxMediaUploadResult uploadMedia(String mediaType, File file) throws WxErrorException {
2014-08-23 12:04:29 +08:00
String url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType;
2014-08-24 14:18:31 +08:00
return execute(new MediaUploadRequestExecutor(), url, file);
2014-08-23 12:04:29 +08:00
}
2014-08-24 14:18:31 +08:00
public File downloadMedia(String media_id) throws WxErrorException {
String url = "http://file.api.weixin.qq.com/cgi-bin/media/get";
return execute(new MediaDownloadRequestExecutor(), url, "media_id=" + media_id);
2014-08-18 16:48:20 +08:00
}
2014-08-25 11:01:09 +08:00
public WxMassMaterialUploadResult uploadMassNews(WxMassNews news) throws WxErrorException {
String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews";
String responseContent = execute(new SimplePostRequestExecutor(), url, news.toJson());
return WxMassMaterialUploadResult.fromJson(responseContent);
}
public WxMassMaterialUploadResult uploadMassVideo(WxMassVideo video) throws WxErrorException {
String url = " https://file.api.weixin.qq.com/cgi-bin/media/uploadvideo";
String responseContent = execute(new SimplePostRequestExecutor(), url, video.toJson());
return WxMassMaterialUploadResult.fromJson(responseContent);
}
public WxMassSendResult sendMassMessage(WxMassMessage message) throws WxErrorException {
String url = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall";
String responseContent = execute(new SimplePostRequestExecutor(), url, message.toJson());
return WxMassSendResult.fromJson(responseContent);
}
2014-08-18 16:48:20 +08:00
/**
* 向微信端发送请求在这里执行的策略是当发生access_token过期时才去刷新然后重新执行请求而不是全局定时请求
2014-08-24 14:18:31 +08:00
* @param executor
* @param uri
* @param data
* @return
* @throws WxErrorException
2014-08-18 16:48:20 +08:00
*/
2014-08-24 14:18:31 +08:00
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
2014-08-22 16:08:30 +08:00
if (StringUtils.isBlank(wxConfigStorage.getAccessToken())) {
2014-08-18 16:48:20 +08:00
refreshAccessToken();
}
2014-08-22 16:08:30 +08:00
String accessToken = wxConfigStorage.getAccessToken();
2014-08-18 16:48:20 +08:00
String uriWithAccessToken = uri;
uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken;
try {
2014-08-24 14:18:31 +08:00
return executor.execute(uriWithAccessToken, data);
} catch (WxErrorException e) {
WxError error = e.getError();
2014-08-18 16:48:20 +08:00
/*
2014-08-21 22:13:13 +08:00
* 发生以下情况时尝试刷新access_token
* 40001 获取access_token时AppSecret错误或者access_token无效
2014-08-18 16:48:20 +08:00
* 42001 access_token超时
*/
if (error.getErrcode() == 42001 || error.getErrcode() == 40001) {
refreshAccessToken();
2014-08-24 14:18:31 +08:00
return execute(executor, uri, data);
}
/**
* -1 系统繁忙, 500ms后重试
*/
if (error.getErrcode() == -1) {
try {
System.out.println("微信系统繁忙500ms后重试");
Thread.sleep(500);
return execute(executor, uri, data);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
2014-08-18 16:48:20 +08:00
}
if (error.getErrcode() != 0) {
throw new WxErrorException(error);
}
2014-08-24 14:18:31 +08:00
return null;
2014-08-18 16:48:20 +08:00
} catch (ClientProtocolException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
2014-08-22 16:08:30 +08:00
public void setWxConfigStorage(WxConfigStorage wxConfigProvider) {
this.wxConfigStorage = wxConfigProvider;
2014-08-18 16:48:20 +08:00
}
}