31 KiB
关系数据库与通用数据平台技术方案
适用平台:旅行知识图谱管理系统 / 云游荔波及后续多项目
方案状态:拟实施
核心结论:PostgreSQL 作为权威业务数据源,FalkorDB 作为只读图谱投影;建设元数据驱动的通用数据管理后台。
1. 背景与问题
当前平台已经同时部署 PostgreSQL 与 FalkorDB:
- PostgreSQL 保存项目、账号权限、采集批次、原始记录、候选实体、审核记录、发布任务等管理数据。
- FalkorDB 保存最终实体、关系、路线、空间片区和图谱浏览数据。
- 管理后台已有项目工作区、数据采集、审核入藏、图谱发布、知识广场等模块。
- 云游荔波已经形成酒店、美食、景区、交通、公交线路、公交站、片区等真实业务数据。
当前主要问题不是缺少数据库,而是两个数据库的职责尚未清晰:
- 最终业务详情过度依赖 FalkorDB 节点属性。
- 酒店房型、设施、评论、团购等一对多数据不适合塞入图节点。
- 一些采集脚本直接双写 PostgreSQL 与 FalkorDB,失败时可能产生数据漂移。
- 当前后台偏向候选实体和图谱审核,缺少正式业务数据的通用增删改查。
- 不同项目未来会拥有不同数据表,为每个项目编写固定后台不可持续。
- 导入、导出、变更历史、恢复和项目隔离需要形成统一机制。
- 数据库 DDL 目前主要由应用启动时的
CREATE TABLE IF NOT EXISTS管理,不适合后续复杂演进。
因此,本方案不新增第三套数据库,也不推翻现有图谱,而是在现有双数据库基础上重新划分数据职责。
2. 建设目标
2.1 目标
- PostgreSQL 成为酒店、美食、景区、交通等业务数据的唯一权威数据源。
- FalkorDB 保留实体关系、空间关系、路线关系和 ToB 图谱展示能力。
- 建设可服务多个项目的通用数据管理后台。
- 通用后台通过元数据自动生成列表、表单、筛选、校验和导入导出能力。
- 所有数据修改先进入 PostgreSQL,再通过可靠事件同步到 FalkorDB。
- 保留高德、携程、大众点评等每一条来源记录及字段级溯源。
- 支持软删除、恢复、版本控制、操作审计和批次回滚。
- 保留现有荔波地图、详情侧栏、公交线路和图谱浏览器。
2.2 非目标
- 不把通用后台建设成可执行任意 SQL 的数据库管理器。
- 不允许普通用户直接创建、删除任意 PostgreSQL 物理表。
- 不把评论、价格方案、实时房态等全部建成图节点。
- 不采用 PostgreSQL 与 FalkorDB 的双向同步。
- 不允许 FalkorDB 成为可独立修改的第二权威数据源。
- 第一阶段不替换现有项目业务页面,只替换其底层数据来源。
3. 架构决策
ADR-01:PostgreSQL 是 System of Record
所有正式业务数据、审核结果、来源明细、评论、房型、设施、套餐和修改记录,以 PostgreSQL 为准。
ADR-02:FalkorDB 是派生图谱
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_id和natural_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 通用后台页面
建议在主菜单增加“业务数据中心”:
- 数据模型。
- 数据表。
- 数据导入。
- 数据导出。
- 数据质量。
- 修改历史。
- 图谱同步。
数据表页面自动支持:
- 服务端分页和游标分页。
- 多字段筛选。
- 模糊搜索。
- 排序和自定义列。
- 固定列与列宽。
- 新增、详情、编辑。
- 批量修改。
- 批量软删除与恢复。
- 保存查询视图。
- 查看关联数据。
- 查看来源与证据。
- 查看图谱节点。
- 重新同步图谱。
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_aliasesentity_imagesentity_categoriesentity_relationsentity_merge_recordsentity_redirectsentity_versions
8.2 酒店
hotel_profileshotel_room_typeshotel_room_offershotel_facilitieshotel_policies
房型、报价和设施必须是一对多明细,不再拼接成长字符串保存在图节点。
实时性较强的“仅剩几间”等信息默认不进入长期知识库;如业务需要保留,只能写入带 observed_at 和 expires_at 的报价快照。
8.3 美食
restaurant_profilesrestaurant_dealsrestaurant_cuisinesrestaurant_business_hours
团购套餐独立保存名称、图片、现价、原价、折扣、规则和采集时间,不把多个套餐塞入单个字段。
8.4 景区
scenic_profilesscenic_childrenscenic_levelsscenic_ticket_policies
明确区分:
- 独立景区。
- 景区子景点。
- 纪念馆、博物馆等旅游目的地。
- 不应入库的广场、商业设施等异常候选。
8.5 交通
transport_stopsbus_routesbus_route_directionsbus_route_stopsroute_geometries
站序表至少保存:
route_direction_id
stop_id
stop_order
arrival_offset
distance_from_previous
8.6 评论与标签
酒店与美食共用:
reviewsreview_tagsentity_review_tags
评论保存来源、评分、正文、采集时间、匿名用户标识和原始记录关联。评价摘要由 PostgreSQL 聚合生成,不把“样本数、平均分”硬编码进图谱。
9. 空间数据设计
现有 kg_place_spatial、kg_geo_cells、kg_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_templates、mapping_profiles、import_batches、raw_records 基础上补充:
import_jobsimport_filesimport_staging_rowsimport_validation_errorsimport_mutationsimport_job_events
现有 candidate_entities 与 candidate_relations 继续承担候选审核层,不直接替代正式实体表。
12.3 导入步骤
- 选择项目和数据模型。
- 下载元数据自动生成的 CSV/XLSX 模板。
- 上传文件并计算 SHA-256。
- 自动识别表头和字符编码。
- 用户确认字段映射。
- 暂存原始行,不直接写正式表。
- 校验字段类型、必填、字典、外键、坐标和行政区。
- 执行来源 ID、自然键、名称地址和空间距离去重。
- 预览新增、更新、跳过、冲突和失败数量。
- 用户确认后写入正式表。
- 同事务写入 Outbox。
- 后台同步图谱并记录任务状态。
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_jobsexport_job_filesexport_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 → GeoCell(UI 显示“片区”)
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_id、project_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. 最终原则
- PostgreSQL 管完整数据,FalkorDB 管关系投影。
- 用户编辑业务模型,不直接编辑任意数据库表。
- 所有正式修改只写 PostgreSQL。
- 图谱通过 Outbox 异步、幂等更新。
- 所有数据带租户、项目、来源、版本和审计。
- 一对多明细使用关系表,不拼接成长文本。
- 批量导入先暂存、校验和预览,再正式提交。
- 批量导出保留多表结构、数据字典和文件哈希。
- 图数据库可重建,关系数据库不可丢失。
- 现有业务页面保持可用,按阶段平滑迁移。