# 中华文明全图鉴——数据模型文档 ## 1. 数据模型目标 本数据模型服务于第一阶段 PC Web 地图站 MVP,覆盖文物、机构、位置、标签、数字资产、权限、操作日志和地图查询所需的核心数据结构。 ## 2. 设计原则 - **PostgreSQL + PostGIS**:结构化业务数据与地理数据统一存储。 - **多位置记录**:一件文物可有多条位置记录,前台默认展示最新有效位置。 - **敏感坐标分级**:同一位置可保存精确坐标和公开展示坐标或模糊区域。 - **标签可扩展**:基础标签固定,扩展标签通过标签分类和标签类型支持动态扩展。 - **操作可追溯**:核心对象修改必须记录操作日志。 - **字段可演进**:对角色卡、故事、AI 推荐等中后期能力保留扩展空间。 ## 3. 枚举定义 ## 3.1 artifact_category | 值 | 含义 | |---|---| | bronze | 青铜 | | painting_calligraphy | 书画 | | porcelain | 陶瓷 | | jade | 玉器 | | gold_silver | 金银器 | | lacquer | 漆器 | | textile | 织绣 | | stone_carving | 石刻 | | wood_carving | 木雕 | | dunhuang | 敦煌 | | ancient_book | 古籍 | | other | 其他 | ## 3.2 artifact_level | 值 | 含义 | |---|---| | level_1 | 一级 | | level_2 | 二级 | | level_3 | 三级 | | general | 一般 | | unknown | 未定级 | ## 3.3 artifact_status | 值 | 含义 | |---|---| | at_home | 在家 | | away | 离家 | | in_transit | 在途 | | unknown | 未知 | ## 3.4 location_type | 值 | 含义 | |---|---| | domestic | 国内 | | overseas | 海外 | | unknown | 未知 | | in_transit | 在途 | ## 3.5 location_precision | 值 | 含义 | |---|---| | exact_room | 展厅 | | exact_building | 建筑 | | city | 城市 | | country | 国家 | | region | 区域 | ## 3.6 display_status | 值 | 含义 | |---|---| | on_display | 在展 | | in_storage | 库藏 | | loaned | 外借 | | repairing | 修复中 | | touring | 巡展中 | | unknown | 未知 | ## 3.7 source_type | 值 | 含义 | |---|---| | institution_feed | 机构直供 | | manual_entry | 后台录入 | | user_report | 用户报告 | | expert_verify | 专家验证 | | public_source | 公开资料 | ## 3.8 publish_status | 值 | 含义 | |---|---| | draft | 草稿 | | pending | 待发布 | | published | 已发布 | | archived | 已归档 | | rejected | 已驳回 | ## 3.9 tag_value_type | 值 | 含义 | |---|---| | single | 单选 | | multiple | 多选 | | boolean | 布尔 | | text | 文本 | | number | 数字 | ## 4. 核心表设计 ## 4.1 users 后台账号和前台用户统一存储,MVP 优先支持后台账号。 | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 用户 ID | | username | varchar(100) | unique | 登录名 | | phone | varchar(30) | nullable | 手机号 | | email | varchar(255) | nullable | 邮箱 | | password_hash | varchar(255) | nullable | 密码 hash | | nickname | varchar(100) | nullable | 昵称 | | avatar_url | text | nullable | 头像 | | user_type | varchar(30) | not null | admin、institution、public、expert、editor | | institution_id | uuid | fk nullable | 绑定机构 | | is_active | boolean | default true | 是否启用 | | created_at | timestamptz | not null | 创建时间 | | updated_at | timestamptz | not null | 更新时间 | ## 4.2 roles | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 角色 ID | | code | varchar(50) | unique | 角色编码 | | name | varchar(100) | not null | 角色名称 | | description | text | nullable | 描述 | | created_at | timestamptz | not null | 创建时间 | ## 4.3 permissions | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 权限 ID | | code | varchar(100) | unique | 权限编码 | | name | varchar(100) | not null | 权限名称 | | module | varchar(50) | not null | 模块 | | created_at | timestamptz | not null | 创建时间 | ## 4.4 user_roles | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | user_id | uuid | pk fk | 用户 ID | | role_id | uuid | pk fk | 角色 ID | | created_at | timestamptz | not null | 创建时间 | ## 4.5 institutions | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 机构 ID | | name | varchar(255) | not null | 机构名称 | | short_name | varchar(100) | nullable | 简称 | | code | varchar(100) | unique nullable | 机构编码 | | institution_type | varchar(50) | not null | museum、research、private、other | | country | varchar(100) | not null | 国家 | | province | varchar(100) | nullable | 省份 | | city | varchar(100) | nullable | 城市 | | address | text | nullable | 地址 | | location | geography(Point, 4326) | nullable | 机构坐标 | | official_website | text | nullable | 官网 | | description | text | nullable | 简介 | | is_verified | boolean | default false | 是否认证 | | publish_status | publish_status | not null | 发布状态 | | created_at | timestamptz | not null | 创建时间 | | updated_at | timestamptz | not null | 更新时间 | 索引: - `idx_institutions_location_gist`:`GIST(location)` - `idx_institutions_name`:`name` - `idx_institutions_country_city`:`country, city` ## 4.6 artifacts | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 文物 ID | | unified_map_id | varchar(50) | unique | 全图唯一编码 | | name | varchar(255) | not null | 文物名称 | | alternative_names | text[] | nullable | 别名 | | category | artifact_category | not null | 门类 | | dynasty | varchar(100) | nullable | 年代 | | level | artifact_level | default unknown | 文物级别 | | material | varchar(255) | nullable | 材质 | | dimensions | varchar(255) | nullable | 尺寸 | | current_status | artifact_status | default unknown | 在家、离家、在途、未知 | | home_institution_id | uuid | fk nullable | 原属或现属国内机构 | | summary | text | nullable | 简介 | | story_hook | varchar(255) | nullable | 一句话故事钩子 | | persona_quote | varchar(255) | nullable | 趣味化角色短句 | | publish_status | publish_status | not null | 发布状态 | | created_by | uuid | fk nullable | 创建人 | | updated_by | uuid | fk nullable | 更新人 | | created_at | timestamptz | not null | 创建时间 | | updated_at | timestamptz | not null | 更新时间 | 索引: - `idx_artifacts_name`:`name` - `idx_artifacts_category`:`category` - `idx_artifacts_dynasty`:`dynasty` - `idx_artifacts_status`:`current_status` - `idx_artifacts_home_institution`:`home_institution_id` ## 4.7 artifact_locations | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 位置记录 ID | | artifact_id | uuid | fk not null | 文物 ID | | location_type | location_type | not null | 国内、海外、未知、在途 | | institution_id | uuid | fk nullable | 关联机构 | | precise_location | geography(Point, 4326) | nullable | 精确坐标 | | public_location | geography(Point, 4326) | nullable | 前台可展示坐标 | | fuzzy_area | geography(Polygon, 4326) | nullable | 模糊区域 | | precision | location_precision | not null | 坐标精度 | | floor_plan_ref | varchar(255) | nullable | 展厅平面图编号 | | room_name | varchar(255) | nullable | 展厅名称 | | cabinet_no | varchar(100) | nullable | 展柜编号 | | display_status | display_status | default unknown | 展出状态 | | source_type | source_type | not null | 来源类型 | | source_description | text | nullable | 来源说明 | | discoverer_user_id | uuid | fk nullable | 发现者 | | is_current | boolean | default false | 是否当前有效位置 | | verified_at | timestamptz | nullable | 审核通过时间 | | valid_from | timestamptz | nullable | 有效开始 | | valid_until | timestamptz | nullable | 有效截止 | | created_by | uuid | fk nullable | 创建人 | | created_at | timestamptz | not null | 创建时间 | | updated_at | timestamptz | not null | 更新时间 | 索引: - `idx_artifact_locations_artifact_id`:`artifact_id` - `idx_artifact_locations_institution_id`:`institution_id` - `idx_artifact_locations_public_location_gist`:`GIST(public_location)` - `idx_artifact_locations_precise_location_gist`:`GIST(precise_location)` - `idx_artifact_locations_fuzzy_area_gist`:`GIST(fuzzy_area)` - `idx_artifact_locations_current`:`artifact_id, is_current` 当前位置规则: 1. 优先取 `is_current = true` 的位置记录。 2. 若存在多条,则取 `verified_at` 最新的一条。 3. 若 `valid_until` 已过期,则不作为前台当前位置展示。 4. 无权限用户只能读取 `public_location` 或 `fuzzy_area`。 ## 4.8 tag_categories | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 标签分类 ID | | code | varchar(100) | unique | 分类编码 | | name | varchar(100) | not null | 分类名称 | | description | text | nullable | 描述 | | sort_order | int | default 0 | 排序 | | is_system | boolean | default false | 是否系统内置 | | created_at | timestamptz | not null | 创建时间 | ## 4.9 tags | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 标签 ID | | category_id | uuid | fk not null | 分类 ID | | code | varchar(100) | unique | 标签编码 | | name | varchar(100) | not null | 展示名 | | value_type | tag_value_type | not null | 值类型 | | description | text | nullable | 描述 | | color | varchar(20) | nullable | 展示颜色 | | icon | varchar(100) | nullable | 图标 | | is_system | boolean | default false | 是否系统标签 | | is_active | boolean | default true | 是否启用 | | sort_order | int | default 0 | 排序 | | created_at | timestamptz | not null | 创建时间 | | updated_at | timestamptz | not null | 更新时间 | ## 4.10 artifact_tags | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 关系 ID | | artifact_id | uuid | fk not null | 文物 ID | | tag_id | uuid | fk not null | 标签 ID | | value_text | text | nullable | 文本值 | | value_number | numeric | nullable | 数字值 | | confidence | numeric(5,2) | nullable | 置信度 | | source_type | source_type | not null | 来源 | | review_status | varchar(30) | default approved | pending、approved、rejected | | created_by | uuid | fk nullable | 创建人 | | created_at | timestamptz | not null | 创建时间 | 唯一约束: - `artifact_id, tag_id, value_text` ## 4.11 digital_assets | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 资产 ID | | artifact_id | uuid | fk nullable | 文物 ID | | institution_id | uuid | fk nullable | 机构 ID | | asset_type | varchar(50) | not null | image、audio、video、model_3d、document | | title | varchar(255) | nullable | 标题 | | url | text | not null | 资源地址 | | thumbnail_url | text | nullable | 缩略图 | | mime_type | varchar(100) | nullable | MIME 类型 | | size_bytes | bigint | nullable | 文件大小 | | copyright_owner | varchar(255) | nullable | 版权方 | | license_scope | text | nullable | 授权范围 | | sort_order | int | default 0 | 排序 | | created_by | uuid | fk nullable | 上传人 | | created_at | timestamptz | not null | 创建时间 | ## 4.12 operation_logs | 字段 | 类型 | 约束 | 说明 | |---|---|---|---| | id | uuid | pk | 日志 ID | | operator_id | uuid | fk nullable | 操作人 | | operator_role | varchar(50) | nullable | 操作角色 | | action | varchar(100) | not null | 操作类型 | | target_type | varchar(100) | not null | 目标类型 | | target_id | uuid | nullable | 目标 ID | | before_data | jsonb | nullable | 变更前 | | after_data | jsonb | nullable | 变更后 | | ip_address | varchar(50) | nullable | IP | | user_agent | text | nullable | UA | | created_at | timestamptz | not null | 创建时间 | 索引: - `idx_operation_logs_operator_id` - `idx_operation_logs_target` - `idx_operation_logs_created_at` ## 5. 地图接口视图建议 ## 5.1 artifact_current_locations_view 用途:地图点位与文物详情快速读取。 字段: - artifact_id - artifact_name - category - dynasty - level - current_status - story_hook - persona_quote - institution_id - institution_name - public_location - fuzzy_area - precision - display_status - source_type - verified_at ## 5.2 map_summary_view 用途:顶部统计栏。 字段: - total_artifacts - domestic_count - overseas_count - on_display_count - in_storage_count - loaned_count - unknown_location_count ## 6. Seed 数据建议 ## 6.1 系统角色 - admin - institution_user - editor - expert - public_user ## 6.2 标签分类 - basic_attribute - circulation_experience - emotional_attribute - content_derivation ## 6.3 基础标签 - 门类 - 年代 - 级别 - 材质 - 功能 - 流失状态 - 回归状态 - 南迁北归 - 修复经历 - 数字化经历 - 情绪主调 - 适合年龄 ## 7. 数据校验规则 ## 7.1 文物校验 - `name` 必填。 - `category` 必须在枚举范围内。 - `unified_map_id` 必须唯一。 - `current_status` 必须在枚举范围内。 - 发布时至少需要一条当前有效位置或明确标记为未知位置。 ## 7.2 位置校验 - `location_type = domestic` 时优先要求 `institution_id`。 - `precision = exact_room` 时需要 `room_name` 或 `floor_plan_ref`。 - 公开坐标必须符合经纬度范围。 - 存在敏感位置时,前台不得返回 `precise_location`。 - `valid_until` 小于当前时间时不作为当前有效位置。 ## 7.3 标签校验 - 系统标签不可随意删除。 - 禁用标签不允许新增绑定。 - AI 推荐标签必须人工确认后才能公开展示。 ## 8. 测试要点 - migration 可从空库完整执行。 - PostGIS 扩展可启用。 - 空间索引创建成功。 - 1000 件文物点位范围查询响应时间符合要求。 - 敏感坐标在游客权限下不可见。 - 机构用户无法访问其他机构文物。 - 文物标签绑定、解绑和筛选结果正确。