Quellcode durchsuchen

fix: 修复数据库表类型不兼容问题,添加完整重建脚本

- 修复 supplement_tables.sql 中 UUID 与 VARCHAR(36) 类型不匹配问题
- 统一使用 create_time/update_time 字段名,兼容 SimpleModel 基类
- 移除重复的 graph_nodes/graph_relations 表定义(已在 graph_tables.sql 中)
- 新增 rebuild_all.sh 完整数据库重建脚本,支持一键删除重建所有表
何文松 vor 1 Monat
Ursprung
Commit
9cd48d5200
2 geänderte Dateien mit 356 neuen und 83 gelöschten Zeilen
  1. 292 0
      backend/sql/rebuild_all.sh
  2. 64 83
      backend/sql/supplement_tables.sql

+ 292 - 0
backend/sql/rebuild_all.sh

@@ -0,0 +1,292 @@
+#!/bin/bash
+
+# ============================================
+# 数据库完整重建脚本
+# 删除所有表并重新初始化(包含所有迁移)
+# ============================================
+
+set -e
+
+# 颜色定义
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m'
+
+# 数据库配置(根据实际情况修改)
+DB_HOST=${DB_HOST:-localhost}
+DB_PORT=${DB_PORT:-5432}
+DB_NAME=${DB_NAME:-lingyue_zhibao}
+DB_USER=${DB_USER:-postgres}
+DB_PASSWORD=${DB_PASSWORD:-postgres}
+
+log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
+log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
+log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
+log_title() { echo -e "\n${BLUE}========== $1 ==========${NC}\n"; }
+
+# 获取脚本所在目录
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+SQL_DIR="${SCRIPT_DIR}"
+MIGRATIONS_DIR="${PROJECT_ROOT}/database/migrations"
+
+# 删除所有表
+drop_all_tables() {
+    log_title "删除所有表"
+    
+    if [ "$FORCE" != "yes" ]; then
+        log_warn "这将删除数据库 ${DB_NAME} 中的所有表和数据!"
+        read -p "是否继续?(yes/no): " confirm
+        
+        if [ "$confirm" != "yes" ]; then
+            log_info "操作已取消"
+            exit 0
+        fi
+    fi
+    
+    log_info "开始删除表..."
+    
+    PGPASSWORD=${DB_PASSWORD} psql -h ${DB_HOST} -p ${DB_PORT} -U ${DB_USER} -d ${DB_NAME} <<EOF
+-- 禁用外键约束检查
+SET session_replication_role = 'replica';
+
+-- 删除所有表
+DO \$\$
+DECLARE
+    r RECORD;
+BEGIN
+    FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public')
+    LOOP
+        EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(r.tablename) || ' CASCADE';
+        RAISE NOTICE 'Dropped table: %', r.tablename;
+    END LOOP;
+END
+\$\$;
+
+-- 删除所有序列
+DO \$\$
+DECLARE
+    r RECORD;
+BEGIN
+    FOR r IN (SELECT sequencename FROM pg_sequences WHERE schemaname = 'public')
+    LOOP
+        EXECUTE 'DROP SEQUENCE IF EXISTS public.' || quote_ident(r.sequencename) || ' CASCADE';
+    END LOOP;
+END
+\$\$;
+
+-- 删除所有函数
+DO \$\$
+DECLARE
+    r RECORD;
+BEGIN
+    FOR r IN (SELECT proname, oidvectortypes(proargtypes) as args 
+              FROM pg_proc 
+              INNER JOIN pg_namespace ns ON (pg_proc.pronamespace = ns.oid)
+              WHERE ns.nspname = 'public')
+    LOOP
+        EXECUTE 'DROP FUNCTION IF EXISTS public.' || quote_ident(r.proname) || '(' || r.args || ') CASCADE';
+    END LOOP;
+END
+\$\$;
+
+-- 恢复外键约束检查
+SET session_replication_role = 'origin';
+
+SELECT 'Tables remaining:' as status, COUNT(*) as count FROM pg_tables WHERE schemaname = 'public';
+EOF
+    
+    log_info "所有表已删除"
+}
+
+# 执行 SQL 文件
+execute_sql() {
+    local file=$1
+    local desc=$2
+    
+    if [ -f "$file" ]; then
+        log_info "执行: ${desc} (${file})"
+        PGPASSWORD=${DB_PASSWORD} psql -h ${DB_HOST} -p ${DB_PORT} -U ${DB_USER} -d ${DB_NAME} -f "$file" 2>&1 | grep -v "^NOTICE:" || true
+        log_info "${desc} 完成"
+    else
+        log_warn "文件不存在: ${file}"
+    fi
+}
+
+# 初始化所有表
+init_all_tables() {
+    log_title "初始化数据库表"
+    
+    # 1. 基础表(users, documents 等核心表)
+    execute_sql "${SQL_DIR}/init.sql" "基础表 (users, documents, elements, etc.)"
+    
+    # 2. 图谱表(graph_nodes, graph_relations - 先于 supplement_tables)
+    execute_sql "${SQL_DIR}/graph_tables.sql" "图谱表 (graph_nodes, graph_relations)"
+    
+    # 3. 补充表(rules, data_sources, templates, text_storage 等)
+    execute_sql "${SQL_DIR}/supplement_tables.sql" "补充表 (rules, data_sources, templates, text_storage)"
+    
+    # 4. RAG 表(text_chunks, vector_embeddings - 可能需要 pgvector)
+    execute_sql "${SQL_DIR}/rag_tables_compatible.sql" "RAG 表 (text_chunks, vector_embeddings)"
+    
+    # 5. 执行迁移文件(按文件名排序)
+    log_title "执行迁移脚本"
+    if [ -d "$MIGRATIONS_DIR" ]; then
+        for migration in $(ls -1 "${MIGRATIONS_DIR}"/*.sql 2>/dev/null | sort); do
+            if [ -f "$migration" ]; then
+                execute_sql "$migration" "迁移: $(basename $migration)"
+            fi
+        done
+    else
+        log_warn "迁移目录不存在: ${MIGRATIONS_DIR}"
+    fi
+}
+
+# 验证表创建
+verify_tables() {
+    log_title "验证表创建"
+    
+    PGPASSWORD=${DB_PASSWORD} psql -h ${DB_HOST} -p ${DB_PORT} -U ${DB_USER} -d ${DB_NAME} <<EOF
+SELECT tablename as "表名"
+FROM pg_tables 
+WHERE schemaname = 'public'
+ORDER BY tablename;
+
+SELECT COUNT(*) as "表总数" FROM pg_tables WHERE schemaname = 'public';
+EOF
+}
+
+# 创建测试用户
+create_test_user() {
+    log_title "创建测试用户"
+    
+    PGPASSWORD=${DB_PASSWORD} psql -h ${DB_HOST} -p ${DB_PORT} -U ${DB_USER} -d ${DB_NAME} <<EOF
+-- 插入测试管理员用户(密码: Admin123!)
+INSERT INTO users (id, username, email, password_hash, role, create_time, update_time)
+VALUES (
+    '00000000000000000000000000000001',
+    'admin',
+    'admin@lingyue.com',
+    '\$2a\$10\$N.zmdr9k7uOCQb376NoUnuTJ8iUtkWBrq.VZbISbAi1L9sNPIiMMi',
+    'admin',
+    CURRENT_TIMESTAMP,
+    CURRENT_TIMESTAMP
+)
+ON CONFLICT (id) DO NOTHING;
+
+-- 插入测试普通用户(密码: User1234!)
+INSERT INTO users (id, username, email, password_hash, role, create_time, update_time)
+VALUES (
+    '00000000000000000000000000000002',
+    'testuser',
+    'test@lingyue.com',
+    '\$2a\$10\$5XvCJaLqDXJz.YqL8TnNKegHH7q6KM7YFzMVxLPNMvJx5f7mxnKQi',
+    'user',
+    CURRENT_TIMESTAMP,
+    CURRENT_TIMESTAMP
+)
+ON CONFLICT (id) DO NOTHING;
+
+SELECT id, username, email, role FROM users;
+EOF
+    
+    log_info "测试用户创建完成"
+    log_info "  admin / Admin123!"
+    log_info "  testuser / User1234!"
+}
+
+# 显示帮助
+show_help() {
+    cat <<EOF
+数据库完整重建脚本
+
+用法: ./rebuild_all.sh [选项]
+
+选项:
+    -h, --help      显示帮助
+    -f, --force     跳过确认提示
+    --no-test-user  不创建测试用户
+
+环境变量:
+    DB_HOST         数据库主机 (默认: localhost)
+    DB_PORT         数据库端口 (默认: 5432)
+    DB_NAME         数据库名称 (默认: lingyue_zhibao)
+    DB_USER         数据库用户 (默认: postgres)
+    DB_PASSWORD     数据库密码 (默认: postgres)
+
+示例:
+    # 使用默认配置
+    ./rebuild_all.sh
+
+    # 指定数据库配置
+    DB_PASSWORD=mypassword ./rebuild_all.sh
+
+    # 强制执行(跳过确认)
+    FORCE=yes ./rebuild_all.sh
+
+    # 服务器上执行
+    DB_USER=lingyue DB_PASSWORD=123123 ./rebuild_all.sh
+
+执行顺序:
+    1. 删除所有表、序列、函数
+    2. 执行 init.sql(用户、文档、元素等基础表)
+    3. 执行 supplement_tables.sql(补充表)
+    4. 执行 graph_tables.sql(图谱表)
+    5. 执行 rag_tables_compatible.sql(RAG表)
+    6. 执行 database/migrations/*.sql(迁移脚本)
+    7. 创建测试用户
+    8. 验证表创建
+
+EOF
+}
+
+# 主函数
+main() {
+    CREATE_TEST_USER=true
+    
+    while [[ $# -gt 0 ]]; do
+        case $1 in
+            -h|--help)
+                show_help
+                exit 0
+                ;;
+            -f|--force)
+                FORCE=yes
+                shift
+                ;;
+            --no-test-user)
+                CREATE_TEST_USER=false
+                shift
+                ;;
+            *)
+                log_error "未知选项: $1"
+                show_help
+                exit 1
+                ;;
+        esac
+    done
+    
+    log_title "数据库完整重建脚本"
+    echo "数据库: ${DB_NAME}"
+    echo "主机: ${DB_HOST}:${DB_PORT}"
+    echo "用户: ${DB_USER}"
+    echo "SQL目录: ${SQL_DIR}"
+    echo "迁移目录: ${MIGRATIONS_DIR}"
+    echo ""
+    
+    drop_all_tables
+    init_all_tables
+    
+    if [ "$CREATE_TEST_USER" = true ]; then
+        create_test_user
+    fi
+    
+    verify_tables
+    
+    log_title "数据库重建完成"
+    log_info "所有表已创建并验证"
+}
+
+main "$@"

+ 64 - 83
backend/sql/supplement_tables.sql

@@ -1,67 +1,28 @@
 -- 灵越智报 v2.0 补充表结构
 -- PostgreSQL 15+
 -- 根据产品设计方案补充的表结构
+-- 注意: ID 使用 VARCHAR(36) 以兼容 MyBatis-Plus 的 ASSIGN_UUID 策略
+-- 注意: graph_nodes 和 graph_relations 表已在 graph_tables.sql 中定义,此处不再重复
 
 -- ============================================
--- 1. 图节点表(graph_nodes)
--- ============================================
-CREATE TABLE IF NOT EXISTS graph_nodes (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
-    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
-    name VARCHAR(255) NOT NULL,
-    type VARCHAR(50) NOT NULL, -- text/table/image/number/date/company/person等
-    value TEXT,
-    position JSONB, -- {file_id, page, line, char_start, char_end}
-    parent_id UUID REFERENCES graph_nodes(id) ON DELETE SET NULL,
-    level INTEGER DEFAULT 0, -- 层级
-    metadata JSONB DEFAULT '{}',
-    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
-);
-
-CREATE INDEX IF NOT EXISTS idx_graph_nodes_document_id ON graph_nodes(document_id);
-CREATE INDEX IF NOT EXISTS idx_graph_nodes_user_id ON graph_nodes(user_id);
-CREATE INDEX IF NOT EXISTS idx_graph_nodes_type ON graph_nodes(type);
-CREATE INDEX IF NOT EXISTS idx_graph_nodes_parent_id ON graph_nodes(parent_id);
-CREATE INDEX IF NOT EXISTS idx_graph_nodes_position ON graph_nodes USING GIN(position);
-
--- ============================================
--- 2. 图关系表(graph_relations)
--- ============================================
-CREATE TABLE IF NOT EXISTS graph_relations (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    from_node_id UUID NOT NULL REFERENCES graph_nodes(id) ON DELETE CASCADE,
-    to_node_id UUID NOT NULL REFERENCES graph_nodes(id) ON DELETE CASCADE,
-    relation_type VARCHAR(50) NOT NULL, -- DEP/ADD/SUB/MUL/DIV/UNION/INTERSECT/AI等
-    action_type VARCHAR(50), -- 动作类型
-    action_config JSONB, -- 动作配置(如AI模型ID)
-    order_index INTEGER DEFAULT 0, -- 顺序
-    condition_expr TEXT, -- 条件表达式
-    metadata JSONB DEFAULT '{}',
-    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
-);
-
-CREATE INDEX IF NOT EXISTS idx_graph_relations_from_node ON graph_relations(from_node_id);
-CREATE INDEX IF NOT EXISTS idx_graph_relations_to_node ON graph_relations(to_node_id);
-CREATE INDEX IF NOT EXISTS idx_graph_relations_type ON graph_relations(relation_type);
-
--- ============================================
--- 3. 规则表(rules)
+-- 1. 规则表(rules)
 -- ============================================
 CREATE TABLE IF NOT EXISTS rules (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    id VARCHAR(36) PRIMARY KEY,
+    user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
     name VARCHAR(255) NOT NULL,
     description TEXT,
-    entry_node_id UUID NOT NULL REFERENCES graph_nodes(id) ON DELETE CASCADE,
-    exit_node_id UUID NOT NULL REFERENCES graph_nodes(id) ON DELETE CASCADE,
+    entry_node_id VARCHAR(36) REFERENCES graph_nodes(id) ON DELETE SET NULL,
+    exit_node_id VARCHAR(36) REFERENCES graph_nodes(id) ON DELETE SET NULL,
     rule_chain JSONB NOT NULL DEFAULT '[]', -- 规则链(节点ID序列)
     status VARCHAR(20) DEFAULT 'active', -- active/inactive
     metadata JSONB DEFAULT '{}',
-    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
 );
 
 CREATE INDEX IF NOT EXISTS idx_rules_user_id ON rules(user_id);
@@ -73,17 +34,21 @@ CREATE INDEX IF NOT EXISTS idx_rules_status ON rules(status);
 -- 4. 数据源表(data_sources)
 -- ============================================
 CREATE TABLE IF NOT EXISTS data_sources (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
-    document_id UUID REFERENCES documents(id) ON DELETE SET NULL,
+    id VARCHAR(36) PRIMARY KEY,
+    user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    document_id VARCHAR(36) REFERENCES documents(id) ON DELETE SET NULL,
     name VARCHAR(255) NOT NULL,
     type VARCHAR(50) NOT NULL, -- table/text/image
     source_type VARCHAR(50) NOT NULL, -- file/manual/rule_result
-    node_ids UUID[] DEFAULT '{}', -- 关联的节点ID数组
+    node_ids TEXT[] DEFAULT '{}', -- 关联的节点ID数组(VARCHAR数组)
     config JSONB DEFAULT '{}', -- 数据源配置
     metadata JSONB DEFAULT '{}',
-    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
 );
 
 CREATE INDEX IF NOT EXISTS idx_data_sources_user_id ON data_sources(user_id);
@@ -94,16 +59,20 @@ CREATE INDEX IF NOT EXISTS idx_data_sources_type ON data_sources(type);
 -- 5. 模板表(templates)
 -- ============================================
 CREATE TABLE IF NOT EXISTS templates (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+    id VARCHAR(36) PRIMARY KEY,
+    user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
     name VARCHAR(255) NOT NULL,
     content TEXT NOT NULL, -- 模板内容(带占位符)
     placeholder_mapping JSONB DEFAULT '{}', -- 占位符到数据源的映射
-    source_template_id UUID REFERENCES templates(id) ON DELETE SET NULL, -- 复制来源
+    source_template_id VARCHAR(36) REFERENCES templates(id) ON DELETE SET NULL, -- 复制来源
     status VARCHAR(20) DEFAULT 'active', -- active/inactive
     metadata JSONB DEFAULT '{}',
-    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
 );
 
 CREATE INDEX IF NOT EXISTS idx_templates_user_id ON templates(user_id);
@@ -114,13 +83,17 @@ CREATE INDEX IF NOT EXISTS idx_templates_status ON templates(status);
 -- 6. 文本存储路径表(text_storage)
 -- ============================================
 CREATE TABLE IF NOT EXISTS text_storage (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
     file_path VARCHAR(500) NOT NULL, -- TXT文件路径
     file_size BIGINT,
     checksum VARCHAR(64), -- 文件校验和
-    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
 );
 
 CREATE INDEX IF NOT EXISTS idx_text_storage_document_id ON text_storage(document_id);
@@ -133,15 +106,20 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_text_storage_document_unique ON text_stora
 -- CREATE EXTENSION IF NOT EXISTS vector;
 
 CREATE TABLE IF NOT EXISTS vector_embeddings (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
+    id VARCHAR(36) PRIMARY KEY,
+    document_id VARCHAR(36) NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
     chunk_text TEXT NOT NULL,
     chunk_index INTEGER NOT NULL, -- 分块索引
     embedding vector(768), -- 向量(需要pgvector扩展,如果未安装可先注释)
     file_name VARCHAR(255),
     file_hash VARCHAR(64), -- MD5哈希
     metadata JSONB DEFAULT '{}',
-    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
+    create_by VARCHAR(36),
+    create_by_name VARCHAR(100),
+    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    update_by VARCHAR(36),
+    update_by_name VARCHAR(100),
+    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
 );
 
 CREATE INDEX IF NOT EXISTS idx_vector_embeddings_document_id ON vector_embeddings(document_id);
@@ -152,23 +130,27 @@ CREATE INDEX IF NOT EXISTS idx_vector_embeddings_file_hash ON vector_embeddings(
 -- ============================================
 -- 创建更新时间触发器
 -- ============================================
-CREATE TRIGGER update_graph_nodes_updated_at BEFORE UPDATE ON graph_nodes
-    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+-- 注意: graph_nodes 和 graph_relations 的触发器已在 graph_tables.sql 中定义
 
-CREATE TRIGGER update_graph_relations_updated_at BEFORE UPDATE ON graph_relations
-    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+DROP TRIGGER IF EXISTS update_rules_update_time ON rules;
+CREATE TRIGGER update_rules_update_time BEFORE UPDATE ON rules
+    FOR EACH ROW EXECUTE FUNCTION update_update_time_column();
 
-CREATE TRIGGER update_rules_updated_at BEFORE UPDATE ON rules
-    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+DROP TRIGGER IF EXISTS update_data_sources_update_time ON data_sources;
+CREATE TRIGGER update_data_sources_update_time BEFORE UPDATE ON data_sources
+    FOR EACH ROW EXECUTE FUNCTION update_update_time_column();
 
-CREATE TRIGGER update_data_sources_updated_at BEFORE UPDATE ON data_sources
-    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+DROP TRIGGER IF EXISTS update_templates_update_time ON templates;
+CREATE TRIGGER update_templates_update_time BEFORE UPDATE ON templates
+    FOR EACH ROW EXECUTE FUNCTION update_update_time_column();
 
-CREATE TRIGGER update_templates_updated_at BEFORE UPDATE ON templates
-    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+DROP TRIGGER IF EXISTS update_text_storage_update_time ON text_storage;
+CREATE TRIGGER update_text_storage_update_time BEFORE UPDATE ON text_storage
+    FOR EACH ROW EXECUTE FUNCTION update_update_time_column();
 
-CREATE TRIGGER update_text_storage_updated_at BEFORE UPDATE ON text_storage
-    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
+DROP TRIGGER IF EXISTS update_vector_embeddings_update_time ON vector_embeddings;
+CREATE TRIGGER update_vector_embeddings_update_time BEFORE UPDATE ON vector_embeddings
+    FOR EACH ROW EXECUTE FUNCTION update_update_time_column();
 
 -- ============================================
 -- 安装pgvector扩展(可选,用于向量检索)
@@ -180,4 +162,3 @@ CREATE TRIGGER update_text_storage_updated_at BEFORE UPDATE ON text_storage
 -- 1. 下载pgvector: https://github.com/pgvector/pgvector
 -- 2. 编译安装
 -- 3. 在数据库中执行: CREATE EXTENSION vector;
-