| @@ -19,7 +19,7 @@ weixin-java-tools | |||||
| <dependency> | <dependency> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
| <version>1.0.5</version> | |||||
| <version>1.0.6</version> | |||||
| </dependency> | </dependency> | ||||
| ``` | ``` | ||||
| @@ -29,7 +29,7 @@ weixin-java-tools | |||||
| <dependency> | <dependency> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
| <version>1.0.5</version> | |||||
| <version>1.0.6</version> | |||||
| </dependency> | </dependency> | ||||
| ``` | ``` | ||||
| @@ -5,7 +5,7 @@ | |||||
| <modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.0.5</version> | |||||
| <version>1.0.6</version> | |||||
| <packaging>pom</packaging> | <packaging>pom</packaging> | ||||
| <name>WeiXin Java Tools - Parent</name> | <name>WeiXin Java Tools - Parent</name> | ||||
| <description>微信公众号、企业号上级POM</description> | <description>微信公众号、企业号上级POM</description> | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.0.5</version> | |||||
| <version>1.0.6</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.0.5</version> | |||||
| <version>1.0.6</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
| @@ -77,6 +77,9 @@ public class WxCpMessageRouter { | |||||
| for (final Rule rule : rules) { | for (final Rule rule : rules) { | ||||
| if (rule.test(wxMessage)) { | if (rule.test(wxMessage)) { | ||||
| matchRules.add(rule); | matchRules.add(rule); | ||||
| if(!rule.reEnter) { | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -84,28 +87,17 @@ public class WxCpMessageRouter { | |||||
| return null; | return null; | ||||
| } | } | ||||
| if (matchRules.get(0).async) { | |||||
| // 只要第一个是异步的,那就异步执行 | |||||
| // 在另一个线程里执行 | |||||
| executorService.submit(new Runnable() { | |||||
| public void run() { | |||||
| for (final Rule rule : matchRules) { | |||||
| rule.service(wxMessage); | |||||
| if (!rule.reEnter) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| }); | |||||
| return null; | |||||
| } | |||||
| WxCpXmlOutMessage res = null; | WxCpXmlOutMessage res = null; | ||||
| for (final Rule rule : matchRules) { | for (final Rule rule : matchRules) { | ||||
| // 返回最后一个匹配规则的结果 | |||||
| res = rule.service(wxMessage); | |||||
| if (!rule.reEnter) { | |||||
| break; | |||||
| // 返回最后一个非异步的rule的执行结果 | |||||
| if(rule.async) { | |||||
| executorService.submit(new Runnable() { | |||||
| public void run() { | |||||
| rule.service(wxMessage); | |||||
| } | |||||
| }); | |||||
| } else { | |||||
| res = rule.service(wxMessage); | |||||
| } | } | ||||
| } | } | ||||
| return res; | return res; | ||||
| @@ -335,7 +335,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?"; | String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?"; | ||||
| url += "access_token=" + wxCpConfigStorage.getAccessToken(); | url += "access_token=" + wxCpConfigStorage.getAccessToken(); | ||||
| url += "&code=" + code; | url += "&code=" + code; | ||||
| url += "agendid=" + wxCpConfigStorage.getAgentId(); | |||||
| url += "&agendid=" + wxCpConfigStorage.getAgentId(); | |||||
| try { | try { | ||||
| RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | RequestExecutor<String, String> executor = new SimpleGetRequestExecutor(); | ||||
| @@ -6,7 +6,7 @@ | |||||
| <parent> | <parent> | ||||
| <groupId>me.chanjar</groupId> | <groupId>me.chanjar</groupId> | ||||
| <artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
| <version>1.0.5</version> | |||||
| <version>1.0.6</version> | |||||
| </parent> | </parent> | ||||
| <artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
| <name>WeiXin Java Tools - MP</name> | <name>WeiXin Java Tools - MP</name> | ||||
| @@ -77,6 +77,9 @@ public class WxMpMessageRouter { | |||||
| for (final Rule rule : rules) { | for (final Rule rule : rules) { | ||||
| if (rule.test(wxMessage)) { | if (rule.test(wxMessage)) { | ||||
| matchRules.add(rule); | matchRules.add(rule); | ||||
| if(!rule.reEnter) { | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -84,28 +87,17 @@ public class WxMpMessageRouter { | |||||
| return null; | return null; | ||||
| } | } | ||||
| if (matchRules.get(0).async) { | |||||
| // 只要第一个是异步的,那就异步执行 | |||||
| // 在另一个线程里执行 | |||||
| executorService.execute(new Runnable() { | |||||
| public void run() { | |||||
| for (final Rule rule : matchRules) { | |||||
| rule.service(wxMessage); | |||||
| if (!rule.reEnter) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| }); | |||||
| return null; | |||||
| } | |||||
| WxMpXmlOutMessage res = null; | WxMpXmlOutMessage res = null; | ||||
| for (final Rule rule : matchRules) { | for (final Rule rule : matchRules) { | ||||
| // 返回最后一个匹配规则的结果 | |||||
| res = rule.service(wxMessage); | |||||
| if (!rule.reEnter) { | |||||
| break; | |||||
| // 返回最后一个非异步的rule的执行结果 | |||||
| if(rule.async) { | |||||
| executorService.submit(new Runnable() { | |||||
| public void run() { | |||||
| rule.service(wxMessage); | |||||
| } | |||||
| }); | |||||
| } else { | |||||
| res = rule.service(wxMessage); | |||||
| } | } | ||||
| } | } | ||||
| return res; | return res; | ||||
| @@ -315,8 +315,9 @@ public interface WxMpService { | |||||
| * </pre> | * </pre> | ||||
| * @param templateMessage | * @param templateMessage | ||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| * @return msgid | |||||
| */ | */ | ||||
| public void templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException; | |||||
| public String templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -286,9 +286,11 @@ public class WxMpServiceImpl implements WxMpService { | |||||
| return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); | return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); | ||||
| } | } | ||||
| public void templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException { | |||||
| public String templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException { | |||||
| String url = "https://api.weixin.qq.com/cgi-bin/message/template/send"; | String url = "https://api.weixin.qq.com/cgi-bin/message/template/send"; | ||||
| execute(new SimplePostRequestExecutor(), url, templateMessage.toJson()); | |||||
| String responseContent = execute(new SimplePostRequestExecutor(), url, templateMessage.toJson()); | |||||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||||
| return tmpJsonElement.getAsJsonObject().get("msgid").getAsString(); | |||||
| } | } | ||||
| public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { | public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { | ||||
| @@ -9,7 +9,7 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
| */ | */ | ||||
| public class WxMpMassGroupMessage { | public class WxMpMassGroupMessage { | ||||
| private long groupId; | |||||
| private Long groupId; | |||||
| private String msgtype; | private String msgtype; | ||||
| private String content; | private String content; | ||||
| private String mediaId; | private String mediaId; | ||||
| @@ -58,11 +58,15 @@ public class WxMpMassGroupMessage { | |||||
| return WxMpGsonBuilder.INSTANCE.create().toJson(this); | return WxMpGsonBuilder.INSTANCE.create().toJson(this); | ||||
| } | } | ||||
| public long getGroupId() { | |||||
| public Long getGroupId() { | |||||
| return groupId; | return groupId; | ||||
| } | } | ||||
| public void setGroupId(long groupId) { | |||||
| /** | |||||
| * 如果不设置则就意味着发给所有用户 | |||||
| * @param groupId | |||||
| */ | |||||
| public void setGroupId(Long groupId) { | |||||
| this.groupId = groupId; | this.groupId = groupId; | ||||
| } | } | ||||
| @@ -381,6 +381,8 @@ public class WxMpXmlMessage { | |||||
| public static WxMpXmlMessage fromXml(String xml) { | public static WxMpXmlMessage fromXml(String xml) { | ||||
| try { | try { | ||||
| // 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId | |||||
| xml = xml.replaceAll("<MsgID>", "<MsgId>").replaceAll("</MsgID>", "</MsgId>"); | |||||
| return XmlTransformer.fromXml(WxMpXmlMessage.class, xml); | return XmlTransformer.fromXml(WxMpXmlMessage.class, xml); | ||||
| } catch (JAXBException e) { | } catch (JAXBException e) { | ||||
| throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
| @@ -389,8 +391,8 @@ public class WxMpXmlMessage { | |||||
| public static WxMpXmlMessage fromXml(InputStream is) { | public static WxMpXmlMessage fromXml(InputStream is) { | ||||
| try { | try { | ||||
| return XmlTransformer.fromXml(WxMpXmlMessage.class, is); | |||||
| } catch (JAXBException e) { | |||||
| return fromXml(IOUtils.toString(is, "UTF-8")); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | throw new RuntimeException(e); | ||||
| } | } | ||||
| } | } | ||||
| @@ -14,7 +14,7 @@ public class WxMpGsonBuilder { | |||||
| INSTANCE.disableHtmlEscaping(); | INSTANCE.disableHtmlEscaping(); | ||||
| INSTANCE.registerTypeAdapter(WxMpCustomMessage.class, new WxMpCustomMessageGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpCustomMessage.class, new WxMpCustomMessageGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpMassNews.class, new WxMpMassNewsGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpMassNews.class, new WxMpMassNewsGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpMassGroupMessage.class, new WxMpMassMessageGsonAdapter()); | |||||
| INSTANCE.registerTypeAdapter(WxMpMassGroupMessage.class, new WxMpMassGroupMessageGsonAdapter()); | |||||
| INSTANCE.registerTypeAdapter(WxMpMassOpenIdsMessage.class, new WxMpMassOpenIdsMessageGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpMassOpenIdsMessage.class, new WxMpMassOpenIdsMessageGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpGroup.class, new WxMpGroupGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpGroup.class, new WxMpGroupGsonAdapter()); | ||||
| INSTANCE.registerTypeAdapter(WxMpUser.class, new WxUserGsonAdapter()); | INSTANCE.registerTypeAdapter(WxMpUser.class, new WxUserGsonAdapter()); | ||||
| @@ -22,13 +22,18 @@ import java.lang.reflect.Type; | |||||
| * @author qianjia | * @author qianjia | ||||
| * | * | ||||
| */ | */ | ||||
| public class WxMpMassMessageGsonAdapter implements JsonSerializer<WxMpMassGroupMessage> { | |||||
| public class WxMpMassGroupMessageGsonAdapter implements JsonSerializer<WxMpMassGroupMessage> { | |||||
| public JsonElement serialize(WxMpMassGroupMessage message, Type typeOfSrc, JsonSerializationContext context) { | public JsonElement serialize(WxMpMassGroupMessage message, Type typeOfSrc, JsonSerializationContext context) { | ||||
| JsonObject messageJson = new JsonObject(); | JsonObject messageJson = new JsonObject(); | ||||
| JsonObject filter = new JsonObject(); | JsonObject filter = new JsonObject(); | ||||
| filter.addProperty("group_id", message.getGroupId()); | |||||
| if(null == message.getGroupId()) { | |||||
| filter.addProperty("is_to_all", true); | |||||
| } else { | |||||
| filter.addProperty("is_to_all", false); | |||||
| filter.addProperty("group_id", message.getGroupId()); | |||||
| } | |||||
| messageJson.add("filter", filter); | messageJson.add("filter", filter); | ||||
| if (WxConsts.MASS_MSG_NEWS.equals(message.getMsgtype())) { | if (WxConsts.MASS_MSG_NEWS.equals(message.getMsgtype())) { | ||||
| @@ -87,5 +87,86 @@ public class WxMpXmlMessageTest { | |||||
| Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); | Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); | ||||
| Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); | Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); | ||||
| } | } | ||||
| public void testFromXml2() { | |||||
| String xml = "<xml>" | |||||
| + "<ToUserName><![CDATA[toUser]]></ToUserName>" | |||||
| + "<FromUserName><![CDATA[fromUser]]></FromUserName> " | |||||
| + "<CreateTime>1348831860</CreateTime>" | |||||
| + "<MsgType><![CDATA[text]]></MsgType>" | |||||
| + "<Content><![CDATA[this is a test]]></Content>" | |||||
| + "<MsgID>1234567890123456</MsgID>" | |||||
| + "<PicUrl><![CDATA[this is a url]]></PicUrl>" | |||||
| + "<MediaId><![CDATA[media_id]]></MediaId>" | |||||
| + "<Format><![CDATA[Format]]></Format>" | |||||
| + "<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>" | |||||
| + "<Location_X>23.134521</Location_X>" | |||||
| + "<Location_Y>113.358803</Location_Y>" | |||||
| + "<Scale>20</Scale>" | |||||
| + "<Label><![CDATA[位置信息]]></Label>" | |||||
| + "<Description><![CDATA[公众平台官网链接]]></Description>" | |||||
| + "<Url><![CDATA[url]]></Url>" | |||||
| + "<Title><![CDATA[公众平台官网链接]]></Title>" | |||||
| + "<Event><![CDATA[subscribe]]></Event>" | |||||
| + "<EventKey><![CDATA[qrscene_123123]]></EventKey>" | |||||
| + "<Ticket><![CDATA[TICKET]]></Ticket>" | |||||
| + "<Latitude>23.137466</Latitude>" | |||||
| + "<Longitude>113.352425</Longitude>" | |||||
| + "<Precision>119.385040</Precision>" | |||||
| + "<ScanCodeInfo>" | |||||
| + " <ScanType><![CDATA[qrcode]]></ScanType>" | |||||
| + " <ScanResult><![CDATA[1]]></ScanResult>" | |||||
| + "</ScanCodeInfo>" | |||||
| + "<SendPicsInfo>" | |||||
| + " <Count>1</Count>\n" | |||||
| + " <PicList>" | |||||
| + " <item>" | |||||
| + " <PicMd5Sum><![CDATA[1b5f7c23b5bf75682a53e7b6d163e185]]></PicMd5Sum>" | |||||
| + " </item>" | |||||
| + " </PicList>" | |||||
| + "</SendPicsInfo>" | |||||
| + "<SendLocationInfo>" | |||||
| + " <Location_X><![CDATA[23]]></Location_X>\n" | |||||
| + " <Location_Y><![CDATA[113]]></Location_Y>\n" | |||||
| + " <Scale><![CDATA[15]]></Scale>\n" | |||||
| + " <Label><![CDATA[ 广州市海珠区客村艺苑路 106号]]></Label>\n" | |||||
| + " <Poiname><![CDATA[wo de poi]]></Poiname>\n" | |||||
| + "</SendLocationInfo>" | |||||
| + "</xml>"; | |||||
| WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml); | |||||
| Assert.assertEquals(wxMessage.getToUserName(), "toUser"); | |||||
| Assert.assertEquals(wxMessage.getFromUserName(), "fromUser"); | |||||
| Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860l)); | |||||
| Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT); | |||||
| Assert.assertEquals(wxMessage.getContent(), "this is a test"); | |||||
| Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l)); | |||||
| Assert.assertEquals(wxMessage.getPicUrl(), "this is a url"); | |||||
| Assert.assertEquals(wxMessage.getMediaId(), "media_id"); | |||||
| Assert.assertEquals(wxMessage.getFormat(), "Format"); | |||||
| Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id"); | |||||
| Assert.assertEquals(wxMessage.getLocationX(), new Double(23.134521d)); | |||||
| Assert.assertEquals(wxMessage.getLocationY(), new Double(113.358803d)); | |||||
| Assert.assertEquals(wxMessage.getScale(), new Double(20)); | |||||
| Assert.assertEquals(wxMessage.getLabel(), "位置信息"); | |||||
| Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接"); | |||||
| Assert.assertEquals(wxMessage.getUrl(), "url"); | |||||
| Assert.assertEquals(wxMessage.getTitle(), "公众平台官网链接"); | |||||
| Assert.assertEquals(wxMessage.getEvent(), "subscribe"); | |||||
| Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123"); | |||||
| Assert.assertEquals(wxMessage.getTicket(), "TICKET"); | |||||
| Assert.assertEquals(wxMessage.getLatitude(), new Double(23.137466)); | |||||
| Assert.assertEquals(wxMessage.getLongitude(), new Double(113.352425)); | |||||
| Assert.assertEquals(wxMessage.getPrecision(), new Double(119.385040)); | |||||
| Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode"); | |||||
| Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1"); | |||||
| Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1l)); | |||||
| Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185"); | |||||
| Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23"); | |||||
| Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113"); | |||||
| Assert.assertEquals(wxMessage.getSendLocationInfo().getScale(), "15"); | |||||
| Assert.assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号"); | |||||
| Assert.assertEquals(wxMessage.getSendLocationInfo().getPoiname(), "wo de poi"); | |||||
| } | |||||
| } | } | ||||
| @@ -30,7 +30,7 @@ public class WxMpDemoServer { | |||||
| ServletHandler servletHandler = new ServletHandler(); | ServletHandler servletHandler = new ServletHandler(); | ||||
| server.setHandler(servletHandler); | server.setHandler(servletHandler); | ||||
| ServletHolder endpointServletHolder = new ServletHolder(new WxMpEndpointServlt(wxMpConfigStorage, wxMpService, wxMpMessageRouter)); | |||||
| ServletHolder endpointServletHolder = new ServletHolder(new WxMpEndpointServlet(wxMpConfigStorage, wxMpService, wxMpMessageRouter)); | |||||
| servletHandler.addServletWithMapping(endpointServletHolder, "/*"); | servletHandler.addServletWithMapping(endpointServletHolder, "/*"); | ||||
| ServletHolder oauthServletHolder = new ServletHolder(new WxMpOAuth2Servlet(wxMpService)); | ServletHolder oauthServletHolder = new ServletHolder(new WxMpOAuth2Servlet(wxMpService)); | ||||
| @@ -22,13 +22,13 @@ import java.util.Map; | |||||
| /** | /** | ||||
| * @author Daniel Qian | * @author Daniel Qian | ||||
| */ | */ | ||||
| public class WxMpEndpointServlt extends HttpServlet { | |||||
| public class WxMpEndpointServlet extends HttpServlet { | |||||
| protected WxMpConfigStorage wxMpConfigStorage; | protected WxMpConfigStorage wxMpConfigStorage; | ||||
| protected WxMpService wxMpService; | protected WxMpService wxMpService; | ||||
| protected WxMpMessageRouter wxMpMessageRouter; | protected WxMpMessageRouter wxMpMessageRouter; | ||||
| public WxMpEndpointServlt(WxMpConfigStorage wxMpConfigStorage, WxMpService wxMpService, | |||||
| public WxMpEndpointServlet(WxMpConfigStorage wxMpConfigStorage, WxMpService wxMpService, | |||||
| WxMpMessageRouter wxMpMessageRouter) { | WxMpMessageRouter wxMpMessageRouter) { | ||||
| this.wxMpConfigStorage = wxMpConfigStorage; | this.wxMpConfigStorage = wxMpConfigStorage; | ||||
| this.wxMpService = wxMpService; | this.wxMpService = wxMpService; | ||||