概述:
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端口映射
如果出现上图就配置成功的提示 到此就可以敬请的玩耍了....................