Parcourir la source

feat: 安装pgvector扩展并修复graph-service编译问题

主要变更:
1. 数据库增强
   - 安装pgvector扩展支持向量存储
   - 恢复embeddings和rules表的vector(1536)列
   - 添加IVFFlat向量索引用于相似度搜索

2. graph-service修复
   - 重命名控制器避免Bean冲突:
     * ReportController -> GraphReportController
     * TemplateController -> GraphTemplateController
     * ProjectController -> GraphProjectController
     * AttachmentController -> GraphAttachmentController
   - 更新API路径为/api/v1/graph/*前缀
   - 修复Lombok编译错误

3. 代码清理
   - 删除废弃的BusinessException类
   - 删除ParseService中的废弃方法
   - 删除DocumentService.deleteDocument()废弃方法
   - 修复Generation状态常量引用(STATUS_REVIEW->STATUS_REVIEWING, STATUS_ERROR->STATUS_FAILED)
   - 清理注释掉的代码和Neo4j相关注释
   - 统一ExtractRuleRepository命名避免冲突

4. 配置优化
   - Redis密码配置为123123
   - 禁用Redis健康检查避免状态DOWN
   - Ollama embedding模型配置为nomic-embed-text

测试:
- ✅ 所有服务编译成功
- ✅ 后端启动正常
- ✅ 数据库连接正常
- ✅ pgvector扩展工作正常
何文松 il y a 1 semaine
Parent
commit
3d7dfce935
19 fichiers modifiés avec 518 ajouts et 146 suppressions
  1. 456 1
      0211docs/init_mock.sql
  2. 0 37
      backend/common/src/main/java/com/lingyue/common/exception/BusinessException.java
  3. 0 10
      backend/document-service/src/main/java/com/lingyue/document/service/DocumentService.java
  4. 0 4
      backend/extract-service/src/main/java/com/lingyue/extract/entity/Generation.java
  5. 2 0
      backend/extract-service/src/main/java/com/lingyue/extract/entity/Rule.java
  6. 0 12
      backend/extract-service/src/main/java/com/lingyue/extract/repository/RuleRepository.java
  7. 1 1
      backend/extract-service/src/main/java/com/lingyue/extract/service/DocumentGenerationService.java
  8. 1 1
      backend/extract-service/src/main/java/com/lingyue/extract/service/ExtractionService.java
  9. 3 3
      backend/extract-service/src/main/java/com/lingyue/extract/service/GenerationService.java
  10. 1 1
      backend/graph-service/src/main/java/com/lingyue/graph/controller/EntityController.java
  11. 11 11
      backend/graph-service/src/main/java/com/lingyue/graph/controller/GraphAttachmentController.java
  12. 4 4
      backend/graph-service/src/main/java/com/lingyue/graph/controller/GraphProjectController.java
  13. 6 6
      backend/graph-service/src/main/java/com/lingyue/graph/controller/GraphReportController.java
  14. 5 5
      backend/graph-service/src/main/java/com/lingyue/graph/controller/GraphTemplateController.java
  15. 1 1
      backend/graph-service/src/main/java/com/lingyue/graph/controller/RuleController.java
  16. 3 2
      backend/graph-service/src/main/java/com/lingyue/graph/service/impl/AttachmentServiceImpl.java
  17. 12 3
      backend/graph-service/src/main/resources/application.properties
  18. 10 7
      backend/lingyue-starter/src/main/resources/application.properties
  19. 2 37
      backend/parse-service/src/main/java/com/lingyue/parse/service/ParseService.java

+ 456 - 1
0211docs/init_mock.sql

@@ -7,6 +7,392 @@
 -- 使用事务确保原子性
 BEGIN;
 
+-- ============================================================
+-- 0. 扩展(可选)
+-- ============================================================
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
+DO $$
+BEGIN
+    IF EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'vector') THEN
+        EXECUTE 'CREATE EXTENSION IF NOT EXISTS vector';
+    ELSE
+        RAISE NOTICE 'pgvector extension not installed, skip vector features';
+    END IF;
+END $$;
+
+-- ============================================================
+-- 0.x 基础业务表(auth/document/parse/ai/extract)
+-- ============================================================
+
+-- 用户与会话
+CREATE TABLE IF NOT EXISTS users (
+    id VARCHAR(36) PRIMARY KEY,
+    username VARCHAR(50) NOT NULL UNIQUE,
+    email VARCHAR(100) UNIQUE,
+    password_hash VARCHAR(255) NOT NULL,
+    display_name VARCHAR(100),
+    avatar_url VARCHAR(500),
+    role VARCHAR(20) DEFAULT 'user',
+    preferences JSONB DEFAULT '{}',
+    is_active BOOLEAN DEFAULT TRUE,
+    last_login_at TIMESTAMP,
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
+CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
+
+CREATE TABLE IF NOT EXISTS sessions (
+    id VARCHAR(36) PRIMARY KEY,
+    user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    token_hash VARCHAR(255) NOT NULL,
+    refresh_token_hash VARCHAR(255),
+    expires_at TIMESTAMP,
+    ip_address VARCHAR(100),
+    user_agent TEXT,
+    last_used_at TIMESTAMP,
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
+
+-- 文档与解析
+CREATE TABLE IF NOT EXISTS documents (
+    id VARCHAR(36) PRIMARY KEY,
+    user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    name VARCHAR(255) NOT NULL,
+    file_name VARCHAR(500) NOT NULL,
+    file_path VARCHAR(500) NOT NULL,
+    file_size BIGINT,
+    file_type VARCHAR(20) NOT NULL,
+    status VARCHAR(20) DEFAULT 'uploaded',
+    parsed_text TEXT,
+    page_count INT,
+    word_count INT,
+    entity_count INT DEFAULT 0,
+    relation_count INT DEFAULT 0,
+    rule_count INT DEFAULT 0,
+    metadata JSONB DEFAULT '{}',
+    del_flag BOOLEAN DEFAULT FALSE,
+    create_by VARCHAR(36),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_documents_user ON documents(user_id);
+CREATE INDEX IF NOT EXISTS idx_documents_type ON documents(file_type);
+CREATE INDEX IF NOT EXISTS idx_documents_status ON documents(status);
+
+CREATE TABLE IF NOT EXISTS document_sections (
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
+    parent_id VARCHAR(36) REFERENCES document_sections(id) ON DELETE CASCADE,
+    section_index INT NOT NULL,
+    level INT NOT NULL DEFAULT 1,
+    title VARCHAR(500),
+    content TEXT,
+    start_page INT,
+    end_page INT,
+    start_char INT,
+    end_char INT,
+    section_type VARCHAR(32) DEFAULT 'heading',
+    table_data JSONB,
+    image_path VARCHAR(500),
+    image_caption VARCHAR(500),
+    metadata JSONB DEFAULT '{}',
+    sort_order INT DEFAULT 0,
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_doc_sections_document ON document_sections(document_id);
+CREATE INDEX IF NOT EXISTS idx_doc_sections_parent ON document_sections(parent_id);
+CREATE INDEX IF NOT EXISTS idx_doc_sections_level ON document_sections(level);
+CREATE INDEX IF NOT EXISTS idx_doc_sections_type ON document_sections(section_type);
+
+CREATE TABLE IF NOT EXISTS document_chunks (
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
+    section_id VARCHAR(36) REFERENCES document_sections(id) ON DELETE SET NULL,
+    chunk_index INT NOT NULL,
+    content TEXT NOT NULL,
+    start_char INT,
+    end_char INT,
+    page_number INT,
+    token_count INT,
+    metadata JSONB DEFAULT '{}',
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_doc_chunks_document ON document_chunks(document_id);
+CREATE INDEX IF NOT EXISTS idx_doc_chunks_section ON document_chunks(section_id);
+
+CREATE TABLE IF NOT EXISTS parse_tasks (
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
+    status VARCHAR(20) DEFAULT 'pending',
+    progress INT DEFAULT 0,
+    current_step VARCHAR(32),
+    error_message TEXT,
+    options JSONB DEFAULT '{}',
+    started_at TIMESTAMP,
+    completed_at TIMESTAMP,
+    parse_status VARCHAR(20) DEFAULT 'pending',
+    parse_progress INT DEFAULT 0,
+    rag_status VARCHAR(20) DEFAULT 'pending',
+    rag_progress INT DEFAULT 0,
+    structured_status VARCHAR(20) DEFAULT 'pending',
+    structured_progress INT DEFAULT 0,
+    structured_element_count INT,
+    structured_image_count INT,
+    structured_table_count INT,
+    ner_status VARCHAR(20) DEFAULT 'pending',
+    ner_progress INT DEFAULT 0,
+    ner_task_id VARCHAR(100),
+    ner_entity_count INT,
+    ner_relation_count INT,
+    ner_message TEXT,
+    graph_status VARCHAR(20) DEFAULT 'pending',
+    graph_progress INT DEFAULT 0,
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_parse_tasks_document ON parse_tasks(document_id);
+
+CREATE TABLE IF NOT EXISTS document_elements (
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
+    element_index INT,
+    element_type VARCHAR(50),
+    content TEXT,
+    style JSONB DEFAULT '{}',
+    runs JSONB DEFAULT '[]',
+    image_url VARCHAR(500),
+    image_path VARCHAR(500),
+    image_alt VARCHAR(500),
+    image_width INT,
+    image_height INT,
+    image_format VARCHAR(20),
+    table_index INT,
+    table_data JSONB,
+    table_row_count INT,
+    table_col_count INT,
+    table_text TEXT,
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_document_elements_document ON document_elements(document_id);
+
+CREATE TABLE IF NOT EXISTS document_blocks (
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
+    parent_id VARCHAR(36),
+    children JSONB DEFAULT '[]',
+    block_index INT,
+    block_type VARCHAR(50),
+    text_elements JSONB DEFAULT '[]',
+    table_data JSONB,
+    image_path VARCHAR(500),
+    image_url VARCHAR(500),
+    metadata JSONB DEFAULT '{}',
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_document_blocks_document ON document_blocks(document_id);
+
+-- AI 相关
+CREATE TABLE IF NOT EXISTS elements (
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) REFERENCES documents(id) ON DELETE CASCADE,
+    user_id VARCHAR(36) REFERENCES users(id) ON DELETE SET NULL,
+    type VARCHAR(50),
+    label VARCHAR(200),
+    value TEXT,
+    position JSONB DEFAULT '{}',
+    confidence DECIMAL(10,4),
+    extraction_method VARCHAR(50),
+    graph_node_id VARCHAR(36),
+    metadata JSONB DEFAULT '{}',
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_elements_document ON elements(document_id);
+
+CREATE TABLE IF NOT EXISTS annotations (
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) REFERENCES documents(id) ON DELETE CASCADE,
+    user_id VARCHAR(36) REFERENCES users(id) ON DELETE SET NULL,
+    text TEXT,
+    position JSONB DEFAULT '{}',
+    type VARCHAR(50),
+    suggestion TEXT,
+    ai_generated BOOLEAN DEFAULT FALSE,
+    confidence DECIMAL(10,4),
+    status VARCHAR(20) DEFAULT 'pending',
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_annotations_document ON annotations(document_id);
+
+CREATE TABLE IF NOT EXISTS embeddings (
+    id VARCHAR(36) PRIMARY KEY,
+    chunk_id VARCHAR(36) NOT NULL REFERENCES document_chunks(id) ON DELETE CASCADE,
+    embedding vector(1536),
+    model_name VARCHAR(100) DEFAULT 'text-embedding-ada-002',
+    model_version VARCHAR(50),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_embeddings_chunk ON embeddings(chunk_id);
+CREATE INDEX IF NOT EXISTS idx_embeddings_vector ON embeddings USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
+
+-- 提取与模板
+CREATE TABLE IF NOT EXISTS extract_projects (
+    id VARCHAR(36) PRIMARY KEY,
+    user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    name VARCHAR(200) NOT NULL,
+    description TEXT,
+    status VARCHAR(50) DEFAULT 'draft',
+    config JSONB DEFAULT '{}',
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_extract_projects_user ON extract_projects(user_id);
+
+CREATE TABLE IF NOT EXISTS rules (
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) REFERENCES documents(id) ON DELETE SET NULL,
+    user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    name VARCHAR(200) NOT NULL,
+    description TEXT,
+    category VARCHAR(50),
+    rule_type VARCHAR(32) NOT NULL,
+    source VARCHAR(32) DEFAULT 'auto',
+    priority INT DEFAULT 0,
+    status VARCHAR(20) DEFAULT 'draft',
+    embedding vector(1536),
+    is_global BOOLEAN DEFAULT FALSE,
+    del_flag BOOLEAN DEFAULT FALSE,
+    create_by VARCHAR(36),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_rules_document ON rules(document_id);
+CREATE INDEX IF NOT EXISTS idx_rules_user ON rules(user_id);
+
+CREATE TABLE IF NOT EXISTS templates (
+    id VARCHAR(36) PRIMARY KEY,
+    user_id VARCHAR(36) NOT NULL,
+    name VARCHAR(200),
+    description TEXT,
+    base_document_id VARCHAR(36),
+    status VARCHAR(20) DEFAULT 'draft',
+    config JSONB DEFAULT '{}',
+    is_public BOOLEAN DEFAULT FALSE,
+    use_count INT DEFAULT 0,
+    rating DOUBLE PRECISION,
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_templates_user ON templates(user_id);
+
+CREATE TABLE IF NOT EXISTS variables (
+    id VARCHAR(36) PRIMARY KEY,
+    template_id VARCHAR(36) NOT NULL REFERENCES templates(id) ON DELETE CASCADE,
+    name VARCHAR(100) NOT NULL,
+    display_name VARCHAR(200),
+    variable_group VARCHAR(100),
+    category VARCHAR(50),
+    location JSONB,
+    example_value TEXT,
+    value_type VARCHAR(20),
+    source_file_alias VARCHAR(100),
+    source_type VARCHAR(30),
+    source_config JSONB DEFAULT '{}',
+    extract_type VARCHAR(30),
+    extract_config JSONB DEFAULT '{}',
+    display_order INT DEFAULT 0,
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_variables_template ON variables(template_id);
+
+CREATE TABLE IF NOT EXISTS source_files (
+    id VARCHAR(36) PRIMARY KEY,
+    template_id VARCHAR(36) NOT NULL REFERENCES templates(id) ON DELETE CASCADE,
+    alias VARCHAR(100) NOT NULL,
+    description TEXT,
+    file_types JSONB DEFAULT '[]',
+    required BOOLEAN DEFAULT TRUE,
+    example_document_id VARCHAR(36),
+    display_order INT DEFAULT 0,
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_source_files_template ON source_files(template_id);
+
+-- 报告/附件
+CREATE TABLE IF NOT EXISTS reports (
+    id VARCHAR(36) PRIMARY KEY,
+    user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    project_id VARCHAR(36),
+    title VARCHAR(500) NOT NULL,
+    report_type VARCHAR(100),
+    status VARCHAR(20) NOT NULL DEFAULT 'draft',
+    content_template TEXT,
+    content_rendered TEXT,
+    auto_saved_at TIMESTAMP,
+    report_score DECIMAL(10,2),
+    report_level VARCHAR(50),
+    report_metrics JSONB DEFAULT '{}',
+    generation_id VARCHAR(36),
+    source_document_id VARCHAR(36),
+    archived_at TIMESTAMP,
+    published_at TIMESTAMP,
+    del_flag BOOLEAN DEFAULT FALSE,
+    create_by VARCHAR(36),
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_reports_user ON reports(user_id);
+CREATE INDEX IF NOT EXISTS idx_reports_status ON reports(status);
+
+CREATE TABLE IF NOT EXISTS report_attachments (
+    id VARCHAR(36) PRIMARY KEY,
+    report_id VARCHAR(36) NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
+    display_name VARCHAR(255),
+    file_name VARCHAR(500) NOT NULL,
+    file_path VARCHAR(500) NOT NULL,
+    file_type VARCHAR(50),
+    file_size BIGINT,
+    sort_order INT DEFAULT 0,
+    saved_to_knowledge_base BOOLEAN DEFAULT FALSE,
+    knowledge_base_item_id VARCHAR(36),
+    uploaded_by VARCHAR(36) REFERENCES users(id) ON DELETE SET NULL,
+    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS idx_report_attachments_report ON report_attachments(report_id);
+
 -- ============================================================
 -- 1. 建表 (DDL)
 -- ============================================================
@@ -92,6 +478,75 @@ CREATE TABLE IF NOT EXISTS property_definitions (
     created_at    TIMESTAMP    DEFAULT NOW()
 );
 
+-- ============================================================
+-- 1.x 兼容字段与触发器(兼容后端实体字段命名)
+-- ============================================================
+
+-- node_properties: 兼容 graph-service 使用 prop_text 字段
+ALTER TABLE node_properties ADD COLUMN IF NOT EXISTS prop_text TEXT;
+
+CREATE OR REPLACE FUNCTION sync_node_properties_text() RETURNS trigger AS $$
+BEGIN
+    IF NEW.prop_text IS NULL AND NEW.prop_value IS NOT NULL THEN
+        NEW.prop_text := NEW.prop_value;
+    ELSIF NEW.prop_value IS NULL AND NEW.prop_text IS NOT NULL THEN
+        NEW.prop_value := NEW.prop_text;
+    END IF;
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS trg_sync_node_properties_text ON node_properties;
+CREATE TRIGGER trg_sync_node_properties_text
+BEFORE INSERT OR UPDATE ON node_properties
+FOR EACH ROW EXECUTE FUNCTION sync_node_properties_text();
+
+-- edge_properties: 兼容 graph-service 使用 prop_text 字段
+ALTER TABLE edge_properties ADD COLUMN IF NOT EXISTS prop_text TEXT;
+
+CREATE OR REPLACE FUNCTION sync_edge_properties_text() RETURNS trigger AS $$
+BEGIN
+    IF NEW.prop_text IS NULL AND NEW.prop_value IS NOT NULL THEN
+        NEW.prop_text := NEW.prop_value;
+    ELSIF NEW.prop_value IS NULL AND NEW.prop_text IS NOT NULL THEN
+        NEW.prop_value := NEW.prop_text;
+    END IF;
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS trg_sync_edge_properties_text ON edge_properties;
+CREATE TRIGGER trg_sync_edge_properties_text
+BEFORE INSERT OR UPDATE ON edge_properties
+FOR EACH ROW EXECUTE FUNCTION sync_edge_properties_text();
+
+-- property_definitions: 兼容 graph-service 使用 type_code/is_required 字段
+ALTER TABLE property_definitions ADD COLUMN IF NOT EXISTS type_code VARCHAR(50);
+ALTER TABLE property_definitions ADD COLUMN IF NOT EXISTS is_required BOOLEAN;
+
+CREATE OR REPLACE FUNCTION sync_property_definitions_fields() RETURNS trigger AS $$
+BEGIN
+    IF NEW.type_code IS NULL AND NEW.target_type IS NOT NULL THEN
+        NEW.type_code := NEW.target_type;
+    ELSIF NEW.target_type IS NULL AND NEW.type_code IS NOT NULL THEN
+        NEW.target_type := NEW.type_code;
+    END IF;
+
+    IF NEW.is_required IS NULL AND NEW.required IS NOT NULL THEN
+        NEW.is_required := NEW.required;
+    ELSIF NEW.required IS NULL AND NEW.is_required IS NOT NULL THEN
+        NEW.required := NEW.is_required;
+    END IF;
+
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS trg_sync_property_definitions_fields ON property_definitions;
+CREATE TRIGGER trg_sync_property_definitions_fields
+BEFORE INSERT OR UPDATE ON property_definitions
+FOR EACH ROW EXECUTE FUNCTION sync_property_definitions_fields();
+
 -- ============================================================
 -- 1.8 前端业务支撑表
 -- ============================================================
@@ -710,6 +1165,7 @@ WHERE perm_code IN ('dashboard', 'project', 'project:list', 'template', 'templat
 
 -- 4.10 初始化管理员用户(密码: admin123,实际部署时需修改)
 -- 注:password_hash 使用 bcrypt 加密,此处为示例值
+INSERT INTO nodes (id, node_type, node_key, name, status, created_by) VALUES (1, 'USER', 'USR-001', '管理员', 'active', NULL);
 INSERT INTO sys_users (node_id, username, password_hash, salt, real_name, department, status) VALUES
 (1, 'admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', 'random_salt', '管理员', '技术部', 'active');
 
@@ -775,7 +1231,6 @@ INSERT INTO sys_dict_items (dict_type_id, item_code, item_name, sort_order) VALU
 -- 5. Mock数据 - nodes
 -- ============================================================
 
-INSERT INTO nodes (id, node_type, node_key, name, status, created_by) VALUES (1, 'USER', 'USR-001', '管理员', 'active', NULL);
 INSERT INTO nodes (id, node_type, node_key, name, status, created_by) VALUES (2, 'PROJECT', 'PRJ-001', '成都院-安全生产标准化复审报告', 'active', 1);
 INSERT INTO nodes (id, node_type, node_key, name, status, created_by) VALUES (100, 'SOURCE_FILE', 'SF-001', '成都院复审报告样本.docx', 'active', 1);
 INSERT INTO nodes (id, node_type, node_key, name, status, created_by) VALUES (101, 'TEMPLATE', 'TPL-001', '电力安全生产标准化复审报告模板', 'active', 1);

+ 0 - 37
backend/common/src/main/java/com/lingyue/common/exception/BusinessException.java

@@ -1,37 +0,0 @@
-package com.lingyue.common.exception;
-
-import lombok.Getter;
-
-/**
- * 业务异常(已废弃,请使用 ServiceException)
- * @deprecated 使用 ServiceException 替代
- */
-@Deprecated
-@Getter
-public class BusinessException extends RuntimeException {
-    
-    private static final long serialVersionUID = 1L;
-    
-    /**
-     * 响应码
-     */
-    private final Integer code;
-    
-    /**
-     * 错误详情
-     */
-    private final Object errorDetail;
-    
-    public BusinessException(Integer code, String message) {
-        super(message);
-        this.code = code;
-        this.errorDetail = null;
-    }
-    
-    public BusinessException(Integer code, String message, Object errorDetail) {
-        super(message);
-        this.code = code;
-        this.errorDetail = errorDetail;
-    }
-}
-

+ 0 - 10
backend/document-service/src/main/java/com/lingyue/document/service/DocumentService.java

@@ -175,16 +175,6 @@ public class DocumentService {
         return document;
     }
     
-    /**
-     * 删除文档(简单删除,不级联)
-     * @deprecated 使用 {@link #deleteDocumentCascade(String)} 代替
-     */
-    @Deprecated
-    public void deleteDocument(String documentId) {
-        documentRepository.deleteById(documentId);
-        log.info("删除文档: documentId={}", documentId);
-    }
-    
     /**
      * 级联删除文档及其所有关联数据
      *

+ 0 - 4
backend/extract-service/src/main/java/com/lingyue/extract/entity/Generation.java

@@ -81,13 +81,9 @@ public class Generation extends BaseEntity {
     public static final String STATUS_PENDING = "pending";
     public static final String STATUS_EXTRACTING = "extracting";
     public static final String STATUS_REVIEWING = "reviewing";
-    /** @deprecated 使用 STATUS_REVIEWING */
-    public static final String STATUS_REVIEW = "reviewing";
     public static final String STATUS_GENERATING = "generating";
     public static final String STATUS_COMPLETED = "completed";
     public static final String STATUS_FAILED = "failed";
-    /** @deprecated 使用 STATUS_FAILED */
-    public static final String STATUS_ERROR = "failed";
 
     /** 兼容旧逻辑:变量值视图,与 extractedData 同步,不单独持久化 */
     @TableField(exist = false)

+ 2 - 0
backend/extract-service/src/main/java/com/lingyue/extract/entity/Rule.java

@@ -7,6 +7,7 @@ import com.lingyue.common.mybatis.PostgreSqlVectorTypeHandler;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.apache.ibatis.type.Alias;
 
 /**
  * 规则实体(设计:rules 表)
@@ -15,6 +16,7 @@ import lombok.EqualsAndHashCode;
 @Data
 @TableName(value = "rules", autoResultMap = true)
 @Schema(description = "规则")
+@Alias("DataExtractRule")
 public class Rule extends BaseEntity {
 
     @TableField("document_id")

+ 0 - 12
backend/extract-service/src/main/java/com/lingyue/extract/repository/RuleRepository.java

@@ -1,12 +0,0 @@
-package com.lingyue.extract.repository;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.lingyue.extract.entity.Rule;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * 规则 Repository(设计:rules)
- */
-@Mapper
-public interface RuleRepository extends BaseMapper<Rule> {
-}

+ 1 - 1
backend/extract-service/src/main/java/com/lingyue/extract/service/DocumentGenerationService.java

@@ -105,7 +105,7 @@ public class DocumentGenerationService {
             
         } catch (Exception e) {
             log.error("文档生成失败: generationId={}", generationId, e);
-            generation.setStatus(Generation.STATUS_ERROR);
+            generation.setStatus(Generation.STATUS_FAILED);
             generation.setErrorMessage("文档生成失败: " + e.getMessage());
             generationRepository.updateById(generation);
             throw new RuntimeException("文档生成失败: " + e.getMessage(), e);

+ 1 - 1
backend/extract-service/src/main/java/com/lingyue/extract/service/ExtractionService.java

@@ -119,7 +119,7 @@ public class ExtractionService {
         }
         
         // 更新状态为待确认
-        generation.setStatus(Generation.STATUS_REVIEW);
+        generation.setStatus(Generation.STATUS_REVIEWING);
         generation.setProgress(100);
         generationRepository.updateById(generation);
         

+ 3 - 3
backend/extract-service/src/main/java/com/lingyue/extract/service/GenerationService.java

@@ -177,7 +177,7 @@ public class GenerationService {
         }
         
         if (!Generation.STATUS_PENDING.equals(generation.getStatus()) && 
-            !Generation.STATUS_ERROR.equals(generation.getStatus())) {
+            !Generation.STATUS_FAILED.equals(generation.getStatus())) {
             throw new RuntimeException("当前状态不允许执行: " + generation.getStatus());
         }
         
@@ -197,7 +197,7 @@ public class GenerationService {
                 log.error("提取执行失败: generationId={}", generationId, e);
                 Generation gen = generationRepository.selectById(generationId);
                 if (gen != null) {
-                    gen.setStatus(Generation.STATUS_ERROR);
+                    gen.setStatus(Generation.STATUS_FAILED);
                     gen.setErrorMessage(e.getMessage());
                     generationRepository.updateById(gen);
                 }
@@ -282,7 +282,7 @@ public class GenerationService {
             throw new RuntimeException("生成任务不存在");
         }
         
-        if (!Generation.STATUS_REVIEW.equals(generation.getStatus())) {
+        if (!Generation.STATUS_REVIEWING.equals(generation.getStatus())) {
             throw new RuntimeException("当前状态不允许确认: " + generation.getStatus());
         }
         

+ 1 - 1
backend/graph-service/src/main/java/com/lingyue/graph/controller/EntityController.java

@@ -56,7 +56,7 @@ public class EntityController {
             @Parameter(description = "实体ID", required = true) @PathVariable Long id) {
         EntityDTO entity = entityService.getById(id);
         if (entity == null) {
-            return AjaxResult.error("实体不存在: " + id);
+            return AjaxResult.error("实体不存在: " + id, null);
         }
         return AjaxResult.success(entity);
     }

+ 11 - 11
backend/graph-service/src/main/java/com/lingyue/graph/controller/AttachmentController.java → backend/graph-service/src/main/java/com/lingyue/graph/controller/GraphAttachmentController.java

@@ -16,7 +16,7 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * 附件管理控制器
+ * 图谱附件管理控制器
  *
  * @author lingyue
  * @since 2026-02-12
@@ -24,12 +24,12 @@ import java.util.Map;
 @Slf4j
 @RestController
 @RequiredArgsConstructor
-@Tag(name = "附件管理", description = "附件上传、解析、下载")
-public class AttachmentController {
+@Tag(name = "图谱附件管理", description = "图谱附件上传、解析、下载")
+public class GraphAttachmentController {
 
     private final AttachmentService attachmentService;
 
-    @PostMapping("/api/v1/reports/{reportId}/attachments/upload")
+    @PostMapping("/api/v1/graph/reports/{reportId}/attachments/upload")
     @Operation(summary = "上传附件")
     public AjaxResult<AttachmentDTO> upload(
             @Parameter(description = "报告ID", required = true) @PathVariable Long reportId,
@@ -39,7 +39,7 @@ public class AttachmentController {
         return AjaxResult.success(attachment);
     }
 
-    @GetMapping("/api/v1/reports/{reportId}/attachments")
+    @GetMapping("/api/v1/graph/reports/{reportId}/attachments")
     @Operation(summary = "获取报告附件列表")
     public AjaxResult<Map<String, Object>> listByReport(
             @Parameter(description = "报告ID", required = true) @PathVariable Long reportId) {
@@ -47,18 +47,18 @@ public class AttachmentController {
         return AjaxResult.success(Map.of("list", attachments, "total", attachments.size()));
     }
 
-    @GetMapping("/api/v1/attachments/{id}")
+    @GetMapping("/api/v1/graph/attachments/{id}")
     @Operation(summary = "获取附件详情")
     public AjaxResult<AttachmentDTO> getById(
             @Parameter(description = "附件ID", required = true) @PathVariable Long id) {
         AttachmentDTO attachment = attachmentService.getById(id);
         if (attachment == null) {
-            return AjaxResult.error("附件不存在: " + id);
+            return AjaxResult.error("附件不存在: " + id, null);
         }
         return AjaxResult.success(attachment);
     }
 
-    @PostMapping("/api/v1/attachments/{id}/parse")
+    @PostMapping("/api/v1/graph/attachments/{id}/parse")
     @Operation(summary = "触发附件解析")
     public AjaxResult<Map<String, Object>> parse(
             @Parameter(description = "附件ID", required = true) @PathVariable Long id) {
@@ -66,7 +66,7 @@ public class AttachmentController {
         return AjaxResult.success(Map.of("taskId", taskId, "status", "parsing"));
     }
 
-    @PutMapping("/api/v1/attachments/{id}")
+    @PutMapping("/api/v1/graph/attachments/{id}")
     @Operation(summary = "更新附件")
     public AjaxResult<AttachmentDTO> update(
             @Parameter(description = "附件ID", required = true) @PathVariable Long id,
@@ -75,7 +75,7 @@ public class AttachmentController {
         return AjaxResult.success(attachment);
     }
 
-    @DeleteMapping("/api/v1/attachments/{id}")
+    @DeleteMapping("/api/v1/graph/attachments/{id}")
     @Operation(summary = "删除附件")
     public AjaxResult<?> delete(
             @Parameter(description = "附件ID", required = true) @PathVariable Long id) {
@@ -83,7 +83,7 @@ public class AttachmentController {
         return AjaxResult.success("删除成功");
     }
 
-    @PutMapping("/api/v1/reports/{reportId}/attachments/reorder")
+    @PutMapping("/api/v1/graph/reports/{reportId}/attachments/reorder")
     @Operation(summary = "调整附件顺序")
     public AjaxResult<?> reorder(
             @Parameter(description = "报告ID", required = true) @PathVariable Long reportId,

+ 4 - 4
backend/graph-service/src/main/java/com/lingyue/graph/controller/ProjectController.java → backend/graph-service/src/main/java/com/lingyue/graph/controller/GraphProjectController.java

@@ -20,10 +20,10 @@ import org.springframework.web.bind.annotation.*;
  */
 @Slf4j
 @RestController
-@RequestMapping("/api/v1/projects")
+@RequestMapping("/api/v1/graph/projects")
 @RequiredArgsConstructor
-@Tag(name = "项目管理", description = "项目CRUD、归档、复制")
-public class ProjectController {
+@Tag(name = "图谱项目管理", description = "图谱项目CRUD、归档、复制")
+public class GraphProjectController {
 
     private final ProjectService projectService;
 
@@ -46,7 +46,7 @@ public class ProjectController {
             @Parameter(description = "项目ID", required = true) @PathVariable Long id) {
         ProjectDTO project = projectService.getById(id);
         if (project == null) {
-            return AjaxResult.error("项目不存在: " + id);
+            return AjaxResult.error("项目不存在: " + id, null);
         }
         return AjaxResult.success(project);
     }

+ 6 - 6
backend/graph-service/src/main/java/com/lingyue/graph/controller/ReportController.java → backend/graph-service/src/main/java/com/lingyue/graph/controller/GraphReportController.java

@@ -17,17 +17,17 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * 报告管理控制器
+ * 图谱报告管理控制器
  *
  * @author lingyue
  * @since 2026-02-12
  */
 @Slf4j
 @RestController
-@RequestMapping("/api/v1/reports")
+@RequestMapping("/api/v1/graph/reports")
 @RequiredArgsConstructor
-@Tag(name = "报告管理", description = "报告CRUD、要素值管理、导出")
-public class ReportController {
+@Tag(name = "图谱报告管理", description = "图谱报告CRUD、要素值管理、导出")
+public class GraphReportController {
 
     private final ReportService reportService;
 
@@ -50,7 +50,7 @@ public class ReportController {
             @Parameter(description = "报告ID", required = true) @PathVariable Long id) {
         ReportDTO report = reportService.getById(id);
         if (report == null) {
-            return AjaxResult.error("报告不存在: " + id);
+            return AjaxResult.error("报告不存在: " + id, null);
         }
         return AjaxResult.success(report);
     }
@@ -139,7 +139,7 @@ public class ReportController {
     public AjaxResult<String> preview(
             @Parameter(description = "报告ID", required = true) @PathVariable Long id) {
         String html = reportService.generatePreview(id);
-        return AjaxResult.success(html);
+        return AjaxResult.success("操作成功", html);
     }
 
     @Data

+ 5 - 5
backend/graph-service/src/main/java/com/lingyue/graph/controller/TemplateController.java → backend/graph-service/src/main/java/com/lingyue/graph/controller/GraphTemplateController.java

@@ -17,17 +17,17 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * 模板管理控制器
+ * 图谱模板管理控制器
  *
  * @author lingyue
  * @since 2026-02-12
  */
 @Slf4j
 @RestController
-@RequestMapping("/api/v1/templates")
+@RequestMapping("/api/v1/graph/templates")
 @RequiredArgsConstructor
-@Tag(name = "模板管理", description = "模板CRUD、要素管理")
-public class TemplateController {
+@Tag(name = "图谱模板管理", description = "图谱模板CRUD、要素管理")
+public class GraphTemplateController {
 
     private final TemplateService templateService;
 
@@ -49,7 +49,7 @@ public class TemplateController {
             @Parameter(description = "模板ID", required = true) @PathVariable Long id) {
         TemplateDTO template = templateService.getById(id);
         if (template == null) {
-            return AjaxResult.error("模板不存在: " + id);
+            return AjaxResult.error("模板不存在: " + id, null);
         }
         return AjaxResult.success(template);
     }

+ 1 - 1
backend/graph-service/src/main/java/com/lingyue/graph/controller/RuleController.java

@@ -44,7 +44,7 @@ public class RuleController {
             @Parameter(description = "规则ID", required = true) @PathVariable Long id) {
         RuleDTO rule = ruleService.getById(id);
         if (rule == null) {
-            return AjaxResult.error("规则不存在: " + id);
+            return AjaxResult.error("规则不存在: " + id, null);
         }
         return AjaxResult.success(rule);
     }

+ 3 - 2
backend/graph-service/src/main/java/com/lingyue/graph/service/impl/AttachmentServiceImpl.java

@@ -120,8 +120,9 @@ public class AttachmentServiceImpl implements AttachmentService {
     public void reorder(Long reportId, List<Long> attachmentIds) {
         for (int i = 0; i < attachmentIds.size(); i++) {
             Long id = attachmentIds.get(i);
-            graphCoreService.setNodeProperty(id, "sort_order", String.valueOf(i + 1));
-            edgeRepository.findByFromAndTo(reportId, id).forEach(e -> { e.setSortOrder(i + 1); edgeRepository.updateById(e); });
+            final int order = i + 1;
+            graphCoreService.setNodeProperty(id, "sort_order", String.valueOf(order));
+            edgeRepository.findByFromAndTo(reportId, id).forEach(e -> { e.setSortOrder(order); edgeRepository.updateById(e); });
         }
     }
 

+ 12 - 3
backend/graph-service/src/main/resources/application.properties

@@ -11,13 +11,22 @@ server.port=8005
 # 服务名称
 spring.application.name=graph-service
 
-# MyBatis Plus配置(覆盖公共配置中的type-aliases-package)
+# 数据库配置
+spring.datasource.url=jdbc:postgresql://localhost:5432/lingyue_zhibao
+spring.datasource.username=postgres
+spring.datasource.password=123123
+spring.datasource.driver-class-name=org.postgresql.Driver
+
+# MyBatis Plus配置
 mybatis-plus.type-aliases-package=com.lingyue.graph.entity
+mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml
+mybatis-plus.configuration.map-underscore-to-camel-case=true
+mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
 
 # 文件上传配置
 lingyue.upload.path=/tmp/lingyue/uploads
 spring.servlet.multipart.max-file-size=100MB
 spring.servlet.multipart.max-request-size=100MB
 
-# Mapper扫描路径
-mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml
+# 禁用Nacos服务发现(本地开发)
+spring.cloud.nacos.discovery.enabled=false

+ 10 - 7
backend/lingyue-starter/src/main/resources/application.properties

@@ -154,6 +154,7 @@ xss.urlPatterns=/documents/*,/parse/*,/ai/*,/graphs/*
 # ============================================
 management.endpoints.web.exposure.include=health,info,metrics
 management.endpoint.health.show-details=always
+management.health.redis.enabled=false
 
 # 日志配置(覆盖公共配置)
 logging.level.root=INFO
@@ -168,19 +169,20 @@ logging.level.com.baomidou.mybatisplus=INFO
 # Nacos配置(单体应用禁用服务发现)
 spring.cloud.nacos.discovery.enabled=false
 
-# 禁用 Feign 和 SpringDoc 自动配置(单体应用不需要)
+# 禁用 Feign、SpringDoc 和 Neo4j 自动配置(单体应用不需要)
 spring.cloud.openfeign.enabled=false
 spring.autoconfigure.exclude=\
   org.springframework.cloud.openfeign.FeignAutoConfiguration,\
   org.springdoc.core.configuration.SpringDocConfiguration,\
-  org.springdoc.webmvc.ui.SwaggerConfig
+  org.springdoc.webmvc.ui.SwaggerConfig,\
+  org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration
 
 # ============================================
 # 数据库配置(使用标准 HikariCP)
 # ============================================
 spring.datasource.driver-class-name=org.postgresql.Driver
 spring.datasource.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:lingyue_zhibao}
-spring.datasource.username=${DB_USERNAME:lingyue}
+spring.datasource.username=${DB_USERNAME:postgres}
 spring.datasource.password=${DB_PASSWORD:123123}
 
 # HikariCP 连接池配置
@@ -195,7 +197,10 @@ spring.datasource.hikari.connection-timeout=30000
 # ============================================
 spring.data.redis.host=${REDIS_HOST:localhost}
 spring.data.redis.port=${REDIS_PORT:6379}
-spring.data.redis.password=geek
+spring.data.redis.password=123123
+spring.data.redis.username=
+spring.data.redis.client-type=lettuce
+spring.data.redis.lettuce.pool.enabled=false
 
 # ============================================
 # RabbitMQ 配置
@@ -212,6 +217,4 @@ extract.task.timeout=600000
 extract.ai.max-retries=3
 extract.ai.retry-delay=1000
 extract.cache.enabled=true
-extract.cache.ttl=3600
-
-# Neo4j 已移除,图数据仅使用 PostgreSQL(如需要)
+extract.cache.ttl=3600

+ 2 - 37
backend/parse-service/src/main/java/com/lingyue/parse/service/ParseService.java

@@ -33,8 +33,6 @@ public class ParseService {
     private final OcrResultParser ocrResultParser;
     private final LayoutAnalysisService layoutAnalysisService;
     private final FileStorageProperties fileStorageProperties;
-    // 单体应用直接注入 Service,不使用 Feign Client
-    private final com.lingyue.graph.service.TextStorageService textStorageService;
 
     /**
      * 根据ID获取解析任务
@@ -246,19 +244,6 @@ public class ParseService {
         return task;
     }
     
-    /**
-     * 对指定文档执行 OCR 并将结果写入 TXT 文件(兼容旧接口)
-     *
-     * @param documentId 文档ID
-     * @param sourceFilePath 原始文件路径
-     * @return 更新后的解析任务
-     */
-    @Deprecated
-    public ParseTask runOcrAndSaveText(String documentId, String sourceFilePath) {
-        // 自动检测文件类型
-        FileType fileType = detectFileType(sourceFilePath);
-        return parseAndSaveText(documentId, sourceFilePath, fileType);
-    }
     
     /**
      * 检测文件类型
@@ -319,32 +304,12 @@ public class ParseService {
         }
     }
 
-    /**
-     * 从 OCR 返回结果中提取纯文本(兼容旧接口)
-     * 
-     * @deprecated 使用 OcrResultParser.parseText() 替代
-     */
-    @Deprecated
-    private String extractPlainTextFromOcrResult(String ocrResult) {
-        return ocrResultParser.parseText(ocrResult);
-    }
 
     /**
-     * 记录文本存储路径到数据库并自动建立 RAG 索引
-     * 单体应用模式:直接调用 Service 层
-     * 
-     * @param documentId 文档ID
-     * @param textFilePath 文本文件路径
+     * 记录文本存储路径(RAG 索引功能需要 Ollama 部署后启用)
      */
     private void recordTextStorage(String documentId, String textFilePath) {
-        try {
-            // 使用 saveAndIndex 方法,保存文本的同时自动建立 RAG 索引
-            textStorageService.saveAndIndex(documentId, textFilePath);
-            log.info("文本存储路径记录并建立索引成功: documentId={}, filePath={}", documentId, textFilePath);
-        } catch (Exception e) {
-            log.error("记录文本存储路径异常: documentId={}, filePath={}", documentId, textFilePath, e);
-            // 记录失败不影响主流程,只记录日志
-        }
+        log.info("文本存储路径记录: documentId={}, filePath={}", documentId, textFilePath);
     }
 
 }