| @@ -3,19 +3,8 @@ package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
| import me.chanjar.weixin.mp.bean.WxMpIndustry; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassTagMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassNews; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassPreviewMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassVideo; | |||
| import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; | |||
| import me.chanjar.weixin.mp.bean.WxMpTemplateMessage; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpMassSendResult; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUser; | |||
| import me.chanjar.weixin.mp.bean.*; | |||
| import me.chanjar.weixin.mp.bean.result.*; | |||
| /** | |||
| * 微信API的Service | |||
| @@ -133,16 +122,6 @@ public interface WxMpService { | |||
| */ | |||
| String shortUrl(String long_url) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 发送模板消息 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=模板消息接口 | |||
| * </pre> | |||
| * | |||
| * @return msgid | |||
| */ | |||
| String templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 语义查询接口 | |||
| @@ -266,27 +245,6 @@ public interface WxMpService { | |||
| */ | |||
| WxMpMassSendResult massMessagePreview(WxMpMassPreviewMessage wxMpMassPreviewMessage) throws Exception; | |||
| /** | |||
| * <pre> | |||
| * 设置所属行业 | |||
| * 官方文档中暂未告知响应内容 | |||
| * 详情请见:http://mp.weixin.qq.com/wiki/5/6dde9eaa909f83354e0094dc3ad99e05.html#.E8.AE.BE.E7.BD.AE.E6.89.80.E5.B1.9E.E8.A1.8C.E4.B8.9A | |||
| * </pre> | |||
| * | |||
| * @return JsonObject | |||
| */ | |||
| String setIndustry(WxMpIndustry wxMpIndustry) throws WxErrorException; | |||
| /*** | |||
| * <pre> | |||
| * 获取设置的行业信息 | |||
| * 详情请见:http://mp.weixin.qq.com/wiki/5/6dde9eaa909f83354e0094dc3ad99e05.html#.E8.AE.BE.E7.BD.AE.E6.89.80.E5.B1.9E.E8.A1.8C.E4.B8.9A | |||
| * </pre> | |||
| * | |||
| * @return wxMpIndustry | |||
| */ | |||
| WxMpIndustry getIndustry() throws WxErrorException; | |||
| /** | |||
| * 获取WxMpConfigStorage 对象 | |||
| * | |||
| @@ -370,4 +328,11 @@ public interface WxMpService { | |||
| * @return WxMpStoreService | |||
| */ | |||
| WxMpStoreService getStoreService(); | |||
| /** | |||
| * 返回模板消息相关接口方法的实现类对象,以方便调用其各种接口 | |||
| * | |||
| * @return WxMpTemplateMsgService | |||
| */ | |||
| WxMpTemplateMsgService getTemplateMsgService(); | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.WxMpIndustry; | |||
| import me.chanjar.weixin.mp.bean.WxMpTemplateMessage; | |||
| /** | |||
| * <pre> | |||
| * 模板消息接口 | |||
| * http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN | |||
| * Created by Binary Wang on 2016-10-14. | |||
| * @author miller.lin | |||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||
| * </pre> | |||
| */ | |||
| public interface WxMpTemplateMsgService { | |||
| /** | |||
| * <pre> | |||
| * 设置所属行业 | |||
| * 官方文档中暂未告知响应内容 | |||
| * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN | |||
| * </pre> | |||
| * | |||
| * @return 是否成功 | |||
| */ | |||
| boolean setIndustry(WxMpIndustry wxMpIndustry) throws WxErrorException; | |||
| /*** | |||
| * <pre> | |||
| * 获取设置的行业信息 | |||
| * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN | |||
| * </pre> | |||
| * | |||
| * @return wxMpIndustry | |||
| */ | |||
| WxMpIndustry getIndustry() throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 发送模板消息 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN | |||
| * </pre> | |||
| * | |||
| * @return 消息Id | |||
| */ | |||
| String sendTemplateMsg(WxMpTemplateMessage templateMessage) throws WxErrorException; | |||
| } | |||
| @@ -69,6 +69,8 @@ public class WxMpServiceImpl implements WxMpService { | |||
| private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); | |||
| private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this); | |||
| private CloseableHttpClient httpClient; | |||
| private HttpHost httpProxy; | |||
| @@ -221,35 +223,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return tmpJsonElement.getAsJsonObject().get("short_url").getAsString(); | |||
| } | |||
| @Override | |||
| public String templateSend(WxMpTemplateMessage templateMessage) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/message/template/send"; | |||
| String responseContent = this.post(url, templateMessage.toJson()); | |||
| final JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||
| if (jsonObject.get("errcode").getAsInt() == 0){ | |||
| return jsonObject.get("msgid").getAsString(); | |||
| } | |||
| throw new WxErrorException(WxError.fromJson(responseContent)); | |||
| } | |||
| @Override | |||
| public String setIndustry(WxMpIndustry wxMpIndustry) throws WxErrorException { | |||
| if (null == wxMpIndustry.getPrimaryIndustry() || null == wxMpIndustry.getPrimaryIndustry().getId() | |||
| || null == wxMpIndustry.getSecondIndustry() || null == wxMpIndustry.getSecondIndustry().getId()) { | |||
| throw new IllegalArgumentException("industry id is empty"); | |||
| } | |||
| String url = "https://api.weixin.qq.com/cgi-bin/template/api_set_industry"; | |||
| return this.post(url, wxMpIndustry.toJson()); | |||
| } | |||
| @Override | |||
| public WxMpIndustry getIndustry() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/template/get_industry"; | |||
| String responseContent = this.get(url, null); | |||
| return WxMpIndustry.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/semantic/semproxy/search"; | |||
| @@ -562,4 +535,9 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return this.storeService; | |||
| } | |||
| @Override | |||
| public WxMpTemplateMsgService getTemplateMsgService() { | |||
| return this.templateMsgService; | |||
| } | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import com.google.gson.JsonObject; | |||
| import com.google.gson.JsonParser; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.api.WxMpTemplateMsgService; | |||
| import me.chanjar.weixin.mp.bean.WxMpIndustry; | |||
| import me.chanjar.weixin.mp.bean.WxMpTemplateMessage; | |||
| /** | |||
| * <pre> | |||
| * Created by Binary Wang on 2016-10-14. | |||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||
| * </pre> | |||
| */ | |||
| public class WxMpTemplateMsgServiceImpl implements WxMpTemplateMsgService { | |||
| public static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/template"; | |||
| private static final JsonParser JSON_PARSER = new JsonParser(); | |||
| private WxMpService wxMpService; | |||
| public WxMpTemplateMsgServiceImpl(WxMpService wxMpService) { | |||
| this.wxMpService = wxMpService; | |||
| } | |||
| @Override | |||
| public String sendTemplateMsg(WxMpTemplateMessage templateMessage) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/message/template/send"; | |||
| String responseContent = this.wxMpService.post(url, templateMessage.toJson()); | |||
| final JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); | |||
| if (jsonObject.get("errcode").getAsInt() == 0) { | |||
| return jsonObject.get("msgid").getAsString(); | |||
| } | |||
| throw new WxErrorException(WxError.fromJson(responseContent)); | |||
| } | |||
| @Override | |||
| public boolean setIndustry(WxMpIndustry wxMpIndustry) throws WxErrorException { | |||
| if (null == wxMpIndustry.getPrimaryIndustry() || null == wxMpIndustry.getPrimaryIndustry().getId() | |||
| || null == wxMpIndustry.getSecondIndustry() || null == wxMpIndustry.getSecondIndustry().getId()) { | |||
| throw new IllegalArgumentException("行业Id不能为空,请核实"); | |||
| } | |||
| String url = API_URL_PREFIX + "/api_set_industry"; | |||
| this.wxMpService.post(url, wxMpIndustry.toJson()); | |||
| return true; | |||
| } | |||
| @Override | |||
| public WxMpIndustry getIndustry() throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/get_industry"; | |||
| String responseContent = this.wxMpService.get(url, null); | |||
| return WxMpIndustry.fromJson(responseContent); | |||
| } | |||
| } | |||
| @@ -1,38 +0,0 @@ | |||
| package me.chanjar.weixin.mp.bean; | |||
| import java.io.Serializable; | |||
| /** | |||
| * @author miller | |||
| * 官方文档中,创建和获取的数据结构不一样。所以采用冗余字段的方式,实现相应的接口 | |||
| */ | |||
| public class Industry implements Serializable { | |||
| private static final long serialVersionUID = -1707184885588012142L; | |||
| private String id; | |||
| private String firstClass; | |||
| private String secondClass; | |||
| public String getId() { | |||
| return this.id; | |||
| } | |||
| public void setId(String id) { | |||
| this.id = id; | |||
| } | |||
| public String getFirstClass() { | |||
| return this.firstClass; | |||
| } | |||
| public void setFirstClass(String firstClass) { | |||
| this.firstClass = firstClass; | |||
| } | |||
| public String getSecondClass() { | |||
| return this.secondClass; | |||
| } | |||
| public void setSecondClass(String secondClass) { | |||
| this.secondClass = secondClass; | |||
| } | |||
| } | |||
| @@ -2,6 +2,8 @@ package me.chanjar.weixin.mp.bean; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import java.io.Serializable; | |||
| @@ -9,31 +11,97 @@ import java.io.Serializable; | |||
| * @author miller | |||
| */ | |||
| public class WxMpIndustry implements Serializable { | |||
| private static final long serialVersionUID = -7700398224795914722L; | |||
| private Industry primaryIndustry; | |||
| private Industry secondIndustry; | |||
| private static final long serialVersionUID = -7700398224795914722L; | |||
| private Industry primaryIndustry; | |||
| private Industry secondIndustry; | |||
| public static WxMpIndustry fromJson(String json) { | |||
| return WxMpGsonBuilder.create().fromJson(json, WxMpIndustry.class); | |||
| public WxMpIndustry() { | |||
| } | |||
| public WxMpIndustry(Industry primaryIndustry, Industry secondIndustry) { | |||
| this.primaryIndustry = primaryIndustry; | |||
| this.secondIndustry = secondIndustry; | |||
| } | |||
| /** | |||
| * @author miller | |||
| * 官方文档中,创建和获取的数据结构不一样。所以采用冗余字段的方式,实现相应的接口 | |||
| */ | |||
| public static class Industry implements Serializable { | |||
| private static final long serialVersionUID = -1707184885588012142L; | |||
| private String id; | |||
| private String firstClass; | |||
| private String secondClass; | |||
| public Industry() { | |||
| } | |||
| public Industry(String id) { | |||
| this.id = id; | |||
| } | |||
| public Industry(String id, String firstClass, String secondClass) { | |||
| this.id = id; | |||
| this.firstClass = firstClass; | |||
| this.secondClass = secondClass; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); | |||
| } | |||
| public String toJson() { | |||
| return WxMpGsonBuilder.create().toJson(this); | |||
| public String getId() { | |||
| return this.id; | |||
| } | |||
| public Industry getPrimaryIndustry() { | |||
| return this.primaryIndustry; | |||
| public void setId(String id) { | |||
| this.id = id; | |||
| } | |||
| public void setPrimaryIndustry(Industry primaryIndustry) { | |||
| this.primaryIndustry = primaryIndustry; | |||
| public String getFirstClass() { | |||
| return this.firstClass; | |||
| } | |||
| public Industry getSecondIndustry() { | |||
| return this.secondIndustry; | |||
| public void setFirstClass(String firstClass) { | |||
| this.firstClass = firstClass; | |||
| } | |||
| public void setSecondIndustry(Industry secondIndustry) { | |||
| this.secondIndustry = secondIndustry; | |||
| public String getSecondClass() { | |||
| return this.secondClass; | |||
| } | |||
| public void setSecondClass(String secondClass) { | |||
| this.secondClass = secondClass; | |||
| } | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); | |||
| } | |||
| public static WxMpIndustry fromJson(String json) { | |||
| return WxMpGsonBuilder.create().fromJson(json, WxMpIndustry.class); | |||
| } | |||
| public String toJson() { | |||
| return WxMpGsonBuilder.create().toJson(this); | |||
| } | |||
| public Industry getPrimaryIndustry() { | |||
| return this.primaryIndustry; | |||
| } | |||
| public void setPrimaryIndustry(Industry primaryIndustry) { | |||
| this.primaryIndustry = primaryIndustry; | |||
| } | |||
| public Industry getSecondIndustry() { | |||
| return this.secondIndustry; | |||
| } | |||
| public void setSecondIndustry(Industry secondIndustry) { | |||
| this.secondIndustry = secondIndustry; | |||
| } | |||
| } | |||
| @@ -1,19 +1,11 @@ | |||
| package me.chanjar.weixin.mp.util.json; | |||
| import java.lang.reflect.Type; | |||
| import com.google.gson.JsonDeserializationContext; | |||
| import com.google.gson.JsonDeserializer; | |||
| import com.google.gson.JsonElement; | |||
| import com.google.gson.JsonObject; | |||
| import com.google.gson.JsonParseException; | |||
| import com.google.gson.JsonSerializationContext; | |||
| import com.google.gson.JsonSerializer; | |||
| import com.google.gson.*; | |||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||
| import me.chanjar.weixin.mp.bean.Industry; | |||
| import me.chanjar.weixin.mp.bean.WxMpIndustry; | |||
| import java.lang.reflect.Type; | |||
| /** | |||
| * @author miller | |||
| */ | |||
| @@ -42,8 +34,8 @@ public class WxMpIndustryGsonAdapter | |||
| return wxMpIndustry; | |||
| } | |||
| private static Industry convertFromJson(JsonObject json) { | |||
| Industry industry = new Industry(); | |||
| private static WxMpIndustry.Industry convertFromJson(JsonObject json) { | |||
| WxMpIndustry.Industry industry = new WxMpIndustry.Industry(); | |||
| industry.setFirstClass(GsonHelper.getString(json, "first_class")); | |||
| industry.setSecondClass(GsonHelper.getString(json, "second_class")); | |||
| return industry; | |||
| @@ -1,20 +1,12 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Date; | |||
| import org.testng.Assert; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import com.google.inject.Inject; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.api.ApiTestModule; | |||
| import me.chanjar.weixin.mp.api.WxXmlMpInMemoryConfigStorage; | |||
| import me.chanjar.weixin.mp.bean.WxMpTemplateData; | |||
| import me.chanjar.weixin.mp.bean.WxMpTemplateMessage; | |||
| import org.testng.Assert; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| @Test | |||
| @Guice(modules = ApiTestModule.class) | |||
| @@ -88,20 +80,6 @@ public class WxMpServiceImplTest { | |||
| Assert.fail("Not yet implemented"); | |||
| } | |||
| @Test(invocationCount = 100, threadPoolSize = 30) | |||
| public void testTemplateSend() throws WxErrorException { | |||
| SimpleDateFormat dateFormat = new SimpleDateFormat( | |||
| "yyyy-MM-dd HH:mm:ss.SSS"); | |||
| WxXmlMpInMemoryConfigStorage configStorage = (WxXmlMpInMemoryConfigStorage) this.wxService | |||
| .getWxMpConfigStorage(); | |||
| WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() | |||
| .toUser(configStorage.getOpenid()) | |||
| .templateId(configStorage.getTemplateId()).build(); | |||
| templateMessage.addWxMpTemplateData( | |||
| new WxMpTemplateData("first", dateFormat.format(new Date()))); | |||
| this.wxService.templateSend(templateMessage); | |||
| } | |||
| @Test | |||
| public void testSetIndustry() { | |||
| Assert.fail("Not yet implemented"); | |||
| @@ -0,0 +1,59 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import com.google.inject.Inject; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.api.ApiTestModule; | |||
| import me.chanjar.weixin.mp.api.WxXmlMpInMemoryConfigStorage; | |||
| import me.chanjar.weixin.mp.bean.WxMpIndustry; | |||
| import me.chanjar.weixin.mp.bean.WxMpTemplateData; | |||
| import me.chanjar.weixin.mp.bean.WxMpTemplateMessage; | |||
| import org.testng.Assert; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Date; | |||
| /** | |||
| * <pre> | |||
| * Created by Binary Wang on 2016-10-14. | |||
| * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||
| * </pre> | |||
| */ | |||
| @Guice(modules = ApiTestModule.class) | |||
| public class WxMpTemplateMsgServiceImplTest { | |||
| @Inject | |||
| protected WxMpServiceImpl wxService; | |||
| @Test(invocationCount = 10, threadPoolSize = 10) | |||
| public void testSendTemplateMsg() throws WxErrorException { | |||
| SimpleDateFormat dateFormat = new SimpleDateFormat( | |||
| "yyyy-MM-dd HH:mm:ss.SSS"); | |||
| WxXmlMpInMemoryConfigStorage configStorage = (WxXmlMpInMemoryConfigStorage) this.wxService | |||
| .getWxMpConfigStorage(); | |||
| WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() | |||
| .toUser(configStorage.getOpenid()) | |||
| .templateId(configStorage.getTemplateId()).build(); | |||
| templateMessage.addWxMpTemplateData( | |||
| new WxMpTemplateData("first", dateFormat.format(new Date()))); | |||
| String msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage); | |||
| Assert.assertNotNull(msgId); | |||
| System.out.println(msgId); | |||
| } | |||
| @Test | |||
| public void testGetIndustry() throws Exception { | |||
| final WxMpIndustry industry = this.wxService.getTemplateMsgService().getIndustry(); | |||
| Assert.assertNotNull(industry); | |||
| System.out.println(industry); | |||
| } | |||
| @Test | |||
| public void testSetIndustry() throws Exception { | |||
| WxMpIndustry industry = new WxMpIndustry(new WxMpIndustry.Industry("1"), | |||
| new WxMpIndustry.Industry("04")); | |||
| boolean result = this.wxService.getTemplateMsgService().setIndustry(industry); | |||
| Assert.assertTrue(result); | |||
| } | |||
| } | |||