BEGIN; CREATE TABLE IF NOT EXISTS rag_dataset ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT NOT NULL DEFAULT '', area VARCHAR(50) NOT NULL DEFAULT '', is_public BOOLEAN NOT NULL DEFAULT FALSE, is_default BOOLEAN NOT NULL DEFAULT FALSE, collection_name VARCHAR(100) NOT NULL UNIQUE, embedding_model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-v4', embedding_dim INTEGER NOT NULL DEFAULT 1024, chunk_max_size INTEGER NOT NULL DEFAULT 800, chunk_min_size INTEGER NOT NULL DEFAULT 20, document_count INTEGER NOT NULL DEFAULT 0, total_chunks INTEGER NOT NULL DEFAULT 0, retrieval_model JSONB NOT NULL DEFAULT '{}'::jsonb, sort_order INTEGER NOT NULL DEFAULT 0, status SMALLINT NOT NULL DEFAULT 1, created_by BIGINT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_by BIGINT, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE TABLE IF NOT EXISTS rag_document ( id BIGSERIAL PRIMARY KEY, dataset_id BIGINT NOT NULL REFERENCES rag_dataset(id), filename VARCHAR(500) NOT NULL, original_name VARCHAR(500) NOT NULL, minio_path VARCHAR(1000) NOT NULL, file_type VARCHAR(20) NOT NULL, file_size BIGINT NOT NULL DEFAULT 0, chunk_count INTEGER NOT NULL DEFAULT 0, indexing_status VARCHAR(20) NOT NULL DEFAULT 'pending', indexing_error TEXT, indexing_started_at TIMESTAMPTZ, indexing_completed_at TIMESTAMPTZ, enabled BOOLEAN NOT NULL DEFAULT TRUE, hit_count INTEGER NOT NULL DEFAULT 0, created_by BIGINT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE TABLE IF NOT EXISTS rag_chat_app ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT NOT NULL DEFAULT '', area VARCHAR(50) NOT NULL DEFAULT '', dataset_id BIGINT REFERENCES rag_dataset(id), system_prompt TEXT NOT NULL DEFAULT '', llm_model VARCHAR(100) NOT NULL DEFAULT '', temperature REAL NOT NULL DEFAULT 0.3, max_tokens INTEGER NOT NULL DEFAULT 2048, opening_statement TEXT NOT NULL DEFAULT '', suggested_questions TEXT NOT NULL DEFAULT '[]', is_default BOOLEAN NOT NULL DEFAULT FALSE, sort_order INTEGER NOT NULL DEFAULT 0, status SMALLINT NOT NULL DEFAULT 1, created_by BIGINT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_by BIGINT, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE TABLE IF NOT EXISTS rag_conversation ( id BIGSERIAL PRIMARY KEY, conversation_id VARCHAR(100) NOT NULL UNIQUE, user_id BIGINT NOT NULL, app_id BIGINT REFERENCES rag_chat_app(id), name VARCHAR(500) NOT NULL DEFAULT '新对话', introduction TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE TABLE IF NOT EXISTS rag_message ( id BIGSERIAL PRIMARY KEY, message_id VARCHAR(100) NOT NULL UNIQUE, conversation_id VARCHAR(100) NOT NULL REFERENCES rag_conversation(conversation_id), role VARCHAR(20) NOT NULL, content TEXT NOT NULL DEFAULT '', sources JSONB NOT NULL DEFAULT '[]'::jsonb, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, feedback VARCHAR(20), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ALTER TABLE rag_dataset ADD COLUMN IF NOT EXISTS retrieval_model JSONB NOT NULL DEFAULT '{}'::jsonb, ADD COLUMN IF NOT EXISTS embedding_model VARCHAR(100) NOT NULL DEFAULT 'text-embedding-v4', ADD COLUMN IF NOT EXISTS embedding_dim INTEGER NOT NULL DEFAULT 1024, ADD COLUMN IF NOT EXISTS chunk_max_size INTEGER NOT NULL DEFAULT 800, ADD COLUMN IF NOT EXISTS chunk_min_size INTEGER NOT NULL DEFAULT 20, ADD COLUMN IF NOT EXISTS document_count INTEGER NOT NULL DEFAULT 0, ADD COLUMN IF NOT EXISTS total_chunks INTEGER NOT NULL DEFAULT 0, ADD COLUMN IF NOT EXISTS sort_order INTEGER NOT NULL DEFAULT 0, ADD COLUMN IF NOT EXISTS status SMALLINT NOT NULL DEFAULT 1, ADD COLUMN IF NOT EXISTS updated_by BIGINT, ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; ALTER TABLE rag_document ADD COLUMN IF NOT EXISTS enabled BOOLEAN NOT NULL DEFAULT TRUE, ADD COLUMN IF NOT EXISTS hit_count INTEGER NOT NULL DEFAULT 0, ADD COLUMN IF NOT EXISTS indexing_started_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS indexing_completed_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; ALTER TABLE rag_chat_app ADD COLUMN IF NOT EXISTS dataset_id BIGINT REFERENCES rag_dataset(id), ADD COLUMN IF NOT EXISTS system_prompt TEXT NOT NULL DEFAULT '', ADD COLUMN IF NOT EXISTS llm_model VARCHAR(100) NOT NULL DEFAULT '', ADD COLUMN IF NOT EXISTS temperature REAL NOT NULL DEFAULT 0.3, ADD COLUMN IF NOT EXISTS max_tokens INTEGER NOT NULL DEFAULT 2048, ADD COLUMN IF NOT EXISTS opening_statement TEXT NOT NULL DEFAULT '', ADD COLUMN IF NOT EXISTS suggested_questions TEXT NOT NULL DEFAULT '[]', ADD COLUMN IF NOT EXISTS is_default BOOLEAN NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS sort_order INTEGER NOT NULL DEFAULT 0, ADD COLUMN IF NOT EXISTS status SMALLINT NOT NULL DEFAULT 1, ADD COLUMN IF NOT EXISTS updated_by BIGINT, ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; ALTER TABLE rag_conversation ADD COLUMN IF NOT EXISTS app_id BIGINT REFERENCES rag_chat_app(id), ADD COLUMN IF NOT EXISTS introduction TEXT NOT NULL DEFAULT '', ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; ALTER TABLE rag_message ADD COLUMN IF NOT EXISTS sources JSONB NOT NULL DEFAULT '[]'::jsonb, ADD COLUMN IF NOT EXISTS metadata JSONB NOT NULL DEFAULT '{}'::jsonb, ADD COLUMN IF NOT EXISTS feedback VARCHAR(20), ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(); UPDATE rag_dataset SET retrieval_model = '{}'::jsonb WHERE retrieval_model IS NULL; UPDATE rag_document SET enabled = TRUE WHERE enabled IS NULL; UPDATE rag_document SET hit_count = 0 WHERE hit_count IS NULL; UPDATE rag_chat_app SET suggested_questions = '[]' WHERE suggested_questions IS NULL; UPDATE rag_message SET sources = '[]'::jsonb WHERE sources IS NULL; UPDATE rag_message SET metadata = '{}'::jsonb WHERE metadata IS NULL; CREATE UNIQUE INDEX IF NOT EXISTS uq_rag_dataset_collection_name ON rag_dataset(collection_name); CREATE INDEX IF NOT EXISTS idx_rag_dataset_area ON rag_dataset(area) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_dataset_status ON rag_dataset(status) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_document_dataset ON rag_document(dataset_id) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_document_status ON rag_document(indexing_status) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_document_original_name ON rag_document(dataset_id, original_name) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_chat_app_area ON rag_chat_app(area) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_chat_app_status ON rag_chat_app(status) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_conversation_user ON rag_conversation(user_id) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_conversation_app ON rag_conversation(app_id) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_rag_message_conversation ON rag_message(conversation_id, created_at); CREATE OR REPLACE FUNCTION update_rag_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DO $$ DECLARE t TEXT; BEGIN FOREACH t IN ARRAY ARRAY['rag_dataset', 'rag_document', 'rag_chat_app', 'rag_conversation'] LOOP EXECUTE format('DROP TRIGGER IF EXISTS trg_%s_updated_at ON %I', t, t); EXECUTE format( 'CREATE TRIGGER trg_%s_updated_at BEFORE UPDATE ON %I FOR EACH ROW EXECUTE FUNCTION update_rag_updated_at()', t, t ); END LOOP; END; $$; COMMIT;