本篇参考微信官方文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html

随着salesforce学习文章越来越多,查找文章也变得越来越不方便。去年有个关注的粉丝私下微信聊天,问是否可以在微信公众号做一个搜索功能,通过关键字返回匹配的文章,这样可以减少了一直拖拽耽误的时间和精力。去年一直懒惰没有实现,其实也是没有接触过微信公众号集成,所以简单的推脱了,说后续会搞定这个功能。今年因为疫情憋在家里正好有机会去进行学习,顺便就简单的学了一下公众号集成以及相关的简单开发,然后将这个功能实现。此功能实现主要通过两个大步骤。

一. 启用微信公众号服务器配置

根据官方文档的描述,接入微信公众平台开发,开发者需要按照如下步骤完成:

  • 填写服务器配置
  • 验证服务器地址的有效性
  • 依据接口文档实现业务逻辑

我们需要先搞定前两步,微信在验证服务器地址的有效性时,会发送几个parameter,然后按照字典化排序以及SHA1加密来判断signature比较,因为我们可以使用oauth认证或者不认证方式,这里我们通过salesforce site方式,这样可以忽略了认证,通过restful接口去接受微信服务器发送过来的验证消息,从而最简单化集成微信。

1. restful接口来接收微信服务器传参以及验证:验证的原理时根据传递的几个参数字典排序然后SHA1加密,然后将结果和微信传过来的signature比对是否相同,相同代表验证通过,并且将标识传递回微信即可。代码部分如下,其中myToken部分为微信公众号要求验证的token,每个人不同,按需修改。

  1. @RestResource(urlMapping='/WeChatRest/*')
  2. global without sharing class WeChatRestController
  3. {
  4. @HttpGet
  5. global static void validateSignature() {
  6. //获取微信端传递的参数
  7. String signature = RestContext.request.params.get('signature'); // 微信加密签名
  8. String timestamp = RestContext.request.params.get('timestamp'); // 微信请求URL时传过来的timestamp值
  9. String nonce = RestContext.request.params.get('nonce'); // 随机数-->微信请求URL时传过来的nonce值
  10. String echostr = RestContext.request.params.get('echostr'); // 随机字符串
  11. // 转换规则详情:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
  12. //1. 字典排序
  13. String myToken = 'zhangyq';
  14. List<String> paramList = new List<String>{myToken,timestamp,nonce};
  15. paramList.sort();
  16. String content = '';
  17. for(String param : paramList) {
  18. content += param;
  19. }
  20. // 2. sha1算法转换
  21. Blob hash = Crypto.generateDigest('SHA1', Blob.valueOf(content));
  22. String hexString= EncodingUtil.convertToHex(hash);
  23. //3. 比对转换后的值是否和传递的echostr相同,相同证明认证通过
  24. Boolean isValid = hexString != null ? signature.equalsIgnoreCase(hexString) : false;
  25.  
  26. if(isValid) {
  27. RestContext.response.addHeader('Content-Type', 'text/plain');
  28. RestContext.response.responseBody = Blob.valueOf(echostr);
  29. }
  30. }
  31. }

2. 创建site,在Set Up处搜索sites以后新建一个site,最后别忘记active。

记住划线的URL,后期需要使用这个配置到微信端。

save以后点击来这个site,点击Public Access Settings,然后Apex Class Access,在Enabled Apex class将WeChatRestController选中。

3. 配置微信端:在公众号的下方有一处是开发-》基本配置,点击此项以后有开发者ID,开发者密码,IP白名单。启用开发者馍是,然后记住开发者密码,这个密码只会出现一次,后期就只能重置,类似salesforce的 reset security token的效果,所以务必记住。在白名单处我们可以配置一些白名单,比如我们可以将上述的URL找到其对应的IP地址,然后配置在白名单中,想要找到域名对应的IP可以访问:http://ip.tool.chinaz.com/,这里搜索使用site的配置的链接,改成https://用来查询。

打开开发者模式以后就可以配置服务器信息,通过下图可以看到,URL配置的是site对应的URL后面拼接的是rest的访问地址,token为我们在代码中写的值,点击提交如果没有报错则配置成功。

二. 解析微信传值并回传给微信

https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html 这个是官方文档关于消息机制的阐明。当配置完服务器以后,用户在公众号里面输入的内容,微信不再做解析和处理,将消息通过post方式传递到配置的服务器URL,所以我们想要解析和处理,需要在刚才的类中添加一个@HttpPost方法来接收和处理数据。

本来想做的效果类似下面的展示,结果开发完以后测试以后只能返回一条图文,查看文档以后才知道一个文字类型的消息只能返回一个图文消息,所以大家开发以前一定要详细读文档,避免走弯路。

微信发送过来以及后期需要接受的数据格式是XML类型,意味着我们在开发时,对数据解析和处理都需要有一定的XML的解析基础,不知道XML如何解析的,请访问此篇博客:https://www.cnblogs.com/zero-zyq/p/5601158.html, 发送和接受类型请自行查看上面提供的链接,这里不做处理。直接上代码:

RequestMessage:用于封装微信传递过来的信息,微信根据不同的类型会有不同的参数传递,这里只封装我们用到的文本类型内容的变量进行封装。

  1. public with sharing class RequestMessage {
  2. public String toUserName;
  3. public String fromUserName;
  4. public String msgType;
  5. public String content;
  6.  
  7. public RequestMessage(String toUserName,String fromUserName,String msgType,String content) {
  8. this.toUserName = toUserName;
  9. this.fromUserName = fromUserName;
  10. this.msgType = msgType;
  11. this.content = content;
  12. }
  13. }

ResponseMessage:因为response部分需要返回多条,无法选择图文方式,所以这里使用文本链接方式发送回到微信,所以我们只需要封装title以及URL即可。

  1. public with sharing class ResponseMessage {
  2. public String title;
  3. public String url;
  4.  
  5. public ResponseMessage(String title,String url) {
  6. this.title = title;
  7. this.url = url;
  8. }
  9. }

最后完整的WeChatRestController代码如下,对post内容解析,使用SOSL搜索我们自定义的存储数据的My_Blog__c,然后对结果进行封装后扔回给微信,目前只支持文本方式,其他类型会有提示。目前最多只返回5条数据。

  1. @RestResource(urlMapping='/WeChatRest/*')
  2. global without sharing class WeChatRestController
  3. {
  4. @HttpGet
  5. global static void validateSignature() {
  6. //获取微信端传递的参数
  7. String signature = RestContext.request.params.get('signature'); // 微信加密签名
  8. String timestamp = RestContext.request.params.get('timestamp'); // 微信请求URL时传过来的timestamp值
  9. String nonce = RestContext.request.params.get('nonce'); // 随机数-->微信请求URL时传过来的nonce值
  10. String echostr = RestContext.request.params.get('echostr'); // 随机字符串
  11. // 转换规则详情:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
  12. //1. 字典排序
  13. String myToken = 'zhangyq';
  14. List<String> paramList = new List<String>{myToken,timestamp,nonce};
  15. paramList.sort();
  16. String content = '';
  17. for(String param : paramList) {
  18. content += param;
  19. }
  20. // 2. sha1算法转换
  21. Blob hash = Crypto.generateDigest('SHA1', Blob.valueOf(content));
  22. String hexString= EncodingUtil.convertToHex(hash);
  23. //3. 比对转换后的值是否和传递的echostr相同,相同证明认证通过
  24. Boolean isValid = hexString != null ? signature.equalsIgnoreCase(hexString) : false;
  25.  
  26. if(isValid) {
  27. RestContext.response.addHeader('Content-Type', 'text/plain');
  28. RestContext.response.responseBody = Blob.valueOf(echostr);
  29. }
  30. }
  31.  
  32. @HttpPost
  33. global static void doPost() {
  34.  
  35. RestRequest req = RestContext.request;
  36. RestResponse res = RestContext.response;
  37.  
  38. string strMsg = req.requestBody.toString();
  39. system.debug('*** message from wechat : ' + strMsg);
  40. XmlStreamReader reader = new XmlStreamReader(strMsg);
  41. String toUserName = '';
  42. String fromUserName = '';
  43. String msgType = '';
  44. String content = '';
  45.  
  46. //解析微信传递过来的XML,将主要的内容的值取出来并进行操作
  47. while(reader.hasNext()) {
  48. if(reader.getLocalName() == 'ToUserName') {
  49. reader.next();
  50. if(String.isNotBlank(reader.getText())) {
  51. toUserName = reader.getText();
  52. }
  53. } else if(reader.getLocalName() == 'FromUserName') {
  54. reader.next();
  55. if(String.isNotBlank(reader.getText())) {
  56. fromUserName = reader.getText();
  57. }
  58. } else if(reader.getLocalName() == 'MsgType') {
  59. reader.next();
  60. if(String.isNotBlank(reader.getText())) {
  61. msgType = reader.getText();
  62. }
  63. } else if(reader.getLocalName() == 'Content') {
  64. reader.next();
  65. if(String.isNotBlank(reader.getText())) {
  66. content = reader.getText();
  67. }
  68. }
  69.  
  70. reader.next();
  71. }
  72.  
  73. //封装到request bean中用于获取传递过来的关键字的值
  74. RequestMessage receiveMsg = new RequestMessage(toUserName,fromUserName,msgType,content);
  75. //返回到微信的XML格式类型的字符串
  76. String resultXML;
  77.  
  78. //根据输入类型进行处理,目前公众号只支持文本类型
  79. if(msgType.equals('text')){
  80. resultXML = buildResponseXMLByContent(receiveMsg);
  81. } else {
  82. resultXML = buildResponseXML(receiveMsg,null);
  83. }
  84. RestContext.response.addHeader('Content-Type', 'text/plain');
  85. RestContext.response.responseBody = Blob.valueOf(resultXML);
  86. }
  87.  
  88. private static String buildResponseXMLByContent(RequestMessage message) {
  89. //用于作为XML拼装的返回结果
  90. String buildXMLString;
  91.  
  92. //通过SOSL根据关键字进行搜索,最多返回5条
  93. String keyword = '\'' + message.content + '\'';
  94. String soslString = 'FIND' + keyword + 'IN ALL FIELDS '
  95. + ' RETURNING '
  96. + ' My_Blog__c(Title__c,Blog_URL__c,Picture_URL__c,Description__c) LIMIT 5';
  97.  
  98. List<List<SObject>> soslResultList = search.query(soslString);
  99. //对搜索出来的结果集进行封装,然后加工处理XML作为微信返回内容
  100. List<ResponseMessage> responseMessageList = new List<ResponseMessage>();
  101.  
  102. List<My_Blog__c> myBlogList = new List<My_Blog__c>();
  103. if(soslResultList.size() > 0) {
  104. myBlogList = (List<My_Blog__c>)soslResultList.get(0);
  105. }
  106.  
  107. for(My_Blog__c myBlog : myBlogList) {
  108. ResponseMessage messageItem = new ResponseMessage(myBlog.Title__c,myBlog.Blog_URL__c);
  109. responseMessageList.add(messageItem);
  110. }
  111. buildXMLString = buildResponseXML(message, responseMessageList);
  112. System.debug(LoggingLevel.INFO, '*** buildXMLString: ' + buildXMLString);
  113. return buildXMLString;
  114. }
  115.  
  116. private static String buildResponseXML(RequestMessage message,List<ResponseMessage> responseMessageList) {
  117.  
  118. String newsTpl = '<item><Title><![CDATA[{0}]]></Title><Description><![CDATA[{1}]]></Description><PicUrl><![CDATA[{2}]]></PicUrl><Url><![CDATA[{3}]]></Url></item>';
  119.  
  120. String currentDateTime = System.now().format('YYYY-MM-dd HH:mm:ss');
  121.  
  122. //根据微信公众号规则拼装XML模板
  123. String responseMessageTemplate = '<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>' + currentDateTime + '</CreateTime><MsgType><![CDATA[text]]></MsgType>' + '<Content><![CDATA[{2}]]></Content>' +'</xml>';
  124. //XML模板中对应的Placeholder的值
  125. String[] arguments;
  126. //非文本输入提示
  127. if(!message.msgType.equalsIgnoreCase('text')) {
  128. arguments = new String[]{message.fromUserName, message.toUserName, '该公众号目前支支持文字输入'};
  129. } else {
  130. //没有搜索出记录提示
  131. if(responseMessageList.isEmpty()) {
  132. arguments = new String[]{message.fromUserName, message.toUserName, '没有匹配的数据,请重新尝试其他的关键字'};
  133. } else {
  134. String messageStringBuffer = '';
  135. for(ResponseMessage responseItem : responseMessageList) {
  136. messageStringBuffer += '<a href="' + responseItem.url + '">' + responseItem.title + '"></a>\n';
  137. }
  138. messageStringBuffer = messageStringBuffer.removeEnd('\n');
  139. arguments = new String[]{message.fromUserName, message.toUserName, messageStringBuffer};
  140. }
  141. }
  142. String results = String.format(responseMessageTemplate, arguments);
  143. return results;
  144. }
  145. }

效果展示:

当使用非文本类型在公众号进行搜索,比如语音,会提示“该公众号目前只支持文字输入”;当输入可以查询到的内容,会以文字的方式返回,点击链接即可进入对应的文章;如果输入的内容在数据库中查询不到,则返回“没有匹配的数据”。

总结:篇中只是以简单的方式对代码进行开发实现微信和salesforce的集成。篇中有错误的地方欢迎指出,有不懂的欢迎留言。

Salesforce与微信公众号集成实现输入关键字搜索文章的更多相关文章

  1. nodejs微信公众号快速开发|自定义关键字回复

    一点说明: nodejs 微信api 扩展,集成大部分功能. 案例 https://github.com/leiroc/node-wxeasy-example 上传example中文件到服务器 ,然后 ...

  2. 微信公众号发送客服消息提示errcode":45015,"errmsg":"response out of time limit or subscription is canceled hint:解决办法【已解决】

    微信公众号发送客服消息提示errcode":45015,"errmsg":"response out of time limit or subscription ...

  3. 在新浪SAE上搭建微信公众号的python应用

    微信公众平台的开发者文档https://www.w3cschool.cn/weixinkaifawendang/ python,flask,SAE(新浪云),搭建开发微信公众账号http://www. ...

  4. 微信公众号开发C#系列-9、多公众号集中管理

    1.概述 通过前面8篇关于微信开发相关文章的学习,我们已经对微信常用开发有了一个比较深入的了解.前面的文章都是基于某一特定公众号的,在现实业务中同一单位个体运营着不至一个公众号,此时就需要对多个公众号 ...

  5. 微信公众号开发《三》微信JS-SDK之地理位置的获取,集成百度地图实现在线地图搜索

    本次讲解微信开发第三篇:获取用户地址位置信息,是非常常用的功能,特别是服务行业公众号,尤为需要该功能,本次讲解的就是如何调用微信JS-SDK接口,获取用户位置信息,并结合百度地铁,实现在线地图搜索,与 ...

  6. weixin-java-mp集成微信公众号自带客服功能

    电脑端登录公众号管理后台,[添加功能插件]开通客服功能,输入"人工客服"接入客服热线 底部有我的微信二维码,如有问题,可加好友进行技术交流! ​ ​ ​ ​ ​ ​ ​ weixi ...

  7. 微信公众号开发《三》微信JS-SDK之地理位置的获取与在线导航,集成百度地图实现在线地图搜索

    本次讲解微信开发第三篇:获取用户地址位置信息,是非常常用的功能,特别是服务行业公众号,尤为需要该功能,本次讲解的就是如何调用微信JS-SDK接口,获取用户位置信息,并结合百度地铁,实现在线地图搜索,与 ...

  8. 基于APPIUM测试微信公众号的UI自动化测试框架(结合Allure2测试报告框架)

    框架初衷 前两周组内的小伙伴跟我说她现在测试的微信公众号项目(保险)每次上新产品时测试起来很费时,存在大量的重复操作(点点点),手工测试每个产品可能需要半天到一天的时间,复杂的产品需要两天. 由于保险 ...

  9. 微信公众号开发之VS远程调试

    目录 (一)微信公众号开发之VS远程调试 (二)微信公众号开发之基础梳理 (三)微信公众号开发之自动消息回复和自定义菜单 前言 微信公众平台消息接口的工作原理大概可以这样理解:从用户端到公众号端一个流 ...

随机推荐

  1. Android开发学习1----AndroidStudio的安装、创建第一个Android Studio文件、Android Studio界面介绍和HelloWord!

    移动开发的工具有很多:Android Studio,eclipse,Hbuilder等,其中,现如今最火的开发工具是Android Studio,Android Studio是谷歌自己推出的一款集成开 ...

  2. JdbcRDD连接MySQL

    (1)添加依赖 <dependencies> <dependency> <groupId>org.apache.spark</groupId> < ...

  3. spring+mybatis配置多个数据源

    http://www.cnblogs.com/lzrabbit/p/3750803.html

  4. poj-3657 Haybale Guessing(二分答案+并查集)

    http://poj.org/problem?id=3657 下方有中文版,不想看英文的可直接点这里看中文版题目 Description The cows, who always have an in ...

  5. 1)PHP,数据库操作类网址

    (1)脚本之家 http://www.jb51.net/article/94347.htm (2)一个博客 http://www.cnblogs.com/lvchenfeng/p/5003629.ht ...

  6. php通过身份证判断性别

    /** 已测试,百度很多写法不行的 * 1就是男性 2就是女性* 通过身份证获取性别类型* @param type $card* @return int*/function getCardSex($i ...

  7. MySQL数据库简单操作

    title date tags layout MySQL简单操作 2018-07-16 Linux post 登录mysql mysql -h 主机名 -u 用户名 -p 查看所有数据库 show d ...

  8. android适配全机型悬浮框、视频APP项目、手势操作、Kotlin妹子App、相机图片处理等源码

    Android精选源码 图片滤镜处理,相机滤镜实时处理,相机拍照录制 android仿爱壁纸App更换壁纸效果源码 基于Kotlin+MVP+Retrofit+RxJava+Glide 等架构实现短视 ...

  9. jmeter测试get post 笔记

    0 环境 系统环境:win7 1 操作 1 post 新建线程组 2 get 和post新建类似 http请求 只是新建一个参数化我测试的2个url http://127.0.0.1:8080/cry ...

  10. 2015-09-15-git配置

    https://help.github.com/articles/set-up-git/ git上传是忽略一些文件 在每个git的项目中有一个.gitignore文件,将忽略的文件或文件夹输入即可. ...