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

本篇参考微信官方文档: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,每个人不同,按需修改。

@RestResource(urlMapping=‘/WeChatRest/*‘)
global without sharing class WeChatRestController
{
    @HttpGet
    global static void validateSignature() {
        //获取微信端传递的参数
        String signature = RestContext.request.params.get(‘signature‘); // 微信加密签名
        String timestamp = RestContext.request.params.get(‘timestamp‘); // 微信请求URL时传过来的timestamp值
        String nonce = RestContext.request.params.get(‘nonce‘); // 随机数-->微信请求URL时传过来的nonce值
        String echostr = RestContext.request.params.get(‘echostr‘); // 随机字符串
        // 转换规则详情:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
        //1. 字典排序
        String myToken = ‘zhangyq‘;
        List<String> paramList = new List<String>{myToken,timestamp,nonce};
        paramList.sort();
        String content = ‘‘;
        for(String param : paramList) {
            content += param;
        }
        // 2. sha1算法转换
        Blob hash = Crypto.generateDigest(‘SHA1‘, Blob.valueOf(content));
        String hexString= EncodingUtil.convertToHex(hash);
        //3. 比对转换后的值是否和传递的echostr相同,相同证明认证通过
        Boolean isValid = hexString != null ? signature.equalsIgnoreCase(hexString) : false;

        if(isValid) {
            RestContext.response.addHeader(‘Content-Type‘, ‘text/plain‘);
            RestContext.response.responseBody = Blob.valueOf(echostr);
        }
    }
}

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

public with sharing class RequestMessage {
    public String toUserName;
    public String fromUserName;
    public String msgType;
    public String content;

    public RequestMessage(String toUserName,String fromUserName,String msgType,String content) {
        this.toUserName = toUserName;
        this.fromUserName = fromUserName;
        this.msgType = msgType;
        this.content = content;
    }
}

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

public with sharing class ResponseMessage {
    public String title;
    public String url;

    public ResponseMessage(String title,String url) {
      this.title = title;
      this.url = url;
    }
}

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

@RestResource(urlMapping=‘/WeChatRest/*‘)
global without sharing class WeChatRestController
{
    @HttpGet
    global static void validateSignature() {
        //获取微信端传递的参数
        String signature = RestContext.request.params.get(‘signature‘); // 微信加密签名
        String timestamp = RestContext.request.params.get(‘timestamp‘); // 微信请求URL时传过来的timestamp值
        String nonce = RestContext.request.params.get(‘nonce‘); // 随机数-->微信请求URL时传过来的nonce值
        String echostr = RestContext.request.params.get(‘echostr‘); // 随机字符串
        // 转换规则详情:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
        //1. 字典排序
        String myToken = ‘zhangyq‘;
        List<String> paramList = new List<String>{myToken,timestamp,nonce};
        paramList.sort();
        String content = ‘‘;
        for(String param : paramList) {
            content += param;
        }
        // 2. sha1算法转换
        Blob hash = Crypto.generateDigest(‘SHA1‘, Blob.valueOf(content));
        String hexString= EncodingUtil.convertToHex(hash);
        //3. 比对转换后的值是否和传递的echostr相同,相同证明认证通过
        Boolean isValid = hexString != null ? signature.equalsIgnoreCase(hexString) : false;

        if(isValid) {
            RestContext.response.addHeader(‘Content-Type‘, ‘text/plain‘);
            RestContext.response.responseBody = Blob.valueOf(echostr);
        }
    }

    @HttpPost
  global static void doPost() {

        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        string strMsg = req.requestBody.toString();
        system.debug(‘*** message from wechat : ‘ + strMsg);
        XmlStreamReader reader = new XmlStreamReader(strMsg);
        String toUserName = ‘‘;
        String fromUserName = ‘‘;
        String msgType = ‘‘;
        String content = ‘‘;

        //解析微信传递过来的XML,将主要的内容的值取出来并进行操作
        while(reader.hasNext()) {
            if(reader.getLocalName() == ‘ToUserName‘) {
                reader.next();
                if(String.isNotBlank(reader.getText())) {
                    toUserName = reader.getText();
                }
            } else if(reader.getLocalName() == ‘FromUserName‘) {
                reader.next();
                if(String.isNotBlank(reader.getText())) {
                    fromUserName = reader.getText();
                }
            } else if(reader.getLocalName() == ‘MsgType‘) {
                reader.next();
                if(String.isNotBlank(reader.getText())) {
                    msgType = reader.getText();
                }
            } else if(reader.getLocalName() == ‘Content‘) {
                reader.next();
                if(String.isNotBlank(reader.getText())) {
                    content = reader.getText();
                }
            }

            reader.next();
        }

        //封装到request bean中用于获取传递过来的关键字的值
        RequestMessage receiveMsg = new RequestMessage(toUserName,fromUserName,msgType,content);
        //返回到微信的XML格式类型的字符串
        String resultXML;

        //根据输入类型进行处理,目前公众号只支持文本类型
        if(msgType.equals(‘text‘)){
           resultXML = buildResponseXMLByContent(receiveMsg);
        } else {
            resultXML = buildResponseXML(receiveMsg,null);
        }
        RestContext.response.addHeader(‘Content-Type‘, ‘text/plain‘);
        RestContext.response.responseBody = Blob.valueOf(resultXML);
    }

    private static String buildResponseXMLByContent(RequestMessage message) {
        //用于作为XML拼装的返回结果
        String buildXMLString;

        //通过SOSL根据关键字进行搜索,最多返回5条
        String keyword = ‘\‘‘ + message.content + ‘\‘‘;
        String soslString = ‘FIND‘ + keyword + ‘IN ALL FIELDS ‘
                                + ‘ RETURNING ‘
                                + ‘ My_Blog__c(Title__c,Blog_URL__c,Picture_URL__c,Description__c) LIMIT 5‘;

        List<List<SObject>> soslResultList = search.query(soslString);
        //对搜索出来的结果集进行封装,然后加工处理XML作为微信返回内容
        List<ResponseMessage> responseMessageList = new List<ResponseMessage>();

        List<My_Blog__c> myBlogList = new List<My_Blog__c>();
        if(soslResultList.size() > 0) {
            myBlogList = (List<My_Blog__c>)soslResultList.get(0);
        }

        for(My_Blog__c myBlog : myBlogList) {
            ResponseMessage messageItem = new ResponseMessage(myBlog.Title__c,myBlog.Blog_URL__c);
            responseMessageList.add(messageItem);
        }
        buildXMLString = buildResponseXML(message, responseMessageList);
        System.debug(LoggingLevel.INFO, ‘*** buildXMLString: ‘ + buildXMLString);
        return buildXMLString;
    }

    private static String buildResponseXML(RequestMessage message,List<ResponseMessage> responseMessageList) {

        String newsTpl = ‘<item><Title><![CDATA[{0}]]></Title><Description><![CDATA[{1}]]></Description><PicUrl><![CDATA[{2}]]></PicUrl><Url><![CDATA[{3}]]></Url></item>‘;

        String currentDateTime = System.now().format(‘YYYY-MM-dd HH:mm:ss‘);

        //根据微信公众号规则拼装XML模板
        String responseMessageTemplate = ‘<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>‘ + currentDateTime + ‘</CreateTime><MsgType><![CDATA[text]]></MsgType>‘ + ‘<Content><![CDATA[{2}]]></Content>‘ +‘</xml>‘;
        //XML模板中对应的Placeholder的值
        String[] arguments;
        //非文本输入提示
        if(!message.msgType.equalsIgnoreCase(‘text‘)) {
            arguments = new String[]{message.fromUserName, message.toUserName, ‘该公众号目前支支持文字输入‘};
        } else {
            //没有搜索出记录提示
            if(responseMessageList.isEmpty()) {
                arguments = new String[]{message.fromUserName, message.toUserName, ‘没有匹配的数据,请重新尝试其他的关键字‘};
            } else {
                String messageStringBuffer = ‘‘;
                for(ResponseMessage responseItem : responseMessageList) {
                    messageStringBuffer += ‘<a href="‘ + responseItem.url + ‘">‘ + responseItem.title + ‘"></a>\n‘;
                }
                messageStringBuffer = messageStringBuffer.removeEnd(‘\n‘);
                arguments = new String[]{message.fromUserName, message.toUserName, messageStringBuffer};
            }
        }
        String results = String.format(responseMessageTemplate, arguments);
        return results;
    }
}

效果展示:

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

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

原文地址:https://www.cnblogs.com/zero-zyq/p/12382156.html

时间: 2024-10-06 21:29:16

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

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

一点说明: nodejs 微信api 扩展,集成大部分功能. 案例 https://github.com/leiroc/node-wxeasy-example 上传example中文件到服务器 ,然后 npm install 成功 https://github.com/leiroc/node-wxeasy-example BUG and NEWS 增加客户功能 增加模板消息 增加扫描带参数二维码 增加 access_token_apiurl 参数,从接口服务器获取 access_token 避免

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

微信公众平台的开发者文档https://www.w3cschool.cn/weixinkaifawendang/ python,flask,SAE(新浪云),搭建开发微信公众账号http://www.oschina.net/code/snippet_1768500_36580 从零开始 Python 微信公众号开发https://zhuanlan.zhihu.com/p/21354943 新浪云应用http://www.sinacloud.com/doc/sae/python/ SAE Pyth

如何避免自己写的代码成为别人眼中的一坨屎 (摘自微信公众号,顶级程序员)

从微信公众号上读到一篇文章,记录下来提醒自己也分享给大家~ 一.注释 不要给不好的名字加注释,一个好的名字比好的注释更重要: 不要"拐杖注释",好代码 > 坏代码 + 好注释: 在文件/类级别使用全局注释来解释所有部分如何工作: 一定要给常量加注释: 团队统一定义标记: TODO  待处理的问题: FIXME  已知有问题的代码: HACK 不得不采用的粗糙的解决方案: 在注释中用精心挑选的输入输出例子进行说明: 注释应该声明代码的高层次意图,而非明显的细节: 不要在代码中加入代

制作了一个微信公众号导航的网站 http://www.wxgzh.in

微信公众号导航 http://www.wxgzh.in 是一个收集微信公众号的网站,手机屏幕看文章的体验并不好,如果能移到pc屏幕上就会舒服很多,而且关注的公众号文章一目了然,比起在微信客户端里一个一个打开看体验会好很多,看到喜欢的图片或者文章还能方便地保存. 现有的所有公众号都是从网上爬下来的,共计一万二千多个,下一步会开放自行注册添加的功能,然后应该学习传送门之类的,可以把每个公众号最新的文章也爬下来,但是这些文章如何存储呢?是只存标题.摘要.发表时间等基本信息还是全文保存?如果全文保存的话

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

1.概述 通过前面8篇关于微信开发相关文章的学习,我们已经对微信常用开发有了一个比较深入的了解.前面的文章都是基于某一特定公众号的,在现实业务中同一单位个体运营着不至一个公众号,此时就需要对多个公众号集中管理,随意切换.本篇文章主要介绍多公众号集中管理的方法.表设计.设置默认公众号.生成指定格式的URL资源服务器.刷新Access_Token等. 2.公众号集中管理的方法 我们知道操作微信公众号时微信服务器都会返回相应的信息到我们自己的中转服务器上,涉及多个微信公众号管理时,我们就需要知道是那个

报表与微信公众号(企业号)集成方案

概述: 有时候我们希望把想查询的数据和微信企业号平台集成,或者我们希望能在微信端,将微信中的账户与对应数据集中的字段关联,并通过访问报表获取要查询的字段信息,就好比各个营业厅查询个人当前[套餐余量].又或者,我们希望能够更加方便的编辑自动回复.如果将报表与公众号集成这些需求都可以实现.下面我就来介绍一下该如何集成. 准备工作:微信公众号 (这里申请的是一个月的企业号体验版) 个人微信 报表工具:Finereport 步骤: 1. 配置微信公众号 通讯录( 只有通讯录内的成员才可以关注企业号 ):

Python微信公众号后台开发&lt;005&gt;:集成智能聊天机器人?

?给公众号集成一个智能聊天机器人 一.前述 ChatterBot是一个基于机器学习的聊天机器人引擎,构建在python上,主要特点是可以自可以从已有的对话中进行学(jiyi)习(pipei). 二.具体 1.安装 是的,安装超级简单,用pip就可以啦 pip install chatterbot 2.流程 大家已经知道chatterbot的聊天逻辑和输入输出以及存储,是由各种adapter来限定的,我们先看看流程图,一会再一起看点例子,看看怎么用. 3.每个部分都设计了不同的“适配器”(Adap

微信公众号项目笔记 二

一.项目介绍  通过公众号页面调用系统接口,完成物流系统客户签收功能. 1界面展示使用html5+css布局,使用weui样式库. 2Web项目使用asp.net MVC4技术.接口调用使用jquery库Ajax异步调用. 3 服务器环境server2008系统,Web容器IIS6 运行环境.net4.0 集成模式 二.用到知识点 1.  用户权限管理,首次登录保存用户token(openId)客户唯一标识符,微信公众号平台获取关注公众号的用户返回给系统调用,系统通过与工作号绑定的webAPI接

WPF 实现微信公众号多客服(效果实现篇)

简介: 这是利用WPF作为前端技术,实现桌面版微信多客服系统.项目采用Prism作为前端框架,采用MVVM模式极好的对UI和逻辑代码分离,使用MefBootstrapper集成的MEF IOC容器,解耦各模块对象.合理利用 IEventAggregator 实现事件和交互.文章在介绍对应功能时候会给出相关实现的参考,读者可以参考改进,引入到自己的项目中. 程序运行界面及功能预览: 一.登陆: 功能:支持记住用户和用户设置,可选择记住用户密码. 实现相关: 自定义登陆窗口,引入Microsoft.