企业微信自建应用实现接收消息和发送消息功能(python)

article/2025/8/15 20:53:52

# 这一周我不断的琢磨企业微信自建应用并且实现了自建应用的消息接收和发送功能

1.笔记,记录

第一步:打开企业微信后台 https://work.weixin.qq.com

1.1 如果没有企业可以在这里申请,如果有可以直接扫码登录

1.2 打开后台-应用管理-自建应用-创建应用——填写自建应用的logo,应用名称,应用介绍等信息。

1.3 获取自建应用的AgentId,Secret,以及我的企业-企业ID信息

第二步:初步测试发送消息功能(注意:将刚刚保存好的信息正确填入到相应的代码段)

import json #用于处理json数据
import urllib.parse # 用于对URL进行解析和构建import requests #用于发送HTTTP请求corpid = ''  # 企业ID
agentid =   # 应用ID
corpsecret = ''  # 应用Secret
touser = ''  # 接收消息的用户# 企业微信API的基础URL
base = 'https://qyapi.weixin.qq.com'# 请求登录凭证(access-token)# 构造函数(获取access-token的API URL)
access_token_api = urllib.parse.urljoin(base, '/cgi-bin/gettoken') # 使用urllib.parse.urljoin来构建获取access-token的完整url
# 定义请求参数(包括企业ID,应用密钥)
params = {'corpid': corpid, 'corpsecret': corpsecret}
# 发送GET请求获取access-token,并且将json响应转化为python字典
response = requests.get(url=access_token_api, params=params).json()
# 从响应中获取access-token
access_token = response['access_token']# 发送消息
# 构建发送消息的完整URL,包含access-token
message_send_api = urllib.parse.urljoin(base, f'/cgi-bin/message/send?access_token={access_token}')
# 定义要发送的消息数据(文本格式)
data = {'touser': touser, 'msgtype': 'text', 'agentid': agentid, 'text': {'content': '测试数据:hello world!'}}
# 发送POST请求以发送消息, 并将json响应转化为python字典
response = requests.post(url=message_send_api, data=json.dumps(data)).json()# 当请求返回值为0时(异常处理)
if response['errcode'] == 0:print('发送成功')
else:print(response)

安装请求处理包 :

pip install requests

发送消息:

第三步:下载微信官方的加解密文件(注:下载对应的我这里下载的时python的加解密包)

下载地址:https://developer.work.weixin.qq.com/document/path/90468

3.1 下载加解密文件并且进行解压安装相关的依赖包

在这个加解密文件中主要使用(WXBizMsgCrypt3.py)文件,解压配置依赖包导入到项目文件中。

#!/usr/bin/env python
# -*- encoding:utf-8 -*-""" 对企业微信发送给企业后台的消息加解密示例代码.
@copyright: Copyright (c) 1998-2014 Tencent Inc."""
# ------------------------------------------------------------------------
import logging
import base64
import random
import hashlib
import time
import struct
from Crypto.Cipher import AES
import xml.etree.cElementTree as ET
import socketimport ierror"""
Crypto.Cipher包已不再维护,开发者可以通过以下命令下载安装最新版的加解密工具包pip install pycryptodome
"""class FormatException(Exception):passdef throw_exception(message, exception_class=FormatException):"""my define raise exception function"""raise exception_class(message)class SHA1:"""计算企业微信的消息签名接口"""def getSHA1(self, token, timestamp, nonce, encrypt):"""用SHA1算法生成安全签名@param token:  票据@param timestamp: 时间戳@param encrypt: 密文@param nonce: 随机字符串@return: 安全签名"""try:sortlist = [token, timestamp, nonce, encrypt]sortlist.sort()sha = hashlib.sha1()sha.update("".join(sortlist).encode())return ierror.WXBizMsgCrypt_OK, sha.hexdigest()except Exception as e:logger = logging.getLogger()logger.error(e)return ierror.WXBizMsgCrypt_ComputeSignature_Error, Noneclass XMLParse:"""提供提取消息格式中的密文及生成回复消息格式的接口"""# xml消息模板AES_TEXT_RESPONSE_TEMPLATE = """<xml>
<Encrypt><![CDATA[%(msg_encrypt)s]]></Encrypt>
<MsgSignature><![CDATA[%(msg_signaturet)s]]></MsgSignature>
<TimeStamp>%(timestamp)s</TimeStamp>
<Nonce><![CDATA[%(nonce)s]]></Nonce>
</xml>"""def extract(self, xmltext):"""提取出xml数据包中的加密消息@param xmltext: 待提取的xml字符串@return: 提取出的加密消息字符串"""try:xml_tree = ET.fromstring(xmltext)encrypt = xml_tree.find("Encrypt")return ierror.WXBizMsgCrypt_OK, encrypt.textexcept Exception as e:logger = logging.getLogger()logger.error(e)return ierror.WXBizMsgCrypt_ParseXml_Error, Nonedef generate(self, encrypt, signature, timestamp, nonce):"""生成xml消息@param encrypt: 加密后的消息密文@param signature: 安全签名@param timestamp: 时间戳@param nonce: 随机字符串@return: 生成的xml字符串"""resp_dict = {'msg_encrypt': encrypt,'msg_signaturet': signature,'timestamp': timestamp,'nonce': nonce,}resp_xml = self.AES_TEXT_RESPONSE_TEMPLATE % resp_dictreturn resp_xmlclass PKCS7Encoder():"""提供基于PKCS7算法的加解密接口"""block_size = 32def encode(self, text):""" 对需要加密的明文进行填充补位@param text: 需要进行填充补位操作的明文@return: 补齐明文字符串"""text_length = len(text)# 计算需要填充的位数amount_to_pad = self.block_size - (text_length % self.block_size)if amount_to_pad == 0:amount_to_pad = self.block_size# 获得补位所用的字符pad = chr(amount_to_pad)return text + (pad * amount_to_pad).encode()def decode(self, decrypted):"""删除解密后明文的补位字符@param decrypted: 解密后的明文@return: 删除补位字符后的明文"""pad = ord(decrypted[-1])if pad < 1 or pad > 32:pad = 0return decrypted[:-pad]class Prpcrypt(object):"""提供接收和推送给企业微信消息的加解密接口"""def __init__(self, key):# self.key = base64.b64decode(key+"=")self.key = key# 设置加解密模式为AES的CBC模式self.mode = AES.MODE_CBCdef encrypt(self, text, receiveid):"""对明文进行加密@param text: 需要加密的明文@return: 加密得到的字符串"""# 16位随机字符串添加到明文开头text = text.encode()text = self.get_random_str() + struct.pack("I", socket.htonl(len(text))) + text + receiveid.encode()# 使用自定义的填充方式对明文进行补位填充pkcs7 = PKCS7Encoder()text = pkcs7.encode(text)# 加密cryptor = AES.new(self.key, self.mode, self.key[:16])try:ciphertext = cryptor.encrypt(text)# 使用BASE64对加密后的字符串进行编码return ierror.WXBizMsgCrypt_OK, base64.b64encode(ciphertext)except Exception as e:logger = logging.getLogger()logger.error(e)return ierror.WXBizMsgCrypt_EncryptAES_Error, Nonedef decrypt(self, text, receiveid):"""对解密后的明文进行补位删除@param text: 密文@return: 删除填充补位后的明文"""try:cryptor = AES.new(self.key, self.mode, self.key[:16])# 使用BASE64对密文进行解码,然后AES-CBC解密plain_text = cryptor.decrypt(base64.b64decode(text))except Exception as e:logger = logging.getLogger()logger.error(e)return ierror.WXBizMsgCrypt_DecryptAES_Error, Nonetry:pad = plain_text[-1]# 去掉补位字符串# pkcs7 = PKCS7Encoder()# plain_text = pkcs7.encode(plain_text)# 去除16位随机字符串content = plain_text[16:-pad]xml_len = socket.ntohl(struct.unpack("I", content[: 4])[0])xml_content = content[4: xml_len + 4]from_receiveid = content[xml_len + 4:]except Exception as e:logger = logging.getLogger()logger.error(e)return ierror.WXBizMsgCrypt_IllegalBuffer, Noneif from_receiveid.decode('utf8') != receiveid:return ierror.WXBizMsgCrypt_ValidateCorpid_Error, Nonereturn 0, xml_contentdef get_random_str(self):""" 随机生成16位字符串@return: 16位字符串"""return str(random.randint(1000000000000000, 9999999999999999)).encode()class WXBizMsgCrypt(object):# 构造函数def __init__(self, sToken, sEncodingAESKey, sReceiveId):try:self.key = base64.b64decode(sEncodingAESKey + "=")assert len(self.key) == 32except:throw_exception("[error]: EncodingAESKey unvalid !", FormatException)# return ierror.WXBizMsgCrypt_IllegalAesKey,Noneself.m_sToken = sTokenself.m_sReceiveId = sReceiveId# 验证URL# @param sMsgSignature: 签名串,对应URL参数的msg_signature# @param sTimeStamp: 时间戳,对应URL参数的timestamp# @param sNonce: 随机串,对应URL参数的nonce# @param sEchoStr: 随机串,对应URL参数的echostr# @param sReplyEchoStr: 解密之后的echostr,当return返回0时有效# @return:成功0,失败返回对应的错误码def VerifyURL(self, sMsgSignature, sTimeStamp, sNonce, sEchoStr):sha1 = SHA1()ret, signature = sha1.getSHA1(self.m_sToken, sTimeStamp, sNonce, sEchoStr)if ret != 0:return ret, Noneif not signature == sMsgSignature:return ierror.WXBizMsgCrypt_ValidateSignature_Error, Nonepc = Prpcrypt(self.key)ret, sReplyEchoStr = pc.decrypt(sEchoStr, self.m_sReceiveId)return ret, sReplyEchoStrdef EncryptMsg(self, sReplyMsg, sNonce, timestamp=None):# 将企业回复用户的消息加密打包# @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串# @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp,如为None则自动用当前时间# @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce# sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,# return:成功0,sEncryptMsg,失败返回对应的错误码Nonepc = Prpcrypt(self.key)ret, encrypt = pc.encrypt(sReplyMsg, self.m_sReceiveId)encrypt = encrypt.decode('utf8')if ret != 0:return ret, Noneif timestamp is None:timestamp = str(int(time.time()))# 生成安全签名sha1 = SHA1()ret, signature = sha1.getSHA1(self.m_sToken, timestamp, sNonce, encrypt)if ret != 0:return ret, NonexmlParse = XMLParse()return ret, xmlParse.generate(encrypt, signature, timestamp, sNonce)def DecryptMsg(self, sPostData, sMsgSignature, sTimeStamp, sNonce):# 检验消息的真实性,并且获取解密后的明文# @param sMsgSignature: 签名串,对应URL参数的msg_signature# @param sTimeStamp: 时间戳,对应URL参数的timestamp# @param sNonce: 随机串,对应URL参数的nonce# @param sPostData: 密文,对应POST请求的数据#  xml_content: 解密后的原文,当return返回0时有效# @return: 成功0,失败返回对应的错误码# 验证安全签名xmlParse = XMLParse()ret, encrypt = xmlParse.extract(sPostData)if ret != 0:return ret, Nonesha1 = SHA1()ret, signature = sha1.getSHA1(self.m_sToken, sTimeStamp, sNonce, encrypt)if ret != 0:return ret, Noneif not signature == sMsgSignature:return ierror.WXBizMsgCrypt_ValidateSignature_Error, Nonepc = Prpcrypt(self.key)ret, xml_content = pc.decrypt(encrypt, self.m_sReceiveId)return ret, xml_content

3.2 导入下载安装包中的(ierror.py)错误码提示文件

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#########################################################################
# Author: jonyqin
# Created Time: Thu 11 Sep 2014 01:53:58 PM CST
# File Name: ierror.py
# Description:定义错误码含义 
########################################################################## "======================================================提供了错误码==================================================="
WXBizMsgCrypt_OK = 0
WXBizMsgCrypt_ValidateSignature_Error = -40001
WXBizMsgCrypt_ParseXml_Error = -40002
WXBizMsgCrypt_ComputeSignature_Error = -40003
WXBizMsgCrypt_IllegalAesKey = -40004
WXBizMsgCrypt_ValidateCorpid_Error = -40005
WXBizMsgCrypt_EncryptAES_Error = -40006
WXBizMsgCrypt_DecryptAES_Error = -40007
WXBizMsgCrypt_IllegalBuffer = -40008
WXBizMsgCrypt_EncodeBase64_Error = -40009
WXBizMsgCrypt_DecodeBase64_Error = -40010
WXBizMsgCrypt_GenReturnXml_Error = -40011

第四步:编写发送和接收消息逻辑(fastapi)

在这里我使用fastapi实现了接口的开发,详细如下,逻辑代码中需要配置(接收消息设置里边的TOKEN,ENCODING_AES_KEY,CORP_ID等信息,这么后面讲怎么获取。

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import PlainTextResponse, Response
from WXBizMsgCrypt3 import WXBizMsgCrypt
import xmltodict
import logging
import time
import xml.etree.ElementTree as ET# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)app = FastAPI()# 企业微信配置
TOKEN = ""  # 设置的Token
ENCODING_AES_KEY = "" #设置密钥
CORP_ID = ""  # 企业IDtry:wxcpt = WXBizMsgCrypt(TOKEN, ENCODING_AES_KEY, CORP_ID)
except Exception as e:logger.error(f"初始化WXBizMsgCrypt失败: {str(e)}")raise@app.get("/callback")
async def verify_url(msg_signature: str, timestamp: str, nonce: str, echostr: str):"""验证URL有效性"""try:logger.info(f"收到验证请求: msg_signature={msg_signature}, timestamp={timestamp}, nonce={nonce}")ret, sEchoStr = wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr)if ret == 0:logger.info("URL验证成功")return PlainTextResponse(content=sEchoStr)else:logger.error(f"URL验证失败,错误码: {ret}")raise HTTPException(status_code=400, detail="验证失败")except Exception as e:logger.error(f"验证过程发生错误: {str(e)}")raise HTTPException(status_code=500, detail="服务器内部错误")@app.post("/callback")
async def receive_message(request: Request):"""接收并处理企业微信消息"""try:# 获取请求参数body = await request.body()msg_signature = request.query_params.get("msg_signature")timestamp = request.query_params.get("timestamp")nonce = request.query_params.get("nonce")if not all([msg_signature, timestamp, nonce]):raise HTTPException(status_code=400, detail="缺少必要的参数")logger.info(f"收到消息推送: msg_signature={msg_signature}, timestamp={timestamp}, nonce={nonce}")# 解密消息ret, sMsg = wxcpt.DecryptMsg(body, msg_signature, timestamp, nonce)if ret != 0:logger.error(f"消息解密失败,错误码: {ret}")raise HTTPException(status_code=400, detail="消息解密失败")# 解析XML消息xml_dict = xmltodict.parse(sMsg)logger.info(f"解密后的消息内容: {xml_dict}")# 提取消息内容xml_content = xml_dict['xml']to_user_name = xml_content.get('ToUserName')from_user_name = xml_content.get('FromUserName')create_time = xml_content.get('CreateTime')msg_type = xml_content.get('MsgType')content = xml_content.get('Content')msg_id = xml_content.get('MsgId')agent_id = xml_content.get('AgentID')logger.info(f"收到消息: {content}")# 构造回复消息reply_content = content.replace('吗', '').replace('?', '!').replace('?', '!')current_time = str(int(time.time()))reply_msg = f"""<xml><ToUserName><![CDATA[{from_user_name}]]></ToUserName><FromUserName><![CDATA[{to_user_name}]]></FromUserName><CreateTime>{current_time}</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[{reply_content}]]></Content><MsgId>{msg_id}</MsgId><AgentID>{agent_id}</AgentID></xml>"""# 加密回复消息ret, encrypted_msg = wxcpt.EncryptMsg(reply_msg, nonce, current_time)if ret != 0:logger.error(f"消息加密失败,错误码: {ret}")raise HTTPException(status_code=500, detail="消息加密失败")logger.info("成功构造并加密回复消息")return Response(content=encrypted_msg, media_type="application/xml")except Exception as e:logger.error(f"处理消息时发生错误: {str(e)}")raise HTTPException(status_code=500, detail="服务器内部错误")if __name__ == "__main__":import uvicornuvicorn.run(app, host="127.0.0.1", port=5000)

第五步:回调配置

5.1 进入企业微信后台配置-接收消息

这里需要我们提供一个处理请求回调的URL,随机生成Token,EncodingAESKey,并且需要把这写数据配置到上方代码中

5.2 配置URL和项目部署

首先需要创建一个域名

添加好的域名中指向项目文件并且配置反向代理(设置-配置文件)

保存起来,然后创建一个文件,把配置好的代码上传到创建的文件中,并且验证回调服务,让程序跑起来。

5.3 配置成功以后保存企业微信的URL以及其他配置信息

结束了。。。

接下来看效果:

日志文件

效果:


http://www.hkcw.cn/article/qJobnxaAXY.shtml

相关文章

【Rust多线程】Rust并发编程,如何轻松实现无畏并发

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

2025粤港澳信息学创新大赛备考指南Python,C++,图形化历年真题训练题

官方指导文件 训练题目 ZCM5:Python小学组-单项选择题 1.小明安装软件的时候发现软件要求Windows环境&#xff0c;这 个要求限制的是? A.操作系统 B.计算机内存 C.网络设置 D.程序语言 答案‌&#xff1a;A. 操作系统 解析‌&#xff1a;软件运行的环境通常指的是操作系统&am…

【C++指南】“单身狗问题”——只出现一次的数字 系列问题

. &#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 文章目录 引言一、只出现一次的数字&#xff08;一&#xff09;简单题目描述解题思路代码实现及解释 二…

优化 InfluxDB 写入性能:高效批处理策略实战指南

在处理高吞吐量时序数据时&#xff0c;合理运用批处理&#xff08;Batching&#xff09;策略是提升 InfluxDB 写入性能的关键。本文介绍 时间驱动、大小驱动和混合批处理策略&#xff0c;并通过 Python 代码示例展示如何优化数据写入&#xff0c;平衡 延迟与吞吐量。同时&#…

RedwoodJS:乱拳打倒老师傅 NextJS!

RedwoodJS 是一个全栈的 JavaScript/TypeScript 框架&#xff0c;其作用是帮助开发者高效地构建现代化的 Web 应用。它将前端、后端和数据库集成在一起&#xff0c;并使用一种“JAMstack”架构&#xff08;JavaScript、API 和 Markup&#xff09;来构建可扩展的应用程序。 Star…

【C++】 —— 笔试刷题day_18

一、压缩字符串(一) 题目解析 题目给定一个字符str&#xff0c;让我们将这个字符串进行压缩&#xff1b; **压缩规则&#xff1a;**出现多次的字符压缩成字符数字&#xff1b;例如aaa压缩成a3。如果字符值出现一次&#xff0c;1不用写。 算法思路 这道题总的来说就非常简单了…

谷歌浏览器如何禁用javaScript

通过禁用js&#xff0c;可以访问一些设置权限的内容。 Chrome 地址栏输入 chrome://settings/content 回车。 找到 JavaScript 选项。 切换为 不允许网站使用 JavaScript。 地址栏输入&#xff1a; chrome://settings/content/javascript?searchJavaScript Firefox 地址栏输入…

Java从入门到“放弃”(精通)之旅——类和对象全面解析⑦

Java从入门到“放弃”&#xff08;精通&#xff09;之旅&#x1f680;——类和对象全面解析⑦ 一、面向对象初探 1.1 什么是面向对象&#xff1f; Java是一门纯面向对象的语言(OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。面向对象是解决问题的一种思想&am…

【Golang】第七弹----map

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;Golang &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 1基本介绍 Go语言中的 map …

C/C++程序员为什么要了解汇编?了解汇编有哪些好处?如何学习汇编?

目录 1、概述 2、从汇编的角度去理解问题的若干实例说明 2.1、使用空指针去访问类的数据成员或调用类的虚函数为什么会引发崩溃? 2.2、从汇编代码的角度去理解多线程的执行细节,去理解多线程在访问共享资源时为什么要加锁 2.3、使用Windbg静态分析dump时先从崩溃的那条汇…

基于谐波线性化方法的跟网型GFL并网变流器/VSC宽频序阻抗建模及扫频(Matlab/Simulink平台)及文献复现

目录 1、课程及模型介绍 2、谐波线性化方法介绍 3、跟网型及构网型并网变流器的特点 4、跟网型变流器/VSC拓扑及控制结构 5、不同坐标系下VSC序阻抗建模推导过程 5.1 abc三相坐标系下的VSC序阻抗建模 5.2 d-q旋转坐标系下的VSC序阻抗建模 5.2.1 Park变换及频率偏移效应…

C++“STL之String”

​ 🌹个人主页🌹:喜欢草莓熊的bear 🌹专栏🌹:C++入门 目录 ​编辑 前言 一、STL简介 1.1 STL是什么? 1.2 STL的版本(这个不是很重要了解即可) 1.3 STL的六大组件 二、 String类 2.1为什么要学习String类? 2.1.1 C语言中的字符串…

C++之多态

开始新的征程啦———多态&#xff0c;它也是C的三大特性之一。 文章目录 一、多态的概念二、多态的定义和实现2.1多态的定义2.2 实现动态多态所需要的条件&#xff08;2个&#xff09;2.3 虚函数的定义2.4 虚函数的重写/覆盖2.5 虚函数重写中的问题2.5.1 协变2.5.2 析构函数的…

【第十六届蓝桥杯省赛】比赛心得与经验分享(PythonA 组)

文章目录 一、我的成绩二、我的备赛经历三、如何备赛&#xff08;个人观点&#xff09;1. 基础语法2. 数据结构3. 算法4. 数学 四、做题技巧与注意事项五、我的题解试题A 偏蓝 &#x1f3c6;100%试题B IPV6 &#x1f3c6;0%试题C 2025图形 &#x1f3c6;100%试题D 最大数字 &am…

21天Python计划:零障碍学语法(更新完毕)

文章目录 前言Python 部分MySQL 部分目录结语资料截图 前言 此技术博客专栏围绕 Python 编程和 MySQL 数据库展开了系统且循序渐进的知识讲解&#xff0c;共包含 21 篇文章。 Python 部分 从基础入门逐步深入到高级应用。首先介绍了 Python 的下载和开发工具&#xff0c;为后续…

JavaScript--js基础(详细 全面)

目录 前言: JavaScript 是什么&#xff1f;JavaScript 简介 1.JavaScript历史 2.JavaScript 具有以下特点 第一个JavaScript程序 1.在脚本文件中编写JavaScript代码 2.JavaScript代码执行顺序 基本语法 1.变量 2.数据类型 3.算术运算符 4.赋值运算 5.字符串运算符 6…

Java识别图片或扫描PDF中的文字

目录 使用工具 Java识别图片中的文字 Java识别扫描PDF中的文字 注意事项 图片和扫描文件通常以非文本格式存在&#xff0c;这使得其中的文字信息难以直接编辑、搜索或复制。为了解决这个问题&#xff0c;光学字符识别&#xff08;OCR&#xff09;技术应运而生。OCR通过分析…

【C++】C++11新特性详解:可变参数模板与emplace系列的应用

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与QueuePriori…

使用宝塔面板快速部署SpringBoot+Vue项目(Java + Node)

使用宝塔面板快速部署SpringBootVue项目&#xff08;Java Node&#xff09; 项目主要技术栈准备工作1. 一台云服务器&#xff08;阿里云、腾讯云等&#xff09;&#xff0c;我这里使用的是阿里云的服务器&#xff08;2核2G&#xff09;2. 已安装宝塔面板3. 已开发完成的Spring…

一文弄懂 | YOLOv8网络结构解读 、yolov8.yaml配置文件详细解读与说明、模型训练参数详细解析 | 通俗易懂!入门必看系列!

看这一篇就够了。本文内含YOLOv8网络结构图 yaml配置文件详细解读与说明 训练教程 训练参数设置参数解析说明等一些有关YOLOv8的内容&#xff01; YOLOv8v10专栏订阅链接&#xff1a;YOLOv10 创新改进高效涨点持续改进300多篇永久免费答疑 &#xff08;订阅的小伙伴&#xf…