Files
Cloud-Tour-to-Libo/docs/关系数据库与通用数据平台技术方案.md

31 KiB
Raw Blame History

关系数据库与通用数据平台技术方案

适用平台:旅行知识图谱管理系统 / 云游荔波及后续多项目
方案状态:拟实施
核心结论PostgreSQL 作为权威业务数据源FalkorDB 作为只读图谱投影;建设元数据驱动的通用数据管理后台。

1. 背景与问题

当前平台已经同时部署 PostgreSQL 与 FalkorDB

  • PostgreSQL 保存项目、账号权限、采集批次、原始记录、候选实体、审核记录、发布任务等管理数据。
  • FalkorDB 保存最终实体、关系、路线、空间片区和图谱浏览数据。
  • 管理后台已有项目工作区、数据采集、审核入藏、图谱发布、知识广场等模块。
  • 云游荔波已经形成酒店、美食、景区、交通、公交线路、公交站、片区等真实业务数据。

当前主要问题不是缺少数据库,而是两个数据库的职责尚未清晰:

  1. 最终业务详情过度依赖 FalkorDB 节点属性。
  2. 酒店房型、设施、评论、团购等一对多数据不适合塞入图节点。
  3. 一些采集脚本直接双写 PostgreSQL 与 FalkorDB失败时可能产生数据漂移。
  4. 当前后台偏向候选实体和图谱审核,缺少正式业务数据的通用增删改查。
  5. 不同项目未来会拥有不同数据表,为每个项目编写固定后台不可持续。
  6. 导入、导出、变更历史、恢复和项目隔离需要形成统一机制。
  7. 数据库 DDL 目前主要由应用启动时的 CREATE TABLE IF NOT EXISTS 管理,不适合后续复杂演进。

因此,本方案不新增第三套数据库,也不推翻现有图谱,而是在现有双数据库基础上重新划分数据职责。

2. 建设目标

2.1 目标

  • PostgreSQL 成为酒店、美食、景区、交通等业务数据的唯一权威数据源。
  • FalkorDB 保留实体关系、空间关系、路线关系和 ToB 图谱展示能力。
  • 建设可服务多个项目的通用数据管理后台。
  • 通用后台通过元数据自动生成列表、表单、筛选、校验和导入导出能力。
  • 所有数据修改先进入 PostgreSQL再通过可靠事件同步到 FalkorDB。
  • 保留高德、携程、大众点评等每一条来源记录及字段级溯源。
  • 支持软删除、恢复、版本控制、操作审计和批次回滚。
  • 保留现有荔波地图、详情侧栏、公交线路和图谱浏览器。

2.2 非目标

  • 不把通用后台建设成可执行任意 SQL 的数据库管理器。
  • 不允许普通用户直接创建、删除任意 PostgreSQL 物理表。
  • 不把评论、价格方案、实时房态等全部建成图节点。
  • 不采用 PostgreSQL 与 FalkorDB 的双向同步。
  • 不允许 FalkorDB 成为可独立修改的第二权威数据源。
  • 第一阶段不替换现有项目业务页面,只替换其底层数据来源。

3. 架构决策

ADR-01PostgreSQL 是 System of Record

所有正式业务数据、审核结果、来源明细、评论、房型、设施、套餐和修改记录,以 PostgreSQL 为准。

ADR-02FalkorDB 是派生图谱

FalkorDB 只保存适合关系查询和图谱展示的节点、边及少量检索属性。图谱可以重建,不单独承担数据恢复责任。

ADR-03采用元数据驱动的通用后台

后台只允许管理注册到平台的数据模型。字段类型、表单、列表列、校验、权限、导入模板和图谱映射均由元数据控制。

ADR-04采用关系字段与 JSONB 混合模型

  • 稳定、高频筛选、需要唯一约束或关联的字段使用正式关系型列。
  • 项目临时扩展、低频使用的字段存入 extra_data JSONB
  • 稳定后的扩展字段通过迁移提升为正式列。
  • 不采用纯 EAV也不把所有业务字段全部放进 JSONB。

ADR-05使用事务 Outbox 同步图谱

业务写入和图谱事件在同一个 PostgreSQL 事务提交。独立 Worker 异步、幂等地更新 FalkorDB。

ADR-06默认软删除

业务数据默认只允许软删除。永久删除需要高级权限、依赖检查和二次确认。

4. 目标架构

flowchart TB
    subgraph Sources["数据来源"]
        A1["高德 API / CSV"]
        A2["携程"]
        A3["大众点评"]
        A4["公交 Excel"]
        A5["人工录入"]
    end

    subgraph Platform["FastAPI 数据平台"]
        B1["导入暂存与校验"]
        B2["实体融合与审核"]
        B3["通用 CRUD 服务"]
        B4["导出任务"]
        B5["Outbox Worker"]
        B6["统一查询聚合"]
    end

    subgraph PG["PostgreSQL 16 + PostGIS"]
        C1["元数据与权限"]
        C2["标准实体与业务扩展表"]
        C3["来源、证据与审核"]
        C4["空间与路线缓存"]
        C5["审计、版本、导入导出任务"]
        C6["图谱同步 Outbox"]
    end

    subgraph Graph["FalkorDB"]
        D1["实体节点"]
        D2["片区与行政区"]
        D3["公交、景区、分类关系"]
        D4["图谱发布版本"]
    end

    subgraph UI["React 管理后台"]
        E1["通用数据中心"]
        E2["项目业务页面"]
        E3["图谱浏览器"]
    end

    Sources --> B1
    B1 --> B2
    B2 --> C2
    B3 --> C2
    B4 --> C2
    C2 --> C6
    C6 --> B5
    B5 --> Graph
    C2 --> B6
    Graph --> B6
    B3 --> E1
    B6 --> E2
    Graph --> E3

5. 数据分层与职责

数据层 PostgreSQL FalkorDB
项目、租户、权限 权威保存 不保存
数据模型与字段配置 权威保存 不保存
正式业务实体 完整保存 保存核心节点投影
酒店房型、报价、设施、政策 完整保存 默认不建节点
美食套餐、菜系、营业信息 完整保存 默认不建节点
评论、评价标签 完整保存 可选同步汇总标签
多来源原始记录 完整保存 默认不保存
融合、审核、版本历史 完整保存 只保存最终结果
坐标、行政区、H3 片区 完整保存并支持空间查询 建立空间关系
景区、子景点、分类关系 完整保存 建立图关系
公交线路与站序 完整保存 建立线路图关系
地图列表与详情 主要读取 补充关系
图谱查询和 ToB 展示 辅助 主要读取
CSV/XLSX 数据交付 唯一导出来源 只导出图节点和边

6. PostgreSQL 技术基线

6.1 数据库组件

  • PostgreSQL 16。
  • PostGIS 3.x用于点、范围、行政边界和距离查询。
  • pg_trgm,用于规范化名称相似检索。
  • pgcrypto 或 PostgreSQL UUID 能力,用于稳定 ID。
  • Alembic负责版本化数据库迁移。
  • psycopg 连接池继续复用。

当前 Docker 使用 postgres:16,目标实施时应切换到带 PostGIS 的兼容镜像,或在受控镜像中安装 PostGIS。现有 scripts/sql/001_kg_core_spatial_schema.sql 已声明 PostGIS 依赖,但运行环境需要同步补齐。

6.2 Schema 边界

建议继续使用一个平台 Schema例如 kg_admin_new2,通过 tenant_id + project_id 进行逻辑隔离。后续租户规模扩大后,再评估独立 Schema 或独立数据库。

所有平台注册业务表必须至少包含:

tenant_id
project_id
id / entity_id
status
version
created_at
created_by
updated_at
updated_by
deleted_at
deleted_by
delete_reason
extra_data

6.3 主键与业务唯一键

  • 关系数据库主键UUID entity_id
  • 兼容现有图谱:保留 natural_key
  • 图谱节点统一写入 entity_idnatural_key
  • 外部来源唯一键:tenant_id + project_id + source_code + source_type + external_id
  • 合并后废弃的 ID 进入重定向表,不能立即删除。

7. 元数据驱动的通用数据后台

7.1 元数据表

建议新增:

作用
data_models 注册后台可管理的数据模型及物理表
data_fields 字段类型、显示、校验、筛选和图谱属性配置
data_model_relations 模型间一对一、一对多、多对多关系
data_dictionaries 字典定义
data_dictionary_items 字典选项
data_views 用户保存的列、筛选、排序视图
data_validation_rules 可复用校验规则
data_model_permissions 模型级和操作级权限
graph_mappings 模型到节点、属性和边的映射

7.2 data_models 核心字段

model_id
tenant_id
project_id
model_code
display_name
physical_table
primary_key
title_field
description
model_kind
map_enabled
graph_enabled
import_enabled
export_enabled
soft_delete_enabled
status
created_at
updated_at

physical_table 必须来自服务器允许列表,不能由前端传递任意表名。

7.3 data_fields 核心字段

field_id
model_id
field_code
physical_column
display_name
data_type
required
unique_enabled
searchable
sortable
editable
visible_in_list
visible_in_detail
importable
exportable
dictionary_code
validation_jsonb
ui_jsonb
graph_property
display_order
status

支持的数据类型至少包括:

  • 单行文本、多行文本。
  • 整数、小数、金额、百分比。
  • 布尔值。
  • 日期、时间、日期时间。
  • 单选、多选、字典。
  • URL、图片 URL、电话。
  • 经纬度、空间点。
  • JSON。
  • 外键、关联列表。

7.4 通用后台页面

建议在主菜单增加“业务数据中心”:

  1. 数据模型。
  2. 数据表。
  3. 数据导入。
  4. 数据导出。
  5. 数据质量。
  6. 修改历史。
  7. 图谱同步。

数据表页面自动支持:

  • 服务端分页和游标分页。
  • 多字段筛选。
  • 模糊搜索。
  • 排序和自定义列。
  • 固定列与列宽。
  • 新增、详情、编辑。
  • 批量修改。
  • 批量软删除与恢复。
  • 保存查询视图。
  • 查看关联数据。
  • 查看来源与证据。
  • 查看图谱节点。
  • 重新同步图谱。

7.5 安全边界

通用后台不开放:

  • 任意 SQL。
  • 任意 Schema 或物理表访问。
  • 系统表修改。
  • 直接删除物理表。
  • 任意修改主键或列类型。
  • 绕过项目范围查询。
  • 直接修改 FalkorDB。

逻辑增加字段由元数据服务受控执行;需要创建正式物理列时,由 Alembic 迁移完成。

8. 正式业务数据模型

8.1 公共实体层

business_entities

保存跨项目、跨类型通用字段:

entity_id UUID PK
tenant_id
project_id
entity_type
natural_key
name
normalized_name
description
category_code
status
confidence
longitude
latitude
geom geometry(Point, 4326)
address
district
adcode
h3_r9
h3_r10
phone
extra_data JSONB
version
created_at / created_by
updated_at / updated_by
deleted_at / deleted_by / delete_reason

关键约束:

UNIQUE (tenant_id, project_id, natural_key)
CHECK (longitude BETWEEN -180 AND 180)
CHECK (latitude BETWEEN -90 AND 90)

entity_source_records

保存高德、携程、大众点评等来源记录:

source_record_id
tenant_id
project_id
entity_id
source_code
source_type
external_id
source_url
source_name
raw_data JSONB
normalized_data JSONB
match_confidence
match_rule
review_status
collected_at
effective_at
expired_at

entity_field_values

仅用于字段级溯源和融合结果,不替代正式业务列:

entity_id
field_code
final_value JSONB
selected_source_record_id
selection_rule
confidence
reviewed_by
reviewed_at

其他公共表

  • entity_aliases
  • entity_images
  • entity_categories
  • entity_relations
  • entity_merge_records
  • entity_redirects
  • entity_versions

8.2 酒店

  • hotel_profiles
  • hotel_room_types
  • hotel_room_offers
  • hotel_facilities
  • hotel_policies

房型、报价和设施必须是一对多明细,不再拼接成长字符串保存在图节点。

实时性较强的“仅剩几间”等信息默认不进入长期知识库;如业务需要保留,只能写入带 observed_atexpires_at 的报价快照。

8.3 美食

  • restaurant_profiles
  • restaurant_deals
  • restaurant_cuisines
  • restaurant_business_hours

团购套餐独立保存名称、图片、现价、原价、折扣、规则和采集时间,不把多个套餐塞入单个字段。

8.4 景区

  • scenic_profiles
  • scenic_children
  • scenic_levels
  • scenic_ticket_policies

明确区分:

  • 独立景区。
  • 景区子景点。
  • 纪念馆、博物馆等旅游目的地。
  • 不应入库的广场、商业设施等异常候选。

8.5 交通

  • transport_stops
  • bus_routes
  • bus_route_directions
  • bus_route_stops
  • route_geometries

站序表至少保存:

route_direction_id
stop_id
stop_order
arrival_offset
distance_from_previous

8.6 评论与标签

酒店与美食共用:

  • reviews
  • review_tags
  • entity_review_tags

评论保存来源、评分、正文、采集时间、匿名用户标识和原始记录关联。评价摘要由 PostgreSQL 聚合生成,不把“样本数、平均分”硬编码进图谱。

9. 空间数据设计

现有 kg_place_spatialkg_geo_cellskg_route_metrics 可以增量复用。

目标空间结构:

业务实体
→ PostGIS Point
→ H3 R9/R10
→ GeoCell前端显示为“片区”
→ Area荔波县

PostgreSQL 负责:

  • 荔波县边界内判断。
  • 地图视窗范围查询。
  • 半径检索。
  • H3 片区归属。
  • 路线距离缓存。
  • 导入时坐标异常检查。

FalkorDB 负责投影:

Entity -[IN_H3_R9]-> GeoCell
GeoCell -[LOCATED_IN]-> Area
Entity -[LOCATED_IN]-> Area

GeoCell 只是技术标签,管理后台和图例统一显示“片区”。

10. 通用 CRUD API

统一前缀建议为:

/v1/admin/data-platform

10.1 模型接口

GET    /models
GET    /models/{model_code}
POST   /models
PATCH  /models/{model_code}
GET    /models/{model_code}/fields
POST   /models/{model_code}/fields
PATCH  /models/{model_code}/fields/{field_code}

普通数据管理员无权修改物理表映射。

10.2 记录接口

GET    /models/{model_code}/records
POST   /models/{model_code}/records
GET    /models/{model_code}/records/{record_id}
PATCH  /models/{model_code}/records/{record_id}
DELETE /models/{model_code}/records/{record_id}
POST   /models/{model_code}/records/{record_id}/restore
POST   /models/{model_code}/records/bulk-update
POST   /models/{model_code}/records/bulk-delete
GET    /models/{model_code}/records/{record_id}/history
GET    /models/{model_code}/records/{record_id}/relations

10.3 查询协议

禁止接收原始 SQL。统一使用安全查询 DSL

{
  "filters": [
    {"field": "status", "operator": "eq", "value": "active"},
    {"field": "name", "operator": "contains", "value": "酒店"}
  ],
  "sort": [{"field": "updated_at", "direction": "desc"}],
  "page_size": 50,
  "cursor": null
}

后端只能使用 data_fields.searchable=true 的字段生成参数化 SQL。

10.4 并发控制

所有修改携带版本:

If-Match: 12

SQL 更新条件:

WHERE entity_id = :id AND version = :expected_version

更新成功后 version + 1。版本冲突返回 HTTP 409并提供当前记录。

11. 删除、恢复与版本

11.1 默认软删除

删除动作写入:

status = deleted
deleted_at
deleted_by
delete_reason
version = version + 1

同时写入图谱删除事件。FalkorDB 删除节点前先删除或改写关联边。

11.2 恢复

恢复前重新检查:

  • 唯一键是否被其他实体占用。
  • 外部来源记录是否仍然有效。
  • 关联实体是否存在。
  • 图谱映射是否仍然启用。

11.3 永久删除

仅系统管理员可执行,并要求:

  • 依赖检查。
  • 二次确认。
  • 审计记录。
  • 数据备份或快照。
  • 明确不可恢复提示。

12. 批量导入

12.1 导入状态机

stateDiagram-v2
    [*] --> uploaded
    uploaded --> mapping
    mapping --> validating
    validating --> preview_ready
    preview_ready --> committing
    committing --> graph_syncing
    graph_syncing --> completed
    validating --> failed
    committing --> failed
    failed --> validating
    completed --> rolled_back

12.2 导入表

在现有 import_templatesmapping_profilesimport_batchesraw_records 基础上补充:

  • import_jobs
  • import_files
  • import_staging_rows
  • import_validation_errors
  • import_mutations
  • import_job_events

现有 candidate_entitiescandidate_relations 继续承担候选审核层,不直接替代正式实体表。

12.3 导入步骤

  1. 选择项目和数据模型。
  2. 下载元数据自动生成的 CSV/XLSX 模板。
  3. 上传文件并计算 SHA-256。
  4. 自动识别表头和字符编码。
  5. 用户确认字段映射。
  6. 暂存原始行,不直接写正式表。
  7. 校验字段类型、必填、字典、外键、坐标和行政区。
  8. 执行来源 ID、自然键、名称地址和空间距离去重。
  9. 预览新增、更新、跳过、冲突和失败数量。
  10. 用户确认后写入正式表。
  11. 同事务写入 Outbox。
  12. 后台同步图谱并记录任务状态。

12.4 导入模式

模式 行为 默认权限
append 只新增,不修改已有记录 数据管理员
upsert 按唯一标识新增或更新 数据管理员,推荐默认
snapshot 文件作为完整快照,可停用缺失记录 项目管理员

12.5 幂等与去重

优先级:

entity_id
→ source_code + source_external_id
→ 高德 POI ID
→ natural_key
→ 规范化名称 + 地址
→ 名称相似度 + 地址相似度 + 坐标距离

同一文件哈希、同一模型和同一导入模式重复提交时,默认返回已有任务,不重复写入。

12.6 回滚

每个正式写入动作记录在 import_mutations

insert / update / soft_delete
record_id
before_jsonb
after_jsonb

回滚不是数据库全量恢复,而是按批次生成反向业务变更,并再次产生图谱同步事件。

13. 批量导出

13.1 导出能力

  • 导出当前表。
  • 导出当前筛选结果。
  • 导出勾选记录。
  • 导出主表及关联明细。
  • 导出完整项目数据包。
  • 导出图谱节点与边。
  • 支持 CSV、XLSX、ZIP。
  • 大任务异步执行。

13.2 导出任务表

  • export_jobs
  • export_job_files
  • export_job_events

任务保存:

tenant_id
project_id
model_code
filter_jsonb
field_list
include_relations
format
status
row_count
file_path / object_key
sha256
created_by
expires_at

13.3 云游荔波完整导出结构

云游荔波数据导出_YYYYMMDD_HHMMSS.zip
├── manifest.json
├── 数据字典.csv
├── 酒店
│   ├── 酒店实体.csv
│   ├── 酒店来源记录.csv
│   ├── 房型.csv
│   ├── 报价方案.csv
│   ├── 服务设施.csv
│   ├── 评论.csv
│   └── 评价标签.csv
├── 美食
│   ├── 美食实体.csv
│   ├── 美食来源记录.csv
│   ├── 团购套餐.csv
│   ├── 评论.csv
│   └── 评价标签.csv
├── 景区
│   ├── 景区实体.csv
│   ├── 子景点.csv
│   └── 景区关系.csv
├── 交通
│   ├── 交通站点.csv
│   ├── 公交线路.csv
│   ├── 公交方向.csv
│   └── 线路站序.csv
└── 图谱
    ├── 节点.csv
    └── 关系.csv

manifest.json 至少包含项目、数据模型版本、筛选条件、导出时间、记录数和各文件哈希。

导出使用 PostgreSQL REPEATABLE READ 快照,保证同一个 ZIP 内各表数据版本一致。

14. 图谱投影与可靠同步

14.1 Outbox 表

建议新增 graph_sync_outbox

event_id UUID PK
tenant_id
project_id
aggregate_type
aggregate_id
event_type
aggregate_version
payload_version
payload JSONB
status
retry_count
next_retry_at
created_at
processed_at
last_error

事件类型至少包括:

entity.upsert
entity.delete
relation.upsert
relation.delete
model.rebuild

14.2 Worker

Worker 使用:

SELECT ... FOR UPDATE SKIP LOCKED

批量领取待处理事件,按 tenant_id + project_id + aggregate_id + version 幂等处理。

失败策略:

  • 指数退避。
  • 最大重试次数。
  • 超限进入 dead-letter 状态。
  • 管理后台支持查看错误和人工重试。

14.3 同步状态

新增 graph_sync_state

tenant_id
project_id
aggregate_type
aggregate_id
relational_version
graph_version
sync_status
last_synced_at
last_error

通用列表显示:

  • 已同步。
  • 待同步。
  • 同步中。
  • 同步失败。
  • 图谱已落后。

14.4 图谱映射

graph_mappings 配置:

model_code
node_label
node_key_field
node_title_field
property_mapping JSONB
relation_mapping JSONB
enabled
mapping_version

示例:

hotel → Hotel
restaurant → Restaurant
scenic → ScenicArea / Attraction
transport_stop → TransportStop / BusStop
bus_route → BusRoute
geo_cell → GeoCellUI 显示“片区”)
area → Area

14.5 一致性原则

  • PostgreSQL 写成功、图谱暂时失败:业务数据仍然有效,图谱显示待同步。
  • FalkorDB 不允许反向覆盖 PostgreSQL。
  • 定时执行全量对账:节点数量、关系数量、版本、孤立节点和悬空边。
  • 必须提供按项目重建图谱能力。

15. 查询与页面数据来源

场景 数据来源
通用表格列表 PostgreSQL
实体详情侧栏 PostgreSQL
酒店房型、政策、设施 PostgreSQL
美食团购、评论、标签 PostgreSQL
地图 POI 与分类统计 PostgreSQL + PostGIS
周边地点 PostgreSQL 空间查询,必要时叠加路线缓存
图谱浏览器 FalkorDB
图关系问答 FalkorDB
属性问答 PostgreSQL
混合业务问答 查询聚合服务组合两边结果
CSV/XLSX 导出 PostgreSQL

现有 http://localhost:8102/admin/plaza/overview 的视觉设计可以保持不变,只把详情、筛选和统计接口逐步切换到 PostgreSQL。

16. 权限与项目隔离

16.1 角色

角色 权限
系统管理员 所有项目、模型、迁移、永久删除
项目管理员 本项目模型配置、导入导出、批量操作
数据管理员 本项目增删改查、导入、导出
审核人员 候选审核、融合、恢复
普通用户 查询及受限导出
只读用户 仅查看

16.2 强制隔离

  • 所有请求通过 ProjectContext 获取 tenant_idproject_id
  • 后端自动拼接项目条件,禁止客户端自行指定任意租户。
  • 关键表使用组合索引和组合唯一约束。
  • 第二阶段评估 PostgreSQL Row Level Security。
  • 文件导入、导出和对象存储路径同样按租户、项目隔离。

17. 审计、质量与可观测性

17.1 操作审计

新增统一 data_change_logs

change_id
tenant_id
project_id
model_code
record_id
operation
before_jsonb
after_jsonb
changed_fields
actor
request_id
reason
created_at

敏感字段可配置脱敏,不在普通审计页面显示原值。

17.2 数据质量

质量规则至少包括:

  • 必填字段。
  • 唯一标识。
  • 荔波县行政区和边界。
  • 坐标异常。
  • 电话格式。
  • 来源 ID 重复。
  • 名称地址相似重复。
  • 孤立房型、评论、站序。
  • 未同步图谱。
  • 图谱孤立节点和悬空边。

17.3 指标

  • CRUD 请求量、错误率、P95 延迟。
  • 导入行数、失败行数、重复率。
  • 导出任务耗时和文件大小。
  • Outbox 积压数量和最老事件时间。
  • 图谱同步成功率和延迟。
  • 每项目实体数、来源覆盖率和字段完整率。

18. 索引与性能

基础索引:

(tenant_id, project_id, entity_type, status)
(tenant_id, project_id, updated_at DESC)
UNIQUE (tenant_id, project_id, natural_key)
UNIQUE (tenant_id, project_id, source_code, source_type, external_id)
GIST (geom)
GIN (normalized_name gin_trgm_ops)
(entity_id, created_at DESC) on reviews
(route_direction_id, stop_order) on bus_route_stops

原则:

  • 列表接口默认服务端分页,默认 50 条,单页上限 200。
  • 大规模列表优先游标分页。
  • 只有注册为可搜索的 JSONB 字段才允许建立表达式索引。
  • 评论和来源记录达到千万级后,再按项目或时间分区。
  • 导入与图谱同步采用批量写入,避免逐行网络往返。
  • 地图接口只返回当前视窗和当前缩放级别需要的数据。

19. 代码结构调整

建议新增:

app/
├── api/
│   └── data_platform/
│       ├── models.py
│       ├── records.py
│       ├── imports.py
│       ├── exports.py
│       └── graph_sync.py
├── data_platform/
│   ├── metadata_service.py
│   ├── query_compiler.py
│   ├── record_service.py
│   ├── validation_service.py
│   ├── import_service.py
│   ├── export_service.py
│   ├── graph_projector.py
│   └── repositories/
├── workers/
│   ├── graph_sync_worker.py
│   ├── import_worker.py
│   └── export_worker.py
└── migrations/

前端建议新增:

admin-web/src/panels/data-platform/
├── DataModelList.tsx
├── DataTableView.tsx
├── DynamicRecordForm.tsx
├── RecordDetail.tsx
├── ImportWizard.tsx
├── ExportCenter.tsx
├── ChangeHistory.tsx
└── GraphSyncMonitor.tsx

现有 app/db.py 暂时保留查询函数,但新增数据平台不应继续把所有 SQL 和 DDL写入单一文件。

20. 数据迁移路线

阶段 0冻结基线与盘点

  • 导出 PostgreSQL 与 FalkorDB 快照。
  • 统计每个项目的实体、关系、来源和孤立节点。
  • 冻结当前节点标签、自然键和前端所需字段。
  • 形成字段映射矩阵。

云游荔波当前界面可见基线可作为参考:

  • 酒店约 1,220。
  • 美食约 1,115。
  • 交通设施约 379。
  • 景点约 100。
  • POI 总量约 2,799。
  • 片区约 384。

最终迁移验收必须以迁移开始时的实时扫描为准。

阶段 1数据库基础

  • 引入 Alembic。
  • 补齐 PostGIS 环境。
  • 建立元数据表、正式实体表、Outbox、审计和任务表。
  • 注册现有业务表,不切换页面。

阶段 2数据回填

  • 从现有 CSV、PostgreSQL 候选层和 FalkorDB 回填正式实体表。
  • 为所有实体分配稳定 entity_id
  • 保留现有 natural_key
  • 建立来源记录和字段溯源。
  • 重建实体与片区、行政区关系。
  • 输出迁移冲突清单,不静默覆盖。

数据取值优先级:

人工审核后的最终值
→ 已确认融合值
→ 高置信结构化来源
→ 当前图谱属性
→ 原始采集值

阶段 3通用后台 MVP

  • 模型列表。
  • 通用数据表格。
  • 动态详情和编辑表单。
  • 单条增删改查。
  • 软删除和恢复。
  • 修改历史。
  • CSV/XLSX 导入预览。
  • 多表 ZIP 导出。

阶段 4图谱同步影子运行

  • 新增 Outbox Worker。
  • 暂时保留现有图谱写入路径。
  • 新 Worker 写入独立测试图或影子版本。
  • 对比节点、边、属性和查询结果。
  • 达标后停止应用直接双写。

阶段 5读路径切换

  • 通用后台完全读取 PostgreSQL。
  • 荔波地图列表、统计和详情切换到 PostgreSQL。
  • 图谱浏览器继续读取 FalkorDB。
  • 问答服务按问题类型路由到 PostgreSQL、FalkorDB 或聚合服务。

阶段 6收敛与清理

  • 移除不受控的直接 FalkorDB 写入。
  • 保留候选审核层和发布机制。
  • 增加全量重建和对账命令。
  • 更新快照、部署文档、API 文档和数据字典。

21. 测试与验收

21.1 功能验收

  • 任意已注册模型可自动生成列表、详情和编辑表单。
  • CRUD、批量导入、批量导出均受项目权限控制。
  • 软删除可恢复,恢复后图谱重新出现。
  • 同一个实体的高德、携程、大众点评记录可独立查看。
  • 酒店房型、设施、评论和美食套餐以独立明细表展示。
  • 地图和现有详情页面视觉上不发生破坏性变化。

21.2 数据验收

  • 迁移前后每类实体数量可解释。
  • 来源记录不丢失。
  • 空坐标、重复来源 ID、孤立明细有明确清单。
  • 所有正式实体具有稳定 entity_id
  • 所有空间实体能够定位到片区和荔波县。
  • 图谱不存在悬空边。

21.3 一致性验收

  • PostgreSQL 提交后 60 秒内完成图谱同步。
  • 同一事件重复消费不产生重复节点或边。
  • 图谱不可用时PostgreSQL CRUD 仍可用。
  • FalkorDB 清空后可以按项目完整重建。
  • 定时对账能够发现并修复图谱漂移。

21.4 性能目标

  • 常规列表 P95 小于 500 ms。
  • 单记录详情 P95 小于 800 ms。
  • 10,000 行 CSV 可完成校验、预览和错误下载。
  • 地图只加载视窗内数据,不一次返回全部 POI。
  • 默认图谱查询限制节点和边数量,避免浏览器卡顿。

21.5 安全验收

  • 不同项目之间无越权读取和修改。
  • 普通用户不能访问未注册表。
  • 所有 SQL 使用参数化查询。
  • 导入文件不能覆盖服务器路径。
  • 导出文件具有有效期和下载权限。
  • API Key、数据库密码不进入仓库和导出文件。

22. 回滚与灾备

  • 每次数据库迁移前导出 PostgreSQL 快照。
  • 图谱投影切换前保留现有 FalkorDB RDB。
  • 数据回填使用新表,不直接覆盖旧表。
  • 页面切换使用功能开关:
DATA_PLATFORM_READ_ENABLED
GRAPH_OUTBOX_WRITE_ENABLED
LEGACY_GRAPH_WRITE_ENABLED
  • 出现问题时可把读取切回旧接口。
  • 正式切换后PostgreSQL 是恢复源FalkorDB 可从 PostgreSQL 重建。

23. 推荐实施优先级

P0数据安全基础

  • Alembic。
  • PostGIS。
  • 正式实体表。
  • 元数据表。
  • Outbox 与审计。

P1通用后台 MVP

  • 通用查询、详情、编辑。
  • 新增、软删除、恢复。
  • 模板化导入。
  • 多表导出。

P2云游荔波迁移

  • 酒店、美食、景区、交通回填。
  • 房型、设施、评论、套餐拆表。
  • 地图和详情切换。

P3图谱投影

  • Graph Projector。
  • 影子图验证。
  • 对账、重建和监控。
  • 停止直接双写。

P4平台化增强

  • 项目管理员创建逻辑模型和扩展字段。
  • 保存视图。
  • 批量编辑。
  • 数据质量规则配置。
  • 定时导入导出。

24. 最终原则

  1. PostgreSQL 管完整数据FalkorDB 管关系投影。
  2. 用户编辑业务模型,不直接编辑任意数据库表。
  3. 所有正式修改只写 PostgreSQL。
  4. 图谱通过 Outbox 异步、幂等更新。
  5. 所有数据带租户、项目、来源、版本和审计。
  6. 一对多明细使用关系表,不拼接成长文本。
  7. 批量导入先暂存、校验和预览,再正式提交。
  8. 批量导出保留多表结构、数据字典和文件哈希。
  9. 图数据库可重建,关系数据库不可丢失。
  10. 现有业务页面保持可用,按阶段平滑迁移。