P0-1 (limits.go): Allow()方法改为全程使用写锁保护counters map读写,避免RLock写入时的data race P0-2 (ticket_workflow.go+ticket_handler.go): Assign/Resolve/Close操作先查询ticket存在性和状态,返回明确的CS_TICKET_4001/CS_TKT_4002/CS_TICKET_4092/CS_TICKET_4093错误码,handler根据错误前缀路由HTTP状态码 P1-1 (ticket_store.go): 移除GetStats中3处手动rows.Close(),只保留defer Close()
72 lines
3.0 KiB
SQL
72 lines
3.0 KiB
SQL
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
|
|
CREATE TABLE IF NOT EXISTS cs_sessions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
channel VARCHAR(16) NOT NULL,
|
|
open_id VARCHAR(128) NOT NULL,
|
|
user_id VARCHAR(64) NULL,
|
|
status VARCHAR(16) NOT NULL DEFAULT 'idle',
|
|
turn_count INT NOT NULL DEFAULT 0,
|
|
last_message_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
CONSTRAINT chk_cs_sessions_channel CHECK (channel IN ('telegram','discord','wechat','widget')),
|
|
CONSTRAINT chk_cs_sessions_status CHECK (status IN ('idle','processing','waiting_feedback','handoff','closed'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_channel_openid ON cs_sessions(channel, open_id);
|
|
|
|
CREATE TABLE IF NOT EXISTS cs_messages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
session_id UUID NOT NULL REFERENCES cs_sessions(id) ON DELETE CASCADE,
|
|
direction VARCHAR(8) NOT NULL,
|
|
content TEXT NOT NULL,
|
|
content_type VARCHAR(16) NOT NULL DEFAULT 'text',
|
|
intent VARCHAR(32) NULL,
|
|
confidence NUMERIC(3,2) NULL,
|
|
model_provider VARCHAR(32) NULL,
|
|
latency_ms INT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
CONSTRAINT chk_cs_messages_direction CHECK (direction IN ('in','out'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_session_id ON cs_messages(session_id, created_at DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS cs_tickets (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
session_id UUID NOT NULL REFERENCES cs_sessions(id) ON DELETE CASCADE,
|
|
user_id VARCHAR(64) NULL,
|
|
priority VARCHAR(4) NOT NULL,
|
|
status VARCHAR(16) NOT NULL DEFAULT 'open',
|
|
handoff_reason VARCHAR(32) NOT NULL,
|
|
assigned_to VARCHAR(64) NULL,
|
|
context_snapshot JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
resolution TEXT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
resolved_at TIMESTAMPTZ NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
CONSTRAINT chk_cs_tickets_priority CHECK (priority IN ('P0','P1','P2','P3')),
|
|
CONSTRAINT chk_cs_tickets_status CHECK (status IN ('open','assigned','processing','resolved','closed'))
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_status_priority ON cs_tickets(status, priority, created_at);
|
|
|
|
CREATE TABLE IF NOT EXISTS cs_audit_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id VARCHAR(64) NOT NULL,
|
|
object_type VARCHAR(32) NOT NULL,
|
|
object_id VARCHAR(64) NOT NULL,
|
|
action VARCHAR(16) NOT NULL,
|
|
before_state JSONB NULL,
|
|
after_state JSONB NULL,
|
|
actor_id VARCHAR(64) NOT NULL,
|
|
source_ip VARCHAR(45) NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_audit_object ON cs_audit_logs(object_type, object_id, created_at DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS cs_message_dedup (
|
|
channel VARCHAR(16) NOT NULL,
|
|
message_id VARCHAR(128) NOT NULL,
|
|
session_id UUID NULL REFERENCES cs_sessions(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (channel, message_id)
|
|
);
|