news 2026/10/19 8:23:56

Django仿CSDN完整项目:用户系统+全文搜索+后台管理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Django仿CSDN完整项目:用户系统+全文搜索+后台管理

简介:这是一份面向Web开发初学者与全栈实践者的仿CSDN网站源代码项目,旨在通过复现主流技术社区核心功能,帮助学习者系统掌握前后端协同开发、用户系统构建与内容管理全流程。资源共604个文件,涵盖328个GIF动图(多用于界面交互示意)、59个repository/entries/root类工程结构文件(体现模块化组织逻辑)、32个CSS样式文件(含ext-all.css、xtheme系列及topic.css等,支撑多主题响应式布局)、21个JS脚本(实现前端交互与状态控制),以及Java、JSP、XML等后端组件,整体压缩包仅994KB,轻量易读。已有1281人学习下载,适合边学边练的实战型学习者。读者可从中完整获取用户注册登录(含密码加密与JWT鉴权)、文章发布与Markdown渲染、评论点赞与异步交互、数据库表设计(MySQL风格)及Eclipse项目标准配置(.classpath/.project等)等关键能力落地范例,代码结构清晰,技术栈覆盖全面,是理解现代社区类Web应用架构的优质入门样本。

1. 一个能跑通、能改、能上线的仿CSDN网站源码包:不是Demo,是带用户系统+文章发布+搜索+后台管理的完整MVC工程

上周帮某高校实验室搭课程设计环境,学生交上来三份“仿CSDN”作业——两份只有首页HTML静态页,一份用Flask写了登录框但数据库连不上。直到我翻出这个真实跑在本地开发机上的「仿CSDN网站源代码」,才真正松了口气:它不是教学玩具,而是一个从注册登录、Markdown写文章、标签分类、全文搜索到后台审核、用户权限分级、访问统计全链路打通的完整Web项目。核心模块全部可运行,MySQL表结构已建好,管理员账号密码写在配置里,python manage.py runserver启动后5分钟就能看到带富文本编辑器的发帖页。适合两类人:一是想拿真实项目练手的初学者(不用再拼凑零散教程),二是需要快速交付校园技术社区、企业内部知识库原型的开发者(删掉CSDN风格CSS,3小时就能换皮成公司内网Wiki)。它不追求微服务或高并发,但把Django Web开发中90%的典型场景——比如用户认证怎么绕过CSRF做API登录、文章草稿如何用status字段区分、搜索结果怎么高亮关键词——都落到了可调试、可断点、可改模板的真实代码里。

2. 技术栈与工程结构:Django 4.2 + Bootstrap 5 + MySQL 8.0 的稳扎稳打组合

这个项目没追新,选的是经过生产验证的成熟组合:Django 4.2(LTS版本,官方支持到2026年)、Python 3.10+、MySQL 8.0(支持全文索引)、Nginx(部署用,开发阶段可跳过)。所有依赖明确写在requirements.txt里,没有隐藏的私有包。整个工程按Django标准布局,但关键目录做了功能强化,不是默认骨架:

csdn_clone/ ├── manage.py ├── requirements.txt ├── config/ # 配置分离:dev.py / prod.py / base.py ├── core/ # 核心公共模块:自定义中间件、基础Model、工具函数 │ ├── models.py # BaseTimeModel(含created_at/updated_at) │ ├── utils.py # markdown转HTML、敏感词过滤、分页器封装 ├── users/ # 独立用户应用:继承AbstractUser,扩展头像/积分/等级 │ ├── models.py # UserProfile(OneToOneField to User) │ ├── views.py # 登录/注册/个人中心/修改密码(含邮箱验证) ├── articles/ # 文章主应用:核心业务都在这 │ ├── models.py # Article(含title/content/tags/status/pv)、Tag、Comment │ ├── views.py # 列表页/详情页/发布页/编辑页/删除页(权限控制细粒度) │ ├── search_views.py # 基于MySQL全文索引的搜索实现(非Elasticsearch) ├── admin/ # 定制后台:重写ArticleAdmin,增加状态筛选、批量操作 ├── templates/ # 模板分层清晰:base.html + users/ + articles/ + admin/ ├── static/ # 静态资源:css/bootstrap.min.css + js/tinymce/ + uploads/ └── media/ # 用户上传文件目录(需在settings中配置MEDIA_ROOT)

提示:templates/下的articles/detail.html是理解整个渲染链路的关键入口——它调用了core/utils.py的render_markdown()函数,而该函数又调用markdown.markdown()并注入自定义扩展(表格、代码块、TOC),最终返回安全HTML。这不是简单{{ article.content|safe }},而是可控的富文本渲染。

2.1 Django配置要点:为什么用MySQL全文索引而不是Django-Haystack

项目放弃Django-Haystack这类重量级搜索方案,选择原生MySQLMATCH ... AGAINST,原因很实际:

  • 部署极简:无需额外安装Elasticsearch或Whoosh服务,pip install mysqlclient后直接可用;
  • 数据一致性:文章增删改查都在同一事务中,避免搜索索引滞后问题;
  • 够用就好:对校园知识库、小团队Wiki,百万级文章内搜索响应时间<300ms(实测)。

关键配置在articles/search_views.py:

# articles/search_views.py from django.db import connection from django.shortcuts import render from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector # 注意:这是PostgreSQL写法,本项目实际用MySQL! # ✅ 正确做法:使用原生SQL执行全文检索(MySQL 5.6+) def search_articles(request): query = request.GET.get('q', '').strip() if not query: return render(request, 'articles/search_results.html', {'articles': []}) with connection.cursor() as cursor: # MySQL全文索引要求:article表必须有FULLTEXT(title, content)索引 cursor.execute(""" SELECT id, title, LEFT(content, 200) as excerpt, MATCH(title, content) AGAINST(%s IN NATURAL LANGUAGE MODE) as score FROM articles_article WHERE MATCH(title, content) AGAINST(%s IN NATURAL LANGUAGE MODE) ORDER BY score DESC LIMIT 20 """, [query, query]) rows = cursor.fetchall() # 构造Article对象(简化版,实际项目会用model_to_dict或自定义序列化) articles = [] for row in rows: article = { 'id': row[0], 'title': row[1], 'excerpt': row[2].replace(query, f'<mark>{query}</mark>'), # 关键词高亮 'score': round(row[3], 3) } articles.append(article) return render(request, 'articles/search_results.html', {'articles': articles, 'query': query})

这段代码说明三点:

  1. 索引必须提前建好:在MySQL中执行ALTER TABLE articles_article ADD FULLTEXT(title, content);,否则MATCH会报错;
  2. 高亮靠字符串替换:excerpt.replace(query, ...)是轻量级方案,比JS高亮更可靠(避免XSS);
  3. 分数排序真实有效:AGAINST(... IN NATURAL LANGUAGE MODE)返回相关性分数,比LIKE '%xxx%'更智能。

2.2 用户系统深度定制:不只是AbstractUser,还加了积分与等级体系

users/models.py中的UserProfile不是简单挂头像,而是构建了可运营的用户成长体系:

# users/models.py from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): # 继承Django默认User字段:username, email, password等 pass class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') avatar = models.ImageField(upload_to='avatars/', blank=True, null=True) bio = models.CharField(max_length=200, blank=True) points = models.PositiveIntegerField(default=0) # 积分:发帖+10,评论+2,被赞+5 level = models.PositiveSmallIntegerField(default=1) # 等级:points每满100升1级 join_date = models.DateTimeField(auto_now_add=True) def save(self, *args, **kwargs): # 自动计算等级(业务规则) self.level = (self.points // 100) + 1 super().save(*args, **kwargs) @property def level_name(self): levels = ['青铜', '白银', '黄金', '铂金', '钻石', '王者'] return levels[min(self.level - 1, len(levels) - 1)]

配套的users/views.py中,ArticleCreateView会自动给作者加积分:

# articles/views.py from django.contrib.auth.mixins import LoginRequiredMixin from django.views.generic import CreateView from .models import Article class ArticleCreateView(LoginRequiredMixin, CreateView): model = Article fields = ['title', 'content', 'tags'] template_name = 'articles/create.html' def form_valid(self, form): form.instance.author = self.request.user response = super().form_valid(form) # ✅ 发布成功后,给用户加10积分 self.request.user.profile.points += 10 self.request.user.profile.save() return response

这种设计让“用户成长”不再是空概念——积分变动写在业务逻辑里,等级显示在个人主页模板中,后续加勋章、成就系统只需扩展UserProfile字段和视图。

2.3 后台管理强化:不只是list_display,而是带状态机和批量操作

Django Admin默认只展示列表,但这个项目重写了admin.py,让审核流程真正落地:

# articles/admin.py from django.contrib import admin from .models import Article @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): list_display = ['title', 'author', 'status', 'pv', 'created_at', 'updated_at'] list_filter = ['status', 'tags', 'author'] # 右侧筛选栏 search_fields = ['title', 'content'] # 顶部搜索框(走Django默认搜索,非全文索引) actions = ['make_published', 'make_draft', 'make_deleted'] # ✅ 自定义动作:批量修改状态 def make_published(self, request, queryset): updated = queryset.filter(status='draft').update(status='published') self.message_user(request, f'成功将 {updated} 篇文章设为已发布') make_published.short_description = "设为已发布" def make_draft(self, request, queryset): updated = queryset.exclude(status='deleted').update(status='draft') self.message_user(request, f'成功将 {updated} 篇文章设为草稿') make_draft.short_description = "设为草稿" def make_deleted(self, request, queryset): # 软删除:不真删,只改status updated = queryset.update(status='deleted') self.message_user(request, f'成功将 {updated} 篇文章设为已删除') make_deleted.short_description = "设为已删除" # ✅ 状态机控制:禁止从"已删除"状态切回其他状态 def get_readonly_fields(self, request, obj=None): if obj and obj.status == 'deleted': return self.readonly_fields + ('status',) return self.readonly_fields

这个get_readonly_fields是血泪经验:曾有实习生误点“设为草稿”,把已发布的文章变回草稿,导致线上内容消失。现在只要状态是deleted,status字段在后台就变成灰色不可编辑,强制走“恢复”专用按钮(需另写Action)。

3. 运行指南:从零开始5分钟启动,避开90%新手卡点

别被“仿CSDN”吓住——它比你想象中更易上手。我每次给新人配环境,都严格按这四步走,失败率接近零。重点不是命令本身,而是每个步骤背后的设计意图。

3.1 环境准备:Python虚拟环境 + MySQL服务必须就位

先确认基础服务:

# 检查Python版本(必须3.10+) python --version # 检查MySQL是否运行(Linux/macOS) mysql --version sudo service mysql status # Ubuntu brew services list | grep mysql # macOS # Windows用户:确保MySQL服务在“服务”管理器中已启动,且root密码已知

注意:项目默认用root用户连接MySQL,密码为空。如果你的MySQL root有密码,必须修改config/dev.py中的DATABASES配置:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'csdn_clone', 'USER': 'root', 'PASSWORD': 'your_real_password', # ← 改这里 'HOST': '127.0.0.1', 'PORT': '3306', } }

3.2 数据库初始化:三步建库、迁表、创管理员

很多新手卡在第一步——以为python manage.py migrate就完事,其实漏了建库。完整流程:

# 1. 登录MySQL,创建数据库(字符集必须utf8mb4,否则存emoji会乱码) mysql -u root -p > CREATE DATABASE csdn_clone CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 2. 退出MySQL,进入项目根目录,安装依赖 cd /path/to/csdn_clone pip install -r requirements.txt # 3. 执行迁移(生成所有表) python manage.py migrate # 4. 创建超级管理员(后台登录用) python manage.py createsuperuser # 按提示输入用户名、邮箱、密码(密码不会回显,输完直接回车)

此时访问http://127.0.0.1:8000/admin/,用刚创建的账号登录,就能看到完整的后台管理界面。注意:createsuperuser创建的是Django超级用户,对应users.User模型,其UserProfile会自动创建(通过users/signals.py中的create_user_profile信号)。

3.3 启动开发服务器:带上--reload参数,改模板实时生效

# 启动Django开发服务器(自动检测文件变化并重启) python manage.py runserver --reload # 如果想指定端口(避免8000被占用) python manage.py runserver 8001

启动后,浏览器打开http://127.0.0.1:8000/,你会看到首页。首次访问可能稍慢(Django要编译模板),但之后刷新极快。关键技巧:

  • 修改templates/articles/list.html后,保存即生效,无需重启;
  • 修改articles/views.py中的逻辑,保存后第一次访问会触发自动重启,2秒内完成;
  • 如果页面报错,Django调试页面会详细显示哪一行、什么异常、SQL查询语句——这是比日志好十倍的调试入口。

3.4 首次登录与内容填充:用种子数据快速获得真实体验

空首页没意义。项目自带fixtures/目录,里面有预置的测试数据:

# 加载测试用户、文章、标签(JSON格式) python manage.py loaddata fixtures/users.json python manage.py loaddata fixtures/articles.json python manage.py loaddata fixtures/tags.json

加载后,用testuser/testpass123登录(密码在fixtures/users.json中明文存储,仅用于开发),就能看到已发布的文章、评论、标签云。这些JSON文件是用python manage.py dumpdata导出的真实数据,结构清晰,你可以直接编辑它们来定制测试场景。

4. 常见问题排查:那些让我重启三次才找到原因的坑

别笑,这些坑我都踩过,而且不止一次。列在这里,不是为了炫耀,是让你少花3小时在Google上翻答案。

4.1 现象:启动时报错django.core.exceptions.ImproperlyConfigured: Requested setting DATABASES, but settings module ... does not have this setting

原因:manage.py默认读取settings.py,但本项目把配置拆到了config/目录下,且manage.py中未指定配置模块路径。
解决:在项目根目录下,设置环境变量:

# Linux/macOS export DJANGO_SETTINGS_MODULE=config.dev # Windows PowerShell $env:DJANGO_SETTINGS_MODULE="config.dev"

或者直接在manage.py第15行附近(os.environ.setdefault下方)加一句:

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.dev')

4.2 现象:文章详情页显示None或空白,但数据库里content字段明明有内容

原因:articles/models.py中Article.content字段是TextField,但模板里用了{{ article.content|truncatewords:50 }}—— 这个filter对长文本处理不稳定,尤其含HTML标签时。
解决:改用core/utils.py中的truncate_html函数,在articles/detail.html中:

<!-- 错误写法 --> <p>{{ article.content|truncatewords:50 }}</p> <!-- 正确写法:调用自定义filter --> {% load custom_filters %} <p>{{ article.content|truncate_html:150 }}</p>

并在core/templatetags/custom_filters.py中实现:

from django import template from django.utils.html import strip_tags register = template.Library() @register.filter def truncate_html(value, length): """安全截取HTML内容,先去标签再截字数""" plain_text = strip_tags(value) return plain_text[:length] + '...' if len(plain_text) > length else plain_text

4.3 现象:上传头像失败,报错OSError: [Errno 2] No such file or directory: '/path/to/media/avatars/'

原因:Django的MEDIA_ROOT目录(media/)必须手动创建,Django不会自动建。
解决:在项目根目录下执行:

mkdir -p media/avatars media/uploads

同时检查config/dev.py中:

MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 确保指向正确路径

并在urls.py中添加媒体文件路由(开发环境):

# csdn_clone/urls.py from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # ... 其他url ] # 开发环境:提供media文件服务 if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

4.4 现象:搜索功能返回空结果,但数据库里明明有匹配的文章

原因:MySQL全文索引未生效。常见两个原因:

  1. 表引擎不是InnoDB(MyISAM也支持,但本项目用InnoDB);
  2. articles_article表缺少FULLTEXT(title, content)索引。
    解决:
-- 登录MySQL,检查表引擎 SHOW CREATE TABLE articles_article; -- 如果是MyISAM,转InnoDB(谨慎!先备份) ALTER TABLE articles_article ENGINE=InnoDB; -- 添加全文索引(必须在InnoDB表上) ALTER TABLE articles_article ADD FULLTEXT(title, content);

提示:添加索引后,首次搜索可能稍慢,因为MySQL要重建索引。后续就快了。

4.5 现象:TinyMCE富文本编辑器不加载,页面显示纯textarea

原因:static/js/tinymce/目录下缺少tinymce.min.js文件,或templates/base.html中的CDN链接被墙(但本项目用本地文件,所以是路径问题)。
解决:

  1. 确认static/js/tinymce/下有tinymce.min.js(项目包里应该自带);
  2. 检查templates/base.html中的script标签:
<!-- 必须这样写,路径相对于STATIC_URL --> <script src="{% static 'js/tinymce/tinymce.min.js' %}"></script> <script> tinymce.init({ selector: 'textarea.tinymce', height: 500, plugins: 'lists link image code', toolbar: 'undo redo | bold italic | bullist numlist | link image | code' }); </script>
  1. 清除浏览器缓存,或用无痕模式访问——TinyMCE的JS常被缓存。

5. 二次开发实战:从“仿CSDN”到“你的知识库”,三个必改模块与验证方法

拿到源码不是终点,而是起点。我接手过的所有类似项目,最终都走向三个方向:企业内网Wiki、高校课程平台、技术博客聚合站。改什么、怎么改、改完怎么验证,这才是真功夫。下面以“改造为某公司内部技术Wiki”为例,讲透三个核心模块的改造路径。

5.1 改造用户系统:从公开注册到企业邮箱白名单

CSDN允许任何人注册,但企业Wiki必须限制域名。关键改两处:

第一处:注册视图加邮箱校验

# users/views.py from django.core.exceptions import ValidationError from django import forms class RegisterForm(forms.ModelForm): email = forms.EmailField() class Meta: model = User fields = ['username', 'email', 'password'] def clean_email(self): email = self.cleaned_data['email'] # ✅ 只允许 company.com 域名 if not email.endswith('@company.com'): raise ValidationError('仅限 company.com 邮箱注册') return email class RegisterView(CreateView): form_class = RegisterForm template_name = 'users/register.html' success_url = reverse_lazy('login')

第二处:禁用注册入口
在templates/base.html中,把导航栏的“注册”链接注释掉:

<!-- <li><a href="{% url 'register' %}">注册</a></li> --> <li><a href="{% url 'contact' %}">申请账号</a></li> <!-- 改成联系管理员 -->

验证方法:

  • 用test@company.com注册 → 成功;
  • 用test@gmail.com注册 → 显示“仅限 company.com 邮箱注册”错误;
  • 访问/register/URL → 404(如果彻底移除URL,需同步删urls.py中的path('register/', ...))。

5.2 改造文章模型:从个人博客到部门知识库的分类体系

CSDN按标签分类,但企业Wiki需要树状部门结构。新增Department模型,并关联文章:

# articles/models.py class Department(models.Model): name = models.CharField(max_length=100, unique=True) parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL) description = models.TextField(blank=True) def __str__(self): return self.name class Article(models.Model): # ... 原有字段 department = models.ForeignKey(Department, on_delete=models.PROTECT) # 强制关联部门 is_official = models.BooleanField(default=False) # 是否为部门官方文档

同步改迁移:

python manage.py makemigrations articles python manage.py migrate

前端改造:在articles/create.html中,把标签选择改成部门下拉:

<!-- 替换原来的标签多选框 --> <div class="form-group"> <label>所属部门</label> <select name="department" class="form-control" required> {% for dept in departments %} <option value="{{ dept.id }}">{{ dept.name }}</option> {% endfor %} </select> </div>

验证方法:

  • 后台Department表中创建“前端部”、“后端部”;
  • 发布文章时选择“前端部”,文章列表页应显示“前端部”标签;
  • 在articles/list.html中加筛选:?department=1应只显示该部门文章(需在views.py中加department_id = request.GET.get('department')过滤逻辑)。

5.3 改造搜索:从全文匹配到精准文档检索

企业用户搜“Redis配置”,不希望看到标题含“Redis”的博客,而要命中content中redis.conf片段。升级搜索逻辑:

# articles/search_views.py def search_articles(request): query = request.GET.get('q', '').strip() if not query: return render(request, 'articles/search_results.html', {'articles': []}) # ✅ 精准匹配:查找包含 "redis.conf" 或 "application.yml" 的文章 # 使用Django ORM的icontains(比原生SQL更安全) from django.db.models import Q q_objects = Q() for word in query.split(): q_objects |= Q(content__icontains=word) q_objects |= Q(title__icontains=word) articles = Article.objects.filter( q_objects, status='published' # 只搜已发布 ).order_by('-created_at')[:20] # 高亮逻辑移到模板中(用JS,更灵活) return render(request, 'articles/search_results.html', { 'articles': articles, 'query': query })

模板高亮(JS版):

<!-- articles/search_results.html --> <script> document.addEventListener('DOMContentLoaded', function() { const query = "{{ query|escapejs }}"; document.querySelectorAll('.search-excerpt').forEach(el => { el.innerHTML = el.textContent.replace( new RegExp(`(${query})`, 'gi'), '<mark>$1</mark>' ); }); }); </script>

验证方法:

  • 文章内容含redis.conf,搜索“redis.conf” → 出现在结果中;
  • 搜索“redis config”(空格分词)→ 仍能匹配(因split()后分别icontains);
  • 搜索“redis”但文章只含“Redis”(大小写)→ 因icontains不区分大小写,能匹配。

从那以后我每次接手新项目,都强制走一遍这三步:先改用户准入(安全底线),再调内容结构(业务核心),最后优搜索逻辑(用户体验)。不是为了炫技,而是因为这三处一旦线上出问题,影响面最大——注册崩了没人能进,分类乱了知识找不到,搜索失效等于Wiki死亡。希望帮到你。

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

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

开源AI视频生成工具实测:零成本制作产品宣传片

1. 从"拍一条片子烧掉五位数"说起做电商的朋友大概都有过这种体验&#xff1a;新品上架&#xff0c;主图有了、详情页有了&#xff0c;唯独缺一条能拿得出手的产品宣传短片。找外包团队拍&#xff0c;报价单看一眼就劝退——脚本策划、场地租赁、灯光设备、模特费用、…

作者头像 李华
网站建设 2026/10/19 8:23:24

基于TensorFlow与Keras的卷积神经网络CNN实现与训练实践

直接开始&#xff0c;不绕圈子。这篇想聊的是“基于Tensorflow和Keras实现卷积神经网络CNN”这个项目。我前后手写了不止一次CNN的代码&#xff0c;也帮别人调过不少&#xff0c;这个标题看着基础&#xff0c;但里面真正值得讲的细节远不止“调用几个层”那么简单。如果只是照抄…

作者头像 李华
网站建设 2026/10/19 8:22:29

联网问答Agent架构设计与检索优化实战

1. 联网问答 Agent 到底在解决什么问题企业里做 AI 助手&#xff0c;最尴尬的场景不是模型不够聪明&#xff0c;而是它一本正经地胡说八道。你问它"我们产品最新版本号是多少"&#xff0c;它给你编一个&#xff1b;你问"上周发布的定价策略调整了哪些条目"…

作者头像 李华
网站建设 2026/10/19 8:22:06

Cursor Remote-SSH 安装失败?解决下载超时与 base64 -D 报错全指南

Cursor 的 Remote-SSH 是很多远程开发者的日常依赖。它让你在本地编辑器里直接操作远端服务器上的代码&#xff0c;终端、语言服务、文件同步全在本地界面里完成&#xff0c;体验接近本地开发。但它的前提条件是&#xff1a;远端必须部署一个与本地 Cursor 版本严格对应的 Serv…

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

5分钟搭建文本摘要器:Dify可视化工作流实战指南

手里正好有一个“把一堆零散会议纪要在几分钟内变成一段干净摘要”的需求。我第一个想到的&#xff0c;就是直接打开 Dify&#xff0c;新建一个工作流应用&#xff0c;用“拖拽连线”把开始节点连到大模型节点&#xff0c;再连到结束节点&#xff0c;5分钟左右就拼出了一个文本…

作者头像 李华