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. 操作函数实现 : 新增 handleLikeConversationhandleUnlikeConversation

    • 包含 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 中增加 likeunlike 的分支处理,连接到 Context 中的操作函数。


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

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

4.1 TypeScript 类型与 Context 同步问题

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

  • 解决

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

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

    3. ChatWithHistoryWrapProvider value 中正确传入这些新属性。

4.2 ESLint 代码质量报错

  • 问题no-dead-storeunused-vars 报错,主要是 handleDeletehandleRename 定义了但未被 JSX 使用。

  • 解决

    1. 恢复被注释的 handleDeletehandleRename 函数定义。

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

4.3 表单初始化逻辑缺陷

  • 问题defaultValueforEach 循环中被重新赋值为 item.default,导致针对 file-listrole 的特殊处理逻辑失效。

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

4.4 AppID 类型安全

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

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

需要源码私我

相关推荐
zlpzlpzyd18 小时前
vue.js 3中全局组件和局部组件的区别
前端·javascript·vue.js
光羽隹衡18 小时前
MySQL的安装
数据库·mysql
脸大是真的好~18 小时前
尚硅谷-mysql专项训练-数据库服务的优化-慢查询-EXPLAIN字段
数据库·mysql·性能优化
浩星18 小时前
css实现类似element官网的磨砂屏幕效果
前端·javascript·css
_一路向北_19 小时前
爬虫框架:Feapder使用心得
爬虫·python
一只小风华~19 小时前
Vue.js 核心知识点全面解析
前端·javascript·vue.js
Dragon online19 小时前
数据分析师成长之路--从SQL恐惧到数据掌控者的蜕变
数据库·sql
2022.11.7始学前端19 小时前
n8n第七节 只提醒重要的待办
前端·javascript·ui·n8n
SakuraOnTheWay19 小时前
React Grab实践 | 记一次与Cursor的有趣对话
前端·cursor
阿星AI工作室19 小时前
gemini3手势互动圣诞树保姆级教程来了!附提示词
前端·人工智能