【微信小程序开发】小程序微信用户授权登录(用户信息手机号)

news2024/10/6 6:01:38

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于小程序的相关操作吧

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

授权流程讲解

一.用户信息授权登录

1.wx.login

2.wx.getUserProfile

3.代码

WXML

JS

二.用户信息授权登录之后台交互

后端代码

WXML

JS

utils/user.js【封装的代码块】

三.手机号授权登录之后台交互

后端代码

WXML

JS


授权流程讲解

我们在使用微信中的小程序时,都会要我们进行授权,例如下面这样

那么这样的用户授权时怎么实现的呢,这一篇主要就是讲解用户授权登录的流程!!!

图片说明:

1.客户端调用 wx.login() 获取 临时登录凭证code,通过 wx.request() 发起网络请求,将 code 传给服务端
2、服务端使用 code + appid + appsecret 向微信换取 (调用 auth.code2Session 接口)用户唯一标识openid 会话密钥session_key
3、服务端自定义 登录状态token(与openid、session_key关联)返回客户端
4、客户端将 登录状态token 存入 缓存storage(推荐使用 wx.setStorageSync(‘key’, ‘value’) 同步存储)
5、客户端wx.request() 发起请求时,携带登录状态token (推荐使用 wx.getStorageSync(‘key’) 同步获取)
6、服务端通过 登录状态token 查询到对应 openid 和 session_key
7、验证成功后返回业务数据给客户端

一.用户信息授权登录

其中有两种方法,第一种方法是点击登录之后便直接获取了用户的个人信息,而第二种会询问用户是否同意授权,这样的话,会更具安全性

1.wx.login

这个方法主要用于获取用户的登录凭证(code)。在用户进入小程序时,前端会调用wx.login来获取这个code,然后将这个code发送给后台服务器。后台服务器再向微信发送请求,通过这个code来获取用户的唯一标识(openid)以及本次登录的会话密钥(session_key)。之后,后台服务器将这两个信息传回前台,用于自定义登录状态和用户唯一标识

2.wx.getUserProfile

这个方法主要用于获取用户的更多详细信息,如昵称、头像等。在使用这个方法之前,需要先调用wx.authorize接口来发起授权请求,请求用户授权提供这些信息。如果用户同意授权,就可以通过调用wx.getUserProfile方法来获取这些详细信息

3.代码

WXML

<!--pages/index/index.wxml-->
<view>
  <button wx:if="{{canIUseGetUserProfile}}" type="primary" class="wx-login-btn" bindtap="getUserProfile">微信直接登录1</button>
  <button wx:else open-type="getUserInfo" type="primary" class="wx-login-btn" bindgetuserinfo="wxLogin">微信直接登录2</button>
  <image mode="scaleToFill" src="{{userInfo.avatarUrl}}" />
  <text>昵称:{{userInfo.nickName}}</text>
</view>

JS

// pages/index/index.js
Page({
  data: {
    userInfo: {},
    canIUseGetUserProfile: false,
  },
  onLoad() {
    // if (wx.getUserProfile) {
    //   this.setData({
    //     canIUseGetUserProfile: true
    //   })
    // }
  },
  getUserProfile(e) {
    console.log('getUserProfile')
    // 推荐使用 wx.getUserProfile 获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
    // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
      desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        console.log(res);
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    })
  },
  wxLogin: function(e) {
    debugger
    console.log('wxLogin')
    console.log(e.detail.userInfo);
    this.setData({
      userInfo: e.detail.userInfo
    })
    if (e.detail.userInfo == undefined) {
      app.globalData.hasLogin = false;
      util.showErrorToast('微信登录失败');
      return;
    }
    
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {

  }
})

用户授权登录后,后台便会保存用户的信息

二.用户信息授权登录之后台交互

后端代码

package com.zking.ssm.wxcontroller;

/**
 * @Autho donkee
 * @Since 2022/6/27
 */

import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.alibaba.fastjson.JSONObject;
import com.zking.ssm.annotation.LoginUser;
import com.zking.ssm.model.UserInfo;
import com.zking.ssm.model.WxLoginInfo;
import com.zking.ssm.model.WxUser;
import com.zking.ssm.service.UserToken;
import com.zking.ssm.service.UserTokenManager;
import com.zking.ssm.service.WxUserService;
import com.zking.ssm.util.JacksonUtil;
import com.zking.ssm.util.ResponseUtil;
import com.zking.ssm.util.UserTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import javax.servlet.http.HttpServletRequest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 鉴权服务
 */
@Slf4j
@RestController
@RequestMapping("/wx/auth")
public class WxAuthController {
    @Autowired
    private WxMaService wxService;
    @Autowired
    private WxUserService userService;
    /**
     * 微信登录
     *
     * @param wxLoginInfo
     *            请求内容,{ code: xxx, userInfo: xxx }
     * @param request
     *            请求对象
     * @return 登录结果
     */
    @PostMapping("login_by_weixin")
    public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) {

        //客户端需携带code与userInfo信息
        String code = wxLoginInfo.getCode();
        UserInfo userInfo = wxLoginInfo.getUserInfo();
        if (code == null || userInfo == null) {
            return ResponseUtil.badArgument();
        }
        //调用微信sdk获取openId及sessionKey
        String sessionKey = null;
        String openId = null;
        try {
            long beginTime = System.currentTimeMillis();
            //
            WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code);
//            Thread.sleep(6000);
            long endTime = System.currentTimeMillis();
            log.info("响应时间:{}",(endTime-beginTime));
            sessionKey = result.getSessionKey();//session id
            openId = result.getOpenid();//用户唯一标识 OpenID
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (sessionKey == null || openId == null) {
            log.error("微信登录,调用官方接口失败:{}", code);
            return ResponseUtil.fail();
        }else{
            log.info("openId={},sessionKey={}",openId,sessionKey);
        }
        //根据openId查询wx_user表
        //如果不存在,初始化wx_user,并保存到数据库中
        //如果存在,更新最后登录时间
        WxUser user = userService.queryByOid(openId);

        if (user == null) {
            user = new WxUser();
            user.setUsername(openId);
            user.setPassword(openId);
            user.setWeixinOpenid(openId);
            user.setAvatar(userInfo.getAvatarUrl());
            user.setNickname(userInfo.getNickName());
            user.setGender(userInfo.getGender());
            user.setUserLevel((byte) 0);
            user.setStatus((byte) 0);
            user.setLastLoginTime(new Date());
            user.setLastLoginIp(IpUtil.client(request));
            user.setShareUserId(1);

            userService.add(user);

        } else {
            user.setLastLoginTime(new Date());
            user.setLastLoginIp(IpUtil.client(request));
            if (userService.updateById(user) == 0) {
                log.error("修改失败:{}", user);
                return ResponseUtil.updatedDataFailed();
            }
        }
        // token
        UserToken userToken = null;
        try {
            userToken = UserTokenManager.generateToken(user.getId());
        } catch (Exception e) {
            log.error("微信登录失败,生成token失败:{}", user.getId());
            e.printStackTrace();
            return ResponseUtil.fail();
        }
        userToken.setSessionKey(sessionKey);
        log.info("SessionKey={}",UserTokenManager.getSessionKey(user.getId()));
        Map<Object, Object> result = new HashMap<Object, Object>();
        result.put("token", userToken.getToken());
        result.put("tokenExpire", userToken.getExpireTime().toString());
        userInfo.setUserId(user.getId());
        if (!StringUtils.isEmpty(user.getMobile())) {// 手机号存在则设置
            userInfo.setPhone(user.getMobile());
        }
        try {
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            String registerDate = df.format(user.getAddTime() != null ? user.getAddTime() : new Date());
            userInfo.setRegisterDate(registerDate);
            userInfo.setStatus(user.getStatus());
            userInfo.setUserLevel(user.getUserLevel());// 用户层级
            userInfo.setUserLevelDesc(UserTypeEnum.getInstance(user.getUserLevel()).getDesc());// 用户层级描述
        } catch (Exception e) {
            log.error("微信登录:设置用户指定信息出错:"+e.getMessage());
            e.printStackTrace();
        }
        result.put("userInfo", userInfo);


        log.info("【请求结束】微信登录,响应结果:{}", JSONObject.toJSONString(result));

        return ResponseUtil.ok(result);
    }


   }
}

WXML

<!--pages/auth/login/login.wxml-->
<view class="container">
  <view class="login-box">
    <button wx:if="{{canIUseGetUserProfile}}" type="primary" class="wx-login-btn" bindtap="getUserProfile">微信直接登录</button>
    <button wx:else open-type="getUserInfo" type="primary" class="wx-login-btn" bindgetuserinfo="wxLogin">微信直接登录</button>
    <button type="primary" class="account-login-btn" bindtap="accountLogin">账号登录</button>
  </view>
</view>

JS

// pages/auth/login/login.js
var util = require('../../../utils/util.js');
var user = require('../../../utils/user.js');
const app = getApp();
Page({

    /**
     * 页面的初始数据
     */
    data: {
        canIUseGetUserProfile: false, // 用于向前兼容
        lock:false
    },
    onLoad: function(options) {
        // 页面初始化 options为页面跳转所带来的参数
        // 页面渲染完成
        if (wx.getUserProfile) {
          this.setData({
            canIUseGetUserProfile: true
          })
        }
        //console.log('login.onLoad.canIUseGetUserProfile='+this.data.canIUseGetUserProfile)
    },

    /**
     * 生命周期函数--监听页面初次渲染完成
     */
    onReady() {

    },

    /**
     * 生命周期函数--监听页面显示
     */
    onShow() {

    },
    getUserProfile(e) {
        console.log('getUserProfile');
        // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
        // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
        wx.getUserProfile({
            desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
            success: (res) => {
                //console.log(res);
                user.checkLogin().catch(() => {
                    user.loginByWeixin(res.userInfo).then(res => {
                      app.globalData.hasLogin = true;
                      wx.navigateBack({
                        delta: 1
                      })
                    }).catch((err) => {
                      app.globalData.hasLogin = false;
                      if(err.errMsg=="request:fail timeout"){
                        util.showErrorToast('微信登录超时');
                      }else{
                        util.showErrorToast('微信登录失败');
                      }
                      this.setData({
                        lock:false
                      })
                    });
                  });
            },
            fail: (res) => {
                app.globalData.hasLogin = false;
                console.log(res);
                util.showErrorToast('微信登录失败');
            }
        });
    },
    wxLogin: function(e) {
        console.log('wxLogin');
        if (e.detail.userInfo == undefined) {
          app.globalData.hasLogin = false;
          util.showErrorToast('微信登录失败');
          return;
        }
        user.checkLogin().catch(() => {
            user.loginByWeixin(e.detail.userInfo).then(res => {
              app.globalData.hasLogin = true;
              wx.navigateBack({
                delta: 1
              })
            }).catch((err) => {
              app.globalData.hasLogin = false;
              if(err.errMsg=="request:fail timeout"){
                util.showErrorToast('微信登录超时');
              }else{
                util.showErrorToast('微信登录失败');
              }
            });
      
          });
    },
    accountLogin() {
        console.log('开发中....')
    }

})

utils/user.js【封装的代码块】

/**
 * 用户相关服务
 */
const util = require('../utils/util.js');
const api = require('../config/api.js');

/**
 * Promise封装wx.checkSession
 */
function checkSession() {
  return new Promise(function(resolve, reject) {
    wx.checkSession({
      success: function() {
        resolve(true);
      },
      fail: function() {
        reject(false);
      }
    })
  });
}
/**
 * Promise封装wx.login
 */
function login() {
  return new Promise(function(resolve, reject) {
    wx.login({
      success: function(res) {
        if (res.code) {
          resolve(res);
        } else {
          reject(res);
        }
      },
      fail: function(err) {
        reject(err);
      }
    });
  });
}
/**
 * 调用微信登录
 */
function loginByWeixin(userInfo) {
  return new Promise(function(resolve, reject) {
    return login().then((res) => {
      //登录远程服务器
      util.request(api.AuthLoginByWeixin, {
        code: res.code,
        userInfo: userInfo
      }, 'POST').then(res => {
        if (res.errno === 0) {
          //存储用户信息
          wx.setStorageSync('userInfo', res.data.userInfo);
          wx.setStorageSync('token', res.data.token);
          resolve(res);
        } else {
          reject(res);
        }
      }).catch((err) => {
        reject(err);
      });
    }).catch((err) => {
      reject(err);
    })
  });
}

/**
 * 判断用户是否登录
 */
function checkLogin() {
  return new Promise(function(resolve, reject) {
    if (wx.getStorageSync('userInfo') && wx.getStorageSync('token')) {
      checkSession().then(() => {
        resolve(true);
      }).catch(() => {
        reject(false);
      });
    } else {
      reject(false);
    }
  });
}

module.exports = {
  loginByWeixin,
  checkLogin,
};

三.手机号授权登录之后台交互

手机号授权登录的流程与用户信息授权登录流程是一样的,只不过向微信调用的接口有所不同

后端代码

package com.zking.ssm.wxcontroller;

/**
 * @Autho donkee
 * @Since 2022/6/27
 */

import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.alibaba.fastjson.JSONObject;
import com.zking.ssm.annotation.LoginUser;
import com.zking.ssm.model.UserInfo;
import com.zking.ssm.model.WxLoginInfo;
import com.zking.ssm.model.WxUser;
import com.zking.ssm.service.UserToken;
import com.zking.ssm.service.UserTokenManager;
import com.zking.ssm.service.WxUserService;
import com.zking.ssm.util.JacksonUtil;
import com.zking.ssm.util.ResponseUtil;
import com.zking.ssm.util.UserTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import javax.servlet.http.HttpServletRequest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 鉴权服务
 */
@Slf4j
@RestController
@RequestMapping("/wx/auth")
public class WxAuthController {
  
    /**
     * 绑定手机号码
     *
     * @param userId
     * @param body
     * @return
     */
    @PostMapping("bindPhone")
    public Object bindPhone(@LoginUser Integer userId, @RequestBody String body) {
        log.info("【请求开始】绑定手机号码,请求参数,body:{}", body);

        String sessionKey = UserTokenManager.getSessionKey(userId);
        String encryptedData = JacksonUtil.parseString(body, "encryptedData");
        String iv = JacksonUtil.parseString(body, "iv");
        WxMaPhoneNumberInfo phoneNumberInfo = null;
        try {
            phoneNumberInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
        } catch (Exception e) {
            log.error("绑定手机号码失败,获取微信绑定的手机号码出错:{}", body);
            e.printStackTrace();
            return ResponseUtil.fail();
        }
        String phone = phoneNumberInfo.getPhoneNumber();
        WxUser user = userService.selectByPrimaryKey(userId);
        user.setMobile(phone);
        if (userService.updateById(user) == 0) {
            log.error("绑定手机号码,更新用户信息出错,id:{}", user.getId());
            return ResponseUtil.updatedDataFailed();
        }
        Map<Object, Object> data = new HashMap<Object, Object>();
        data.put("phone", phone);

        log.info("【请求结束】绑定手机号码,响应结果:{}", JSONObject.toJSONString(data));
        return ResponseUtil.ok(data);
    }
  
    }
}

WXML

<!--pages/ucenter/user/user.wxml-->
<form bindsubmit="formSubmit">
    <view class='personal-data'>
        <view class='list'>
            <view class='item acea-row row-between-wrapper'>
                <view>头像</view>
                <view class='pictrue'>
                    <image src='{{userInfo.avatarUrl}}'></image>
                </view>
            </view>
            <view class='item acea-row row-between-wrapper'>
                <view>名字</view>
                <view class='input'><input type='text' disabled='true' name='nickname' value='{{userInfo.nickName}}'></input></view>
            </view>
            <view class='item acea-row row-between-wrapper'>
                <view>手机号码</view>
                <button name='phone' class='phoneW' value='{{userInfo.phone}}' wx:if="{{!userInfo.phone}}" bindgetphonenumber="getPhoneNumber" hover-class='none' open-type='getPhoneNumber'>
                    点击获取
                </button>
                <view class='input acea-row row-between-wrapper' wx:else>
                    <input type='text' disabled='true' name='phone' value='{{userInfo.phone}}' class='id'></input>
                    <text class='iconfont icon-suozi'></text>
                </view>
            </view>

            <view class='item acea-row row-between-wrapper'>
                <view>ID号</view>
                <view class='input acea-row row-between-wrapper'>
                    <input type='text' value='1000{{userInfo.userId}}' disabled='true' class='id'></input>
                    <text class='iconfont icon-suozi'></text>
                </view>
            </view>
        </view>
        <button class='modifyBnt' bindtap="exitLogin">退 出</button>
    </view>
</form>

JS

var util = require('../../../utils/util.js');
var api = require('../../../config/api.js');
var user = require('../../../utils/user.js');
var app = getApp();
Page({

  /**
   * 页面的初始数据
   */
  data: {
    userInfo: {},
    hasLogin: false,
    userSharedUrl: ''
  },

  /**
      * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {

  },
  onShow: function () {
    let that = this;
    //获取用户的登录信息
    let userInfo = wx.getStorageSync('userInfo');
    this.setData({
      userInfo: userInfo,
      hasLogin: true
    });

  },
  getPhoneNumber: function (e) {
      console.log(e);
    let that = this;
    if (e.detail.errMsg !== "getPhoneNumber:ok") {
      // 拒绝授权
      return;
    }

    if (!this.data.hasLogin) {
      wx.showToast({
        title: '绑定失败:请先登录',
        icon: 'none',
        duration: 2000
      });
      return;
    }

    util.request(api.AuthBindPhone, {
      iv: e.detail.iv,
      encryptedData: e.detail.encryptedData
    }, 'POST').then(function (res) {
      if (res.errno === 0) {
        let userInfo = wx.getStorageSync('userInfo');
        userInfo.phone = res.data.phone;//设置手机号码
        wx.setStorageSync('userInfo', userInfo);
        that.setData({
          userInfo: userInfo,
          hasLogin: true
        });
        wx.showToast({
          title: '绑定手机号码成功',
          icon: 'success',
          duration: 2000
        });
      }
    });
  },
  exitLogin: function () {
    wx.showModal({
      title: '',
      confirmColor: '#b4282d',
      content: '退出登录?',
      success: function (res) {
        if (!res.confirm) {
          return;
        }

        util.request(api.AuthLogout, {}, 'POST');
        app.globalData.hasLogin = false;
        wx.removeStorageSync('token');
        wx.removeStorageSync('userInfo');
        wx.reLaunch({
          url: '/pages/index/index'
        });
      }
    })
  }
})

好啦,今天的分享就到这了,希望能够帮到你呢!😊😊  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118596.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于springboot小区物业管理系统

功能如下图所示 摘要 基于Spring Boot的小区物业管理系统是一项重要的解决方案&#xff0c;旨在提升小区物业管理的效率和质量。这个系统整合了现代技术和管理实践&#xff0c;为小区内的住户和物业管理人员提供了便捷的工具&#xff0c;以更好地管理和维护住宅区。该系统的关键…

10_集成学习方法:随机森林、Boosting

文章目录 1 集成学习&#xff08;Ensemble Learning)1.1 集成学习1.2 Why need Ensemble Learning?1.3 Bagging方法 2 随机森林(Random Forest)2.1 随机森林的优点2.2 随机森林算法案例2.3 随机森林的思考&#xff08;--->提升学习&#xff09; 3 随机森林&#xff08;RF&a…

【RNA biology】RNA的多功能性与早期生命进化

文章目录 RNARNA plays core functions in Central Dogma of BiologyrRNAsnRNA RNA worldReplication催化作用感知环境变化并作出响应 来自Manolis Kellis教授&#xff08;MIT计算生物学主任&#xff09;的课 油管链接&#xff1a;6.047/6.878 Lecture 7 - RNA folding, RNA wo…

PHP代码审计工具

PHP代码审计工具 1 环境准备 Seay源代码审计系统.exe 和准备靶场的源码php 2 Seay下载地址 https://github.com/f1tz/cnseay安装Seay源代码审计系统.exe报错时&#xff0c;安装.net framework 3.5 # windows插件.net framework 3.5 下砸地址 https://www.microsoft.com/en…

CSS基础入门01

目录 1.CSS是什么 2.基本语法规范 3.引入方式 3.1内部样式表 3.2行内样式表 3.3外部样式 4.代码风格 4.1样式格式 4.2样式大小写 4.3空格规范 5.选择器 5.1选择器的功能 5.2选择器的种类 6.基础选择器 6.1标签选择器 6.2类选择器 6.3id 选择器 6.4通配符选择…

【趣味随笔】盘点那些国内外知名的扫地机器人品牌

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

电容屏物体识别手工制作

电容屏识别物体效果2 电容屏识别物体效果1 电容屏识别物体效果3 电容屏识别物体效果4 电容识别物理效果5 我们感兴趣的是找到让我们的平面屏幕与物理三维物体和表面交互的方法。 触摸屏无处不在&#xff0c;成千上万的应用程序中有多种设备和屏幕格式&#xff0c;但我们只找到…

十三水中各种牌型判断LUA版

近期回归程序行业&#xff0c;由于业务需求需要做十三水游戏&#xff0c;什么是十三水就不在多讲&#xff0c;下面是判断十三水牌型的方法&#xff08;带大小王&#xff09; GetSSSPaiType {}; local this GetSSSPaiType; local huaseTable {}; local numTable {}; functi…

计算机组成原理 new06 第二章 BCD码

文章目录 BCD码8421码余3码2421码三种码的总结 BCD码 概念&#xff1a;在计算机中&#xff0c;除了能够用二进制表示十进制之外&#xff0c;在二进制的基础上还衍生出了很多种的表示方式这些种表示方式的统称就是BCD码。 8421码 概念&#xff1a;8421码用4位二进制来表示一个十…

Python学习第1天-安装Python

文章目录 前言一、下载Python二、执行安装程序三、命令行验证总结 前言 以下榜单来自于TIOBE编程语言流行指数 不多说了&#xff0c;Python天下第一 一、下载Python 从官网下载Python安装程序 二、执行安装程序 找到python-3.12.0-amd64.exe执行&#xff0c;选择Install …

如何使用postman调用若依系统接口(报错401,认证失败,无法访问系统资源)

有时候我们想使用postman调用若依接口&#xff0c;会报下面的401错误&#xff0c;认证失败&#xff0c;无法访问系统资源。 原因是请求中没有token&#xff0c;没法通过若依的权限认证&#xff0c;下面来说一下如何解决。 {"msg": "请求访问&#xff1a;/syste…

提高工作效率的有效途径:五分钟快速学会搭建悟空CRM内网穿透

文章目录 前言1. 无需公网IP&#xff0c;使用cpolar实现悟空CRM远程访问2. 通过公网来访问公司内网悟空CRM3. 设置固定连接公网地址 前言 悟空CRM是一款开源的客户关系管理系统&#xff0c;以"客户关系一对一理论"为基础&#xff0c;通过对企业业务流程的重组来整合…

【数据分享】2023年我国高新技术企业数据(免费获取\excel格式\shp格式)

企业是经济活动的参与主体。一个城市的企业数量决定了这个城市的经济发展水平&#xff01;比如一个城市的金融企业较多&#xff0c;那这个城市的金融产业肯定比较发达&#xff1b;一个城市的制造业企业较多&#xff0c;那这个城市的制造业肯定比较发达。目前&#xff0c;在城市…

大数据Flink(一百):SQL自定义函数(UDF)和标量函数(Scalar Function)

文章目录 SQL自定义函数(UDF)和标量函数(Scalar Function)

计算机组成原理 new14 双端口RAM和多模块存储器

文章目录 双端口RAM 多模块存储器多体并行存储器高位交叉编址低位交叉编址单体多字存储器存储周期和访存 双端口RAM 在双端口RAM中&#xff0c;两个端口使用了不同的译码器&#xff0c;数据线&#xff0c;控制线&#xff0c;和读写电路。所以两个端口可以做到同时访问相同的存储…

Ettus USRP X410 配件讲解,如何选择对应的配件

Ettus USRP X410 产品图片 产品编号: 787272-01 附件 所需的附件 USRP电源线 可选附件 连接套件 SMA连接线 线缆 安装套件 通信电缆 风扇盒 汇总列表 名称编号价格Ettus USRP X410787272-01215,550.00USRP RIO电源线&#xff0c;中国785023-10165.00双百兆以太网PCIe接口…

C语言实现输入一行字符统计其中有多少个单词,单词之间用空格分隔开

完整代码&#xff1a; // 输入一行字符统计其中有多少个单词&#xff0c;单词之间用空格分隔开 #include<stdio.h>int main() {char ch;//num为单词的个数int num0;printf("请输入一行字符&#xff0c;单词之间用一个空格分隔开\n");while ((chgetchar())!\n)…

Linux:程序地址空间/虚拟地址等相关概念理解

文章目录 程序地址空间虚拟地址和物理地址地址的转换地址空间是什么&#xff1f; 程序地址空间 在C和C程序中&#xff0c;一直有一个观点是&#xff0c;程序中的各个变量等都会有一定的地址空间&#xff0c;因此才会有诸如取地址&#xff0c;通过地址访问等操作&#xff0c;那…

【软件设计师-中级——刷题记录7(纯干货)】

目录 每日一言&#xff1a;持续更新中...你的编程水平是&#xff1f; A 入门&#xff1a;我对编程没有一点儿经验 B. 初级&#xff1a;了解一点基础知识&#xff0c;但没有写过代码 C. 中级&#xff1a;会写点简单代码&#xff0c;能看懂代码 D. 高级&#xff1a;写过多段代码&…

基于springboot实现学生综合成绩测评系统项目【项目源码】计算机毕业设计

基于springboot实现学生综合成绩测评系统演示 开发技术与环境配置 以Java语言为开发工具&#xff0c;利用了当前先进的springboot框架&#xff0c;以MyEclipse10为系统开发工具&#xff0c;MySQL为后台数据库&#xff0c;开发的一个学生综合测评系统。 2.1 SpringBoot框架 S…