由于微信小程序长期订阅的消息模板和下发统一消息推送接口全部失效以后,对于小程序的消息推送可以往公众号推可以使用本文章方案。在网上看了挺多方案,有用用户列表做匹配的等,最终觉得通过关注事件触发的方案是最省事。
1、注册服务号(订阅号是不可以推送的)与小程序,两者都需要认证并且认证主体是一致
2、注册账号(该账号也需要认证),绑定小程序与公众号
通过在开放平台绑定的公众号与小程序后,我们在调用微信接口的时候会返回unionid,这个unionid就是推送的关键。
这里使用第三方工具包Wx-Java(非常方便),直接实现推送代码。具体源码可以浏览https://gitee.com/binary/weixin-java-tools
# 公众号配置
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;
}
}
// 通过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);
}