2026-08-04 15:46:09 +08:00
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""PostgreSQL 关系表 schema(模板 / 模型 / 用户 / 权限)—— 子任务 #30。
|
|
|
|
|
|
|
|
|
|
|
|
按模板自动生成隔离 schema(`tpl_{tpl}`),内含四张基础关系表:
|
|
|
|
|
|
- templates 模板注册表(行业模板配置资产);
|
|
|
|
|
|
- models 模型注册表(模型版本 / 算法 / 超参包);
|
|
|
|
|
|
- users 用户表(角色 + 细粒度权限 JSONB);
|
|
|
|
|
|
- permissions 角色-资源-动作权限矩阵。
|
|
|
|
|
|
|
|
|
|
|
|
另生成角色授权语句(GRANT),按模板配置声明 databus_ro / databus_rw 等角色。
|
|
|
|
|
|
不依赖 psycopg2:仅产出 DDL 文本,供模板配置台预览 / 运维执行。
|
|
|
|
|
|
"""
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from typing import List, Optional
|
|
|
|
|
|
|
|
|
|
|
|
from .templating import TemplateNaming
|
|
|
|
|
|
|
|
|
|
|
|
# 默认基础表清单(对应子任务 #30「模板/模型/用户/权限」)
|
|
|
|
|
|
DEFAULT_TABLES: List[str] = ["templates", "models", "users", "permissions"]
|
|
|
|
|
|
|
|
|
|
|
|
# 表 → 列定义(可被模板 YAML 的 schema_tables 覆盖为子集/扩展)
|
|
|
|
|
|
TABLE_COLUMNS: dict = {
|
|
|
|
|
|
"templates": [
|
|
|
|
|
|
"id BIGSERIAL PRIMARY KEY",
|
|
|
|
|
|
"template VARCHAR(64) NOT NULL UNIQUE",
|
|
|
|
|
|
"version VARCHAR(32) NOT NULL DEFAULT '0.1.0'",
|
2026-08-04 16:58:06 +08:00
|
|
|
|
"config JSONB NOT NULL DEFAULT '{{}}'::jsonb", # format() 转义 {}
|
2026-08-04 15:46:09 +08:00
|
|
|
|
"status VARCHAR(16) NOT NULL DEFAULT 'draft'",
|
|
|
|
|
|
"created_at TIMESTAMPTZ NOT NULL DEFAULT now()",
|
|
|
|
|
|
],
|
|
|
|
|
|
"models": [
|
|
|
|
|
|
"id BIGSERIAL PRIMARY KEY",
|
|
|
|
|
|
"model_id VARCHAR(128) NOT NULL UNIQUE",
|
|
|
|
|
|
"template VARCHAR(64) REFERENCES {schema}.templates(template) ON DELETE CASCADE",
|
|
|
|
|
|
"algorithm VARCHAR(32) NOT NULL DEFAULT 'xgboost'",
|
2026-08-04 16:58:06 +08:00
|
|
|
|
"hyperparams JSONB NOT NULL DEFAULT '{{}}'::jsonb", # format() 转义 {}
|
2026-08-04 15:46:09 +08:00
|
|
|
|
"version VARCHAR(32) NOT NULL DEFAULT '0.1.0'",
|
|
|
|
|
|
"status VARCHAR(16) NOT NULL DEFAULT 'staging'",
|
|
|
|
|
|
"created_at TIMESTAMPTZ NOT NULL DEFAULT now()",
|
|
|
|
|
|
],
|
|
|
|
|
|
"users": [
|
|
|
|
|
|
"id BIGSERIAL PRIMARY KEY",
|
|
|
|
|
|
"username VARCHAR(64) NOT NULL UNIQUE",
|
|
|
|
|
|
"role VARCHAR(32) NOT NULL DEFAULT 'viewer'",
|
|
|
|
|
|
"permissions JSONB NOT NULL DEFAULT '[]'::jsonb",
|
|
|
|
|
|
"created_at TIMESTAMPTZ NOT NULL DEFAULT now()",
|
|
|
|
|
|
],
|
|
|
|
|
|
"permissions": [
|
|
|
|
|
|
"id BIGSERIAL PRIMARY KEY",
|
|
|
|
|
|
"role VARCHAR(32) NOT NULL",
|
|
|
|
|
|
"resource VARCHAR(64) NOT NULL",
|
|
|
|
|
|
"action VARCHAR(16) NOT NULL",
|
|
|
|
|
|
"UNIQUE (role, resource, action)",
|
|
|
|
|
|
],
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_schema_ddl(
|
|
|
|
|
|
naming: TemplateNaming,
|
|
|
|
|
|
tables: Optional[List[str]] = None,
|
|
|
|
|
|
table_columns: Optional[dict] = None,
|
|
|
|
|
|
) -> str:
|
|
|
|
|
|
"""生成 schema 与基础关系表的完整 DDL(幂等 IF NOT EXISTS)。
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
naming: 模板命名器(决定 schema 名)。
|
|
|
|
|
|
tables: 表清单(默认 DEFAULT_TABLES)。
|
|
|
|
|
|
table_columns: 表→列定义覆盖(默认 TABLE_COLUMNS)。
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
多语句 DDL 文本(含 CREATE SCHEMA / CREATE TABLE)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
names = tables or DEFAULT_TABLES
|
|
|
|
|
|
cols = table_columns or TABLE_COLUMNS
|
|
|
|
|
|
schema = naming.pg_schema()
|
|
|
|
|
|
|
|
|
|
|
|
lines = [f"CREATE SCHEMA IF NOT EXISTS {schema};", ""]
|
|
|
|
|
|
for table in names:
|
|
|
|
|
|
columns = cols.get(table)
|
|
|
|
|
|
if not columns:
|
|
|
|
|
|
continue
|
|
|
|
|
|
lines.append(f"CREATE TABLE IF NOT EXISTS {schema}.{table} (")
|
|
|
|
|
|
lines.append(" " + ",\n ".join(c.format(schema=schema) for c in columns))
|
|
|
|
|
|
lines.append(");")
|
|
|
|
|
|
lines.append("")
|
|
|
|
|
|
return "\n".join(lines).rstrip() + "\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_grant_ddl(
|
|
|
|
|
|
naming: TemplateNaming,
|
|
|
|
|
|
roles: Optional[List[str]] = None,
|
|
|
|
|
|
) -> List[str]:
|
|
|
|
|
|
"""按模板声明的关系角色生成授权语句。
|
|
|
|
|
|
|
|
|
|
|
|
角色约定:`{role}_ro` 只读、`{role}_rw` 读写(可扩展)。
|
|
|
|
|
|
Args:
|
|
|
|
|
|
naming: 模板命名器。
|
|
|
|
|
|
roles: 角色清单(如 ["databus"] → databus_ro / databus_rw)。
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
GRANT 语句列表。
|
|
|
|
|
|
"""
|
|
|
|
|
|
schema = naming.pg_schema()
|
|
|
|
|
|
grants: List[str] = []
|
|
|
|
|
|
for role in (roles or ["databus"]):
|
|
|
|
|
|
for suffix, privileges in (("ro", "SELECT"), ("rw", "SELECT, INSERT, UPDATE, DELETE")):
|
|
|
|
|
|
role_name = f"{role}_{suffix}"
|
|
|
|
|
|
grants.append(f"GRANT USAGE ON SCHEMA {schema} TO {role_name};")
|
|
|
|
|
|
grants.append(
|
|
|
|
|
|
f"GRANT {privileges} ON ALL TABLES IN SCHEMA {schema} TO {role_name};"
|
|
|
|
|
|
)
|
|
|
|
|
|
if suffix == "rw":
|
|
|
|
|
|
grants.append(
|
|
|
|
|
|
f"GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA {schema} TO {role_name};"
|
|
|
|
|
|
)
|
|
|
|
|
|
return grants
|