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

news2025/7/7 6:38:04

目录

概述

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

一、申请公众号模板消息

二、获取安装“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
来        源:CSDN (Chinese Software Developer Network)
原        文:https://blog.csdn.net/Hello_World_QWP/article/details/125871196
版权声明:本文为博主原创文章,请在转载时务必注明博文出处!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/407642.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

flex布局(弹性盒子一)

目录 一、弹性盒子的开启 二、弹性盒子的不同样式 1.flex-direction&#xff1a;子项目的排列方向 2.flex-wrap&#xff1a;子项目多行 3. flex-flow 是flex-direction和flex-wrap的一起指定&#xff08;简写&#xff09; 一、弹性盒子的开启 设置内容区的样式&#xff1a…

Vue组件库实现按需引入可以这么做

本文为Varlet组件库源码主题阅读系列第七篇&#xff0c;读完本篇&#xff0c;可以了解到如何通过unplugin-vue-components插件来为你的组件库实现按需引入。 手动引入 前面的文章中我们介绍过Varlet组件库的打包流程&#xff0c;最后会打包成几种格式&#xff0c;其中module和…

JavaScript对象

文章目录前言一、对象1.1什么是对象1.2为什么需要对象二、创建对象的三种方式1.利用字面量创建对象2.对象的调用2.1变量、属性、函数、方法总结3.利用new Object创建对象3.1利用构造函数创建对象4.new关键字总结前言 一、对象 1.1什么是对象 现实生活中&#xff1a;万物皆对象…

vue-在组件中使用v-model

一、使用场景 子组件想要使用父组件的值,又想去改父组件的值 二、V-Model的本质 1.给子组件的 value 传个变量 2.监听子组件的input事件,并且把传过来的值赋给父组件的变量 三、关键步骤 1. props 的使用 在自定义的 vue 文件中&#xff0c;声明父组件要向子组件传递的 pr…

左右等高布局

开篇概述 作为这个专栏的开篇&#xff0c;简单介绍一下开这个专栏的初衷。之前在团队中做代码 review 时&#xff0c;经常会发现一些不太“健壮”的 css 代码。这些不太“健壮”的 css 代码&#xff0c;当遇到一些特殊情况时&#xff0c;界面显示就有可能出现问题。其中&#…

uniapp/vue虚拟列表,数据列表渲染优化

引言 相信大家经常会遇到展示一堆数据的需求&#xff0c;几十条数据还好&#xff0c;要是几百上千条&#xff0c;甚至带上了图片。那就会卡得不行。这时候就需要按需加载。 按需加载有懒加载和虚拟列表。 懒加载&#xff1a;通过JS滚动或触底触发事件来加载更多的数据&#…

报错“Cannot read properties of null (reading ‘addEventListener‘)“

场景 控制台报错"Cannot read properties of null (reading addEventListener)" 错误原因 因为 JavaScript 中操作DOM元素的函数方法需要在 HTML 文档渲染完成后才可以使用&#xff0c;如果没有渲染完成&#xff0c;此时的 DOM 树是不完整的&#xff0c;这样在调用…

css鼠标变成小手(css中鼠标悬停是为小手)

CSS控制鼠标样式变换如何写代码呢&#xff1f; 代码&#xff1a;&#xff1a;p stylecursor: crosshair演示&#xff1a;定位指示/p如果需要将鼠标变换成帮助状态的时候。代码&#xff1a;p stylecursor: help演示&#xff1a;帮助/p当然这些只是一些常见的比较实用的代码。我们…

前端实现可拖拽流程的js框架

1 AntV X6 是 AntV 旗下的图编辑引擎&#xff0c;提供了一系列开箱即用的交互组件和简单易用的节点定制能力&#xff0c;方便我们快速搭建 DAG 图、ER 图、流程图等应用。 XFlow是基于 X6 图编辑引擎、面向 React 技术栈用户的专业图编辑应用级解决方案, 帮助您轻松开发复杂的…

前端使用print.js实现打印

前言 项目中经常会用到前端调用浏览器打印的功能&#xff0c;也经常会遇到一些问题&#xff0c;写这篇文章是为了更好的梳理一下相关内容。下面的内容基于vue。 如果需要用到前端生成二维码可以看我的这篇文章&#xff1a;在vue项目中使用qrcodesjs2生成二维码 注&#xff1a…

【学姐面试宝典】—— 前端基础篇Ⅰ(HTTP/HTML/浏览器)

前言 博主主页&#x1f449;&#x1f3fb;蜡笔雏田学代码 专栏链接&#x1f449;&#x1f3fb;【前端面试专栏】 今天开始学习前端面试题相关的知识&#xff01; 感兴趣的小伙伴一起来看看吧~&#x1f91e; 文章目录http 和 https 的基本概念http 和 https 的区别https 协议的工…

【React Router v6】这17个API,你真的都懂了吗?(建议收藏)

😇本文目录😇 Component1.` <BrowserRouter> `2.`<HashRouter>`3.`<Routes />与<Route />`4.`<Link>`5.`<NavLink>`6.`<Navigate />`7.`<Outlet>`Hooks1.`useRoutes()`2.`useNavigate()`3.`useParams()`

【微信小程序】电商移动前端API文档

文章目录一、文档更新说明1、返回值调整2、分类页图片3、订单支付接口二、路径说明三、电商登录实现方式四、API详情1、全局权限访问&#xff08;1&#xff09;主页API获取首页轮播图数据获取首页分类选项数据获取首页楼层数据&#xff08;2&#xff09;商品API商品列表搜索搜索…

pinia详细使用步骤(0基础)

Pinia简介 学vue2的都知道vuex状态管理&#xff0c;所谓状态管理&#xff0c;简单来说就是一个存储数据的地方&#xff0c;存放在Vuex中的数据在各个组件中都能访问到&#xff0c;它是Vue生态中重要的组成部分。 而pinia同理也是起到状态管理的作用&#xff0c;但是它又不完全…

TDesign小程序组件库体验

原来小程序开发有组件库选择的问题&#xff0c;可以使用WeUI或者Vant。今年腾讯开源了前端的框架TDesign&#xff0c;我也分享了两篇使用文章。 年初分享的主要是PC端的框架&#xff0c;除了有PC端的框架外&#xff0c;最近TDesign又新出了小程序的框架&#xff0c;组件比较丰…

前端vue视频vue-video-player插件总结知识点案例(带源码)

目录文档安装main.js文件全局引入视频格式组件方法切换视频清晰度基础案例效果如下完整案例1效果如下完整案例2效果如下最后文档 文档地址 选项参考 API文档 配置函数方法等 Git地址 Git地址 安装 1.vue-video-player 插件下载 npm install vue-video-player --save2.推…

vue3-admin商品管理后台项目(登录页开发和功能实现)

今天来实现vue3-admin商品管理后台项目的登录页功能 首先在pages文件夹下面添加一个login.vue文件&#xff0c;里面先写入简单的template <template><div>登录</div> </template>然后在router文件夹下面的Index.js里面编辑&#xff0c;仍然是引入页面…

【前端开发工具】VUE3 devtools安装

背景 尤雨溪在2020年9月19日晚正式发布vue3.0 one piece。此版本相较于vue2版本&#xff0c;更快、更小、更易维护、更易于原生、让开发者更轻松&#xff1b;所以学习vue3&#xff0c;对于一个前端开发者来说是一个刻不容缓的学习趋势。 学习vue3自然也离不开debug啦~~ Vue官方…

JS——正则表达式(超详细)

正则表达式概念创建正则表达式正则表达式常用方法test(字符串)search(正则表达式&#xff09;正则表达式.exec(字符串&#xff09;字符串.match(正则表达式)字符串.replace(正则表达式&#xff0c;新的内容&#xff09;断言范围类字符类字符类取反修饰符g&#xff1a;global全文…

Web前端 | HTML嵌入JS代码的三种方式

✅作者简介&#xff1a;一位材料转码农的选手&#xff0c;希望一起努力&#xff0c;一起进步&#xff01; &#x1f4c3;个人主页&#xff1a;每天都要敲代码的个人主页 &#x1f525;系列专栏&#xff1a;Web前端 目录 一&#xff1a;JavaScript概述 二&#xff1a; HTML嵌入…