• 微信登录的接口


    微信开放平台:

    https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html

    第一步:生成微信扫描二维码(网页内嵌的二维码)接口

    提供了第二种获取 code 的方式,支持网站将微信登录二维码内嵌到自己页面中,用户使用微信扫码授权后通过 JS 将code返回给网站。 JS微信登录主要用途:网站希望用户在网站内就能完成登录,无需跳转到微信域下登录后再返回,提升微信登录的流畅性与成功率。 网站内嵌二维码微信登录 JS 实现办法:

    步骤1:在页面中先引入如下 JS 文件(支持https):

    http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js

    步骤2:在需要使用微信登录的地方实例以下 JS 对象:

    var obj = new WxLogin({
     self_redirect:true,
     id:"login_container", 
     appid: "", 
     scope: "", 
     redirect_uri: "",
      state: "",
     style: "",
     href: ""
     });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参数说明
    在这里插入图片描述
    我们需要写必须的那几个参数;

    生成微信扫描二维码(网页内嵌的二维码)接口.此几口返回的数据是:生成二维码需要的参数

    注意:接口的后台需要准备这几个参数:

    appid : 应用唯一标识,在微信开放平台提交应用审核通过后获得
    scope :应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
    redirect_uri : 重定向地址,需要进行UrlEncode
    state : 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止 csrf 攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加 session 进行校验

    1.需要在显示二维码的页面 引入前端api接口:

    import weixinApi from ‘@/api/weixin’

    2.然后在 mounted() 方法中添加以下方法

      mounted() {
     //初始化微信js
        const script = document.createElement('script')
        script.type = 'text/javascript'
        script.src = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js'
        document.body.appendChild(script)
       },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、实例化微信JS对象
    添加微信登录方法,即点击微信登录按钮,触发展示微信二维码的方法:

    weixinLogin() {
      this.dialogAtrr.showLoginType = 'weixin'
    
      weixinApi.getLoginParam().then(response => {
        var obj = new WxLogin({
          self_redirect:true,
          id: 'weixinLogin', // 需要显示的容器id
          appid: response.data.appid, // 公众号appid wx*******
          scope: response.data.scope, // 网页默认即可
          redirect_uri: response.data.redirectUri, // 授权成功后回调的url
          state: response.data.state, // 可设置为简单的随机数加session用来校验
          style: 'black', // 提供"black"、"white"可选。二维码的样式
          href: '' // 外部css文件url,需要https
        })
      })
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    然后记得网关设置:

    在这里插入图片描述

    测试二维码展示:

    第二步:处理微信回调 ,获取扫描人信息

    在这里插入图片描述

    手机点击确认后的:

    请求网址: http://localhost:8160/api/ucenter/wx/callback?code=0815s6ll2O8vK94DBoll2u3htf45s6lP&state=1661166686989

    请求网址: http://localhost:8160/api/ucenter/wx/callback?code=091927ll2UTvK94eW6nl2segEs3927lr&state=1661167265572

    简单说,OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。

    四、令牌与密码
    令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。

    (1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。

    (2)令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。

    (3)令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

    上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。

    注意,只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。


    在这里插入图片描述

    1. 客户应用请求授权服务器获取Access Token
    2. 授权服务器咨询用户意见
    3. 用户同意授权
    4. 授权服务器颁发Access Token 给 客户应用

    在这里插入图片描述

    在这里插入图片描述

    创建工具类:httpclint:

    在server_user模块:
    在这里插入图片描述

    在common-util中引入httpclient依赖:

    
            
                org.apache.httpcomponents
                httpclient
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    在这里插入图片描述

    第二步:通过 code 获取access_token

    通过 code 获取access_token

    https://api.weixin.qq.com/sns/oauth2/
    access_token?appid=APPID
    &secret=SECRET&
    code=CODE&
    grant_type=authorization_code
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    说明:我们根据返回openid判断是否需要绑定手机号码,如果需要绑定,那么我们要根据openid用户用户信息,然后更新上手机号码

    前端:

    3.4 回调返回页面
    操作:yygh-site
    说明:我们只期望返回一个空页面,然后跟登录层通信就可以了,其实就是一个过渡页面,所以我们要给这个过渡页面定义一个空模板

    3.4.2回调返回页面
    根据返回路径/weixin/cakkback,我们创建组件/weixin/cakkback.vue

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    说明:在页面我们就能够接收到返回来的参数

    3.4.3 父组件定义回调方法
    在myheader.vue添加方法

    mounted() 中增加方法:

     // 微信登录回调处理
       let self = this;
       window["loginCallback"] = (name,token, openid) => {
         self.loginCallback(name, token, openid);
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    然后methods中增加方法:

    loginCallback(name, token, openid) {
          // 打开手机登录层,绑定手机号,改逻辑与手机登录一致
          if(openid != '') {
            this.userInfo.openid = openid
            this.showLogin()
          } else {
            this.setCookies(name, token)
          }
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.5 服务器绑定手机号码
    页面绑定手机号码会把openid传递过来,我们根据openid找到用户信息,然后绑定手机号码
    修改UserInfoServiceImpl类登录方法

     //绑定手机号码
            UserInfo userInfo = null;
            if(!StringUtils.isEmpty(loginVo.getOpenid())) {
                userInfo = this.selectWxInfoOpenId(loginVo.getOpenid());
                if(null != userInfo) {
                    userInfo.setPhone(loginVo.getPhone());
                    this.updateById(userInfo);
                } else {
                    throw new YyghException(ResultCodeEnum.DATA_ERROR);
                }
            }
    
            if(userInfo == null){
                //判断是否第一次登录,根据手机号查询数据库,如果不存在相同的手机号就是第一次登录
                QueryWrapper wrapper = new QueryWrapper<>();
                wrapper.eq("phone",phone);
                 userInfo = baseMapper.selectOne(wrapper);
                if(null==userInfo ){//第一次使用这个手机号登录
                    //添加信息到数据库
                    userInfo = new UserInfo();
                    userInfo.setName("");
                    userInfo.setPhone(phone);
                    userInfo.setStatus(1);//设置可用
                    //this.save(userInfo);//保存到数据库
                    baseMapper.insert(userInfo);
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述

    package com.fan.yygh.user.controller;
    
    import com.alibaba.fastjson.JSONObject;
    import com.fan.yygh.common.exception.YyghException;
    import com.fan.yygh.common.result.Result;
    import com.fan.yygh.common.result.ResultCodeEnum;
    import com.fan.yygh.helper.JwtHelper;
    import com.fan.yygh.model.user.UserInfo;
    import com.fan.yygh.user.service.UserInfoService;
    import com.fan.yygh.user.utils.ConstantPropertiesUtil;
    import com.fan.yygh.user.utils.HttpClientUtils;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.stereotype.Controller;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.annotation.Resource;
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    import java.util.HashMap;
    import java.util.Map;
    //微信操作的接口
    @Api("微信")
    @Controller
    @RequestMapping("/api/ucenter/wx")
    public class WeixinApiController {
        @Resource
        private UserInfoService userInfoService;
    
        /**
         * 获取微信登录参数
         * 1.生成微信扫描二维码
         * 返回生成二维码需要的参数
         */
        @GetMapping("getLoginParam")
        @ResponseBody
        public Result genQrConnect() throws UnsupportedEncodingException {
            String redirect_uri = URLEncoder.encode(
                    ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL, "UTF-8");
            Map map = new HashMap<>();
    
            map.put("appid", ConstantPropertiesUtil.WX_OPEN_APP_ID);
            map.put("redirect_uri", redirect_uri);
            map.put("scope", "snsapi_login");
            map.put("state", System.currentTimeMillis()+"");//System.currentTimeMillis()+""
            return Result.ok(map);
        }
        //微信扫描后回调的方法
        @GetMapping("callback")
        public String callback(String code,String state){
            //第一步,获取 临时授权临时票据code
            System.out.println("code:"+code);
            //第二步:拿着code和微信appid和秘钥appscrect(三个参数换一个参数),--》请求微信固定地址--》换取access_token
            StringBuffer baseAccessTokenUrl = new StringBuffer()
                    .append("https://api.weixin.qq.com/sns/oauth2/access_token")
                    .append("?appid=%s")
                    .append("&secret=%s")
                    .append("&code=%s")
                    .append("&grant_type=authorization_code");
            //baseAccessTokenUrl的参数填充
            String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),
                    ConstantPropertiesUtil.WX_OPEN_APP_ID, //参数一appid
                    ConstantPropertiesUtil.WX_OPEN_APP_SECRET, //参数二appscrec
                    code); //参数三code
    
            //使用httpclient请求这个地址
            String accessTokenInfo  = null;
            try {
                //使用三个参数换取token,accessTokenInfo包含token令牌信息
                accessTokenInfo = HttpClientUtils.get(accessTokenUrl);//accessTokenUrl包含三个参数
                /*令牌accessTokenInfo:
                {"access_token":"60_jEgLorfJRsMwTfuDdzUN_7mApa4D7c",
                "expires_in":7200,
                "refresh_token":"60_LPZ7M5sigEJ_wB09HKCeMETuYwLBjg",
                "openid":"o3_SC5yAwrC4BO587_u3_llgcHZU",
                "scope":"snsapi_login",
                "unionid":"oWgGz1KqBehsbtgjrIjCR8-5hxkU"}*/
                System.out.println("使用code换取的access_token结果accessTokenInfo:"+accessTokenInfo);
                /*=============================以下是对换取的token做处理=======================================*/
                //从返回字符串拿到两个值openid  和 access_token
                JSONObject jsonObject = JSONObject.parseObject(accessTokenInfo);
                String access_token = jsonObject.getString("access_token");
                String openid = jsonObject.getString("openid");
                //判断数据库是否存在微信的扫描人信息,根据openid判断
                UserInfo userInfo = userInfoService.selectWxInfoOpenId(openid);
                if(userInfo == null){ //数据库不存在微信信息
                    //第三步:拿着openid 和access_token 请求微信地址,得到扫码人 信息
                    //根据access_token获取微信用户的基本信息
                    //先根据openid进行数据库查询
                    // UserInfo userInfo = userInfoService.getByOpenid(openId);
                    // 如果没有查到用户信息,那么调用微信个人信息获取的接口
                    // if(null == userInfo){
                    //如果查询到个人信息,那么直接进行登录
                    //使用access_token换取受保护的资源:微信的个人信息
                    //换取token的地址:   https://api.weixin.qq.com/sns/oauth2/access_token
                    //换取user信息的地址:https://api.weixin.qq.com/sns/userinfo
                    String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                            "?access_token=%s" +
                            "&openid=%s";
                    String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);
    
                    String resultInfo = HttpClientUtils.get(userInfoUrl);
                    //得到扫码 用户信息resultInfo,是一个json字符串
                    /*扫码人信息resultInfo:
                    {"openid":"o3_SC5yAwrC4BO587_u3_llgcHZUxxx",
                    "nickname":"xxx","sex":0,"language":"","city":"","province":"","country":"",
                    "headimgurl":"https:\23\/132",
                    "privilege":[],
                    "unionid":"oWgGz1KqBehsbtgjrIjCR8-5hxkU"}*/
                    //================第四步:利用access_token 和openid  换取 扫码人信息=============
                    System.out.println("扫码人信息resultInfo:"+resultInfo);
                    //解析用户信息,将string转成json对象
                    JSONObject resultUserInfoJson = JSONObject.parseObject(resultInfo);
                    String nickname = resultUserInfoJson.getString("nickname");//得到用户昵称
                    String headimgurl = resultUserInfoJson.getString("headimgurl");//得到用户头像
                    //获取扫码人信息 并添加到数据库
                    userInfo = new UserInfo();
                    userInfo.setName(nickname);
                    userInfo.setOpenid(openid);
                    userInfo.setStatus(1);
                    //保存到数据库
                    userInfoService.save(userInfo);
                }
    
                //返回name和token令牌字符串
                HashMap map = new HashMap<>();
                String name = userInfo.getName();
                if(StringUtils.isEmpty(name)){
                    name = userInfo.getNickName();
                }
                if(StringUtils.isEmpty(name)){
                    name = userInfo.getPhone();
                }
                map.put("name",name);//将名字放入map
                //要求每个微信用户要绑定手机号,判断userInfo是否有手机号,
                // 如果手机号为空,返回openid
                //如果手机号不为空,返回openid值是空字符串
                //前端判断:如果openid不为空,绑定手机号,如果openid为空,不需要绑定手机号
                if(StringUtils.isEmpty(userInfo.getPhone())){
                    map.put("openid", userInfo.getOpenid());
                }else{
                    map.put("openid","");
                }
                //产生token令牌字符串
                String token = JwtHelper.createToken(userInfo.getId(), name);
                map.put("token",token);//将令牌token放入map
    
                //登录成功后的页面跳转
                return "redirect:"+ ConstantPropertiesUtil.YYGH_BASE_URL + "/weixin/callback?token="+
                        map.get("token")+"&openid="+
                        map.get("openid")+"&name="+URLEncoder.encode(map.get("name"),"utf-8");
    
            } catch (Exception e) {
                throw new YyghException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
                //return null;
            }
    
        }
    
        /*============我的微信二维码生成的方法====================*/
        /*方法返回生成展示二维码的各种前端页面参数*/
        @ApiOperation("我的微信二维码生成的方法")
        @GetMapping("createWeixinImage")
        @ResponseBody  //此注解返回json
        public Result   createWeixinImage() throws UnsupportedEncodingException {
            //微信官方规定:redirect_uri		重定向地址,需要进行UrlEncode
            String redirect_uri = URLEncoder.encode(
                    ConstantPropertiesUtil.WX_OPEN_REDIRECT_URL,
                    "UTF-8");
            HashMap map = new HashMap<>();
            //从官网给的参数中获取appid,appid在配置文件中的
            map.put("appid",ConstantPropertiesUtil.WX_OPEN_APP_ID);
            //scope:应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
            map.put("scope","snsapi_login");
            map.put("redirect_uri",redirect_uri);
            /*用于保持请求和回调的状态,授权请求后原样带回给第三方。
            该参数可用于防止 csrf 攻击(跨站请求伪造攻击),
            建议第三方带上该参数,可设置为简单的随机数加 session 进行校验*/
            map.put("state",System.currentTimeMillis()+"");
            return Result.ok(map);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    package com.fan.yygh.user.service.impl;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.fan.yygh.common.exception.YyghException;
    import com.fan.yygh.common.result.ResultCodeEnum;
    import com.fan.yygh.helper.JwtHelper;
    import com.fan.yygh.model.user.UserInfo;
    import com.fan.yygh.user.mapper.UserInfoMapper;
    import com.fan.yygh.user.service.UserInfoService;
    import com.fan.yygh.vo.user.LoginVo;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    
    import javax.annotation.Resource;
    import java.util.HashMap;
    import java.util.Map;
    
    @Service //先继承《M是mapper,T是实体类》后实现
    public class UserInfoImpl extends ServiceImpl
            implements UserInfoService {
        @Resource
        private UserInfoMapper userInfoMapper;
        @Resource
        private RedisTemplate redisTemplate;
    
    
        //判断数据库是否存在微信的扫描人信息,根据openid判断
        @Override
        public UserInfo selectWxInfoOpenId(String openid) {
            QueryWrapper wrapper = new QueryWrapper<>();
            wrapper.eq("openid",openid);
            UserInfo userInfo = baseMapper.selectOne(wrapper);
            return userInfo;
        }
    
        //用户手机号登录接口
        @Override
        public Map loginUser(LoginVo loginVo) {
            //从vo中获取参数
            String phone = loginVo.getPhone();
            String code = loginVo.getCode();
            //参数校验
            if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)){
                throw new YyghException(ResultCodeEnum.PARAM_ERROR);//ResultCodeEnum枚举类
            }
            //TODO 校验校验验证码
            //校验校验验证码
            String mobleCode = redisTemplate.opsForValue().get(phone);
            if(!code.equals(mobleCode)) {
                throw new YyghException(ResultCodeEnum.CODE_ERROR);
            }
    
    
            //绑定手机号码
            UserInfo userInfo = null;
            if(!StringUtils.isEmpty(loginVo.getOpenid())) {
                userInfo = this.selectWxInfoOpenId(loginVo.getOpenid());
                if(null != userInfo) {
                    userInfo.setPhone(loginVo.getPhone());
                    this.updateById(userInfo);
                } else {
                    throw new YyghException(ResultCodeEnum.DATA_ERROR);
                }
            }
    
            if(userInfo == null){
                //判断是否第一次登录,根据手机号查询数据库,如果不存在相同的手机号就是第一次登录
                QueryWrapper wrapper = new QueryWrapper<>();
                wrapper.eq("phone",phone);
                 userInfo = baseMapper.selectOne(wrapper);
                if(null==userInfo ){//第一次使用这个手机号登录
                    //添加信息到数据库
                    userInfo = new UserInfo();
                    userInfo.setName("");
                    userInfo.setPhone(phone);
                    userInfo.setStatus(1);//设置可用
                    //this.save(userInfo);//保存到数据库
                    baseMapper.insert(userInfo);
                }
            }
    
            //校验是否被禁用
            if(userInfo.getStatus() == 0) {
                throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);
            }
    
            //返回给页面的map信息
            HashMap map = new HashMap<>();
            String name = userInfo.getName();
            if (StringUtils.isEmpty(name)) {//如果名字为空
                name = userInfo.getNickName();//用昵称代替名字
            }
            if(StringUtils.isEmpty(name)){
                name = userInfo.getPhone();//如果名字为空,用手机号代替名字
            }
            map.put("name",name);//返回name和token
            //利用工具生成token
            String token = JwtHelper.createToken(userInfo.getId(), name);
            map.put("token",token);//token  被写错了
            return map;  //记得返回map
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
  • 相关阅读:
    人机智能的逻辑哲学论
    Mysql事务
    二叉树经典oj面试题
    格式化硬盘时提示“此驱动器正在使用”解决占用问题
    Spring -Spring之依赖注入源码解析
    vue组件实现搜索下拉框功能
    vector的介绍使用及模拟实现
    《股票大作手回忆录》读书
    lvs项目
    Linux系统部署PostgreSQL 单机数据库
  • 原文地址:https://blog.csdn.net/weixin_38568503/article/details/126469125