概述:
Jfinal_weixin已經出了有好一段時間了!一直在關注當中......最近工作上有需要到這個東西,所以,話了兩個小時來看看這個東西,看完demo以后,豁然開朗,原理微信和一般的web項目什么的都是一樣的!!所以,為了讓后面的同學能夠少走一些彎路,我覺得我很有必要把這個學習的過程記錄下來,然后給大家進行參考,這樣能夠讓更多的人完成這個微信項目的學習,從零開始學習。
在看此博客有什么不懂的地方可以在我的微信公眾號或者微社區中交流。 微信開發交流群:114196246
如何開發微信?,這個東西大家可以去參考TX的那個微信開發文檔,今天主要講的都是Jfinal_weixin這個東西(如何創建一個WEB項目,如何獲取Jfinal_weixin的開發包),也就是官網的那個demo。JFinal官網地址:http://www.jfinal.com/ 開源社區 http://git.oschina.net/jfinal/jfinal-weixin
一:開源社區下載jfinal-weixin源碼
下載解壓的目錄如下
二:將jfinal-weixin源碼導入到IDE
三:分析源碼如何使用jfinal-weixin
1、WeixinConfig 微信開發環境的配置
public class WeixinConfig extends JFinalConfig { /** * 如果生產環境配置文件存在,則優先加載該配置,否則加載開發環境配置文件 * @param pro 生產環境配置文件 * @param dev 開發環境配置文件 */ public void loadProp(String pro, String dev) { try { PropKit.use(pro); } catch (Exception e) { PropKit.use(dev); } } public void configConstant(Constants me) { loadProp("a_little_config_pro.txt", "a_little_config.txt"); me.setDevMode(PropKit.getBoolean("devMode", false)); // ApiConfigKit 設為開發模式可以在開發階段輸出請求交互的 xml 與 json 數據 ApiConfigKit.setDevMode(me.getDevMode()); } public void configRoute(Routes me) { me.add("/msg", WeixinMsgController.class); me.add("/api", WeixinApiController.class, "/api"); } public void configPlugin(Plugins me) { // C3p0Plugin c3p0Plugin = new C3p0Plugin(PropKit.get("jdbcUrl"), PropKit.get("user"), PropKit.get("password").trim()); // me.add(c3p0Plugin); // EhCachePlugin ecp = new EhCachePlugin(); // me.add(ecp); } public void configInterceptor(Interceptors me) { } public void configHandler(Handlers me) { } public static void main(String[] args) { JFinal.start("webapp", 80, "/", 5); } }
以上通過 configRoute 方法配置了訪問路由 "/msg" 與 "/api"。項目啟動后,在微信服以務器上配置 url:http://域名/msg 填寫Token時需要與配置文件(a_little_config.txt)中的保持一次
2、WeixinMsgController 微信消息處理
package com.jfinal.weixin.demo; import com.jfinal.kit.PropKit; import com.jfinal.log.Logger; import com.jfinal.weixin.sdk.api.ApiConfig; import com.jfinal.weixin.sdk.jfinal.MsgController; import com.jfinal.weixin.sdk.msg.in.*; import com.jfinal.weixin.sdk.msg.in.event.*; import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResults; import com.jfinal.weixin.sdk.msg.out.*; /** * 將此 DemoController 在YourJFinalConfig 中注冊路由, * 並設置好weixin開發者中心的 URL 與 token ,使 URL 指向該 * DemoController 繼承自父類 WeixinController 的 index * 方法即可直接運行看效果,在此基礎之上修改相關的方法即可進行實際項目開發 */ public class WeixinMsgController extends MsgController { static Logger logger = Logger.getLogger(WeixinMsgController.class); private static final String helpStr = "\t發送 help 可獲得幫助,發送\"視頻\" 可獲取視頻教程,發送 \"美女\" 可看美女,發送 music 可聽音樂 ,發送新聞可看JFinal新版本消息。公眾號功能持續完善中"; /** * 如果要支持多公眾賬號,只需要在此返回各個公眾號對應的 ApiConfig 對象即可 * 可以通過在請求 url 中掛參數來動態從數據庫中獲取 ApiConfig 屬性值 */ public ApiConfig getApiConfig() { ApiConfig ac = new ApiConfig(); // 配置微信 API 相關常量 ac.setToken(PropKit.get("token")); ac.setAppId(PropKit.get("appId")); ac.setAppSecret(PropKit.get("appSecret")); /** * 是否對消息進行加密,對應於微信平台的消息加解密方式: * 1:true進行加密且必須配置 encodingAesKey * 2:false采用明文模式,同時也支持混合模式 */ ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false)); ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file")); return ac; } // protected void processInTextMsg(InTextMsg inTextMsg) // { // //轉發給多客服PC客戶端 // OutCustomMsg outCustomMsg = new OutCustomMsg(inTextMsg); // render(outCustomMsg); // } // // @Override // protected void processInVoiceMsg(InVoiceMsg inVoiceMsg) // { // //轉發給多客服PC客戶端 // OutCustomMsg outCustomMsg = new OutCustomMsg(inVoiceMsg); // render(outCustomMsg); // } // // @Override // protected void processInVideoMsg(InVideoMsg inVideoMsg) // { // //轉發給多客服PC客戶端 // OutCustomMsg outCustomMsg = new OutCustomMsg(inVideoMsg); // render(outCustomMsg); // } // // @Override // protected void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg) // { // //轉發給多客服PC客戶端 // OutCustomMsg outCustomMsg = new OutCustomMsg(inShortVideoMsg); // render(outCustomMsg); // } // // @Override // protected void processInLocationMsg(InLocationMsg inLocationMsg) // { // //轉發給多客服PC客戶端 // OutCustomMsg outCustomMsg = new OutCustomMsg(inLocationMsg); // render(outCustomMsg); // } // // @Override // protected void processInLinkMsg(InLinkMsg inLinkMsg) // { // //轉發給多客服PC客戶端 // OutCustomMsg outCustomMsg = new OutCustomMsg(inLinkMsg); // render(outCustomMsg); // } // // @Override // protected void processInCustomEvent(InCustomEvent inCustomEvent) // { // logger.debug("測試方法:processInCustomEvent()"); // renderNull(); // } // // protected void processInImageMsg(InImageMsg inImageMsg) // { // //轉發給多客服PC客戶端 // OutCustomMsg outCustomMsg = new OutCustomMsg(inImageMsg); // render(outCustomMsg); // } // // /** // * 實現父類抽方法,處理關注/取消關注消息 // */ // protected void processInFollowEvent(InFollowEvent inFollowEvent) // { // if (InFollowEvent.EVENT_INFOLLOW_SUBSCRIBE.equals(inFollowEvent.getEvent())) // { // logger.debug("關注:" + inFollowEvent.getFromUserName()); // OutTextMsg outMsg = new OutTextMsg(inFollowEvent); // outMsg.setContent("這是Jfinal-weixin測試服務</br>\r\n感謝您的關注"); // render(outMsg); // } // // 如果為取消關注事件,將無法接收到傳回的信息 // if (InFollowEvent.EVENT_INFOLLOW_UNSUBSCRIBE.equals(inFollowEvent.getEvent())) // { // logger.debug("取消關注:" + inFollowEvent.getFromUserName()); // } // } // // @Override // protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent) // { // if (InQrCodeEvent.EVENT_INQRCODE_SUBSCRIBE.equals(inQrCodeEvent.getEvent())) // { // logger.debug("掃碼未關注:" + inQrCodeEvent.getFromUserName()); // OutTextMsg outMsg = new OutTextMsg(inQrCodeEvent); // outMsg.setContent("感謝您的關注,二維碼內容:" + inQrCodeEvent.getEventKey()); // render(outMsg); // } // if (InQrCodeEvent.EVENT_INQRCODE_SCAN.equals(inQrCodeEvent.getEvent())) // { // logger.debug("掃碼已關注:" + inQrCodeEvent.getFromUserName()); // } // } // // @Override // protected void processInLocationEvent(InLocationEvent inLocationEvent) // { // logger.debug("發送地理位置事件:" + inLocationEvent.getFromUserName()); // OutTextMsg outMsg = new OutTextMsg(inLocationEvent); // outMsg.setContent("地理位置是:" + inLocationEvent.getLatitude()); // render(outMsg); // } // // @Override // protected void processInMassEvent(InMassEvent inMassEvent) // { // logger.debug("測試方法:processInMassEvent()"); // renderNull(); // } // // /** // * 實現父類抽方法,處理自定義菜單事件 // */ // protected void processInMenuEvent(InMenuEvent inMenuEvent) // { // logger.debug("菜單事件:" + inMenuEvent.getFromUserName()); // OutTextMsg outMsg = new OutTextMsg(inMenuEvent); // outMsg.setContent("菜單事件內容是:" + inMenuEvent.getEventKey()); // render(outMsg); // } // // @Override // protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults) // { // logger.debug("語音識別事件:" + inSpeechRecognitionResults.getFromUserName()); // OutTextMsg outMsg = new OutTextMsg(inSpeechRecognitionResults); // outMsg.setContent("語音識別內容是:" + inSpeechRecognitionResults.getRecognition()); // render(outMsg); // } // // @Override // protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent) // { // logger.debug("測試方法:processInTemplateMsgEvent()"); // renderNull(); // } /** * 實現父類抽方法,處理文本消息 * 本例子中根據消息中的不同文本內容分別做出了不同的響應,同時也是為了測試 jfinal weixin sdk的基本功能: * 本方法僅測試了 OutTextMsg、OutNewsMsg、OutMusicMsg 三種類型的OutMsg, * 其它類型的消息會在隨后的方法中進行測試 */ protected void processInTextMsg(InTextMsg inTextMsg) { String msgContent = inTextMsg.getContent().trim(); // 幫助提示 if ("help".equalsIgnoreCase(msgContent) || "幫助".equals(msgContent)) { OutTextMsg outMsg = new OutTextMsg(inTextMsg); outMsg.setContent(helpStr); render(outMsg); } // 圖文消息測試 else if ("news".equalsIgnoreCase(msgContent) || "新聞".equalsIgnoreCase(msgContent)) { OutNewsMsg outMsg = new OutNewsMsg(inTextMsg); outMsg.addNews("我的微信公眾號javenlife", "Jfinal開發微信技術交流","https://mmbiz.qlogo.cn/mmbiz/ibHRiaZ9MRcUosol56OtHjVibWTK9opiaxsYTQHXuRwoib8YobOfqCbykp3ZSaEk8czAqdkAARU0OdKDtv34F5evFIQ/0?wx_fmt=jpeg", "http://mp.weixin.qq.com/s?__biz=MzA4MDA2OTA0Mg==&mid=208184833&idx=1&sn=d9e615e45902c3c72db6c24b65c4af3e#rd"); outMsg.addNews("我的博客《智慧雲端日記》", "現在就加入 JFinal 極速開發世界,節省更多時間去跟女友游山玩水 ^_^", "https://mmbiz.qlogo.cn/mmbiz/ibHRiaZ9MRcUosol56OtHjVibWTK9opiaxsY9tPDricojmV5xxuLJyibZJXMAdNOx1qbZFcic9SvsPF2fTUnSc9oQB1IQ/0?wx_fmt=jpeg","http://mp.weixin.qq.com/s?__biz=MzA4MDA2OTA0Mg==&mid=208413033&idx=1&sn=06e816e1b2905c46c9a81df0ac0b3bad#rd"); render(outMsg); } // 音樂消息測試 else if ("music".equalsIgnoreCase(msgContent) || "音樂".equals(msgContent)) { OutMusicMsg outMsg = new OutMusicMsg(inTextMsg); outMsg.setTitle("When The Stars Go Blue-Venke Knutson"); outMsg.setDescription("建議在 WIFI 環境下流暢欣賞此音樂"); outMsg.setMusicUrl("http://www.jfinal.com/When_The_Stars_Go_Blue-Venke_Knutson.mp3"); outMsg.setHqMusicUrl("http://www.jfinal.com/When_The_Stars_Go_Blue-Venke_Knutson.mp3"); outMsg.setFuncFlag(true); render(outMsg); } else if ("美女".equalsIgnoreCase(msgContent)) { OutNewsMsg outMsg = new OutNewsMsg(inTextMsg); outMsg.addNews( "javenlife 寶貝更新嘍", "javenlife 寶貝更新嘍,我們只看美女 ^_^", "https://mmbiz.qlogo.cn/mmbiz/ibHRiaZ9MRcUosol56OtHjVibWTK9opiaxsYTQHXuRwoib8YobOfqCbykp3ZSaEk8czAqdkAARU0OdKDtv34F5evFIQ/0?wx_fmt=jpeg", "http://mp.weixin.qq.com/s?__biz=MzA4MDA2OTA0Mg==&mid=207820985&idx=1&sn=4ef9361e68495fc3ba1d2f7f2bca0511#rd"); render(outMsg); } // 其它文本消息直接返回原值 + 幫助提示 else { renderOutTextMsg("\t文本消息已成功接收,內容為: " + inTextMsg.getContent() + "\n\n" + helpStr); } } /** * 實現父類抽方法,處理圖片消息 */ protected void processInImageMsg(InImageMsg inImageMsg) { OutImageMsg outMsg = new OutImageMsg(inImageMsg); // 將剛發過來的圖片再發回去 outMsg.setMediaId(inImageMsg.getMediaId()); render(outMsg); } /** * 實現父類抽方法,處理語音消息 */ protected void processInVoiceMsg(InVoiceMsg inVoiceMsg) { OutVoiceMsg outMsg = new OutVoiceMsg(inVoiceMsg); // 將剛發過來的語音再發回去 outMsg.setMediaId(inVoiceMsg.getMediaId()); render(outMsg); } /** * 實現父類抽方法,處理視頻消息 */ protected void processInVideoMsg(InVideoMsg inVideoMsg) { /* 騰訊 api 有 bug,無法回復視頻消息,暫時回復文本消息代碼測試 OutVideoMsg outMsg = new OutVideoMsg(inVideoMsg); outMsg.setTitle("OutVideoMsg 發送"); outMsg.setDescription("剛剛發來的視頻再發回去"); // 將剛發過來的視頻再發回去,經測試證明是騰訊官方的 api 有 bug,待 api bug 卻除后再試 outMsg.setMediaId(inVideoMsg.getMediaId()); render(outMsg); */ OutTextMsg outMsg = new OutTextMsg(inVideoMsg); outMsg.setContent("\t視頻消息已成功接收,該視頻的 mediaId 為: " + inVideoMsg.getMediaId()); render(outMsg); } @Override protected void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg) { OutTextMsg outMsg = new OutTextMsg(inShortVideoMsg); outMsg.setContent("\t視頻消息已成功接收,該視頻的 mediaId 為: " + inShortVideoMsg.getMediaId()); render(outMsg); } /** * 實現父類抽方法,處理地址位置消息 */ protected void processInLocationMsg(InLocationMsg inLocationMsg) { OutTextMsg outMsg = new OutTextMsg(inLocationMsg); outMsg.setContent("已收到地理位置消息:" + "\nlocation_X = " + inLocationMsg.getLocation_X() + "\nlocation_Y = " + inLocationMsg.getLocation_Y() + "\nscale = " + inLocationMsg.getScale() + "\nlabel = " + inLocationMsg.getLabel()); render(outMsg); } /** * 實現父類抽方法,處理鏈接消息 * 特別注意:測試時需要發送我的收藏中的曾經收藏過的圖文消息,直接發送鏈接地址會當做文本消息來發送 */ protected void processInLinkMsg(InLinkMsg inLinkMsg) { OutNewsMsg outMsg = new OutNewsMsg(inLinkMsg); outMsg.addNews("鏈接消息已成功接收", "鏈接使用圖文消息的方式發回給你,還可以使用文本方式發回。點擊圖文消息可跳轉到鏈接地址頁面,是不是很好玩 :)" , "http://mmbiz.qpic.cn/mmbiz/zz3Q6WSrzq1ibBkhSA1BibMuMxLuHIvUfiaGsK7CC4kIzeh178IYSHbYQ5eg9tVxgEcbegAu22Qhwgl5IhZFWWXUw/0", inLinkMsg.getUrl()); render(outMsg); } @Override protected void processInCustomEvent(InCustomEvent inCustomEvent) { System.out.println("processInCustomEvent() 方法測試成功"); } /** * 實現父類抽方法,處理關注/取消關注消息 */ protected void processInFollowEvent(InFollowEvent inFollowEvent) { OutTextMsg outMsg = new OutTextMsg(inFollowEvent); outMsg.setContent("感謝關注 JFinal Weixin 極速開發服務號,為您節約更多時間,去陪戀人、家人和朋友 :) \n\n\n " + helpStr); // 如果為取消關注事件,將無法接收到傳回的信息 render(outMsg); } /** * 實現父類抽方法,處理掃描帶參數二維碼事件 */ protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent) { OutTextMsg outMsg = new OutTextMsg(inQrCodeEvent); outMsg.setContent("processInQrCodeEvent() 方法測試成功"); render(outMsg); } /** * 實現父類抽方法,處理上報地理位置事件 */ protected void processInLocationEvent(InLocationEvent inLocationEvent) { OutTextMsg outMsg = new OutTextMsg(inLocationEvent); outMsg.setContent("processInLocationEvent() 方法測試成功"); render(outMsg); } @Override protected void processInMassEvent(InMassEvent inMassEvent) { System.out.println("processInMassEvent() 方法測試成功"); } /** * 實現父類抽方法,處理自定義菜單事件 */ protected void processInMenuEvent(InMenuEvent inMenuEvent) { renderOutTextMsg("processInMenuEvent() 方法測試成功"); } /** * 實現父類抽方法,處理接收語音識別結果 */ protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults) { renderOutTextMsg("語音識別結果: " + inSpeechRecognitionResults.getRecognition()); } // 處理接收到的模板消息是否送達成功通知事件 protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent) { String status = inTemplateMsgEvent.getStatus(); renderOutTextMsg("模板消息是否接收成功:" + status); } }
3、WeixinApiController 微信接口處理
package com.jfinal.weixin.demo; import com.jfinal.kit.PropKit; import com.jfinal.weixin.sdk.api.*; import com.jfinal.weixin.sdk.jfinal.ApiController; public class WeixinApiController extends ApiController { /** * 如果要支持多公眾賬號,只需要在此返回各個公眾號對應的 ApiConfig 對象即可 * 可以通過在請求 url 中掛參數來動態從數據庫中獲取 ApiConfig 屬性值 */ public ApiConfig getApiConfig() { ApiConfig ac = new ApiConfig(); // 配置微信 API 相關常量 ac.setToken(PropKit.get("token")); ac.setAppId(PropKit.get("appId")); ac.setAppSecret(PropKit.get("appSecret")); /** * 是否對消息進行加密,對應於微信平台的消息加解密方式: * 1:true進行加密且必須配置 encodingAesKey * 2:false采用明文模式,同時也支持混合模式 */ ac.setEncryptMessage(PropKit.getBoolean("encryptMessage", false)); ac.setEncodingAesKey(PropKit.get("encodingAesKey", "setting it in config file")); return ac; } /** * 獲取公眾號菜單 */ public void getMenu() { ApiResult apiResult = MenuApi.getMenu(); if (apiResult.isSucceed()) renderText(apiResult.getJson()); else renderText(apiResult.getErrorMsg()); } /** * 創建菜單 */ public void createMenu() { String str = "{\n" + " \"button\": [\n" + " {\n" + " \"name\": \"進入理財\",\n" + " \"url\": \"http://m.bajie8.com/bajie/enter\",\n" + " \"type\": \"view\"\n" + " },\n" + " {\n" + " \"name\": \"安全保障\",\n" + " \"key\": \"112\",\n" + "\t \"type\": \"click\"\n" + " },\n" + " {\n" + "\t \"name\": \"使用幫助\",\n" + "\t \"url\": \"http://m.bajie8.com/footer/cjwt\",\n" + "\t \"type\": \"view\"\n" + " }\n" + " ]\n" + "}"; ApiResult apiResult = MenuApi.createMenu(str); if (apiResult.isSucceed()) renderText(apiResult.getJson()); else renderText(apiResult.getErrorMsg()); } /** * 獲取公眾號關注用戶 */ public void getFollowers() { ApiResult apiResult = UserApi.getFollows(); renderText(apiResult.getJson()); } /** * 獲取用戶信息 */ public void getUserInfo() { ApiResult apiResult = UserApi.getUserInfo("ohbweuNYB_heu_buiBWZtwgi4xzU"); renderText(apiResult.getJson()); } /** * 發送模板消息 */ public void sendMsg() { String str = " {\n" + " \"touser\":\"ohbweuNYB_heu_buiBWZtwgi4xzU\",\n" + " \"template_id\":\"9SIa8ph1403NEM3qk3z9-go-p4kBMeh-HGepQZVdA7w\",\n" + " \"url\":\"http://www.sina.com\",\n" + " \"topcolor\":\"#FF0000\",\n" + " \"data\":{\n" + " \"first\": {\n" + " \"value\":\"恭喜你購買成功!\",\n" + " \"color\":\"#173177\"\n" + " },\n" + " \"keyword1\":{\n" + " \"value\":\"去哪兒網發的酒店紅包(1個)\",\n" + " \"color\":\"#173177\"\n" + " },\n" + " \"keyword2\":{\n" + " \"value\":\"1元\",\n" + " \"color\":\"#173177\"\n" + " },\n" + " \"remark\":{\n" + " \"value\":\"歡迎再次購買!\",\n" + " \"color\":\"#173177\"\n" + " }\n" + " }\n" + " }"; ApiResult apiResult = TemplateMsgApi.send(str); renderText(apiResult.getJson()); } /** * 獲取參數二維碼 */ public void getQrcode() { String str = "{\"expire_seconds\": 604800, \"action_name\": \"QR_SCENE\", \"action_info\": {\"scene\": {\"scene_id\": 123}}}"; ApiResult apiResult = QrcodeApi.create(str); renderText(apiResult.getJson()); // String str = "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"123\"}}}"; // ApiResult apiResult = QrcodeApi.create(str); // renderText(apiResult.getJson()); } /** * 長鏈接轉成短鏈接 */ public void getShorturl() { String str = "{\"action\":\"long2short\"," + "\"long_url\":\"http://wap.koudaitong.com/v2/showcase/goods?alias=128wi9shh&spm=h56083&redirect_count=1\"}"; ApiResult apiResult = ShorturlApi.getShorturl(str); renderText(apiResult.getJson()); } /** * 獲取客服聊天記錄 */ public void getRecord() { String str = "{\n" + " \"endtime\" : 987654321,\n" + " \"pageindex\" : 1,\n" + " \"pagesize\" : 10,\n" + " \"starttime\" : 123456789\n" + " }"; ApiResult apiResult = CustomServiceApi.getRecord(str); renderText(apiResult.getJson()); } /** * 獲取微信服務器IP地址 */ public void getCallbackIp() { ApiResult apiResult = CallbackIpApi.getCallbackIp(); renderText(apiResult.getJson()); } }
通過調用 MenuApi、UserApi 等 Api 的相關方法即可獲取封裝成 ApiResult 對象的結果,使用 render 系列方法即可快捷輸出結果
四:運行項目測試
1、修改配置文件如圖
例如:我的微信測試號
2、運行項目
在瀏覽器中輸入:http://localhost/msg 測試 說明項目啟動成功 歐耶........
3、微信開發模式接口配置信息
本地測試需要做外網的80端口映射
如果出現上圖就配置成功的提示 到此就可以敬請的玩耍了....................