该部分内容包括:
- 公众号的申请、登陆以及绑定用户
- 公众号菜单的推送和删除
- 内网穿透工具的使用
公众号申请
打开微信公众平台,登陆:
菜单推送
实现效果
工具依赖
使用工具完成菜单推送、消息推送等功能。
<dependency> <groupId>com.github.binarywang</groupId> <artifactId>wx-java-mp-spring-boot-starter</artifactId> <version>4.1.0</version> </dependency>
|
小程序配置
拷贝微信公众平台的 appID 和 appsecret
application-dev.yamlwx: mp: app-id: wxxxxxxxxxxxxxxxxx secret: xxxxxxxxxxxxxxxxxxxxxx
|
URL设置
公众号菜单的类型一共有三种:
- view:表示网页类型
- click:表示点击类型
- miniprogram:表示小程序类型
我们需要设置网页类型的跳转URL,可以在配置文件中固定
application-dev.yamlwechat: prefix: http://vvvckt.natappfree.cc/oa/#
|
代码实现
MenuServiceImpl.java@Service public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
private final WxMpService wxMpService;
@Value("${wechat.prefix}") private String prefix;
public MenuServiceImpl(WxMpService wxMpService) { this.wxMpService = wxMpService; }
@Override public List<Menu> listMenu() { List<Menu> menuList = baseMapper.selectList(null); List<Menu> parents = menuList.stream() .filter(menu -> menu.getParentId() == 0) .collect(Collectors.toList()); ArrayList<Menu> menus = new ArrayList<>(); for (Menu menu : parents) { List<Menu> children = menuList.stream() .filter(item -> Objects.equals(item.getParentId(), menu.getId())) .collect(Collectors.toList()); menu.setChildren(children); menus.add(menu); } return menus; }
@Override public void syncMenu() throws WxErrorException { List<Menu> menuList = this.listMenu(); JSONArray buttonList = new JSONArray(); for (Menu menu : menuList) { JSONObject button = new JSONObject(); button.put("name", menu.getName()); if (CollectionUtils.isEmpty(menu.getChildren())) { button.put("type", menu.getType()); button.put("url", prefix + menu.getUrl()); } else { JSONArray subButton = new JSONArray(); for (Menu child : menu.getChildren()) { JSONObject view = new JSONObject(); String type = child.getType(); view.put("type", type); view.put("name", child.getName()); if (type.equals("click")) { view.put("key", child.getMeunKey()); } else { view.put("url", prefix + child.getUrl()); } subButton.add(view); } button.put("sub_button", subButton); } buttonList.add(button); } JSONObject button = new JSONObject(); button.put("button", buttonList);
wxMpService.getMenuService().menuCreate(button.toString()); }
@Override public void removeMenu() throws WxErrorException { wxMpService.getMenuService().menuDelete(); } }
|
MenuServiceImpl 包括查询菜单,同步菜单,删除菜单功能。
官方对自定义菜单格式要求如下:
{ "button":[ { "type":"click", "name":"今日歌曲", "key":"V1001_TODAY_MUSIC" }, { "name":"菜单", "sub_button":[ { "type":"view", "name":"搜索", "url":"http://www.soso.com/" }, { "type":"miniprogram", "name":"wxa", "url":"http://mp.weixin.qq.com", "appid":"wx286b93c14bbf93aa", "pagepath":"pages/lunar/index" }, { "type":"click", "name":"赞一下我们", "key":"V1001_GOOD" }] }] }
|
授权登陆
创建登陆页面,负责判断是否需要授权以及获取token
等信息。
授权唤起页面:首页(无token) > 登陆页 > 授权页(微信官方) > 登陆页 > 首页。
详细流程:
完整的代码如下:
Controller:WechatController
Vue:login.vue
配置回调页面域名
在公众平台 > 网页服务 > 网页账号 > 修改,我们回调的页面是前端的登陆页面,这里填写前端页面的URI。
获取code
按照官方要求向其服务发送请求,携带redirect_url
等参数,该请求会返回code
等信息。
请求 URL 这里交给服务器进行拼接,Controller代码如下:
@GetMapping("/authorize") public Map<String, String> authorize(@RequestParam("redirect_url") String returnUrl) { String redirectURL = wxMpService.getOAuth2Service() .buildAuthorizationUrl(URLDecoder.decode(returnUrl), WxConsts.OAuth2Scope.SNSAPI_USERINFO, null); HashMap<String, String> map = new HashMap<>(); map.put("redirectUrl", redirectURL); return map; }
|
前端拿到redirectUrl
之后,直接发起请求即可
const REDIRECT_URI = window.location.href; const REDIRECT_URI_EC = encodeURIComponent(REDIRECT_URI); jump2Auth(REDIRECT_URI_EC).then((res) => { window.location.replace(res.redirectUrl); });
|
- 这里直接将
redirect_url
设置为登陆页,即请求成功后重定向到登陆页。
获取openId
有了code
就可以获取openId
和userInfo
等信息:
@GetMapping("/userInfo") public Map<String, String> userInfo(@RequestParam("code") String code) throws WxErrorException { WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code); String openId = accessToken.getOpenId(); WxOAuth2UserInfo wxMpUser = wxMpService.getOAuth2Service().getUserInfo(accessToken, null);
SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getOpenId, openId)); String token = null; if (null != sysUser) { token = JwtHelper.createToken(sysUser.getId(), sysUser.getUsername()); } HashMap<String, String> map = new HashMap<>(); map.put("token", token); map.put("openId", openId); return map; }
|
前端通过是否有 token 判断是否绑定,唤起绑定功能:
getUserInfo(code).then((res) => { const { token, openId } = res; if (!token && openId) { bindPhoneVo.openId = openId; show.value = true; } else { loading.value = false; storage.set(ACCESS_TOKEN, res.token); const redirect = storage.get(LAND_PAGE) || '/'; redirectTo(redirect); } });
|
消息推送
微信消息推送需要安装官方提供的模板,点击新增测试模板
模板设置
待处理审批
{{first.DATA}} 审批编号: {{keyword1.DATA}} 提交时间: {{keyword2.DATA}} {{content.DATA}}
|
审批已处理
{{first.DATA}} 审批编号:{{keyword1.DATA}} 提交时间:{{keyword2.DATA}} 审批状态:{{keyword3.DATA}} 当前审批人:{{keyword4.DATA}} {{content.DATA}}
|
代码实现
MessageServiceImpl.java@Service public class MessageServiceImpl implements MessageService {
private final WxMpService wxMpService; private final ProcessTemplateService processTemplateService; private final SysUserService sysUserService;
@Value("${wechat.prefix}") private String prefix;
public MessageServiceImpl(WxMpService wxMpService, ProcessTemplateService processTemplateService, SysUserService sysUserService) { this.wxMpService = wxMpService; this.processTemplateService = processTemplateService; this.sysUserService = sysUserService; }
@SneakyThrows @Override public void pushPendingMessage(Process process, SysUser user, String taskId) { ProcessTemplate processTemplate = processTemplateService.getOne( new LambdaQueryWrapper<ProcessTemplate>() .select(ProcessTemplate::getName) .eq(ProcessTemplate::getId, process.getProcessTemplateId())); SysUser submitUser = sysUserService.getOne( new LambdaQueryWrapper<SysUser>() .select(SysUser::getName) .eq(SysUser::getId, LoginUserInfoHelper.getUserId())); String openId = user.getOpenId(); if (openId == null) { return; } WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() .toUser(openId) .templateId("PJaBg1zM5JeOsyc8T-2YVrYchQawCTT6etOhrpVequU") .url(prefix + "/show/" + process.getId() + "/" + taskId + "/0") .build();
JSONObject jsonObject = JSON.parseObject(process.getFormValues()); JSONObject formShowData = jsonObject.getJSONObject("formShowData"); StringBuilder content = new StringBuilder(); for (Map.Entry<String, Object> entry : formShowData.entrySet()) { content.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); }
templateMessage.addData(new WxMpTemplateData("first", submitUser.getName() + "提交" + processTemplate.getName() + "审批申请, 请注意查看。", "#272727")); templateMessage.addData(new WxMpTemplateData("keyword1", process.getProcessCode(), "#272727")); templateMessage.addData(new WxMpTemplateData("keyword2", new DateTime(process.getCreateTime()).toString("yyyy-MM-dd HH:mm:ss"), "#272727")); templateMessage.addData(new WxMpTemplateData("content", content.toString(), "#272727"));
wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage); }
@SneakyThrows @Override public void pushProcessedMessage(Process process, Integer status) { ProcessTemplate processTemplate = processTemplateService.getOne( new LambdaQueryWrapper<ProcessTemplate>() .select(ProcessTemplate::getName) .eq(ProcessTemplate::getId, process.getProcessTemplateId())); SysUser user = sysUserService.getById(process.getUserId()); SysUser currentUser = sysUserService.getById(LoginUserInfoHelper.getUserId()); String openId = user.getOpenId(); if (openId == null) { return; } WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() .toUser(openId) .templateId("FUinAm420s82h7nHRFB-cuZFgOxdVJTtv4faWn2xA74") .url(prefix + "/show/" + process.getId() + "/0/0") .build();
JSONObject jsonObject = JSON.parseObject(process.getFormValues()); JSONObject formShowData = jsonObject.getJSONObject("formShowData"); StringBuilder content = new StringBuilder(); for (Map.Entry<String, Object> entry : formShowData.entrySet()) { content.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); }
templateMessage.addData(new WxMpTemplateData("first", user.getName() + "你发起的" + processTemplate.getName() + "审批申请已经被处理了,请注意查看。", "#272727")); templateMessage.addData(new WxMpTemplateData("keyword1", process.getProcessCode(), "#272727")); templateMessage.addData(new WxMpTemplateData("keyword2", new DateTime(process.getCreateTime()).toString("yyyy-MM-dd HH:mm:ss"), "#272727")); templateMessage.addData(new WxMpTemplateData("keyword3", status == 1 ? "审批通过" : "审批拒绝", status == 1 ? "#009966" : "#FF0033")); templateMessage.addData(new WxMpTemplateData("keyword4", currentUser.getName(), "#272727")); templateMessage.addData(new WxMpTemplateData("content", content.toString(), "#272727"));
wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage); } }
|