feat: add rag backend and review access fixes
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Merge historical split document version chains under the new root-group rule.
|
||||
|
||||
Default mode is dry-run. Use --apply to write changes.
|
||||
|
||||
Merge key:
|
||||
- region
|
||||
- root business group (一级分组)
|
||||
- normalized_name
|
||||
|
||||
Within one merge group:
|
||||
- sort by created_at ASC, then document_id ASC
|
||||
- keep the earliest document as root version
|
||||
- reuse the earliest document's version_group_key as the canonical chain key
|
||||
- renumber version_no from 1..N
|
||||
- link previous_version_id sequentially
|
||||
- mark only the newest document as is_latest_version = true
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import asyncpg
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP_TOML = ROOT / "app.toml"
|
||||
|
||||
|
||||
@dataclass
|
||||
class CandidateRow:
|
||||
document_id: int
|
||||
region: str
|
||||
normalized_name: str
|
||||
root_group_id: int | None
|
||||
root_group_name: str | None
|
||||
type_id: int | None
|
||||
type_name: str | None
|
||||
group_id: int | None
|
||||
group_name: str | None
|
||||
version_group_key: str
|
||||
version_no: int
|
||||
is_latest_version: bool
|
||||
file_name: str | None
|
||||
updated_at: Any
|
||||
created_at: Any
|
||||
|
||||
|
||||
def load_target_dsn() -> str:
|
||||
try:
|
||||
import tomllib
|
||||
except ImportError: # pragma: no cover
|
||||
import tomli as tomllib
|
||||
|
||||
with APP_TOML.open("rb") as fh:
|
||||
config = tomllib.load(fh)
|
||||
db = config["DB"]
|
||||
return (
|
||||
f"postgresql://{db['USER']}:{db['PASSWORD']}"
|
||||
f"@{db['HOST']}:{db['PORT']}/{db['NAME']}"
|
||||
)
|
||||
|
||||
|
||||
async def fetch_candidates(
|
||||
conn: asyncpg.Connection,
|
||||
*,
|
||||
region: str | None,
|
||||
normalized_name: str | None,
|
||||
) -> list[CandidateRow]:
|
||||
filters = ["d.deleted_at IS NULL"]
|
||||
params: list[Any] = []
|
||||
|
||||
if region:
|
||||
params.append(region)
|
||||
filters.append(f"d.region = ${len(params)}")
|
||||
if normalized_name:
|
||||
params.append(normalized_name.strip().lower())
|
||||
filters.append(f"d.normalized_name = ${len(params)}")
|
||||
|
||||
where_clause = " AND ".join(filters)
|
||||
|
||||
rows = await conn.fetch(
|
||||
f"""
|
||||
WITH doc_scope AS (
|
||||
SELECT
|
||||
d.id AS document_id,
|
||||
d.region,
|
||||
d.normalized_name,
|
||||
d.version_group_key,
|
||||
d.version_no,
|
||||
d.is_latest_version,
|
||||
d.type_id,
|
||||
dt.name AS type_name,
|
||||
d.group_id,
|
||||
child.name AS group_name,
|
||||
COALESCE(
|
||||
CASE
|
||||
WHEN child.id IS NULL THEN NULL
|
||||
WHEN COALESCE(child.pid, 0) = 0 THEN child.id
|
||||
ELSE child.pid
|
||||
END,
|
||||
inferred.root_group_id
|
||||
) AS root_group_id,
|
||||
COALESCE(root.name, inferred.root_group_name) AS root_group_name,
|
||||
d.created_at,
|
||||
d.updated_at,
|
||||
f.file_name
|
||||
FROM leaudit_documents d
|
||||
LEFT JOIN leaudit_document_types dt
|
||||
ON dt.id = d.type_id
|
||||
LEFT JOIN leaudit_evaluation_point_groups child
|
||||
ON child.id = d.group_id
|
||||
LEFT JOIN leaudit_evaluation_point_groups root
|
||||
ON root.id = CASE
|
||||
WHEN child.id IS NULL THEN NULL
|
||||
WHEN COALESCE(child.pid, 0) = 0 THEN child.id
|
||||
ELSE child.pid
|
||||
END
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
child2.document_type_id,
|
||||
CASE WHEN COUNT(DISTINCT child2.pid) = 1 THEN MIN(child2.pid) END AS root_group_id,
|
||||
CASE WHEN COUNT(DISTINCT child2.pid) = 1 THEN MIN(parent.name) END AS root_group_name
|
||||
FROM leaudit_evaluation_point_groups child2
|
||||
JOIN leaudit_evaluation_point_groups parent
|
||||
ON parent.id = child2.pid
|
||||
WHERE COALESCE(child2.pid, 0) <> 0
|
||||
AND child2.deleted_at IS NULL
|
||||
AND child2.is_enabled = true
|
||||
AND child2.document_type_id IS NOT NULL
|
||||
GROUP BY child2.document_type_id
|
||||
) inferred
|
||||
ON inferred.document_type_id = d.type_id
|
||||
LEFT JOIN leaudit_document_files f
|
||||
ON f.document_id = d.id
|
||||
AND f.is_active = true
|
||||
AND f.file_role = 'primary'
|
||||
WHERE {where_clause}
|
||||
),
|
||||
conflict_keys AS (
|
||||
SELECT
|
||||
region,
|
||||
normalized_name,
|
||||
root_group_id
|
||||
FROM doc_scope
|
||||
WHERE normalized_name IS NOT NULL
|
||||
AND normalized_name <> ''
|
||||
AND root_group_id IS NOT NULL
|
||||
GROUP BY region, normalized_name, root_group_id
|
||||
HAVING COUNT(DISTINCT version_group_key) > 1
|
||||
)
|
||||
SELECT ds.*
|
||||
FROM doc_scope ds
|
||||
JOIN conflict_keys ck
|
||||
ON ck.region = ds.region
|
||||
AND ck.normalized_name = ds.normalized_name
|
||||
AND ck.root_group_id = ds.root_group_id
|
||||
ORDER BY ds.region, ds.root_group_name, ds.normalized_name, ds.created_at ASC, ds.document_id ASC
|
||||
""",
|
||||
*params,
|
||||
)
|
||||
|
||||
return [CandidateRow(**dict(row)) for row in rows]
|
||||
|
||||
|
||||
def group_candidates(rows: list[CandidateRow]) -> list[list[CandidateRow]]:
|
||||
grouped: dict[tuple[str, str, int], list[CandidateRow]] = defaultdict(list)
|
||||
for row in rows:
|
||||
if row.root_group_id is None:
|
||||
continue
|
||||
grouped[(row.region, row.normalized_name, row.root_group_id)].append(row)
|
||||
|
||||
result: list[list[CandidateRow]] = []
|
||||
for group_rows in grouped.values():
|
||||
group_rows.sort(key=lambda item: (item.created_at, item.document_id))
|
||||
result.append(group_rows)
|
||||
result.sort(key=lambda items: (items[0].region, items[0].root_group_name or "", items[0].normalized_name))
|
||||
return result
|
||||
|
||||
|
||||
def print_plan(groups: list[list[CandidateRow]], limit: int) -> None:
|
||||
print(f"计划处理候选组: {len(groups)}")
|
||||
if not groups:
|
||||
return
|
||||
|
||||
for index, rows in enumerate(groups[:limit], start=1):
|
||||
first = rows[0]
|
||||
canonical_key = first.version_group_key
|
||||
root_id = first.document_id
|
||||
latest_id = rows[-1].document_id
|
||||
print()
|
||||
print(
|
||||
f"[{index}] 地区={first.region} | 一级分组={first.root_group_name or first.root_group_id} | "
|
||||
f"名称={first.normalized_name} | 目标链={canonical_key} | 根文档={root_id} | 最新文档={latest_id}"
|
||||
)
|
||||
for version_no, row in enumerate(rows, start=1):
|
||||
previous_id = rows[version_no - 2].document_id if version_no > 1 else None
|
||||
latest_mark = "latest" if row.document_id == latest_id else "history"
|
||||
print(
|
||||
f" - doc={row.document_id} old_chain={row.version_group_key} old_v={row.version_no} "
|
||||
f"-> new_v={version_no} prev={previous_id} {latest_mark} type={row.type_name or row.type_id}"
|
||||
)
|
||||
|
||||
if len(groups) > limit:
|
||||
print()
|
||||
print(f"... 还剩 {len(groups) - limit} 组未显示,可通过 --limit 查看更多")
|
||||
|
||||
|
||||
async def apply_groups(conn: asyncpg.Connection, groups: list[list[CandidateRow]]) -> None:
|
||||
for rows in groups:
|
||||
root_row = rows[0]
|
||||
canonical_key = root_row.version_group_key
|
||||
root_document_id = root_row.document_id
|
||||
latest_document_id = rows[-1].document_id
|
||||
|
||||
for version_no, row in enumerate(rows, start=1):
|
||||
previous_id = rows[version_no - 2].document_id if version_no > 1 else None
|
||||
await conn.execute(
|
||||
"""
|
||||
UPDATE leaudit_documents
|
||||
SET version_group_key = $1,
|
||||
version_no = $2,
|
||||
previous_version_id = $3,
|
||||
root_version_id = $4,
|
||||
is_latest_version = $5,
|
||||
updated_at = NOW()
|
||||
WHERE id = $6
|
||||
""",
|
||||
canonical_key,
|
||||
version_no,
|
||||
previous_id,
|
||||
root_document_id,
|
||||
row.document_id == latest_document_id,
|
||||
row.document_id,
|
||||
)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="合并历史上被拆开的文档版本链")
|
||||
parser.add_argument("--region", help="仅处理指定地区")
|
||||
parser.add_argument("--name", help="仅处理指定 normalized_name")
|
||||
parser.add_argument("--limit", type=int, default=50, help="最多展示多少组计划")
|
||||
parser.add_argument("--apply", action="store_true", help="真正写库;默认仅预览")
|
||||
args = parser.parse_args()
|
||||
|
||||
conn = await asyncpg.connect(load_target_dsn())
|
||||
try:
|
||||
rows = await fetch_candidates(
|
||||
conn,
|
||||
region=args.region,
|
||||
normalized_name=args.name,
|
||||
)
|
||||
groups = group_candidates(rows)
|
||||
print_plan(groups, max(1, args.limit))
|
||||
|
||||
if not args.apply:
|
||||
print()
|
||||
print("dry-run complete; rerun with --apply to write data")
|
||||
return
|
||||
|
||||
async with conn.transaction():
|
||||
await apply_groups(conn, groups)
|
||||
|
||||
print()
|
||||
print(f"apply complete; merged groups: {len(groups)}")
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Preview split document version chains that can be merged under the new rule.
|
||||
|
||||
The new rule groups versions by:
|
||||
- region
|
||||
- root business group (一级分组)
|
||||
- normalized_name
|
||||
|
||||
This script is read-only. It prints candidate groups whose records currently
|
||||
span more than one version_group_key, which means historical uploads were
|
||||
split into multiple chains under the old same-type-only rule.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import asyncpg
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP_TOML = ROOT / "app.toml"
|
||||
|
||||
|
||||
@dataclass
|
||||
class CandidateRow:
|
||||
document_id: int
|
||||
region: str
|
||||
normalized_name: str
|
||||
root_group_id: int | None
|
||||
root_group_name: str | None
|
||||
type_id: int | None
|
||||
type_name: str | None
|
||||
group_id: int | None
|
||||
group_name: str | None
|
||||
version_group_key: str
|
||||
version_no: int
|
||||
is_latest_version: bool
|
||||
file_name: str | None
|
||||
updated_at: Any
|
||||
created_at: Any
|
||||
|
||||
|
||||
def load_target_dsn() -> str:
|
||||
try:
|
||||
import tomllib
|
||||
except ImportError: # pragma: no cover
|
||||
import tomli as tomllib
|
||||
|
||||
with APP_TOML.open("rb") as fh:
|
||||
config = tomllib.load(fh)
|
||||
db = config["DB"]
|
||||
return (
|
||||
f"postgresql://{db['USER']}:{db['PASSWORD']}"
|
||||
f"@{db['HOST']}:{db['PORT']}/{db['NAME']}"
|
||||
)
|
||||
|
||||
|
||||
async def fetch_candidates(
|
||||
conn: asyncpg.Connection,
|
||||
*,
|
||||
region: str | None,
|
||||
normalized_name: str | None,
|
||||
) -> list[CandidateRow]:
|
||||
filters = ["d.deleted_at IS NULL"]
|
||||
params: list[Any] = []
|
||||
|
||||
if region:
|
||||
params.append(region)
|
||||
filters.append(f"d.region = ${len(params)}")
|
||||
if normalized_name:
|
||||
params.append(normalized_name.strip().lower())
|
||||
filters.append(f"d.normalized_name = ${len(params)}")
|
||||
|
||||
where_clause = " AND ".join(filters)
|
||||
|
||||
rows = await conn.fetch(
|
||||
f"""
|
||||
WITH doc_scope AS (
|
||||
SELECT
|
||||
d.id AS document_id,
|
||||
d.region,
|
||||
d.normalized_name,
|
||||
d.version_group_key,
|
||||
d.version_no,
|
||||
d.is_latest_version,
|
||||
d.type_id,
|
||||
dt.name AS type_name,
|
||||
d.group_id,
|
||||
child.name AS group_name,
|
||||
COALESCE(
|
||||
CASE
|
||||
WHEN child.id IS NULL THEN NULL
|
||||
WHEN COALESCE(child.pid, 0) = 0 THEN child.id
|
||||
ELSE child.pid
|
||||
END,
|
||||
inferred.root_group_id
|
||||
) AS root_group_id,
|
||||
COALESCE(
|
||||
root.name,
|
||||
inferred.root_group_name
|
||||
) AS root_group_name,
|
||||
d.created_at,
|
||||
d.updated_at,
|
||||
f.file_name
|
||||
FROM leaudit_documents d
|
||||
LEFT JOIN leaudit_document_types dt
|
||||
ON dt.id = d.type_id
|
||||
LEFT JOIN leaudit_evaluation_point_groups child
|
||||
ON child.id = d.group_id
|
||||
LEFT JOIN leaudit_evaluation_point_groups root
|
||||
ON root.id = CASE
|
||||
WHEN child.id IS NULL THEN NULL
|
||||
WHEN COALESCE(child.pid, 0) = 0 THEN child.id
|
||||
ELSE child.pid
|
||||
END
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
child2.document_type_id,
|
||||
CASE WHEN COUNT(DISTINCT child2.pid) = 1 THEN MIN(child2.pid) END AS root_group_id,
|
||||
CASE WHEN COUNT(DISTINCT child2.pid) = 1 THEN MIN(parent.name) END AS root_group_name
|
||||
FROM leaudit_evaluation_point_groups child2
|
||||
JOIN leaudit_evaluation_point_groups parent
|
||||
ON parent.id = child2.pid
|
||||
WHERE COALESCE(child2.pid, 0) <> 0
|
||||
AND child2.deleted_at IS NULL
|
||||
AND child2.is_enabled = true
|
||||
AND child2.document_type_id IS NOT NULL
|
||||
GROUP BY child2.document_type_id
|
||||
) inferred
|
||||
ON inferred.document_type_id = d.type_id
|
||||
LEFT JOIN leaudit_document_files f
|
||||
ON f.document_id = d.id
|
||||
AND f.is_active = true
|
||||
AND f.file_role = 'primary'
|
||||
WHERE {where_clause}
|
||||
),
|
||||
conflict_keys AS (
|
||||
SELECT
|
||||
region,
|
||||
normalized_name,
|
||||
root_group_id
|
||||
FROM doc_scope
|
||||
WHERE normalized_name IS NOT NULL
|
||||
AND normalized_name <> ''
|
||||
AND root_group_id IS NOT NULL
|
||||
GROUP BY region, normalized_name, root_group_id
|
||||
HAVING COUNT(DISTINCT version_group_key) > 1
|
||||
)
|
||||
SELECT ds.*
|
||||
FROM doc_scope ds
|
||||
JOIN conflict_keys ck
|
||||
ON ck.region = ds.region
|
||||
AND ck.normalized_name = ds.normalized_name
|
||||
AND ck.root_group_id = ds.root_group_id
|
||||
ORDER BY ds.region, ds.root_group_name, ds.normalized_name, ds.created_at ASC, ds.document_id ASC
|
||||
""",
|
||||
*params,
|
||||
)
|
||||
|
||||
return [CandidateRow(**dict(row)) for row in rows]
|
||||
|
||||
|
||||
def print_preview(rows: list[CandidateRow], limit: int) -> None:
|
||||
grouped: dict[tuple[str, str, int | None], list[CandidateRow]] = defaultdict(list)
|
||||
for row in rows:
|
||||
grouped[(row.region, row.normalized_name, row.root_group_id)].append(row)
|
||||
|
||||
items = sorted(
|
||||
grouped.items(),
|
||||
key=lambda item: (
|
||||
item[0][0],
|
||||
item[1][0].root_group_name or "",
|
||||
item[0][1],
|
||||
),
|
||||
)
|
||||
|
||||
print(f"候选合并组: {len(items)}")
|
||||
if not items:
|
||||
return
|
||||
|
||||
shown = 0
|
||||
for (_, _, _), group_rows in items:
|
||||
if shown >= limit:
|
||||
break
|
||||
shown += 1
|
||||
|
||||
sample = group_rows[0]
|
||||
distinct_chain_keys = sorted({row.version_group_key for row in group_rows})
|
||||
print()
|
||||
print(
|
||||
f"[{shown}] 地区={sample.region} | 一级分组={sample.root_group_name or sample.root_group_id} | "
|
||||
f"归一化名称={sample.normalized_name} | 当前链数={len(distinct_chain_keys)} | 文档数={len(group_rows)}"
|
||||
)
|
||||
|
||||
for idx, row in enumerate(group_rows, start=1):
|
||||
latest_mark = "latest" if row.is_latest_version else "history"
|
||||
display_name = row.file_name or row.normalized_name
|
||||
print(
|
||||
f" - #{idx} doc={row.document_id} chain={row.version_group_key} v{row.version_no} {latest_mark} "
|
||||
f"type={row.type_name or row.type_id} child={row.group_name or row.group_id} "
|
||||
f"time={row.updated_at} file={display_name}"
|
||||
)
|
||||
|
||||
if len(items) > limit:
|
||||
print()
|
||||
print(f"... 还剩 {len(items) - limit} 组未显示,可通过 --limit 查看更多")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="预览需要合并的历史文档版本链")
|
||||
parser.add_argument("--region", help="仅查看指定地区,如 cz / mz / default")
|
||||
parser.add_argument("--name", help="仅查看指定 normalized_name")
|
||||
parser.add_argument("--limit", type=int, default=50, help="最多展示多少组候选,默认 50")
|
||||
args = parser.parse_args()
|
||||
|
||||
conn = await asyncpg.connect(load_target_dsn())
|
||||
try:
|
||||
rows = await fetch_candidates(
|
||||
conn,
|
||||
region=args.region,
|
||||
normalized_name=args.name,
|
||||
)
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
print_preview(rows, max(1, args.limit))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,187 @@
|
||||
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;
|
||||
+48
-13
@@ -48,8 +48,8 @@ VALUES
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
UPDATE role_route
|
||||
SET deleted_at = NOW(), updated_at = NOW()
|
||||
WHERE deleted_at IS NULL
|
||||
SET status = 0, updated_at = NOW()
|
||||
WHERE status <> 0
|
||||
AND route_id IN (
|
||||
SELECT id FROM sys_routes WHERE route_path = '/rules/sets' AND deleted_at IS NULL
|
||||
);
|
||||
@@ -96,15 +96,15 @@ VALUES
|
||||
('evaluation_point:create:write', 'evaluation_point', 'create', 'write', '创建评查点', '创建评查点', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 54, NULL, '/api/v3/evaluation-points', 'POST', NULL),
|
||||
('evaluation_point:update:write', 'evaluation_point', 'update', 'write', '更新评查点', '更新评查点', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 55, NULL, '/api/v3/evaluation-points/{id}', 'PUT', NULL),
|
||||
('evaluation_point:delete:delete', 'evaluation_point', 'delete', 'delete', '删除评查点', '删除评查点', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 56, NULL, '/api/v3/evaluation-points/{id}', 'DELETE', NULL),
|
||||
('cross_review:task:create', 'cross_review', 'task', 'create', '创建交叉评查任务', '创建交叉评查任务', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 57, NULL, '/api/v3/cross-review/tasks', 'POST', ARRAY['/cross-checking/upload']),
|
||||
('cross_review:task:read', 'cross_review', 'task', 'read', '查看交叉评查任务', '查看交叉评查任务', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 58, NULL, '/api/v3/cross-review/tasks/query', 'POST', ARRAY['/cross-checking']),
|
||||
('cross_review:progress:view', 'cross_review', 'progress', 'view', '查看交叉评查任务进度', '查看交叉评查任务进度', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 59, NULL, '/api/v3/cross-review/tasks/{task_id}/progress', 'GET', ARRAY['/cross-checking']),
|
||||
('cross_review:document:read', 'cross_review', 'document', 'read', '查看交叉评查任务文档', '查看交叉评查任务文档', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 60, NULL, '/api/v3/cross-review/tasks/{task_id}/documents', 'GET', ARRAY['/cross-checking','/cross-checking/result']),
|
||||
('cross_review:document:complete', 'cross_review', 'document', 'complete', '确认交叉评查文档完成', '确认交叉评查文档完成', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 61, NULL, '/api/v3/cross-review/tasks/{task_id}/can-confirm', 'GET', ARRAY['/cross-checking/result']),
|
||||
('cross_review:proposal:create', 'cross_review', 'proposal', 'create', '创建交叉评查提案', '创建交叉评查提案', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 62, NULL, '/api/v3/cross-review/proposals', 'POST', ARRAY['/cross-checking/result']),
|
||||
('cross_review:proposal:read', 'cross_review', 'proposal', 'read', '查看交叉评查提案', '查看交叉评查提案', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 63, NULL, '/api/v3/cross-review/documents/{document_id}/proposals', 'GET', ARRAY['/cross-checking/result']),
|
||||
('cross_review:proposal:delete', 'cross_review', 'proposal', 'delete', '撤销交叉评查提案', '撤销交叉评查提案', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 64, NULL, '/api/v3/cross-review/proposals/{proposal_id}', 'DELETE', ARRAY['/cross-checking/result']),
|
||||
('cross_review:proposal:vote', 'cross_review', 'proposal', 'vote', '交叉评查提案投票', '交叉评查提案投票', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 65, NULL, '/api/v3/cross-review/proposals/{proposal_id}/votes', 'POST', ARRAY['/cross-checking/result']),
|
||||
('cross_review:task:create', 'cross_review', 'task', 'create', '创建交叉评查任务', '创建交叉评查任务', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 57, NULL, '/api/v3/cross-review/tasks', 'POST', NULL),
|
||||
('cross_review:task:read', 'cross_review', 'task', 'read', '查看交叉评查任务', '查看交叉评查任务', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 58, NULL, '/api/v3/cross-review/tasks/query', 'POST', NULL),
|
||||
('cross_review:progress:view', 'cross_review', 'progress', 'view', '查看交叉评查任务进度', '查看交叉评查任务进度', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 59, NULL, '/api/v3/cross-review/tasks/{task_id}/progress', 'GET', NULL),
|
||||
('cross_review:document:read', 'cross_review', 'document', 'read', '查看交叉评查任务文档', '查看交叉评查任务文档', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 60, NULL, '/api/v3/cross-review/tasks/{task_id}/documents', 'GET', NULL),
|
||||
('cross_review:document:complete', 'cross_review', 'document', 'complete', '确认交叉评查文档完成', '确认交叉评查文档完成', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 61, NULL, '/api/v3/cross-review/tasks/{task_id}/can-confirm', 'GET', NULL),
|
||||
('cross_review:proposal:create', 'cross_review', 'proposal', 'create', '创建交叉评查提案', '创建交叉评查提案', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 62, NULL, '/api/v3/cross-review/proposals', 'POST', NULL),
|
||||
('cross_review:proposal:read', 'cross_review', 'proposal', 'read', '查看交叉评查提案', '查看交叉评查提案', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 63, NULL, '/api/v3/cross-review/documents/{document_id}/proposals', 'GET', NULL),
|
||||
('cross_review:proposal:delete', 'cross_review', 'proposal', 'delete', '撤销交叉评查提案', '撤销交叉评查提案', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 64, NULL, '/api/v3/cross-review/proposals/{proposal_id}', 'DELETE', NULL),
|
||||
('cross_review:proposal:vote', 'cross_review', 'proposal', 'vote', '交叉评查提案投票', '交叉评查提案投票', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 65, NULL, '/api/v3/cross-review/proposals/{proposal_id}/votes', 'POST', NULL),
|
||||
|
||||
('users:list:read', 'users', 'list', 'read', '查看用户列表', '用户列表', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 70, NULL, '/api/users/list', 'GET', NULL),
|
||||
('users:create:write', 'users', 'create', 'write', '创建用户', '创建用户', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 71, NULL, '/api/users', 'POST', NULL),
|
||||
@@ -116,7 +116,14 @@ VALUES
|
||||
('rbac:roles:update', 'rbac', 'roles', 'update', '维护角色信息', '维护角色', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 81, NULL, '/api/rbac/roles/{role_id}', 'PUT', NULL),
|
||||
('rbac:permissions:read', 'rbac', 'permissions', 'read', '查看权限点列表', '权限点列表', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 82, NULL, '/api/rbac/permissions', 'GET', NULL),
|
||||
('rbac:role_permissions:write', 'rbac', 'role_permissions', 'write', '分配角色权限', '分配角色权限', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 83, NULL, '/api/rbac/roles/{role_id}/permissions', 'POST', NULL),
|
||||
('rbac:role_routes:write', 'rbac', 'role_routes', 'write', '分配角色菜单', '分配角色菜单', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 84, NULL, '/api/rbac/roles/{role_id}/routes', 'PUT', NULL)
|
||||
('rbac:role_routes:write', 'rbac', 'role_routes', 'write', '分配角色菜单', '分配角色菜单', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 84, NULL, '/api/rbac/roles/{role_id}/routes', 'PUT', NULL),
|
||||
('rag:app:read', 'rag', 'app', 'read', '查看 RAG 应用', '查看 RAG 应用', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 85, NULL, '/api/v3/rag/apps', 'GET', NULL),
|
||||
('rag:chat:use', 'rag', 'chat', 'use', '使用 RAG 对话', '使用 RAG 对话', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 86, NULL, '/api/v3/rag/chat/messages', 'POST', NULL),
|
||||
('rag:conversation:read', 'rag', 'conversation', 'read', '查看 RAG 会话', '查看 RAG 会话', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 87, NULL, '/api/v3/rag/chat/conversations', 'GET', NULL),
|
||||
('rag:conversation:update', 'rag', 'conversation', 'update', '重命名 RAG 会话', '重命名 RAG 会话', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 88, NULL, '/api/v3/rag/chat/conversations/{ConversationId}', 'PATCH', NULL),
|
||||
('rag:conversation:delete', 'rag', 'conversation', 'delete', '删除 RAG 会话', '删除 RAG 会话', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 89, NULL, '/api/v3/rag/chat/conversations/{ConversationId}', 'DELETE', NULL),
|
||||
('rag:message:feedback', 'rag', 'message', 'feedback', '反馈 RAG 消息', '反馈 RAG 消息', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 90, NULL, '/api/v3/rag/chat/messages/{MessageId}/feedback', 'POST', NULL),
|
||||
('rag:dataset:read', 'rag', 'dataset', 'read', '查看 RAG 知识库', '查看 RAG 知识库', 'API', TRUE, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL, NULL, 91, NULL, '/api/v3/rag/datasets/my', 'GET', NULL)
|
||||
ON CONFLICT (permission_key) DO UPDATE SET
|
||||
module = EXCLUDED.module,
|
||||
resource = EXCLUDED.resource,
|
||||
@@ -258,6 +265,13 @@ seed(role_key, permission_key, grant_type, data_scope) AS (
|
||||
('super_admin', 'users:roles_assign:write', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rbac:roles:read', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rbac:roles:update', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rag:app:read', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rag:chat:use', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rag:conversation:read', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rag:conversation:update', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rag:conversation:delete', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rag:message:feedback', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rag:dataset:read', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rbac:permissions:read', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rbac:role_permissions:write', 'GRANT', 'ALL'),
|
||||
('super_admin', 'rbac:role_routes:write', 'GRANT', 'ALL'),
|
||||
@@ -304,6 +318,13 @@ seed(role_key, permission_key, grant_type, data_scope) AS (
|
||||
('provincial_admin', 'users:roles_assign:write', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rbac:roles:read', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rbac:roles:update', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rag:app:read', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rag:chat:use', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rag:conversation:read', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rag:conversation:update', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rag:conversation:delete', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rag:message:feedback', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rag:dataset:read', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rbac:permissions:read', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rbac:role_permissions:write', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'rbac:role_routes:write', 'GRANT', 'ALL'),
|
||||
@@ -339,6 +360,13 @@ seed(role_key, permission_key, grant_type, data_scope) AS (
|
||||
('admin', 'evaluation_point:create:write', 'GRANT', 'DEPT'),
|
||||
('admin', 'evaluation_point:update:write', 'GRANT', 'DEPT'),
|
||||
('admin', 'evaluation_point:delete:delete', 'GRANT', 'DEPT'),
|
||||
('admin', 'rag:app:read', 'GRANT', 'DEPT'),
|
||||
('admin', 'rag:chat:use', 'GRANT', 'DEPT'),
|
||||
('admin', 'rag:conversation:read', 'GRANT', 'DEPT'),
|
||||
('admin', 'rag:conversation:update', 'GRANT', 'DEPT'),
|
||||
('admin', 'rag:conversation:delete', 'GRANT', 'DEPT'),
|
||||
('admin', 'rag:message:feedback', 'GRANT', 'DEPT'),
|
||||
('admin', 'rag:dataset:read', 'GRANT', 'DEPT'),
|
||||
('admin', 'users:list:read', 'GRANT', 'DEPT'),
|
||||
('admin', 'users:update:write', 'GRANT', 'DEPT'),
|
||||
|
||||
@@ -356,7 +384,14 @@ seed(role_key, permission_key, grant_type, data_scope) AS (
|
||||
('common', 'rules:list:read', 'GRANT', 'DEPT'),
|
||||
('common', 'rules:version_list:read', 'GRANT', 'DEPT'),
|
||||
('common', 'rules:content:read', 'GRANT', 'DEPT'),
|
||||
('common', 'rules:binding_list:read', 'GRANT', 'DEPT')
|
||||
('common', 'rules:binding_list:read', 'GRANT', 'DEPT'),
|
||||
('common', 'rag:app:read', 'GRANT', 'SELF'),
|
||||
('common', 'rag:chat:use', 'GRANT', 'SELF'),
|
||||
('common', 'rag:conversation:read', 'GRANT', 'SELF'),
|
||||
('common', 'rag:conversation:update', 'GRANT', 'SELF'),
|
||||
('common', 'rag:conversation:delete', 'GRANT', 'SELF'),
|
||||
('common', 'rag:message:feedback', 'GRANT', 'SELF'),
|
||||
('common', 'rag:dataset:read', 'GRANT', 'SELF')
|
||||
)
|
||||
INSERT INTO role_permissions (role_id, permission_id, grant_type, data_scope, created_at, updated_at)
|
||||
SELECT rm.id, pm.id, s.grant_type, s.data_scope, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
||||
|
||||
Reference in New Issue
Block a user