Java實(shí)現(xiàn)公眾號(hào)功能、關(guān)注及消息推送實(shí)例代碼
描述
使用Java實(shí)現(xiàn)公眾號(hào)功能的接入,配合業(yè)務(wù)實(shí)現(xiàn):
用戶關(guān)注、取消關(guān)注、推送數(shù)據(jù)服務(wù)、用戶在公眾號(hào)信息轉(zhuǎn)發(fā)人工服務(wù)等功能
本文章說(shuō)明公眾號(hào)代碼配置實(shí)現(xiàn),公眾號(hào)申請(qǐng),注冊(cè)方式,自行查看官網(wǎng)說(shuō)明
引入依賴(lài)
<!--微信公眾號(hào)(包括訂閱號(hào)和服務(wù)號(hào))--> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.4.2.B</version> </dependency>
注冊(cè)公眾號(hào)配置
@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)); } /** * 加載公眾號(hào)配置,注冊(cè)Bean對(duì)象 */ @Bean public WxMpService wxMpService() { //這個(gè)是數(shù)據(jù)庫(kù)公眾號(hào)信息配置表 List<MpConfig> mpConfigs = mpConfigMapper.selectList(Wrappers.emptyWrapper()); if (CollectionUtil.isEmpty(mpConfigs)) { log.error("讀取配置為空,公眾號(hào)配置類(lèi)加載失??!"); return new WxMpServiceImpl(); } log.info("公眾號(hào)的配置:{}", 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; } /** * 配置公眾號(hào)個(gè)事件路由,分類(lèi)處理:關(guān)注、取消關(guān)注、用戶發(fā)送消息 */ @Bean public WxMpMessageRouter messageRouter(WxMpService wxMpService) { final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); // 記錄所有事件的日志 (異步執(zhí)行) newRouter.rule().handler(this.logHandler).next(); // 關(guān)注事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT). event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler).end(); //取消關(guān)注事件 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT). event(WxConsts.EventType.UNSUBSCRIBE).handler(this.unsubscribeHandler).end(); //用戶往公眾號(hào)發(fā)送消息事件處理 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.TEXT). handler(this.textMsgHandler).end(); return newRouter; } }
公眾號(hào)各事件處理
實(shí)現(xiàn)公眾號(hào)事件處理類(lèi)
/** * 微信公眾號(hào)AbstractHandler */ public abstract class AbstractHandler implements WxMpMessageHandler { protected Logger logger = LoggerFactory.getLogger(getClass()); }
實(shí)現(xiàn)公眾號(hào)日志記錄處理器
/** * 微信公眾號(hào)日志記錄處理器 */ @Component public class LogHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { this.logger.info("【公眾號(hào)】-記錄請(qǐng)求日志,內(nèi)容:{}", JSON.toJSONString(wxMessage)); return null; } }
實(shí)現(xiàn)公眾號(hào)用戶關(guān)注處理器
/** * 微信公眾號(hào)用戶關(guān)注 */ @Component @RequiredArgsConstructor public class SubscribeHandler extends AbstractHandler { //公眾號(hào)用戶表 private final MpUserParentMapper mpUserParentMapper; //小程序用戶表(一般公眾號(hào)和小程序配套使用) private final MiniappUserParentMapper miniappUserParentMapper; //公眾號(hào)配置表 private final MpConfigMapper mpConfigMapper; @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) throws WxErrorException { this.logger.info("【公眾號(hào)-關(guān)注功能】-新關(guān)注用戶 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) { //查詢(xún)數(shù)據(jù)庫(kù),公眾號(hào)用戶表,是否有這個(gè)用戶 MpUserParent existMpUser = mpUserParentMapper.selectOneByOpenId(userWxInfo.getOpenId()); //不存在則新增一條公眾號(hào)用戶記錄 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); //檢測(cè)是否有跟小程序賬號(hào)關(guān)聯(lián) 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("【公眾號(hào)-關(guān)注功能】-公眾號(hào)賬號(hào)和小程序綁定成功!unionId:{},mpOpenId:{} ", mpUser.getUnionId(), mpUser.getOpenId()); } } else { //之前有關(guān)注過(guò)的,無(wú)需重復(fù)寫(xiě)入,更改關(guān)注狀態(tài)即可 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("【公眾號(hào)-關(guān)注功能】-該公眾號(hào)沒(méi)有獲取用戶信息權(quán)限!"); } logger.error("【公眾號(hào)-關(guān)注功能異常】-異常信息:{}", 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("獲取公眾號(hào)appid:{}", appId); MpConfig miniappConfig = mpConfigMapper.selectByAppid(appId); //關(guān)注公眾號(hào),發(fā)送消息給用戶 return new TextBuilder().build("感謝關(guān)注,將持續(xù)為您推送信息", wxMessage, wxMpService); } catch (Exception e) { this.logger.error(e.getMessage(), e); } return null; } /** * 處理特殊請(qǐng)求,比如如果是掃碼進(jìn)來(lái)的,可以做相應(yīng)處理 */ private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage) throws Exception { //TODO return null; } }
實(shí)現(xiàn)公眾號(hào)用戶取消關(guān)注處理器
/** * 微信公眾號(hào)用戶取消關(guān)注 */ @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("取消關(guān)注用戶 OPENID: " + openId); //更新關(guān)注狀態(tài)為取消關(guān)注狀態(tài) UpdateWrapper<MpUserParent> updateWrapper = new UpdateWrapper(); updateWrapper.eq("openId", openId); updateWrapper.eq("followStatus", 1); MpUserParent updateMpUser = new MpUserParent(); updateMpUser.setFollowStatus(2); //不刪除公眾號(hào)用戶信息,修改關(guān)注狀態(tài)值即可 mpUserParentMapper.update(updateMpUser, updateWrapper); String appId = wxMpService.getWxMpConfigStorage().getAppId(); logger.info("獲取公眾號(hào)appid:{}",appId); MpConfig miniappConfig = mpConfigMapper.selectByAppid(appId); //取消關(guān)注,發(fā)送信息給用戶 return new TextBuilder().build("已退訂", wxMessage, wxMpService); } }
實(shí)現(xiàn)公眾號(hào)用戶發(fā)送消息處理器
@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("【公眾號(hào)-消息】-用戶:{} , 發(fā)送消息:{}, ", wxMessage.getFromUser(), content); // 獲取微信用戶基本信息 try { String appId = wxMpService.getWxMpConfigStorage().getAppId(); Long customerId = WxMpConfiguration.getCustomId(appId); //自動(dòng)回復(fù)匹配的關(guān)鍵字 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 { //自動(dòng)回復(fù)沒(méi)有匹配的就轉(zhuǎn)到人工 WxMpXmlOutMessage build = new TextBuilder().build(wxMessage.getContent(), wxMessage, wxMpService); //msgType設(shè)置固定值就轉(zhuǎn)到人工:transfer_customer_service build.setMsgType("transfer_customer_service"); return build; } } catch (Exception e) { log.info("回復(fù)失敗e:{}", e); } return null; } }
用戶消息事件分類(lèi)處理Builder
定義處理抽象類(lèi)
//該類(lèi)定義處理標(biāo)準(zhǔn),后續(xù)根據(jù)業(yè)務(wù)自行擴(kuò)展 public abstract class AbstractBuilder { protected final Logger logger = LoggerFactory.getLogger(getClass()); public abstract WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage, WxMpService service); }
實(shí)現(xiàn)處理抽象類(lèi)–子類(lèi)–文本消息
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(); } }
實(shí)現(xiàn)處理抽象類(lèi)–子類(lèi)–圖片消息
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(); } }
公眾號(hào)模板消息推送
以上實(shí)現(xiàn)了Java整合公眾號(hào),后續(xù)業(yè)務(wù)擴(kuò)展,需要往關(guān)注公眾號(hào)的用戶進(jìn)行相關(guān)的消息推送,消息推送前,
需要在微信公眾號(hào)申請(qǐng)推送模板,拿到對(duì)應(yīng)的模板ID,這個(gè)有用,生成推送消息時(shí),需要往模板填充信息,
如何申請(qǐng)公眾號(hào)模板,自行參考下官網(wǎng)說(shuō)明
/** * 微信公眾號(hào)信息推送 */ @Slf4j @RequiredArgsConstructor @Component public class MpMsgPush { //微信的公眾號(hào)推送處理類(lèi) private final WxMpService wxMpAgentService; //數(shù)據(jù)庫(kù)公眾號(hào)模板消息表 private final MpTemplateManage mpTemplateManage; /** * 發(fā)送微信模板信息 * * @param mpPushDTO 推送信息實(shí)體類(lèi) * @return 是否推送成功 */ @Async public Boolean sendTemplateMsg(MpPushDTO mpPushDTO) { log.info("【公眾號(hào)告警模板信息推送】-推送信息:{}", JSONObject.toJSONString(mpPushDTO)); Long customerId = mpPushDTO.getCustomerId(); String name = mpPushDTO.getMpName(); //選擇往哪個(gè)公眾號(hào)發(fā)送模板消息,這個(gè)上面注冊(cè)公眾號(hào)的Bean的時(shí)候已經(jīng)注入 WxMpService wxMpService = wxMpAgentService.switchoverTo(mpPushDTO.getCustomerCode()); //自己搞的一個(gè)枚舉,根據(jù)當(dāng)前的消息實(shí)體類(lèi)信息,來(lái)判斷使用哪個(gè)模板ID String templateId = TemplateConfConstant.getWXTemplateConf(customerId, mpPushTypeEnum.getValue()); // 獲取模板消息接口 WxMpTemplateMessage templateMessage = mpTemplateManage.getWarnTemplate(mpPushDTO, templateId, name); if (templateMessage != null) { log.info("發(fā)送消息:{}", JSONObject.toJSONString(templateMessage)); } else { log.error("獲取消息模板為空!"); return false; } String msgId = null; try { Long parentId = mpPushDTO.getParentId(); String imeiNo = mpPushDTO.getImeiNo(); // 發(fā)送模板消息 msgId = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage); } catch (Exception e) { log.error("【公眾號(hào)模板信息推送】-推送失敗,異常信息:", e); } log.warn("·==++--·推送公眾號(hào)模板信息:{}·--++==·", msgId != null ? "成功" : "失敗"); return msgId != null; } /** * 獲取提醒模板 * * @param mpPushDTO * @return */ public WxMpTemplateMessage getWarnTemplate(MpPushDTO mpPushDTO, String templateId, String name) { // 發(fā)送模板消息接口 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(")"); // 添加模板數(shù)據(jù) templateMessage.addData(new WxMpTemplateData("first", "您好,你有一條新的提醒")) .addData(new WxMpTemplateData("keyword1", keyword1.toString())) .addData(new WxMpTemplateData("keyword2", mpPushDTO.getTemplateType())) .addData(new WxMpTemplateData("remark", name + "智能推送系統(tǒng)")); return templateMessage; } }
總結(jié)
到此這篇關(guān)于Java實(shí)現(xiàn)公眾號(hào)功能、關(guān)注及消息推送的文章就介紹到這了,更多相關(guān)Java公眾號(hào)關(guān)注、消息推送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
兩種實(shí)現(xiàn)Java類(lèi)隔離加載的方法
這篇文章主要介紹了兩種實(shí)現(xiàn)Java類(lèi)隔離加載的方法,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下2021-02-02Spring?Boot自動(dòng)配置源碼實(shí)例解析
Spring Boot作為Java領(lǐng)域最為流行的快速開(kāi)發(fā)框架之一,其核心特性之一就是其強(qiáng)大的自動(dòng)配置機(jī)制,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot自動(dòng)配置源碼的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08Java8 stream 中利用 groupingBy 進(jìn)行多字段分組求和案例
這篇文章主要介紹了Java8 stream 中利用 groupingBy 進(jìn)行多字段分組求和案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Java8中新特性O(shè)ptional、接口中默認(rèn)方法和靜態(tài)方法詳解
Java 8 已經(jīng)發(fā)布很久了,很多報(bào)道表明Java 8 是一次重大的版本升級(jí)。下面這篇文章主要給大家介紹了關(guān)于Java8中新特性O(shè)ptional、接口中默認(rèn)方法和靜態(tài)方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2017-12-12Java實(shí)現(xiàn)在Word中嵌入多媒體(視頻、音頻)文件
這篇文章主要介紹了Java如何實(shí)現(xiàn)在Word中嵌入多媒體(視頻、音頻)文件,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)java有一定的幫助,感興趣的同學(xué)可以了解一下2021-12-12