chore: batch commit remaining changes
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s

Includes search engine improvements, Alembic migrations,
new services (pubmed_daily_update, query_expansion),
frontend updates, and documentation sync.
This commit is contained in:
34047007@qq.com
2026-07-27 08:35:12 +08:00
parent 35b0a5c565
commit 62ca8fa6b8
82 changed files with 314248 additions and 1519 deletions
+48 -11
View File
@@ -2,7 +2,7 @@
## 日期时间类型规范
**全部使用 `TIMESTAMPTZ`**固定日期字段(出生日期、发表日期、批准日期、排期日期)用 `DATE`
**TIMESTAMPTZ 为主**日期字段(出生日期、发表日期、批准日期、排期日期、PubMed 纯日期字段)用 `DATE`
```sql
-- ✅ 正确
@@ -152,6 +152,7 @@ CREATE TABLE global_literature (
issue VARCHAR(50), -- 期号 【PubMed: Article/Journal/JournalIssue/Issue】
pages VARCHAR(100), -- 页码 【PubMed: Article/Pagination/MedlinePgn】
pub_date DATE, -- 纸质出版日期(期刊卷期日期,若纯电子刊则为电子日期)【PubMed: Article/Journal/JournalIssue/PubDate 组合 Year+Month+Day】
print_date DATE, -- 纯纸质出版见刊日期(仅纸质版有值,从 PubDate 解析并经 history ppdat 校验)【PubMed: PubmedData/History/PubMedPubDate[@PubStatus="ppdat"]】
pub_year INTEGER, -- 出版年份 【PubMed: 从 PubDate/Year 提取】
pub_types JSONB NOT NULL DEFAULT '[]', -- 出版类型列表 【PubMed: Article/PublicationTypeList/PublicationType】
mesh_headings JSONB NOT NULL DEFAULT '[]', -- MeSH 标引词列表(含 DescriptorName+QualifierName)【PubMed: MeshHeadingList/MeshHeading】
@@ -160,7 +161,7 @@ CREATE TABLE global_literature (
pmc_id VARCHAR(20), -- PMC IDPMC1234567)【PubMed: ArticleId[@IdType="pmc"]】
is_oa BOOLEAN NOT NULL DEFAULT FALSE, -- 是否开放获取【根据 pmc_id 或 PMC OA API 判定,非 PubMed 原始字段】
keywords JSONB NOT NULL DEFAULT '[]', -- 作者关键词 【PubMed: Article/KeywordList/Keyword】
pubmed_revised TIMESTAMPTZ, -- PubMed 数据库修订日期(NLM 说明不可靠)【PubMed: MedlineCitation/DateRevised】
pubmed_revised DATE, -- PubMed 数据库修订日期(NLM 说明不可靠)【PubMed: MedlineCitation/DateRevised】
grants JSONB NOT NULL DEFAULT '[]', -- 基金资助信息 【PubMed: Article/GrantList/Grant】
cited_by_count INTEGER NOT NULL DEFAULT 0, -- 被引次数 【来自 NCBI elink 接口 pubmed_pubmed_citedin,非 PMID 固定字段】
full_text_sections JSONB, -- PMC OA 全文结构化段落【来自 PMC OA Service API JATS XML,非 PubMed EFetch】
@@ -169,7 +170,8 @@ CREATE TABLE global_literature (
study_design JSONB, -- 研究设计分类 {primary: "RCT", sub: "Phase 3", design: "parallel"}AI 推断,非 PubMed 原始字段)
pico JSONB, -- PICO 结构化抽取(AI 推断,非 PubMed 原始字段)
citation_status VARCHAR(20), -- MedlineCitation 状态 【PubMed: MedlineCitation/@Status 属性,如 "MEDLINE"/"PubMed-not-MEDLINE"/"In-Data-Review"】
date_completed TIMESTAMPTZ, -- MeSH 标引完成日期 【PubMed: MedlineCitation/DateCompleted】
date_completed DATE, -- NLM 整条编目记录完成日(含 MeSH 标引在内的全部处理完成)
meshed_date DATE, -- MeSH 数据版本日期。通常=date_completed(MeSH 是编目最后一步,两者同天)。年更刷新后=新的 date_completed。NULL=无MeSH(in-process/publisher)。NOT datetime.now()
retracted BOOLEAN NOT NULL DEFAULT FALSE, -- 是否已撤稿【依据 PubMed: Article/PublicationTypeList/PublicationType[.="Retracted Publication"]】
retraction_details JSONB, -- 撤稿详情(关联撤稿文献等)【PubMed: CommentsCorrectionsList/CommentsCorrections[@RefType="retractionof"/"retractionin"]】
license_info JSONB, -- 许可证信息(来自 PMC OA XML 或出版商元数据,非 PubMed 原始字段)
@@ -180,10 +182,15 @@ CREATE TABLE global_literature (
negative_result_details JSONB, -- 阴性结果详情(AI 抽取,非 PubMed 原始字段)
ai_summary JSONB, -- AI 摘要(内部,非 PubMed 字段)
chemical_list JSONB NOT NULL DEFAULT '[]', -- 化学物质列表 【PubMed: ChemicalList/Chemical】
investigators JSONB NOT NULL DEFAULT '[]', -- 研究者列表 [{family,given,affiliation,identifiers}] 【PubMed: InvestigatorList/Investigator】
personal_name_subjects JSONB NOT NULL DEFAULT '[]', -- 作为主题的人名 [{family,given}] 【PubMed: PersonalNameSubjectList/PersonalNameSubject】
publication_notes JSONB NOT NULL DEFAULT '[]', -- 出版注释列表 ["note1","note2"] 【PubMed: PubmedData/PublicationNote】
gene_symbols JSONB NOT NULL DEFAULT '[]', -- 基因符号列表 【PubMed: SupplMeshList/SupplMeshName;部分来自 Gene Symbol 或 ChemicalList】
num_refs INTEGER, -- 参考文献数量(来自 PMC 全文或解析,非 PubMed EFetch 原始字段)
publication_status VARCHAR(30), -- 出版状态 【PubMed: PubmedData/PublicationStatus,取值 epublish/ppublish/aheadofprint】
article_date DATE, -- 电子出版日期(先于印刷版)【PubMed: Article/ArticleDate】
create_date DATE, -- PubMed 记录创建日期(纯日期,无需时间)【PubMed: PubmedData/History/PubMedPubDate[@PubStatus="pubmed"] / MedlineCitation/DateCreated】
entrez_date DATE, -- PubMed 收录日期(纯日期,无需时间)【PubMed: PubmedData/History/PubMedPubDate[@PubStatus="entrez"]】
databank_list JSONB NOT NULL DEFAULT '[]', -- 数据库引用列表(如 ClinicalTrials.gov)【PubMed: Article/DataBankList/DataBank】
suppl_mesh_list JSONB NOT NULL DEFAULT '[]', -- 补充 MeSH 词列表(含物质名)【PubMed: SupplMeshList/SupplMeshName】
source VARCHAR(30) NOT NULL DEFAULT 'pubmed_ftp', -- 数据来源(内部追踪用:pubmed_api/pubmed_ftp,非 PubMed 字段)
@@ -197,16 +204,30 @@ CREATE INDEX idx_gl_journal_iso ON global_literature(journal_iso);
CREATE INDEX idx_gl_created ON global_literature(created_at);
CREATE INDEX idx_gl_pmc_id ON global_literature(pmc_id);
CREATE INDEX idx_gl_search_tsv ON global_literature USING gin(search_tsv);
CREATE INDEX ix_gl_doi ON global_literature(doi);
CREATE INDEX ix_gl_language ON global_literature(language);
CREATE INDEX ix_gl_retracted ON global_literature(retracted);
CREATE INDEX ix_gl_pub_types_gin ON global_literature USING gin(pub_types);
CREATE INDEX ix_gl_study_design_gin ON global_literature USING gin(study_design);
CREATE INDEX ix_gl_grants_gin ON global_literature USING gin(grants);
CREATE INDEX ix_gl_chemical_list_gin ON global_literature USING gin(chemical_list);
CREATE INDEX ix_gl_citation_status ON global_literature(citation_status); -- 筛选:MEDLINE only
CREATE INDEX ix_gl_is_oa ON global_literature(is_oa); -- 筛选:Free full text
CREATE INDEX ix_gl_is_negative ON global_literature(is_negative_result); -- 筛选:阴性结果
CREATE INDEX ix_gl_is_preprint ON global_literature(is_preprint); -- 筛选:Exclude preprints
CREATE INDEX ix_gl_mesh_headings ON global_literature USING gin(mesh_headings); -- 筛选:Species/Sex/Age JSONB @> 查询
CREATE INDEX ix_global_literature_journal_iso_trgm ON global_literature USING gin(journal_iso gin_trgm_ops); -- PubMed [TA] ILIKE 兜底
```
> 预估值:约 600 万条(PubMed 中肿瘤相关的历史累积),单表不需要分区
**全文搜索:**
> `search_tsv``TSVECTOR` 类型,GIN 索引 `ix_gl_search_tsv`)由 PG 触发器自动维护,包含 **title + abstract + authors.family + authors.affiliation**。所有搜索(普通搜索 `field="all"` 和高级搜索字段选择中的作者/机构)均走 tsvector `@@` `plainto_tsquery()`ILIKE 仅作为兜底(NULL tsvector 记录)。触发器 `trg_global_literature_tsv` 在 INSERT 或 UPDATE title/abstract 时触发重建。千万级无压力。
> `search_tsv``TSVECTOR` 类型,GIN 索引 `ix_gl_search_tsv`)由 PG 触发器 `trg_global_literature_tsv` 自动维护。使用 `setweight()` 区分字段权重:**title=A 权重、abstract=B 权重、authors.family=A 权重**。所有搜索(普通搜索 `field="all"` 和高级搜索字段选择中的作者/机构)均走 tsvector `@@` `plainto_tsquery()`ILIKE 仅作为兜底(NULL tsvector 记录)。触发器在 INSERT 或 UPDATE title/abstract/authors 时触发重建。千万级无压力。
>
> > **GIN 索引重建说明:** 迁移 `f80dca5baa02`add_pico_column_to_review_literatures)的 `upgrade()` 误将 `ix_gl_search_tsv` 删除后未重建,导致所有 tsvector 搜索走全表扫描。迁移 `0314f4d28728`rebuild_search_tsv_gin_index)已修复,紧跟在 `e341edea85e2` 之后。
**日期字段说明:** `pub_date`印刷/电子出版日期)+ `article_date`(电子出版日期,实际上线时间)+ `date_completed`(MeSH 标引完成日,检索质量控制用)+ `pubmed_revised`NLM 定义为不可靠)+ `publication_status`epublish / ppublish / aheadofprint 出版阶段标识)。
**日期字段说明:** `pub_date`[DP] 出版日期 = LEAST(EPDAT, PPDAT))由 `article_date`[EPDAT] 电子出版日期)+ `print_date`([PPDAT] 纸质出版日期)的最小值计算得出。`create_date`[CRDT] PubMed 记录创建日期)+ `entrez_date`[EDAT] PubMed 入库日期)+ `date_completed`(NLM 整条编目记录完成日,含 MeSH 在内的全部处理完成,实践中作为 [MHDA] 近似值)。`meshed_date` 是系统内部字段,记录 MeSH 数据的版本日期:通常=`date_completed`(MeSH 是编目最后一步,两者同天);年更刷新后设为新的 `date_completed`。NULL=无 MeSH。`pubmed_revised`NLM 定义为不可靠)`publication_status`epublish / ppublish / aheadofprint 出版阶段标识)。
> **注意:** `create_date`、`entrez_date`、`date_completed`、`meshed_date`、`pubmed_revised` 五个字段原为 TIMESTAMPTZ,现统一为 DATE 类型。原因是 NLM 所有日期来源(DateCompleted、History/PubMedPubDate、DateRevised、ArticleDate、DateCreated)仅提供 Y/M/D,无时间分量。DATE 类型更精确地反映语义,减少存储开销。
**PubMed 对应 XML 结构速查:**
@@ -223,24 +244,35 @@ CREATE INDEX idx_gl_search_tsv ON global_literature USING gin(search_tsv);
| `volume` | `Article/Journal/JournalIssue/Volume` | 卷 |
| `issue` | `Article/Journal/JournalIssue/Issue` | 期 |
| `pages` | `Article/Pagination/MedlinePgn` | 页码范围 |
| `pub_date` | `Article/Journal/JournalIssue/PubDate` | 仅取 Year/Month/Day 组合 |
| `pub_date` | `Article/Journal/JournalIssue/PubDate` 取 Year/Month/Day 组合,与 print_date/articledate LEAST 计算 | [DP] 出版日期 = COALESCE(LEAST(EPDAT, PPDAT), 任一可用值) |
| `print_date` | `PubmedData/History/PubMedPubDate[@PubStatus="ppdat"]``Journal/JournalIssue/PubDate` | [PPDAT] 纸质出版日期 |
| `pub_year` | `PubDate/Year` | 单独提取年份 |
| `pub_types` | `Article/PublicationTypeList/PublicationType` | 可多个,含 "Retracted Publication" |
| `mesh_headings` | `MeshHeadingList/MeshHeading` | DescriptorName(UI) + QualifierName(UI) |
| `language` | `Article/Language` | 2-3 字母 ISO 语言码 |
| `pmc_id` | `ArticleId[@IdType="pmc"]` | 带 "PMC" 前缀 |
| `keywords` | `Article/KeywordList/Keyword` | 作者关键词(非 MeSH |
| `pubmed_revised` | `MedlineCitation/DateRevised` | 不可靠,不应视为修订依据 |
| `pubmed_revised` | `MedlineCitation/DateRevised` | 不可靠,不应视为修订依据。仅 Year/Month/DayDATE 类型 |
| `grants` | `Article/GrantList/Grant` | GrantID + Agency + Country |
| `chemical_list` | `ChemicalList/Chemical` | RegistryNumber + NameOfSubstance(UI) |
| `suppl_mesh_list` | `SupplMeshList/SupplMeshName` | 补充概念词 |
| `databank_list` | `Article/DataBankList/DataBank` | DataBankName + AccessionNumberList |
| `trial_reg` | `Article/DataBankList/DataBank` | 从中提取 NCT/EudraCT/ChiCTR |
| `citation_status` | `MedlineCitation/@Status` | "MEDLINE","PubMed-not-MEDLINE","In-Data-Review","PubMed" |
| `date_completed` | `MedlineCitation/DateCompleted` | MeSH 标引完成日 |
| `retraction_details` | `CommentsCorrectionsList/CommentsCorrections[@RefType="retractionof" or "retractionin"]` | 撤稿关联关系 |
| `date_completed` | `MedlineCitation/DateCompleted` | NLM 整条编目记录完成日(含 MeSH 标引在内的全部处理完成)。实践中作为 [MHDA] 近似值使用。仅 Year/Month/DayDATE 类型 |
| `meshed_date` | 系统内部字段 | MeSH 数据版本日期。通常=date_completed(MeSH 是编目最后一步,两者同天)。年更后=新的 date_completed。NULL=无MeSH。DATE 类型 |
| `publication_status` | `PubmedData/PublicationStatus` | "epublish" / "ppublish" / "aheadofprint" |
| `article_date` | `Article/ArticleDate` | 电子出版日期(印刷版之前的日期)|
| `article_date` | `Article/ArticleDate` | [EPDAT] 电子出版日期(印刷版之前的日期)|
| `create_date` | `PubmedData/History/PubMedPubDate[@PubStatus="pubmed"]``MedlineCitation/DateCreated` | [CRDT] PubMed 记录创建日期。仅 Year/Month/DayDATE 类型 |
| `entrez_date` | `PubmedData/History/PubMedPubDate[@PubStatus="entrez"]` | [EDAT] PubMed 收录日期。仅 Year/Month/DayDATE 类型 |
| `pharmacological_actions` | `MedlineCitation/ChemicalList/PharmacologicalAction/NameOfSubstance` | [PA] 药理作用 [{name, ui}] |
| `cois_statement` | `Article/CoiStatement` | [COIS] 利益冲突声明 |
| `vernacular_title` | `Article/VernacularTitle` | [TT] 非英文标题的拉丁转写 |
| `is_preprint` | DOI prefix + journal name 检测 | 是否为预印本(非 PubMed 原始字段) |
| `auid_data` | `Author/Identifier[Source="ORCID"]` | [AUID] 作者标识符 [{type, value, author_index}] |
| `investigators` | `InvestigatorList/Investigator` | [IR] 多中心试验研究者 [{family, given, affiliation, identifiers}] |
| `personal_name_subjects` | `PersonalNameSubjectList/PersonalNameSubject` | [PS] 作为主题的人名 [{family, given}] |
| `publication_notes` | `PubmedData/PublicationNote` | [PUBN] 出版注释列表 |
### 2.2 global_tags — 肿瘤科标签树
@@ -254,6 +286,9 @@ CREATE TABLE global_tags (
level INTEGER NOT NULL DEFAULT 1, -- 层级深度
parent_id UUID REFERENCES global_tags(id) ON DELETE CASCADE, -- 父标签ID
tree_number VARCHAR(100), -- MeSH 树编号
entry_terms JSONB, -- MeSH 入口词列表(~200kNLM desc2025.asc 导入,用于 ATM 自动术语映射)
source VARCHAR(20) DEFAULT 'auto', -- 数据来源:manual(手工)/mesh(C04批量)/auto(管道懒创建)
is_active BOOLEAN DEFAULT FALSE, -- 是否已确认(True=用户可见,False=仅后台可见)
tag_category VARCHAR(30) NOT NULL DEFAULT 'cancer', -- 标签分类
sort_order INTEGER DEFAULT 0, -- 排序序号
icon VARCHAR(50), -- 图标名
@@ -713,7 +748,7 @@ CREATE TABLE ai_provider_config (
```sql
CREATE TABLE pipeline_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 运行ID
run_type VARCHAR(20) NOT NULL, -- 运行类型:daily/weekly/manual
run_type VARCHAR(20) NOT NULL, -- 运行类型:daily_ftp_update / baseline_import / daily_citation_update / manual_full / etc.
status VARCHAR(20) NOT NULL DEFAULT 'running', -- 状态:running/completed/failed
files_processed INTEGER DEFAULT 0, -- 处理文件数
articles_total INTEGER DEFAULT 0, -- 总文献数
@@ -724,6 +759,8 @@ CREATE TABLE pipeline_runs (
users_matched INTEGER DEFAULT 0, -- 匹配用户数
feeds_generated INTEGER DEFAULT 0, -- 生成 Feed 数
error_log TEXT, -- 错误日志
processed_date DATE, -- FTP 增量检查点:已处理的 EDAT 日期(2026-07-25 新增)
metadata JSON, -- 扩展元数据(基线年份、文件序号范围等)(2026-07-25 新增)
started_at TIMESTAMPTZ, -- 开始时间
completed_at TIMESTAMPTZ, -- 完成时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间