diff --git a/docs/关系数据库与通用数据平台技术方案.md b/docs/关系数据库与通用数据平台技术方案.md index da92c31..1462977 100644 --- a/docs/关系数据库与通用数据平台技术方案.md +++ b/docs/关系数据库与通用数据平台技术方案.md @@ -1,255 +1,393 @@ # 关系数据库与通用数据平台技术方案 -> 适用平台:旅行知识图谱管理系统 / 云游荔波及后续多项目 -> 方案状态:拟实施 -> 核心结论:PostgreSQL 作为权威业务数据源,FalkorDB 作为只读图谱投影;建设元数据驱动的通用数据管理后台。 +> 版本:V2(精简版) +> +> 适用范围:云游荔波及平台后续项目 +> +> 数据前提:进入平台的数据已经完成采集、清洗、去重、融合和人工确认。 -## 1. 背景与问题 +## 1. 核心结论 -当前平台已经同时部署 PostgreSQL 与 FalkorDB: +平台只保留两套业务数据存储: -- PostgreSQL 保存项目、账号权限、采集批次、原始记录、候选实体、审核记录、发布任务等管理数据。 -- FalkorDB 保存最终实体、关系、路线、空间片区和图谱浏览数据。 -- 管理后台已有项目工作区、数据采集、审核入藏、图谱发布、知识广场等模块。 -- 云游荔波已经形成酒店、美食、景区、交通、公交线路、公交站、片区等真实业务数据。 +1. **PostgreSQL:保存处理完成后的正式业务数据,是唯一权威数据源。** +2. **FalkorDB:保存正式业务数据生成的图谱节点和关系,主要用于关系查询和 ToB 可视化。** -当前主要问题不是缺少数据库,而是两个数据库的职责尚未清晰: +不在新的关系数据平台中重复建设原始采集、候选审核、字段级溯源等复杂流程。未经处理的数据不能直接进入正式业务库,应在平台外完成处理后,再按标准模板导入。 -1. 最终业务详情过度依赖 FalkorDB 节点属性。 -2. 酒店房型、设施、评论、团购等一对多数据不适合塞入图节点。 -3. 一些采集脚本直接双写 PostgreSQL 与 FalkorDB,失败时可能产生数据漂移。 -4. 当前后台偏向候选实体和图谱审核,缺少正式业务数据的通用增删改查。 -5. 不同项目未来会拥有不同数据表,为每个项目编写固定后台不可持续。 -6. 导入、导出、变更历史、恢复和项目隔离需要形成统一机制。 -7. 数据库 DDL 目前主要由应用启动时的 `CREATE TABLE IF NOT EXISTS` 管理,不适合后续复杂演进。 +```mermaid +flowchart LR + A["平台外数据处理\n采集、清洗、去重、融合、审核"] --> B["标准数据文件或 API"] + B --> C["格式与关联校验"] + C -->|通过| D["PostgreSQL\n正式业务数据"] + C -->|不通过| E["错误报告\n退回修正"] + D --> F["图谱同步队列"] + F --> G["FalkorDB\n图谱投影"] + D --> H["通用数据后台\n地图、详情、导入导出"] + G --> I["图谱浏览器\n关系查询、ToB 展示"] +``` -因此,本方案不新增第三套数据库,也不推翻现有图谱,而是在现有双数据库基础上重新划分数据职责。 +## 2. 数据职责 -## 2. 建设目标 +### 2.1 PostgreSQL 保存 -### 2.1 目标 +- 酒店、美食、景区、交通等正式实体。 +- 酒店房型、设施、政策等正式明细。 +- 美食团购、菜系、营业时间等正式明细。 +- 公交线路、方向、站点顺序和线路坐标。 +- 评论、评价标签和图片。 +- 实体之间已经确认的正式关系。 +- 高德、携程、大众点评等外部平台 ID 和 URL。 +- 数据修改记录。 +- 导入、导出任务结果。 +- 图谱同步队列和同步状态。 -- PostgreSQL 成为酒店、美食、景区、交通等业务数据的唯一权威数据源。 -- FalkorDB 保留实体关系、空间关系、路线关系和 ToB 图谱展示能力。 -- 建设可服务多个项目的通用数据管理后台。 -- 通用后台通过元数据自动生成列表、表单、筛选、校验和导入导出能力。 -- 所有数据修改先进入 PostgreSQL,再通过可靠事件同步到 FalkorDB。 -- 保留高德、携程、大众点评等每一条来源记录及字段级溯源。 -- 支持软删除、恢复、版本控制、操作审计和批次回滚。 -- 保留现有荔波地图、详情侧栏、公交线路和图谱浏览器。 +### 2.2 PostgreSQL 不保存 -### 2.2 非目标 +- 未清洗的网页或接口原始响应。 +- 未确认的候选实体。 +- 自动匹配过程中的每个中间结果。 +- 重复数据的全部计算过程。 +- 临时爬虫缓存。 +- 无业务意义的实时文案,例如“仅剩 2 间”。 -- 不把通用后台建设成可执行任意 SQL 的数据库管理器。 -- 不允许普通用户直接创建、删除任意 PostgreSQL 物理表。 -- 不把评论、价格方案、实时房态等全部建成图节点。 -- 不采用 PostgreSQL 与 FalkorDB 的双向同步。 -- 不允许 FalkorDB 成为可独立修改的第二权威数据源。 -- 第一阶段不替换现有项目业务页面,只替换其底层数据来源。 +### 2.3 FalkorDB 保存 -## 3. 架构决策 +- 酒店、美食、景区、交通、公交线路、公交站、片区和行政区节点。 +- 实体与片区、片区与荔波县的空间关系。 +- 景区与子景点关系。 +- 公交线路与站点关系。 +- 已确认的附近、包含、分类等业务关系。 +- 图谱展示需要的名称、类型、坐标和少量摘要属性。 -### ADR-01:PostgreSQL 是 System of Record +### 2.4 FalkorDB 不保存 -所有正式业务数据、审核结果、来源明细、评论、房型、设施、套餐和修改记录,以 PostgreSQL 为准。 +- 完整评论正文。 +- 酒店全部房型和报价。 +- 美食全部团购套餐。 +- 大量设施、政策等详情字段。 +- 导入文件和错误数据。 +- 可从 PostgreSQL 重新生成的重复明细。 -### ADR-02:FalkorDB 是派生图谱 - -FalkorDB 只保存适合关系查询和图谱展示的节点、边及少量检索属性。图谱可以重建,不单独承担数据恢复责任。 - -### ADR-03:采用元数据驱动的通用后台 - -后台只允许管理注册到平台的数据模型。字段类型、表单、列表列、校验、权限、导入模板和图谱映射均由元数据控制。 - -### ADR-04:采用关系字段与 JSONB 混合模型 - -- 稳定、高频筛选、需要唯一约束或关联的字段使用正式关系型列。 -- 项目临时扩展、低频使用的字段存入 `extra_data JSONB`。 -- 稳定后的扩展字段通过迁移提升为正式列。 -- 不采用纯 EAV,也不把所有业务字段全部放进 JSONB。 - -### ADR-05:使用事务 Outbox 同步图谱 - -业务写入和图谱事件在同一个 PostgreSQL 事务提交。独立 Worker 异步、幂等地更新 FalkorDB。 - -### ADR-06:默认软删除 - -业务数据默认只允许软删除。永久删除需要高级权限、依赖检查和二次确认。 - -## 4. 目标架构 +## 3. 简化后的平台结构 ```mermaid flowchart TB - subgraph Sources["数据来源"] - A1["高德 API / CSV"] - A2["携程"] - A3["大众点评"] - A4["公交 Excel"] - A5["人工录入"] + subgraph PG["PostgreSQL 正式业务库"] + P1["项目与权限"] + P2["通用实体"] + P3["酒店、美食、景区、交通明细"] + P4["评论、图片、设施、套餐"] + P5["正式关系"] + P6["导入导出记录"] + P7["图谱同步队列"] end - subgraph Platform["FastAPI 数据平台"] - B1["导入暂存与校验"] - B2["实体融合与审核"] - B3["通用 CRUD 服务"] - B4["导出任务"] - B5["Outbox Worker"] - B6["统一查询聚合"] + subgraph API["FastAPI"] + A1["通用数据表 API"] + A2["批量导入导出"] + A3["地图与详情 API"] + A4["图谱同步服务"] end - subgraph PG["PostgreSQL 16 + PostGIS"] - C1["元数据与权限"] - C2["标准实体与业务扩展表"] - C3["来源、证据与审核"] - C4["空间与路线缓存"] - C5["审计、版本、导入导出任务"] - C6["图谱同步 Outbox"] - end - - subgraph Graph["FalkorDB"] - D1["实体节点"] - D2["片区与行政区"] - D3["公交、景区、分类关系"] - D4["图谱发布版本"] + subgraph FK["FalkorDB"] + F1["正式实体节点"] + F2["正式业务关系"] + F3["片区与行政区关系"] end subgraph UI["React 管理后台"] - E1["通用数据中心"] - E2["项目业务页面"] - E3["图谱浏览器"] + U1["通用数据管理"] + U2["荔波地图与详情"] + U3["图谱浏览器"] 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 + PG <--> API + API --> FK + API <--> UI + FK --> U3 ``` -## 5. 数据分层与职责 +## 4. PostgreSQL 表结构 -| 数据层 | PostgreSQL | FalkorDB | -| --- | --- | --- | -| 项目、租户、权限 | 权威保存 | 不保存 | -| 数据模型与字段配置 | 权威保存 | 不保存 | -| 正式业务实体 | 完整保存 | 保存核心节点投影 | -| 酒店房型、报价、设施、政策 | 完整保存 | 默认不建节点 | -| 美食套餐、菜系、营业信息 | 完整保存 | 默认不建节点 | -| 评论、评价标签 | 完整保存 | 可选同步汇总标签 | -| 多来源原始记录 | 完整保存 | 默认不保存 | -| 融合、审核、版本历史 | 完整保存 | 只保存最终结果 | -| 坐标、行政区、H3 片区 | 完整保存并支持空间查询 | 建立空间关系 | -| 景区、子景点、分类关系 | 完整保存 | 建立图关系 | -| 公交线路与站序 | 完整保存 | 建立线路图关系 | -| 地图列表与详情 | 主要读取 | 补充关系 | -| 图谱查询和 ToB 展示 | 辅助 | 主要读取 | -| CSV/XLSX 数据交付 | 唯一导出来源 | 只导出图节点和边 | +整体分成四组,避免结构混乱: -## 6. PostgreSQL 技术基线 +1. 平台管理表。 +2. 正式实体表。 +3. 业务明细表。 +4. 同步与任务表。 -### 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 或独立数据库。 - -所有平台注册业务表必须至少包含: - -```text -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 元数据表 - -建议新增: +### 4.1 平台管理表 | 表 | 作用 | | --- | --- | -| `data_models` | 注册后台可管理的数据模型及物理表 | -| `data_fields` | 字段类型、显示、校验、筛选和图谱属性配置 | -| `data_model_relations` | 模型间一对一、一对多、多对多关系 | -| `data_dictionaries` | 字典定义 | -| `data_dictionary_items` | 字典选项 | -| `data_views` | 用户保存的列、筛选、排序视图 | -| `data_validation_rules` | 可复用校验规则 | -| `data_model_permissions` | 模型级和操作级权限 | -| `graph_mappings` | 模型到节点、属性和边的映射 | +| `projects` | 项目工作区 | +| `data_table_registry` | 注册通用后台可管理的业务表 | +| `data_field_registry` | 定义业务表字段显示和校验规则 | +| `data_relation_registry` | 定义表之间的关联 | +| `data_change_logs` | 保存数据修改记录 | -### 7.2 `data_models` 核心字段 +通用后台只能访问 `data_table_registry` 中已注册的表,不能任意访问 PostgreSQL。 + +### 4.2 正式实体主表 + +所有地点类业务统一进入 `poi_entities`: ```text -model_id +poi_entities +├── entity_id UUID PK +├── tenant_id +├── project_id +├── entity_type +├── name +├── category_l1 +├── category_l2 +├── category_l3 +├── address +├── district +├── adcode +├── phone +├── longitude +├── latitude +├── geom +├── h3_r9 +├── h3_r10 +├── status +├── version +├── created_at +├── updated_at +└── extra_data JSONB +``` + +`entity_type` 的当前标准值: + +```text +hotel +restaurant +scenic +transport +bus_stop +``` + +主表只保存所有 POI 共有且经常查询的字段。项目特有但暂不稳定的少量字段可以放入 `extra_data`,不能把大段明细或一对多数据塞入其中。 + +### 4.3 外部平台标识 + +`entity_external_links` 只保存已经确认与实体对应的平台标识,不保存原始响应: + +```text +entity_external_links +├── id +├── tenant_id +├── project_id +├── entity_id +├── platform +├── external_id +├── external_name +├── external_url +└── updated_at +``` + +示例: + +```text +同一个酒店实体 +├── amap:高德 POI ID 和 URL +└── ctrip:携程酒店 ID、名称和 URL + +同一个美食实体 +├── amap:高德 POI ID 和 URL +└── dianping:大众点评商户 ID、名称和 URL +``` + +### 4.4 公共明细表 + +| 表 | 内容 | +| --- | --- | +| `entity_images` | 实体和房型、套餐图片 | +| `entity_relations` | 已确认的正式实体关系 | +| `reviews` | 酒店、美食等真实评论 | +| `review_tags` | 评论摘要标签 | + +`entity_relations` 保存正式关系: + +```text +relation_id tenant_id project_id -model_code +source_entity_id +relation_type +target_entity_id +properties JSONB +status +updated_at +``` + +### 4.5 酒店表 + +```text +hotel_profiles +hotel_room_types +hotel_facilities +hotel_policies +``` + +#### `hotel_profiles` + +一间酒店一条记录,保存: + +- 携程酒店名。 +- 开业时间。 +- 客房数量。 +- 酒店钻级。 +- 携程评分。 +- 用户点评数量。 +- 参考起价。 +- 酒店简介。 + +#### `hotel_room_types` + +一个房型一条记录: + +- 房型名称。 +- 图片 URL。 +- 床型。 +- 面积。 +- 楼层。 +- 窗户。 +- 可住人数。 +- 早餐。 +- 取消政策。 +- 支付方式。 +- 参考价格。 + +不保存“仅剩几间”等采集时刻的临时库存文案。 + +#### `hotel_facilities` + +一个设施一条记录: + +- 设施分类。 +- 设施名称。 +- 是否免费。 +- 收费说明。 +- 来源 URL。 + +### 4.6 美食表 + +```text +restaurant_profiles +restaurant_deals +restaurant_business_hours +``` + +#### `restaurant_profiles` + +- 大众点评店名。 +- 点评分类。 +- 榜单排名。 +- 营业状态。 +- 点评评分。 +- 用户点评数。 +- 人均消费。 + +#### `restaurant_deals` + +一个套餐一条记录: + +- 套餐名称。 +- 图片 URL。 +- 当前价格。 +- 原价。 +- 折扣。 +- 使用规则。 +- 有效期。 + +### 4.7 景区表 + +```text +scenic_profiles +scenic_children +``` + +#### `scenic_profiles` + +- 景区类型。 +- 景区等级。 +- 是否国家级。 +- 游客价值分类。 +- 开放时间。 +- 门票说明。 +- 官方简介。 + +#### `scenic_children` + +保存景区内部子景点: + +- 所属景区。 +- 子景点名称。 +- 子景点类型。 +- 坐标。 +- 游览说明。 + +### 4.8 交通表 + +```text +transport_profiles +bus_routes +bus_route_directions +bus_route_stops +route_geometries +``` + +线路站序必须使用独立表,不把站点数组拼接到一个文本字段。 + +### 4.9 空间表 + +保留并复用: + +```text +kg_geo_cells +kg_route_metrics +``` + +实体坐标在 `poi_entities.geom` 中使用 PostGIS 保存。H3 片区 ID 同时保存在实体主表中。 + +## 5. 通用数据后台 + +通用后台管理的是“平台注册的正式数据表”,不是数据库管理器。 + +### 5.1 表注册 + +`data_table_registry`: + +```text +table_code +project_id display_name physical_table primary_key title_field -description -model_kind -map_enabled +parent_table_code +allow_create +allow_update +allow_delete +allow_import +allow_export graph_enabled -import_enabled -export_enabled -soft_delete_enabled -status -created_at -updated_at +display_order ``` -`physical_table` 必须来自服务器允许列表,不能由前端传递任意表名。 +### 5.2 字段注册 -### 7.3 `data_fields` 核心字段 +`data_field_registry`: ```text -field_id -model_id +table_code field_code -physical_column display_name data_type required -unique_enabled searchable sortable editable @@ -257,974 +395,505 @@ visible_in_list visible_in_detail importable exportable -dictionary_code -validation_jsonb -ui_jsonb -graph_property +dictionary_values JSONB +validation_rule JSONB display_order -status ``` -支持的数据类型至少包括: +后台根据注册信息自动生成: -- 单行文本、多行文本。 -- 整数、小数、金额、百分比。 -- 布尔值。 -- 日期、时间、日期时间。 -- 单选、多选、字典。 -- URL、图片 URL、电话。 -- 经纬度、空间点。 -- JSON。 -- 外键、关联列表。 +- 数据列表。 +- 查询条件。 +- 新增表单。 +- 编辑表单。 +- 详情页面。 +- CSV/XLSX 模板。 +- 导入校验。 +- 导出字段。 -### 7.4 通用后台页面 +### 5.3 第一阶段不做在线建物理表 -建议在主菜单增加“业务数据中心”: +为了保持结构清晰: -1. 数据模型。 -2. 数据表。 -3. 数据导入。 -4. 数据导出。 -5. 数据质量。 -6. 修改历史。 -7. 图谱同步。 +- 项目管理员可以配置显示、字段顺序、校验和权限。 +- 新增正式物理表或修改列类型仍然通过数据库迁移完成。 +- 后续确实需要用户在线建表时,再作为独立能力设计。 -数据表页面自动支持: +这样既能复用一个通用后台,又不会让数据库结构失控。 -- 服务端分页和游标分页。 -- 多字段筛选。 -- 模糊搜索。 -- 排序和自定义列。 -- 固定列与列宽。 -- 新增、详情、编辑。 -- 批量修改。 -- 批量软删除与恢复。 -- 保存查询视图。 -- 查看关联数据。 -- 查看来源与证据。 -- 查看图谱节点。 -- 重新同步图谱。 +## 6. 增删改查 -### 7.5 安全边界 - -通用后台不开放: - -- 任意 SQL。 -- 任意 Schema 或物理表访问。 -- 系统表修改。 -- 直接删除物理表。 -- 任意修改主键或列类型。 -- 绕过项目范围查询。 -- 直接修改 FalkorDB。 - -逻辑增加字段由元数据服务受控执行;需要创建正式物理列时,由 Alembic 迁移完成。 - -## 8. 正式业务数据模型 - -### 8.1 公共实体层 - -#### `business_entities` - -保存跨项目、跨类型通用字段: +统一 API: ```text -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 +GET /v1/admin/data/{table_code} +POST /v1/admin/data/{table_code} +GET /v1/admin/data/{table_code}/{record_id} +PATCH /v1/admin/data/{table_code}/{record_id} +DELETE /v1/admin/data/{table_code}/{record_id} +POST /v1/admin/data/{table_code}/{record_id}/restore ``` -关键约束: +### 6.1 查询 -```text -UNIQUE (tenant_id, project_id, natural_key) -CHECK (longitude BETWEEN -180 AND 180) -CHECK (latitude BETWEEN -90 AND 90) -``` +- 所有查询自动限制 `tenant_id` 和 `project_id`。 +- 只允许查询已注册且标记为可搜索的字段。 +- 使用参数化 SQL,不接受原始 SQL。 +- 默认每页 50 条,单页上限 200 条。 +- 支持筛选、搜索、排序和保存列配置。 -#### `entity_source_records` +### 6.2 新增和修改 -保存高德、携程、大众点评等来源记录: +- 按字段注册规则校验。 +- `entity_id` 由服务器生成。 +- 使用 `version` 防止多人覆盖。 +- 保存操作人、时间和修改前后内容。 +- 同一事务写入图谱同步队列。 -```text -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 -``` +### 6.3 删除 -#### `entity_field_values` - -仅用于字段级溯源和融合结果,不替代正式业务列: - -```text -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_at` 和 `expires_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` - -站序表至少保存: - -```text -route_direction_id -stop_id -stop_order -arrival_offset -distance_from_previous -``` - -### 8.6 评论与标签 - -酒店与美食共用: - -- `reviews` -- `review_tags` -- `entity_review_tags` - -评论保存来源、评分、正文、采集时间、匿名用户标识和原始记录关联。评价摘要由 PostgreSQL 聚合生成,不把“样本数、平均分”硬编码进图谱。 - -## 9. 空间数据设计 - -现有 `kg_place_spatial`、`kg_geo_cells`、`kg_route_metrics` 可以增量复用。 - -目标空间结构: - -```text -业务实体 -→ PostGIS Point -→ H3 R9/R10 -→ GeoCell(前端显示为“片区”) -→ Area(荔波县) -``` - -PostgreSQL 负责: - -- 荔波县边界内判断。 -- 地图视窗范围查询。 -- 半径检索。 -- H3 片区归属。 -- 路线距离缓存。 -- 导入时坐标异常检查。 - -FalkorDB 负责投影: - -```text -Entity -[IN_H3_R9]-> GeoCell -GeoCell -[LOCATED_IN]-> Area -Entity -[LOCATED_IN]-> Area -``` - -`GeoCell` 只是技术标签,管理后台和图例统一显示“片区”。 - -## 10. 通用 CRUD API - -统一前缀建议为: - -```text -/v1/admin/data-platform -``` - -### 10.1 模型接口 - -```text -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 记录接口 - -```text -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: - -```json -{ - "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 并发控制 - -所有修改携带版本: - -```text -If-Match: 12 -``` - -SQL 更新条件: - -```text -WHERE entity_id = :id AND version = :expected_version -``` - -更新成功后 `version + 1`。版本冲突返回 HTTP 409,并提供当前记录。 - -## 11. 删除、恢复与版本 - -### 11.1 默认软删除 - -删除动作写入: +默认软删除: ```text status = deleted deleted_at deleted_by -delete_reason -version = version + 1 ``` -同时写入图谱删除事件。FalkorDB 删除节点前先删除或改写关联边。 +删除后图谱同步服务删除对应投影。管理员可以恢复。 -### 11.2 恢复 +## 7. 批量导入 -恢复前重新检查: - -- 唯一键是否被其他实体占用。 -- 外部来源记录是否仍然有效。 -- 关联实体是否存在。 -- 图谱映射是否仍然启用。 - -### 11.3 永久删除 - -仅系统管理员可执行,并要求: - -- 依赖检查。 -- 二次确认。 -- 审计记录。 -- 数据备份或快照。 -- 明确不可恢复提示。 - -## 12. 批量导入 - -### 12.1 导入状态机 +进入平台的是已经处理好的数据,因此导入流程只做结构校验和正式入库,不再做清洗、融合和候选审核。 ```mermaid -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 +flowchart LR + A["上传处理好的 CSV/XLSX"] --> B["字段和关联校验"] + B -->|失败| C["下载错误报告"] + B -->|通过| D["预览新增和更新数量"] + D --> E["确认导入"] + E --> F["事务写入正式表"] + F --> G["同步图谱"] ``` -### 12.2 导入表 - -在现有 `import_templates`、`mapping_profiles`、`import_batches`、`raw_records` 基础上补充: - -- `import_jobs` -- `import_files` -- `import_staging_rows` -- `import_validation_errors` -- `import_mutations` -- `import_job_events` - -现有 `candidate_entities` 与 `candidate_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 幂等与去重 - -优先级: +### 7.1 导入只保留一个任务表 ```text -entity_id -→ source_code + source_external_id -→ 高德 POI ID -→ natural_key -→ 规范化名称 + 地址 -→ 名称相似度 + 地址相似度 + 坐标距离 +data_import_jobs +├── job_id +├── tenant_id +├── project_id +├── table_code +├── file_name +├── file_hash +├── import_mode +├── total_rows +├── inserted_rows +├── updated_rows +├── failed_rows +├── error_file +├── status +├── created_by +├── created_at +└── completed_at ``` -同一文件哈希、同一模型和同一导入模式重复提交时,默认返回已有任务,不重复写入。 +不把每一条原始行长期保存在 PostgreSQL。校验失败行写入临时错误文件,供用户下载修正。 -### 12.6 回滚 +### 7.2 导入模式 -每个正式写入动作记录在 `import_mutations`: +| 模式 | 说明 | +| --- | --- | +| 仅新增 | 已存在的唯一键直接报错或跳过 | +| 新增并更新 | 按主键或外部平台 ID 更新,推荐默认 | + +第一阶段不提供“完整覆盖并删除缺失数据”,避免误删。 + +### 7.3 校验内容 + +- 必填字段。 +- 数据类型。 +- 唯一 ID。 +- 外键是否存在。 +- 枚举值。 +- 经纬度范围。 +- 坐标是否位于项目允许区域。 +- 公交站序是否连续。 +- 酒店房型是否能找到酒店。 +- 评论是否能找到对应实体。 + +### 7.4 多表数据包 + +酒店、美食等一对多数据使用 ZIP 多表导入: ```text -insert / update / soft_delete -record_id -before_jsonb -after_jsonb +酒店数据.zip +├── 酒店实体.csv +├── 酒店详情.csv +├── 房型.csv +├── 设施.csv +├── 政策.csv +├── 评论.csv +└── 图片.csv ``` -回滚不是数据库全量恢复,而是按批次生成反向业务变更,并再次产生图谱同步事件。 +系统先校验整个数据包的主外键,全部通过后再提交,避免只导入主表、明细缺失。 -## 13. 批量导出 +## 8. 批量导出 -### 13.1 导出能力 +导出全部来自 PostgreSQL 正式表。 -- 导出当前表。 -- 导出当前筛选结果。 -- 导出勾选记录。 -- 导出主表及关联明细。 -- 导出完整项目数据包。 -- 导出图谱节点与边。 -- 支持 CSV、XLSX、ZIP。 -- 大任务异步执行。 +支持: -### 13.2 导出任务表 +- 当前表。 +- 当前筛选结果。 +- 选中记录。 +- 主表及全部关联明细。 +- 整个项目数据包。 +- 图谱节点和关系。 -- `export_jobs` -- `export_job_files` -- `export_job_events` - -任务保存: +云游荔波项目导出结构: ```text -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 云游荔波完整导出结构 - -```text -云游荔波数据导出_YYYYMMDD_HHMMSS.zip -├── manifest.json +云游荔波正式数据_YYYYMMDD.zip ├── 数据字典.csv ├── 酒店 │ ├── 酒店实体.csv -│ ├── 酒店来源记录.csv +│ ├── 酒店详情.csv │ ├── 房型.csv -│ ├── 报价方案.csv -│ ├── 服务设施.csv -│ ├── 评论.csv -│ └── 评价标签.csv +│ ├── 设施.csv +│ ├── 政策.csv +│ └── 评论.csv ├── 美食 │ ├── 美食实体.csv -│ ├── 美食来源记录.csv +│ ├── 美食详情.csv │ ├── 团购套餐.csv -│ ├── 评论.csv -│ └── 评价标签.csv +│ └── 评论.csv ├── 景区 │ ├── 景区实体.csv -│ ├── 子景点.csv -│ └── 景区关系.csv +│ └── 子景点.csv ├── 交通 │ ├── 交通站点.csv │ ├── 公交线路.csv -│ ├── 公交方向.csv +│ ├── 线路方向.csv │ └── 线路站序.csv └── 图谱 ├── 节点.csv └── 关系.csv ``` -`manifest.json` 至少包含项目、数据模型版本、筛选条件、导出时间、记录数和各文件哈希。 +导出任务只需要一个 `data_export_jobs` 表记录状态、筛选条件、记录数、文件路径和文件哈希。 -导出使用 PostgreSQL `REPEATABLE READ` 快照,保证同一个 ZIP 内各表数据版本一致。 +## 9. 图谱同步 -## 14. 图谱投影与可靠同步 - -### 14.1 Outbox 表 - -建议新增 `graph_sync_outbox`: +为了避免 PostgreSQL 保存成功但 FalkorDB 写入失败,保留一个精简的同步队列表: ```text -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 +graph_sync_queue +├── event_id +├── tenant_id +├── project_id +├── table_code +├── record_id +├── operation +├── record_version +├── status +├── retry_count +├── error_message +├── created_at +└── completed_at ``` -事件类型至少包括: +业务写入和队列记录在同一个 PostgreSQL 事务中完成。 + +同步流程: + +1. PostgreSQL 新增、修改或删除正式数据。 +2. 同事务写入 `graph_sync_queue`。 +3. 后台 Worker 读取待处理记录。 +4. 按 `entity_id + version` 幂等更新 FalkorDB。 +5. 成功后标记完成。 +6. 失败自动重试并在后台显示错误。 + +不允许从 FalkorDB 反向修改 PostgreSQL。 + +## 10. 图谱投影 + +### 10.1 节点 ```text -entity.upsert -entity.delete -relation.upsert -relation.delete -model.rebuild +Area +GeoCell(界面显示“片区”) +Hotel +Restaurant +ScenicArea +Attraction +TransportStop +BusStop +BusRoute +Category ``` -### 14.2 Worker - -Worker 使用: +### 10.2 关系 ```text -SELECT ... FOR UPDATE SKIP LOCKED +POI -[IN_H3_R9]-> GeoCell +GeoCell -[LOCATED_IN]-> Area +POI -[LOCATED_IN]-> Area +ScenicArea -[CONTAINS]-> Attraction +BusRoute -[SERVES_STOP]-> BusStop +BusStop -[NEXT_STOP]-> BusStop +POI -[HAS_CATEGORY]-> Category +POI -[NEARBY]-> POI ``` -批量领取待处理事件,按 `tenant_id + project_id + aggregate_id + version` 幂等处理。 +### 10.3 图节点属性 -失败策略: +只同步: -- 指数退避。 -- 最大重试次数。 -- 超限进入 dead-letter 状态。 -- 管理后台支持查看错误和人工重试。 +- `entity_id` +- 名称。 +- 类型。 +- 分类。 +- 经纬度。 +- 地址摘要。 +- 状态。 +- PostgreSQL 数据版本。 -### 14.3 同步状态 +详情页面需要的完整房型、设施、评论和套餐继续从 PostgreSQL 查询。 -新增 `graph_sync_state`: +## 11. 页面数据来源 -```text -tenant_id -project_id -aggregate_type -aggregate_id -relational_version -graph_version -sync_status -last_synced_at -last_error -``` - -通用列表显示: - -- 已同步。 -- 待同步。 -- 同步中。 -- 同步失败。 -- 图谱已落后。 - -### 14.4 图谱映射 - -`graph_mappings` 配置: - -```text -model_code -node_label -node_key_field -node_title_field -property_mapping JSONB -relation_mapping JSONB -enabled -mapping_version -``` - -示例: - -```text -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 空间查询,必要时叠加路线缓存 | +| 通用数据后台 | PostgreSQL | +| 荔波地图 POI | PostgreSQL + PostGIS | +| 酒店、美食详情侧栏 | PostgreSQL | +| 房型、设施、套餐、评论 | PostgreSQL | +| 公交路线地图 | PostgreSQL | | 图谱浏览器 | FalkorDB | -| 图关系问答 | FalkorDB | -| 属性问答 | PostgreSQL | -| 混合业务问答 | 查询聚合服务组合两边结果 | +| 图谱关系查询 | FalkorDB | | CSV/XLSX 导出 | PostgreSQL | -现有 `http://localhost:8102/admin/plaza/overview` 的视觉设计可以保持不变,只把详情、筛选和统计接口逐步切换到 PostgreSQL。 +现有荔波地图和详情布局保持不变,只调整后端数据来源。 -## 16. 权限与项目隔离 +## 12. 权限与项目隔离 -### 16.1 角色 +所有正式业务表都必须包含: + +```text +tenant_id +project_id +``` + +后端从当前项目上下文自动获得这两个值,不接受普通用户任意修改。 + +角色建议: | 角色 | 权限 | | --- | --- | -| 系统管理员 | 所有项目、模型、迁移、永久删除 | -| 项目管理员 | 本项目模型配置、导入导出、批量操作 | -| 数据管理员 | 本项目增删改查、导入、导出 | -| 审核人员 | 候选审核、融合、恢复 | -| 普通用户 | 查询及受限导出 | -| 只读用户 | 仅查看 | +| 系统管理员 | 管理所有项目和注册表 | +| 项目管理员 | 管理本项目数据、导入导出 | +| 数据编辑 | 本项目增删改查 | +| 只读用户 | 查询和受限导出 | -### 16.2 强制隔离 +## 13. 数据修改记录 -- 所有请求通过 `ProjectContext` 获取 `tenant_id`、`project_id`。 -- 后端自动拼接项目条件,禁止客户端自行指定任意租户。 -- 关键表使用组合索引和组合唯一约束。 -- 第二阶段评估 PostgreSQL Row Level Security。 -- 文件导入、导出和对象存储路径同样按租户、项目隔离。 - -## 17. 审计、质量与可观测性 - -### 17.1 操作审计 - -新增统一 `data_change_logs`: +只保留一个清晰的修改日志表: ```text -change_id -tenant_id -project_id -model_code -record_id -operation -before_jsonb -after_jsonb -changed_fields -actor -request_id -reason -created_at +data_change_logs +├── change_id +├── tenant_id +├── project_id +├── table_code +├── record_id +├── operation +├── before_data JSONB +├── after_data JSONB +├── actor +├── reason +└── created_at ``` -敏感字段可配置脱敏,不在普通审计页面显示原值。 +不再额外设计字段级来源选择、候选状态机和多套版本表。 -### 17.2 数据质量 +## 14. 索引 -质量规则至少包括: - -- 必填字段。 -- 唯一标识。 -- 荔波县行政区和边界。 -- 坐标异常。 -- 电话格式。 -- 来源 ID 重复。 -- 名称地址相似重复。 -- 孤立房型、评论、站序。 -- 未同步图谱。 -- 图谱孤立节点和悬空边。 - -### 17.3 指标 - -- CRUD 请求量、错误率、P95 延迟。 -- 导入行数、失败行数、重复率。 -- 导出任务耗时和文件大小。 -- Outbox 积压数量和最老事件时间。 -- 图谱同步成功率和延迟。 -- 每项目实体数、来源覆盖率和字段完整率。 - -## 18. 索引与性能 - -基础索引: +核心索引: ```text -(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 +poi_entities (tenant_id, project_id, entity_type, status) +poi_entities (tenant_id, project_id, updated_at DESC) +poi_entities UNIQUE (tenant_id, project_id, entity_id) +entity_external_links UNIQUE + (tenant_id, project_id, platform, external_id) +poi_entities USING GIST (geom) +bus_route_stops UNIQUE (route_direction_id, stop_order) +reviews (entity_id, created_at DESC) +graph_sync_queue (status, created_at) ``` -原则: +地图接口必须按当前视窗查询,不允许一次返回全部 POI。 -- 列表接口默认服务端分页,默认 50 条,单页上限 200。 -- 大规模列表优先游标分页。 -- 只有注册为可搜索的 JSONB 字段才允许建立表达式索引。 -- 评论和来源记录达到千万级后,再按项目或时间分区。 -- 导入与图谱同步采用批量写入,避免逐行网络往返。 -- 地图接口只返回当前视窗和当前缩放级别需要的数据。 +## 15. 现有系统如何迁移 -## 19. 代码结构调整 +### 第一步:备份 -建议新增: +- 导出当前 PostgreSQL 快照。 +- 导出当前 FalkorDB RDB。 +- 统计酒店、美食、景区、交通、公交和片区数量。 + +### 第二步:建立正式关系表 + +- 引入 Alembic。 +- 补齐 PostGIS。 +- 创建正式实体和业务明细表。 +- 创建表注册、导入导出、修改日志和图谱队列表。 + +### 第三步:只迁移最终数据 + +- 从当前 FalkorDB 和已经整理好的 CSV 导出最终结果。 +- 不迁移临时爬虫数据和中间匹配过程。 +- 按 `entity_id`、高德 POI ID 和已确认平台 ID 建立关联。 +- 酒店房型、设施、评论和美食套餐拆成明细表。 + +### 第四步:上线通用后台 + +- 注册酒店、美食、景区、交通等正式表。 +- 自动生成列表、详情、编辑和导入导出页面。 +- 现有业务页面暂时不变。 + +### 第五步:切换详情和地图 + +- 荔波地图、统计、筛选和详情读取 PostgreSQL。 +- 图谱浏览器继续读取 FalkorDB。 + +### 第六步:启用图谱同步 + +- 开启 `graph_sync_queue` Worker。 +- 对比新图谱和当前图谱。 +- 确认一致后停止其他脚本直接修改 FalkorDB。 + +### 第七步:旧流程只读保留 + +现有以下数据仍可保留用于历史查看,但不属于新正式业务数据链路: + +- `raw_records` +- `candidate_entities` +- `candidate_relations` +- `review_actions` +- `publish_jobs` + +新数据不再经过这些表。 + +## 16. 代码结构 + +后端: ```text -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/ +app/data_platform/ +├── registry.py +├── record_service.py +├── query_service.py +├── import_service.py +├── export_service.py +└── graph_sync_service.py + +app/api/data_platform.py +app/workers/graph_sync_worker.py +app/migrations/ ``` -前端建议新增: +前端: ```text admin-web/src/panels/data-platform/ -├── DataModelList.tsx -├── DataTableView.tsx -├── DynamicRecordForm.tsx +├── TableList.tsx +├── DataTable.tsx +├── RecordForm.tsx ├── RecordDetail.tsx -├── ImportWizard.tsx -├── ExportCenter.tsx -├── ChangeHistory.tsx -└── GraphSyncMonitor.tsx +├── ImportDialog.tsx +├── ExportDialog.tsx +└── SyncStatus.tsx ``` -现有 `app/db.py` 暂时保留查询函数,但新增数据平台不应继续把所有 SQL 和 DDL写入单一文件。 +## 17. 验收标准 -## 20. 数据迁移路线 +### 数据 -### 阶段 0:冻结基线与盘点 +- PostgreSQL 中的实体数量与最终确认数据一致。 +- 每个实体只有一个稳定 `entity_id`。 +- 酒店房型、设施、评论等明细均能正确关联主实体。 +- 所有地图实体都有合法坐标或明确的无坐标状态。 +- 不迁移临时库存和重复文本。 -- 导出 PostgreSQL 与 FalkorDB 快照。 -- 统计每个项目的实体、关系、来源和孤立节点。 -- 冻结当前节点标签、自然键和前端所需字段。 -- 形成字段映射矩阵。 +### 后台 -云游荔波当前界面可见基线可作为参考: - -- 酒店约 1,220。 -- 美食约 1,115。 -- 交通设施约 379。 -- 景点约 100。 -- POI 总量约 2,799。 -- 片区约 384。 - -最终迁移验收必须以迁移开始时的实时扫描为准。 - -### 阶段 1:数据库基础 - -- 引入 Alembic。 -- 补齐 PostGIS 环境。 -- 建立元数据表、正式实体表、Outbox、审计和任务表。 -- 注册现有业务表,不切换页面。 - -### 阶段 2:数据回填 - -- 从现有 CSV、PostgreSQL 候选层和 FalkorDB 回填正式实体表。 -- 为所有实体分配稳定 `entity_id`。 -- 保留现有 `natural_key`。 -- 建立来源记录和字段溯源。 -- 重建实体与片区、行政区关系。 -- 输出迁移冲突清单,不静默覆盖。 - -数据取值优先级: - -```text -人工审核后的最终值 -→ 已确认融合值 -→ 高置信结构化来源 -→ 当前图谱属性 -→ 原始采集值 -``` - -### 阶段 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。 -- 数据回填使用新表,不直接覆盖旧表。 -- 页面切换使用功能开关: +- PostgreSQL 修改后自动更新 FalkorDB。 +- 同步失败不影响 PostgreSQL 保存。 +- 重复处理同一同步事件不会产生重复节点。 +- FalkorDB 可以从 PostgreSQL 全量重建。 +- 实体、片区和荔波县保持连通,不产生独立片区。 -```text -DATA_PLATFORM_READ_ENABLED -GRAPH_OUTBOX_WRITE_ENABLED -LEGACY_GRAPH_WRITE_ENABLED -``` +### 性能 -- 出现问题时可把读取切回旧接口。 -- 正式切换后,PostgreSQL 是恢复源,FalkorDB 可从 PostgreSQL 重建。 +- 列表默认 50 条分页。 +- 常规查询 P95 小于 500 ms。 +- 地图只查询当前视窗。 +- 10,000 行处理好数据能够完成校验和导入。 -## 23. 推荐实施优先级 +## 18. 实施优先级 -### P0:数据安全基础 +### P0:正式数据表 -- Alembic。 -- PostGIS。 -- 正式实体表。 -- 元数据表。 -- Outbox 与审计。 +- `poi_entities` +- 外部平台链接。 +- 酒店、美食、景区、交通明细。 +- 评论、图片和正式关系。 -### P1:通用后台 MVP +### P1:通用后台 -- 通用查询、详情、编辑。 -- 新增、软删除、恢复。 -- 模板化导入。 -- 多表导出。 +- 表和字段注册。 +- 通用增删改查。 +- 软删除和修改日志。 -### P2:云游荔波迁移 +### P2:导入导出 -- 酒店、美食、景区、交通回填。 -- 房型、设施、评论、套餐拆表。 -- 地图和详情切换。 +- 标准模板。 +- 单表和 ZIP 多表导入。 +- 错误报告。 +- 多表数据包导出。 -### P3:图谱投影 +### P3:图谱同步 -- Graph Projector。 -- 影子图验证。 -- 对账、重建和监控。 -- 停止直接双写。 +- `graph_sync_queue`。 +- 图谱 Worker。 +- 同步状态和失败重试。 -### P4:平台化增强 +### P4:现有页面切换 -- 项目管理员创建逻辑模型和扩展字段。 -- 保存视图。 -- 批量编辑。 -- 数据质量规则配置。 -- 定时导入导出。 +- 荔波地图。 +- 酒店、美食、景区、交通详情。 +- 公交线路。 +- 图谱浏览器保持不变。 -## 24. 最终原则 - -1. PostgreSQL 管完整数据,FalkorDB 管关系投影。 -2. 用户编辑业务模型,不直接编辑任意数据库表。 -3. 所有正式修改只写 PostgreSQL。 -4. 图谱通过 Outbox 异步、幂等更新。 -5. 所有数据带租户、项目、来源、版本和审计。 -6. 一对多明细使用关系表,不拼接成长文本。 -7. 批量导入先暂存、校验和预览,再正式提交。 -8. 批量导出保留多表结构、数据字典和文件哈希。 -9. 图数据库可重建,关系数据库不可丢失。 -10. 现有业务页面保持可用,按阶段平滑迁移。 +## 19. 最终原则 +1. 只允许处理完成的数据进入正式业务库。 +2. PostgreSQL 只保存最终业务数据和必要运行记录。 +3. FalkorDB 只保存最终数据的图谱投影。 +4. 通用后台只管理平台注册的正式表。 +5. 一对多数据必须拆成关系表。 +6. 导入只做校验和入库,不再做数据清洗与融合。 +7. 导出以 PostgreSQL 为唯一来源。 +8. 所有业务修改先写 PostgreSQL,再异步更新图谱。 +9. 现有候选审核流程与新正式数据平台分离。 +10. 先完成清晰、稳定的数据主链路,再增加高级能力。