【微信公众平台】扫码登陆

文章目录

    • 前置准备
      • 测试号接口配置
    • 带参数二维码登陆
      • 获取access token
      • 获取Ticket
      • 拼装二维码Url
      • 编写接口返回二维码
      • 接收扫描带参数二维码事件
      • 编写登陆轮训接口
      • 测试页面
    • 网页授权二维码登陆
      • 生成ticket
      • 生成授权地址
      • 获取QR码
        • 静态文件支持
        • 编写获取QR码的接口
      • 接收重定向参数
      • 轮训登陆接口
      • 测试页面

前置准备

80/443端口能被联网访问:内网穿透、云服务器(测试号无需域名,ip即可)

申请测试号:微信公众平台 (qq.com)

测试号接口配置

开始开发 / 接入指南 (qq.com)

在这里插入图片描述

Token随便写入,用于判断请求是否来自微信服务器。

URl绑定需要编写一个接口用来接收微信的信息:

  • 接收微信GET请求传来四个参数 (后续事件推送也会POST到这个接口)

  • 返回参数中的Echostr参数

(gin框架)

wxgroup := r.Group("/wechat")
{
	wxgroup.Any("/message",controller.WxMessage)
}
r.Run(":80") // 80 端口启动服务

wxgo是博主学习过程写的微信登陆小工具包: go get github.com/beropero/mysdk/wxgo

func WxMessage(ctx *gin.Context) {
	vp := &wxgo.VerifyParams{
		Signature: ctx.Query("signature"),
		Echostr:   ctx.Query("echostr"),
		Timestamp: ctx.Query("timestamp"),
		Nonce:     ctx.Query("nonce"),
	}
    // 判断请求是否来自微信服务器。
	if flag, _ := wechat.Wx.VerifySignature(*vp); flag {
		log.Println(vp)
        // 返回echostr字符串
		ctx.String(http.StatusOK, vp.Echostr)
	}
}

开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

参数描述
signature微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp时间戳
nonce随机数
echostr随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:

1)将token、timestamp、nonce三个参数进行字典序排序

2)将三个参数字符串拼接成一个字符串进行sha1加密

3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

检验signature的Golang示例代码:

// 签名验证
func (w *Wechat) VerifySignature(vp VerifyParams) (res bool, err error) {
	// 获取 token 字段, 为接口配置设置的token参数
	token, err := w.Cfg.GetToken()
	if err != nil {
		return false, err
	}
	// 构造匹配字段
	strs := []string{vp.Timestamp, vp.Nonce, token}
	// 按字典序排列后拼接成一个字符串
	sort.Strings(strs)
	str := strings.Join(strs, "")
	// 对拼接后的字符串进行 SHA1 加密
	hash := sha1.New()
	hash.Write([]byte(str))
	hashed := fmt.Sprintf("%x", hash.Sum(nil))
	// 加密结果与 signature 比较
	if hashed != vp.Signature {
		return false, errors.New("error: Signature mismatch")
	}
	return true, nil
}

将接口地址http://your_ip:80/wechat/message写入配置信息中的URL即可配置成功

带参数二维码登陆

账号管理 / 生成带参数的二维码 (qq.com)

流程:

  • 获取access token
  • 通过access token与一些请求参数获取二维码ticket
  • 通过ticket换取二维码url,返回给前端
  • 用户扫描二维码后事件信息推送到消息接口、将ticket放入redis
  • 前端用ticket轮训是否已经登陆

获取access token

开始开发 / 获取 Access token (qq.com)

  • 在测试号信息下获取:appID、appsecret

  • 用http client发送请求获取access token

接口调用请求说明

https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

参数说明

参数是否必须说明
grant_type获取access_token填写client_credential
appid第三方用户唯一凭证
secret第三方用户唯一凭证密钥,即appsecret

返回说明

正常情况下,微信会返回下述JSON数据包给公众号:

{"access_token":"ACCESS_TOKEN","expires_in":7200}

参数说明

参数说明
access_token获取到的凭证
expires_in凭证有效时间,单位:秒

错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):

{"errcode":40013,"errmsg":"invalid appid"}
// 获取普通access token
func (w *Wechat) GetATReq() error {
	// 获取appID与appsecret
	appid, err := w.Cfg.GetAppid()
	if err != nil {
		return err
	}
	appsecret, err := w.Cfg.GetAppsecret()
	if err != nil {
		return err
	}
	// 构造请求地址
    // ReqUrl.ATUrl: "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
	url := fmt.Sprintf(ReqUrl.ATUrl, appid, appsecret)
	// 发送 GET 请求获取响应
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	// 读取响应
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	// 如果响应结果包含错误错误码,返回错误信息
	if strings.Contains(string(body), "errcode") {
		return fmt.Errorf("wechat response error: %s", string(body))
	}
	// 解析字符串
	err = json.Unmarshal(body, &w.LatestAT)
	if err != nil {
		return errors.New("json Unmarshal fail")
	}
	// 设置成功获取时间
	w.LatestAT.Time = time.Now()
	return nil
}
// wechat 用于保存at与配置信息
type Wechat struct {
	Cfg      *WechatCfg
	LatestAT *AT
}
// access token
type AT struct {
	AccessToken string `json:"access_token"`
	ExpiresTime int    `json:"expires_in"`
	Time        time.Time // access token获取时间
}
// 配置
type WechatCfg struct {
	Token       string
	Appid       string
	Appsecret   string
	ExpiresTime int
}
// 获取 accesstoken
func (w *Wechat) GetAccessToken() (at string, err error) {
	w.RefreshAT()
	return w.LatestAT.AccessToken, nil
}
// 刷新 LatestAT
func (w *Wechat) RefreshAT() error {
	// 先判断上次获取的是否超时
	duration := time.Since(w.LatestAT.Time)
	durationInSeconds := int(duration.Seconds())
	if durationInSeconds < (w.LatestAT.ExpiresTime - 600) {
		return nil
	}
    // 发送请求获取access token
	err := w.GetATReq()
	return err
}

获取Ticket

创建二维码ticket

每次创建二维码ticket需要提供一个开发者自行设定的参数(scene_id),分别介绍临时二维码和永久二维码的创建二维码ticket过程。

临时二维码请求说明

http请求方式: POST URL: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN POST数据格式:json POST数据例子:{“expire_seconds”: 604800, “action_name”: “QR_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}} 或者也可以使用以下POST数据创建字符串形式的二维码参数:{“expire_seconds”: 604800, “action_name”: “QR_STR_SCENE”, “action_info”: {“scene”: {“scene_str”: “test”}}}

参数说明

参数说明
expire_seconds该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为60秒。
action_name二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
action_info二维码详细信息
scene_id场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1–100000)
scene_str场景值ID(字符串形式的ID),字符串类型,长度限制为1到64
func (w *Wechat) GetQRTicketReq(codetype string, sceneId int) (string, error) {
	// 获取 access token
	at, err := w.GetAccessToken()
	if err != nil {
		return "", err
	}
	// 拼接请求地址
	url := fmt.Sprintf(ReqUrl.TicketUrl, at)
	// 构造请求数据
	data := &QRCodeReq{
		ExpireSeconds: w.Cfg.GetExpiresTime(),
		ActionName:    codetype, // QR码 类型
		ActionInfo: ActionInfo{
			Scene: Scene{
				SceneId: sceneId,
			},
		},
	}
	// 发送 post 请求获取响应
	client := &http.Client{}
	Jsondata, err := json.Marshal(&data)
	if err != nil {
		return "", err
	}
	reader := bytes.NewReader(Jsondata)
	req, err := http.NewRequest("POST", url, reader)
	if err != nil {
		return "", err
	}
	// 设置请求头 json格式
	req.Header.Set("Content-Type", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	// 读取响应
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	var respData = QRCodeRes{}
	// 解析字符串
	err = json.Unmarshal(body, &respData)
	if err != nil {
		return "", errors.New("json unmarsha fail")
	}
	return respData.Ticket, nil
}

拼装二维码Url

通过ticket换取二维码

获取二维码ticket后,开发者可用ticket换取二维码图片。请注意,本接口无须登录态即可调用。

请求说明

HTTP GET请求(请使用https协议)https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET 提醒:TICKET记得进行UrlEncode

返回说明

ticket正确情况下,http 返回码是200,是一张图片,可以直接展示或者下载。

HTTP头(示例)如下: Accept-Ranges:bytes Cache-control:max-age=604800 Connection:keep-alive Content-Length:28026 Content-Type:image/jpg Date:Wed, 16 Oct 2013 06:37:10 GMT Expires:Wed, 23 Oct 2013 14:37:10 +0800 Server:nginx/1.4.1

错误情况下(如ticket非法)返回HTTP错误码404。

func (w *Wechat) GetQrImageUrl(ticket string) string {
	ticket = url.QueryEscape(ticket) // 进行UrlEncode
    // ReqUrl.QRImgUrl: "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s"
	url := fmt.Sprintf(ReqUrl.QRImgUrl,ticket)
	return url
}

编写接口返回二维码

wxgroup.GET("/getloginqr",controller.GetGZQrUrl)
// 获取公众号登陆二维码
func GetGZQrUrl(ctx *gin.Context) {
    // 获取ticket
	ticket, _ := wechat.Wx.GetQRTicketReq("QR_STR_SCENE", 123)
    // 拼装二维码url
	qrUrl := wechat.Wx.GetQrImageUrl(ticket)
	ctx.JSON(http.StatusOK, gin.H{
		"ticket": ticket,
		"qrUrl":  qrUrl,
	})
}

接收扫描带参数二维码事件

基础消息能力 / 接收事件推送 (qq.com)基础消息能力 / 接收事件推送 (qq.com)

用户扫描带场景值二维码时,可能推送以下两种事件:

  1. 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
  2. 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。

推送XML数据包示例:

<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[FromUser]]></FromUserName>
  <CreateTime>123456789</CreateTime>
  <MsgType><![CDATA[event]]></MsgType>
  <Event><![CDATA[subscribe]]></Event>
  <EventKey><![CDATA[qrscene_123123]]></EventKey>
  <Ticket><![CDATA[TICKET]]></Ticket>
</xml>

参数说明:

参数描述
ToUserName开发者微信号
FromUserName发送方账号(一个OpenID)
CreateTime消息创建时间 (整型)
MsgType消息类型,event
Event事件类型,subscribe(关注) / SCAN(之前已关注)
EventKey事件KEY值,qrscene_为前缀,后面为二维码的参数值
Ticket二维码的ticket,可用来换取二维码图片

在最开始时的消息接口添加消息处理逻辑,接收到扫描带参数二维码事件推送后将ticket和openid放入redis

func WxMessage(ctx *gin.Context) {
	vp := &wxgo.VerifyParams{
		Signature: ctx.Query("signature"),
		Echostr:   ctx.Query("echostr"),
		Timestamp: ctx.Query("timestamp"),
		Nonce:     ctx.Query("nonce"),
	}
	if flag, _ := wechat.Wx.VerifySignature(*vp); flag {
		log.Println(vp)
		ctx.String(http.StatusOK, vp.Echostr)
	}
    // 添加消息处理逻辑
	uEvent := &wxgo.UserEvent{}
	ctx.ShouldBindXML(&uEvent)
	fmt.Println(uEvent)
	if uEvent.Ticket != "" && (uEvent.Event == "SCAN" || uEvent.Event == "subscribe") {
		openid := uEvent.FromUserName
		log.Printf("ticket:%s, openid:%s", uEvent.Ticket, openid)
		// 将ticket和openid存入redis
		err := redis.RedisClient.Set(uEvent.Ticket, openid, 60*time.Second).Err()
		if err != nil {
			log.Println(err.Error())
		}
	}
}

// 用户事件
type UserEvent struct {
	ToUserName   string `xml:"ToUserName"`   // 开发者微信号
	FromUserName string `xml:"FromUserName"` // 发送方账号(一个OpenID)
	CreateTime   int    `xml:"CreateTime"`   // 消息创建时间(整型)
	MsgType      string `xml:"MsgType"`      // 消息类型,event,
	Event        string `xml:"Event"`        // 事件类型,subscribe(关注), SCAN(已关注)
	EventKey     string `xml:"EventKey"`     // 事件KEY值,qrscene_为前缀,后面为二维码的参数值
	Ticket       string `xml:"Ticket"`       // 二维码的ticket,可用来换取二维码图片
}

编写登陆轮训接口

前端需要传递参数ticket询问是否登陆

// 登陆轮训接口
func CheckLogin(ctx *gin.Context) {
	ticket := ctx.Query("ticket")
    // 从redis中读取ticket
	openid, err := redis.RedisClient.Get(ticket).Result()
	if err != nil {
		ctx.JSON(http.StatusOK, gin.H{
			"login": false,
		})
	} else {
		ctx.JSON(http.StatusOK, gin.H{
			"login":  true,
			"openid": openid,
		})
	}
}
wxgroup.GET("/checklogin",controller.CheckLogin)

测试页面

修改两处请求地址

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录页面</title>
    <style>
        #qr-code {
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>

    <div id="qr-code-container">
        <img id="qr-code" src="#" alt="QR Code">
    </div>
    <h1 id="login-status"></h1>
    <h3 id="login-user"></h3>
    <script>
        // 请求后端获取二维码图片地址
        function getQRCode() {
            // 修改请求地址
            fetch('http://your_ip/wechat/getloginqr') // 假设'/get_qr_code'是后端提供的接口地址
            .then(response => response.json())
            .then(data => {
                console.log(data.qrUrl)
                document.getElementById('qr-code').src = data.qrUrl;
                // 开始轮询检查登录状态
                // 根据问号分割URL,获取问号后面的部分
                var queryString = data.qrUrl.split('?')[1];
                // 根据等号分割查询字符串,获取ticket参数的值
                var ticket = queryString.split('=')[1];
                console.log(ticket);
                checkLoginStatus(ticket);
            })
            .catch(error => console.error('Error:', error));
        }

        // 轮询检查登录状态
        function checkLoginStatus(ticket) {
            // 修改请求地址
            url = 'http://your_ip/wechat/checklogin?ticket='+ ticket
            console.log(url)
            var checkLoginInterval = setInterval(() => {
                fetch(url) // 假设'/check_login_status'是后端提供的接口地址
                .then(response => response.json())
                .then(data => {
                    console.log(data)
                    if (data.login) {
                        document.getElementById('login-status').innerText = '登录成功';
                        document.getElementById('login-user').innerText = 'Openid: ' + data.openid; 
                        clearInterval(checkLoginInterval); // 登录成功后停止轮询
                    } 
                })
                .catch(error => console.error('Error:', error));
            }, 2000); // 每隔2秒轮询一次
        }

        // 页面加载完成后立即获取二维码
        window.onload = function() {
            getQRCode();
        };
    </script>
</body>
</html>

网页授权二维码登陆

微信网页开发 / 网页授权 (qq.com)

微信公众测试号不支持PC端网页登陆,但是可以模拟带参数二维码的登陆流程来实现类似的功能

流程:

  • 生成ticket作为授权地址的state参数
  • 编写接口作为授权地址都redirect_uri参数
  • 用授权地址生成QR码,并将QR码与ticket返回给前端
  • 用户扫描QR码将携带code和state参数重定向到接口
  • 接口接收code和state中的ticket参数,用code向微信服务器获取用户的access token和open id
  • 将ticket和openid存入redis
  • 前端用ticket轮训是否登陆

生成ticket

生成随机字符串

func GenerateRandomTicket(length int) string {
	rand.Seed(time.Now().UnixNano())
	var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
	b := make([]rune, length)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

生成授权地址

在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(已认证服务号,默认拥有scope参数中的snsapi_base和snsapi_userinfo 权限),引导关注者打开如下页面:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

参数说明

参数是否必须说明
appid公众号的唯一标识
redirect_uri授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
response_type返回类型,请填写code
scope应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
state重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
#wechat_redirect无论直接打开还是做页面302重定向时候,必须带此参数
forcePopup强制此次授权需要用户弹窗确认;默认为false;需要注意的是,若用户命中了特殊场景下的静默授权逻辑,则此参数不生效
// 生成ticket
ticket := wxgo.GenerateRandomTicket(20)
// 生成授权地址
redirect_url := "http://your_ip/wechat/accessusercode" // 微信授权后重定向地址,用于接收用户code
scope := "snsapi_base" //授权权限
oauthUrl := wechat.Wx.GetOauth2CodeUrl(redirect_url, scope, ticket) // 授权地址
// Oauth2CodeUrl: "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect"
func (w *Wechat) GetOauth2CodeUrl(redirectUrl string, scope string, state string) string {
	encodeUrl := url.QueryEscape(redirectUrl)
	url := fmt.Sprintf(ReqUrl.Oauth2CodeUrl, w.Cfg.Appid, encodeUrl, scope, state)
	return url
}

获取QR码

静态文件支持

qr码保存到服务器中,需要联网访问到

r.Static("/static","resource")
编写获取QR码的接口
wxgroup.GET("/getauthqr",controller.GetAuthQrUrl)
// 获取网页授权登陆二维码
func GetAuthQrUrl(ctx *gin.Context) {
	// 生成ticket
	ticket := wxgo.GenerateRandomTicket(20)
	// 生成授权地址
	redirect_url := "http://your_ip/wechat/accessusercode" // 微信授权后重定向地址,用于接收用户code
	scope := "snsapi_base"                                  //授权权限
	oauthUrl := wechat.Wx.GetOauth2CodeUrl(redirect_url, scope, ticket)
	// 将授权地址生成QR码
	savePath := "./resource/image"
	err := wxgo.GenerateQrCode(oauthUrl, savePath, ticket)
	if err != nil {
		log.Fatal(err.Error())
		return
	}
	qrUrl := fmt.Sprintf("http://your_ip/static/image/%s.png", ticket)
	ctx.JSON(http.StatusOK, gin.H{
		"ticket": ticket,
		"qrUrl":  qrUrl,
	})
}

GenerateQrCode:

func GenerateQrCode(url string, savedir string, fname string) error {
    // "github.com/skip2/go-qrcode" 包
	qrcode, err := qrcode.New(url, qrcode.Highest)
	if err != nil {
		return err
	}
	qrcode.DisableBorder = true
	//保存成文件
	savepath := fmt.Sprintf("%s/%s.png", savedir, fname)
	err = qrcode.WriteFile(256, savepath)
	return err
}

接收重定向参数

如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

code说明:code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。

// 网页授权接收code
func AccessUserCode(ctx *gin.Context) {
	code := ctx.Query("code")
	ticke := ctx.Query("state")
	// 用code获取用户access token
	uat, _ := wechat.Wx.GetUserATReq(code)
	// 用access token获取用户信息
	uInfo, _ := wechat.Wx.GetUserInfoReq(uat)
	log.Printf(uInfo.NickName, uInfo.Headimgurl, uInfo.City)
	// 将ticket和openid放入redis
	redis.RedisClient.Set(ticke, uInfo.OpenId, 60*time.Second)
	// 重定向到成功/失败页面
	ctx.Redirect(http.StatusTemporaryRedirect, "http://your_ip/static/html/loginsucceed.html")
}

轮训登陆接口

复用带参数二维码的登陆轮训接口

测试页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录页面</title>
    <style>
        #qr-code {
            width: 200px;
            height: 200px;
        }
    </style>
</head>
<body>

    <div id="qr-code-container">
        <img id="qr-code" src="#" alt="QR Code">
    </div>
    <h1 id="login-status"></h1>
    <h3 id="login-user"></h3>
    <script>
        // 请求后端获取二维码图片地址
        function getQRCode() {
        	// 修改ip
            fetch('http://your_ip/wechat/getauthqr') 
            .then(response => response.json())
            .then(data => {
                console.log(data.qrUrl)
                document.getElementById('qr-code').src = data.qrUrl;
                // 开始轮询检查登录状态
                var ticket = data.ticket;
                console.log(ticket);
                checkLoginStatus(ticket);
            })
            .catch(error => console.error('Error:', error));
        }

        // 轮询检查登录状态
        function checkLoginStatus(ticket) {
            // 修改ip
            url = 'http://your_ip/wechat/checklogin?ticket='+ ticket
            console.log(url)
            var checkLoginInterval = setInterval(() => {
                fetch(url) // 假设'/check_login_status'是后端提供的接口地址
                .then(response => response.json())
                .then(data => {
                    console.log(data)
                    if (data.login) {
                        document.getElementById('login-status').innerText = '登录成功';
                        document.getElementById('login-user').innerText = 'Openid: ' + data.openid; 
                        clearInterval(checkLoginInterval); // 登录成功后停止轮询
                    } 
                })
                .catch(error => console.error('Error:', error));
            }, 2000); // 每隔2秒轮询一次
        }

        // 页面加载完成后立即获取二维码
        window.onload = function() {
            getQRCode();
        };
    </script>
</body>
</html>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux的vim下制作进度条

目录 前言&#xff1a; 回车和换行有区别吗&#xff1f; 回车和换行的区别展示&#xff08;这个我在Linux下演示&#xff09; 为什么会消失呢? 回车和换行的区别 为什么\r和\n产生的效果不同&#xff1f; 打印进度条&#xff1a; &#xff08;1&#xff09;打印字符串 …

【再探】设计模式—抽象工厂及建造者模式

抽象工厂模式和建造者模式都属于创建型模式。两者都能创建对应的对象&#xff0c;而创建者模式更侧重于创建复杂对象&#xff0c;将对象的创建过程封装起来&#xff0c;让客户端不需要知道对象的内部细节。 1 抽象工厂模式 需求&#xff1a; 在使用工厂方法模式时&#xff0…

TCP协议关于速率的优化机制-滑动窗口详解

在上一章中&#xff0c;我们讲述了TCP协议在传输过程中的可靠性http://t.csdnimg.cn/BsImO&#xff0c;这里衔接上一篇文章继续讲&#xff0c;TCP协议的特性&#xff0c;TCP协议写完之后就写&#xff0c;Http和Https等内容吧 1. 滑动窗口 这里的滑动窗口不是指算法里面的双指…

品牌百度百科词条需要什么资料?

品牌百度百科词条是一个品牌的数字化名片&#xff0c;更是品牌历史、文化、实力的全面展现。 作为一个相当拿得出手的镀金名片&#xff0c;品牌百度百科词条创建需要什么资料&#xff0c;今天伯乐网络传媒就来给大家讲解一下。 一、品牌基本信息&#xff1a;品牌身份的明确 品…

用 PyTorch 构建液态神经网络(LNN)

用 PyTorch 构建液态神经网络&#xff08;LNN&#xff09; 文章目录 什么是液态神经网络为什么需要液态神经网络LNN 与 RNN 的区别用 PyTorch 实现 LNNStep 1. 导入必要的库Step 2. 定义网络架构Step 3. 实现 ODE 求解器Step 4. 定义训练逻辑 LNN 的缺陷总结 什么是液态神经网络…

C语言-嵌入式-STM32:FreeRTOS说明和详解

Free即免费的&#xff0c;RTOS的全称是Real time operating system&#xff0c;中文就是实时操作系统。 注意&#xff1a;RTOS不是指某一个确定的系统&#xff0c;而是指一类操作系统。比如&#xff1a;uc/OS&#xff0c;FreeRTOS&#xff0c;RTX&#xff0c;RT-Thread 等这些都…

docker自定义java运行环境镜像

一、下载jre/jdk 压缩包&#xff0c;centos:7基础镜像 1、 下载jdk/dre 下载jdk或jre 官网下载 根据需求下载 jdk:SE Development Kit(开发环境) jre: SE Runtime Environment (运行环境)2、下载centos:7 # 下载centos7 docker镜像 docker pull centos:7#centos查看系统时间 …

面试经典算法题之双指针专题

力扣经典面试题之双指针 ( 每天更新, 每天一题 ) 文章目录 力扣经典面试题之双指针 ( 每天更新, 每天一题 )验证回文串收获 392. 判断子序列 验证回文串 思路 一: 筛选 双指针验证 class Solution { public:bool isPalindrome(string s) {// 所有大写字母 > 小写 去除非字母…

对比mongodb查询的执行计划,说一说组合索引的优化方案(上)

一、背景 Mongodb数据库&#xff0c;有个160w数据量规模的集合&#xff0c;字段多达几十个&#xff0c;随着需求的迭代&#xff0c;查询条件也是五花八门。 为了提高某个查询的效率&#xff0c;结果都以新增索引解决问题&#xff0c;最后多达16个索引。 这里仅贴出本文会提及…

引领农业新质生产力,鸿道(Intewell®)操作系统助力农业机器人创新发展

4月27日至29日&#xff0c;2024耒耜国际会议在江苏大学召开。科东软件作为特邀嘉宾出席此次盛会&#xff0c;并为江苏大学-科东软件“农业机器人操作系统”联合实验室揭牌。 校企联合实验室揭牌 在开幕式上&#xff0c;江苏大学、科东软件、上交碳中和动力研究院、遨博智能研究…

Spring Boot Admin

概述 Spirng Boot Admin 登录页面 Spring Boot Admin是一个用于管理Spring Boot应用的监控工具,它允许你查看和管理多个Spring Boot应用实例。用于应用信息进行界面化的展示&#xff0c;常常辅助我们开发人员快速查看服务运行状态在微服务架构中&#xff0c;Spring Boot Admin通…

中科院突破:TalkingGaussian技术实现3D人脸动态无失真,高效同步嘴唇运动!

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; 引言&#xff1a;探索高质量3D对话头像的新方法 在数字媒体和虚拟互动领域&#xff0c;高质量的3D对话头像技术正变得日益重要。这种技术能够在虚拟现实、电影…

谷粒商城实战(020 RabbitMQ-消息确认)

Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第258p-第p261的内容 消息确认 生产者 publishers 消费者 consumers 设置配置类 调用api 控制台 抵达brocker 代理 新版本ReturnCallbac…

【webrtc】MessageHandler 8: 基于线程的消息处理:处理音频输入输出断开

m98代码,看起来m114 去掉了MessageHandler :音频的录制和播放 都使用了on message,但只是用来通知并处理流的断开的。AAudioRecorder AAudioRecorder 处理流断开 OnErrorCallback :有可能 错误回调是别处来的,是其他线程, 但是这个错误的处理要再自己的线程执行: 音频播…

北京大学肖臻老师《区块链技术与应用》P14(ETH概述)和P15(ETH账户)

1️⃣ 参考 北京大学肖臻老师《区块链技术与应用》 P14 - ETH概述篇P15 - ETH账户篇 1️⃣4️⃣ETH概述 ① 比特币与以太坊的对比 比特币&#xff08;区块链 1.0&#xff09;以太坊&#xff08;区块链 2.0&#xff09;出块时间大约10 min十几秒mining puzzle计算密集型Memo…

【计算智能】基本遗传算法在优化问题中的应用与实验【理论到程序】

文章目录 1. 引言&#xff1a;遗传算法简介2. 基本遗传算法&#xff08;SGA&#xff09;2.1 基本遗传算法的构成要素1. 染色体编码2. 适应度函数3. 遗传算子 2.2 实验设计与方法1. 算法流程2. 伪代码3. python实现1. 导入模块2. 目标函数 f(x)3 初始化种群4. 计算适应度5. 选择…

Django后台项目开发实战二

我们的需求是开发职位管理系统 三个功能&#xff1a; 管理员发布职位候选人能浏览职位用户能投递职位 第二阶段 创建应用 jobs&#xff0c;实现职位数据的建模 python manage.py startapp jobs 然后再 setting .py 注册应用&#xff0c;只需添加应用名称到最后一行 INST…

VTK —— 二、教程六 - 为模型加入3D微件(按下i键隐藏或显示)(附完整源码)

代码效果 本代码编译运行均在如下链接文章生成的库执行成功&#xff0c;若无VTK库则请先参考如下链接编译vtk源码&#xff1a; VTK —— 一、Windows10下编译VTK源码&#xff0c;并用Vs2017代码测试&#xff08;附编译流程、附编译好的库、vtk测试源码&#xff09; 教程描述 本…

探索未来道路:智慧高速系统架构的革命性进步

随着科技的飞速发展&#xff0c;智慧高速系统架构正在成为道路交通领域的一项重要创新。这一系统结合了先进的信息技术和智能化设备&#xff0c;为高速公路提供了全新的管理和服务模式&#xff0c;极大地提升了交通运输效率和安全性。本文将深入探讨智慧高速系统架构的革命性进…

ping命令操作记录

1&#xff0c;ping 主机可查看主机是否在线 2&#xff0c;ping -a参数&#xff0c;解析主机的名称 3&#xff0c;ping -r 跟踪打印路由信息 ping命令的作用&#xff1a;确认目标主机是否存活&#xff0c;确定网络是否畅通 ping的原理&#xff1a;ping发送ICMP&#xff08;互联…