OncoLit: a multi-tenant oncology literature search, feed, and collaboration platform. Built with FastAPI + Vue 3 + PostgreSQL. Includes PubMed pipeline, drug approvals, AI summaries, and systematic review tools.
618 lines
20 KiB
Markdown
618 lines
20 KiB
Markdown
# 肿瘤科标签体系设计
|
||
|
||
## 设计原则
|
||
|
||
- 基于 MeSH (Medical Subject Headings),从 NLM XML 导入
|
||
- MeSH C04 (Neoplasms) 为核心,交叉 E02(治疗) + D27(药物)
|
||
- 补充 MeSH 未覆盖的临床维度:驱动基因/靶点、临床终点、临床场景
|
||
- 三级来源体系:手工维护(manual)→ MeSH 导入(mesh)→ 管道懒创建(auto)
|
||
- 用户端只展示已激活(is_active=true)的标签
|
||
|
||
---
|
||
|
||
## 一、标签分类(tag_category)
|
||
|
||
| 分类 | 枚举值 | 来源 | 示例 |
|
||
|------|--------|------|------|
|
||
| 癌种部位 | `cancer` | MeSH C + 手工 | 肺癌、乳腺癌、结直肠癌 |
|
||
| 组织分型 | `histology` | MeSH C04.557 | 腺癌、鳞癌、淋巴瘤 |
|
||
| 驱动基因/靶点 | `gene` | 手工维护(无 MeSH UI,但种子标签有 name_en) | EGFR、ALK、KRAS、PD-L1 |
|
||
| 治疗方式 | `treatment` | MeSH E + 手工 | 靶向治疗、免疫治疗、放疗 |
|
||
| 研究类型 | `study_type` | MeSH Publication Type | 指南、RCT、Meta分析 |
|
||
| 临床终点 | `endpoint` | 手工维护 | OS、PFS、ORR |
|
||
| 临床场景 | `scenario` | 手工维护 | 新辅助、一线治疗、维持治疗 |
|
||
| 其他 MeSH | `mesh_other` | 管道懒创建 | 无法归类的 MeSH Descriptor |
|
||
|
||
`mesh_other` 是管道路径的兜底分类,按 mesh_ui 前缀映射(见 §3.5)。
|
||
|
||
---
|
||
|
||
## 二、三级来源体系
|
||
|
||
### 2.1 来源定义
|
||
|
||
| 来源 | 数量(生产) | 含义 | is_active 默认值 |
|
||
|------|-------------|------|-----------------|
|
||
| `manual` | 99 | 手工维护的种子标签,有中文名、有层级 | `true` |
|
||
| `mesh` | ~670 | C04 + E02 + D27 批量导入标签(NLM 权威,全英文) | `true` |
|
||
| `auto` | ~20,122 | 管道懒创建,meeting 未匹配 MeSH 术语自动创建 | `false` |
|
||
|
||
### 2.2 manual 标签详情(99 条)
|
||
|
||
```
|
||
category total active articles has_mesh_ui
|
||
cancer 33 33 484 27
|
||
treatment 18 18 126 7
|
||
gene 18 18 46 0
|
||
endpoint 10 10 153 4
|
||
study_type 9 9 103 4
|
||
scenario 11 11 85 3
|
||
```
|
||
|
||
- 45/99 有 mesh_ui 能直接匹配 PubMed MeSH
|
||
- 17 个关键基因标签(EGFR、ALK、KRAS、BRAF 等)通过 `tmp_fix_tag_mesh_ui.py` 脚本映射到正确 mesh_ui
|
||
- 28 个标签 article_count=0(主要为 grouping 节点和部分罕见癌种),is_selectable=false 的 grouping 节点不参与打标
|
||
- 33 个非 selectable 的 grouping 节点(如"肺癌→非小细胞肺癌"下的层级节点)
|
||
|
||
### 2.3 mesh 标签详情(~670 条)
|
||
|
||
从 `scripts/import_mesh_tags.py` 导入,import_mesh_tags.py 先按 name_en 匹配,匹配不到的创 building 建有 mesh_ui 的新标签。
|
||
|
||
- **导入策略**:先匹配已有 manual 标签的 name_en,命中则关联不新建;未命中则创建新行
|
||
- **匹配问题**:约 15 个标签因命名不一致(如 seed "Lung Cancer" vs MeSH "Lung Neoplasms")未匹配到,创建了重复标签
|
||
|
||
### 2.4 auto 标签详情(~20,122 条)
|
||
|
||
自动创建,无中文名,无层级,is_active=false。主要用于:
|
||
1. 保证打标覆盖(即使没有 manual/mesh 标签匹配)
|
||
2. 后台可见,供管理员审核后提升为 manual 标签
|
||
|
||
---
|
||
|
||
## 三、MeSH → 标签匹配策略(三阶段)
|
||
|
||
### 3.1 总体流程
|
||
|
||
```
|
||
mesh_headings[](来自 PubMed 文章)
|
||
│
|
||
├─ 阶段 1:mesh_ui 精确匹配 ──→ 查询 global_tags WHERE mesh_ui IN (...)
|
||
│ 命中 → 使用 manual/mesh 标签
|
||
│ 未命中 → 进入阶段 2
|
||
│
|
||
├─ 阶段 2:name_en 回退匹配 ──→ 查询 global_tags WHERE name_en IN (...)
|
||
│ 命中 → 使用 manual/mesh 标签(解决无 mesh_ui 的种子标签)
|
||
│ 未命中 → 进入阶段 3
|
||
│
|
||
└─ 阶段 3:懒创建 ──→ GlobalTag(source="auto", is_active=false)
|
||
name_zh=null,仅存英文名
|
||
等待后台审核后激活
|
||
```
|
||
|
||
### 3.2 阶段 1:mesh_ui 精确匹配
|
||
|
||
```python
|
||
# tag_service.py:42-46
|
||
result = await db.execute(
|
||
select(GlobalTag).where(GlobalTag.mesh_ui.in_(mesh_uis))
|
||
)
|
||
matched: list[GlobalTag] = list(result.scalars().all())
|
||
```
|
||
|
||
- 每条 PubMed 文章的 MeSH Headings 包含 `{descriptor, ui, major}`
|
||
- 用 `ui`(如 `D008175`)精准匹配 `global_tags.mesh_ui`
|
||
- 命中率高,但要求标签预先设置 mesh_ui
|
||
|
||
### 3.3 阶段 2:name_en 回退匹配
|
||
|
||
```python
|
||
# tag_service.py:54-62 — 阶段 2 回退
|
||
result = await db.execute(
|
||
select(GlobalTag).where(GlobalTag.name_en.in_(unmatched_names))
|
||
)
|
||
```
|
||
|
||
- 为没有 mesh_ui 的种子标签提供匹配机会(如大部分 gene 标签)
|
||
- 依赖 MeSH heading 的 `descriptor` 字段与标签 `name_en` 的精确匹配
|
||
- **局限性**:MeSH 官方名称(如 "ErbB Receptors")与 seed 标签名称("EGFR")不一致时无法匹配
|
||
|
||
### 3.4 阶段 3:懒创建
|
||
|
||
```python
|
||
# tag_service.py:70-86 — 懒创建
|
||
tag = GlobalTag(
|
||
mesh_ui=ui,
|
||
name_en=mh.get("descriptor", ""),
|
||
name_zh=None,
|
||
source="auto",
|
||
is_active=False,
|
||
)
|
||
```
|
||
|
||
- 前面两阶段均未命中时才触发
|
||
- 创建 source=auto, is_active=false 的标签
|
||
- 无中文名、无层级
|
||
- 确保每篇文章的每个 MeSH heading 都能映射到标签
|
||
|
||
### 3.5 分类推断规则
|
||
|
||
懒创建时按 mesh_ui 前缀决定 tag_category:
|
||
|
||
```python
|
||
_CATEGORY_MAP = {
|
||
"C": "cancer", "D": "gene", "E": "treatment",
|
||
"V": "study_type",
|
||
# F, G, H, I, J, K, L, M, N, Z → "mesh_other"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 四、关键词回填策略
|
||
|
||
### 4.1 为无 mesh_ui 的 manual 标签补充文章关联
|
||
|
||
部分 manual 标签(如 scenario/endpoint 类别)没有 MeSH 对等词,无法通过 mesh_ui 或 name_en 匹配。改用标题/摘要关键词 ILIKE 匹配:
|
||
|
||
```python
|
||
# tmp_keyword_tag_backfill.py(一次执行)
|
||
UPDATE global_tags SET is_active = false
|
||
WHERE source = 'manual' AND article_count = 0 AND is_selectable = true;
|
||
# → 临时停用 32 个零文章 manual 标签
|
||
|
||
# 对每个零文章标签,按 keywords[] 做 title ILIKE 查询
|
||
INSERT INTO global_literature_tags (literature_id, tag_id, is_major)
|
||
SELECT lit.id, :tag_id, false
|
||
FROM global_literature lit
|
||
WHERE ... AND NOT EXISTS (...)
|
||
LIMIT 200;
|
||
```
|
||
|
||
### 4.2 回填结果
|
||
|
||
| 标签 | 关键词 | 新增关联数 |
|
||
|------|--------|-----------|
|
||
| Case Report | case report, case series | ~35,686 |
|
||
| Adjuvant | adjuvant | ~20,603 |
|
||
| Real World Study | real.world, retrospective | ~6,002 |
|
||
| Overall Survival | overall survival | ~4,968 |
|
||
| 其他 12 个 | 各场景关键词 | ~16,683 |
|
||
| **合计** | | **~83,942** |
|
||
|
||
**局限**:ILIKE 匹配假阳性高(如 "adjuvant" 可能匹配到非临床场景),但作为初始覆盖足够。
|
||
|
||
---
|
||
|
||
## 五、article_count 维护
|
||
|
||
### 5.1 增量更新
|
||
|
||
```python
|
||
# tag_service.py:114 — 每次新增关联时更新
|
||
tag.article_count += 1
|
||
```
|
||
|
||
- `tag_article()` 中每次新增 `GlobalLiteratureTag` 记录时 +1
|
||
- 不需要定期重算,实时准确
|
||
- 删除标签关联时不自动减(当前平台无删除操作入口)
|
||
|
||
### 5.2 手动全量刷新
|
||
|
||
```sql
|
||
UPDATE global_tags gt
|
||
SET article_count = (SELECT count(*) FROM global_literature_tags lt WHERE lt.tag_id = gt.id)
|
||
WHERE source = 'manual';
|
||
```
|
||
|
||
---
|
||
|
||
## 六、标签激活体系
|
||
|
||
### 6.1 激活规则
|
||
|
||
| 场景 | 操作 |
|
||
|------|------|
|
||
| source=manual, article_count=0, is_selectable=true | is_active=false(临时隐藏,待回填后激活) |
|
||
| source=manual, article_count>0, is_active=false | is_active=true(回填完成后激活) |
|
||
| source=mesh, article_count=0 | is_active=false(无文献的过分精细节点) |
|
||
| source=auto | is_active=false(待审核) |
|
||
|
||
### 6.2 生产环境执行
|
||
|
||
```sql
|
||
-- 停用零文章 mesh 标签
|
||
UPDATE global_tags SET is_active = false WHERE source = 'mesh' AND article_count = 0;
|
||
-- 165 个零文章 mesh 标签 → 停用
|
||
|
||
-- 停用零文章且非 selectable 的 manual 标签
|
||
UPDATE global_tags SET is_selectable = false WHERE source = 'manual' AND article_count = 0
|
||
AND EXISTS (SELECT 1 FROM global_tags t2 WHERE t2.source = 'manual' AND t2.path LIKE global_tags.path || '::%');
|
||
-- 8 个 grouping 节点 → 不可选
|
||
|
||
-- 停用零文章 manual 叶子标签
|
||
UPDATE global_tags SET is_active = false WHERE source = 'manual' AND article_count = 0 AND is_selectable = true;
|
||
-- 32 个 → 停用。mesh_ui 修复 + 关键词回填后重新激活
|
||
```
|
||
|
||
---
|
||
|
||
## 七、标签树结构
|
||
|
||
### 7.1 level 的含义
|
||
|
||
| level | 含义 | 示例 | selectable |
|
||
|-------|------|------|------------|
|
||
| 1 | 大类(按部位/类型分组) | 肺癌、乳腺癌 | false |
|
||
| 2 | 具体癌种 | 非小细胞肺癌、三阴性乳腺癌 | true |
|
||
| 3+ | 亚型/细分 | 肺腺癌、19del | true |
|
||
|
||
**注意**:level 不代表层级深度,而是按语义粒度划分。某些 level=2 的节点也是 grouping 节点(如"妇科肿瘤"下有子节点但 level=2),通过 is_selectable=false 标记。
|
||
|
||
### 7.2 grouping 节点识别
|
||
|
||
```sql
|
||
-- 判断某节点是否为 grouping 节点
|
||
-- 存在其他 manual 标签的 path 以此为前缀
|
||
SELECT EXISTS (
|
||
SELECT 1 FROM global_tags t2
|
||
WHERE t2.source = 'manual'
|
||
AND t2.path LIKE '当前标签.path' || '::%'
|
||
AND t2.id != '当前标签.id'
|
||
);
|
||
```
|
||
|
||
### 7.3 完整标签树
|
||
|
||
#### 📍 癌种部位(cancer, 33 条)
|
||
|
||
```
|
||
肿瘤科标签体系
|
||
├── 肺癌 (level 1, selectable=false)
|
||
│ ├── 非小细胞肺癌 NSCLC (level 2)
|
||
│ │ ├── 肺腺癌 (level 3)
|
||
│ │ ├── 肺鳞癌 (level 3)
|
||
│ │ └── 大细胞肺癌 (level 3)
|
||
│ └── 小细胞肺癌 SCLC (level 2)
|
||
├── 乳腺癌 (level 1, selectable=false)
|
||
│ ├── HR+/HER2- Luminal型 (level 2)
|
||
│ ├── HER2+ (level 2)
|
||
│ └── 三阴性乳腺癌 TNBC (level 2)
|
||
├── 结直肠癌 (level 1, selectable=false)
|
||
│ ├── 结肠癌 (level 2)
|
||
│ ├── 直肠癌 (level 2)
|
||
│ └── MSI-H/dMMR 结直肠癌 (level 2)
|
||
├── 胃癌 (level 1, selectable=false)
|
||
│ ├── 胃腺癌 (level 2)
|
||
│ └── 胃食管结合部癌 (level 2)
|
||
├── 肝癌 (level 1, selectable=false)
|
||
│ ├── 肝细胞癌 HCC (level 2)
|
||
│ ├── 胆管癌 (level 2)
|
||
│ └── 肝转移癌 (level 2)
|
||
├── 食管癌 (level 1, selectable=false)
|
||
├── 胰腺癌 (level 1, selectable=false)
|
||
├── 前列腺癌 (level 1, selectable=false)
|
||
├── 膀胱癌 / 肾癌 (level 1, selectable=false)
|
||
├── 头颈癌 (level 1, selectable=false)
|
||
│ ├── 鼻咽癌 (level 2)
|
||
│ ├── 口腔癌 (level 2)
|
||
│ ├── 喉癌 (level 2)
|
||
│ └── 甲状腺癌 (level 2)
|
||
├── 妇科肿瘤 (level 2, selectable=false)
|
||
│ ├── 卵巢癌 (level 3)
|
||
│ ├── 宫颈癌 (level 3)
|
||
│ └── 子宫内膜癌 (level 3)
|
||
├── 黑色素瘤 / 皮肤癌 (level 1, selectable=false)
|
||
├── 脑肿瘤 (level 1, selectable=false)
|
||
│ ├── 胶质母细胞瘤 GBM (level 2)
|
||
│ ├── 脑膜瘤 (level 2)
|
||
│ └── 脑转移瘤 (level 2)
|
||
├── 血液肿瘤 (level 1, selectable=false)
|
||
│ ├── 白血病 (level 2, selectable=false)
|
||
│ │ ├── AML (level 3)
|
||
│ │ ├── ALL (level 3)
|
||
│ │ ├── CML (level 3)
|
||
│ │ └── CLL (level 3)
|
||
│ ├── 淋巴瘤 (level 2, selectable=false)
|
||
│ │ ├── 霍奇金淋巴瘤 HL (level 3)
|
||
│ │ └── 非霍奇金淋巴瘤 NHL (level 3)
|
||
│ │ ├── DLBCL (level 4)
|
||
│ │ ├── 滤泡性淋巴瘤 (level 4)
|
||
│ │ ├── 套细胞淋巴瘤 (level 4)
|
||
│ │ └── T细胞淋巴瘤 (level 4)
|
||
│ └── 多发性骨髓瘤 (level 2)
|
||
├── 肉瘤 (level 1, selectable=false)
|
||
├── 原发不明肿瘤 CUP (level 1)
|
||
└── 儿童肿瘤 (level 1)
|
||
```
|
||
|
||
#### 🧬 驱动基因/靶点(gene, 18 条)
|
||
|
||
```
|
||
├── EGFR → 19del / L858R / T790M / C797S / exon20ins
|
||
├── ALK → EML4-ALK / 耐药突变
|
||
├── ROS1
|
||
├── BRAF V600E
|
||
├── KRAS → G12C / G12D
|
||
├── HER2 (ERBB2) → 扩增 / 突变
|
||
├── MET → exon14跳读 / 扩增
|
||
├── RET
|
||
├── NTRK1-3
|
||
├── FGFR1-4
|
||
├── IDH1/2
|
||
├── FLT3
|
||
├── KIT / PDGFRA
|
||
├── BRCA1/2 / HRD
|
||
├── MSI-H / dMMR
|
||
├── TMB-H
|
||
├── PIK3CA / PTEN / AKT
|
||
├── TP53 / RB1 / MYC
|
||
└── PD-L1
|
||
```
|
||
|
||
所有 gene 标签均无 mesh_ui,依靠 stage 2 name_en 回退匹配或关键词回填。
|
||
|
||
#### 💊 治疗方式(treatment, 18 条)
|
||
|
||
```
|
||
├── 靶向治疗 → TKI / 单克隆抗体 / ADC / 双特异性抗体
|
||
├── 免疫治疗 → PD-1抑制剂 / PD-L1抑制剂 / CTLA-4抑制剂 / LAG-3抑制剂 / CAR-T
|
||
├── 化疗 → 铂类 / 紫杉类 / 抗代谢 / 拓扑异构酶抑制剂
|
||
├── 放疗 → 常规分割 / SBRT/SRS / 质子 / 重离子
|
||
├── 手术 → 微创 / 机器人 / 器官保留
|
||
├── 内分泌治疗 → 他莫昔芬 / 芳香化酶抑制剂 / 抗雄激素
|
||
├── 抗血管生成 → 贝伐珠单抗 / 安罗替尼 / 阿帕替尼
|
||
├── 骨髓移植 → 自体 / 异基因
|
||
└── 支持治疗 → 骨转移 / 止吐 / 疼痛 / 营养
|
||
```
|
||
|
||
7/18 有 mesh_ui。
|
||
|
||
#### 📊 研究类型(study_type, 9 条)
|
||
|
||
```
|
||
├── 临床实践指南 → NCCN / CSCO / ESMO
|
||
├── 随机对照试验 RCT → 3期 / 2期
|
||
├── 系统综述/Meta分析
|
||
├── 真实世界研究 RWS
|
||
├── 病例报告/病例系列
|
||
├── 转化研究
|
||
├── 基础研究
|
||
└── 卫生经济学/HTA
|
||
```
|
||
|
||
4/9 有 mesh_ui。
|
||
|
||
#### 📈 临床终点(endpoint, 10 条)
|
||
|
||
```
|
||
├── OS (总生存期)
|
||
├── PFS (无进展生存期)
|
||
├── DFS (无病生存期)
|
||
├── ORR (客观缓解率) / DCR (疾病控制率)
|
||
├── 安全性/AE
|
||
├── 生物标志物 → ctDNA / CTC / MRD
|
||
├── 生活质量 QoL / PRO
|
||
└── 耐药机制
|
||
```
|
||
|
||
#### 🏥 临床场景(scenario, 11 条)
|
||
|
||
```
|
||
├── 早期/可手术 → 新辅助 / 辅助
|
||
├── 局部晚期 → 同步放化疗 / 转化治疗
|
||
├── 晚期/转移性 → 一线 / 二线+ / 后线
|
||
├── 维持治疗
|
||
├── 姑息/支持治疗
|
||
├── 寡转移 / 寡进展
|
||
├── 老年肿瘤
|
||
├── 儿童肿瘤
|
||
└── 遗传性肿瘤 / 家系
|
||
```
|
||
|
||
---
|
||
|
||
## 八、API 端点
|
||
|
||
### 8.1 公共标签
|
||
|
||
```
|
||
GET /public/tags → 全量标签树(Redis 缓存,1h TTL,key="public:tags")
|
||
GET /public/cancer-tags → 仅癌种标签树
|
||
```
|
||
|
||
返回字段:
|
||
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"name_zh": "肺癌",
|
||
"name_en": "Lung Cancer",
|
||
"path": "肿瘤科标签体系::肺癌",
|
||
"tag_category": "cancer",
|
||
"level": 1,
|
||
"article_count": 62,
|
||
"children": [...]
|
||
}
|
||
```
|
||
|
||
### 8.2 管理后台标签
|
||
|
||
```
|
||
GET /admin/tags?page=1&page_size=20&search=&source=&category=&active=
|
||
→ 分页列表,支持 source/mesh/manual/auto + category + active 筛选
|
||
→ 默认按 article_count DESC 排序
|
||
POST /admin/tags/refresh-counts → 全量刷新 article_count
|
||
```
|
||
|
||
### 8.3 首页热门标签
|
||
|
||
```vue
|
||
<!-- HomeView.vue — 侧栏热门标签 -->
|
||
const cancerTags = computed(() => allTags.value
|
||
.filter((t: TagOption) =>
|
||
t.tag_category === 'cancer' && t.level === 2 && t.name_zh)
|
||
.sort((a, b) => ((b.article_count as number) || 0) - ((a.article_count as number) || 0))
|
||
)
|
||
// 限制显示 7 条 + "更多"
|
||
```
|
||
|
||
- 只选 level=2 的具体癌种(排除 grouping 节点)
|
||
- 过滤掉无中文名的标签
|
||
- 按 article_count 降序排列
|
||
|
||
---
|
||
|
||
## 九、前端标签显示规则
|
||
|
||
### 9.1 名称显示
|
||
|
||
```vue
|
||
<!-- 所有标签组件的统一规则 -->
|
||
{{ tag.name_zh || tag.name_en }}
|
||
```
|
||
|
||
如果 `name_zh` 为空(auto 标签常见),退而显示 `name_en`。此规则在以下组件统一使用:
|
||
- [LiteratureCard.vue](frontend/src/components/literature/LiteratureCard.vue)
|
||
- [LiteratureDetailView.vue](frontend/src/views/app/LiteratureDetailView.vue)(登录态)
|
||
- [LiteratureDetailView.vue](frontend/src/views/public/LiteratureDetailView.vue)(公开态)
|
||
- [CancerBrowseView.vue](frontend/src/views/public/CancerBrowseView.vue)
|
||
- [InterestSettingsView.vue](frontend/src/views/app/InterestSettingsView.vue)
|
||
|
||
### 9.2 文献卡片标签加载
|
||
|
||
```python
|
||
# tag_loader.py — 共享标签加载工具
|
||
async def load_tags_for_literature(db, literature_ids):
|
||
select(GlobalLiteratureTag.literature_id, GlobalTag.id,
|
||
GlobalTag.name_zh, GlobalTag.name_en, # ← 必须同时加载
|
||
GlobalTag.path, GlobalTag.tag_category,
|
||
GlobalLiteratureTag.is_major)
|
||
...
|
||
```
|
||
|
||
**历史修复**:曾因 SELECT 遗漏 `name_en` 导致 name_zh=null 的标签显示为空白。
|
||
|
||
### 9.3 暗色模式
|
||
|
||
```vue
|
||
// PublicLayout.vue + AppLayout.vue — 用户头像背景色适配暗色模式
|
||
const avatarStyle = computed(() => ({
|
||
background: ui.isDark ? '#1a3a5c' : '#e6f4ff',
|
||
color: 'var(--text-primary)',
|
||
}))
|
||
```
|
||
|
||
使用 computed `:style` 绑定而非 CSS class,因为 NAvatar 有内联样式优先级高于外部 CSS。
|
||
|
||
---
|
||
|
||
## 十、缓存策略
|
||
|
||
| 缓存 | Key | TTL | 刷新机制 |
|
||
|------|-----|-----|---------|
|
||
| 公共标签树 | `public:tags` | 3600s | 被动过期,首次请求回退 DB |
|
||
| 首页 Feed | `homepage:feed` | 1800s | ARQ 定时每 30min 刷新 |
|
||
| 热门文章 | `hot_articles` | 1800s | ARQ 定时每 30min 刷新 |
|
||
|
||
---
|
||
|
||
## 十一、数据维护
|
||
|
||
### 11.1 标签导入
|
||
|
||
```bash
|
||
# 种子标签导入(dev 环境)
|
||
cd backend && python scripts/seed_data.py
|
||
|
||
# C04 MeSH 批量导入
|
||
python scripts/import_mesh_tags.py \
|
||
--mesh-xml desc2025.xml.gz \
|
||
--categories C04 \
|
||
--cross-categories E02 E04 D27 \
|
||
--specialty oncology
|
||
```
|
||
|
||
### 11.2 一次性数据清洗
|
||
|
||
```sql
|
||
-- 查看标签分布
|
||
SELECT source, tag_category, count(*), sum(article_count) FROM global_tags
|
||
GROUP BY source, tag_category ORDER BY source, tag_category;
|
||
|
||
-- 查看零文章标签
|
||
SELECT name_zh, name_en, source, tag_category, is_active
|
||
FROM global_tags WHERE (article_count = 0 OR article_count IS NULL)
|
||
ORDER BY source, tag_category;
|
||
|
||
-- 刷新 article_count
|
||
UPDATE global_tags gt
|
||
SET article_count = (SELECT count(*) FROM global_literature_tags lt WHERE lt.tag_id = gt.id);
|
||
|
||
-- 合并重复标签(name_en 近似但不同 ID)
|
||
-- 先将旧标签的 lit association 迁移到新标签
|
||
INSERT INTO global_literature_tags (literature_id, tag_id, is_major)
|
||
SELECT lt.literature_id, :target_tag_id, lt.is_major
|
||
FROM global_literature_tags lt
|
||
WHERE lt.tag_id = :source_tag_id
|
||
AND NOT EXISTS (SELECT 1 FROM global_literature_tags lt2
|
||
WHERE lt2.literature_id = lt.literature_id AND lt2.tag_id = :target_tag_id);
|
||
-- 然后删除旧标签
|
||
DELETE FROM global_literature_tags WHERE tag_id = :source_tag_id;
|
||
DELETE FROM global_tags WHERE id = :source_tag_id;
|
||
```
|
||
|
||
### 11.3 生产部署注意事项
|
||
|
||
```bash
|
||
# 部署前确保标签变更已提交
|
||
cd backend && python -m alembic -c alembic/alembic.ini upgrade head
|
||
|
||
# 重建前端
|
||
cd frontend && rm -rf dist node_modules/.vite && npm run build
|
||
|
||
# 重启 worker(使标签缓存刷新)
|
||
docker compose restart worker
|
||
```
|
||
|
||
---
|
||
|
||
## 十二、已知问题与改进方向
|
||
|
||
### 12.1 约 26% 的文献无标签
|
||
|
||
- **原因**:`mesh_headings=[]` 的文献(in-process/publisher 记录,NLM 尚未标引)
|
||
- **影响范围**:约 322,507 篇(生产数据)
|
||
- **改进方向**:标题/摘要关键词匹配(已对 scenario/endpoint 标签实现,可推广到癌种)
|
||
|
||
### 12.2 name_en 匹配精度
|
||
|
||
- MeSH 官方术语名称与种子标签名称不一致(如 "ErbB Receptors" ≠ "EGFR")
|
||
- 当前方案:为关键基因标签手动设置正确的 mesh_ui 绕过此问题
|
||
|
||
### 12.3 不完善的自动分类
|
||
|
||
- `_guess_category()` 仅按 mesh_ui 首字母分类,粒度过粗
|
||
- Phase 2 计划:基于 LLM 的精细分类
|
||
|
||
### 12.4 删除标签关联不减 article_count
|
||
|
||
- 当前 article_count 只增不减
|
||
- 如需删除标签关联(如同一 auto 标签合并到 manual 标签),需手动刷新计数
|
||
|
||
### 12.5 重复标签
|
||
|
||
- 约 15 个 seed 标签因 name_en 命名差异未匹配到 C04 导入标签,导致重复
|
||
- 可通过 `tmp_fix_tag_mesh_ui.py` 类似的合并脚本处理
|
||
|
||
---
|
||
|
||
## 十三、关键维护脚本
|
||
|
||
| 脚本 | 用途 | 执行环境 |
|
||
|------|------|---------|
|
||
| `seed_data.py` | 导入 99 条种子标签 + 期刊 | dev |
|
||
| `import_mesh_tags.py` | 导入 C04 MeSH 标签 | 一次性 |
|
||
| `tmp_fix_tag_mesh_ui.py` | 修复 17 个 gene/treatment 标签的 mesh_ui 映射 | 生产(已执行) |
|
||
| `tmp_keyword_tag_backfill.py` | 关键词回填 16 个 scenario/endpoint 标签 | 生产(已执行) |
|
||
| `tmp_audit_tags.py` | 审计标签分布 | 生产 |
|