From ce1ab01b6d2347d2d134a531b1c042f7d4286bf4 Mon Sep 17 00:00:00 2001 From: zizi Date: Mon, 13 Jul 2026 10:23:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A1=86=E6=9E=B6:=20A2=E6=94=B6=E5=8F=A3?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=B8=89=E5=9F=9F20=E8=A1=A8=E6=8C=89?= =?UTF-8?q?=E4=B8=BB=E4=BB=93DDL=E5=8E=9F=E6=A0=B7=E5=BB=BA=E6=88=90(V1/V3?= =?UTF-8?q?/V5=E5=85=A8=E6=96=87+V9V10V14V26V30=E8=A7=A6=E8=A1=A8ALTER?= =?UTF-8?q?=E6=91=98=E5=BD=95)+example=5F*=E5=9B=9B=E8=A1=A8(=E5=B5=8C?= =?UTF-8?q?=E5=85=A5=E8=BE=B9=E8=A1=A8vector1024+HNSW);db=20skill=E4=BA=A4?= =?UTF-8?q?=E4=BB=98(psycopg=E7=9B=B4=E8=BF=9E/=E5=8D=A1=E7=89=87=E6=89=93?= =?UTF-8?q?=E5=8D=B0/=E6=96=87=E4=BB=B6=E4=BA=8B=E5=8A=A1);=E8=A1=A8?= =?UTF-8?q?=E6=98=A0=E5=B0=84=E4=B8=8E=E6=9A=82=E7=BC=93=E7=99=BB=E8=AE=B0?= =?UTF-8?q?=E8=90=BD=E6=A1=A3;README=E6=8B=8D=E6=9D=BF=E5=8F=B0=E8=B4=A6?= =?UTF-8?q?=E2=91=A0-=E2=91=A3=E5=85=A8=E6=8B=8D+=E5=9F=BA=E5=BA=A7?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/skills/db/SKILL.md | 38 ++++ .claude/skills/db/scripts/db.py | 106 +++++++++ README.md | 6 +- db/ddl/90-主仓ALTER摘录-V9V10V14V30.sql | 39 ++++ db/ddl/91-example实验私货.sql | 96 ++++++++ db/ddl/V1__init_content_schema.sql | 180 +++++++++++++++ ...ix_content_softdelete_order_uk_partial.sql | 28 +++ db/ddl/V3__init_meta_schema.sql | 133 +++++++++++ db/ddl/V5__init_knowledge_schema.sql | 206 ++++++++++++++++++ db/表映射.md | 56 +++++ 10 files changed, 885 insertions(+), 3 deletions(-) create mode 100644 .claude/skills/db/SKILL.md create mode 100644 .claude/skills/db/scripts/db.py create mode 100644 db/ddl/90-主仓ALTER摘录-V9V10V14V30.sql create mode 100644 db/ddl/91-example实验私货.sql create mode 100644 db/ddl/V1__init_content_schema.sql create mode 100644 db/ddl/V26__fix_content_softdelete_order_uk_partial.sql create mode 100644 db/ddl/V3__init_meta_schema.sql create mode 100644 db/ddl/V5__init_knowledge_schema.sql create mode 100644 db/表映射.md diff --git a/.claude/skills/db/SKILL.md b/.claude/skills/db/SKILL.md new file mode 100644 index 0000000..f4e5f8e --- /dev/null +++ b/.claude/skills/db/SKILL.md @@ -0,0 +1,38 @@ +--- +name: db +description: muse-example 实验库的唯一数据库通道——查询/DML/DDL/SQL 文件应用全走 scripts/db.py(psycopg 直连),SELECT 结果卡片式打印即审查面。主会话与智能体要读写 PG 一律经此,不得裸连或散写一次性脚本。 +--- + +# db —— muse-example 唯一数据库通道 + +对应 muse API 面:数据访问层。连接事实与凭据见 [`db/连接信息.md`](../../../db/连接信息.md)(DSN 已锁死在脚本内,只连 `muse-example`)。 + +## 用法(仓库根目录执行,python 一律用 `.venv/bin/python`) + +```bash +# 查询:卡片式打印(默认最多 50 行、长值截 160 字) +.venv/bin/python .claude/skills/db/scripts/db.py query "SELECT id,title FROM muse_content_work" +.venv/bin/python .claude/skills/db/scripts/db.py query "SELECT ..." --json # JSON 数组输出(给脚本消费) +.venv/bin/python .claude/skills/db/scripts/db.py query "SELECT ..." --full # 长值不截断 +.venv/bin/python .claude/skills/db/scripts/db.py query "SELECT ..." --max 200 # 放宽行数 + +# 单条写操作(INSERT/UPDATE/DELETE/DDL):报影响行数 +.venv/bin/python .claude/skills/db/scripts/db.py exec "UPDATE ... WHERE ..." + +# SQL 文件应用:整文件一个事务,失败全回滚 +.venv/bin/python .claude/skills/db/scripts/db.py apply db/ddl/91-example实验私货.sql + +# 表清单+活行数(deleted=FALSE 计数,无 deleted 列的表计全行) +.venv/bin/python .claude/skills/db/scripts/db.py tables +``` + +## 红线 + +- **只连 `muse-example`**:DSN 硬编码锁库;严禁改造脚本去碰共享 PG 上的 muse_local / muse_slice_live / *_test。 +- 软删约定照主仓:删除=UPDATE `deleted=TRUE`,不物理删(example_* 表同样遵守)。 +- 批量导入/嵌入等专用写路径由 import/embed skill 封装(内部同走 psycopg 直连),本 skill 承担通用查改与 DDL 应用。 +- 建表/改表先落 `db/ddl/` 文件再 `apply`,不敲一次性 DDL——文件即审计。 + +## 元数据驱动 + +库内 `muse_meta_schema` / `muse_meta_field` / `muse_meta_visibility_policy` 是 23 型结构本体的权威行(A3 起);拆书与抽取的字段合同一律 `query` 读库取,不再读 `meta/schemas/` YAML(YAML 退为设计稿与种子来源)。 diff --git a/.claude/skills/db/scripts/db.py b/.claude/skills/db/scripts/db.py new file mode 100644 index 0000000..8d31cf0 --- /dev/null +++ b/.claude/skills/db/scripts/db.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""muse-example 唯一数据库通道(db skill 脚本层)。 + +- DSN 锁死 muse-example:严禁触碰共享 PG 上其他库(muse_local / muse_slice_live / *_test)。 +- query 卡片式打印=审查面;exec 报影响行数;apply 整文件一个事务失败全回滚。 +- 失败原样抛错不静默(公约)。 +""" +import json +import sys + +import click +import psycopg + +# 连接事实与凭据来源:db/连接信息.md(内网 Tailscale 段,凭据明文入仓为既定政策) +DSN = "postgresql://root:f6710e2d0294eb1c10e26a805a64bc54@100.64.0.8:5433/muse-example" +TRUNC = 160 # 卡片模式长值截断阈值(字符) + + +def _fmt(value, full: bool) -> str: + """卡片值格式化:NULL 显示 ∅;长值默认截断并标注总长。""" + if value is None: + return "∅" + s = str(value) + if not full and len(s) > TRUNC: + return s[:TRUNC] + f"…(共{len(s)}字)" + return s + + +@click.group() +def cli(): + """muse-example 数据库通道""" + + +@cli.command() +@click.argument("sql_text") +@click.option("--json", "as_json", is_flag=True, help="按 JSON 数组输出(给脚本消费)") +@click.option("--full", is_flag=True, help="长值不截断") +@click.option("--max", "max_rows", default=50, show_default=True, help="最多打印行数") +def query(sql_text: str, as_json: bool, full: bool, max_rows: int): + """执行 SELECT,卡片式打印(审查面)。""" + with psycopg.connect(DSN) as conn: + cur = conn.execute(sql_text) + if cur.description is None: + raise click.ClickException("该语句无结果集——写操作请用 exec") + cols = [d.name for d in cur.description] + rows = cur.fetchall() + if as_json: + out = [dict(zip(cols, r)) for r in rows[:max_rows]] + click.echo(json.dumps(out, ensure_ascii=False, default=str, indent=1)) + else: + width = max((len(c) for c in cols), default=0) + for i, r in enumerate(rows[:max_rows], 1): + click.echo(f"── 行 {i} " + "─" * 30) + for c, v in zip(cols, r): + click.echo(f"{c:<{width}} │ {_fmt(v, full)}") + if len(rows) > max_rows: + click.echo(f"…(共 {len(rows)} 行,仅示 {max_rows} 行,--max 放宽)") + else: + click.echo(f"(共 {len(rows)} 行)") + + +@cli.command("exec") +@click.argument("sql_text") +def exec_(sql_text: str): + """执行单条写操作(INSERT/UPDATE/DELETE/DDL),报影响行数。""" + with psycopg.connect(DSN) as conn: + cur = conn.execute(sql_text) + conn.commit() + n = cur.rowcount + click.echo(f"OK(影响 {n} 行)" if n >= 0 else "OK") + + +@cli.command() +@click.argument("file", type=click.Path(exists=True)) +def apply(file: str): + """应用 SQL 文件:整文件一个事务(无参数走 simple protocol 支持多语句),失败全回滚。""" + with open(file, encoding="utf-8") as f: + sql = f.read() + with psycopg.connect(DSN) as conn: + conn.execute(sql) + conn.commit() + click.echo(f"已应用: {file}") + + +@cli.command() +def tables(): + """表清单 + 活行数(有 deleted 列的表计 deleted=FALSE,其余计全行)。""" + with psycopg.connect(DSN) as conn: + names = [r[0] for r in conn.execute( + "SELECT tablename FROM pg_tables WHERE schemaname='public' ORDER BY tablename")] + for t in names: + has_deleted = conn.execute( + "SELECT 1 FROM information_schema.columns WHERE table_name=%s AND column_name='deleted'", + (t,)).fetchone() + where = " WHERE deleted = FALSE" if has_deleted else "" + n = conn.execute(f'SELECT count(*) FROM "{t}"{where}').fetchone()[0] + click.echo(f"{t:<45} {n}") + + +if __name__ == "__main__": + try: + cli() + except psycopg.Error as e: + # 数据库错误原样透出(可追踪、不静默) + click.echo(f"[db错误] {type(e).__name__}: {e}", err=True) + sys.exit(1) diff --git a/README.md b/README.md index 83ca0b6..dd7ac52 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ agent-example/ | read-context | 统一读取器:读装配绑定→按用途+aiContext 三级裁剪→组装六分区上下文包→裁剪清单回显 | 统一创作数据读取器(专题-06 §7) | 现有(文件版);C4 升 PG 检索版(内部调 search) | | confirm | 唯一确认通道:确认=commit、丢弃=restore,仅创始人触发;PG 版加知识行草稿→已确认翻转 | 双轨提交 / 候选三决策入口 | 现有 | | eval | 质量收敛环:n=5、每轮只动一个变量、达标固化 `golden/` | 质量域 | 现有 | -| db | `muse-example` 唯一数据库通道:ssh+容器 psql 封装(凭据按 `db/连接信息.md`),查询/DDL/DML 全走这,结果卡片式打印(=审查面) | 数据访问层 | A2 交付 | +| db | `muse-example` 唯一数据库通道:psycopg 直连封装(凭据按 `db/连接信息.md`),查询/DDL/DML/SQL 文件应用全走这,结果卡片式打印(=审查面) | 数据访问层 | 已交付(2026-07-13) | | import | 导入解析:txt→回目正则静态分章→作品/章行入库(调 db) | 导入解析旅程 | B1 交付;C8 用户旧稿复用 | | embed | New-API 嵌入封装:Qwen3-Embedding-8B、`dimensions:1024`、`--noproxy`、批量+失败重试 | AI 网关(嵌入) | B2 交付 | | search | 向量检索:意图→embed→pgvector 相似度召回→授权过滤(仅已确认+已绑定)+字段裁剪→带相似度分的结果集 | 知识检索 | B3 交付 | @@ -218,8 +218,8 @@ flowchart LR ## 九、当前位置(2026-07-10 快照) - 执行主线=§七闭环序,知识域(A/B)先行;创作域(C)后置,待 B5 过门后按 C 线走。 -- **拍板台账**:①已拍 A——B2 首轮公共面只拆 5 型范式,双层型二轮凭脚手架实据定;细纲字数比例已拍入 outline schema。**待拍**:②参考书书单——封神演义因创始人未读降级为分章正则测试样本,换创始人熟读的 2–3 本(已定框架=每本全本导入分章+首轮拆 30–50 章;原文只入私有库,公共面只有脱敏范式);③A2 口径(主仓表原样不改列+私货全进 `example_*`);④整序确认。齐后从 A1 起跑。 +- **拍板台账(①–④已全拍)**:①B2 首轮公共面只拆 5 型范式,双层型二轮凭脚手架实据定;细纲字数比例已拍入 outline schema。②已拍(2026-07-10)——参考书=`../小说清单/` 7 本**全部导入**私有库;首轮拆书 4 本:超神机械师/机战无限/深空之影/机动风暴(每本 30–50 章;原文只入私有库,公共面只有脱敏范式)。③已拍——主仓表原样不改列+私货全进 `example_*`。④已拍——整序确认,A1 已起跑(B2 跑批前有成本确认闸)。 - **能力就位**:skill 16 只(基建 7:read-context/confirm/eval 现有+db/import/embed/search 按 A2–B3 交付;功能 9 全建成,对齐官方规范)× agent 5(已瘦身为身份段);prompt 三段式(身份段×功能 skill×L0);上下文=四层+章级基线包共享前缀+按 agent 差异矩阵(read-context skill 为准);拆书=workflow 编排+逐章 agent 有界任务(parse-book skill 为准)。 -- **基座**:本机 psycopg 直连与 psql 18.4 均验通;`muse-example` 库与 vector 插件**尚未创建**(A1 第一步);凭据与配方在 `db/连接信息.md`;`.venv` 已建(requirements.txt)。 +- **基座**:A1/A2 已收口(2026-07-13)——`muse-example` 库+pgvector 0.8.5 验通(容器重建恢复命令见连接信息),24 表建成(主仓一致 20 + `example_*` 4,映射与暂缓登记见 `db/表映射.md`),db skill 已交付;凭据与配方在 `db/连接信息.md`;`.venv` 丢失时按 requirements.txt 重建即用。 - **待审资产已清零(2026-07-10 创始人清理)**:文件版首轮的 13 张范式卡、参考书档案 extracted 态、《焚忆》全部文件资产已清除,工作区(干净 clone)无任何待审存量;首轮拆书的字段设计发现已蒸馏入 `meta/schemas/`(已提交),不受影响。 - 进度惯例:看 git log 与本节拍板台账,不设过程状态文档。 diff --git a/db/ddl/90-主仓ALTER摘录-V9V10V14V30.sql b/db/ddl/90-主仓ALTER摘录-V9V10V14V30.sql new file mode 100644 index 0000000..4540558 --- /dev/null +++ b/db/ddl/90-主仓ALTER摘录-V9V10V14V30.sql @@ -0,0 +1,39 @@ +-- 主仓 ALTER 摘录(只取触达 V1/V3/V5 三个 init 文件所建 20 表的语句,一字不改) +-- 口径(创始人拍板③ 2026-07-10):主仓表原样不改列;本文件各段出处逐一注明。 +-- 未摘录说明:V9/V10/V14 等文件中"新建表"部分(planning_section、meta 治理 8 表、 +-- knowledge RAGFlow 14 表、各 outbox 表)实验现阶段不用,暂缓建表,登记见 db/表映射.md。 + +-- ── 出自 sql/muse/V9__extend_content_real_api_schema.sql ── +ALTER TABLE muse_content_export_task + ADD COLUMN IF NOT EXISTS download_expires_at TIMESTAMP, + ADD COLUMN IF NOT EXISTS completed_at TIMESTAMP, + ADD COLUMN IF NOT EXISTS progress INT NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS source_snapshot JSONB; + +-- ── 出自 sql/muse/V10__extend_meta_real_api_schema.sql ── +CREATE UNIQUE INDEX uk_muse_meta_schema_version_active_schema_id + ON muse_meta_schema_version(tenant_id, schema_id) + WHERE active_flag = TRUE AND deleted = FALSE; + +ALTER TABLE muse_meta_protection_node + ADD COLUMN IF NOT EXISTS node_type VARCHAR(64) NOT NULL DEFAULT 'system', + ADD COLUMN IF NOT EXISTS irreplaceable_reason TEXT, + ADD COLUMN IF NOT EXISTS permission_requirement JSONB, + ADD COLUMN IF NOT EXISTS audit_requirement JSONB, + ADD COLUMN IF NOT EXISTS shadow_to_canonical_boundary JSONB, + ADD COLUMN IF NOT EXISTS observability_metrics JSONB; + +-- ── 出自 sql/muse/V14__extend_knowledge_ragflow_real_api_schema.sql ── +ALTER TABLE muse_knowledge_draft + ALTER COLUMN source_snapshot_id TYPE VARCHAR(128) USING source_snapshot_id::text; +ALTER TABLE muse_knowledge_draft + ALTER COLUMN authorization_snapshot_id TYPE VARCHAR(128) USING authorization_snapshot_id::text; +ALTER TABLE muse_knowledge_binding + ALTER COLUMN source_snapshot_id TYPE VARCHAR(128) USING source_snapshot_id::text; +ALTER TABLE muse_knowledge_binding + ALTER COLUMN authorization_snapshot_id TYPE VARCHAR(128) USING authorization_snapshot_id::text; + +-- ── 出自 sql/muse/V30__widen_authorization_snapshot_id_to_varchar.sql ── +-- (同文件另有 muse_ai_suggestion 一条,该表实验未建,不摘) +ALTER TABLE muse_content_block_source_attribution + ALTER COLUMN authorization_snapshot_id TYPE VARCHAR(128) USING authorization_snapshot_id::text; diff --git a/db/ddl/91-example实验私货.sql b/db/ddl/91-example实验私货.sql new file mode 100644 index 0000000..fcdfe1e --- /dev/null +++ b/db/ddl/91-example实验私货.sql @@ -0,0 +1,96 @@ +-- 实验私货表(口径③:主仓表原样不改列,实验扩展全进 example_* 前缀) +-- 公共列/触发器沿用主仓惯例(creator/create_time/updater/update_time/deleted/tenant_id + updated_at 触发器); +-- tenant_id/creator 按拍板默认系统主账号=1(写入层给值,列默认仍照主仓惯例)。 +-- 软引用无外键(同主仓约定:引用完整性由应用/skill 层保证)。 + +-- ══ example_reference_work:参考书档案(23 型之 reference_work 的库内实例载体)══ +-- 一本参考书 = muse_content_work 一行(原文私有库) + 本表一行(档案与拆书进度)。 +CREATE TABLE example_reference_work ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, -- → muse_content_work.id(软引用) + author VARCHAR(100), -- 作者 + source_file VARCHAR(500), -- 原始 txt 文件名 + declared_chapter_count INT, -- 原文件头部声明章数(无则 NULL) + imported_chapter_count INT NOT NULL DEFAULT 0, -- 实际导入章数 + char_count BIGINT NOT NULL DEFAULT 0, -- 全文字符数 + parse_scope JSONB, -- 首轮拆书范围,如 {"from":1,"to":50} + parse_status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending/parsing/parsed + notes TEXT, -- 清洗记录/章题格式特点等备注 + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_example_reference_work UNIQUE (tenant_id, work_id) +); +CREATE TRIGGER trg_example_reference_work_updated_at BEFORE UPDATE ON example_reference_work FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ══ example_parse_task:拆书任务状态机(workflow 断点续跑与幂等,状态全在库)══ +-- 一章一行;两阶段各自状态:scaffold=细纲逆推+实体脚手架,pattern=范式拆解。 +CREATE TABLE example_parse_task ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, -- → muse_content_work.id + chapter_id BIGINT NOT NULL, -- → muse_content_chapter.id + scaffold_status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending/running/done/failed + pattern_status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending/running/done/failed + attempt_count INT NOT NULL DEFAULT 0, -- 累计尝试次数(含重试) + agent_model VARCHAR(50), -- 执行模型标记(审计用) + error_message TEXT, -- 最近一次失败原因 + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_example_parse_task UNIQUE (tenant_id, work_id, chapter_id), + CONSTRAINT chk_example_parse_task_scaffold CHECK (scaffold_status IN ('pending','running','done','failed')), + CONSTRAINT chk_example_parse_task_pattern CHECK (pattern_status IN ('pending','running','done','failed')) +); +CREATE INDEX idx_example_parse_task_work ON example_parse_task(tenant_id, work_id, scaffold_status, pattern_status); +CREATE TRIGGER trg_example_parse_task_updated_at BEFORE UPDATE ON example_parse_task FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ══ example_parse_scaffold:逐章脚手架(parse-book 2b 第一步产物)══ +-- 细纲字数比例按 outline schema 拍板值(章细纲=原文 3–5%);实体清单供判重与二轮双层型决策。 +CREATE TABLE example_parse_scaffold ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, -- → muse_content_work.id + chapter_id BIGINT NOT NULL, -- → muse_content_chapter.id + outline_text TEXT, -- 逆推细纲(对原文 3–5% 字数) + entities JSONB, -- 实体清单 [{type,name,brief}] + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_example_parse_scaffold UNIQUE (tenant_id, chapter_id) +); +CREATE INDEX idx_example_parse_scaffold_work ON example_parse_scaffold(tenant_id, work_id); +CREATE TRIGGER trg_example_parse_scaffold_updated_at BEFORE UPDATE ON example_parse_scaffold FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +-- ══ example_knowledge_embedding:知识行嵌入边表(vector(1024),Qwen3-Embedding-8B)══ +-- 挂 draft(草稿期)或 entity(确认后),至少其一非空;content_hash 幂等判重(同文同模型不重嵌)。 +CREATE TABLE example_knowledge_embedding ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + draft_id BIGINT, -- → muse_knowledge_draft.id(软引用) + entity_id BIGINT, -- → muse_knowledge_entity.id(软引用) + content_hash CHAR(64) NOT NULL, -- 嵌入文本 sha256(幂等键) + embed_text TEXT NOT NULL, -- 被嵌入文本(名称+摘要+正文拼接) + model VARCHAR(100) NOT NULL DEFAULT 'Qwen/Qwen3-Embedding-8B', + dimensions INT NOT NULL DEFAULT 1024, + embedding vector(1024) NOT NULL, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_example_knowledge_embedding UNIQUE (tenant_id, content_hash, model), + CONSTRAINT chk_example_knowledge_embedding_ref CHECK (draft_id IS NOT NULL OR entity_id IS NOT NULL) +); +CREATE INDEX idx_example_knowledge_embedding_draft ON example_knowledge_embedding(tenant_id, draft_id) WHERE draft_id IS NOT NULL; +CREATE INDEX idx_example_knowledge_embedding_entity ON example_knowledge_embedding(tenant_id, entity_id) WHERE entity_id IS NOT NULL; +-- 余弦相似度检索索引(B3 search skill 用 <=> 召回;数据量小,HNSW 默认参数即可) +CREATE INDEX idx_example_knowledge_embedding_hnsw ON example_knowledge_embedding USING hnsw (embedding vector_cosine_ops); +CREATE TRIGGER trg_example_knowledge_embedding_updated_at BEFORE UPDATE ON example_knowledge_embedding FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/db/ddl/V1__init_content_schema.sql b/db/ddl/V1__init_content_schema.sql new file mode 100644 index 0000000..eb0ffea --- /dev/null +++ b/db/ddl/V1__init_content_schema.sql @@ -0,0 +1,180 @@ +-- Muse Content 模块初始 Schema(PostgreSQL 16) +-- 影响范围:作品、章节、Block、来源归因、导入导出任务。 +-- 说明:业务表不使用数据库外键,引用完整性由应用层在权限、租户和 owner 校验后保证。 + +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.update_time = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE muse_content_work ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + owner_user_id BIGINT NOT NULL, + title VARCHAR(200) NOT NULL, + description TEXT, + genre VARCHAR(50), + cover_image_url TEXT, + summary TEXT, + status VARCHAR(20) NOT NULL DEFAULT 'writing', + work_schema_id BIGINT, + import_status VARCHAR(20), + parse_status VARCHAR(20), + word_count INT NOT NULL DEFAULT 0, + chapter_count INT NOT NULL DEFAULT 0, + command_id VARCHAR(120), + revision INT NOT NULL DEFAULT 1, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_muse_content_work_owner ON muse_content_work(tenant_id, owner_user_id, update_time); +CREATE INDEX idx_muse_content_work_status ON muse_content_work(tenant_id, status); +CREATE UNIQUE INDEX uk_muse_content_work_command ON muse_content_work(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_content_work_updated_at BEFORE UPDATE ON muse_content_work FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_content_chapter ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + title VARCHAR(200) NOT NULL, + order_no INT NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'draft', + goal_snapshot JSONB, + outline_snapshot JSONB, + parse_review_status VARCHAR(20), + command_id VARCHAR(120), + revision INT NOT NULL DEFAULT 1, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_content_chapter_work_order UNIQUE (tenant_id, work_id, order_no) +); + +CREATE INDEX idx_muse_content_chapter_work ON muse_content_chapter(tenant_id, work_id); +CREATE UNIQUE INDEX uk_muse_content_chapter_command ON muse_content_chapter(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_content_chapter_updated_at BEFORE UPDATE ON muse_content_chapter FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_content_block ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + chapter_id BIGINT NOT NULL, + order_no INT NOT NULL, + block_type VARCHAR(30) NOT NULL DEFAULT 'scene', + title VARCHAR(500), + content_doc JSONB, + content_text TEXT, + revision INT NOT NULL DEFAULT 1, + word_count INT NOT NULL DEFAULT 0, + command_id VARCHAR(120), + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_content_block_chapter_order UNIQUE (tenant_id, chapter_id, order_no) +); + +CREATE INDEX idx_muse_content_block_work ON muse_content_block(tenant_id, work_id); +CREATE INDEX idx_muse_content_block_chapter ON muse_content_block(tenant_id, chapter_id); +CREATE UNIQUE INDEX uk_muse_content_block_command ON muse_content_block(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_content_block_updated_at BEFORE UPDATE ON muse_content_block FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_content_block_source_attribution ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + block_id BIGINT NOT NULL, + revision INT NOT NULL, + source_type VARCHAR(50) NOT NULL, + source_object_id VARCHAR(100), + source_version INT, + lineage_payload JSONB, + authorization_snapshot_id BIGINT, + source_status VARCHAR(30), + license_restriction_snapshot JSONB, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_content_block_src_revision UNIQUE (tenant_id, block_id, revision) +); + +CREATE INDEX idx_muse_content_block_src_block ON muse_content_block_source_attribution(tenant_id, block_id); +CREATE TRIGGER trg_muse_content_block_src_updated_at BEFORE UPDATE ON muse_content_block_source_attribution FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_content_import_task ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + owner_user_id BIGINT NOT NULL, + source_type VARCHAR(50) NOT NULL, + source_snapshot JSONB, + status VARCHAR(30) NOT NULL DEFAULT 'queued', + error_code VARCHAR(80), + error_message TEXT, + command_id VARCHAR(120) NOT NULL, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_content_import_command UNIQUE (tenant_id, command_id) +); + +CREATE INDEX idx_muse_content_import_work ON muse_content_import_task(tenant_id, work_id, status); +CREATE TRIGGER trg_muse_content_import_updated_at BEFORE UPDATE ON muse_content_import_task FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_content_export_task ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + owner_user_id BIGINT NOT NULL, + export_format VARCHAR(30) NOT NULL, + status VARCHAR(30) NOT NULL DEFAULT 'queued', + credential_id VARCHAR(120), + package_ref TEXT, + error_code VARCHAR(80), + error_message TEXT, + command_id VARCHAR(120) NOT NULL, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_content_export_command UNIQUE (tenant_id, command_id) +); + +CREATE INDEX idx_muse_content_export_work ON muse_content_export_task(tenant_id, work_id, status); +CREATE TRIGGER trg_muse_content_export_updated_at BEFORE UPDATE ON muse_content_export_task FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_content_command_log ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + command_id VARCHAR(120) NOT NULL, + command_type VARCHAR(80) NOT NULL, + owner_user_id BIGINT NOT NULL, + target_type VARCHAR(50), + target_id BIGINT, + request_hash VARCHAR(128), + result_snapshot JSONB, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_content_command UNIQUE (tenant_id, command_id) +); + +CREATE INDEX idx_muse_content_command_owner ON muse_content_command_log(tenant_id, owner_user_id, create_time); +CREATE TRIGGER trg_muse_content_command_updated_at BEFORE UPDATE ON muse_content_command_log FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/db/ddl/V26__fix_content_softdelete_order_uk_partial.sql b/db/ddl/V26__fix_content_softdelete_order_uk_partial.sql new file mode 100644 index 0000000..b4c4239 --- /dev/null +++ b/db/ddl/V26__fix_content_softdelete_order_uk_partial.sql @@ -0,0 +1,28 @@ +-- 修复 content 章节/Block 软删后 order_no 唯一约束碰撞 schema bug。 +-- +-- 背景:muse_content_chapter / muse_content_block 继承 yudao 软删(deleted 列 + @TableLogic, +-- 删除是 UPDATE deleted=1、行仍在表)。但 uk_muse_content_chapter_work_order(tenant_id, work_id, order_no) +-- 与 uk_muse_content_block_chapter_order(tenant_id, chapter_id, order_no) 是完整唯一约束、不含 deleted 条件, +-- 软删行仍占 order_no 名额;而 createChapter/createBlock 用 orderNo = selectCount(active)+1(只算未删行), +-- 删章节/Block 后再建同序号 → 与软删行碰撞 → PostgreSQL duplicate key → 500 系统异常。 +-- (反假绿:studio 前端补齐 chapter create 契约字段后真后端建章才暴露此 bug;见 docs/mvp/进度总账.md。) +-- +-- 修法:改为 partial unique index(WHERE deleted = FALSE),软删行不占名额、active 行唯一性不变, +-- 对齐同库 uk_muse_content_chapter_command 等既有 partial index 惯例。 +-- 安全性:order_no uk 不被任何 ON CONFLICT 引用(content 的 ON CONFLICT 仅在 command/outbox 表、用完整 uk), +-- 故改 partial 不影响 insert/ON CONFLICT 仲裁(区别于 V22 须保留完整索引的 command_id 场景)。 +-- 幂等:DROP CONSTRAINT/INDEX IF EXISTS 后重建,对已应用 V1 的库与全新库均安全。 + +-- 章节:DROP 原完整唯一约束,改建 partial unique index(同名) +ALTER TABLE muse_content_chapter DROP CONSTRAINT IF EXISTS uk_muse_content_chapter_work_order; +DROP INDEX IF EXISTS uk_muse_content_chapter_work_order; +CREATE UNIQUE INDEX uk_muse_content_chapter_work_order + ON muse_content_chapter (tenant_id, work_id, order_no) + WHERE deleted = FALSE; + +-- Block:同理(createBlock 同样用 selectCountByChapterId(active)+1,删后重建同序号会碰撞) +ALTER TABLE muse_content_block DROP CONSTRAINT IF EXISTS uk_muse_content_block_chapter_order; +DROP INDEX IF EXISTS uk_muse_content_block_chapter_order; +CREATE UNIQUE INDEX uk_muse_content_block_chapter_order + ON muse_content_block (tenant_id, chapter_id, order_no) + WHERE deleted = FALSE; diff --git a/db/ddl/V3__init_meta_schema.sql b/db/ddl/V3__init_meta_schema.sql new file mode 100644 index 0000000..3cf1323 --- /dev/null +++ b/db/ddl/V3__init_meta_schema.sql @@ -0,0 +1,133 @@ +-- Muse Meta 模块初始 Schema(PostgreSQL 16) +-- 影响范围:MetaSchema、字段、可见性策略、保护节点、功能链路。 + +CREATE TABLE muse_meta_schema ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + schema_key VARCHAR(100) NOT NULL, + domain VARCHAR(20) NOT NULL, + scope VARCHAR(20) NOT NULL, + target_type VARCHAR(50), + display_name VARCHAR(200) NOT NULL, + active_version_id BIGINT, + effective_scope JSONB, + projection_version INT NOT NULL DEFAULT 1, + status VARCHAR(20) NOT NULL DEFAULT 'active', + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_meta_schema_key UNIQUE (tenant_id, domain, scope, target_type, schema_key) +); + +CREATE TRIGGER trg_muse_meta_schema_updated_at BEFORE UPDATE ON muse_meta_schema FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_meta_schema_version ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + schema_id BIGINT NOT NULL, + schema_key VARCHAR(100) NOT NULL, + version_no INT NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'draft', + active_flag BOOLEAN NOT NULL DEFAULT FALSE, + effective_scope JSONB, + field_contract_snapshot JSONB, + impact_preview_snapshot JSONB, + rollback_from_version_id BIGINT, + rollback_to_version_id BIGINT, + published_by VARCHAR(64), + published_at TIMESTAMP, + activated_by VARCHAR(64), + activated_at TIMESTAMP, + change_note VARCHAR(500), + command_id VARCHAR(120), + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_meta_schema_version_key UNIQUE (tenant_id, schema_key, version_no) +); + +CREATE INDEX idx_muse_meta_schema_ver_sid ON muse_meta_schema_version(tenant_id, schema_id); +CREATE UNIQUE INDEX uk_muse_meta_schema_ver_command ON muse_meta_schema_version(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_meta_schema_version_updated_at BEFORE UPDATE ON muse_meta_schema_version FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_meta_field ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + schema_version_id BIGINT NOT NULL, + field_key VARCHAR(100) NOT NULL, + display_name VARCHAR(200) NOT NULL, + field_type VARCHAR(20) NOT NULL, + is_required BOOLEAN NOT NULL DEFAULT FALSE, + default_value JSONB, + enum_values JSONB, + validation_rules JSONB, + sort_order INT NOT NULL DEFAULT 0, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_muse_meta_field_ver ON muse_meta_field(tenant_id, schema_version_id); +CREATE TRIGGER trg_muse_meta_field_updated_at BEFORE UPDATE ON muse_meta_field FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_meta_visibility_policy ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + schema_version_id BIGINT NOT NULL, + ui_visible BOOLEAN NOT NULL DEFAULT TRUE, + ai_context BOOLEAN NOT NULL DEFAULT FALSE, + user_editable BOOLEAN NOT NULL DEFAULT TRUE, + user_searchable BOOLEAN NOT NULL DEFAULT FALSE, + exportable BOOLEAN NOT NULL DEFAULT FALSE, + policy_snapshot JSONB, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_muse_meta_vis_policy_ver ON muse_meta_visibility_policy(tenant_id, schema_version_id); +CREATE TRIGGER trg_muse_meta_vis_policy_updated_at BEFORE UPDATE ON muse_meta_visibility_policy FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_meta_protection_node ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + node_key VARCHAR(100) NOT NULL, + display_name VARCHAR(200) NOT NULL, + owner_module VARCHAR(50) NOT NULL, + replaceable BOOLEAN NOT NULL DEFAULT FALSE, + policy_snapshot JSONB, + status VARCHAR(20) NOT NULL DEFAULT 'active', + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_meta_protection_node UNIQUE (tenant_id, node_key) +); + +CREATE TRIGGER trg_muse_meta_protection_node_updated_at BEFORE UPDATE ON muse_meta_protection_node FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_meta_function_chain ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + chain_key VARCHAR(100) NOT NULL, + active_version VARCHAR(50), + chain_snapshot JSONB, + status VARCHAR(20) NOT NULL DEFAULT 'active', + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_meta_function_chain UNIQUE (tenant_id, chain_key) +); + +CREATE TRIGGER trg_muse_meta_function_chain_updated_at BEFORE UPDATE ON muse_meta_function_chain FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/db/ddl/V5__init_knowledge_schema.sql b/db/ddl/V5__init_knowledge_schema.sql new file mode 100644 index 0000000..7f3c901 --- /dev/null +++ b/db/ddl/V5__init_knowledge_schema.sql @@ -0,0 +1,206 @@ +-- Muse Knowledge 模块初始 Schema(PostgreSQL 16) +-- 影响范围:用户/全局知识库、资料版本、实体关系、草稿、作品绑定。 + +CREATE TABLE muse_knowledge_base ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name VARCHAR(200) NOT NULL, + description VARCHAR(500), + kb_type VARCHAR(20) NOT NULL DEFAULT 'user', + owner_user_id BIGINT, + source_market_asset_id BIGINT, + status VARCHAR(20) NOT NULL DEFAULT 'active', + active_version INT NOT NULL DEFAULT 1, + visibility_policy JSONB, + license_snapshot_id BIGINT, + command_id VARCHAR(120), + revision INT NOT NULL DEFAULT 1, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_muse_kb_owner_status ON muse_knowledge_base(tenant_id, owner_user_id, status); +CREATE INDEX idx_muse_kb_type ON muse_knowledge_base(tenant_id, kb_type); +CREATE UNIQUE INDEX uk_muse_kb_command ON muse_knowledge_base(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_knowledge_base_updated_at BEFORE UPDATE ON muse_knowledge_base FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_knowledge_document ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + kb_id BIGINT NOT NULL, + title VARCHAR(200) NOT NULL, + file_name VARCHAR(500), + file_size BIGINT, + mime_type VARCHAR(100), + file_hash VARCHAR(100), + storage_ref VARCHAR(500), + scan_status VARCHAR(20) NOT NULL DEFAULT 'pending', + parse_status VARCHAR(20), + index_status VARCHAR(20), + retention_policy JSONB, + author VARCHAR(200), + source_url VARCHAR(1000), + tags JSONB, + command_id VARCHAR(120), + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_muse_kb_doc_kb ON muse_knowledge_document(tenant_id, kb_id, scan_status); +CREATE INDEX idx_muse_kb_doc_hash ON muse_knowledge_document(tenant_id, file_hash); +CREATE UNIQUE INDEX uk_muse_kb_doc_command ON muse_knowledge_document(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_knowledge_document_updated_at BEFORE UPDATE ON muse_knowledge_document FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_knowledge_document_version ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + document_id BIGINT NOT NULL, + version INT NOT NULL, + file_hash VARCHAR(100) NOT NULL, + storage_ref VARCHAR(500), + content_hash VARCHAR(100), + source_snapshot_id BIGINT, + scan_status VARCHAR(20) NOT NULL DEFAULT 'pending', + parse_status VARCHAR(20) NOT NULL DEFAULT 'pending', + processing_status VARCHAR(20) NOT NULL DEFAULT 'pending', + previous_version_id BIGINT, + change_summary JSONB, + change_reason VARCHAR(500), + command_id VARCHAR(120), + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_kb_doc_ver UNIQUE (tenant_id, document_id, version) +); + +CREATE INDEX idx_muse_kb_doc_ver_doc ON muse_knowledge_document_version(tenant_id, document_id); +CREATE TRIGGER trg_muse_kb_doc_ver_updated_at BEFORE UPDATE ON muse_knowledge_document_version FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_knowledge_entity ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + kb_id BIGINT, + entity_type VARCHAR(50) NOT NULL, + normalized_name VARCHAR(200) NOT NULL, + scope VARCHAR(50) NOT NULL DEFAULT 'global', + description TEXT, + attributes JSONB, + source_snapshot_id BIGINT, + authorization_snapshot_id BIGINT, + source_status VARCHAR(30) NOT NULL DEFAULT 'active', + source_action_policy VARCHAR(30) NOT NULL DEFAULT 'allowed', + source_event_version INT, + lineage_payload JSONB, + status VARCHAR(20) NOT NULL DEFAULT 'active', + confidence DOUBLE PRECISION DEFAULT 1.0, + source_type VARCHAR(50), + source_id BIGINT, + command_id VARCHAR(120), + revision INT NOT NULL DEFAULT 1, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_knowledge_entity UNIQUE (tenant_id, work_id, entity_type, normalized_name, scope) +); + +CREATE INDEX idx_muse_knowledge_entity_work_type ON muse_knowledge_entity(tenant_id, work_id, entity_type); +CREATE INDEX idx_muse_knowledge_entity_kb ON muse_knowledge_entity(tenant_id, kb_id); +CREATE UNIQUE INDEX uk_muse_knowledge_entity_command ON muse_knowledge_entity(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_knowledge_entity_updated_at BEFORE UPDATE ON muse_knowledge_entity FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_knowledge_relation ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + source_entity_id BIGINT NOT NULL, + target_entity_id BIGINT NOT NULL, + relation_type VARCHAR(50) NOT NULL, + description VARCHAR(500), + attributes JSONB, + source_status VARCHAR(30) NOT NULL DEFAULT 'active', + source_action_policy VARCHAR(30) NOT NULL DEFAULT 'allowed', + command_id VARCHAR(120), + revision INT NOT NULL DEFAULT 1, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_muse_knowledge_rel_work ON muse_knowledge_relation(tenant_id, work_id); +CREATE INDEX idx_muse_knowledge_rel_src ON muse_knowledge_relation(tenant_id, source_entity_id); +CREATE INDEX idx_muse_knowledge_rel_tgt ON muse_knowledge_relation(tenant_id, target_entity_id); +CREATE UNIQUE INDEX uk_muse_knowledge_rel_command ON muse_knowledge_relation(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_knowledge_relation_updated_at BEFORE UPDATE ON muse_knowledge_relation FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_knowledge_draft ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + entity_id BIGINT, + draft_type VARCHAR(30) NOT NULL DEFAULT 'entity', + target_object_id BIGINT, + proposed_changes JSONB, + draft_payload JSONB, + current_canonical_snapshot JSONB, + source_snapshot_id BIGINT, + authorization_snapshot_id BIGINT, + risk_marker_snapshot JSONB, + source_status VARCHAR(30), + source_action_policy VARCHAR(30), + status VARCHAR(20) NOT NULL DEFAULT 'pending', + confidence DOUBLE PRECISION, + source_type VARCHAR(50), + source_id BIGINT, + command_id VARCHAR(120), + revision INT NOT NULL DEFAULT 1, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_muse_knowledge_draft_work_status ON muse_knowledge_draft(tenant_id, work_id, status, create_time); +CREATE INDEX idx_muse_knowledge_draft_entity ON muse_knowledge_draft(tenant_id, entity_id); +CREATE UNIQUE INDEX uk_muse_knowledge_draft_command ON muse_knowledge_draft(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_knowledge_draft_updated_at BEFORE UPDATE ON muse_knowledge_draft FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); + +CREATE TABLE muse_knowledge_binding ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + work_id BIGINT NOT NULL, + kb_id BIGINT NOT NULL, + binding_type VARCHAR(50) NOT NULL DEFAULT 'read', + binding_scope VARCHAR(50) NOT NULL DEFAULT 'read', + binding_status VARCHAR(30) NOT NULL DEFAULT 'active', + source_snapshot_id BIGINT, + authorization_snapshot_id BIGINT, + source_version INT, + target_version INT, + command_id VARCHAR(120), + revision INT NOT NULL DEFAULT 1, + creator VARCHAR(64) NOT NULL DEFAULT '', + create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updater VARCHAR(64) NOT NULL DEFAULT '', + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + deleted BOOLEAN NOT NULL DEFAULT FALSE, + tenant_id BIGINT NOT NULL DEFAULT 0, + CONSTRAINT uk_muse_knowledge_binding_work_kb UNIQUE (tenant_id, work_id, kb_id) +); + +CREATE INDEX idx_muse_knowledge_binding_work ON muse_knowledge_binding(tenant_id, work_id); +CREATE INDEX idx_muse_knowledge_binding_kb ON muse_knowledge_binding(tenant_id, kb_id); +CREATE UNIQUE INDEX uk_muse_knowledge_binding_command ON muse_knowledge_binding(tenant_id, command_id) WHERE command_id IS NOT NULL; +CREATE TRIGGER trg_muse_knowledge_binding_updated_at BEFORE UPDATE ON muse_knowledge_binding FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); diff --git a/db/表映射.md b/db/表映射.md new file mode 100644 index 0000000..a549014 --- /dev/null +++ b/db/表映射.md @@ -0,0 +1,56 @@ +# muse-example 表 ↔ 主仓迁移来源映射(A2 审查面) + +> 口径(创始人拍板③ 2026-07-10):主仓表**原样不改列**;实验私货全进 `example_*` 前缀。 +> 建表方式:`db/ddl/` 下文件经 db skill `apply`,主仓部分为 `muse-cloud/sql/muse/` 原文拷贝或逐字摘录。 +> 应用顺序:V1 → V3 → V5 → 90-ALTER摘录 → V26 → 91-example。已于 2026-07-13 应用,共 **24 张表**。 + +## 主仓一致表(20 张) + +| 表 | 来源迁移 | 叠加的 ALTER | 实验角色 | +|---|---|---|---| +| muse_content_work | V1 | — | 参考书/未来新作的作品行(B1 导入) | +| muse_content_chapter | V1 | V26(order 唯一键改 partial) | 分章行(B1 导入) | +| muse_content_block | V1 | V26(同上) | 章正文载体(一章一 block,content_text) | +| muse_content_block_source_attribution | V1 | V30(authorization_snapshot_id→varchar(128)) | 暂空置;C 线来源归因 | +| muse_content_import_task | V1 | — | B1 导入任务审计 | +| muse_content_export_task | V1 | V9(+4 列) | 暂空置;C9 导出 | +| muse_content_command_log | V1 | — | 写命令幂等审计(import/confirm 用) | +| muse_meta_schema | V3 | — | 23 型结构本体行(A3 入库) | +| muse_meta_schema_version | V3 | V10(active 唯一索引) | 型版本(A3:每型 v1 active) | +| muse_meta_field | V3 | — | 逐型字段合同行(A3 入库) | +| muse_meta_visibility_policy | V3 | — | aiContext 控制项落点(A3 入库) | +| muse_meta_protection_node | V3 | V10(+6 列) | 保护节点登记(read-context/confirm/judge) | +| muse_meta_function_chain | V3 | — | 功能链登记(meta/chains 的库内影子,随需入库) | +| muse_knowledge_base | V5 | — | 私有参考书库 + 公共范式库(kb 行) | +| muse_knowledge_document | V5 | — | 参考书原文档案指针(storage_ref 指仓内路径) | +| muse_knowledge_document_version | V5 | — | 暂空置 | +| muse_knowledge_entity | V5 | — | **已确认**知识行(B5 确认后落此) | +| muse_knowledge_relation | V5 | — | 知识行间关系(二轮再启用) | +| muse_knowledge_draft | V5 | V14(两快照列→varchar(128)) | **草稿**知识行(B2 拆书产出落此) | +| muse_knowledge_binding | V5 | V14(同上) | 作品↔库绑定(C3 起用) | + +## 实验私货表(4 张,`db/ddl/91-example实验私货.sql`) + +| 表 | 用途 | +|---|---| +| example_reference_work | 参考书档案(reference_work 型的库内实例载体):作者/章数对账/拆书范围与状态 | +| example_parse_task | 拆书任务状态机(一章一行,scaffold/pattern 两阶段状态)——workflow 断点续跑与幂等 | +| example_parse_scaffold | 逐章脚手架:逆推细纲(3–5%)+ 实体清单 JSONB | +| example_knowledge_embedding | 知识行嵌入边表:vector(1024)+HNSW(余弦),content_hash 幂等,挂 draft 或 entity | + +## 暂缓建表登记(主仓有、实验现阶段未建;需要时按原样加建) + +| 迁移 | 表 | 暂缓原因 | +|---|---|---| +| V9 | muse_content_planning_section / governance_action | C2 规划确认时加建 planning_section | +| V10 | meta 治理 8 表(command/audit/validation/impact/gray/chain_version/chain_slot/chain_node) | 治理命令流实验不镜像;装配入库随阶段二 | +| V14 | knowledge RAGFlow/治理 14 表 | 实验用 pgvector 自持嵌入,不接 RAGFlow | +| V18/V21 | 两张 event_publish_outbox | 事件出箱不镜像 | +| V25/V28/V32/V36/V37 | field_snapshot / asset_use_precheck / block_revision_snapshot / ai_generated_fulltext / task 批次列 | 对应场景(用量投影/市场/版本快照/SSE 瞬态)不在实验闭环内 | + +## 实验写入约定 + +- `tenant_id=1`、`creator='1'`、`owner_user_id=1`(系统主账号)——写入层统一给值;列默认仍照主仓(0/''),不改列。 +- 公共范式的全局知识行:`work_id=0` + `scope='global'` + 挂 `kb_type='global'` 的库;参考书私有库 `kb_type='user'`。 +- 软删照主仓:`deleted=TRUE`,不物理删。 +- 双轨对应:B2 产出全落 `muse_knowledge_draft(status='pending')`;B5 管理员确认(仅创始人触发)= draft 翻 `confirmed` + 落 `muse_knowledge_entity(status='active')`;丢弃= draft 翻 `ignored`。