fix(knowledge): deep audit — 18 bugs fixed across backend + frontend
CRITICAL: - Migration permission seed WHERE name → WHERE id (matched 0 rows, all KB APIs broken) HIGH: - analytics_quality SQL alias + missing comma fix - search() duplicate else block compile error - chunk_content duplicate var declarations + type mismatch - SQL invalid escape sequences - delete_category missing rows_affected check MEDIUM: - analytics_overview hit_rate vs positive_feedback_rate separation - analytics_quality GROUP BY kc.id,kc.name (same-name category merge) - update_category handler trim + empty name validation - update_item duplicate VALID_STATUSES inside transaction - page_size max(1) lower bound in list handlers - batch_create title/content/length validation - embedding dispatch silent error → tracing::warn - Version modal close clears detailItem state - Search empty state distinguishes not-searched vs no-results - Create modal cancel resets form
This commit is contained in:
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS knowledge_categories (
|
||||
id TEXT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
parent_id TEXT REFERENCES knowledge_categories(id),
|
||||
parent_id TEXT REFERENCES knowledge_categories(id) ON DELETE RESTRICT,
|
||||
icon VARCHAR(50),
|
||||
sort_order INT DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
@@ -22,13 +22,13 @@ CREATE INDEX IF NOT EXISTS idx_kc_parent ON knowledge_categories(parent_id);
|
||||
-- 知识条目
|
||||
CREATE TABLE IF NOT EXISTS knowledge_items (
|
||||
id TEXT PRIMARY KEY,
|
||||
category_id TEXT NOT NULL REFERENCES knowledge_categories(id),
|
||||
category_id TEXT NOT NULL REFERENCES knowledge_categories(id) ON DELETE RESTRICT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
keywords TEXT[] DEFAULT '{}',
|
||||
related_questions TEXT[] DEFAULT '{}',
|
||||
priority INT DEFAULT 0,
|
||||
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'archived', 'deprecated')),
|
||||
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'archived', 'deprecated', 'draft')),
|
||||
version INT DEFAULT 1,
|
||||
source VARCHAR(50) DEFAULT 'manual',
|
||||
tags TEXT[] DEFAULT '{}',
|
||||
@@ -38,6 +38,7 @@ CREATE TABLE IF NOT EXISTS knowledge_items (
|
||||
CHECK (length(content) <= 100000)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_ki_category ON knowledge_items(category_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ki_status_updated ON knowledge_items(status, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_ki_keywords ON knowledge_items USING GIN(keywords);
|
||||
|
||||
-- 知识分块(RAG 检索核心)
|
||||
@@ -50,6 +51,7 @@ CREATE TABLE IF NOT EXISTS knowledge_chunks (
|
||||
keywords TEXT[] DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_kchunks_item_idx ON knowledge_chunks(item_id, chunk_index);
|
||||
CREATE INDEX IF NOT EXISTS idx_kchunks_item ON knowledge_chunks(item_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_kchunks_keywords ON knowledge_chunks USING GIN(keywords);
|
||||
|
||||
@@ -57,7 +59,7 @@ CREATE INDEX IF NOT EXISTS idx_kchunks_keywords ON knowledge_chunks USING GIN(ke
|
||||
-- 仅在有数据后创建此索引可提升性能,这里预创建
|
||||
CREATE INDEX IF NOT EXISTS idx_kchunks_embedding ON knowledge_chunks
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
WITH (m = 16, ef_construction = 64);
|
||||
WITH (m = 16, ef_construction = 128);
|
||||
|
||||
-- 版本快照
|
||||
CREATE TABLE IF NOT EXISTS knowledge_versions (
|
||||
@@ -77,8 +79,8 @@ CREATE INDEX IF NOT EXISTS idx_kv_item ON knowledge_versions(item_id);
|
||||
-- 使用追踪
|
||||
CREATE TABLE IF NOT EXISTS knowledge_usage (
|
||||
id TEXT PRIMARY KEY,
|
||||
item_id TEXT NOT NULL REFERENCES knowledge_items(id),
|
||||
chunk_id TEXT REFERENCES knowledge_chunks(id),
|
||||
item_id TEXT REFERENCES knowledge_items(id) ON DELETE SET NULL,
|
||||
chunk_id TEXT REFERENCES knowledge_chunks(id) ON DELETE SET NULL,
|
||||
session_id VARCHAR(100),
|
||||
query_text TEXT,
|
||||
relevance_score FLOAT,
|
||||
@@ -86,24 +88,36 @@ CREATE TABLE IF NOT EXISTS knowledge_usage (
|
||||
agent_feedback VARCHAR(20) CHECK (agent_feedback IN ('positive', 'negative')),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_ku_item ON knowledge_usage(item_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ku_created ON knowledge_usage(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_ku_item ON knowledge_usage(item_id) WHERE item_id IS NOT NULL;
|
||||
-- BRIN 索引:追加写入的时间序列数据比 B-tree 更高效
|
||||
CREATE INDEX IF NOT EXISTS idx_ku_created_brin ON knowledge_usage USING brin(created_at);
|
||||
|
||||
-- 权限种子数据
|
||||
-- 权限种子数据(使用 jsonb 操作避免 REPLACE 脆弱性)
|
||||
UPDATE roles
|
||||
SET permissions = REPLACE(
|
||||
permissions,
|
||||
']',
|
||||
', "knowledge:read", "knowledge:write", "knowledge:admin", "knowledge:search"]'
|
||||
SET permissions = (
|
||||
SELECT '[' || string_agg('"' || elem || '"', ', ') || ']'
|
||||
FROM (
|
||||
SELECT DISTINCT elem
|
||||
FROM json_array_elements_text(permissions::json) AS elem
|
||||
UNION ALL SELECT 'knowledge:read'
|
||||
UNION ALL SELECT 'knowledge:write'
|
||||
UNION ALL SELECT 'knowledge:admin'
|
||||
UNION ALL SELECT 'knowledge:search'
|
||||
) sub
|
||||
)
|
||||
WHERE name = 'super_admin'
|
||||
WHERE id = 'super_admin'
|
||||
AND permissions NOT LIKE '%knowledge:read%';
|
||||
|
||||
UPDATE roles
|
||||
SET permissions = REPLACE(
|
||||
permissions,
|
||||
']',
|
||||
', "knowledge:read", "knowledge:write", "knowledge:search"]'
|
||||
SET permissions = (
|
||||
SELECT '[' || string_agg('"' || elem || '"', ', ') || ']'
|
||||
FROM (
|
||||
SELECT DISTINCT elem
|
||||
FROM json_array_elements_text(permissions::json) AS elem
|
||||
UNION ALL SELECT 'knowledge:read'
|
||||
UNION ALL SELECT 'knowledge:write'
|
||||
UNION ALL SELECT 'knowledge:search'
|
||||
) sub
|
||||
)
|
||||
WHERE name = 'admin'
|
||||
WHERE id = 'admin'
|
||||
AND permissions NOT LIKE '%knowledge:read%';
|
||||
|
||||
Reference in New Issue
Block a user