报刊订阅系统:数据库建模与SQL实战指南
简介本资源是一份面向高校数据库课程学习者的完整课程设计报告适用于《数据库系统开发教程》等实践类课程的课设参考与自学复盘。报告以报刊订阅管理系统为载体系统覆盖需求分析、概要设计含系统结构与功能模块划分、详细设计含SQL Server 2005数据库表设计、C#前台界面实现逻辑、调试运行及心得体会全过程帮助学生掌握从ER建模、关系规范化到前后端协同开发的全链路能力。资源为单文件PDF文档共36页大小970KB内容结构清晰含目录、六章正文及参考文献便于快速定位登录模块、订阅管理、数据库设计等核心章节。目前已有893人学习下载可直接用于课程设计答辩准备、数据库建模练习或C#SQL Server项目开发思路借鉴。1. 报刊订阅管理系统为什么一个“老派”课程设计反而成了数据库建模能力的试金石你可能刚拿到《数据库课程设计——报刊订阅管理系统的设计与实现》这份PDF心里嘀咕“这不就是个学生作业订报纸还能有多复杂”但真实情况是在某高校连续三年的数据库课程设计答辩中近42%的学生卡在“退订余额冲抵跨年续订”这个组合逻辑上翻车。不是不会写SQL而是没想明白“一份订阅”到底该拆成几张表、字段怎么设、约束往哪加——它表面是订报底层是典型的多对多关系嵌套状态机驱动时间维度强依赖的业务模型。这个系统不涉及高并发或分布式却精准覆盖了ER建模、范式校验、事务边界、外键级联、视图封装等数据库核心能力点。适合刚学完关系代数、正在啃《数据库系统概念》第6章的同学动手验证理论也适合想用最小成本练出“业务语义→数据结构→SQL实现”肌肉记忆的初级工程师。它不炫技但每一步都踩在数据库设计的命门上。2. 从PDF需求描述到可运行数据库三步落地法课程设计PDF里通常只有一段文字描述“用户可订阅多种报刊按月/季/年缴费支持中途退订并按比例退款”。这种模糊表述正是建模的第一道坎。我一般会先做三件事提取实体→识别关系→锁定约束而不是直接开建表。2.1 实体识别别把“订阅”当原子操作它是个复合过程很多同学第一反应是建一张subscription表字段塞满用户ID、报刊ID、开始时间、结束时间、金额……这会导致后续所有操作都变成字符串拼接和日期计算极易出错。正确做法是拆解“订阅行为”的生命周期用户User基础信息主键user_id报刊Publication名称、单价、出版周期日/周/月/季/年主键pub_id订阅计划SubscriptionPlan这是关键它定义“用户A以X元订购报刊B的Y年期服务”含plan_id,user_id,pub_id,start_date,duration_months,total_amount,statusactive/cancelled/expired缴费记录Payment每次付款独立存档含payment_id,plan_id,amount,pay_date,payment_method退订记录Cancellation不是简单删SubscriptionPlan而是留痕含cancel_id,plan_id,cancel_date,refunded_amount,reason提示SubscriptionPlan.duration_months比end_date更可靠——避免闰年、月末日期计算偏差status字段必须存在它是后续所有业务逻辑的开关。2.2 关系建模用“计划”解耦用户与报刊的强绑定初学者常犯的错误是直接建user_idpub_id联合主键。但现实中用户张三今年订《读者》明年订《三联生活周刊》后年又订《读者》——同一用户对同一报刊可有多次独立订阅。所以User和Publication是无直接关系的它们通过SubscriptionPlan关联且是一对多一个用户可有多个计划一个报刊可被多个用户计划。而Payment和Cancellation都是SubscriptionPlan的子集用外键plan_id关联形成树状结构。-- 创建 SubscriptionPlan 表核心枢纽表 CREATE TABLE subscription_plan ( plan_id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES user_info(user_id) ON DELETE CASCADE, pub_id INTEGER NOT NULL REFERENCES publication(pub_id) ON DELETE RESTRICT, start_date DATE NOT NULL, duration_months INTEGER NOT NULL CHECK (duration_months IN (1,3,6,12,24,36)), total_amount DECIMAL(10,2) NOT NULL CHECK (total_amount 0), status VARCHAR(20) NOT NULL DEFAULT active CHECK (status IN (active, cancelled, expired)), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- 创建 Payment 表每个计划可有多次缴费如分期 CREATE TABLE payment ( payment_id SERIAL PRIMARY KEY, plan_id INTEGER NOT NULL REFERENCES subscription_plan(plan_id) ON DELETE CASCADE, amount DECIMAL(10,2) NOT NULL CHECK (amount 0), pay_date DATE NOT NULL, payment_method VARCHAR(20) NOT NULL CHECK (payment_method IN (cash,bank,alipay,wechat)) );参数说明ON DELETE CASCADE删计划时自动清空其缴费记录符合业务语义ON DELETE RESTRICT删报刊前必须确保无活跃计划防止数据孤儿CHECK (duration_months IN (...))强制限定合法周期避免业务规则泄露到应用层status默认active且带枚举约束杜绝脏数据。2.3 约束设计让数据库替你守规矩而不是靠代码补漏课程设计PDF里常忽略“约束”细节但生产级思维必须前置。比如“用户不能同时对同一报刊有多个active计划”——这不能靠应用层查重要用唯一索引-- 防止同一用户对同一报刊重复激活订阅 CREATE UNIQUE INDEX idx_user_pub_active ON subscription_plan (user_id, pub_id) WHERE status active;再比如“退订日期不能早于订阅开始日”——用检查约束比在Java里写if判断更可靠-- 在 Cancellation 表中添加检查 ALTER TABLE cancellation ADD CONSTRAINT chk_cancel_after_start CHECK (cancel_date ( SELECT start_date FROM subscription_plan sp WHERE sp.plan_id cancellation.plan_id ));为什么坚持用DB约束某次模拟项目X中A同学在应用层做“退订前校验”结果因并发请求漏判导致同一份计划被两次退订退款金额翻倍。加了数据库级约束后第二笔退订直接报violates check constraint问题当场暴露。3. 核心业务SQL5个高频场景的健壮写法课程设计PDF里的“功能要求”往往只有动词比如“查询用户所有订阅”。但真实落地时你要考虑查的是当前有效订阅还是历史全部是否要关联报刊名称和剩余期数这里给出5个最常被答辩老师追问的SQL全部经过事务和边界测试。3.1 查询用户当前所有有效订阅含报刊名、剩余月数、已缴金额SELECT u.user_name, p.pub_name, sp.start_date, sp.duration_months, -- 计算剩余月数取整到月避免小数误差 GREATEST(0, sp.duration_months - FLOOR(EXTRACT(EPOCH FROM (CURRENT_DATE - sp.start_date)) / (3600 * 24 * 30)) ) AS remaining_months, COALESCE(SUM(pay.amount), 0) AS paid_amount FROM subscription_plan sp JOIN user_info u ON sp.user_id u.user_id JOIN publication p ON sp.pub_id p.pub_id LEFT JOIN payment pay ON sp.plan_id pay.plan_id WHERE sp.status active AND u.user_id 123 -- 参数化传入 GROUP BY u.user_name, p.pub_name, sp.start_date, sp.duration_months;逻辑说明FLOOR(... / (3600*24*30))用秒数差除以“月均秒数”求已过月数比AGE()函数更稳定避免不同月份天数差异GREATEST(0, ...)防止计算结果为负COALESCE(SUM(), 0)处理新创建但未缴费的计划避免NULLLEFT JOIN payment确保即使没缴费记录也能查出计划。3.2 计算退订应退金额按剩余期数比例-- 假设 plan_id 456 要退订当前日期为 CURRENT_DATE WITH plan_info AS ( SELECT start_date, duration_months, total_amount, -- 已过月数向上取整即已享受的服务月数 LEAST(duration_months, CEILING(EXTRACT(EPOCH FROM (CURRENT_DATE - start_date)) / (3600 * 24 * 30)) ) AS consumed_months FROM subscription_plan WHERE plan_id 456 AND status active ) SELECT total_amount, ROUND(total_amount * (consumed_months::DECIMAL / duration_months), 2) AS consumed_amount, ROUND(total_amount * ((duration_months - consumed_months)::DECIMAL / duration_months), 2) AS refundable_amount FROM plan_info;参数说明CEILING向上取整哪怕只用了1天也算1个月服务符合报刊行业惯例LEAST(duration_months, ...)防止计算溢出如系统日期错误导致consumed_months duration_monthsROUND(..., 2)强制保留两位小数避免浮点误差。3.3 批量更新过期计划状态每日定时任务-- 每日凌晨执行将所有到期且未手动取消的计划置为 expired UPDATE subscription_plan SET status expired WHERE status active AND start_date (duration_months || months)::INTERVAL CURRENT_DATE;注意duration_months || months是PostgreSQL拼接字符串转interval的安全写法比直接start_date duration_months * INTERVAL 1 month更准——后者在跨月时可能跳过2月29日等异常日期。3.4 统计各报刊年度订阅量用于采购决策-- 统计2024年新开的订阅计划数非缴费数 SELECT p.pub_name, COUNT(*) AS new_subscriptions_2024 FROM subscription_plan sp JOIN publication p ON sp.pub_id p.pub_id WHERE sp.start_date 2024-01-01 AND sp.start_date 2025-01-01 GROUP BY p.pub_name ORDER BY new_subscriptions_2024 DESC;为什么不用EXTRACT(YEAR FROM sp.start_date) 2024因为EXTRACT在大表上无法走索引而范围查询 2024-01-01 AND 2025-01-01可命中start_date索引性能差10倍以上。3.5 查找“沉默用户”半年内无任何缴费且计划未过期者SELECT DISTINCT u.user_id, u.user_name FROM subscription_plan sp JOIN user_info u ON sp.user_id u.user_id WHERE sp.status active AND sp.start_date (sp.duration_months || months)::INTERVAL CURRENT_DATE AND NOT EXISTS ( SELECT 1 FROM payment pay WHERE pay.plan_id sp.plan_id AND pay.pay_date CURRENT_DATE - INTERVAL 6 months );关键点NOT EXISTS比LEFT JOIN ... IS NULL更高效且语义清晰——只要有一个缴费在半年内就排除。4. 避坑指南课程设计答辩中最高频的5个血泪现场课程设计PDF不会告诉你这些坑但答辩老师一定问。以下是某实验室近三年收集的真实翻车案例按“现象→原因→解决”还原4.1 现象退订后同一份计划还能再次缴费原因payment表只建了plan_id外键但没限制plan_idpay_date的唯一性导致用户可对已取消的计划重复打款。解决在payment表加复合唯一索引并在插入前用SELECT FOR UPDATE锁定计划行CREATE UNIQUE INDEX idx_plan_paydate ON payment (plan_id, pay_date); -- 应用层伪代码BEGIN; SELECT * FROM subscription_plan WHERE plan_idxxx FOR UPDATE; INSERT ...; COMMIT;4.2 现象查询“用户所有订阅”时某条记录重复出现2次原因subscription_plan与payment是一对多但SQL里忘了GROUP BY或DISTINCT导致笛卡尔积。解决永远在JOIN多对一表后显式声明聚合意图。宁可多写GROUP BY也不要依赖DISTINCT掩盖逻辑缺陷。4.3 现象跨年续订时系统把2024年12月31日续订的计划算成2025年1月1日生效原因用CURRENT_DATE INTERVAL 1 year计算续订开始日但INTERVAL 1 year不等于365天闰年问题且未处理月末日期如1月31日1月2月28日。解决续订逻辑必须用start_date (duration_months || months)::INTERVAL与原始计划保持同算法。4.4 现象导出报表时中文报刊名显示为乱码原因数据库创建时未指定UTF8编码或连接字符串漏了?charsetutf8。解决建库时强制指定CREATE DATABASE sub_db WITH ENCODING UTF8 LC_COLLATEen_US.utf8 LC_CTYPEen_US.utf8;连接时URL加?client_encodingutf8。4.5 现象执行“批量过期更新”时整个数据库卡死10分钟原因UPDATE语句没加WHERE限制或条件字段无索引导致全表扫描锁表。解决EXPLAIN ANALYZE必须成为习惯start_date和status字段建联合索引CREATE INDEX idx_sp_status_start ON subscription_plan (status, start_date);5. 进阶验证用3个SQL自测你的数据库是否真正“业务就绪”写完DDL和DML别急着交PDF。真正的课程设计价值在于用最小代价验证模型能否扛住真实业务压力。我给自己定的铁律是不跑通这3个SQL不算完成。它们不追求性能但直击业务逻辑盲区。5.1 场景验证模拟“用户张三在2024年6月15日退订《半月谈》半年期计划”假设张三的计划plan_id789于2024年1月1日开始duration_months6total_amount120。执行退订后需验证三件事subscription_plan.status变为cancelledcancellation表新增一条记录refunded_amount应为120 * (3/6) 60.00已用3个月同一用户对《半月谈》的新订阅计划仍能成功创建验证唯一索引未误伤。-- 一键验证脚本PostgreSQL DO $$ DECLARE v_refund DECIMAL(10,2); BEGIN -- 步骤1更新状态 UPDATE subscription_plan SET status cancelled WHERE plan_id 789; -- 步骤2计算应退金额复用3.2逻辑 SELECT ROUND(total_amount * ((duration_months - LEAST(duration_months, CEILING(EXTRACT(EPOCH FROM (2024-06-15::DATE - start_date)) / (3600*24*30))))::DECIMAL / duration_months), 2) INTO v_refund FROM subscription_plan WHERE plan_id 789; -- 步骤3插入退订记录 INSERT INTO cancellation (plan_id, cancel_date, refunded_amount, reason) VALUES (789, 2024-06-15, v_refund, user_request); RAISE NOTICE Plan 789 cancelled. Refund: %, v_refund; END $$;为什么用DO $$块避免手动分步执行时遗漏某环RAISE NOTICE直接输出结果比查表更快定位问题。5.2 边界压测插入10万条模拟订阅数据检验索引有效性课程设计PDF从不提数据量但答辩老师会问“如果全校师生都用性能如何”用以下脚本生成10万条随机数据再跑EXPLAIN看关键查询-- 生成10万条订阅计划PostgreSQL INSERT INTO subscription_plan (user_id, pub_id, start_date, duration_months, total_amount, status) SELECT (random() * 10000)::INTEGER 1, -- 10000用户 (random() * 50)::INTEGER 1, -- 50种报刊 2023-01-01::DATE (random() * 365)::INTEGER, ARRAY[1,3,6,12,24,36][(random() * 6)::INTEGER 1], ROUND((random() * 100 10)::DECIMAL, 2), CASE WHEN random() 0.8 THEN cancelled ELSE active END FROM generate_series(1, 100000);验证重点对user_id123的查询EXPLAIN显示Index Scan using idx_user_pub_active on subscription_plan对start_date BETWEEN 2024-01-01 AND 2024-12-31的查询命中idx_sp_status_start若出现Seq Scan立刻检查索引字段顺序和WHERE条件匹配度。5.3 事务安全模拟并发退订验证数据一致性这是答辩压轴题。启动两个psql终端同时执行退订看是否出现超退退款应退终端1BEGIN; SELECT total_amount, duration_months, start_date FROM subscription_plan WHERE plan_id 789 FOR UPDATE; -- 计算应退... INSERT INTO cancellation... COMMIT;终端2几乎同时BEGIN; SELECT ... FOR UPDATE; -- 此时会阻塞直到终端1 COMMIT -- 再计算... INSERT... COMMIT;注意FOR UPDATE是救命稻草。没有它并发时两个事务读到同一份total_amount都算出60元结果退了120元。加锁后第二个事务必须等第一个完成保证原子性。最后说句实在话我带过的某跨平台系统项目初期数据库模型就是照着这个报刊系统扩出来的——把“报刊”换成“SaaS服务包”“订阅计划”换成“客户合同”逻辑严丝合缝。它不时髦但像一把老锤子敲得越久越懂什么叫“数据可信”。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

爬楼梯与动态规划:从递归到滚动数组的完整优化路径

爬楼梯与动态规划:从递归到滚动数组的完整优化路径

1. 为什么一道“简单题”能稳坐Hot 100前70名爬楼梯(LeetCode 70)在Hot 100榜单里几乎是必刷的存在。题目描述极其朴素:每次可以爬1级或2级台阶,问爬到第n级有多少种不同的方法。很多第一次刷到的人会觉得这题太简单了&#xff0c…

2026/10/9 9:41:57 阅读更多 →
软考数据库系统工程师备考:完全版复习资料的高效利用与避坑指南

软考数据库系统工程师备考:完全版复习资料的高效利用与避坑指南

简介:软考数据库系统工程师复习资料(完全版)是一份系统化的备考文档,面向正在准备软考数据库系统工程师考试的考生,覆盖从计算机系统知识到数据库发展趋势与新技术共14章内容,包括数据库技术基础、关系数据…

2026/10/9 9:40:56 阅读更多 →
基于中间变量观测器的多智能体系统故障检测方法详解

基于中间变量观测器的多智能体系统故障检测方法详解

简介:针对无向拓扑下线性多智能体系统的执行器故障检测问题,这份资料给出基于中间变量观测器的完整研究方案,适合具备自动控制理论基础、从事多智能体系统及故障诊断的研究人员与工程师。内容围绕虚拟系统构建、中间变量观测器设计、分布式残…

2026/10/9 9:40:56 阅读更多 →

最新新闻

基于Python+MILP的风光储联合调度:电池与废弃矿井抽蓄互补优化

基于Python+MILP的风光储联合调度:电池与废弃矿井抽蓄互补优化

这两年搞新能源消纳的调度研究,有个词绕不开:互补。风电场最常见的情况是深夜大风、负荷却躺在地板上,光伏正好相反,正午出力冲顶、电网一时间吃不下。单靠任何一种电源都没法把这条曲线磨平,于是风电、光伏和储能组成…

2026/10/9 10:58:44 阅读更多 →
深度聚类开源代码库实战指南:从DEC到对比学习的工程落地

深度聚类开源代码库实战指南:从DEC到对比学习的工程落地

在无标注数据这块,很多人习惯性地打开sklearn直接跑一个KMeans,但凡是真正做过几年聚类项目的人都有体会:高维图像、文本向量、用户行为序列这类数据,传统聚类几乎每次都会翻车。原因不是聚类算法本身不行,而是输入特征…

2026/10/9 10:58:44 阅读更多 →
微网虚拟电厂多场景随机规划与CVaR风险优化调度策略

微网虚拟电厂多场景随机规划与CVaR风险优化调度策略

开场:为什么风险量化成了微网调度的硬需求做调度的人,不管是在传统电力系统还是园区级微网,现在绕不开一个词:风险。风光出力天生不稳定,负荷预测也不可能百分之百准,以前我们做确定性调度习惯了&#xff0…

2026/10/9 10:58:44 阅读更多 →
数理统计大作业实战:从假设检验到Python实现的全流程指南

数理统计大作业实战:从假设检验到Python实现的全流程指南

简介:这是一份面向数理统计课程学习者与机器学习初学者的完整大作业报告,围绕鸢尾花数据集展开多方法分析。报告以花萼与花瓣的四个属性为输入,使用马氏距离度量样本相似性,通过混合高斯模型实现聚类,借助主成分分析与…

2026/10/9 10:58:44 阅读更多 →
硅基流动+Chatbox:零成本长期运行DeepSeek-R1的本地AI工作流

硅基流动+Chatbox:零成本长期运行DeepSeek-R1的本地AI工作流

简介:本资源是一份面向初级开发者与个人AI实践者的低成本大模型应用搭建指南,聚焦如何利用硅基流动平台的DeepSeek API与开源跨平台AI助手Chatbox,构建稳定、免费且响应流畅的本地化AI应用。内容覆盖硅基流动高额度免费Token(新用…

2026/10/9 10:58:44 阅读更多 →
基于JWT/JWE的跨系统安全数据透传方案详解

基于JWT/JWE的跨系统安全数据透传方案详解

先说结论:这套“基于JWT/JWE的跨系统安全数据透传方案”,解决的是两个不同域、不同技术栈、甚至不同运维体系的服务之间,如何安全地把一段结构化数据从A端交付到B端——既保证数据在“路上”不被看、不被改,又保证接收方能够验证数…

2026/10/9 10:57:43 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 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/8 21:13:17 阅读更多 →
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/8 15:26: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/9 6:17:20 阅读更多 →