| @@ -1,12 +1,18 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.*; | |||
| import java.io.File; | |||
| import java.util.Date; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.WxMpCustomMessage; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfMsgList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionGetResult; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; | |||
| /** | |||
| * 客服接口 , | |||
| * 命名采用kefu拼音的原因是: | |||
| @@ -17,6 +23,14 @@ import java.util.Date; | |||
| */ | |||
| public interface WxMpKefuService { | |||
| /** | |||
| * <pre> | |||
| * 发送客服消息 | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140547&token=&lang=zh_CN">发送客服消息</a> | |||
| * </pre> | |||
| */ | |||
| boolean customMessageSend(WxMpCustomMessage message) throws WxErrorException; | |||
| //*******************客服管理接口***********************// | |||
| /** | |||
| @@ -3,7 +3,6 @@ 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.WxMpCustomMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpIndustry; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassGroupMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassNews; | |||
| @@ -82,14 +81,6 @@ public interface WxMpService { | |||
| */ | |||
| WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 发送客服消息 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息 | |||
| * </pre> | |||
| */ | |||
| void customMessageSend(WxMpCustomMessage message) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 上传群发用的图文消息,上传后才能群发图文消息 | |||
| @@ -3,15 +3,18 @@ package me.chanjar.weixin.mp.api.impl; | |||
| import java.io.File; | |||
| import java.util.Date; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import com.google.gson.JsonObject; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
| import me.chanjar.weixin.mp.api.WxMpKefuService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpCustomMessage; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfSessionRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; | |||
| @@ -27,6 +30,8 @@ import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; | |||
| * | |||
| */ | |||
| public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| protected final Logger log = LoggerFactory | |||
| .getLogger(WxMpKefuServiceImpl.class); | |||
| private static final String API_URL_PREFIX = "https://api.weixin.qq.com/customservice"; | |||
| private WxMpService wxMpService; | |||
| @@ -34,19 +39,31 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| this.wxMpService = wxMpService; | |||
| } | |||
| @Override | |||
| public boolean customMessageSend(WxMpCustomMessage message) | |||
| throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | |||
| String responseContent = this.wxMpService.post(url, message.toJson()); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, message.toJson(), | |||
| responseContent); | |||
| return true; | |||
| } | |||
| @Override | |||
| public WxMpKfList kfList() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist"; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| String url = API_URL_PREFIX + "/getkflist"; | |||
| String responseContent = this.wxMpService.get(url, null); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, null, | |||
| responseContent); | |||
| return WxMpKfList.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpKfOnlineList kfOnlineList() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist"; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| String url = API_URL_PREFIX + "/getonlinekflist"; | |||
| String responseContent = this.wxMpService.get(url, null); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, null, | |||
| responseContent); | |||
| return WxMpKfOnlineList.fromJson(responseContent); | |||
| } | |||
| @@ -54,8 +71,9 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| public boolean kfAccountAdd(WxMpKfAccountRequest request) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/add"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| String responseContent = this.wxMpService.post(url, request.toJson()); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, request.toJson(), | |||
| responseContent); | |||
| return true; | |||
| } | |||
| @@ -63,16 +81,18 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| public boolean kfAccountUpdate(WxMpKfAccountRequest request) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/update"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| String responseContent = this.wxMpService.post(url, request.toJson()); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, request.toJson(), | |||
| responseContent); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean kfAccountInviteWorker(WxMpKfAccountRequest request) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/inviteworker"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| String responseContent = this.wxMpService.post(url, request.toJson()); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, request.toJson(), | |||
| responseContent); | |||
| return true; | |||
| } | |||
| @@ -80,14 +100,20 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| public boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/uploadheadimg?kf_account=" + kfAccount; | |||
| this.wxMpService.execute(new MediaUploadRequestExecutor(), url, imgFile); | |||
| WxMediaUploadResult responseContent = this.wxMpService | |||
| .execute(new MediaUploadRequestExecutor(), url, imgFile); | |||
| this.log.debug("\nurl:{}\nparams:{}&file:{}\nresponse:{}", url, kfAccount, | |||
| imgFile.getAbsolutePath(), | |||
| responseContent); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean kfAccountDel(String kfAccount) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/del?kf_account=" + kfAccount; | |||
| this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null); | |||
| String responseContent = this.wxMpService.get(url, null); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, null, | |||
| responseContent); | |||
| return true; | |||
| } | |||
| @@ -96,8 +122,9 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| throws WxErrorException { | |||
| WxMpKfSessionRequest request = new WxMpKfSessionRequest(kfAccount, openid); | |||
| String url = API_URL_PREFIX + "/kfsession/create"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| String responseContent = this.wxMpService.post(url, request.toJson()); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, request.toJson(), | |||
| responseContent); | |||
| return true; | |||
| } | |||
| @@ -106,8 +133,9 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| throws WxErrorException { | |||
| WxMpKfSessionRequest request = new WxMpKfSessionRequest(kfAccount, openid); | |||
| String url = API_URL_PREFIX + "/kfsession/close"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| String responseContent = this.wxMpService.post(url, request.toJson()); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, request.toJson(), | |||
| responseContent); | |||
| return true; | |||
| } | |||
| @@ -115,8 +143,9 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| public WxMpKfSessionGetResult kfSessionGet(String openid) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfsession/getsession?openid=" + openid; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| String responseContent = this.wxMpService.get(url, null); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, null, | |||
| responseContent); | |||
| return WxMpKfSessionGetResult.fromJson(responseContent); | |||
| } | |||
| @@ -124,8 +153,9 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| public WxMpKfSessionList kfSessionList(String kfAccount) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfsession/getsessionlist?kf_account=" + kfAccount; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| String responseContent = this.wxMpService.get(url, null); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, null, | |||
| responseContent); | |||
| return WxMpKfSessionList.fromJson(responseContent); | |||
| } | |||
| @@ -133,8 +163,9 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| public WxMpKfSessionWaitCaseList kfSessionGetWaitCase() | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfsession/getwaitcase"; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| String responseContent = this.wxMpService.get(url, null); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, null, | |||
| responseContent); | |||
| return WxMpKfSessionWaitCaseList.fromJson(responseContent); | |||
| } | |||
| @@ -156,7 +187,9 @@ public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| param.addProperty("msgid", msgId); //msgid 消息id顺序从小到大,从1开始 | |||
| param.addProperty("number", number); //number 每次获取条数,最多10000条 | |||
| String responseContent = this.wxMpService.execute(new SimplePostRequestExecutor(), url, param.toString()); | |||
| String responseContent = this.wxMpService.post(url, param.toString()); | |||
| this.log.debug("\nurl:{}\nparams:{}\nresponse:{}", url, param.toString(), | |||
| responseContent); | |||
| return WxMpKfMsgList.fromJson(responseContent); | |||
| } | |||
| @@ -44,7 +44,6 @@ import me.chanjar.weixin.mp.api.WxMpQrcodeService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.api.WxMpUserService; | |||
| import me.chanjar.weixin.mp.api.WxMpUserTagService; | |||
| import me.chanjar.weixin.mp.bean.WxMpCustomMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpIndustry; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassGroupMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpMassNews; | |||
| @@ -203,12 +202,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return jsapiSignature; | |||
| } | |||
| @Override | |||
| public void customMessageSend(WxMpCustomMessage message) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send"; | |||
| execute(new SimplePostRequestExecutor(), url, message.toJson()); | |||
| } | |||
| @Override | |||
| public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; | |||
| @@ -1,44 +0,0 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| 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.impl.WxMpServiceImpl; | |||
| import me.chanjar.weixin.mp.bean.WxMpCustomMessage; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| /*** | |||
| * 测试发送客服消息 | |||
| * @author chanjarster | |||
| * | |||
| */ | |||
| @Test(groups="customMessageAPI") | |||
| @Guice(modules = ApiTestModule.class) | |||
| public class WxMpCustomMessageAPITest { | |||
| @Inject | |||
| protected WxMpServiceImpl wxService; | |||
| public void testSendCustomMessage() throws WxErrorException { | |||
| ApiTestModule.WxXmlMpInMemoryConfigStorage configStorage = (ApiTestModule.WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage(); | |||
| WxMpCustomMessage message = new WxMpCustomMessage(); | |||
| message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); | |||
| message.setToUser(configStorage.getOpenId()); | |||
| message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>"); | |||
| this.wxService.customMessageSend(message); | |||
| } | |||
| public void testSendCustomMessageWithKfAccount() throws WxErrorException { | |||
| ApiTestModule.WxXmlMpInMemoryConfigStorage configStorage = (ApiTestModule.WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage(); | |||
| WxMpCustomMessage message = new WxMpCustomMessage(); | |||
| message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); | |||
| message.setToUser(configStorage.getOpenId()); | |||
| message.setKfAccount(configStorage.getKfAccount()); | |||
| message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>"); | |||
| this.wxService.customMessageSend(message); | |||
| } | |||
| } | |||
| @@ -11,9 +11,11 @@ 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.ApiTestModule.WxXmlMpInMemoryConfigStorage; | |||
| import me.chanjar.weixin.mp.bean.WxMpCustomMessage; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfInfo; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; | |||
| @@ -35,6 +37,31 @@ public class WxMpKefuServiceImplTest { | |||
| @Inject | |||
| protected WxMpServiceImpl wxService; | |||
| public void testSendCustomMessage() throws WxErrorException { | |||
| ApiTestModule.WxXmlMpInMemoryConfigStorage configStorage = (ApiTestModule.WxXmlMpInMemoryConfigStorage) this.wxService | |||
| .getWxMpConfigStorage(); | |||
| WxMpCustomMessage message = new WxMpCustomMessage(); | |||
| message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); | |||
| message.setToUser(configStorage.getOpenId()); | |||
| message.setContent( | |||
| "欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>"); | |||
| this.wxService.getKefuService().customMessageSend(message); | |||
| } | |||
| public void testSendCustomMessageWithKfAccount() throws WxErrorException { | |||
| ApiTestModule.WxXmlMpInMemoryConfigStorage configStorage = (ApiTestModule.WxXmlMpInMemoryConfigStorage) this.wxService | |||
| .getWxMpConfigStorage(); | |||
| WxMpCustomMessage message = new WxMpCustomMessage(); | |||
| message.setMsgType(WxConsts.CUSTOM_MSG_TEXT); | |||
| message.setToUser(configStorage.getOpenId()); | |||
| message.setKfAccount(configStorage.getKfAccount()); | |||
| message.setContent( | |||
| "欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>"); | |||
| this.wxService.getKefuService().customMessageSend(message); | |||
| } | |||
| public void testKfList() throws WxErrorException { | |||
| WxMpKfList kfList = this.wxService.getKefuService().kfList(); | |||
| Assert.assertNotNull(kfList); | |||
| @@ -123,7 +150,7 @@ public class WxMpKefuServiceImplTest { | |||
| } | |||
| @Test(dataProvider = "getKfAccountAndOpenid") | |||
| public void testKfSessionGet(String kfAccount, | |||
| public void testKfSessionGet(@SuppressWarnings("unused") String kfAccount, | |||
| String openid) throws WxErrorException { | |||
| WxMpKfSessionGetResult result = this.wxService.getKefuService() | |||
| .kfSessionGet(openid); | |||
| @@ -1,5 +1,9 @@ | |||
| package me.chanjar.weixin.mp.demo; | |||
| import java.util.Map; | |||
| import java.util.Random; | |||
| import java.util.regex.Pattern; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.session.WxSession; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| @@ -10,10 +14,6 @@ import me.chanjar.weixin.mp.bean.WxMpCustomMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| import java.util.Map; | |||
| import java.util.Random; | |||
| import java.util.regex.Pattern; | |||
| public class DemoGuessNumberHandler implements WxMpMessageHandler, WxMpMessageMatcher { | |||
| private Random random = new Random(); | |||
| @@ -57,14 +57,14 @@ public class DemoGuessNumberHandler implements WxMpMessageHandler, WxMpMessageMa | |||
| .toUser(wxMessage.getFromUserName()) | |||
| .content("请猜一个100以内的数字") | |||
| .build(); | |||
| wxMpService.customMessageSend(m); | |||
| wxMpService.getKefuService().customMessageSend(m); | |||
| } else { | |||
| WxMpCustomMessage m = WxMpCustomMessage | |||
| .TEXT() | |||
| .toUser(wxMessage.getFromUserName()) | |||
| .content("放弃了吗?那请重新猜一个100以内的数字") | |||
| .build(); | |||
| wxMpService.customMessageSend(m); | |||
| wxMpService.getKefuService().customMessageSend(m); | |||
| } | |||
| session.setAttribute("guessing", Boolean.TRUE); | |||
| @@ -92,7 +92,7 @@ public class DemoGuessNumberHandler implements WxMpMessageHandler, WxMpMessageMa | |||
| .toUser(wxMessage.getFromUserName()) | |||
| .content("小了") | |||
| .build(); | |||
| wxMpService.customMessageSend(m); | |||
| wxMpService.getKefuService().customMessageSend(m); | |||
| } else if (guessNumber > answer) { | |||
| WxMpCustomMessage m = WxMpCustomMessage | |||
| @@ -100,7 +100,7 @@ public class DemoGuessNumberHandler implements WxMpMessageHandler, WxMpMessageMa | |||
| .toUser(wxMessage.getFromUserName()) | |||
| .content("大了") | |||
| .build(); | |||
| wxMpService.customMessageSend(m); | |||
| wxMpService.getKefuService().customMessageSend(m); | |||
| } else { | |||
| WxMpCustomMessage m = WxMpCustomMessage | |||
| .TEXT() | |||
| @@ -108,7 +108,7 @@ public class DemoGuessNumberHandler implements WxMpMessageHandler, WxMpMessageMa | |||
| .content("Bingo!") | |||
| .build(); | |||
| session.removeAttribute("guessing"); | |||
| wxMpService.customMessageSend(m); | |||
| wxMpService.getKefuService().customMessageSend(m); | |||
| } | |||
| } | |||