news 2026/9/19 18:40:21

TikTok Shop API对接实战:PHP密钥获取与自动化开发指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TikTok Shop API对接实战:PHP密钥获取与自动化开发指南

做跨境电商的,尤其是多店铺运营的老哥,一定对TikTok Shop后台的重复操作深有体会:商品上架、库存同步、订单整理、物流单号回填、退款单处理……每个店单独登后台翻来覆去点,时间全耗在机械劳动上。所以我一直建议团队尽早接入TikTok Shop API,把能自动化的全部自动化。这个主题其实不算新,但真正阻碍大多数人落地的,往往是第一步——API密钥获取,以及拿到密钥之后怎么用PHP快速和业务对接起来。这篇就把我实际跑通的经验完整写出来,从密钥体系、认证流程、PHP封装到常见报错排查,一步都不落下,适合正在自建轻量ERP、做独立开发或者服务商系统对接的PHP工程师参考。

先说结论:TikTok Shop API对接没有想象中那么神秘,核心就是理解它的App Key/App Secret认证体系,会写签名,会处理Access Token生命周期,然后按官方文档把接口一个个调通。PHP在这件事上有天然优势,生态成熟、部署灵活,中小团队不用像Java那样堆一堆框架,一个轻量客户端类就能把所有事情办了。下面直接进入正题。

1. 项目核心拆解:TikTok Shop API到底能做什么

1.1 为什么我说API对接是跨境电商团队的“分水岭”

TikTok Shop当下的体量已经不小,很多卖家手里不止一个店铺,甚至同时开美区、英区、东南亚区的店。没有API对接之前,运营每天的工作状态基本是:打开A店铺后台,导出订单,手工核对库存,再去B店铺重复同样的事。一旦单量上来,人力完全跟不上,错单漏单也就成了常态。

接API的作用就是把“人盯后台”变成“系统盯后台”。商品信息可以批量同步到多个店铺,订单数据可以实时下载到本地系统,发货之后物流单号通过接口回填,售后单也能定时抓取。我在自己项目里落实之后,运营每天花在重复操作上的时间至少降了六成,而且人工误操作的概率明显下降。API对接已经不是“要不要做”的问题,而是“什么时候做”的问题。

1.2 为什么选PHP而不是Java或者Python

技术选型上,我见到不少团队一上来就考虑Java或者Go,理由是“大厂都在用”。但说实话,对于中小卖家和独立开发者,PHP的性价比非常高。首先是部署简单,一台普通云服务器装好Nginx和PHP-FPM就能跑;其次PHP对数组和JSON的处理太顺手了,API返回的数据基本都是JSON结构,直接用json_decode转成数组操作,逻辑写起来非常快。

拿我当时的情况举例:项目需要对接TikTok Shop商品、订单、物流三个模块,另外还有本地MySQL库存表要联动。用PHP实现,一个核心请求类加三个业务Service文件,总共不到一千行代码就完成了第一版。如果换成Java,光Spring Boot环境和依赖管理就得折腾一两天。当然PHP在高并发场景下有短板,但普通卖家的店铺接口调用量远没到那个级别,完全够用。

1.3 整体对接架构怎么搭

对接TikTok Shop API,我建议按三层结构来组织代码:

  • 配置层:保存App Key、App Secret、商店的Cipher(如果涉及)、Access Token等敏感信息,单独放配置文件,不进Git仓库。
  • 核心客户端层:负责签名生成、HTTP请求发送、Token自动刷新、错误处理,封装成独立的TikTokShop Client类。
  • 业务逻辑层:按商品、订单、物流、售后等模块拆成不同的Service,只关心数据拼装和本地存储,不重复处理签名和请求细节。

这样分层的优势很明显,以后官方接口升级或者新增模块,只需要改核心Client类或者新增Service,不会牵一发动全身。我踩过的坑是早期把所有逻辑都堆在一个文件里,后来加订单模块时差点改崩,重构一次就长记性了。

2. API密钥体系与认证机制详解

2.1 从App Key到Access Token:整条认证链路

了解密钥之前,要先建立TikTok Shop开放平台的整体认证模型。和大多数现代开放平台一样,TikTok Shop API使用的是OAuth 2.0授权流程,涉及的关键凭证有这么几个:

  • App Key(应用Key):相当于应用的“用户名”,创建应用后由开放平台生成,用于识别调用方是谁。
  • App Secret(应用密钥):相当于应用的“密码”,用于生成签名和换取Token,必须严格保密。
  • Authorization Code(授权码):商家授权你的应用访问其店铺数据后产生的临时凭证,有效期很短,通常只有几分钟。
  • Access Token(访问令牌):真正调接口时用的凭证,放在请求头里标识“我有权限访问这个店铺的数据”。
  • Refresh Token(刷新令牌):Access Token过期后,用它重新换取新的Access Token,生命周期较长。

整个授权链路简单来说就是:创建应用拿到App Key/App Secret,商家点击授权跳转到TikTok Shop的授权页,同意后回调地址收到Authorization Code,然后用Code换Access Token和Refresh Token,接下来调接口就带Access Token。Token过期后用Refresh Token续期,Refresh Token也过期就重新走授权流程。

这里有个新手经常搞混的点:很多人以为拿到App Key和App Secret就能直接调接口,实际上还差一步授权。App Key只是证明“你是谁”,Access Token才证明“你能访问哪个店铺的什么数据”。我自己刚接触时也犯过这个错,拿着App Key试了半天接口,一直报未授权错误,后来把授权流程走通才解决。

2.2 签名机制为什么不能省

TikTok Shop API的每个请求除了带Access Token,还要求对请求参数做签名,常见做法是用HMAC-SHA256算法,把时间戳、请求路径、业务参数等按规则排序后,用App Secret作为密钥生成一个签名串,拼到请求里。服务端收到后会按照同样规则重新计算签名,如果不一致就拒绝请求。

签名机制的核心目的有两个:一是防止参数被篡改,如果有人拦截了请求,改了商品价格或者订单数量,签名校验失败服务端直接拒绝;二是防止重放攻击,因为签名里带了时间戳,超过一定时间范围的请求会被判定为无效,即使被抓包也不能无限重放历史请求。

在我的实际项目里,签名这块是坑最多的环节。前后花了差不多一晚上,发现是参数排序规则和官方文档不一致,少了对某些参数的处理,导致签名一直校验失败。后面我把签名逻辑单独封装成一个方法,并用单元测试固定了一批已知参数和结果的测试用例,之后再也没有因为签名问题卡过。

2.3 密钥安全这件事,怎么说都不为过

关于App Secret,有一点我必须单独拿出来强调:绝对不能放到前端代码里,也不能提交到公开的Git仓库。我之前帮一个客户排查问题,发现他把App Secret直接写在了JS文件里,等于把账号密码挂在门口,任何人打开网页源码就能看到,后果不堪设想。

正确的做法是把密钥放在服务端环境变量或者独立的配置文件中,并且确保这个文件被Git忽略。线上环境可以通过部署工具注入环境变量,本地开发用.env文件管理,这样即使代码仓库公开,密钥也不会泄露。Access Token同理,不要存在前端LocalStorage里,要存在服务端数据库或者缓存中。

2.4 沙箱环境:调试阶段的“安全区”

对接初期,强烈建议在TikTok Shop开放平台提供的沙箱环境里调试。沙箱环境和正式API是隔离的,用的是一套模拟数据,不会影响真实店铺。我在沙箱里测试了商品创建、订单下载、退款处理这些接口,把签名、Token刷新、异常处理都调通了,才切换到生产环境。

沙箱和生产的密钥通常不是同一套,注意区分。切换环境时最容易出的问题就是忘了换App Key/App Secret,用沙箱凭证调生产接口,返回的全是身份验证失败。别问我是怎么知道的,问就是经历过。

3. 密钥获取实操:从注册到拿到Token

3.1 第一步:确定你的开发者身份

进入TikTok Shop开放平台前,先想清楚你的开发者身份。官方体系里主要有两类:

  • 平台卖家开发者:如果你只是自己运营若干TikTok Shop店铺,想通过API管理自己的店铺,那就以卖家身份进入开发者后台,创建的应用关联自己的店铺。
  • 服务商开发者:如果你是给多家商家做工具、做ERP系统,需要以服务商身份入驻,经过平台审核后,可以调用接口时让商家授权,管理不同店铺的数据。

这个身份选择直接影响后续创建应用和授权流程,建议提前确认。我见过有人用个人账号注册了应用,想帮别的店铺做数据对接,结果授权流程根本走不通,后来重新以服务商身份走了入驻流程。

3.2 第二步:登录开发者后台,创建应用拿App Key/App Secret

登录TikTok Shop开放平台(卖家后台里通过“开发者中心”入口进入)后,找到“应用管理”或“我的应用”,点击创建应用。填写应用名称、应用描述、应用图标,选择应用类型(卖家应用还是服务商应用),绑定你要对接的店铺。

创建成功后,应用详情页就会显示App Key和App Secret。这两个值就是后续所有API调用的通行证基础。建议第一时间把App Secret复制到本地配置文件里,因为有些平台只展示一次,关闭页面后要重新生成,比较麻烦。

App Key通常是明文显示,App Secret一般有“显示”按钮,点击后才展示完整内容。整体流程和大多数开放平台类似,实际操作时如果界面有变动,以官方页面提示为准。

3.3 第三步:配置授权回调地址和应用权限

拿到密钥后,别急着写代码,先配置应用的授权设置。这里有几个关键项:

  • 授权回调地址(Redirect URI):商家授权完成后,平台会带着Authorization Code跳回这个地址。回调地址必须和配置时的域名完全匹配,包括协议和端口,否则授权时直接报错。
  • 应用权限(Scope):按需申请权限范围,比如商品读取、商品编辑、订单读取、物流信息读取、售后管理等等。原则是最小化授权,用不到的功能不要勾选,降低安全风险。

回调地址这个坑非常经典。我当年把回调地址配成了https://example.com/callback,但实际跳转时带了http协议,或者后面多了一个斜杠,平台直接提示回调地址不匹配。所以配置时尽量把地址写完整,并在代码里做兼容处理。

3.4 第四步:完整走一遍授权换Token流程

应用配置完成后,访问授权链接让商家确认授权。授权链接通常长这样(以官方实际文档为准):

https://open-api.tiktokglobalshop.com/audit/page/authorize?app_key=你的AppKey&state=随机字符串&redirect_uri=你的回调地址

商家登录后点击“同意授权”,浏览器会带着auth_code参数跳回回调地址。PHP这边在回调接口里接收这个参数,然后调用官方token接口换取Access Token和Refresh Token。换Token的关键参数包括app_keyapp_secretauth_codegrant_type=authorized_code等。

换Token的PHP简化示例:

$appKey = 'your_app_key'; $appSecret = 'your_app_secret'; $authCode = $_GET['auth_code'] ?? ''; $tokenUrl = 'https://open-api.tiktokglobalshop.com/auth/token'; $payload = [ 'app_key' => $appKey, 'app_secret' => $appSecret, 'auth_code' => $authCode, 'grant_type' => 'authorized_code', ]; $ch = curl_init($tokenUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); $response = curl_exec($ch); curl_close($ch); $data = json_decode($response, true); // 成功响应中通常会拿到 access_token 和 refresh_token file_put_contents(__DIR__ . '/access_token.json', json_encode($data));

第一次成功拿到Access Token后,建议先把Token持久化保存。后面所有接口调用都靠它,而它又有有效期,所以要规划好刷新机制。最简单的做法是把Token和刷新Token存到数据库或缓存文件,每次请求前判断是否快过期,快过期就用刷新接口先刷新一遍。

3.5 刷新Access Token的自动续期方案

Access Token的有效期通常是几个小时到几天不等(具体以官方文档为准),Refresh Token的有效期更长。为了不让业务过程中突然出现Token过期报错,我建议做一个自动续期逻辑:

  • 在配置文件或数据库里记录Token的获取时间和过期时间。
  • 每次调用API前检查当前时间 + 预留时间是否超过过期时间,如果超过,就使用Refresh Token调刷新接口,拿到新的Access Token后更新存储。
  • 刷新接口的调用示例和换Token类似,只是把grant_type换成refresh_token,并传入refresh_token字段。

这个“检查-刷新-更新”的闭环一定要做,否则你会在某个深更半夜的批量同步任务里被一堆401错误砸醒。我是把刷新逻辑直接封装进了核心Client类的getAccessToken()方法里,调用方完全无感。

4. PHP实战对接:从配置到拉取数据

4.1 项目目录结构怎么摆

一份清爽的项目结构能帮你省掉大量后期维护时间。我这边的参考结构是这样的:

tiktok-shop-api/ ├── config/ │ └── config.php # 密钥、环境配置 ├── src/ │ ├── Client.php # 核心请求客户端 │ ├── ProductService.php # 商品模块 │ ├── OrderService.php # 订单模块 │ └── LogisticsService.php # 物流模块 ├── storage/ │ └── tokens.json # Token持久化存储 ├── callback/ │ └── oauth_callback.php # 授权回调入口 └── public/ └── index.php # 测试入口

config.php里不写真实密钥,而是读取环境变量,比如getenv('TIKTOK_APP_KEY'),这样配置文件本身即使被误传,也不会泄露敏感信息。Token存储建议用数据库表,Redis也可以,总之别写在临时文件里,服务器重启就丢。

4.2 核心Client类的签名与请求封装

所有API调用的公共逻辑都放在Client.php里。它至少要完成这几件事:

  • 组装基础参数,包括app_keytimestampaccess_token等。
  • 按照官方规则生成签名。
  • 发送HTTP请求并解析响应。
  • 自动处理Token刷新。
  • 统一记录请求日志。

签名生成逻辑(以HMAC-SHA256为例)的核心思路是:把所有请求参数按键名升序排序,拼接成字符串,再用App Secret作为密钥做HMAC-SHA256运算。有些接口还会要求把请求路径加入签名串,以官方文档为准。PHP侧可以这样实现:

private function generateSignature(array $params, string $path): string { // 1. 过滤掉签名字段本身 unset($params['sign'], $params['access_token']); // 2. 按键名升序排序 ksort($params); // 3. 拼接参数 $signStr = $path; foreach ($params as $key => $value) { $signStr .= $key . $value; } // 4. HMAC-SHA256,密钥为App Secret return hash_hmac('sha256', $signStr, $this->appSecret); }

这里要注意几个细节:布尔值要转成true或者false字符串,不要传PHP的1或空值;数组参数要序列化成JSON字符串再参与签名;不同接口的签名拼接规则可能有差异,务必以开放平台文档的“签名算法”章节为准。我一位朋友就是直接把所有参数透传,结果数组类型的参数签名永远对不上,折腾了一天。

4.3 拉取商品列表的完整示例

商品接口通常是开发者最需要也最先调试的接口。以拉取店铺商品列表为例,假设接口路径是/product/list,请求时带上分页参数pagepage_size,核心请求方法大致如下:

public function getProductList(int $page = 1, int $pageSize = 20): array { $path = '/product/list'; $params = [ 'app_key' => $this->appKey, 'timestamp' => time(), 'access_token' => $this->getAccessToken(), 'page' => $page, 'page_size' => $pageSize, ]; $params['sign'] = $this->generateSignature($params, $path); return $this->request('POST', $path, $params); }

request方法内部用cURL发送请求,设置JSON请求头,接收响应后统一判断code字段是否为成功值,不是就抛出对应的业务异常。拿到返回数据后,把商品名称、SKU、库存、价格等信息解析出来,存入本地MySQL表,就完成了最基础的商品同步。

我第一次调通这个接口时,内心是有点小激动的,原来后台页面上那些商品数据,通过API十几毫秒就全部拉下来了,而且拿到的是结构化数据,想怎么处理都行。那种“自己动手打通系统”的成就感,确实是单纯看文档体会不到的。

4.4 订单下载与本地数据落地

商品同步只是热身,真正体现API价值的是订单模块。订单接口能拉到买家信息、商品明细、金额、地址、订单状态等,数据量比商品大得多。

我的做法是:写一个定时任务,每五分钟拉取一次增量订单,以订单号为唯一键插入本地订单表,避免重复。状态有变更的订单则执行更新。这样运营查看订单列表时,完全不用去TikTok Shop后台切来切去,在本地系统就能处理发货、打印面单等操作。

订单下载时要注意接口的分页限制和频率限制。大部分开放平台接口单次最多返回一定数量的订单(比如100条),超过就要用游标或者页数翻页。配合update_time之类的参数做增量筛选,可以大幅减少接口调用量。我遇到过一次死循环,是因为翻页参数一直传page=1,结果同一批订单反复拉取,差点把接口配额打满,后来认真看了文档才发现要page++

4.5 回调与Webhook:让数据实时推送给你

轮询是一种玩法,更高效的是接入TikTok Shop的Webhook(回调通知)。比如订单状态变更、买家发起退款、商品审核通过等事件,平台会主动推送消息到你配置的回调URL。

Webhook接收端有一个很重要的点:验签。推送过来的数据带有签名头,需要在PHP端用和平台约定的算法校验一遍,确认数据确实是来自TikTok Shop,避免伪造请求。验签通过后,把事件数据丢进消息队列或者直接处理。

我的验签实现思路是:读取请求头的签名和时间戳,用App Secret对接收到的原始body做HMAC-SHA256,对比是否一致,同时校验时间戳是否在可接受范围内。这样能过滤掉绝大多数的伪造请求和重放请求。这里就不贴完整代码了,因为每个平台的验签规则细节不一样,参考官方文档实现是唯一靠谱的方案。

5. 常见问题与排查技巧实录

5.1 高频错误码速查表

对接过程中一定会遇到各种错误码,我把常见的几类整理成了一张表,方便快速定位。注意具体的错误码数字以官方文档为准,我这里主要是帮大家建立排查思路。

错误类型可能原因排查方向
身份验证失败App Key/App Secret错误核对密钥是否复制完整,是否换了环境
Token无效或过期Access Token过期或未正确传递检查Token刷新逻辑,确认请求头正确
签名不一致参数排序或拼接规则不对用官方接口调试工具比对签名规则
权限不足Scope未勾选或未重新授权检查应用权限配置,重新走授权流程
接口限流请求太频繁增加退避策略,控制并发
参数校验失败参数类型或格式不对对照文档逐项检查参数
店铺未授权商家未同意授权或授权已解除重新发起授权链接

遇到错误码,先不要慌,第一步永远是把完整的请求参数和响应体打印出来,结合文档逐字对比。很多问题其实早就在响应信息里写明白了,只是我们一眼扫过去忽略了。

5.2 签名错误的排查三板斧

签名问题是API调试中最大的拦路虎,我自己的排查流程基本固定为三步:

  • 第一板斧:检查参数是否按照官方规则完成了排序、拼接、转字符串等预处理。很多人栽在数组转JSON后多空格或者key顺序不对。
  • 第二板斧:确认参与签名的参数集合和实际发送的参数一致。有个经典坑是签名时包含了sign自身,或者漏掉了access_token
  • 第三板斧:用官方调试工具或者Postman的预请求脚本,拿一组固定的参数和密钥,对比自己代码生成的签名是否一致。如果一致说明签名逻辑没有问题,问题出在了请求发送环节。

我强烈建议写一个独立的签名测试用例,固定输入输出,作为回归测试的一部分,这样以后官方升级或者改逻辑时,能第一时间发现签名代码是否被破坏。

5.3 权限范围:为什么一直提示无权限

搞定了密钥和签名,又冒出来“无权限”报错,这多半和Scope有关。创建应用时勾选的应用权限,决定这个App能访问哪些接口。比如你只勾选了“商品读取”,但跑去调订单接口,那必然被拒。

另外还有一种情况:应用创建后,如果后来增加了新的Scope,之前已经授权过的商家可能需要重新走一遍授权流程,新的权限才会生效。所以遇到权限相关报错,建议先去开发者后台确认应用权限,再去商家后台确认授权状态。有个项目里运营反馈某个功能突然不能用了,我查了半天,发现是平台侧更新了权限体系,需要重新审核应用,流程走完才恢复。

5.4 限流与并发:别把接口当没有上限的数据库用

很多开放平台接口都有调用频率限制,TikTok Shop也不例外。一旦单位时间内请求过多,返回的错误码会提示限流。我的实战体会是,批量任务里一定要做限速处理,比如每次请求之间sleep 200毫秒,分摊请求压力。同时把任务拆成小批次,避免单个进程长时间霸占接口。

如果业务量确实很大,建议实现一个简单的token bucket限流器,控制本地请求速率。还可以加上指数退避重试逻辑:遇到限流错误时,等待一段时间再重试,而不是瞬间把接口打爆。我早期就是太急躁,循环里不加任何停顿,结果全网店铺的Token集体被限流,当天所有同步任务全部失败,教训深刻。

5.5 密钥泄露之后的紧急处理

万一App Secret泄露了,不要犹豫,立刻去开发者后台重置密钥。重置后,旧的App Secret会立即失效,所有用旧密钥签名的请求都会失败,需要用新密钥重新跑一遍签名逻辑。同时检查一下Access Token是否也需要重新授权,因为这取决于平台的Token体系设计。

处理泄露问题的动作要点:

  • 先去后台重置App Secret,生成新的。
  • 更新服务端环境变量和配置,确保旧密钥不再被使用。
  • 观察一段时间日志,确认没有异常调用。
  • 如果Token体系也暴露,考虑撤销所有已授权的Token,让商家重新授权。

安全无小事,哪怕只是疑似泄露,也不要抱侥幸心理,该重置就重置。

写在最后的几点实在建议

整个TikTok Shop API对接流程走下来,我最想强调的是“别跳步”:先沙箱调通认证,再对接商品,然后订单,最后才轮到复杂的售后和Webhook。很多人一上来就想把所有功能一次做完,结果被签名、授权、权限一个一个卡住,心态直接崩掉。我个人的做法是每完成一个模块就部署到测试环境跑几天,确认稳定了再继续下一个模块,这样问题边界清晰,排查起来也快。

另外,日志记录这项工作千万别省。我在Client类里会记录每一次请求的URL、参数(脱敏后的)、响应返回和耗时,出了问题直接翻日志,很多所谓的神秘报错其实都有迹可循。最后还有一个实用小建议:接口调用尽量在PHP的错误处理里把官方返回的messagerequest_id完整记录下来,找平台支持时这些信息是他们定位问题的关键凭证。把这些基础工作做好,整个项目的稳定性会提升一大截。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 18:40:02

如何快速定制 Matter ZAP 插件:面向新手的完整开发指南

如何快速定制 Matter ZAP 插件:面向新手的完整开发指南 【免费下载链接】connectedhomeip Matter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumer…

作者头像 李华
网站建设 2026/9/19 18:37:14

华为2288H-V5装系统全指南:RAID配置与驱动加载实战排障

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

作者头像 李华
网站建设 2026/9/19 18:37:10

fsolve求解电力系统潮流计算:MATLAB实现与工程技巧

简介:电力系统分析中,潮流计算是网络规划与运行的基础,其本质是求解一组节点功率平衡的高阶非线性方程。MATLAB优化工具箱中的fsolve作为通用非线性方程组求根器,只需将节点导纳矩阵Ybus与PQ、PV、松弛节点的物理约束映射为F(x)0的…

作者头像 李华
网站建设 2026/9/19 18:36:57

Swoole 协程 sleep 阻塞,把 Codex 的 Base URL 改到 TaoToken 就能查

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

作者头像 李华
网站建设 2026/9/19 18:36:11

Skills 和 MCP 分不清?TaoToken 这样改 Claude Code 的 settings.json

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

作者头像 李华