前言
没必要说废话直接开整…
开始
(一)先直接上效果图
(二)开始前的准备工作
- 首先你得有一个公众号吧 🤷♂️微信公众号登录入口
- 登录进去之后,你要查看你的基本配置,如下图:
- 开发者ID,开发者密码必备条件
- springboot开发环境的搭建(这里就不作过多说明,无非就是些依赖包),但特别注意,要引入老王这个sdk这个依赖!!!如下:
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>${weixin-java-mp.version}</version>
</dependency>
这里你想引入什么版本随便你,我的是4.0.0,都22年了就不要引入3版本了(个人认为)
- yml文件的配置(你也可以不配置到yml中个人习惯而已)
wx:
appId: wx0bc4588855d
secret: 49fb1947bba1446bb
token: Csy2001
aesKey: rdDwt7K448074aL7Mt6QwOBPYCwIHqSaN
appId、secret、aesKey这三个值都是微信公众号的(下图也有解释),至于这个token嘛随便你怎么填
- 这里先给上面yml作一个解释说明,看下图:
- yml配置好之后,进行代码编写,首先引入老王这个源码
- github地址:sdk源码
- SpringBoot-WeChat示例参考项目
- 结构目录如下:
- controller层中的MpEntryController等一下有大用处
微信接入
(一)编写配置类
- 在wxmp包下的config层中的WxMpProperties类配置我们之前在yml中写的配置
package com.aw.wxmp.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "wx")
public class WxMpProperties {
/**
* 设置微信公众号的appid
*/
@Value("${wx.appId}")
private String appId;
/**
* 设置微信公众号的app secret
*/
@Value("${wx.secret}")
private String secret;
/**
* 设置微信公众号的token
*/
@Value(("${wx.token}"))
private String token;
/**
* 设置微信公众号的EncodingAESKey
*/
@Value("${wx.aesKey}")
private String aesKey;
}
- 在wxmp包下的controller层中的MpEntryController编写代码如下:
package com.aw.wxmp.controller;
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.stereotype.Component;
import org.springframework.web.bind.annotation.*;
/**
* 微信公众号/服务号入口
*
* @author huan.fu 2020/12/19 - 下午3:30
*/
@Component
@RestController
@Slf4j
public class MpEntryController {
@Autowired
private WxMpService wxMpService;
@Autowired
private WxMpMessageRouter wxMpMessageRouter;
/**
* 微信接入
*
* @param signature 签名
* @param timestamp 时间戳
* @param nonce 随机数
* @param echoStr 随机字符串
* @return 接入成功返回 echoStr 的值,否则随便返回
*/
@GetMapping("/WeChat/token")
public String entry(@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echoStr) {
log.info("微信公众号/服务号接入传递的参数 signature:[{}],timestamp:[{}],nonce:[{}],echostr:[{}]",
signature, timestamp, nonce, echoStr);
if (StringUtils.isAnyBlank(signature, timestamp, nonce, echoStr)) {
log.error("接收到微信认证信息,参数非法,存在为空的参数");
return "error";
}
boolean result = wxMpService.checkSignature(timestamp, nonce, signature);
log.info("微信公众号/服务号接入成功?[{}]", result);
return result ? echoStr : "error";
}
/**
* 微信回调
*
* @param requestBody 回复
* @param signature 签名
* @param timestamp 时间戳
* @param nonce 随机数
* @param openid 用户id
* @param encType 未知参数
* @param msgSignature 未知参数
* @return xml
*/
@PostMapping("/WeChat/token")
public String entryCallback(@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("\n接收微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
// + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
// openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
if (!wxMpService.checkSignature(timestamp, nonce, signature)) {
throw new IllegalArgumentException("非法请求,可能属于伪造的请求!");
}
String out = null;
if (encType == null) {
// 明文传输的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
WxMpXmlOutMessage outMessage = this.wxMpMessageRouter.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toXml();
} else if ("aes".equalsIgnoreCase(encType)) {
// aes加密的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxMpService.getWxMpConfigStorage(),
timestamp, nonce, msgSignature);
log.info("\n消息解密后内容为:\n[{}] ", inMessage.toString());
WxMpXmlOutMessage outMessage = this.wxMpMessageRouter.route(inMessage);
if (outMessage == null) {
return "";
}
out = outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage());
}
log.info("\n组装回复信息:[{}]", out);
return out;
}
}
- 到了这一步就可以接入了但是因为我们的地址是localhost,微信服务器无法找到,所以我们要借助一个内网穿透工具👉🏻内网穿透natapp,这个不会用的话去哔哩哔哩学一下很简单的
- 将上面这个地址复制到你微信公众号里面(先要运行你的项目!!!),如下图:
- 不过注意的是你MpEntryController这个类中的请求参数和我的是否一致如果不是填你的
如:http://caosiyu.nat300.top/WeChat/token(这是我的)
你:内网地址+你的请求参数
GetMapping与PostMapping的请求参数一点要一致!!!,后面要用到你可以自己定义但是两个请求方式中的参数一定要一样
- 当上面弹出配置成功,就代表接入成功了
消息发送
消息发送前的配置
- 首先你还是要到公众号上添加一条模板
- 将模板Id的值复制到yml中(也可以写在工具类中,随便你)
wechat:
templateId: 你的模板id
color: 这个可以不配就是文字的颜色你想要骚一点就找16进制的就可以了默认为黑色
url: 这个可以不配点击模板跳转的url
package com.aw.common.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WeChatMsgPushConfig {
/**
* 设置要取用的公众号模板
*/
@Value("${wechat.templateId}")
private String templateId;
//颜色 、url我没配,你要是配了自己写上
}
- 在消息发送核心代码之前还有要在wxmp包下的config层中的WxMpConfiguration类加上一段代码,如下
package com.aw.wxmp.config;
import com.aw.wxmp.handler.*;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE;
import static me.chanjar.weixin.common.api.WxConsts.EventType.UNSUBSCRIBE;
import static me.chanjar.weixin.common.api.WxConsts.EventType;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType;
import static me.chanjar.weixin.mp.constant.WxMpEventConstants.CustomerService.*;
@AllArgsConstructor
@Configuration
public class WxMpConfiguration {
@Autowired
private ImgHandler imgHandler;
@Autowired
private TextMsgHandler textMsgHandler;
@Autowired
private KfSessionHandler kfSessionHandler;
@Autowired
private MenuHandler menuHandler;
@Autowired
private ScanHandler scanHandler;
@Autowired
private MsgHandler msgHandler;
@Autowired
private LogHandler logHandler;
@Autowired
private WxMpProperties wxMpProperties;
@Autowired
private SubscribeHandler subscribeHandler;
@Autowired
private UnsubscribeHandler unsubscribeHandler;
@Bean
public WxMpService wxMpService() {
WxMpServiceImpl wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
// 设置多个微信公众号的配置
// wxMpService.setMultiConfigStorages();
return wxMpService;
}
/**
* 这个地方的配置是保存在本地,生产环境需要自己扩展,可以保存在Redis中等等
*
* @return WxMpConfigStorage
*/
public WxMpConfigStorage wxMpConfigStorage() {
WxMpDefaultConfigImpl storage = new WxMpDefaultConfigImpl();
storage.setAppId(wxMpProperties.getAppId());
storage.setSecret(wxMpProperties.getSecret());
storage.setAesKey(wxMpProperties.getAesKey());
storage.setToken(wxMpProperties.getToken());
return storage;
这里是加的!!!!
}
@Bean
public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
// 记录所有事件的日志 (异步执行)
newRouter.rule().handler(this.logHandler).next();
// 接收客服会话管理事件
newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION)
.handler(this.kfSessionHandler).end();
newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION)
.handler(this.kfSessionHandler).end();
newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION)
.handler(this.kfSessionHandler).end();
// 自定义菜单事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.CLICK).handler(this.menuHandler).end();
// 关注事件
newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end();
// 取消关注事件
newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end();
// 扫码事件
newRouter.rule().async(false).msgType(EVENT).event(EventType.SCAN).handler(this.scanHandler).end();
// 文本消息处理
newRouter.rule().async(false).msgType(XmlMsgType.TEXT).handler(this.textMsgHandler).end();
// 图片消息处理
newRouter.rule().async(false).msgType(XmlMsgType.IMAGE).handler(this.imgHandler).end();
// 默认
newRouter.rule().async(false).handler(this.msgHandler).end();
return newRouter;
}
}
openId的获取方法
不知道openId是什么的,可以去微信文档或者百度搜索一下
- F12😂?(也可以)
- 同样在老王这个源码中也有,下面图片中的handler层中的类都可以取到
- 上代码
package com.aw.wxmp.handler;
import com.aw.common.utils.WxMsgSendUserUtil;
import com.aw.entity.FocusUser;
import com.aw.mapper.FocusUserMapper;
import com.aw.wxmp.builder.TextBuilder;
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.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;
@Component
@Slf4j
public class SubscribeHandler extends AbstractHandler {
@Autowired
private FocusUserMapper focusUserMapper;
@Override
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage,
Map<String, Object> context, WxMpService weixinService,
WxSessionManager sessionManager) throws WxErrorException {
this.logger.info("新关注用户 OPENID: " + wxMessage.getFromUser());
用户关注之后会触发这个类,其他类也都可以例如:对话,取关什么的,看你业务需求
log.info("这就是你想要的openId{}",wxMessage.getOpenId());
// 获取微信用户基本信息
try {
WxMpUser userWxInfo = weixinService.getUserService()
.userInfo(wxMessage.getFromUser(), null);
if (userWxInfo != null) {
// TODO 可以添加关注用户到本地数据库
这里逻辑代码不展示
}
} catch (WxErrorException e) {
if (e.getError().getErrorCode() == 48001) {
this.logger.info("该公众号没有获取用户信息权限!");
}
}
WxMpXmlOutMessage responseResult = null;
try {
responseResult = this.handleSpecial(wxMessage);
} catch (Exception e) {
this.logger.error(e.getMessage(), e);
}
if (responseResult != null) {
return responseResult;
}
try {
return new TextBuilder().build(WxMsgSendUserUtil.USER_SUBSCRIBE, wxMessage, weixinService);
} catch (Exception e) {
this.logger.error(e.getMessage(), e);
}
return null;
}
/**
* 处理特殊请求,比如如果是扫码进来的,可以做相应处理
*/
private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage)
throws Exception {
//TODO
return null;
}
}
消息发送核心代码
我这是将他这核心代码写入了一个工具类里面
package com.aw.common.utils;
import com.aw.common.config.WeChatMsgPushConfig;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class WeChatMsgPushUtil {
@Autowired
private WxMpService wxMpService;
@Autowired
private WeChatMsgPushConfig weChatMsgPushConfig; //这个是什么配置的模板类
/**
* 对用户发送消息的接口
* @param tradeName 公司名称
* @param subscribeTime 预约时间
* @param subscribeNum 预约数量
* @param Kind 预约种类
* @return boolean
*/
public Boolean sendOrderMsg(String tradeName,
String subscribeTime,
String subscribeNum,
String Kind,
String openId,
String urlRel) {
WxMpTemplateMessage templateMessage=WxMpTemplateMessage.builder()
.toUser(openId) //这个openid是你要推送的人,如果你不知道怎么拿用户的openid可以来往上面看看
.templateId(weChatMsgPushConfig.getTemplateId())//这里是你要发送消息的模板id
.url(urlRel)//这里是你的配置的url 例如:www.baidu.com,
.build();
templateMessage
.addData(new WxMpTemplateData(WxMsgSendUserUtil.FIRST,WxMsgSendUserUtil.FIRST_CONTENT,这里你要是刚才配置了文字的颜色导入这个color,下面同理))
.addData(new WxMpTemplateData(WxMsgSendUserUtil.KEYWORD_ONE,tradeName))
.addData(new WxMpTemplateData(WxMsgSendUserUtil.KEYWORD_TWO,subscribeTime))
.addData(new WxMpTemplateData(WxMsgSendUserUtil.KEYWORD_THREE,subscribeNum))
.addData(new WxMpTemplateData(WxMsgSendUserUtil.KEYWORD_FOUR,Kind))
.addData(new WxMpTemplateData(WxMsgSendUserUtil.REMARK,WxMsgSendUserUtil.REMARK_CONTENT));
String msgPush=null;
try {
msgPush = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
throw new RuntimeException(e);
}
log.warn("·==++--·推送微信模板信息:{}·--++==·", msgPush != null ? "成功" : "失败");
return msgPush!=null;
}
}
- 编写一个业务类调用这个接口,然后再controller层中调用这个业务类就可以发送消息了,具体不展示了
- 如果没有公众号可以用微信公众平台接口测试帐号申请有这个需要在下方评论,后期出