# 灵越智报 2.0 数据库设计文档 > **基于 2026-02-11 会议讨论设计(完整版)** > > **使用模型**: Claude Opus 4.5 > > **最后更新**: 2026-02-11 > > **负责人**: 何文松(后端接口+Mock数据) --- ## 目录 1. [设计理念](#1-设计理念) 2. [业务流程与数据模型](#2-业务流程与数据模型) 3. [核心概念](#3-核心概念) 4. [表结构总览](#4-表结构总览) 5. [表详细设计](#5-表详细设计) 6. [视图设计](#6-视图设计) 7. [索引设计](#7-索引设计) 8. [核心操作流程](#8-核心操作流程) --- ## 1. 设计理念 ### 1.1 图数据库思想在关系型数据库中的实现 > **来源**: 2026-02-11 会议 - 吕强讲解 传统做法的问题: - 安源项目采用"一个报告类型一张表"的方式,导致有 **140 张表**还没建完 - 每次有新报告类型,就需要新建表,表数量**无限膨胀** - 维护成本高,扩展性差 **我们的解决方案**: 将图数据库的核心概念(节点 Node、关系 Relationship)平铺到关系型数据库中: ``` ┌─────────────────────────────────────────────────────────────┐ │ 图数据库核心概念 │ ├─────────────────────────────────────────────────────────────┤ │ 节点 (Node) → 圆圈圈,代表实体 │ │ 关系 (Edge) → 连线,描述节点之间的关联 │ │ 属性 (Property) → 节点或关系上的附加信息 │ └─────────────────────────────────────────────────────────────┘ ↓ 平铺到关系型数据库 ↓ ┌─────────────────────────────────────────────────────────────┐ │ 关系型数据库实现 │ ├─────────────────────────────────────────────────────────────┤ │ nodes 表 → 存储所有节点(实体) │ │ edges 表 → 存储所有关系 │ │ properties 表 → 存储节点/关系的属性 │ │ 视图 (View) → 组合出业务接口 │ └─────────────────────────────────────────────────────────────┘ ``` ### 1.2 设计优势 | 优势 | 说明 | |------|------| | **表结构稳定** | 不管有多少种报告类型,底层表结构不变 | | **扩展性强** | 新增报告类型只需新增数据,不需要改表结构 | | **AI 友好** | 结构化的关系描述,AI 容易理解和操作 | | **查询灵活** | 通过视图组合出任意业务接口 | | **实体复用** | 同一个实体(如手机号)不管出现在哪里,只存一份 | ### 1.3 读写分离 - **写入**: 通过基础表(nodes、edges、properties) - **读取**: 通过视图(View)组合出业务接口 - **好处**: 一个视图对应一个前端接口,底层表结构不用改 --- ## 2. 业务流程与数据模型 ### 2.1 用户使用流程 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ 用户使用流程 │ └─────────────────────────────────────────────────────────────────────────────┘ ┌───────────────────────────────────────────────────────┐ │ 1. 用户上传文件 │ │ • 样本文档(人工整理好的报告) │ │ • 附件(可选,可同时上传多个) │ └──────────────────────────┬────────────────────────────┘ │ ┌──────────────┴──────────────┐ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ 2. 解析样本文档 │ │ 3. 解析附件 │ │ 识别要素结构 │ │ 提取实体(NER) │ └────────┬─────────┘ └────────┬─────────┘ │ │ └──────────────┬──────────────┘ │ ▼ ┌───────────────────────────────────────────────────────┐ │ 4. 要素提取 + 规则提取(现阶段 Mock) │ │ • 从样本中提取动态要素定义 │ │ • 从附件实体中推荐规则配置 │ └──────────────────────────┬────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────┐ │ 5. 页面确认 │ │ • 确认要素定义 │ │ • 确认/编辑规则配置 │ │ • 【用户可从附件实体中添加规则】 │ │ • 确认后形成【模板】 │ └──────────────────────────┬────────────────────────────┘ │ ┌──────────────┴──────────────┐ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ 6. 规则执行 │ │ 7. 复制到新项目 │ │ 填充要素值 │ │ 复用模板+规则 │ └────────┬─────────┘ └────────┬─────────┘ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ 8. 生成文档 │ │ 新报告实例 │ │ 导出下载 │ │ (重新上传附件) │ └──────────────────┘ └──────────────────┘ ``` **关键说明**: - **附件可与样本同时上传**:用户在上传样本文档时,可以同时上传相关附件 - **用户可从附件添加规则**:附件解析后,用户可以在页面上选择附件中的实体,配置规则来填充动态要素 ### 2.2 三层数据模型 基于业务流程,我们设计了 **三层数据模型**: ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ 三层数据模型 │ └─────────────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────┐ │ 第一层:原始文件 (SOURCE_FILE) │ │ ───────────────────────────── │ │ • 用户上传的样本文档 │ │ • 存储原文内容、文档结构 │ │ • 作为溯源和重新解析的依据 │ │ • 生命周期:上传后基本不变 │ └─────────────────────────────────────────────────────────────────────────┘ │ │ PARSED_TO(解析生成) ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 第二层:模板 (TEMPLATE) │ │ ───────────────────────── │ │ • 从样本中提取的"规则骨架" │ │ • 包含:要素定义、规则配置、占位符、DSL │ │ • 【可复用】- 一个模板可生成多个报告 │ │ • 生命周期:确认后相对稳定 │ └─────────────────────────────────────────────────────────────────────────┘ │ │ INSTANCE_OF(实例化) ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 第三层:报告 (REPORT) │ │ ───────────────────────── │ │ • 基于模板生成的项目实例 │ │ • 包含:要素值、附件、生成结果 │ │ • 每个项目一份,可独立编辑 │ │ • 生命周期:每个项目不同,可编辑 │ └─────────────────────────────────────────────────────────────────────────┘ ``` ### 2.3 为什么要分三层? | 问题 | 如果不分层 | 分层后 | |------|-----------|--------| | **复用** | 复制时需要复制整个记录,清空要素值,保留规则,逻辑混乱 | 模板独立存在,报告只需引用模板 | | **多项目** | 无法表达"一个模板对应多个报告"的关系 | 清晰的一对多关系 | | **溯源** | 无法追溯原始样本 | 原始文件保留,可重新解析 | | **维护** | 修改模板会影响所有报告 | 模板和报告独立,互不影响 | ### 2.4 各层存储内容 | 层次 | node_type | 存储内容 | 关联的子节点 | |------|-----------|---------|-------------| | **原始文件** | `SOURCE_FILE` | 原文HTML、文档结构、上传信息 | - | | **模板** | `TEMPLATE` | 模板名称、分类、描述 | `ELEMENT`(要素定义) | | **报告** | `REPORT` | 报告标题、状态、项目关联 | `VALUE`(要素值)、`ATTACHMENT`(附件)、`RULE`(规则) | ### 2.5 实体提取与规则添加 用户可以从两个来源提取实体并添加规则: 1. **样本文件(本文/原文)**:用户上传的样本文档本身 2. **附件**:与样本相关的参考文件 #### 2.5.1 样本文件的实体提取 样本文件解析后,系统会提取其中的实体,用户可以直接从本文中选择实体绑定到动态要素: ``` 原始文件 (SOURCE_FILE) │ │ HAS_ENTITY(本文实体) ▼ 实体 (ENTITY) ←── 从样本文件中提取的实体 │ │ INPUT_FROM(被规则引用) ▼ 规则 (RULE) ←── 规则可以引用本文中的实体作为输入 ``` #### 2.5.2 附件的实体提取 附件可以在两个时机上传: 1. **与样本文件同时上传**:用户上传样本时可同时上传附件 2. **后续单独上传**:在报告编辑过程中随时上传 ``` 报告 (REPORT) │ │ HAS_ATTACHMENT ▼ 附件 (ATTACHMENT) ←── 解析状态、解析文本 │ │ HAS_ENTITY(附件实体) ▼ 实体 (ENTITY) ←── 从附件中提取的实体 │ │ INPUT_FROM(被规则引用) ▼ 规则 (RULE) ←── 规则可以引用附件中的实体作为输入 ``` #### 2.5.3 实体解析流程 **样本文件解析**: 1. 用户上传样本文件 2. 系统解析样本内容 3. 提取样本中的实体(NER) 4. 页面展示本文实体列表 **附件解析**: 1. 用户上传附件(可与样本同时上传,或后续单独上传) 2. 系统解析附件内容 3. 提取附件中的实体(NER) 4. 页面展示附件实体列表 #### 2.5.4 用户从实体添加规则 用户可以从**本文**或**附件**中选择实体,绑定到动态要素: 1. 用户在页面上查看实体列表(本文实体 / 附件实体) 2. 选择某个实体(如"中国电建集团成都勘测设计研究院有限公司") 3. 将该实体绑定到某个动态要素(如"评审对象") 4. 系统自动创建规则,配置 INPUT_FROM 关系指向该实体 5. 执行规则时,直接使用该实体的值填充要素 ``` 用户操作示例: ┌─────────────────────────────────────────────────────────────────────────┐ │ 📄 本文:成都院复审报告样本.docx │ │ ──────────────────────────── │ │ 识别的实体: │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ ☑ 中国电建集团成都勘测设计研究院有限公司 [ORG] │ │ │ │ └─ [添加规则] → 绑定到要素:评审对象 │ │ │ │ ☐ 成都院 [ORG] │ │ │ │ ☑ BZ-0092-2024 [CODE] │ │ │ │ └─ [添加规则] → 绑定到要素:项目编号 │ │ │ └───────────────────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────────┤ │ 📎 附件:复审通知.docx │ │ ───────────────────── │ │ 识别的实体: │ │ ┌───────────────────────────────────────────────────────────────┐ │ │ │ ☐ 何彦锋 [PERSON] │ │ │ │ ☑ 2024年7月13日 [DATE] │ │ │ │ └─ [添加规则] → 绑定到要素:评审开始日期 │ │ │ │ ☑ 2024年10月17日 [DATE] │ │ │ │ └─ [添加规则] → 绑定到要素:评审结束日期 │ │ │ │ ☐ 93.33 [NUMBER] │ │ │ └───────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────┘ ``` --- ## 3. 核心概念 ### 3.1 节点类型 (Node Types) | 类型 | 所属层 | 说明 | 示例 | |------|--------|------|------| | `SOURCE_FILE` | 第一层 | 用户上传的原始样本文件 | 成都院复审报告样本.docx | | `TEMPLATE` | 第二层 | 报告模板(规则骨架) | 电力安全生产标准化复审报告模板 | | `ELEMENT` | 第二层 | 动态要素定义(属于模板) | 项目编号、评审对象 | | `REPORT` | 第三层 | 报告实例(属于项目) | 成都院复审报告 | | `VALUE` | 第三层 | 要素值(属于报告) | "BZ-0092-2024" | | `RULE` | 第三层 | 规则实例(属于报告) | 项目编号提取规则 | | `ATTACHMENT` | 第三层 | 附件(属于报告) | 复审通知.docx | | `ENTITY` | 跨层 | 识别实体(属于样本文件或附件) | 中国电建集团成都勘测设计研究院有限公司 | | `USER` | 基础 | 用户 | 管理员 | | `PROJECT` | 基础 | 项目 | 电力安全评审项目 | > **注意**:`ENTITY` 是跨层的,可以从 `SOURCE_FILE`(本文)或 `ATTACHMENT`(附件)中提取,通过 `HAS_ENTITY` 关系关联。 ### 3.2 关系类型 (Edge Types) | 关系 | 说明 | 方向 | |------|------|------| | `PARSED_TO` | 原始文件解析生成模板 | 原始文件 → 模板 | | `HAS_ELEMENT` | 模板包含要素定义 | 模板 → 要素定义 | | `INSTANCE_OF` | 报告基于模板 | 报告 → 模板 | | `HAS_VALUE` | 报告有要素值 | 报告 → 要素值 | | `HAS_RULE` | 报告有规则 | 报告 → 规则 | | `FOR_ELEMENT` | 规则/值对应的要素 | 规则/值 → 要素定义 | | `HAS_ATTACHMENT` | 报告有附件 | 报告 → 附件 | | `HAS_ENTITY` | 文件有实体(本文或附件) | 原始文件/附件 → 实体 | | `INPUT_FROM` | 规则输入来源 | 规则 → 原始文件/附件/实体/要素值 | | `COPIED_FROM` | 复制来源 | 报告 → 源报告 | | `BELONGS_TO` | 归属关系 | 报告 → 项目 | | `CREATED_BY` | 创建者 | 节点 → 用户 | ### 3.3 动态要素类型 | 类型 | 占位符格式 | 说明 | |------|-----------|------| | `text` | `{{namespace.field}}` | 短文本 | | `paragraph` | `{{namespace.field}}` | 长文本/段落 | | `table` | `{{+tableName}}` | 表格数据 | ### 3.4 实体类型 | 类型 | 说明 | 示例 | |------|------|------| | `ORG` | 组织机构 | 中国电建集团成都勘测设计研究院有限公司 | | `PERSON` | 人名 | 何彦锋 | | `DATE` | 日期 | 2024年7月13日 | | `NUMBER` | 数值 | 93.33 | | `LOCATION` | 地址 | 成都市温江区政和街8号 | ### 3.5 完整关系图 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ 节点与关系完整图 │ └─────────────────────────────────────────────────────────────────────────────┘ ┌─────────┐ │ USER │ └────┬────┘ │ CREATED_BY ▼ ┌─────────────┐ PARSED_TO ┌─────────────┐ HAS_ELEMENT ┌─────────────┐ │ SOURCE_FILE │─────────────▶│ TEMPLATE │──────────────▶│ ELEMENT │ │ (原始文件) │ │ (模板) │ │ (要素定义) │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ HAS_ENTITY │ INSTANCE_OF │ FOR_ELEMENT │ (本文实体) ▼ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ REPORT │ ┌─────────────┐ │ │ │ PROJECT │◀───│ (报告) │───▶│ PROJECT │ │ │ │ (项目) │ └──────┬──────┘ └─────────────┘ │ │ └─────────────┘ │ BELONGS_TO │ │ │ │ │ ┌──────────────┼──────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ VALUE │ │ RULE │ │ATTACHMENT │ │ │ │ (要素值) │ │ (规则) │ │ (附件) │ │ │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ │ │ │ │ │ │ │ HAS_ENTITY │ │ │ │ │ (附件实体) │ │ │ │ ▼ │ │ │ │ ┌───────────┐ │ └────────────┼──────────────┼───────▶│ ENTITY │ │ │ │ │ (实体) │ │ │ │ └─────┬─────┘ │ │ │ │ │ │ │◀─────────────┘ │ │ │ INPUT_FROM │ │◀─────────────┘ │ │ INPUT_FROM │ │ │ └─────────────────────────────────────────────┘ FOR_ELEMENT 说明: - SOURCE_FILE(本文)和 ATTACHMENT(附件)都可以通过 HAS_ENTITY 关联到 ENTITY - RULE 可以通过 INPUT_FROM 引用来自本文或附件的实体 ``` --- ## 4. 表结构总览 ### 4.1 核心图结构表(与业务弱关联) | 表名 | 说明 | |------|------| | `nodes` | 节点表 - 存储所有实体 | | `edges` | 关系表 - 存储节点间关系 | | `node_properties` | 节点属性表 | | `edge_properties` | 关系属性表 | ### 4.2 业务辅助表 | 表名 | 说明 | |------|------| | `node_types` | 节点类型定义 | | `edge_types` | 关系类型定义 | | `property_definitions` | 属性定义 | ### 4.3 表关系图 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ 核心图结构 │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ node_types │ │ edge_types │ │ │ │ (节点类型) │ │ (关系类型) │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ nodes │◄────────│ edges │ │ │ │ (节点) │ │ (关系) │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │node_properties│ │edge_properties│ │ │ │ (节点属性) │ │ (关系属性) │ │ │ └──────────────┘ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ 通过视图组合 ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 业务视图层 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ v_templates │ │ v_reports │ │ v_elements │ │ v_rules │ │ │ │ (模板视图) │ │ (报告视图) │ │ (要素视图) │ │ (规则视图) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │v_attachments│ │ v_entities │ │v_rule_config│ │ │ │ (附件视图) │ │ (实体视图) │ │(规则配置视图)│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ ``` --- ## 5. 表详细设计 ### 5.1 node_types - 节点类型定义表 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | id | SERIAL | PRIMARY KEY | 类型ID | | type_code | VARCHAR(50) | NOT NULL, UNIQUE | 类型编码 | | type_name | VARCHAR(100) | NOT NULL | 类型名称 | | description | TEXT | | 描述 | | icon | VARCHAR(100) | | 图标 | | color | VARCHAR(20) | | 颜色 | | created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 | ### 5.2 edge_types - 关系类型定义表 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | id | SERIAL | PRIMARY KEY | 类型ID | | type_code | VARCHAR(50) | NOT NULL, UNIQUE | 类型编码 | | type_name | VARCHAR(100) | NOT NULL | 类型名称 | | from_node_type | VARCHAR(50) | | 起始节点类型 | | to_node_type | VARCHAR(50) | | 目标节点类型 | | description | TEXT | | 描述 | | created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 | ### 5.3 nodes - 节点表(核心) | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | id | BIGSERIAL | PRIMARY KEY | 节点ID | | node_type | VARCHAR(50) | NOT NULL | 节点类型 | | node_key | VARCHAR(200) | | 节点唯一标识(业务键) | | name | VARCHAR(500) | NOT NULL | 节点名称 | | status | VARCHAR(50) | DEFAULT 'active' | 状态 | | created_by | BIGINT | | 创建人ID | | created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 | | updated_at | TIMESTAMP | DEFAULT NOW() | 更新时间 | **唯一约束**: (node_type, node_key) - 同类型下 node_key 唯一 ### 5.4 edges - 关系表(核心) | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | id | BIGSERIAL | PRIMARY KEY | 关系ID | | edge_type | VARCHAR(50) | NOT NULL | 关系类型 | | from_node_id | BIGINT | NOT NULL, FK | 起始节点ID | | to_node_id | BIGINT | NOT NULL, FK | 目标节点ID | | sort_order | INT | DEFAULT 0 | 排序 | | created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 | **唯一约束**: (edge_type, from_node_id, to_node_id) - 防止重复关系 ### 5.5 node_properties - 节点属性表 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | id | BIGSERIAL | PRIMARY KEY | 属性ID | | node_id | BIGINT | NOT NULL, FK | 节点ID | | prop_key | VARCHAR(100) | NOT NULL | 属性键 | | prop_value | TEXT | | 文本值 | | prop_json | JSONB | | JSON值 | | prop_number | DECIMAL(20,4) | | 数值 | | prop_date | TIMESTAMP | | 日期值 | | created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 | | updated_at | TIMESTAMP | DEFAULT NOW() | 更新时间 | **唯一约束**: (node_id, prop_key) - 同节点下属性键唯一 ### 5.6 edge_properties - 关系属性表 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | id | BIGSERIAL | PRIMARY KEY | 属性ID | | edge_id | BIGINT | NOT NULL, FK | 关系ID | | prop_key | VARCHAR(100) | NOT NULL | 属性键 | | prop_value | TEXT | | 文本值 | | prop_json | JSONB | | JSON值 | | created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 | **唯一约束**: (edge_id, prop_key) ### 5.7 property_definitions - 属性定义表 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | id | SERIAL | PRIMARY KEY | 定义ID | | owner_type | VARCHAR(20) | NOT NULL | 所属类型: node/edge | | target_type | VARCHAR(50) | NOT NULL | 目标节点/关系类型 | | prop_key | VARCHAR(100) | NOT NULL | 属性键 | | prop_name | VARCHAR(200) | NOT NULL | 属性名称 | | data_type | VARCHAR(50) | NOT NULL | 数据类型: text/json/number/date | | required | BOOLEAN | DEFAULT false | 是否必填 | | default_value | TEXT | | 默认值 | | description | TEXT | | 描述 | | created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 | --- ## 6. 视图设计 > **设计原则**: 一个视图对应一个前端接口,底层表结构不用改 ### 6.1 v_source_files - 原始文件视图 ```sql CREATE OR REPLACE VIEW v_source_files AS SELECT n.id, n.name AS file_name, n.node_key AS file_key, n.status, n.created_at, n.updated_at, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'original_name') AS original_name, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'file_path') AS file_path, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'file_type') AS file_type, (SELECT prop_number FROM node_properties WHERE node_id = n.id AND prop_key = 'file_size')::bigint AS file_size, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'content_html') AS content_html, (SELECT prop_json FROM node_properties WHERE node_id = n.id AND prop_key = 'content_json') AS content_json, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'parse_status') AS parse_status, (SELECT prop_date FROM node_properties WHERE node_id = n.id AND prop_key = 'parsed_at') AS parsed_at, -- 生成的模板 (SELECT n2.id FROM edges e JOIN nodes n2 ON n2.id = e.to_node_id WHERE e.from_node_id = n.id AND e.edge_type = 'PARSED_TO' LIMIT 1) AS template_id, -- 创建人 n.created_by, (SELECT n2.name FROM nodes n2 WHERE n2.id = n.created_by) AS created_by_name FROM nodes n WHERE n.node_type = 'SOURCE_FILE'; ``` ### 6.2 v_templates - 模板视图 ```sql CREATE OR REPLACE VIEW v_templates AS SELECT n.id, n.name, n.node_key AS template_code, n.status, n.created_at, n.updated_at, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'category') AS category, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'description') AS description, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'content_html') AS content_html, (SELECT prop_json FROM node_properties WHERE node_id = n.id AND prop_key = 'content_json') AS content_json, (SELECT COUNT(*) FROM edges e WHERE e.from_node_id = n.id AND e.edge_type = 'HAS_ELEMENT') AS element_count, (SELECT n2.name FROM nodes n2 WHERE n2.id = n.created_by) AS created_by_name FROM nodes n WHERE n.node_type = 'TEMPLATE'; ``` ### 6.3 v_reports - 报告视图 ```sql CREATE OR REPLACE VIEW v_reports AS SELECT n.id, n.name AS title, n.node_key AS report_code, n.status, n.created_at, n.updated_at, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'report_type') AS report_type, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'content_html') AS content_html, -- 模板信息 (SELECT n2.id FROM edges e JOIN nodes n2 ON n2.id = e.to_node_id WHERE e.from_node_id = n.id AND e.edge_type = 'INSTANCE_OF' LIMIT 1) AS template_id, (SELECT n2.name FROM edges e JOIN nodes n2 ON n2.id = e.to_node_id WHERE e.from_node_id = n.id AND e.edge_type = 'INSTANCE_OF' LIMIT 1) AS template_name, -- 项目信息 (SELECT n2.id FROM edges e JOIN nodes n2 ON n2.id = e.to_node_id WHERE e.from_node_id = n.id AND e.edge_type = 'BELONGS_TO' AND n2.node_type = 'PROJECT' LIMIT 1) AS project_id, (SELECT n2.name FROM edges e JOIN nodes n2 ON n2.id = e.to_node_id WHERE e.from_node_id = n.id AND e.edge_type = 'BELONGS_TO' AND n2.node_type = 'PROJECT' LIMIT 1) AS project_name, -- 复制来源 (SELECT n2.id FROM edges e JOIN nodes n2 ON n2.id = e.to_node_id WHERE e.from_node_id = n.id AND e.edge_type = 'COPIED_FROM' LIMIT 1) AS source_report_id, -- 统计 (SELECT COUNT(*) FROM edges e WHERE e.from_node_id = n.id AND e.edge_type = 'HAS_ATTACHMENT') AS attachment_count, -- 创建人 n.created_by, (SELECT n2.name FROM nodes n2 WHERE n2.id = n.created_by) AS created_by_name FROM nodes n WHERE n.node_type = 'REPORT'; ``` ### 6.4 v_template_elements - 模板动态要素视图 ```sql CREATE OR REPLACE VIEW v_template_elements AS SELECT n.id, n.name AS element_name, n.node_key AS element_key, n.created_at, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'element_type') AS element_type, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'namespace') AS namespace, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'field_name') AS field_name, (SELECT prop_json FROM node_properties WHERE node_id = n.id AND prop_key = 'table_columns') AS table_columns, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'required')::boolean AS required, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'default_value') AS default_value, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'description') AS description, -- 所属模板 (SELECT e.from_node_id FROM edges e WHERE e.to_node_id = n.id AND e.edge_type = 'HAS_ELEMENT' LIMIT 1) AS template_id, (SELECT e.sort_order FROM edges e WHERE e.to_node_id = n.id AND e.edge_type = 'HAS_ELEMENT' LIMIT 1) AS sort_order FROM nodes n WHERE n.node_type = 'ELEMENT'; ``` ### 6.5 v_report_element_values - 报告要素值视图 ```sql CREATE OR REPLACE VIEW v_report_element_values AS SELECT n.id AS value_id, n.node_key AS element_key, n.created_at, n.updated_at, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'value_text') AS value_text, (SELECT prop_json FROM node_properties WHERE node_id = n.id AND prop_key = 'value_json') AS value_json, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'is_filled')::boolean AS is_filled, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'fill_source') AS fill_source, -- 所属报告 (SELECT e.from_node_id FROM edges e WHERE e.to_node_id = n.id AND e.edge_type = 'HAS_VALUE' LIMIT 1) AS report_id FROM nodes n WHERE n.node_type = 'VALUE'; ``` ### 6.6 v_attachments - 附件视图 ```sql CREATE OR REPLACE VIEW v_attachments AS SELECT n.id, n.name AS display_name, n.node_key AS file_key, n.created_at, n.updated_at, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'file_name') AS file_name, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'file_path') AS file_path, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'file_type') AS file_type, (SELECT prop_number FROM node_properties WHERE node_id = n.id AND prop_key = 'file_size')::bigint AS file_size, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'parse_status') AS parse_status, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'parsed_text') AS parsed_text, (SELECT prop_date FROM node_properties WHERE node_id = n.id AND prop_key = 'parsed_at') AS parsed_at, -- 实体数量 (SELECT COUNT(*) FROM edges e WHERE e.from_node_id = n.id AND e.edge_type = 'HAS_ENTITY') AS entity_count, -- 所属报告 (SELECT e.from_node_id FROM edges e WHERE e.to_node_id = n.id AND e.edge_type = 'HAS_ATTACHMENT' LIMIT 1) AS report_id, (SELECT e.sort_order FROM edges e WHERE e.to_node_id = n.id AND e.edge_type = 'HAS_ATTACHMENT' LIMIT 1) AS sort_order FROM nodes n WHERE n.node_type = 'ATTACHMENT'; ``` ### 6.7 v_entities - 实体视图 ```sql CREATE OR REPLACE VIEW v_entities AS SELECT n.id, n.name AS entity_text, n.node_key AS entity_key, n.created_at, n.updated_at, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'entity_type') AS entity_type, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'business_label') AS business_label, (SELECT prop_number FROM node_properties WHERE node_id = n.id AND prop_key = 'confidence') AS confidence, (SELECT prop_number FROM node_properties WHERE node_id = n.id AND prop_key = 'occurrence_count')::int AS occurrence_count, -- 所属附件 (SELECT e.from_node_id FROM edges e WHERE e.to_node_id = n.id AND e.edge_type = 'HAS_ENTITY' LIMIT 1) AS attachment_id FROM nodes n WHERE n.node_type = 'ENTITY'; ``` ### 6.8 v_rules - 规则视图 ```sql CREATE OR REPLACE VIEW v_rules AS SELECT n.id, n.name AS rule_name, n.node_key AS element_key, n.status, n.created_at, n.updated_at, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'description') AS description, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'rule_type') AS rule_type, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'action_type') AS action_type, (SELECT prop_json FROM node_properties WHERE node_id = n.id AND prop_key = 'action_config') AS action_config, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'dsl_content') AS dsl_content, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'last_output_text') AS last_output_text, (SELECT prop_json FROM node_properties WHERE node_id = n.id AND prop_key = 'last_output_json') AS last_output_json, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'last_run_status') AS last_run_status, (SELECT prop_date FROM node_properties WHERE node_id = n.id AND prop_key = 'last_run_time') AS last_run_time, (SELECT prop_value FROM node_properties WHERE node_id = n.id AND prop_key = 'last_run_error') AS last_run_error, -- 所属报告 (SELECT e.from_node_id FROM edges e WHERE e.to_node_id = n.id AND e.edge_type = 'HAS_RULE' LIMIT 1) AS report_id FROM nodes n WHERE n.node_type = 'RULE'; ``` ### 6.9 v_rule_inputs - 规则输入视图 ```sql CREATE OR REPLACE VIEW v_rule_inputs AS SELECT e.id AS input_id, e.from_node_id AS rule_id, e.to_node_id AS source_node_id, e.sort_order, (SELECT prop_value FROM edge_properties WHERE edge_id = e.id AND prop_key = 'input_key') AS input_key, (SELECT prop_value FROM edge_properties WHERE edge_id = e.id AND prop_key = 'input_name') AS input_name, (SELECT prop_value FROM edge_properties WHERE edge_id = e.id AND prop_key = 'input_type') AS input_type, (SELECT prop_value FROM edge_properties WHERE edge_id = e.id AND prop_key = 'fixed_value') AS fixed_value, -- 来源节点信息 n.node_type AS source_type, n.name AS source_name FROM edges e JOIN nodes n ON n.id = e.to_node_id WHERE e.edge_type = 'INPUT_FROM'; ``` ### 6.10 v_report_full - 报告完整视图(聚合) ```sql CREATE OR REPLACE VIEW v_report_full AS SELECT r.*, -- 动态要素填充统计 (SELECT COUNT(*) FROM v_report_element_values v WHERE v.report_id = r.id AND v.is_filled = true) AS filled_count, (SELECT COUNT(*) FROM v_template_elements te WHERE te.template_id = r.template_id) AS total_elements, -- 规则统计 (SELECT COUNT(*) FROM v_rules ru WHERE ru.report_id = r.id) AS rule_count, (SELECT COUNT(*) FROM v_rules ru WHERE ru.report_id = r.id AND ru.last_run_status = 'success') AS success_rule_count FROM v_reports r; ``` --- ## 7. 索引设计 ### 7.1 nodes 表索引 ```sql -- 类型索引(最常用) CREATE INDEX idx_nodes_type ON nodes(node_type); -- 类型+状态组合索引 CREATE INDEX idx_nodes_type_status ON nodes(node_type, status); -- 业务键索引 CREATE INDEX idx_nodes_key ON nodes(node_key); -- 类型+业务键唯一索引 CREATE UNIQUE INDEX idx_nodes_type_key ON nodes(node_type, node_key) WHERE node_key IS NOT NULL; -- 创建人索引 CREATE INDEX idx_nodes_created_by ON nodes(created_by); -- 创建时间索引 CREATE INDEX idx_nodes_created_at ON nodes(created_at DESC); ``` ### 7.2 edges 表索引 ```sql -- 关系类型索引 CREATE INDEX idx_edges_type ON edges(edge_type); -- 起始节点索引 CREATE INDEX idx_edges_from ON edges(from_node_id); -- 目标节点索引 CREATE INDEX idx_edges_to ON edges(to_node_id); -- 类型+起始节点组合索引(查询某节点的特定关系) CREATE INDEX idx_edges_type_from ON edges(edge_type, from_node_id); -- 类型+目标节点组合索引(反向查询) CREATE INDEX idx_edges_type_to ON edges(edge_type, to_node_id); -- 防止重复关系 CREATE UNIQUE INDEX idx_edges_unique ON edges(edge_type, from_node_id, to_node_id); ``` ### 7.3 node_properties 表索引 ```sql -- 节点ID索引 CREATE INDEX idx_node_props_node ON node_properties(node_id); -- 节点+属性键组合索引 CREATE UNIQUE INDEX idx_node_props_unique ON node_properties(node_id, prop_key); -- 属性键索引(用于跨节点查询特定属性) CREATE INDEX idx_node_props_key ON node_properties(prop_key); ``` ### 7.4 edge_properties 表索引 ```sql -- 关系ID索引 CREATE INDEX idx_edge_props_edge ON edge_properties(edge_id); -- 关系+属性键组合索引 CREATE UNIQUE INDEX idx_edge_props_unique ON edge_properties(edge_id, prop_key); ``` --- ## 8. 核心操作流程 ### 8.1 上传文件并生成模板(样本+附件可同时上传) ``` 操作:用户上传样本文件(可同时上传附件) → 系统解析 → 生成模板 步骤: 1. 创建 SOURCE_FILE 节点(样本文件) 2. 解析样本文件,提取要素定义 3. 创建 TEMPLATE 节点 4. 创建 PARSED_TO 关系(SOURCE_FILE → TEMPLATE) 5. 为每个要素定义创建 ELEMENT 节点 6. 创建 HAS_ELEMENT 关系(TEMPLATE → ELEMENT) 7. 【可选】同时上传附件: a. 创建 ATTACHMENT 节点 b. 创建 HAS_ATTACHMENT 关系(关联到后续创建的 REPORT) c. 解析附件,提取实体 d. 创建 ENTITY 节点 e. 创建 HAS_ENTITY 关系(ATTACHMENT → ENTITY) ``` **说明**:附件可以在上传样本时同时上传,系统会先解析附件并提取实体,这样用户在确认要素和规则时,可以直接从附件实体中选择绑定。 **SQL 示例**: ```sql -- 1. 创建原始文件节点 INSERT INTO nodes (node_type, node_key, name, created_by) VALUES ('SOURCE_FILE', 'SF-001', '成都院复审报告样本.docx', 1) RETURNING id; -- 假设返回 id = 100 -- 2. 添加原始文件属性 INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (100, 'original_name', '成都院复审报告样本.docx'), (100, 'file_path', '/uploads/2026/02/SF-001.docx'), (100, 'file_type', 'docx'), (100, 'parse_status', 'completed'); -- 3. 创建模板节点 INSERT INTO nodes (node_type, node_key, name, created_by) VALUES ('TEMPLATE', 'TPL-001', '电力安全生产标准化复审报告模板', 1) RETURNING id; -- 假设返回 id = 101 -- 4. 创建 PARSED_TO 关系 INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('PARSED_TO', 100, 101); -- 5. 创建要素定义节点 INSERT INTO nodes (node_type, node_key, name) VALUES ('ELEMENT', 'basicInfo.projectCode', '项目编号') RETURNING id; -- 假设返回 id = 102 -- 6. 添加要素定义属性 INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (102, 'element_type', 'text'), (102, 'namespace', 'basicInfo'), (102, 'field_name', 'projectCode'), (102, 'required', 'true'); -- 7. 创建 HAS_ELEMENT 关系 INSERT INTO edges (edge_type, from_node_id, to_node_id, sort_order) VALUES ('HAS_ELEMENT', 101, 102, 1); ``` ### 8.2 基于模板创建报告 ``` 操作:用户选择模板 → 创建报告 → 关联项目 步骤: 1. 创建 REPORT 节点 2. 创建 INSTANCE_OF 关系(REPORT → TEMPLATE) 3. 创建 BELONGS_TO 关系(REPORT → PROJECT) 4. 为每个要素定义创建空的 VALUE 节点 5. 创建 HAS_VALUE 关系(REPORT → VALUE) 6. 创建 FOR_ELEMENT 关系(VALUE → ELEMENT) ``` **SQL 示例**: ```sql -- 1. 创建报告节点 INSERT INTO nodes (node_type, node_key, name, status, created_by) VALUES ('REPORT', 'RPT-001', '成都院2024年复审报告', 'draft', 1) RETURNING id; -- 假设返回 id = 200 -- 2. 创建 INSTANCE_OF 关系(报告 → 模板) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('INSTANCE_OF', 200, 101); -- 3. 创建 BELONGS_TO 关系(报告 → 项目) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('BELONGS_TO', 200, 1); -- 假设项目ID为1 -- 4. 为每个要素创建空的 VALUE 节点 INSERT INTO nodes (node_type, node_key, name) VALUES ('VALUE', 'RPT-001:basicInfo.projectCode', '项目编号值') RETURNING id; -- 假设返回 id = 201 -- 5. 添加 VALUE 属性(初始为空) INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (201, 'is_filled', 'false'); -- 6. 创建 HAS_VALUE 关系(报告 → 值) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('HAS_VALUE', 200, 201); -- 7. 创建 FOR_ELEMENT 关系(值 → 要素定义) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('FOR_ELEMENT', 201, 102); ``` ### 8.3 复制报告到新项目 ``` 操作:用户选择已有报告 → 复制到新项目 复制内容: ✓ 模板引用(INSTANCE_OF) ✓ 规则配置(RULE 节点及其 INPUT_FROM 关系结构) ✗ 要素值(VALUE 节点清空,需重新填充) ✗ 附件(ATTACHMENT 节点不复制,需重新上传) ✗ 实体(ENTITY 节点不复制) 步骤: 1. 创建新的 REPORT 节点 2. 复制 INSTANCE_OF 关系(指向同一模板) 3. 创建 BELONGS_TO 关系(指向新项目) 4. 创建 COPIED_FROM 关系(新报告 → 源报告) 5. 为每个要素创建空的 VALUE 节点 6. 复制 RULE 节点(复制规则配置,但清空执行结果) 7. 复制 INPUT_FROM 关系结构(但引用需要重新绑定) ``` **SQL 示例**: ```sql -- 1. 创建新报告节点 INSERT INTO nodes (node_type, node_key, name, status, created_by) VALUES ('REPORT', 'RPT-002', '华东院2024年复审报告', 'draft', 1) RETURNING id; -- 假设返回 id = 300 -- 2. 获取源报告的模板ID SELECT to_node_id FROM edges WHERE from_node_id = 200 AND edge_type = 'INSTANCE_OF'; -- 返回 101 -- 3. 创建 INSTANCE_OF 关系(指向同一模板) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('INSTANCE_OF', 300, 101); -- 4. 创建 BELONGS_TO 关系(指向新项目) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('BELONGS_TO', 300, 2); -- 假设新项目ID为2 -- 5. 创建 COPIED_FROM 关系(溯源) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('COPIED_FROM', 300, 200); -- 6. 复制规则节点(清空执行结果) INSERT INTO nodes (node_type, node_key, name, status) SELECT 'RULE', REPLACE(node_key, 'RPT-001', 'RPT-002'), name, 'active' FROM nodes WHERE node_type = 'RULE' AND id IN (SELECT to_node_id FROM edges WHERE from_node_id = 200 AND edge_type = 'HAS_RULE'); -- 7. 复制规则属性(保留配置,清空结果) INSERT INTO node_properties (node_id, prop_key, prop_value, prop_json) SELECT new_rule.id, np.prop_key, CASE WHEN np.prop_key IN ('last_output_text', 'last_run_status', 'last_run_error') THEN NULL ELSE np.prop_value END, CASE WHEN np.prop_key = 'last_output_json' THEN NULL ELSE np.prop_json END FROM node_properties np JOIN nodes old_rule ON old_rule.id = np.node_id AND old_rule.node_type = 'RULE' JOIN nodes new_rule ON new_rule.node_key = REPLACE(old_rule.node_key, 'RPT-001', 'RPT-002') WHERE old_rule.id IN (SELECT to_node_id FROM edges WHERE from_node_id = 200 AND edge_type = 'HAS_RULE'); ``` ### 8.4 上传附件并解析实体 ``` 操作:用户上传附件 → 系统解析 → 提取实体 步骤: 1. 创建 ATTACHMENT 节点 2. 创建 HAS_ATTACHMENT 关系(REPORT → ATTACHMENT) 3. 解析附件内容 4. 提取实体(NER) 5. 为每个实体创建 ENTITY 节点(全局去重) 6. 创建 HAS_ENTITY 关系(ATTACHMENT → ENTITY) ``` **SQL 示例**: ```sql -- 1. 创建附件节点 INSERT INTO nodes (node_type, node_key, name) VALUES ('ATTACHMENT', 'ATT-001', '01-复审通知') RETURNING id; -- 假设返回 id = 400 -- 2. 添加附件属性 INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (400, 'file_name', '复审通知.docx'), (400, 'file_path', '/uploads/2026/02/复审通知.docx'), (400, 'file_type', 'docx'), (400, 'parse_status', 'completed'); -- 3. 创建 HAS_ATTACHMENT 关系 INSERT INTO edges (edge_type, from_node_id, to_node_id, sort_order) VALUES ('HAS_ATTACHMENT', 200, 400, 1); -- 4. 创建实体节点(全局去重,使用 ON CONFLICT) INSERT INTO nodes (node_type, node_key, name) VALUES ('ENTITY', 'ORG:中国电建集团成都勘测设计研究院有限公司', '中国电建集团成都勘测设计研究院有限公司') ON CONFLICT (node_type, node_key) DO UPDATE SET updated_at = NOW() RETURNING id; -- 假设返回 id = 500 -- 5. 添加实体属性 INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (500, 'entity_type', 'ORG'), (500, 'business_label', '评审对象') ON CONFLICT (node_id, prop_key) DO UPDATE SET prop_value = EXCLUDED.prop_value; -- 6. 创建 HAS_ENTITY 关系 INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('HAS_ENTITY', 400, 500) ON CONFLICT DO NOTHING; ``` ### 8.5 用户从附件实体添加规则 ``` 操作:用户在页面上选择附件中的实体 → 绑定到动态要素 → 自动创建规则 场景:用户查看附件解析出的实体列表,选择某个实体直接绑定到要素 步骤: 1. 用户选择实体(如"中国电建集团成都勘测设计研究院有限公司") 2. 用户选择要绑定的动态要素(如"评审对象") 3. 系统自动创建 RULE 节点(规则类型为 direct_entity) 4. 创建 HAS_RULE 关系(REPORT → RULE) 5. 创建 FOR_ELEMENT 关系(RULE → ELEMENT) 6. 创建 INPUT_FROM 关系(RULE → ENTITY) 7. 执行规则,直接使用实体值填充要素 ``` **SQL 示例**: ```sql -- 假设: -- 报告ID = 200 -- 要素定义ID = 102(评审对象) -- 实体ID = 500(中国电建集团成都勘测设计研究院有限公司) -- 1. 创建规则节点(直接引用实体类型) INSERT INTO nodes (node_type, node_key, name, status) VALUES ('RULE', 'RPT-001:project.reviewObject', '评审对象-直接引用实体', 'active') RETURNING id; -- 假设返回 id = 610 -- 2. 添加规则属性(规则类型为 direct_entity,表示直接使用实体值) INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (610, 'rule_type', 'direct_entity'), (610, 'action_type', 'use_entity_value'), (610, 'description', '直接使用附件实体值填充要素'); -- 3. 创建 HAS_RULE 关系(报告 → 规则) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('HAS_RULE', 200, 610); -- 4. 创建 FOR_ELEMENT 关系(规则 → 要素定义) INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('FOR_ELEMENT', 610, 102); -- 5. 创建 INPUT_FROM 关系(规则 → 实体) INSERT INTO edges (edge_type, from_node_id, to_node_id, sort_order) VALUES ('INPUT_FROM', 610, 500, 1); -- 6. 添加输入属性 INSERT INTO edge_properties (edge_id, prop_key, prop_value) SELECT id, 'input_key', 'entity' FROM edges WHERE edge_type = 'INPUT_FROM' AND from_node_id = 610 AND to_node_id = 500; INSERT INTO edge_properties (edge_id, prop_key, prop_value) SELECT id, 'input_type', 'entity_ref' FROM edges WHERE edge_type = 'INPUT_FROM' AND from_node_id = 610 AND to_node_id = 500; -- 7. 执行规则:直接使用实体的 name 作为要素值 -- 获取实体值 SELECT name FROM nodes WHERE id = 500; -- 返回 '中国电建集团成都勘测设计研究院有限公司' -- 8. 更新要素值节点 UPDATE node_properties SET prop_value = '中国电建集团成都勘测设计研究院有限公司', updated_at = NOW() WHERE node_id = (SELECT id FROM nodes WHERE node_key = 'RPT-001:project.reviewObject' AND node_type = 'VALUE') AND prop_key = 'value_text'; ``` ### 8.6 配置复杂规则并执行 ``` 操作:用户配置复杂规则(正则提取/LLM生成等) → 绑定输入 → 执行规则 → 填充要素值 步骤: 1. 创建 RULE 节点 2. 创建 HAS_RULE 关系(REPORT → RULE) 3. 创建 FOR_ELEMENT 关系(RULE → ELEMENT) 4. 配置规则输入(创建 INPUT_FROM 关系,可引用附件/实体/其他要素值) 5. 执行规则,获取结果 6. 更新 VALUE 节点的值 ``` **SQL 示例**: ```sql -- 1. 创建规则节点 INSERT INTO nodes (node_type, node_key, name, status) VALUES ('RULE', 'RPT-001:basicInfo.projectCode', '项目编号提取规则', 'active') RETURNING id; -- 假设返回 id = 600 -- 2. 添加规则属性 INSERT INTO node_properties (node_id, prop_key, prop_value, prop_json) VALUES (600, 'rule_type', 'extraction', NULL), (600, 'action_type', 'extract_pattern', NULL), (600, 'action_config', NULL, '{"pattern": "项目编号[::]\\s*(\\S+)", "group": 1}'), (600, 'dsl_content', 'EXTRACT pattern="项目编号[::]\\s*(\\S+)" FROM input1', NULL); -- 3. 创建 HAS_RULE 关系 INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('HAS_RULE', 200, 600); -- 4. 创建 FOR_ELEMENT 关系 INSERT INTO edges (edge_type, from_node_id, to_node_id) VALUES ('FOR_ELEMENT', 600, 102); -- 5. 配置规则输入(引用附件) INSERT INTO edges (edge_type, from_node_id, to_node_id, sort_order) VALUES ('INPUT_FROM', 600, 400, 1); -- 6. 添加输入属性 INSERT INTO edge_properties (edge_id, prop_key, prop_value) SELECT id, 'input_key', 'input1' FROM edges WHERE edge_type = 'INPUT_FROM' AND from_node_id = 600 AND to_node_id = 400; -- 7. 执行规则后,更新 VALUE 节点 UPDATE node_properties SET prop_value = 'BZ-0092-2024', updated_at = NOW() WHERE node_id = 201 AND prop_key = 'value_text'; UPDATE node_properties SET prop_value = 'true', updated_at = NOW() WHERE node_id = 201 AND prop_key = 'is_filled'; INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (201, 'fill_source', 'rule') ON CONFLICT (node_id, prop_key) DO UPDATE SET prop_value = EXCLUDED.prop_value; -- 8. 更新规则执行状态 UPDATE node_properties SET prop_value = 'BZ-0092-2024', updated_at = NOW() WHERE node_id = 600 AND prop_key = 'last_output_text'; INSERT INTO node_properties (node_id, prop_key, prop_value) VALUES (600, 'last_run_status', 'success') ON CONFLICT (node_id, prop_key) DO UPDATE SET prop_value = EXCLUDED.prop_value; ``` --- ## 附录 ### A. 与传统设计对比 | 维度 | 传统设计(安源) | 图结构设计(灵越) | |------|----------------|------------------| | 表数量 | 140+ 张,持续增长 | 4 张核心表,固定不变 | | 新增报告类型 | 需要新建表 | 只需新增数据 | | 实体复用 | 每张表独立存储 | 全局唯一,引用ID | | AI 操作 | 需要针对每张表编码 | 统一的图结构,易于理解 | | 查询灵活性 | 需要修改表结构 | 通过视图组合 | ### B. 变更记录 | 日期 | 版本 | 变更内容 | 作者 | |-----|------|---------|------| | 2026-02-11 | 1.0 | 初始版本,基于会议讨论的图数据库思想设计 | Claude Opus 4.5 | | 2026-02-11 | 1.1 | 增加三层数据模型(原始文件→模板→报告)、业务流程、核心操作流程 | Claude Opus 4.5 | | 2026-02-11 | 1.2 | 补充附件与样本同时上传、用户从附件添加规则的流程 | Claude Opus 4.5 | | 2026-02-11 | 1.3 | 补充本文(样本文件)也可以提取实体并添加规则 | Claude Opus 4.5 |