您的当前位置:首页正文

【微信公众号推送小程序消息通知】--下发统一消息接口被回收后新方案

2024-11-27 来源:个人技术集锦

序言

由于微信小程序长期订阅的消息模板和下发统一消息推送接口全部失效以后,对于小程序的消息推送可以往公众号推可以使用本文章方案。在网上看了挺多方案,有用用户列表做匹配的等,最终觉得通过关注事件触发的方案是最省事。

准备

1、注册服务号(订阅号是不可以推送的)与小程序,两者都需要认证并且认证主体是一致

2、注册账号(该账号也需要认证),绑定小程序与公众号

整体实现流程

通过在开放平台绑定的公众号与小程序后,我们在调用微信接口的时候会返回unionid,这个unionid就是推送的关键。

  • 用到微信接口:
    1、关注事件推送:
    2、查询用户基础信息:
    3、获取accessToken:
    4、消息模板推送:

实现代码(Java)

这里使用第三方工具包Wx-Java(非常方便),直接实现推送代码。具体源码可以浏览https://gitee.com/binary/weixin-java-tools

  • 先在配置文件yml配置公众号信息
# 公众号配置
wx:
  mp:
    appId: xiaochengxuappid
    secret: xiaochengxusecrect
    token: xiaochengxutoken
    aesKey:  xiaochengxuaes
    # token存储在redis
    config-storage:
      type: RedisTemplate
  • 封装推送工具类
	/**
     * 微信公众号推送模板消息
     *
     * @param openid       用户openid
     * @param templateData 模板参数
     */
    public void sendTemplateMsg(String openid, List<WxMpTemplateData> templateData) {
        WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
                .toUser(openid)
                // 模板id
                .templateId(templateId)
                // 跳转小程序appid,跳转路径
                .miniProgram(new WxMpTemplateMessage.MiniProgram(weAppAppId, "", false))
                // 模板参数
                .data(templateData)
                .build();
        try {
            log.debug("微信公众号推送模板消息入参:{}", templateMessage);
            // 成功返回msgId,否则都是抛异常
            wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
        } catch (WxErrorException e) {
            log.error("微信公众号推送模板消息异常", e);
        }
    }
  • 公众号接入服务器开发的代码
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author quan
 * @date 2023/11/7 18:19
 */
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/wx")
public class WxPortalController {
    @Autowired
    private final WxMpService wxService;
    @Autowired
    private final WxMpMessageRouter messageRouter;

    @GetMapping(produces = "text/plain;charset=utf-8")
    public String authGet(@PathVariable String appid,
                          @RequestParam(name = "signature", required = false) String signature,
                          @RequestParam(name = "timestamp", required = false) String timestamp,
                          @RequestParam(name = "nonce", required = false) String nonce,
                          @RequestParam(name = "echostr", required = false) String echostr) {
        log.info("接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature, timestamp, nonce, echostr);
        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
            throw new IllegalArgumentException("请求参数非法,请核实!");
        }

        if (!this.wxService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
        }

        if (wxService.checkSignature(timestamp, nonce, signature)) {
            return echostr;
        }

        return "非法请求";
    }

    @PostMapping(produces = "application/xml; charset=UTF-8")
    public String post(@PathVariable String appid,
                       @RequestBody String requestBody,
                       @RequestParam("signature") String signature,
                       @RequestParam("timestamp") String timestamp,
                       @RequestParam("nonce") String nonce,
                       @RequestParam("openid") String openid,
                       @RequestParam(name = "encrypt_type", required = false) String encType,
                       @RequestParam(name = "msg_signature", required = false) String msgSignature) {
        log.info("接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
                        + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
                openid, signature, encType, msgSignature, timestamp, nonce, requestBody);

        if (!this.wxService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
        }

        if (!wxService.checkSignature(timestamp, nonce, signature)) {
            throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
        }

        String out = null;
        if (encType == null) {
            // 明文传输的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
            WxMpXmlOutMessage outMessage = this.route(inMessage);
            if (outMessage == null) {
                return "";
            }

            out = outMessage.toXml();
        } else if ("aes".equalsIgnoreCase(encType)) {
            // aes加密的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(),
                    timestamp, nonce, msgSignature);
            log.debug("消息解密后内容为:{} ", inMessage);
            WxMpXmlOutMessage outMessage = this.route(inMessage);
            if (outMessage == null) {
                return "";
            }

            out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
        }

        log.debug("组装回复信息:{}", out);
        return out;
    }

    private WxMpXmlOutMessage route(WxMpXmlMessage message) {
        try {
            return this.messageRouter.route(message);
        } catch (Exception e) {
            log.error("路由消息时出现异常!", e);
        }

        return null;
    }
}

  • 微信消息路由配置类
/**
 * @author quan
 * @date 2023/11/7 18:23
 */
@AllArgsConstructor
@Configuration
public class WxMpConfiguration {
    private final SubscribeHandler subscribeHandler;

    @Bean
    public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
        final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);

        // 关注事件
        newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();

        return newRouter;
    }
}
  • 微信关注事件处理类
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tfcs.web.domain.bus.BusWxMpUser;
import com.tfcs.web.service.BusWxMpUserService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 关注处理器
 *
 * @author quan
 * @date 2023/9/15 14:05
 */
@Slf4j
@Component
public class SubscribeHandler implements WxMpMessageHandler {
    @Autowired
    private BusWxMpUserService wxMpUserService;

    @Override
    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
                                    Map<String, Object> context, WxMpService weixinService,
                                    WxSessionManager sessionManager) {

        log.info("新关注用户 OPENID: " + wxMessage.getFromUser());

        // 获取微信用户基本信息
        try {
            WxMpUser userWxInfo = weixinService.getUserService()
                    .userInfo(wxMessage.getFromUser(), null);
            if (userWxInfo != null) {
                // TODO 先检查是否存在该用户,不存在再存到数据库
            }
        } catch (WxErrorException e) {
            log.error("微信公众号获取用户信息异常:", e);
            if (e.getError().getErrorCode() == 48001) {
                log.info("该公众号没有获取用户信息权限!");
            }
        }
        return null;
    }
}
  • 业务代码调用demo
// 通过unionid查出公众号openid
BusWxMpUser wxMpUser = wxMpUserService.getOne(new LambdaQueryWrapper<BusWxMpUser>()
         .eq(BusWxMpUser::getUnionid, user.getUnionid()));
 if (null != wxMpUser) {
     List<WxMpTemplateData> templateData = new ArrayList<>(5);
     // 对应消息模板的key
     templateData.add(new WxMpTemplateData("character_string2", event.getFlightNo()));
     templateData.add(new WxMpTemplateData("thing10", event.getStartPlace()));
     templateData.add(new WxMpTemplateData("thing11", event.getDestPlace()));
     templateData.add(new WxMpTemplateData("time3", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, event.getFlightStartTime())));
     templateData.add(new WxMpTemplateData("thing9", "请及时处理关注事件"));
     weChatUtil.sendTemplateMsg(wxMpUser.getOpenid(), templateData);
 }
  • 推送效果
显示全文