Files
TanWenyan 27aff59152 feat: 添加知识库配置管理功能
新增地区-知识库绑定管理功能,支持增删改查操作
- 添加 V3 API 路由层:area-datasets 相关接口
- 添加 API 客户端:area-datasets.ts
- 添加自定义 Hook:use-area-dataset-config.ts
- 添加管理组件:area-dataset-config.tsx
- 修复路由冲突问题,删除重复的 .ts 路由文件
- 更新 dataset-manager 页面,添加 Tabs 导航

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-07 23:12:21 +08:00

876 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dify 模块 API 接口文档
> 版本:2.3
> 更新时间:2025-01-22
> 基础路径:`/api/v3`
---
## 目录
1. [概述](#概述)
2. [权限说明](#权限说明)
3. [安全设计](#安全设计)
4. [地区知识库管理接口](#地区知识库管理接口)
- [获取当前用户可访问的知识库](#1-获取当前用户可访问的知识库)
- [获取所有知识库绑定列表](#2-获取所有知识库绑定列表管理员)
- [获取可用地区列表](#3-获取可用地区列表)
- [获取知识库绑定详情](#4-获取知识库绑定详情)
- [创建知识库绑定](#5-创建知识库绑定)
- [更新知识库绑定](#6-更新知识库绑定)
- [删除知识库绑定](#7-删除知识库绑定)
- [检查用户知识库访问权限](#8- [对话应用管理接口](#9-检查用户知识库访问权限)
- [获取可用的对话应用列表](#9-获取可用的对话应用列表)
- [获取默认对话应用](#10-获取默认对话应用)
5. [对话应用多实例支持](#对话应用多实例支持)
- [配置方式](#配置方式)
- [前端切换应用](#前端切换应用)
- [端口多实例特性](#端口多实例特性)
6. [错误码说明](#错误码说明)
- [接口说明](#接口说明)
7. [数据字典](#数据字典)
---
## 概述
Dify 模块提供 AI 对话和知识库管理功能。本文档主要描述**地区-知识库绑定管理**相关接口。
### 核心概念
|概念 |说明 |
|---|---|
|**地区(area)** |用户所属地区,如:梅州、云浮、揭阳、潮州 |
|**知识库绑定** |将 Dify 平台的知识库与特定地区关联,控制用户可访问的知识库范围 |
|**公共知识库** |`is_public=true` 的知识库,所有地区用户均可访问(如省级知识库) |
|**默认知识库** |`is_default=true` 的知识库,该地区用户的首选知识库 |
### 业务规则
1. **普通用户(common)只能访问**本地区绑定的知识库 + **公共知识库**
2. **市级管理员(admin)只能访问**本地区绑定的知识库 + **公共知识库**,可编辑本地区知识库内容
3. **省级管理员(provincial_admin)可以访问和管理**所有地区的知识库
4. 只有**省级管理员**可以管理知识库绑定(增删改查绑定关系)
5. 同一地区不能重复绑定同一知识库
6. 删除为软删除,可恢复
---
## 权限说明
### 权限键定义(无通配符)
|权限键 |说明 |适用角色 |
|---|---|---|
|`dify:chat:use` |使用 AI 对话 |common, provincial_admin, admin |
|`dify:conversation:read` |查看对话历史(仅自己) |common, provincial_admin, admin |
|`dify:conversation:delete` |删除对话(仅自己) |common, provincial_admin, admin |
|`dify:message:feedback` |消息反馈 |common, provincial_admin, admin |
|`dify:dataset:read` |查看知识库列表 |common, admin, provincial_admin |
|`dify:dataset:write` |编辑知识库内容 |admin(仅本地区), provincial_admin(全部) |
|`dify:dataset:manage` |管理知识库绑定(增删改查) |provincial_admin(仅省级管理员) |
|`dify:file:read` |下载/预览文件 |common, provincial_admin, admin |
|`dify:file:upload` |上传文件 |common, provincial_admin, admin |
### 角色权限矩阵
|接口 |common |admin(市级管理员) |provincial_admin(省级管理员) |
|---|---|---|---|
|获取用户可访问的知识库 |✅ 本地区+公共 |✅ 本地区+公共 |✅ 全部 |
|编辑知识库内容 |❌ |✅ 仅本地区 |✅ 全部 |
|获取所有绑定列表(管理) |❌ |❌ |✅ |
|创建/更新/删除绑定 |❌ |❌ |✅ |
|检查访问权限 |✅ |✅ |✅ |
### 数据范围说明
|数据范围 |代码值 |说明 |
|---|---|---|
|全部 |`ALL` |可访问所有地区数据(仅 provincial_admin |
|本地区 |`DEPT` |只能访问本地区数据 + 公共数据(common, admin |
|仅自己 |`SELF` |只能操作自己的数据(对话历史) |
---
## 安全设计
### 1. 对话数据越权防护
**问题**:用户 A 不能查看/删除用户 B 的对话
**解决方案**
1. **Dify 端隔离**:请求时强制注入 `user` 参数为当前登录用户的 `username`,无法伪造
2. **本地数据库隔离**`dify_conversation` 表的操作都带 `user_id` 条件
```sql
-- 删除对话时的 SQL
UPDATE dify_conversation
SET deleted_at = CURRENT_TIMESTAMP
WHERE user_id = $1 AND conversation_id = $2 -- 必须是自己的
```
### 2. 知识库数据范围控制
**问题**:普通用户和市级管理员不能访问其他地区的知识库
**解决方案**
```python
# 根据用户角色控制数据范围
if user_role == "provincial_admin":
# 仅省级管理员返回所有知识库
else:
# 市级管理员和普通用户:只返回本地区 + 公共知识库
WHERE area = {user_area} OR is_public = true
```
### 3. 请求伪造防护
**问题**:用户伪造请求参数访问他人数据
**解决方案**
- 所有用户身份信息从 JWT Token 解析,不信任请求参数
- `user` 参数强制覆盖,不允许前端传入
```python
# process_request_body 中
body_data['user'] = username # 强制使用当前用户
```
---
## 地区知识库管理接口
### 1. 获取当前用户可访问的知识库
根据当前登录用户的地区和角色,返回可访问的知识库列表。
**请求**
```
GET /api/v3/dify/area-datasets/my
```
**请求头**
|参数 |类型 |必填 |说明 |
|---|---|---|---|
|Authorization |string |是 |Bearer {token} |
**数据范围控制**
|角色 |返回数据 |
|---|---|
|common(普通员工) |用户所属地区的知识库 + 公共知识库 |
|admin(市级管理员) |用户所属地区的知识库 + 公共知识库 |
|provincial_admin(省级管理员) |所有地区的知识库 |
**响应**
```json
{
"code": 0,
"message": "success",
"data": {
"data": [
{
"id": 1,
"area": "省级",
"dataset_id": "fd680642-4493-416b-b592-972c69b3f595",
"dataset_name": "省级法务知识库",
"dataset_description": "全省通用法务知识库",
"is_default": false,
"is_public": true,
"sort_order": 0,
"status": 1,
"created_at": "2025-01-22T10:00:00",
"updated_at": "2025-01-22T10:00:00"
},
{
"id": 2,
"area": "梅州",
"dataset_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"dataset_name": "梅州法务知识库",
"dataset_description": "梅州地区专用知识库",
"is_default": true,
"is_public": false,
"sort_order": 10,
"status": 1,
"created_at": "2025-01-22T10:30:00",
"updated_at": "2025-01-22T10:30:00"
}
],
"total": 2,
"user_area": "梅州",
"user_role": "common"
}
}
```
**响应字段说明**
|字段 |类型 |说明 |
|---|---|---|
|data.data |array |知识库列表 |
|data.total |int |知识库总数 |
|data.user_area |string |当前用户所属地区 |
|data.user_role |string |当前用户角色 |
**错误响应**
|HTTP 状态码 |错误信息 |说明 |
|---|---|---|
|400 |用户未设置地区,无法获取知识库列表 |用户 area 字段为空(非管理员) |
|401 |JWT 认证失败 |Token 无效或过期 |
|403 |Permission denied |无 `dify:dataset:read` 权限 |
---
### 2. 获取所有知识库绑定列表(管理员)
获取系统中所有地区的知识库绑定记录,支持按地区筛选和分页。
**权限要求**`dify:dataset:manage`(仅 provincial_admin
**请求**
```
GET /api/v3/dify/area-datasets
```
**请求头**
|参数 |类型 |必填 |说明 |
|---|---|---|---|
|Authorization |string |是 |Bearer {token} |
**查询参数**
|参数 |类型 |必填 |默认值 |说明 |
|---|---|---|---|---|
|area |string |否 |- |筛选地区,如:梅州 |
|only_enabled |boolean |否 |true |是否只返回启用的记录 |
|page |int |否 |1 |页码,从 1 开始 |
|page_size |int |否 |20 |每页数量,范围 1-100 |
**请求示例**
```bash
curl -X GET "http://localhost:8000/api/v3/dify/area-datasets?area=梅州&page=1&page_size=20" \
-H "Authorization: Bearer {token}"
```
**响应**
```json
{
"code": 0,
"message": "success",
"data": {
"data": [
{
"id": 2,
"area": "梅州",
"dataset_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"dataset_name": "梅州法务知识库",
"dataset_description": "梅州地区专用知识库",
"is_default": true,
"is_public": false,
"sort_order": 10,
"status": 1,
"created_by": 1,
"created_at": "2025-01-22T10:30:00",
"updated_by": null,
"updated_at": "2025-01-22T10:30:00"
}
],
"total": 1,
"page": 1,
"page_size": 20,
"has_more": false
}
}
```
---
### 3. 获取可用地区列表
获取系统中所有可用的地区列表,用于管理员创建绑定时选择地区。
**权限要求**`dify:dataset:manage`
**请求**
```
GET /api/v3/dify/area-datasets/areas
```
**响应**
```json
{
"code": 0,
"message": "success",
"data": {
"data": ["云浮", "揭阳", "梅州", "潮州"]
}
}
```
---
### 4. 获取知识库绑定详情
根据绑定记录 ID 获取详情。
**权限要求**`dify:dataset:manage`
**请求**
```
GET /api/v3/dify/area-datasets/{dataset_bind_id}
```
**路径参数**
|参数 |类型 |必填 |说明 |
|---|---|---|---|
|dataset_bind_id |int |是 |绑定记录 ID |
**响应**
```json
{
"code": 0,
"message": "success",
"data": {
"data": {
"id": 2,
"area": "梅州",
"dataset_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"dataset_name": "梅州法务知识库",
"dataset_description": "梅州地区专用知识库",
"is_default": true,
"is_public": false,
"sort_order": 10,
"status": 1,
"created_by": 1,
"created_at": "2025-01-22T10:30:00",
"updated_by": null,
"updated_at": "2025-01-22T10:30:00"
}
}
}
```
---
### 5. 创建知识库绑定
将 Dify 知识库绑定到指定地区。
**权限要求**`dify:dataset:manage`
**请求**
```
POST /api/v3/dify/area-datasets
```
**请求体**
|参数 |类型 |必填 |默认值 |说明 |
|---|---|---|---|---|
|area |string |是 |- |地区名称(1-50字符) |
|dataset_id |string |是 |- |Dify 知识库 ID1-100字符) |
|dataset_name |string |是 |- |知识库名称(1-255字符) |
|dataset_description |string |否 |null |知识库描述 |
|is_default |boolean |否 |false |是否为该地区默认知识库 |
|is_public |boolean |否 |false |是否对所有地区公开 |
|sort_order |int |否 |0 |排序顺序,越小越靠前 |
**请求示例**
```bash
curl -X POST "http://localhost:8000/api/v3/dify/area-datasets" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"area": "梅州",
"dataset_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"dataset_name": "梅州法务知识库",
"dataset_description": "梅州地区专用知识库",
"is_default": true,
"is_public": false,
"sort_order": 10
}'
```
**响应**
```json
{
"code": 0,
"message": "success",
"data": {
"data": {
"id": 2,
"area": "梅州",
"dataset_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"dataset_name": "梅州法务知识库",
"dataset_description": "梅州地区专用知识库",
"is_default": true,
"is_public": false,
"sort_order": 10,
"status": 1,
"created_at": "2025-01-22T10:30:00"
},
"message": "创建成功"
}
}
```
**错误响应**
|HTTP 状态码 |错误信息 |说明 |
|---|---|---|
|400 |地区 '梅州' 已绑定知识库 'xxx' |重复绑定 |
|422 |Validation Error |参数校验失败 |
---
### 6. 更新知识库绑定
更新知识库绑定信息,支持部分更新。
**权限要求**`dify:dataset:manage`
**请求**
```
PUT /api/v3/dify/area-datasets/{dataset_bind_id}
```
**请求体**
|参数 |类型 |必填 |说明 |
|---|---|---|---|
|dataset_name |string |否 |知识库名称 |
|dataset_description |string |否 |知识库描述 |
|is_default |boolean |否 |是否默认 |
|is_public |boolean |否 |是否公开 |
|sort_order |int |否 |排序顺序 |
|status |int |否 |状态:1=启用, 0=禁用 |
**响应**
```json
{
"code": 0,
"message": "success",
"data": {
"data": {
"id": 2,
"area": "梅州",
"dataset_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"dataset_name": "梅州法务知识库(更新)",
"is_default": true,
"sort_order": 5,
"status": 1,
"updated_at": "2025-01-22T11:00:00"
},
"message": "更新成功"
}
}
```
---
### 7. 删除知识库绑定
软删除知识库绑定记录。
**权限要求**`dify:dataset:manage`
**请求**
```
DELETE /api/v3/dify/area-datasets/{dataset_bind_id}
```
**响应**
```json
{
"code": 0,
"message": "success",
"data": {
"message": "删除成功"
}
}
```
---
### 8. 检查用户知识库访问权限
检查当前用户是否有权访问指定的 Dify 知识库。
**权限要求**`dify:dataset:read`
**请求**
```
GET /api/v3/dify/area-datasets/check/{dataset_id}
```
**路径参数**
|参数 |类型 |必填 |说明 |
|---|---|---|---|
|dataset_id |string |是 |Dify 知识库 ID |
**响应(有权限)**
```json
{
"code": 0,
"message": "success",
"data": {
"has_access": true,
"user_area": "梅州",
"dataset_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
}
}
```
**响应(无权限)**
```json
{
"code": 0,
"message": "success",
"data": {
"has_access": false,
"user_area": "梅州",
"dataset_id": "xyz-not-allowed"
}
}
```
---
## 错误码说明
### HTTP 状态码
|状态码 |说明 |
|---|---|
|200 |请求成功 |
|400 |请求参数错误 |
|401 |未认证或 Token 无效 |
|403 |权限不足 |
|404 |资源不存在 |
|422 |参数校验失败 |
|500 |服务器内部错误 |
### 业务错误码
|code |说明 |
|---|---|
|0 |成功 |
|-1 |通用错误 |
---
## 数据字典
### dify_area_dataset 表
|字段 |类型 |说明 |
|---|---|---|
|id |int |主键 ID |
|area |varchar(50) |地区名称 |
|dataset_id |varchar(100) |Dify 知识库 ID |
|dataset_name |varchar(255) |知识库名称 |
|dataset_description |text |知识库描述 |
|is_default |boolean |是否为该地区默认知识库 |
|is_public |boolean |是否对所有地区公开 |
|sort_order |int |排序顺序,越小越靠前 |
|status |smallint |状态:1=启用, 0=禁用 |
|created_by |int |创建人 ID |
|created_at |timestamp |创建时间 |
|updated_by |int |更新人 ID |
|updated_at |timestamp |更新时间 |
|deleted_at |timestamp |删除时间(软删除) |
### 地区枚举值
|值 |说明 |
|---|---|
|梅州 |梅州地区 |
|云浮 |云浮地区 |
|揭阳 |揭阳地区 |
|潮州 |潮州地区 |
|省级 |省级(用于公共知识库) |
---
## 使用示例
### 场景1:管理员配置知识库
```bash
# 1. 创建省级公共知识库(所有用户可见)
curl -X POST "/api/v3/dify/area-datasets" \
-H "Authorization: Bearer {admin_token}" \
-d '{
"area": "省级",
"dataset_id": "省级知识库ID",
"dataset_name": "省级法务知识库",
"is_public": true,
"sort_order": 0
}'
# 2. 为各地区创建专属知识库(仅该地区用户可见)
curl -X POST "/api/v3/dify/area-datasets" \
-d '{"area": "梅州", "dataset_id": "梅州知识库ID", "dataset_name": "梅州法务知识库", "is_default": true}'
curl -X POST "/api/v3/dify/area-datasets" \
-d '{"area": "云浮", "dataset_id": "云浮知识库ID", "dataset_name": "云浮法务知识库", "is_default": true}'
```
### 场景2:普通用户获取可访问的知识库
```bash
# 梅州用户(common 角色)
curl -X GET "/api/v3/dify/area-datasets/my" \
-H "Authorization: Bearer {meizhou_user_token}"
# 返回:省级公共知识库 + 梅州专属知识库(2条)
```
### 场景3:省级管理员获取所有知识库
```bash
# 省级管理员(provincial_admin 角色)
curl -X GET "/api/v3/dify/area-datasets/my" \
-H "Authorization: Bearer {provincial_admin_token}"
# 返回:所有地区的知识库(包括梅州、云浮、揭阳、潮州、省级)
```
### 场景4:前端调用 Dify API 前检查权限
```bash
# 检查用户是否有权访问某个知识库
curl -X GET "/api/v3/dify/area-datasets/check/某知识库ID" \
-H "Authorization: Bearer {token}"
# 根据 has_access 字段决定是否允许调用 Dify API
```
---
## 9. 对话应用管理接口
### 9.1 获取可用的对话应用列表
**接口**: `GET /api/v3/dify/chat-apps`
**权限**: `dify:chat:use`
**说明**: 获取当前实例配置的所有对话应用列表,供前端切换使用。
**请求示例**:
```bash
curl -X GET "http://localhost:8000/api/v3/dify/chat-apps" \
-H "Authorization: Bearer <token>"
```
**Response Body**: `success (data)`
```json
{
"code": 200,
"msg": "OK",
"data": {
"data": [
{
"app_id": "app-xxx",
"app_name": "AI法务助手",
"description": "通用法律咨询助手",
"is_default": true
},
{
"app_id": "app-yyy",
"app_name": "合同审查助手",
"description": "专业合同分析",
"is_default": false
}
],
"total": 2
}
}
```
**字段说明**:
|字段 |类型 |说明 |
|---|---|---|
|`app_id` |string |对话应用 ID |
|`app_name` |string |对话应用名称 |
|`description` |string |应用描述 |
|`is_default` |boolean |是否为默认应用 |
### 9.2 获取默认对话应用
**接口**: `GET /api/v3/dify/chat-apps/default`
**权限**: `dify:chat:use`
**说明**: 获取当前配置的默认对话应用。
**请求示例**:
```bash
curl -X GET "http://localhost:8000/api/v3/dify/chat-apps/default" \
-H "Authorization: Bearer <token>"
```
**Response Body**: `success (data)`
```json
{
"code": 200,
"msg": "OK",
"data": {
"data": {
"app_id": "app-xxx",
"app_name": "AI法务助手",
"description": "通用法律咨询助手",
"is_default": true
}
}
}
```
---
## 10. 对话应用多实例支持
### 10.1 配置方式
`config/env.{port}` 配置文件中添加 `DIFY_CHAT_APPS` 配置项:
**格式**:
```ini
DIFY_CHAT_APPS=app_id:api_key:app_name:description|app_id2:api_key2:app_name2:description
**说明**:
- 多个应用用 `|` 分隔
- 每个应用格式:`app_id`:`api_key`:`app_name`:`description`
- 第一个应用为默认应用
**示例**:
```ini
DIFY_CHAT_APPS=app-dmYZISz60ZGQHlJAPHmngoZZ:app-dmYZISz60ZGQHlJAPHmngoZZ:AI法务助手:通用法律咨询助手|app-xxx:app-xxx:合同审查助手:专业合同分析
```
### 10.2 前端切换应用
发送对话消息时,可通过以下方式指定应用:
**方式 1:请求头(推荐)**
```javascript
axios.post('/api/dify/chat/chat-messages', data, {
headers: {
'Authorization': 'Bearer {token}',
'X-Dify-App-Id': 'app-xxx' // 指定应用 ID
}
})
```
**方式 2:查询参数**
```javascript
axios.post('/api/dify/chat/chat-messages?app_id=app-xxx', data, {
headers: {
'Authorization': 'Bearer {token}'
}
})
```
**使用默认应用**:
不指定 `X-Dify-App-Id``app_id` 时,使用配置文件中的第一个应用作为默认应用。
### 10.3 端口多实例特性
本系统的特点是**端口多实例**部署架构,每个端口读取对应的配置文件:
|端口 |配置文件 |默认对话应用 |
|---|---|---|
|8073 |`config/env.8073` |梅州实例的对话应用 |
|8000 |`config/env.8000` |云浮实例的对话应用 |
|8001 |`config/env.8001` |揭阳实例的对话应用 |
|8002 |`config/env.8002` |潮州实例的对话应用 |
因此,前端请求不同端口时,会获得不同的对话应用列表:
```javascript
// 请求梅州实例
GET http://172.16.0.55:8073/api/v3/dify/chat-apps
// 返回梅州配置: [梅州法务助手, 梅州合同审查]
// 请求云浮实例
GET http://172.16.0.55:8000/api/v3/dify/chat-apps
// 返回云浮配置: [云浮法务助手, 云浮合同审查]
```
这种设计实现了区域间的业务隔离,无需后端代码判断,完全由配置驱动。
---
## 11. 更新日志
|版本 |日期 |说明 |
|---|---|---|
|2.3 |2025-01-22 |新增对话应用多实例支持,支持配置多个对话应用供前端切换 |
|2.2 |2025-01-22 |修正角色权限:市级管理员(admin)只能访问本地区,省级管理员(provincial_admin)可访问全部;知识库绑定管理仅限省级管理员 |
|2.1 |2025-01-22 |修正权限角色(去掉 area_admin),添加数据范围控制和越权防护说明 |
|2.0 |2025-01-22 |新增地区-知识库绑定管理接口 |
|1.0 |2025-01-20 |初始版本,Dify 代理接口 |