5 小时以前 16697df76d7b27ff65d229937b3f3ac4cc0e56a0
docs(knowledge-base): 添加知识库模块传参方式和参数命名规范文档

- 定义了参数命名基本规范,包括ID、列表、状态、数量、时间相关命名约定
- 明确了不同HTTP方法的传参方式,GET使用params、POST/DELETE使用data
- 提供了完整的知识库管理、文件管理、知识问答接口参数对照表
- 规定了响应数据字段结构和前端组件传参标准
- 制定了参数类型转换和验证规范,确保数据一致性
- 总结了常见错误和最佳实践建议,避免开发中的常见问题
已添加1个文件
已修改2个文件
1239 ■■■■■ 文件已修改
doc/知识库模块传参方式和参数命名规范文档.md 941 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/collaborativeApproval/knowledgeBase.js 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/knowledgeBase/index.vue 126 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/֪ʶ¿âÄ£¿é´«²Î·½Ê½ºÍ²ÎÊýÃüÃû¹æ·¶Îĵµ.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,941 @@
# çŸ¥è¯†åº“模块传参方式和参数命名规范文档
## ä¸€ã€æ¦‚è¿°
本文档详细说明知识库模块中所有接口、组件、方法的传参方式和参数命名规范,旨在:
- ç»Ÿä¸€å‰åŽç«¯å‚数命名规范
- æ˜Žç¡®å‚数类型和必填性
- è§„范传参方式(GET params、POST body、DELETE data)
- æä¾›æ¸…晰的参数映射关系
---
## äºŒã€å‚数命名规范
### 2.1 åŸºæœ¬è§„范
#### å‘½åé£Žæ ¼
- **后端参数**: éµå¾ª Java é©¼å³°å‘½åæ³• (camelCase)
- **前端参数**: éµå¾ª JavaScript é©¼å³°å‘½åæ³• (camelCase)
- **数据库字段**: éµå¾ª MySQL ä¸‹åˆ’线命名法 (snake_case)
- **接口URL**: éµå¾ª RESTful é£Žæ ¼,使用小写和连字符
#### å‘½åçº¦å®š
1. **ID相关**: ç»Ÿä¸€ä½¿ç”¨ `id`、`Id` åŽç¼€
   - `knowledgeBaseId` - çŸ¥è¯†åº“ID
   - `storageBlobId` - æ–‡ä»¶blob ID
   - `vectorId` - å‘量记录ID
   - `memoryId` - ä¼šè¯ID
2. **列表相关**: ç»Ÿä¸€ä½¿ç”¨ `Ids` åŽç¼€æˆ–数组类型
   - `storageBlobIds` - æ–‡ä»¶blob ID列表
   - `ids` - é€šç”¨ID列表
3. **状态相关**: ç»Ÿä¸€ä½¿ç”¨ `Status` åŽç¼€
   - `vectorStatus` - å‘量化状态
4. **数量相关**: ç»Ÿä¸€ä½¿ç”¨ `Count` åŽç¼€
   - `fileCount` - æ–‡ä»¶æ•°é‡
   - `chunkCount` - åˆ‡ç‰‡æ•°é‡
   - `totalChunkCount` - æ€»åˆ‡ç‰‡æ•°é‡
   - `usageCount` - ä½¿ç”¨æ¬¡æ•°
5. **时间相关**: ç»Ÿä¸€ä½¿ç”¨ `Time` åŽç¼€
   - `createTime` - åˆ›å»ºæ—¶é—´
   - `updateTime` - æ›´æ–°æ—¶é—´
---
## ä¸‰ã€æŽ¥å£ä¼ å‚方式规范
### 3.1 GET è¯·æ±‚ - ä½¿ç”¨ params
**适用场景**: æŸ¥è¯¢ã€åˆ—表、分页等获取数据的接口
**传参方式**: é€šè¿‡ URL å‚数传递,使用 `params`
**示例**:
```javascript
// æŸ¥è¯¢çŸ¥è¯†åº“列表
export function listKnowledgeBase(query) {
  return request({
    url: "/knowledgeBase/getList",
    method: "get",
    params: query,  // âœ… GET请求使用 params
  });
}
// å®žé™…调用
listKnowledgeBase({
  current: 1,      // å½“前页码
  size: 20,        // æ¯é¡µæ¡æ•°
  title: "",       // çŸ¥è¯†æ ‡é¢˜(可选)
  type: ""         // çŸ¥è¯†ç±»åž‹(可选)
});
```
**URL格式**: `/knowledgeBase/getList?current=1&size=20&title=&type=`
**规范要点**:
- âœ… æŸ¥è¯¢å‚数统一放在 `params` ä¸­
- âœ… åˆ†é¡µå‚数命名: `current` (当前页)、`size` (每页条数)
- âœ… æœç´¢å‚数命名: ä¸Žå®žä½“字段保持一致
- âœ… è·¯å¾„参数使用 URL å ä½ç¬¦: `/path/{id}`
---
### 3.2 POST è¯·æ±‚ - ä½¿ç”¨ data
**适用场景**: æ–°å¢žã€æ›´æ–°ã€ä¿å­˜ç­‰æäº¤æ•°æ®çš„æŽ¥å£
**传参方式**: é€šè¿‡è¯·æ±‚体传递,使用 `data`
**示例**:
```javascript
// æ–°å¢žçŸ¥è¯†åº“
export function addKnowledgeBase(data) {
  return request({
    url: "/knowledgeBase/add",
    method: "post",
    data: data,  // âœ… POST请求使用 data
  });
}
// å®žé™…调用
addKnowledgeBase({
  title: "操作手册",
  type: "guide",
  scenario: "系统操作指导",
  efficiency: "high",
  problem: "用户不会操作系统",
  solution: "按照操作手册执行...",
  keyPoints: "步骤1,步骤2,步骤3",
  creator: "张三",
  usageCount: 0
});
```
**请求体格式**: JSON æ ¼å¼,`Content-Type: application/json`
**规范要点**:
- âœ… æäº¤æ•°æ®ç»Ÿä¸€æ”¾åœ¨ `data` ä¸­
- âœ… å‚数名与后端实体字段保持一致
- âœ… å¿…填参数需要在表单验证规则中声明
- âœ… æ•°å€¼ç±»åž‹å‚数需指定默认值
---
### 3.3 DELETE è¯·æ±‚ - ä½¿ç”¨ data
**适用场景**: åˆ é™¤ã€æ‰¹é‡åˆ é™¤ç­‰æ“ä½œ
**传参方式**: é€šè¿‡è¯·æ±‚体传递数组或对象,使用 `data`
**示例**:
```javascript
// åˆ é™¤çŸ¥è¯†åº“
export function delKnowledgeBase(query) {
  return request({
    url: "/knowledgeBase/delete",
    method: "delete",
    data: query,  // âœ… DELETE请求使用 data ä¼ é€’数组
  });
}
// å®žé™…调用(批量删除)
delKnowledgeBase([1, 2, 3]);  // âœ… ç›´æŽ¥ä¼ é€’ID数组
```
**请求体格式**: JSON æ•°ç»„ `[1, 2, 3]`
**规范要点**:
- âœ… DELETE请求的参数放在 `data` ä¸­
- âœ… æ‰¹é‡åˆ é™¤ä¼ é€’ID数组
- âœ… å•个删除也可以传递数组 `[id]`
- âš ï¸ ä¸è¦ä½¿ç”¨ `params` ä¼ é€’删除参数
---
### 3.4 æµå¼è¯·æ±‚ - ä½¿ç”¨ Fetch API
**适用场景**: AI问答、流式输出等需要实时响应的接口
**传参方式**: ä½¿ç”¨åŽŸç”Ÿ Fetch API,不支持 axios
**示例**:
```javascript
// çŸ¥è¯†åº“问答(流式)
export function knowledgeChat(data) {
  const token = getToken();
  return fetch(import.meta.env.VITE_APP_BASE_API + '/ai/knowledge/chat', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + token
    },
    body: JSON.stringify(data)  // âœ… ä½¿ç”¨ body ä¼ é€’参数
  });
}
// å®žé™…调用
knowledgeChat({
  knowledgeBaseId: 10,
  memoryId: "session-xxx",
  question: "如何操作审批流程?"
});
```
**规范要点**:
- âœ… æµå¼æŽ¥å£å¿…须使用 Fetch API
- âœ… å‚数使用 `JSON.stringify()` åºåˆ—化
- âœ… å¿…须携带 Authorization header
- âš ï¸ axios ä¸æ”¯æŒæµå¼å“åº”,不要使用
---
## å››ã€å®Œæ•´å‚数对照表
### 4.1 çŸ¥è¯†åº“管理接口参数
#### æŸ¥è¯¢åˆ—表 (`GET /knowledgeBase/getList`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| current | Integer | æ˜¯ | params | å½“前页码 | 1 |
| size | Integer | æ˜¯ | params | æ¯é¡µæ¡æ•° | 20 |
| title | String | å¦ | params | çŸ¥è¯†æ ‡é¢˜(模糊搜索) | "操作" |
| type | String | å¦ | params | çŸ¥è¯†ç±»åž‹(精确匹配) | "guide" |
**前端调用**:
```javascript
listKnowledgeBase({
  current: 1,
  size: 20,
  title: "",
  type: ""
});
```
---
#### æ–°å¢žçŸ¥è¯†åº“ (`POST /knowledgeBase/add`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| title | String | æ˜¯ | data | çŸ¥è¯†æ ‡é¢˜ | "操作手册" |
| type | String | æ˜¯ | data | çŸ¥è¯†ç±»åž‹ | "guide" |
| scenario | String | å¦ | data | é€‚用场景 | "系统操作" |
| efficiency | String | å¦ | data | è§£å†³æ•ˆçއ | "high" |
| problem | String | æ˜¯ | data | é—®é¢˜æè¿° | "用户不会操作" |
| solution | String | æ˜¯ | data | è§£å†³æ–¹æ¡ˆ | "参考手册" |
| keyPoints | String | å¦ | data | å…³é”®è¦ç‚¹ | "步骤1,步骤2" |
| creator | String | å¦ | data | åˆ›å»ºäºº | "张三" |
| usageCount | Integer | å¦ | data | ä½¿ç”¨æ¬¡æ•° | 0 |
**前端调用**:
```javascript
addKnowledgeBase({
  title: "操作手册",
  type: "guide",
  scenario: "系统操作指导",
  efficiency: "high",
  problem: "用户不会操作系统",
  solution: "按照操作手册执行...",
  keyPoints: "步骤1,步骤2,步骤3",
  creator: "张三",
  usageCount: 0
});
```
---
#### æ›´æ–°çŸ¥è¯†åº“ (`POST /knowledgeBase/update`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| id | Long | æ˜¯ | data | çŸ¥è¯†åº“ID | 10 |
| *(其他参数同新增)* | - | - | data | - | - |
**前端调用**:
```javascript
updateKnowledgeBase({
  id: 10,
  title: "操作手册(更新)",
  type: "guide",
  // ...其他参数
});
```
---
#### åˆ é™¤çŸ¥è¯†åº“ (`DELETE /knowledgeBase/delete`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| ids | Long[] | æ˜¯ | data | çŸ¥è¯†åº“ID数组 | [1, 2, 3] |
**前端调用**:
```javascript
delKnowledgeBase([1, 2, 3]);
```
---
### 4.2 æ–‡ä»¶ç®¡ç†æŽ¥å£å‚æ•°
#### æŸ¥è¯¢å‘量化状态 (`GET /knowledgeBase/vector/status/{knowledgeBaseId}`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| knowledgeBaseId | Long | æ˜¯ | URL路径 | çŸ¥è¯†åº“ID | 10 |
**前端调用**:
```javascript
getVectorStatus(10);
```
**URL**: `/knowledgeBase/vector/status/10`
---
#### ä¿å­˜æ–‡ä»¶å…³è” (`POST /knowledgeBase/file/save`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| knowledgeBaseId | Long | æ˜¯ | data | çŸ¥è¯†åº“ID | 10 |
| storageBlobIds | Long[] | æ˜¯ | data | æ–‡ä»¶blob ID数组 | [123, 124] |
**前端调用**:
```javascript
saveKnowledgeBaseFiles({
  knowledgeBaseId: 10,
  storageBlobIds: [123, 124]
});
```
**重要说明**:
- âš ï¸ **必须先调用 `/common/upload` ä¸Šä¼ æ–‡ä»¶**
- âš ï¸ **获取返回的 `data.id` ä½œä¸º `storageBlobId`**
- âš ï¸ **此接口触发异步向量化处理**
---
#### åˆ é™¤çŸ¥è¯†åº“文件 (`DELETE /knowledgeBase/file/delete`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| ids | Long[] | æ˜¯ | data | å‘量记录ID数组 | [1, 2, 3] |
**前端调用**:
```javascript
deleteKnowledgeBaseFile([row.id]);
// æ³¨æ„: row.id æ˜¯å‘量记录的ID,不是 storageBlobId
```
---
#### é‡æ–°å‘量化 (`POST /knowledgeBase/vector/reprocess/{vectorId}`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| vectorId | Long | æ˜¯ | URL路径 | å‘量记录ID | 1 |
**前端调用**:
```javascript
reprocessVector(1);
```
**URL**: `/knowledgeBase/vector/reprocess/1`
---
### 4.3 çŸ¥è¯†é—®ç­”接口参数
#### çŸ¥è¯†åº“问答 (`POST /ai/knowledge/chat` - æµå¼)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| knowledgeBaseId | Long | æ˜¯ | body | çŸ¥è¯†åº“ID | 10 |
| memoryId | String | æ˜¯ | body | ä¼šè¯ID | "kb-chat-xxx" |
| question | String | æ˜¯ | body | ç”¨æˆ·é—®é¢˜ | "如何操作?" |
**前端调用**:
```javascript
knowledgeChat({
  knowledgeBaseId: chatKnowledgeBaseId.value,
  memoryId: memoryId.value,
  question: currentQuestion
});
```
**会话ID生成规范**:
```javascript
// æ–¹å¼1: ä½¿ç”¨ crypto.randomUUID() (推荐)
memoryId.value = crypto.randomUUID();
// æ–¹å¼2: ä½¿ç”¨æ—¶é—´æˆ³
memoryId.value = 'kb-chat-' + Date.now();
```
---
#### æŸ¥è¯¢é—®ç­”历史 (`GET /ai/knowledge/history/{memoryId}`)
| å‚数名 | ç±»åž‹ | å¿…å¡« | ä¼ å‚位置 | è¯´æ˜Ž | ç¤ºä¾‹å€¼ |
|--------|------|------|----------|------|--------|
| memoryId | String | æ˜¯ | URL路径 | ä¼šè¯ID | "kb-chat-xxx" |
**前端调用**:
```javascript
getKnowledgeHistory('kb-chat-xxx');
```
---
## äº”、响应数据字段对照表
### 5.1 çŸ¥è¯†åº“列表响应
```javascript
{
  code: 200,
  data: {
    total: 100,
    records: [
      {
        id: 1,                     // çŸ¥è¯†åº“ID
        title: "操作手册",          // çŸ¥è¯†æ ‡é¢˜
        type: "guide",             // çŸ¥è¯†ç±»åž‹
        scenario: "系统操作",       // é€‚用场景
        efficiency: "high",        // è§£å†³æ•ˆçއ
        problem: "...",            // é—®é¢˜æè¿°
        solution: "...",           // è§£å†³æ–¹æ¡ˆ
        keyPoints: "...",          // å…³é”®è¦ç‚¹
        creator: "张三",           // åˆ›å»ºäºº
        usageCount: 10,            // ä½¿ç”¨æ¬¡æ•°
        fileCount: 3,              // æ–‡ä»¶æ•°é‡
        totalChunkCount: 45,       // æ€»åˆ‡ç‰‡æ•°é‡
        createTime: "2026-06-08",  // åˆ›å»ºæ—¶é—´
        updateTime: "2026-06-08"   // æ›´æ–°æ—¶é—´
      }
    ]
  }
}
```
---
### 5.2 æ–‡ä»¶å‘量化状态响应
```javascript
{
  code: 200,
  data: [
    {
      id: 1,                      // å‘量记录ID
      storageBlobId: 123,         // æ–‡ä»¶blob ID
      fileName: "操作手册.docx",  // æ–‡ä»¶å
      fileType: "docx",           // æ–‡ä»¶ç±»åž‹
      vectorStatus: 2,            // å‘量化状态: 0-待处理, 1-处理中, 2-已完成, 3-失败
      chunkCount: 15,             // åˆ‡ç‰‡æ•°é‡
      namespace: "kb-10",         // å‘量命名空间
      vectorError: null,          // å‘量化错误信息
      createTime: "2026-06-08"    // åˆ›å»ºæ—¶é—´
    }
  ]
}
```
---
### 5.3 æ–‡ä»¶ä¸Šä¼ å“åº”
```javascript
{
  code: 200,
  data: {
    id: 123,                      // âš ï¸ è¿™æ˜¯ storageBlobId,用于保存文件关联
    name: "操作手册.docx",        // æ–‡ä»¶å
    url: "/profile/upload/...",   // æ–‡ä»¶URL
    previewURL: "...",            // é¢„览URL
    downloadURL: "..."            // ä¸‹è½½URL
  }
}
```
**重要**: ä¸Šä¼ æˆåŠŸåŽ,需要提取 `response.data.id` ä½œä¸º `storageBlobId`
---
## å…­ã€å‰ç«¯ç»„件传参规范
### 6.1 è¡¨æ ¼ç»„件传参
```vue
<PIMTable
  rowKey="id"              <!-- è¡Œå”¯ä¸€æ ‡è¯†å­—段 -->
  :column="tableColumn"    <!-- åˆ—配置 -->
  :tableData="tableData"   <!-- è¡¨æ ¼æ•°æ® -->
  :page="page"             <!-- åˆ†é¡µé…ç½® -->
  :isSelection="true"      <!-- æ˜¯å¦æ”¯æŒé€‰æ‹© -->
  @selection-change="handleSelectionChange"
  :tableLoading="tableLoading"
  @pagination="pagination"
  :total="page.total"
/>
```
**分页配置**:
```javascript
page: {
  current: 1,    // å½“前页码
  size: 20,      // æ¯é¡µæ¡æ•°
  total: 0       // æ€»è®°å½•æ•°
}
```
---
### 6.2 å¼¹çª—组件传参
```vue
<FormDialog
  v-model="dialogVisible"       <!-- æŽ§åˆ¶æ˜¾ç¤º -->
  :title="dialogTitle"          <!-- å¼¹çª—标题 -->
  :width="'800px'"              <!-- å¼¹çª—宽度 -->
  @close="closeDialog"          <!-- å…³é—­å›žè°ƒ -->
  @confirm="submitForm"         <!-- ç¡®è®¤å›žè°ƒ -->
  @cancel="closeDialog"         <!-- å–消回调 -->
>
  <!-- å¼¹çª—内容 -->
</FormDialog>
```
---
### 6.3 ä¸Šä¼ ç»„件传参
```vue
<el-upload
  :action="uploadUrl"                    <!-- ä¸Šä¼ åœ°å€ -->
  :headers="uploadHeaders"              <!-- è¯·æ±‚头 -->
  :on-success="handleUploadSuccess"     <!-- æˆåŠŸå›žè°ƒ -->
  :on-error="handleUploadError"         <!-- å¤±è´¥å›žè°ƒ -->
  :before-upload="beforeUpload"         <!-- ä¸Šä¼ å‰æ ¡éªŒ -->
  multiple                               <!-- æ”¯æŒå¤šé€‰ -->
  :show-file-list="false"               <!-- ä¸æ˜¾ç¤ºæ–‡ä»¶åˆ—表 -->
  accept=".txt,.md,.docx,.xlsx,.xls,.pdf"  <!-- æ–‡ä»¶ç±»åž‹é™åˆ¶ -->
/>
```
**上传配置**:
```javascript
const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/common/upload";
const uploadHeaders = {
  Authorization: "Bearer " + getToken()
};
```
---
## ä¸ƒã€å‚数类型转换规范
### 7.1 å­—符串转数值
```javascript
// åŽç«¯è¿”回的数值可能是字符串,需要转换
const id = Number(row.id);
const count = parseInt(row.chunkCount, 10);
```
---
### 7.2 æ•°å€¼è½¬å­—符串
```javascript
// URL路径参数需要字符串
const url = `/knowledgeBase/vector/status/${String(knowledgeBaseId)}`;
```
---
### 7.3 æ•°ç»„处理
```javascript
// ID数组处理
const ids = selection.map(item => item.id);  // âœ… æå–ID
await delKnowledgeBase(ids);                 // âœ… ä¼ é€’数组
// æ–‡ä»¶blob ID数组
const blobIds = uploadedFiles.map(file => file.id);
await saveKnowledgeBaseFiles({
  knowledgeBaseId: currentKnowledgeBase.id,
  storageBlobIds: blobIds
});
```
---
## å…«ã€å‚数验证规范
### 8.1 è¡¨å•验证规则
```javascript
const rules = {
  title: [
    { required: true, message: "请输入知识标题", trigger: "blur" }
  ],
  type: [
    { required: true, message: "请选择知识类型", trigger: "change" }
  ],
  problem: [
    { required: true, message: "请描述遇到的问题", trigger: "blur" }
  ],
  solution: [
    { required: true, message: "请详细描述解决方案", trigger: "blur" }
  ]
};
```
---
### 8.2 ä¸Šä¼ æ–‡ä»¶æ ¡éªŒ
```javascript
const beforeUpload = (file) => {
  // æ–‡ä»¶ç±»åž‹æ ¡éªŒ
  const allowedTypes = ['.txt', '.md', '.docx', '.xlsx', '.xls', '.pdf'];
  const fileName = file.name.toLowerCase();
  const isAllowed = allowedTypes.some(type => fileName.endsWith(type));
  if (!isAllowed) {
    ElMessage.error('只支持 txt、md、docx、xlsx、xls、pdf æ ¼å¼çš„æ–‡ä»¶');
    return false;
  }
  // æ–‡ä»¶å¤§å°æ ¡éªŒ
  const isLt50M = file.size / 1024 / 1024 < 50;
  if (!isLt50M) {
    ElMessage.error('文件大小不能超过 50MB');
    return false;
  }
  return true;
};
```
---
### 8.3 é—®ç­”参数校验
```javascript
const sendQuestion = async () => {
  // ç©ºå†…容校验
  if (!questionInput.value.trim()) {
    ElMessage.warning("请输入问题");
    return;
  }
  // çŸ¥è¯†åº“选择校验
  if (!chatKnowledgeBaseId.value) {
    ElMessage.warning("请先选择知识库");
    return;
  }
  // å‘送状态校验
  if (sending.value) {
    return;  // é˜²æ­¢é‡å¤å‘送
  }
  // ...发送请求
};
```
---
## ä¹ã€å¸¸è§é”™è¯¯å’Œè§£å†³æ–¹æ¡ˆ
### 9.1 å‚数名不匹配
**错误示例**:
```javascript
// âŒ é”™è¯¯: ä½¿ç”¨äº†ä¸‹åˆ’线命名
saveKnowledgeBaseFiles({
  knowledge_base_id: 10,
  storage_blob_ids: [123]
});
// âœ… æ­£ç¡®: ä½¿ç”¨é©¼å³°å‘½å
saveKnowledgeBaseFiles({
  knowledgeBaseId: 10,
  storageBlobIds: [123]
});
```
---
### 9.2 ä¼ å‚位置错误
**错误示例**:
```javascript
// âŒ é”™è¯¯: POST请求使用 params
export function addKnowledgeBase(data) {
  return request({
    url: "/knowledgeBase/add",
    method: "post",
    params: data  // âŒ é”™è¯¯
  });
}
// âœ… æ­£ç¡®: POST请求使用 data
export function addKnowledgeBase(data) {
  return request({
    url: "/knowledgeBase/add",
    method: "post",
    data: data  // âœ… æ­£ç¡®
  });
}
```
---
### 9.3 DELETE请求参数错误
**错误示例**:
```javascript
// âŒ é”™è¯¯: DELETE使用 params ä¼ é€’数组
export function delKnowledgeBase(ids) {
  return request({
    url: "/knowledgeBase/delete",
    method: "delete",
    params: ids  // âŒ é”™è¯¯
  });
}
// âœ… æ­£ç¡®: DELETE使用 data ä¼ é€’数组
export function delKnowledgeBase(ids) {
  return request({
    url: "/knowledgeBase/delete",
    method: "delete",
    data: ids  // âœ… æ­£ç¡®
  });
}
```
---
### 9.4 æµå¼æŽ¥å£é”™è¯¯
**错误示例**:
```javascript
// âŒ é”™è¯¯: æµå¼æŽ¥å£ä½¿ç”¨ axios
export function knowledgeChat(data) {
  return request({
    url: "/ai/knowledge/chat",
    method: "post",
    data: data  // âŒ axios不支持流式
  });
}
// âœ… æ­£ç¡®: æµå¼æŽ¥å£ä½¿ç”¨ Fetch API
export function knowledgeChat(data) {
  const token = getToken();
  return fetch(import.meta.env.VITE_APP_BASE_API + '/ai/knowledge/chat', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + token
    },
    body: JSON.stringify(data)  // âœ… æ­£ç¡®
  });
}
```
---
### 9.5 æ–‡ä»¶ä¸Šä¼ ID获取错误
**错误示例**:
```javascript
// âŒ é”™è¯¯: ä¸Šä¼ æˆåŠŸåŽæ²¡æœ‰ä¿å­˜ storageBlobId
const handleUploadSuccess = (response) => {
  if (response.code === 200) {
    ElMessage.success("上传成功");
    // âŒ æ²¡æœ‰ä¿å­˜ response.data.id
  }
};
// âœ… æ­£ç¡®: ä¿å­˜ storageBlobId ç”¨äºŽåŽç»­å…³è”
const handleUploadSuccess = (response, file) => {
  if (response.code === 200) {
    uploadedBlobIds.value.push(response.data.id);  // âœ… ä¿å­˜ID
    ElMessage.success(`文件 ${file.name} ä¸Šä¼ æˆåŠŸ`);
  }
};
```
---
## åã€æœ€ä½³å®žè·µå»ºè®®
### 10.1 å‚数命名一致性
✅ **前端参数名与后端实体字段保持一致**
```javascript
// å‰ç«¯
{
  knowledgeBaseId: 10,
  storageBlobIds: [123]
}
// åŽç«¯å®žä½“字段
private Long knowledgeBaseId;
private List<Long> storageBlobIds;
```
---
### 10.2 å‚数类型一致性
✅ **明确参数类型,避免自动类型转换**
```javascript
// æ•°å€¼ç±»åž‹
const id = 10;  // number
const count = 0;  // number
// å­—符串类型
const title = "";  // string
const type = "";  // string
// æ•°ç»„类型
const ids = [];  // array
```
---
### 10.3 å¿…填参数校验
✅ **在调用接口前校验必填参数**
```javascript
const saveFiles = async () => {
  if (!currentKnowledgeBase.value?.id) {
    ElMessage.error("知识库信息异常");
    return;
  }
  if (uploadedBlobIds.value.length === 0) {
    ElMessage.warning("请先上传文件");
    return;
  }
  // ...调用接口
};
```
---
### 10.4 å‚数默认值
✅ **为可选参数设置合理的默认值**
```javascript
const form = {
  title: "",
  type: "",
  usageCount: 0,  // âœ… æ•°å€¼ç±»åž‹é»˜è®¤å€¼ä¸º0
  creator: userStore.nickName || ""  // âœ… ä½¿ç”¨å½“前用户名作为默认值
};
```
---
### 10.5 URL路径参数
✅ **路径参数使用模板字符串**
```javascript
const url = `/knowledgeBase/vector/status/${knowledgeBaseId}`;
const url = `/knowledgeBase/vector/reprocess/${vectorId}`;
```
---
## åä¸€ã€å‚数映射关系总结
### çŸ¥è¯†åº“ID相关
| åœºæ™¯ | å‚数名 | ç±»åž‹ | æ¥æº |
|------|--------|------|------|
| çŸ¥è¯†åº“列表查询 | - | - | URL路径无参数 |
| çŸ¥è¯†åº“详情 | id | Long | URL路径参数 |
| æ–‡ä»¶å…³è”保存 | knowledgeBaseId | Long | POST body参数 |
| å‘量化状态查询 | knowledgeBaseId | Long | URL路径参数 |
| çŸ¥è¯†é—®ç­” | knowledgeBaseId | Long | POST body参数 |
---
### æ–‡ä»¶ID相关
| åœºæ™¯ | å‚数名 | ç±»åž‹ | æ¥æº |
|------|--------|------|------|
| æ–‡ä»¶ä¸Šä¼ å“åº” | data.id | Long | å“åº”数据(作为storageBlobId) |
| æ–‡ä»¶å…³è”保存 | storageBlobIds | Long[] | POST body参数 |
| æ–‡ä»¶åˆ é™¤ | ids | Long[] | DELETE body参数(向量记录ID) |
| é‡æ–°å‘量化 | vectorId | Long | URL路径参数 |
---
### ä¼šè¯ID相关
| åœºæ™¯ | å‚数名 | ç±»åž‹ | æ¥æº |
|------|--------|------|------|
| çŸ¥è¯†é—®ç­” | memoryId | String | å‰ç«¯ç”ŸæˆUUID |
| é—®ç­”历史查询 | memoryId | String | URL路径参数 |
---
## åäºŒã€é™„录
### é™„录A: å‚数类型对照表
| å‚数类型 | JavaScript | Java | MySQL |
|----------|------------|------|-------|
| ID | number/Long | Long | BIGINT |
| æ ‡é¢˜ | String | String | VARCHAR |
| ç±»åž‹ | String | String | VARCHAR |
| çŠ¶æ€ | Integer | Integer | TINYINT |
| æ•°é‡ | Integer | Integer | INT |
| æ—¶é—´ | String/Date | LocalDateTime | DATETIME |
| æ•°ç»„ | Array | List | - |
---
### é™„录B: HTTP方法与传参位置对照表
| HTTP方法 | ä¼ å‚位置 | request配置 | é€‚用场景 |
|----------|----------|--------------|----------|
| GET | URL参数 | `params: query` | æŸ¥è¯¢ã€åˆ—表 |
| POST | è¯·æ±‚体 | `data: data` | æ–°å¢žã€æ›´æ–°ã€ä¿å­˜ |
| DELETE | è¯·æ±‚体 | `data: ids` | åˆ é™¤ã€æ‰¹é‡åˆ é™¤ |
| PUT | è¯·æ±‚体 | `data: data` | æ›´æ–°(部分使用POST) |
| æµå¼POST | è¯·æ±‚体 | `body: JSON.stringify()` | AI问答 |
---
### é™„录C: å‘量化状态值对照表
| çŠ¶æ€å€¼ | çŠ¶æ€åç§° | å‰ç«¯æ˜¾ç¤º | æ ‡ç­¾é¢œè‰² |
|--------|----------|----------|----------|
| 0 | å¾…处理 | "待处理" | info (灰色) |
| 1 | å¤„理中 | "处理中" | warning (橙色) |
| 2 | å·²å®Œæˆ | "已完成" | success (绿色) |
| 3 | å¤±è´¥ | "失败" | danger (红色) |
---
## åä¸‰ã€æ€»ç»“
本文档详细规范了知识库模块的参数命名和传参方式,遵循以下原则:
1. âœ… **命名一致性**: å‰åŽç«¯å‚数名保持一致(驼峰命名)
2. âœ… **传参规范性**: GET用params、POST用data、DELETE用data
3. âœ… **类型明确性**: æ˜Žç¡®å‚数类型,合理设置默认值
4. âœ… **校验完整性**: å¿…填参数需校验,可选参数有默认值
5. âœ… **错误避免**: éµå¾ªè§„范避免常见错误
建议团队成员严格遵循本规范,确保前后端参数传递的一致性和可靠性。
src/api/collaborativeApproval/knowledgeBase.js
@@ -1,60 +1,92 @@
import request from "@/utils/request";
import { getToken } from '@/utils/auth';
// æŸ¥è¯¢çŸ¥è¯†åº“列表
/**
 * çŸ¥è¯†åº“管理接口
 * ä¼ å‚规范:
 * - GET请求: ä½¿ç”¨ params
 * - POST请求: ä½¿ç”¨ data
 * - DELETE请求: ä½¿ç”¨ data
 * - æµå¼è¯·æ±‚: ä½¿ç”¨ Fetch API
 */
// ==================== çŸ¥è¯†åº“CRUD接口 ====================
/**
 * æŸ¥è¯¢çŸ¥è¯†åº“列表
 * @param {Object} query - æŸ¥è¯¢å‚æ•°
 * @param {number} query.current - å½“前页码(必填)
 * @param {number} query.size - æ¯é¡µæ¡æ•°(必填)
 * @param {string} [query.title] - çŸ¥è¯†æ ‡é¢˜(可选,模糊搜索)
 * @param {string} [query.type] - çŸ¥è¯†ç±»åž‹(可选,精确匹配)
 * @returns {Promise}
 */
export function listKnowledgeBase(query) {
  return request({
    url: "/knowledgeBase/getList",
    method: "get",
    params: query,
    params: query,  // GET请求使用params
  });
}
// æŸ¥è¯¢çŸ¥è¯†åº“详细
// export function getKnowledgeBase(knowledgeBaseId) {
//   return request({
//     url: "/collaborativeApproval/knowledgeBase/" + knowledgeBaseId,
//     method: "get",
//   });
// }
// æ–°å¢žçŸ¥è¯†åº“
/**
 * æ–°å¢žçŸ¥è¯†åº“
 * @param {Object} data - çŸ¥è¯†åº“数据
 * @param {string} data.title - çŸ¥è¯†æ ‡é¢˜(必填)
 * @param {string} data.type - çŸ¥è¯†ç±»åž‹(必填)
 * @param {string} [data.scenario] - é€‚用场景(可选)
 * @param {string} [data.efficiency] - è§£å†³æ•ˆçއ(可选)
 * @param {string} data.problem - é—®é¢˜æè¿°(必填)
 * @param {string} data.solution - è§£å†³æ–¹æ¡ˆ(必填)
 * @param {string} [data.keyPoints] - å…³é”®è¦ç‚¹(可选)
 * @param {string} [data.creator] - åˆ›å»ºäºº(可选)
 * @param {number} [data.usageCount=0] - ä½¿ç”¨æ¬¡æ•°(可选)
 * @returns {Promise}
 */
export function addKnowledgeBase(data) {
  return request({
    url: "/knowledgeBase/add",
    method: "post",
    data: data,
    data: data,  // POST请求使用data
  });
}
// ä¿®æ”¹çŸ¥è¯†åº“
/**
 * ä¿®æ”¹çŸ¥è¯†åº“
 * @param {Object} data - çŸ¥è¯†åº“数据
 * @param {number} data.id - çŸ¥è¯†åº“ID(必填)
 * @param {string} data.title - çŸ¥è¯†æ ‡é¢˜(必填)
 * @param {string} data.type - çŸ¥è¯†ç±»åž‹(必填)
 * @returns {Promise}
 */
export function updateKnowledgeBase(data) {
  return request({
    url: "/knowledgeBase/update",
    method: "post",
    data: data,
    data: data,  // POST请求使用data
  });
}
// åˆ é™¤çŸ¥è¯†åº“
export function delKnowledgeBase(query) {
/**
 * åˆ é™¤çŸ¥è¯†åº“(支持批量删除)
 * @param {number[]} ids - çŸ¥è¯†åº“ID数组
 * @returns {Promise}
 */
export function delKnowledgeBase(ids) {
  return request({
    url: "/knowledgeBase/delete",
    method: "delete",
    data: query,
    data: ids,  // DELETE请求使用data传递数组
  });
}
// æ‰¹é‡åˆ é™¤çŸ¥è¯†åº“
export function delKnowledgeBaseBatch(knowledgeBaseIds) {
  return request({
    url: "/knowledgeBase/batch",
    method: "delete",
    data: knowledgeBaseIds,
  });
}
// ==================== æ–‡ä»¶ç®¡ç†æŽ¥å£ ====================
// èŽ·å–çŸ¥è¯†åº“æ–‡ä»¶å‘é‡åŒ–çŠ¶æ€
/**
 * æŸ¥è¯¢çŸ¥è¯†åº“文件向量化状态
 * @param {number} knowledgeBaseId - çŸ¥è¯†åº“ID
 * @returns {Promise} è¿”回文件列表及向量化状态
 */
export function getVectorStatus(knowledgeBaseId) {
  return request({
    url: `/knowledgeBase/vector/status/${knowledgeBaseId}`,
@@ -62,7 +94,39 @@
  });
}
// é‡æ–°å‘量化文件
/**
 * ä¿å­˜çŸ¥è¯†åº“文件关联(触发向量化)
 * @param {Object} data - æ–‡ä»¶å…³è”数据
 * @param {number} data.knowledgeBaseId - çŸ¥è¯†åº“ID(必填)
 * @param {number[]} data.storageBlobIds - æ–‡ä»¶blob ID数组(必填)
 * @returns {Promise}
 */
export function saveKnowledgeBaseFiles(data) {
  return request({
    url: "/knowledgeBase/file/save",
    method: "post",
    data: data,  // POST请求使用data
  });
}
/**
 * åˆ é™¤çŸ¥è¯†åº“文件
 * @param {number[]} ids - å‘量记录ID数组
 * @returns {Promise}
 */
export function deleteKnowledgeBaseFile(ids) {
  return request({
    url: "/knowledgeBase/file/delete",
    method: "delete",
    data: ids,  // DELETE请求使用data传递数组
  });
}
/**
 * é‡æ–°å‘量化文件
 * @param {number} vectorId - å‘量记录ID
 * @returns {Promise}
 */
export function reprocessVector(vectorId) {
  return request({
    url: `/knowledgeBase/vector/reprocess/${vectorId}`,
@@ -70,36 +134,22 @@
  });
}
// ä¿å­˜çŸ¥è¯†åº“文件关联
export function saveKnowledgeBaseFiles(data) {
  return request({
    url: "/knowledgeBase/file/save",
    method: "post",
    data: data,
  });
}
// ==================== çŸ¥è¯†é—®ç­”接口 ====================
// åˆ é™¤çŸ¥è¯†åº“文件
export function deleteKnowledgeBaseFiles(vectorIds) {
  return request({
    url: "/knowledgeBase/file/delete",
    method: "delete",
    data: vectorIds,
  });
}
// æŸ¥è¯¢çŸ¥è¯†åº“问答历史
export function getKnowledgeHistory(memoryId) {
  return request({
    url: `/ai/knowledge/history/${memoryId}`,
    method: "get",
  });
}
// çŸ¥è¯†åº“问答(流式)
export async function knowledgeChat(data) {
/**
 * çŸ¥è¯†åº“问答(流式)
 * åŽç«¯æŽ¥å£: POST /ai/knowledge/chat
 * å“åº”类型: text/stream;charset=utf-8 (Spring Flux<String>)
 *
 * @param {Object} data - é—®ç­”参数
 * @param {number} data.knowledgeBaseId - çŸ¥è¯†åº“ID(必填)
 * @param {string} data.memoryId - ä¼šè¯ID(必填,用于保持上下文)
 * @param {string} data.question - ç”¨æˆ·é—®é¢˜(必填)
 * @returns {Promise<Response>} è¿”回Fetch Response对象
 */
export function knowledgeChat(data) {
  const token = getToken();
  const response = await fetch(import.meta.env.VITE_APP_BASE_API + '/ai/knowledge/chat', {
  return fetch(import.meta.env.VITE_APP_BASE_API + '/ai/knowledge/chat', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
@@ -107,6 +157,16 @@
    },
    body: JSON.stringify(data)
  });
  return response.body;
}
/**
 * æŸ¥è¯¢çŸ¥è¯†åº“问答历史
 * @param {string} memoryId - ä¼šè¯ID
 * @returns {Promise}
 */
export function getKnowledgeHistory(memoryId) {
  return request({
    url: `/ai/knowledge/history/${memoryId}`,
    method: "get",
  });
}
src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -244,6 +244,7 @@
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            :before-upload="beforeUpload"
            name="files"
            multiple
            :show-file-list="false"
            accept=".txt,.md,.docx,.xlsx,.xls,.pdf"
@@ -379,7 +380,7 @@
  getVectorStatus,
  reprocessVector,
  saveKnowledgeBaseFiles,
  deleteKnowledgeBaseFiles,
  deleteKnowledgeBaseFile,
  knowledgeChat
} from "@/api/collaborativeApproval/knowledgeBase.js";
import useUserStore from '@/store/modules/user';
@@ -624,21 +625,31 @@
const getList = () => {
  tableLoading.value = true;
  listKnowledgeBase({...page.value, ...searchForm.value})
  // âœ… GET请求使用params传参
  listKnowledgeBase({
    current: page.value.current,
    size: page.value.size,
    title: searchForm.value.title,
    type: searchForm.value.type
  })
  .then(res => {
    tableLoading.value = false;
    page.value.total = res.data.total;
    // å¦‚果当前页数超过总页数,重置到第1页并重新查询
    // å¦‚果当前页数超过总页数,重置到第1页并重新查询
    const maxPage = Math.ceil(res.data.total / page.value.size) || 1;
    if (page.value.current > maxPage && maxPage > 0) {
      page.value.current = 1;
      // é‡æ–°æŸ¥è¯¢ç¬¬1页数据
      return getList();
    }
    tableData.value = res.data.records;
  }).catch(err => {
    tableLoading.value = false;
  })
  .catch(err => {
    tableLoading.value = false;
    console.error("查询知识库列表失败:", err);
  });
};
// åˆ†é¡µå¤„理
@@ -813,27 +824,47 @@
const submitForm = async () => {
  try {
    await formRef.value.validate();
    // âœ… POST请求使用data传参,明确参数结构
    const formData = {
      title: form.value.title,
      type: form.value.type,
      scenario: form.value.scenario || "",
      efficiency: form.value.efficiency || "",
      problem: form.value.problem,
      solution: form.value.solution,
      keyPoints: form.value.keyPoints || "",
      creator: form.value.creator || "",
      usageCount: form.value.usageCount || 0
    };
    if (dialogType.value === "add") {
      // æ–°å¢žçŸ¥è¯†
      addKnowledgeBase({...form.value}).then(res => {
      addKnowledgeBase(formData).then(res => {
        if(res.code == 200){
          ElMessage.success("添加成功");
          closeKnowledgeDialog();
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
        console.error("添加知识库失败:", err);
        ElMessage.error(err.msg || "添加失败");
      });
    } else {
      updateKnowledgeBase({...form.value}).then(res => {
      // æ›´æ–°çŸ¥è¯† - æ·»åŠ id参数
      updateKnowledgeBase({
        id: form.value.id,
        ...formData
      }).then(res => {
        if(res.code == 200){
          ElMessage.success("更新成功");
          closeKnowledgeDialog();
          getList();
        }
      }).catch(err => {
        ElMessage.error(err.msg);
      })
        console.error("更新知识库失败:", err);
        ElMessage.error(err.msg || "更新失败");
      });
    }
  } catch (error) {
    console.error("表单验证失败:", error);
@@ -847,19 +878,22 @@
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    // console.log(selectedIds.value);
    // âœ… DELETE请求使用data传递ID数组
    delKnowledgeBase(selectedIds.value).then(res => {
      if(res.code == 200){
        ElMessage.success("删除成功");
        selectedIds.value = [];
        getList();
      }
    })
    }).catch(err => {
      console.error("删除知识库失败:", err);
      ElMessage.error(err.msg || "删除失败");
    });
  }).catch(() => {
    // ç”¨æˆ·å–消
  });
@@ -963,9 +997,24 @@
// ä¸Šä¼ æˆåŠŸ
const handleUploadSuccess = (response, file) => {
  console.log("上传响应:", response);  // è°ƒè¯•日志
  if (response.code === 200) {
    uploadedBlobIds.value.push(response.data.id);
    ElMessage.success(`文件 ${file.name} ä¸Šä¼ æˆåŠŸ`);
    // âœ… åŽç«¯è¿”回的是 List<StorageBlobVO>,所以data是数组
    if (Array.isArray(response.data) && response.data.length > 0) {
      // å–数组第一个元素的id
      const blobId = response.data[0].id;
      if (blobId) {
        uploadedBlobIds.value.push(blobId);
        ElMessage.success(`文件 ${file.name} ä¸Šä¼ æˆåŠŸ`);
      } else {
        console.error("上传响应中未找到id:", response.data[0]);
        ElMessage.error("上传失败: æœªèŽ·å–åˆ°æ–‡ä»¶ID");
      }
    } else {
      console.error("上传响应格式错误:", response);
      ElMessage.error("上传失败: å“åº”格式错误");
    }
  } else {
    ElMessage.error(response.msg || "上传失败");
  }
@@ -978,22 +1027,30 @@
// ä¿å­˜æ–‡ä»¶å…³è”
const saveFiles = async () => {
  // å‚数校验
  if (!currentKnowledgeBase.value?.id) {
    ElMessage.error("知识库信息异常");
    return;
  }
  if (uploadedBlobIds.value.length === 0) {
    ElMessage.warning("请先上传文件");
    return;
  }
  savingFiles.value = true;
  try {
    // âœ… POST请求使用data传参,明确参数结构
    await saveKnowledgeBaseFiles({
      knowledgeBaseId: currentKnowledgeBase.value.id,
      storageBlobIds: uploadedBlobIds.value
      knowledgeBaseId: currentKnowledgeBase.value.id,  // çŸ¥è¯†åº“ID
      storageBlobIds: uploadedBlobIds.value             // æ–‡ä»¶blob ID数组
    });
    ElMessage.success("文件关联保存成功,正在后台处理向量化");
    ElMessage.success("文件关联保存成功,正在后台处理向量化");
    uploadedBlobIds.value = [];
    // å»¶è¿Ÿåˆ·æ–°æ–‡ä»¶åˆ—表,给后台处理时间
    // å»¶è¿Ÿåˆ·æ–°æ–‡ä»¶åˆ—表,给后台处理时间
    setTimeout(() => {
      loadFileList();
    }, 1000);
@@ -1030,7 +1087,7 @@
const deleteFile = async (row) => {
  try {
    await ElMessageBox.confirm(
      "确定要删除该文件吗?删除后将无法恢复向量数据",
      "确定要删除该文件吗?删除后将无法恢复向量数据",
      "删除确认",
      {
        confirmButtonText: "确定",
@@ -1039,7 +1096,8 @@
      }
    );
    await deleteKnowledgeBaseFiles([row.id]);
    // âœ… DELETE请求使用data传递ID数组
    await deleteKnowledgeBaseFile([row.id]);  // æ³¨æ„: row.id是向量记录ID,不是storageBlobId
    ElMessage.success("删除成功");
    loadFileList();
  } catch (error) {
@@ -1095,6 +1153,7 @@
// å‘送消息
const sendMessage = async () => {
  // å‚数校验
  if (!inputQuestion.value.trim()) {
    ElMessage.warning("请输入问题");
    return;
@@ -1121,18 +1180,11 @@
  scrollToBottom();
  try {
    // æµå¼è¯·æ±‚
    const response = await fetch('/api/ai/knowledge/chat', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + getToken()
      },
      body: JSON.stringify({
        knowledgeBaseId: currentKnowledgeBase.value.id,
        memoryId: memoryId.value,
        question: question
      })
    // âœ… æµå¼è¯·æ±‚使用Fetch API
    const response = await knowledgeChat({
      knowledgeBaseId: currentKnowledgeBase.value.id,  // çŸ¥è¯†åº“ID
      memoryId: memoryId.value,                         // ä¼šè¯ID
      question: question                                // ç”¨æˆ·é—®é¢˜
    });
    if (!response.ok) {
@@ -1140,7 +1192,7 @@
      throw new Error(errorText || '请求失败');
    }
    // å¤„理SSE流式响应
    // âœ… åŽç«¯è¿”回 text/stream;charset=utf-8
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let aiContent = '';
@@ -1151,7 +1203,7 @@
      const { done, value } = await reader.read();
      if (done) break;
      const text = decoder.decode(value);
      const text = decoder.decode(value, { stream: true });  // âœ… æ·»åŠ stream选项
      aiContent += text;
      messages.value[messages.value.length - 1].content = aiContent;