草庐IT

实现微信小程序定时发送通知(1)发送请求篇

For&Get 2023-04-05 原文

 引言 关于access_token

小程序发通知需要获取获取小程序全局唯一后台接口调用凭据(access_token)。调用绝大多数后台接口时都需使用 access_token,后面会进行使用redis的存取。

但是急于求成加上先去看了小马如何获取openId,打算使用前端发过来的code获取access_token,这确实是一个获取access_token的方法,照着这个去查找找到了微信小程序官网文档,按照里面的接口介绍,通过前端获取的code实现了获取。在此之间我并不知道access_token是会过期的,后面又了解到前端不能发起定时任务,在微信小程序官方文档中又看到了用AppID和AppSecret获取access_token

官方链接如下:auth.getAccessToken | 微信开放文档

于是我们可以在自己定义的定时任务中实现在需要发送消息通知之前的10秒内(时间上可以接近一点)获取,同时,除了可以用定时任务来自动更新access_token,access_token的有效期为2h,也需要弄一个报错之后可以调用的主动方法获取最新的access_token并存进redis中。

发送通知可参考参考官网文档:templateMessage.send | 微信开放文档

于是我们发现,小程序发送消息通知需要很多的组成,比如OpenId,access_token,template_id(模板id)等,于是我们可以将其封装为一个专门的类。

@Data
public class WxMssVo {
    //小程序用户的OpenId
    private String touser;
    //模板id
    private String template_id;
    //跳转首页
    private String page;
    //接口凭证
    private String access_token;
    //请求路径
    private String request_url;
    //放置数据
    private HashMap<String, Object> map = new HashMap<>(3);

}

用小程序的AppID和AppSecret获取access_token(一般有效期为2min,需要与后面的定时任务结合使用,实现不间断的刷新获取最新可用的access_token)。

 /**
     * 根据AppID和AppSecret获取最新可用的AccessToken
     * @return
     */
    public static String getAccessToken() throws IOException {
//        https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
        String appid = PropUtils.getProp("APPID");
        String appsecret = PropUtils.getProp("APPSECRET");

        //构建url,用于向微信服务器请求用户的openId
        StringBuffer url = new StringBuffer("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&");
        url.append("appid=").append(appid)
                .append("&secret=").append(appsecret);

        //向微信的服务器发送Get请求
        HttpClient client = HttpClientBuilder.create().build();
        HttpGet httpGet = new HttpGet(url.toString());
        HttpResponse httpResponse = client.execute(httpGet);
        HttpEntity result = httpResponse.getEntity();
        String resultStr = EntityUtils.toString(result);
        System.out.println(resultStr);
        JSONObject resultJsonObject = JSONUtil.parseObj(resultStr);
        String accessToken = (String) resultJsonObject.get("access_token");
        return accessToken;
    }

通过输出resultStr 可以得到一个{"access_token":"长字符串","expires_in":7200}的access_token信息

小程序的AppID和AppSecret在发布小程序的官方网页获取对应信息

这些都是不变的,我们可以将其存进resousce下的appconfig.properties

 通过书写一个PropUtil来获取

public class PropUtils {
    private static Properties properties;

    static {
        properties = new Properties();
    }

    public static String getProp(String key) throws IOException {
        //读取配置文件
        ClassPathResource classPathResource = new ClassPathResource("appconfig.properties");
        properties = PropertiesLoaderUtils.loadProperties(classPathResource);
        return properties.getProperty(key);
    }
}

发送模板信息

//发送模板消息
    public static String sendTemplateMessage(WxMssVo wxMssVo) {
        String info = "";
        try {
            //创建连接
            URL url = new URL(wxMssVo.getRequest_url());
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setRequestMethod("POST");
            connection.setUseCaches(false);
            connection.setInstanceFollowRedirects(true);
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("Content-Type", "utf-8");
            connection.connect();

            //POST请求
            DataOutputStream out = new DataOutputStream(connection.getOutputStream());
            JSONObject obj = new JSONObject();

            //设置参数
            //不可缺失 缺失会出现invalid openid rid
            //putOpt 等效于当两个参数都为非空时;除此之外什么都不做。put(name, value)
            obj.putOpt("touser", wxMssVo.getTouser());
          //不可缺失 缺失出现invalid template_id
            obj.putOpt("template_id", wxMssVo.getTemplate_id());
            obj.putOpt("page", wxMssVo.getPage());

            JSONObject jsonObject = new JSONObject();

            //发送自定义数据
            Set<Map.Entry<String, Object>> entries = wxMssVo.getMap().entrySet();

            for (Map.Entry<String, Object>entry:entries){

                JSONObject dataInfo = new JSONObject();
                dataInfo.putOpt("value", entry.getValue());
                jsonObject.putOpt(entry.getKey(), dataInfo);
            }
            obj.putOpt("data", jsonObject);

            System.out.println(obj.toString());
            out.write(obj.toString().getBytes());
            out.flush();
            out.close();

            //读取响应
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String lines;
            StringBuffer sb = new StringBuffer("");
            while ((lines = reader.readLine()) != null) {
                lines = new String(lines.getBytes(), "utf-8");
                sb.append(lines);
            }
            info = sb.toString();
            System.out.println(sb);
            reader.close();
            // 断开连接
            connection.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return info;
    }

创建测试类

  @Test
    public void publishModelMessage(){
        WxMssVo wxMssVo = new WxMssVo();
        //设置模板id
        wxMssVo.setTemplate_id("");
        String accessToken=null;
        try {
            //设置openId
            wxMssVo.setTouser("");
//            设置accessToken
            accessToken=WXUtils.getAccessToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
//        设置小程序 跳转首页
        wxMssVo.setPage("pages/index/index");

        wxMssVo.setRequest_url("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken);
//        将模板中的值一一赋值 发送到小程序的数据要转化为json格式 可以使用下面的方法直接拼接成JSONObject
      /*  JSONObject jsonObject = new JSONObject();
        JSONObject dataInfo = new JSONObject();
        dataInfo.putOpt("value", TimeUtils.getNowTime());
        jsonObject.putOpt("time5", dataInfo);
*/
//        使用map结合模板 后续再加遍历拼接成JSONObject
        HashMap<String, Object> map = new HashMap<>(3);
        //根据模板写值  
        map.put("time",TimeUtils.getNowTime());
        map.put("thing1","下班打卡");
        wxMssVo.setMap(map);
        WXUtils.sendTemplateMessage(wxMssVo);
    }

发送请求

没有权限用户,返回错误,需要每一次都设置权限(企业版才不需要)比较容易出现

小程序发送通知需要传递json数据,订阅号的模板对于每⼀个字段的类型都有特别要求,例如这个模板中的“通知”字段是叫time5.DATA,那么你在后端调取微信的时候也必须使用这个名字,而且对于类型也有一定的限制。

首先,我们看看小程序模板大概的样子

这时,我们就需要设置成data->time5->value(替换为值)这样的json格式传值。

传值之后容易出现数据不合法,需要参考参数值内容限制说明

 参数类别 参数说明             参数值限制   说明
  thing.DATA   事务           20个以内字符  可汉字、数字、字母或符号组合
number.DATA   数字           32位以内数字      只能数字,可带小数
 letter.DATA   字母            32位以内字母               只能字母
 symbol.DATA    符号              5位以内符号               只能符号
 character_string.DATA   字符串    32位以内数字、字母或符号   可数字、字母或符号组合
 time.DATA   时间  24小时制时间格式(可以为年月日),支持时间段;两个时间点之间用~连接(如2022-09-27 19:31:28或19:38)
date.DATA    日期  年月日格式(支持加24小时制时间和时间段),两个时间点之间用~连接    如2022年9月27日,2022年9月27日 19:31
amount.DATA    金额一个货币符号+10位以内纯数字,可带小数,结尾可带元 ¥8.8
phone_number.DATA   电话号码   17位以内数字、符号

如11122223333,+86-1111-11111111

car_number.DATA 车牌 8位以内,第一位与最后一位可为汉字,其余为字母或数字车牌号 :如粤A8888Z桂
name.DATA 姓名 10字以内纯汉字或20个以内纯字母或符号小明
phrase.DATA汉字5个字以内汉字 点赞的都帅

成功结果

 

 

总结

在实现不同外部接口的调用时,应多去看相对应的使用文档,积极搜索相同需求下不同的实现方法,再搭配上业务需求,实现特定功能。下期将更新基于SchedulingConfigurer实现多定时任务来实现定时的发送下班请求。

有关实现微信小程序定时发送通知(1)发送请求篇的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

    rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

  5. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

  6. ruby-on-rails - 如何在发布新的 Ruby 或 Rails 版本时收到通知? - 2

    有人知道在发布新版本的Ruby和Rails时收到电子邮件的方法吗?他们有邮件列表,RubyonRails有一个推特,但我不想听到那些随之而来的喧嚣,我只想知道什么时候发布新版本,尤其是那些有安全修复的版本。 最佳答案 从therailsblog获取提要.http://weblog.rubyonrails.org/feed/atom.xml 关于ruby-on-rails-如何在发布新的Ruby或Rails版本时收到通知?,我们在StackOverflow上找到一个类似的问题:

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  10. 微信小程序通过字典表匹配对应数据 - 2

    前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立

随机推荐