使用Java实现公众号功能的接入,配合业务实现:
用户关注、取消关注、推送数据服务、用户在公众号信息转发人工服务等功能
本文章说明公众号代码配置实现,公众号申请,注册方式,自行查看官网说明
引入依赖
<!--微信公众号(包括订阅号和服务号)--> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.5.0</version> </dependency>
注册公众号配置
@Slf4j @Configuration @RequiredArgsConstructor @ConditionalOnClass(WxMpService.class) @EnableConfigurationProperties() public class WxMpConfiguration { private final MpConfigMapper mpConfigMapper; private final LogHandler logHandler; private final SubscribeHandler subscribeHandler; private final UnsubscribeHandler unsubscribeHandler; private final TextMsgHandler textMsgHandler; /** * 用户配置(获取用户客户代码) */ private static Map<String, Integer> MP_USER_CONF = new HashMap<>(); public static Long getCustomId(String appid) { return Long.valueOf(MP_USER_CONF.getOrDefault(appid, 0)); } /** * 加载公众号配置,注册Bean对象 */ @Bean public WxMpService wxMpService() { //这个是数据库公众号信息配置表 List<MpConfig> mpConfigs = mpConfigMapper.selectList(Wrappers.emptyWrapper()); if (CollectionUtil.isEmpty(mpConfigs)) { log.error("读取配置为空,公众号配置类加载失败!"); return new WxMpServiceImpl(); } log.info("公众号的配置:{}", JSON.toJSONString(mpConfigs)); Map<String, WxMpConfigStorage> multiConfigStorages = new HashMap<>(16); for (MpConfig mpConfig : mpConfigs) { WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl(); configStorage.setAppId(mpConfig.getAppid()); configStorage.setSecret(mpConfig.getSecret()); configStorage.setToken(mpConfig.getToken()); configStorage.setAesKey(mpConfig.getAeskey()); multiConfigStorages.put(mpConfig.getCustomerCode(), configStorage); MP_USER_CONF.put(mpConfig.getAppid(), mpConfig.getCustomerId()); } WxMpService mpService = new WxMpServiceImpl(); mpService.setMultiConfigStorages(multiConfigStorages); return mpService; } /** * 配置公众号个事件路由,分类处理:关注、取消关注、用户发送消息 */ @Bean public WxMpMessageRouter messageRouter(WxMpService wxMpService) { final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); // 记录所有事件的日志 (异步执行) newRouter.rule().handler(this.logHandler).next(); // 关注事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT). event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler).end(); //取消关注事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT). event(WxConsts.EventType.UNSUBSCRIBE).handler(this.unsubscribeHandler).end(); //用户往公众号发送消息事件处理 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.TEXT). handler(this.textMsgHandler).end(); return newRouter; } }
公众号各事件处理
实现公众号事件处理类
/** * 微信公众号AbstractHandler */ public abstract class AbstractHandler implements WxMpMessageHandler { protected Logger logger = LoggerFactory.getLogger(getClass()); }
实现公众号日志记录处理器
/** * 微信公众号日志记录处理器 */ @Component public class LogHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { this.logger.info("【公众号】-记录请求日志,内容:{}", JSON.toJSONString(wxMessage)); return null; } }
实现公众号用户关注处理器
/** * 微信公众号用户关注 */ @Component @RequiredArgsConstructor public class SubscribeHandler extends AbstractHandler { //公众号用户表 private final MpUserParentMapper mpUserParentMapper; //小程序用户表(一般公众号和小程序配套使用) private final MiniappUserParentMapper miniappUserParentMapper; //公众号配置表 private final MpConfigMapper mpConfigMapper; @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { this.logger.info("【公众号-关注功能】-新关注用户 OPENID: " + wxMessage.getFromUser()); // 获取微信用户基本信息 try { WxMpUser userWxInfo = wxMpService.getUserService() .userInfo(wxMessage.getFromUser(), null); String appId = wxMpService.getWxMpConfigStorage().getAppId(); Long customerId = WxMpConfiguration.getCustomId(appId); logger.info("userWxInfo:{}", userWxInfo); if (userWxInfo != null) { //查询数据库,公众号用户表,是否有这个用户 MpUserParent existMpUser = mpUserParentMapper.selectOneByOpenId(userWxInfo.getOpenId()); //不存在则新增一条公众号用户记录 if (existMpUser == null) { MpUserParent mpUser = new MpUserParent(); mpUser.setOpenId(userWxInfo.getOpenId()); mpUser.setUnionId(userWxInfo.getUnionId()); mpUser.setAvatarUrl(userWxInfo.getHeadImgUrl()); mpUser.setNickName(userWxInfo.getNickname()); mpUser.setCustomerId(customerId); mpUserParentMapper.insert(mpUser); //检测是否有跟小程序账号关联 MiniappUserParent maUserParent = miniappUserParentMapper.selectOneByUnionId(mpUser.getUnionId(), customerId); if (maUserParent != null && StringUtil.isBlank(maUserParent.getMpOpenId())) { maUserParent.setMpOpenId(mpUser.getOpenId()); maUserParent.setUpdateTime(new Date()); miniappUserParentMapper.updateById(maUserParent); this.logger.info("【公众号-关注功能】-公众号账号和小程序绑定成功!unionId:{},mpOpenId:{} ", mpUser.getUnionId(), mpUser.getOpenId()); } } else { //之前有关注过的,无需重复写入,更改关注状态即可 MpUserParent updateUser = new MpUserParent(); updateUser.setMpId(existMpUser.getMpId()); updateUser.setFollowStatus(1); updateUser.setUpdateTime(new Date()); updateUser.setCustomerId(customerId); updateUser.setUnionId(userWxInfo.getUnionId()); mpUserParentMapper.updateById(updateUser); } } } catch (WxErrorException e) { if (e.getError().getErrorCode() == 48001) { this.logger.error("【公众号-关注功能】-该公众号没有获取用户信息权限!"); } logger.error("【公众号-关注功能异常】-异常信息:{}", e); } WxMpXmlOutMessage responseResult = null; try { responseResult = this.handleSpecial(wxMessage); } catch (Exception e) { this.logger.error(e.getMessage(), e); } if (responseResult != null) { return responseResult; } String appId = wxMpService.getWxMpConfigStorage().getAppId(); try { logger.info("获取公众号appid:{}", appId); MpConfig miniappConfig = mpConfigMapper.selectByAppid(appId); //关注公众号,发送消息给用户 return new TextBuilder().build("感谢关注,将持续为您推送信息", wxMessage, wxMpService); } catch (Exception e) { this.logger.error(e.getMessage(), e); } return null; } /** * 处理特殊请求,比如如果是扫码进来的,可以做相应处理 */ private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage) throws Exception { //TODO return null; } }
实现公众号用户取消关注处理器
/** * 微信公众号用户取消关注 */ @Component @RequiredArgsConstructor public class UnsubscribeHandler extends AbstractHandler { private final MpUserParentMapper mpUserParentMapper; private final MpConfigMapper mpConfigMapper; @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { String openId = wxMessage.getFromUser(); this.logger.info("取消关注用户 OPENID: " + openId); //更新关注状态为取消关注状态 UpdateWrapper<MpUserParent> updateWrapper = new UpdateWrapper(); updateWrapper.eq("openId", openId); updateWrapper.eq("followStatus", 1); MpUserParent updateMpUser = new MpUserParent(); updateMpUser.setFollowStatus(2); //不删除公众号用户信息,修改关注状态值即可 mpUserParentMapper.update(updateMpUser, updateWrapper); String appId = wxMpService.getWxMpConfigStorage().getAppId(); logger.info("获取公众号appid:{}",appId); MpConfig miniappConfig = mpConfigMapper.selectByAppid(appId); //取消关注,发送信息给用户 return new TextBuilder().build("已退订", wxMessage, wxMpService); } }
实现公众号用户发送消息处理器
@Slf4j @Component @RequiredArgsConstructor public class TextMsgHandler extends AbstractHandler { private final static Integer TEXT_TYPE = 1; @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { String content = wxMessage.getContent(); log.info("【公众号-消息】-用户:{} , 发送消息:{}, ", wxMessage.getFromUser(), content); // 获取微信用户基本信息 try { String appId = wxMpService.getWxMpConfigStorage().getAppId(); Long customerId = WxMpConfiguration.getCustomId(appId); //自动回复匹配的关键字 MpAutoReplyDTO autoReply = InitConfDataUtil.getAutoReply(customerId, content); if (autoReply != null) { Integer replyType = autoReply.getReplyType(); String reply = autoReply.getReply(); if (TEXT_TYPE.equals(replyType)) { //文字处理 return new TextBuilder().build(reply, wxMessage, wxMpService); } else { //图片处理 return new ImageBuilder().build(reply, wxMessage, wxMpService); } } else { //自动回复没有匹配的就转到人工 WxMpXmlOutMessage build = new TextBuilder().build(wxMessage.getContent(), wxMessage, wxMpService); //msgType设置固定值就转到人工:transfer_customer_service build.setMsgType("transfer_customer_service"); return build; } } catch (Exception e) { log.info("回复失败e:{}", e); } return null; } }
用户消息事件分类处理Builder
定义处理抽象类
//该类定义处理标准,后续根据业务自行扩展 public abstract class AbstractBuilder { protected final Logger logger = LoggerFactory.getLogger(getClass()); public abstract WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage, WxMpService service); }
实现处理抽象类–子类–文本消息
public class TextBuilder extends AbstractBuilder { @Override public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage, WxMpService service) { return WxMpXmlOutMessage.TEXT().content(content) .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .build(); } }
实现处理抽象类–子类–图片消息
public class ImageBuilder extends AbstractBuilder { @Override public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage, WxMpService service) { return WxMpXmlOutMessage.IMAGE().mediaId(content) .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .build(); } }
公众号模板消息推送
以上实现了Java整合公众号,后续业务扩展,需要往关注公众号的用户进行相关的消息推送,消息推送前,
需要在微信公众号申请推送模板,拿到对应的模板ID,这个有用,生成推送消息时,需要往模板填充信息,
如何申请公众号模板,自行参考下官网说明
/** * 微信公众号信息推送 */ @Slf4j @RequiredArgsConstructor @Component public class MpMsgPush { //微信的公众号推送处理类 private final WxMpService wxMpAgentService; //数据库公众号模板消息表 private final MpTemplateManage mpTemplateManage; /** * 发送微信模板信息 * * @param mpPushDTO 推送信息实体类 * @return 是否推送成功 */ @Async public Boolean sendTemplateMsg(MpPushDTO mpPushDTO) { log.info("【公众号告警模板信息推送】-推送信息:{}", JSONObject.toJSONString(mpPushDTO)); Long customerId = mpPushDTO.getCustomerId(); String name = mpPushDTO.getMpName(); //选择往哪个公众号发送模板消息,这个上面注册公众号的Bean的时候已经注入 WxMpService wxMpService = wxMpAgentService.switchoverTo(mpPushDTO.getCustomerCode()); //自己搞的一个枚举,根据当前的消息实体类信息,来判断使用哪个模板ID String templateId = TemplateConfConstant.getWXTemplateConf(customerId, mpPushTypeEnum.getValue()); // 获取模板消息接口 WxMpTemplateMessage templateMessage = mpTemplateManage.getWarnTemplate(mpPushDTO, templateId, name); if (templateMessage != null) { log.info("发送消息:{}", JSONObject.toJSONString(templateMessage)); } else { log.error("获取消息模板为空!"); return false; } String msgId = null; try { Long parentId = mpPushDTO.getParentId(); String imeiNo = mpPushDTO.getImeiNo(); // 发送模板消息 msgId = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage); } catch (Exception e) { log.error("【公众号模板信息推送】-推送失败,异常信息:", e); } log.warn("·==++--·推送公众号模板信息:{}·--++==·", msgId != null ? "成功" : "失败"); return msgId != null; } /** * 获取提醒模板 * * @param mpPushDTO * @return */ public WxMpTemplateMessage getWarnTemplate(MpPushDTO mpPushDTO, String templateId, String name) { // 发送模板消息接口 WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() // 接收者openid .toUser(mpPushDTO.getOpenId()) // 模板id .templateId(templateId) .build(); StringBuilder keyword1 = new StringBuilder(); keyword1.append(mpPushDTO.getStudentName()). append("(").append(mpPushDTO.getImeiNo()).append(")"); // 添加模板数据 templateMessage.addData(new WxMpTemplateData("first", "您好,你有一条新的提醒")) .addData(new WxMpTemplateData("keyword1", keyword1.toString())) .addData(new WxMpTemplateData("keyword2", mpPushDTO.getTemplateType())) .addData(new WxMpTemplateData("remark", name + "智能推送系统")); return templateMessage; } }
转自:https://blog.csdn.net/weixin_41451078/article/details/129818448