ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于Django与DeepSeek的古诗词知识图谱情感分析推荐系统

基于Django与DeepSeek的古诗词知识图谱情感分析推荐系统

每年到毕业设计选题季,实验室里讨论最多的不是“做什么题目”,而是“做什么题目能既撑得起工作量,又不会被答辩老师一句话问倒”。古诗词方向以前听着冷门,但一旦把Django、DeepSeek大模型、知识图谱、情感分析、推荐系统和可视化组合到一起,它就不再是那种只写几个增删改查页面的管理系统,而是一个工程与算法结合、有大模型应用场景、还能做出漂亮展示页的综合型项目。

我最近就把这套系统完整地做了一遍。整套系统说白了就是让计算机“读懂”中国古代诗歌:对每首诗做情感判断,根据情感、意象、诗人之间的关系给用户推荐诗,再把诗与诗之间复杂的语义关联用知识图谱和图表呈现出来。标题里那一长串关键词,并不是随便拼凑的,它们各自对应真实的技术环节。

这篇文章我会把项目的技术拆解、数据层设计、算法实现、实操过程和踩坑经验全部过一遍。如果你是正在准备毕业设计的学生,或者想找一个“大模型+知识图谱”入门实战项目的开发者,这篇文章应该能给你一条明确的技术路线。

1. 毕业设计做古诗词分析,为什么这届学生都盯上了它

1.1 这个系统到底解决了什么问题

先说说选题背景。最近几年的毕业设计里,纯管理系统类题目已经很难做出区分度,“用户管理、订单管理、分类管理”三板斧做完,工作量一眼就能被看穿。纯算法类题目又往往缺少工程载体,算法模型跑了半天,没有一个能演示的界面,答辩时用户也看不出价值。古诗词情感分析这个方向,恰好站在两者中间。

这个系统解决的实际问题有三个。第一,传统情感分析手段对古文效果很差。普通的文本情感分析模型,大多是基于现代汉语训练的,遇到“举头望明月,低头思故乡”这种表面写景、实际写情的诗句,经常识别成中性或者错误情绪。第二,古诗词之间的关系不是用普通数据库就能优雅表达的。诗人与诗人之间有唱和,诗歌与意象之间有引用,意象与情感之间有象征,这些关系如果用关系型数据库反复join,查询逻辑会非常难写。第三,新上线的推荐系统普遍有冷启动问题,没有足够的用户行为数据,协同过滤根本跑不起来,需要有基于内容或基于知识图谱的推荐策略来兜底。

所以整套系统解决的就是这三个层面的问题:用大模型的情感理解能力替代传统情感词典,用知识图谱承载诗词间的语义网络,用混合推荐策略解决冷启动。通过这三个能力,最后把古诗词数据变成一个人人可浏览、可交互、可探索的智能化平台。

1.2 技术选型:Django、DeepSeek、知识图谱的组合逻辑

后端框架我选了Django而不是FastAPI,理由很实际。第一,Django自带Admin后台,诗词数据管理页面几乎是免费的,这对于毕设来说性价比极高。第二,Django的ORM对初学者非常友好,模型写好之后迁移、建表、增删改查都是现成的。第三,答辩时老师问“你用了什么成熟的企业级技术”,Django是一个从简历到工业界都说得过去的答案。FastAPI更轻量,但很多组件需要自己拼装,在有限时间内不太划算。

大模型选型方面,DeepSeek在这类题目里优势很突出。首先是中文古文理解能力在同类开源模型里属于第一梯队,其次API风格和常用的OpenAI协议接近,对接成本很低,另外它的调用成本也便宜,业余项目完全可以承受。这里要强调一下,本地没有GPU的学生,完全不需要自己部署模型,用云端API就可以完成整个项目的载体工作。

知识图谱的存储层可以分成两条路线。一条是认真上Neo4j,图数据库,支持多跳路径查询,可视化和推理能力都很强;另一条是用MySQL存三元组,前端再用ECharts画关系图。两种方式都叫“知识图谱项目”,但是含金量完全不同。我的做法是MySQL和Neo4j并行,MySQL保存业务数据和诗词原始信息,Neo4j保存图谱关系,两边通过同步脚本保持数据一致。这样答辩时你既可以说清业务数据流,又能在知识图谱页面上展示真正的图结构查询。

2. 系统架构与数据层设计:从数据采集到图谱建模

2.1 整体架构与数据流设计

整套系统的数据流可以概括成一条链:数据采集 → 清洗入库 → 情感分析 → 图谱构建 → 推荐计算 → API输出 → 前端可视化。

最开始的输入是公开的古诗词数据集,一般以JSON或CSV的格式存在,里面包含朝代、作者、标题、正文、注解和译文。拿到之后先做清洗,把重复记录、繁体字、缺字段的数据处理干净,再写入MySQL。这一步完成之后,会启动一个批量分析任务,逐首调用DeepSeek的情感分析接口,把返回的情感标签、情感分数和推理原因回填到诗词表里。接下来,从诗词正文中抽取出“月、花、酒、孤舟”这类意象名词,再把诗人、朝代、情感、意象这些实体和关系导入Neo4j。用户在前端完成收藏、评分、浏览后,推荐模块结合用户行为和知识图谱路径,为每一首诗生成推荐候选集。最后通过REST API统一输出,前端用ECharts完成可视化展示。

我建议在Django里按模块拆分app,而不是把代码全部堆在同一个地方。我当时是拆成了poem、user、kg、recommend、stats这五个核心app,分别处理诗词管理、用户体系、知识图谱、推荐逻辑和统计可视化。这样代码维护起来非常轻松,后期写论文时也可以直接拿app的划分来说明系统模块设计。

2.2 数据库设计:MySQL表结构和Neo4j图谱模型

MySQL这边主要是业务数据。核心表包括朝代表、诗人表、诗词表、用户表、收藏表和评分表。其中最关键的是poem表,它除了保存诗文的原文、译文和注释之外,还会冗余存储情感标签和情感分数。这里做适度冗余是有意的,因为毕设场景里绝大多数查询是只读的,减少join次数可以显著降低写SQL的复杂度。

表名主要字段作用
dynastyid, name, start_year, end_year朝代信息
poetid, name, dynasty_id, alias, intro诗人信息
poemid, title, content, author_id, dynasty_id, translation, notes, emotion_label, emotion_score诗词原文与情感分析结果
userid, username, password_hash, created_at用户账户
favoriteid, user_id, poem_id, created_at用户收藏关系
ratingid, user_id, poem_id, score, created_at用户评分行为

Neo4j这边的图模型比MySQL更复杂一些。我定义了四类主要节点:Poem(诗)、Poet(诗人)、Emotion(情感)、Image(意象),外加Dynasty朝代节点。关系上,诗人节点用WROTE关系指向诗歌,诗歌节点用BELONGS_TO指向朝代,用EXPRESSES关系指向情感,用CONTAINS关系指向意象。最重要的语义设计是“Image-SYMBOLIZES-Emotion”,也就是“意象象征情感”。比如“月”常常象征思乡,“柳”常常象征送别,“酒”有时候象征豪放,有时候象征愁绪。这样建模的好处是,推荐系统可以通过“诗包含意象,意象象征情感,情感关联更多诗”这样的路径做推理,而不是只做文本相似度那种机械匹配。这个“意象-情感”双层结构,是整个项目里最适合写进论文创新点的设计。

2.3 数据处理管线:清洗、分词、情感标注

数据质量直接决定图谱和推荐系统做出来的效果。很多同学一上来就直接把数据集读入数据库,结果页面上全是空字段,图谱里全是孤立节点,观感很差。我当时花了大概一周的课余时间专门处理数据,事实证明非常值。

清洗阶段主要做五件事。第一,以“标题+正文+作者”作为唯一键去重,避免同一个版本的数据重复入库。第二,用OpenCC把繁体中文转简体,统一文本格式。第三,去除全角空格和遗留的注释标记。第四,删除那些缺少朝代、缺少正文的脏数据,对于“佚名”作者则统一标记为“无名氏”,而不是直接删除,否则图谱里会少一大片节点。第五,对词牌名和曲牌名做单独字段保存,这个信息在后续知识图谱里可以作为标签使用,比如“同词牌名的作品可以在推荐时作为相似性特征”。

情感标注是整个数据管线的核心步骤。因为古诗词语料规模通常只有几千到几万首,在这个量级上微调一个大模型性价比很低,而且很多同学没有GPU资源。所以我的做法是用DeepSeek做批量离线标注,输出“情感标签+情感分数+分析原因”的JSON结果,然后把结果保存成标注文件。为了让后面写论文时有可信的评估数据,我还额外抽了200首诗人工复核了一遍,统计模型标注和人工标注的一致率,这部分内容在系统测试章节里非常加分。

3. 核心算法与模型实现:情感分析、推荐算法、知识图谱推理

3.1 基于DeepSeek大模型的古诗词情感分析实现

情感分析模块是整个系统的技术重点,核心实现思路是提示词工程加结果解析。我没有采用传统微调模型路线,原因是古诗词语料规模太小,几万条文本微调一个大模型,效果不一定比精心设计的Prompt更好,而且本地没有GPU基本上跑不动。改用大模型接口做zero-shot分类,反而能得到更符合语义的判断结果。

情感标签体系设计要合理。标签太少,比如只有“积极/消极”两类,论文里没有细节;标签太多,模型输出会不稳定,人工复核也累。我采用的是八分类标签集合:喜悦、愤怒、悲伤、忧思、闲适、豪迈、思念、愁苦。因为情感本身存在交叉,允许一首诗输出1到3个标签,既可以体现情感的复杂性,又不会让结果发散。

下面给一个简化版的调用示例,实际项目里还需要把这段逻辑封装成独立的服务文件。

import requests import json def analyze_emotion(content: str, labels: list[str]) -> dict: api_key = "your_api_key" url = "https://api.deepseek.com/chat/completions" prompt = f""" 你是一位精通中国古典诗词的文学研究者。请分析下面这首古诗表达的情感。 可选情感标签:{labels} 要求: 1. 只输出JSON,不要任何多余解释。 2. JSON格式:{{"emotions": ["思念"], "score": 0.9, "reason": "诗句通过描写 ... 表达 ..."}} 古诗内容: {content} """ resp = requests.post( url, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json={ "model": "deepseek-chat", "messages": [ {"role": "system", "content": "你是一个严谨的中文古诗词研究者。"}, {"role": "user", "content": prompt}, ], "temperature": 0.2, "max_tokens": 300, }, timeout=30, ) data = resp.json() raw = data["choices"][0]["message"]["content"] return parse_json_safe(raw)

大模型输出结果不完全稳定,偶尔会带Markdown代码块,或者夹带解释性文本,所以解析函数必须写得足够健壮。

import re import json def parse_json_safe(raw: str) -> dict: raw = raw.strip() if raw.startswith("```"): raw = re.sub(r"^```(?:json)?", "", raw).strip() raw = re.sub(r"```$", "", raw).strip() try: return json.loads(raw) except json.JSONDecodeError: match = re.search(r"\{.*\}", raw, re.S) if match: return json.loads(match.group()) raise ValueError("模型输出无法解析为JSON")

调用参数方面,我对temperature设置得很低,一般是0.2。因为情感分类是一个确定性强、主观性弱的任务,输出越稳定越好。max_tokens控制在300以内,每首诗的分析结果量级很小,不需要模型长篇大论。批量分析时还要注意限流问题,两次请求之间加随机延迟,并且做好失败重试。

情感分析结果最终要回写到MySQL的poem表中,再同步到Neo4j。我还在诗表里加了一个audit_status字段,标注这条情感结果是否已经人工审核,这样后期可以随时修正模型的误差,也让整个系统的可信度更高。

3.2 融合知识图谱的推荐系统设计

推荐模块我采用了一种混合策略,而不是依赖单一的协同过滤。表面原因是项目早期没有用户数据,协同过滤冷启动。深层原因是古诗词推荐非常适合做“可解释推荐”:告诉用户“推荐这首诗,因为你喜欢的诗和它都表达了思念,并且都使用了明月这个意象”,比冷冰冰的算法结果有说服力得多。

第一种策略是基于内容的相似推荐。把每首诗的分词结果转换成TF-IDF向量,用余弦相似度计算最相近的Top-K首诗。实现简单,运行稳定,适合做兜底候选池。

第二种策略是知识图谱路径推荐。这是项目的亮点。给定当前诗P,在图谱中先找到它关联的情感节点、意象节点和诗人节点,再通过这些节点向外扩展,找到与P共享情感、共享意象或者属于同一位诗人的其他作品。路径越短,关联越紧密。比如当前诗包含“月”并表达“思念”,那么另一个也包含“月”并表达“思念”的诗,就会获得很高的路径分数。

第三种策略才用到协同过滤。当用户行为数据积累到一定量之后,可以根据“收藏了这首诗的人也收藏了哪些诗”来做物品协同过滤。在论文的系统测试部分,我会说明协同过滤在数据稀疏时效果不佳,所以它只作为候选池的补充来源。

三种策略各有特点,用一张表能看得更清楚:

推荐策略数据依赖优点缺点
基于内容相似诗词文本冷启动友好,实现简单容易同质化
知识图谱路径图谱三元组可解释性强,推荐结果有语义图谱不完善时路径稀少
协同过滤用户行为个性化程度高冷启动问题明显

最终候选集的排序分数采用加权融合:final_score = w1 乘以文本相似度 + w2 乘以图谱路径分 + w3 乘以情感偏好分。三个权重可以根据演示效果手动调整,论文中也可以多设几组对比实验来说明权重选择的合理性。

3.3 可视化模块:从ECharts词云到图谱动态渲染

可视化部分直接决定答辩时的第一印象。我当时把整个页面按“数据总览、内容检索、图谱探索、情感分析”四个维度来设计,其中最有视觉冲击力的就是知识图谱关系图。

为了让ECharts能展示Neo4j里的图数据,后端需要先把Cypher查询结果转成前端要求的nodes和links格式。我统计情感分布、高频意象、朝代分布等指标时,也统一通过REST API返回聚合JSON,不在前端做复杂计算。

一个基础的知识图谱接口响应长这样:

{ "nodes": [ {"id": "p1", "name": "某首古诗", "category": "poem"}, {"id": "e1", "name": "思念", "category": "emotion"}, {"id": "i1", "name": "月", "category": "image"} ], "links": [ {"source": "p1", "target": "e1", "relation": "表达"}, {"source": "p1", "target": "i1", "relation": "包含"} ] }

前端用ECharts的graph类型渲染,设置force布局,节点大小代表这首诗被收藏的次数或者情感节点关联的诗数,颜色按poet、poem、emotion、image四类区分。这样做出来之后,页面不仅好看,而且交互性很强,点击任意一个意象节点就能看到所有包含这个意象的诗,点击情感节点就能看到持有该情感的作品集合。

除了知识图谱,还有三个必做的图表:情感分布玫瑰图、高频意象词云、朝代诗作量柱状图。这三个图表的数据量不大,渲染很快,放在首页可以做数据总览。如果想让情感分析更有深度,还可以在诗词详情页放一个单诗雷达图,把模型返回的1到3个情感标签扩展成多维度强度分布,视觉上很出效果。

4. 实操落地:从环境搭建到发布上线的完整流程

4.1 环境准备与项目初始化

动手开发之前,环境准备环节值得认真对待。我建议使用Python 3.10以上版本,用虚拟环境管理依赖,避免系统Python环境被搞乱。

依赖安装的核心清单是:django、djangorestframework、jieba、opencc、requests、pandas、neo4j、gunicorn、mysqlclient或pymysql。MySQL我用的是8.0,Neo4j用的是Community 5.x。Neo4j我强烈建议用Docker部署,一条命令就能把服务和可视化控制台一起带起来,省去手动配置的环境坑。

初始化Django项目的步骤如下:django-admin startproject创建项目根目录,python manage.py startapp分别创建poem、user、kg、recommend、stats五个应用。然后在settings.py里配置MySQL连接、时区、静态文件目录和REST Framework配置。全部配置完成后执行makemigrations和migrate,数据库表就生成了。

这里插一句:不要为了图省事用SQLite。毕设系统最终要演示收藏、推荐、图谱等业务,数据量大一点SQLite就会响应变慢,而且答辩老师看到SQLite会默认你的系统很初级。MySQL是成本很低但专业度很高的选择。

4.2 Django后端核心代码实现

后端模型是整个系统的地基。我在这里给出核心模型的设计示例,实际开发时可以直接参考:

from django.db import models class Dynasty(models.Model): name = models.CharField(max_length=32) start_year = models.IntegerField(null=True, blank=True) end_year = models.IntegerField(null=True, blank=True) class Poet(models.Model): name = models.CharField(max_length=64) dynasty = models.ForeignKey(Dynasty, on_delete=models.SET_NULL, null=True) alias = models.CharField(max_length=255, blank=True) intro = models.TextField(blank=True) class Poem(models.Model): title = models.CharField(max_length=128) content = models.TextField() author = models.ForeignKey(Poet, on_delete=models.SET_NULL, null=True) dynasty = models.ForeignKey(Dynasty, on_delete=models.SET_NULL, null=True) translation = models.TextField(blank=True) notes = models.TextField(blank=True) emotion_label = models.CharField(max_length=128, blank=True) emotion_score = models.FloatField(default=0.0) is_audited = models.BooleanField(default=False)

这里有一个设计细节值得注意:外键都用SET_NULL而不是CASCADE。因为古诗文数据是珍贵的语料,不能因为误删一个诗人就连带把所有诗作删掉,这是数据安全意识,答辩时可以主动提出来。

API层我统一用Django REST Framework实现,不直接在视图函数里拼JSON字符串。主要接口包括:诗词列表分页接口、诗词详情接口、相似推荐接口、收藏接口、评分接口、情感分布统计接口、知识图谱图数据接口。每个接口对应一个viewset或APIView,代码结构非常清晰。

DeepSeek调用逻辑建议放在services/deepseek.py这个独立服务模块里,视图和模型都不要直接引用requests包。这样后期如果换模型或者调整Prompt,只需要改一个文件。

4.3 前端页面与ECharts可视化接入

前端页面我采用传统Django模板加Bootstrap的方案,图表部分用ECharts。页面规划为五个核心页面:首页、诗词列表、诗词详情、知识图谱、个人中心。

首页最重要的部分是统计图。打开页面时,前端会并发请求情感分布接口、朝代统计接口和高频意象接口,拿到数据后分别渲染玫瑰图、柱状图和词云。ECharts对中文支持很好,但要注意后端返回的JSON编码必须是UTF-8,否则会出现中文乱码。

知识图谱页是演示的重点。页面做成半透明仪表盘风格,左侧是节点类型筛选器,右侧是ECharts的graph实例,点击节点时拉取详情面板。ECharts的graph类型提供force布局、拖拽、缩放等交互能力,基本能满足演示需求。

这里给一个最小可用的前端示例:

<div id="kgChart" style="width:100%; height:600px;"></div> <script src="echarts.min.js"></script> <script> fetch("/api/kg/graph") .then((r) => r.json()) .then((data) => { const chart = echarts.init(document.getElementById("kgChart")); chart.setOption({ tooltip: {}, series: [ { type: "graph", layout: "force", data: data.nodes, links: data.links, roam: true, force: { repulsion: 300, edgeLength: 100 }, categories: data.categories, }, ], }); }); </script>

演示时强烈建议在页面里做“点击节点显示关联诗”的交互,而不只是静态展示。因为答辩老师十有八九会想上手点一点,交互越流畅,系统越显得成熟。

4.4 系统部署与演示技巧

开发阶段用runserver跑很舒服,但到了答辩前,建议还是用Gunicorn加Nginx部署成正式服务,好处有三个:答辨现场访问更稳定,避免runserver在并发访问下崩掉;静态文件加载更快,页面美观度上一个台阶;简历上写“部署于Linux环境”也更有说服力。

部署的简要流程是:先执行collectstatic收集静态文件,然后用Gunicorn启动Django应用,监听8000端口,最后用Nginx反向代理80端口到8000端口,并把静态文件目录交给Nginx处理。数据库口令和密钥不要写死在settings.py里,最好通过环境变量注入,细节虽小,但写到论文里很体现工程素养。

演示技巧方面,最重要的一条是:不要高估现场网络的可靠性。大模型API在公网环境下可能有延迟,如果答辩现场网络不好,实时调用DeepSeek会很尴尬。我给系统的建议是将情感分析做成离线批量的方式,所有情感结果都预先入库,前端只读缓存结果,线上预测只作为后台管理功能来演示。这样整个演示流程完全不依赖外部API,稳定性大大提升。

5. 踩坑实录与答辩要点:开发中的真实问题与解决方案

5.1 大模型调用不稳定怎么兜底

在实际开发过程中,大模型接口带来的问题是最多的,主要集中在四个方面。

第一个问题是限流。批量分析几千首诗时,连续请求很容易触发限流。解决办法是每次请求之间加随机延迟,设置指数退避重试,同时把算好的结果永久缓存到本地,避免同一首诗被反复请求。缓存可以放在数据库里,也可以放在Redis里,对毕设来说数据库缓存就足够了。

第二个问题是返回格式不稳定。模型偶尔会在JSON前后加解释文字,偶尔会输出Markdown格式的代码块。我写的parse_json_safe函数专门处理这类情况,先剥掉代码块,再尝试正则取花括号内容,基本能兜住绝大多数异常情况。

第三个问题是标签不落在预设集合里。比如模型返回“思念故乡”,而标准标签是“思念”,如果不做映射就会导致统计图表里出现一堆杂乱的标签。解决方法是建立一个同义词映射表,把“思乡”“怀人”“思念故乡”等都映射到“思念”。个映射表本身也可以作为论文里“标签体系构建”的一部分来写。

第四个问题是离线环境保障。答辩现场断网的情况并非罕见,所以一定要避免“页面打开时才实时请求大模型”这种设计。最稳妥的方案是全部情感分析结果提前入库,前端和推荐模块只做本地数据读取,模型的实时调用作为后台管控功能单独封存。

5.2 知识图谱可视化性能优化

Neo4j里存几千个节点、几万条关系时,数据库查询本身没有压力,真正的压力在前端渲染。ECharts force布局在节点数超过几百个时就会开始卡顿,尤其是同时开启缩放、拖拽和动画的时候。

我的优化方案有三个方向。第一,后端接口限制返回节点数量,默认只返回100个核心节点,而不是把全图数据一次性发给前端。第二,对展示内容做子图划分,比如首页图谱只展示“诗-意象-情感”关系,用户点开诗人节点后再展示诗人的关联子图。第三,过滤低频意象,只保留出现频率Top 50的意象进入图谱,这样图谱既不会稀疏到没内容,也不会密到看不清关系。

还有一个细节是查询语句的优化。比如只查必要字段、在Cypher中使用LOI限制条数,这些工作虽然简单,但能让接口响应时间从几百毫秒降到几十毫秒。答辩时如果老师问到性能问题,你可以直接说出这套方案,会显得思考非常完整。

5.3 论文写作与答辩演示的心得

这个题目写论文的素材非常充足。需求分析阶段可以写传统情感分析方法在文言古诗中的不足;关键技术阶段可以写大模型提示词设计和知识图谱路径推荐;系统实现阶段可以写MySQL与Neo4j的双库架构;系统测试阶段可以写情感标注一致率评估和推荐TopN准确率统计。整套论文的逻辑闭环非常清晰。

创新点的提炼建议放在“引入”和“融合”这两个关键词上。比如“将大模型语义理解能力引入古诗词情感识别,缓解传统方法在隐喻表达下的理解偏差”“设计意象到情感的图谱关系层,提升推荐结果的可解释性”。这里要注意措辞,不要写“首创”这种过于绝对的表达,否则答辩时容易被追问。

答辩老师最常问的问题我提前模拟过一轮。问为什么用大模型而不用微调小模型时,可以从数据规模、成本、古诗词语料特点三个角度回答。问知识图谱数据来源时,要说明是公开数据集清洗加模型抽取、人工复核。问推荐效果如何评价时,可以承认目前以离线统计和演示体验为主,并补充说模拟用户行为数据进行过TopN准确率测试。回答既要自信,也要实事求是,不要夸大系统能力。

演示流程我建议按照“首页图表 → 搜索一首诗 → 查看情感分析 → 点击知识图谱 → 体验相似推荐 → 收藏后刷新推荐”的顺序走一遍,每一步都能自然衔接到下一步。每个页面的停留时间控制在半分钟左右,把系统的核心亮点快速展示完。

最后再分享一个我实际操作中的体会:这类项目的第一优先级永远是数据。我在数据清洗和情感标注上花的时间,比写前端页面多出好几倍,但效果也正正都体现在最终演示里。如果你正在为这个题目做准备,先花几天时间把“一条诗从清洗、分析、入库、推荐到可视化”的完整链路跑通,再回来写页面做优化。骨架通了,后面加什么功能都顺理成章。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进