如何在项目中整合微信公众号开发(上)

最近在写web项目的过程中接触到公众号开发,在这码一码整个开发的流程

一、配置微信公众号测试号

微信测试号平台链接 mp.weixin.qq.com
扫描登录进入,获取测试号信息:appID与appsecret,这两个很重要!!页面下方的二维码就是我们本次项目中使用到的测试号,可以自己扫码关注一下

图片.png
如下是官方的开发文档,感兴趣的可以看看 微信公众平台开发概述 | 微信开放文档 (qq.com)

在本项目中采用的是weixin-java-mp工具进行开发,下面对其进行配置。

二、配置weixin-java-mp

2.1 在application-dev.yml中添加配置

# 注意不是在spring下!!!
wechat:
mpAppId: # 上面提到的两个属性复制粘贴进去
mpAppSecret:
# 注意不是在spring下!!!
wechat:
  mpAppId: # 上面提到的两个属性复制粘贴进去
  mpAppSecret: 
# 注意不是在spring下!!! wechat: mpAppId: # 上面提到的两个属性复制粘贴进去 mpAppSecret:

2.2 引入依赖

在项目pom文件中引入依赖,记得更新重启一下项目?

<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-mp</artifactId>
    <version>4.1.0</version>
</dependency>
<dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.1.0</version> </dependency>

2.3 添加工具类和配置类

工具类

package com.oa.wechat.config;
/**
* @author Charles
* @create 2023-05-15-13:05
*/
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "wechat") //读取配置配置中写的值
public class WechatAccountConfig {
private String mpAppId;
private String mpAppSecret;
}
package com.oa.wechat.config;



/**
 * @author Charles
 * @create 2023-05-15-13:05
 */

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


@Data
@Component
@ConfigurationProperties(prefix = "wechat") //读取配置配置中写的值
public class WechatAccountConfig {
    private String mpAppId;

    private String mpAppSecret;
}
package com.oa.wechat.config; /** * @author Charles * @create 2023-05-15-13:05 */ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Data @Component @ConfigurationProperties(prefix = "wechat") //读取配置配置中写的值 public class WechatAccountConfig { private String mpAppId; private String mpAppSecret; }

配置类

package com.oa.wechat.config;
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.stereotype.Component;
@Component
public class WeChatMpConfig {
@Autowired
private WechatAccountConfig wechatAccountConfig;
@Bean
public WxMpService wxMpService(){
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
@Bean
public WxMpConfigStorage wxMpConfigStorage(){
WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl();
wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId());
wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret());
return wxMpConfigStorage;
}
}
package com.oa.wechat.config;



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.stereotype.Component;



@Component
public class WeChatMpConfig {

    @Autowired
    private WechatAccountConfig wechatAccountConfig;



    @Bean
    public WxMpService wxMpService(){
        WxMpService wxMpService = new WxMpServiceImpl();
        wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
        return wxMpService;
    }

    @Bean
    public WxMpConfigStorage wxMpConfigStorage(){
        WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl();
        wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId());
        wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret());
        return wxMpConfigStorage;
    }
}
package com.oa.wechat.config; 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.stereotype.Component; @Component public class WeChatMpConfig { @Autowired private WechatAccountConfig wechatAccountConfig; @Bean public WxMpService wxMpService(){ WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } @Bean public WxMpConfigStorage wxMpConfigStorage(){ WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl(); wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId()); wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret()); return wxMpConfigStorage; } }

后续通过这里配置的WxMpService来操作微信公众号

三、添加自定义菜单

一个公众号肯定少不了相关的菜单栏,这里步里会讲述如何添加自定义菜单
下图是微信官方指定的菜单格式,由于官方只认这一个格式,所以我们需要将从数据库获取到的数据转换成该格式

image-20230622171602354.png

3.1 menuController中定义方法

@PreAuthorize("hasAuthority('bnt.menu.syncMenu')") //权限控制
@ApiOperation(value = "同步菜单")
@GetMapping("syncMenu")
public Result syncMenu() {
menuService.syncMenu();
return Result.success();
}
@PreAuthorize("hasAuthority('bnt.menu.syncMenu')") //权限控制
@ApiOperation(value = "同步菜单")
@GetMapping("syncMenu")
public Result syncMenu() {
    menuService.syncMenu();
    return Result.success();
}
@PreAuthorize("hasAuthority('bnt.menu.syncMenu')") //权限控制 @ApiOperation(value = "同步菜单") @GetMapping("syncMenu") public Result syncMenu() { menuService.syncMenu(); return Result.success(); }

3.2 menuService中重写该方法

@Service
public void menuServiceImpl(){
@Autowired
private WxMpService wxMpService;
//获取菜单树形结构
@Override
public List<MenuVo> findMenuInfo() {
List<Menu> menus = weChatMenuMapper.selectList(null);
//获取到所有的父级菜单
List<Menu> menusParent = menus.stream().filter(menu -> menu.getParentId().longValue() == 0)
.collect(Collectors.toList());
List<MenuVo> menuVoList = new ArrayList<>();
for(Menu menu : menusParent){
MenuVo menuVo = new MenuVo();
BeanUtils.copyProperties(menu,menuVo);
//获取到所有的二级目录
List<Menu> subMenuList = menus.stream().filter(myMenu -> myMenu.getParentId().longValue() == menu.getId())
.sorted(Comparator.comparing(Menu::getSort))
.collect(Collectors.toList());
//遍历转换
List<MenuVo> children = new ArrayList<>();
for(Menu menu1 : subMenuList){
MenuVo myMenuVo = new MenuVo();
BeanUtils.copyProperties(menu1,myMenuVo);
children.add(myMenuVo);
}
menuVo.setChildren(children);
menuVoList.add(menuVo);
}
return menuVoList;
}
@Override
public void syncMenu() {
//将数据库中存储的数据封装为层级关系
List<MenuVo> menuVoList = this.findMenuInfo();
//菜单
JSONArray buttonList = new JSONArray();
for(MenuVo oneMenuVo : menuVoList) {
JSONObject one = new JSONObject();
one.put("name", oneMenuVo.getName());
if(CollectionUtils.isEmpty(oneMenuVo.getChildren())) {
one.put("type", oneMenuVo.getType());
one.put("url", "http://oa.charles.vip.tunnel/#"+oneMenuVo.getUrl());
} else {
JSONArray subButton = new JSONArray();
for(MenuVo twoMenuVo : oneMenuVo.getChildren()) {
JSONObject view = new JSONObject();
view.put("type", twoMenuVo.getType());
if(twoMenuVo.getType().equals("view")) {
view.put("name", twoMenuVo.getName());
//H5页面地址 这里队应的是内网穿透的地址 后面会讲
view.put("url", "http://oa.charles.vip.tunnel/#"+twoMenuVo.getUrl())
} else {
view.put("name", twoMenuVo.getName());
view.put("key", twoMenuVo.getMeunKey());
}
subButton.add(view);
}
one.put("sub_button", subButton);
}
buttonList.add(one);
}
//菜单
JSONObject button = new JSONObject();
button.put("button", buttonList);
try {
//固定格式 调用方法来生成菜单
wxMpService.getMenuService().menuCreate(button.toJSONString());
} catch (WxErrorException e) {
throw new RuntimeException(e);
}
}
}
@Service
public void menuServiceImpl(){
    @Autowired
    private WxMpService wxMpService;

    //获取菜单树形结构
    @Override
    public List<MenuVo> findMenuInfo() {
        List<Menu> menus = weChatMenuMapper.selectList(null);



        //获取到所有的父级菜单
        List<Menu> menusParent = menus.stream().filter(menu -> menu.getParentId().longValue() == 0)
                .collect(Collectors.toList());

        List<MenuVo> menuVoList = new ArrayList<>();
        for(Menu menu : menusParent){
            MenuVo menuVo = new MenuVo();
            BeanUtils.copyProperties(menu,menuVo);


            //获取到所有的二级目录
            List<Menu> subMenuList = menus.stream().filter(myMenu -> myMenu.getParentId().longValue() == menu.getId())
                    .sorted(Comparator.comparing(Menu::getSort))
                    .collect(Collectors.toList());
            //遍历转换
            List<MenuVo> children = new ArrayList<>();
            for(Menu menu1 : subMenuList){
                MenuVo myMenuVo  = new MenuVo();
                BeanUtils.copyProperties(menu1,myMenuVo);
                children.add(myMenuVo);
            }

            menuVo.setChildren(children);
            menuVoList.add(menuVo);
        }

        return menuVoList;
    }
    
    @Override
    public void syncMenu() {
        //将数据库中存储的数据封装为层级关系
        List<MenuVo> menuVoList = this.findMenuInfo();
        //菜单
        JSONArray buttonList = new JSONArray();
        for(MenuVo oneMenuVo : menuVoList) {
            JSONObject one = new JSONObject();
            one.put("name", oneMenuVo.getName());
            if(CollectionUtils.isEmpty(oneMenuVo.getChildren())) {
                one.put("type", oneMenuVo.getType());
                one.put("url", "http://oa.charles.vip.tunnel/#"+oneMenuVo.getUrl());
            } else {
                JSONArray subButton = new JSONArray();
                for(MenuVo twoMenuVo : oneMenuVo.getChildren()) {
                    JSONObject view = new JSONObject();
                    view.put("type", twoMenuVo.getType());
                    if(twoMenuVo.getType().equals("view")) {
                        view.put("name", twoMenuVo.getName());
                        //H5页面地址 这里队应的是内网穿透的地址 后面会讲
                        view.put("url", "http://oa.charles.vip.tunnel/#"+twoMenuVo.getUrl())
                    } else {
                        view.put("name", twoMenuVo.getName());
                        view.put("key", twoMenuVo.getMeunKey());
                    }
                    subButton.add(view);
                }
                one.put("sub_button", subButton);
            }
            buttonList.add(one);
        }
        //菜单
        JSONObject button = new JSONObject();
        button.put("button", buttonList);
        try {
            //固定格式 调用方法来生成菜单
            wxMpService.getMenuService().menuCreate(button.toJSONString());
        } catch (WxErrorException e) {
            throw new RuntimeException(e);
        }
    }
}
@Service public void menuServiceImpl(){ @Autowired private WxMpService wxMpService; //获取菜单树形结构 @Override public List<MenuVo> findMenuInfo() { List<Menu> menus = weChatMenuMapper.selectList(null); //获取到所有的父级菜单 List<Menu> menusParent = menus.stream().filter(menu -> menu.getParentId().longValue() == 0) .collect(Collectors.toList()); List<MenuVo> menuVoList = new ArrayList<>(); for(Menu menu : menusParent){ MenuVo menuVo = new MenuVo(); BeanUtils.copyProperties(menu,menuVo); //获取到所有的二级目录 List<Menu> subMenuList = menus.stream().filter(myMenu -> myMenu.getParentId().longValue() == menu.getId()) .sorted(Comparator.comparing(Menu::getSort)) .collect(Collectors.toList()); //遍历转换 List<MenuVo> children = new ArrayList<>(); for(Menu menu1 : subMenuList){ MenuVo myMenuVo = new MenuVo(); BeanUtils.copyProperties(menu1,myMenuVo); children.add(myMenuVo); } menuVo.setChildren(children); menuVoList.add(menuVo); } return menuVoList; } @Override public void syncMenu() { //将数据库中存储的数据封装为层级关系 List<MenuVo> menuVoList = this.findMenuInfo(); //菜单 JSONArray buttonList = new JSONArray(); for(MenuVo oneMenuVo : menuVoList) { JSONObject one = new JSONObject(); one.put("name", oneMenuVo.getName()); if(CollectionUtils.isEmpty(oneMenuVo.getChildren())) { one.put("type", oneMenuVo.getType()); one.put("url", "http://oa.charles.vip.tunnel/#"+oneMenuVo.getUrl()); } else { JSONArray subButton = new JSONArray(); for(MenuVo twoMenuVo : oneMenuVo.getChildren()) { JSONObject view = new JSONObject(); view.put("type", twoMenuVo.getType()); if(twoMenuVo.getType().equals("view")) { view.put("name", twoMenuVo.getName()); //H5页面地址 这里队应的是内网穿透的地址 后面会讲 view.put("url", "http://oa.charles.vip.tunnel/#"+twoMenuVo.getUrl()) } else { view.put("name", twoMenuVo.getName()); view.put("key", twoMenuVo.getMeunKey()); } subButton.add(view); } one.put("sub_button", subButton); } buttonList.add(one); } //菜单 JSONObject button = new JSONObject(); button.put("button", buttonList); try { //固定格式 调用方法来生成菜单 wxMpService.getMenuService().menuCreate(button.toJSONString()); } catch (WxErrorException e) { throw new RuntimeException(e); } } }

最终效果如下图所示
Snipaste_2023-06-22_17-37-29.png

四、 配置内网穿透

前面我们完成配置公众号的菜单,当点击菜单的时候会跳转到我们本地的页面中,但是由于是内网,外网无法直接访问。这时候可以采用内网穿透技术或者ip来实现功能,当然注册域名更好? 这里以ngrok为例子进行内网穿透 ngrok.cc

4.1 开通隧道(需实名认证)

配置好本地端口,由于我们有两个端口所以需要两条隧道(前端页面,后端接口)

Snipaste_2023-06-22_17-45-04.png
开通成功后会看到隧道id,通过下载客户端然后启动当前隧道就ok了

4.2 配置“授权回调页面域名”

网页服务-网页账号-修改 处修改,配置的地址为后端接口的地址,比如我后端接口8080对应地址oa.charles.vip.tunnel.com (即复制该地址上去 注意无需前缀http://)

Snipaste_2023-06-22_17-53-27.png

4.3 配置授权回调获取用户信息接口地址

wechat:
# 跟着前面配置过的myAppId等
# 授权回调获取用户信息接口地址
userInfoUrl: http://oa.charles.vip.tunnel.com/admin/wechat/userInfo
wechat:
      # 跟着前面配置过的myAppId等
  # 授权回调获取用户信息接口地址
  userInfoUrl: http://oa.charles.vip.tunnel.com/admin/wechat/userInfo
wechat: # 跟着前面配置过的myAppId等 # 授权回调获取用户信息接口地址 userInfoUrl: http://oa.charles.vip.tunnel.com/admin/wechat/userInfo

五、编写后端接口

在controller中我们需要定义三个方法,分别是
userInfo — 获取用户信息 OpenId为微信的唯一标识

@GetMapping("/userInfo")
public String userInfo(@RequestParam("code") String code,
@RequestParam("state") String returnUrl) throws Exception {
System.out.println("code " + code);
System.out.println("returnUrl " + returnUrl);
//获取认证token
WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code);
//从token中获取openId
String openId = accessToken.getOpenId();
//查找user表中是否存在该openId
LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<SysUser>();
lambdaQueryWrapper.eq(SysUser::getOpenId,openId);
SysUser sysUser = sysUserService.getOne(lambdaQueryWrapper);
//最终前端都是通过token来判断
String token = "";
//null != sysUser 说明已经绑定,反之为建立账号绑定,去页面建立账号绑定
if(null != sysUser) {
token = JWTHelper.createToken(sysUser.getId(), sysUser.getUsername());
System.out.println("token " + token);
}
if(returnUrl.indexOf("?") == -1) {
return "redirect:" + returnUrl + "?token=" + token + "&openId=" + openId;
} else {
return "redirect:" + returnUrl + "&token=" + token + "&openId=" + openId;
}
}
@GetMapping("/userInfo")
public String userInfo(@RequestParam("code") String code,
                       @RequestParam("state") String returnUrl) throws Exception {
    
    System.out.println("code " + code);
    System.out.println("returnUrl " + returnUrl);
    //获取认证token
    WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code);
    //从token中获取openId
    String openId = accessToken.getOpenId();


    //查找user表中是否存在该openId
    LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<SysUser>();
    lambdaQueryWrapper.eq(SysUser::getOpenId,openId);
    SysUser sysUser = sysUserService.getOne(lambdaQueryWrapper);



    //最终前端都是通过token来判断
    String token = "";
    //null != sysUser 说明已经绑定,反之为建立账号绑定,去页面建立账号绑定
    if(null != sysUser) {
        token = JWTHelper.createToken(sysUser.getId(), sysUser.getUsername());
        System.out.println("token " + token);
    }
    if(returnUrl.indexOf("?") == -1) {
        return "redirect:" + returnUrl + "?token=" + token + "&openId=" + openId;
    } else {
        return "redirect:" + returnUrl + "&token=" + token + "&openId=" + openId;
    }
}
@GetMapping("/userInfo") public String userInfo(@RequestParam("code") String code, @RequestParam("state") String returnUrl) throws Exception { System.out.println("code " + code); System.out.println("returnUrl " + returnUrl); //获取认证token WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code); //从token中获取openId String openId = accessToken.getOpenId(); //查找user表中是否存在该openId LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<SysUser>(); lambdaQueryWrapper.eq(SysUser::getOpenId,openId); SysUser sysUser = sysUserService.getOne(lambdaQueryWrapper); //最终前端都是通过token来判断 String token = ""; //null != sysUser 说明已经绑定,反之为建立账号绑定,去页面建立账号绑定 if(null != sysUser) { token = JWTHelper.createToken(sysUser.getId(), sysUser.getUsername()); System.out.println("token " + token); } if(returnUrl.indexOf("?") == -1) { return "redirect:" + returnUrl + "?token=" + token + "&openId=" + openId; } else { return "redirect:" + returnUrl + "&token=" + token + "&openId=" + openId; } }

authorize — 授权登录

@GetMapping("/authorize")
public String authorize(@RequestParam("returnUrl") String returnUrl, HttpServletRequest request) {
//设置回调URL
/* 1. 获取info信息地址
* 2. 定义URL类型,这里是UserInfo
* 3. 回调地址
**/
System.out.println("InfoUrl " + InfoUrl);
System.out.println("returnUrl " + returnUrl);
String redirectUrl = "";
try {
redirectUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(InfoUrl,
WxConsts.OAuth2Scope.SNSAPI_USERINFO,
URLEncoder.encode(returnUrl.replace("guiguoa", "#"), "utf-8"));
System.out.println("微信网页端回调地址 "+redirectUrl);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//跳转
return "redirect:" + redirectUrl;
}
@GetMapping("/authorize")
public String authorize(@RequestParam("returnUrl") String returnUrl, HttpServletRequest request) {
    //设置回调URL
    /* 1. 获取info信息地址
     *  2. 定义URL类型,这里是UserInfo
     *  3. 回调地址
    **/
    System.out.println("InfoUrl  "  + InfoUrl);
    System.out.println("returnUrl " + returnUrl);
    String redirectUrl = "";
    try {
        redirectUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(InfoUrl,
                WxConsts.OAuth2Scope.SNSAPI_USERINFO,
                URLEncoder.encode(returnUrl.replace("guiguoa", "#"), "utf-8"));
        System.out.println("微信网页端回调地址 "+redirectUrl);
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }


    //跳转
    return "redirect:" + redirectUrl;
}
@GetMapping("/authorize") public String authorize(@RequestParam("returnUrl") String returnUrl, HttpServletRequest request) { //设置回调URL /* 1. 获取info信息地址 * 2. 定义URL类型,这里是UserInfo * 3. 回调地址 **/ System.out.println("InfoUrl " + InfoUrl); System.out.println("returnUrl " + returnUrl); String redirectUrl = ""; try { redirectUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(InfoUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, URLEncoder.encode(returnUrl.replace("guiguoa", "#"), "utf-8")); System.out.println("微信网页端回调地址 "+redirectUrl); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //跳转 return "redirect:" + redirectUrl; }

bindPhone — 当成功授权后,如果微信号是第一次登录,需要提供窗口给用户用户手机号绑定

@ApiOperation(value = "微信账号绑定手机")
@PostMapping("/bindPhone")
@ResponseBody
public Result bindPhone(@RequestBody BindPhoneVo bindPhoneVo) {
String phone = bindPhoneVo.getPhone();
//查找user表中是否有当前记录
SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhone, phone));
if(null != sysUser){
//如果用户存在则绑定当前手机号
sysUser.setOpenId(bindPhoneVo.getOpenId());
sysUserService.updateById(sysUser);
}else
return Result.fail("找不到当前号码,请联系管理员");
return Result.success(JWTHelper.createToken(sysUser.getId(),sysUser.getUsername()));
}
@ApiOperation(value = "微信账号绑定手机")
@PostMapping("/bindPhone")
@ResponseBody
public Result bindPhone(@RequestBody BindPhoneVo bindPhoneVo) {
    String phone = bindPhoneVo.getPhone();


    //查找user表中是否有当前记录
    SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhone, phone));

    if(null != sysUser){
        //如果用户存在则绑定当前手机号
        sysUser.setOpenId(bindPhoneVo.getOpenId());
        sysUserService.updateById(sysUser);
    }else
        return Result.fail("找不到当前号码,请联系管理员");



    return Result.success(JWTHelper.createToken(sysUser.getId(),sysUser.getUsername()));
}
@ApiOperation(value = "微信账号绑定手机") @PostMapping("/bindPhone") @ResponseBody public Result bindPhone(@RequestBody BindPhoneVo bindPhoneVo) { String phone = bindPhoneVo.getPhone(); //查找user表中是否有当前记录 SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhone, phone)); if(null != sysUser){ //如果用户存在则绑定当前手机号 sysUser.setOpenId(bindPhoneVo.getOpenId()); sysUserService.updateById(sysUser); }else return Result.fail("找不到当前号码,请联系管理员"); return Result.success(JWTHelper.createToken(sysUser.getId(),sysUser.getUsername())); }

当然如果项目中配置了SpringSecurity 还需要在config中排除上述三个方法的路径

六、前端修改(基于vue)

前端的项目中主要修改两个地方,一个是src/App.vue 可以直接copy一下

<template>
<div id="app">
<router-view />
<el-dialog title="绑定手机" :visible.sync="dialogVisible" width="80%" >
<el-form ref="dataForm" :model="bindPhoneVo" size="small">
<h4>绑定你的手机号</h4>
<el-form-item label="手机号码">
<el-input v-model="bindPhoneVo.phone"/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" icon="el-icon-check" @click="saveBind()" size="small">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import userInfoApi from '@/api/userInfo'
export default {
data() {
return {
show: true,
dialogVisible: false,
bindPhoneVo: {
openId: '',
phone: ''
}
};
},
created() {
// 处理微信授权登录
this.wechatLogin();
},
methods: {
wechatLogin() {
// 处理微信授权登录
let token = this.getQueryString('token') || '';
let openId = this.getQueryString('openId') || '';
// token === '' && openId != '' 只要这种情况,未绑定账号
if(token === '' && openId != '') {
// 绑定账号
this.bindPhoneVo.openId = openId
this.dialogVisible = true
} else {
// 如果绑定了,授权登录直接返回token
if(token !== '') {
window.localStorage.setItem('token', token);
}
token = window.localStorage.getItem('token') || '';
if (token == '') {
window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url
}
}
},
saveBind() {
if(this.bindPhoneVo.phone.length != 11) {
alert('手机号码格式不正确')
return
}
userInfoApi.bindPhone(this.bindPhoneVo).then(response => {
window.localStorage.setItem('token', response.data);
this.dialogVisible = false
window.location = 'http://oa.charles.free.idcfengye.com'
})
},
getQueryString (paramName) {
if(window.location.href.indexOf('?') == -1) return '';
let searchString = window.location.href.split('?')[1];
let i, val, params = searchString.split("&");
for (i=0;i<params.length;i++) {
val = params[i].split("=");
if (val[0] == paramName) {
return val[1];
}
}
return '';
}
}
};
</script>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
}
</style>
<template>
  <div id="app">
    <router-view />
    <el-dialog title="绑定手机" :visible.sync="dialogVisible" width="80%" >
      <el-form ref="dataForm" :model="bindPhoneVo" size="small">
        <h4>绑定你的手机号</h4>
        <el-form-item label="手机号码">
          <el-input v-model="bindPhoneVo.phone"/>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button type="primary" icon="el-icon-check" @click="saveBind()" size="small">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>

import userInfoApi from '@/api/userInfo'
export default {
  data() {
    return {
      show: true,
      dialogVisible: false,
      bindPhoneVo: {
        openId: '',
        phone: ''
      }
    };
  },
  
  created() {
    // 处理微信授权登录
    this.wechatLogin();
  },

  methods: {
    wechatLogin() {
      // 处理微信授权登录
      let token = this.getQueryString('token') || '';
      let openId = this.getQueryString('openId') || '';
      // token === '' && openId != '' 只要这种情况,未绑定账号
      if(token === '' && openId != '') {
        // 绑定账号
        this.bindPhoneVo.openId = openId
        this.dialogVisible = true
      } else {
        // 如果绑定了,授权登录直接返回token
        if(token !== '') {
          window.localStorage.setItem('token', token);
        }

        token = window.localStorage.getItem('token') || '';
        if (token == '') {
          window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url
        }
      }
    },

    saveBind() {
      if(this.bindPhoneVo.phone.length != 11) {
        alert('手机号码格式不正确')
        return
      }

      userInfoApi.bindPhone(this.bindPhoneVo).then(response => {
        window.localStorage.setItem('token', response.data);
        this.dialogVisible = false
        window.location = 'http://oa.charles.free.idcfengye.com'
      })
    },

    getQueryString (paramName) {
      if(window.location.href.indexOf('?') == -1) return '';
      let searchString = window.location.href.split('?')[1];
      let i, val, params = searchString.split("&");
      for (i=0;i<params.length;i++) {
        val = params[i].split("=");
        if (val[0] == paramName) {
          return val[1];
        }
      }
      return '';
    }
  }
};
</script>

<style lang="scss">
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}
</style>
<template>   <div id="app">     <router-view /> <el-dialog title="绑定手机" :visible.sync="dialogVisible" width="80%" >       <el-form ref="dataForm" :model="bindPhoneVo" size="small">         <h4>绑定你的手机号</h4>         <el-form-item label="手机号码">           <el-input v-model="bindPhoneVo.phone"/>         </el-form-item>       </el-form>       <span slot="footer" class="dialog-footer">         <el-button type="primary" icon="el-icon-check" @click="saveBind()" size="small">确 定</el-button>       </span>     </el-dialog>   </div> </template> <script> import userInfoApi from '@/api/userInfo' export default {   data() {     return {       show: true,       dialogVisible: false,       bindPhoneVo: {         openId: '',         phone: ''       }     };   },   created() {     // 处理微信授权登录     this.wechatLogin();   },   methods: {     wechatLogin() {       // 处理微信授权登录       let token = this.getQueryString('token') || '';       let openId = this.getQueryString('openId') || '';       // token === '' && openId != '' 只要这种情况,未绑定账号       if(token === '' && openId != '') {         // 绑定账号         this.bindPhoneVo.openId = openId         this.dialogVisible = true       } else {         // 如果绑定了,授权登录直接返回token         if(token !== '') {           window.localStorage.setItem('token', token);         }         token = window.localStorage.getItem('token') || '';         if (token == '') {           window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url         }       }     },     saveBind() {       if(this.bindPhoneVo.phone.length != 11) {         alert('手机号码格式不正确')         return       }       userInfoApi.bindPhone(this.bindPhoneVo).then(response => {         window.localStorage.setItem('token', response.data);         this.dialogVisible = false         window.location = 'http://oa.charles.free.idcfengye.com'       })     },     getQueryString (paramName) {       if(window.location.href.indexOf('?') == -1) return ''; let searchString = window.location.href.split('?')[1];       let i, val, params = searchString.split("&");       for (i=0;i<params.length;i++) {         val = params[i].split("=");         if (val[0] == paramName) {           return val[1];         }       }       return '';     }   } }; </script> <style lang="scss"> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; } </style>

另一个是src/utils/request.js

// 创建axios实例
const service = axios.create({
baseURL: "http://oa.charles.vip.tunnel.com", // api 的 base_url
timeout: 30000 // 请求超时时间
});
// http response 拦截器
service.interceptors.response.use(response => {
if (response.data.code == 208) { //登录失败
window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url
} else {
if (response.data.code == 200) {
return response.data;
} else {
// 209没有权限 系统会自动跳转授权登录的,已在App.vue处理过,不需要提示
if (response.data.code != 209) {
alert(response.data.message || "error");
}
return Promise.reject(response);
}
}
},
error => {
return Promise.reject(error.response); // 返回接口返回的错误信息
});
// 创建axios实例
const service = axios.create({
  baseURL: "http://oa.charles.vip.tunnel.com", // api 的 base_url
  timeout: 30000 // 请求超时时间
});


// http response 拦截器
service.interceptors.response.use(response => {
    if (response.data.code == 208) { //登录失败



      window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url
    } else {
      if (response.data.code == 200) {
        return response.data;
      } else {
        // 209没有权限 系统会自动跳转授权登录的,已在App.vue处理过,不需要提示
        if (response.data.code != 209) {
          alert(response.data.message || "error");
        }
        return Promise.reject(response);
      }
    }
  },
  error => {
    return Promise.reject(error.response);   // 返回接口返回的错误信息
  });
// 创建axios实例 const service = axios.create({ baseURL: "http://oa.charles.vip.tunnel.com", // api 的 base_url timeout: 30000 // 请求超时时间 }); // http response 拦截器 service.interceptors.response.use(response => {     if (response.data.code == 208) { //登录失败       window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url     } else {       if (response.data.code == 200) {         return response.data;       } else {         // 209没有权限 系统会自动跳转授权登录的,已在App.vue处理过,不需要提示         if (response.data.code != 209) {           alert(response.data.message || "error");         }         return Promise.reject(response);       }     }   },   error => {     return Promise.reject(error.response);   // 返回接口返回的错误信息   });

由于篇幅过长,我将会在下一篇中讲述如何进行公众号消息推送,感谢各位看到这里?

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYhbo0MX' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片