企业微信会话存档配置与拉取消息全链路实战

简介:这份资源面向熟悉Java开发、需要落地企业微信会话内容存档功能的技术人员,围绕服务器配置、事件接收、公钥生成与配置、依赖引入、SDK使用以及消息拉取与解密等关键环节,提供从零开始的完整实现思路。压缩包共20个文件,约31KB,以10个java源码为主体,辅以3个md说明文档、2个yml配置、2个lst清单、1个class、1个xml及1个inscode工程文件,覆盖代码、配置与说明三类内容,便于直接对照项目结构理解实现方式。目前已有223人学习。资源重点给出可运行的代码示例,并针对IP可信问题、依赖冲突处理、SO文件加载等常见难题提供解决思路,帮助开发者减少踩坑、加快调试进度,适合作为企业微信会话存档功能开发与排错的参考材料。

1. 企业微信会话存档配置:从开通到拉取消息的完整链路

企业微信会话存档配置这件事,很多团队第一次做都会卡在同一个地方:后台明明开了开关,代码拉消息却一直报错。我见过最典型的场景是某公司的后端同学,花了两天排查网络和证书,最后发现是可信 IP 没配——请求根本没到企业微信的服务器就被拦了。会话存档本质上是企业微信开放能力里偏「重」的一块:它涉及密钥管理、SDK 调用、拉取游标维护、媒体文件解密四个环节,任何一环没对齐,整条链路就是黑匣子。它适合谁?适合需要做合规审计、客服质检、销售会话分析的团队,尤其是已经用企业微信做客户运营、想把聊天记录沉淀成结构化数据的公司。这篇笔记按「开通配置 → SDK 拉取 → 解密落库 → 排错」的顺序讲,每一步都给可复现的命令和参数,新手能照着跑通,熟手能对照检查自己的边界条件。

2. 开通与密钥配置:三个参数决定你能不能拉到数据

会话存档不是装个库就能用的东西,它首先是一个需要企业管理员在后台手动开通的能力。开通之后你会拿到一组用于调用接口的凭证,这组凭证的配置方式直接决定了后续代码能不能跑通。这一章把开通流程和密钥配置讲清楚,因为后面所有代码都依赖这三个参数。

2.1 后台开通会话存档与可信 IP 设置

登录企业微信管理后台,进入「管理工具 → 会话内容存档」,这里会看到开通入口。开通时需要选择存档范围:可以是全公司,也可以指定部门或成员。我的建议是第一次先用一个小范围(比如一个测试部门)跑通,确认链路没问题再扩大——因为存档一旦开启,产生的数据量是按消息条数算的,全量开启后再调试,日志会被真实数据淹没。

开通后最关键的一步是配置可信 IP。企业微信的接口调用有 IP 白名单机制,你服务器出口 IP 不在白名单里,所有请求都会返回60020之类的错误码。配置位置在会话存档的「API 配置」里,把调用方的公网出口 IP 填进去。注意这里填的是出口 IP,不是内网 IP;如果你的服务部署在容器或 NAT 网关后面,要确认实际出口地址。

提示:可信 IP 支持配置多个,用换行分隔。调试阶段可以临时把你的开发机出口 IP 加进去,但上线前记得清理,避免留下不必要的入口。

配置完成后,后台会生成Secret(用于获取 access_token)和RSA 公钥(用于加密对称密钥)。这两个东西加上企业 ID(CorpID),就是后续代码里的三个核心参数。

2.2 三个核心参数的含义与获取方式

把这三个参数理解清楚,后面看代码就不会懵:

参数来源作用注意事项
CorpID企业微信后台「我的企业」标识企业身份固定值,不随成员变化
Secret会话存档「API 配置」生成换取 access_token泄露等于别人能拉你的存档,务必放密钥管理服务
RSA 公私钥后台生成或自行上传公钥加密/解密对称密钥私钥自己保管,公钥配置到后台

获取 access_token 的请求长这样,用 curl 就能验证:

# 用 CorpID 和 Secret 换取 access_token # corpid 和 corpsecret 替换成你自己的值 curl -s "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=YOUR_CORPID&corpsecret=YOUR_SECRET"

返回结果里access_token有效期是7200 秒,需要缓存并在过期前刷新。这里有个血泪经验:不要每次请求都去换 token,企业微信对获取 token 的频率有限制,频繁调用会触发限流,表现为后续接口间歇性失败。常见做法是用 Redis 缓存,设置 7000 秒过期,加一个分布式锁防止并发刷新。

2.3 配置对称密钥:RSA 加密那一步到底在做什么

会话存档的媒体文件(图片、语音、文件)不是明文传输的,企业微信为每个会话生成一个随机的对称密钥,然后用你配置的RSA 公钥加密后传给你。你要做的就是用私钥解密拿到对称密钥,再用它解密媒体内容。

这一步的配置动作是:在后台「API 配置」里填入你的 RSA 公钥。公钥格式要求是PKCS#1或PKCS#8的 PEM 格式,很多同学在这里翻车——用工具生成的默认是 PKCS#8,但后台可能只认某一种,报错信息又不明确。我的做法是统一用 OpenSSL 生成并显式指定格式:

# 生成 2048 位 RSA 私钥(PKCS#1 格式) openssl genrsa -out private_key.pem 2048 # 从私钥导出公钥 openssl rsa -in private_key.pem -pubout -out public_key.pem # 查看公钥内容,确认是 PEM 格式 cat public_key.pem

生成后把public_key.pem的内容完整粘贴到后台配置框。私钥private_key.pem保存在服务端,后面解密对称密钥要用。注意私钥不要提交到代码仓库,用环境变量或配置中心注入。

3. SDK 拉取消息:游标、批量与媒体文件处理

配置通了,接下来是真正拉数据。会话存档的拉取不是「查询历史消息」那种模式,而是基于游标的增量拉取:你告诉它从哪个位置开始拉,它返回一批消息和一个新的游标,下次用新游标继续。这个设计决定了你的代码必须自己维护游标状态,否则要么重复拉取,要么漏消息。

3.1 拉取接口的调用方式与游标维护

企业微信提供了 SDK(C++/Java/Python 等),也支持直接调 HTTP 接口。SDK 的好处是帮你处理了加解密和协议细节,坏处是部署依赖多。我一般先用 HTTP 接口验证链路,确认没问题再换 SDK。

拉取的核心接口是getchatdata,关键参数:

  • seq:起始游标,第一次传 0
  • limit:单次拉取条数,最大 1000
  • proxy和passwd:SDK 模式下用于解密,HTTP 模式下传空
import requests import json def pull_messages(access_token, seq=0, limit=100): """从指定游标拉取会话存档消息""" url = "https://qyapi.weixin.qq.com/cgi-bin/msgaudit/getchatdata" params = {"access_token": access_token} body = { "seq": seq, "limit": limit, # HTTP 模式下 proxy 和 passwd 留空 "proxy": "", "passwd": "" } resp = requests.post(url, params=params, json=body) data = resp.json() if data.get("errcode") != 0: raise RuntimeError(f"拉取失败: {data}") # 返回消息列表和下一个游标 return data.get("chatdata", []), data.get("next_seq", seq)

逻辑说明:seq是游标,第一次传 0,之后每次用返回的next_seq。limit控制单次批量,建议设 500 到 1000,太小会导致请求次数多、触发限流,太大则单次响应体过大、容易超时。返回的chatdata里每条消息包含发送者、接收者、消息类型、时间戳,以及加密后的内容。

游标维护是这一环最容易出问题的地方。我的做法是每拉一批就持久化一次游标,用数据库或 Redis 存,key 用企业 ID 加模块名。这样即使服务重启,也能从上次位置继续。千万不要把游标只放在内存里,进程一挂就全丢了。

3.2 消息解密:从密文到可读内容的完整流程

拉回来的chatdata里,encrypt_random_key和encrypt_chat_msg两个字段是加密的。解密分两步:

第一步,用 RSA 私钥解密encrypt_random_key,得到对称密钥。注意这个字段是Base64 编码的,先解码再解密。

第二步,用对称密钥解密encrypt_chat_msg,得到明文消息体。企业微信 SDK 里封装了这个过程,如果走 HTTP 接口,需要自己实现。

import base64 from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 def decrypt_random_key(encrypt_random_key, private_key_pem): """用 RSA 私钥解密对称密钥""" # encrypt_random_key 是 Base64 编码的密文 ciphertext = base64.b64decode(encrypt_random_key) private_key = RSA.import_key(private_key_pem) cipher = PKCS1_v1_5.new(private_key) # 解密后得到对称密钥,用于后续消息解密 # 注意:这里需要配合企业微信提供的解密库处理填充 return cipher.decrypt(ciphertext, None)

参数说明:encrypt_random_key来自拉取接口返回,private_key_pem是你之前生成的私钥。解密出的对称密钥是一个字节串,直接传给消息解密函数。消息解密后是 JSON 格式,包含msgtype、text、image等字段,按类型解析即可。

注意:RSA 解密对填充格式敏感,如果报解密失败,先检查私钥格式是否和后台配置的公钥匹配。常见错误是用 PKCS#8 私钥去解 PKCS#1 公钥加密的数据,虽然都是 RSA,但填充方式不同会直接失败。

3.3 媒体文件拉取与本地落盘

文本消息解密后直接就是内容,但图片、语音、文件这类媒体消息,解密后拿到的是一个sdkfileid,需要再调一次接口下载。

def download_media(access_token, sdkfileid, save_path): """根据 sdkfileid 下载媒体文件""" url = "https://qyapi.weixin.qq.com/cgi-bin/msgaudit/getmediafile" params = {"access_token": access_token} body = {"sdkfileid": sdkfileid} resp = requests.post(url, params=params, json=body) if resp.headers.get("Content-Type") == "application/json": # 返回 JSON 说明出错了 raise RuntimeError(f"下载失败: {resp.json()}") # 二进制内容直接写文件 with open(save_path, "wb") as f: f.write(resp.content)

逻辑说明:这个接口返回的是二进制流,不是 JSON。判断方式看Content-Type,如果是application/json说明返回了错误码。sdkfileid有有效期,一般是几天,所以拉取到媒体消息后要尽快下载,不要攒着批量处理。落盘路径建议按日期和会话 ID 分目录,避免单目录文件过多导致文件系统性能下降。

4. 避坑与排查:那些让你怀疑人生的错误码

会话存档的报错信息普遍不友好,很多错误码只给一个数字,不告诉你哪里错了。这一章整理几个我实际踩过的坑,按「现象 → 原因 → 解决」写,希望能帮你省下几个小时的排查时间。

4.1 坑一:60020 错误——可信 IP 没生效

现象:所有接口都返回errcode: 60020,提示 IP 不在白名单。

原因:可信 IP 配置后不是立即生效,有几分钟延迟;或者你填的是内网 IP,实际出口是另一个地址。

解决:先用curl ifconfig.me确认服务器真实出口 IP,再和后台配置比对。如果用了容器或网关,确认是网关出口 IP。配置后等 5 分钟再试,别急着改代码。

4.2 坑二:拉取返回空但明明有消息

现象:getchatdata返回errcode: 0,但chatdata是空数组。

原因:游标seq传错了。如果你之前已经拉到某个位置,再用 0 去拉,可能因为数据保留策略拿不到旧数据;或者存档范围没包含目标成员。

解决:检查游标是否从上次的next_seq继续。确认存档范围配置里包含了你要拉取的成员。企业微信的存档数据有保留期限,超期的拉不到。

4.3 坑三:媒体文件下载返回 JSON 错误

现象:调getmediafile返回的不是二进制,而是{"errcode": xxx}。

原因:sdkfileid过期了,或者 access_token 权限不足。

解决:sdkfileid有效期短,收到媒体消息后立即下载。access_token 要用会话存档对应的 Secret 换取,不要用其他应用的 token。

4.4 坑四:解密报填充错误

现象:RSA 解密时抛ValueError: Incorrect decryption或类似填充错误。

原因:私钥和后台配置的公钥不匹配,或者私钥格式不对。

解决:重新生成一对密钥,公钥重新配置到后台,私钥更新到服务端。确认生成命令用的是同一套参数。如果之前配置过旧公钥,先删除再添加新的。

4.5 坑五:高频拉取触发限流

现象:跑一段时间后接口开始间歇性失败,过一会又恢复。

原因:请求频率超过企业微信限制。会话存档接口有调用频次上限,具体数值随企业规模变化。

解决:拉取循环里加 sleep,比如每批之间停 200 毫秒。access_token 用缓存,不要每次重新获取。如果数据量大,考虑分时段拉取,避开业务高峰。

5. 进阶:把拉取做成可持续运行的管道

跑通单次拉取只是开始,真正要落地的是7×24 小时稳定运行的管道。这一章讲几个让管道更可靠的具体技巧,都是我在实际运维里攒下来的。

5.1 用状态机管理拉取生命周期

把拉取逻辑拆成「获取 token → 拉取批次 → 解密 → 落库 → 更新游标」几个状态,每个状态失败都有明确的恢复路径。比如 token 过期就重新获取,拉取失败就重试当前游标,解密失败就记录原始密文到死信队列后续人工处理。这样任何一环出问题都不会导致整个管道卡死。

import time import redis r = redis.Redis() def run_pipeline(): """持续拉取的主循环""" while True: try: token = get_cached_token() # 从缓存获取,过期自动刷新 seq = int(r.get("chat_archive_seq") or 0) messages, next_seq = pull_messages(token, seq) for msg in messages: try: plain = decrypt_message(msg) save_to_db(plain) except Exception as e: # 单条失败不影响整批,记录后继续 r.lpush("chat_archive_dlq", json.dumps(msg)) print(f"单条处理失败: {e}") # 整批成功后更新游标 r.set("chat_archive_seq", next_seq) # 控制频率,避免限流 time.sleep(0.2) except Exception as e: print(f"批次失败,10 秒后重试: {e}") time.sleep(10)

逻辑说明:游标存在 Redis 里,整批处理成功才更新,保证至少一次语义。单条失败进死信队列,不阻塞后续消息。批次失败等 10 秒重试,避免雪崩。time.sleep(0.2)是频率控制,根据实际限流情况调整。

5.2 监控指标:怎么知道管道还活着

管道跑起来之后,你需要知道它是不是在正常工作。我一般监控四个指标:

指标含义异常阈值
拉取延迟当前时间与最新消息时间差超过 10 分钟告警
死信队列长度处理失败的消息数持续增长告警
游标推进速度每分钟游标增量长时间为 0 告警
token 刷新失败次数获取 token 失败计数连续 3 次失败告警

这些指标用 Prometheus 或简单的定时脚本都能采集。关键是拉取延迟,它直接反映管道是否跟得上消息产生速度。如果延迟持续增大,说明拉取速度不够,需要调大limit或增加并发。

5.3 数据落库的表结构设计

消息落库建议分两张表:一张存消息主体,一张存媒体文件索引。消息主体表按时间分区,字段包括消息 ID、发送者、接收者、消息类型、内容、时间戳。媒体文件索引表存 sdkfileid、本地路径、文件类型、关联的消息 ID。

-- 消息主体表,按月份分区 CREATE TABLE chat_message ( id BIGINT AUTO_INCREMENT, msg_id VARCHAR(64) NOT NULL, sender VARCHAR(64), receiver VARCHAR(64), msg_type VARCHAR(32), content TEXT, msg_time DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id, msg_time), KEY idx_msg_time (msg_time), KEY idx_sender (sender) ) PARTITION BY RANGE (TO_DAYS(msg_time)) ( PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')), PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')) );

参数说明:msg_id加唯一索引可以防重复插入。按时间分区方便清理旧数据和按时间范围查询。content用 TEXT 存明文内容,如果合规要求加密存储,在这一层做字段级加密。

5.4 一个容易忽略的细节:时区

企业微信返回的时间戳是Unix 时间戳,单位秒。落库时如果用FROM_UNIXTIME转换,注意数据库时区设置。我遇到过服务器时区是 UTC、数据库时区是东八区,导致消息时间差了 8 小时,排查了半天。统一做法是:存储时用 UTC,展示时在前端转换。或者干脆存时间戳整数,查询时再格式化。

这个管道我维护了一年多,最大的教训是不要相信任何一次成功。每次企业微信接口调整、每次服务器迁移、每次证书更新,都可能让管道悄悄挂掉。所以监控和告警不是可选项,是必选项。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

STM32F103 I2C从机中断接收实战:事件状态机与避坑指南

STM32F103 I2C从机中断接收实战:事件状态机与避坑指南

简介:STM32F103 I2C通信-中断接收数据是一份面向嵌入式开发者的标准库工程资源,专注解决STM32F103CB在I2C通信中通过中断方式高效接收数据的实现问题,适用于工业控制、医疗设备、消费电子等板级短距离通信场景。包内共227个文件,包…

2026/10/20 18:54:56 阅读更多 →
YOLO复杂场景检测提升:C3k2融合CSAM三重注意力改进实践

YOLO复杂场景检测提升:C3k2融合CSAM三重注意力改进实践

最近在调一个复杂场景检测项目,场景里全是遮挡、近色背景和低照度小目标,用YOLO系列默认的C3k2做主干部件,mAP卡在某个值上死活上不去。后来参考了WACV 2024上那篇关于跨切片建模注意力(CSAM)的思路,把C3k2…

2026/10/20 18:54:56 阅读更多 →
时域降维STAP:机载雷达mDT杂波抑制从原理到仿真

时域降维STAP:机载雷达mDT杂波抑制从原理到仿真

简介:面向雷达信号处理方向的学生、科研人员及工程开发者,资源提供时域降维空时自适应处理中多普勒单元截断算法的仿真实现。传统方法需要对全部多普勒单元联合处理,计算复杂度随数据维度急剧上升;该算法只选取目标附近的少量多普…

2026/10/20 18:54:55 阅读更多 →

最新新闻

MSA存储入门指南:从SAN协议到选型场景的全面解析

MSA存储入门指南:从SAN协议到选型场景的全面解析

简介:这份文档面向备考H3C认证工程师-MSA存储(H3CNE-MSA)的技术人员与H3C代理服务工程师,聚焦MSA存储设备基础配置与维护知识,帮助读者在有限时间内系统梳理考点、检验掌握程度。资源为1个docx文件,压缩包约…

2026/10/20 19:39:14 阅读更多 →
C语言大作业高分实践:工程结构、内存安全与评分对齐

C语言大作业高分实践:工程结构、内存安全与评分对齐

简介:这是一份面向高校C语言初学者与课程设计学生的高分期末大作业实战项目,完整实现了一个具备学生端与管理员双角色的学生成绩管理系统。资源包含可直接编译运行的C/Qt源码(含18个.h头文件与18个.cpp实现文件)、图形界面.ui文件…

2026/10/20 19:39:14 阅读更多 →
Redis Java客户端选型与实战:Jedis、Lettuce、Redisson对比

Redis Java客户端选型与实战:Jedis、Lettuce、Redisson对比

1. 先搞清楚需求,再选客户端写Java应用连Redis,第一步不是去翻依赖、复制代码,而是先想清楚:你的业务到底需要Redis做什么,以及你的应用是什么形态。这话可能听起来像废话,但我在实际项目里见过太多人一上来…

2026/10/20 19:39:14 阅读更多 →
轻量级AI助手ZeroClaw实战:有限算力下的部署与Agent落地

轻量级AI助手ZeroClaw实战:有限算力下的部署与Agent落地

1. 项目定位:为什么轻量级AI助手反而更实用去年年底我拿到一台配置平平的旧工作站,8GB内存、无独显、CPU还是几年前的型号,跑几个容器就喘。当时手头正好有个工具自动化需求:定时抓取内部系统数据,做简单清洗&#xff…

2026/10/20 19:39:14 阅读更多 →
单相桥式半控整流电路原理与Matlab/Simulink仿真建模分析

单相桥式半控整流电路原理与Matlab/Simulink仿真建模分析

单相桥式半控整流电路,这个名字在电力电子课程里出现的频率极高。很多电气工程专业的朋友第一次接触晶闸管整流,就是从它开始的。最近正好有人在问Matlab仿真的思路,我把自己做这个仿真报告的全过程梳理了一遍,从拓扑结构分析到模…

2026/10/20 19:39:14 阅读更多 →
Java动漫链接生成器:批量整理番剧资源链接的轻量工具

Java动漫链接生成器:批量整理番剧资源链接的轻量工具

简介:这是一款面向动漫资源采集与二次元工具开发者的轻量级命令行程序,基于Java实现,可根据指定标签批量生成动漫图片链接,适合需要批量获取Danbooru图源、做数据采集或练手Java命令行项目的开发者。资源包共17个文件,…

2026/10/20 19:38:12 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →