Java實現(xiàn)公眾號功能、關(guān)注及消息推送實例代碼
描述
使用Java實現(xiàn)公眾號功能的接入,配合業(yè)務(wù)實現(xiàn):
用戶關(guān)注、取消關(guān)注、推送數(shù)據(jù)服務(wù)、用戶在公眾號信息轉(zhuǎn)發(fā)人工服務(wù)等功能
本文章說明公眾號代碼配置實現(xiàn),公眾號申請,注冊方式,自行查看官網(wǎng)說明
引入依賴
<!--微信公眾號(包括訂閱號和服務(wù)號)-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.4.2.B</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() {
//這個是數(shù)據(jù)庫公眾號信息配置表
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;
}
/**
* 配置公眾號個事件路由,分類處理:關(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();
//用戶往公眾號發(fā)送消息事件處理
newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.TEXT).
handler(this.textMsgHandler).end();
return newRouter;
}
}
公眾號各事件處理
實現(xiàn)公眾號事件處理類
/**
* 微信公眾號AbstractHandler
*/
public abstract class AbstractHandler implements WxMpMessageHandler {
protected Logger logger = LoggerFactory.getLogger(getClass());
}
實現(xiàn)公眾號日志記錄處理器
/**
* 微信公眾號日志記錄處理器
*/
@Component
public class LogHandler extends AbstractHandler {
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService wxMpService,
WxSessionManager sessionManager) {
this.logger.info("【公眾號】-記錄請求日志,內(nèi)容:{}", JSON.toJSONString(wxMessage));
return null;
}
}
實現(xiàn)公眾號用戶關(guān)注處理器
/**
* 微信公眾號用戶關(guān)注
*/
@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("【公眾號-關(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) {
//查詢數(shù)據(jù)庫,公眾號用戶表,是否有這個用戶
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);
//檢測是否有跟小程序賬號關(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("【公眾號-關(guān)注功能】-公眾號賬號和小程序綁定成功!unionId:{},mpOpenId:{} ", mpUser.getUnionId(), mpUser.getOpenId());
}
} else {
//之前有關(guān)注過的,無需重復(fù)寫入,更改關(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("【公眾號-關(guān)注功能】-該公眾號沒有獲取用戶信息權(quán)限!");
}
logger.error("【公眾號-關(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("獲取公眾號appid:{}", appId);
MpConfig miniappConfig = mpConfigMapper.selectByAppid(appId);
//關(guān)注公眾號,發(fā)送消息給用戶
return new TextBuilder().build("感謝關(guān)注,將持續(xù)為您推送信息", wxMessage, wxMpService);
} catch (Exception e) {
this.logger.error(e.getMessage(), e);
}
return null;
}
/**
* 處理特殊請求,比如如果是掃碼進(jìn)來的,可以做相應(yīng)處理
*/
private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage) throws Exception {
//TODO
return null;
}
}
實現(xiàn)公眾號用戶取消關(guān)注處理器
/**
* 微信公眾號用戶取消關(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);
//不刪除公眾號用戶信息,修改關(guān)注狀態(tài)值即可
mpUserParentMapper.update(updateMpUser, updateWrapper);
String appId = wxMpService.getWxMpConfigStorage().getAppId();
logger.info("獲取公眾號appid:{}",appId);
MpConfig miniappConfig = mpConfigMapper.selectByAppid(appId);
//取消關(guān)注,發(fā)送信息給用戶
return new TextBuilder().build("已退訂", wxMessage, wxMpService);
}
}
實現(xiàn)公眾號用戶發(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("【公眾號-消息】-用戶:{} , 發(fā)送消息:{}, ", wxMessage.getFromUser(), content);
// 獲取微信用戶基本信息
try {
String appId = wxMpService.getWxMpConfigStorage().getAppId();
Long customerId = WxMpConfiguration.getCustomId(appId);
//自動回復(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 {
//自動回復(fù)沒有匹配的就轉(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;
}
}
用戶消息事件分類處理Builder
定義處理抽象類
//該類定義處理標(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);
}
實現(xiàn)處理抽象類–子類–文本消息
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();
}
}
實現(xiàn)處理抽象類–子類–圖片消息
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();
}
}
公眾號模板消息推送
以上實現(xiàn)了Java整合公眾號,后續(xù)業(yè)務(wù)擴(kuò)展,需要往關(guān)注公眾號的用戶進(jìn)行相關(guān)的消息推送,消息推送前,
需要在微信公眾號申請推送模板,拿到對應(yīng)的模板ID,這個有用,生成推送消息時,需要往模板填充信息,
如何申請公眾號模板,自行參考下官網(wǎng)說明
/**
* 微信公眾號信息推送
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class MpMsgPush {
//微信的公眾號推送處理類
private final WxMpService wxMpAgentService;
//數(shù)據(jù)庫公眾號模板消息表
private final MpTemplateManage mpTemplateManage;
/**
* 發(fā)送微信模板信息
*
* @param mpPushDTO 推送信息實體類
* @return 是否推送成功
*/
@Async
public Boolean sendTemplateMsg(MpPushDTO mpPushDTO) {
log.info("【公眾號告警模板信息推送】-推送信息:{}", JSONObject.toJSONString(mpPushDTO));
Long customerId = mpPushDTO.getCustomerId();
String name = mpPushDTO.getMpName();
//選擇往哪個公眾號發(fā)送模板消息,這個上面注冊公眾號的Bean的時候已經(jīng)注入
WxMpService wxMpService = wxMpAgentService.switchoverTo(mpPushDTO.getCustomerCode());
//自己搞的一個枚舉,根據(jù)當(dāng)前的消息實體類信息,來判斷使用哪個模板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("【公眾號模板信息推送】-推送失敗,異常信息:", e);
}
log.warn("·==++--·推送公眾號模板信息:{}·--++==·", 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實現(xiàn)公眾號功能、關(guān)注及消息推送的文章就介紹到這了,更多相關(guān)Java公眾號關(guān)注、消息推送內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于mybatis batch實現(xiàn)批量提交大量數(shù)據(jù)
這篇文章主要介紹了基于mybatis batch實現(xiàn)批量提交大量數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-05-05
Spring Boot中slf4j日志依賴關(guān)系示例詳解
在項目開發(fā)中,記錄日志是必做的一件事情。而當(dāng)我們使用Springboot框架時,記錄日志就變得極其簡單了。下面這篇文章主要給大家介紹了關(guān)于Spring Boot中slf4j日志依賴關(guān)系的相關(guān)資料,需要的朋友可以參考下2018-11-11
Java讀取json數(shù)據(jù)并存入數(shù)據(jù)庫的操作代碼
很多朋友問大佬們JAVA怎么把json存入數(shù)據(jù)庫啊,這一問題就把我難倒了,糾結(jié)如何操作呢,下面小編把我的經(jīng)驗分享給大家,感興趣的朋友一起看看吧2021-08-08
Springboot jdbctemplate整合實現(xiàn)步驟解析
這篇文章主要介紹了Springboot jdbctemplate整合實現(xiàn)步驟解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08
SpringCloud Feign轉(zhuǎn)發(fā)請求頭(防止session失效)的解決方案
這篇文章主要介紹了SpringCloud Feign轉(zhuǎn)發(fā)請求頭(防止session失效)的解決方案,本文給大家分享兩種解決方案供大家參考,感興趣的朋友跟隨小編一起看看吧2020-10-10

