From 150b2ae23fbed97795e0099381c3666c8a39d89d Mon Sep 17 00:00:00 2001 From: "jy.hu" Date: Tue, 30 Sep 2014 21:11:32 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=83=A8=E5=88=86=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 国庆提交,一个大部分接口都完整的版本. --- .gitignore | 1 + pom.xml | 50 +- .../com/foxinmy/weixin4j/WeixinExecutor.java | 540 +++++++ .../com/foxinmy/weixin4j/WeixinProxy.java | 1295 +++++++---------- .../com/foxinmy/weixin4j/api/BaseApi.java | 15 + .../com/foxinmy/weixin4j/api/GroupApi.java | 142 ++ .../com/foxinmy/weixin4j/api/HelperApi.java | 45 + .../com/foxinmy/weixin4j/api/MassApi.java | 270 ++++ .../com/foxinmy/weixin4j/api/MediaApi.java | 174 +++ .../com/foxinmy/weixin4j/api/MenuApi.java | 89 ++ .../com/foxinmy/weixin4j/api/NotifyApi.java | 166 +++ .../java/com/foxinmy/weixin4j/api/QrApi.java | 115 ++ .../com/foxinmy/weixin4j/api/TemplApi.java | 49 + .../com/foxinmy/weixin4j/api/UserApi.java | 182 +++ .../weixin4j/api/token/FileTokenApi.java | 95 ++ .../weixin4j/api/token/RedisTokenApi.java | 82 ++ .../foxinmy/weixin4j/api/token/TokenApi.java | 19 + .../com/foxinmy/weixin4j/config.properties | 18 +- .../foxinmy/weixin4j/http/HttpRequest.java | 184 ++- .../com/foxinmy/weixin4j/http/Parameter.java | 17 +- .../foxinmy/weixin4j/http/PartParameter.java | 35 + .../com/foxinmy/weixin4j/http/Response.java | 34 +- .../foxinmy/weixin4j/model/AuthResult.java | 13 +- .../com/foxinmy/weixin4j/model/Button.java | 40 +- .../foxinmy/weixin4j/model/CustomRecord.java | 1 - .../com/foxinmy/weixin4j/model/MpArticle.java | 28 +- .../foxinmy/weixin4j/model/QRParameter.java | 68 +- .../com/foxinmy/weixin4j/model/Token.java | 35 +- .../java/com/foxinmy/weixin4j/model/User.java | 35 +- .../com/foxinmy/weixin4j/model/UserToken.java | 23 +- .../com/foxinmy/weixin4j/msg/BaseMessage.java | 54 +- .../com/foxinmy/weixin4j/msg/BaseResult.java | 78 + .../foxinmy/weixin4j/msg/ErrorMessage.java | 54 - .../com/foxinmy/weixin4j/msg/TextMessage.java | 12 +- .../msg/event/LocationEventMessage.java | 24 +- .../weixin4j/msg/event/MassEventMessage.java | 26 +- .../weixin4j/msg/event/ScanEventMessage.java | 21 +- .../msg/event/ScribeEventMessage.java | 2 - .../event/TemplatesendjobfinishMessage.java | 35 + .../event/{ => menu}/MenuEventMessage.java | 18 +- .../event/menu/MenuLocationEventMessage.java | 74 + .../msg/event/menu/MenuPhotoEventMessage.java | 63 + .../msg/event/menu/MenuScanEventMessage.java | 51 + .../foxinmy/weixin4j/msg/in/ImageMessage.java | 16 +- .../foxinmy/weixin4j/msg/in/LinkMessage.java | 20 +- .../weixin4j/msg/in/LocationMessage.java | 41 +- .../foxinmy/weixin4j/msg/in/VideoMessage.java | 14 +- .../foxinmy/weixin4j/msg/in/VoiceMessage.java | 24 +- .../foxinmy/weixin4j/msg/model/Article.java | 75 + .../foxinmy/weixin4j/msg/model/BaseMsg.java | 33 + .../com/foxinmy/weixin4j/msg/model/Image.java | 38 + .../com/foxinmy/weixin4j/msg/model/Music.java | 93 ++ .../com/foxinmy/weixin4j/msg/model/Text.java | 40 + .../com/foxinmy/weixin4j/msg/model/Video.java | 88 ++ .../com/foxinmy/weixin4j/msg/model/Voice.java | 38 + .../weixin4j/msg/notify/ArticleNotify.java | 77 +- .../weixin4j/msg/notify/ImageNotify.java | 32 +- .../weixin4j/msg/notify/MusicNotify.java | 47 +- .../weixin4j/msg/notify/TextNotify.java | 30 +- .../weixin4j/msg/notify/VideoNotify.java | 40 +- .../weixin4j/msg/notify/VoiceNotify.java | 32 +- .../weixin4j/msg/out/ArticleMessage.java | 88 +- .../weixin4j/msg/out/ImageMessage.java | 30 +- .../weixin4j/msg/out/MusicMessage.java | 61 +- .../weixin4j/msg/out/TemplateMessage.java | 117 ++ .../weixin4j/msg/out/TransferMessage.java | 25 + .../weixin4j/msg/out/VideoMessage.java | 50 +- .../weixin4j/msg/out/VoiceMessage.java | 47 +- .../com/foxinmy/weixin4j/type/ButtonType.java | 55 + .../com/foxinmy/weixin4j/type/EventType.java | 31 +- .../com/foxinmy/weixin4j/type/MediaType.java | 12 +- .../foxinmy/weixin4j/type/MessageType.java | 9 +- .../{WeixinConfig.java => ConfigUtil.java} | 5 +- .../com/foxinmy/weixin4j/util/IOUtil.java | 67 + .../foxinmy/weixin4j/util/MessageUtil.java | 135 ++ .../com/foxinmy/weixin4j/util/WeixinUtil.java | 8 - .../com/foxinmy/weixin4j/test/ButtonTest.java | 4 +- .../com/foxinmy/weixin4j/test/GroupTest.java | 2 +- .../com/foxinmy/weixin4j/test/HttpTest.java | 22 + .../com/foxinmy/weixin4j/test/MediaTest.java | 4 +- .../weixin4j/test/MessageEventInTest.java | 194 +++ .../foxinmy/weixin4j/test/MessageInTest.java | 137 +- .../weixin4j/test/MessageMassTest.java | 114 ++ .../weixin4j/test/MessageNotifyTest.java | 30 +- .../foxinmy/weixin4j/test/MessageOutTest.java | 46 +- .../weixin4j/test/MessageTemplTest.java | 30 + .../com/foxinmy/weixin4j/test/MpNewsTest.java | 67 - .../com/foxinmy/weixin4j/test/QRTest.java | 8 +- .../com/foxinmy/weixin4j/test/TokenTest.java | 9 +- 89 files changed, 4788 insertions(+), 1769 deletions(-) create mode 100644 src/main/java/com/foxinmy/weixin4j/WeixinExecutor.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/BaseApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/GroupApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/HelperApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/MassApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/MediaApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/MenuApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/NotifyApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/QrApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/TemplApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/UserApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/token/FileTokenApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/token/RedisTokenApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/api/token/TokenApi.java create mode 100644 src/main/java/com/foxinmy/weixin4j/http/PartParameter.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/BaseResult.java delete mode 100644 src/main/java/com/foxinmy/weixin4j/msg/ErrorMessage.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/event/TemplatesendjobfinishMessage.java rename src/main/java/com/foxinmy/weixin4j/msg/event/{ => menu}/MenuEventMessage.java (64%) create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/event/menu/MenuLocationEventMessage.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/event/menu/MenuPhotoEventMessage.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/event/menu/MenuScanEventMessage.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/model/Article.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/model/BaseMsg.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/model/Image.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/model/Music.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/model/Text.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/model/Video.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/model/Voice.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/out/TemplateMessage.java create mode 100644 src/main/java/com/foxinmy/weixin4j/msg/out/TransferMessage.java create mode 100644 src/main/java/com/foxinmy/weixin4j/type/ButtonType.java rename src/main/java/com/foxinmy/weixin4j/util/{WeixinConfig.java => ConfigUtil.java} (91%) create mode 100644 src/main/java/com/foxinmy/weixin4j/util/IOUtil.java create mode 100644 src/main/java/com/foxinmy/weixin4j/util/MessageUtil.java delete mode 100644 src/main/java/com/foxinmy/weixin4j/util/WeixinUtil.java create mode 100644 src/test/java/com/foxinmy/weixin4j/test/HttpTest.java create mode 100644 src/test/java/com/foxinmy/weixin4j/test/MessageEventInTest.java create mode 100644 src/test/java/com/foxinmy/weixin4j/test/MessageMassTest.java create mode 100644 src/test/java/com/foxinmy/weixin4j/test/MessageTemplTest.java delete mode 100644 src/test/java/com/foxinmy/weixin4j/test/MpNewsTest.java diff --git a/.gitignore b/.gitignore index 4898b587..9208afde 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ target/* *.log *.tmp Thumbs.db +/target/ diff --git a/pom.xml b/pom.xml index b17171c6..79c6bbea 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,8 @@ 4.0.0 com.foxinmy.weixin 0.0.1-SNAPSHOT - weixin4j-sdk - weixin4j-sdk + weixin4j + weixin4j @@ -32,7 +32,7 @@ src/main/resources - logback.xml + *.* @@ -43,7 +43,7 @@ - weixin4j-sdk + weixin4j @@ -58,14 +58,9 @@ ${xstream.version} - commons-codec - commons-codec - ${commons.codec.version} - - - commons-httpclient - commons-httpclient - ${commons.httpclient.version} + org.apache.httpcomponents + httpclient + ${httpclient.version} commons-logging @@ -77,6 +72,16 @@ + + org.apache.httpcomponents + httpmime + ${httpclient.version} + + + commons-codec + commons-codec + ${commons.codec.version} + com.alibaba fastjson @@ -125,7 +130,23 @@ + + org.jsoup + jsoup + ${jsoup.version} + + + jaxen + jaxen + ${jaxen.version} + + + redis.clients + jedis + ${jedis.version} + + 4.8.2 1.6.1 @@ -133,9 +154,12 @@ 1.7.6 1.7.6 1.4.7 - 3.1 + 4.2.5 1.9 1.1.9 + 1.7.3 + 1.1.6 + 2.6.0 UTF-8 diff --git a/src/main/java/com/foxinmy/weixin4j/WeixinExecutor.java b/src/main/java/com/foxinmy/weixin4j/WeixinExecutor.java new file mode 100644 index 00000000..9fd61b87 --- /dev/null +++ b/src/main/java/com/foxinmy/weixin4j/WeixinExecutor.java @@ -0,0 +1,540 @@ +package com.foxinmy.weixin4j; + +import java.io.Serializable; +import java.net.URI; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.StatusLine; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.params.ClientPNames; +import org.apache.http.client.params.CookiePolicy; +import org.apache.http.cookie.Cookie; +import org.apache.http.impl.client.AbstractHttpClient; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicHeader; +import org.apache.http.message.BasicNameValuePair; +import org.jsoup.Jsoup; +import org.jsoup.helper.StringUtil; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.foxinmy.weixin4j.util.IOUtil; + +/** + * 用于微信公众号绑定(模拟登录|启用开发者模式|修改服务器配置|修改回调地址|创建自定义菜单) + * + * @className WeixinBind + * @author jy + * @date 2014年8月15日 + * @since JDK 1.7 + * @see + */ +public class WeixinExecutor implements Serializable { + + private static final long serialVersionUID = 4253859892138066462L; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final static Charset charset = Charset.forName("utf-8"); + private final static Map accountMap = new HashMap() { + private static final long serialVersionUID = 1L; + + { + put("名称", "name"); + put("头像", "avatar"); + put("登录邮箱", "loginEmail"); + put("原始ID", "originalId"); + put("微信号", "weixinNo"); + put("类型", "accountType"); + put("认证情况", "weixinVerify"); + put("介绍", "introduce"); + put("所在地址", "address"); + put("二维码", "qrcodeUrl"); + } + }; + + private AbstractHttpClient client; + private HttpHost host; + private JSONObject weixin; + + // 服务器响应地址 + private String pushurl; + // oauth授权回调地址 + private String backurl; + // 服务器校验token + private String token; + + // 公众号用户名 + private String uname; + // 公众号密码 + private String pwd; + // 登录时验证码(如果有) + private String imgcode; + // 当要求输入验证码时,cookie需带上 + private String sig; + + public WeixinExecutor(String backurl, String pushurl, String token, String uname, String pwd, String imgcode, String sig) { + this.backurl = backurl; + this.pushurl = pushurl; + this.token = token; + + this.uname = uname; + this.pwd = pwd; + this.imgcode = StringUtil.isBlank(imgcode) ? "" : imgcode; + this.sig = sig; + + weixin = new JSONObject(); + weixin.put("host", "mp.weixin.qq.com"); + weixin.put("base", "https://mp.weixin.qq.com"); + weixin.put("auth", "https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN"); + weixin.put("call", "https://mp.weixin.qq.com/advanced/callbackprofile?t=ajax-response&token=%s&lang=zh_CN"); + weixin.put("start", "https://mp.weixin.qq.com/misc/skeyform?form=advancedswitchform"); + weixin.put("back", "https://mp.weixin.qq.com/merchant/myservice?action=set_oauth_domain&f=json"); + weixin.put("verifycode", "https://mp.weixin.qq.com/cgi-bin/verifycode?username=" + uname + "&r=%s"); + + List headers = new ArrayList(); + headers.add(new BasicHeader("Origin", weixin.getString("base"))); + headers.add(new BasicHeader("Connection", "keep-alive")); + headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36")); + + client = new DefaultHttpClient(); + client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY); + client.getParams().setBooleanParameter("http.protocol.single-cookie-header", true); + client.getParams().setParameter(ClientPNames.DEFAULT_HEADERS, headers); + + host = new HttpHost(weixin.getString("host"), -1, "https"); + } + + public JSONObject process() { + // 1.登陆微信公众号 + step1_login(); + int code = weixin.getIntValue("code"); + if (code == 0) { + // 2.收集相关信息 + step2_collect(); + code = weixin.getIntValue("code"); + // 3.填写服务器配置 + // 3-1.未初始化账号 + // 3-2.已配置账号 + if (code == 0) { + step3_setting(); + } + code = weixin.getIntValue("code"); + // 4.修改网页授权地址 + if (code == 0) { + step4_back(); + } + // 5.创建底部菜单 (调用封装好的API) + // 5-1.订阅号 + // 5-2.服务号 + // 6.完成 + } + return weixin; + } + + /** + * step1:登录操作 + */ + private void step1_login() { + HttpPost method = new HttpPost(weixin.getString("auth")); + try { + List parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("username", uname)); + parameters.add(new BasicNameValuePair("pwd", DigestUtils.md5Hex(pwd.getBytes()))); + parameters.add(new BasicNameValuePair("f", "json")); + parameters.add(new BasicNameValuePair("imgcode", imgcode)); + if (!StringUtil.isBlank(imgcode)) { + method.addHeader("Cookie", "sig=" + sig); + } + method.setEntity(new UrlEncodedFormEntity(parameters, charset)); + method.addHeader("Referer", weixin.getString("base")); + + HttpResponse response = client.execute(host, method); + HttpEntity entity = response.getEntity(); + Document root = Jsoup.parse(entity.getContent(), charset.name(), weixin.getString("base")); + StatusLine line = response.getStatusLine(); + logger.info("step1_login--->status={},body=\n{}", line, root.toString()); + if (line.getStatusCode() == HttpStatus.SC_OK) { + JSONObject body = JSON.parseObject(root.body().text()); + + String msg = ""; + int code = 0; + switch (body.getJSONObject("base_resp").getIntValue("ret")) { + case -1: + msg = "系统错误,请稍候再试。"; + code = -1; + break; + case -2: + msg = "帐号或密码错误。"; + code = 100; + break; + case -23: + msg = "您输入的帐号或者密码不正确,请重新输入。"; + code = 101; + break; + case -21: + msg = "不存在该帐户。"; + code = 102; + break; + case -7: + msg = "您目前处于访问受限状态。"; + code = 103; + break; + case -8: + msg = "请输入图中的验证码"; + code = 104; + break; + case -27: + msg = "您输入的验证码不正确,请重新输入"; + code = 105; + break; + case -26: + msg = "该公众会议号已经过期,无法再登录使用。"; + code = 106; + break; + case 0: + msg = "成功登录,正在跳转..."; + break; + case -25: + msg = "海外帐号请在公众平台海外版登录,点击登录"; + code = 107; + break; + default: + msg = "未知错误"; + code = 108; + break; + } + if (code == 0) { + weixin.put("urlToken", getQueryMap(body.getString("redirect_url")).get("token")); + weixin.put("indexUrl", String.format("%s%s", weixin.getString("base"), body.getString("redirect_url"))); + weixin.put("step", "1"); + } else { + if (code == 104 || code == 105) { + // 下载验证码 + HttpGet get = new HttpGet(String.format(weixin.getString("verifycode"), System.currentTimeMillis())); + get.setHeaders(method.getAllHeaders()); + response = client.execute(host, get); + StringBuffer base64 = new StringBuffer(); + base64.append("data:").append(response.getFirstHeader("Content-Type").getValue()).append(";base64,"); + base64.append(new String(Base64.encodeBase64(IOUtil.toByteArray(response.getEntity().getContent())), charset)); + weixin.put("verifydata", base64.toString()); + List cookieList = client.getCookieStore().getCookies(); + for (Cookie cookie : cookieList) { + if (cookie.getName().equals("sig")) { + weixin.put("sig", cookie.getValue()); + break; + } + } + } + weixin.put("code", code); + weixin.put("msg", msg); + } + } else { + weixin.put("code", "-3"); + weixin.put("msg", "网络异常,请稍后重试!"); + } + } catch (Exception e) { + weixin.put("code", "-2"); + weixin.put("msg", "服务器繁忙,请稍后重试!"); + weixin.put("exception", e.getMessage()); + logger.error("step1_login catch error", e); + } finally { + if (weixin.getIntValue("code") != 0) { + client.getConnectionManager().shutdown(); + } + } + } + + /** + * step2:收集信息 + */ + private void step2_collect() { + String url = weixin.getString("indexUrl"); + HttpGet method = new HttpGet(url); + try { + method.addHeader("Referer", weixin.getString("base")); + + HttpResponse response = client.execute(host, method); + HttpEntity entity = response.getEntity(); + Document root = Jsoup.parse(entity.getContent(), charset.name(), weixin.getString("base")); + StatusLine line = response.getStatusLine(); + logger.info("step2_setting--->status={},body=\n{}", line, root.toString()); + if (line.getStatusCode() == HttpStatus.SC_OK) { + Element ele = root.getElementById("menuBar").getElementsByTag("dl").last(); + url = ele.getElementsByTag("a").last().absUrl("href"); + + weixin.put("developerUrl", url); + + method.addHeader("Referer", url); + url = ele.previousElementSibling().getElementsByTag("a").first().absUrl("href"); + weixin.put("settingUrl", url); + method.setURI(URI.create(url)); + + response = client.execute(host, method); + entity = response.getEntity(); + root = Jsoup.parse(entity.getContent(), charset.name(), weixin.getString("base")); + line = response.getStatusLine(); + weixin.put("step", "2-1"); + // 公众号配置页面 + if (line.getStatusCode() == HttpStatus.SC_OK) { + Elements eles = root.getElementById("settingArea").getElementsByTag("li"); + String key, value; + for (Element element : eles) { + key = element.getElementsByTag("h4").first().text(); + ele = element.getElementsByClass("meta_content").first(); + if (ele.children().isEmpty()) { + value = ele.text(); + } else { + if (ele.child(0).tagName().equalsIgnoreCase("a")) { + value = ele.child(0).absUrl("href"); + } else if (ele.child(0).tagName().equalsIgnoreCase("img")) { + value = ele.child(0).absUrl("src"); + } else { + value = ele.text(); + } + } + weixin.put(accountMap.get(key), value); + } + weixin.put("isVerify", weixin.getString("weixinVerify").contains("微信认证")); + weixin.put("isService", weixin.getString("accountType").contains("服务号")); + weixin.put("isSubscribe", weixin.getString("accountType").contains("订阅号")); + value = weixin.getString("qrcodeUrl"); + + method.setURI(URI.create(value)); + response = client.execute(host, method); + weixin.put("qrcodeData", IOUtil.toByteArray(response.getEntity().getContent())); + + weixin.put("step", "2-2"); + // 开发者页面 + method.addHeader("Referer", url); + method.setURI(URI.create(weixin.getString("developerUrl"))); + + response = client.execute(host, method); + entity = response.getEntity(); + root = Jsoup.parse(entity.getContent(), charset.name(), weixin.getString("base")); + line = response.getStatusLine(); + + if (line.getStatusCode() == HttpStatus.SC_OK) { + // 初始化状态 + // 配置未启用状态 + // 配置已启用状态 + eles = root.getElementsByClass("developer_info_opr"); + if (eles != null && eles.hasText()) { + weixin.put("developerModifyUrl", eles.first().children().last().absUrl("href")); + weixin.put("status", eles.first().children().first().text().contains("启用") ? "READY" : "RUNNING"); + } else { + weixin.put("status", "INIT"); + } + + // appid&appsecret + if (weixin.getBooleanValue("isService") || (weixin.getBooleanValue("isSubscribe") && weixin.getBooleanValue("isVerify"))) { + eles = root.getElementsByClass("developer_info_item").first().children().last().getElementsByClass("frm_controls"); + weixin.put("appId", eles.first().text()); + weixin.put("appSecret", eles.last().text().replace("重置", "").trim()); + } + weixin.put("step", "2-3"); + } + } else { + weixin.put("code", "-3"); + weixin.put("msg", "网络异常,请稍后重试!"); + } + } else { + weixin.put("code", "-3"); + weixin.put("msg", "网络异常,请稍后重试!"); + } + } catch (Exception e) { + weixin.put("code", "-2"); + weixin.put("msg", "服务器繁忙,请稍后重试!"); + weixin.put("exception", e.getMessage()); + logger.error("step2_collect catch error", e); + } finally { + if (weixin.getIntValue("code") != 0) { + client.getConnectionManager().shutdown(); + } + } + } + + /** + * step3:填写配置 + */ + private void step3_setting() { + HttpPost method = new HttpPost(String.format(weixin.getString("call"), weixin.getString("urlToken"))); + try { + List parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("url", pushurl)); + parameters.add(new BasicNameValuePair("callback_token", token)); + method.setEntity(new UrlEncodedFormEntity(parameters, charset)); + method.addHeader("Referer", weixin.getString("developerModifyUrl")); + + HttpResponse response = client.execute(host, method); + HttpEntity entity = response.getEntity(); + Document root = Jsoup.parse(entity.getContent(), charset.name(), weixin.getString("base")); + StatusLine line = response.getStatusLine(); + logger.info("step3_setting--->status={},body=\n{}", line, root.toString()); + if (line.getStatusCode() == HttpStatus.SC_OK) { + JSONObject body = JSON.parseObject(root.body().text()); + String msg = ""; + int code = 0; + switch (body.getIntValue("ret")) { + case -201: + msg = "无效的URL"; + code = 200; + break; + case -202: + msg = "无效的Token"; + code = 201; + break; + case -203: + msg = "操作频率太快,请休息一下。"; + code = 202; + break; + case -204: + msg = "请先在设置页面完善当前帐号信息"; + code = 203; + break; + case -205: + msg = "该URL可能存在安全风险,请检查"; + code = 207; + case -301: + msg = "请求URL超时"; + code = 204; + break; + case -302: + msg = "服务器没有正确响应Token验证,请稍后重试"; + code = 205; + break; + case -104: + msg = "参数错误,请重新填写。"; + code = 206; + break; + case 0: + msg = "配置成功.."; + break; + default: + msg = "未知错误"; + code = 108; + break; + } + if (code == 0) { + // 触发启用按钮 + if (!weixin.getString("status").equals("RUNNING")) { + parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("token", weixin.getString("urlToken"))); + parameters.add(new BasicNameValuePair("f", "json")); + parameters.add(new BasicNameValuePair("ajax", "1")); + parameters.add(new BasicNameValuePair("flag", "1")); + parameters.add(new BasicNameValuePair("type", "2")); + parameters.add(new BasicNameValuePair("lang", "zh_CN")); + parameters.add(new BasicNameValuePair("random", System.currentTimeMillis() + "")); + + method.setEntity(new UrlEncodedFormEntity(parameters, charset)); + method.setURI(URI.create(weixin.getString("start"))); + + response = client.execute(host, method); + entity = response.getEntity(); + root = Jsoup.parse(entity.getContent(), charset.name(), weixin.getString("base")); + line = response.getStatusLine(); + logger.info("step3_setting--->status={},body=\n{}", line, root.toString()); + if (line.getStatusCode() == HttpStatus.SC_OK) { + body = JSON.parseObject(root.body().text()); + if (body.getIntValue("ret") != 0 || body.getJSONObject("base_resp").getIntValue("ret") != 0) { + weixin.put("code", 300); + weixin.put("msg", "启用开发者模式失败,请稍后再试!"); + } + } + } + weixin.put("step", "3"); + } else { + weixin.put("code", code); + weixin.put("msg", msg); + } + } else { + weixin.put("code", "-3"); + weixin.put("msg", "网络异常,请稍后重试!"); + } + } catch (Exception e) { + weixin.put("code", "-2"); + weixin.put("msg", "服务器繁忙,请稍后重试!"); + weixin.put("exception", e.getMessage()); + logger.error("step3_setting catch error", e); + } finally { + if (weixin.getIntValue("code") != 0) { + client.getConnectionManager().shutdown(); + } + } + } + + /** + * step4:修改回调 + */ + private void step4_back() { + try { + if (weixin.getBooleanValue("isVerify")) { + HttpPost method = new HttpPost(weixin.getString("back")); + + List parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("domain", backurl)); + parameters.add(new BasicNameValuePair("token", weixin.getString("urlToken"))); + parameters.add(new BasicNameValuePair("f", "json")); + parameters.add(new BasicNameValuePair("ajax", "1")); + parameters.add(new BasicNameValuePair("flag", "1")); + parameters.add(new BasicNameValuePair("lang", "zh_CN")); + parameters.add(new BasicNameValuePair("random", System.currentTimeMillis() + "")); + method.setEntity(new UrlEncodedFormEntity(parameters, charset)); + method.addHeader("Referer", weixin.getString("developerUrl")); + + HttpResponse response = client.execute(host, method); + HttpEntity entity = response.getEntity(); + Document root = Jsoup.parse(entity.getContent(), charset.name(), weixin.getString("base")); + StatusLine line = response.getStatusLine(); + logger.info("step4_back--->status={},body=\n{}", line, root.toString()); + if (line.getStatusCode() == HttpStatus.SC_OK) { + JSONObject body = JSON.parseObject(root.body().text()); + if (body.getIntValue("ret") != 0 || body.getJSONObject("base_resp").getIntValue("ret") != 0) { + weixin.put("code", "400"); + weixin.put("msg", "修改授权回调地址失败!"); + } + weixin.put("step", "4"); + } + } else { + logger.info("公众号尚未认证,放弃本次修改授权回调地址操作。{}", weixin); + } + } catch (Exception e) { + weixin.put("code", "-2"); + weixin.put("msg", "服务器繁忙,请稍后重试!"); + weixin.put("exception", e.getMessage()); + logger.error("step4_back catch error", e); + } finally { + client.getConnectionManager().shutdown(); + } + } + + private Map getQueryMap(String query) { + String[] params = query.split("&"); + Map map = new HashMap(); + for (String param : params) { + String name = param.split("=")[0]; + String value = param.split("=")[1]; + map.put(name, value); + } + return map; + } +} diff --git a/src/main/java/com/foxinmy/weixin4j/WeixinProxy.java b/src/main/java/com/foxinmy/weixin4j/WeixinProxy.java index 79b1db03..7333d0be 100644 --- a/src/main/java/com/foxinmy/weixin4j/WeixinProxy.java +++ b/src/main/java/com/foxinmy/weixin4j/WeixinProxy.java @@ -1,370 +1,80 @@ package com.foxinmy.weixin4j; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; import java.util.List; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.httpclient.methods.StringRequestEntity; -import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource; -import org.apache.commons.httpclient.methods.multipart.FilePart; -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.DocumentHelper; -import org.dom4j.io.SAXReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.foxinmy.weixin4j.api.GroupApi; +import com.foxinmy.weixin4j.api.MassApi; +import com.foxinmy.weixin4j.api.MediaApi; +import com.foxinmy.weixin4j.api.MenuApi; +import com.foxinmy.weixin4j.api.NotifyApi; +import com.foxinmy.weixin4j.api.QrApi; +import com.foxinmy.weixin4j.api.TemplApi; +import com.foxinmy.weixin4j.api.UserApi; +import com.foxinmy.weixin4j.api.token.FileTokenApi; +import com.foxinmy.weixin4j.api.token.TokenApi; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.HttpRequest; -import com.foxinmy.weixin4j.http.Response; import com.foxinmy.weixin4j.model.Button; import com.foxinmy.weixin4j.model.CustomRecord; import com.foxinmy.weixin4j.model.Following; import com.foxinmy.weixin4j.model.Group; import com.foxinmy.weixin4j.model.MpArticle; import com.foxinmy.weixin4j.model.QRParameter; -import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.model.User; import com.foxinmy.weixin4j.model.UserToken; -import com.foxinmy.weixin4j.msg.BaseMessage; +import com.foxinmy.weixin4j.msg.BaseResult; +import com.foxinmy.weixin4j.msg.model.Article; +import com.foxinmy.weixin4j.msg.model.BaseMsg; import com.foxinmy.weixin4j.msg.notify.BaseNotify; -import com.foxinmy.weixin4j.type.EventType; +import com.foxinmy.weixin4j.msg.out.TemplateMessage; import com.foxinmy.weixin4j.type.MediaType; -import com.foxinmy.weixin4j.type.MessageType; -import com.foxinmy.weixin4j.util.WeixinConfig; -import com.foxinmy.weixin4j.util.WeixinUtil; -import com.foxinmy.weixin4j.xml.XStream; /** * 微信服务实现 * - * @className WeixinServiceImpl + * @className WeixinProxy * @author jy.hu * @date 2014年3月23日 * @since JDK 1.7 * @see api文档 */ public class WeixinProxy { - - private final Logger log = LoggerFactory.getLogger(getClass()); - - private HttpRequest request = new HttpRequest(); + private final MediaApi mediaApi; + private final NotifyApi notifyApi; + private final MassApi massApi; + private final UserApi userApi; + private final GroupApi groupApi; + private final MenuApi menuApi; + private final QrApi qrApi; + private final TemplApi templApi; /** - * 验证微信签名 - * - * @param echostr - * 随机字符串 - * @param timestamp - * 时间戳 - * @param nonce - * 随机数 - * @param signature - * 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数 - * @return 开发者通过检验signature对请求进行相关校验。若确认此次GET请求来自微信服务器 - * 请原样返回echostr参数内容,则接入生效 成为开发者成功,否则接入失败 - * @see 接入指南 + * 默认采用文件存放Token跟配置文件中的appi信息 */ - public String signature(String echostr, String timestamp, String nonce, - String signature) { - String app_token = WeixinConfig.getValue("app_token"); - if (WeixinUtil.isBlank(app_token)) { - log.error("signature fail : token is null!"); - return null; - } - if (WeixinUtil.isBlank(echostr) || WeixinUtil.isBlank(timestamp) - || WeixinUtil.isBlank(nonce)) { - log.error("signature fail : invalid parameter!"); - return null; - } - String _signature = null; - try { - String[] a = {app_token, timestamp, nonce}; - Arrays.sort(a); - StringBuilder sb = new StringBuilder(3); - for (String str : a) { - sb.append(str); - } - _signature = DigestUtils.sha1Hex(sb.toString()); - } catch (Exception e) { - log.error("signature error", e); - } - if (signature.equals(_signature)) { - return echostr; - } else { - log.error("signature fail : invalid signature!"); - return null; - } + public WeixinProxy() { + this(new FileTokenApi()); } /** - * xml消息转换为消息对象 - *

- * 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次 - *

+ * 也可接受传递过来的appid跟appsecret * - * @param xml - * 消息字符串 - * @return 消息对象 - * @throws DocumentException - * @see 验证消息的合法性 - * @see 普通消息 - * @see 事件触发 - * @see com.foxinmy.weixin4j.type.MessageType - * @see com.feican.weixin.msg.BaeMessage - * @see com.foxinmy.weixin4j.msg.TextMessage - * @see com.foxinmy.weixin4j.msg.in.ImageMessage - * @see com.foxinmy.weixin4j.msg.in.VoiceMessage - * @see com.foxinmy.weixin4j.msg.in.VideoMessage - * @see com.foxinmy.weixin4j.msg.in.LocationMessage - * @see com.foxinmy.weixin4j.msg.in.LinkMessage - * @see com.foxinmy.weixin4j.msg.event.ScribeEventMessage - * @see com.foxinmy.weixin4j.msg.event.ScanEventMessage - * @see com.foxinmy.weixin4j.msg.event.LocationEventMessage - * @see com.foxinmy.weixin4j.msg.event.MenuEventMessage + * @param appid + * @param appsecret */ - public BaseMessage xml2msg(String xmlStr) throws DocumentException { - if (WeixinUtil.isBlank(xmlStr)) - return null; - Document doc = DocumentHelper.parseText(xmlStr); - String type = doc.selectSingleNode("/xml/MsgType").getStringValue(); - if (WeixinUtil.isBlank(type)) { - return null; - } - XStream xstream = new XStream(); - MessageType messageType = MessageType.valueOf(type.toLowerCase()); - Class messageClass = messageType - .getMessageClass(); - if (messageType == MessageType.event) { - type = doc.selectSingleNode("/xml/Event").getStringValue(); - messageClass = EventType.valueOf(type.toLowerCase()) - .getEventClass(); - } - xstream.alias("xml", messageClass); - xstream.ignoreUnknownElements(); - xstream.autodetectAnnotations(true); - xstream.processAnnotations(messageClass); - return xstream.fromXML(doc.asXML(), messageClass); + public WeixinProxy(String appid, String appsecret) { + this(new FileTokenApi(appid, appsecret)); } - /** - * xml消息转换为消息对象 - * - * @param inputStream - * 带消息字符串的输入流 - * @return 消息对象 - * @throws DocumentException - * @see {@link com.foxinmy.weixin4j.WeixinProxy#xml2msg(String)} - */ - public BaseMessage xml2msg(InputStream inputStream) - throws DocumentException { - SAXReader reader = new SAXReader(); - Document doc = reader.read(inputStream); - return xml2msg(doc.asXML()); - } - - /** - * 获取token - *

- * 正常情况下返回{"access_token":"ACCESS_TOKEN","expires_in":7200},否则抛出异常. - *

- * - * @return token对象 - * @throws WeixinException - * @see 获取token说明 - * @see com.foxinmy.weixin4j.model.Token - */ - public Token getToken() throws WeixinException { - XStream xstream = new XStream(); - xstream.autodetectAnnotations(true); - xstream.processAnnotations(Token.class); - String appOpenId = WeixinConfig.getValue("app_openId"); - String token_path = WeixinConfig.getValue("token_path"); - File token_file = new File(String.format("%s/%s_token.xml", token_path, - appOpenId)); - Token token = null; - Calendar ca = Calendar.getInstance(); - long now_time = ca.getTimeInMillis(); - try { - token_path = WeixinConfig.getValue("api_token_uri"); - Response response = request.get(token_path); - if (token_file.exists()) { - token = (Token) xstream.fromXML(token_file); - - long expise_time = token.getTime() - + (token.getExpires_in() * 1000) - 5; - if (expise_time > now_time) { - return token; - } - response = request.get(token_path); - } else { - response = request.get(token_path); - try { - token_file.createNewFile(); - } catch (IOException e) { - token_file.getParentFile().mkdirs(); - } - } - token = response.getAsObject(Token.class); - token.setTime(now_time); - token.setOpenid(appOpenId); - xstream.toXML(token, new FileOutputStream(token_file)); - } catch (IOException e) { - log.error("获取token出错", e); - } - return token; - } - - /** - * 获取token - * - * @param code - * 用户授权后返回的code - * @return token对象 - * @throws WeixinException - * @see 获取用户token - * @see com.foxinmy.weixin4j.model.UserToken - */ - public UserToken getAccessToken(String code) throws WeixinException { - String user_token_uri = WeixinConfig.getValue("sns_user_token_uri"); - Response response = request.get(String.format(user_token_uri, code)); - // 暂时不保存用户token - return response.getAsObject(UserToken.class); - } - - /** - * 获取用户信息 - * - * @param token - * 授权票据 - * @return 用户对象 - * @throws WeixinException - * @see 拉取用户信息 - * @see com.foxinmy.weixin4j.model.User - * @see com.foxinmy.weixin4j.model.UserToken - * {@link com.foxinmy.weixin4j.WeixinProxy#getAccessToken(String)} - */ - public User getUser(UserToken token) throws WeixinException { - String user_info_uri = WeixinConfig.getValue("sns_user_info_uri"); - Response response = request.get(String.format(user_info_uri, - token.getAccess_token(), token.getOpenid())); - return response.getAsObject(User.class); - } - - /** - * 获取用户信息 - *

- * 在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的,对于不同公众号, - * 同一用户的openid不同),公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间 - *

- * - * @param openId - * 用户对应的ID - * @return 用户对象 - * @throws WeixinException - * @see 获取用户信息 - * @see com.foxinmy.weixin4j.model.User - */ - public User getUser(String openId) throws WeixinException { - String user_info_uri = WeixinConfig.getValue("api_user_info_uri"); - Token token = getToken(); - Response response = request.get(String.format(user_info_uri, - token.getAccess_token(), openId)); - return response.getAsObject(User.class); - } - - /** - * 生成带参数的二维码 - * - * @param parameter - * @return byte数据包 - * @throws WeixinException - * @see {@link com.foxinmy.weixin4j.WeixinProxy#getQR(QRParameter)} - */ - public byte[] getQRData(QRParameter parameter) throws WeixinException { - Token token = getToken(); - String qr_uri = WeixinConfig.getValue("qr_ticket_uri"); - Response response = null; - try { - response = request.post(String.format(qr_uri, - token.getAccess_token()), - new StringRequestEntity(parameter.toJson(), - "application/json", "UTF-8")); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - String ticket = response.getAsJson().getString("ticket"); - qr_uri = WeixinConfig.getValue("qr_image_uri"); - response = request.get(String.format(qr_uri, ticket)); - - return response.getBody(); - } - - /** - * 生成带参数的二维码 - *

- * 二维码分为临时跟永久两种,扫描时触发推送带参数事件 - *

- * - * @param parameter - * 二维码参数 - * @return 硬盘存储的文件对象 - * @throws WeixinException - * @see 二维码 - * @see com.foxinmy.weixin4j.model.QRParameter - */ - public File getQR(QRParameter parameter) throws WeixinException { - String qr_path = WeixinConfig.getValue("qr_path"); - String filename = String - .format("%s_%d_%d.jpg", parameter.getQrType().name(), - parameter.getScene_id(), parameter.getExpire_seconds()); - File file = new File(qr_path + File.separator + filename); - if (file.exists()) { - return file; - } - FileOutputStream out = null; - try { - try { - file.createNewFile(); - } catch (IOException e) { - file.getParentFile().mkdirs(); - file.createNewFile(); - } - out = new FileOutputStream(file); - byte[] b = getQRData(parameter); - out.write(b); - } catch (IOException e) { - - } finally { - try { - if (out != null) { - out.close(); - } - } catch (IOException e) { - - } - } - return file; + public WeixinProxy(TokenApi tokenApi) { + this.mediaApi = new MediaApi(tokenApi); + this.notifyApi = new NotifyApi(tokenApi); + this.massApi = new MassApi(tokenApi); + this.userApi = new UserApi(tokenApi); + this.groupApi = new GroupApi(tokenApi); + this.menuApi = new MenuApi(tokenApi); + this.qrApi = new QrApi(tokenApi); + this.templApi = new TemplApi(tokenApi); } /** @@ -386,35 +96,7 @@ public class WeixinProxy { */ public String uploadMedia(File file, MediaType mediaType) throws WeixinException { - byte[] b = null; - ByteArrayOutputStream out = null; - FileInputStream in = null; - try { - b = new byte[4096]; - out = new ByteArrayOutputStream(); - in = new FileInputStream(file); - int len = -1; - while ((len = in.read(b)) != -1) { - out.write(b, 0, len); - } - b = out.toByteArray(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - } - } - if (in != null) { - try { - in.close(); - } catch (IOException e) { - } - } - } - return uploadMedia(file.getName(), b, mediaType); + return mediaApi.uploadMedia(file, mediaType); } /** @@ -428,18 +110,9 @@ public class WeixinProxy { * @throws WeixinException * @see {@link com.foxinmy.weixin4j.WeixinProxy#uploadMedia(File, MediaType)} */ - public String uploadMedia(String fileName, byte[] bytes, MediaType mediaType) + public String uploadMedia(String fileName, byte[] data, MediaType mediaType) throws WeixinException { - Token token = getToken(); - String file_upload_uri = WeixinConfig.getValue("file_upload_uri"); - Response response = null; - - response = request.post(String.format(file_upload_uri, - token.getAccess_token(), mediaType.name()), new FilePart( - "media", new ByteArrayPartSource(fileName, bytes), null, - "UTF-8")); - - return response.getAsJson().getString("media_id"); + return mediaApi.uploadMedia(fileName, data, mediaType); } /** @@ -460,35 +133,7 @@ public class WeixinProxy { */ public File downloadMedia(String mediaId, MediaType mediaType) throws WeixinException { - String media_path = WeixinConfig.getValue("media_path"); - String filename = mediaId + mediaType.getFormatType(); - File file = new File(media_path + File.separator + filename); - if (file.exists()) { - return file; - } - FileOutputStream out = null; - try { - try { - file.createNewFile(); - } catch (IOException e) { - file.getParentFile().mkdirs(); - file.createNewFile(); - } - out = new FileOutputStream(file); - byte[] b = downloadMediaData(mediaId, mediaType); - out.write(b); - } catch (IOException e) { - - } finally { - try { - if (out != null) { - out.close(); - } - } catch (IOException e) { - - } - } - return file; + return mediaApi.downloadMedia(mediaId, mediaType); } /** @@ -502,11 +147,7 @@ public class WeixinProxy { */ public byte[] downloadMediaData(String mediaId, MediaType mediaType) throws WeixinException { - Token token = getToken(); - String file_download_uri = WeixinConfig.getValue("file_download_uri"); - Response response = request.get(String.format(file_download_uri, - token.getAccess_token(), mediaId)); - return response.getBody(); + return mediaApi.downloadMediaData(mediaId, mediaType); } /** @@ -517,399 +158,52 @@ public class WeixinProxy { * @throws WeixinException * @see 发送客服消息 + * @see com.foxinmy.weixin4j.msg.notify.TextNotify + * @see com.foxinmy.weixin4j.msg.notify.ImageNotify + * @see com.foxinmy.weixin4j.msg.notify.MusicNotify + * @see com.foxinmy.weixin4j.msg.notify.VideoNotify + * @see com.foxinmy.weixin4j.msg.notify.VoiceNotify + * @see com.foxinmy.weixin4j.msg.notify.ArticleNotify */ - public void sendNotify(BaseNotify notify) throws WeixinException { - String custom_notify_uri = WeixinConfig.getValue("custom_notify_uri"); - Token token = getToken(); - try { - request.post(String.format(custom_notify_uri, - token.getAccess_token(), notify.getTouser()), - new StringRequestEntity(notify.toJson(), - "application/json", "UTF-8")); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } + public BaseResult sendNotify(BaseNotify notify) throws WeixinException { + return notifyApi.sendNotify(notify); } /** - * 创建分组 - * - * @param name - * 组名称 - * @return group对象 - * @throws WeixinException - * @see 创建分组 - * @see com.foxinmy.weixin4j.model.Group - * @see com.foxinmy.weixin4j.model.Group#toCreateJson() - */ - public Group createGroup(String name) throws WeixinException { - String group_create_uri = WeixinConfig.getValue("group_create_uri"); - Token token = getToken(); - Response response = null; - Group group = new Group(name); - try { - response = request.post(String.format(group_create_uri, - token.getAccess_token()), - new StringRequestEntity(group.toCreateJson(), - "application/json", "UTF-8")); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - return response.getAsJson().getObject("group", Group.class); - } - - /** - * 查询所有分组 - * - * @return 组集合 - * @throws WeixinException - * @see 查询所有分组 - * @see com.foxinmy.weixin4j.model.Group - */ - public List getGroups() throws WeixinException { - String group_get_uri = WeixinConfig.getValue("group_get_uri"); - Token token = getToken(); - Response response = request.get(String.format(group_get_uri, - token.getAccess_token())); - - return JSON.parseArray(response.getAsJson().getString("groups"), - Group.class); - } - - /** - * 查询用户所在分组 - * - * @param openId - * 用户对应的ID - * @return 组ID - * @throws WeixinException - * @see 查询用户所在分组 - * @see com.foxinmy.weixin4j.model.Group - */ - public int findGroupByOpenId(String openId) throws WeixinException { - String group_getid_uri = WeixinConfig.getValue("group_getid_uri"); - Token token = getToken(); - Response response = null; - try { - response = request.post( - String.format(group_getid_uri, token.getAccess_token()), - new StringRequestEntity(String.format( - "{\"openid\":\"%s\"}", openId), "application/json", - "UTF-8")); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - return response.getAsJson().getIntValue("groupid"); - } - - /** - * 修改分组名 - * - * @param groupId - * 组ID - * @param name - * 组名称 - * @throws WeixinException - * @see 修改分组名 - * @see com.foxinmy.weixin4j.model.Group - * @see com.foxinmy.weixin4j.model.Group#toModifyJson() - */ - public void modifyGroup(int groupId, String name) throws WeixinException { - String group_modify_uri = WeixinConfig.getValue("group_modify_uri"); - Token token = getToken(); - Group group = new Group(groupId, name); - try { - request.post(String.format(group_modify_uri, - token.getAccess_token()), - new StringRequestEntity(group.toModifyJson(), - "application/json", "UTF-8")); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - } - - /** - * 移动分组 - * - * @param openId - * 用户对应的ID - * @param groupId - * 组ID - * @throws WeixinException - * @see 移动分组 - * @see com.foxinmy.weixin4j.model.Group - */ - public void moveGroup(String openId, int groupId) throws WeixinException { - String group_move_uri = WeixinConfig.getValue("group_move_uri"); - Token token = getToken(); - try { - request.post( - String.format(group_move_uri, token.getAccess_token()), - new StringRequestEntity(String.format( - "{\"openid\":\"%s\",\"to_groupid\":%d}", openId, - groupId), "application/json", "UTF-8")); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - } - - /** - * 获取用户一定数量(10000)的关注者列表 - * - * @param nextOpenId - * 下一次拉取数据的openid - * @return 关注信息 - * @throws WeixinException - * @see 获取关注者列表 - * @see com.foxinmy.weixin4j.model.Following - */ - public Following getFollowing(String nextOpenId) throws WeixinException { - String fllowing_uri = WeixinConfig.getValue("following_uri"); - Token token = getToken(); - Response response = request.get(String.format(fllowing_uri, - token.getAccess_token(), nextOpenId == null ? "" : nextOpenId)); - - Following following = response.getAsObject(Following.class); - - if (following.getCount() > 0) { - List openIds = JSON.parseArray(following.getDataJson() - .getString("openid"), String.class); - List userList = new ArrayList(); - for (String openId : openIds) { - userList.add(getUser(openId)); - } - following.setUserList(userList); - } - return following; - } - - /** - * 获取用户全部的关注者列表 - *

- * 当公众号关注者数量超过10000时,可通过填写next_openid的值,从而多次拉取列表的方式来满足需求, - * 将上一次调用得到的返回中的next_openid值,作为下一次调用中的next_openid值 - *

- * - * @return 用户对象集合 - * @throws WeixinException - * @see 获取关注者列表 - * @see com.foxinmy.weixin4j.model.Following - * @see com.foxinmy.weixin4j.WeixinProxy#getFollowing(String) - */ - public List getAllFollowing() throws WeixinException { - List userList = new ArrayList(); - String nextOpenId = null; - Following f = null; - for (;;) { - f = getFollowing(nextOpenId); - if (f.getCount() == 0) { - break; - } - userList.addAll(f.getUserList()); - nextOpenId = f.getNextOpenId(); - } - return userList; - } - - /** - * 自定义菜单 - * - * @param btnList - * @throws WeixinException - * @see 创建自定义菜单 - * @see com.foxinmy.weixin4j.model.Button - */ - public void createMenu(List