Dify二次开发-新增聊天会话收藏(页签)功能实现方案

1. 核心思路

本项目标是在不修改核心会话表结构(即不新增 is_starred 字段)的前提下,利用现有的反馈机制或关联表,实现会话的收藏与展示分离。

  • 数据源逻辑 :后端通过查询 MessageFeedback 表(或关联表)中标记为 like 的记录来确定哪些 conversation_id 属于收藏会话。

  • API 过滤策略:

    • 请求 liked=true:后端计算出收藏的 ID 列表,并通过 include_ids 强制只返回这些会话。

    • 请求 liked=false (或默认):后端计算出收藏的 ID 列表,并通过 exclude_ids 将其排除(或视业务逻辑而定),实现"历史"与"收藏"的数据隔离。

  • 前端双流架构:

    • 前端维护两个独立的数据流(SWR Hooks):一个获取常规历史记录,一个专门获取收藏列表。

    • 通过客户端状态 activeTab 控制 UI 展示,避免频繁刷新整个页面。


2. 后端详细修改

涉及文件 :ConversationListApi (Controller层)

2.1 参数解析扩展

在 get 方法的 RequestParser 中新增了 liked 参数的接收:

Python

复制代码
# 接收前端传递的 liked 过滤参数 ('true', 'false' 或 None)
parser.add_argument("liked", type=str, choices=["true", "false", None], location="args")

2.2 核心过滤逻辑实现

在调用 Service 层之前,插入了针对 liked 参数的预处理逻辑:

  1. 获取收藏 ID 集合 : 查询 MessageFeedback 表,筛选出当前用户、当前 App 下 rating == 'like' 的所有 conversation_id。

    Python

    复制代码
    liked_stmt = (
        select(MessageFeedback.conversation_id)
        .where(
            MessageFeedback.app_id == app_model.id,
            MessageFeedback.rating == 'like',
            # ... 用户身份校验 ...
        )
        .distinct()
    )
    liked_conversation_ids = session.scalars(liked_stmt).all()
  2. ID 列表控制 (Include/Exclude):

    • 当 liked=True (请求收藏页) : 将 include_ids 设置为上述查询到的 liked_conversation_ids。如果同时存在 pinned 过滤,则取交集。

    • 当 liked=False (请求历史页) : 将 liked_conversation_ids 添加到 exclude_ids 中,确保历史页签不显示已收藏的内容(具体视需求而定,代码逻辑倾向于隔离)。

  3. 服务调用 : 将处理后的 liked 状态以及隐含的 include_ids/exclude_ids 传递给底层分页服务。

    Python

    复制代码
    return WebConversationService.pagination_by_last_id(
        # ... 其他参数
        liked=liked, # 传递处理后的参数
        # ...
    )

3. 前端详细修改

3.1 API 服务层 (service/share.ts)

  1. fetchConversations 修改 : 函数签名增加 liked?: boolean 参数,并在请求 params 中构造 liked: 'true'/'false'。

  2. 新增 updateConversationLike : 实现 PATCH 请求,用于变更会话的收藏状态。

    TypeScript

    复制代码
    export const updateConversationLike = async (..., liked: boolean) => {
      return getAction('patch', ...)(url, { body: { liked } })
    }

3.2 状态管理 Hook (useChatWithHistory.ts)

  1. 双 SWR 数据源: 保留原有的历史会话 SWR,新增专门用于获取收藏列表的 SWR:

    TypeScript

    复制代码
    const { data: appStarredConversationData, ... } = useSWR(
      appId ? ['appConversationData', ..., true] : null, // Key 中包含 true 以区分缓存
      () => fetchConversations(..., true), // 传入 liked=true
      { ... }
    )
  2. 表单默认值逻辑修正 : 在处理 inputsForms 时,修复了 defaultValue 变量作用域和覆盖的问题,确保 file-list 等特殊类型能正确初始化。

  3. 操作函数实现 : 新增 handleLikeConversation 和 handleUnlikeConversation。

    • 包含 App ID 非空校验 (Guard Clause)。

    • 调用 API 成功后,触发 handleUpdateConversationList 同时刷新历史和收藏两个列表。

3.3 UI 组件层 (Sidebar.tsx)

  1. 页签状态管理 : 引入 activeTab 状态 ('history' | 'starred')。

  2. 导航栏渲染 : 新增 Tab 切换区域,根据 activeTab 切换样式。

  3. 列表条件渲染:

    • activeTab === 'history':渲染置顶列表 + 普通历史列表(带时间分组)。

    • activeTab === 'starred':渲染 filteredStarredConversations(纯列表,不带分组)。

  4. 操作绑定 : 在 handleOperate 中增加 like 和 unlike 的分支处理,连接到 Context 中的操作函数。


4. 遇到的问题与解决方案

在开发过程中,主要解决了以下几个关键问题:

4.1 TypeScript 类型与 Context 同步问题

  • 问题 :在 Sidebar 中使用 starredConversationList 报错"属性不存在",或者 Hook 返回值与 Context 定义不匹配。

  • 解决:

    1. 修改 ChatWithHistoryContextValue 类型定义,显式增加 starredConversationList 及操作函数类型。

    2. 修改 Hook 的 return 语句,确保返回的对象键名 (starredConversationList) 与 Context 类型完全一致(之前错误写成了 starredConversation)。

    3. 在 ChatWithHistoryWrap 的 Provider value 中正确传入这些新属性。

4.2 ESLint 代码质量报错

  • 问题 :no-dead-store 和 unused-vars 报错,主要是 handleDelete 和 handleRename 定义了但未被 JSX 使用。

  • 解决:

    1. 恢复被注释的 handleDelete 和 handleRename 函数定义。

    2. 在 JSX 底部正确渲染 <Confirm> 和 <RenameModal> 组件,并将上述函数作为 onConfirm 属性传入,形成闭环引用。

4.3 表单初始化逻辑缺陷

  • 问题 :defaultValue 在 forEach 循环中被重新赋值为 item.default,导致针对 file-list 或 role 的特殊处理逻辑失效。

  • 解决 :优化逻辑,确保特殊处理后的 defaultValue 直接赋值给 conversationInputs,不再被后续代码覆盖。

4.4 AppID 类型安全

  • 问题 :调用 updateConversationLike 时,appId 可能为 undefined,导致 TS 报错或运行时隐患。

  • 解决 :在操作函数开头增加 if (!appId) return 的保护性判断,确保 API 调用时的参数安全性。

需要源码私我

相关推荐
信誓旦旦的程序猿4 小时前
【Python量化实战宝典 #08】可转债与债券接口手册:转股价·溢价率·纯债价值(Python 可跑)
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
Lethehong4 小时前
Oracle 加个字段,目标库就报错?KFS 用“先拆后组“打通异构 DDL
数据库·oracle
砚底藏山河4 小时前
python量化:行情数据回溯
java·开发语言·python·金融·maven
今年下半年4 小时前
AI智能体项目实战记录(02):初始化 Python 项目
开发语言·人工智能·python·fastapi
Williamtym4 小时前
PickGo Lab实践:用浏览器、Python和MCP串起机器人搬运仿真
开发语言·python·机器人
Web极客码4 小时前
锁定真正的磁盘卡顿:我用 OpenCode 配合 eBPF 诊断 NVMe 队列深度延迟与 I/O 写放大
数据库·oracle
CS创新实验室5 小时前
Python 对象“死“了之后去了哪?——引用计数与分代垃圾回收
开发语言·jvm·python
CCblandy5 小时前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
原则猫5 小时前
UDP 到底怎么实现可靠的
前端
Java后端的Ai之路5 小时前
14、ReactJS - 路由(Router)
前端·react.js·状态模式