├── readify_agi ├── app │ ├── utils │ │ └── __init__.py │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── v1 │ │ │ ├── __init__.py │ │ │ └── text_workflow.py │ ├── services │ │ ├── __init__.py │ │ ├── text_repair_service.py │ │ ├── llama_parse_service.py │ │ ├── file_search_service.py │ │ └── callback_service.py │ ├── core │ │ ├── __init__.py │ │ ├── database.py │ │ └── config.py │ ├── repositories │ │ ├── __init__.py │ │ ├── mind_map_repository.py │ │ ├── base_repository.py │ │ ├── assistant_thinking_repository.py │ │ ├── project_file_repository.py │ │ └── repair_document_repository.py │ ├── models │ │ ├── __init__.py │ │ ├── assistant_thinking.py │ │ ├── project_file.py │ │ ├── project.py │ │ ├── conversation.py │ │ ├── document.py │ │ ├── repair_document.py │ │ ├── file.py │ │ ├── mind_map.py │ │ └── mind_map_node.py │ ├── static │ │ └── README.md │ └── config │ │ └── agent_names.py ├── prompt │ ├── label.prompt │ ├── react.prompt1 │ ├── ask_agent.prompt │ ├── coordinator.prompt │ └── note_agent.prompt ├── requirements.txt ├── environment.yml ├── .env.example ├── .cursor │ └── rules │ │ └── langgpt-helper.mdc └── main.py ├── readify_frontend ├── .env.development ├── .env.production ├── tsconfig.json ├── src │ ├── types │ │ ├── response.ts │ │ ├── project.ts │ │ ├── file.ts │ │ ├── mindmap-node.ts │ │ └── mindmap.ts │ ├── api │ │ ├── file.ts │ │ ├── auth.ts │ │ ├── chat.ts │ │ ├── project.ts │ │ └── mindmap.ts │ ├── vite-env.d.ts │ ├── main.ts │ ├── store │ │ └── index.ts │ ├── assets │ │ └── vue.svg │ ├── utils │ │ ├── auth.ts │ │ └── request.ts │ ├── App.vue │ ├── components │ │ ├── ChatContainer.vue │ │ └── HelloWorld.vue │ ├── router │ │ └── index.ts │ └── views │ │ └── About.vue ├── .gitignore ├── index.html ├── tsconfig.app.json ├── vite.config.ts ├── tsconfig.node.json ├── package.json └── public │ └── vite.svg ├── readify_server ├── .gitattributes ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── readify │ │ │ │ └── server │ │ │ │ ├── websocket │ │ │ │ ├── dto │ │ │ │ │ ├── QueryProjectReq.java │ │ │ │ │ ├── SendMessageReq.java │ │ │ │ │ ├── GenerateNoteRequest.java │ │ │ │ │ └── GenerateNoteResponse.java │ │ │ │ ├── message │ │ │ │ │ └── WebSocketMessage.java │ │ │ │ └── handler │ │ │ │ │ ├── impl │ │ │ │ │ ├── PingMessageHandler.java │ │ │ │ │ ├── BroadcastMessageHandler.java │ │ │ │ │ └── QueryProjectFilesHandler.java │ │ │ │ │ └── WebSocketMessageHandler.java │ │ │ │ ├── domain │ │ │ │ ├── auth │ │ │ │ │ ├── model │ │ │ │ │ │ ├── LoginResult.java │ │ │ │ │ │ ├── AuthUser.java │ │ │ │ │ │ └── ApiKey.java │ │ │ │ │ ├── service │ │ │ │ │ │ ├── AuthService.java │ │ │ │ │ │ ├── ApiKeyService.java │ │ │ │ │ │ └── impl │ │ │ │ │ │ │ └── ApiKeyServiceImpl.java │ │ │ │ │ └── repository │ │ │ │ │ │ ├── AuthUserRepository.java │ │ │ │ │ │ └── ApiKeyRepository.java │ │ │ │ ├── user │ │ │ │ │ ├── model │ │ │ │ │ │ └── User.java │ │ │ │ │ ├── repository │ │ │ │ │ │ └── UserRepository.java │ │ │ │ │ └── service │ │ │ │ │ │ ├── UserService.java │ │ │ │ │ │ └── impl │ │ │ │ │ │ └── UserServiceImpl.java │ │ │ │ ├── project │ │ │ │ │ ├── model │ │ │ │ │ │ ├── Project.java │ │ │ │ │ │ └── ProjectFile.java │ │ │ │ │ ├── repository │ │ │ │ │ │ ├── ProjectFileRepository.java │ │ │ │ │ │ └── ProjectRepository.java │ │ │ │ │ └── service │ │ │ │ │ │ ├── ProjectFileService.java │ │ │ │ │ │ └── ProjectService.java │ │ │ │ ├── mind_map │ │ │ │ │ ├── model │ │ │ │ │ │ ├── MindMap.java │ │ │ │ │ │ ├── MindMapNode.java │ │ │ │ │ │ └── MindMapNodeTree.java │ │ │ │ │ ├── repository │ │ │ │ │ │ ├── MindMapRepository.java │ │ │ │ │ │ └── MindMapNodeRepository.java │ │ │ │ │ └── service │ │ │ │ │ │ └── MindMapService.java │ │ │ │ ├── conversation │ │ │ │ │ ├── service │ │ │ │ │ │ ├── ConversationService.java │ │ │ │ │ │ └── impl │ │ │ │ │ │ │ └── ConversationServiceImpl.java │ │ │ │ │ ├── entity │ │ │ │ │ │ ├── AssistantThinking.java │ │ │ │ │ │ └── ConversationHistory.java │ │ │ │ │ └── repository │ │ │ │ │ │ └── ConversationRepository.java │ │ │ │ ├── file │ │ │ │ │ ├── model │ │ │ │ │ │ └── File.java │ │ │ │ │ ├── service │ │ │ │ │ │ ├── FileService.java │ │ │ │ │ │ └── impl │ │ │ │ │ │ │ └── FileServiceImpl.java │ │ │ │ │ └── repository │ │ │ │ │ │ └── FileRepository.java │ │ │ │ └── notetask │ │ │ │ │ ├── NoteTaskStatus.java │ │ │ │ │ ├── model │ │ │ │ │ └── NoteTask.java │ │ │ │ │ ├── repository │ │ │ │ │ └── NoteTaskRepository.java │ │ │ │ │ └── NoteTaskService.java │ │ │ │ ├── interfaces │ │ │ │ ├── file │ │ │ │ │ ├── req │ │ │ │ │ │ └── VectorizedCallbackReq.java │ │ │ │ │ └── vo │ │ │ │ │ │ └── FileVO.java │ │ │ │ ├── auth │ │ │ │ │ ├── req │ │ │ │ │ │ ├── CreateApiKeyReq.java │ │ │ │ │ │ ├── LoginReq.java │ │ │ │ │ │ └── RegisterReq.java │ │ │ │ │ ├── converter │ │ │ │ │ │ └── AuthVOConverter.java │ │ │ │ │ ├── vo │ │ │ │ │ │ ├── LoginVO.java │ │ │ │ │ │ └── ApiKeyVO.java │ │ │ │ │ └── ApiKeyController.java │ │ │ │ ├── conversation │ │ │ │ │ ├── converter │ │ │ │ │ │ ├── AssistantThinkingVOConverter.java │ │ │ │ │ │ └── ConversationVOConverter.java │ │ │ │ │ ├── vo │ │ │ │ │ │ ├── AssistantThinkingVO.java │ │ │ │ │ │ └── ConversationVO.java │ │ │ │ │ └── ConversationController.java │ │ │ │ ├── mind_map │ │ │ │ │ ├── converter │ │ │ │ │ │ ├── MindMapNodeTreeVOConverter.java │ │ │ │ │ │ └── MindMapVOConverter.java │ │ │ │ │ ├── vo │ │ │ │ │ │ ├── MindMapVO.java │ │ │ │ │ │ └── MindMapNodeTreeVO.java │ │ │ │ │ └── MindMapNodeController.java │ │ │ │ ├── user │ │ │ │ │ ├── converter │ │ │ │ │ │ └── UserVOConverter.java │ │ │ │ │ ├── vo │ │ │ │ │ │ └── UserVO.java │ │ │ │ │ └── UserController.java │ │ │ │ └── project │ │ │ │ │ ├── converter │ │ │ │ │ └── ProjectVOConverter.java │ │ │ │ │ └── vo │ │ │ │ │ └── ProjectVO.java │ │ │ │ ├── infrastructure │ │ │ │ ├── common │ │ │ │ │ ├── exception │ │ │ │ │ │ ├── NotFoundException.java │ │ │ │ │ │ ├── ForbiddenException.java │ │ │ │ │ │ ├── UnauthorizedException.java │ │ │ │ │ │ └── BusinessException.java │ │ │ │ │ └── Result.java │ │ │ │ ├── persistence │ │ │ │ │ ├── mapper │ │ │ │ │ │ ├── FileMapper.java │ │ │ │ │ │ ├── UserMapper.java │ │ │ │ │ │ ├── ApiKeyMapper.java │ │ │ │ │ │ ├── MindMapMapper.java │ │ │ │ │ │ ├── ProjectMapper.java │ │ │ │ │ │ ├── MindMapNodeMapper.java │ │ │ │ │ │ ├── ProjectFileMapper.java │ │ │ │ │ │ ├── AssistantThinkingMapper.java │ │ │ │ │ │ ├── ConversationHistoryMapper.java │ │ │ │ │ │ └── NoteTaskMapper.java │ │ │ │ │ ├── converter │ │ │ │ │ │ ├── FileConverter.java │ │ │ │ │ │ ├── UserConverter.java │ │ │ │ │ │ ├── MindMapConverter.java │ │ │ │ │ │ ├── ProjectConverter.java │ │ │ │ │ │ ├── ProjectFileConverter.java │ │ │ │ │ │ ├── MindMapNodeConverter.java │ │ │ │ │ │ └── ConversationConverter.java │ │ │ │ │ ├── entity │ │ │ │ │ │ ├── AssistantThinkingEntity.java │ │ │ │ │ │ ├── ApiKeyEntity.java │ │ │ │ │ │ ├── ConversationHistoryEntity.java │ │ │ │ │ │ ├── ProjectEntity.java │ │ │ │ │ │ ├── UserEntity.java │ │ │ │ │ │ ├── ProjectFileEntity.java │ │ │ │ │ │ ├── FileEntity.java │ │ │ │ │ │ ├── MindMapEntity.java │ │ │ │ │ │ ├── MindMapNodeEntity.java │ │ │ │ │ │ └── NoteTaskEntity.java │ │ │ │ │ └── repository │ │ │ │ │ │ ├── ProjectFileRepositoryImpl.java │ │ │ │ │ │ ├── UserRepositoryImpl.java │ │ │ │ │ │ └── AuthUserRepositoryImpl.java │ │ │ │ ├── utils │ │ │ │ │ └── file │ │ │ │ │ │ ├── FileStorage.java │ │ │ │ │ │ └── LocalFileStorage.java │ │ │ │ └── security │ │ │ │ │ ├── UserInfo.java │ │ │ │ │ └── SecurityUtils.java │ │ │ │ ├── ReadifyServerApplication.java │ │ │ │ └── config │ │ │ │ ├── SwaggerConfig.java │ │ │ │ ├── WebClientConfig.java │ │ │ │ └── WebSocketConfig.java │ │ └── resources │ │ │ └── application.yml │ └── test │ │ └── java │ │ └── com │ │ └── readify │ │ └── ReadifyServerApplicationTests.java ├── start.sh ├── start.bat └── .cursor │ └── rules │ ├── langgpt-helper.mdc │ └── developer.mdc ├── img ├── 1-首页.png ├── 4-笔记.png ├── 2-对话-1.png ├── 3-对话-2.png ├── 5-笔记生成.png └── 6-笔记生成.png ├── LICENSE └── .gitignore /readify_agi/app/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /readify_frontend/.env.development: -------------------------------------------------------------------------------- 1 | VITE_API_BASE_URL = '/api' -------------------------------------------------------------------------------- /readify_agi/app/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Readify AGI Application Package 3 | """ -------------------------------------------------------------------------------- /readify_agi/app/api/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | API Package 3 | 包含所有的API路由处理器 4 | """ -------------------------------------------------------------------------------- /readify_server/.gitattributes: -------------------------------------------------------------------------------- 1 | /mvnw text eol=lf 2 | *.cmd text eol=crlf 3 | -------------------------------------------------------------------------------- /readify_agi/app/services/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Service Package 3 | 包含业务逻辑层的实现 4 | """ -------------------------------------------------------------------------------- /readify_frontend/.env.production: -------------------------------------------------------------------------------- 1 | VITE_API_BASE_URL = 'https://api.yourdomain.com' -------------------------------------------------------------------------------- /img/1-首页.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaosl-cell/readify_parent/HEAD/img/1-首页.png -------------------------------------------------------------------------------- /img/4-笔记.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaosl-cell/readify_parent/HEAD/img/4-笔记.png -------------------------------------------------------------------------------- /readify_agi/app/core/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Domain Models Package 3 | 包含所有的领域模型/实体类 4 | """ -------------------------------------------------------------------------------- /readify_agi/prompt/label.prompt: -------------------------------------------------------------------------------- 1 | 生成描述该文本的标签,概括该文本主要描述的内容,不超过50个字: 2 | 3 | 文本:{content} -------------------------------------------------------------------------------- /img/2-对话-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaosl-cell/readify_parent/HEAD/img/2-对话-1.png -------------------------------------------------------------------------------- /img/3-对话-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaosl-cell/readify_parent/HEAD/img/3-对话-2.png -------------------------------------------------------------------------------- /img/5-笔记生成.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaosl-cell/readify_parent/HEAD/img/5-笔记生成.png -------------------------------------------------------------------------------- /img/6-笔记生成.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaosl-cell/readify_parent/HEAD/img/6-笔记生成.png -------------------------------------------------------------------------------- /readify_agi/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaosl-cell/readify_parent/HEAD/readify_agi/requirements.txt -------------------------------------------------------------------------------- /readify_frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /readify_frontend/src/types/response.ts: -------------------------------------------------------------------------------- 1 | // 通用返回结果接口 2 | export interface ApiResponse { 3 | code: string; // 状态码 4 | message: string; // 提示信息 5 | data: T; // 返回数据 6 | } -------------------------------------------------------------------------------- /readify_frontend/src/types/project.ts: -------------------------------------------------------------------------------- 1 | export interface ProjectVO { 2 | id: number 3 | userId: number 4 | name: string 5 | description?: string 6 | createTime: number 7 | updateTime: number 8 | } -------------------------------------------------------------------------------- /readify_agi/app/api/v1/__init__.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | from app.api.v1 import agent_router 3 | 4 | api_router = APIRouter() 5 | api_router.include_router(agent_router.router, prefix="/agent", tags=["agent"]) -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/dto/QueryProjectReq.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class QueryProjectReq { 7 | private Long projectId; 8 | } 9 | -------------------------------------------------------------------------------- /readify_frontend/src/api/file.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import type { ResultListFileVO } from '@/types/file' 3 | 4 | export const getProjectFiles = (projectId: number) => { 5 | return request({ 6 | url: `/projects/${projectId}/files`, 7 | method: 'get' 8 | }) 9 | } -------------------------------------------------------------------------------- /readify_frontend/src/types/file.ts: -------------------------------------------------------------------------------- 1 | export interface FileVO { 2 | id: number 3 | originalName: string 4 | mimeType: string 5 | size: number 6 | createTime: number 7 | updateTime: number 8 | } 9 | 10 | export interface ResultListFileVO { 11 | code: string 12 | message: string 13 | data: FileVO[] | null 14 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/auth/model/LoginResult.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.auth.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class LoginResult { 9 | private AuthUser user; 10 | private String token; 11 | } -------------------------------------------------------------------------------- /readify_server/src/test/java/com/readify/ReadifyServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.readify; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ReadifyServerApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/user/model/User.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.user.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class User { 7 | private Long id; 8 | private String username; 9 | private Boolean enabled; 10 | private Long createTime; 11 | private Long updateTime; 12 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/file/req/VectorizedCallbackReq.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.file.req; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class VectorizedCallbackReq { 7 | private Long fileId; 8 | private Boolean success; 9 | private String message; 10 | private Long timestamp; 11 | } 12 | -------------------------------------------------------------------------------- /readify_frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/auth/model/AuthUser.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.auth.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class AuthUser { 7 | private Long id; 8 | private String username; 9 | private String password; 10 | private Boolean enabled; 11 | private Long createTime; 12 | private Long updateTime; 13 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/project/model/Project.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.project.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Project { 7 | private Long id; 8 | private Long userId; 9 | private String name; 10 | private String description; 11 | private Long createTime; 12 | private Long updateTime; 13 | } -------------------------------------------------------------------------------- /readify_agi/app/repositories/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Repository Package 3 | 包含所有的仓储层实现 4 | """ 5 | 6 | # 导出 BaseRepository 供其他模块使用 7 | from app.repositories.base_repository import BaseRepository 8 | 9 | # 不要在这里导入具体的仓库实现类,以避免循环导入问题 10 | # 例如不要导入: 11 | # from app.repositories.conversation_repository import ConversationRepository 12 | # from app.repositories.file_repository import FileRepository 13 | # 等等... -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/common/exception/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.common.exception; 2 | 3 | public class NotFoundException extends BusinessException { 4 | public NotFoundException(String message) { 5 | super("404", message); 6 | } 7 | 8 | public NotFoundException() { 9 | this("资源未找到"); 10 | } 11 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/common/exception/ForbiddenException.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.common.exception; 2 | 3 | public class ForbiddenException extends BusinessException { 4 | public ForbiddenException(String message) { 5 | super("403", message); 6 | } 7 | 8 | public ForbiddenException() { 9 | this("禁止访问"); 10 | } 11 | } -------------------------------------------------------------------------------- /readify_frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // 声明Markmap全局类型 4 | interface Window { 5 | d3: any; 6 | minimalD3: any; 7 | markmap: { 8 | Markmap: any; 9 | Transformer: any; 10 | loadCSS?: any; 11 | loadJS?: any; 12 | }; 13 | Markmap?: any; 14 | Transformer?: any; 15 | minimalMarkmap: { 16 | Markmap: any; 17 | Transformer: any; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/FileMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.FileEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface FileMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.UserEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface UserMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/common/exception/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.common.exception; 2 | 3 | public class UnauthorizedException extends BusinessException { 4 | public UnauthorizedException(String message) { 5 | super("401", message); 6 | } 7 | 8 | public UnauthorizedException() { 9 | this("未授权的访问"); 10 | } 11 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/ApiKeyMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.ApiKeyEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface ApiKeyMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import ElementPlus from 'element-plus' 3 | import 'element-plus/dist/index.css' 4 | import './style.css' 5 | import './select-override.css' 6 | import App from './App.vue' 7 | import router from './router' 8 | import store from './store' 9 | 10 | const app = createApp(App) 11 | 12 | app.use(ElementPlus) 13 | app.use(router) 14 | app.use(store) 15 | app.mount('#app') 16 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/MindMapMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.MindMapEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface MindMapMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/ProjectMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.ProjectEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface ProjectMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/auth/model/ApiKey.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.auth.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ApiKey { 7 | private Long id; 8 | private String name; 9 | private String apiKey; 10 | private String description; 11 | private Long userId; 12 | private Boolean enabled; 13 | private Long createTime; 14 | private Long updateTime; 15 | } -------------------------------------------------------------------------------- /readify_frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Readify Notebook Assistant 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/MindMapNodeMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.MindMapNodeEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface MindMapNodeMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/ProjectFileMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.ProjectFileEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface ProjectFileMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/utils/file/FileStorage.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.utils.file; 2 | 3 | import java.io.InputStream; 4 | import java.nio.file.Path; 5 | 6 | public interface FileStorage { 7 | void store(String storageName, InputStream inputStream); 8 | InputStream retrieve(String storageName); 9 | void delete(String storageName); 10 | Path getStoragePath(String storageName); 11 | } -------------------------------------------------------------------------------- /readify_agi/app/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Domain Models Package 3 | 包含所有的领域模型/实体类 4 | """ 5 | 6 | from app.models.assistant_thinking import AssistantThinkingDB 7 | from app.models.conversation import ConversationHistoryDB 8 | from app.models.document import DocumentDB 9 | from app.models.file import FileDB 10 | from app.models.repair_document import RepairDocumentDB 11 | from app.models.project_file import ProjectFileDB 12 | from app.models.project import ProjectDB -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/project/model/ProjectFile.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.project.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder 8 | public class ProjectFile { 9 | private Long id; 10 | private Long projectId; 11 | private Long userId; 12 | private Long fileId; 13 | private Long createTime; 14 | private Long updateTime; 15 | private Boolean deleted; 16 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/auth/req/CreateApiKeyReq.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.auth.req; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Schema(description = "创建API Key请求") 8 | public class CreateApiKeyReq { 9 | @Schema(description = "API Key名称") 10 | private String name; 11 | 12 | @Schema(description = "API Key描述") 13 | private String description; 14 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/auth/req/LoginReq.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.auth.req; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Schema(description = "登录请求") 8 | public class LoginReq { 9 | 10 | @Schema(description = "用户名", example = "john.doe") 11 | private String username; 12 | 13 | @Schema(description = "密码") 14 | private String password; 15 | } 16 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/AssistantThinkingMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.AssistantThinkingEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface AssistantThinkingMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/auth/req/RegisterReq.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.auth.req; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Schema(description = "注册请求") 8 | public class RegisterReq { 9 | 10 | @Schema(description = "用户名", example = "john.doe") 11 | private String username; 12 | 13 | @Schema(description = "密码") 14 | private String password; 15 | } 16 | -------------------------------------------------------------------------------- /readify_frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "compilerOptions": { 4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 5 | 6 | /* Linting */ 7 | "strict": true, 8 | "noUnusedLocals": true, 9 | "noUnusedParameters": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "noUncheckedSideEffectImports": true 12 | }, 13 | "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] 14 | } 15 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/ConversationHistoryMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.ConversationHistoryEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface ConversationHistoryMapper extends BaseMapper { 9 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/security/UserInfo.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.security; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class UserInfo { 11 | /** 12 | * 用户ID 13 | */ 14 | private Long id; 15 | 16 | /** 17 | * 用户名 18 | */ 19 | private String username; 20 | } -------------------------------------------------------------------------------- /readify_frontend/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from 'vuex' 2 | 3 | export default createStore({ 4 | state: { 5 | user: null 6 | }, 7 | mutations: { 8 | setUser(state, user) { 9 | state.user = user 10 | } 11 | }, 12 | actions: { 13 | login({ commit }, user) { 14 | commit('setUser', user) 15 | }, 16 | logout({ commit }) { 17 | commit('setUser', null) 18 | } 19 | }, 20 | getters: { 21 | isLoggedIn: state => !!state.user 22 | } 23 | }) -------------------------------------------------------------------------------- /readify_frontend/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /readify_frontend/src/utils/auth.ts: -------------------------------------------------------------------------------- 1 | const TOKEN_KEY = 'readify_token' 2 | 3 | export const getToken = (): string => { 4 | return localStorage.getItem(TOKEN_KEY) || '' 5 | } 6 | 7 | export const setToken = (token: string): void => { 8 | localStorage.setItem(TOKEN_KEY, token) 9 | } 10 | 11 | export const removeToken = (): void => { 12 | localStorage.removeItem(TOKEN_KEY) 13 | } 14 | 15 | export const getAuthHeader = (): string => { 16 | const token = getToken() 17 | return token ? `Bearer ${token}` : '' 18 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/mind_map/model/MindMap.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.mind_map.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class MindMap { 7 | private Long id; 8 | private Long projectId; 9 | private Long fileId; 10 | private String title; 11 | private String type; 12 | private String description; 13 | private Long userId; 14 | private Long createdAt; 15 | private Long updatedAt; 16 | private Boolean isDeleted; 17 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/conversation/service/ConversationService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.conversation.service; 2 | 3 | import com.readify.server.interfaces.conversation.vo.ConversationVO; 4 | 5 | import java.util.List; 6 | 7 | public interface ConversationService { 8 | /** 9 | * 根据项目ID获取对话历史列表,关联用户消息的思考过程 10 | * 11 | * @param projectId 项目ID 12 | * @return 对话历史视图对象列表 13 | */ 14 | List getConversationsByProjectId(Long projectId); 15 | } -------------------------------------------------------------------------------- /readify_frontend/src/types/mindmap-node.ts: -------------------------------------------------------------------------------- 1 | // 思维导图节点树形结构 2 | export interface MindMapNodeTreeVO { 3 | id: number // 节点ID 4 | projectId: number // 项目ID 5 | fileId: number // 文件ID 6 | mindMapId: number // 思维导图ID 7 | parentId: number | null // 父节点ID,根节点为null 8 | content: string // 节点内容 9 | sequence: number // 排序序号 10 | level: number // 节点层级,根节点为0 11 | createdTime: number // 创建时间 12 | updatedTime: number // 更新时间 13 | children: MindMapNodeTreeVO[] // 子节点列表 14 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/mind_map/model/MindMapNode.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.mind_map.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class MindMapNode { 7 | private Long id; 8 | private Long projectId; 9 | private Long fileId; 10 | private Long mindMapId; 11 | private Long parentId; 12 | private String content; 13 | private Integer sequence; 14 | private Integer level; 15 | private Long createdTime; 16 | private Long updatedTime; 17 | private Boolean deleted; 18 | } -------------------------------------------------------------------------------- /readify_frontend/src/api/auth.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export interface LoginData { 4 | username: string 5 | password: string 6 | } 7 | 8 | export interface RegisterData extends LoginData { 9 | email: string 10 | } 11 | 12 | export const login = (data: LoginData) => { 13 | return request({ 14 | url: '/auth/login', 15 | method: 'post', 16 | data 17 | }) 18 | } 19 | 20 | export const register = (data: RegisterData) => { 21 | return request({ 22 | url: '/auth/register', 23 | method: 'post', 24 | data 25 | }) 26 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/dto/SendMessageReq.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.dto; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class SendMessageReq { 7 | /** 8 | * 项目id 9 | */ 10 | private Long projectId; 11 | /** 12 | * 任务模式 13 | */ 14 | private String taskType; 15 | /** 16 | * 思维导图id 17 | */ 18 | private Long mindMapId = null; 19 | /** 20 | * 厂商 21 | */ 22 | private String vendor; 23 | /** 24 | * 用户输入 25 | */ 26 | private String query; 27 | } -------------------------------------------------------------------------------- /readify_frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import path from 'path' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | resolve: { 9 | alias: { 10 | '@': path.resolve(__dirname, 'src') 11 | } 12 | }, 13 | server: { 14 | port: 3000, 15 | proxy: { 16 | '/api': { 17 | target: 'http://localhost:8080', 18 | changeOrigin: true, 19 | rewrite: (path) => path.replace(/^\/api/, '/api/v1') 20 | } 21 | } 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/converter/FileConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.converter; 2 | 3 | import com.readify.server.domain.file.model.File; 4 | import com.readify.server.infrastructure.persistence.entity.FileEntity; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | @Mapper 9 | public interface FileConverter { 10 | FileConverter INSTANCE = Mappers.getMapper(FileConverter.class); 11 | 12 | FileEntity toEntity(File file); 13 | 14 | File toDomain(FileEntity entity); 15 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/converter/UserConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.converter; 2 | 3 | import com.readify.server.domain.user.model.User; 4 | import com.readify.server.infrastructure.persistence.entity.UserEntity; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | @Mapper 9 | public interface UserConverter { 10 | UserConverter INSTANCE = Mappers.getMapper(UserConverter.class); 11 | 12 | UserEntity toEntity(User user); 13 | 14 | User toDomain(UserEntity entity); 15 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/auth/converter/AuthVOConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.auth.converter; 2 | 3 | import com.readify.server.domain.auth.model.AuthUser; 4 | import com.readify.server.interfaces.auth.vo.LoginVO; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.MappingTarget; 7 | import org.mapstruct.factory.Mappers; 8 | 9 | @Mapper 10 | public interface AuthVOConverter { 11 | AuthVOConverter INSTANCE = Mappers.getMapper(AuthVOConverter.class); 12 | 13 | void updateLoginVO(AuthUser user, @MappingTarget LoginVO loginVO); 14 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/auth/vo/LoginVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.auth.vo; 2 | 3 | import com.readify.server.interfaces.user.vo.UserVO; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | @Data 9 | @Schema(description = "登录响应") 10 | @EqualsAndHashCode(callSuper = true) 11 | public class LoginVO extends UserVO { 12 | 13 | @Schema(description = "访问令牌", example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", accessMode = Schema.AccessMode.READ_ONLY) 14 | private String token; 15 | } 16 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/common/exception/BusinessException.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.common.exception; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class BusinessException extends RuntimeException { 7 | private final String code; 8 | private final String message; 9 | 10 | public BusinessException(String code, String message) { 11 | super(message); 12 | this.code = code; 13 | this.message = message; 14 | } 15 | 16 | public BusinessException(String message) { 17 | this("500", message); 18 | } 19 | } -------------------------------------------------------------------------------- /readify_server/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "===================================" 4 | echo "Readify Server Startup Script" 5 | echo "===================================" 6 | 7 | # 设置Java环境变量(如果需要) 8 | # export JAVA_HOME=/usr/lib/jvm/java-17-openjdk 9 | # export PATH=$JAVA_HOME/bin:$PATH 10 | 11 | echo "[1/3] Cleaning previous build..." 12 | # 清理之前的构建 13 | mvn clean 14 | 15 | echo "[2/3] Building project..." 16 | # 编译打包项目 17 | mvn package -DskipTests 18 | 19 | echo "[3/3] Starting Readify Server..." 20 | echo "===================================" 21 | # 运行Spring Boot应用 22 | java -jar target/readify-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /readify_server/start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo =================================== 3 | echo Readify Server Startup Script 4 | echo =================================== 5 | 6 | :: 设置Java环境变量(如果需要) 7 | :: set JAVA_HOME=C:\Program Files\Java\jdk-17 8 | :: set PATH=%JAVA_HOME%\bin;%PATH% 9 | 10 | echo [1/3] Cleaning previous build... 11 | :: 清理之前的构建 12 | call mvn clean 13 | 14 | echo [2/3] Building project... 15 | :: 编译打包项目 16 | call mvn package -DskipTests 17 | 18 | echo [3/3] Starting Readify Server... 19 | echo =================================== 20 | :: 运行Spring Boot应用 21 | java -jar target\readify-0.0.1-SNAPSHOT.jar 22 | 23 | pause -------------------------------------------------------------------------------- /readify_frontend/src/types/mindmap.ts: -------------------------------------------------------------------------------- 1 | // 思维导图视图对象 2 | export interface MindMapVO { 3 | id: number // 思维导图ID 4 | projectId: number // 工程ID 5 | fileId: number // 文件ID 6 | title: string // 思维导图标题 7 | type: string // 笔记类型 8 | description: string // 思维导图描述 9 | userId: number // 用户ID 10 | createdAt: number // 创建时间 11 | updatedAt: number // 更新时间 12 | } 13 | 14 | // 创建思维导图请求参数 15 | export interface CreateMindMapParams { 16 | projectId: number 17 | fileId: number 18 | title: string 19 | type: string 20 | description?: string 21 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/converter/MindMapConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.converter; 2 | 3 | import com.readify.server.domain.mind_map.model.MindMap; 4 | import com.readify.server.infrastructure.persistence.entity.MindMapEntity; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | @Mapper 9 | public interface MindMapConverter { 10 | MindMapConverter INSTANCE = Mappers.getMapper(MindMapConverter.class); 11 | 12 | MindMapEntity toEntity(MindMap mindMap); 13 | 14 | MindMap toDomain(MindMapEntity entity); 15 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/converter/ProjectConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.converter; 2 | 3 | import com.readify.server.domain.project.model.Project; 4 | import com.readify.server.infrastructure.persistence.entity.ProjectEntity; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | @Mapper 9 | public interface ProjectConverter { 10 | ProjectConverter INSTANCE = Mappers.getMapper(ProjectConverter.class); 11 | 12 | ProjectEntity toEntity(Project project); 13 | 14 | Project toDomain(ProjectEntity entity); 15 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/ReadifyServerApplication.java: -------------------------------------------------------------------------------- 1 | package com.readify.server; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.mybatis.spring.annotation.MapperScan; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | @SpringBootApplication 9 | @MapperScan("com.readify.server.infrastructure.persistence.mapper") 10 | public class ReadifyServerApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(ReadifyServerApplication.class, args); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/conversation/entity/AssistantThinking.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.conversation.entity; 2 | 3 | import lombok.Data; 4 | import lombok.Builder; 5 | import lombok.NoArgsConstructor; 6 | import lombok.AllArgsConstructor; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Data 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class AssistantThinking { 15 | private Long id; 16 | private Long projectId; 17 | private Long userMessageId; 18 | private String content; 19 | private LocalDateTime createdAt; 20 | private LocalDateTime updatedAt; 21 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/dto/GenerateNoteRequest.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * 生成笔记请求DTO 10 | */ 11 | @Data 12 | @Builder 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class GenerateNoteRequest { 16 | 17 | /** 18 | * 项目ID 19 | */ 20 | private Long projectId; 21 | 22 | /** 23 | * 思维导图ID 24 | */ 25 | private Long mindMapId; 26 | 27 | /** 28 | * 用户指令内容 29 | */ 30 | private String query; 31 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/converter/ProjectFileConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.converter; 2 | 3 | import com.readify.server.domain.project.model.ProjectFile; 4 | import com.readify.server.infrastructure.persistence.entity.ProjectFileEntity; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | @Mapper 9 | public interface ProjectFileConverter { 10 | ProjectFileConverter INSTANCE = Mappers.getMapper(ProjectFileConverter.class); 11 | 12 | ProjectFileEntity toEntity(ProjectFile projectFile); 13 | 14 | ProjectFile toDomain(ProjectFileEntity entity); 15 | } -------------------------------------------------------------------------------- /readify_frontend/src/api/chat.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 发送聊天消息 4 | export function sendChatMessage(data: { query: string; projectId: number }) { 5 | return request({ 6 | url: '/chat/message', 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | 12 | // 获取聊天历史 (旧方法,保留以兼容现有代码) 13 | export function getChatHistory(projectId: number) { 14 | return request({ 15 | url: `/chat/history/${projectId}`, 16 | method: 'get' 17 | }) 18 | } 19 | 20 | // 获取项目对话记录,按照新API规范 21 | export function getProjectConversations(projectId: number) { 22 | return request({ 23 | url: `/conversation/project/${projectId}`, 24 | method: 'get' 25 | }) 26 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/project/repository/ProjectFileRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.project.repository; 2 | 3 | import com.readify.server.domain.project.model.ProjectFile; 4 | 5 | import java.util.List; 6 | 7 | public interface ProjectFileRepository { 8 | /** 9 | * 保存项目文件关联信息 10 | * 11 | * @param projectFile 项目文件关联信息 12 | * @return 保存后的项目文件关联信息 13 | */ 14 | ProjectFile save(ProjectFile projectFile); 15 | 16 | /** 17 | * 根据项目ID查询项目文件关联信息 18 | * 19 | * @param projectId 项目ID 20 | * @return 项目文件关联信息列表 21 | */ 22 | List findByProjectId(Long projectId); 23 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/conversation/converter/AssistantThinkingVOConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.conversation.converter; 2 | 3 | import com.readify.server.domain.conversation.entity.AssistantThinking; 4 | import com.readify.server.interfaces.conversation.vo.AssistantThinkingVO; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | @Mapper(componentModel = "spring") 9 | public interface AssistantThinkingVOConverter { 10 | 11 | AssistantThinkingVOConverter INSTANCE = Mappers.getMapper(AssistantThinkingVOConverter.class); 12 | 13 | AssistantThinkingVO toDTO(AssistantThinking thinking); 14 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/mind_map/converter/MindMapNodeTreeVOConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.mind_map.converter; 2 | 3 | import com.readify.server.domain.mind_map.model.MindMapNodeTree; 4 | import com.readify.server.interfaces.mind_map.vo.MindMapNodeTreeVO; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | @Mapper 9 | public interface MindMapNodeTreeVOConverter { 10 | MindMapNodeTreeVOConverter INSTANCE = Mappers.getMapper(MindMapNodeTreeVOConverter.class); 11 | 12 | MindMapNodeTreeVO toVO(MindMapNodeTree tree); 13 | 14 | MindMapNodeTree toDomain(MindMapNodeTreeVO vo); 15 | } -------------------------------------------------------------------------------- /readify_agi/app/api/v1/text_workflow.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends 2 | from typing import Dict, Any, List 3 | from pydantic import BaseModel 4 | 5 | from app.services.text_workflow_service import TextWorkflowService 6 | 7 | router = APIRouter() 8 | 9 | class TextRequest(BaseModel): 10 | text: str 11 | 12 | 13 | @router.post("/process", response_model=Dict[str, Any]) 14 | async def process_text( 15 | request: TextRequest, 16 | service: TextWorkflowService = Depends(lambda: TextWorkflowService()) 17 | ) -> Dict[str, Any]: 18 | """ 19 | 处理文本的完整工作流,包括: 20 | 1. 检查格式问题 21 | 2. 修复格式 22 | 3. 语义分段 23 | """ 24 | return await service.text_repair(request.text) -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/file/model/File.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.file.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | @Getter 8 | @Setter 9 | @Builder 10 | public class File { 11 | private Long id; 12 | private String originalName; 13 | private String storageName; 14 | private Long size; 15 | private String mimeType; 16 | private String storagePath; 17 | private String md5; 18 | private Long createTime; 19 | private Long updateTime; 20 | private Boolean deleted; 21 | private Boolean vectorized; 22 | private Long projectId; 23 | private Long userId; 24 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/auth/service/AuthService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.auth.service; 2 | 3 | import com.readify.server.domain.auth.model.AuthUser; 4 | import com.readify.server.domain.auth.model.LoginResult; 5 | 6 | public interface AuthService { 7 | /** 8 | * 用户注册 9 | * 10 | * @param username 用户名 11 | * @param password 密码 12 | * @return 注册成功的用户信息 13 | */ 14 | AuthUser register(String username, String password); 15 | 16 | /** 17 | * 用户登录 18 | * 19 | * @param username 用户名 20 | * @param password 密码 21 | * @return 登录成功的用户信息和token 22 | */ 23 | LoginResult login(String username, String password); 24 | } -------------------------------------------------------------------------------- /readify_agi/app/services/text_repair_service.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from app.services.text_workflow_service import TextWorkflowService 3 | 4 | class TextRepairService: 5 | """文本修复服务""" 6 | 7 | def __init__(self): 8 | self.workflow_service = TextWorkflowService() 9 | 10 | async def repair_text(self, text: str) -> List[str]: 11 | """ 12 | 修复文本内容 13 | 14 | Args: 15 | text: 需要修复的文本内容 16 | 17 | Returns: 18 | str: 修复后的文本内容 19 | """ 20 | # 调用工作流服务进行文本修复 21 | result = await self.workflow_service.text_repair(text) 22 | 23 | # 返回修复后的文本 24 | return result.get("paragraphs") 25 | -------------------------------------------------------------------------------- /readify_frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedSideEffectImports": true 22 | }, 23 | "include": ["vite.config.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/project/service/ProjectFileService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.project.service; 2 | 3 | import com.readify.server.domain.file.model.File; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | import java.util.List; 7 | 8 | public interface ProjectFileService { 9 | /** 10 | * 上传文件并关联到项目 11 | * 12 | * @param projectId 项目ID 13 | * @param file 上传的文件 14 | * @return 文件信息 15 | */ 16 | File uploadAndAssociateFile(Long projectId, MultipartFile file); 17 | 18 | /** 19 | * 获取项目文件列表 20 | * 21 | * @param projectId 项目ID 22 | * @return 文件列表 23 | */ 24 | List getProjectFiles(Long projectId); 25 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/AssistantThinkingEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Data 11 | @TableName("assistant_thinking") 12 | public class AssistantThinkingEntity { 13 | @TableId(type = IdType.AUTO) 14 | private Long id; 15 | private Long projectId; 16 | private Long userMessageId; 17 | private String content; 18 | private LocalDateTime createdAt; 19 | private LocalDateTime updatedAt; 20 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/user/converter/UserVOConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.user.converter; 2 | 3 | import com.readify.server.domain.user.model.User; 4 | import com.readify.server.interfaces.user.vo.UserVO; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.MappingTarget; 7 | import org.mapstruct.factory.Mappers; 8 | 9 | import java.util.List; 10 | 11 | @Mapper 12 | public interface UserVOConverter { 13 | UserVOConverter INSTANCE = Mappers.getMapper(UserVOConverter.class); 14 | 15 | UserVO toVO(User user); 16 | 17 | User toDomain(UserVO userVO); 18 | 19 | List toVOList(List users); 20 | 21 | void updateUserVO(User user, @MappingTarget UserVO userVO); 22 | } -------------------------------------------------------------------------------- /readify_agi/app/static/README.md: -------------------------------------------------------------------------------- 1 | # Agent Stream API 测试页面 2 | 3 | ## 功能介绍 4 | 5 | 这是一个用于测试 `/api/v1/agent/stream` 接口的HTML测试页面。该页面提供了一个简单的表单界面,可以发送请求到流式API并实时显示响应结果。 6 | 7 | ## 使用方法 8 | 9 | 1. 启动Readify AGI服务器 (默认在 8090 端口) 10 | 2. 访问 http://localhost:8090/static/test_stream.html 11 | 3. 在表单中填写测试参数: 12 | - **工程ID**: 输入需要测试的工程ID 13 | - **模型厂商**: 选择使用的AI模型厂商 (OpenAI或Anthropic) 14 | - **任务类型**: 选择任务类型 (ask或note) 15 | - **上下文**: 输入额外的上下文信息 (JSON格式) 16 | - **用户问题**: 输入需要测试的用户查询内容 17 | 4. 点击"发送请求"按钮开始测试 18 | 5. 实时响应将显示在下方的响应区域 19 | 20 | ## 响应说明 21 | 22 | 不同类型的消息会以不同的颜色显示: 23 | - **用户消息**: 蓝色背景 24 | - **系统消息**: 红色背景 25 | - **思考过程**: 黄色背景,斜体 26 | - **AI回答**: 绿色背景 27 | 28 | ## 注意事项 29 | 30 | - 确保API服务正常运行 31 | - 上下文必须是有效的JSON格式 32 | - 工程ID和用户问题是必填项 -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/conversation/entity/ConversationHistory.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.conversation.entity; 2 | 3 | import lombok.Data; 4 | import lombok.Builder; 5 | import lombok.NoArgsConstructor; 6 | import lombok.AllArgsConstructor; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Data 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class ConversationHistory { 15 | private Long id; 16 | private Long projectId; 17 | private String messageType; 18 | private String content; 19 | private Integer priority; 20 | private Boolean isIncludedInContext; 21 | private Integer sequence; 22 | private LocalDateTime createdAt; 23 | private LocalDateTime updatedAt; 24 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/auth/repository/AuthUserRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.auth.repository; 2 | 3 | import com.readify.server.domain.auth.model.AuthUser; 4 | 5 | public interface AuthUserRepository { 6 | /** 7 | * 保存认证用户信息 8 | * 9 | * @param authUser 认证用户信息 10 | * @return 保存后的认证用户信息 11 | */ 12 | AuthUser save(AuthUser authUser); 13 | 14 | /** 15 | * 根据用户名查找认证用户信息 16 | * 17 | * @param username 用户名 18 | * @return 认证用户信息 19 | */ 20 | AuthUser findByUsername(String username); 21 | 22 | /** 23 | * 检查用户名是否已存在 24 | * 25 | * @param username 用户名 26 | * @return true 如果用户名已存在,否则返回 false 27 | */ 28 | boolean existsByUsername(String username); 29 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/conversation/converter/ConversationVOConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.conversation.converter; 2 | 3 | import com.readify.server.domain.conversation.entity.ConversationHistory; 4 | import com.readify.server.interfaces.conversation.vo.ConversationVO; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.Mapping; 7 | import org.mapstruct.factory.Mappers; 8 | 9 | @Mapper(componentModel = "spring", uses = AssistantThinkingVOConverter.class) 10 | public interface ConversationVOConverter { 11 | 12 | ConversationVOConverter INSTANCE = Mappers.getMapper(ConversationVOConverter.class); 13 | 14 | @Mapping(target = "thinking", ignore = true) 15 | ConversationVO toResponseDTO(ConversationHistory conversation); 16 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/ApiKeyEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | 9 | @Data 10 | @TableName("api_keys") 11 | public class ApiKeyEntity { 12 | @TableId(type = IdType.AUTO) 13 | private Long id; 14 | private String name; 15 | @TableField("api_key") 16 | private String apiKey; 17 | private String description; 18 | private Long userId; 19 | private Boolean enabled; 20 | private Long createTime; 21 | private Long updateTime; 22 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/user/vo/UserVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.user.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Schema(description = "用户视图对象") 8 | public class UserVO { 9 | @Schema(description = "用户ID", accessMode = Schema.AccessMode.READ_ONLY) 10 | private Long id; 11 | 12 | @Schema(description = "用户名", example = "johndoe") 13 | private String username; 14 | 15 | @Schema(description = "账户是否启用", example = "true") 16 | private Boolean enabled; 17 | 18 | @Schema(description = "创建时间", accessMode = Schema.AccessMode.READ_ONLY) 19 | private Long createTime; 20 | 21 | @Schema(description = "更新时间", accessMode = Schema.AccessMode.READ_ONLY) 22 | private Long updateTime; 23 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/project/converter/ProjectVOConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.project.converter; 2 | 3 | import com.readify.server.domain.project.model.Project; 4 | import com.readify.server.interfaces.project.vo.ProjectVO; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.MappingTarget; 7 | import org.mapstruct.factory.Mappers; 8 | 9 | import java.util.List; 10 | 11 | @Mapper 12 | public interface ProjectVOConverter { 13 | ProjectVOConverter INSTANCE = Mappers.getMapper(ProjectVOConverter.class); 14 | 15 | ProjectVO toVO(Project project); 16 | 17 | Project toDomain(ProjectVO projectVO); 18 | 19 | List toVOList(List projects); 20 | 21 | void updateProjectVO(Project project, @MappingTarget ProjectVO projectVO); 22 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/mind_map/converter/MindMapVOConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.mind_map.converter; 2 | 3 | import com.readify.server.domain.mind_map.model.MindMap; 4 | import com.readify.server.interfaces.mind_map.vo.MindMapVO; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.MappingTarget; 7 | import org.mapstruct.factory.Mappers; 8 | 9 | import java.util.List; 10 | 11 | @Mapper 12 | public interface MindMapVOConverter { 13 | MindMapVOConverter INSTANCE = Mappers.getMapper(MindMapVOConverter.class); 14 | 15 | MindMapVO toVO(MindMap mindMap); 16 | 17 | MindMap toDomain(MindMapVO mindMapVO); 18 | 19 | List toVOList(List mindMaps); 20 | 21 | void updateMindMapVO(MindMap mindMap, @MappingTarget MindMapVO mindMapVO); 22 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/conversation/repository/ConversationRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.conversation.repository; 2 | 3 | import com.readify.server.domain.conversation.entity.ConversationHistory; 4 | import com.readify.server.domain.conversation.entity.AssistantThinking; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public interface ConversationRepository { 10 | /** 11 | * 根据项目ID获取对话历史列表 12 | */ 13 | List findByProjectId(Long projectId); 14 | 15 | /** 16 | * 根据用户消息ID获取思考过程 17 | */ 18 | Optional findThinkingByUserMessageId(Long userMessageId); 19 | 20 | /** 21 | * 根据项目ID获取所有用户消息相关的思考过程 22 | */ 23 | List findThinkingsByProjectId(Long projectId); 24 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/dto/GenerateNoteResponse.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * 生成笔记响应DTO 10 | */ 11 | @Data 12 | @Builder 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class GenerateNoteResponse { 16 | 17 | /** 18 | * 任务ID 19 | */ 20 | private Long taskId; 21 | 22 | /** 23 | * 项目ID 24 | */ 25 | private Long projectId; 26 | 27 | /** 28 | * 思维导图ID 29 | */ 30 | private Long mindMapId; 31 | 32 | /** 33 | * 响应内容,流式返回的部分内容 34 | */ 35 | private String content; 36 | 37 | /** 38 | * 标记是否为最后一条消息 39 | */ 40 | private boolean done; 41 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/converter/MindMapNodeConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.converter; 2 | 3 | import com.readify.server.domain.mind_map.model.MindMapNode; 4 | import com.readify.server.infrastructure.persistence.entity.MindMapNodeEntity; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.factory.Mappers; 7 | 8 | import java.util.List; 9 | 10 | @Mapper 11 | public interface MindMapNodeConverter { 12 | MindMapNodeConverter INSTANCE = Mappers.getMapper(MindMapNodeConverter.class); 13 | 14 | MindMapNodeEntity toEntity(MindMapNode node); 15 | 16 | MindMapNode toDomain(MindMapNodeEntity entity); 17 | 18 | List toEntityList(List nodes); 19 | 20 | List toDomainList(List entities); 21 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/ConversationHistoryEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | @Data 11 | @TableName("conversation_history") 12 | public class ConversationHistoryEntity { 13 | @TableId(type = IdType.AUTO) 14 | private Long id; 15 | private Long projectId; 16 | private String messageType; 17 | private String content; 18 | private Integer priority; 19 | private Boolean isIncludedInContext; 20 | private Integer sequence; 21 | private LocalDateTime createdAt; 22 | private LocalDateTime updatedAt; 23 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/mind_map/vo/MindMapVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.mind_map.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Schema(description = "思维导图视图对象") 8 | public class MindMapVO { 9 | @Schema(description = "思维导图ID", accessMode = Schema.AccessMode.READ_ONLY) 10 | private Long id; 11 | 12 | @Schema(description = "工程ID", example = "1") 13 | private Long projectId; 14 | 15 | @Schema(description = "文件ID", example = "1") 16 | private Long fileId; 17 | 18 | @Schema(description = "思维导图标题", example = "书名") 19 | private String title; 20 | 21 | @Schema(description = "笔记类型", example = "mind_map") 22 | private String type; 23 | 24 | @Schema(description = "思维导图描述", example = "这是一个项目架构分析图") 25 | private String description; 26 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/file/service/FileService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.file.service; 2 | 3 | import com.readify.server.domain.file.model.File; 4 | 5 | import java.io.InputStream; 6 | import java.util.List; 7 | 8 | public interface FileService { 9 | File upload(String originalFilename, String mimeType, long size, InputStream inputStream); 10 | void delete(Long fileId); 11 | File getFileInfo(Long fileId); 12 | List getFilesByIds(List fileIds); 13 | 14 | /** 15 | * 更新文件向量化状态 16 | * 17 | * @param fileId 文件ID 18 | * @param vectorized 是否已向量化 19 | * @return 更新后的文件信息 20 | */ 21 | File updateVectorizedStatus(Long fileId, Boolean vectorized); 22 | 23 | /** 24 | * 获取未向量化的文件列表 25 | * 26 | * @return 未向量化的文件列表 27 | */ 28 | List getNonVectorizedFiles(); 29 | } -------------------------------------------------------------------------------- /readify_agi/environment.yml: -------------------------------------------------------------------------------- 1 | name: readify_agi 2 | channels: 3 | - conda-forge 4 | - defaults 5 | dependencies: 6 | - python=3.9 7 | - pip 8 | - pip: 9 | - fastapi>=0.100.0 10 | - uvicorn 11 | - sqlalchemy<2.0.0 12 | - aiomysql 13 | - python-dotenv 14 | - pydantic>=2.0.0 15 | - pydantic-settings>=2.0.0 16 | - starlette>=0.27.0 17 | - typing-extensions>=4.5.0 18 | - email-validator>=2.0.0 19 | - cryptography 20 | - llama-parse 21 | - langchain>=0.1.5 22 | - langchain-core>=0.1.14 23 | - langchain-community>=0.1.5 24 | - langchain-openai>=0.1.7 25 | - chromadb 26 | - openai>=1.3.0 27 | - tiktoken 28 | - agently>=0.4.0 29 | - pytest>=7.0.0 30 | - httpx>=0.24.0 31 | - pytest-asyncio>=0.21.0 32 | - python-multipart # 用于文件上传 33 | - pypdf # 用于PDF处理 34 | - google-search-results # SerpAPI 库 35 | -------------------------------------------------------------------------------- /readify_frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 46 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/message/WebSocketMessage.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.message; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @Builder 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class WebSocketMessage { 13 | /** 14 | * 消息类型 15 | */ 16 | private String type; 17 | 18 | /** 19 | * 消息数据 20 | */ 21 | private T data; 22 | 23 | /** 24 | * 创建时间 25 | */ 26 | private Long timestamp; 27 | 28 | public static WebSocketMessage create(String type, T data) { 29 | return WebSocketMessage.builder() 30 | .type(type) 31 | .data(data) 32 | .timestamp(System.currentTimeMillis()) 33 | .build(); 34 | } 35 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/auth/service/ApiKeyService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.auth.service; 2 | 3 | import com.readify.server.domain.auth.model.ApiKey; 4 | 5 | import java.util.List; 6 | 7 | public interface ApiKeyService { 8 | /** 9 | * 创建API Key 10 | * 11 | * @param name API Key名称 12 | * @param description API Key描述 13 | * @return API Key信息 14 | */ 15 | ApiKey create(String name, String description); 16 | 17 | /** 18 | * 获取API Key列表 19 | * 20 | * @return API Key列表 21 | */ 22 | List list(); 23 | 24 | /** 25 | * 删除API Key 26 | * 27 | * @param id API Key ID 28 | */ 29 | void delete(Long id); 30 | 31 | /** 32 | * 验证API Key 33 | * 34 | * @param key API Key 35 | * @return API Key信息,如果验证失败返回null 36 | */ 37 | ApiKey validate(String key); 38 | } -------------------------------------------------------------------------------- /readify_frontend/src/components/ChatContainer.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/auth/repository/ApiKeyRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.auth.repository; 2 | 3 | import com.readify.server.domain.auth.model.ApiKey; 4 | 5 | import java.util.List; 6 | 7 | public interface ApiKeyRepository { 8 | /** 9 | * 保存API Key 10 | * 11 | * @param apiKey API Key信息 12 | * @return 保存后的API Key信息 13 | */ 14 | ApiKey save(ApiKey apiKey); 15 | 16 | /** 17 | * 根据key查找API Key信息 18 | * 19 | * @param key API Key 20 | * @return API Key信息 21 | */ 22 | ApiKey findByKey(String key); 23 | 24 | /** 25 | * 根据用户ID查找API Key列表 26 | * 27 | * @param userId 用户ID 28 | * @return API Key列表 29 | */ 30 | List findByUserId(Long userId); 31 | 32 | /** 33 | * 删除API Key 34 | * 35 | * @param id API Key ID 36 | */ 37 | void delete(Long id); 38 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/conversation/vo/AssistantThinkingVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.conversation.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.Builder; 6 | import lombok.NoArgsConstructor; 7 | import lombok.AllArgsConstructor; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | @Data 12 | @Builder 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @Schema(description = "AI思考过程视图对象") 16 | public class AssistantThinkingVO { 17 | @Schema(description = "思考过程ID", example = "1") 18 | private Long id; 19 | 20 | @Schema(description = "关联的用户消息ID", example = "10") 21 | private Long userMessageId; 22 | 23 | @Schema(description = "思考过程内容") 24 | private String content; 25 | 26 | @Schema(description = "创建时间", example = "2025-03-07T12:00:00") 27 | private LocalDateTime createdAt; 28 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/project/vo/ProjectVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.project.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Schema(description = "工程视图对象") 8 | public class ProjectVO { 9 | @Schema(description = "工程ID", accessMode = Schema.AccessMode.READ_ONLY) 10 | private Long id; 11 | 12 | @Schema(description = "用户ID", accessMode = Schema.AccessMode.READ_ONLY) 13 | private Long userId; 14 | 15 | @Schema(description = "工程名称", example = "示例工程") 16 | private String name; 17 | 18 | @Schema(description = "工程描述", example = "这是一个示例工程") 19 | private String description; 20 | 21 | @Schema(description = "创建时间", accessMode = Schema.AccessMode.READ_ONLY) 22 | private Long createTime; 23 | 24 | @Schema(description = "更新时间", accessMode = Schema.AccessMode.READ_ONLY) 25 | private Long updateTime; 26 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.config; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.info.Info; 5 | import io.swagger.v3.oas.models.servers.Server; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | import java.util.List; 11 | 12 | @Configuration 13 | public class SwaggerConfig { 14 | 15 | @Value("${spring.mvc.servlet.path:/}") 16 | private String contextPath; 17 | 18 | @Bean 19 | public OpenAPI customOpenAPI() { 20 | return new OpenAPI() 21 | .info(new Info() 22 | .title("Readify API文档") 23 | .description("Readify服务端API接口文档") 24 | .version("1.0.0")) 25 | .servers(List.of(new Server().url(contextPath))); 26 | } 27 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/file/vo/FileVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.file.vo; 2 | 3 | import com.readify.server.domain.file.model.File; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | @Data 8 | @Builder 9 | public class FileVO { 10 | private Long id; 11 | private String originalName; 12 | private String mimeType; 13 | private Long size; 14 | private Long createTime; 15 | private Long updateTime; 16 | private Boolean vectorized; 17 | 18 | public static FileVO from(File file) { 19 | return FileVO.builder() 20 | .id(file.getId()) 21 | .originalName(file.getOriginalName()) 22 | .mimeType(file.getMimeType()) 23 | .size(file.getSize()) 24 | .createTime(file.getCreateTime()) 25 | .updateTime(file.getUpdateTime()) 26 | .vectorized(file.getVectorized()) 27 | .build(); 28 | } 29 | } -------------------------------------------------------------------------------- /readify_agi/app/services/llama_parse_service.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | import os 3 | from llama_cloud_services import LlamaParse 4 | from llama_index.core import Document 5 | from app.core.config import settings 6 | 7 | class LlamaParseService: 8 | def __init__(self): 9 | """ 10 | 初始化LlamaParse服务 11 | """ 12 | self.parser = LlamaParse(api_key=settings.LLAMA_PARSE_API_KEY) 13 | 14 | async def parse_file(self, file_path: str) -> List[Document]: 15 | """ 16 | 解析文件 17 | 18 | Args: 19 | file_path: 文件路径 20 | 21 | Returns: 22 | Document: 解析后的文档对象 23 | """ 24 | if not os.path.exists(file_path): 25 | raise FileNotFoundError(f"文件不存在: {file_path}") 26 | 27 | documents = await self.parser.aload_data(file_path) 28 | if not documents: 29 | raise ValueError(f"文件解析失败: {file_path}") 30 | return documents # 返回第一个文档 31 | -------------------------------------------------------------------------------- /readify_frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "readify_frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc -b && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@element-plus/icons-vue": "^2.3.1", 13 | "axios": "^1.7.9", 14 | "d3": "^7.9.0", 15 | "element-plus": "^2.9.5", 16 | "highlight.js": "^11.11.1", 17 | "markdown-it": "^14.1.0", 18 | "markdown-it-highlightjs": "^4.2.0", 19 | "marked": "^15.0.7", 20 | "markmap-lib": "^0.18.11", 21 | "markmap-view": "^0.18.10", 22 | "md-editor-v3": "^5.4.1", 23 | "vue": "^3.5.13", 24 | "vue-router": "^4.5.0", 25 | "vuex": "^4.0.2" 26 | }, 27 | "devDependencies": { 28 | "@types/node": "^22.13.4", 29 | "@vitejs/plugin-vue": "^5.2.1", 30 | "@vue/tsconfig": "^0.7.0", 31 | "typescript": "~5.7.2", 32 | "vite": "^6.1.0", 33 | "vue-tsc": "^2.2.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /readify_agi/app/models/assistant_thinking.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, DateTime, BigInteger 2 | from sqlalchemy.sql import func 3 | from sqlalchemy.dialects.mysql import LONGTEXT 4 | 5 | from app.core.database import Base 6 | 7 | 8 | class AssistantThinkingDB(Base): 9 | __tablename__ = "assistant_thinking" 10 | 11 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键ID") 12 | project_id = Column(BigInteger, nullable=False, index=True, comment="工程ID") 13 | user_message_id = Column(BigInteger, nullable=False, index=True, comment="对应的用户消息ID") 14 | content = Column(LONGTEXT, nullable=False, comment="AI思考过程内容") 15 | created_at = Column(DateTime, nullable=False, server_default=func.now(), index=True, comment="创建时间") 16 | updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now(), comment="更新时间") 17 | 18 | def __repr__(self): 19 | return f" 2 | import { ref } from 'vue' 3 | 4 | defineProps<{ msg: string }>() 5 | 6 | const count = ref(0) 7 | 8 | 9 | 36 | 37 | 42 | -------------------------------------------------------------------------------- /readify_agi/.env.example: -------------------------------------------------------------------------------- 1 | # 数据库配置 2 | DB_HOST=localhost 3 | DB_PORT=3306 4 | DB_USER=root 5 | DB_PASSWORD=YOUR_PASSWORD 6 | DB_NAME=readify 7 | 8 | # Chroma配置 9 | CHROMA_SERVER_HOST=localhost 10 | CHROMA_SERVER_PORT=8000 11 | CHROMA_SERVER_SSL_ENABLED=false 12 | 13 | # LlamaParse配置 14 | LLAMA_PARSE_API_KEY=YOUR_LLAMA_PARSE_API_KEY 15 | 16 | # OpenAI配置 17 | OPENAI_API_KEY=YOUR_OPENAI_API_KEY 18 | OPENAI_EMBEDDING_MODEL=text-embedding-ada-002 19 | OPENAI_API_BASE=https://api.openai.com/v1 20 | 21 | # Deepseek 22 | DEEPSEEK_API_KEY=YOUR_DEEPSEEK_API_KEY 23 | DEEPSEEK_API_BASE=https://api.deepseek.com 24 | 25 | # OpenAI-国内专线 26 | OPENAI_API_BASE_CHINA=YOUR_OPENAI_API_PROXY_BASE_URL 27 | OPENAI_API_KEY_CHINA=YOUR_OPENAI_API_KEY_PROXY 28 | 29 | # Qwen 30 | QWEN_API_KEY=YOUR_QWEN_API_KEY 31 | QWEN_API_BASE=https://dashscope.aliyuncs.com/compatible-mode/v1 32 | 33 | 34 | # 回调配置 35 | FILE_PROCESS_CALLBACK_URL=http://localhost:8080/api/v1/files/vectorized 36 | FILE_PROCESS_CALLBACK_API_KEY=uFjMA2BAZ4xK1b-cfHyPJNF7lFkQ6CDZVk0p7--yGnQ 37 | 38 | # SerpAPI配置 39 | SERPAPI_API_KEY=YOUR_SERPAPI_API_KEY 40 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/ProjectEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableLogic; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | 9 | @Data 10 | @TableName("project") 11 | public class ProjectEntity { 12 | /** 13 | * 主键ID 14 | */ 15 | @TableId(type = IdType.AUTO) 16 | private Long id; 17 | 18 | /** 19 | * 用户ID 20 | */ 21 | private Long userId; 22 | 23 | /** 24 | * 工程名称 25 | */ 26 | private String name; 27 | 28 | /** 29 | * 工程描述 30 | */ 31 | private String description; 32 | 33 | /** 34 | * 创建时间 35 | */ 36 | private Long createTime; 37 | 38 | /** 39 | * 更新时间 40 | */ 41 | private Long updateTime; 42 | 43 | /** 44 | * 是否删除 45 | */ 46 | @TableLogic 47 | private Boolean deleted; 48 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/converter/ConversationConverter.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.converter; 2 | 3 | import com.readify.server.domain.conversation.entity.AssistantThinking; 4 | import com.readify.server.domain.conversation.entity.ConversationHistory; 5 | import com.readify.server.infrastructure.persistence.entity.AssistantThinkingEntity; 6 | import com.readify.server.infrastructure.persistence.entity.ConversationHistoryEntity; 7 | import org.mapstruct.Mapper; 8 | import org.mapstruct.factory.Mappers; 9 | 10 | import java.util.List; 11 | 12 | @Mapper(componentModel = "spring") 13 | public interface ConversationConverter { 14 | 15 | ConversationConverter INSTANCE = Mappers.getMapper(ConversationConverter.class); 16 | 17 | ConversationHistory toDomain(ConversationHistoryEntity entity); 18 | 19 | ConversationHistoryEntity toEntity(ConversationHistory domain); 20 | 21 | AssistantThinking toDomain(AssistantThinkingEntity entity); 22 | 23 | AssistantThinkingEntity toEntity(AssistantThinking domain); 24 | } -------------------------------------------------------------------------------- /readify_agi/app/repositories/mind_map_repository.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List 2 | from sqlalchemy.ext.asyncio import AsyncSession 3 | from sqlalchemy import select, update 4 | from sqlalchemy.sql import and_ 5 | import time 6 | from app.models.mind_map import MindMapDB, MindMapCreate 7 | from app.repositories import BaseRepository 8 | 9 | class MindMapRepository(BaseRepository): 10 | """思维导图仓储层""" 11 | 12 | async def get_by_id(self, mind_map_id: int) -> Optional[MindMapDB]: 13 | """根据ID获取思维导图 14 | 15 | Args: 16 | mind_map_id: 思维导图ID 17 | 18 | Returns: 19 | 思维导图对象,如果不存在则返回None 20 | """ 21 | try: 22 | db = await self._ensure_session() 23 | query = select(MindMapDB).where( 24 | and_( 25 | MindMapDB.id == mind_map_id, 26 | MindMapDB.is_deleted == False 27 | ) 28 | ) 29 | result = await db.execute(query) 30 | return result.scalar_one_or_none() 31 | finally: 32 | await self._cleanup_session() -------------------------------------------------------------------------------- /readify_agi/.cursor/rules/langgpt-helper.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 生成LangGPT格式的提示词 3 | globs: 4 | --- 5 | Role: LangGPT Prompt Engineer 6 | 7 | ## Profile 8 | - Author: Assistant 9 | - Version: 1.0 10 | - Language: 中文 11 | - Description: 我是一位专业的 LangGPT 提示词工程师,精通结构化提示词的设计与创建。我可以帮助你将想法转化为高质量的 LangGPT 格式提示词。 12 | 13 | ### 专业技能 14 | 1. 精通 LangGPT 的结构化提示词框架 15 | 2. 擅长角色设计和定义 16 | 3. 熟练运用变量、条件语句等高级功能 17 | 4. 可以创建清晰的工作流程和规则 18 | 5. 能够优化和改进现有的提示词 19 | 20 | ### 模板掌握 21 | 1. 基础角色模板 22 | 2. 高级功能模板(命令、提醒、条件语句等) 23 | 3. 多角色协作模板 24 | 4. 提示词链模板 25 | 26 | ## Rules 27 | 1. 始终遵循 LangGPT 的标准格式和结构 28 | 2. 确保生成的提示词清晰、可用且易于理解 29 | 3. 提供必要的解释和使用建议 30 | 4. 根据用户需求灵活调整模板内容 31 | 5. 不生成可能导致有害输出的提示词 32 | 33 | ## Workflow 34 | 1. 了解用户需求: 35 | - 目标角色是什么 36 | - 需要什么功能 37 | - 特殊要求或限制 38 | 2. 选择合适的模板结构 39 | 3. 设计角色属性和技能 40 | 4. 制定规则和工作流程 41 | 5. 添加必要的高级功能 42 | 6. 提供完整的提示词和使用说明 43 | 44 | ## Commands 45 | - /template - 显示基础模板结构 46 | - /help - 显示使用帮助 47 | - /example - 展示示例提示词 48 | - /optimize - 优化现有提示词 49 | - /advanced - 显示高级功能用法 50 | 51 | ## Initialization 52 | 作为 LangGPT Prompt Engineer,我会遵循以上规则,使用中文与用户交流。我将首先问候用户,然后介绍自己的能力和工作方式。让我们开始创建专业的 LangGPT 提示词吧! -------------------------------------------------------------------------------- /readify_server/.cursor/rules/langgpt-helper.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 生成LangGPT格式的提示词 3 | globs: 4 | --- 5 | Role: LangGPT Prompt Engineer 6 | 7 | ## Profile 8 | - Author: Assistant 9 | - Version: 1.0 10 | - Language: 中文 11 | - Description: 我是一位专业的 LangGPT 提示词工程师,精通结构化提示词的设计与创建。我可以帮助你将想法转化为高质量的 LangGPT 格式提示词。 12 | 13 | ### 专业技能 14 | 1. 精通 LangGPT 的结构化提示词框架 15 | 2. 擅长角色设计和定义 16 | 3. 熟练运用变量、条件语句等高级功能 17 | 4. 可以创建清晰的工作流程和规则 18 | 5. 能够优化和改进现有的提示词 19 | 20 | ### 模板掌握 21 | 1. 基础角色模板 22 | 2. 高级功能模板(命令、提醒、条件语句等) 23 | 3. 多角色协作模板 24 | 4. 提示词链模板 25 | 26 | ## Rules 27 | 1. 始终遵循 LangGPT 的标准格式和结构 28 | 2. 确保生成的提示词清晰、可用且易于理解 29 | 3. 提供必要的解释和使用建议 30 | 4. 根据用户需求灵活调整模板内容 31 | 5. 不生成可能导致有害输出的提示词 32 | 33 | ## Workflow 34 | 1. 了解用户需求: 35 | - 目标角色是什么 36 | - 需要什么功能 37 | - 特殊要求或限制 38 | 2. 选择合适的模板结构 39 | 3. 设计角色属性和技能 40 | 4. 制定规则和工作流程 41 | 5. 添加必要的高级功能 42 | 6. 提供完整的提示词和使用说明 43 | 44 | ## Commands 45 | - /template - 显示基础模板结构 46 | - /help - 显示使用帮助 47 | - /example - 展示示例提示词 48 | - /optimize - 优化现有提示词 49 | - /advanced - 显示高级功能用法 50 | 51 | ## Initialization 52 | 作为 LangGPT Prompt Engineer,我会遵循以上规则,使用中文与用户交流。我将首先问候用户,然后介绍自己的能力和工作方式。让我们开始创建专业的 LangGPT 提示词吧! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2024 xiao-cell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableLogic; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.Data; 9 | 10 | @Data 11 | @TableName("user") 12 | public class UserEntity { 13 | /** 14 | * 主键ID 15 | */ 16 | @TableId(type = IdType.AUTO) 17 | private Long id; 18 | 19 | /** 20 | * 用户名 21 | */ 22 | private String username; 23 | 24 | /** 25 | * 密码 26 | */ 27 | private String password; 28 | 29 | /** 30 | * 是否启用 31 | */ 32 | private Boolean enabled; 33 | 34 | /** 35 | * 创建时间 36 | */ 37 | private Long createTime; 38 | 39 | /** 40 | * 更新时间 41 | */ 42 | private Long updateTime; 43 | 44 | /** 45 | * 是否删除 46 | */ 47 | @TableLogic 48 | private Boolean deleted; 49 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/ProjectFileEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableLogic; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.Data; 9 | 10 | @Data 11 | @TableName("project_file") 12 | @Schema(description = "项目文件关联实体") 13 | public class ProjectFileEntity { 14 | 15 | @TableId(type = IdType.AUTO) 16 | @Schema(description = "主键ID") 17 | private Long id; 18 | 19 | @Schema(description = "项目ID") 20 | private Long projectId; 21 | 22 | @Schema(description = "项目ID") 23 | private Long userId; 24 | 25 | @Schema(description = "文件ID") 26 | private Long fileId; 27 | 28 | @Schema(description = "创建时间") 29 | private Long createTime; 30 | 31 | @Schema(description = "更新时间") 32 | private Long updateTime; 33 | 34 | @TableLogic 35 | @Schema(description = "是否删除") 36 | private Boolean deleted; 37 | } -------------------------------------------------------------------------------- /readify_agi/app/repositories/base_repository.py: -------------------------------------------------------------------------------- 1 | """ 2 | 基础仓库类文件 3 | """ 4 | 5 | from sqlalchemy.ext.asyncio import AsyncSession 6 | from app.core.database import async_session_maker 7 | 8 | 9 | class BaseRepository: 10 | """ 11 | 基础仓库类,所有仓库类的父类,提供统一的数据库会话管理 12 | """ 13 | 14 | def __init__(self, db: AsyncSession = None): 15 | """ 16 | 初始化仓库 17 | 18 | Args: 19 | db: 可选的数据库会话,如果提供则使用该会话,否则使用会话工厂创建新会话 20 | """ 21 | self.db = db 22 | self._own_session = db is None # 标记是否拥有自己的会话 23 | 24 | async def _ensure_session(self) -> AsyncSession: 25 | """ 26 | 确保有可用的数据库会话 27 | 28 | Returns: 29 | AsyncSession: 数据库会话 30 | """ 31 | if self.db is None: 32 | self.db = async_session_maker() 33 | self._own_session = True 34 | return self.db 35 | 36 | async def _cleanup_session(self): 37 | """ 38 | 如果使用自己创建的会话,则在操作完成后关闭会话 39 | """ 40 | if self._own_session and self.db is not None: 41 | await self.db.close() 42 | self.db = None 43 | self._own_session = False -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/user/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.user.repository; 2 | 3 | import com.readify.server.domain.user.model.User; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public interface UserRepository { 9 | /** 10 | * 保存用户信息 11 | * 12 | * @param user 用户信息 13 | * @return 保存后的用户信息 14 | */ 15 | User save(User user); 16 | 17 | /** 18 | * 根据ID查找用户 19 | * 20 | * @param id 用户ID 21 | * @return 用户信息 22 | */ 23 | Optional findById(Long id); 24 | 25 | /** 26 | * 根据用户名查找用户 27 | * 28 | * @param username 用户名 29 | * @return 用户信息 30 | */ 31 | Optional findByUsername(String username); 32 | 33 | /** 34 | * 获取所有用户 35 | * 36 | * @return 用户列表 37 | */ 38 | List findAll(); 39 | 40 | /** 41 | * 根据ID删除用户 42 | * 43 | * @param id 用户ID 44 | */ 45 | void deleteById(Long id); 46 | 47 | /** 48 | * 检查用户名是否已存在 49 | * 50 | * @param username 用户名 51 | * @return true 如果用户名已存在,否则返回 false 52 | */ 53 | boolean existsByUsername(String username); 54 | } -------------------------------------------------------------------------------- /readify_agi/app/core/database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession 2 | from sqlalchemy.orm import sessionmaker 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from typing import AsyncGenerator 5 | from .config import settings 6 | 7 | # 创建异步引擎 8 | engine = create_async_engine( 9 | settings.DATABASE_URL, 10 | pool_pre_ping=True, # 连接前进行ping检测 11 | pool_recycle=1800, # 半小时回收一次连接 12 | pool_size=20, # 连接池大小 13 | max_overflow=10, # 最大溢出连接数 14 | echo=False, # 生产环境不记录每个SQL日志 15 | future=True # 使用SQLAlchemy 2.0风格 16 | ) 17 | 18 | # 创建异步会话工厂 19 | async_session_maker = sessionmaker( 20 | engine, 21 | class_=AsyncSession, 22 | expire_on_commit=False, 23 | autoflush=False # 防止自动刷新导致的额外查询 24 | ) 25 | 26 | # 创建基类 27 | Base = declarative_base() 28 | 29 | async def get_db() -> AsyncGenerator[AsyncSession, None]: 30 | """获取数据库会话""" 31 | async with async_session_maker() as session: 32 | try: 33 | yield session 34 | finally: 35 | await session.close() 36 | 37 | # 优雅关闭数据库连接池的函数 38 | async def close_db_connection(): 39 | """关闭数据库连接池""" 40 | await engine.dispose() -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/mind_map/vo/MindMapNodeTreeVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.mind_map.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | @Data 10 | @Schema(description = "思维导图节点树形结构") 11 | public class MindMapNodeTreeVO { 12 | @Schema(description = "节点ID") 13 | private Long id; 14 | 15 | @Schema(description = "项目ID") 16 | private Long projectId; 17 | 18 | @Schema(description = "文件ID") 19 | private Long fileId; 20 | 21 | @Schema(description = "思维导图ID") 22 | private Long mindMapId; 23 | 24 | @Schema(description = "父节点ID,根节点为null") 25 | private Long parentId; 26 | 27 | @Schema(description = "节点内容") 28 | private String content; 29 | 30 | @Schema(description = "排序序号") 31 | private Integer sequence; 32 | 33 | @Schema(description = "节点层级,根节点为0") 34 | private Integer level; 35 | 36 | @Schema(description = "创建时间") 37 | private Long createdTime; 38 | 39 | @Schema(description = "更新时间") 40 | private Long updatedTime; 41 | 42 | @Schema(description = "子节点列表") 43 | private List children = new ArrayList<>(); 44 | } -------------------------------------------------------------------------------- /readify_agi/prompt/react.prompt1: -------------------------------------------------------------------------------- 1 | Using Chinese answer the following questions as best you can. You have access to the following tools: 2 | 3 | {tools} 4 | 5 | Current project ID: {project_id}, Project name: {project_name}, Project description: {project_description}, 6 | 7 | 8 | You MUST follow the exact key: value format in your response. 9 | Use the following format: 10 | 11 | 12 | Question: the input question you must answer 13 | Thought: you should always think about what to do. example: Thought: 思考内容 14 | Action: the action to take, should be one of [{tool_names}]. example: Action: 工具名称 15 | Action Input: the input to the action. example: Action Input: 工具输入参数 16 | Observation: the result of the action 17 | ... (this Thought/Action/Action Input/Observation can repeat N times) 18 | Thought: I now know the final answer 19 | Final Answer: the final answer to the original input question 20 | 21 | example: 22 | Question: 1+2+3= 23 | Thought: 我需要计算1+2+3的值,由于这是一个简单的问题,我直接计算 24 | Final Answer: 1+2+3=6 25 | 26 | 特别说明: 27 | 1. 思考过程应考虑当前的项目上下文,如项目名称和描述 28 | 2. 在不需要全文的数据时,请向量检索,如果未找到需要的数据,请使用搜索引擎和你本身的知识回答 29 | 3. 在需要文件的完整信息时,读取文件前,需要考虑文件的大小,当文档大小超过1000000字节时,请分页读取,如小于1000000可以考虑直接读取全文 30 | 31 | {history} 32 | 33 | Begin! 34 | 35 | Question: {input} 36 | Thought:{agent_scratchpad} -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/FileEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | @Data 8 | @TableName("file") 9 | @Schema(description = "文件实体") 10 | public class FileEntity { 11 | 12 | @TableId(type = IdType.AUTO) 13 | @Schema(description = "主键ID") 14 | private Long id; 15 | 16 | @Schema(description = "原始文件名") 17 | private String originalName; 18 | 19 | @Schema(description = "存储文件名") 20 | private String storageName; 21 | 22 | @Schema(description = "文件大小(字节)") 23 | private Long size; 24 | 25 | @Schema(description = "文件MIME类型") 26 | private String mimeType; 27 | 28 | @Schema(description = "存储路径") 29 | private String storagePath; 30 | 31 | @Schema(description = "文件MD5值") 32 | private String md5; 33 | 34 | @Schema(description = "创建时间") 35 | private Long createTime; 36 | 37 | @Schema(description = "更新时间") 38 | private Long updateTime; 39 | 40 | @TableLogic 41 | @Schema(description = "是否删除") 42 | private Boolean deleted; 43 | 44 | @Schema(description = "是否已向量化") 45 | private Boolean vectorized; 46 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/auth/vo/ApiKeyVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.auth.vo; 2 | 3 | import com.readify.server.domain.auth.model.ApiKey; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | @Data 8 | @Schema(description = "API Key信息") 9 | public class ApiKeyVO { 10 | @Schema(description = "API Key ID") 11 | private Long id; 12 | 13 | @Schema(description = "API Key名称") 14 | private String name; 15 | 16 | @Schema(description = "API Key") 17 | private String apiKey; 18 | 19 | @Schema(description = "API Key描述") 20 | private String description; 21 | 22 | @Schema(description = "是否启用") 23 | private Boolean enabled; 24 | 25 | @Schema(description = "创建时间") 26 | private Long createTime; 27 | 28 | public static ApiKeyVO from(ApiKey apiKey) { 29 | if (apiKey == null) { 30 | return null; 31 | } 32 | ApiKeyVO vo = new ApiKeyVO(); 33 | vo.setId(apiKey.getId()); 34 | vo.setName(apiKey.getName()); 35 | vo.setApiKey(apiKey.getApiKey()); 36 | vo.setDescription(apiKey.getDescription()); 37 | vo.setEnabled(apiKey.getEnabled()); 38 | vo.setCreateTime(apiKey.getCreateTime()); 39 | return vo; 40 | } 41 | } -------------------------------------------------------------------------------- /readify_agi/app/models/project_file.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, BigInteger, Boolean 2 | from pydantic import BaseModel, ConfigDict 3 | from app.core.database import Base 4 | 5 | 6 | class ProjectFileDB(Base): 7 | """项目文件关联数据库模型""" 8 | __tablename__ = "project_file" 9 | 10 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键ID") 11 | project_id = Column(BigInteger, nullable=False, index=True, comment="项目ID") 12 | user_id = Column(BigInteger, nullable=True, comment="用户ID") 13 | file_id = Column(BigInteger, nullable=False, index=True, comment="文件ID") 14 | create_time = Column(BigInteger, nullable=False, comment="创建时间") 15 | update_time = Column(BigInteger, nullable=False, comment="更新时间") 16 | deleted = Column(Boolean, nullable=False, default=False, comment="是否删除") 17 | 18 | 19 | class ProjectFileBase(BaseModel): 20 | """项目文件关联基础模型""" 21 | model_config = ConfigDict(from_attributes=True) 22 | 23 | project_id: int 24 | user_id: int = None 25 | file_id: int 26 | 27 | 28 | class ProjectFileCreate(ProjectFileBase): 29 | """项目文件关联创建模型""" 30 | pass 31 | 32 | 33 | class ProjectFileResponse(ProjectFileBase): 34 | """项目文件关联响应模型""" 35 | id: int 36 | create_time: int 37 | update_time: int 38 | deleted: bool = False -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/notetask/NoteTaskStatus.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.notetask; 2 | 3 | /** 4 | * 笔记任务状态枚举 5 | */ 6 | public enum NoteTaskStatus { 7 | /** 8 | * 待处理 9 | */ 10 | PENDING("pending", "待处理"), 11 | 12 | /** 13 | * 处理中 14 | */ 15 | PROCESSING("processing", "处理中"), 16 | 17 | /** 18 | * 已完成 19 | */ 20 | COMPLETED("completed", "已完成"), 21 | 22 | /** 23 | * 失败 24 | */ 25 | FAILED("failed", "失败"); 26 | 27 | private final String code; 28 | private final String desc; 29 | 30 | NoteTaskStatus(String code, String desc) { 31 | this.code = code; 32 | this.desc = desc; 33 | } 34 | 35 | public String getCode() { 36 | return code; 37 | } 38 | 39 | public String getDesc() { 40 | return desc; 41 | } 42 | 43 | /** 44 | * 根据code获取枚举 45 | * 46 | * @param code 状态码 47 | * @return 枚举 48 | */ 49 | public static NoteTaskStatus getByCode(String code) { 50 | for (NoteTaskStatus status : NoteTaskStatus.values()) { 51 | if (status.getCode().equals(code)) { 52 | return status; 53 | } 54 | } 55 | return null; 56 | } 57 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/user/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.user.service; 2 | 3 | import com.readify.server.domain.user.model.User; 4 | 5 | import java.util.List; 6 | 7 | public interface UserService { 8 | /** 9 | * 创建新用户 10 | * 11 | * @param user 用户信息 12 | * @return 创建后的用户信息 13 | */ 14 | User createUser(User user); 15 | 16 | /** 17 | * 更新用户信息 18 | * 19 | * @param user 用户信息 20 | * @return 更新后的用户信息 21 | */ 22 | User updateUser(User user); 23 | 24 | /** 25 | * 删除用户 26 | * 27 | * @param id 用户ID 28 | */ 29 | void deleteUser(Long id); 30 | 31 | /** 32 | * 根据ID获取用户信息 33 | * 34 | * @param id 用户ID 35 | * @return 用户信息 36 | */ 37 | User getUserById(Long id); 38 | 39 | /** 40 | * 根据用户名获取用户信息 41 | * 42 | * @param username 用户名 43 | * @return 用户信息 44 | */ 45 | User getUserByUsername(String username); 46 | 47 | /** 48 | * 获取所有用户 49 | * 50 | * @return 用户列表 51 | */ 52 | List getAllUsers(); 53 | 54 | /** 55 | * 检查用户名是否已存在 56 | * 57 | * @param username 用户名 58 | * @return true 如果用户名已存在,否则返回 false 59 | */ 60 | boolean isUsernameExists(String username); 61 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/notetask/model/NoteTask.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.notetask.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * 笔记任务领域模型 10 | */ 11 | @Data 12 | @Builder 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class NoteTask { 16 | /** 17 | * 主键ID 18 | */ 19 | private Long id; 20 | 21 | /** 22 | * 用户ID 23 | */ 24 | private Long userId; 25 | 26 | /** 27 | * 关联项目ID 28 | */ 29 | private Long projectId; 30 | 31 | /** 32 | * 关联的思维导图ID 33 | */ 34 | private Long mindMapId; 35 | 36 | /** 37 | * 关联的文件ID 38 | */ 39 | private Long fileId; 40 | 41 | /** 42 | * 用户提问/任务内容 43 | */ 44 | private String content; 45 | 46 | /** 47 | * 任务状态 48 | */ 49 | private String status; 50 | 51 | /** 52 | * 任务结果 53 | */ 54 | private String result; 55 | 56 | /** 57 | * 创建时间 58 | */ 59 | private Long createTime; 60 | 61 | /** 62 | * 更新时间 63 | */ 64 | private Long updateTime; 65 | 66 | /** 67 | * 是否删除 68 | */ 69 | private Boolean deleted; 70 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/mind_map/model/MindMapNodeTree.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.mind_map.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | @Data 9 | public class MindMapNodeTree { 10 | private Long id; 11 | private Long projectId; 12 | private Long fileId; 13 | private Long mindMapId; 14 | private Long parentId; 15 | private String content; 16 | private Integer sequence; 17 | private Integer level; 18 | private Long createdTime; 19 | private Long updatedTime; 20 | private List children = new ArrayList<>(); 21 | 22 | public static MindMapNodeTree fromMindMapNode(MindMapNode node) { 23 | MindMapNodeTree tree = new MindMapNodeTree(); 24 | tree.setId(node.getId()); 25 | tree.setProjectId(node.getProjectId()); 26 | tree.setFileId(node.getFileId()); 27 | tree.setMindMapId(node.getMindMapId()); 28 | tree.setParentId(node.getParentId()); 29 | tree.setContent(node.getContent()); 30 | tree.setSequence(node.getSequence()); 31 | tree.setLevel(node.getLevel()); 32 | tree.setCreatedTime(node.getCreatedTime()); 33 | tree.setUpdatedTime(node.getUpdatedTime()); 34 | return tree; 35 | } 36 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/MindMapEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableLogic; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | 9 | @Data 10 | @TableName("mind_map") 11 | public class MindMapEntity { 12 | /** 13 | * 思维导图ID 14 | */ 15 | @TableId(type = IdType.AUTO) 16 | private Long id; 17 | 18 | /** 19 | * 工程ID 20 | */ 21 | private Long projectId; 22 | 23 | /** 24 | * 文件id 25 | */ 26 | private Long fileId; 27 | 28 | /** 29 | * 思维导图标题 30 | */ 31 | private String title; 32 | 33 | /** 34 | * 笔记类型 35 | */ 36 | private String type; 37 | 38 | /** 39 | * 思维导图描述 40 | */ 41 | private String description; 42 | 43 | /** 44 | * 用户ID 45 | */ 46 | private Long userId; 47 | 48 | /** 49 | * 创建时间 50 | */ 51 | private Long createdAt; 52 | 53 | /** 54 | * 更新时间 55 | */ 56 | private Long updatedAt; 57 | 58 | /** 59 | * 是否删除 60 | */ 61 | @TableLogic 62 | private Boolean isDeleted; 63 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/handler/impl/PingMessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.handler.impl; 2 | 3 | import com.readify.server.websocket.WebSocketSessionManager; 4 | import com.readify.server.websocket.handler.WebSocketMessageHandler; 5 | import com.readify.server.websocket.message.WebSocketMessage; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.socket.WebSocketSession; 10 | 11 | /** 12 | * Ping消息处理器 13 | */ 14 | @Slf4j 15 | @Component 16 | @RequiredArgsConstructor 17 | public class PingMessageHandler implements WebSocketMessageHandler { 18 | 19 | private final WebSocketSessionManager sessionManager; 20 | 21 | @Override 22 | public String supportType() { 23 | return "ping"; 24 | } 25 | 26 | @Override 27 | public Class getDataType() { 28 | return Void.class; 29 | } 30 | 31 | @Override 32 | public void processMessage(WebSocketSession session, WebSocketMessage message) { 33 | log.debug("Processing ping message from session: {}", session.getId()); 34 | WebSocketMessage pongMessage = WebSocketMessage.create("pong", "Server is alive"); 35 | sessionManager.sendMessage(session.getId(), pongMessage); 36 | } 37 | } -------------------------------------------------------------------------------- /readify_frontend/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import { getToken } from '@/utils/auth' 3 | 4 | const routes = [ 5 | { 6 | path: '/', 7 | redirect: '/home' 8 | }, 9 | { 10 | path: '/login', 11 | name: 'Login', 12 | component: () => import('../views/Login.vue') 13 | }, 14 | { 15 | path: '/register', 16 | name: 'Register', 17 | component: () => import('../views/Register.vue') 18 | }, 19 | { 20 | path: '/home', 21 | name: 'Home', 22 | component: () => import('../views/Home.vue'), 23 | meta: { requiresAuth: true } 24 | }, 25 | { 26 | path: '/project/:id', 27 | name: 'ProjectDetail', 28 | component: () => import('../views/ProjectDetail.vue'), 29 | meta: { requiresAuth: true } 30 | }, 31 | { 32 | path: '/about', 33 | name: 'About', 34 | component: () => import('../views/About.vue'), 35 | meta: { requiresAuth: true } 36 | } 37 | ] 38 | 39 | const router = createRouter({ 40 | history: createWebHistory(), 41 | routes 42 | }) 43 | 44 | // 导航守卫 45 | router.beforeEach((to, from, next) => { 46 | const token = getToken() 47 | if (to.meta.requiresAuth && !token) { 48 | next('/login') 49 | } else if ((to.path === '/login' || to.path === '/register') && token) { 50 | next('/home') 51 | } else { 52 | next() 53 | } 54 | }) 55 | 56 | export default router -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/file/repository/FileRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.file.repository; 2 | 3 | import com.readify.server.domain.file.model.File; 4 | 5 | import java.io.InputStream; 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public interface FileRepository { 10 | /** 11 | * 保存文件信息 12 | */ 13 | File save(File file); 14 | 15 | /** 16 | * 根据ID查找文件 17 | */ 18 | Optional findById(Long id); 19 | 20 | /** 21 | * 根据ID列表查找文件 22 | */ 23 | List findAllById(List ids); 24 | 25 | /** 26 | * 根据MD5查找文件 27 | */ 28 | Optional findByMd5(String md5); 29 | 30 | /** 31 | * 根据ID删除文件 32 | */ 33 | void deleteById(Long id); 34 | 35 | /** 36 | * 上传文件 37 | */ 38 | File upload(String originalFilename, String mimeType, long size, InputStream inputStream); 39 | 40 | /** 41 | * 删除物理文件 42 | */ 43 | void deletePhysicalFile(String storageName); 44 | 45 | /** 46 | * 更新文件向量化状态 47 | * 48 | * @param fileId 文件ID 49 | * @param vectorized 是否已向量化 50 | * @return 更新后的文件信息 51 | */ 52 | File updateVectorizedStatus(Long fileId, Boolean vectorized); 53 | 54 | /** 55 | * 查找未向量化的文件 56 | * 57 | * @return 未向量化的文件列表 58 | */ 59 | List findNonVectorizedFiles(); 60 | } -------------------------------------------------------------------------------- /readify_agi/app/models/project.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | from typing import Optional 3 | from sqlalchemy import Column, Integer, String, Boolean, BigInteger, Text, ForeignKey 4 | from sqlalchemy.ext.declarative import declarative_base 5 | 6 | Base = declarative_base() 7 | 8 | class ProjectDB(Base): 9 | """项目数据库模型""" 10 | __tablename__ = "project" 11 | 12 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键ID") 13 | user_id = Column(BigInteger, nullable=False, comment="用户ID") 14 | name = Column(String(100), nullable=False, comment="工程名称") 15 | description = Column(Text, nullable=True, comment="工程描述") 16 | create_time = Column(BigInteger, nullable=False, comment="创建时间") 17 | update_time = Column(BigInteger, nullable=False, comment="更新时间") 18 | deleted = Column(Boolean, default=False, nullable=False, comment="是否删除") 19 | 20 | def __repr__(self): 21 | return f"" 22 | 23 | 24 | class ProjectCreate(BaseModel): 25 | """创建项目请求模型""" 26 | user_id: int 27 | name: str 28 | description: Optional[str] = None 29 | 30 | 31 | class ProjectResponse(BaseModel): 32 | """项目响应模型""" 33 | id: int 34 | user_id: int 35 | name: str 36 | description: Optional[str] = None 37 | create_time: int 38 | update_time: int 39 | 40 | class Config: 41 | from_attributes = True -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/config/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.config; 2 | 3 | import com.readify.server.websocket.ReadifyWebSocketHandler; 4 | import com.readify.server.websocket.WebSocketAuthInterceptor; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.web.socket.config.annotation.EnableWebSocket; 9 | import org.springframework.web.socket.config.annotation.WebSocketConfigurer; 10 | import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; 11 | 12 | @Slf4j 13 | @Configuration 14 | @EnableWebSocket 15 | @RequiredArgsConstructor 16 | public class WebSocketConfig implements WebSocketConfigurer { 17 | 18 | private final ReadifyWebSocketHandler readifyWebSocketHandler; 19 | private final WebSocketAuthInterceptor webSocketAuthInterceptor; 20 | 21 | @Override 22 | public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { 23 | // 注意:spring.mvc.servlet.path: /api/v1 会自动添加前缀 24 | // 所以这里只需要配置相对路径,不需要包含/api/v1 25 | log.info("Registering WebSocket handler at path: /ws/readify (实际访问路径: /api/v1/ws/readify)"); 26 | 27 | registry.addHandler(readifyWebSocketHandler, "/ws/readify") 28 | .addInterceptors(webSocketAuthInterceptor) 29 | .setAllowedOrigins("*"); 30 | } 31 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/project/repository/ProjectRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.project.repository; 2 | 3 | import com.readify.server.domain.project.model.Project; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public interface ProjectRepository { 9 | /** 10 | * 保存工程信息 11 | * 12 | * @param project 工程信息 13 | * @return 保存后的工程信息 14 | */ 15 | Project save(Project project); 16 | 17 | /** 18 | * 根据ID查找工程 19 | * 20 | * @param id 工程ID 21 | * @return 工程信息 22 | */ 23 | Optional findById(Long id); 24 | 25 | /** 26 | * 根据名称查找工程 27 | * 28 | * @param name 工程名称 29 | * @return 工程信息 30 | */ 31 | Optional findByName(String name); 32 | 33 | /** 34 | * 获取所有工程 35 | * 36 | * @return 工程列表 37 | */ 38 | List findAll(); 39 | 40 | /** 41 | * 获取用户的所有工程 42 | * 43 | * @param userId 用户ID 44 | * @return 工程列表 45 | */ 46 | List findByUserId(Long userId); 47 | 48 | /** 49 | * 根据ID删除工程 50 | * 51 | * @param id 工程ID 52 | */ 53 | void deleteById(Long id); 54 | 55 | /** 56 | * 检查工程名称是否已存在 57 | * 58 | * @param name 工程名称 59 | * @param userId 用户ID 60 | * @return true 如果名称已存在,否则返回 false 61 | */ 62 | boolean existsByNameAndUserId(String name, Long userId); 63 | } -------------------------------------------------------------------------------- /readify_server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | driver-class-name: com.mysql.cj.jdbc.Driver 4 | url: jdbc:mysql://localhost:3306/readify?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true 5 | username: root 6 | password: 7 | mvc: 8 | servlet: 9 | path: /api/v1 10 | servlet: 11 | multipart: 12 | max-file-size: 100MB 13 | max-request-size: 100MB 14 | 15 | logging: 16 | level: 17 | com.readify.server.infrastructure.security: DEBUG 18 | com.readify.server.domain.auth.service: DEBUG 19 | org.springframework.security: DEBUG 20 | 21 | jwt: 22 | # JWT密钥,至少32位 23 | secret: 8Zz5tw0Ionm3XPZZfN0NOml3z9FMfmpgXwovR9fp6ryDIoGRM8EPHAB6iHsc0fb 24 | # token有效期为24小时 25 | validity-in-seconds: 86400 26 | 27 | mybatis-plus: 28 | configuration: 29 | map-underscore-to-camel-case: true 30 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 31 | global-config: 32 | db-config: 33 | id-type: auto 34 | logic-delete-field: deleted 35 | logic-delete-value: 1 36 | logic-not-delete-value: 0 37 | mapper-locations: classpath*:/mapper/**/*.xml 38 | type-aliases-package: com.readify.server.infrastructure.persistence.entity 39 | 40 | readify: 41 | file: 42 | storage-path: D:\it\workspace\readify_parent\readify_server\files 43 | vector-service: 44 | url: http://localhost:8090 45 | -------------------------------------------------------------------------------- /readify_agi/app/models/conversation.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, Boolean, DateTime, Enum, BigInteger 2 | from sqlalchemy.dialects.mysql import LONGTEXT 3 | from sqlalchemy.sql import func 4 | 5 | from app.core.database import Base 6 | 7 | 8 | class ConversationHistoryDB(Base): 9 | __tablename__ = "conversation_history" 10 | 11 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键ID") 12 | project_id = Column(BigInteger, nullable=False, index=True, comment="工程ID") 13 | message_type = Column( 14 | Enum('system', 'user', 'assistant'), 15 | nullable=False, 16 | index=True, 17 | comment="消息类型:系统消息/用户问题/助手消息" 18 | ) 19 | content = Column(LONGTEXT, nullable=False, comment="消息内容") 20 | priority = Column(Integer, nullable=False, default=1, comment="优先级:数值越大优先级越高,裁剪时优先保留") 21 | is_included_in_context = Column(Boolean, nullable=False, default=True, comment="是否包含在上下文中:0-不包含,1-包含") 22 | sequence = Column(Integer, nullable=False, default=0, index=True, comment="对话序号,同一会话中的排序") 23 | created_at = Column(DateTime, nullable=False, server_default=func.now(), index=True, comment="创建时间") 24 | updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now(), comment="更新时间") 25 | 26 | def __repr__(self): 27 | return f"" -------------------------------------------------------------------------------- /readify_agi/app/models/document.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, ConfigDict 2 | from sqlalchemy import Column, BigInteger, Text, Integer, ForeignKey, Boolean 3 | 4 | from app.core.database import Base 5 | 6 | 7 | class DocumentDB(Base): 8 | """文档解析内容数据库模型""" 9 | __tablename__ = "document" 10 | 11 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键ID") 12 | file_id = Column(BigInteger, ForeignKey("file.id"), nullable=False, comment="关联的文件ID") 13 | content = Column(Text, nullable=False, comment="解析的文本内容") 14 | label = Column(Text, nullable=True, comment="文本内容概括标签") 15 | sequence = Column(Integer, nullable=False, comment="文档块序号") 16 | create_time = Column(BigInteger, nullable=False, comment="创建时间") 17 | update_time = Column(BigInteger, nullable=False, comment="更新时间") 18 | deleted = Column(Boolean, nullable=False, default=False, comment="是否删除") 19 | 20 | # 删除关联关系 21 | # file = relationship("FileDB", back_populates="documents") 22 | 23 | class DocumentBase(BaseModel): 24 | """文档基础模型""" 25 | model_config = ConfigDict(from_attributes=True) 26 | 27 | file_id: int 28 | content: str 29 | sequence: int 30 | label: str = None 31 | 32 | class DocumentCreate(DocumentBase): 33 | """文档创建模型""" 34 | pass 35 | 36 | class DocumentResponse(DocumentBase): 37 | """文档响应模型""" 38 | id: int 39 | create_time: int 40 | update_time: int 41 | deleted: bool = False -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/MindMapNodeEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableLogic; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.Data; 8 | 9 | @Data 10 | @TableName("mind_map_node") 11 | public class MindMapNodeEntity { 12 | /** 13 | * 节点唯一标识 14 | */ 15 | @TableId(type = IdType.AUTO) 16 | private Long id; 17 | 18 | /** 19 | * 项目ID 20 | */ 21 | private Long projectId; 22 | 23 | /** 24 | * 所属文件ID 25 | */ 26 | private Long fileId; 27 | 28 | /** 29 | * 所属思维导图ID 30 | */ 31 | private Long mindMapId; 32 | 33 | /** 34 | * 父节点ID,根节点为NULL 35 | */ 36 | private Long parentId; 37 | 38 | /** 39 | * 节点内容 40 | */ 41 | private String content; 42 | 43 | /** 44 | * 同级节点间的排序顺序 45 | */ 46 | private Integer sequence; 47 | 48 | /** 49 | * 节点层级,根节点为0 50 | */ 51 | private Integer level; 52 | 53 | /** 54 | * 创建时间 55 | */ 56 | private Long createdTime; 57 | 58 | /** 59 | * 更新时间 60 | */ 61 | private Long updatedTime; 62 | 63 | /** 64 | * 是否删除,0-未删除,1-已删除 65 | */ 66 | @TableLogic 67 | private Boolean deleted; 68 | } -------------------------------------------------------------------------------- /readify_frontend/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/conversation/vo/ConversationVO.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.conversation.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.Builder; 6 | import lombok.NoArgsConstructor; 7 | import lombok.AllArgsConstructor; 8 | 9 | import java.time.LocalDateTime; 10 | 11 | @Data 12 | @Builder 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @Schema(description = "对话记录视图对象") 16 | public class ConversationVO { 17 | @Schema(description = "对话ID", example = "1") 18 | private Long id; 19 | 20 | @Schema(description = "项目ID", example = "100") 21 | private Long projectId; 22 | 23 | @Schema(description = "消息类型: SYSTEM/USER/ASSISTANT", example = "USER") 24 | private String messageType; 25 | 26 | @Schema(description = "消息内容") 27 | private String content; 28 | 29 | @Schema(description = "优先级: 数值越大优先级越高", example = "1") 30 | private Integer priority; 31 | 32 | @Schema(description = "是否包含在上下文中: true-包含, false-不包含", example = "true") 33 | private Boolean isIncludedInContext; 34 | 35 | @Schema(description = "对话序号", example = "1") 36 | private Integer sequence; 37 | 38 | @Schema(description = "创建时间", example = "2025-03-07T12:00:00") 39 | private LocalDateTime createdAt; 40 | 41 | // 仅用户消息包含思考过程 42 | @Schema(description = "AI思考过程,仅用户消息包含") 43 | private AssistantThinkingVO thinking; 44 | } -------------------------------------------------------------------------------- /readify_agi/app/config/agent_names.py: -------------------------------------------------------------------------------- 1 | """ 2 | 专业智能体的名称和描述配置文件 3 | 4 | 这个文件定义了系统中所有专业智能体的名称和描述,便于统一管理和引用。 5 | 使用这些名称替代代码中的硬编码字符串,可以提高代码的可维护性。 6 | """ 7 | 8 | # 智能体名称常量 9 | class AgentNames: 10 | # 协调器智能体 11 | COORDINATOR = "COORDINATOR" 12 | 13 | # 问题回答智能体 14 | QUESTIONER = "QUESTIONER" 15 | 16 | # 笔记智能体 17 | NOTE_AGENT = "NOTE_AGENT" 18 | 19 | 20 | # 智能体描述 21 | AGENT_DESCRIPTIONS = { 22 | AgentNames.COORDINATOR: "负责分析用户需求并协调调度其他专业智能体,合理安排工作流程", 23 | AgentNames.QUESTIONER: "专注于回答用户问题,善于从文档中检索相关信息提供精准解答", 24 | AgentNames.NOTE_AGENT: "专注于处理文档笔记和内容摘要,提供智能笔记管理和关键信息提取服务", 25 | } 26 | 27 | def is_agent_name(name: str) -> bool: 28 | """ 29 | 判断给定的字符串是否为有效的智能体名称 30 | 31 | Args: 32 | name: 要判断的字符串 33 | 34 | Returns: 35 | bool: 如果给定的字符串为有效的智能体名称,则返回True,否则返回False 36 | """ 37 | return name in get_all_agent_names() 38 | 39 | # 获取所有可用的智能体名称列表 40 | def get_all_agent_names(): 41 | """ 42 | 获取所有可用的智能体名称列表 43 | 44 | Returns: 45 | list: 智能体名称列表 46 | """ 47 | return [getattr(AgentNames, attr) for attr in dir(AgentNames) 48 | if not attr.startswith('__') and isinstance(getattr(AgentNames, attr), str)] 49 | 50 | # 获取智能体描述 51 | def get_agent_description(agent_name): 52 | """ 53 | 获取指定智能体的描述 54 | 55 | Args: 56 | agent_name: 智能体名称 57 | 58 | Returns: 59 | str: 智能体描述,如果找不到则返回默认描述 60 | """ 61 | return AGENT_DESCRIPTIONS.get(agent_name, "未提供描述的专业智能体") -------------------------------------------------------------------------------- /readify_agi/app/models/repair_document.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, BigInteger, String, Text, Integer, ForeignKey, Boolean 2 | from sqlalchemy.orm import relationship 3 | from pydantic import BaseModel, ConfigDict 4 | from typing import Optional 5 | from app.core.database import Base 6 | 7 | class RepairDocumentDB(Base): 8 | """修复后的文档内容数据库模型""" 9 | __tablename__ = "repair_document" 10 | 11 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键ID") 12 | file_id = Column(BigInteger, ForeignKey("file.id"), nullable=False, comment="关联的文件ID") 13 | content = Column(Text, nullable=False, comment="修复后的文本内容") 14 | sequence = Column(Integer, nullable=False, comment="文档块序号") 15 | create_time = Column(BigInteger, nullable=False, comment="创建时间") 16 | update_time = Column(BigInteger, nullable=False, comment="更新时间") 17 | deleted = Column(Boolean, nullable=False, default=False, comment="是否删除") 18 | 19 | # 删除关联关系 20 | # file = relationship("FileDB", back_populates="repair_documents") 21 | 22 | class RepairDocumentBase(BaseModel): 23 | """修复文档基础模型""" 24 | model_config = ConfigDict(from_attributes=True) 25 | 26 | file_id: int 27 | content: str 28 | sequence: int 29 | 30 | class RepairDocumentCreate(RepairDocumentBase): 31 | """修复文档创建模型""" 32 | pass 33 | 34 | class RepairDocumentResponse(RepairDocumentBase): 35 | """修复文档响应模型""" 36 | id: int 37 | create_time: int 38 | update_time: int 39 | deleted: bool = False -------------------------------------------------------------------------------- /readify_agi/prompt/ask_agent.prompt: -------------------------------------------------------------------------------- 1 | # Role: 智能体问答助手 2 | 3 | ## Profile 4 | - 名称: 读书助手问答智能体 5 | - 角色: 专业读书助手和文献分析专家 6 | - 专业领域: 文本理解、知识提取、阅读分析和内容讲解 7 | 8 | ## Goals 9 | - 帮助用户深入理解书籍和文档内容 10 | - 提供精准的文本解析和知识提取 11 | - 回答用户关于文档内容的问题 12 | - 分析文本中的关键概念和主题 13 | 14 | ## Skills 15 | - 文本理解与摘要能力 16 | - 文档内容检索与定位 17 | - 上下文理解与关联分析 18 | - 知识点提取与解释 19 | - 回答用户的问题 20 | 21 | ## Constraints 22 | - 始终以中文回答用户问题 23 | - 当用户的问题仅涉及单一或少量知识点时,不要读取全文或分页读取,请使用向量检索和搜索引擎 24 | - 回答必须使用key: Value的形式,比如:Thought: 思考内容 / Action: 工具名称/ Action Input: 入参/ Observation: 观察结果/ Final Answer: 最终回答 25 | - 最终回答必须在最后,且必须存在 Final Answer: 26 | - 回答须基于实际内容,优先检索文档,如没检索到信息则使用浏览器搜索,不臆测或添加不存在的信息 27 | - 保持客观,不带个人偏见进行分析 28 | 29 | ## Tools 30 | {tools} 31 | 32 | ## WorkingProcess 33 | 你有权限使用以下工具: 34 | [{tool_names}] 35 | 36 | 使用以下步骤与格式严格回应: 37 | 38 | 1. Question: 你需要回答的输入问题。 39 | 2. Thought: 你应该始终思考下一步行动。示例: Thought: 我需要分析这个问题并确定需要哪个专业智能体来处理。 40 | 3. Action: 要采取的行动,应为[{tool_names}]中的一个。示例: Action: delegate_task。 41 | 4. Action Input: 行动的输入参数。 42 | 5. Observation: action执行结束后返回的结果。 43 | ... (这个Thought/Action/Action Input/Observation可以重复多次) 44 | 6. Thought: 现在我知道最终答案了。 45 | 7. Final Answer: 对原始输入问题的最终详细回答。 46 | 47 | ## Guidelines 48 | 1. 思考过程应考虑当前的项目上下文,包括项目名称和描述 49 | 2. 在不需要全文数据时,优先使用向量检索;如未找到需要的数据,再使用搜索引擎和自身知识回答 50 | 3. 阅读分析时注重文本结构和逻辑关系,提取核心观点和支撑证据 51 | 4. 回答问题时,先给出简明直接的回答,再提供详细解释和相关引用 52 | 53 | ## Current Context 54 | 当前工程信息:project_id: {project_id}, name: {project_name}, description: {project_description} 55 | 56 | {history} 57 | 58 | 开始! 59 | 60 | Question: {input} 61 | Thought:{agent_scratchpad} -------------------------------------------------------------------------------- /readify_agi/prompt/coordinator.prompt: -------------------------------------------------------------------------------- 1 | # Role: 智能体协调器 2 | 3 | ## Profile 4 | - 名称: 协调器 5 | - 角色: 专业智能体协调管理专家 6 | - 专业领域:任务分配、多智能体协作管理、问题分解与综合 7 | 8 | ## Goals 9 | - 分析用户查询,确定最适合处理该任务的专业智能体 10 | - 将任务委派给合适的专业智能体 11 | - 如果需要多个智能体协作,将任务分解并按顺序委派 12 | - 整合各智能体的输出,提供连贯的最终答案 13 | - **没有合适的Agent处理时,交给QUESTIONER解决** 14 | 15 | ## Skills 16 | - 问题分析与分解能力 17 | - 任务分配与管理 18 | - 结果整合与综合 19 | - 多智能体协作调度 20 | 21 | ## Constraints 22 | - 始终以中文回答用户问题 23 | - 回答必须使用key: Value的形式,比如:Thought: 思考内容 / Action: 工具名称/ Action Input: 入参/ Observation: 观察结果/ Final Answer: 最终回答 24 | - Action Input: **入参后禁止跟Observation** 25 | - Final Answer必须在最后且必须存在, 包含完整的答案而不是片段 26 | - 根据任务性质选择最合适的专业智能体 27 | - 对于复杂任务,应创建合理的工作流程安排多个智能 体协作 28 | 29 | ## Task Type 30 | 用户可以做的任务分为以下两类: 31 | 1. ask 问答任务 32 | 2. node 笔记生成任务 33 | 当前用户需要处理的任务类型: {task_type} 34 | 35 | ## Tools 36 | {tools} 37 | 38 | ## WorkingProcess 39 | 你有权限使用以下工具: 40 | [{tool_names}] 41 | 42 | 使用以下步骤与格式严格回应: 43 | 44 | 1. Question: 你需要回答的输入问题。 45 | 2. Thought: 你应该始终思考下一步行动。示例: Thought: 我需要分析这个问题并确定需要哪个专业智能体来处理。 46 | 3. Action: 要采取的行动,应为[{tool_names}]中的一个。示例: Action: delegate_task。 47 | 4. Action Input: 行动的输入参数。 48 | 5. Observation: action执行结束后返回的结果。 49 | ... (这个Thought/Action/Action Input/Observation可以重复多次) 50 | 6. Thought: 现在我知道最终答案了。 51 | 7. Final Answer: 对原始输入问题的最终详细回答。 52 | 53 | ## Current Context 54 | 目前可用的专业智能体: {available_agents} 55 | 当前工程信息: project_id: {project_id}, name: {project_name}, description: {project_description} 56 | 当前扩展信息: {context} 57 | 58 | {history} 59 | 60 | 开始! 61 | 62 | Question: {input} 63 | Thought:{agent_scratchpad} -------------------------------------------------------------------------------- /readify_frontend/src/api/project.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import type { ProjectVO } from '@/types/project' 3 | import type { ApiResponse } from '@/types/response' 4 | 5 | // 获取我的工程列表 6 | export const getMyProjects = () => { 7 | return request>({ 8 | url: '/projects/my', 9 | method: 'get' 10 | }) 11 | } 12 | 13 | // 创建工程 14 | export const createProject = (data: Partial) => { 15 | return request>({ 16 | url: '/projects', 17 | method: 'post', 18 | data 19 | }) 20 | } 21 | 22 | // 更新工程 23 | export const updateProject = (id: number, data: Partial) => { 24 | return request>({ 25 | url: `/projects/${id}`, 26 | method: 'put', 27 | data 28 | }) 29 | } 30 | 31 | // 删除工程 32 | export const deleteProject = (id: number) => { 33 | return request>({ 34 | url: `/projects/${id}`, 35 | method: 'delete' 36 | }) 37 | } 38 | 39 | // 获取项目文件列表 40 | export const getProjectFiles = (projectId: number) => { 41 | return request>({ 42 | url: `/projects/${projectId}/files`, 43 | method: 'get' 44 | }) 45 | } 46 | 47 | // 获取项目详情 48 | export const getProjectById = (id: number) => { 49 | return request>({ 50 | url: `/projects/${id}`, 51 | method: 'get' 52 | }) 53 | } 54 | 55 | // 定义文件VO类型 56 | export interface FileVO { 57 | id: number 58 | originalName: string 59 | mimeType: string 60 | size: number 61 | createTime: number 62 | updateTime: number 63 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/security/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.security; 2 | 3 | import com.readify.server.infrastructure.common.exception.UnauthorizedException; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | @Slf4j 7 | public class SecurityUtils { 8 | private static final ThreadLocal userInfoHolder = new ThreadLocal<>(); 9 | 10 | /** 11 | * 设置当前用户信息 12 | * 13 | * @param userInfo 用户信息 14 | */ 15 | public static void setCurrentUser(UserInfo userInfo) { 16 | userInfoHolder.set(userInfo); 17 | } 18 | 19 | /** 20 | * 获取当前用户信息 21 | * 22 | * @return 用户信息 23 | * @throws UnauthorizedException 如果用户未登录 24 | */ 25 | public static UserInfo getCurrentUser() { 26 | UserInfo userInfo = userInfoHolder.get(); 27 | if (userInfo == null) { 28 | throw new UnauthorizedException("用户未登录"); 29 | } 30 | return userInfo; 31 | } 32 | 33 | /** 34 | * 获取当前用户ID 35 | * 36 | * @return 用户ID 37 | * @throws UnauthorizedException 如果用户未登录 38 | */ 39 | public static Long getCurrentUserId() { 40 | return getCurrentUser().getId(); 41 | } 42 | 43 | /** 44 | * 获取当前用户名 45 | * 46 | * @return 用户名 47 | * @throws UnauthorizedException 如果用户未登录 48 | */ 49 | public static String getCurrentUsername() { 50 | return getCurrentUser().getUsername(); 51 | } 52 | 53 | /** 54 | * 清除当前用户信息 55 | */ 56 | public static void clearCurrentUser() { 57 | userInfoHolder.remove(); 58 | } 59 | } -------------------------------------------------------------------------------- /readify_agi/app/models/file.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, BigInteger, String, Boolean 2 | from pydantic import BaseModel, ConfigDict 3 | from typing import Optional, List 4 | from app.core.database import Base 5 | 6 | class FileDB(Base): 7 | """文件数据库模型""" 8 | __tablename__ = "file" 9 | 10 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键ID") 11 | original_name = Column(String(255), nullable=False, comment="原始文件名") 12 | storage_name = Column(String(100), nullable=False, comment="存储文件名") 13 | size = Column(BigInteger, nullable=False, comment="文件大小(字节)") 14 | mime_type = Column(String(100), comment="文件MIME类型") 15 | storage_path = Column(String(500), nullable=False, comment="存储路径") 16 | md5 = Column(String(32), comment="文件MD5值") 17 | create_time = Column(BigInteger, nullable=False, comment="创建时间") 18 | update_time = Column(BigInteger, nullable=False, comment="更新时间") 19 | deleted = Column(Boolean, nullable=False, default=False, comment="是否删除") 20 | vectorized = Column(Boolean, nullable=False, default=False, comment="是否已向量化") 21 | 22 | class FileBase(BaseModel): 23 | """文件基础模型""" 24 | model_config = ConfigDict(from_attributes=True) 25 | 26 | original_name: str 27 | storage_name: str 28 | size: int 29 | mime_type: Optional[str] = None 30 | storage_path: str 31 | md5: Optional[str] = None 32 | 33 | class FileCreate(FileBase): 34 | """文件创建模型""" 35 | pass 36 | 37 | class FileResponse(FileBase): 38 | """文件响应模型""" 39 | id: int 40 | create_time: int 41 | update_time: int 42 | deleted: bool = False 43 | vectorized: bool = False -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/notetask/repository/NoteTaskRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.notetask.repository; 2 | 3 | import com.readify.server.domain.notetask.model.NoteTask; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | /** 9 | * 笔记任务仓储接口 10 | */ 11 | public interface NoteTaskRepository { 12 | 13 | /** 14 | * 保存笔记任务 15 | * 16 | * @param noteTask 笔记任务 17 | * @return 保存后的笔记任务 18 | */ 19 | NoteTask save(NoteTask noteTask); 20 | 21 | /** 22 | * 根据ID查询笔记任务 23 | * 24 | * @param id 笔记任务ID 25 | * @return 笔记任务 26 | */ 27 | Optional findById(Long id); 28 | 29 | /** 30 | * 根据用户ID查询笔记任务列表 31 | * 32 | * @param userId 用户ID 33 | * @return 笔记任务列表 34 | */ 35 | List findByUserId(Long userId); 36 | 37 | /** 38 | * 根据项目ID查询笔记任务列表 39 | * 40 | * @param projectId 项目ID 41 | * @return 笔记任务列表 42 | */ 43 | List findByProjectId(Long projectId); 44 | 45 | /** 46 | * 根据思维导图ID查询笔记任务列表 47 | * 48 | * @param mindMapId 思维导图ID 49 | * @return 笔记任务列表 50 | */ 51 | List findByMindMapId(Long mindMapId); 52 | 53 | /** 54 | * 根据文件ID查询笔记任务列表 55 | * 56 | * @param fileId 文件ID 57 | * @return 笔记任务列表 58 | */ 59 | List findByFileId(Long fileId); 60 | 61 | /** 62 | * 更新笔记任务 63 | * 64 | * @param noteTask 笔记任务 65 | * @return 更新后的笔记任务 66 | */ 67 | NoteTask update(NoteTask noteTask); 68 | 69 | /** 70 | * 根据ID删除笔记任务 71 | * 72 | * @param id 笔记任务ID 73 | */ 74 | void deleteById(Long id); 75 | } -------------------------------------------------------------------------------- /readify_agi/prompt/note_agent.prompt: -------------------------------------------------------------------------------- 1 | # Role: 智能体读书笔记专家 2 | 3 | ## Profile 4 | - 名称: 智能读书助手读书笔记专家 5 | - 角色: 专业读书助手和文献分析专家 6 | - 专业领域: 文本理解、知识提取、读书笔记整理 7 | 8 | ## Goals 9 | - 根据用户的描述对当前的思维导图笔记进行整理、补充和修正 10 | - 帮助用户构建系统化的知识框架 11 | - 提炼文本核心观点和关键信息 12 | - 生成结构化的读书笔记和思维导图 13 | - 连接新旧知识点,形成知识网络 14 | 15 | ## Skills 16 | - 文本内容结构化与知识点提取 17 | - 笔记整理与知识框架构建 18 | - 概念关联与知识图谱绘制 19 | - 关键信息提炼与重点标注 20 | - 内容总结与知识体系化 21 | 22 | ## Constraints 23 | - 始终以中文回答用户问题 24 | - 尽量引用、概括和精炼原文中的内容,而不是自己创造 25 | - 回答必须使用key: Value的形式,比如:Thought: 思考内容 / Action: 工具名称/ Action Input: 入参/ Observation: 观察结果/ Final Answer: 最终回答 26 | - 最终回答必须在最后,且必须存在 Final Answer: 27 | - 回答须基于实际内容,优先检索文档,如没检索到信息则使用浏览器搜索,不臆测或添加不存在的信息 28 | - 保持客观,不带个人偏见进行分析 29 | 30 | ## Tools 31 | {tools} 32 | 33 | ## WorkingProcess 34 | 你有权限使用以下工具: 35 | [{tool_names}] 36 | 37 | 使用以下步骤与格式严格回应: 38 | 39 | 1. Question: 你需要回答的输入问题。 40 | 2. Thought: 你应该始终思考下一步行动。示例: Thought: 我需要分析这个问题并确定如何最好地整理相关笔记。 41 | 3. Action: 要采取的行动,应为[{tool_names}]中的一个。示例: Action: retrieve_document。 42 | 4. Action Input: 行动的输入参数。 43 | 5. Observation: action执行结束后返回的结果。 44 | ... (这个Thought/Action/Action Input/Observation可以重复多次) 45 | 6. Thought: 现在我知道最终答案了。 46 | 7. Final Answer: 对原始输入问题的最终详细回答。 47 | 48 | ## Guidelines 49 | 1. 在不需要全文数据时,优先使用向量检索;如未找到需要的数据,再使用搜索引擎和自身知识回答 50 | 2. 整理笔记时注重层次结构和逻辑关系,建立清晰的知识框架 51 | 3. 优先使用结构化形式(如列表、表格、树形结构)组织笔记内容 52 | 4. 识别并突出核心概念、关键术语和重要观点 53 | 5. 尝试将新知识与用户已有知识建立联系,形成更完整的知识网络 54 | 6. 在笔记中添加相关引用和出处,确保信息可追溯 55 | 56 | ## Current Context 57 | 当前工程信息:project_id: {project_id}, name: {project_name}, description: {project_description} 58 | 当前思维导图信息:mind_map_id: {mind_map_id}, title: {mind_map_title}, description: {mind_map_description}, file_id: {file_id} 59 | 60 | {history} 61 | 62 | 开始! 63 | 64 | Question: {input} 65 | Thought:{agent_scratchpad} -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/mind_map/repository/MindMapRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.mind_map.repository; 2 | 3 | import com.readify.server.domain.mind_map.model.MindMap; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public interface MindMapRepository { 9 | /** 10 | * 保存思维导图信息 11 | * 12 | * @param mindMap 思维导图信息 13 | * @return 保存后的思维导图信息 14 | */ 15 | MindMap save(MindMap mindMap); 16 | 17 | /** 18 | * 根据ID查找思维导图 19 | * 20 | * @param id 思维导图ID 21 | * @return 思维导图信息 22 | */ 23 | Optional findById(Long id); 24 | 25 | /** 26 | * 根据标题和用户ID查找思维导图 27 | * 28 | * @param title 思维导图标题 29 | * @param userId 用户ID 30 | * @return 思维导图信息 31 | */ 32 | Optional findByTitleAndUserId(String title, Long userId); 33 | 34 | /** 35 | * 获取用户的所有思维导图 36 | * 37 | * @param userId 用户ID 38 | * @return 思维导图列表 39 | */ 40 | List findByUserId(Long userId); 41 | 42 | /** 43 | * 获取项目下的所有思维导图 44 | * 45 | * @param projectId 项目ID 46 | * @return 思维导图列表 47 | */ 48 | List findByProjectId(Long projectId); 49 | 50 | /** 51 | * 根据ID和用户ID删除思维导图(逻辑删除) 52 | * 53 | * @param id 思维导图ID 54 | * @param userId 用户ID 55 | * @return 受影响的行数 56 | */ 57 | int deleteByIdAndUserId(Long id, Long userId); 58 | 59 | /** 60 | * 检查思维导图标题是否已存在 61 | * 62 | * @param title 思维导图标题 63 | * @param projectId 项目ID 64 | * @param userId 用户ID 65 | * @return true 如果标题已存在,否则返回 false 66 | */ 67 | boolean existsByTitleAndProjectIdAndUserId(String title, Long projectId, Long userId); 68 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/handler/impl/BroadcastMessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.handler.impl; 2 | 3 | import com.readify.server.websocket.WebSocketSessionManager; 4 | import com.readify.server.websocket.handler.WebSocketMessageHandler; 5 | import com.readify.server.websocket.message.WebSocketMessage; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.socket.WebSocketSession; 10 | 11 | /** 12 | * 广播消息处理器 13 | */ 14 | @Slf4j 15 | @Component 16 | @RequiredArgsConstructor 17 | public class BroadcastMessageHandler implements WebSocketMessageHandler { 18 | 19 | private final WebSocketSessionManager sessionManager; 20 | 21 | @Override 22 | public String supportType() { 23 | return "broadcast"; 24 | } 25 | 26 | @Override 27 | public Class getDataType() { 28 | return String.class; 29 | } 30 | 31 | @Override 32 | public void processMessage(WebSocketSession session, WebSocketMessage message) { 33 | Long userId = getUserIdFromSession(session); 34 | log.debug("Processing broadcast message from user {}, session: {}", userId, session.getId()); 35 | 36 | // 在广播消息中添加发送者信息 37 | WebSocketMessage broadcastMessage = WebSocketMessage.create( 38 | "broadcast", 39 | String.format("Message from user %d: %s", userId, message.getData()) 40 | ); 41 | sessionManager.broadcastMessage(broadcastMessage); 42 | } 43 | 44 | private Long getUserIdFromSession(WebSocketSession session) { 45 | return (Long) session.getAttributes().get("userId"); 46 | } 47 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/common/Result.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.common; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Schema(description = "通用返回结果") 8 | public class Result { 9 | 10 | @Schema(description = "状态码", example = "200") 11 | private String code; 12 | 13 | @Schema(description = "提示信息", example = "操作成功") 14 | private String message; 15 | 16 | @Schema(description = "返回数据", nullable = true) 17 | private T data; 18 | 19 | public static Result success() { 20 | return success(null); 21 | } 22 | 23 | public static Result success(String message) { 24 | Result result = new Result<>(); 25 | result.setCode("200"); 26 | result.setMessage(message != null ? message : "操作成功"); 27 | return result; 28 | } 29 | 30 | public static Result success(T data) { 31 | Result result = new Result<>(); 32 | result.setCode("200"); 33 | result.setMessage("操作成功"); 34 | result.setData(data); 35 | return result; 36 | } 37 | 38 | public static Result success(String message, T data) { 39 | Result result = new Result<>(); 40 | result.setCode("200"); 41 | result.setMessage(message); 42 | result.setData(data); 43 | return result; 44 | } 45 | 46 | public static Result error(String code, String message) { 47 | Result result = new Result<>(); 48 | result.setCode(code); 49 | result.setMessage(message); 50 | return result; 51 | } 52 | 53 | public static Result error(String message) { 54 | return error("500", message); 55 | } 56 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/mapper/NoteTaskMapper.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.readify.server.infrastructure.persistence.entity.NoteTaskEntity; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | import org.apache.ibatis.annotations.Select; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 笔记任务Mapper接口 13 | */ 14 | @Mapper 15 | public interface NoteTaskMapper extends BaseMapper { 16 | 17 | /** 18 | * 根据用户ID查询笔记任务列表 19 | * 20 | * @param userId 用户ID 21 | * @return 笔记任务列表 22 | */ 23 | @Select("SELECT * FROM note_task WHERE user_id = #{userId} AND deleted = 0") 24 | List findByUserId(@Param("userId") Long userId); 25 | 26 | /** 27 | * 根据项目ID查询笔记任务列表 28 | * 29 | * @param projectId 项目ID 30 | * @return 笔记任务列表 31 | */ 32 | @Select("SELECT * FROM note_task WHERE project_id = #{projectId} AND deleted = 0") 33 | List findByProjectId(@Param("projectId") Long projectId); 34 | 35 | /** 36 | * 根据思维导图ID查询笔记任务列表 37 | * 38 | * @param mindMapId 思维导图ID 39 | * @return 笔记任务列表 40 | */ 41 | @Select("SELECT * FROM note_task WHERE mind_map_id = #{mindMapId} AND deleted = 0") 42 | List findByMindMapId(@Param("mindMapId") Long mindMapId); 43 | 44 | /** 45 | * 根据文件ID查询笔记任务列表 46 | * 47 | * @param fileId 文件ID 48 | * @return 笔记任务列表 49 | */ 50 | @Select("SELECT * FROM note_task WHERE file_id = #{fileId} AND deleted = 0") 51 | List findByFileId(@Param("fileId") Long fileId); 52 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/project/service/ProjectService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.project.service; 2 | 3 | import com.readify.server.domain.project.model.Project; 4 | 5 | import java.util.List; 6 | 7 | public interface ProjectService { 8 | /** 9 | * 创建新工程 10 | * 11 | * @param project 工程信息 12 | * @param userId 用户ID 13 | * @return 创建后的工程信息 14 | */ 15 | Project createProject(Project project, Long userId); 16 | 17 | /** 18 | * 更新工程信息 19 | * 20 | * @param project 工程信息 21 | * @param userId 用户ID 22 | * @return 更新后的工程信息 23 | */ 24 | Project updateProject(Project project, Long userId); 25 | 26 | /** 27 | * 删除工程 28 | * 29 | * @param id 工程ID 30 | * @param userId 用户ID 31 | */ 32 | void deleteProject(Long id, Long userId); 33 | 34 | /** 35 | * 根据ID获取工程信息 36 | * 37 | * @param id 工程ID 38 | * @param userId 用户ID 39 | * @return 工程信息 40 | */ 41 | Project getProjectById(Long id, Long userId); 42 | 43 | /** 44 | * 根据名称获取工程信息 45 | * 46 | * @param name 工程名称 47 | * @param userId 用户ID 48 | * @return 工程信息 49 | */ 50 | Project getProjectByName(String name, Long userId); 51 | 52 | /** 53 | * 获取所有工程 54 | * 55 | * @return 工程列表 56 | */ 57 | List getAllProjects(); 58 | 59 | /** 60 | * 获取用户的所有工程 61 | * 62 | * @param userId 用户ID 63 | * @return 工程列表 64 | */ 65 | List getUserProjects(Long userId); 66 | 67 | /** 68 | * 检查工程名称是否已存在 69 | * 70 | * @param name 工程名称 71 | * @param userId 用户ID 72 | * @return true 如果名称已存在,否则返回 false 73 | */ 74 | boolean isProjectNameExists(String name, Long userId); 75 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/entity/NoteTaskEntity.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * 笔记任务持久化实体 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @TableName("note_task") 17 | public class NoteTaskEntity { 18 | 19 | /** 20 | * 主键ID 21 | */ 22 | @TableId(value = "id", type = IdType.AUTO) 23 | private Long id; 24 | 25 | /** 26 | * 用户ID 27 | */ 28 | @TableField("user_id") 29 | private Long userId; 30 | 31 | /** 32 | * 关联项目ID 33 | */ 34 | @TableField("project_id") 35 | private Long projectId; 36 | 37 | /** 38 | * 关联的思维导图ID 39 | */ 40 | @TableField("mind_map_id") 41 | private Long mindMapId; 42 | 43 | /** 44 | * 关联的文件ID 45 | */ 46 | @TableField("file_id") 47 | private Long fileId; 48 | 49 | /** 50 | * 用户提问/任务内容 51 | */ 52 | @TableField("content") 53 | private String content; 54 | 55 | /** 56 | * 任务状态 57 | */ 58 | @TableField("status") 59 | private String status; 60 | 61 | /** 62 | * 任务结果 63 | */ 64 | @TableField("result") 65 | private String result; 66 | 67 | /** 68 | * 创建时间 69 | */ 70 | @TableField("create_time") 71 | private Long createTime; 72 | 73 | /** 74 | * 更新时间 75 | */ 76 | @TableField("update_time") 77 | private Long updateTime; 78 | 79 | /** 80 | * 是否删除 81 | */ 82 | @TableField("deleted") 83 | @TableLogic 84 | private Boolean deleted; 85 | } -------------------------------------------------------------------------------- /readify_frontend/src/utils/request.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { ElMessage } from 'element-plus' 3 | import { getAuthHeader, removeToken } from './auth' 4 | import type { ApiResponse } from '@/types/response' 5 | import router from '@/router' 6 | import store from '@/store' 7 | 8 | const request = axios.create({ 9 | baseURL: '/api', 10 | timeout: 10000 11 | }) 12 | 13 | // 请求拦截器 14 | request.interceptors.request.use( 15 | config => { 16 | const token = getAuthHeader() 17 | if (token) { 18 | config.headers.Authorization = token 19 | } 20 | return config 21 | }, 22 | error => { 23 | return Promise.reject(error) 24 | } 25 | ) 26 | 27 | // 响应拦截器 28 | request.interceptors.response.use( 29 | response => { 30 | const res = response.data as ApiResponse 31 | 32 | if (res.code === '200') { 33 | return res 34 | } 35 | 36 | // 处理未认证或认证过期的情况 37 | if (res.code === '401' || res.code === '403') { 38 | ElMessage.error('登录已过期,请重新登录') 39 | // 清除用户状态 40 | store.dispatch('logout') 41 | removeToken() 42 | // 跳转到登录页 43 | router.push('/login') 44 | return Promise.reject(new Error('未认证')) 45 | } 46 | 47 | // 统一处理其他错误 48 | ElMessage.error(res.message || '请求失败') 49 | return Promise.reject(new Error(res.message || '请求失败')) 50 | }, 51 | error => { 52 | // 处理网络错误等情况 53 | if (error.response) { 54 | const status = error.response.status 55 | if (status === 401 || status === 403) { 56 | ElMessage.error('登录已过期,请重新登录') 57 | store.dispatch('logout') 58 | removeToken() 59 | router.push('/login') 60 | return Promise.reject(new Error('未认证')) 61 | } 62 | } 63 | ElMessage.error(error.message || '请求失败') 64 | return Promise.reject(error) 65 | } 66 | ) 67 | 68 | export default request -------------------------------------------------------------------------------- /readify_agi/app/models/mind_map.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, ConfigDict 2 | from typing import Optional 3 | from sqlalchemy import Column, Integer, String, Boolean, BigInteger, Text, ForeignKey, Index 4 | from app.core.database import Base 5 | from datetime import datetime 6 | 7 | class MindMapDB(Base): 8 | """思维导图数据库模型""" 9 | __tablename__ = "mind_map" 10 | 11 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="思维导图ID") 12 | project_id = Column(BigInteger, nullable=False, comment="工程id") 13 | file_id = Column(BigInteger, nullable=False, comment="所属文件ID") 14 | title = Column(String(255), nullable=False, comment="思维导图标题") 15 | type = Column(String(10), nullable=False, comment="笔记类型") 16 | description = Column(Text, nullable=True, comment="思维导图描述") 17 | user_id = Column(BigInteger, nullable=False, comment="创建者用户ID") 18 | created_at = Column(BigInteger, nullable=False, comment="创建时间") 19 | updated_at = Column(BigInteger, nullable=False, comment="更新时间") 20 | is_deleted = Column(Boolean, nullable=False, default=False, comment="逻辑删除标记,0-未删除,1-已删除") 21 | 22 | # 索引 23 | __table_args__ = ( 24 | Index('idx_user_id', user_id), 25 | ) 26 | 27 | def __repr__(self): 28 | return f"" 29 | 30 | 31 | class MindMapCreate(BaseModel): 32 | """创建思维导图请求模型""" 33 | project_id: int 34 | file_id: int 35 | title: str 36 | type: str 37 | description: Optional[str] = None 38 | user_id: int 39 | 40 | 41 | class MindMapResponse(BaseModel): 42 | """思维导图响应模型""" 43 | model_config = ConfigDict(from_attributes=True) 44 | 45 | id: int 46 | project_id: int 47 | file_id: int 48 | title: str 49 | type: str 50 | description: Optional[str] = None 51 | user_id: int 52 | created_at: int 53 | updated_at: int -------------------------------------------------------------------------------- /readify_server/.cursor/rules/developer.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 编写代码 3 | globs: 4 | --- 5 | # Role: DDD架构Java开发助手 6 | 7 | ## Profile 8 | - Author: Assistant 9 | - Version: 1.0 10 | - Language: 中文 11 | - Description: 我是一位专注于DDD架构的Java开发助手,精通SpringBoot技术栈,能够帮助开发者进行代码开发、重构和优化。 12 | 13 | ## Background 14 | - 项目采用DDD(领域驱动设计)架构 15 | - 基于SpringBoot 4.2.2构建 16 | - 使用JDK 17 17 | - 使用Maven 3.9.3进行依赖管理 18 | - 数据库采用MySQL 8.0 19 | - ORM框架为MyBatis-Plus 3.5.5 20 | - 文档使用SpringDoc OpenAPI 21 | 22 | ## Skills 23 | 1. DDD分层架构设计与实现 24 | - 领域层(Domain Layer)开发 25 | - 基础设施层(Infrastructure Layer)实现 26 | - 接口层(Interface Layer)构建 27 | - 应用层(Application Layer)开发 28 | 29 | 2. 技术栈专长 30 | - SpringBoot 4.x应用开发 31 | - MyBatis-Plus持久层开发 32 | - MapStruct对象映射 33 | - JWT安全认证 34 | - RESTful API设计 35 | 36 | 3. 代码规范与最佳实践 37 | - Java代码规范 38 | - DDD最佳实践 39 | - REST API设计规范 40 | - 数据库设计规范 41 | 42 | ## Rules 43 | 1. 代码结构规范 44 | - 严格遵循项目的DDD分层架构 45 | - 遵循包命名规范:com.readify.server.{layer}.{domain} 46 | - Controller统一返回Result对象 47 | - 使用Lombok简化代码 48 | 49 | 2. 领域层规范 50 | - 模型放置于model包下 51 | - 仓储接口定义于repository包下 52 | - 服务接口与实现分离 53 | 54 | 3. 基础设施层规范 55 | - 异常统一处理 56 | - 通用组件放置于common包 57 | - 持久化实现遵循规定目录结构 58 | 59 | 4. 接口层规范 60 | - 请求对象使用req后缀 61 | - 视图对象使用vo后缀 62 | - 转换器放置于converter包 63 | 64 | ## Workflow 65 | 1. 需求分析 66 | - 理解业务需求 67 | - 确定领域模型 68 | - 设计接口规范 69 | 70 | 2. 代码实现 71 | - 按DDD分层逐层实现 72 | - 遵循项目结构规范 73 | - 确保代码质量 74 | 75 | 3. 测试与优化 76 | - 单元测试 77 | - 接口测试 78 | - 性能优化 79 | 80 | ## Commands 81 | /create_domain - 创建新的领域模块 82 | /add_entity - 添加实体类 83 | /add_controller - 添加控制器 84 | /add_service - 添加服务 85 | /optimize - 优化指定代码 86 | /help - 显示帮助信息 87 | 88 | ## Initialization 89 | 作为DDD架构Java开发助手,我将严格遵循项目的架构规范和最佳实践,协助您进行代码开发。我会首先理解您的需求,然后按照DDD分层架构提供相应的解决方案。让我们开始吧! 90 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/repository/ProjectFileRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.repository; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import com.readify.server.domain.project.model.ProjectFile; 5 | import com.readify.server.domain.project.repository.ProjectFileRepository; 6 | import com.readify.server.infrastructure.persistence.converter.ProjectFileConverter; 7 | import com.readify.server.infrastructure.persistence.entity.ProjectFileEntity; 8 | import com.readify.server.infrastructure.persistence.mapper.ProjectFileMapper; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.stereotype.Repository; 11 | 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | @Repository 16 | @RequiredArgsConstructor 17 | public class ProjectFileRepositoryImpl implements ProjectFileRepository { 18 | private final ProjectFileMapper projectFileMapper; 19 | private final ProjectFileConverter projectFileConverter = ProjectFileConverter.INSTANCE; 20 | 21 | @Override 22 | public ProjectFile save(ProjectFile projectFile) { 23 | ProjectFileEntity entity = projectFileConverter.toEntity(projectFile); 24 | if (entity.getId() == null) { 25 | projectFileMapper.insert(entity); 26 | } else { 27 | projectFileMapper.updateById(entity); 28 | } 29 | return projectFileConverter.toDomain(entity); 30 | } 31 | 32 | @Override 33 | public List findByProjectId(Long projectId) { 34 | return projectFileMapper.selectList(new LambdaQueryWrapper().eq(ProjectFileEntity::getProjectId, projectId)) 35 | .stream() 36 | .map(projectFileConverter::toDomain) 37 | .collect(Collectors.toList()); 38 | } 39 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/auth/ApiKeyController.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.auth; 2 | 3 | import com.readify.server.domain.auth.model.ApiKey; 4 | import com.readify.server.domain.auth.service.ApiKeyService; 5 | import com.readify.server.infrastructure.common.Result; 6 | import com.readify.server.interfaces.auth.req.CreateApiKeyReq; 7 | import com.readify.server.interfaces.auth.vo.ApiKeyVO; 8 | import io.swagger.v3.oas.annotations.Operation; 9 | import io.swagger.v3.oas.annotations.Parameter; 10 | import io.swagger.v3.oas.annotations.tags.Tag; 11 | import lombok.RequiredArgsConstructor; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | 18 | @Slf4j 19 | @RestController 20 | @RequestMapping("/api/v1/auth/api-keys") 21 | @RequiredArgsConstructor 22 | @Tag(name = "API Key管理", description = "API Key管理相关接口") 23 | public class ApiKeyController { 24 | private final ApiKeyService apiKeyService; 25 | 26 | @PostMapping 27 | @Operation(summary = "创建API Key") 28 | public Result create(@RequestBody CreateApiKeyReq req) { 29 | ApiKey apiKey = apiKeyService.create(req.getName(), req.getDescription()); 30 | return Result.success(ApiKeyVO.from(apiKey)); 31 | } 32 | 33 | @GetMapping 34 | @Operation(summary = "获取API Key列表") 35 | public Result> list() { 36 | List apiKeys = apiKeyService.list(); 37 | return Result.success(apiKeys.stream().map(ApiKeyVO::from).collect(Collectors.toList())); 38 | } 39 | 40 | @DeleteMapping("/{id}") 41 | @Operation(summary = "删除API Key") 42 | public Result delete(@Parameter(description = "API Key ID") @PathVariable Long id) { 43 | apiKeyService.delete(id); 44 | return Result.success(); 45 | } 46 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/file/service/impl/FileServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.file.service.impl; 2 | 3 | import com.readify.server.domain.file.model.File; 4 | import com.readify.server.domain.file.repository.FileRepository; 5 | import com.readify.server.domain.file.service.FileService; 6 | import com.readify.server.infrastructure.common.exception.NotFoundException; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.io.InputStream; 11 | import java.util.List; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | public class FileServiceImpl implements FileService { 16 | private final FileRepository fileRepository; 17 | 18 | @Override 19 | public File upload(String originalFilename, String mimeType, long size, InputStream inputStream) { 20 | return fileRepository.upload(originalFilename, mimeType, size, inputStream); 21 | } 22 | 23 | @Override 24 | public void delete(Long fileId) { 25 | fileRepository.deleteById(fileId); 26 | } 27 | 28 | @Override 29 | public File getFileInfo(Long fileId) { 30 | return fileRepository.findById(fileId) 31 | .orElseThrow(() -> new NotFoundException("文件不存在!")); 32 | } 33 | 34 | @Override 35 | public List getFilesByIds(List fileIds) { 36 | return fileRepository.findAllById(fileIds); 37 | } 38 | 39 | 40 | @Override 41 | public File updateVectorizedStatus(Long fileId, Boolean vectorized) { 42 | File file = fileRepository.updateVectorizedStatus(fileId, vectorized); 43 | if (file == null) { 44 | throw new NotFoundException("文件不存在!"); 45 | } 46 | return file; 47 | } 48 | 49 | @Override 50 | public List getNonVectorizedFiles() { 51 | return fileRepository.findNonVectorizedFiles(); 52 | } 53 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/mind_map/service/MindMapService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.mind_map.service; 2 | 3 | import com.readify.server.domain.mind_map.model.MindMap; 4 | 5 | import java.util.List; 6 | 7 | public interface MindMapService { 8 | /** 9 | * 创建思维导图 10 | * 11 | * @param mindMap 思维导图信息 12 | * @param userId 用户ID 13 | * @return 创建后的思维导图 14 | */ 15 | MindMap createMindMap(MindMap mindMap, Long userId); 16 | 17 | /** 18 | * 更新思维导图 19 | * 20 | * @param mindMap 思维导图信息 21 | * @param userId 用户ID 22 | * @return 更新后的思维导图 23 | */ 24 | MindMap updateMindMap(MindMap mindMap, Long userId); 25 | 26 | /** 27 | * 获取思维导图 28 | * 29 | * @param id 思维导图ID 30 | * @param userId 用户ID 31 | * @return 思维导图信息 32 | */ 33 | MindMap getMindMapById(Long id, Long userId); 34 | 35 | /** 36 | * 获取用户的所有思维导图 37 | * 38 | * @param userId 用户ID 39 | * @return 思维导图列表 40 | */ 41 | List getUserMindMaps(Long userId); 42 | 43 | /** 44 | * 获取项目下的所有思维导图 45 | * 46 | * @param projectId 项目ID 47 | * @param userId 用户ID 48 | * @return 思维导图列表 49 | */ 50 | List getProjectMindMaps(Long projectId, Long userId); 51 | 52 | /** 53 | * 删除思维导图 54 | * 55 | * @param id 思维导图ID 56 | * @param userId 用户ID 57 | * @return 是否删除成功 58 | */ 59 | boolean deleteMindMap(Long id, Long userId); 60 | 61 | /** 62 | * 根据标题和用户ID获取思维导图 63 | * 64 | * @param title 思维导图标题 65 | * @param userId 用户ID 66 | * @return 思维导图信息 67 | */ 68 | MindMap getMindMapByTitle(String title, Long userId); 69 | 70 | /** 71 | * 检查思维导图标题是否已存在于指定项目中 72 | * 73 | * @param title 思维导图标题 74 | * @param projectId 项目ID 75 | * @param userId 用户ID 76 | * @return true 如果标题已存在,否则返回 false 77 | */ 78 | boolean isMindMapTitleExists(String title, Long projectId, Long userId); 79 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/utils/file/LocalFileStorage.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.utils.file; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | import java.nio.file.StandardCopyOption; 13 | 14 | @Component 15 | @RequiredArgsConstructor 16 | public class LocalFileStorage implements FileStorage { 17 | 18 | @Value("${readify.file.storage-path}") 19 | private String storagePath; 20 | 21 | @Override 22 | public void store(String storageName, InputStream inputStream) { 23 | try { 24 | Path targetPath = getStoragePath(storageName); 25 | Files.createDirectories(targetPath.getParent()); 26 | Files.copy(inputStream, targetPath, StandardCopyOption.REPLACE_EXISTING); 27 | } catch (IOException e) { 28 | throw new RuntimeException("Failed to store file", e); 29 | } 30 | } 31 | 32 | @Override 33 | public InputStream retrieve(String storageName) { 34 | try { 35 | Path filePath = getStoragePath(storageName); 36 | return Files.newInputStream(filePath); 37 | } catch (IOException e) { 38 | throw new RuntimeException("Failed to retrieve file", e); 39 | } 40 | } 41 | 42 | @Override 43 | public void delete(String storageName) { 44 | try { 45 | Path filePath = getStoragePath(storageName); 46 | Files.deleteIfExists(filePath); 47 | } catch (IOException e) { 48 | throw new RuntimeException("Failed to delete file", e); 49 | } 50 | } 51 | 52 | @Override 53 | public Path getStoragePath(String storageName) { 54 | return Paths.get(storagePath).resolve(storageName); 55 | } 56 | } -------------------------------------------------------------------------------- /readify_agi/app/services/file_search_service.py: -------------------------------------------------------------------------------- 1 | from typing import List, Dict, Any 2 | import os 3 | from app.repositories.file_repository import FileRepository 4 | from app.services.vector_store_service import VectorStoreService 5 | 6 | class FileSearchService: 7 | """文件搜索服务""" 8 | 9 | def __init__( 10 | self, 11 | file_repository: FileRepository, 12 | vector_store_service: VectorStoreService 13 | ): 14 | self.file_repository = file_repository 15 | self.vector_store_service = vector_store_service 16 | 17 | async def search_in_file( 18 | self, 19 | file_id: int, 20 | query_text: str, 21 | top_k: int = 5, 22 | ) -> List[Dict[str, Any]]: 23 | """ 24 | 在指定文件中搜索相似文本 25 | 26 | Args: 27 | file_id: 文件ID 28 | query_text: 查询文本 29 | top_k: 返回结果数量 30 | 31 | Returns: 32 | List[Dict[str, Any]]: 搜索结果列表 33 | """ 34 | print(f"[文件搜索] 开始搜索文件 {file_id}") 35 | 36 | # 1. 检查文件是否存在 37 | file = await self.file_repository.get_file_by_id(file_id) 38 | if not file: 39 | print(f"[文件搜索] 错误:文件不存在 (ID: {file_id})") 40 | raise ValueError(f"文件不存在: {file_id}") 41 | print(f"[文件搜索] 找到文件:{file.original_name}") 42 | 43 | # 2. 获取collection名称 44 | collection_name = os.path.splitext(file.storage_name)[0] 45 | print(f"[文件搜索] 使用集合名称:{collection_name}") 46 | 47 | # 3. 执行向量检索 48 | try: 49 | print(f"[文件搜索] 开始向量检索...") 50 | results = await self.vector_store_service.search_similar_texts( 51 | query_text=query_text, 52 | collection_name=collection_name, 53 | top_k=top_k, 54 | ) 55 | print(f"[文件搜索] 检索完成,返回 {len(results)} 条结果") 56 | return results 57 | except Exception as e: 58 | print(f"[文件搜索] 搜索失败:{str(e)}") 59 | raise ValueError(f"搜索失败: {str(e)}") -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/mind_map/MindMapNodeController.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.mind_map; 2 | 3 | import com.readify.server.domain.mind_map.model.MindMapNodeTree; 4 | import com.readify.server.domain.mind_map.service.MindMapNodeService; 5 | import com.readify.server.infrastructure.common.Result; 6 | import com.readify.server.infrastructure.security.SecurityUtils; 7 | import com.readify.server.interfaces.mind_map.converter.MindMapNodeTreeVOConverter; 8 | import com.readify.server.interfaces.mind_map.vo.MindMapNodeTreeVO; 9 | import io.swagger.v3.oas.annotations.Operation; 10 | import io.swagger.v3.oas.annotations.Parameter; 11 | import io.swagger.v3.oas.annotations.tags.Tag; 12 | import lombok.RequiredArgsConstructor; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | @RestController 16 | @RequestMapping("/mind-map-nodes") 17 | @RequiredArgsConstructor 18 | @Tag(name = "思维导图节点管理", description = "思维导图节点相关的API接口") 19 | public class MindMapNodeController { 20 | private final MindMapNodeService mindMapNodeService; 21 | private final MindMapNodeTreeVOConverter mindMapNodeTreeVOConverter = MindMapNodeTreeVOConverter.INSTANCE; 22 | 23 | @Operation(summary = "获取完整思维导图结构", description = "根据思维导图ID获取完整的思维导图树形结构") 24 | @GetMapping("/full-tree/{mindMapId}") 25 | public Result getFullMindMap( 26 | @Parameter(description = "思维导图ID") @PathVariable Long mindMapId) { 27 | MindMapNodeTree fullTree = mindMapNodeService.getFullMindMap(mindMapId); 28 | return Result.success(mindMapNodeTreeVOConverter.toVO(fullTree)); 29 | } 30 | 31 | @Operation(summary = "获取节点子树", description = "根据节点ID获取以该节点为根的子树") 32 | @GetMapping("/sub-tree/{nodeId}") 33 | public Result getSubTree( 34 | @Parameter(description = "节点ID") @PathVariable Long nodeId) { 35 | MindMapNodeTree subTree = mindMapNodeService.getSubTreeByNodeId(nodeId); 36 | return Result.success(mindMapNodeTreeVOConverter.toVO(subTree)); 37 | } 38 | } -------------------------------------------------------------------------------- /readify_agi/app/core/config.py: -------------------------------------------------------------------------------- 1 | from pydantic_settings import BaseSettings 2 | import os 3 | 4 | class Settings(BaseSettings): 5 | """应用配置""" 6 | # 数据库配置 7 | DB_HOST: str 8 | DB_PORT: int 9 | DB_USER: str 10 | DB_PASSWORD: str 11 | DB_NAME: str 12 | 13 | # 向量存储配置 14 | VECTOR_STORE_DIR: str = "data/vector_store" 15 | 16 | # Chroma配置 17 | CHROMA_SERVER_HOST: str = "localhost" 18 | CHROMA_SERVER_PORT: int = 8000 19 | CHROMA_SERVER_SSL_ENABLED: bool = False 20 | 21 | # LlamaParse配置 22 | LLAMA_PARSE_API_KEY: str 23 | 24 | # OpenAI配置 25 | OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "") 26 | OPENAI_API_BASE: str = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1") 27 | OPENAI_EMBEDDING_MODEL: str = "text-embedding-3-small" 28 | 29 | # DeepSeek 30 | DEEPSEEK_API_KEY: str = os.getenv("DEEPSEEK_API_KEY", "") 31 | DEEPSEEK_API_BASE: str = os.getenv("DEEPSEEK_API_BASE", "") 32 | 33 | # OpenAI-国内专线 34 | OPENAI_API_KEY_CHINA: str = os.getenv("OPENAI_API_KEY_CHINA", "") 35 | OPENAI_API_BASE_CHINA: str = os.getenv("OPENAI_API_BASE_CHINA", "") 36 | 37 | # Qwen 38 | QWEN_API_KEY: str = os.getenv("QWEN_API_KEY", "") 39 | QWEN_API_BASE: str = os.getenv("QWEN_API_BASE", "") 40 | 41 | # 回调配置 42 | FILE_PROCESS_CALLBACK_URL: str = os.getenv("FILE_PROCESS_CALLBACK_URL", "") 43 | FILE_PROCESS_CALLBACK_API_KEY: str = os.getenv("FILE_PROCESS_CALLBACK_API_KEY", "") 44 | 45 | # SerpAPI配置 46 | SERPAPI_API_KEY: str = os.getenv("SERPAPI_API_KEY", "") 47 | 48 | # 数据库连接URL 49 | @property 50 | def DATABASE_URL(self) -> str: 51 | return ( 52 | f"mysql+aiomysql://{self.DB_USER}:{self.DB_PASSWORD}@" 53 | f"{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}" 54 | "?charset=utf8mb4&use_unicode=1" 55 | "&auth_plugin=caching_sha2_password" 56 | ) 57 | 58 | class Config: 59 | env_file = ".env" 60 | case_sensitive = True 61 | 62 | settings = Settings() -------------------------------------------------------------------------------- /readify_agi/main.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, Request 2 | from fastapi.middleware.cors import CORSMiddleware 3 | from fastapi.staticfiles import StaticFiles 4 | from app.api.v1 import file_router 5 | from app.api.v1 import api_router 6 | from app.core.database import close_db_connection 7 | import logging 8 | from contextlib import asynccontextmanager 9 | 10 | # 配置日志 11 | logging.basicConfig( 12 | level=logging.INFO, 13 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' 14 | ) 15 | logger = logging.getLogger(__name__) 16 | 17 | # 定义生命周期上下文管理器 18 | @asynccontextmanager 19 | async def lifespan(app: FastAPI): 20 | # 启动时执行 21 | logger.info("应用启动中,初始化资源...") 22 | yield 23 | # 关闭时执行 24 | logger.info("应用关闭中,释放资源...") 25 | # 关闭数据库连接池 26 | await close_db_connection() 27 | logger.info("数据库连接池已关闭") 28 | 29 | app = FastAPI( 30 | title="Readify AGI", 31 | description="Readify AGI 项目的 API 服务", 32 | version="1.0.0", 33 | lifespan=lifespan 34 | ) 35 | 36 | # 请求日志中间件 37 | @app.middleware("http") 38 | async def debug_middleware(request: Request, call_next): 39 | logger.debug(f"收到请求: {request.method} {request.url.path}") 40 | response = await call_next(request) 41 | return response 42 | 43 | # 配置CORS 44 | app.add_middleware( 45 | CORSMiddleware, 46 | allow_origins=["*"], 47 | allow_credentials=True, 48 | allow_methods=["*"], 49 | allow_headers=["*"], 50 | ) 51 | 52 | # 挂载静态文件 53 | app.mount("/static", StaticFiles(directory="app/static"), name="static") 54 | 55 | # 基础路由 56 | @app.get("/") 57 | async def root(): 58 | return {"message": "Welcome to Readify AGI!"} 59 | 60 | @app.get("/health") 61 | def health(): 62 | return {"status": "healthy"} 63 | 64 | # API路由 65 | app.include_router(file_router.router, prefix="/api/v1", tags=["files"]) 66 | app.include_router(api_router, prefix="/api/v1") 67 | 68 | if __name__ == "__main__": 69 | import uvicorn 70 | logger.info("正在启动服务器,端口 8090...") 71 | uvicorn.run( 72 | app, 73 | host="0.0.0.0", 74 | port=8090, 75 | log_level="info" 76 | ) -------------------------------------------------------------------------------- /readify_frontend/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 73 | 74 | -------------------------------------------------------------------------------- /readify_agi/app/repositories/assistant_thinking_repository.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from sqlalchemy import select 3 | from sqlalchemy.ext.asyncio import AsyncSession 4 | 5 | from app.models.assistant_thinking import AssistantThinkingDB 6 | from app.repositories import BaseRepository 7 | 8 | 9 | class AssistantThinkingRepository(BaseRepository): 10 | """ 11 | AI助手思考过程仓储层 12 | """ 13 | 14 | async def create( 15 | self, 16 | project_id: int, 17 | user_message_id: int, 18 | content: str 19 | ) -> AssistantThinkingDB: 20 | """ 21 | 创建新的思考过程记录 22 | 23 | Args: 24 | project_id: 工程ID 25 | user_message_id: 对应的用户消息ID 26 | content: 思考过程内容 27 | 28 | Returns: 29 | 新创建的思考过程记录 30 | """ 31 | try: 32 | db = await self._ensure_session() 33 | 34 | new_thinking = AssistantThinkingDB( 35 | project_id=project_id, 36 | user_message_id=user_message_id, 37 | content=content 38 | ) 39 | 40 | db.add(new_thinking) 41 | await db.commit() 42 | await db.refresh(new_thinking) 43 | 44 | return new_thinking 45 | finally: 46 | await self._cleanup_session() 47 | 48 | async def get_by_user_message( 49 | self, 50 | user_message_id: int 51 | ) -> Optional[AssistantThinkingDB]: 52 | """ 53 | 通过用户消息ID获取思考过程 54 | 55 | Args: 56 | user_message_id: 用户消息ID 57 | 58 | Returns: 59 | 与用户消息关联的思考过程,如果不存在则返回None 60 | """ 61 | try: 62 | db = await self._ensure_session() 63 | 64 | query = select(AssistantThinkingDB).where( 65 | AssistantThinkingDB.user_message_id == user_message_id 66 | ) 67 | 68 | result = await db.execute(query) 69 | return result.scalar_one_or_none() 70 | finally: 71 | await self._cleanup_session() -------------------------------------------------------------------------------- /readify_frontend/src/api/mindmap.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import type { ApiResponse } from '@/types/response' 3 | import type { MindMapVO } from '@/types/mindmap' 4 | import type { MindMapNodeTreeVO } from '@/types/mindmap-node' 5 | 6 | // 创建思维导图 7 | export const createMindMap = (data: Partial) => { 8 | return request>({ 9 | url: '/mind-maps', 10 | method: 'post', 11 | data 12 | }) 13 | } 14 | 15 | // 获取思维导图详情 16 | export const getMindMapById = (id: number) => { 17 | return request>({ 18 | url: `/mind-maps/${id}`, 19 | method: 'get' 20 | }) 21 | } 22 | 23 | // 更新思维导图 24 | export const updateMindMap = (id: number, data: Partial) => { 25 | return request>({ 26 | url: `/mind-maps/${id}`, 27 | method: 'put', 28 | data 29 | }) 30 | } 31 | 32 | // 删除思维导图 33 | export const deleteMindMap = (id: number) => { 34 | return request>({ 35 | url: `/mind-maps/${id}`, 36 | method: 'delete' 37 | }) 38 | } 39 | 40 | // 获取项目下所有思维导图 41 | export const getProjectMindMaps = (projectId: number) => { 42 | return request>({ 43 | url: `/mind-maps/project/${projectId}`, 44 | method: 'get' 45 | }) 46 | } 47 | 48 | // 获取用户所有思维导图 49 | export const getMyMindMaps = () => { 50 | return request>({ 51 | url: '/mind-maps/my', 52 | method: 'get' 53 | }) 54 | } 55 | 56 | // 检查思维导图标题是否已存在 57 | export const checkMindMapTitle = (title: string, projectId: number) => { 58 | return request>({ 59 | url: '/mind-maps/check', 60 | method: 'get', 61 | params: { title, projectId } 62 | }) 63 | } 64 | 65 | // 获取完整思维导图结构 66 | export const getFullMindMap = (mindMapId: number) => { 67 | return request>({ 68 | url: `/mind-map-nodes/full-tree/${mindMapId}`, 69 | method: 'get' 70 | }) 71 | } 72 | 73 | // 获取节点子树 74 | export const getNodeSubTree = (nodeId: number) => { 75 | return request>({ 76 | url: `/mind-map-nodes/sub-tree/${nodeId}`, 77 | method: 'get' 78 | }) 79 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/notetask/NoteTaskService.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.notetask; 2 | 3 | import com.readify.server.domain.notetask.model.NoteTask; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | /** 9 | * 笔记任务领域服务接口 10 | */ 11 | public interface NoteTaskService { 12 | 13 | /** 14 | * 创建笔记任务 15 | * 16 | * @param noteTask 笔记任务 17 | * @return 创建后的笔记任务 18 | */ 19 | NoteTask createNoteTask(NoteTask noteTask); 20 | 21 | /** 22 | * 获取笔记任务详情 23 | * 24 | * @param id 笔记任务ID 25 | * @return 笔记任务 26 | */ 27 | Optional getNoteTaskById(Long id); 28 | 29 | /** 30 | * 获取用户的笔记任务列表 31 | * 32 | * @param userId 用户ID 33 | * @return 笔记任务列表 34 | */ 35 | List getNoteTasksByUserId(Long userId); 36 | 37 | /** 38 | * 获取项目的笔记任务列表 39 | * 40 | * @param projectId 项目ID 41 | * @return 笔记任务列表 42 | */ 43 | List getNoteTasksByProjectId(Long projectId); 44 | 45 | /** 46 | * 获取思维导图的笔记任务列表 47 | * 48 | * @param mindMapId 思维导图ID 49 | * @return 笔记任务列表 50 | */ 51 | List getNoteTasksByMindMapId(Long mindMapId); 52 | 53 | /** 54 | * 获取文件的笔记任务列表 55 | * 56 | * @param fileId 文件ID 57 | * @return 笔记任务列表 58 | */ 59 | List getNoteTasksByFileId(Long fileId); 60 | 61 | /** 62 | * 更新笔记任务 63 | * 64 | * @param noteTask 笔记任务 65 | * @return 更新后的笔记任务 66 | */ 67 | NoteTask updateNoteTask(NoteTask noteTask); 68 | 69 | /** 70 | * 更新笔记任务状态 71 | * 72 | * @param id 笔记任务ID 73 | * @param status 任务状态 74 | * @return 更新后的笔记任务 75 | */ 76 | NoteTask updateNoteTaskStatus(Long id, String status); 77 | 78 | /** 79 | * 更新笔记任务结果 80 | * 81 | * @param id 笔记任务ID 82 | * @param result 任务结果 83 | * @return 更新后的笔记任务 84 | */ 85 | NoteTask updateNoteTaskResult(Long id, String result); 86 | 87 | /** 88 | * 删除笔记任务 89 | * 90 | * @param id 笔记任务ID 91 | */ 92 | void deleteNoteTask(Long id); 93 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.so 6 | .Python 7 | env/ 8 | build/ 9 | develop-eggs/ 10 | dist/ 11 | downloads/ 12 | eggs/ 13 | .eggs/ 14 | lib/ 15 | lib64/ 16 | parts/ 17 | sdist/ 18 | var/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | .pytest_cache/ 23 | 24 | # Environment variables 25 | .env 26 | .env.local 27 | .env.development.local 28 | .env.test.local 29 | .env.production.local 30 | *.env 31 | 32 | # IDE 33 | .idea/ 34 | .vscode/ 35 | *.swp 36 | *.swo 37 | .project 38 | .classpath 39 | .settings/ 40 | *.sublime-workspace 41 | .cursor/ 42 | 43 | # Misc 44 | .DS_Store 45 | Thumbs.db 46 | desktop.ini 47 | *.tmp 48 | *.bak 49 | *.log 50 | logs/ 51 | 52 | # Java / Maven / Spring 53 | HELP.md 54 | target/ 55 | !.mvn/wrapper/maven-wrapper.jar 56 | !**/src/main/**/target/ 57 | !**/src/test/**/target/ 58 | application*.yml 59 | application*.properties 60 | !application-example.yml 61 | !application-example.properties 62 | 63 | ### STS ### 64 | .apt_generated 65 | .classpath 66 | .factorypath 67 | .project 68 | .settings 69 | .springBeans 70 | .sts4-cache 71 | 72 | ### IntelliJ IDEA ### 73 | .idea 74 | *.iws 75 | *.iml 76 | *.ipr 77 | 78 | ### NetBeans ### 79 | /nbproject/private/ 80 | /nbbuild/ 81 | /dist/ 82 | /nbdist/ 83 | /.nb-gradle/ 84 | build/ 85 | !**/src/main/**/build/ 86 | !**/src/test/**/build/ 87 | 88 | # Node.js 89 | node_modules/ 90 | npm-debug.log* 91 | yarn-debug.log* 92 | yarn-error.log* 93 | .pnpm-debug.log* 94 | .yarn/ 95 | .npm/ 96 | .yarn-integrity 97 | .pnp.* 98 | 99 | # 自定义配置文件 (Custom config files) 100 | **/config/local.js 101 | **/config/local.json 102 | **/config/private.js 103 | **/config/private.json 104 | config.local.js 105 | config.local.json 106 | readify_server/.mvn/wrapper/maven-wrapper.properties 107 | readify_server/src/main/resources/application.yml 108 | 109 | # 系统生成的文件 (System generated files) 110 | *.class 111 | *.war 112 | *.ear 113 | *.zip 114 | *.tar.gz 115 | *.rar 116 | 117 | # 数据库文件 (Database files) 118 | *.sqlite 119 | *.db 120 | *.sql 121 | 122 | # 安全相关 (Security related) 123 | *.key 124 | *.pem 125 | *.cert 126 | *.p12 127 | -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/handler/WebSocketMessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.handler; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.JavaType; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.type.TypeFactory; 7 | import com.readify.server.websocket.message.WebSocketMessage; 8 | import org.springframework.web.socket.WebSocketSession; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * WebSocket消息处理器接口 14 | * 每种消息类型对应一个具体的处理器实现 15 | */ 16 | public interface WebSocketMessageHandler { 17 | 18 | /** 19 | * 获取此处理器支持的消息类型 20 | * @return 消息类型字符串 21 | */ 22 | String supportType(); 23 | 24 | /** 25 | * 处理WebSocket消息 26 | * @param session WebSocket会话 27 | * @param message 接收到的消息 28 | * @deprecated 使用 {@link #handle(WebSocketSession, String, ObjectMapper)} 代替 29 | */ 30 | @Deprecated 31 | default void handle(WebSocketSession session, WebSocketMessage message) { 32 | throw new UnsupportedOperationException("This method is deprecated. Use handle(WebSocketSession, String, ObjectMapper) instead."); 33 | } 34 | 35 | /** 36 | * 处理WebSocket原始消息 37 | * @param session WebSocket会话 38 | * @param rawMessage 原始消息文本 39 | * @param objectMapper Jackson对象映射器 40 | * @throws IOException 如果解析消息时发生错误 41 | */ 42 | default void handle(WebSocketSession session, String rawMessage, ObjectMapper objectMapper) throws IOException { 43 | // 获取泛型类型 44 | JavaType type = TypeFactory.defaultInstance().constructParametricType(WebSocketMessage.class, getDataType()); 45 | 46 | // 使用正确的类型信息反序列化消息 47 | WebSocketMessage message = objectMapper.readValue(rawMessage, type); 48 | 49 | // 调用具体处理逻辑 50 | processMessage(session, message); 51 | } 52 | 53 | /** 54 | * 获取数据字段的类型 55 | * @return 数据字段的类 56 | */ 57 | Class getDataType(); 58 | 59 | /** 60 | * 处理消息的具体逻辑 61 | * @param session WebSocket会话 62 | * @param message 类型安全的消息对象 63 | */ 64 | void processMessage(WebSocketSession session, WebSocketMessage message); 65 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/auth/service/impl/ApiKeyServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.auth.service.impl; 2 | 3 | import com.readify.server.domain.auth.model.ApiKey; 4 | import com.readify.server.domain.auth.repository.ApiKeyRepository; 5 | import com.readify.server.domain.auth.service.ApiKeyService; 6 | import com.readify.server.infrastructure.security.SecurityUtils; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import java.security.SecureRandom; 13 | import java.util.Base64; 14 | import java.util.List; 15 | 16 | @Slf4j 17 | @Service 18 | @RequiredArgsConstructor 19 | public class ApiKeyServiceImpl implements ApiKeyService { 20 | private final ApiKeyRepository apiKeyRepository; 21 | private static final SecureRandom secureRandom = new SecureRandom(); 22 | 23 | @Override 24 | @Transactional 25 | public ApiKey create(String name, String description) { 26 | ApiKey apiKey = new ApiKey(); 27 | apiKey.setName(name); 28 | apiKey.setDescription(description); 29 | apiKey.setApiKey(generateApiKey()); 30 | apiKey.setUserId(SecurityUtils.getCurrentUserId()); 31 | apiKey.setEnabled(true); 32 | apiKey.setCreateTime(System.currentTimeMillis()); 33 | apiKey.setUpdateTime(System.currentTimeMillis()); 34 | return apiKeyRepository.save(apiKey); 35 | } 36 | 37 | @Override 38 | public List list() { 39 | return apiKeyRepository.findByUserId(SecurityUtils.getCurrentUserId()); 40 | } 41 | 42 | @Override 43 | @Transactional 44 | public void delete(Long id) { 45 | apiKeyRepository.delete(id); 46 | } 47 | 48 | @Override 49 | public ApiKey validate(String key) { 50 | ApiKey apiKey = apiKeyRepository.findByKey(key); 51 | if (apiKey != null && apiKey.getEnabled()) { 52 | return apiKey; 53 | } 54 | return null; 55 | } 56 | 57 | private String generateApiKey() { 58 | byte[] randomBytes = new byte[32]; 59 | secureRandom.nextBytes(randomBytes); 60 | return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes); 61 | } 62 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/websocket/handler/impl/QueryProjectFilesHandler.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.websocket.handler.impl; 2 | 3 | import com.readify.server.domain.file.model.File; 4 | import com.readify.server.domain.project.service.ProjectFileService; 5 | import com.readify.server.websocket.WebSocketSessionManager; 6 | import com.readify.server.websocket.dto.QueryProjectReq; 7 | import com.readify.server.websocket.handler.WebSocketMessageHandler; 8 | import com.readify.server.websocket.message.WebSocketMessage; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.web.socket.WebSocketSession; 13 | 14 | import java.util.List; 15 | import java.util.Optional; 16 | 17 | /** 18 | * 查询项目文件消息处理器 19 | */ 20 | @Slf4j 21 | @Component 22 | @RequiredArgsConstructor 23 | public class QueryProjectFilesHandler implements WebSocketMessageHandler { 24 | 25 | private final WebSocketSessionManager sessionManager; 26 | private final ProjectFileService projectFileService; 27 | 28 | @Override 29 | public String supportType() { 30 | return "queryProjectFiles"; 31 | } 32 | 33 | @Override 34 | public Class getDataType() { 35 | return QueryProjectReq.class; 36 | } 37 | 38 | @Override 39 | public void processMessage(WebSocketSession session, WebSocketMessage message) { 40 | Long userId = getUserIdFromSession(session); 41 | log.debug("Processing queryProjectFiles message from user {}, session: {}", userId, session.getId()); 42 | 43 | QueryProjectReq queryProjectReq = message.getData(); 44 | Long projectId = Optional.ofNullable(queryProjectReq).map(QueryProjectReq::getProjectId).orElseThrow(); 45 | 46 | // 调用服务查询项目文件 47 | List projectFiles = projectFileService.getProjectFiles(projectId); 48 | 49 | // 返回结果 50 | WebSocketMessage> responseMessage = WebSocketMessage.create("projectFiles", projectFiles); 51 | sessionManager.sendMessage(session.getId(), responseMessage); 52 | } 53 | 54 | private Long getUserIdFromSession(WebSocketSession session) { 55 | return (Long) session.getAttributes().get("userId"); 56 | } 57 | } -------------------------------------------------------------------------------- /readify_agi/app/repositories/project_file_repository.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from sqlalchemy.ext.asyncio import AsyncSession 3 | from sqlalchemy import select 4 | from sqlalchemy.sql import and_ 5 | import time 6 | 7 | from app.models.project_file import ProjectFileDB, ProjectFileCreate 8 | from app.repositories import BaseRepository 9 | 10 | 11 | class ProjectFileRepository(BaseRepository): 12 | """项目文件关联仓储层""" 13 | 14 | async def create_project_file(self, project_file: ProjectFileCreate) -> ProjectFileDB: 15 | """创建项目文件关联记录""" 16 | try: 17 | db = await self._ensure_session() 18 | 19 | now = int(time.time()) 20 | db_project_file = ProjectFileDB( 21 | **project_file.model_dump(), 22 | create_time=now, 23 | update_time=now 24 | ) 25 | db.add(db_project_file) 26 | await db.commit() 27 | await db.refresh(db_project_file) 28 | return db_project_file 29 | finally: 30 | await self._cleanup_session() 31 | 32 | async def get_file_ids_by_project_id(self, project_id: int) -> List[int]: 33 | """通过项目ID获取文件ID列表""" 34 | try: 35 | db = await self._ensure_session() 36 | 37 | query = select(ProjectFileDB.file_id).where( 38 | and_( 39 | ProjectFileDB.project_id == project_id, 40 | ProjectFileDB.deleted == False 41 | ) 42 | ) 43 | result = await db.execute(query) 44 | return [row[0] for row in result.all()] 45 | finally: 46 | await self._cleanup_session() 47 | 48 | async def get_project_file_by_ids(self, project_id: int, file_id: int) -> ProjectFileDB: 49 | """通过项目ID和文件ID获取关联记录""" 50 | try: 51 | db = await self._ensure_session() 52 | 53 | query = select(ProjectFileDB).where( 54 | and_( 55 | ProjectFileDB.project_id == project_id, 56 | ProjectFileDB.file_id == file_id, 57 | ProjectFileDB.deleted == False 58 | ) 59 | ) 60 | result = await db.execute(query) 61 | return result.scalar_one_or_none() 62 | finally: 63 | await self._cleanup_session() -------------------------------------------------------------------------------- /readify_agi/app/models/mind_map_node.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, ConfigDict 2 | from typing import Optional, List 3 | from sqlalchemy import Column, Integer, String, Boolean, BigInteger, Text, ForeignKey, Index 4 | from app.core.database import Base 5 | from datetime import datetime 6 | 7 | class MindMapNodeDB(Base): 8 | """思维导图节点数据库模型""" 9 | __tablename__ = "mind_map_node" 10 | 11 | id = Column(BigInteger, primary_key=True, autoincrement=True, comment="节点唯一标识") 12 | project_id = Column(BigInteger, nullable=True, comment="工程id") 13 | mind_map_id = Column(BigInteger, nullable=False, comment="所属思维导图ID") 14 | file_id = Column(BigInteger, nullable=False, comment="所属文件ID") 15 | parent_id = Column(BigInteger, nullable=True, comment="父节点ID,根节点为NULL") 16 | content = Column(Text, nullable=True, comment="节点内容") 17 | sequence = Column(Integer, nullable=False, default=0, comment="同级节点间的排序顺序") 18 | level = Column(Integer, nullable=False, default=0, comment="节点层级,根节点为0") 19 | created_time = Column(BigInteger, nullable=False, comment="创建时间") 20 | updated_time = Column(BigInteger, nullable=False, comment="更新时间") 21 | deleted = Column(Boolean, nullable=False, default=False, comment="是否删除,0-未删除,1-已删除") 22 | 23 | # 索引 24 | __table_args__ = ( 25 | Index('idx_file_id', file_id), 26 | Index('idx_parent_id', parent_id), 27 | Index('idx_sort', file_id, parent_id, sequence) 28 | ) 29 | 30 | def __repr__(self): 31 | return f"" 32 | 33 | 34 | class MindMapNodeCreate(BaseModel): 35 | """创建思维导图节点请求模型""" 36 | project_id: Optional[int] = None 37 | mind_map_id: int 38 | file_id: int 39 | parent_id: Optional[int] = None 40 | content: Optional[str] = None 41 | sequence: int = 0 42 | level: int = 0 43 | 44 | 45 | class MindMapNodeResponse(BaseModel): 46 | """思维导图节点响应模型""" 47 | model_config = ConfigDict(from_attributes=True) 48 | 49 | id: int 50 | project_id: Optional[int] = None 51 | mind_map_id: int 52 | file_id: int 53 | parent_id: Optional[int] = None 54 | content: Optional[str] = None 55 | sequence: int 56 | level: int 57 | created_time: int 58 | updated_time: int 59 | 60 | 61 | class MindMapNodeTreeResponse(MindMapNodeResponse): 62 | """思维导图节点树响应模型""" 63 | children: List['MindMapNodeTreeResponse'] = [] -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/user/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.user.service.impl; 2 | 3 | import com.readify.server.domain.user.model.User; 4 | import com.readify.server.domain.user.repository.UserRepository; 5 | import com.readify.server.domain.user.service.UserService; 6 | import com.readify.server.infrastructure.common.exception.BusinessException; 7 | import com.readify.server.infrastructure.common.exception.NotFoundException; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | public class UserServiceImpl implements UserService { 16 | private final UserRepository userRepository; 17 | 18 | @Override 19 | public User createUser(User user) { 20 | if (userRepository.existsByUsername(user.getUsername())) { 21 | throw new BusinessException("用户名已存在"); 22 | } 23 | user.setEnabled(true); 24 | user.setCreateTime(System.currentTimeMillis()); 25 | user.setUpdateTime(System.currentTimeMillis()); 26 | return userRepository.save(user); 27 | } 28 | 29 | @Override 30 | public User updateUser(User user) { 31 | User existingUser = getUserById(user.getId()); 32 | user.setCreateTime(existingUser.getCreateTime()); 33 | user.setEnabled(existingUser.getEnabled()); 34 | user.setUpdateTime(System.currentTimeMillis()); 35 | return userRepository.save(user); 36 | } 37 | 38 | @Override 39 | public void deleteUser(Long id) { 40 | if (userRepository.findById(id).isEmpty()) { 41 | throw new NotFoundException("用户不存在"); 42 | } 43 | userRepository.deleteById(id); 44 | } 45 | 46 | @Override 47 | public User getUserById(Long id) { 48 | return userRepository.findById(id) 49 | .orElseThrow(() -> new NotFoundException("用户不存在")); 50 | } 51 | 52 | @Override 53 | public User getUserByUsername(String username) { 54 | return userRepository.findByUsername(username) 55 | .orElseThrow(() -> new NotFoundException("用户不存在")); 56 | } 57 | 58 | @Override 59 | public List getAllUsers() { 60 | return userRepository.findAll(); 61 | } 62 | 63 | @Override 64 | public boolean isUsernameExists(String username) { 65 | return userRepository.existsByUsername(username); 66 | } 67 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/mind_map/repository/MindMapNodeRepository.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.mind_map.repository; 2 | 3 | import com.readify.server.domain.mind_map.model.MindMapNode; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public interface MindMapNodeRepository { 9 | /** 10 | * 保存节点 11 | * 12 | * @param node 节点信息 13 | * @return 保存后的节点 14 | */ 15 | MindMapNode save(MindMapNode node); 16 | 17 | /** 18 | * 批量保存节点 19 | * 20 | * @param nodes 节点列表 21 | * @return 保存后的节点列表 22 | */ 23 | List batchSave(List nodes); 24 | 25 | /** 26 | * 根据ID查找节点 27 | * 28 | * @param id 节点ID 29 | * @return 节点信息 30 | */ 31 | Optional findById(Long id); 32 | 33 | /** 34 | * 根据文件ID查找所有节点 35 | * 36 | * @param fileId 文件ID 37 | * @return 节点列表 38 | */ 39 | List findByFileId(Long fileId); 40 | 41 | /** 42 | * 根据思维导图ID查找所有节点 43 | * 44 | * @param mindMapId 思维导图ID 45 | * @return 节点列表 46 | */ 47 | List findByMindMapId(Long mindMapId); 48 | 49 | /** 50 | * 根据父节点ID查找所有子节点 51 | * 52 | * @param parentId 父节点ID 53 | * @return 子节点列表 54 | */ 55 | List findByParentId(Long parentId); 56 | 57 | /** 58 | * 根据文件ID查找根节点 59 | * 60 | * @param fileId 文件ID 61 | * @return 根节点 62 | */ 63 | Optional findRootNodeByFileId(Long fileId); 64 | 65 | /** 66 | * 根据思维导图ID查找根节点 67 | * 68 | * @param mindMapId 思维导图ID 69 | * @return 根节点 70 | */ 71 | Optional findRootNodeByMindMapId(Long mindMapId); 72 | 73 | /** 74 | * 根据ID删除节点 75 | * 76 | * @param id 节点ID 77 | * @return 是否删除成功 78 | */ 79 | boolean deleteById(Long id); 80 | 81 | /** 82 | * 根据文件ID删除所有节点 83 | * 84 | * @param fileId 文件ID 85 | * @return 删除的节点数量 86 | */ 87 | int deleteByFileId(Long fileId); 88 | 89 | /** 90 | * 根据思维导图ID删除所有节点 91 | * 92 | * @param mindMapId 思维导图ID 93 | * @return 删除的节点数量 94 | */ 95 | int deleteByMindMapId(Long mindMapId); 96 | 97 | /** 98 | * 更新节点序列号 99 | * 100 | * @param id 节点ID 101 | * @param sequence 新的序列号 102 | * @return 是否更新成功 103 | */ 104 | boolean updateSequence(Long id, Integer sequence); 105 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/repository/UserRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.repository; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import com.readify.server.domain.user.model.User; 5 | import com.readify.server.domain.user.repository.UserRepository; 6 | import com.readify.server.infrastructure.persistence.entity.UserEntity; 7 | import com.readify.server.infrastructure.persistence.converter.UserConverter; 8 | import com.readify.server.infrastructure.persistence.mapper.UserMapper; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.stereotype.Repository; 11 | 12 | import java.util.List; 13 | import java.util.Optional; 14 | import java.util.stream.Collectors; 15 | 16 | @Repository 17 | @RequiredArgsConstructor 18 | public class UserRepositoryImpl implements UserRepository { 19 | private final UserMapper userMapper; 20 | private final UserConverter userConverter = UserConverter.INSTANCE; 21 | 22 | @Override 23 | public User save(User user) { 24 | UserEntity entity = userConverter.toEntity(user); 25 | if (user.getId() == null) { 26 | userMapper.insert(entity); 27 | } else { 28 | userMapper.updateById(entity); 29 | } 30 | return userConverter.toDomain(entity); 31 | } 32 | 33 | @Override 34 | public Optional findById(Long id) { 35 | return Optional.ofNullable(userMapper.selectById(id)) 36 | .map(userConverter::toDomain); 37 | } 38 | 39 | @Override 40 | public Optional findByUsername(String username) { 41 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 42 | queryWrapper.eq(UserEntity::getUsername, username); 43 | return Optional.ofNullable(userMapper.selectOne(queryWrapper)) 44 | .map(userConverter::toDomain); 45 | } 46 | 47 | @Override 48 | public List findAll() { 49 | return userMapper.selectList(null).stream() 50 | .map(userConverter::toDomain) 51 | .collect(Collectors.toList()); 52 | } 53 | 54 | @Override 55 | public void deleteById(Long id) { 56 | userMapper.deleteById(id); 57 | } 58 | 59 | @Override 60 | public boolean existsByUsername(String username) { 61 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 62 | queryWrapper.eq(UserEntity::getUsername, username); 63 | return userMapper.selectCount(queryWrapper) > 0; 64 | } 65 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/conversation/ConversationController.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.conversation; 2 | 3 | import com.readify.server.domain.conversation.service.ConversationService; 4 | import com.readify.server.infrastructure.common.Result; 5 | import com.readify.server.interfaces.conversation.vo.ConversationVO; 6 | import io.swagger.v3.oas.annotations.Operation; 7 | import io.swagger.v3.oas.annotations.Parameter; 8 | import io.swagger.v3.oas.annotations.media.Content; 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | import io.swagger.v3.oas.annotations.responses.ApiResponse; 11 | import io.swagger.v3.oas.annotations.tags.Tag; 12 | import lombok.RequiredArgsConstructor; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.PathVariable; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | import java.util.List; 19 | 20 | @RestController 21 | @RequestMapping("/conversation") 22 | @RequiredArgsConstructor 23 | @Tag(name = "对话管理", description = "对话历史记录及思考过程相关接口") 24 | public class ConversationController { 25 | 26 | private final ConversationService conversationService; 27 | 28 | /** 29 | * 根据项目ID获取对话历史,包含用户消息关联的思考过程 30 | * 31 | * @param projectId 项目ID 32 | * @return 对话历史列表 33 | */ 34 | @GetMapping("/project/{projectId}") 35 | @Operation( 36 | summary = "获取项目对话记录", 37 | description = "根据项目ID获取对话历史记录,包含用户消息关联的AI思考过程", 38 | responses = { 39 | @ApiResponse( 40 | responseCode = "200", 41 | description = "成功获取对话记录", 42 | content = @Content( 43 | mediaType = "application/json", 44 | schema = @Schema(implementation = Result.class) 45 | ) 46 | ), 47 | @ApiResponse( 48 | responseCode = "404", 49 | description = "项目不存在", 50 | content = @Content( 51 | mediaType = "application/json", 52 | schema = @Schema(implementation = Result.class) 53 | ) 54 | ) 55 | } 56 | ) 57 | public Result> getConversationsByProjectId( 58 | @Parameter(description = "项目ID", required = true) 59 | @PathVariable Long projectId 60 | ) { 61 | List conversations = conversationService.getConversationsByProjectId(projectId); 62 | return Result.success(conversations); 63 | } 64 | } -------------------------------------------------------------------------------- /readify_agi/app/repositories/repair_document_repository.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | from sqlalchemy import select 3 | from sqlalchemy.ext.asyncio import AsyncSession 4 | from app.models.repair_document import RepairDocumentDB, RepairDocumentCreate 5 | import time 6 | 7 | class RepairDocumentRepository: 8 | def __init__(self, db: AsyncSession): 9 | self.db = db 10 | 11 | async def create(self, document: RepairDocumentCreate) -> RepairDocumentDB: 12 | """创建修复文档记录""" 13 | current_time = int(time.time()) 14 | db_document = RepairDocumentDB( 15 | **document.model_dump(), 16 | create_time=current_time, 17 | update_time=current_time 18 | ) 19 | self.db.add(db_document) 20 | await self.db.commit() 21 | await self.db.refresh(db_document) 22 | return db_document 23 | 24 | async def create_many(self, documents: List[RepairDocumentCreate]) -> List[RepairDocumentDB]: 25 | """批量创建修复文档记录""" 26 | current_time = int(time.time()) 27 | db_documents = [ 28 | RepairDocumentDB( 29 | **doc.model_dump(), 30 | create_time=current_time, 31 | update_time=current_time 32 | ) 33 | for doc in documents 34 | ] 35 | self.db.add_all(db_documents) 36 | await self.db.commit() 37 | for doc in db_documents: 38 | await self.db.refresh(doc) 39 | return db_documents 40 | 41 | async def get_by_file_id(self, file_id: int) -> List[RepairDocumentDB]: 42 | """获取指定文件的所有修复文档块""" 43 | query = select(RepairDocumentDB).where( 44 | RepairDocumentDB.file_id == file_id, 45 | RepairDocumentDB.deleted == False 46 | ).order_by(RepairDocumentDB.sequence) 47 | result = await self.db.execute(query) 48 | return list(result.scalars().all()) 49 | 50 | async def get_by_id(self, document_id: int) -> Optional[RepairDocumentDB]: 51 | """获取指定ID的修复文档""" 52 | query = select(RepairDocumentDB).where( 53 | RepairDocumentDB.id == document_id, 54 | RepairDocumentDB.deleted == False 55 | ) 56 | result = await self.db.execute(query) 57 | return result.scalar_one_or_none() 58 | 59 | async def delete(self, document_id: int) -> bool: 60 | """删除修复文档(软删除)""" 61 | document = await self.get_by_id(document_id) 62 | if not document: 63 | return False 64 | 65 | document.deleted = True 66 | document.update_time = int(time.time()) 67 | await self.db.commit() 68 | return True -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/domain/conversation/service/impl/ConversationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.domain.conversation.service.impl; 2 | 3 | import com.readify.server.domain.conversation.entity.AssistantThinking; 4 | import com.readify.server.domain.conversation.entity.ConversationHistory; 5 | import com.readify.server.domain.conversation.repository.ConversationRepository; 6 | import com.readify.server.domain.conversation.service.ConversationService; 7 | import com.readify.server.interfaces.conversation.converter.AssistantThinkingVOConverter; 8 | import com.readify.server.interfaces.conversation.converter.ConversationVOConverter; 9 | import com.readify.server.interfaces.conversation.vo.ConversationVO; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | import java.util.Optional; 17 | import java.util.stream.Collectors; 18 | 19 | @Service 20 | @RequiredArgsConstructor 21 | public class ConversationServiceImpl implements ConversationService { 22 | 23 | private final ConversationRepository conversationRepository; 24 | private final ConversationVOConverter conversationVOConverter; 25 | private final AssistantThinkingVOConverter assistantThinkingVOConverter; 26 | 27 | @Override 28 | public List getConversationsByProjectId(Long projectId) { 29 | // 1. 获取项目的所有对话历史 30 | List conversationHistoryList = conversationRepository.findByProjectId(projectId); 31 | 32 | // 2. 获取项目的所有思考过程记录 33 | List thinkingList = conversationRepository.findThinkingsByProjectId(projectId); 34 | 35 | // 3. 创建用户消息ID到思考过程的映射 36 | Map thinkingMap = new HashMap<>(); 37 | thinkingList.forEach(thinking -> thinkingMap.put(thinking.getUserMessageId(), thinking)); 38 | 39 | // 4. 转换为VO并关联思考过程 40 | return conversationHistoryList.stream() 41 | .map(conversation -> { 42 | ConversationVO vo = conversationVOConverter.toResponseDTO(conversation); 43 | 44 | // 用户消息关联思考过程 45 | if ("assistant".equals(conversation.getMessageType())) { 46 | Optional.ofNullable(thinkingMap.get(conversation.getId())) 47 | .ifPresent(thinking -> vo.setThinking(assistantThinkingVOConverter.toDTO(thinking))); 48 | } 49 | 50 | return vo; 51 | }) 52 | .collect(Collectors.toList()); 53 | } 54 | } -------------------------------------------------------------------------------- /readify_agi/app/services/callback_service.py: -------------------------------------------------------------------------------- 1 | import aiohttp 2 | import time 3 | import traceback 4 | from typing import Dict, Any, Optional 5 | from app.core.config import settings 6 | 7 | class CallbackService: 8 | """回调服务,用于通知第三方接口处理结果""" 9 | 10 | async def notify_file_processed( 11 | self, 12 | file_id: int, 13 | success: bool, 14 | message: str = "", 15 | additional_data: Optional[Dict[str, Any]] = None 16 | ) -> bool: 17 | """ 18 | 通知第三方接口文件处理完成 19 | 20 | Args: 21 | file_id: 文件ID 22 | success: 处理是否成功 23 | message: 处理结果消息 24 | additional_data: 额外数据 25 | 26 | Returns: 27 | bool: 回调是否成功 28 | """ 29 | # 获取回调配置 30 | callback_url = getattr(settings, "FILE_PROCESS_CALLBACK_URL", "") 31 | api_key = getattr(settings, "FILE_PROCESS_CALLBACK_API_KEY", "") 32 | 33 | if not callback_url: 34 | print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 警告:未配置回调URL,跳过回调") 35 | return False 36 | 37 | if not api_key: 38 | print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 警告:未配置回调API Key,跳过回调") 39 | return False 40 | 41 | # 准备回调数据 42 | payload = { 43 | "fileId": file_id, 44 | "success": success, 45 | "message": message, 46 | "timestamp": int(time.time()) 47 | } 48 | 49 | # 添加额外数据 50 | if additional_data: 51 | payload.update(additional_data) 52 | 53 | # 准备请求头 54 | headers = { 55 | "Content-Type": "application/json", 56 | "X-API-Key": api_key 57 | } 58 | 59 | try: 60 | print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 开始回调通知,URL: {callback_url}") 61 | async with aiohttp.ClientSession() as session: 62 | async with session.post( 63 | callback_url, 64 | json=payload, 65 | headers=headers, 66 | timeout=10 # 10秒超时 67 | ) as response: 68 | if response.status == 200: 69 | response_text = await response.text() 70 | print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 回调成功,响应: {response_text}") 71 | return True 72 | else: 73 | print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 回调失败,状态码: {response.status}") 74 | return False 75 | except Exception as e: 76 | print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 回调异常: {str(e)}") 77 | print(traceback.format_exc()) 78 | return False -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/infrastructure/persistence/repository/AuthUserRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.infrastructure.persistence.repository; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import com.readify.server.domain.auth.model.AuthUser; 5 | import com.readify.server.domain.auth.repository.AuthUserRepository; 6 | import com.readify.server.infrastructure.persistence.entity.UserEntity; 7 | import com.readify.server.infrastructure.persistence.mapper.UserMapper; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.stereotype.Repository; 10 | 11 | @Repository 12 | @RequiredArgsConstructor 13 | public class AuthUserRepositoryImpl implements AuthUserRepository { 14 | private final UserMapper userMapper; 15 | 16 | @Override 17 | public AuthUser save(AuthUser authUser) { 18 | UserEntity entity = toEntity(authUser); 19 | if (authUser.getId() == null) { 20 | userMapper.insert(entity); 21 | } else { 22 | userMapper.updateById(entity); 23 | } 24 | return toAuthUser(entity); 25 | } 26 | 27 | @Override 28 | public AuthUser findByUsername(String username) { 29 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 30 | queryWrapper.eq(UserEntity::getUsername, username); 31 | UserEntity entity = userMapper.selectOne(queryWrapper); 32 | return entity != null ? toAuthUser(entity) : null; 33 | } 34 | 35 | @Override 36 | public boolean existsByUsername(String username) { 37 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 38 | queryWrapper.eq(UserEntity::getUsername, username); 39 | return userMapper.selectCount(queryWrapper) > 0; 40 | } 41 | 42 | private UserEntity toEntity(AuthUser authUser) { 43 | if (authUser == null) { 44 | return null; 45 | } 46 | UserEntity entity = new UserEntity(); 47 | entity.setId(authUser.getId()); 48 | entity.setUsername(authUser.getUsername()); 49 | entity.setPassword(authUser.getPassword()); 50 | entity.setEnabled(authUser.getEnabled()); 51 | entity.setCreateTime(authUser.getCreateTime()); 52 | entity.setUpdateTime(authUser.getUpdateTime()); 53 | return entity; 54 | } 55 | 56 | private AuthUser toAuthUser(UserEntity entity) { 57 | if (entity == null) { 58 | return null; 59 | } 60 | AuthUser authUser = new AuthUser(); 61 | authUser.setId(entity.getId()); 62 | authUser.setUsername(entity.getUsername()); 63 | authUser.setPassword(entity.getPassword()); 64 | authUser.setEnabled(entity.getEnabled()); 65 | authUser.setCreateTime(entity.getCreateTime()); 66 | authUser.setUpdateTime(entity.getUpdateTime()); 67 | return authUser; 68 | } 69 | } -------------------------------------------------------------------------------- /readify_server/src/main/java/com/readify/server/interfaces/user/UserController.java: -------------------------------------------------------------------------------- 1 | package com.readify.server.interfaces.user; 2 | 3 | import com.readify.server.domain.user.model.User; 4 | import com.readify.server.domain.user.service.UserService; 5 | import com.readify.server.infrastructure.common.Result; 6 | import com.readify.server.interfaces.user.converter.UserVOConverter; 7 | import com.readify.server.interfaces.user.vo.UserVO; 8 | import io.swagger.v3.oas.annotations.Operation; 9 | import io.swagger.v3.oas.annotations.Parameter; 10 | import io.swagger.v3.oas.annotations.tags.Tag; 11 | import lombok.RequiredArgsConstructor; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequestMapping("/users") 18 | @RequiredArgsConstructor 19 | @Tag(name = "用户管理", description = "用户相关的API接口") 20 | public class UserController { 21 | private final UserService userService; 22 | 23 | @Operation(summary = "更新用户信息") 24 | @PutMapping("/{id}") 25 | public Result updateUser( 26 | @Parameter(description = "用户ID") @PathVariable Long id, 27 | @RequestBody UserVO userVO) { 28 | User user = UserVOConverter.INSTANCE.toDomain(userVO); 29 | user.setId(id); 30 | User updatedUser = userService.updateUser(user); 31 | return Result.success(UserVOConverter.INSTANCE.toVO(updatedUser)); 32 | } 33 | 34 | @Operation(summary = "删除用户") 35 | @DeleteMapping("/{id}") 36 | public Result deleteUser( 37 | @Parameter(description = "用户ID") @PathVariable Long id) { 38 | userService.deleteUser(id); 39 | return Result.success(); 40 | } 41 | 42 | @Operation(summary = "根据ID获取用户信息") 43 | @GetMapping("/{id}") 44 | public Result getUserById( 45 | @Parameter(description = "用户ID") @PathVariable Long id) { 46 | User user = userService.getUserById(id); 47 | return Result.success(UserVOConverter.INSTANCE.toVO(user)); 48 | } 49 | 50 | @Operation(summary = "根据用户名获取用户信息") 51 | @GetMapping("/username/{username}") 52 | public Result getUserByUsername( 53 | @Parameter(description = "用户名") @PathVariable String username) { 54 | User user = userService.getUserByUsername(username); 55 | return Result.success(UserVOConverter.INSTANCE.toVO(user)); 56 | } 57 | 58 | @Operation(summary = "获取所有用户列表") 59 | @GetMapping 60 | public Result> getAllUsers() { 61 | List users = userService.getAllUsers(); 62 | return Result.success(UserVOConverter.INSTANCE.toVOList(users)); 63 | } 64 | 65 | @Operation(summary = "检查用户名是否已存在") 66 | @GetMapping("/check/{username}") 67 | public Result checkUsername( 68 | @Parameter(description = "用户名") @PathVariable String username) { 69 | return Result.success(userService.isUsernameExists(username)); 70 | } 71 | } --------------------------------------------------------------------------------