PHP微交易源码解析:高频轻量级金融系统实战指南
简介本资源是一套基于PHP开发的微盘微交易系统完整源码面向Web开发者、金融系统学习者及PHP中级以上技术人员用于快速搭建小额外汇或虚拟货币类交易平台掌握高频交易系统核心模块设计。压缩包共4362个文件主体为2854个PHP业务逻辑文件与272个PHPT测试脚本辅以124个JS交互脚本、173个PNG界面素材、90个YML配置文件及3个SQL数据库脚本涵盖用户认证、订单管理、资金结算、安全防护防SQL注入/XSS等全链路功能整体19.41MB结构清晰模块耦合度适中便于二次开发与教学分析。已有1650人学习下载配套说明文档详述环境部署PHP5.4MySQLApache/Nginx、数据库初始化及关键配置项结合源码可深入理解实时交易数据流处理与金融级会话管理机制。1. 微盘微交易PHP源码不是“开盒即用”的交易系统而是理解高频轻量级金融逻辑的实战沙盒“微盘微交易PHP源码.zip”这个标题在开发者社区里常被误读为“一套能直接上线跑现货/期权的合规交易平台”。事实恰恰相反——它本质是一套高度简化、去监管适配、聚焦前端交互与后端状态同步逻辑的教学级参考实现。它不包含真实资金清算、第三方支付对接、KYC认证、风控引擎或交易所行情网关但完整呈现了“用户下单→订单生成→倒计时→结果判定→盈亏计算→账户余额更新”这一闭环中PHP如何用最小耦合方式组织状态管理、避免并发写冲突、控制请求幂等性。适合两类人一是刚接触金融类Web开发的PHP工程师想绕过庞杂的券商API文档先吃透“时间驱动型交易”的核心状态机二是需要快速搭建内部模拟训练平台的技术负责人用它作基线再逐层叠加风控、审计、对账模块。它解决的不是“怎么持牌”而是“怎么让100个用户同时点‘涨’时数据库不丢单、不超卖、不重复结算”——这才是真正卡住很多团队落地的第一道坎。2. 源码结构解剖从入口文件到核心状态机看清每一层在做什么2.1 入口路由与请求生命周期index.php 如何成为流量守门员整个系统以index.php为唯一入口典型PHP单入口模式所有HTTP请求均经由它分发。关键不在路由逻辑本身而在于它强制注入的三重守卫// index.php 片段 session_start(); if (!isset($_SESSION[user_id])) { header(Location: /login.php); exit; } // 防暴力刷单基于IPUserAgent的简易限流非生产级 $ip $_SERVER[REMOTE_ADDR]; $user_agent substr($_SERVER[HTTP_USER_AGENT] ?? , 0, 64); $cache_key rate_limit:{$ip}:{$user_agent}; if ($redis-exists($cache_key) $redis-incr($cache_key) 5) { http_response_code(429); echo json_encode([code429, msg请求过于频繁]); exit; }提示此处的$redis是硬编码连接实际部署必须抽离为配置项。新手易犯错误是直接改localhost地址却忽略Redis密码和DB索引导致所有接口返回空JSON——因为json_encode(null)就是空字符串。该入口文件不做业务处理只做身份校验、基础限流、全局异常捕获set_exception_handler和自动加载注册spl_autoload_register。真正的业务逻辑全部下沉至/app/Controller/目录下按功能切分TradeController.php处理下单/平仓BalanceController.php管理余额ReportController.php生成简单统计。这种分层让代码可测试性大幅提升你可以单独require_once某个Controllermock掉$this-model后直接调用方法验证逻辑。2.2 核心模型TradeModel.php 中的状态机与原子操作交易状态流转是本源码最值得深挖的部分。TradeModel.php并未使用Eloquent或Doctrine等ORM而是手写PDO预处理事务封装原因很实在微交易每笔操作必须在毫秒级完成ORM的查询构建、对象映射、关系加载全是不可控延迟。关键状态字段定义在数据库表trades中字段名类型含义取值示例statusTINYINT订单状态0待开始,1进行中,2已结束,3已取消resultTINYINT结果标识-1跌,0平,1涨end_timeINT结束Unix时间戳1717023600下单逻辑的核心是createTrade()方法它用一个事务包裹三步操作// app/Model/TradeModel.php public function createTrade($userId, $amount, $direction, $duration) { $pdo $this-pdo; try { $pdo-beginTransaction(); // 1. 检查用户余额是否充足SELECT FOR UPDATE 锁行 $stmt $pdo-prepare(SELECT balance FROM users WHERE id ? FOR UPDATE); $stmt-execute([$userId]); $user $stmt-fetch(PDO::FETCH_ASSOC); if ($user[balance] $amount) { throw new Exception(余额不足); } // 2. 扣减余额UPDATE $stmt $pdo-prepare(UPDATE users SET balance balance - ? WHERE id ?); $stmt-execute([$amount, $userId]); // 3. 创建订单INSERT $endTime time() $duration; $stmt $pdo-prepare(INSERT INTO trades (user_id, amount, direction, status, end_time) VALUES (?, ?, ?, 0, ?)); $stmt-execute([$userId, $amount, $direction, $endTime]); $tradeId $pdo-lastInsertId(); $pdo-commit(); return $tradeId; } catch (Exception $e) { $pdo-rollback(); throw $e; } }参数说明$duration单位为秒常见值为601分钟、3005分钟。注意FOR UPDATE必须在事务内且后续有UPDATE/DELETE才生效否则锁会立即释放——这是新手调试时发现“并发下单仍超卖”的根本原因他们把SELECT FOR UPDATE写在了事务外。此设计牺牲了ORM的便利性换来了对数据库行为的完全掌控。当你看到SELECT ... FOR UPDATE和显式beginTransaction()你就该明白这里没有魔法只有对ACID最朴素的敬畏。2.3 倒计时与结果判定Cron脚本 vs WebSocket本源码选了第三条路微交易最反直觉的设计在于它不依赖WebSocket实时推送结果也不靠高频Cron轮询。源码采用“懒判定”策略——结果只在用户主动请求/api/trade/status?id123时计算。判定逻辑在TradeModel::checkResult($tradeId)中public function checkResult($tradeId) { $stmt $this-pdo-prepare( SELECT id, user_id, amount, direction, end_time, status, result FROM trades WHERE id ? AND status 1 ); $stmt-execute([$tradeId]); $trade $stmt-fetch(PDO::FETCH_ASSOC); if (!$trade || time() $trade[end_time]) { return [status running, remain $trade[end_time] - time()]; } // 到期生成随机结果真实场景应接入行情API $random mt_rand(0, 100); $result ($random 50) ? 1 : (($random 45) ? -1 : 0); // 涨5%、跌5%、平90% // 更新订单状态与结果带条件更新防止重复执行 $stmt $this-pdo-prepare( UPDATE trades SET status 2, result ? WHERE id ? AND status 1 ); $stmt-execute([$result, $tradeId]); if ($stmt-rowCount() 1) { // 仅当更新成功即首次判定才更新用户余额 $payout $this-calculatePayout($trade[amount], $result); $this-updateUserBalance($trade[user_id], $payout); } return [ status finished, result $result, payout $this-calculatePayout($trade[amount], $result) ]; }逻辑说明mt_rand()仅为演示生产环境必须替换为加密安全的随机数如random_int()或行情快照。关键在WHERE id ? AND status 1—— 这保证了即使10个请求同时到达也只有一个能成功更新状态其余9个rowCount()返回0直接跳过余额更新彻底规避重复结算。这种设计大幅降低服务器压力没有长连接维持没有每秒扫描的Cron任务。用户看到的“实时倒计时”实为前端JS计算endTime - now后端只在“终点时刻”做一次原子判定。它用空间换时间把复杂度从服务端转移到了客户端是轻量级系统的经典取舍。3. 本地运行四步法从解压到首笔模拟交易成功3.1 环境准备PHP版本、扩展与数据库初始化本源码要求明确避坑第一关就在环境PHP版本严格限定7.4.33或8.0.28因使用了match表达式及部分废弃函数的兼容写法。php -v必须精确匹配高版本如8.2会因mysql_connect()被移除而报错。必需扩展pdo_mysql,redis,mbstring,json,curl。特别注意redis扩展需与Redis服务端协议兼容源码用phpredis非predis。数据库MySQL 5.7InnoDB引擎字符集utf8mb4_unicode_ci。初始化SQL脚本位于/sql/init.sql执行前请确认# 登录MySQL并创建数据库 mysql -u root -p -e CREATE DATABASE microtrade DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 导入表结构与初始数据 mysql -u root -p microtrade sql/init.sql注意init.sql中users表插入了一条测试用户testuser密码为明文123456仅用于本地测试。生产环境必须删除此行并强制用户通过注册流程创建。3.2 配置文件修改database.php 与 config.php 的三个致命参数所有配置集中于/config/目录。必须修改的三项/config/database.phpreturn [ host 127.0.0.1, // 不要写 localhost可能走socket dbname microtrade, // 必须与你创建的库名一致 username root, // 建议新建专用账号权限仅限本库 password , // 密码为空时留空字符串勿删此项 charset utf8mb4 ];/config/config.phpreturn [ debug true, // 开发期设true关闭时错误不暴露 base_url http://localhost/microtrade, // 必须与你的访问URL完全一致影响Cookie域 redis_host 127.0.0.1, redis_port 6379, redis_db 0 ];Web服务器根目录设置Apache需开启mod_rewriteNginx需配置try_files $uri $uri/ /index.php?$query_string;。若用PHP内置服务器命令为php -S localhost:8000 -t public/ public/router.php其中public/router.php是重写规则文件确保静态资源CSS/JS能正确加载。3.3 首笔交易全流程验证从登录到查看盈亏浏览器访问http://localhost/microtrade/login.php输入账号testuser密码123456登录成功跳转至首页首页点击“开始交易”选择金额10方向“涨”周期“60秒”点击“确认下单”页面显示“订单已创建剩余时间59秒…”等待60秒后手动刷新页面或点击“查询结果”看到{status:finished,result:1,payout:18}表示盈利8元本金10元收益率80%验证要点此时检查数据库users表testuser的balance应为1000 8 1008初始余额1000。若仍是1000说明结果判定未触发——大概率是end_time时间戳计算错误时区未设为Asia/Shanghai或Redis连接失败导致限流拦截。4. 避坑指南五个血泪经验总结省下三天调试时间4.1 现象下单后页面卡死Network面板显示500 Internal Server Error原因PHP错误日志未开启或display_errorsOff导致错误被静默吞掉。源码中index.php有error_reporting(E_ALL)但若服务器全局配置display_errorsOff则前端只看到500。解决在index.php顶部立即加入ini_set(display_errors, 1); ini_set(error_log, __DIR__./../logs/php_error.log); error_reporting(E_ALL);并确保logs/目录存在且Web服务器用户有写权限。4.2 现象多用户同时下单数据库trades表出现负余额原因SELECT ... FOR UPDATE未生效。常见于① 表引擎不是InnoDBMyISAM不支持行锁② 查询条件未命中索引user_id字段无索引③ 事务未开启或提前提交。解决执行SHOW CREATE TABLE users;确认引擎为InnoDB执行SHOW INDEX FROM users;确认user_id有索引在createTrade()开头加var_dump($pdo-inTransaction()); die;确认事务处于活动状态。4.3 现象倒计时显示“剩余时间-120秒”结果立即判定为“已结束”原因服务器时区与前端JavaScript时区不一致。PHP默认时区为UTC而JSnew Date()使用浏览器本地时区如Asia/Shanghai。time()返回UTC时间戳但前端计算endTime - now时now是本地时间戳差值错误。解决在index.php顶部强制设置时区date_default_timezone_set(Asia/Shanghai);并确保MySQL服务器时区也为08:00SELECT global.time_zone, session.time_zone;。4.4 现象Redis连接失败但错误日志无任何提示所有接口返回空原因源码中Redis实例化未做异常捕获。new Redis()失败时抛出RedisException但代码未try/catch导致脚本中断且无输出。解决修改/app/Helper/RedisClient.php的构造方法public function __construct($host, $port, $db) { try { $this-redis new Redis(); $this-redis-connect($host, $port); $this-redis-select($db); } catch (RedisException $e) { error_log(Redis connection failed: . $e-getMessage()); throw new Exception(缓存服务不可用请检查Redis配置); } }4.5 现象登录后无法跳转始终停留在login.phpCookie中无PHPSESSID原因session_start()前有输出包括BOM头、空格、echo。Windows记事本保存的PHP文件常含UTF-8 BOM导致session_start()失败。解决用VS Code或Notepad打开所有PHP文件编码格式选“UTF-8 无BOM”另存。并在index.php第一行加入if (session_status() PHP_SESSION_NONE) { session_start(); }5. 进阶改造把教学沙盒变成可用原型的三个关键动作5.1 接入真实行情用 WebSocket 替代mt_rand()的最小改动方案源码的随机结果显然不能用于真实场景。最轻量的行情接入是订阅公开WebSocket行情源如币安测试网wss://testnet.binance.vision/ws。改造点仅两处新增行情监听服务创建/bin/ticker_listener.php用Ratchet或原生stream_socket_client连接WS解析kline数据将最新K线收盘价存入Redis// 存入 key: ticker:btcusdt:close value: 26543.21 $redis-setex(ticker:btcusdt:close, 60, $kline[c]);修改checkResult()中的结果生成逻辑// 替换原来的 mt_rand() $lastPrice $redis-get(ticker:btcusdt:close); $openPrice $redis-get(ticker:btcusdt:open_{$tradeId}); // 下单时存入 if ($lastPrice $openPrice) { $result ($lastPrice $openPrice) ? 1 : (($lastPrice $openPrice) ? -1 : 0); } else { $result 0; // 行情不可用时判平保障系统可用性 }关键技巧行情价格必须在用户下单时createTrade就存一份open_{$tradeId}否则判定时拿不到“开盘价”无法计算涨跌。这就是为什么源码中createTrade除了扣款、建单还要额外写一次Redis。5.2 添加防机器人验证用极简Token机制替代图形验证码微交易场景下图形验证码体验差、易被破解。源码自带的IP限流太粗暴。推荐用“一次性Token”用户进入交易页时后端生成token hash_hmac(sha256, uniqid().time(), $_SESSION[secret])存入Redistoken:{$token}有效期60秒前端将token放入下单请求HeaderX-Trade-Token: {$token}TradeController::createTrade()中验证$token $_SERVER[HTTP_X_TRADE_TOKEN] ?? ; if (!$redis-exists(token:{$token}) || !$redis-delete(token:{$token})) { throw new Exception(非法请求); }此方案无前端交互成本Token一次有效且与用户Session绑定比纯IP限流精准得多。5.3 审计追踪给每一笔关键操作打上不可篡改的时间戳金融系统必须留痕。源码缺少操作日志。最务实的做法是不建新日志表复用现有trades表的扩展字段。修改表结构ALTER TABLE trades ADD COLUMN created_ip VARCHAR(45) DEFAULT , ADD COLUMN created_ua TEXT DEFAULT , ADD COLUMN updated_at INT DEFAULT 0;然后在createTrade()和checkResult()的UPDATE语句中加入$updated_at time(); $stmt $pdo-prepare(UPDATE trades SET ... , updated_at ? WHERE id ?); $stmt-execute([$updated_at, $tradeId]);我的习惯绝不单独建logs表。所有业务实体的操作痕迹都沉淀在该实体的扩展字段里。这样查一笔订单的全生命周期SELECT * FROM trades WHERE id123一条SQL搞定不用跨表JOIN运维排查时少踩80%的坑。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Python+OpenCV手势识别实战:源码拆解与调参避坑指南

Python+OpenCV手势识别实战:源码拆解与调参避坑指南

简介:基于Python与OpenCV的手势识别课程设计源代码材料,面向计算机视觉初学者及需要完成图像处理类课题的学生。项目以PyCharm为集成环境,完整演示了调用电脑摄像头按帧采集图像、镜像翻转处理、HSV肤色检测、高斯滤波、轮廓查找、凹凸点计算…

2026/10/15 21:05:09 阅读更多 →
政务统一身份认证隐私保护:数据定级、脱敏与不可逆加密落地指南

政务统一身份认证隐私保护:数据定级、脱敏与不可逆加密落地指南

简介:《C0131国家政务服务平台统一身份认证隐私保护要求》是国家政务服务平台统一身份认证体系的重要规范文件,面向政务平台建设者、系统运维人员及信息安全合规人员,解决自然人身份信息在收集、保存、共享、销毁等环节的隐私保护与分级管控问…

2026/10/15 21:05:09 阅读更多 →
CMMI V2.0不是打分表,而是工程能力诊断框架

CMMI V2.0不是打分表,而是工程能力诊断框架

简介:本资源为CMMI能力成熟度模型集成V2.0官方中文完整版PDF文档,面向项目管理从业者、过程改进工程师、质量体系负责人及企业数字化转型推进人员,旨在提供一套权威、系统、可落地的过程改进路线图。文档由CMMI研究院主导开发,覆盖…

2026/10/15 21:05:09 阅读更多 →

最新新闻

FlinkSql on YARN 提交避坑指南:从踩坑到稳定运行

FlinkSql on YARN 提交避坑指南:从踩坑到稳定运行

1. 为什么一个提交命令能折腾我一整天第一次把 FlinkSql 任务往 YARN 上提交的时候,我以为这活儿半小时就能收工。结果从上午十点一直折腾到凌晨一点,中间经历了任务提交后立刻消失、日志里全是看不懂的类加载报错、资源申请卡在 ACCEPTED 状态不动、SQL…

2026/10/15 21:59:11 阅读更多 →
贝塞尔曲线从原理到实战:控制点、德卡斯特里奥算法与工程避坑指南

贝塞尔曲线从原理到实战:控制点、德卡斯特里奥算法与工程避坑指南

1. 为什么贝塞尔曲线值得你花时间搞懂如果你做过图形界面、玩过矢量绘图、写过动画引擎,甚至只是用过PPT里的曲线工具,那你大概率已经和贝塞尔曲线打过照面了,只是当时未必叫得出它的名字。贝塞尔曲线是一种用控制点来定义的参数化曲线&#…

2026/10/15 21:59:11 阅读更多 →
Flask实战:构建AI科普与智能客服可视化平台

Flask实战:构建AI科普与智能客服可视化平台

标题里的“840p03tc”其实是一串内部项目备案编号,第一次看到的人基本都会愣一下。拆开来看,这个项目要做的事情很清晰:用 Flask 搭建一个集人工智能知识科普、AI 智能客服、人脸识别能力于一体的可视化 Web 平台。整个项目既涉及 Web 后端开…

2026/10/15 21:59:11 阅读更多 →
海康威视存储服务器配置:从容量估算到RAID与录像计划落地

海康威视存储服务器配置:从容量估算到RAID与录像计划落地

简介:海康威视存储服务器配置步骤.pdf 是一份面向安防监控集成商、IT运维及弱电项目人员的实操型文档,重点解决在 iVMS-4200 环境下启用存储服务器并完成录像存储配置的常见问题。资源格式为PDF,仅1个文件,压缩包大小228KB&#x…

2026/10/15 21:59:11 阅读更多 →
Linux下用exfat-fuse挂载exFAT设备:从原理到排错全指南

Linux下用exfat-fuse挂载exFAT设备:从原理到排错全指南

简介:Linux 默认不直接支持 ExFAT 文件系统,exfat-fuse 正是基于 FUSE 框架的开源驱动,让普通用户无需内核模块即可挂载、读写 ExFAT 格式的 U 盘、SD 卡等便携设备。面向需要跨平台交换大文件的 Linux 使用者、嵌入式开发者及系统运维人员。…

2026/10/15 21:59:11 阅读更多 →
一招教你解决deepseek不能识别图片的问题:用TaoToken统一Key接入qwen3-vl-plus多模态

一招教你解决deepseek不能识别图片的问题:用TaoToken统一Key接入qwen3-vl-plus多模态

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

2026/10/15 21:58:08 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →