diff --git a/README.md b/README.md index 3db3ff3..663fdb6 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,163 @@ curl http://localhost:9900/milvus/health ``` +--- + +## 🏗️ Phase 1: 基础设施搭建(已完成) + +### 架构概览 + +Phase 1 完成了项目的基础设施搭建,包括: +- ✅ 数据持久化层(MySQL + JPA + Flyway) +- ✅ 会话管理(Redis) +- ✅ 向量索引(Milvus 集成) +- ✅ 文档管理服务(上传/查询/删除) +- ✅ 统一异常处理 +- ✅ RESTful API 接口 + +### 本地开发环境 + +#### 前置要求 + +- Java 17+ +- Maven 3.8+ +- Docker & Docker Compose(用于本地数据库) + +#### 快速开始 + +**1. 启动依赖服务** + +```bash +# 启动 MySQL + Redis + Milvus(本地开发) +docker-compose up -d + +# 查看服务状态 +docker-compose ps +``` + +**2. 配置应用** + +复制 `src/main/resources/application.yml` 并根据需要修改: + +```yaml +spring: + datasource: + url: jdbc:mysql://localhost:3306/super_biz_agent + username: superbiz + password: superbiz123 + + data: + redis: + host: localhost + port: 6379 + password: redis123 + +milvus: + host: localhost + port: 19530 +``` + +**3. 运行应用** + +```bash +# 编译 +mvn clean compile + +# 运行测试 +mvn test + +# 启动应用 +mvn spring-boot:run +``` + +应用将在 `http://localhost:9900` 启动。 + +#### 数据库迁移 + +Flyway 会自动执行数据库迁移: + +``` +src/main/resources/db/migration/ +├── V001__create_diagnosis_record.sql +├── V002__create_case_library.sql +└── V003__create_api_document.sql +``` + +#### API 文档 + +**文档管理接口**: + +```bash +# 上传文档(仅支持 .md 和 .txt) +POST /api/documents/upload +Content-Type: multipart/form-data + +# 查询文档 +GET /api/documents/{docId} +GET /api/documents/status/{status}?page=0&size=20 +GET /api/documents/faultSource/{faultSource} + +# 删除文档 +DELETE /api/documents/{docId} +``` + +**健康检查**: + +```bash +# Milvus 连接测试 +mvn test -Dtest=SimpleMilvusTest + +# MySQL 连接测试 +mvn test -Dtest=MySQLConnectionTest + +# Redis 连接测试 +mvn test -Dtest=RedisConnectionTest +``` + +### 项目结构 + +``` +com.superbiz.agent/ +├── controller/ # REST 控制器 +│ ├── ChatController.java +│ ├── DocumentController.java +│ └── FileUploadController.java +├── service/ # 业务逻辑层 +│ ├── DocumentManagementService.java +│ ├── TextExtractorService.java +│ ├── session/ # 会话管理 +│ └── ... +├── repository/ # 数据访问层 +│ ├── ApiDocumentRepository.java +│ ├── CaseLibraryRepository.java +│ └── DiagnosisRecordRepository.java +├── domain/ # 领域模型 +│ ├── entity/ # JPA 实体 +│ ├── model/ # 数据模型 +│ └── enums/ # 枚举类 +├── dto/ # 数据传输对象 +├── exception/ # 异常处理 +│ ├── GlobalExceptionHandler.java +│ ├── SessionNotFoundException.java +│ └── DocumentProcessException.java +└── config/ # 配置类 +``` + +### 待办事项 + +- [ ] 向量化索引实现(VectorIndexService.indexDocumentChunks) +- [ ] 混合检索工具(精确匹配 + 语义检索 + RRF 融合) +- [ ] 文档管理集成测试 + +### 技术决策 + +- **包名重构**:`org.example` → `com.superbiz.agent` +- **文本格式**:仅支持 Markdown (.md) 和纯文本 (.txt),其他格式需外部转换服务 +- **分块策略**:使用 DocumentChunkService 的智能分块(按标题、段落边界) +- **向量数据库**:生产环境推荐 Zilliz Cloud,本地开发可用 Docker Milvus + +--- + **版本**: v1.0.0 **作者**: chief **许可证**: MIT diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3bac534 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,108 @@ +version: '3.8' + +services: + # MySQL 数据库 + mysql: + image: mysql:8.0 + container_name: superbiz-mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: root123456 + MYSQL_DATABASE: super_biz_agent + MYSQL_USER: superbiz + MYSQL_PASSWORD: superbiz123 + TZ: Asia/Shanghai + ports: + - "3306:3306" + volumes: + - mysql-data:/var/lib/mysql + - ./docker/mysql/init:/docker-entrypoint-initdb.d + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis 缓存 + redis: + image: redis:7-alpine + container_name: superbiz-redis + restart: always + ports: + - "6379:6379" + volumes: + - redis-data:/data + command: redis-server --appendonly yes --requirepass redis123 + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # Milvus 向量数据库(Standalone 模式) + # 注意:生产环境建议使用 Zilliz Cloud 或 Milvus 集群 + etcd: + image: quay.io/coreos/etcd:v3.5.5 + container_name: superbiz-etcd + environment: + - ETCD_AUTO_COMPACTION_MODE=revision + - ETCD_AUTO_COMPACTION_RETENTION=1000 + - ETCD_QUOTA_BACKEND_BYTES=4294967296 + - ETCD_SNAPSHOT_COUNT=50000 + volumes: + - etcd-data:/etcd + command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd + healthcheck: + test: ["CMD", "etcdctl", "endpoint", "health"] + interval: 30s + timeout: 20s + retries: 3 + + minio: + image: minio/minio:RELEASE.2023-03-20T20-16-18Z + container_name: superbiz-minio + environment: + MINIO_ACCESS_KEY: minioadmin + MINIO_SECRET_KEY: minioadmin + volumes: + - minio-data:/minio_data + command: minio server /minio_data --console-address ":9001" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + + milvus: + image: milvusdb/milvus:v2.3.3 + container_name: superbiz-milvus + depends_on: + - etcd + - minio + environment: + ETCD_ENDPOINTS: etcd:2379 + MINIO_ADDRESS: minio:9000 + volumes: + - milvus-data:/var/lib/milvus + ports: + - "19530:19530" + - "9091:9091" + command: ["milvus", "run", "standalone"] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] + interval: 30s + start_period: 90s + timeout: 20s + retries: 3 + +volumes: + mysql-data: + redis-data: + etcd-data: + minio-data: + milvus-data: + +networks: + default: + name: superbiz-network diff --git a/openspec/changes/phase-1-infrastructure/tasks.md b/openspec/changes/phase-1-infrastructure/tasks.md index 27a614d..cfccc8f 100644 --- a/openspec/changes/phase-1-infrastructure/tasks.md +++ b/openspec/changes/phase-1-infrastructure/tasks.md @@ -47,6 +47,6 @@ ## 6. 全局完善 -- [ ] 6.1 统一异常处理 (GlobalExceptionHandler, SessionNotFoundException, DocumentProcessException) -- [ ] 6.2 Docker Compose 配置 (MySQL + Redis + Milvus) -- [ ] 6.3 更新 README.md (Phase 1 安装说明与本地开发指南) +- [x] 6.1 统一异常处理 (GlobalExceptionHandler, SessionNotFoundException, DocumentProcessException) +- [x] 6.2 Docker Compose 配置 (MySQL + Redis + Milvus) +- [x] 6.3 更新 README.md (Phase 1 安装说明与本地开发指南) diff --git a/src/main/java/com/superbiz/agent/exception/GlobalExceptionHandler.java b/src/main/java/com/superbiz/agent/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..796cd79 --- /dev/null +++ b/src/main/java/com/superbiz/agent/exception/GlobalExceptionHandler.java @@ -0,0 +1,72 @@ +package com.superbiz.agent.exception; + +import com.superbiz.agent.dto.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MaxUploadSizeExceededException; + +/** + * 全局异常处理器 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * 处理会话未找到异常 + */ + @ExceptionHandler(SessionNotFoundException.class) + public ResponseEntity> handleSessionNotFound(SessionNotFoundException e) { + log.warn("会话未找到: {}", e.getMessage()); + return ResponseEntity + .status(HttpStatus.NOT_FOUND) + .body(Result.error(404, e.getMessage())); + } + + /** + * 处理文档处理异常 + */ + @ExceptionHandler(DocumentProcessException.class) + public ResponseEntity> handleDocumentProcess(DocumentProcessException e) { + log.error("文档处理异常: {}", e.getMessage(), e); + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(Result.error(400, e.getMessage())); + } + + /** + * 处理文件上传大小超限异常 + */ + @ExceptionHandler(MaxUploadSizeExceededException.class) + public ResponseEntity> handleMaxUploadSizeExceeded(MaxUploadSizeExceededException e) { + log.warn("文件大小超限: {}", e.getMessage()); + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(Result.error(400, "文件大小超限,最大允许 10MB")); + } + + /** + * 处理非法参数异常 + */ + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity> handleIllegalArgument(IllegalArgumentException e) { + log.warn("非法参数: {}", e.getMessage()); + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(Result.error(400, "参数错误: " + e.getMessage())); + } + + /** + * 处理所有未捕获的异常 + */ + @ExceptionHandler(Exception.class) + public ResponseEntity> handleGenericException(Exception e) { + log.error("系统异常", e); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(Result.error(500, "系统内部错误,请稍后重试")); + } +}