微信支付 二维码生成 域名穿透 封装 如何实现 看这篇就够了!

2023-12-17

微信支付

扫码支付

微信扫码支付介绍

具体参考 官方文档

微信扫码支付流程

总结我们接下来要做的事情

  1. 调用下单接口,获取code_url
  2. 根据code_url生成二维码
  3. 查询订单的支付状态

统一下单

核心对象创建

1.添加依赖

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.9</version>
</dependency>

2.初始化httpclient对象、加载商户私钥、加载平台证书

@Before
public void setup() throws IOException {
    // 加载商户私钥(privateKey:私钥字符串)
    PrivateKey merchantPrivateKey = PemUtil
            .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
 
    // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
    AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
            new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8"));
 
    // 初始化httpClient
    httpClient = WechatPayHttpClientBuilder.create()
            .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
            .withValidator(new WechatPay2Validator(verifier)).build();
}
 
@After
public void after() throws IOException {
    httpClient.close();
}

3.定义商户私钥等参数

    private String mchId = "xxx"; //商户号
    //private String appId = "wx6592a2db3f85ed25"; //应用号
    private String appId = "xxx"; //应用号
    private String privateKey = "xx" +
            "-----END PRIVATE KEY-----\n"; //私钥字符串
    private String mchSerialNo = "xxx"; //商户证书序列号
    private String apiV3Key = "xxx"; //V3密钥
native下单接口代码

1.定义参数实体

金额实体类:

package com.itheima.pojo;

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class Amount {
    private Integer total;
    private String currency;
}

请求参数实体类:

package com.itheima.pojo;

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class NativePayParams {

    private String appid; // 应用id
    private String mchid;  // 商户id
    private String description; //商品描述
    private String out_trade_no; //订单号
    private String notify_url; // 支付成功回调通知地址
    private Amount amount; //订单金额信息
}

2.修改官方提供的代码如下:

@Test
public void testNativePay() throws Exception{
    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
    // 请求body参数

    Amount amount = Amount.builder().currency("CNY").total(1).build();

    NativePayParams nativePayParams = NativePayParams.builder().appid(appId)
            .description("百世可乐")
            .mchid(mchId)
            .notify_url("http://localhost:8080")
            .out_trade_no("AEFA234DAF342342FRADFAS1")
            .amount(amount)
            .build();


    String reqdata = JSON.toJSONString(nativePayParams);
    StringEntity entity = new StringEntity(reqdata,"utf-8");
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    httpPost.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpPost);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }


}
测试

二维码

二维码生成

直接拷贝资料/qrcodejs中的index.html和jquery.min.js和qrcode.js文件到工程的resource/static目录下,如下图所示:

二维码扫码测试

直接打开index.html文件,然后将微信支付的地址粘贴到数据框中,按下enter键,生成新的二维码图片,如下图所示:

打开手机微信进行扫码测试

支付成功通知

微信主动通知

详细查阅微信 官方文档

核心思路分析

  1. 编写接口,接受微信的支付成功通知
  2. 支付通知的内容需要解密
  3. 本地测试的ip不是公网ip,外部无法访问,需要域名穿透
  4. 微信主动通知的地址是通过下单接口中的请求参数“notify_url”来设置的 ,要求必须为https地址 。
代码实现

根据微信官方的接口文档定义实体,接收参数

package com.itheima.pojo;

import lombok.Data;

@Data
public class ResourceDto {

    private String algorithm;
    private String ciphertext;
    private String associated_data;
    private String original_type;
    private String nonce;

}
package com.itheima.pojo;

import lombok.Data;

@Data
public class NotifyDto {

    private String id;
    private String create_time;
    private String event_type;
    private String resource_type;
    private ResourceDto resource;
    private String summary;
}

定义接口:

package com.itheima.controller;

import com.itheima.pojo.NotifyDto;
import com.itheima.service.NativePayService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
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 javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/native")
@Log4j2
public class NativePayController {


    @Autowired
    private NativePayService nativePayService;

    @PostMapping("/notify")
    public Map<String,String> nativeNotify(@RequestBody NotifyDto dto){
        System.out.println("--微信回调--");
        Map<String ,String> map = null;
        try {
            nativePayService.notify(dto);
        } catch (Exception e) {
            e.printStackTrace();
            map = new HashMap<>();
            map.put("code","FAIL");
            map.put("message","失败");
        }

        return map;
    }
}

service

此处的解密参考 官方文档

package com.itheima.service.impl;

import com.alibaba.fastjson2.JSON;
import com.itheima.pojo.NotifyDto;
import com.itheima.service.NativePayService;
import com.itheima.utils.AesUtil;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Map;


@Service
public class NativePayServiceImpl implements NativePayService {

    private String apiV3Key ="CZBK51236435wxpay435434323FFDuv3";

    @Override
    public void notify(NotifyDto dto) {


        String jsonStr = null;
        try {
            jsonStr = new AesUtil(apiV3Key.getBytes())
                    .decryptToString(dto.getResource().getAssociated_data().getBytes(),
                            dto.getResource().getNonce().getBytes(),
                            dto.getResource().getCiphertext());
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Map map = JSON.parseObject(jsonStr, Map.class);
        String outTradeNo = map.get("out_trade_no").toString();

        System.out.println("订单"+outTradeNo+"支付成功");
    }
}
域名穿透

本地电脑的ip是局域网ip,外界无法访问。此处我们只能使用域名穿透的一些软件。此处个人使用的是花生壳

然后再下单的代码中修改notify_url的地址为 域名穿透地址/资源路径 ,代码如下:

测试

此时只需要再次支付即可,对比解析后的订单号和支付的订单号:

主动查询支付结果

微信官方不保证支付通知的调用是正确的,所以我们必须加上双保险,可以主动去查询支付的结果,参考 官方文档

参考下单的api和接口文档,编写测试代码如下:

@Test
public void checkPayStatus() throws Exception{
    String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/1AEFA234DAF442342FRADFAS12?mchid=1561414331";
    HttpGet httpGet = new HttpGet(url);
    httpGet.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpGet);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }
}

将请求的结果通过三方json解析工具解析:得到如下结果:

提问:什么时候去查询订单的支付状态呢?

退款

api介绍

详细参考 官方文档

代码实现

实体对象

package com.itheima.pojo;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class RefundAmount {
    private int refund;// 退款金额
    private int total; //原支付交易的订单总金额

    private String currency;//

}
package com.itheima.pojo;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class RefundParams {



    private String out_trade_no;  //支付交易对应的商户订单号
    private String out_refund_no; //商户系统内部的退款单号
    private RefundAmount amount;

}

接口代码:

@Test
public void refund() throws Exception{
    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
    // 请求body参数

    RefundAmount refundAmount = RefundAmount.builder()
                                .currency("CNY")
                                .refund(1)
                                .total(1)
                                .build();

    RefundParams refundParams = RefundParams.builder()
                                .out_refund_no("AAAAAEWRTWERWE23432423")
                                .amount(refundAmount)
                                .out_trade_no("1AEFA234DAF442342FRADFAS12")
                                .build();


    String reqdata = JSON.toJSONString(refundParams);
    StringEntity entity = new StringEntity(reqdata,"utf-8");
    entity.setContentType("application/json");
    httpPost.setEntity(entity);
    httpPost.setHeader("Accept", "application/json");

    //完成签名并执行请求
    CloseableHttpResponse response = httpClient.execute(httpPost);

    try {
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) { //处理成功
            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
        } else if (statusCode == 204) { //处理成功,无返回Body
            System.out.println("success");
        } else {
            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
            throw new IOException("request failed");
        }
    } finally {
        response.close();
    }

}

测试

将结果使用三方json工具解析:如下图所示

然后再调用上面的查单,查询结果

springBoot的starter封装

springBoot自动化配置原理

基于启动类上的注解@SpringBootApplication中的@EnableAutoConfiguration来开启自动化配置,这个注解通过@Import来导入了配置类,如下图所示:

上述配置类会加载所有jar包以及源码下的META-INF下得spring.factories文件,如下图所示:

然后会加载配置中指定的自动化配置key所对应的所有自动化配置类,示例如下图所示:

但是自动化配置类并不是都需要加载的,springBoot通过自动化配置类上添加的各种条件注解@Conditional来判断这个自动化配置类是否满足条件,如果满足条件,即加载配置类,如下图webmvc自动化配置类示例:

微信扫码支付starter封装

流程分析--逆向思维

我们最终需要能够实现的目的 -->引入依赖,yaml中做好配置,直接依赖注入xxxTemplate能够直接使用

--->需要创建一个xxxTemplate对象---> 提供一个xxxTemplate类,封装扫码支付的api,一些环境级别的参数做成配置

--> 提供xxxProperties配置类,然后加载yaml中的配置

最终实现步骤整理

0.创建工程,引入相关依赖

  1. 提供WxPayProperties配置类,用于加载application.yaml中的环境配置
  2. 提供WxPayTemplate类,封装扫码支付的api,环境配置依赖注入得到
  3. 提供配置类,启动WxPayProperties类对象,创建WxPayTemplate对象
  4. 提供META-INF/spring.factory配置类,配置自动化配置
  5. 创建新测试工程,测试starter使用

代码实现

0.创建工程,引入相关依赖

<dependencies>
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.9</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
    </dependencies>
  1. 提供WxPayProperties配置类,用于加载application.yaml中的环境配置
package com.heima.wxpay.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@ConfigurationProperties(prefix = "wxpay")
public class WxPayProperties {

    private String mchId = "1561414331"; //商户号
    //private String appId = "wx6592a2db3f85ed25"; //应用号
    private String appId = "wxffb3637a228223b8"; //应用号
    private String privateKey = "-----BEGIN PRIVATE KEY-----\n" +
            "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBHGgIh80193Gh\n" +
            "dpD1LtMZfTRpcWI0fImyuBCyrd3gYb3rrsARebGcHdJsQA3mVjVqVp5ybhEZDPa4\n" +
            "ecoK4Ye1hTppNpI/lmLt4/uUV/zhF5ahli7hi+116Ty6svHSbuMQBuUZeTFOwGrx\n" +
            "jvofU/4pGIwh8ZvkcSnyOp9uX2177UVxDBkhgbZbJp9XF2b83vUa5eHo93CziPzn\n" +
            "3hFdAlBCdTXB7DH+m0nN3Jou0szGukvq7cIgGpHku4ycKSTkIhhl9WRhN6OoSEJx\n" +
            "q88MXzjkzTruc85PHN52aUTUifwg3T8Y4XqFQ61dTnEmgxeD2O6/pLdB9gLsp6yC\n" +
            "GqN5Lqk7AgMBAAECggEBAL4X+WzUSbSjFS9NKNrCMjm4H1zgqTxjj6TnPkC1mGEl\n" +
            "tjAHwLgzJBw62wWGdGhWWpSIGccpBBm1wjTMZpAZfF66fEpP1t1Ta6UjtGZNyvfF\n" +
            "IZmE3jdWZ/WXGBnsxtFQKKKBNwrBW0Fbdqq9BQjLxLitmlxbmwrgPttcy855j6vZ\n" +
            "qq4MBT1v8CtUT/gz4UWW2xWovVnmWOrRSScv7Nh0pMbRpPLkNHXrBwSSNz/keORz\n" +
            "XB9JSm85wlkafa7n5/IJbdTml3A/uAgW3q3JZZQotHxQsYvD4Zb5Cnc9CPAXE5L2\n" +
            "Yk877kVXZMGt5QPIVcPMj/72AMtaJT67Y0fN0RYHEGkCgYEA38BIGDY6pePgPbxB\n" +
            "7N/l6Df0/OKPP0u8mqR4Q0aQD3VxeGiZUN1uWXEFKsKwlOxLfIFIFk1/6zQeC0xe\n" +
            "tNTKk0gTL8hpMUTNkE7vI9gFWws2LY6DE86Lm0bdFEIwh6d7Fr7zZtyQKPzMsesC\n" +
            "3XV9sdSUExEi5o/VwAyf+xZlOXcCgYEA3PGZYlILjg3esPNkhDz2wxFw432i8l/B\n" +
            "CPD8ZtqIV9eguu4fVtFYcUVfawBb0T11RamJkc4eiSOqayC+2ehgb+GyRLJNK4Fq\n" +
            "bFcsIT+CK0HlscZw51jrMR0MxTc4RzuOIMoYDeZqeGB6/YnNyG4pw2sD8bIwHm84\n" +
            "06gtJsX/v10CgYAo8g3/aEUZQHcztPS3fU2cTkkl0ev24Ew2XGypmwsX2R0XtMSB\n" +
            "uNPNyFHyvkgEKK2zrhDcC/ihuRraZHJcUyhzBViFgP5HBtk7VEaM36YzP/z9Hzw7\n" +
            "bqu7kZ85atdoq6xpwC3Yn/o9le17jY8rqamD1mv2hUdGvAGYsHbCQxnpBwKBgHTk\n" +
            "eaMUBzr7yZLS4p435tHje1dQVBJpaKaDYPZFrhbTZR0g+IGlNmaPLmFdCjbUjiPy\n" +
            "A2+Znnwt227cHz0IfWUUAo3ny3419QkmwZlBkWuzbIO2mms7lwsf9G6uvV6qepKM\n" +
            "eVd5TWEsokVbT/03k27pQmfwPxcK/wS0GFdIL/udAoGAOYdDqY5/aadWCyhzTGI6\n" +
            "qXPLvC+fsJBPhK2RXyc+jYV0KmrEv4ewxlK5NksuFsNkyB7wlI1oMCa/xB3T/2vT\n" +
            "BALgGFPi8BJqceUjtnTYtI4R2JIVEl08RtEJwyU5JZ2rvWcilsotVZYwfuLZ9Kfd\n" +
            "hkTrgNxlp/KKkr+UuKce4Vs=\n" +
            "-----END PRIVATE KEY-----\n"; //私钥字符串
    private String mchSerialNo = "25FBDE3EFD31B03A4377EB9A4A47C517969E6620"; //商户证书序列号
    private String apiV3Key = "CZBK51236435wxpay435434323FFDuv3"; //V3密钥

}
  1. 提供WxPayTemplate类,封装扫码支付的api,环境配置依赖注入得到

2个参数实体类:

package com.heima.wxpay.dto;

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class NativePayParams {

    private String appid; // 应用id
    private String mchid;  // 商户id
    private String description; //商品描述
    private String out_trade_no; //订单号
    private String notify_url; // 支付成功回调通知地址
    private Amount amount; //订单金额信息
}
package com.heima.wxpay.dto;

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class Amount {
    private Integer total;
    private String currency;
}

工具类:

package com.heima.wxpay.config;

import com.alibaba.fastjson.JSON;
import com.heima.wxpay.dto.Amount;
import com.heima.wxpay.dto.NativePayParams;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.Map;

public class WxPayTemplate {

    private WxPayProperties wxPayProperties;

    private CloseableHttpClient httpClient;

    public WxPayTemplate(WxPayProperties wxPayProperties,CloseableHttpClient httpClient) {
        this.wxPayProperties = wxPayProperties;
        this.httpClient = httpClient;
    }

    public String nativePay(Integer total, String description, String outTradeNo) throws Exception{
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
        // 请求body参数

        Amount amount = Amount.builder().currency("CNY").total(total).build();

        NativePayParams nativePayParams = NativePayParams.builder().appid(wxPayProperties.getAppId())
                .description(description)
                .mchid(wxPayProperties.getMchId())
                .notify_url("https://36d5634033.vicp.fun/native/notify")
                .out_trade_no(outTradeNo)
                .amount(amount)
                .build();


        String reqdata = JSON.toJSONString(nativePayParams);
        StringEntity entity = new StringEntity(reqdata,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);

        String url = "";
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) { //处理成功
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
                url = JSON.parseObject( EntityUtils.toString(response.getEntity()), Map.class).get("code_url").toString();
            } else if (statusCode == 204) { //处理成功,无返回Body
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
        }
        return url;
    }
}
  1. 提供配置类,启动WxPayProperties类对象,创建WxPayTemplate对象
package com.heima.wxpay.config;

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.ByteArrayInputStream;
import java.security.PrivateKey;

@Configuration
@EnableConfigurationProperties(WxPayProperties.class)
public class WxPayAutoConfig {


    @Bean
    public CloseableHttpClient httpClient(WxPayProperties wxPayProperties) throws Exception{
        // 加载商户私钥(privateKey:私钥字符串)
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(wxPayProperties.getPrivateKey().getBytes("utf-8")));

        // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(wxPayProperties.getMchId(), new PrivateKeySigner(wxPayProperties.getMchSerialNo(), merchantPrivateKey)),wxPayProperties.getApiV3Key().getBytes("utf-8"));

        // 初始化httpClient
        return WechatPayHttpClientBuilder.create()
                .withMerchant(wxPayProperties.getMchId(), wxPayProperties.getMchSerialNo(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();
    }

    @Bean
    public WxPayTemplate wxPayTemplate(WxPayProperties wxPayProperties,CloseableHttpClient httpClient){

        return new WxPayTemplate(wxPayProperties,httpClient);
    }

}
  1. 提供META-INF/spring.factory配置类,配置自动化配置

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.heima.wxpay.config.WxPayAutoConfig
  1. 创建新测试工程,测试starter使用

导入starter依赖:

<dependency>
    <groupId>com.heima</groupId>
    <artifactId>wx_pay_starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

直接springBoot测试

package com.itheima.wxpay;


import com.heima.wxpay.config.WxPayTemplate;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class StarterTest {

    @Autowired
    private WxPayTemplate wxPayTemplate;

    @Test
    public void testNativePay() throws Exception{

        String url = wxPayTemplate.nativePay(1, "javaEE企业级开发", "ADFADSFS4353534");
        System.out.println(url);
    }
}

非常感谢您阅读到这里,创作不易!如果这篇文章对您有帮助,希望能留下您的点赞 ???? 关注 ???? 收藏 ???? 评论 ???? 感谢支持!!!

听说 三连能够给人 带来好运! 更有可能年入百w,进入大厂,上岸

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

微信支付 二维码生成 域名穿透 封装 如何实现 看这篇就够了! 的相关文章

随机推荐

  • 【数据结构】双链表的定义和操作

    目录 1 双链表的定义 2 双链表的创建和初始化 3 双链表的插入节点操作 4 双链表的删除节点操作 5 双链表的查找节点操作 6 双链表的更新节点操作 7 完整代码 嗨 我是 Filotimo 很高兴与大家相识 希望我的博客能对你有所帮助
  • 谷歌浏览器导出非http无法直接打开

    解决方法 1 打开谷歌浏览器右上角三个点 点击设置前往隐私和安全菜单 2 打开隐私和安全点击网站设置 网站设置拉至页面最下方 展开更多设置点击不安全内容页面 不安全内容页面最下方添加网站网址即可
  • 【数据结构】单链表的定义和操作

    目录 1 单链表的定义 2 单链表的创建和初始化 3 单链表的插入节点操作 4 单链表的删除节点操作 5 单链表的查找节点操作 6 单链表的更新节点操作 7 完整代码 嗨 我是 Filotimo 很高兴与大家相识 希望我的博客能对你有所帮助
  • 基于ssm足球联赛管理系统

    摘 要 随着计算机技术发展 计算机系统的应用已延伸到社会的各个领域 大量基于网络的广泛应用给生活带来了十分的便利 所以把足球联赛管理与现在网络相结合 利用计算机搭建足球联赛管理系统 实现足球联赛管理系统的信息化 则对于进一步提高足球联赛管理
  • WOA-BILSTM-Attention基于鲸鱼算法优化双向长短期记忆网络结合注意力机制回归预测,多变量输入模型

    文章目录 效果一览 文章概述 订阅专栏只能获取专栏内一份代码 部分源码 参考资料
  • arm Linux中dma的cache管理

    概述 前两周有人询问DMA下的cache操作和dma coherent 以前零碎看过代码 临时找 还没有找到 这两天整理了调用流程 也找到了dma coherent的用法 Linux的文档里没有详细说明dma coherent的用法 根据代
  • 设计模式—迭代器模式解析

    本文参考学习了 图解设计模式 中的代码实现和原理解释 迭代器模式 简介 Iterator 模式用于在数据集合中按照顺序遍历集合 就类似于我们的循环 一个个去遍历一个集合中的所有元素 示例代码 首先我们思考一个书本和书架的关系 显然 书架可以
  • linux面试题分享

    1 简述RAID0 1 5 10的区别 什么是LVM RAID与LVM的使用场景 RAID0需要两块以上硬盘 不提供冗余或错误修复能力 速度快 磁盘利用率100 RAID1需要两块以上硬盘 提供数据块冗余 磁盘利用率50 RAID5需要三块
  • 应对压力:14个方法助你有效缓解

    在快节奏的现代生活中 我们常常会感受到各种各样的压力 这些压力可能来自于工作 学习 人际关系 甚至是生活中的一些琐事 过度的压力会对我们的身心健康造成严重的影响 因此学会有效地缓解压力变得尤为重要 本文将介绍14个简单易行的方法 帮助你应对
  • 嵌入式开发、C++后端开发、C++音视频开发怎么选择?

    嵌入式开发 C 后端开发和C 音视频开发的选择问题 近年来 随着互联网和物联网的快速发展 嵌入式开发 C 后端开发和C 音视频开发等职业领域也逐渐受到广泛关注 对于有志于从事这些领域的人来说 如何做出选择呢 下面将从前景 薪酬和职业稳定性等
  • The content of element type “mapper“ must match “(cache-ref|cache|resultMap*|parameterMap*|sql*|inse

    这段话的意思是告诉你标签没有写全 比如
  • 软件测试市场复苏了!

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读1 9k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • lv12 uboot移植深化 9

    u boot 2013 0 1移植 实验目的 了解 u boot 的代码结构及移植 的基本 方法 实验环境 u buntu 14 04
  • 200.岛屿数量(bfs写法)

    宽搜的正常思路 只不过每次加上计算岛屿数量cnt即可 class Solution public int numIslands char grid int n grid length int m grid 0 length int dx n
  • 测试用例设计方法六脉神剑——第一剑:入门试招,等价边界初探

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读1 8k次 点赞82次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • WOA-BIGRU-Attention基于鲸鱼算法优化双向门控循环单元结合注意力机制回归预测,多变量输入模型

    文章目录 效果一览 文章概述 订阅专栏只能获取专栏内一份代码 部分源码 参考资料
  • lv12 交叉编译工具链 7

    目录 1 交叉编译 1 1 镜像来源 1 2 编译原理 1 3 编译过程 编辑 1 4 交叉编译 1 5 交叉编译工具链获取 2 ELF文件格式 编辑3 BIN文件格式 4 交叉编译工具链常用工具 4 1 size命令举例 4 2 rm命令
  • 算法(1)——双指针

    双指针 我们常见的双指针的形式有两种 一种是对撞指针 一种是快慢指针 对撞指针 一般用于顺序结构中 也称左右指针 1 对撞指针从两端向中间移动 一个指针从最左端开始 另一个从最右端开始 然后逐渐向中间逼近 2 对撞指针的终止条件一般是两个指
  • 使用jquery加原生html+css+javascript实现一个树形下拉

  • 微信支付 二维码生成 域名穿透 封装 如何实现 看这篇就够了!

    微信支付 扫码支付 微信扫码支付介绍 具体参考 官方文档 微信扫码支付流程 总结我们接下来要做的事情 调用下单接口 获取code url 根据code url生成二维码 查询订单的支付状态 统一下单 核心对象创建 1 添加依赖