微信公众号扫码登录(一)—— 获取微信公众号二维码

news2024/11/20 10:46:59

引言

这几天在研究微信登录,今天解决了获取微信二维码问题;在这里总结一下

关于微信登录想说的话

第一次接触微信登录,开始就弄混了登录方式;简单来说,微信扫码登录分为两种,一种是微信公众平台,一种是微信开放平台,两者的开发文档也不一样,开始就是一直用的微信公众号的参数却使用的是微信开放平台提供的接口,找了半天问题;


总结一下:

微信公众号(公众平台) 和 微信开放平台 是两码事。

  • 微信公众平台是扫码通过微信公众号授权登录的,个人用户可以申请订阅号,但是没有一些接口调用权限,企业用户可以申请服务号,有许多接口权限;但是个人用户可以通过测试号获取一些权限进行学习测试;
  • 微信开放平台是微信为了接入更多第三方应用而开放的接口,对于web应用,可以申请web应用,从而获取权限,但是只能是企业用户才能申请;(个人学习很不方便)

两者开发文档是不同的,所以看网上教程一定要看清楚是公众平台还是开放平台,不要跟错教程了;最好就是看官方文档,这样就能避免踩坑;(我开始就是看不下去文档,因为微信是php示例代码,所以一直看网上教程,就被各种各样的教程弄晕了;最后还是乖乖看文档去了)


下面就进入正题

准备环境

首先申请了一个公众号,然后从开发者工具中进入测试号:

image-20220922221842878

然后就是这个界面:

image-20220922221943712

appID和appsecret都是操作所需参数;

接口配置下面介绍;

接口配置

接口配置后面会用到,所以需要先配置一下,其实看官方文档就能看懂,但是示例代码是php,所以这里我来演示一下我的操作;

官方文档:传送门

官方文档意思就是你得有一个域名,代码还得跑在该域名下的服务器上,然后你自己需要再代码中实现一个接口获取微信发来的信息进行处理;

如果你和我一样是个学生,开发都在本地127.0.0.1,或者没有域名,那该怎么办?因为微信不能直接调用本地ip,这就需要用到内网穿透;

简单来说就是微信想要向你填入的url发送请求数据,但是它不能直接向本地127.0.0.1发送,我们可以通过内网穿透获取一个域名,让该域名映射到本地127.0.0.1,然后微信向该域名发送数据,这样就把数据发送到了本地;(个人理解)


内网穿透

我通过ngrok进行的内网穿透,就花2块钱实名了一下,然后有一个免费的隧道可以使用:

image-20220922222849476

然后进行配置,它就会給你分一个域名:

image-20220922223101957

接下来下载ngrok客户端,启动隧道:

image-20220922223534052

点击.bat文件,按要求输入隧道id:

image-20220922223631622

链接成功,这时时就意味着访问本地127.0.0.1和访问生成的域名的效果是一样的;


访问流程就是:

image-20220922223402119

文档:image-20220922223204499

微信向自己填的url发送请求;

实现接入代码

然后就是代码配置,就是官网文档的第二步,官方检验是一个php代码,下面是java代码:

controller接口:

/**
 *  接入微信接口
 */
@GetMapping("/callback")
@ResponseBody
public String checkSign (HttpServletRequest request) throws Exception {
    log.info("===========>checkSign");
    // 获取微信请求参数
    String signature = request.getParameter ("signature");
    String timestamp = request.getParameter ("timestamp");
    String nonce = request.getParameter ("nonce");
    String echostr = request.getParameter ("echostr");
    log.info("开始校验此次消息是否来自微信服务器,param->signature:{},\ntimestamp:{},\nnonce:{},\nechostr:{}",
            signature, timestamp, nonce, echostr);
    if (CheckWXTokenUtils.checkSignature(signature, timestamp, nonce)) {
        return echostr;
    }
    return "";
}

校验工具类(直接cv)

import lombok.extern.log4j.Log4j2;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
 * 和微信建立链接参数校验
 */
@Log4j2
public class CheckWXTokenUtils {
    private static final String TOKEN = "xxxxxx"; // 自定义的token

    /**
     * 校验微信服务器Token签名
     *
     * @param signature 微信加密签名
     * @param timestamp 时间戳
     * @param nonce     随机数
     * @return boolean
     */
    public static boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] arr = {TOKEN, timestamp, nonce};
        Arrays.sort(arr);
        StringBuilder stringBuilder = new StringBuilder();
        for (String param : arr) {
            stringBuilder.append(param);
        }
        String hexString = SHA1(stringBuilder.toString());
        return signature.equals(hexString);
    }

    private static String SHA1(String str) {
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(str.getBytes());
            return toHexString(digest);
        } catch (NoSuchAlgorithmException e) {
            log.info("校验令牌Token出现错误:{}", e.getMessage());
        }
        return "";
    }

    /**
     * 字节数组转化为十六进制
     *
     * @param digest 字节数组
     * @return String
     */
    private static String toHexString(byte[] digest) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : digest) {
            String shaHex = Integer.toHexString(b & 0xff);
            if (shaHex.length() < 2) {
                hexString.append(0);
            }
            hexString.append(shaHex);
        }
        return hexString.toString();
    }
}

几个校验参数官方文档也说了,自己对比着看就行了;

再次放入官方文档地址:接入概述


配置就是:

url是内网穿透域名+自己实现的接口

token也是自己写的;

image-20220924171800451

image-20220922224459977

然后在网页服务中:

image-20220922224534059

点击修改:(还是内网穿透域名)

image-20220922224616319

然后就配置好了;

下面就是正式开始二维码生成了;

生成二维码

先放出文档:

生成带参数的二维码

获取Access token

获取ticket

我们先看第一个文档,大致意思就是先发送一个获取二维码ticket的post请求,获取ticket:

image-20220922225008145

整理一下:

url: post
https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN

参数:
param参数:access_token
json参数(两个必要的,其他可以自己看文档加): {"expire_seconds": 604800, "action_name": "QR_SCENE"}

结果:
{"ticket":"gQH47joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL2taZ2Z3TVRtNzJXV1Brb3ZhYmJJAAIEZ23sUwMEmm
3sUw==","expire_seconds":60,"url":"http://weixin.qq.com/q/kZgfwMTm72WWPkovabbI"}

那么就很简单了,就是调用就接口获取ticket,但是可以发现所需要的参数中的access_token我们并没有,所以调用该接口前需要先获取,同样也有官方文档,上面已经放出来了;


获取access_token

image-20220922232119249

同样整理一下:

url: get
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

参数:
grant_type、appid、secret

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

其中grant_type就写client_credential就行了,固定的;

appid和secret就是测试号的:

image-20220922232341075

所以很轻易就可以获取到access_token;

获取到access_token就可以获取ticket,到这里这两步就完成了;

接下来就是获取二维码了:

image-20220922232555368

获取二维码

image-20220922232708795

这就不过多介绍了,就加一个ticket参数即可;

测试流程

下面我使用postman测试一遍流程:

1,首先获取access_token:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=&secret=

image-20220922232959786

2,然后获取ticket:

https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=ACCESS_TOKEN

image-20220922233105499

3,最后获取二维码:

https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET

直接浏览器访问该链接,即可看到该二维码

image-20220922233213152

整个流程就走通了;

下面就是代码实现该流程;

代码实现

这里思考一个问题,后端内部如何自己发送请求获取响应?

平时都是前端向后端发送请求,后端响应;

因为我们需要先发送请求获取access_token,再获取ticket,最后响应给前端的就是一个二维码url,所以后端需要自己发请求并获取响应结果;

这里可以使用httpclient,具体细节可以查资料,这里不过多介绍;

下面是实现代码:

httpclient依赖:

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

httpclient工具类(直接cv):

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
 
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
 
/**
 * HttpClient工具类
 */
public class HttpClientUtils {
 
    private static final CloseableHttpClient httpClient;
 
    // 采用静态代码块,初始化超时时间配置,再根据配置生成默认httpClient对象
    static {
        RequestConfig config = RequestConfig.custom().setConnectTimeout(30000).setSocketTimeout(15000).build();
        httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
    }
 
    /**
     * 发送 HTTP GET请求,不带请求参数和请求头
     * @param url 请求地址
     * @return
     * @throws Exception
     */
    public static String doGet(String url) throws Exception {
        HttpGet httpGet = new HttpGet(url);
        return doHttp(httpGet);
    }
 
    /**
     * 发送 HTTP GET,请求带参数,不带请求头
     * @param url 请求地址
     * @param params 请求参数
     * @return
     * @throws Exception
     */
    public static String doGet(String url, Map<String, Object> params) throws Exception {
        // 转换请求参数
        List<NameValuePair> pairs = covertParamsToList(params);
        // 装载请求地址和参数
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);
        ub.setParameters(pairs);
        HttpGet httpGet = new HttpGet(ub.build());
        return doHttp(httpGet);
    }
 
    /**
     * 发送 HTTP GET请求,带请求参数和请求头
     * @param url 请求地址
     * @param headers 请求头
     * @param params 请求参数
     * @return
     * @throws Exception
     */
    public static String doGet(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
        // 转换请求参数
        List<NameValuePair> pairs = covertParamsToList(params);
        // 装载请求地址和参数
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);
        ub.setParameters(pairs);
 
        HttpGet httpGet = new HttpGet(ub.build());
        // 设置请求头
        for (Map.Entry<String, Object> param : headers.entrySet()) {
            httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
        }
        return doHttp(httpGet);
    }
 
    /**
     * 发送 HTTP POST请求,不带请求参数和请求头
     *
     * @param url 请求地址
     * @return
     * @throws Exception
     */
    public static String doPost(String url) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        return doHttp(httpPost);
    }
 
    /**
     * 发送 HTTP POST请求,带请求参数,不带请求头
     *
     * @param url    请求地址
     * @param params 请求参数
     * @return
     * @throws Exception
     */
    public static String doPost(String url, Map<String, Object> params) throws Exception {
        // 转换请求参数
        List<NameValuePair> pairs = covertParamsToList(params);
        HttpPost httpPost = new HttpPost(url);
        // 设置请求参数
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));
 
        return doHttp(httpPost);
    }
 
    /**
     * 发送 HTTP POST请求,带请求参数和请求头
     *
     * @param url     地址
     * @param headers 请求头
     * @param params  参数
     * @return
     * @throws Exception
     */
    public static String doPost(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
        // 转换请求参数
        List<NameValuePair> pairs = covertParamsToList(params);
        HttpPost httpPost = new HttpPost(url);
        // 设置请求参数
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));
        // 设置请求头
        for (Map.Entry<String, Object> param : headers.entrySet()) {
            httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
        }
        return doHttp(httpPost);
    }
 
    /**
     * 发送 HTTP POST请求,请求参数是JSON格式,数据编码是UTF-8
     *
     * @param url 请求地址
     * @param param 请求参数
     * @return
     * @throws Exception
     */
    public static String doPostJson(String url, String param) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        // 设置请求头
        httpPost.addHeader("Content-Type", "application/json; charset=UTF-8");
        // 设置请求参数
        httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
        return doHttp(httpPost);
    }
 
    /**
     * 发送 HTTP POST请求,请求参数是XML格式,数据编码是UTF-8
     *
     * @param url 请求地址
     * @param param 请求参数
     * @return
     * @throws Exception
     */
    public static String doPostXml(String url, String param) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        // 设置请求头
        httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8");
        // 设置请求参数
        httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
 
        return doHttp(httpPost);
    }
 
    /**
     * 发送 HTTPS POST请求,使用指定的证书文件及密码,不带请求头信息<
     *
     * @param url 请求地址
     * @param param 请求参数
     * @param path 证书全路径
     * @param password 证书密码
     * @return
     * @throws Exception
     * @throws Exception
     */
    public static String doHttpsPost(String url, String param, String path, String password) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        // 设置请求参数
        httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
 
        return doHttps(httpPost, path, password);
    }
 
    /**
     * 发送 HTTPS POST请求,使用指定的证书文件及密码,请求头为“application/xml;charset=UTF-8”
     *
     * @param url 请求地址
     * @param param 请求参数
     * @param path 证书全路径
     * @param password 证书密码
     * @return
     * @throws Exception
     * @throws Exception
     */
    public static String doHttpsPostXml(String url, String param, String path, String password) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        // 设置请求头
        httpPost.addHeader("Content-Type", "application/xml; charset=UTF-8");
        // 设置请求参数
        httpPost.setEntity(new StringEntity(param, StandardCharsets.UTF_8.name()));
        return doHttps(httpPost, path, password);
    }
 
    /**
     * 发送 HTTPS 请求,使用指定的证书文件及密码
     *
     * @param request
     * @param path 证书全路径
     * @param password 证书密码
     * @return
     * @throws Exception
     * @throws Exception
     */
    private static String doHttps(HttpRequestBase request, String path, String password) throws Exception {
        // 获取HTTPS SSL证书
        SSLConnectionSocketFactory csf = getHttpsFactory(path, password);
        // 通过连接池获取连接对象
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
        return doRequest(httpClient, request);
    }
 
    /**
     * 获取HTTPS SSL连接工厂,使用指定的证书文件及密码
     *
     * @param path     证书全路径
     * @param password 证书密码
     * @return
     * @throws Exception
     * @throws Exception
     */
    private static SSLConnectionSocketFactory getHttpsFactory(String path, String password) throws Exception {
 
        // 初始化证书,指定证书类型为“PKCS12”
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 读取指定路径的证书
        FileInputStream input = new FileInputStream(new File(path));
        try {
            // 装载读取到的证书,并指定证书密码
            keyStore.load(input, password.toCharArray());
        } finally {
            input.close();
        }
        // 获取HTTPS SSL证书连接上下文
        SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).build();
        // 获取HTTPS连接工厂,指定TSL版本
        SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        return sslCsf;
    }
    /**
     * 发送 HTTP 请求
     *
     * @param request
     * @return
     * @throws Exception
     */
    private static String doHttp(HttpRequestBase request) throws Exception {
        // 通过连接池获取连接对象
        return doRequest(httpClient, request);
    }
 
    /**
     * 处理Http/Https请求,并返回请求结果,默认请求编码方式:UTF-8
     * @param httpClient
     * @param request
     * @return
     */
    private static String doRequest(CloseableHttpClient httpClient, HttpRequestBase request) throws Exception {
        String result = null;
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            // 获取请求结果
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != 200) {
                request.abort();
                throw new RuntimeException("HttpClient error status code: " + statusCode);
            }
            // 解析请求结果
            HttpEntity entity = response.getEntity();
            // 转换结果
            result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());
            // 关闭IO流
            EntityUtils.consume(entity);
        }
        return result;
    }
 
    /**
     * 转换请求参数,将Map键值对拼接成QueryString字符串
     *
     * @param params
     * @return
     */
    public static String covertMapToQueryStr(Map<String, Object> params) {
        List<NameValuePair> pairs = covertParamsToList(params);
        return URLEncodedUtils.format(pairs, StandardCharsets.UTF_8.name());
    }
 
    /**
     * 转换请求参数
     *
     * @param params
     * @return
     */
    public static List<NameValuePair> covertParamsToList(Map<String, Object> params) {
        List<NameValuePair> pairs = new ArrayList<>();
        for (Map.Entry<String, Object> param : params.entrySet()) {
            pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
        }
        return pairs;
    }
}

然后controller实现一个接口,前端调用该接口获取二维码url:

// 获取二维码
@GetMapping("/qr/login/param")
@ResponseBody
public BaseResponse<String> getWxQRCodeParam() {
    String QRUrl = null;
    try {
        // 第一步:发送请求获取access_token
        String getAccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
                "&appid=" + WXConstant.APP_ID +
                "&secret=" + WXConstant.APP_SECRET;
        String accessTokenRes = HttpClientUtils.doGet(getAccessTokenUrl);
        log.info("accessTokenRes=>" + accessTokenRes);
        String accessToken = (String) JSON.parseObject(accessTokenRes).get("access_token"); // 获取到access_token

        // 第二步:通过access_token和一些参数发送post请求获取二维码Ticket
        String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;
        // 封装参数
        Map<String, Object> ticketInfo = new HashMap<>();
        ticketInfo.put("expire_seconds", 604800); // 二维码超时时间
        ticketInfo.put("action_name", "QR_SCENE");
        String ticketJsonInfo = JSON.toJSON(ticketInfo).toString();
        String ticketRes = HttpClientUtils.doPostJson(getTicketUrl, ticketJsonInfo);
        log.info("ticketRes=>" + ticketRes);
        String ticket = (String) JSON.parseObject(ticketRes).get("ticket");

        // 第三步:通过ticket获取二维码url
        String encodeTicket = URLEncoder.encode(ticket, "utf-8"); // 编码ticket
        String getQRUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + encodeTicket;
        QRUrl = getQRUrl; // 二维码url
    } catch (Exception e) {
        e.printStackTrace();
    }
    return ResultUtils.success(QRUrl);
}

注:该接口只注重了功能实现,具体安全性并未考虑;

可以调用该接口测试一下:

image-20220922234115109

那么前端只需要获取到这个url并展示出来就行了:

image-20220922234223728

image-20220922234237697

可以测试一下:

GIF

可以看到每次都能获取到不同二维码;


到这里二维码生成就完成了,可以扫码测试一下:
在这里插入图片描述

扫码候就是公众号界面了,接下来就是一些授权操作了,下集在讲;

总结

总的来说只要细心看文档还不算是特别难理解的,所以一定要多看文档!

总的来说流程就是那三步:

image-20220922235213262

因为文章是本人理解记录的,可能会有错误,有错误问题欢迎交流!

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

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

相关文章

SAP 从入门到放弃系列之工作中心(workcenter)

目录 概念 数据收集 主要配置点&#xff1a; 工作中心类别 工作中心字段选择 工作中心公式 标准值码 工作中心位置 工序控制码 概念 工作中心是为制造过程增加价值的一台机器或一组机器、一个人或一组人&#xff0c;或一组人和机器。 数据收集 在 实施项目期间&#x…

汉诺塔问题(解出来了带你看洛丽塔)

&#x1f929;本文作者&#xff1a;大家好&#xff0c;我是paperjie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;内容专栏&#xff1a;这里是《算法详解》&#xff0c;笔者用重金(时间和精力)打造&#xff0c;将算法知识一网打尽&#xff0c;希望可以…

[230508]托福听力真题TPO66|词汇总结| 精听|19:30~20:10 20:50~23:00 8:30~9:40

TPO66 L1&#xff1a; 3/6 TPO66 L2&#xff1a; TPO66 L3&#xff1a; 目录 TPO66 C1 一、要点 二、文本 三、文本注释 四、复听笔记 TPO66 C2 一、要点 二、文本 三、文本注释 四、复听笔记 TPO66 L1 重点词汇 TPO66 L2 重点词汇 TPO66 L3 重点词汇 TPO66 C1 一、要点 …

大家都去荷兰注册公司到底是为了什么?

荷兰位于欧洲大陆西北部&#xff0c;北濒北海&#xff0c;与德国和比利时接壤。国土总面积为41528平方公里&#xff0c; 2020年2月人口达1741万人。荷兰是中国企业海外投资的重要目的地国之一。目前&#xff0c;中国是荷兰至二大外资项目来源国。据荷方统计&#xff0c;目前&am…

用 ChatGPT 尝试 JavaScript 交互式学习体验,有用但不完美

很好&#xff0c;但还不能取代专家导师&#xff0c;有时还会犯错&#xff01; ChatGPT 教小狗编程&#xff08; Midjourney 创作&#xff09; GPT-4刚刚发布&#xff0c;相较于GPT-3.5&#xff0c;它有显著的增强功能。其中之一是它在更长时间的交互和更大的提示下&#xff0c;…

Java程序设计入门教程--数组

目录 一、一维数组创建 1. 定义数组 &#xff08;1&#xff09;声明 &#xff08;2&#xff09;分配数组内存空间 2. 数组初始化 &#xff08;1&#xff09;静态初始化 &#xff08;2&#xff09;动态初始化 3. 一维数组的拷贝 &#xff08;1&#xff09;数组元素的拷…

家用洗地机哪种好?怎么选购家用洗地机

在这个高度关注环境、健康和安全的时代&#xff0c;洗地机已成为多种设施不可或缺的清洁工具。它不仅可以提高清洁速度和质量&#xff0c;还可以协助您提高生产效率和管理流程。洗地机的优点在于它易于使用&#xff0c;具有多种高效能力和灵活性&#xff0c;是一个为现代清洁服…

(C语言版)力扣(LeetCode)+牛客网(nowcoder)链表相关面试题OJ题解析

链表面试题 203. 移除链表元素题目解法一&#xff1a;递归解法二&#xff1a;迭代 206. 反转链表题目解法一&#xff1a;递归解法二&#xff1a;迭代 876. 链表的中间结点题目解法一&#xff1a;快慢指针法解法二&#xff1a;单指针法 链表中倒数第k个结点题目解法 21. 合并两个…

22.碳交易机制下考虑需求响应的综合能源系统优化运行

说明书 MATLAB代码&#xff1a;碳交易机制下考虑需求响应的综合能源系统优化运行 注意&#xff1a;另外还有含义柔性负荷、蓄冷式空调、共享储能以及碳捕集的综合能源系统优化运行代码&#xff0c;欢迎咨询 关键词&#xff1a;需求响应 碳交易机制 日前优化调度 综合能源系统…

pdf怎么分割成一页一页的文件?

pdf怎么分割成一页一页的文件&#xff1f;相信很多使用电脑办公的小伙伴们都知道&#xff0c;无论是pdf文件还是ppt文件其都是由很多页组成的。一般当我们需要其中的部分内容时候&#xff0c;可以通过手动的方式将ppt文件来拆分成一页一页的来使用。但是这种手动的方法对于pdf文…

数据库中数据的操作(进阶)

数据库中数据的约束 键值约束 primary key唯一性约束 unique key非空约束 not null默认值 default val扩展属性&#xff08;auto_increment&#xff09;外键约束 foreign keycheck 子句约束 键值约束 primary key 主键约束&#xff1a;primary key 约束指定字段中的值&#xff…

虹科干货 | 零售业数智升级不掉队,get数据,get未来!

电商崛起&#xff0c;传统零售行业危机四伏&#xff0c;全渠道盈利与可持续化成为难点&#xff0c;库存管理这块难啃的“硬骨头”也同样让零售商倍感压力… 背腹受敌的零售商&#xff0c;如何才能在数字化转型道路上避免利润缩水&#xff0c;与供应商协作共赢&#xff0c;摆脱困…

【Mac教学】如何打开macOS 的最大权限

相信有不少用户都知道&#xff0c;目前苹果的Mac 电脑中&#xff0c;有不少功能为了安全问题&#xff0c;设立了多项安全措施&#xff0c;当中有一些需要安装第三方的软件&#xff0c;但因为缺少了苹果认证&#xff0c;而无法使用或安装&#xff0c;因此为各位讲解一个设定&…

汇编语言-复习自用

本文用于自我复习汇编语言&#xff0c;参考b站一位老师的讲解整理而成&#xff0c;感谢老师的无私付出视频链接链接 文章目录 1.第一章1.1计算机组成1.2读取1.3 寄存器及数据存储1.4 mov和and指令1.5 确定物理地址1.6 内存分段表示法1.7debug使用1.8CS:IP1.9jmp指令改变csip1.1…

Git教程(一)

1、Git概述 1.1 、Git历史 同生活中的许多伟大事件一样&#xff0c;Git 诞生于一个极富纷争大举创新的年代。Linux 内核开源项目有着为数众广的参与者。绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上&#xff08;1991&#xff0d;2002年间&#xff09;…

ThingsBoard使用jar包进行初始化数据库

1、概述 ThingsBoard的官方虽然提供了直接使用他们的官方镜像来部署,但是根据我了解到的一些信息,目前国内几乎都是基于ThingsBoard进行二开,都需要拉取ThingsBoard的源代码,然后自己进行修改,然后部署自己修改后的代码,在这里我就不说如何进行本地编译了,目前网上有很…

SpringBoot的依赖管理和自动配置

目录 依赖管理自动配置 依赖管理 1.父项目做依赖管理 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version> </parent>他的父项目 &…

如何远程访问本地jupyter notebook服务器,实现无公网IP端口映射

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 转载自远控源码文章&#xff1a;公网远程访问jupyter notebook【cpolar内网穿透】 前言 Jupyter Notebook&am…

【数据结构】插入排序

插入排序 1. 排序2.插入排序2.1直接插入排序2.2折半插入法2.3希尔排序 1. 排序 排序的概念 排序就是将一组杂乱无章的数据按一定规律&#xff08;顺序或者逆序&#xff09;排列起来。 排序的目的 方便查找元素。 内部排序和外部排序 若待排序记录都在内存中&#xff0c;称为内…

ASEMI代理ADI亚德诺LTC6992IS6-1#TRMPBF车规级芯片

编辑-Z LTC6992IS6-1#TRMPBF参数描述&#xff1a; 型号&#xff1a;LTC6992IS6-1#TRMPBF 输出频率&#xff1a;3.81Hz 工作电源电压范围&#xff1a;2.25 - 5.5V 通电复位电压&#xff1a;1.95V 电源电流&#xff1a;105-365A SET引脚处的电压&#xff1a;1V 频率设置电…