diff --git a/design/services/comment.md b/design/services/comment.md index fd6510cf30c633d76e5fbb4c302d595dbf3e689f..e85d6c68d2e33e6e790f4c98076542c8ce611b3d 100644 --- a/design/services/comment.md +++ b/design/services/comment.md @@ -25,24 +25,29 @@ apps/ #### Comment Table Schema -| 字段名 | 类型 | 说明 | 约束 | -|--------|------|------|------| -| id | BigInteger | 主键ID | Primary Key, Auto Increment | -| recordId | UUID | 问答对ID | Foreign Key → framework_record.id, Indexed | -| userSub | String(50) | 用户标识 | Foreign Key → framework_user.userSub | -| commentType | Enum(CommentType) | 评论类型 | Not Null | -| feedbackType | ARRAY(String) | 投诉类别列表 | Not Null | -| feedbackLink | String(1000) | 投诉相关链接 | Not Null | -| feedbackContent | String(1000) | 投诉详细内容 | Not Null | -| createdAt | DateTime(TZ) | 创建时间 | Not Null, Default: UTC Now | +```mermaid +erDiagram + COMMENT { + bigint id "主键ID (Primary Key, Auto Increment)" + uuid recordId "问答对ID (Foreign Key → framework_record.id, Indexed)" + string userSub "用户标识 (Foreign Key → framework_user.userSub, max 50)" + CommentType commentType "评论类型 (Not Null)" + string[] feedbackType "投诉类别列表 (Not Null)" + string feedbackLink "投诉相关链接 (Not Null, max 1000)" + string feedbackContent "投诉详细内容 (Not Null, max 1000)" + datetime createdAt "创建时间 (Not Null, Default: UTC Now, 带时区)" + } +``` #### CommentType Enum -```python -class CommentType(str, PyEnum): - LIKE = "liked" # 点赞 - DISLIKE = "disliked" # 点踩 - NONE = "none" # 无评论 +```mermaid +erDiagram + CommentType { + string liked "点赞" + string disliked "点踩" + string none "无评论" + } ``` ### 2. 业务逻辑层 (services/comment.py) @@ -67,26 +72,28 @@ class CommentType(str, PyEnum): #### AddCommentData (请求模型) -```python -{ - "record_id": str, # 问答记录ID - "comment": CommentType, # 评论类型 (liked/disliked/none) - "dislike_reason": str, # 点踩原因 (分号分隔, max 200字符) - "reason_link": str, # 相关链接 (max 200字符) - "reason_description": str # 详细描述 (max 500字符) -} +```mermaid +erDiagram + AddCommentData { + string record_id "问答记录ID" + CommentType comment "评论类型 (liked/disliked/none)" + string dislike_reason "点踩原因 (分号分隔, max 200字符)" + string reason_link "相关链接 (max 200字符)" + string reason_description "详细描述 (max 500字符)" + } ``` #### RecordComment (内部数据模型) -```python -{ - "comment": CommentType, # 评论类型 - "feedback_type": list[str], # 反馈类型列表 (别名: dislike_reason) - "feedback_link": str, # 反馈链接 (别名: reason_link) - "feedback_content": str, # 反馈内容 (别名: reason_description) - "feedback_time": float # 反馈时间戳 -} +```mermaid +erDiagram + RecordComment { + CommentType comment "评论类型" + string[] feedback_type "反馈类型列表 (别名: dislike_reason)" + string feedback_link "反馈链接 (别名: reason_link)" + string feedback_content "反馈内容 (别名: reason_description)" + float feedback_time "反馈时间戳" + } ``` ## 架构设计 @@ -513,25 +520,6 @@ curl -X POST "http://localhost:8000/api/comment" \ }' ``` -**Python:** - -```python -import requests - -response = requests.post( - "http://localhost:8000/api/comment", - json={ - "record_id": "550e8400-e29b-41d4-a716-446655440000", - "comment": "disliked", - "dislike_reason": "答非所问;信息不准确;", - "reason_link": "https://example.com/issue/123", - "reason_description": "回答内容与问题不符" - }, - headers={"Authorization": "Bearer "}, - cookies={"session": ""} -) -``` - ## 关键特性 ### 1. 幂等性设计 @@ -569,7 +557,7 @@ response = requests.post( ## 错误处理 -### 常见错误场景 +常见错误场景: | 错误场景 | HTTP状态码 | 处理方式 | |----------|-----------|----------| @@ -581,55 +569,6 @@ response = requests.post( | 数据库连接失败 | 500 | 异常传播至错误处理中间件 | | 外键约束违反 | 500 | 数据库异常 | -### 异常传播链 - -```mermaid -flowchart LR - A[Router Layer] --> B{验证异常?} - B -->|是| C[422 Unprocessable Entity] - B -->|否| D[Service Layer] - D --> E{业务异常?} - E -->|是| F[400 Bad Request] - E -->|否| G[Database Layer] - G --> H{DB异常?} - H -->|是| I[500 Internal Server Error] - H -->|否| J[200 OK] -``` - -## 性能优化 - -### 1. 数据库索引 - -```sql -CREATE INDEX idx_comment_record_id ON framework_comment(recordId); -``` - -- 优化基于 recordId 的查询性能 -- 支持快速定位单个记录的评论 - -### 2. 异步IO - -- 使用 SQLAlchemy 异步会话:`async with postgres.session()` -- 非阻塞数据库操作 -- 提高并发处理能力 - -### 3. 连接池管理 - -- PostgreSQL 连接池复用 -- 避免频繁建立/关闭连接的开销 - -### 4. 查询优化 - -```python -# 使用 one_or_none() 替代 all()[0] -result = (await session.scalars( - select(Comment).where(Comment.recordId == uuid.UUID(record_id)) -)).one_or_none() -``` - -- 提前终止查询(最多返回1条) -- 减少数据传输量 - ## 安全考虑 ### 1. 认证与授权 @@ -646,174 +585,3 @@ result = (await session.scalars( - reason_description: 500字符 - **类型校验**:Pydantic自动验证数据类型 - **枚举约束**:CommentType限定为三个固定值 - -### 3. SQL注入防护 - -- 使用 SQLAlchemy ORM -- 参数化查询 -- 无原生SQL拼接 - -### 4. 数据完整性 - -- 外键约束防止孤立记录 -- 事务保证原子性操作 -- NOT NULL约束防止空值 - -## 扩展性设计 - -### 1. 评论类型扩展 - -当前支持三种类型,如需扩展: - -```python -class CommentType(str, PyEnum): - LIKE = "liked" - DISLIKE = "disliked" - NONE = "none" - # 未来扩展 - REPORT = "reported" # 举报 - FAVORITE = "favorited" # 收藏 -``` - -### 2. 反馈类型扩展 - -feedbackType 使用数组类型,支持多选和动态扩展: - -```python -# 前端定义的反馈类型选项 -FEEDBACK_OPTIONS = [ - "答非所问", - "信息不准确", - "内容不完整", - "格式混乱", - "其他问题" -] -``` - -### 3. 多语言支持 - -在 RecordComment 中添加语言字段: - -```python -class RecordComment(BaseModel): - comment: CommentType - language: str = "zh-CN" # 新增字段 - # ... 其他字段 -``` - -### 4. 统计分析扩展 - -可基于现有数据进行扩展: - -- 点赞率统计 -- 点踩原因分布 -- 时间趋势分析 -- 用户反馈热点 - -## 部署注意事项 - -### 1. 数据库迁移 - -使用 Alembic 进行数据库版本管理: - -```bash -# 生成迁移脚本 -alembic revision --autogenerate -m "create comment table" - -# 执行迁移 -alembic upgrade head -``` - -### 2. 环境变量配置 - -```bash -# PostgreSQL 连接配置 -DATABASE_URL=postgresql+asyncpg://user:pass@host:5432/dbname - -# 日志级别 -LOG_LEVEL=INFO -``` - -### 3. 性能调优 - -```python -# 连接池配置 -pool_size = 20 # 连接池大小 -max_overflow = 10 # 最大溢出连接数 -pool_timeout = 30 # 连接超时时间 -pool_recycle = 3600 # 连接回收时间 -``` - -## 未来优化方向 - -### 1. 缓存策略 - -- Redis 缓存热点评论数据 -- 减少数据库查询压力 -- 设置合理的缓存过期时间 - -### 2. 批量操作支持 - -- 支持批量查询评论 -- 支持批量更新评论 -- 减少网络往返次数 - -### 3. 评论审核机制 - -- 敏感词过滤 -- 内容审核工作流 -- 人工复审接口 - -### 4. 数据分析增强 - -- 实时统计面板 -- 评论情感分析 -- 用户行为画像 - -## 常见问题 (FAQ) - -### Q1: 为什么使用 merge 而不是 add? - -**A:** `merge` 操作实现了 UPSERT 语义,可以自动判断是插入还是更新,简化了业务逻辑。 - -### Q2: dislike_reason 为什么使用分号分隔? - -**A:** 前端传递多个原因时使用分号分隔的字符串格式,后端进行split转换为数组存储到数据库。 - -### Q3: 为什么 update_comment 返回 None? - -**A:** 当前设计中更新操作无需返回值,失败时通过异常处理。未来可以改为返回布尔值或更新后的对象。 - -### Q4: 如何防止恶意刷评论? - -**A:** - -- 身份认证确保用户真实性 -- 可增加频率限制(Rate Limiting) -- 可增加同一用户对同一记录的评论次数限制 - -### Q5: 评论数据如何归档? - -**A:** - -- 可按时间分区存储历史数据 -- 定期将旧数据迁移到冷存储 -- 保留索引供查询分析使用 - -## 参考资料 - -- [FastAPI Documentation](https://fastapi.tiangolo.com/) -- [SQLAlchemy Async Documentation](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) -- [Pydantic V2 Documentation](https://docs.pydantic.dev/latest/) -- [PostgreSQL ARRAY Types](https://www.postgresql.org/docs/current/arrays.html) - -## 版本历史 - -| 版本 | 日期 | 变更说明 | -|------|------|----------| -| 1.0.0 | 2025-10-13 | 初始版本,完成基础评论功能 | - ---- - -**文档维护者**: Euler Copilot Framework Team -**最后更新**: 2025-10-13 diff --git a/design/services/conversation.md b/design/services/conversation.md new file mode 100644 index 0000000000000000000000000000000000000000..9aca8c31a1695e2b119fcb2216f37ebb5b338ef5 --- /dev/null +++ b/design/services/conversation.md @@ -0,0 +1,431 @@ +# Conversation 模块设计文档 + +## 1. 模块概述 + +Conversation 模块负责管理用户的对话会话,包括对话的创建、查询、更新、删除等核心功能。该模块由服务层(`services/conversation.py`)和路由层(`routers/conversation.py`)组成,提供完整的对话生命周期管理能力。 + +### 1.1 主要组件 + +- **ConversationManager**: 对话管理器,提供对话的底层数据操作 +- **Router**: FastAPI 路由层,提供 RESTful API 接口 + +### 1.2 核心功能 + +- 获取用户对话列表 +- 创建新对话 +- 更新对话信息 +- 删除对话及关联资源 +- 对话权限验证 +- App 使用统计 + +## 2. 模块架构 + +### 2.1 整体架构图 + +```mermaid +graph TB + Client[客户端] --> Router[FastAPI Router] + Router --> Middleware[依赖中间件] + Middleware --> SessionVerify[会话验证] + Middleware --> TokenVerify[个人令牌验证] + Router --> ConvMgr[ConversationManager] + ConvMgr --> PostgreSQL[(PostgreSQL)] + ConvMgr --> TaskMgr[TaskManager] + Router --> DocMgr[DocumentManager] + + style Client fill:#e1f5ff + style Router fill:#fff4e1 + style ConvMgr fill:#e7f5e1 + style PostgreSQL fill:#f5e1e1 +``` + +### 2.2 数据模型关系 + +```mermaid +erDiagram + Conversation ||--o{ UserAppUsage : "tracks" + Conversation ||--o{ Task : "contains" + Conversation ||--o{ Document : "contains" + + Conversation { + uuid id PK + string userSub FK + uuid appId FK + string title + boolean isTemporary + datetime createdAt + } + + UserAppUsage { + string userSub FK + uuid appId FK + int usageCount + datetime lastUsed + } + + Task { + uuid id PK + uuid conversationId FK + } + + Document { + uuid id PK + uuid conversationId FK + } +``` + +## 3. 核心流程 + +### 3.1 获取对话列表流程 + +```mermaid +sequenceDiagram + participant C as 客户端 + participant R as Router + participant Auth as 认证中间件 + participant CM as ConversationManager + participant DM as DocumentManager + participant DB as PostgreSQL + + C->>R: GET /api/conversation + R->>Auth: 验证会话和令牌 + Auth-->>R: 验证通过 + R->>CM: get_conversation_by_user_sub(user_sub) + CM->>DB: SELECT * FROM Conversation WHERE userSub=? + DB-->>CM: 返回对话列表 + CM-->>R: 返回Conversation对象列表 + + loop 遍历每个对话 + R->>DM: get_doc_count(conversation_id) + DM-->>R: 返回文档数量 + end + + R-->>C: 返回ConversationListRsp +``` + +**流程说明:** + +1. 客户端发起 GET 请求获取对话列表 +2. 通过认证中间件验证用户身份 +3. 从数据库查询用户的所有非临时对话 +4. 按创建时间倒序排列 +5. 为每个对话获取关联文档数量 +6. 返回格式化的对话列表响应 + +### 3.2 创建对话流程 + +```mermaid +flowchart TD + Start([开始创建对话]) --> Input[接收参数: title, user_sub, app_id, debug] + Input --> CreateObj[创建Conversation对象] + CreateObj --> SaveDB{保存到数据库} + SaveDB -->|失败| Error[记录错误日志] + Error --> ReturnNull[返回None] + SaveDB -->|成功| CheckDebug{是否为调试模式?} + CheckDebug -->|是| ReturnConv[返回对话对象] + CheckDebug -->|否| CheckAppId{app_id是否存在?} + CheckAppId -->|否| ReturnConv + CheckAppId -->|是| QueryUsage[查询UserAppUsage] + QueryUsage --> UsageExists{使用记录存在?} + UsageExists -->|是| UpdateUsage[更新usageCount+1
更新lastUsed时间] + UsageExists -->|否| CreateUsage[创建新的使用记录
usageCount=1] + UpdateUsage --> CommitUsage[提交事务] + CreateUsage --> CommitUsage + CommitUsage --> ReturnConv + ReturnConv --> End([结束]) + ReturnNull --> End + + style Start fill:#e1f5ff + style End fill:#e1f5ff + style Error fill:#ffe1e1 + style ReturnConv fill:#e7f5e1 +``` + +**流程说明:** + +1. 创建 Conversation 对象并设置基本属性 +2. 尝试保存到数据库 +3. 如果非调试模式且提供了 app_id,则更新 App 使用统计 +4. 如果 UserAppUsage 记录存在,增加计数器;否则创建新记录 +5. 返回创建的对话对象 + +### 3.3 更新对话流程 + +```mermaid +sequenceDiagram + participant C as 客户端 + participant R as Router + participant CM as ConversationManager + participant DM as DocumentManager + participant DB as PostgreSQL + + C->>R: POST /api/conversation?conversationId=xxx + Note over C,R: Body: {title: "新标题"} + + R->>CM: get_conversation_by_conversation_id(user_sub, conversation_id) + CM->>DB: SELECT * FROM Conversation WHERE id=? AND userSub=? + DB-->>CM: 返回对话对象 + CM-->>R: 返回Conversation + + alt 对话不存在或权限不足 + R-->>C: 400 Bad Request + else 对话存在 + R->>CM: update_conversation_by_conversation_id(user_sub, id, data) + CM->>DB: UPDATE Conversation SET title=? WHERE id=? + DB-->>CM: 更新成功 + CM-->>R: 返回True + + R->>DM: get_doc_count(conversation_id) + DM-->>R: 返回文档数量 + + R-->>C: 200 OK + 更新后的对话信息 + end +``` + +**流程说明:** + +1. 客户端发送更新请求,包含新的标题 +2. 验证对话是否存在且属于当前用户 +3. 如果验证失败,返回 400 错误 +4. 如果验证通过,更新对话信息 +5. 获取文档数量并返回完整的对话信息 + +### 3.4 删除对话流程 + +```mermaid +sequenceDiagram + participant C as 客户端 + participant R as Router + participant CM as ConversationManager + participant DM as DocumentManager + participant TM as TaskManager + participant DB as PostgreSQL + + C->>R: DELETE /api/conversation + Note over C,R: Body: {conversation_list: [id1, id2, ...]} + + loop 遍历每个conversation_id + R->>CM: delete_conversation_by_conversation_id(user_sub, id) + CM->>DB: SELECT * FROM Conversation WHERE id=? AND userSub=? + DB-->>CM: 返回对话对象 + + alt 对话存在 + CM->>DB: DELETE FROM Conversation WHERE id=? + DB-->>CM: 删除成功 + CM->>TM: delete_tasks_by_conversation_id(id) + TM-->>CM: 任务删除完成 + end + + CM-->>R: 删除完成 + + R->>DM: delete_document_by_conversation_id(user_sub, id) + DM-->>R: 文档删除完成 + end + + R-->>C: 200 OK + 已删除的对话ID列表 +``` + +**流程说明:** + +1. 客户端发送删除请求,包含要删除的对话ID列表 +2. 遍历每个对话ID +3. 从数据库删除对话记录 +4. 级联删除关联的任务(通过 TaskManager) +5. 级联删除关联的文档(通过 DocumentManager) +6. 返回成功删除的对话ID列表 + +### 3.5 对话权限验证流程 + +```mermaid +flowchart TD + Start([开始验证]) --> Input[输入: user_sub, conversation_id] + Input --> Query[查询数据库] + Query --> SQL["SELECT COUNT(id) FROM Conversation
WHERE id=? AND userSub=?"] + SQL --> GetResult[获取查询结果] + GetResult --> CheckCount{count > 0?} + CheckCount -->|是| ReturnTrue[返回 True] + CheckCount -->|否| ReturnFalse[返回 False] + ReturnTrue --> End([结束]) + ReturnFalse --> End + + style Start fill:#e1f5ff + style End fill:#e1f5ff + style ReturnTrue fill:#e7f5e1 + style ReturnFalse fill:#ffe1e1 +``` + +## 4. API 接口定义 + +### 4.1 获取对话列表 + +**接口:** `GET /api/conversation` + +**认证:** 需要会话验证和个人令牌验证 + +**响应示例:** + +```json +{ + "code": 200, + "message": "success", + "result": { + "conversations": [ + { + "conversationId": "uuid", + "title": "对话标题", + "docCount": 5, + "createdTime": "2025-01-15 10:30:00", + "appId": "uuid", + "debug": false + } + ] + } +} +``` + +### 4.2 更新对话 + +**接口:** `POST /api/conversation?conversationId={uuid}` + +**请求体:** + +```json +{ + "title": "新的对话标题" +} +``` + +**响应示例:** + +```json +{ + "code": 200, + "message": "success", + "result": { + "conversationId": "uuid", + "title": "新的对话标题", + "docCount": 5, + "createdTime": "2025-01-15 10:30:00", + "appId": "uuid", + "debug": false + } +} +``` + +### 4.3 删除对话 + +**接口:** `DELETE /api/conversation` + +**请求体:** + +```json +{ + "conversation_list": ["uuid1", "uuid2"] +} +``` + +**响应示例:** + +```json +{ + "code": 200, + "message": "success", + "result": { + "conversationIdList": ["uuid1", "uuid2"] + } +} +``` + +## 5. ConversationManager 类方法 + +### 5.1 方法列表 + +| 方法名 | 功能描述 | 参数 | 返回值 | +|-------|---------|------|--------| +| `get_conversation_by_user_sub` | 获取用户的对话列表 | `user_sub: str` | `list[Conversation]` | +| `get_conversation_by_conversation_id` | 通过ID获取对话 | `user_sub: str, conversation_id: UUID` | `Conversation \| None` | +| `verify_conversation_access` | 验证对话访问权限 | `user_sub: str, conversation_id: UUID` | `bool` | +| `add_conversation_by_user_sub` | 创建新对话 | `title: str, user_sub: str, app_id: UUID, debug: bool` | `Conversation \| None` | +| `update_conversation_by_conversation_id` | 更新对话信息 | `user_sub: str, conversation_id: UUID, data: dict` | `bool` | +| `delete_conversation_by_conversation_id` | 删除指定对话 | `user_sub: str, conversation_id: UUID` | `None` | +| `delete_conversation_by_user_sub` | 删除用户所有对话 | `user_sub: str` | `None` | +| `verify_conversation_id` | 验证对话ID有效性 | `user_sub: str, conversation_id: UUID` | `bool` | + +### 5.2 方法调用关系 + +```mermaid +graph LR + API[API Router] --> Get[get_conversation_by_user_sub] + API --> GetById[get_conversation_by_conversation_id] + API --> Add[add_conversation_by_user_sub] + API --> Update[update_conversation_by_conversation_id] + API --> Delete[delete_conversation_by_conversation_id] + API --> DeleteUser[delete_conversation_by_user_sub] + API --> Verify[verify_conversation_access] + API --> VerifyId[verify_conversation_id] + + Delete --> TaskDel[TaskManager.delete_tasks] + DeleteUser --> TaskDel + + Add --> Usage[更新UserAppUsage] + + style API fill:#fff4e1 + style TaskDel fill:#e1f5ff + style Usage fill:#f5e1ff +``` + +## 6. 关键特性 + +### 6.1 权限控制 + +所有操作都基于 `user_sub` 进行权限验证,确保用户只能访问自己的对话: + +```python +and_( + Conversation.id == conversation_id, + Conversation.userSub == user_sub, +) +``` + +### 6.2 临时对话(Debug 模式) + +- 通过 `isTemporary` 字段标识调试对话 +- 临时对话不会出现在对话列表中 +- 临时对话不会更新 App 使用统计 + +### 6.3 级联删除 + +删除对话时,会自动删除关联资源: + +- 通过 TaskManager 删除关联任务 +- 通过 DocumentManager 删除关联文档 + +### 6.4 App 使用统计 + +创建非调试对话时,会自动维护 App 使用统计: + +- 增加 `usageCount` 计数 +- 更新 `lastUsed` 时间戳 +- 如果记录不存在,则创建新记录 + +## 7. 错误处理 + +### 7.1 异常捕获 + +创建对话时使用 try-except 捕获异常: + +```python +try: + # 数据库操作 +except Exception: + logger.exception("[ConversationManager] 新建对话失败") + return None +``` + +### 7.2 状态码定义 + +| 状态码 | 场景 | 说明 | +|-------|------|------| +| 200 | 成功 | 操作成功完成 | +| 400 | 客户端错误 | 对话不存在或无权限 | +| 500 | 服务器错误 | 数据库操作失败 | diff --git a/design/services/document.md b/design/services/document.md new file mode 100644 index 0000000000000000000000000000000000000000..f559a7bd806239b2cfb8b5e7273091ccea4e51a4 --- /dev/null +++ b/design/services/document.md @@ -0,0 +1,833 @@ +# Document 文档管理模块 + +## 1. 模块概述 + +Document 模块负责处理文档上传、存储、检索和删除等核心功能。该模块分为两层: + +- **Router 层** (`apps/routers/document.py`): 提供 RESTful API 接口 +- **Service 层** (`apps/services/document.py`): 提供文档管理的核心业务逻辑 + +### 1.1 核心功能 + +1. 文档上传到指定对话 +2. 查询对话的文档列表(已使用/未使用) +3. 删除单个文档 +4. 文档状态管理(unused -> used) +5. 与 RAG 系统集成 + +### 1.2 技术架构 + +- **存储**: MinIO (对象存储) + PostgreSQL (元数据) +- **文件类型检测**: python-magic +- **异步处理**: asyncio + asyncer +- **API 框架**: FastAPI + +## 2. 数据模型 + +### 2.1 Document (文档实体) + +```python +{ + "id": "uuid", + "userSub": "string", # 用户标识 + "name": "string", # 文件名 + "extension": "string", # MIME 类型 + "size": "float", # 文件大小 (KB) + "conversationId": "uuid", # 所属对话ID + "createdAt": "datetime" # 创建时间 +} +``` + +### 2.2 ConversationDocument (对话-文档关联) + +```python +{ + "id": "uuid", + "conversationId": "uuid", # 对话ID + "documentId": "uuid", # 文档ID + "recordId": "uuid", # 关联的记录ID + "isUnused": "boolean", # 是否未使用 + "associated": "enum" # 关联类型: QUESTION/ANSWER +} +``` + +## 3. API 接口文档 + +### 3.1 上传文档 + +**接口**: `POST /api/document/{conversation_id}` + +**功能**: 上传一个或多个文档到指定对话 + +**请求参数**: + +- Path: `conversation_id` (UUID) - 对话ID +- Body: `multipart/form-data` + - `documents`: 文件列表 + +**请求示例**: + +```http +POST /api/document/550e8400-e29b-41d4-a716-446655440000 +Content-Type: multipart/form-data + +--boundary +Content-Disposition: form-data; name="documents"; filename="example.pdf" +Content-Type: application/pdf + +[binary data] +--boundary-- +``` + +**响应示例** (成功): + +```json +{ + "code": 200, + "message": "上传成功", + "result": { + "documents": [ + { + "id": "123e4567-e89b-12d3-a456-426614174000", + "name": "example.pdf", + "type": "application/pdf", + "size": 2048.5 + } + ] + } +} +``` + +**响应 Schema**: + +```python +UploadDocumentRsp: + - code: int + - message: str + - result: UploadDocumentMsg + - documents: list[BaseDocumentItem] + - id: UUID + - name: str + - type: str + - size: float +``` + +### 3.2 获取文档列表 + +**接口**: `GET /api/document/{conversation_id}` + +**功能**: 获取指定对话的文档列表 + +**请求参数**: + +- Path: `conversation_id` (UUID) - 对话ID +- Query: + - `used` (boolean, default=False) - 是否包含已使用文档 + - `unused` (boolean, default=True) - 是否包含未使用文档 + +**请求示例**: + +```http +GET /api/document/550e8400-e29b-41d4-a716-446655440000?used=true +``` + +**响应示例** (成功): + +```json +{ + "code": 200, + "message": "获取成功", + "result": { + "documents": [ + { + "id": "123e4567-e89b-12d3-a456-426614174000", + "name": "example.pdf", + "type": "application/pdf", + "size": 2048.5, + "status": "USED", + "created_at": "2025-01-15T10:30:00Z" + } + ] + } +} +``` + +**响应示例** (无权限): + +```json +{ + "code": 403, + "message": "无权限访问", + "result": {} +} +``` + +**文档状态说明**: + +- `USED`: 已在对话中使用 +- `UNUSED`: 已上传但未使用(RAG 处理成功) +- `PROCESSING`: RAG 正在处理中 +- `FAILED`: RAG 处理失败 + +**响应 Schema**: + +```python +ConversationDocumentRsp: + - code: int + - message: str + - result: ConversationDocumentMsg + - documents: list[ConversationDocumentItem] + - id: UUID + - name: str + - type: str + - size: float + - status: DocumentStatus + - created_at: datetime +``` + +### 3.3 删除文档 + +**接口**: `DELETE /api/document/{document_id}` + +**功能**: 删除单个未使用的文档 + +**请求参数**: + +- Path: `document_id` (string) - 文档ID + +**请求示例**: + +```http +DELETE /api/document/123e4567-e89b-12d3-a456-426614174000 +``` + +**响应示例** (成功): + +```json +{ + "code": 200, + "message": "删除成功", + "result": {} +} +``` + +**响应示例** (Framework侧删除失败): + +```json +{ + "code": 500, + "message": "删除文件失败", + "result": {} +} +``` + +**响应示例** (RAG侧删除失败): + +```json +{ + "code": 500, + "message": "RAG端删除文件失败", + "result": {} +} +``` + +**注意**: 只能删除 `isUnused=True` 的文档,已使用的文档无法删除。 + +## 4. DocumentManager 核心方法 + +### 4.1 storage_docs + +**功能**: 存储多个文档到 MinIO 和数据库 + +**输入参数**: + +- 用户标识符 +- 对话ID +- 待上传文件列表 + +**返回值**: 成功上传的文档信息列表 + +**处理流程**: + +1. 遍历每个上传文件 +2. 使用 python-magic 检测文件 MIME 类型 +3. 上传到 MinIO 对象存储(bucket名称为"document") +4. 保存文档元数据到 PostgreSQL 数据库 +5. 返回成功上传的文档列表 + +**MinIO 存储配置**: + +- 存储桶名称: document +- 对象名称: 文档的UUID +- 分块大小: 10MB +- 元数据: 使用Base64编码存储文件名 + +### 4.2 get_unused_docs + +**功能**: 获取对话中未使用的文档 + +**输入参数**: 对话ID + +**返回值**: 未使用的文档列表 + +**查询逻辑**: + +从 ConversationDocument 表中查询标记为"未使用"的记录, +然后根据文档ID从 Document 表获取完整的文档信息。 + +### 4.3 get_used_docs + +**功能**: 获取对话中最近 N 次问答使用的文档 + +**输入参数**: + +- 对话ID +- 记录数量(可选,默认10条) +- 文档类型(可选,可筛选"问题"或"答案"关联的文档) + +**返回值**: 已使用的文档列表 + +**查询逻辑**: + +1. 查询该对话最近 N 条对话记录 +2. 查询这些记录关联的所有文档ID +3. 从 Document 表获取文档详情并去重返回 + +### 4.4 delete_document + +**功能**: 删除未使用的文档 + +**输入参数**: + +- 用户标识符 +- 待删除文档ID列表 + +**返回值**: 无 + +**删除步骤**: + +1. 验证文档所有权(检查文档是否属于当前用户) +2. 验证文档未使用状态(只能删除未使用的文档) +3. 从 PostgreSQL 数据库删除文档记录 +4. 从 MinIO 对象存储删除文件数据 + +**安全机制**: + +- 权限控制: 只能删除自己上传的文档 +- 状态保护: 只能删除未使用的文档,已使用的文档不可删除 + +### 4.5 change_doc_status + +**功能**: 将文档状态从"未使用"改为"已使用" + +**输入参数**: + +- 用户标识符 +- 对话ID + +**返回值**: 无 + +**使用场景**: + +当用户在对话中发送消息并引用已上传的文档时, +系统调用此方法将对话中的所有未使用文档标记为已使用状态。 + +**处理逻辑**: + +查询该对话下所有标记为"未使用"的文档, +将它们的状态批量更新为"已使用"。 + +### 4.6 save_answer_doc + +**功能**: 保存与答案关联的文档 + +**输入参数**: + +- 用户标识符 +- 对话记录ID +- 文档关联信息列表 + +**返回值**: 无 + +**处理逻辑**: + +1. 验证对话记录存在且属于当前用户 +2. 更新文档关联表(ConversationDocument),设置: + - 文档状态为"已使用" + - 关联类型为"答案" + - 关联的记录ID + +**使用场景**: + +当AI生成答案时,如果答案中引用或生成了文档, +系统调用此方法将这些文档与答案记录关联起来。 + +## 5. 流程图 + +### 5.1 文档上传流程 + +```mermaid +flowchart TD + Start([用户上传文档]) --> A[POST /api/document/:conversation_id] + A --> B{验证 Session & Token} + B -->|失败| Err1[返回 401/403] + B -->|成功| C[DocumentManager.storage_docs] + + C --> D[遍历上传文件] + D --> E{文件名/大小合法?} + E -->|否| D + E -->|是| F[生成 UUID] + + F --> G[检测 MIME 类型] + G --> H[上传到 MinIO] + H --> I{上传成功?} + I -->|否| D + I -->|是| J[保存元数据到 PostgreSQL] + + J --> K{还有文件?} + K -->|是| D + K -->|否| L[发送文件到 RAG 系统] + + L --> M[KnowledgeBaseService.send_file_to_rag] + M --> N[返回文档列表] + N --> End([返回 200 OK]) + + Err1 --> End +``` + +### 5.2 文档列表查询流程 + +```mermaid +flowchart TD + Start([用户请求文档列表]) --> A[GET /api/document/:conversation_id] + A --> B{验证权限} + B -->|无权限| Err1[返回 403] + B -->|有权限| C{used=true?} + + C -->|是| D[DocumentManager.get_used_docs] + D --> E[查询最近10条 Record] + E --> F[查询关联文档] + F --> G[标记状态为 USED] + + C -->|否| H{unused=true?} + G --> H + + H -->|是| I[DocumentManager.get_unused_docs] + I --> J[查询 isUnused=true 的文档] + J --> K[KnowledgeBaseService.get_doc_status_from_rag] + + K --> L{RAG 状态} + L -->|success| M[标记为 UNUSED] + L -->|failed| N[标记为 FAILED] + L -->|processing| O[标记为 PROCESSING] + + M --> P[合并结果] + N --> P + O --> P + G --> P + H -->|否| P + + P --> Q[返回文档列表] + Q --> End([返回 200 OK]) + + Err1 --> End +``` + +### 5.3 文档删除流程 + +```mermaid +flowchart TD + Start([用户删除文档]) --> A[DELETE /api/document/:document_id] + A --> B[DocumentManager.delete_document] + + B --> C{验证文档存在?} + C -->|否| Err1[返回 500: 删除文件失败] + C -->|是| D{验证所有权?} + D -->|否| Err1 + D -->|是| E{验证 isUnused=true?} + E -->|否| Err1 + + E -->|是| F[从 PostgreSQL 删除] + F --> G[从 MinIO 删除] + G --> H[KnowledgeBaseService.delete_doc_from_rag] + + H --> I{RAG 删除成功?} + I -->|否| Err2[返回 500: RAG端删除失败] + I -->|是| J[返回 200 OK] + + Err1 --> End([结束]) + Err2 --> End + J --> End +``` + +### 5.4 文档状态转换流程 + +```mermaid +stateDiagram-v2 + [*] --> Uploading: 用户上传文档 + Uploading --> Processing: 发送到 RAG + + Processing --> UNUSED: RAG 处理成功 + Processing --> FAILED: RAG 处理失败 + + UNUSED --> USED: 用户在对话中引用 + UNUSED --> [*]: 用户删除文档 + + USED --> [*]: 删除对话时级联删除 + FAILED --> [*]: 用户删除文档 + + note right of Processing + RAG 系统处理中 + 状态: PROCESSING + end note + + note right of UNUSED + 已上传但未使用 + 可被删除 + end note + + note right of USED + 已在对话中使用 + 不可单独删除 + end note +``` + +## 6. 时序图 + +### 6.1 文档上传完整时序 + +```mermaid +sequenceDiagram + actor User as 用户 + participant API as FastAPI Router + participant Auth as 认证中间件 + participant DM as DocumentManager + participant MinIO as MinIO 存储 + participant DB as PostgreSQL + participant RAG as RAG 系统 + + User->>API: POST /api/document/:conv_id + API->>Auth: verify_session() + Auth-->>API: ✓ user_sub + API->>Auth: verify_personal_token() + Auth-->>API: ✓ session_id + + API->>DM: storage_docs(user_sub, conv_id, files) + + loop 每个文件 + DM->>DM: 检测 MIME 类型 + DM->>MinIO: upload_file(doc_id, data) + MinIO-->>DM: ✓ 存储成功 + DM->>DB: INSERT Document + DB-->>DM: ✓ 保存成功 + end + + DM-->>API: 返回文档列表 + + API->>RAG: send_file_to_rag(session_id, docs) + RAG-->>API: ✓ 接收成功 + + API->>User: 200 OK + 文档列表 + + Note over RAG: 异步处理文档
解析、向量化、索引 +``` + +### 6.2 文档列表查询时序 + +```mermaid +sequenceDiagram + actor User as 用户 + participant API as FastAPI Router + participant CM as ConversationManager + participant DM as DocumentManager + participant DB as PostgreSQL + participant RAG as RAG 系统 + + User->>API: GET /api/document/:conv_id?used=true&unused=true + + API->>CM: verify_conversation_access(user_sub, conv_id) + CM->>DB: SELECT Conversation + DB-->>CM: Conversation 数据 + CM-->>API: ✓ 有权限 + + alt used=true + API->>DM: get_used_docs(conv_id) + DM->>DB: SELECT Record (最近10条) + DB-->>DM: Record 列表 + DM->>DB: SELECT ConversationDocument + DB-->>DM: 关联的文档ID + DM->>DB: SELECT Document + DB-->>DM: 文档详情 + DM-->>API: 已使用文档列表 (状态=USED) + end + + alt unused=true + API->>DM: get_unused_docs(conv_id) + DM->>DB: SELECT ConversationDocument (isUnused=true) + DB-->>DM: 未使用文档ID + DM->>DB: SELECT Document + DB-->>DM: 文档详情 + DM-->>API: 未使用文档列表 + + API->>RAG: get_doc_status_from_rag(session_id, doc_ids) + RAG-->>API: 文档处理状态列表 + + loop 每个未使用文档 + alt RAG状态=success + API->>API: 标记为 UNUSED + else RAG状态=failed + API->>API: 标记为 FAILED + else RAG状态=processing + API->>API: 标记为 PROCESSING + end + end + end + + API->>API: 合并所有文档 + API->>User: 200 OK + 完整文档列表 +``` + +### 6.3 文档删除时序 + +```mermaid +sequenceDiagram + actor User as 用户 + participant API as FastAPI Router + participant DM as DocumentManager + participant DB as PostgreSQL + participant MinIO as MinIO 存储 + participant RAG as RAG 系统 + + User->>API: DELETE /api/document/:doc_id + + API->>DM: delete_document(user_sub, [doc_id]) + + DM->>DB: SELECT Document (验证所有权) + DB-->>DM: Document 数据 + + DM->>DB: SELECT ConversationDocument (验证未使用) + DB-->>DM: ConversationDocument 数据 + + alt 验证失败 + DM-->>API: 返回 None + API->>User: 500 删除文件失败 + else 验证成功 + DM->>DB: DELETE ConversationDocument + DB-->>DM: ✓ + DM->>DB: DELETE Document + DB-->>DM: ✓ + DM->>MinIO: delete_file("document", doc_id) + MinIO-->>DM: ✓ + DM-->>API: 删除成功 + + API->>RAG: delete_doc_from_rag(session_id, [doc_id]) + RAG-->>API: 删除结果 + + alt RAG删除失败 + API->>User: 500 RAG端删除失败 + else RAG删除成功 + API->>User: 200 OK 删除成功 + end + end +``` + +### 6.4 文档状态变更时序 + +```mermaid +sequenceDiagram + actor User as 用户 + participant Chat as 对话服务 + participant DM as DocumentManager + participant DB as PostgreSQL + + User->>Chat: 发送消息引用文档 + + Chat->>DM: change_doc_status(user_sub, conv_id) + + DM->>DB: SELECT Conversation (验证权限) + DB-->>DM: Conversation 数据 + + DM->>DB: SELECT ConversationDocument (isUnused=true) + DB-->>DM: 未使用文档列表 + + loop 每个未使用文档 + DM->>DB: UPDATE isUnused = false + DB-->>DM: ✓ + end + + DM-->>Chat: 状态更新完成 + + Note over Chat: 继续处理对话
生成回答 + + Chat->>DM: save_answer_doc(user_sub, record_id, doc_infos) + + DM->>DB: SELECT Record (验证) + DB-->>DM: Record 数据 + + loop 每个答案关联文档 + DM->>DB: UPDATE ConversationDocument + DB-->>DM: ✓ + end + + DM-->>Chat: 保存完成 + Chat->>User: 返回答案 +``` + +## 7. 架构图 + +### 7.1 整体架构 + +```mermaid +graph TB + subgraph "客户端" + UI[前端界面] + end + + subgraph "API 层" + Router[document.py Router] + Auth[认证中间件] + end + + subgraph "Service 层" + DM[DocumentManager] + CM[ConversationManager] + KB[KnowledgeBaseService] + end + + subgraph "存储层" + MinIO[(MinIO
对象存储)] + PG[(PostgreSQL
关系数据库)] + end + + subgraph "外部服务" + RAG[RAG 系统] + end + + UI -->|HTTP/HTTPS| Router + Router --> Auth + Auth --> DM + Router --> CM + Router --> KB + + DM --> MinIO + DM --> PG + CM --> PG + KB --> RAG + + style Router fill:#e1f5ff + style DM fill:#fff3e0 + style MinIO fill:#f3e5f5 + style PG fill:#f3e5f5 + style RAG fill:#e8f5e9 +``` + +### 7.2 数据库关系图 + +```mermaid +erDiagram + Document ||--o{ ConversationDocument : "1:N" + Conversation ||--o{ ConversationDocument : "1:N" + Record ||--o{ ConversationDocument : "1:N" + + Document { + uuid id PK + string userSub + string name + string extension + float size + uuid conversationId FK + datetime createdAt + } + + ConversationDocument { + uuid id PK + uuid conversationId FK + uuid documentId FK + uuid recordId FK + boolean isUnused + enum associated + } + + Conversation { + uuid id PK + string userSub + string title + datetime createdAt + } + + Record { + uuid id PK + uuid conversationId FK + string userSub + string content + datetime createdAt + } +``` + +## 8. 错误处理 + +### 8.1 常见错误码 + +| 错误码 | 说明 | 处理建议 | +|--------|------|----------| +| 400 | 请求参数错误 | 检查请求格式和参数 | +| 401 | 未认证 | 检查 Session Token | +| 403 | 无权限访问 | 验证对话所有权 | +| 404 | 资源不存在 | 确认文档/对话ID正确 | +| 500 | 服务器内部错误 | 查看日志,联系管理员 | + +### 8.2 异常处理机制 + +**文档上传异常处理**: + +在上传多个文档时,如果某个文档上传失败,系统会跳过该文档继续处理其他文档, +并记录详细错误日志。最终只返回成功上传的文档列表。 + +**特点**: + +- 部分失败不影响整体流程 +- 只返回成功上传的文档 +- 详细错误日志便于排查 + +## 9. 集成说明 + +### 9.1 与 RAG 系统集成 + +**文档上传后发送到 RAG**: + +调用 KnowledgeBaseService.send_file_to_rag 方法, +将文档发送到 RAG 系统进行异步处理(解析、向量化、索引)。 + +**查询文档处理状态**: + +调用 KnowledgeBaseService.get_doc_status_from_rag 方法, +获取文档在 RAG 系统中的处理状态。 + +**从 RAG 删除文档**: + +调用 KnowledgeBaseService.delete_doc_from_rag 方法, +从 RAG 系统中删除文档的索引数据。 + +### 9.2 RAG 状态映射 + +| RAG 状态 | Framework 状态 | 说明 | +|----------|----------------|------| +| success | UNUSED | 文档已解析,可供检索 | +| failed | FAILED | 文档解析失败 | +| processing | PROCESSING | 文档处理中 | +| - | USED | 已在对话中引用 | + +## 10. 配置参数 + +### 10.1 MinIO 配置 + +- **BUCKET_NAME**: "document" +- **PART_SIZE**: 10MB (10 \* 1024 \* 1024 字节) + +### 10.2 查询配置 + +- **DEFAULT_RECORD_NUM**: 10 (获取最近10条记录的文档) + +### 10.3 文件名编码 + +使用 Base64 编码存储文件名,避免特殊字符导致的问题。 diff --git a/design/services/flow.md b/design/services/flow.md index 89dcf269309646a59e3ce635054bbc2620536e8b..e235172f2311cbbc46c9cd40f92c45abf899755b 100644 --- a/design/services/flow.md +++ b/design/services/flow.md @@ -602,82 +602,16 @@ flowchart TD Process -->|成功| Return200[返回200 OK] ``` -## 9. 性能优化 - -### 9.1 并发处理 - -- 使用 `async/await` 异步处理 -- 数据库查询使用连接池 -- 文件I/O使用 `aiofiles` 异步操作 - -### 9.2 数据库优化 - -- 创建复合索引: `idx_app_id_id`, `idx_app_id_name` -- 批量查询减少数据库往返 -- 使用 `select().where().order_by()` 优化查询 - -### 9.3 缓存策略 - -- FlowLoader 通过 Pool 单例复用 -- 节点参数 schema 可缓存 -- 向量化数据独立存储和更新 - -## 10. 安全性 - -### 10.1 访问控制 - -```mermaid -flowchart TD - Request[用户请求] --> CheckSession{会话验证} - CheckSession -->|失败| Reject1[拒绝访问] - CheckSession -->|成功| CheckToken{Personal Token验证} - CheckToken -->|失败| Reject2[拒绝访问] - CheckToken -->|成功| CheckPerm{权限检查} - CheckPerm -->|读取| ValidateRead{验证读权限} - CheckPerm -->|写入/删除| ValidateWrite{验证写权限} - ValidateRead -->|有权限| AllowRead[允许读取] - ValidateRead -->|无权限| Reject3[拒绝访问] - ValidateWrite -->|是所有者| AllowWrite[允许写入] - ValidateWrite -->|非所有者| Reject4[拒绝访问] -``` - -### 10.2 数据验证 +## 9. 安全性 - 输入参数使用 Pydantic 模型验证 - 分支ID禁止包含"."等非法字符 - 节点/边ID唯一性检查 - YAML文件格式验证 -### 10.3 文件安全 +## 10. 配置示例 -- 文件路径使用 `anyio.Path` 安全处理 -- 限制文件访问在 `data_dir` 范围内 -- 文件hash值校验(AppHashes表) - -## 11. 扩展性 - -### 11.1 支持的节点类型 - -- **START**: 起始节点 -- **END**: 终止节点 -- **CHOICE**: 分支节点(多出边) -- **Empty**: 空节点(工具被删除时的占位符) -- **自定义节点**: 通过Service和NodeInfo扩展 - -### 11.2 支持的边类型 - -- **NORMAL**: 普通边 -- **其他自定义类型**: 通过 `EdgeType` 枚举扩展 - -### 11.3 插件机制 - -- 通过 `Pool().get_call()` 动态加载节点实现 -- Service机制支持用户自定义服务 -- 节点参数schema动态生成 - -## 12. 配置示例 - -### 12.1 YAML配置文件示例 +YAML配置文件示例: ```yaml name: 示例工作流 @@ -734,7 +668,7 @@ edges: edge_type: NORMAL ``` -## 13. 文件存储结构 +## 11. 文件存储结构 ```text data_dir/ @@ -747,17 +681,3 @@ data_dir/ ├── {flow_id}.yaml # 其他工作流 └── ... ``` - -## 14. 总结 - -FlowManager 模块提供了完整的工作流管理功能,具有以下特点: - -✅ **完整的CRUD操作**: 支持工作流的创建、读取、更新、删除 -✅ **严格的数据验证**: 多层验证确保工作流配置正确性 -✅ **连通性检查**: BFS算法验证图的连通性 -✅ **权限控制**: 完善的用户权限和访问控制 -✅ **异步处理**: 高性能的异步I/O操作 -✅ **扩展性强**: 支持自定义节点和边类型 -✅ **数据一致性**: 数据库与文件系统双重存储保证一致性 - -该模块是 openEuler Intelligence 框架工作流引擎的核心组件,为上层应用提供了稳定可靠的工作流管理服务。 diff --git a/design/services/parameter.md b/design/services/parameter.md new file mode 100644 index 0000000000000000000000000000000000000000..0f2e75154d6b7ce793ba91e44eb5a31f5ee1e10e --- /dev/null +++ b/design/services/parameter.md @@ -0,0 +1,805 @@ +# Parameter 模块设计文档 + +## 1. 模块概述 + +Parameter 模块负责管理流程步骤的参数信息,包括获取前置步骤的输出参数、获取不同数据类型支持的操作符等核心功能。该模块由服务层(`services/parameter.py`)和路由层(`routers/parameter.py`)组成,为流程编排中的参数选择和条件判断提供支撑。 + +### 1.1 主要组件 + +- **ParameterManager**: 参数管理器,提供参数查询和类型映射功能 +- **Router**: FastAPI 路由层,提供 RESTful API 接口 + +### 1.2 核心功能 + +- 获取指定步骤的前置参数树 +- 根据参数类型获取支持的操作符 +- 操作符与绑定类型的映射关系管理 +- 参数路径的图遍历和拓扑排序 + +## 2. 模块架构 + +### 2.1 整体架构图 + +```mermaid +graph TB + Client[客户端] --> Router[FastAPI Router] + Router --> Middleware[依赖中间件] + Middleware --> SessionVerify[会话验证] + Middleware --> TokenVerify[个人令牌验证] + Router --> ParamMgr[ParameterManager] + ParamMgr --> FlowMgr[FlowManager] + ParamMgr --> NodeMgr[NodeManager] + ParamMgr --> CondHandler[ConditionHandler] + ParamMgr --> Slot[Slot处理器] + FlowMgr --> PostgreSQL[(PostgreSQL)] + NodeMgr --> PostgreSQL + + style Client fill:#e1f5ff + style Router fill:#fff4e1 + style ParamMgr fill:#e7f5e1 + style PostgreSQL fill:#f5e1e1 +``` + +### 2.2 数据类型与操作符关系 + +```mermaid +erDiagram + Type ||--o{ Operate : "supports" + Operate ||--|| BindType : "binds to" + + Type { + string NUMBER + string STRING + string LIST + string DICT + string BOOL + } + + Operate { + string operate_name + enum operate_type + } + + BindType { + string type_name + string description + } +``` + +## 3. 核心流程 + +### 3.1 获取前置参数流程 + +```mermaid +sequenceDiagram + participant C as 客户端 + participant R as Router + participant Auth as 认证中间件 + participant PM as ParameterManager + participant AM as AppCenterManager + participant FM as FlowManager + participant NM as NodeManager + participant S as Slot + participant DB as PostgreSQL + + C->>R: GET /api/parameter?appId&flowId&stepId + R->>Auth: 验证会话和令牌 + Auth-->>R: 验证通过 + + R->>AM: validate_user_app_access(user_sub, appId) + AM->>DB: 查询用户应用权限 + DB-->>AM: 返回权限结果 + AM-->>R: 返回权限验证结果 + + alt 无权限访问 + R-->>C: 403 Forbidden + end + + R->>FM: get_flow_by_app_and_flow_id(appId, flowId) + FM->>DB: SELECT * FROM Flow WHERE appId=? AND flowId=? + DB-->>FM: 返回Flow对象 + FM-->>R: 返回Flow + + alt Flow不存在 + R-->>C: 404 Not Found + end + + R->>PM: get_pre_params_by_flow_and_step_id(flow, stepId) + + Note over PM: 构建步骤ID到节点ID的映射 + Note over PM: 构建边的入度关系图 + + PM->>PM: 执行BFS图遍历 + Note over PM: 从stepId开始反向遍历所有前置步骤 + + loop 遍历每个前置步骤 + PM->>NM: get_node_params(node_id) + NM->>DB: 查询节点输出Schema + DB-->>NM: 返回output_schema + NM-->>PM: 返回output_schema + + PM->>S: 创建Slot(output_schema) + PM->>S: get_params_node_from_schema() + S-->>PM: 返回ParamsNode树 + + PM->>PM: 构造StepParams对象 + end + + PM-->>R: 返回前置参数列表 + R-->>C: 200 OK + StepParams列表 +``` + +**流程说明:** + +1. 客户端发起获取参数请求,提供应用ID、流程ID和步骤ID +2. 验证用户身份和应用访问权限 +3. 查询指定的流程对象 +4. 构建步骤间的依赖关系图(通过边的入度关系) +5. 使用BFS算法从当前步骤反向遍历所有前置步骤 +6. 排除起始步骤和结束步骤 +7. 为每个有效的前置步骤获取节点输出Schema +8. 通过Slot处理器将Schema转换为ParamsNode树结构 +9. 返回所有前置步骤的参数信息 + +### 3.2 获取操作符流程 + +```mermaid +sequenceDiagram + participant C as 客户端 + participant R as Router + participant PM as ParameterManager + participant CH as ConditionHandler + + C->>R: GET /api/parameter/operate?paramType=STRING + R->>PM: get_operate_and_bind_type(paramType) + + PM->>PM: 根据paramType匹配Operate枚举 + Note over PM: STRING -> StringOperate
NUMBER -> NumberOperate
LIST -> ListOperate
BOOL -> BoolOperate
DICT -> DictOperate + + loop 遍历该类型的所有操作符 + PM->>CH: get_value_type_from_operate(operate) + CH-->>PM: 返回绑定类型(Type) + + PM->>PM: 构造OperateAndBindType + end + + PM-->>R: 返回操作符列表 + R-->>C: 200 OK + OperateAndBindType列表 +``` + +**流程说明:** + +1. 客户端请求指定参数类型支持的操作符 +2. 根据参数类型匹配对应的操作符枚举类 +3. 遍历该类型的所有操作符 +4. 通过ConditionHandler查询每个操作符绑定的值类型 +5. 构造操作符与绑定类型的映射关系 +6. 返回完整的操作符列表 + +### 3.3 参数图遍历算法 + +```mermaid +flowchart TD + Start([开始]) --> Input[输入: flow, step_id] + Input --> InitMap[初始化映射表] + InitMap --> BuildStepMap[构建step_id->node_id映射] + BuildStepMap --> BuildEdgeMap[构建边的入度关系图] + + BuildEdgeMap --> InitQueue["初始化队列q=[step_id]"] + InitQueue --> BFSLoop{队列是否为空?} + + BFSLoop -->|否| DeQueue[取出队首元素tmp_step_id] + DeQueue --> GetInEdges["获取入边in_edges[tmp_step_id]"] + GetInEdges --> LoopEdges{遍历所有入边} + + LoopEdges -->|有未访问节点| CheckVisited{节点已访问?} + CheckVisited -->|否| AddQueue[加入队列] + AddQueue --> LoopEdges + CheckVisited -->|是| LoopEdges + + LoopEdges -->|遍历完成| BFSLoop + BFSLoop -->|是| ProcessParams[处理参数队列] + + ProcessParams --> SkipFirst{跳过第一个元素
index从1开始} + SkipFirst --> LoopQueue{遍历队列} + + LoopQueue -->|有元素| CheckSpecial{是否为起始/结束步骤?} + CheckSpecial -->|是| SkipStep[跳过该步骤] + SkipStep --> LoopQueue + + CheckSpecial -->|否| GetNodeId[获取node_id和name] + GetNodeId --> CheckNodeId{node_id存在?} + CheckNodeId -->|否| LogError[记录错误日志] + LogError --> LoopQueue + + CheckNodeId -->|是| GetSchema[获取节点输出Schema] + GetSchema --> CreateSlot[创建Slot处理器] + CreateSlot --> ExtractParams[提取ParamsNode树] + ExtractParams --> AddResult[添加到结果列表] + AddResult --> LoopQueue + + LoopQueue -->|遍历完成| Return[返回StepParams列表] + Return --> End([结束]) + + style Start fill:#e1f5ff + style End fill:#e1f5ff + style BFSLoop fill:#fff4e1 + style CheckSpecial fill:#ffe1e1 + style GetSchema fill:#e7f5e1 +``` + +**算法说明:** + +1. **初始化阶段**: 构建步骤ID到节点ID的映射和边的入度关系图 +2. **BFS遍历**: 从目标步骤开始,反向遍历所有可达的前置步骤 +3. **过滤阶段**: 排除起始步骤、结束步骤和队列首元素(当前步骤自身) +4. **参数提取**: 为每个有效步骤获取输出Schema并转换为参数树 +5. **结果构造**: 组装包含步骤ID、名称和参数节点的完整信息 + +## 4. API 接口定义 + +### 4.1 获取步骤参数 + +**接口:** `GET /api/parameter` + +**认证:** 需要会话验证和个人令牌验证 + +**查询参数:** + +| 参数名 | 类型 | 必填 | 说明 | +|-------|------|------|------| +| appId | UUID | 是 | 应用ID | +| flowId | string | 是 | 流程ID | +| stepId | UUID | 是 | 步骤ID | + +**响应示例:** + +```json +{ + "code": 200, + "message": "获取参数成功", + "result": [ + { + "stepId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "name": "API调用步骤", + "paramsNode": { + "paramName": "root", + "paramPath": "/root", + "paramType": "dict", + "subParams": [ + { + "paramName": "statusCode", + "paramPath": "/root/statusCode", + "paramType": "number", + "subParams": null + }, + { + "paramName": "responseBody", + "paramPath": "/root/responseBody", + "paramType": "dict", + "subParams": [ + { + "paramName": "data", + "paramPath": "/root/responseBody/data", + "paramType": "list", + "subParams": null + }, + { + "paramName": "message", + "paramPath": "/root/responseBody/message", + "paramType": "string", + "subParams": null + } + ] + } + ] + } + }, + { + "stepId": "b2c3d4e5-f6a7-8901-bcde-f12345678901", + "name": "数据转换步骤", + "paramsNode": { + "paramName": "root", + "paramPath": "/root", + "paramType": "dict", + "subParams": [ + { + "paramName": "transformedData", + "paramPath": "/root/transformedData", + "paramType": "list", + "subParams": null + }, + { + "paramName": "count", + "paramPath": "/root/count", + "paramType": "number", + "subParams": null + } + ] + } + } + ] +} +``` + +**错误响应:** + +```json +{ + "code": 403, + "message": "用户没有权限访问该流", + "result": [] +} +``` + +```json +{ + "code": 404, + "message": "未找到该流", + "result": [] +} +``` + +### 4.2 获取操作符列表 + +**接口:** `GET /api/parameter/operate` + +**认证:** 需要会话验证和个人令牌验证 + +**查询参数:** + +| 参数名 | 类型 | 必填 | 说明 | +|-------|------|------|------| +| paramType | Type | 是 | 参数类型(string/number/list/dict/bool) | + +**响应示例 (STRING类型):** + +```json +{ + "code": 200, + "message": "获取操作成功", + "result": [ + { + "operate": "string_equal", + "bind_type": "string" + }, + { + "operate": "string_not_equal", + "bind_type": "string" + }, + { + "operate": "string_contains", + "bind_type": "string" + }, + { + "operate": "string_not_contains", + "bind_type": "string" + }, + { + "operate": "string_starts_with", + "bind_type": "string" + }, + { + "operate": "string_ends_with", + "bind_type": "string" + }, + { + "operate": "string_length_equal", + "bind_type": "number" + }, + { + "operate": "string_length_greater_than", + "bind_type": "number" + }, + { + "operate": "string_length_greater_than_or_equal", + "bind_type": "number" + }, + { + "operate": "string_length_less_than", + "bind_type": "number" + }, + { + "operate": "string_length_less_than_or_equal", + "bind_type": "number" + }, + { + "operate": "string_regex_match", + "bind_type": "string" + } + ] +} +``` + +**响应示例 (NUMBER类型):** + +```json +{ + "code": 200, + "message": "获取操作成功", + "result": [ + { + "operate": "number_equal", + "bind_type": "number" + }, + { + "operate": "number_not_equal", + "bind_type": "number" + }, + { + "operate": "number_greater_than", + "bind_type": "number" + }, + { + "operate": "number_less_than", + "bind_type": "number" + }, + { + "operate": "number_greater_than_or_equal", + "bind_type": "number" + }, + { + "operate": "number_less_than_or_equal", + "bind_type": "number" + } + ] +} +``` + +**响应示例 (BOOL类型):** + +```json +{ + "code": 200, + "message": "获取操作成功", + "result": [ + { + "operate": "bool_equal", + "bind_type": "bool" + }, + { + "operate": "bool_not_equal", + "bind_type": "bool" + } + ] +} +``` + +**响应示例 (DICT类型):** + +```json +{ + "code": 200, + "message": "获取操作成功", + "result": [ + { + "operate": "dict_equal", + "bind_type": "dict" + }, + { + "operate": "dict_not_equal", + "bind_type": "dict" + }, + { + "operate": "dict_contains_key", + "bind_type": "string" + }, + { + "operate": "dict_not_contains_key", + "bind_type": "string" + } + ] +} +``` + +## 5. ParameterManager 类方法 + +### 5.1 方法列表 + +| 方法名 | 功能描述 | 参数 | 返回值 | +|-------|---------|------|--------| +| `get_operate_and_bind_type` | 获取指定类型支持的操作符列表 | `param_type: Type` | `list[OperateAndBindType]` | +| `get_pre_params_by_flow_and_step_id` | 获取指定步骤的前置参数树 | `flow: FlowItem, step_id: UUID` | `list[StepParams]` | + +### 5.2 方法调用关系 + +```mermaid +graph TB + Router[API Router] --> GetOperate[get_operate_and_bind_type] + Router --> GetPreParams[get_pre_params_by_flow_and_step_id] + + GetOperate --> MatchEnum[匹配操作符枚举类] + MatchEnum --> LoopOperate[遍历操作符] + LoopOperate --> GetBindType[ConditionHandler.get_value_type_from_operate] + GetBindType --> BuildResult[构造OperateAndBindType] + + GetPreParams --> BuildMap[构建映射关系] + BuildMap --> BFS[BFS图遍历] + BFS --> FilterSteps[过滤特殊步骤] + FilterSteps --> GetSchema[NodeManager.get_node_params] + GetSchema --> CreateSlot[创建Slot处理器] + CreateSlot --> ExtractParams[Slot.get_params_node_from_schema] + ExtractParams --> BuildStepParams[构造StepParams] + + style Router fill:#fff4e1 + style GetOperate fill:#e1f5ff + style GetPreParams fill:#e7f5e1 + style BFS fill:#ffe1e1 +``` + +## 6. 数据结构详解 + +### 6.1 ParamsNode 结构 + +ParamsNode 是参数节点的树形结构表示,用于描述参数的层级关系: + +```mermaid +classDiagram + class ParamsNode { + +string paramName + +string paramPath + +Type paramType + +list~ParamsNode~ subParams + } + + ParamsNode "1" --> "*" ParamsNode : subParams + + class Type { + <> + STRING + NUMBER + LIST + DICT + BOOL + } + + ParamsNode --> Type : paramType +``` + +**字段说明:** + +- `paramName`: 参数名称,表示该参数的标识符 +- `paramPath`: 参数路径,使用JSON Pointer格式(如 `/root/data/items`) +- `paramType`: 参数类型,支持string/number/list/dict/bool五种基础类型 +- `subParams`: 子参数列表,仅当类型为dict时存在,用于表示嵌套结构 + +### 6.2 StepParams 结构 + +StepParams 封装了步骤级别的参数信息: + +```mermaid +classDiagram + class StepParams { + +UUID stepId + +string name + +ParamsNode paramsNode + } + + class ParamsNode { + +string paramName + +string paramPath + +Type paramType + +list~ParamsNode~ subParams + } + + StepParams "1" --> "0..1" ParamsNode : paramsNode +``` + +**字段说明:** + +- `stepId`: 步骤的唯一标识符 +- `name`: 步骤的显示名称 +- `paramsNode`: 该步骤输出的参数树根节点 + +### 6.3 OperateAndBindType 结构 + +OperateAndBindType 描述了操作符与其绑定值类型的映射关系: + +```mermaid +classDiagram + class OperateAndBindType { + +Operate operate + +Type bind_type + } + + class Operate { + <> + NumberOperate + StringOperate + ListOperate + BoolOperate + DictOperate + } + + class Type { + <> + STRING + NUMBER + LIST + DICT + BOOL + } + + OperateAndBindType --> Operate : operate + OperateAndBindType --> Type : bind_type +``` + +**字段说明:** + +- `operate`: 操作符名称,如 `string_equal`、`number_greater_than` 等 +- `bind_type`: 该操作符右侧值应该绑定的数据类型 + +## 7. 操作符类型系统 + +### 7.1 类型与操作符映射 + +```mermaid +graph LR + STRING[STRING类型] --> StringOps[12种字符串操作符] + NUMBER[NUMBER类型] --> NumberOps[6种数字操作符] + LIST[LIST类型] --> ListOps[9种列表操作符] + BOOL[BOOL类型] --> BoolOps[2种布尔操作符] + DICT[DICT类型] --> DictOps[4种字典操作符] + + StringOps --> Equal[相等/不相等] + StringOps --> Content[包含/不包含] + StringOps --> Position[开始/结束于] + StringOps --> Length[长度比较5种] + StringOps --> Regex[正则匹配] + + NumberOps --> NumEqual[相等/不相等] + NumberOps --> NumCompare[大于/小于/大于等于/小于等于] + + ListOps --> ListEqual[相等/不相等] + ListOps --> ListContent[包含/不包含] + ListOps --> ListLength[长度比较5种] + + BoolOps --> BoolEqual[相等/不相等] + + DictOps --> DictEqual[相等/不相等] + DictOps --> DictKey[包含键/不包含键] + + style STRING fill:#e1f5ff + style NUMBER fill:#ffe1e1 + style LIST fill:#e7f5e1 + style BOOL fill:#fff4e1 + style DICT fill:#f5e1ff +``` + +### 7.2 操作符绑定规则 + +不同操作符对右侧值的类型要求不同: + +| 操作符 | 左值类型 | 绑定类型 | 说明 | +|-------|---------|---------|------| +| `string_equal` | STRING | STRING | 字符串完全匹配 | +| `string_contains` | STRING | STRING | 包含子字符串 | +| `string_length_equal` | STRING | NUMBER | 字符串长度比较 | +| `string_regex_match` | STRING | STRING | 正则表达式匹配 | +| `number_greater_than` | NUMBER | NUMBER | 数值大小比较 | +| `list_contains` | LIST | ANY | 列表元素包含检查 | +| `list_length_equal` | LIST | NUMBER | 列表长度比较 | +| `dict_contains_key` | DICT | STRING | 字典键存在性检查 | +| `dict_equal` | DICT | DICT | 字典完全匹配 | +| `bool_equal` | BOOL | BOOL | 布尔值比较 | + +## 8. 关键特性 + +### 8.1 图遍历算法 + +使用广度优先搜索(BFS)算法反向遍历流程图: + +- **时间复杂度**: O(V + E),V为步骤数,E为边数 +- **空间复杂度**: O(V),需要存储访问队列和映射表 +- **遍历顺序**: 按依赖关系从远到近排列前置步骤 +- **去重处理**: 自动去除重复访问的步骤节点 + +### 8.2 Schema 到参数树的转换 + +通过 Slot 处理器将 JSON Schema 转换为 ParamsNode 树: + +- **类型映射**: 将JSON Schema的类型映射到系统定义的五种基础类型 +- **递归解析**: 递归处理object和array类型的嵌套结构 +- **路径构建**: 自动生成JSON Pointer格式的参数路径 +- **错误容忍**: 遇到不支持的类型时记录日志并跳过 + +### 8.3 权限控制 + +多层级权限验证机制: + +1. **会话验证**: 验证用户登录状态 +2. **令牌验证**: 验证个人访问令牌有效性 +3. **应用访问验证**: 通过 AppCenterManager 验证用户是否有权限访问指定应用 +4. **流程验证**: 验证流程是否存在于指定应用下 + +### 8.4 特殊步骤过滤 + +在构建前置参数列表时,会过滤掉特殊步骤: + +- **起始步骤**: 由 `flow.basic_config.startStep` 标识 +- **结束步骤**: 由 `flow.basic_config.endStep` 标识 +- **当前步骤**: 队列的第一个元素(索引0)即为当前步骤自身 + +### 8.5 操作符扩展性 + +操作符系统支持灵活扩展: + +- 通过枚举类定义新的操作符 +- 通过 ConditionHandler 配置操作符的绑定类型 +- 类型系统与操作符系统解耦,便于独立维护 + +## 9. 错误处理 + +异常场景: + +| 场景 | HTTP状态码 | 处理方式 | +|-----|-----------|---------| +| 用户无应用访问权限 | 403 | 返回空结果列表和提示信息 | +| 流程不存在 | 404 | 返回空结果列表和提示信息 | +| 节点ID不存在 | 200 | 记录错误日志,跳过该步骤,继续处理 | +| Schema解析失败 | 200 | 记录异常日志,跳过该步骤,继续处理 | +| 参数类型不匹配 | 200 | 返回空操作符列表 | + +## 10. 依赖关系 + +### 10.1 上游依赖 + +```mermaid +graph TD + Parameter[ParameterManager] --> FlowManager[FlowManager] + Parameter --> NodeManager[NodeManager] + Parameter --> ConditionHandler[ConditionHandler] + Parameter --> Slot[Slot处理器] + Parameter --> AppCenterManager[AppCenterManager] + + FlowManager --> Database[(PostgreSQL)] + NodeManager --> Database + AppCenterManager --> Database + + style Parameter fill:#e7f5e1 + style Database fill:#f5e1e1 +``` + +**依赖说明:** + +- **FlowManager**: 提供流程查询功能 +- **NodeManager**: 提供节点参数Schema查询 +- **ConditionHandler**: 提供操作符类型映射 +- **Slot**: 提供Schema解析和参数树构建 +- **AppCenterManager**: 提供应用权限验证 + +### 10.2 下游使用场景 + +Parameter 模块主要服务于以下场景: + +- **Choice节点配置**: 在配置条件分支时选择前置步骤的输出参数 +- **参数绑定**: 在配置节点输入参数时引用前置步骤的输出 +- **条件判断编辑器**: UI组件根据参数类型显示支持的操作符 +- **表达式验证**: 验证用户输入的条件表达式是否符合类型规则 + +## 11. 使用示例 + +### 11.1 获取前置参数的典型场景 + +假设有以下流程: + +```text +[开始] -> [API调用] -> [数据转换] -> [条件判断] -> [结束] +``` + +当用户在"条件判断"步骤配置条件时: + +1. 前端调用 `GET /api/parameter?stepId=条件判断ID` +2. 后端通过BFS遍历得到前置步骤: `[数据转换, API调用]` +3. 获取"数据转换"的输出Schema并转换为参数树 +4. 获取"API调用"的输出Schema并转换为参数树 +5. 返回两个StepParams对象,供前端渲染参数选择器 + +### 11.2 操作符选择的典型场景 + +用户选择了一个STRING类型的参数后: + +1. 前端调用 `GET /api/parameter/operate?paramType=STRING` +2. 后端返回12种字符串操作符及其绑定类型 +3. 前端渲染操作符下拉列表,如"等于"、"包含"、"匹配正则"等 +4. 用户选择"长度等于"操作符时,前端根据绑定类型(NUMBER)显示数字输入框 diff --git a/design/services/user.md b/design/services/user.md index ec646910e89f2a0b6ef62e26e628f8e71e9b53f0..86255d62cd1b5cad35fab2a914cf5d296fe6e908 100644 --- a/design/services/user.md +++ b/design/services/user.md @@ -227,16 +227,6 @@ flowchart TD ## API接口设计 -### 路由配置 - -```python -router = APIRouter( - prefix="/api/user", - tags=["user"], - dependencies=[Depends(verify_session), Depends(verify_personal_token)], -) -``` - ### 接口列表 | 方法 | 路径 | 功能 | 描述 | @@ -327,40 +317,17 @@ graph LR ## 安全机制 -### 1. 身份验证 - -- **会话验证**: 通过`verify_session`依赖验证用户会话 -- **个人令牌**: 通过`verify_personal_token`依赖验证个人访问令牌 - -### 2. 数据隔离 +### 1. 数据隔离 - 用户只能访问和修改自己的数据 - 用户列表接口排除当前用户,避免自我操作 -### 3. 数据完整性 +### 2. 数据完整性 - 用户删除时自动清理相关对话记录 - LLM配置更新时验证模型存在性 - 标签更新时验证标签有效性 -## 性能优化 - -### 1. 数据库优化 - -- 用户标识(userSub)建立唯一索引 -- 分页查询避免一次性加载大量数据 -- 使用数据库事务确保数据一致性 - -### 2. 缓存策略 - -- 用户信息可以考虑缓存,减少数据库查询 -- 标签数据可以定期更新,避免实时计算 - -### 3. 异步处理 - -- 向量化过程异步执行,不阻塞用户请求 -- 使用异步数据库会话提高并发性能 - ## 错误处理 ### 常见错误场景 @@ -380,27 +347,3 @@ graph LR "result": null } ``` - -## 扩展性设计 - -### 1. 模块化设计 - -- 服务层独立,便于单元测试 -- 数据模型与业务逻辑分离 -- 支持插件式扩展 - -### 2. 配置化 - -- 用户权限配置化 -- LLM模型配置化 -- 标签系统可扩展 - -### 3. 监控和日志 - -- 完整的操作日志记录 -- 性能指标监控 -- 错误追踪和告警 - -## 总结 - -User模块作为openEuler Intelligence框架的核心组件,提供了完整的用户管理功能。通过分层架构设计、完善的错误处理机制和良好的扩展性,确保了系统的稳定性和可维护性。模块支持用户信息管理、LLM配置、标签系统等核心功能,为用户提供了个性化的AI助手体验。