SpringBoot整合调用微信模板方法实现微信公众号消息通知推送,Java实现微信公众号给关注用户推送自定义消息通知(手把手从0到1)

SpringBoot实现微信订阅消息推送 小程序的订阅消息推送 阅读详情

目录

概述

公众号给关注用户推送自定义消息

一、申请公众号模板消息

二、获取安装“web开发者工具”

三、微信网页授权说明

四、微信网页授权 - 流程时序图

五、HTTPClient 实现微信公众号消息推送与发布(四步走)

六、通过weixin-java-mp SDK实现微信公众号消息推送与发布(七步走)

七、抽取与封装


概述

本篇文章主要基于Java+Spring Boot+Spring Cloud的应用中接入微信公众号,调用微信的JavaSDK > weixin-java-mp进行应用消息推送,实现业务数据推送到指定的微信用户客户端。通过本篇博客,将快速上手,从0到1构建起消息推送与发布。

公众号给关注用户推送自定义消息

一、申请公众号模板消息

1、开通微信公众号平台的“模板消息”栏

提交申请:

添加功能插件>功能详情>申请开通模板接口> 填写业务服务目标所属的行业,申请理由,

如果是新申请的消息模板,需要注意规范,否则会被封号的可能!!!

等待审核通过就可以使用了!

“模板消息”开通审核通过后,在微信公众号平台>左边栏>广告与服务>就可以看到模板消息栏了,

接着,就可以添加用于业务系统推送公众号的模板消息内容了,

如下图: 

 添加完成后,就可以在,模板消息>我的模板,中进行查看了,如下图:

关于推送的模板消息内容,用两种定义方式:
1、用公众号模板库已经存在的,也就是别人之前申请过的,
2、如果在模板消息库中检索不到符合当下业务系统需求的消息模板内容,则可以自定义,在模板库中选择“帮助我们完善模板库”
如下图:

 添加自定义的微信公众号模板的内容的注意事项:
1、添加模版前,需要先仔细阅读《模版消息申请添加前必读指引》。请勿违反运营规则,否则可能被停用模版消息接口甚至封号的可能;
2、贡献新模版需要等待“7-15”天审核期,且内容可能被审核人员修改。每月只可申请新建3个新模版;
3、审核通过后,模版将放入模版库以供他人使用,会被官方共享出去,也就是这里的消息模板,没有私有这一说,之前博主的客户提需求,说必须要私有的,不能共享,因为这不是自己能控制的,遂进行了多轮沟通后最终才说服了客户;

二、获取安装“web开发者工具”

《web开发者工具稳定版下载》

微信web开发者工具,安装完成后,打开应用程序,选择“公众号网页项目”,
如下图;

为公众号绑定开发者:

如果出现 “ 该微信用户未开启“公众号安全助手”的消息接收功能,请先开启后再绑定 ”

参考:

《该微信用户未开启“公众号安全助手”的消息接收功能,请先开启后再绑定》

邀请绑定,

设置完成后,再次邀请绑定即可完成绑定了,如下图:

绑定成功后,再打开,微信公众号平台>设置与开发>开发者工具>选择“web开发者工具”>如下图(web开发者工具最多可绑定50人),
如下图:

三、微信网页授权说明

1、微信开发网页授权五步走

第一步:用户同意授权,获取code
在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(已认证服务号,默认拥有 scope 参数中的snsapi_base和snsapi_userinfo 权限),引导关注者打开如下页面:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有 scope 参数对应的授权作用域权限。

注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问,跳转回调redirect_uri,应当使用 https 链接来确保授权 code 的安全性。

参考链接(请在微信客户端中打开此链接体验):

scope为snsapi_base:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect

scope为snsapi_userinfo:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

用户同意授权后

如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

code说明:

code作为换取access_token的票据,每次用户授权带上的 code 将不一样,code只能使用一次,5分钟未被使用自动过期。

请求参数说明:

回调错误码说明:

第二步:通过 code 换取网页授权access_token

首先请注意,这里通过 code 换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。

注意:由于公众号的 secret 和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。

请求方法

获取 code 后,请求以下链接获取access_token:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

请求参数说明:

  

回调参数说明:

回调错误码说明:

 

更多返回码说明请参看:

【全局返回码说明】

第三步:刷新access_token(可选项)

由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。

请求方法

获取第二步的refresh_token后,请求以下链接获取access_token:

https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN

请求参数说明:

回调参数说明:

回调错误码说明:参看第二步回调错误码说明!

第四步:拉取用户信息(scope 为 snsapi_userinfo)

如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和 openid 拉取用户信息了。

请求方法

http:GET(请使用 https 协议):

https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

关于scope授权作用域:

  /**
   * oauth2网页授权的scope.
   */
  public static class OAuth2Scope {
    /**
     * 不弹出授权页面,直接跳转,只能获取用户openid.
     */
    public static final String SNSAPI_BASE = "snsapi_base";

    /**
     * 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息.
     */
    public static final String SNSAPI_USERINFO = "snsapi_userinfo";

    /**
     * 手动授权,可获取成员的详细信息,包含手机、邮箱。只适用于企业微信或企业号.
     */
    public static final String SNSAPI_PRIVATEINFO = "snsapi_privateinfo";
  }

请求参数说明:

回调参数说明:

回调错误码说明:参看第二步回调错误码说明!

第五步:检验授权凭证(access_token)是否有效(可选项)

请求方法

http:GET(请使用 https 协议):

https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID

请求参数说明:

回调参数说明:

两个参数(errcode、errmsg)

回调错误码说明:参看第二步回调错误码说明!

2、其它说明

一、关于网页授权回调域名的说明
1、在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头。


2、授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com 无法进行OAuth2.0鉴权。
3、如果公众号登录授权给了第三方开发者来进行管理,则不必做任何设置,由第三方代替公众号实现网页授权即可。

二、关于网页授权的两种 scope 的区别说明
1、以snsapi_base为 scope 发起的网页授权,是用来获取进入页面的用户的 openid 的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)。


2、以snsapi_userinfo为 scope 发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。


3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户 OpenID 来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

三、关于网页授权access_token和普通access_token的区别
1、微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息。


2、其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。

四、微信网页授权 - 流程时序图

五、HTTPClient 实现微信公众号消息推送与发布(四步走)

第一步:获取微信公众号CODE

    /**
     * Description:[获取公众号CODE]
     *
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/getWeChatCode")
    @ApiOperation(value = "/getWeChatCode", notes = "获取公众号CODE")
    public void getWeChatCode(HttpServletResponse response) {
        try {
            // 构建公众号消息体
            String weChatGetCodeUrl = String.format(this.WE_CHAT_CODE_URL, this.WE_CHAT_APP_ID, this.WE_CHAT_CALL_BACK_DOMAIN_URL, WxConsts.OAuth2Scope.SNSAPI_BASE);
            log.info("we_chat_get_code_url:" + weChatGetCodeUrl);
            response.sendRedirect(weChatGetCodeUrl);

        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }
    }

 请求回调获取的用户授权CODE,如下图:

第二部:根据Code获取用户OpenId

    /**
     * Description:[根据Code获取用户OpenId]
     *
     * @return JSONResult
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/getWeChatOpenId")
    @ApiOperation(value = "/getWeChatOpenId", notes = "根据Code获取用户OpenId")
    public JSONResult getWeChatOpenId(@RequestParam String code, @RequestParam String state) {
        try {
            log.info("we_chat_code: " + code);
            String weChatDomain = String.format(this.WE_CHAT_AUTHORIZATION_URL, this.WE_CHAT_APP_ID, this.WE_CHAT_SECRET, code);

            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(weChatDomain, String.class);
            String openid = JSONObject.parseObject(responseEntity.getBody()).getString("openid");
            String access_token = JSONObject.parseObject(responseEntity.getBody()).getString("access_token");

            // 用户的OpenId,用户的微信授权Access_Token
            log.info("we_chat_open_id: " + openid);
            log.info("we_chat_access_token: " + access_token);

            return JSONResult.success(openid);
        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }
        return null;
    }

 获取的openid如下图:

第三步:根据Code获取用户Access_Token(可选)

    /**
     * Description:[根据Code获取用户Access_Token]
     *
     * @return JSONResult
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/getUserAccessToken")
    @ApiOperation(value = "/getUserAccessToken", notes = "根据Code获取用户Access_Token")
    public JSONResult getUserAccessToken(@RequestParam String code, @RequestParam String state) {
        try {
            String weChatDomain = String.format(this.WE_CHAT_AUTHORIZATION_URL, this.WE_CHAT_APP_ID, this.WE_CHAT_SECRET, code);
            log.info("we_chat_authorization_url:{}" + weChatDomain);

            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(weChatDomain, String.class);

            String accessToken = JSONObject.parseObject(responseEntity.getBody()).getString("access_token");
            log.info("we_chat_access_token: " + accessToken);

            return JSONResult.success(accessToken);
        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }
        return null;
    }

第四步:获取微信公众号的Access_Token

    /**
     * Description:[获取微信公众号的Access_Token]
     *
     * @return JSONResult
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/getWeChatAccessToken")
    @ApiOperation(value = "/getWeChatAccessToken", notes = "微信公众号的Access_Token")
    public JSONResult getWeChatAccessToken() {
        try {
            // 微信公众号官方获取AccessToken
            RestTemplate restTemplate = new RestTemplate();
            String requestParams = String.format(this.WE_CHAT_ACCESS_TOKEN_URL, this.WE_CHAT_APP_ID, this.WE_CHAT_SECRET);
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(requestParams, String.class);

            String accessToken = JSONObject.parseObject(responseEntity.getBody()).getString("access_token");
            log.info("we_chat_access_token: " + accessToken);

            return JSONResult.success(accessToken);
        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }
        return null;
    }

第五步:微信公众号指定用户消息推送与发布

    /**
     * Description:[公众号指定用户消息推送]
     *
     * @return JSONResult
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/sendWeChatRecharge")
    @ApiOperation(value = "/sendWeChatRecharge", notes = "公众号指定用户消息推送")
    public JSONResult sendWeChatRecharge() {

        // 1、构建公众号消息体
        WeChatRechargeTemplateParamsDTO weChatRechargeTemplateParamsDTO = WeChatRechargeTemplateParamsDTO.builder()
                .first("尊敬用户您好,你的缴费结果如下:")
                .keyword1("HuaZai")
                .keyword2("10010110010001")
                .keyword3(BigDecimal.valueOf(2000))
                .keyword4(BigDecimal.valueOf(10000182.92))
                .keyword5(DateUtil.getDateTime())
                .remark("缴费成功,祝您生活愉快!")
                .touser("**********")
                .customerNumber("549527")
                .build();

        // 2、组装消息数据
        Map<String, WeChatMsgDTO> weChatMsgMap = new HashMap<>();
        weChatMsgMap.put("first", new WeChatMsgDTO(weChatRechargeTemplateParamsDTO.getFirst()));
        weChatMsgMap.put("keyword1", new WeChatMsgDTO(weChatRechargeTemplateParamsDTO.getKeyword1()));
        weChatMsgMap.put("keyword2", new WeChatMsgDTO(weChatRechargeTemplateParamsDTO.getKeyword2()));
        weChatMsgMap.put("keyword3", new WeChatMsgDTO(weChatRechargeTemplateParamsDTO.getKeyword3().toString()));
        weChatMsgMap.put("keyword4", new WeChatMsgDTO(weChatRechargeTemplateParamsDTO.getKeyword4().toString()));
        weChatMsgMap.put("keyword5", new WeChatMsgDTO(weChatRechargeTemplateParamsDTO.getKeyword5()));
        weChatMsgMap.put("remark", new WeChatMsgDTO(weChatRechargeTemplateParamsDTO.getRemark()));
        String customerCallUrl = String.format(this.WE_CHAT_CUSTOMER_CALL_URL, weChatRechargeTemplateParamsDTO.getCustomerNumber());

        //3、构建模板消息体
        WeChatTemplateParamsDTO weChatTemplateParamsDTO = WeChatTemplateParamsDTO.builder()
                .touser(weChatRechargeTemplateParamsDTO.getTouser())
                .template_id(this.WE_CHAT_TEMPLATE_ID)
                .url(customerCallUrl)
                .topcolor(this.WE_CHAT_TOP_COLOR)
                .data(weChatMsgMap)
                .build();

        // 4、微信公众号消息推送与发布
        RestTemplate restTemplate = new RestTemplate();
        String resultUrl = String.format(this.WE_CHAT_REQUEST_URL, this.WE_CHAT_ACCESS_TOKEN);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(resultUrl, weChatTemplateParamsDTO, String.class);

        if (!OK.equals(responseEntity.getStatusCode())) {
            log.error("公众号消息推送失败!", responseEntity.getBody());
        }

        return JSONResult.success();
    }

消息推送,返回code为0,即表示消息已成功推送(否则失败,需要code码和上面的错误信息说明进行对比,针对性问题处理),如下图:

再打开微信公众号,就可以看到推送的消息了,如下图:

 

常量说明:

    // 微信公众号的 app_id
    String WE_CHAT_APP_ID = "**********";
    // 微信公众号的 secret
    String WE_CHAT_SECRET = "**********";
    // 微信公众号的 access_token
    String WE_CHAT_ACCESS_TOKEN = "**********";
    // 微信公众号code获取地址
    String WE_CHAT_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STATE#wechat_redirect";
    // 回调地址,获取open_id
    String WE_CHAT_CALL_BACK_DOMAIN_URL = "https://***.***.***.***/***/***/getWeChatOpenId";
    // 微信公众号的token获取地址
    String WE_CHAT_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    // 微信公众号消息推送地址
    String WE_CHAT_REQUEST_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
    // 微信公众号推送消息模板id
    String WE_CHAT_TEMPLATE_ID = "**********";
    // 微信公众号的消息回调地址(这儿可根据业务需求自定义动作,可选)
    String WE_CHAT_CUSTOMER_CALL_URL = "https://***.***.***.***/***/***/accountInfo?keyword=%s";
    // 微信公众号的主题颜色
    String WE_CHAT_TOP_COLOR = "#A349A4";
    // 微信公众号微信用户授权地址
    String WE_CHAT_AUTHORIZATION_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

六、通过weixin-java-mp SDK实现微信公众号消息推送与发布(七步走)

第一步:pom.xml新增依赖

        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-mp</artifactId>
            <version>4.3.0</version>
        </dependency>

第二步:配置WxMpService

配置me.chanjar.weixin.mp.api.WxMpService,可以通过@Component组件注入交由Spring进行管理,或者直接在启动类@SpringBootApplication中@Bean注入,

注入的内容如下:

    @Bean
    public WxMpService wxMpService() {
        WxMpMapConfigImpl wxMpMapConfig = new WxMpMapConfigImpl();
        wxMpMapConfig.setAppId(Constant.WE_CHAT_APP_ID);
        wxMpMapConfig.setSecret(Constant.WE_CHAT_SECRET);
        val wxMpService = new WxMpServiceImpl();
        wxMpService.setWxMpConfigStorage(wxMpMapConfig);

        return wxMpService;
    }

 第三步:获取微信公众号CODE


    @Autowired
    private WxMpService wxMpService;


    /**
     * Description:[获取公众号CODE]
     *
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/getWeChatCode")
    @ApiOperation(value = "/getWeChatCode", notes = "获取公众号CODE")
    public RedirectView getWeChatCode() {
        try {
            // 构造网页授权url
            String authorizationUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(this.WE_CHAT_CALL_BACK_DOMAIN_URL, WxConsts.OAuth2Scope.SNSAPI_BASE, null);
            log.info("we_chat_get_code_url:" + authorizationUrl);

            return new RedirectView(authorizationUrl);
        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }
        return null;
    }

第四步:根据Code获取用户OpenId

    /**
     * Description:[根据Code获取用户OpenId]
     *
     * @return JSONResult
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/getWeChatOpenId")
    @ApiOperation(value = "/getWeChatOpenId", notes = "根据Code获取用户OpenId")
    public JSONResult getWeChatOpenId(@RequestParam String code, @RequestParam String state) {
        try {
            log.info("we_chat_code: " + code);
            // 通过微信回调过来的code获得access token,其中也包含用户的openid等信息
            WxOAuth2AccessToken wxOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(code);

            String openid = wxOAuth2AccessToken.getOpenId();
            String access_token = wxOAuth2AccessToken.getAccessToken();
            // 用户的OpenId,用户的微信授权Access_Token
            log.info("we_chat_open_id: " + openid);
            log.info("we_chat_access_token: " + access_token);

            return JSONResult.success(openid);
        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }
        return null;
    }

第五步:根据Code获取用户Access_Token(可选)

    /**
     * Description:[根据Code获取用户Access_Token]
     *
     * @return JSONResult
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/getUserAccessToken")
    @ApiOperation(value = "/getUserAccessToken", notes = "根据Code获取用户Access_Token")
    public JSONResult getUserAccessToken(@RequestParam String code, @RequestParam String state) {
        try {
            // 根据Code获取用户Access_Token
            WxOAuth2AccessToken wxOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(code);

            String accessToken = wxOAuth2AccessToken.getAccessToken();
            log.info("we_chat_user_access_token: " + accessToken);

            return JSONResult.success(accessToken);
        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }
        return null;
    }

第六步:获取微信公众号的Access_Token
 

    /**
     * Description:[获取微信公众号的Access_Token]
     *
     * @return JSONResult
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/getWeChatAccessToken")
    @ApiOperation(value = "/getWeChatAccessToken", notes = "微信公众号的Access_Token")
    public JSONResult getWeChatAccessToken() {
        try {
            // 微信公众号官方获取AccessToken
            String accessToken = wxMpService.getAccessToken();
            log.info("we_chat_access_token: " + accessToken);

            return JSONResult.success(accessToken);
        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }
        return null;
    }

第七步:微信公众号指定用户消息推送与发布

    /**
     * Description:[公众号指定用户消息推送]
     *
     * @return JSONResult
     * @date 2019-05-19
     * @author huazai
     */
    @GetMapping("/sendWeChatRecharge")
    @ApiOperation(value = "/sendWeChatRecharge", notes = "公众号指定用户消息推送")
    public JSONResult sendWeChatRecharge() {
        try {
            // 1、构建公众号消息体
            WeChatRechargeTemplateParamsDTO weChatRechargeTemplateParamsDTO = WeChatRechargeTemplateParamsDTO.builder()
                    .first("尊敬用户您好,你的缴费结果如下:")
                    .keyword1("HuaZai")
                    .keyword2("10010110010001")
                    .keyword3(BigDecimal.valueOf(2000))
                    .keyword4(BigDecimal.valueOf(10000182.92))
                    .keyword5(DateUtil.getDateTime())
                    .remark("缴费成功,祝您生活愉快!")
                    .touser("**********")
                    .customerNumber("549527")
                    .build();

            // 2、组装消息数据
            List<WxMpTemplateData> wxMpTemplateDataList = new ArrayList<>();
            wxMpTemplateDataList.add(new WxMpTemplateData("first", weChatRechargeTemplateParamsDTO.getFirst(), this.WE_CHAT_TOP_COLOR));
            wxMpTemplateDataList.add(new WxMpTemplateData("keyword1", weChatRechargeTemplateParamsDTO.getKeyword1(), this.WE_CHAT_TOP_COLOR));
            wxMpTemplateDataList.add(new WxMpTemplateData("keyword2", weChatRechargeTemplateParamsDTO.getKeyword2(), this.WE_CHAT_TOP_COLOR));
            wxMpTemplateDataList.add(new WxMpTemplateData("keyword3", weChatRechargeTemplateParamsDTO.getKeyword3().toString(), this.WE_CHAT_TOP_COLOR));
            wxMpTemplateDataList.add(new WxMpTemplateData("keyword4", weChatRechargeTemplateParamsDTO.getKeyword4().toString(), this.WE_CHAT_TOP_COLOR));
            wxMpTemplateDataList.add(new WxMpTemplateData("keyword5", weChatRechargeTemplateParamsDTO.getKeyword5(), this.WE_CHAT_TOP_COLOR));
            wxMpTemplateDataList.add(new WxMpTemplateData("remark", weChatRechargeTemplateParamsDTO.getRemark(), this.WE_CHAT_TOP_COLOR));
            String customerCallUrl = String.format(this.WE_CHAT_CUSTOMER_CALL_URL, weChatRechargeTemplateParamsDTO.getCustomerNumber());

            //3、构建模板消息体
            WxMpTemplateMessage wxMpTemplateMessage = WxMpTemplateMessage.builder()
                    .toUser(weChatRechargeTemplateParamsDTO.getTouser())
                    .templateId(this.WE_CHAT_TEMPLATE_ID)
                    .url(customerCallUrl)
                    .data(wxMpTemplateDataList)
                    .build();

            // 4、微信公众号消息推送与发布
            String msgId = wxMpService.getTemplateMsgService().sendTemplateMsg(wxMpTemplateMessage);
            log.error("公众号模板消息推送与发布结果:{}", msgId);
        } catch (Exception e) {
            log.info("异常信息:{}", e);
        }

        return JSONResult.success();
    }

七、抽取与封装

这儿的封装需要更具自身业务的不同,封装的深度也不同,需要自由抽取封装,但万变不离其宗,

1、简单工厂-普通模式

建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

如下图:

2、简单工厂-多方法模式

对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

如下图:

3、简单工厂-多静态方法模式

将多工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

4、工厂方法模式

简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?

解决这个问题就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

5、抽象工厂模式

工厂方法模式:

  1. 一个抽象产品类,可以派生出多个具体产品类;
  2. 一个抽象工厂类,可以派生出多个具体工厂类; 
  3. 每个具体工厂类只能创建一个具体产品类的实例;

抽象工厂模式:

  1. 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类;
  2. 一个抽象工厂类,可以派生出多个具体工厂类;
  3. 每个具体工厂类可以创建多个具体产品类的实例,也就是创建的是一个产品线下的多个产品;

区别:

  1. 工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个;
  2. 工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个;
  3. 工厂方法创建 "一种" 产品,他的着重点在于"怎么创建",也就是说如果你开发,你的大量代码很可能围绕着这种产品的构造,初始化这些细节上面。也因为如此,类似的产品之间有很多可以复用的特征,所以会和模版方法相随;
  4. 抽象工厂需要创建一些列产品,着重点在于"创建哪些"产品上,也就是说,如果你开发,你的主要任务是划分不同差异的产品线,并且尽量保持每条产品线接口一致,从而可以从同一个抽象工厂继承;

对于Java应用程序来说,能见到的大部分抽象工厂模式都是这样的:里面是一堆工厂方法,每个工厂方法返回某种类型的对象。
例如:工厂可以生产鼠标和键盘。那么抽象工厂的实现类(它的某个具体子类)的对象都可以生产鼠标和键盘,但可能工厂 A 生产的是先科的键盘和鼠标,工厂 B 是Microsoft的。这样 A 和 B 就是工厂,对应于抽象工厂;每个工厂生产的鼠标和键盘就是产品,对应于工厂方法;用了工厂方法模式,你替换生成键盘的工厂方法,就可以把键盘从先科如丝滑般的切换到Microsoft。但是用了抽象工厂模式,要想切换工厂 C 的雷蛇,就可以同时替换鼠标和键盘一套。如果产品有几十个,当然用抽象工厂模式一次替换全部最方便快捷(工厂会替换相应的工厂方法)

所以抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线!!!

微信公众号开发注意两点:
1、公众号开发获取调用auoth2授权接口获取,Token_assecc授权时,需要授权IP,
参考:
《errcode“:40164,“errmsg“:“invalid ip ...微信公众号开发调用失败的解决办法》
2、在发布时,由于是基于Web页面开发的,需要授权域名的绑定验证,
参考:
《微信公众号开发redirect_uri 参数错误 的解决办法,Oauth2授权重定向域名参数错误解决办法》

3、SpringBoot整合调用微信模板方法实现微信公众号消息通知推送

《 HTTPClient 实现微信公众号消息推送与发布(四步走) 》

4、SpringBoot整合调用微信模板方法实现微信公众号消息通知推送

《 通过weixin-java-mp SDK实现微信公众号消息推送与发布(七步走) 》

参考:

【公众号模板消息接口文档】

【微信网页开发-Web开发者工具下载】

【微信开发包】更多内容查看WIKI,MP_OAuth2网页授权

【模板消息接口文档】

【MP_OAuth2网页授权】


 好了,关于 SpringBoot整合调用微信模板方法实现微信公众号消息通知推送,Java实现微信公众号给关注用户推送自定义消息通知(从0到1)  就写到这儿了,如果还有什么疑问或遇到什么问题欢迎扫码提问,也可以给我留言哦,我会一一详细的解答的。 
歇后语:“ 共同学习,共同进步 ”,也希望大家多多关注CSND的IT社区。


作       者:华    仔
联系作者:who.seek.me@java98k.vip
来        源:优快云 (Chinese Software Developer Network)
原        文:https://blog.youkuaiyun.com/Hello_World_QWP/article/details/125871196
版权声明:本文为博主原创文章,请在转载时务必注明博文出处!
springboot整合微信公众号实现模版消息推送 本文将详细介绍如何使用Spring Boot整合微信公众号,实现模版消息推送。我们将从项目的搭建开始,逐步讲解每个步骤,并通过示例代码帮助读者更好地理解和实现这一功能。无论你是新手还是有经验的开发者,都能从中找到实用的技巧和方法。 阅读详情

相关推荐

【微信公众号】8、SpringBoot整合WxJava发送模板消息

模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。 1、主动发送消息 1、在微信公众号中我们多数都是被动回复用户消息,在文章:【微信开发】4、SpringBoot整合WxJava处理微信消息、事件推送 中我们可以收到用户的消息、事件推送,从而被动回复用户信息。 2、微信公众号中能主动给用户发送消息的方式有以下几种: 1、模板消息,每个账号的模板消息的日调用上限为10万次 2、群

JACKAL 20万+

基于SpringBoot实现微信消息推送

SpringBoot微信推送

m0_63823719的博客 8154

【微信公众号】5、SpringBoot整合WxJava网页授权获取用户信息

如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑

JACKAL 20万+

java发送公众号/服务通知模板消息到指定用户(完整流程|亲测可用)

微信发送服务通知工具类 WxMssVO TemplateData 前端代码实现: 用户在在开发者提供的 H5 页面中,通过 JSSDK 拉起订阅按钮 wx.requestSubscribeMessage 2、java发送微信公众号模板消息到指定用户 这里和发送服务通知的不同 1> 要多一步配置并验证token(token需要验签) 2> 需要配置ip白名单 3> 发送消息的https接口不同 这里最好是公众号关联小程序,绑定后虽然每个小主体的用户openid不同但是用户的unionid都是相同的...

yushun的博客 1万+

【项目实战】Spring Boot整合Kafka消息队列(基于KafkaTemplate和@KafkaListener实现)

SpringBoot整合Kafka消息队列(基于KafkaTemplate和@KafkaListener实现)

奶爸的编程之路,也就一周冷个三天 7080

springboot实现微信模板消息推送

springboot实现微信模板消息推送,包含微信端代码。简单封装,方便二次开发!

SpringBoot(JAVA)整合微信公众号消息推送

在使用JAVA(springboot)开发微信公众号时,有时需要向微信服务器上传一些临时素材,比如图片、语音、视频等。本文将介绍如何实现JAVA微信新增临时素材的功能

weixin_46204877的博客 6873

java实现微信公众号发送模版消息

以订单推送为例,主要为大家详细介绍了java实现微信公众号发送模版消息,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

springboot公众号模板消息推送

本篇文章的主题是 如何通过springboot来实现实现效果:在当今的信息化时代,微信作为国人最为常用的通讯工具之一,已经不仅仅是一个简单的社交应用,更是连接人与服务、人与信息的桥梁。企业微信模板消息作为一种高效、便捷的信息传递方式,被广泛应用于各类业务场景中,如订单通知、会议提醒、活动推送等。通过本教程的学习,您将掌握如何在Spring Boot项目中集成微信SDK,如何编写代码发送微信模板消息,并了解整个推送的过程。

pscool的博客 1699

非目标代谢组学(untargeted metabolomics)中常用的方法学考察的方法(四)

最早使用的一种方法,从QC样本的总离子流图中选择具有代表性的离子峰(覆盖不同的保留时间,不同的强度),在对QC样本进行重复检测之后,计算这些离子的保留时间以及峰面积的相对标准偏差(RSD),用以考察分析方法的稳定性以及重复性。所有样品检测完之后,收集所有的QC样本的原始数据进行数据预处理,包括(峰提取,排列,归一化等),经过数据过滤(80%规则)之后,计算剩下的峰的峰面积的RSD值。由于QC样本是 等量混合了所有的被检测样本,理论上QC样本包含了所有样本中的代谢物,因此QC样本理论上会。

hlllllllhhhhh的博客 1487

Ubuntu开机卡在BusyBox?3种方法快速修复initramfs错误(附详细命令)

本文详细介绍了Ubuntu开机卡在BusyBox并出现initramfs错误的3种修复方法,包括使用fsck检查文件系统、调整BIOS/UEFI存储设置以及修改GRUB内核参数。针对常见的文件系统错误和根设备无法找到问题,提供了具体的命令行操作步骤和实用技巧,帮助用户快速恢复系统启动。

milk8的博客 783

手把手教您如何在Springboot项目中使用微信消息模板每日定时推送天气预报等消息(附录项目完整代码)

文章目录一、准备工作1.1、申请微信公众平台(服务号)1.2、申请百度地图API账号1.3、申请天行数据API账号二、创建SpringBoot项目2.1、pom.xml2.2、主启动类2.3、application.yml2.4、业务类三、启动项目进行测试3.1、访问接口地址3.2、查看后端后台输出3.3、查看微信通知列表四、源码 一、准备工作 本项目中需要注册开通和认证以下第三方开放平台: 微信公众平台申请账号及微信开放接口的调用。 百度地图天气API接口的调用。 天行数据API接口的调用。 1.1、

tigerhhzz的博客 1625

生物信息学实战:如何用STRING数据库和R语言搞定蛋白互作网络分析(附完整代码)

本文提供了一份完整的生物信息学实战指南,详细介绍了如何利用STRING数据库和R语言进行蛋白互作网络分析。从差异表达基因列表出发,涵盖数据准备、数据库连接、网络构建到高级可视化与模块识别的全流程,并附有可复现的完整R代码,帮助研究人员深入解析转录组数据背后的蛋白互作关系。

milk5的博客 943

java向微信公众号---发送模板和图文消息

微信公众号对接开发项目搭建mavenapplication.ymlyml参数配置微信客户端配置搭建完成 实现业务模板消息推送准备工作模板消息填写要求图文消息填写要求推送模板消息推送模板消息结果——效果推送图文消息问题和结果获取关注的用户openId既然有了openId集合和要发送的消息怎么群发 链接: 书接上文:微信测试号申请步骤和调试. 项目搭建 maven <dependencies> <dependency> <groupId&gt

不知所终的博客 3924

CFD-POST中建立变量的方法

本文以测量文丘里管中全压为例阐述CFD_POST中建立变量的方式 点击进入CFD-POST界面的expressions栏中 2.与expression栏中空白处右键,new-输入新建的表达式名称 3. 在下方的空白栏中右键-varible选择需要的变量,并根据公式进行修改 4.点击进入varibles栏,空白处右击-new-输入变量名totalpressure,在下拉框中找到之前定义的totalpressure 完成后可以在varibles栏中看到新建的...

sidxi的博客 1万+

C++中const和指针*的组合问题

今天在复习> 章节7.3的时候想到以前做的一个题,const和指针运算符*的若干种组合的合法性和意义问题,有点混淆,所以写了一个测试代码来验证一下,简单明了,一目了然。如果有什么遗漏的话欢迎指出。 首先是在Ubuntu14.04.1下用gcc 4.8.4编译运行的结果,编译器报错信息都附加在对应语句后面的注释里 /* * Note : Test for the combinations of

theColdOne的专栏 1080

Spring Boot 2.0 快速集成整合消息中间件 Kafka

欢迎关注个人微信公众号: 小哈学Java, 每日推送 Java 领域干货文章,关注即免费无套路附送 100G 海量学习、面试资源哟!! 个人网站: https://www.exception.site/springboot/spring-boot2-kafka 什么是 Kafka? Kafka 是 Apache 基金会开源的一个分布式发布 - 订阅消息中间件,流处理平台。 它起源于 ...

weixin_30252155的博客 247

FPGA:UART串口接收(高干扰情况)

在上一篇文章中介绍过UART串口接收模块,其中一个需要优化的方向就是在高干扰情况下,如何实现可靠的UART串口接收任务。

weixin_50164178的博客 1671

【课程设计】(附源码)springboot基于微信小程序的运动软件前端的设计与实现 毕业设计100932

运动软件小程序的开发是采用java语言,基于MVVM模式进行开发,采取MySQL作为后台数据的主要存储单元,采用Springboot框架实现了本系统的全部功能。运动软件微信小程序,具有运动资讯、运动记录、运动心得、运动视频上传等功能,本系统代码的复用率高,系统维护代价小,具有方便、灵活、高效等特征。

Biye_Design的博客 2594

CNN经典论文(一)LeNet

Gradient-Based Learning Applied to Document Recognition(梯度学习在文档识别中的应用) Yann LeCun, Leon Bottou, Yoshua Bengio, and Patrick Haffner 发表时间:December 1998 期刊:Proceedings of the IEEE 影响:世界上第一个卷积神经网络 论文地址:Gradient-Based Learning Applied to Document Recognition

loveling丶的博客 3597

知道LPDDR和DDR什么关系吗?

话不多说,先上大白话,一个网友总结的很好:简单地说DRR4是标准电压版的PC和服务器内存,LPDDR4属于手机等嵌入式移动设备使用的低电压低功耗内存,LPDDR4X则是LPDDR4的升级版,下面详细看看老哥的博客。LPDDR4是与DDR4内存等效的移动设备。。LPDDR4具有两个16位通道,从而使每个DIMM的总线总长度为32位。相比之下,DDR4每个DIMM具有64位通道。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 2805

【课程设计】小程序校园拼车微信小程序 毕业设计 091617

1.信息获取方便,用户可在网页上快速浏览到新的信息 2.拼车详情,单独查看校园拼车信息等。 3.注册登录,需要吸引学生,即通过注册来提高用户的存留率。 4.历史记录,用户可以查看自己拼车订单信息以及其他操作。 5.修改用户信息,可以修改用户密码或者用户名等一些个性化操作。 6.管理员功能,管理员可以对用户或者拼车信息进行管理。 7.系统安全,操作简便,不过于复杂。 8.系统可以稳定运行,不存在卡顿等问题造成用户反感。

WeiXin_DZbishe的博客 5908

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Spring Boot 2.0 教程 | 快速集成整合消息中间件 Kafka

欢迎关注个人微信公众号: 小哈学Java, 每日推送 Java 领域干货文章,关注即免费无套路附送 100G 海量学习、面试资源哟!! 个人网站: https://www.exception.site/springboot/spring-boot2-kafka 什么是 Kafka? Kafka 是 Apache 基金会开源的一个分布式发布 - 订阅消息中间件,流处理平台。 它起源于 ...

dongduohuan0337的博客 136

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

SpringBoot整合阿里RocketMQ

什么是RocketMQ 阿里消息队列 RocketMQ版既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性,同时是收费的产品。 应用场景 削峰填谷 诸如秒杀、抢红包、企业开门红等大型活动时皆会带来较高的流量脉冲,或因没做相应的保护而导致系统超负荷甚至崩溃,或因限制太过导致请求大量失败而影响用户体验,消息队列 RocketMQ 版可提供削...

RickSunLee的博客 3609

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Java全渠道同城回收系统技术实现与优化

O2O系统开发中,Java技术栈因其稳定性和成熟生态成为主流选择,尤其适合构建高并发的同城服务系统。通过SpringBoot+MyBatis框架组合,配合Redis缓存和RabbitMQ消息队列,可有效解决多终端数据同步与实时调度等核心问题。这类系统典型应用于回收服务、即时配送等场景,关键技术点包括智能路径规划算法(如改进遗传算法)、WebSocket实时通信及分布式事务处理。实践中需特别注意微信生态整合(如解决'小程序textarea样式问题')和Java版本兼容性(如避免'java: you aren'

weixin_30328063的博客 381
上一篇: 微信公众号开发redirect_uri 参数错误 的解决办法,Oauth2授权重定向域名参数错误解决办法
下一篇: ***.http.converter.HttpMessageNotReadableException: I/O error while reading input message; 的解决办法
TechBro华仔
TechBro华仔 优质创作者: Java技术领域 优质创作者: Java技术领域 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄11年 5万+粉丝 430原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechBro华仔

日拱一卒无有尽,功不唐捐终入海

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值