当前位置: 首页 > news >正文

(JAVA)自建应用调用企业微信API接口,设置企业可信IP

(JAVA)自建应用调用企业微信API接口,设置企业可信IP

本人目前是一名运维、云原生人员,那为什么要用JAVA写一个接口呢?是这样的,在使用zabbix 监控服务的时候,将告警信息通过企业微信发送给自建应用用于提醒用户,但是在发送的时候提示下面错误:

[root@zabbix-server.example.com ~]#python3.12 weixin_sender.py XingYuYu  sdklj message
正在初始化并尝试获取 access_token...==================================================
成功获取到 Access Token: 
1_jkRPWEkV6ylL9m5oqUTn9Sa0HLQg1McmLFz-xfNLeLieqxn5B6Gh6TX9I110yPLAFSgFAbV4XLXy1iAeinpp-_ykvfXHSQZPGbecQYrJs0cUj6bJIN-nizsRQOT3qB_m0hk5nDaU9lrZQ_tCEywJCh4jICDKDGUSVQlkndjmycPqHWlIsKR20WEtaoacxnhnnDmecb_Q-Z-nsTwLkbRA
==================================================准备向 XingYuYu 发送消息...
错误: 发送消息失败: not allow to access from your ip, hint: [1754296456358772672706616], from ip: 223.104.209.42, more info at https://open.work.weixin.qq.com/devtool/query?e=60020
[root@zabbix-server.example.com ~]

这个的意思是需要将你所在地的ip加入到企业微信自建应用的白名单当中。但是现在企业微信加入白名单有一个前提:1.要么设置可信域名 2.要么设置接收消息服务器URL。域名需要备案,如果是自己在学习或者测试的话,这个耗费的时间周期就太长了。所以下面就是使用JAVA代码来调去企业微信的接口,这个前提是要有一台公网服务器,例如阿里云,正好本人有一台。

image-20250804163719223

但是问题来了,我是运维人员,JAVA虽然听过但是没有做过,这个很头疼,只能找Gemini、ChatGPT来,终于在一番努力之下成功了。

这里的token和EncodingAESKey随机获取,重要的URL。

image-20250804164624188

验证URL函数

企业微信开发者中心,提供了demo

加解密库下载与返回码 - 文档 - 企业微信开发者中心

image-20250804164915688

image-20250804164952394

image-20250804165303197

会JAVA开发的,看到这里就应该知道怎么做了,下面我按照小白如何把这个做出来。

纯小白完成

前提是你有IDEA,配置好了jdk1.8、配置好了Maven环境。

创建项目文件夹结构

首先,创建一个项目文件夹,例如 wechat-callback。然后,在里面创建如下的目录结构。这是标准的Maven项目结构。

wechat-callback/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── wechat/
│   │   │               ├── WechatApplication.java  (这是新的主启动类)
│   │   │               ├── controller/
│   │   │               │   └── WxVerifyController.java (这是您提供的,已优化)
│   │   │               └── util/
│   │   │                   ├── AesException.java
│   │   │                   ├── ByteGroup.java
│   │   │                   ├── PKCS7Encoder.java
│   │   │                   ├── SHA1.java
│   │   │                   ├── WXBizMsgCrypt.java
│   │   │                   └── XMLParse.java
│   │   └── resources/
│   │       └── application.properties (新的配置文件)
└── pom.xml (新的Maven配置文件)

image-20250804165859138

将代码放入正确的位置

下面提供所有优化后的代码。请将下面的代码块内容,分别复制并保存到上面结构中对应的文件里。

  • pom.xml: 直接放在项目根目录 wechat-callback/ 下。
  • application.properties: 放在 src/main/resources/ 目录下。
  • WechatApplication.java: 放在 src/main/java/com/example/wechat/ 目录下。
  • WxVerifyController.java: 放在 src/main/java/com/example/wechat/controller/ 目录下。
  • 其余所有 util 包的文件: 全部放在 src/main/java/com/example/wechat/util/ 目录下。
WechatApplication
package com.example.wechat;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** Spring Boot 主启动类* 这个注解包含了@Configuration, @EnableAutoConfiguration, @ComponentScan*/
@SpringBootApplication
public class WechatApplication {public static void main(String[] args) {SpringApplication.run(WechatApplication.class, args);System.out.println("企业微信回调服务启动成功!");}}
WxVerifyController
package com.example.wechat.controller;import com.example.wechat.util.WXBizMsgCrypt;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 企业微信验证与消息回调控制层*/
@RestController
@RequestMapping("/wx/verify") // 定义基础路径
public class WxVerifyController {// 从 application.properties 文件中注入配置@Value("${wechat.token}")private String token;@Value("${wechat.encodingAesKey}")private String encodingAesKey;@Value("${wechat.corpId}")private String corpId;/*** 处理企业微信的URL验证请求 (GET方法)* @param msg_signature 企业微信加密签名* @param timestamp 时间戳* @param nonce 随机数* @param echostr 加密的随机字符串* @return 解密后的echostr*/@GetMapping("/url") // 路径为 /wx/verify/urlpublic String verifyUrl(@RequestParam("msg_signature") String msg_signature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam("echostr") String echostr) {try {System.out.println("收到URL验证请求...");WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(token, encodingAesKey, corpId);// 验证 URLString sReplyEchoStr = wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr);System.out.println("URL验证成功, 返回: " + sReplyEchoStr);return sReplyEchoStr;} catch (Exception e) {e.printStackTrace();System.out.println("URL验证失败!");return "error";}}/*** 处理企业微信推送过来的消息 (POST方法)* @param msg_signature 签名* @param timestamp 时间戳* @param nonce 随机数* @param postData 加密的请求体* @return 固定返回 "success"*/@PostMapping("/url") // 同样使用 /wx/verify/url 路径public String receiveMessage(@RequestParam("msg_signature") String msg_signature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestBody String postData) {try {System.out.println("收到消息推送...");WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(token, encodingAesKey, corpId);String decryptedMsg = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, postData);System.out.println("解密后的消息: " + decryptedMsg);// 在这里添加您处理消息的业务逻辑// ...} catch (Exception e) {e.printStackTrace();System.out.println("消息处理失败!");}// 企业微信要求成功处理后返回 "success" 或空字符串return "success";}
}
AesException
package com.example.wechat.util;@SuppressWarnings("serial")
public class AesException extends Exception {// ... (此处省略和您提供的一样的代码)public final static int OK = 0;public final static int ValidateSignatureError = -40001;public final static int ParseXmlError = -40002;public final static int ComputeSignatureError = -40003;public final static int IllegalAesKey = -40004;public final static int ValidateCorpidError = -40005;public final static int EncryptAESError = -40006;public final static int DecryptAESError = -40007;public final static int IllegalBuffer = -40008;private int code;private static String getMessage(int code) {switch (code) {case ValidateSignatureError: return "签名验证错误";case ParseXmlError: return "xml解析失败";case ComputeSignatureError: return "sha加密生成签名失败";case IllegalAesKey: return "SymmetricKey非法";case ValidateCorpidError: return "corpid校验失败";case EncryptAESError: return "aes加密失败";case DecryptAESError: return "aes解密失败";case IllegalBuffer: return "解密后得到的buffer非法";default: return null;}}public int getCode() {return code;}AesException(int code) {super(getMessage(code));this.code = code;}
}
ByteGroup
// 文件: src/main/java/com/example/wechat/util/ByteGroup.java
package com.example.wechat.util;import java.util.ArrayList;class ByteGroup {public static PKCS7Encoder PKCS7Encoder;// ... (此处省略和您提供的一样的代码)ArrayList<Byte> byteContainer = new ArrayList<Byte>();public byte[] toBytes() {byte[] bytes = new byte[byteContainer.size()];for (int i = 0; i < byteContainer.size(); i++) {bytes[i] = byteContainer.get(i);}return bytes;}public ByteGroup addBytes(byte[] bytes) {for (byte b : bytes) {byteContainer.add(b);}return this;}public int size() {return byteContainer.size();}
}
PKCS7Encoder
// 文件: src/main/java/com/example/wechat/util/PKCS7Encoder.java
package com.example.wechat.util;import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;class PKCS7Encoder {// ... (此处省略和您提供的一样的代码)static Charset CHARSET = StandardCharsets.UTF_8;static int BLOCK_SIZE = 32;static byte[] encode(int count) {int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);if (amountToPad == 0) {amountToPad = BLOCK_SIZE;}char padChr = chr(amountToPad);String tmp = "";for (int index = 0; index < amountToPad; index++) {tmp += padChr;}return tmp.getBytes(CHARSET);}static byte[] decode(byte[] decrypted) {int pad = (int) decrypted[decrypted.length - 1];if (pad < 1 || pad > 32) {pad = 0;}return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);}static char chr(int a) {byte target = (byte) (a & 0xFF);return (char) target;}
}
SHA1
// 文件: src/main/java/com/example/wechat/util/SHA1.java
package com.example.wechat.util;import java.security.MessageDigest;
import java.util.Arrays;class SHA1 {// ... (此处省略和您提供的一样的代码)public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException {try {String[] array = new String[]{token, timestamp, nonce, encrypt};StringBuilder sb = new StringBuilder();Arrays.sort(array);for (int i = 0; i < 4; i++) {sb.append(array[i]);}String str = sb.toString();MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(str.getBytes());byte[] digest = md.digest();StringBuilder hexstr = new StringBuilder();String shaHex = "";for (int i = 0; i < digest.length; i++) {shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.ComputeSignatureError);}}
}
WXBizMsgCrypt
// 文件: src/main/java/com/example/wechat/util/WXBizMsgCrypt.java
// 注意:这个文件依赖于其他的几个util文件,请确保它们都在同一个包下
package com.example.wechat.util;import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;public class WXBizMsgCrypt {static Charset CHARSET = StandardCharsets.UTF_8;byte[] aesKey;String token;String receiveId;public WXBizMsgCrypt(String token, String encodingAesKey, String receiveId) throws AesException {if (encodingAesKey.length() != 43) {throw new AesException(AesException.IllegalAesKey);}this.token = token;this.receiveId = receiveId;aesKey = Base64.decodeBase64(encodingAesKey + "=");}byte[] getNetworkBytesOrder(int sourceNumber) {byte[] orderBytes = new byte[4];orderBytes[3] = (byte) (sourceNumber & 0xFF);orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);return orderBytes;}int recoverNetworkBytesOrder(byte[] orderBytes) {int sourceNumber = 0;for (int i = 0; i < 4; i++) {sourceNumber <<= 8;sourceNumber |= orderBytes[i] & 0xff;}return sourceNumber;}String getRandomStr() {String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuilder sb = new StringBuilder();for (int i = 0; i < 16; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}String encrypt(String randomStr, String text) throws AesException {ByteGroup byteCollector = new ByteGroup();byte[] randomStrBytes = randomStr.getBytes(CHARSET);byte[] textBytes = text.getBytes(CHARSET);byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);byte[] receiveIdBytes = receiveId.getBytes(CHARSET);byteCollector.addBytes(randomStrBytes);byteCollector.addBytes(networkBytesOrder);byteCollector.addBytes(textBytes);byteCollector.addBytes(receiveIdBytes);byte[] padBytes = ByteGroup.PKCS7Encoder.encode(byteCollector.size());byteCollector.addBytes(padBytes);byte[] unencrypted = byteCollector.toBytes();try {Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);byte[] encrypted = cipher.doFinal(unencrypted);return Base64.encodeBase64String(encrypted);} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.EncryptAESError);}}String decrypt(String text) throws AesException {byte[] original;try {Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);byte[] encrypted = Base64.decodeBase64(text);original = cipher.doFinal(encrypted);} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.DecryptAESError);}String xmlContent, from_receiveId;try {byte[] bytes = ByteGroup.PKCS7Encoder.decode(original);byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);int xmlLength = recoverNetworkBytesOrder(networkOrder);xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);from_receiveId = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET);} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.IllegalBuffer);}if (!from_receiveId.equals(receiveId)) {throw new AesException(AesException.ValidateCorpidError);}return xmlContent;}public String EncryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException {String encrypt = encrypt(getRandomStr(), replyMsg);if (timeStamp == null || timeStamp.isEmpty()) {timeStamp = Long.toString(System.currentTimeMillis());}String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);return XMLParse.generate(encrypt, signature, timeStamp, nonce);}public String DecryptMsg(String msgSignature, String timeStamp, String nonce, String postData) throws AesException {Object[] encrypt = XMLParse.extract(postData);String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());if (!signature.equals(msgSignature)) {throw new AesException(AesException.ValidateSignatureError);}return decrypt(encrypt[1].toString());}public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr) throws AesException {String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);if (!signature.equals(msgSignature)) {throw new AesException(AesException.ValidateSignatureError);}return decrypt(echoStr);}
}
XMLParse
// 文件: src/main/java/com/example/wechat/util/XMLParse.java
package com.example.wechat.util;import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;class XMLParse {// ... (此处省略和您提供的一样的代码)public static Object[] extract(String xmltext) throws AesException     {Object[] result = new Object[2];try {DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);dbf.setXIncludeAware(false);dbf.setExpandEntityReferences(false);DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(xmltext);InputSource is = new InputSource(sr);Document document = db.parse(is);Element root = document.getDocumentElement();NodeList nodelist1 = root.getElementsByTagName("Encrypt");result[0] = 0;result[1] = nodelist1.item(0).getTextContent();return result;} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.ParseXmlError);}}public static String generate(String encrypt, String signature, String timestamp, String nonce) {String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n"+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n"+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>";return String.format(format, encrypt, signature, timestamp, nonce);}
}
application.properties
# 服务器端口号,默认为8080,您可以根据需要修改
server.port=8080# 企业微信回调配置
# 请将下面的中文提示替换为自己的真实配置
wechat.corpId=企业微信ID
wechat.token=对应的token
wechat.encodingAesKey=对应的EncodingAESKey
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><!-- 继承Spring Boot的父项目,它能帮助我们管理版本号 --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.5</version> <!-- 您可以根据需要选择更新的版本 --><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>wechat-callback</artifactId><version>0.0.1-SNAPSHOT</version><name>wechat-callback</name><description>企业微信回调验证服务</description><properties><java.version>1.8</java.version></properties><dependencies><!-- Spring Boot Web 启动器,包含了构建Web应用所需的一切 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Apache Commons Codec, WXBizMsgCrypt.java 中使用到的Base64解码 --><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.15</version></dependency><!-- Spring Boot 测试启动器,用于编写测试用例 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><!-- Spring Boot Maven 插件,用于将应用打包成一个可执行的JAR --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
修改配置

打开 src/main/resources/application.properties 文件,将里面的中文提示替换成自己的企业微信后台获取的真实信息。

使用Maven打包

打开命令行工具(终端、CMD或PowerShell)。

使用 cd 命令进入到您的项目根目录 wechat-callback/

Maven会自动下载所有需要的依赖库,编译代码,然后生成一个可执行的JAR包。在项目下的 target/ 文件夹里找到它,文件名通常是 wechat-callback-0.0.1-SNAPSHOT.jar

运行以下Maven命令:

PS E:\XYY_WorkSpaces\JetBrains_WorkSpace\IDEA\wechat-callback> mvn clean package
[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------< com.example:wechat-callback >---------------------
[INFO] Building wechat-callback 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:3.2.0:clean (default-clean) @ wechat-callback ---
[INFO] 
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ wechat-callback ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ wechat-callback ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 9 source files to E:\XYY_WorkSpaces\JetBrains_WorkSpace\IDEA\wechat-callback\target\classes
[INFO] --- maven-compiler-plugin:3.10.1:testCompile (default-testCompile) @ wechat-callback ---
[INFO] Changes detected - recompiling the module!
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ wechat-callback ---
[INFO]
[INFO] --- maven-jar-plugin:3.2.2:jar (default-jar) @ wechat-callback ---
[INFO] Building jar: E:\XYY_WorkSpaces\JetBrains_WorkSpace\IDEA\wechat-callback\target\wechat-callback-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.7.5:repackage (repackage) @ wechat-callback ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.210 s
[INFO] Finished at: 2025-08-04T15:06:22+08:00
[INFO] ------------------------------------------------------------------------
PS E:\XYY_WorkSpaces\JetBrains_WorkSpace\IDEA\wechat-callback>

image-20250804171402414

在服务器上运行
java -jar wechat-callback-0.0.1-SNAPSHOT.jar

image-20250804171601465

验证成功

41278c66a2aad2816d1f6a367fab6a0

下面就可以设置白名单了

image-20250804171821582


[INFO] BUILD SUCCESS
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.210 s
[INFO] Finished at: 2025-08-04T15:06:22+08:00
[INFO] ------------------------------------------------------------------------
PS E:\XYY_WorkSpaces\JetBrains_WorkSpace\IDEA\wechat-callback>


[外链图片转存中...(img-MCjey3tJ-1754302984793)]#### 在服务器上运行```bash
java -jar wechat-callback-0.0.1-SNAPSHOT.jar

[外链图片转存中…(img-kZc2WX2C-1754302984793)]

验证成功

[外链图片转存中…(img-Q9MXIvbQ-1754302984793)]

下面就可以设置白名单了

[外链图片转存中…(img-o8WFLWDQ-1754302984793)]

image-20250804171834384

http://www.dtcms.com/a/314135.html

相关文章:

  • 开疆智能ModbusTCP转Profient网关连接ER机器人配置案例
  • DPDK中的TCP头部处理
  • 第五篇: 深入解析基于 SQLAlchemy 的聊天记录持久化模块:`message_model` 与数据库操作封装
  • 高速信号设计之 PCIe6.0 篇
  • Windows中Idea或者其他开发工具如何使用Google Sans Code - 码农开源等宽字体
  • 数据结构:如何判断一个链表中是否存在环(Check for LOOP in Linked List)
  • JSqlParser学习笔记 快速使用JSqlParser
  • 从exec到Shell:深度解析Linux进程等待,程序替换与自主Shell实现
  • 电脑一键重装系统win7/win10/win11无需U盘(无任何捆绑软件图文教程)
  • OBS 基础 21 充满某个源的策略
  • Android GPU测试
  • 电子电气架构 ---智能电动汽车嵌入式软件开发过程中的block点
  • 【Linux指南】软件安装全解析:从源码到包管理器的进阶之路
  • 移动端生产网页设计误区:工业级操作场景下的手势交互创新
  • 【Django】-3- 处理HTTP响应
  • AUTOSAR CP:深度揭秘APPL层(Application Layer)!SWC分配策略与端口交互的终极指南
  • IntelliJIDEA上传GitHub全攻略
  • 国产智能三防手机哪款最好?这款支持单北斗、5G-A、IP68
  • 进一步分析云手机的优势有哪些?
  • chatgpt plus简单得,不需要求人,不需要野卡,不需要合租,不需要昂贵的价格
  • 手机防沉迷新招:安卓手机如何成为管理iPhone的遥控器?
  • Redis 实现互斥锁解决Redis击穿
  • Realme手机怎样相互远程控制?Realme可以被其他手机远程控制吗?
  • GPTs——定制的小型智能体
  • 微算法科技(NASDAQ: MLGO)开发量子边缘检测算法,为实时图像处理与边缘智能设备提供了新的解决方案
  • vue3+天地图。添加标注和点击当前去掉其他的标注
  • 1. 什么是柯里化
  • SpringBoot自动装配原理
  • XSS的原型链污染1--原型链解释
  • 如何选择一个容易被搜索引擎发现的域名?