1. 业务开发
1.1. 事件推送
1.1.1. 获取带参数的二维码
net.mingsoft.mweixin.biz.impl.QrCodeBizImpl
场景二维码管理实体作为入参,每个字段都应有值,返回值是一个存储二维码图片字节和ticket
public Map qrCodeCreateTmpTicket(QrCodeEntity qrCodeEntity) {
if (qrCodeEntity == null && StringUtils.isEmpty(qrCodeEntity.getWeixinId())) {
throw new BusinessException("未找到配置");
}
//获取微信配置
WeixinEntity weixin = (WeixinEntity)weixinBiz.getEntity(Integer.parseInt(qrCodeEntity.getWeixinId()));
if (weixin == null) {
throw new BusinessException("未找到配置");
}
PortalService service = new PortalService().build(weixin);
WxMpQrcodeService qrcodeService = service.getQrcodeService();
WxMpQrCodeTicket wxMpQrCodeTicket = null;
try {
wxMpQrCodeTicket = qrcodeService.qrCodeCreateTmpTicket(qrCodeEntity.getQrSceneStr(), qrCodeEntity.getQrExpireSeconds());
File file = qrcodeService.qrCodePicture(wxMpQrCodeTicket);
HashMap<String, Object> map = new HashMap<>();
map.put("qCode",FileUtil.readBytes(file));
map.put("key", wxMpQrCodeTicket.getTicket());
return map;
} catch (WxErrorException e) {
e.printStackTrace();
throw new BusinessException(e.getMessage());
}
}
[!tip] 返回值中的key可以用于前台页面检测这次二维码的状态
<img :src="previewUrl" width="300" height="300"/>
···
var codeImg = "data:image/jpeg;base64," + res.data.qCode;
···
1.1.2. 关注及扫码等事件处理
对于一些特殊事件,微信服务器会推送消息到系统,例如用户在扫描携带参数的二维码时,分为用户已关注再扫码和用户未关注再扫码关注两种情况。
用户已关注时扫码对应扫码事件,由net.mingsoft.mweixin.service.ScanService做处理; 用户未关注时扫码关注对应关注事件,由net.mingsoft.mweixin.service.SubscribeService做处理;
SubscribeService: 关注服务类第一步保存了关注用户的信息,用户扫码关注或者主动查询关注没有区别;第二步检测是不是带参数二维码,带参数二维码通常在关注和扫码两种事件上的业务是高度重合的, 所以当检测成功时调用扫码事件处理器去处理相关业务,开发者可以自行增加场景二维码管理,并添加与其beanName相对应的处理器;第三步是用于一二步仍不能满足开发业务需求时, 需要额外扩展的处理,只需要写一个实现类去实现net.mingsoft.mweixin.handle.ISubscribeHandle接口即可,注意和扫码处理器接口不同,关注处理器只允许一个实现类
···
if (userWxInfo != null) {
// TODO 可以添加关注用户到本地
this.logger.debug("保存用户信息");
int weixinId = weixinService.getWeixin().getIntId();
weixinPeopleBiz.saveOrUpdate(userWxInfo, weixinId);
}
WxMpXmlOutMessage responseResult = null;
try {
//处理特殊请求,比如如果是扫码进来的,可以做相应处理
this.logger.info("处理特殊请求,比如如果是扫码进来的,可以做相应处理");
// todo 未关注扫码也获取实体的beanName走扫码事件统一处理
IScanHandle scanHandle = scanService.getScanHandleBySceneStrAndWeixinId(wxMessage.getEventKey().replace("qrscene_", ""), weixin.getId());
if (scanHandle!=null){
responseResult = scanHandle.handleSpecial(wxMessage,weixinService);
}
// todo 如果还想在关注的时候做一些事情,请写一个类实现ISubscribeHandle
if (subscribeHandle!=null){
subscribeHandle.handleSpecial(wxMessage,weixinService);
}
} catch (Exception e) {
logger.debug("扫码实现类或关注实现类处理异常");
e.printStackTrace();
}
if (responseResult != null) {
return responseResult;
}
···
ScanService: 扫码服务类中没有统一要处理的业务代码,默认每个不同的场景二维码对应一个不同的处理器,开发者在新增场景二维码时,应在业务代码中新增具体的处理实现类。
···
WeixinEntity weixin = ((PortalService) wxMpService).getWeixin();
PortalService weixinService = (PortalService) wxMpService;
// 获取扫码处理对象,为null不会执行
IScanHandle scanHandle = getScanHandleBySceneStrAndWeixinId(wxMessage.getEventKey(), weixin.getId());
if (scanHandle!=null){
return scanHandle.handleSpecial(wxMessage,weixinService);
}
return null;
···
1.1.3. 其余事件处理
其他不同的事件基本都有默认处理的业务代码,基本都能满足业务需求,如果有需要可以参考上面两种方式进行二次开发。
1.2. 微信消息模板
插件在微信官方的模板字段基础上统一了字段前缀规范,并且新增了两个字段,模板编码和模板关键词,在做业务开发时不需要关心微信本身的字段, 模板编码作为唯一标识,模板关键词作为参数传递;模板编码字段值全表唯一,不可重复
[!tip] 初次使用需要先同步微信消息模板,然后自定义模板编码和模板关键词,后续再同步,同一个模板的编码和关键词字段会保留
1.2.1. 定义规范
对于模板编码字段,插件没有做任何约束,命名时能见名知意即可;
模板关键词字段需要严格按照规范定义,需遵守 微信关键词顺序对应关键词参数顺序
注意关键词中必须使用${}取参数,命名不做约束,顺序按照模板内容占位顺序依次排列,占位关键词之间以逗号隔开
1.2.2. 业务开发范例
// 参数组装 注意微信消息已经不再发送头尾信息
Map wordList = new HashMap();
wordList.put("first", "这是头信息");
wordList.put("keyword1", "这是关键词1");
wordList.put("keyword2", "这是关键词2");
wordList.put("remark", "这是备注信息");
TemplateBean templateBean = new TemplateBean();
templateBean.setTemplateCode("test-template-code");
// openId错误会发送失败
templateBean.setOpenIds("openId1,openId2,openId3");
templateBean.setWordList(wordList);
// 校验参数,组装消息,注意在实际业务中做参数和查询结果的校验
TemplateEntity template = templateBiz.getOne(new LambdaQueryWrapper<TemplateEntity>().eq(TemplateEntity::getTemplateCode, templateBean.getTemplateCode()));
List<WxMpTemplateData> wxMpTemplateData = TemplateMessageUtil.buildParams(template.getTemplateContent(), template.getTemplateKeyword(), templateBean.getWordList());
String[] receives = templateBean.getOpenIds().split(",");
// 根据模板获取微信
WeixinEntity weixin = weixinBiz.getById(template.getWeixinId());
PortalService service = new PortalService().build(weixin);
TemplateMessageUtil.send(service,wxMpTemplateData,templateBean.getUrl(), template.getTemplateId(),receives);
1.3. 移动端自动授权登录
移动端登录核心点在于授权,通过授权获取到用户信息再核对进行登录
场景分为两种:1.用户访问会员层接口 2.用户访问非会员层
1.3.1. 会员层接口自动授权登录
会员层接口拥有共同特征,接口路径都是由 /people 开头,根据这个特点可以用拦截器来实现自动授权
代码片段示例参考:
···
if (BasicUtil.isMobileDevice() && BasicUtil.isWechatBrowser()) {
//组装请求参数地址
response.sendRedirect(BasicUtil.getUrl()+"/mweixin/oauth/redirectUrl.do?weixinNo="+weixinNo+"&url="+URLUtil.encodeAll(BasicUtil.buildRequestUrl(request)));
return true;
}
···
1.3.2. 非会员层授权自动登录
如果出现非会员层也要获取用户信息的需求情况,例如菜单上有非会员层网站入口要组装成授权地址加菜单实际地址:
BasicUtil.getUrl()+"/mweixin/oauth/redirectUrl.do?weixinNo="+weixinNo+"&url="+ 任意地址(需经过url转码)
例如上面这个地址作为菜单地址,用户会经过授权再回到实际要访问的页面,这样就可以拿到用户信息了
1.4. 页面分享
1.4.1. 页面制作
页面分享首先需要引入 http://res.wx.qq.com/open/js/jweixin-1.6.0.js
用来调用微信js接口。
同时需要引入微信插件中的 /static/mweixin/weixin.js
js文件来快速请求签名算法接口,在页面自定义标题描述以及图片就可以达到diy分享效果
示例:
···
<#include "head-file.htm" />
<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script src="/static/mweixin/weixin.js"></script>
···
<script>
var shareConfig = {
weixinNo: "weixinNo",// 微信号
title: "分享卡片标题",// 标题随意
desc: "分享卡片描述",// 分享朋友圈没有描述
link: location.href,// 获取签名必须要用本页面地址,不一致会导致签名失败
imgUrl: "{ms:global.host/}template/1/default//images/bg-saas.jpg",// 图片路径必须要是绝对路径
}
ms.weixin.share(shareConfig);
</script>
[!tip] 示例这段js代码通常情况不需要放到vue实例中,用js标签包裹即可
1.4.2. 数据统计
如果还需要数据统计功能,开发者需要在现有基础上进行小幅度二开
二次开发流程可以参考如下步骤:
1.增加一个会员层接口当做分享页面的入口来统计分享的点击记录(需注意参数合法性),处理完数据后再转发到实际地址
2.在获取签名时,可以将返回的签名实体中的url重新组装,将url当做参数转码拼接在分享入口的接口地址后面,这样其他用户点击的时候就会是访问分享入口
[!tip] 注意在写接口时要考虑微信重复请求的情况,解决办法就是给个返回值
微信插件基本基本的接口,具体参考 swagger
接口文档。