跳转至

数据模型

三层定义保持一致:本体 schemasrc/ontology.py::build_schema(),驱动 /ontology 页与 RDF 导出)↔ SQLite 实例库data/xgb_wiki.db)↔ RDF 导出src/rdf_export.py)。本页按 SQLite 实际表结构(2026-09-05 实测)整理。 概念含义见 名词解释;模型取舍的设计动机见 设计方案 §2

1. 实体表(8 张)

chain 产业链

类型 说明
id TEXT PK 链标识(slug),即本体 chainId
name TEXT 如"AI算力"
description TEXT

segment 环节

类型 说明
id TEXT PK 环节标识,即本体 segmentId
chain_id TEXT 所属链(→ chain.id,segment_part_of_chain)
name TEXT 自由命名,用研报原文词汇
segment_type TEXT 角色类型,开放字典(默认六类,见 §3)
stage_tag TEXT 派生粗分组(上游/中游/下游/配套)
order_no INTEGER 链内顺序(工序型链有意义)
is_bottleneck INTEGER 卡脖子标记 0/1

theme 题材

类型 说明
plate_id INTEGER PK 板块 ID,即本体 plateId
name TEXT NOT NULL
classification TEXT 概念 / 行业 / 风格(+ 待审核/未知)
occurrence_count INTEGER 出现次数(全库聚合)
first_seen_at / last_seen_at TEXT 首次/最近提及
decay_score REAL 衰减热度(见设计方案 §2.4)
cnt_5d / cnt_10d / cnt_20d / cnt_30d INTEGER 滚动时间窗出现次数(自然日口径,截至最新研报日;src/trends.py 重算)
streak_days INTEGER 后端指标(已不用于展示):从最后一次提及往前数的连续数据日数。页面口径已改为活跃天数=所选窗口内出现过的数据日数(src/trends.theme_active_days 现算,不落列)

theme_daily 每日题材(时间轴基础表)

类型 说明
day + theme_id PK 数据日(YYYY-MM-DD)× 题材
cnt INTEGER 当日提及篇数
core_cnt INTEGER 当日强证据提及数(is_core=1)

任意时间窗(非仅 5/10/20/30)与逐日时间轴视图均由本表现算;scripts/refresh_trends.py 可脱离 MySQL 单独重算。

report_section 观点单元(每日简报基础表)

类型 说明
report_id + seq PK 所属研报 × 小节序号(枚举位置;live/兜底单卡 seq=0)
heading TEXT 小节标题(digest 正文编号小节;live 为空)
viewpoint TEXT 观点句(≤70 字:信号句 → 首句 → 标题兜底)
evidence TEXT 小节正文摘录(≤300 字,截去文末引用块)
reason_type TEXT 催化类型(同 report_stock 口径,开放字典)
is_core INTEGER 命中 is_core=1 提及(强股/编辑打标)时为 1
broker_name / source_title TEXT 可选增强:券商名出现在节内文本时归属,否则为空

配套关联表 section_stock(report_id, seq, stock_code) / section_theme(report_id, seq, theme_id)口径:个股/题材以小节内文本匹配为主源(report_stock/report_theme 是编辑打标口径,"关注"清单入库率仅 20%); live 研报为单卡(标题即观点),个股/题材取编辑打标作交叉引用。由 src/daily.py 解析, 同步管线自动回填(近 7 日,幂等);存量回填用 python scripts/refresh_daily.py。实现细节表,不入本体 schema。

stock 个股

类型 说明
code TEXT PK 股票代码
name TEXT

broker 券商

类型 说明
id INTEGER PK 自增 即本体 brokerId
name TEXT UNIQUE 如"东吴证券"

report 研报

类型 说明
id TEXT PK "{src_type}-{src_id}" 组合,即本体 reportId
src_type TEXT NOT NULL live / digest(两源合一表)
src_id INTEGER NOT NULL 源表主键
column_name TEXT 栏目(config.py 栏目字典映射)
title / summary / display_time TEXT 标题/摘要/展示时间
is_premium INTEGER APP 端标记(库里存全文,仅存档用)
content TEXT 正文 HTML(渲染原文页用)

rating_event 评级事件

类型 说明
id INTEGER PK 自增 即本体 eventId
event_type TEXT 开放字典(见 §3)
event_date TEXT 评级行动日期(来源块日期)
analyst_name / analyst_license TEXT 分析师姓名 / S 开头执业编号
broker_id INTEGER 发出方(→ broker.id,rating_event_issued_by_broker)
stock_code / stock_name TEXT 针对标的(stock_code 关联 stock.code,stock_name 为冗余显示名)
source_title TEXT 原始研报标题(事件识别依据)
report_id TEXT 引用自哪篇脱水研报(→ report.id)

注:评级事件的三条关系(targets/issuedBy/citedIn)以本表外键列物化,不再建关联表。

image_asset 图片

类型 说明
id TEXT PK URL 哈希,即本体 imageId
url TEXT UNIQUE image.xuangubao.cn 直链
context_text TEXT 正文上下文(前后各 100 字)
parse_status TEXT unparsed / pending / parsed / failed
parsed_type TEXT 解析归类:产业链图 / 数据图表 / 其他
parsed_content TEXT 解析文本结果
parsed_at TEXT 解析时间(运维字段,不进本体 schema)

2. 关系表(7 张 + 3 条物化)

全库关联总览(实体只列主键/外键与识别列,全量字段见 §1;多对多边的标签即中间表名,边上的关系属性见下方表格):

erDiagram
    chain {
        TEXT id PK "链 slug"
        TEXT name "如 AI算力"
    }
    segment {
        TEXT id PK
        TEXT chain_id FK
        TEXT segment_type "开放字典 §3"
    }
    theme {
        INTEGER plate_id PK
        TEXT name
        TEXT classification "概念/行业/风格"
    }
    theme_daily {
        TEXT day PK "数据日"
        INTEGER theme_id PK
    }
    stock {
        TEXT code PK
        TEXT name
    }
    broker {
        INTEGER id PK
        TEXT name
    }
    report {
        TEXT id PK "live-或digest-前缀"
        TEXT src_type "live / digest"
        INTEGER src_id "源表主键"
    }
    report_section {
        TEXT report_id PK
        INTEGER seq PK "小节序号"
        TEXT viewpoint "观点句"
    }
    rating_event {
        INTEGER id PK
        TEXT event_type "开放字典 §3"
        INTEGER broker_id FK
        TEXT stock_code FK
        TEXT report_id FK
    }
    image_asset {
        TEXT id PK "URL 哈希"
        TEXT url "image.xuangubao.cn 直链"
    }

    chain ||--o{ segment : segment_part_of_chain
    theme }o--o{ segment : theme_segment
    theme }o--o{ stock : theme_stock
    report }o--o{ theme : report_theme
    report }o--o{ stock : report_stock 推荐个股
    report }o--o{ broker : report_broker
    report ||--o{ image_asset : report_image
    image_asset }o--o{ theme : image_theme
    broker ||--o{ rating_event : issued_by
    stock ||--o{ rating_event : targets
    report ||--o{ rating_event : cited_in
    report ||--o{ report_section : 正文分节
    report_section }o--o{ stock : section_stock
    report_section }o--o{ theme : section_theme
    theme ||--o{ theme_daily : 每日题材
本体关系 SQLite 表 基数 关系属性
segment_part_of_chain segment.chain_id 列 多对一
theme_located_at_segment theme_segment 多对多 evidence(来源出处)
theme_associates_stock theme_stock 多对多 occurrence_count, first_seen_at, last_seen_at, decay_score, core_count(核心提及数)
report_mentions_theme report_theme 多对多 reason, reason_type, evidence_quote, is_core, weight
report_mentions_stock report_stock 多对多 同上
report_cites_broker report_broker 多对多 analyst_name, analyst_license, source_title, cited_date
rating_event_targets_stock rating_event.stock_code 列 多对一
rating_event_issued_by_broker rating_event.broker_id 列 多对一
rating_event_cited_in_report rating_event.report_id 列 多对一
report_cites_image report_image 一对多
image_depicts_theme image_theme 多对多

提及关系(report_theme / report_stock)即推荐观点记录:一行 = "某研报在某时间以某原因提及某标的"。weight 为该次提及的热度权重(核心 1.0 / 共现 0.3,写库时由 config 固化),is_core 保留原始证据强度供重新加权。

辅助表 etl_meta(key, value)built_at(构建时间)、ref_date(源数据基准时间)、 wm_live_id / wm_digest_id(增量同步水位 = 源表主键最大 id,含已删除行;旧库缺失时按 report.MAX(src_id) 自动引导)。

同步日志表 sync_logsrc/sync.py 写入,每次同步一条,含失败记录): started_at/finished_at/mode(full|incremental)/status(success|env_failed|failed_rolled_back)/ duration_s/new_live/new_digest/deleted_live/reports_before/reports_after/wm_before/wm_after/ warnings/errors。同名文件日志 data/xgb_wiki.sync.log(gitignore)同步追加,库不可写时仍留存。

3. 开放字典(data/*.json)

三个字典同构:types 为已转正类型(有序,键即枚举值),_pending 为待审区。

// data/segment_types.json —— 额外带 stageTag 映射与展示方式
{
  "types": {
    "material":       {"label": "材料资源", "stageTag": "上游", "display": "group"},
    "process":        {"label": "工艺制造", "stageTag": "中游", "display": "flow"},
    "component":      {"label": "核心部件", "stageTag": "中游", "display": "group"},
    "infrastructure": {"label": "配套设施", "stageTag": "配套", "display": "group"},
    "service":        {"label": "服务",     "stageTag": "配套", "display": "group"},
    "application":    {"label": "下游应用", "stageTag": "下游", "display": "group"}
  },
  "_pending": {}
}

// data/reason_types.json —— 催化类型:业绩/订单/政策/技术进展/供需价格/产能/事件催化/资金动向
// data/event_types.json —— 评级事件:首次覆盖/上调/下调/目标价/增持/维持
//   两者条目格式:{"types": {"<值>": {"label": "<中文名>"}}, "_pending": {}}

display: flow 的类型按 orderNo 排横向流程带;group 类型按 stageTag 分组卡片。字典修改后:/ontology 页与 RDF 导出的枚举实时跟随,存量数据需重跑 build_wiki.py 重新归类。

4. 链映射文件(data/chain_map.json)

{
  "磷化铟": {                          // 键 = 题材名
    "chain": "AI算力",                 // 链名(slugs 化后入 chain.id)
    "segment": "上游材料",             // 环节名(原文词汇)
    "segmentType": "material",         // 角色类型(对齐开放字典)
    "order": 1, "isBottleneck": true,
    "manual": true,                    // 人工确认位:重跑 LLM 不覆盖
    "evidence": [ {"reportId": 4342, "quote": "最上游的铟,95%以上是锌矿冶炼的副产品…"} ]
  }
}

当前 300 个题材已映射,全部 manual: true;ETL 载入时据此生成 chain / segment 实例与 theme_segment 关联。

5. 本体 ↔ 存储对照要点

  • 本体标识属性 → SQLite 主键一一对应(chainId→chain.id、plateId→theme.plate_id 等)
  • 实体/关系显示名中文化(name="产业链"/"提及题材"…),技术 id 保留英文(chain、report_mentions_theme);SQLite 表名、RDF 类名、代码引用均走英文 id
  • 本体 enum → SQLite TEXT 存字符串(加类型零迁移),枚举值在渲染时从字典读取
  • 本体关系属性 → 关联表列(提及关系的观点五属性 + weight)
  • image_asset.parsed_atrating_event.stock_nameetl_meta实现细节,不在本体 schema 中暴露
  • RDF 导出(data/output/xgb-a-chain/)只含 schema 层(类/属性/关系定义),实例数据不导出——实例由 Flask Web 展示(两层的分工见 设计方案 §5

6. 源库 → 本地映射(ETL 入库口径)

选股宝脱水研报源库(内网 MySQL xuangubao,只读)库内共 42 张表(上游文档口径),同步管线只读其中 5 张,结构与连接关系如下(实线 = SQL JOIN,虚线 = 字符串内 ID 的逻辑关联):

erDiagram
    live_tuoshui_news {
        BIGINT id PK "同步水位 wm_live_id"
        BIGINT msg_id "消息ID 接标签表"
        VARCHAR title "研报标题"
        MEDIUMTEXT summary "摘要"
        MEDIUMTEXT content "正文 HTML"
        VARCHAR stocks "推荐个股 代码,名称;…"
        VARCHAR plates "题材 板块ID,名称;…"
        TINYINT is_deleted "1=源端已删除"
        DATETIME msg_display_time "展示时间"
    }
    live_tuoshui_tag_related {
        BIGINT live_msg_id "= live_tuoshui_news.msg_id"
        BIGINT tag_id "栏目 LIVE_TAG_COLUMNS"
    }
    tuoshui_msgs {
        BIGINT id PK "同步水位 wm_digest_id"
        BIGINT msg_id "消息ID 接标签表"
        VARCHAR title "研报标题"
        TEXT summary "摘要"
        TEXT content "正文 HTML"
        VARCHAR related_stocks "推荐个股 代码,名称;…"
        VARCHAR plates "题材 板块ID,名称;…"
        TINYINT is_premium "APP 端付费标记"
        DATETIME msg_created_at "创建时间"
    }
    tuoshui_tag_related {
        BIGINT msg_id "= tuoshui_msgs.msg_id"
        BIGINT tag_id "栏目 DIGEST_TAG_COLUMNS"
    }
    plate_rank_infos {
        BIGINT plate_id PK "即本地 theme.plate_id"
        VARCHAR plate_name "题材名"
        VARCHAR classification "概念/行业/风格"
    }

    live_tuoshui_news ||--o{ live_tuoshui_tag_related : "n.msg_id = t.live_msg_id 取栏目"
    tuoshui_msgs ||--o{ tuoshui_tag_related : "m.msg_id = t.msg_id 取栏目"
    live_tuoshui_news }o..o{ plate_rank_infos : "plates 内板块 ID 逻辑关联"
    tuoshui_msgs }o..o{ plate_rank_infos : "plates 内板块 ID 逻辑关联"

要点:

  • 字段口径:同步依赖列以 src/sync.py::REQUIRED_MYSQL_COLUMNS 为准——每次同步启动前对 information_schema 做存在性校验(最近一次通过 2026-09-03);连接关系按 src/etl.pyLIVE_SQL / DIGEST_SQL 实际 JOIN 绘制,两处标签表均经 msg_id 关联(不是主键 id)。
  • stocks / plates 是分隔符字符串"代码,名称;…" / "板块ID,名称;…"),源库无个股/题材外键——实体与关系由 ETL 解析字符串后落库,plate_rank_infos 提供 plate_id → 名称/分类的主数据。
  • ⚠️ 上游 2_xuangubao 项目《脱水研报数据库Wiki文档》部分列名已过时:其写 live_tuoshui_tag_related.news_idtuoshui_tag_related.tuoshui_idtuoshui_msgs.stocks,实际为 live_msg_idmsg_idrelated_stocks(且缺 is_premium)。参考该文档时以本节同步校验口径为准。
  • messagesoriginal_msgs 等其余表本管线不读取。

各表如何落到本地 SQLite——取数 SQL 与解析在 src/etl.pyLIVE_SQL / DIGEST_SQL / apply_live_deletions),同步前置校验所需列清单在 src/sync.py::REQUIRED_MYSQL_COLUMNS

源(MySQL xuangubao) → 本地(SQLite) 说明
live_tuoshui_news(is_deleted=0)⨝ live_tuoshui_tag_related(经 msg_id,tag_id→栏目字典 LIVE_TAG_COLUMNS report(src_type='live') display_time 取 msg_display_time;tag_id 缺省按未分类
tuoshui_msgstuoshui_tag_related(经 msg_id,tag_id→DIGEST_TAG_COLUMNS report(src_type='digest') is_premium 直传;display_time 取 msg_created_at
live_tuoshui_news.stocks / tuoshui_msgs.related_stocks(解析 "代码,名称;…") stock + report_stock(is_core=0, weight=0.3) 编辑打标"关注"清单,入库率约 20%
digest 每日强股结构段(tag_id=4,正文解析) report_stock(is_core=1, weight=1.0, evidence_quote) 强证据推荐
*.plates(解析 "ID,名称;…")+ plate_rank_infos(plate_id→name/classification) theme + report_theme(is_core=0, weight=0.3) 题材主数据来自 plate_rank_infos
研报标题正则(extract_title_broker broker + report_broker 券商引用
正文图片直链(extract_images image_asset + report_image 待解析队列
正文评级段解析 rating_event 事件日期/分析师/券商
正文分节 + 提及原因提取(src/daily.py 分节、src/viewpoint.py 归因;同步自动回填近 7 日,幂等;全量 python scripts/refresh_daily.py report_section + section_stock + section_theme,并 UPDATE report_stock / report_theme 的 reason/reason_type 观点单元口径,正文文本匹配为主源
live_tuoshui_news.is_deleted=1 删除本地对应 report(连同 report_theme/stock/broker/image、rating_event、report_section、section_stock/theme) 增量同步检测源端删除
—(本地派生/配置,无源表) theme_stock 聚合与 theme.occurrence_count/decay_scoresrc/etl.py 重算);theme_dailytheme.cnt_*src/trends.py);chain/segment/theme_segmentdata/chain_map.json);etl_meta(水位);sync_log(同步日志) 聚合、链映射配置与运维字段