Java实现公众号功能开发
描述
使用Java实现公众号功能的接入,配合业务实现:
用户关注、取消关注、推送数据服务、用户在公众号信息转发人工服务等功能
本文章说明公众号代码配置实现,公众号申请,注册方式,自行查看官网说明
引入依赖
<!--微信公众号(包括订阅号和服务号)-->
<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
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;
}
}
公众号各事件处理
实现公众号事件处理类
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 {
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);
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;
@Async
public Boolean sendTemplateMsg(MpPushDTO mpPushDTO) {
log.info("【公众号告警模板信息推送】-推送信息:{}", JSONObject.toJSONString(mpPushDTO));
Long customerId = mpPushDTO.getCustomerId();
String name = mpPushDTO.getMpName();
WxMpService wxMpService = wxMpAgentService.switchoverTo(mpPushDTO.getCustomerCode());
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;
}
public WxMpTemplateMessage getWarnTemplate(MpPushDTO mpPushDTO, String templateId, String name) {
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
.toUser(mpPushDTO.getOpenId())
.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;
}
}