• 87-分布式前端微信操作


    微信登录和微信支付

    在上一章我们初步的完成了前端的编写,接下来我们来操作微信的登录和微信的支付
    微信开放平台(针对开发者和公司):
    对应的微信官方文档:
    https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
    登录用,对应的AppID和AppSecret需要申请才可操作,需要在管理中心创建申请对应的网站,一般是必须要上线的网站:
    https://open.weixin.qq.com/
    后面我给出了对应的信息(用来测试用),这样就不用你自己申请了
    准备工作:
    网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统
    在进行微信OAuth2.0授权登录接入之前,在微信开放平台注册开发者帐号
    并拥有一个已审核通过的网站应用(或者其他应用,这里就以网站即网页为例子,后面的都说明网站)
    并获得相应的AppID和AppSecret,申请微信登录且通过审核后,可开始接入流程
    注册帐号和申请应用都是免费的,必须要有一个线上的网站,才能审核通过(过程还是挺麻烦的)
    就可以使用微信的登录了,但是如果想使用微信支付的功能,就必须认证开发者资质(认证一次300块人民币)
    名词解释:
    OAuth2.0协议

    在这里插入图片描述

    玩抖音,发视频,抖音需要访问你相册的授权,话筒的授权,地理位置的授权等等
    一句话,我不想帐号密码给第三方应用,但我还想用他们的功能,而他们的功能需要我的部分数据来协助
    ok,咱玩令牌,令牌与密码的作用都可以进入系统,但是有三点差异:
    1:令牌是短期的,到期会自动失效,用户自己无法修改,密码一般长期有效,用户不修改,就不会发生变化
    2:令牌可以被数据所有者撤销,会立即失效,以上例而言,屋主可以随时取消快递员的令牌,密码一般不允许被他人撤销
    3:令牌有权限范围,比如只能进小区的二号门,对于网络服务来说,只读令牌就比读写令牌更安全,密码一般是完整权限
    上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全
    OAuth的四种授权模式:
    1:授权码模式(功能最完整,流程最严密的授权模式)
    说白了,授权码模式,不再client和user之间商量授权,而是client想要被授权
    所以client去找了一个和事佬大妈,大妈将client和user叫到了一起(认证服务器)
    给大妈个面子,这事就这么定了,就是这样的一个过程,全程中认证服务器会发布一个认证码贯穿始终
    下面的可以百度了解即可
    2:密码模式
    一般通过账号密码就可以访问,他们可以通过你的账号密码进行访问(保存在他们的数据库里)
    3:简化模式
    授权码模式的减低版,没有code授权码
    4:客户端模式
    最不安全的模式,基本上不需要什么操作就可以访问,他们保存了你的令牌(你给的),保存在数据库里
    安全性:授权码模式 > 简化模式 > 密码模式 > 客户端模式
    那么具体的信任程度,一般是:客户端模式>密码模式>简化模式>授权码模式
    因为对应的第三方必须信任要高,才会放心的给出最简便,但最不安全的模式,但最好还是使用授权码模式
    因为在不安全的情况下,黑客也更加容易得到你的信息
    由于授权码模式总体来说是最好的,所以我们操作授权码模式
    AppID:应用ID,唯一标识(身份证号)
    AppSecret :应用的密钥(密码)
    code:授权的临时凭证(例如:临时身份证)
    access_token :接口调用凭证(例如:真正的身份证,虎符,令牌)
    登录授权时序图 :

    在这里插入图片描述

    上面的二维码一般保存对应的地址,你可以进行测试,在百度上搜索"草料二维码生成器",进行官方网站
    输入"中华人民共和国"这个内容,点击生成二维码,用微信扫一扫,就会出现该内容
    当然多次生成一样的,对应的码基本都相同
    当然你也可以输入网站,如http://www.baidu.com,那么会自动的进行跳转,那么为什么不会直接显示内容,而是跳转呢
    主要是观察是否有"😕/“,其中单独的”//“,若前面没有值,那么默认”//"后面是一个网站,否则就是内容
    而"😕/"代表整体是一个网站了(第一个开始,分割线)
    具体可以自己进行测试
    接下来我们继续说明一下对应的模式
    其中授权码模式,上面的图片中,就是一个授权码模式
    code就是授权码,token就是令牌,AppID和AppSecret 是网站的对应唯一值,这样该网站可以通过令牌得到用户的信息了
    而简化模式,就是没有code这个授权码
    而密码模式,一般输入的密码是对应的第三方的,第三方直接通过你的密码进行授权并操作
    而客户端模式,自己通过自己的信息得到令牌,然后将令牌给第三方
    第三方可以说是我们要访问的客户端,或者网站等等,通过上面的描述,可以知道
    的确客户端模式是最不安全的,因为他能直接操作最终的令牌
    开发步骤:
    vue项目安装
    微信官方提供的生成二维码的js,有对应的组件,包含了对应的js
    npm install vue-wxlogin 
    
    • 1
    如果不是vue的项目,可以直接引用官方提供的js文件,来生成二维码
    http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
    
    • 1
    页面引入:
    在对应的前端找到Header.vue(上一章的前端项目)
    这里只会给出部分代码,需要自己去进行对比修改,由于是部分,对应的代码可能不是全的
    <script>
    
    //引入
    import wxlogin from 'vue-wxlogin'; 
    export default {
      name: "Header",
      components:{
        wxlogin //声明引用的组件,可以使用他的标签来操作,也就是直接操作该组件
      },
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
     
        <el-dialog
          style="width:800px;margin:0px auto;"
          title=""
          :visible.sync="dialogFormVisible">
          <div id="loginForm">
          <el-form>
            <el-form-item>
              <h1 style="font-size:30px;color:#00B38A">拉勾h1>
            el-form-item>
            <el-form-item>
              <el-input v-model="phone" placeholder="请输入常用手机号...">el-input>
            el-form-item>
            <el-form-item>
              <el-input v-model="password" placeholder="请输入密码...">el-input>
            el-form-item>
          el-form>
          <el-button
            style="width:100%;margin:0px auto;background-color: #00B38A;font-size:20px"
            type="primary"
            @click="login">确 定el-button>
          <p>p>
          
          <img
            @click="goToLoginWX"
    src="http://www.lgstatic.com/lg-passport-fed/static/pc/modules/common/img/icon-wechat@2x_68c86d1.png"
            alt=""
          />
          div>
    
    
    <wxlogin id="wxLoginForm" style="display:none"  
    :appid="appid" :scope="scope" :redirect_uri="redirect_uri">wxlogin>
    
    
        el-dialog>
        
    
    • 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
    data() {
        return {
          isLogin: false, // 登录状态,true:已登录,false:未登录
          userDTO:null, // 用来保存登录的用户信息
          isHasNewMessage: false, // 是否有新的推送消息
          dialogFormVisible: false, // 是否显示登录框,true:显示,false:隐藏
          phone: "", // 双向绑定表单 手机号
          password: "", // 双向绑定表单 密码
          appid:"wxd99431bbff8305a0", 
            // 应用唯一标识,在微信开放平台提交应用审核通过后获得,这里就使用我申请好的
            //注释,并不是必须写在代码上方,也是可以写在下方的,只是一般习惯于写在代码上方
          scope:"snsapi_login", // 应用授权作用域,网页应用目前仅填写snsapi_login即可,该值基本固定死的
          redirect_uri:"http://www.pinzhi365.com/wxlogin",  //重定向地址,(回调地址)
          //该地址一般保存对应的第三方地址,或者说微信服务地址(一般需要自己写)
            //他只能检测申请后的值是否正确,但可不可以访问却不会检测
            //一般是我们自己编写后端使得去接受code的值
            
            //也就是说,我们扫描二维码后,我们会得到微信官方的地址,并去微信官方发送申请
            //微信官方给出提示,在你手机上显示是否确认允许登录,当你确认后
            //微信官方就会将对应的参数给你的上线的网站,由于你的网站是上线的
            //那么当前的前端项目可以访问该网站(但通常是本身,而不是这里的其他项目)
            //从而得到对应的参数值(申请后,会有的方法调用),在根据对应的地址(redirect_uri)使用get请求来操作
            //总体来说,就是允许后,相当于请求对应的地址,并给出对应的参数
    		//从而当前前端对应的后端就得到了对应的code参数的值以及其他参数的值(一般是state参数的值)
            //所以code的得到,也是由对应根据微信的上线网站来得到的,所以基本三个参数必须要正确才可
     			//appid基本是唯一,用它来表示不同的网站,从而得到对应的报错信息是否正确(而不是每个网站得到)
            //后面的就是网站的不同信息了
            
            //上面三个参数,一般申请后,基本都会给出对应的值
            
          //第三方在一些情况下,是客户与服务之间统一操作的一方,如这里
            
            
            //这里你可能会有疑问,为什么需要code,而不直接给令牌呢
            //从浏览器出发,如果给令牌,必然会暴露,我们从上知道,我们的前端将数据给后端时是get请求
            //这样的请求必然是通过浏览器的,所以我们需要对应的临时凭证
            //实际上你允许后,页面的确发生跳转,并可以在url上看到对应的参数信息(这样容易被拦截知道信息)
            //所以为了不在浏览器上操作,一般我们会直接在程序里进行请求(如java),而不会在浏览器上进行
            //而正是因为初始数据的传递基本必须要经过浏览器,所以令牌也基本在java程序里面进行操作
            //也为了防止code给拦截,所以发送的get请求中,不止是有code
            //还有当前网站的信息,一般对应的唯一id不会暴露出去
            //若你能破解网站的对应的唯一值
            //那么也必定可以得到对应的微信用户的私隐信息
            //但账号密码基本不会得到,因为单独的授权并不能得到账号和密码
            //实际上程序的get请求虽然也会被拦截,但是相对于安全一些,因为基本是使用类似于https来操作的
            //实际上就算被拦截,但对应的账号和密码也基本上得不到的
            //那为什么不一直使用https呢,既然更加安全,那么对应的操作也就会越多
            //特别是大型多次的操作,使用https会大量的使用资源,所以不会一直用
        };
      },
    
    • 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
     goToLoginWX() {
          //alert("微信登录");
          //普通的登录表单隐藏
          document.getElementById("loginForm").style.display="none"
    
          //显示二维码
           document.getElementById("wxLoginForm").style.display="block"
    
    
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    接下来点击对应的微信图标,那么就会出现二维码
    假设:如果对应的网站Scope权限没有开通或者scope的值不正确,那么会提示对应错误,这是该网站的问题
    具体可以百度,这里并没有问题
    当然对应的appid的值不正确或者redirect_uri的值不正确,也会出现对应的提示错误,他们都会去验证的
    所以申请时也一般只能是上线的网站或者其他应用,否则申请基本会失败,你可以自己试验一下(故意修改参数值)
    总体来说,若二维码生成失败,那么就是上面三个参数的问题,只要都正确,基本才会生成出二维码
    修改hosts文件(若有自己的远程服务器可以不用这样操作):
    文件位置:C:\Windows\System32\drivers\etc\hosts
    127.0.0.1 www.pinzhi365.com
    
    • 1
    回调默认指定的是80端口,别忘记将对应的tomcat的端口修改成80再次启动一个项目(web层项目的那个服务器)
    可能80端口被占用,且杀死不了,主要是因为对应的服务是系统的服务,所以我们需要以管理员的方式进入命令行窗口(cmd)
    执行net stop http,选择y,等待关闭,注意需要管理员方式,否则可能操作不了
    这时80端口没有占用了(对应关闭的服务并不是特别需要)
    但可能重启还是会继续占用,则继续执行sc config http start= disabled,使得重启不占用
    来到后端的web层项目:
    引入依赖:
    
    <dependency>
        <groupId>javax.servletgroupId>
        <artifactId>servlet-apiartifactId>
        <version>2.4version>
        <scope>providedscope>dependency>
    
    <dependency>
        <groupId>org.apache.httpcomponentsgroupId>
        <artifactId>httpclientartifactId>
        <version>4.5.12version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    在lagou包下,创建wx包,并在里面创建WxLoginController类:
    package com.lagou.wx;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import javax.servlet.http.HttpServletRequest;
    
    /**
     *
     */
    @RestController
    public class WxLoginController {
    
            @GetMapping("wxlogin")
            public Object wxlogin(HttpServletRequest request){
                String code = request.getParameter("code");
                System.out.println("【临时凭证】code="+code);
                return null;
            }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    记得观察对应扫描包时,是否包括这个包,否则相当于没有写(不会跳转)
    
        <dubbo:annotation package="com.lagou.controller,com.lagou.wx"/>
    
    
    • 1
    • 2
    • 3
    这时我们进行扫描二维码(点击登录的框框中的微信图形,用手机扫描一下二维码,点击允许
    在后端观看是否得到code的数据,若得到,则操作成功
    注意:对应的不同的浏览器可能会有对应的问题(如谷歌,若出现问题,可以换一个浏览器)
    特别是新的版本,现在一般会有,具体的问题在后面说明
    至此我们最后来操作code这个临时数据(临时授权码或者临时凭证):
    在程序上进行发送请求(这里是java):
    在对应的web层项目上的java资源文件下,创建包commons,并在里面创建HttpClientUtil类:
    package commons;
    
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    import java.net.URI;
    import java.util.Map;
    
    /**
     *
     */
    public class HttpClientUtil {
    
        public static String doGet(String url) {
            String s = doGet(url,null);
            return s;
    
        }
    
        /**
         * get请求 支持request请求方式,不支持restfull方式(了解即可)
         * @param url 请求地址
         * @param param 参数
         * @return 响应字符串
         */
        public static String doGet(String url, Map<String,String> param){
            //创建httpclient对象
            CloseableHttpClient aDefault = HttpClients.createDefault();
    
            String s = null;
            CloseableHttpResponse response = null;
            try {
                //创建url
                URIBuilder uriBuilder = new URIBuilder(url);
    
                if(param!=null){
                    //在url后面拼接请求参数
                    //map本身并不能操作迭代器,需要先变成set集合来操作,这里是操作key当set
                    for(String key:param.keySet()){
                        //当前的set,假设对应key的value值,实现拼接
                        uriBuilder.addParameter(key,param.get(key));
                    }
                }
                //对应的请求总地址,上面只是操作url的值,如加上http://等等
                URI uri = uriBuilder.build();
    
                //创建http get请求,并将总地址给他进行请求,使得是get方式
                HttpGet httpGet = new HttpGet(uri);
    
                //执行请求,并得到响应的结果
                response = aDefault.execute(httpGet);
    
                //获取响应结果中的状态码
                int statusCode = response.getStatusLine().getStatusCode();
    
                System.out.println(response); 
                //我这里是:HttpResponseProxy{HTTP/1.1 200 OK [Connection: keep-alive, Content-Type: text/plain, Date: Tue, 26 Jul 2022 02:09:12 GMT, Content-Length: 79] ResponseEntityProxy{[Content-Type: text/plain,Content-Length: 79,Chunked: false]}}
                System.out.println("响应的状态:" +response.getStatusLine()); 
                //响应的状态:HTTP/1.1 200 OK
                System.out.println("响应的状态:" +statusCode); //响应的状态:200
    
                //200表示响应成功
                if(statusCode==200){
                    //响应的内容字符串
                    System.out.println(response.getEntity());
                    //ResponseEntityProxy{[Content-Type: text/plain,Content-Length: 79,Chunked: false]}
                    System.out.println("---");
                    //得到有令牌的json字符串,只能获得一次
                    s = EntityUtils.toString(response.getEntity(),"UTF-8");
                    System.out.println(s);
                    //我这里是:不告诉你,嘿嘿(因为文章的检测不能写上去)
    //若出现类似于这样的:{"errcode":40125,"errmsg":"invalid appsecret, rid: 62df4cc8-00ff426a-331a0ac7"}
                    //则代表对应的secret不正确,前端写的是授权作用域,并不是对应的值
                    System.out.println("---");
                    System.out.println(1);
                    //System.out.println(EntityUtils.toString(response.getEntity()));
                    //不给你(主要是文章的检测不能写)
                    // 虽然UTF-8并没有起作用(好像的默认的),但也要防止默认识别不了中文
    //注意EntityUtils.toString(response.getEntity())中EntityUtils.toString()方法只能获得一次,如果再次获得
                    //就会报错,自然后面的代码就不会执行,当然也包括他的赋值,即得不到值
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if (response != null) {
                        response.close(); //关闭对应的对象,总得也让其他人也能执行请求吧
                    }
                    aDefault.close(); //关闭对应的对象
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
    
            return s;
        }
    }
    
    
    • 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
    对应后端代码:
     @GetMapping("wxlogin")
            public Object wxlogin(HttpServletRequest request){
                //微信官方给我们的临时凭证
                String code = request.getParameter("code");
                System.out.println("【临时凭证】code="+code);
    
                //通过code去微信官方申请一个正式的令牌(token)
                //发出一个get请求,httpclient-封装好的操作
                //微信官方文档上获得token令牌的请求
    
      String getTokenByCode_url = 
          "https://api.weixin.qq.com/sns/oauth2/access_token?
          appid=wxd99431bbff8305a0&secret=60f78681d063590a469f1b297feff3c4&code="+
          code+"&grant_type=authorization_code";
    
    
                
                String tokenString = HttpClientUtil.doGet(getTokenByCode_url);
    
                System.out.println(tokenString);
                return null;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    扫描二维码允许后,就得到对应的json字符串
    对应格式化之后是如下:
    {
        "access_token":不能写(文章的检测不能写) 
        "expires_in": 7200, 
        "refresh_token": "59_4V4PgKjvJ3JAGgRAH46VwqelRNNNgQ2-6FU48jtR98DgTWFWnVuFCMWgN0v5bI1DZ-VCL7a7IVP36dVddV-jkFlZVpLXPj3IW_Zys5P5Z6k", 
        "openid": "od4PTw7JYdV2XYGkJPPqF4nZvlfA", 
        "scope": "snsapi_login", 
        "unionid": "oEg8VuHZxBTgpzcrTi3rAEvwsU88"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    查看对应的微信官方文档中,是否是正确的格式:

    在这里插入图片描述

    对比发现,的确是是正确的,至此得到对应的token令牌成功(字符串有令牌参数及其值,简称为token字符串),即操作成功
    接下来我们通过token字符串来获得微信用户的信息
    在这之前我们需要先创建一个类:
    在java资源文件夹下,创建entity包,并在里面创建Token类:
    package entity;
    
    /**
     *
     */
    public class Token {
          private String access_token;//接口调用凭证
          private String expires_in; //access_token接口调用凭证超时时间,单位(秒)
          private String refresh_token;//用户刷新access_token
          private String openid; //授权用户唯一标识
          private String scope; //用户授权的作用域,使用逗号(,)分隔
          private String unionid; //当且仅当该网站应用已获得该用户的userinfo授权时,才会出现该字段
    
          public Token() {
          }
    
          public Token(String access_token, String expires_in, String refresh_token, String openid, 
                       String scope, String unionid) {
                this.access_token = access_token;
                this.expires_in = expires_in;
                this.refresh_token = refresh_token;
                this.openid = openid;
                this.scope = scope;
                this.unionid = unionid;
          }
    
          @Override
          public String toString() {
                return "Token{" +
                        "xxx'" + access_token + '\'' +
                        ", expires_in='" + expires_in + '\'' +
                        ", refresh_token='" + refresh_token + '\'' +
                        ", openid='" + openid + '\'' +
                        ", scope='" + scope + '\'' +
                        ", unionid='" + unionid + '\'' +
                        '}';
          }
    
          public String getAccess_token() {
                return access_token;
          }
    
          public void setAccess_token(String access_token) {
                this.access_token = access_token;
          }
    
          public String getExpires_in() {
                return expires_in;
          }
    
          public void setExpires_in(String expires_in) {
                this.expires_in = expires_in;
          }
    
          public String getRefresh_token() {
                return refresh_token;
          }
    
          public void setRefresh_token(String refresh_token) {
                this.refresh_token = refresh_token;
          }
    
          public String getOpenid() {
                return openid;
          }
    
          public void setOpenid(String openid) {
                this.openid = openid;
          }
    
          public String getScope() {
                return scope;
          }
    
          public void setScope(String scope) {
                this.scope = scope;
          }
    
          public String getUnionid() {
                return unionid;
          }
    
          public void setUnionid(String unionid) {
                this.unionid = unionid;
          }
    }
    
    
    • 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
    创建User类:
    package entity;
    
    /**
     *
     */
    public class User {
          private String openid;//普通用户的标识,对当前开发者帐号唯一
          private String nickname;//普通用户昵称
          private String sex;//普通用户性别,1为男性,2为女性
          private String province;//普通用户个人资料填写的省份
          private String city;//普通用户个人资料填写的城市
          private String country;//国家,如中国为CN
          private String headimgurl;//用户头像
        //最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像)
        //用户没有头像时该项为空
          private String privilege;//用户特权信息,json数组,如微信沃卡用户为(chinaunicom)
          private String unionid;//用户统一标识,针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的
    
        public User() {
        }
    
        public User(String openid, String nickname, String sex, String province, String city, String 
                    country, String headimgurl, String privilege, String unionid) {
            this.openid = openid;
            this.nickname = nickname;
            this.sex = sex;
            this.province = province;
            this.city = city;
            this.country = country;
            this.headimgurl = headimgurl;
            this.privilege = privilege;
            this.unionid = unionid;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "openid='" + openid + '\'' +
                    ", nickname='" + nickname + '\'' +
                    ", sex='" + sex + '\'' +
                    ", province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    ", country='" + country + '\'' +
                    ", headimgurl='" + headimgurl + '\'' +
                    ", privilege='" + privilege + '\'' +
                    ", unionid='" + unionid + '\'' +
                    '}';
        }
    
        public String getOpenid() {
            return openid;
        }
    
        public void setOpenid(String openid) {
            this.openid = openid;
        }
    
        public String getNickname() {
            return nickname;
        }
    
        public void setNickname(String nickname) {
            this.nickname = nickname;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getProvince() {
            return province;
        }
    
        public void setProvince(String province) {
            this.province = province;
        }
    
        public String getCity() {
            return city;
        }
    
        public void setCity(String city) {
            this.city = city;
        }
    
        public String getCountry() {
            return country;
        }
    
        public void setCountry(String country) {
            this.country = country;
        }
    
        public String getHeadimgurl() {
            return headimgurl;
        }
    
        public void setHeadimgurl(String headimgurl) {
            this.headimgurl = headimgurl;
        }
    
        public String getPrivilege() {
            return privilege;
        }
    
        public void setPrivilege(String privilege) {
            this.privilege = privilege;
        }
    
        public String getUnionid() {
            return unionid;
        }
    
        public void setUnionid(String unionid) {
            this.unionid = unionid;
        }
    }
    
    
    • 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
    接下来继续修改对应的后端:
       @GetMapping("wxlogin")
            public Object wxlogin(HttpServletRequest request){
                //微信官方给我们的临时凭证
                String code = request.getParameter("code");
                System.out.println("【临时凭证】code="+code);
    
                //通过code去微信官方申请一个正式的令牌(token)
                //发出一个get请求,httpclient-封装好的操作
                //微信官方文档上获得token令牌的请求
    
                String getTokenByCode_url = 
                    "https://api.weixin.qq.com/sns/oauth2/access_token?
                    appid=wxd99431bbff8305a0&secret=60f78681d063590a469f1b297feff3c4&code="+
                    code+"&grant_type=authorization_code";
    
                String tokenString = HttpClientUtil.doGet(getTokenByCode_url);
    
                System.out.println(tokenString);
    
                //将json格式的该token字符串转换成实体对象,方便存和取
                Token token = JSON.parseObject(tokenString, Token.class);
    
                //通过token,去微信官方获取用户的信息
    String getUserByToken =
        "https://api.weixin.qq.com/sns/userinfo?access_token="+token.getAccess_token()+
        "&openid="+token.getOpenid();
    
                System.out.println("----------------------");
                String UserString = HttpClientUtil.doGet(getUserByToken);
                System.out.println("UserString = " + UserString);
                //UserString = {"openid":"od4PTw7JYdV2XYGkJPPqF4nZvlfA","nickname":"king","sex":0,"language":"","city":"","province":"","country":"","headimgurl":"https:\/\/thirdwx.qlogo.cn\/mmopen\/vi_32\/YmfJ8sPH0ibZ6s8gl7p4ZOz4H6us5w3cwTNVBQ84YicdLt24NHbxc1vwFJZuePz7nicAKhQicQZuOSMI8Piah73MIxA\/132","privilege":[],"unionid":"oEg8VuHZxBTgpzcrTi3rAEvwsU88"}
    //发先对应的值不同了,因为请求不同,虽然具体的显示信息打印是相同的
                //但对应的信息还是需要EntityUtils.toString()来根据参数获得信息
                //该json字符串包含微信的信息,简称用户字符串
                
                //将json格式的用户字符串转换成实体对象,方便存和取
                //记得User类的对应的包是对应的
                User user = JSON.parseObject(UserString, User.class);
                System.out.println("微信的用户昵称 = " + user.getNickname());
                System.out.println("微信的用户头像 = " + user.getHeadimgurl());
                //项目流程
                //由于我们登录需要手机号和密码
                //所以我们需要将用户字符串中的数据当作手机号和密码进行登录(没有会自动注册的)
                //但要注意,一般需要是唯一的标识,我们通过用户字符串中,可以发现unionid参数一般是用户的唯一
                //你可以继续扫描,发现无论扫描多少次,该值一直不变,换个手机后(不同的微信用户)
                //该值就改变了,也就是说一个微信用户对应的该值是唯一的
                //那么就可以使用他来当作手机号和密码(两者都是这个)
                //为什么密码也要是这个呢,因为微信登录绑定的是微信
                //我们必须在有限的数据下进行登录且要安全,那么一般都会操作这个unionid参数
                //这样也可以防止,扫描码时,不会出现登录失败的情况
                //因为该值在就可以登录,而不会出现密码不正确的情况
                //至此通过分析可知,我们的手机号和密码用unionid参数值
                //但是一般我们需要对应的微信用户昵称和微信用户头像
                //这些在对应的登录后端中并没有操作(注册的操作),所以我们需要修改对应的注册逻辑(后面操作)
                return null;
            }
    
    • 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
    至此微信用户的信息就得到了,比如说"nickname":“king”,其中king就是我微信的名称
    当然一些值是0或者是""的,则是我们微信并没有设置的
    接下来我们修改对应的注册逻辑:
    dao层:
      /**
         * 用户注册
         *
         * @param phone 手机号
         * @param password 密码
         * @param nickname 昵称
         * @param headimg 头像
         * @return 受影响的行数,一般是1
         */
        Integer register(@Param("phone") String phone, @Param("password") String 
                         Password,@Param("nickname") String nickname,@Param("headimg") String headimg);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    service层:
      /**
         * 用户注册
         *
         * @param phone 手机号
         * @param password 密码
         * @param nickname 昵称
         * @param headimg 头像
         * @return 受影响的行数,一般是1
         */
        Integer register(String phone, String Password,String nickname,String headimg);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
      @Override
        public Integer register(String phone, String Password,String nickname,String headimg) {
            Integer register = userDao.register(phone, Password,nickname,headimg);
    
            return register;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    web层:
       /**
         * 用户注册
         *
         * @param phone 手机号
         * @param password 密码
         * @param nickname 昵称
         * @param headimg 头像
         * @return 受影响的行数,一般是1
         */
        Intege
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    对应的UserController类的ogin方法:
     @GetMapping("login")
    //参数也进行了修改
        public UserDTO login(String phone, String password,String nickname,String headimg) {
            UserDTO userDTO = new UserDTO<>();
            System.out.println(phone);
            System.out.println(password);
            System.out.println(nickname);
            System.out.println(headimg);
    
            User login = null;
            //检测手机号是否注册
            Integer integer = userService.checkPhone(phone);
            if (integer == 0) {
                //未注册,注册并登录
                userService.register(phone, password,nickname,headimg); //这里进行了修改
                userDTO.setMessage("手机号尚未注册,系统已帮您自动注册,请牢记密码!");
                login = userService.login(phone, password);
            } else {
                login = userService.login(phone, password);
                if (login != null) {
                    userDTO.setState(200); //规定200表示成功
                    userDTO.setMessage("登录成功");
                } else {
                    userDTO.setState(300); //规定300表示失败
                    userDTO.setMessage("登录失败");
                }
    
            }
            userDTO.setContent(login);
    
            System.out.println(login);
            return userDTO;
    
        }
    
    • 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
    改变后,对应的xml配置文件也进行改变:
    <insert id="register" parameterType="string">
            insert into user
                (name,portrait,phone,password,create_time,update_time)
            values
                (#{nickname},#{headimg},#{phone},#{password},sysdate(),sysdate())
    
         
        insert>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    至此我们运行对应的测试类TestUser里的register方法:
      @Test
        public void register(){
            Integer integer = userDao.register("11544","123123","1","2");
            System.out.println(integer); //0:未注册,大于0(通常为1):已注册
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    若数据库有对应的数据,则修改成功
    对应的部分前端代码(Header.vue组件里面的代码):
     return this.axios.get("http://localhost:8002/user/login",{
            params:{
              phone:this.phone,
              password:this.password,
              nickname:"",
              headimg:"",
            }
          }).then(res =>{
            
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    至此测试登录,随便写一个没有的,登录后,会自动注册,并登录,然后去数据库看看是否有对应数据,若有,则操作成功
    我们在对应的wxlogin方法里添加如下代码:
      System.out.println("微信的unionid参数值:" + user.getUnionid());
                //跳转到当前项目开始的这个路径,也就能找到了
    return "user/login?phone="+user.getUnionid()+"&password="
        +user.getUnionid()+"&nickname="+user.getNickname()+"&headimg="+user.getHeadimgurl();
         //记得在最后添加
    
    • 1
    • 2
    • 3
    • 4
    • 5
    至此启动对应的项目,并在前端扫描二维码进行登录,若返回了对应的数据,且数据库里也有对应数据,则登录成功
    且你会发现,对应的url那里前端进行了访问,但后端的对应的请求并没有显示出来
    也就是不依靠浏览器的显示了,安全性的确提高
    但是对应的使用return会到其他的方法里面去,实际上我们操作该方法后,需要跳转到原来的前端页面
    至此对应的wxlogin方法要进行改变,但对应的数据并不会得到,所以我们需要修改WxLoginController类:
    package com.lagou.wx;
    
    import com.alibaba.dubbo.config.annotation.Reference;
    import com.alibaba.fastjson.JSON;
    import com.lagou.entity.UserDTO;
    import com.lagou.service.UserService;
    import commons.HttpClientUtil;
    import entity.Token;
    import entity.User;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     *
     */
    @RestController
    //@Controller
    public class WxLoginController {
    
        @Reference //远程调用
        private UserService userService;
    
        private UserDTO userDTO = null; //是否用微信登录成功,若是null,则表示尚未登录
    
    
        @GetMapping("wxlogin")
        public UserDTO wxlogin(HttpServletRequest request, HttpServletResponse response) throws 
            IOException {
            //微信官方给我们的临时凭证
            String code = request.getParameter("code");
            System.out.println("【临时凭证】code=" + code);
    
            //通过code去微信官方申请一个正式的令牌(token)
            //发出一个get请求,httpclient-封装好的操作
            //微信官方文档上获得token令牌的请求
    
            String getTokenByCode_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=wxd99431bbff8305a0&secret=60f78681d063590a469f1b297feff3c4&code=" + code + "&grant_type=authorization_code";
    
            String tokenString = HttpClientUtil.doGet(getTokenByCode_url);
    
            System.out.println(tokenString);
    
            //将json格式的该token字符串转换成实体对象,方便存和取
            Token token = JSON.parseObject(tokenString, Token.class);
    
            //通过token,去微信官方获取用户的信息
            String getUserByToken = "https://api.weixin.qq.com/sns/userinfo?access_token=" + 
                token.getAccess_token() + "&openid=" + token.getOpenid();
    
            System.out.println("----------------------");
            String UserString = HttpClientUtil.doGet(getUserByToken);
            System.out.println("UserString = " + UserString);
            //UserString = {"openid":"od4PTw7JYdV2XYGkJPPqF4nZvlfA","nickname":"king","sex":0,"language":"","city":"","province":"","country":"","headimgurl":"https:\/\/thirdwx.qlogo.cn\/mmopen\/vi_32\/YmfJ8sPH0ibZ6s8gl7p4ZOz4H6us5w3cwTNVBQ84YicdLt24NHbxc1vwFJZuePz7nicAKhQicQZuOSMI8Piah73MIxA\/132","privilege":[],"unionid":"oEg8VuHZxBTgpzcrTi3rAEvwsU88"}
            //发先对应的值不同了,因为请求不同,虽然具体的显示信息打印是相同的
            //但对应的信息还是需要EntityUtils.toString()来根据参数获得信息
            //该json字符串包含微信的信息,简称用户字符串
    
    
            //将json格式的用户字符串转换成实体对象,方便存和取
            //记得User类的对应的包是对应的
            User user = JSON.parseObject(UserString, User.class);
            System.out.println("微信的用户昵称 = " + user.getNickname());
            System.out.println("微信的用户头像 = " + user.getHeadimgurl());
    
            //项目流程
            //由于我们登录需要手机号和密码
            //所以我们需要将用户字符串中的数据当作手机号和密码进行登录(没有会自动注册的)
            //但要注意,一般需要是唯一的标识,我们通过用户字符串中,可以发现unionid参数一般是用户的唯一
            //你可以继续扫描,发现无论扫描多少次,该值一直不变,换个手机后(不同的微信用户)
            //该值就改变了,也就是说一个微信用户对应的该值是唯一的
            //那么就可以使用他来当作手机号和密码(两者都是这个)
            //为什么密码也要是这个呢,因为微信登录绑定的是微信
            //我们必须在有限的数据下进行登录且要安全,那么一般都会操作这个unionid参数
            //这样也可以防止,扫描码时,不会出现登录失败的情况,因为该值在就可以登录,而不会出现密码不正确的情况
            //至此通过分析可知,我们的手机号和密码用unionid参数值
            //但是一般我们需要对应的微信用户昵称和微信用户头像
            //这些在对应的登录后端中并没有操作(注册的操作),所以我们需要修改对应的注册逻辑
    
            System.out.println("微信的unionid参数值:" + user.getUnionid());
            //跳转到当前项目开始的这个路径,也就能找到了
            /*
            return "user/login?phone="+user.getUnionid()+"
            &password="+user.getUnionid()+"
                &nickname="+user.getNickname()+"&headimg="+user.getHeadimgurl();
                */
    
            userDTO = new UserDTO<>(); //不要自己声明,因为优先使用局部变量,就近原则
    
            com.lagou.entity.User login = null;
            //检测手机号是否注册
            Integer integer = userService.checkPhone(user.getUnionid());
            if (integer == 0) {
                //未注册,注册并登录
                userService.register(user.getUnionid(), user.getUnionid(), user.getNickname(), 
                                     user.getHeadimgurl());
                userDTO.setMessage("手机号尚未注册,系统已帮您自动注册,请牢记密码!");
                login = userService.login(user.getUnionid(), user.getUnionid());
            } else {
                login = userService.login(user.getUnionid(), user.getUnionid());
                if (login != null) {
                    userDTO.setState(200); //规定200表示成功
                    userDTO.setMessage("登录成功");
                } else {
                    userDTO.setState(300); //规定300表示失败
                    userDTO.setMessage("登录失败");
                }
    
            }
            userDTO.setContent(login);
    
    
            response.sendRedirect("http://localhost:8080/");
            //这样我们又回到的前端那个页面,但是由于是重定向,即对应的返回的数据并没有返回
            //不会经过其他操作,转发也是如此
            //但是对应的数据并没有过去
            return null;
        }
    
        @GetMapping("checkWXStatus")
        public UserDTO checkWXStatus() {
            return userDTO;
        }
        
          @GetMapping("logout")
        public Object logout() { //若返回值为void,则默认是空的值,相当于null和""
            userDTO = null;
            return null;
        }
    
    }
    
    
    • 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
    对应的前端Header.vue:
      created(){
        
        //当刷新页面,组件创建成功之后,立刻检测本地储存中是否存在用户对象
        console.log(JSON.parse(localStorage.getItem("user")));
        //将得到的字符串变成对应的对象,也可以说是JSON对象,使得可以操作,即"对象.key"的获取值
         this.userDTO =JSON.parse(localStorage.getItem("user")); 
         console.log(this.userDTO)
         if( this.userDTO != null){
             this.isLogin = true //更新登录状态,表示已登录
         }else{
           //检测微信是否登录过
            return this.axios.get("http://localhost:80/checkWXStatus").then(res =>{
              console.log(22)
           console.log(res)
            this.userDTO = res.data 
            //这里我们得到了对应扫描二维码的数据,对应的操作基本在login方法里面,这里我们直接重新调用即可
            //反正已经注册了
            if(this.userDTO!=""){
            this.phone = this.userDTO.content.phone
            this.password = this.userDTO.content.password
            //有了账号密码,就直接走登录
            this.login()
                //若不执行上面的this.login(),则需要执行下面的代码,很明显
                //代码很多(实际上就算对应方法里面的操作),但也减少了一次后端的访问
                //有时候代码虽多,但性能更好,在好的情况下,可以牺牲性能来更好的维护
                //if(this.userDTO.content!=null){
            // this.isLogin = true //更新登录状态
            // //将登录成功的信息进行保存(本地),使得刷新界面时,可以继续得到信息,基本只与浏览器有关
            // localStorage.setItem("user",JSON.stringify(this.userDTO)) //将字符串数据变过去
                //}
            // //初始化
            // this.phone = "";
            // this.password="";
          
            // this.dialogFormVisible = false; // 不显示登录框
    
            // this.$router.push("/" );
            // window.location.reload()
            }
            //虽然可以不走登录,复制登录里面的操作即可,因为this.userDTO有值了
          }).catch(err =>{
            this.$message.error("登录失败")
          })
         }
      },
    
    • 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
     logout(){//登出
    localStorage.setItem("user",null)
     this.isLogin = false
    
      return this.axios.get("http://localhost:80/logout").then(res =>{
           this.$router.push("/");
            window.location.reload()
     alert("已登出")
          }).catch(err =>{
            this.$message.error("登出失败")
          })
          alert(1)
    
     
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    我们可以发现,对应的端口都是80(虽然也可以是其他端口),但最好相同,因为他们都是得到和设置值
    需要在一个服务器里面操作(可以说,一个端口代表一个服务器的运行)
    至此我们扫描二维码进行操作,若有对应的信息(登录那里),则微信登录完成
    在前面应该说过,微信的扫描二维码并允许后,可能会出现浏览器的问题(一般是谷歌)
    解决二维码在谷歌浏览器的bug :
    通过官方js:http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
    可以知道,对应的二维码是放在iframe标签下的,但是一般新版本的谷歌可能并没有设置跨域问题
    旧版的谷歌却可以(但不一定)也就是如下:

    在这里插入图片描述

    为什么不能让他跨域呢,因为是安全问题,iframe可以内嵌其他网页,而对应的网页我们却无法控制
    也就是说,若该网页是恶意的网站,那么对应用户来说是非常危险的,所以一般会让他不能跨域
    sandbox包含的属性及作用:

    在这里插入图片描述

    加上 sandbox="allow-scripts allow-top-navigation allow-same-origin"属性
    即可解决官方js:http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
    由于基本无法修改微信服务器上的js文件,所以我们将js代码放在本地并进行修改(复制上面官方的js):
    created(){
        //下面的代码记得放在最前面,防止对应的return使得他没有操作
    !(function (a, b, c) {
            function d(a) {
              var c = "default";
              a.self_redirect === !0
                ? (c = "true")
                : a.self_redirect === !1 && (c = "false");
              var d = b.createElement("iframe"),
                e =
                  "https://open.weixin.qq.com/connect/qrconnect?appid=" +
                  a.appid +
                  "&scope=" +
                  a.scope +
                  "&redirect_uri=" +
                  a.redirect_uri +
                  "&state=" +
                  a.state +
                  "&login_type=jssdk&self_redirect=" +
                  c +
                  "&styletype=" +
                  (a.styletype || "") +
                  "&sizetype=" +
                  (a.sizetype || "") +
                  "&bgcolor=" +
                  (a.bgcolor || "") +
                  "&rst=" +
                  (a.rst || "");
              (e += a.style ? "&style=" + a.style : ""),
                (e += a.href ? "&href=" + a.href : ""),
                (d.src = e),
                (d.frameBorder = "0"),
                (d.allowTransparency = "true"),
                // 允许多种请求,下面的就是我加上的,用来解决浏览器中对应的跨域(iframe)问题
                (d.sandbox = "allow-scripts allow-top-navigation allow-same-origin"), 
                (d.scrolling = "no"),
                (d.width = "300px"),
                (d.height = "400px");
              var f = b.getElementById(a.id);
              (f.innerHTML = ""), f.appendChild(d);
            }
            a.WxLogin = d;
          })(window, document);
    }
    
    • 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
    我们直接将该代码放到对应的created()的钩子函数里面,使得操作我们自己的js,而不使用官方的
    将对应的引入的注释:
    //引入
    //import wxlogin from 'vue-wxlogin'; 
    
    • 1
    • 2
    组件也不同声明了:
    components:{
        //wxlogin
      },
    
    • 1
    • 2
    • 3
    对应的html也注释掉:
    
    
    
    • 1
    • 2
    • 3
    实际上对应的组件操作也是封装了二维码js的执行,我们可以直接自己定义来保存二维码:
    <div id="wxLoginForm">div> 
    
    • 1
    接下来我们找到对应的方法,因为组件操作了对应二维码的生成,和一些属性
    但我们自己定义的,需要自己进行操作,这里当然是默认没有的,因为什么都没有操作,看如下代码:
     goToLoginWX() {
          //alert("微信登录");
          //普通的登录表单隐藏
      
          document.getElementById("loginForm").style.display="none"
    
          //显示二维码,这一步可以不需要,因为对应的并没有设置为none
          //  document.getElementById("wxLoginForm").style.display="block"
    
          //生成二维码
          //待dom更新后再用二维码渲染内容
          this.$nextTick(function(){ 
            //好像这个this.$nextTick并不需要写,直接执行 this.createCode()也可以
              //微信登录可以不用,但微信支付一般要用,因为容易先出来
            //主要是为了使得二维码后出来,而不会抢先出来,使得出现问题
            //若报错,则写上
            this.createCode(); 
            // 直接调用可能会报错(先出来):TypeError: Cannot read property 'appendChild' of null
            //这时就需要对应的this.$nextTick
         
         
      });
        },
        //生成二维码
      createCode(){
        
     
         //由于对应的官方js已经操作其函数,也就是使得WxLogin=对应的自己写的函数
         //所以我们直接创建其对象赋值即可,在创建对象的时候是会执行他的内容的,也就是执行方法进行赋值而已
         //其中返回的是基本是对应的方法的变量
         //所以说,在创建对象时,就执行的对应的方法
        var obj = new WxLogin({
          id:"wxLoginForm",//挂载点,二维码的容器
          appid:"wxd99431bbff8305a0", 
          scope:"snsapi_login", 
          redirect_uri:"http://www.pinzhi365.com/wxlogin", 
          })
    
          //所以总体来说,就是要使得对应的参数,如上面的
          /*
          appid:"wxd99431bbff8305a0", 
          scope:"snsapi_login", 
          redirect_uri:"http://www.pinzhi365.com/wxlogin", 
          */
         //给对应的方法里面,这时只要你进行扫描
        //就会发送二维码里面的请求(其中对应 function d(a)方法里面有一个请求)
         //即https://open.weixin.qq.com/connect/qrconnect?appid=,后面省略
        //他就是对应二维码的显示
        //因为d.src = e,e就是对应的请求地址,不同的e对应的二维码显示可能不同,如有对应的样式操作
        //而扫描的就是二维码自带的请求
         //而我们操作组件时,里面封装了对应js,和对应的参数,一般需要我们去绑定,然后扫描也会去请求
        //所以是一样的操作,只是方式不同而已,只是组件帮我们封装好了的
        
      },
    
    • 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
    至此,浏览器的问题就解决了,我们发现,对应的js实际上就是直接设置iframe的属性
    可以得知,浏览器可能会给该标签默认设置其他不跨域的属性,所以需要我们手动设置,使得不操作默认
    若我们需要修改二维码的样式,可以添加如下代码:
    .impowerBox .qrcode {width: 200px;}
    .impowerBox .title {display: none;}
    .impowerBox .info {width: 200px;}
    .status_icon {display: none}cs
    .impowerBox .status {text-align: center;}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    但是实际上要操作该样式,在对应的参数下面基本不能这样的写,因为{}不能对json格式不对,如:
    href:"
    .impowerBox .qrcode {width: 200px;} //二维码的宽度
    .impowerBox .title {display: none;}
    .impowerBox .info {width: 200px;} //二维码的高度
    .status_icon {display: none}cs
    .impowerBox .status {text-align: center;}
    "
    //你将他放在前端json里面,会发现,全部看成一个字符串,并没有css的操作
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    那么如何使得可以操作对应的样式呢,我们可以在对应的WxLogin对象中(方法里的操作)
    直接传递href值,由于该值会被请求的地址服务器解析(二维码的显示的请求)
    使得进行对应的操作显示,而该解析我们需要对应的加密,这是规定的,因为对应地址服务的解析时,会解密进行操作
    我们用站长工具对样式代码进行base64加密:http://tool.chinaz.com/Tools/Base64.aspx
    //加密后的数据是
    LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDIwMHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDIwMHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZX1jcw0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30=
    
    • 1
    • 2
    这时,对应的js代码是:
     var obj = new WxLogin({
          id:"wxLoginForm",//挂载点,二维码的容器
          appid:"wxd99431bbff8305a0", 
          scope:"snsapi_login", 
          redirect_uri:"http://www.pinzhi365.com/wxlogin", 
          href: "data:text/css;base64,LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDIwMHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDIwMHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZX1jcw0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30=" //加载修改二维码的css样式
          })
     
     
     //其中data:text/css;base64规定了对应的解密方式,基本不能改变
     
       //LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDMwMHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDMwMHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZX1jcw0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30= 宽300,高300
         //LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDIwMHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDIwMHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZX1jcw0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30= 宽200,高200
    //LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6ID,至此后面的只有部分不同,即Mw和Iw(其余基本相同)
     //防止你以为他是一样的,所以这里给出了不同
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    这时我们再次看看二维码,发现二维码变小的,至此样式也操作成功
    我们操作完了微信的登录,接下来我们操作微信的支付:
    创建二维码:
    安装 qrcodejs2 (注意:安装的是qrcodejs2,不要安装qrcode —> 会报错)
    npm install qrcodejs2 --save #之前的是操作登录的,这里是操作支付
    
    • 1
    在Course.vue组件的页面中引入:
    
    
    <el-dialog :visible.sync="dialogFormVisible" style="width:800px;margin:0px auto;">
      <h1 style="font-size:30px;color:#00B38A">微信扫一扫支付h1>
      <div id="qrcode" style="width:210px;margin:20px auto;">div>
    el-dialog>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
     data() {
        return {
          comment:null, //待发表的留言内容
          activeName: "intro",
          course:null,
          totalLessons:0, //本门课程的总节数
          commentList:null, //所有留言
          isLogin:false, //false,未登录
          isBuy:false,//未购买
          user:null, //当前用户,{}对象可以","结尾
          myCourseList:[], //当前用户购买过的所有课程
          dialogFormVisible:false //这里操作是否显示弹出的框框
        };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    
    <script>
    import Header from "./Header/Header"; //顶部登录条
    import Footer from "./Footer/index"; //顶部登录条
    import QRCode from 'qrcodejs2'; // 引入qrcodejs
    export default {
    
      name: "Course",
      components: {
        Header,
        Footer,
        QRCode //声明组件
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    这里有个问题,前面的操作问题
    if(this.user != null){
      this.isLogin = true //已登陆
    
    if(this.course!=null){ 
      this.getMyCourseList() //查询登录用户购买的所有课程
    }
    this.getComment(); 
        //这里放在这个判断里面,因为其中的操作会用到user,若user没有值,那么可能会使得当前页面操作不了
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
     buy(courseid) {
      
          //alert("购买第【" + courseid + "】门课程成功,加油!");
          this.dialogFormVisible = true; //显示提示框框
                  //一般不能有对应的当前所在标签的报错,如上面的user没有值,那么可能会使得不会显示出来
         
         //待dom更新后再用二维码渲染内容(使得二维码后出来,而不是先出来)
          this.$nextTick(function(){ 
          this.createCode()  
              // 直接调用可能会报错(先出来):TypeError: Cannot read property 'appendChild' of null
          });
        },
        //生成二维码
        createCode(){
           document.getElementById("qrcode").innerHTML = ""; 
            //防止多次点击时,对应的前面的二维码一直存在,使得出现多个二维码
            //写在最前面,使得最先删除对应的信息
          //QRCode(dom元素的id,二维码的属性参数)
    let qrcode = new QRCode("qrcode",{
      width:200, //记得不能加上px,因为他默认给你加上了,否则会报错,因为没有200pxpx
      height:200,
      text:"我爱你中国" //二维码中包含的信息
    }); 
                    //qrcode就是当前有id="qrcode"的标签
        },
    
    • 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
    至此,点击立即购买,发现,对应的二维码出来了,用微信扫描,就会出现吗设置的信息"我爱你中国",至此二维码显示操作完毕
    准备工作:
    名词介绍:

    在这里插入图片描述

    如果获得这些信息,需要注册认证公众号,费用300元/次
    获取认证的流程 :
    注册公众号(类型:服务号) ,或者微信开发平台操作的网站(如企业),这里就操作公众号了
    根据营业执照类型选择以下主体注册:个体工商户 | 企业/公司 | 政府 | 媒体 | 其他类型
    认证公众号:
    公众号认证后才基本可以去申请微信支付:300元/次
    提交材料申请微信支付 :
    登录公众平台,左侧菜单【微信支付】,开始填写资料等待审核,审核时间1~5工作日这里需要提交的资料有营业执照
    开户成功,登录商户平台进行验证:
    资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证
    在线签署协议:
    本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效
    查看自己的公众号的参数 :
    测试的数据,一般不能操作,下面只是用来看的,让你知道需要哪些数据
    即当你使用下面的信息时,一个返回的数据中会说明签名错误,因为没有对应数据(如appid,企业一般是corpid,对应于appid)
    他们一般是微信公众号和企业微信,都是微信,因为使用微信支付总得是微信吧
    所以到这里,后面的可以进行了解,当你有对应企业或者微信公众号的这些数据时(自己进行申请)
    可以回到这里再次查看进行操作:
    public class PayConfig {    
        //企业公众号ID
        public static String appid = "wx8397f8696b538317";    
        // 财付通平台的商户帐号
        public static String partner = "1473426802";    
        // 财付通平台的商户密钥
        public static String partnerKey = "8A627A4578ACE384017C997F12D68B23";    
        // 回调URL
        public static String notifyurl = "http://a31ef7db.ngrok.io/WeChatPay/WeChatPayNotify";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    支付流程:

    在这里插入图片描述

    工具介绍:
    SDK(软开开发工具包,也有对应的其他介绍,如API列表):
    https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=11_1

    在这里插入图片描述

    如果不是maven项目,一般需要下载对应的jar包,若是maven项目,则引入依赖即可
    对应的依赖:
    <dependency>
        
        <groupId>com.github.wxpaygroupId>
        <artifactId>wxpay-sdkartifactId>
        <version>0.0.3version>
        dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    主要使用sdk中的三个功能:
    获取随机字符串(生成订单编号):
    WXPayUtil.generateNonceStr();
    
    • 1
    将map转换成xml字符串(自动添加签名,对应给微信支付的信息一般需要是xml信息):
    WXPayUtil.generateSignedXml(map,partnerKey);
    
    • 1
    将xml字符串转换整map:
    WXPayUtil.xmlToMap(result);
    
    • 1
    JFinal 框架:
    JFinal 是基于Java 语言的极速 web 开发框架
    其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展
    基本可以取代HttpClient
    对应的依赖:
    <dependency>
        
        <groupId>com.jfinalgroupId>
        <artifactId>jfinalartifactId>
        <version>3.5version>
        dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    构建二维码 :
    外面需要将对应的支付连接放在二维码里面
    在Course.vue组件里面:
    //生成二维码
        createCode(){
    
             document.getElementById("qrcode").innerHTML = ""; 
            //防止多次点击时,对应的前面的二维码一直存在,使得出现多个二维码
            //写在最前面,使得最先删除对应的信息,不要放在对应的axios里面
            //防止,他请求失败或者缓慢时,对应的信息没有及时删除,而出现二维码
            
    //去获取支付连接
     this.axios.get("http://localhost:80/createCode",{
       params:{
         courseid:this.course.id,
         coursename:  this.course.courseName,//总不能显示对应id的数据吧,用这个来显示
         price:this.course.discounts 
           //测试的时候,最好设置为1,代表1分钱,总不能测试是使用真实数据吧
           //除非你是土豪,或者就是你的公众号和企业
       }
     }).then(res => {
    console.log(res)
        
       //QRCode(dom元素的id,二维码的属性参数)
      let qrcode = new QRCode("qrcode",{
      width:200,
      height:200,
      text:res  //将返回的数据放入到二维码中
          //一般直接扫描得到的对象数据,手机上基本是显示不了的,但我们并不是操作显示
      //二维码中包含的信息
    }); //qrcode就是当前有id="qrcode"的标签
        }).catch(err =>{
          this.$message.error("生成二维码失败")
        })
    
       
        },
    
    • 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
    在web层项目下的commons包下创建实体类PayConfig:
    package commons;
    
    /**
     *
     */
    public class PayConfig {
    //下面的数据是错误的,因为并不会给出真实的数据,我操作时,使用了正确的数据
        
        //企业公众号ID
        public static String appid = "wx8397f8696b538317";
        // 财付通平台的商户帐号
        public static String partner = "1473426802";
        // 财付通平台的商户密钥
        public static String partnerKey = "8A627A4578ACE384017C997F12D68B23";
        // 回调URL
        public static String notifyurl = "http://a31ef7db.ngrok.io/WeChatPay/WeChatPayNotify";
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    该类先放在这里,后面有可能会再次进行操作(如修改数值,虽然并不会)
    在对应的web层项目中的wx包下,创建WxPayController类:
    package com.lagou.wx;
    
    import com.github.wxpay.sdk.WXPayConfig;
    import com.github.wxpay.sdk.WXPayUtil;
    import com.jfinal.kit.HttpKit;
    import commons.PayConfig;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     *
     */
    @RestController
    public class WxPayController {
    
        @GetMapping("createCode")
        public Object createCode(String courseid,String coursename,String price) throws Exception {
    
            //注意:一般对应map的value值不能是null,否则报错,其他的map值也是如此,当然""可以,但最好不要
            //因为信息最好按照规定来,否则可能容易会被盗取或者发生错误等等
            
            //编写商户信息写入到map中
     //在这之前,查看对应的https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1地址
            //看看参数的必填项,并操作必填项
            Map<String,String> map = new HashMap<>();
            map.put("appid", PayConfig.appid); //公众账号ID,微信分配的公众账号ID(企业号corpid即为此appid)
            map.put("mch_id",PayConfig.partner); //商户号,微信支付分配的商户号
            map.put("nonce_str", WXPayUtil.generateNonceStr()); //随机字符串,不长于32位
            //WXPayUtil.generateNonceStr()在底层实际上是UUID的操作,且不长于32位
            //即return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
            //replaceAll("-", "")作用是将-替换成""
            //substring(0, 32)作用是取得0开始到32的字符串,包括0,但不包括32
            //一般UUID.randomUUID().toString()的值,除了-外
            //正好是32个字母(即32个数),但加上substring(0, 32)是为了以防万一
            //对应的(数字)签名(sign)虽然是必填项,但并不需要写,因为会自动帮我们生成
            map.put("body",coursename); //商品的描述,这里就传递对应的课程名称
            map.put("out_trade_no",WXPayUtil.generateNonceStr()); //随机生成的商户订单号
            //商户系统内部订单号,要求32个字符内(最少6个字符)
            //只能是数字、大小写字母,_,-,|,*,且在同一个商户号下唯一,所以这里也随机生成字符串
            map.put("total_fee",price); //订单金额,说明:订单总金额,单位为分,只能为整数
            map.put("spbill_create_ip","127.0.0.1");
            //支持IPV4和IPV6两种格式的IP地址,调用微信支付API的机器IP,一般是当前服务器的ip
            //付款码也是自动生成的,所以也不用写
            //这里有几个可能对应的介绍并没有给出说明,如下面的map:
            map.put("notify_url",PayConfig.notifyurl); //通知地址(回调URL)
            map.put("trade_type","NATIVE"); //交易类型
    
            System.out.println("商户信息:" +map);
    
            //生成数字签名,并把商户信息转换成xml格式
            String xml = WXPayUtil.generateSignedXml(map, PayConfig.partnerKey);
    
            System.out.println("商户的xml信息:" +xml);
            /*
            返回如下:
    
    
        674572d7a645449492f570b99a73aaf6
        d2359dec6bec4961bdc2bd6958aa7bd5
        wx8397f8696b538317
        100
        08FB22C809DAEA9E6748B352E73B3358 这个签名的确进行了自动添加,一般根据对应的密钥值来操作
        NATIVE
        1473426802
        文案高手的18项修炼
        http://a31ef7db.ngrok.io/WeChatPay/WeChatPayNotify
        127.0.0.1
    
    我们发现付款码并没有添加,实际上他由前端来进行添加了,因为对应的码在前端,这里看不到
             */
            //上面的信息总体来说我们称为数字签名
            //实际上该签名由当前的map集合数据以及时间戳(一般没有)
            //根据MD5算法来操作(一般操作有随机数,且不会相同,如UUID,或者密钥),来生成的
            //正是因为随机数和时间戳(一般没有),所以基本不会一样
    
            //将xml数据(包含了对应的商户信息和对应企业公众号id,微信的)发送给微信支付平台
            //从而使得可以生成订单,因为有对应的id,那么对应的支付对象也就是该企业了
            //而商户信息则代表合法的商品,因为不能随便填写的,至此,商家合法,企业合法
            //即可以给出支付的链接(虽然也有其他信息,包括该链接,通常称该总的信息为支付链接)
            String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            //发送请求,并返回一个xml格式的字符串
            String post = HttpKit.post(url, xml); 
            //将对应的xml信息给微信支付平台(因为一般他们通常操作xml信息)
    
            System.out.println(post);
            //到这里,由于是测试数据,那么基本会显示
            /*
            
    
    
    
    这是因为对应的密钥错误,进行解密签名时出错的,密钥不可用
    更有可能是对应的id和账号,由于是测试的,所以你将他看成是成功的
             */
    
            //微信支付平台返回xml格式的数据,将其转换成map格式并返回给前端
            Map<String, String> map1 = WXPayUtil.xmlToMap(post);
    
            System.out.println(map1);
            //{return_msg=签名错误,请检查后再试, return_code=FAIL}
            
            //正确的数据中,有对应的code_url,这个就是支付连接(链接),用来进行支付的,一般有过期时间
            //我们需要将该地址,给对应的二维码里面,然后手机扫描后,经过对应的操作(后面会进行编写)就会到该地址
            //进行支付操作,使得我们会给对应的注册的商家进行支付,然后返回支付的状态
    
            //当然若到了过期时间,那么再次的扫描自然也是没有作用的
            return map1;
            
        }
    }
    
    
    • 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
    对应的Course.vue:
      //QRCode(dom元素的id,二维码的属性参数)
      let qrcode = new QRCode("qrcode",{
      width:200,
      height:200,
      text:res.data.code_url   //将该地址放在该二维码里面,就会进行跳转
     
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    这时打开你的手机,扫描对应的二维码,我的出现如下:

    在这里插入图片描述

    100分也就是1.00元
    至此若出现如上的操作,那么说明链接正确,即操作完成
    但是我们会发现有对应的乱码,对应解决方法:
    coursename = new String(coursename.getBytes("ISO-8859-1"),"UTF-8");
    
    • 1
    与前面的操作(好像是上一章的操作)一样,操作完成后,那么应该会出现如下(我这里是):

    在这里插入图片描述

    至此中文乱码解决
    检查支付状态:
    在这之前,我们需要修改一下对应后端的代码:
      Map<String, String> map1 = WXPayUtil.xmlToMap(post);
            map1.put("orderId",map.get("out_trade_no"));
    //需要在返回之前,传递订单编号,因为对应微信支付平台,返回的xml数据中,并没有订单编号,虽然你传递了过去
    //为什么需要订单编号呢,这是为了使得查询状态
    //由于该订单编号(基本唯一,操作的UUID)和一系列信息使得得到了对应的支付链接
    //实际上在高并发下UUID也可能会重复
    //这时一般需要用户的具体方位或者结合小锁(有条件的锁)来实现解决重复问题,当然还有其他方法,这里就不做说明
    //只要我们扫描支付链接并支付成功,那么对应的该订单编号对应的状态在微信支付平台那里有所改变
    //这时我们可以通过该编号去对于的微信支付平台获取状态(一般需要做循环)
    //其中扫描支付和该获得状态是互不影响的,实际上都是操作微信支付平台对应的状态
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    对应的前端(Course.vue):
     //生成二维码
        createCode(){
    
    //去获取支付连接
      document.getElementById("qrcode").innerHTML = ""; 
            //防止多次点击时,对应的前面的二维码一直存在,使得出现多个二维码
            //写在最前面,使得最先删除对应的信息,不要放在对应的axios里面
            //防止,他请求失败或者缓慢时,对应的信息没有及时删除,而出现二维码
            
     this.axios.get("http://localhost:80/createCode",{
       params:{
         courseid:this.course.id,
         coursename:  this.course.courseName,//总不能显示对应id的数据吧,用这个来显示
         price:this.course.discounts
       }
     }).then(res => {
    
    
       //QRCode(dom元素的id,二维码的属性参数)
      let qrcode = new QRCode("qrcode",{
      width:200,
      height:200,
      text:res.data.code_url  //将返回的数据放入到二维码中
      //二维码中包含的信息
    }); //qrcode就是当前有id="qrcode"的标签
    
    
          //检查支付状态
          this.axios.get("http://localhost:80/checkOrderStatus",{
            params:{
             orderId:res.data.orderId //传递 订单编号 进行查询
            }
          }).then(res => {
          //检查支付状态
              }).catch(err =>{
                this.$message.error("查询订单状态失败")
              })
         
         //发现这里二维码生成后,也就是支付链接放到二维码里面后,然后再次进行检查支付状态
    
    
              }).catch(err =>{
                this.$message.error("生成二维码失败")
              })
    
       
        },
    
    • 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
    对应的后端:
      @GetMapping("checkOrderStatus")
        public Object createCode(String orderId) throws Exception {
    
            //编写商户信息
            //在该https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_02地址下
            //看看参数的必填项,并操作必填项
            Map<String,String> map = new HashMap<>();
            map.put("appid", PayConfig.appid); //公众账号ID,微信分配的公众账号ID(企业号corpid即为此appid)
            map.put("mch_id",PayConfig.partner); //商户号,微信支付分配的商户号
            map.put("out_trade_no ", orderId); //商户订单号
            //商户系统内部订单号,要求32个字符内(最少6个字符),只能是数字、大小写字母,_,-,|,*
            //且在同一个商户号下唯一,即值为orderId(因为他是通过前面的UUID操作来的,搞好符合要求)
            map.put("nonce_str",WXPayUtil.generateNonceStr()); //随机字符串,不长于32位
    
            ///对应的(数字)签名(sign)虽然是必填项,但并不需要写,因为会自动帮我们生成
    
            //生成数字签名
            String xml = WXPayUtil.generateSignedXml(map, PayConfig.partnerKey);
    
            //发送查询请求给微信支付平台
            String url = "https://api.mch.weixin.qq.com/pay/orderquery";
    
            String post = HttpKit.post(url, xml);
    
            //对微信支付平台返回的查询结果进行处理
            Map<String, String> map1 = WXPayUtil.xmlToMap(post);
            return map1;
        }
    
    • 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
    至此,我们点击二维码,不需要扫描,执行对应的方法
    在对应的map集合中可以获得对应的状态(一般是订单未支付,一般保存在trade_state_desc字段)
    对于状态的判断,我们一般需要在后端进行如下的编写,前面分析过
    我们的支付成功与否,对应修改对应的编号的状态,所以我们需要循环获取:
     //发送查询请求给微信支付平台
            String url = "https://api.mch.weixin.qq.com/pay/orderquery";
    
            //设置订单状态的开始时间点
            long l = System.currentTimeMillis();
    
            //不停的去微信支付平台询问是否支付成功
            while(true) {
                String post = HttpKit.post(url, xml);
    
                //对微信支付平台返回的查询结果进行处理
                Map<String, String> map1 = WXPayUtil.xmlToMap(post);
    
                //已经支付成功,我们的企业得到钱,微信支付平台,修改数据,得到支付成功的数据,就可以返回
                //trade_state就是对应的状态信息,用符号表示
                //但并没有trade_state_desc对应的"订单未支付"这个信息
                //只是用符号表示
                //所以他们是互不影响的
                /*
                对应的trade_state的可能值
                SUCCESS--支付成功
                REFUND--转入退款
                NOTPAY--未支付
                CLOSED--已关闭
                REVOKED--已撤销(刷卡支付)
                USERPAYING--用户支付中
                PAYERROR--支付失败(其他原因,如银行返回失败)
                ACCEPT--已接收,等待扣款
                 */
                if(map1.get("trade_state").equalsIgnoreCase("SUCCESS")) {
                    return map1;
                }
    
                //超过30秒未支付就停止询问,因为不可能一直循环浪费资源
                if(System.currentTimeMillis() - l>30*1000){
                    return map1;
                }
                 Thread.sleep(3000); //每隔三秒访问一次,总不能一直访问吧(太浪费空间)
                
            }
    
    • 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
    上面如果在30秒内支付成功,那对应前端界面会显示出来你已经支付
    但是如果30秒内没有支付或者没有支付成功,前端界面会显示未支付
    一般的我们会重新生成二维码,或者对应过期时间设置为二维码的过期时间
    防止在前端返回未支付的情况下,可以支付,操作对应数据的不合理,甚至会购买不上
    对应的前端:
    
    <el-dialog :visible.sync="dialogFormVisible" style="width:800px;margin:0px auto;" >
     <h1 style="font-size:30px;color:#00B38A">微信扫一扫支付h1>
     <div id="qrcode" style="width:210px;margin:20px auto;">div>
     <h2 id="statusText">h2>
     el-dialog>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
     //检查支付状态
          this.axios.get("http://localhost:80/checkOrderStatus",{
            params:{
             orderId:res.data.orderId
            }
          }).then(res => {
          //检查支付状态
          console.log(res)
          if(res.data.trade_state=="SUCCESS"){
              document.getElementById("statusText").innerHTML=
                  " 支付成功"
          }
              }).catch(err =>{
                this.$message.error("查询订单状态失败")
              })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    至此可以扫描二维码,进行支付,发现,出现对应的样式,显示支付成功
    当你再次扫描时一般会提示已经操作(因为一个已经支付的二维码一般是会销毁的,可能有些情况下会继续支付)
    至此,微信支付完成
    接下来操作关闭二维码:
    
    <el-dialog :visible.sync="dialogFormVisible" style="width:800px;margin:0px auto;" >
     <h1 style="font-size:30px;color:#00B38A">微信扫一扫支付h1>
     <div id="qrcode" style="width:210px;margin:20px auto;">div>
     <h2 id="statusText">h2>
     <p id="closeTest">p>
     el-dialog>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
     data() {
        return {
          comment:null, //待发表的留言内容
          activeName: "intro",
          course:null,
          totalLessons:0, //本门课程的总节数
          commentList:null, //所有留言
          isLogin:false, //false,未登录
          isBuy:false,//未购买
          user:null, //当前用户,{}对象可以","结尾
          myCourseList:[], //当前用户购买过的所有课程
          dialogFormVisible:false,
          time:null, //计时对象,这是新加的
        };
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
       //检查支付状态
          console.log(res)
          if(res.data.trade_state=="SUCCESS"){
              document.getElementById("statusText").innerHTML=
                  " 支付成功"
                 //真实的修改数据库,保存订单
           this.saveOrder();
          let s = 3; //倒计时的秒数
          this.closeQRForm(s) //关闭二维码窗口
           
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    修改web层项目的对应的OrderController类的saveOrder方法:
     @GetMapping("saveOrder/{userid}/{courseid}/{acid}/{stype}")
        public String saveOrder(String orderNo,@PathVariable("userid") String userid,
                                @PathVariable("courseid") String courseid,
                                @PathVariable("acid") String acid,@PathVariable("stype") String stype) {
    
            //String orderNo = UUID.randomUUID().toString();
            orderService.saveOrder(orderNo,userid,courseid,acid,stype);
            return orderNo;
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    给data加上对应的订单属性(这里就不全部写出来了):
         orderNo:"",//订单编号
             //在java里点击重新启动,实际上是根据当前数据启动,这时若端口改变,则使用改变的端口
             //而不是以前的端口
    
    • 1
    • 2
    • 3
       //QRCode(dom元素的id,二维码的属性参数)
      let qrcode = new QRCode("qrcode",{
      width:200,
      height:200,
      text:res.data.code_url  //将返回的数据放入到二维码中
      //二维码中包含的信息
    }); //qrcode就是当前有id="qrcode"的标签
    
    this.orderNo = res.data.orderId; //这里进行赋值
          //检查支付状态
          this.axios.get("http://localhost:80/checkOrderStatus",{
            params:{
             orderId:this.orderNo //这里修改一下
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    //保存订单
        saveOrder(){
    
          this.axios.get("http://localhost:8002/order/saveOrder/"+this.user.content.id+
                         "/"+this.course.id+"/"+this.course.id+"/1",{
            params:{
             orderNo:this.orderNo
            }
          }).then(res => {
         console.log(res)
              }).catch(err =>{
                this.$message.error("保存订单失败")
              })
    
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
        //倒计时关闭二维码窗口
        closeQRForm(s){
         let that = this; //用来关闭下面的循环,因为是不同的this了
          that.time = setInterval(function(){
        
    document.getElementById("closeTest").innerHTML = "("+ s-- +")秒后关闭本窗口"
    if(s==0){
      clearInterval(that.time) //停止计时器
      that.dialogFormVisible = false //二维码窗口隐藏
      that.isBuy = true; //修改购买状态(已购买),对应的视频就会变成播放
    
    }
          },1000)
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    但是我们发现,对应的数据不对,因为订单中的status并不是20,使得还是试看,而不是播放
    若还是播放,那么应该是设置了购买,或者已经购买
    已经购买的话,一般保存订单会失败,即后端返回异常信息,使得前端报错
    所以我们需要进行修改:
       //保存订单
        saveOrder(){
    
          this.axios.get("http://localhost:8002/order/saveOrder/"+this.user.content.id+
                         "/"+this.course.id+"/"+this.course.id+"/1",{
            params:{
             orderNo:this.orderNo
            }
          }).then(res => {
    
              //这里进行修改成20,表示已支付,这样就会得到已经购买的课程了,对应的视频也基本都是播放了
              //实际上我们只需要将保存的代码中,对应的status值从0修改成20即可
              //而不用写下面的代码,但是为了以后,我们对应的保存订单也最好设置为0,默认创建
              //因为总不能只要有个订单(无论是否支付),就相当于是已支付吧,这不符合实际情况
              //我们在生活中可以知道,订单有时是存在的,需要我们去支付,否则一般会显示待支付
              //所以一般我们设置为0,表示以创建,而不是默认支付
    this.axios.get("http://localhost:8002/order/updateOrder/"+this.orderNo+"/20").then(res => {
         console.log(res)
              }).catch(err =>{
                this.$message.error("设置订单状态失败")
              })
    
    
              }).catch(err =>{
                this.$message.error("保存订单失败")
              })
    
        },
    
    • 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
    实际上对应的保存订单在查询支付状态里面,假如查询不了怎么办,那么订单也就不会生成,所以说
    对应的订单应该在状态之前,而不会受任何影响,且是创建的订单
    this.orderNo = res.data.orderId;
    
         //真实的修改数据库,保存订单
           this.saveOrder(); //一般购买后,就基本到不了这里,也就不会报错
          //检查支付状态
          this.axios.get("http://localhost:80/checkOrderStatus",{
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
     if(res.data.trade_state=="SUCCESS"){
              document.getElementById("statusText").innerHTML=
                  " 支付成功"
    
           
          }
    //无论是否支付成功,对应的窗口都需要进行关闭,自然也要等待后端访问得到返回数据
           let s = 3; //倒计时的秒数
          this.closeQRForm(s) //关闭二维码窗口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    那么对应的保存订单中的设置20,就要进行位置变化了:
     //检查支付状态
          console.log(res)
          if(res.data.trade_state=="SUCCESS"){
              document.getElementById("statusText").innerHTML=
                  " 支付成功"
            this.axios.get("http://localhost:8002/order/updateOrder/"+this.orderNo+"/20").then(res => {
         console.log(res)
              }).catch(err =>{
                this.$message.error("设置订单状态失败")
              })
           
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    我们可以将对应的更新状态封装成一个函数:
    updateOrder(ss){
     this.axios.get("http://localhost:8002/order/updateOrder/"+this.orderNo+"/"+ss).then(res => {
         console.log(res)
              }).catch(err =>{
                this.$message.error("设置订单状态失败")
              })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    那么原来的可以这样写:
     //检查支付状态
          console.log(res)
          if(res.data.trade_state=="SUCCESS"){
              document.getElementById("statusText").innerHTML=
                  " 支付成功"
           //支付成功
           this.updateOrder(20)
                  //进行判断等等
          //  }else if(res.data.trade_state=="NOTPAY"){
          //    document.getElementById("statusText").innerHTML="
                  <i style='color:#00B38A' class='el-icon-success'></i> 未支付"
          // //未支付
          // this.updateOrder(10)
          //  }
           
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    至此可以进行测试,每次的购买后,去已购那里看看发现的确在那里了,且对应都是播放
    但是这里有一个问题,如果是未支付的情况下,可能是有相同的保存,那么会报错
    通过修改后端代码可以解决问题(在文章最后面)
    若不修改后端代码,如何解决:
    在查询支付状态里面可以解决此问题(位置没改变之前,就是这样,只是回来了而已)
    因为和状态的设置是一起的(且是支付成功后的操作)
    所以为了解决此问题,外面还是将对应的保存订单放回到对应的判断里面:
     if(res.data.trade_state=="SUCCESS"){
              document.getElementById("statusText").innerHTML=
                  " 支付成功"
            //真实的修改数据库,保存订单
           this.saveOrder(); //放回来,里面执行状态修改
       
          //  }else if(res.data.trade_state=="NOTPAY"){
          //    document.getElementById("statusText").innerHTML="
               <i style='color:#00B38A' class='el-icon-success'></i> 未支付"
          // //未支付
          // this.updateOrder(10)
          //  }
           
          }
    
    //实际上外面应该在后端进行判断是否有订单,而不是放在该判断里面
    //因为以后该判断会非常的多,那么该代码也就是多了起来
    //这里就只是一个测试,所以放在该判断里面了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
      //保存订单
        saveOrder(){
          this.axios.get("http://localhost:8002/order/saveOrder/"
                         +this.user.content.id+"/"+this.course.id+"/"+this.course.id+"/1",{
            params:{
             orderNo:this.orderNo
            }
          }).then(res => {
    
         this.updateOrder(20) //这里执行
    
    
              }).catch(err =>{
                this.$message.error("保存订单失败")
              })
    
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    注意:这只是测试,实际情况下,外面需要修改对应的后端代码使得进行判断,而不是修改前端
    因为该保存的订单也基本固定了20的状态,或者也可以将对应的方法加上参数,如:
     this.saveOrder(20);
     
      //保存订单
        saveOrder(ss){
          this.axios.get("http://localhost:8002/order/saveOrder/"
                         +this.user.content.id+"/"+this.course.id+"/"+this.course.id+"/1",{
            params:{
             orderNo:this.orderNo
            }
          }).then(res => {
    
         this.updateOrder(ss)
    
    
              }).catch(err =>{
                this.$message.error("保存订单失败")
              })
    
        },
            //这样也可,虽然代码非常多(操作后端即可)
            //后端思路:根据传递的参数查询表里面是否有数据,有则不执行保存订单
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    至此微信的操作完成
    后端代码(修改后,就可以将保存方法放在查询状态外面,状态修改方法放在查询状态里面,他们都是单独的):
    添加部分对应的OrderDap.xml:
    <select id="getOrder" resultMap="UserCourseOrderMap">
            select * from user_course_order where user_id = #{user_id}
                                              and course_id = #{course_id}
                                              and source_type = #{source_type}
        select>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    对应的接口:
        /**
         *
         * @param user_id 用户id
         * @param course_id 课程id
         * @param source_type '订单来源类型: 1 用户下单购买 2 后台添加专栏',一般是1
         * @return
         */
        UserCourseOrder getOrder(@Param("user_id") String user_id,
                                 @Param("course_id") String course_id,
                                 @Param("source_type") String source_type);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    对应的service层的OrderServiceImpl实现类的saveOrder方法:
     @Override
        public void saveOrder(String orderNo, String user_id, 
                              String course_id, String activity_course_id, String source_type) {
    
            //只要没有订单才能保存,否则会报错的
                //因为通常对应的字段值会相同,由于表的索引设置对应的唯一,所以会报错
            
            UserCourseOrder order = orderDao.getOrder(user_id, course_id,source_type);
            System.out.println(order);
            if(order == null) {
                orderDao.saveOrder(orderNo, user_id, course_id, activity_course_id, source_type);
            }
    
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    通过后端代码的编写,使得对应操作完成
    这时候的前端可以是:
      this.saveOrder();
          //检查支付状态
          this.axios.get("http://localhost:80/checkOrderStatus",{
            params:{
             orderId:this.orderNo
            }
          }).then(res => {
          //检查支付状态
          console.log(res)
          if(res.data.trade_state=="SUCCESS"){
              document.getElementById("statusText").innerHTML=
                  " 支付成功"
            //真实的修改数据库,保存订单
           this.updateOrder(20)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    至此,微信的登录和微信的支付都操作完毕
    但是要注意:除了在课程里购买,其他的地方的购买代码基本是类似的,即他们都是重复的代码
    至此,并没有给出操作,可以自己进行尝试,还有些细节问题,可以自己进行查看,比如buy()方法,好像固定是7的参数
    注意:当你的localhost被设置其他域名,虽然前端会执行,但还是默认本机,并不会执行对应ip
    但是一些情况下,并不会显示,如富文本编辑器和对应的检查(有元素,网络,等等的)
    说到富文本编辑器,接下来说明一下,如何在vue里操作吧:
    很简单的步骤:
    #首先安装对应的组件
    npm install mavon-editor --save
    
    • 1
    • 2
    <template>
      <div>
          
            <mavon-editor
                @save="saveDoc"
                @change="updateDoc"
                ref="editor"
                v-model="doc">
             mavon-editor>
             <button @click="s">sssbutton>
      div>
    template>
    
    <script>
    import {mavonEditor} from "mavon-editor";
    import "mavon-editor/dist/css/index.css";
    export default {
        name:"jj",
    components: {mavonEditor},
    data() {
            return {
                doc: "1" //绑定的值
            };
        },
        methods: {
            s(){
                alert(1)
            },
            updateDoc(markdown, html) {
                // 此时会自动将 markdown 和 html 传递到这个方法中
                console.log("markdown内容: " + markdown);
                console.log("html内容:" + markdown);
            },
            saveDoc(markdown, html) {
                // 此时会自动将 markdown 和 html 传递到这个方法中
                console.log("markdown内容:" + markdown);
                console.log("html内容:" + html);
            }
        }
    
    }
    script>
    
    <style>
    
    style>
    
    • 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
    至此操作完成,简单吧
  • 相关阅读:
    LeetCode【279】完全平方数
    JavaScript的三大组成部分是什么?JavaScript的核心组成部分解析:语法、BOM和DOM
    Flutter视图原理之StatefulWidget,InheritedWidget
    adb shell dumpsys meminfo 详解
    黑号照妖镜API接口 淘宝旺旺信誉
    差分,前缀和,离散化——模板
    windows xp 邮件服务器漏洞溢出
    C#__基本的读写文件方式
    测试人需要的数据库知识:MySQL常用语法
    基于IDEA进行Maven工程构建
  • 原文地址:https://blog.csdn.net/qq_59609098/article/details/126061248