feat: add database migrations for mid-platform tables [AC-IDMP-SHARE, AC-MARH-01~12]
- Add mid-platform tables migration (005_create_mid_tables.sql) - Add shared_sessions table migration (006_create_shared_sessions.sql) - Add migration helper scripts
This commit is contained in:
parent
9e77923d3a
commit
c7c94e8cd9
|
|
@ -0,0 +1,67 @@
|
|||
-- [AC-IDMP-05/07/09/20] 中台改造数据库迁移
|
||||
-- 创建高风险策略表、会话模式记录表、审计日志表
|
||||
|
||||
-- 高风险策略配置表
|
||||
CREATE TABLE IF NOT EXISTS high_risk_policies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id VARCHAR(255) NOT NULL,
|
||||
scenario VARCHAR(64) NOT NULL,
|
||||
handler_mode VARCHAR(32) NOT NULL DEFAULT 'micro_flow',
|
||||
flow_id UUID REFERENCES script_flows(id),
|
||||
transfer_message TEXT,
|
||||
keywords JSONB,
|
||||
patterns JSONB,
|
||||
priority INTEGER DEFAULT 0,
|
||||
is_enabled BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_high_risk_policies_tenant ON high_risk_policies(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_high_risk_policies_tenant_enabled ON high_risk_policies(tenant_id, is_enabled);
|
||||
|
||||
-- 会话模式记录表
|
||||
CREATE TABLE IF NOT EXISTS session_mode_records (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id VARCHAR(255) NOT NULL,
|
||||
session_id VARCHAR(255) NOT NULL,
|
||||
mode VARCHAR(32) NOT NULL DEFAULT 'BOT_ACTIVE',
|
||||
reason TEXT,
|
||||
switched_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
CONSTRAINT ix_session_mode_records_tenant_session UNIQUE (tenant_id, session_id)
|
||||
);
|
||||
|
||||
-- 中台审计日志表
|
||||
CREATE TABLE IF NOT EXISTS mid_audit_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id VARCHAR(255) NOT NULL,
|
||||
session_id VARCHAR(255) NOT NULL,
|
||||
request_id VARCHAR(255) NOT NULL,
|
||||
generation_id VARCHAR(255) NOT NULL,
|
||||
mode VARCHAR(32) NOT NULL,
|
||||
intent VARCHAR(255),
|
||||
tool_calls JSONB,
|
||||
guardrail_triggered BOOLEAN DEFAULT FALSE,
|
||||
fallback_reason_code VARCHAR(64),
|
||||
react_iterations INTEGER,
|
||||
high_risk_scenario VARCHAR(64),
|
||||
latency_ms INTEGER,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_mid_audit_logs_tenant_session ON mid_audit_logs(tenant_id, session_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_mid_audit_logs_tenant_request ON mid_audit_logs(tenant_id, request_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_mid_audit_logs_tenant_generation ON mid_audit_logs(tenant_id, generation_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_mid_audit_logs_created ON mid_audit_logs(created_at);
|
||||
|
||||
-- 插入默认高风险策略(空集保护)
|
||||
-- 注意:这里只插入示例,实际由租户配置
|
||||
INSERT INTO high_risk_policies (tenant_id, scenario, handler_mode, keywords, priority, is_enabled)
|
||||
VALUES
|
||||
('default', 'refund', 'micro_flow', '["退款", "退货", "退钱", "退费"]'::jsonb, 100, TRUE),
|
||||
('default', 'complaint_escalation', 'micro_flow', '["投诉", "举报", "差评", "曝光"]'::jsonb, 100, TRUE),
|
||||
('default', 'privacy_sensitive_promise', 'micro_flow', '["承诺", "保证", "隐私", "个人信息"]'::jsonb, 100, TRUE),
|
||||
('default', 'transfer', 'transfer', '["人工", "转人工", "人工客服", "真人"]'::jsonb, 100, TRUE)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
-- [AC-IDMP-SHARE] 对话分享功能数据库迁移
|
||||
-- 创建 shared_sessions 表,支持通过链接分享对话
|
||||
|
||||
-- 分享会话表
|
||||
CREATE TABLE IF NOT EXISTS shared_sessions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
share_token VARCHAR(255) NOT NULL UNIQUE,
|
||||
session_id VARCHAR(255) NOT NULL,
|
||||
tenant_id VARCHAR(255) NOT NULL,
|
||||
title VARCHAR(255),
|
||||
description TEXT,
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
max_concurrent_users INTEGER DEFAULT 10,
|
||||
current_users INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_share_token ON shared_sessions(share_token);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_session_id ON shared_sessions(session_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_tenant ON shared_sessions(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_tenant_session ON shared_sessions(tenant_id, session_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_expires_at ON shared_sessions(expires_at);
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
"""
|
||||
Migration script to create shared_sessions table.
|
||||
Run: python scripts/migrations/create_shared_sessions_table.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from app.core.config import get_settings
|
||||
|
||||
|
||||
async def run_migration():
|
||||
"""Run the migration to create shared_sessions table."""
|
||||
settings = get_settings()
|
||||
engine = create_async_engine(settings.database_url, echo=True)
|
||||
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
|
||||
migration_sql = """
|
||||
CREATE TABLE IF NOT EXISTS shared_sessions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
share_token VARCHAR(255) NOT NULL UNIQUE,
|
||||
session_id VARCHAR(255) NOT NULL,
|
||||
tenant_id VARCHAR(255) NOT NULL,
|
||||
title VARCHAR(255),
|
||||
description TEXT,
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
max_concurrent_users INTEGER DEFAULT 10,
|
||||
current_users INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_share_token ON shared_sessions(share_token);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_session_id ON shared_sessions(session_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_tenant ON shared_sessions(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_tenant_session ON shared_sessions(tenant_id, session_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_expires_at ON shared_sessions(expires_at);
|
||||
"""
|
||||
|
||||
async with async_session_maker() as session:
|
||||
for statement in migration_sql.strip().split(';'):
|
||||
statement = statement.strip()
|
||||
if statement and not statement.startswith('--'):
|
||||
try:
|
||||
await session.execute(text(statement))
|
||||
print(f"Executed: {statement[:60]}...")
|
||||
except Exception as e:
|
||||
error_str = str(e).lower()
|
||||
if "already exists" in error_str or "duplicate" in error_str:
|
||||
print(f"Skipped (already exists): {statement[:60]}...")
|
||||
else:
|
||||
print(f"Error: {e}")
|
||||
raise
|
||||
|
||||
await session.commit()
|
||||
print("\nMigration completed successfully!")
|
||||
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_migration())
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
"""
|
||||
Migration script to create shared_sessions table.
|
||||
Run: python scripts/migrations/run_shared_sessions_migration.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import create_async_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from app.core.config import get_settings
|
||||
|
||||
|
||||
async def run_migration():
|
||||
"""Run the migration to create shared_sessions table."""
|
||||
settings = get_settings()
|
||||
engine = create_async_engine(settings.database_url, echo=True)
|
||||
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
|
||||
migration_sql = """
|
||||
CREATE TABLE IF NOT EXISTS shared_sessions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
share_token VARCHAR(255) NOT NULL UNIQUE,
|
||||
session_id VARCHAR(255) NOT NULL,
|
||||
tenant_id VARCHAR(255) NOT NULL,
|
||||
title VARCHAR(255),
|
||||
description TEXT,
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
max_concurrent_users INTEGER DEFAULT 10,
|
||||
current_users INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_share_token ON shared_sessions(share_token);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_session_id ON shared_sessions(session_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_tenant ON shared_sessions(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_tenant_session ON shared_sessions(tenant_id, session_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_shared_sessions_expires_at ON shared_sessions(expires_at);
|
||||
"""
|
||||
|
||||
async with async_session_maker() as session:
|
||||
for statement in migration_sql.strip().split(';'):
|
||||
statement = statement.strip()
|
||||
if statement and not statement.startswith('--'):
|
||||
try:
|
||||
await session.execute(text(statement))
|
||||
print(f"Executed: {statement[:60]}...")
|
||||
except Exception as e:
|
||||
error_str = str(e).lower()
|
||||
if "already exists" in error_str or "duplicate" in error_str:
|
||||
print(f"Skipped (already exists): {statement[:60]}...")
|
||||
else:
|
||||
print(f"Error: {e}")
|
||||
raise
|
||||
|
||||
await session.commit()
|
||||
print("\nMigration completed successfully!")
|
||||
|
||||
await engine.dispose()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_migration())
|
||||
Loading…
Reference in New Issue