全部 auth 类 fixture 从 HTTP register 改为 DB 直接创建,绕过 6次/小时 IP 限速 (conftest、test_literature、test_subscriptions、test_verification、test_auth、 test_approvals、test_notifications、test_user_settings 共 8 个文件)。 同步修复 europe_pmc 解析、admin pipeline、ai_summary、email_service、 security/permissions 等共 21 个文件的断言和适配问题。
1060 lines
67 KiB
Markdown
1060 lines
67 KiB
Markdown
# 数据库设计
|
||
|
||
## 日期时间类型规范
|
||
|
||
**TIMESTAMPTZ 为主**。纯日期字段(出生日期、发表日期、批准日期、排期日期、PubMed 纯日期字段)用 `DATE`。
|
||
|
||
```sql
|
||
-- ✅ 正确
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||
pub_date DATE -- 文献发表日期不精确到时间
|
||
|
||
-- ❌ 禁止
|
||
created_at TIMESTAMP -- 不知道时区
|
||
created_at INTEGER -- Unix时间戳,人不可读,SQL 中日期运算困难
|
||
```
|
||
|
||
---
|
||
|
||
## ER 关系总图
|
||
|
||
```
|
||
tenants ──1:N── user_tenants ──N:1── users
|
||
│ │
|
||
├── teams ──N:M── team_members ├── user_subscriptions ── global_tags
|
||
├── invitations ├── user_journal_subscriptions ── global_journals
|
||
├── export_jobs ├── user_feed ── global_literature
|
||
├── shared_folders ├── user_literature ── global_literature
|
||
├── journal_club_queue ├── user_notes ── global_literature
|
||
├── approval_workflows ├── user_pdf_highlights
|
||
├── api_keys ├── user_folders
|
||
├── tenant_subscriptions ├── user_feedback
|
||
├── subscription_events └── systematic_reviews ── review_literatures
|
||
├── payment_orders
|
||
└── ai_provider_config
|
||
|
||
global_literature ──N:M── global_literature_tags ──N:M── global_tags
|
||
│ │
|
||
├── drug_approvals(pmid) ├── drug_approvals(cancer_type_tag)
|
||
├── guideline_evidence ├── guideline_versions(cancer_type_tag)
|
||
├── review_literatures └── user_subscriptions(tag)
|
||
└── global_journals(issn)
|
||
|
||
user_journal_subscriptions ──N:1── global_journals
|
||
|
||
mdt_cases ──1:N── mdt_decisions ──1:N── mdt_evidence
|
||
└── global_literature
|
||
```
|
||
|
||
---
|
||
|
||
## 一、基础架构表
|
||
|
||
### 1.1 tenants — 租户
|
||
|
||
```sql
|
||
CREATE TABLE tenants (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 租户ID
|
||
name VARCHAR(200) NOT NULL, -- 租户名称
|
||
slug VARCHAR(100) UNIQUE NOT NULL, -- 唯一标识符(用于URL)
|
||
hospital_name VARCHAR(200), -- 医院名称
|
||
department_name VARCHAR(200), -- 科室名称
|
||
plan_type VARCHAR(30) NOT NULL DEFAULT 'free', -- 套餐类型:free/pro/team/enterprise
|
||
is_personal BOOLEAN NOT NULL DEFAULT TRUE, -- 是否个人租户
|
||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态:active/inactive/suspended
|
||
logo_url VARCHAR(500), -- Logo 图片 URL
|
||
primary_color VARCHAR(7) DEFAULT '#1a5276', -- 主题色(十六进制)
|
||
settings JSONB NOT NULL DEFAULT '{}', -- 租户级 JSON 配置
|
||
max_members INTEGER NOT NULL DEFAULT 1, -- 最大成员数
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
```
|
||
|
||
### 1.2 users — 用户
|
||
|
||
```sql
|
||
CREATE TABLE users (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 用户ID
|
||
email VARCHAR(320) NOT NULL UNIQUE, -- 邮箱(登录账号)
|
||
hashed_password VARCHAR(128), -- 密码哈希(SSO用户可为NULL)
|
||
display_name VARCHAR(150) NOT NULL, -- 显示名称
|
||
title VARCHAR(100), -- 职称/头衔
|
||
avatar_url VARCHAR(500), -- 头像 URL
|
||
phone VARCHAR(30), -- 手机号
|
||
is_active BOOLEAN NOT NULL DEFAULT TRUE, -- 是否激活
|
||
is_superuser BOOLEAN NOT NULL DEFAULT FALSE, -- 是否超级管理员
|
||
sso_provider VARCHAR(20), -- SSO 提供商:wechat/oauth
|
||
sso_subject_id VARCHAR(255), -- SSO 主体 ID
|
||
email_verified BOOLEAN NOT NULL DEFAULT FALSE, -- 邮箱是否已验证
|
||
phone_verified BOOLEAN NOT NULL DEFAULT FALSE, -- 手机是否已验证
|
||
notify_email BOOLEAN NOT NULL DEFAULT TRUE, -- 是否接收邮件通知
|
||
digest_frequency VARCHAR(10) NOT NULL DEFAULT 'daily', -- 摘要频率:daily/weekly/never
|
||
last_login_at TIMESTAMPTZ, -- 最后登录时间
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX idx_users_email ON users(email);
|
||
```
|
||
|
||
用户通知偏好(`notify_email`, `digest_frequency`)和验证状态(`email_verified`, `phone_verified`)存储在 users 表内,不单独建通知表。
|
||
|
||
### 1.3 user_tenants — 用户-租户关联
|
||
|
||
```sql
|
||
CREATE TABLE user_tenants (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 关联ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
role VARCHAR(20) NOT NULL DEFAULT 'viewer', -- 角色:viewer/member/admin/owner
|
||
is_default BOOLEAN NOT NULL DEFAULT FALSE, -- 是否为该用户的默认租户
|
||
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 加入时间
|
||
UNIQUE (user_id, tenant_id)
|
||
);
|
||
CREATE INDEX idx_ut_user ON user_tenants(user_id);
|
||
CREATE INDEX idx_ut_user_tenant ON user_tenants(user_id, tenant_id);
|
||
```
|
||
|
||
### 1.4 login_logs — 登录日志
|
||
|
||
```sql
|
||
CREATE TABLE login_logs (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 日志ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
tenant_id UUID REFERENCES tenants(id) ON DELETE SET NULL, -- 租户ID(可能为空)
|
||
ip_address VARCHAR(45), -- 登录IP(支持IPv6)
|
||
user_agent VARCHAR(500), -- 用户代理字符串
|
||
success BOOLEAN NOT NULL DEFAULT TRUE, -- 是否登录成功
|
||
fail_reason VARCHAR(200), -- 失败原因
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 登录时间
|
||
);
|
||
CREATE INDEX idx_ll_user_created ON login_logs(user_id, created_at);
|
||
```
|
||
|
||
---
|
||
|
||
## 二、文献核心表(全局库,所有租户共享)
|
||
|
||
### 2.1 global_literature — 全局文献库
|
||
|
||
```sql
|
||
CREATE TABLE global_literature (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 文献ID(内部)
|
||
pmid INTEGER UNIQUE NOT NULL, -- PubMed ID 【PubMed: MedlineCitation/PMID】
|
||
title TEXT NOT NULL, -- 标题 【PubMed: Article/ArticleTitle】
|
||
abstract TEXT, -- 摘要 【PubMed: Article/Abstract/AbstractText】
|
||
authors JSONB NOT NULL DEFAULT '[]', -- 作者列表 [{family,given,affiliations}] 【PubMed: Article/AuthorList/Author】
|
||
doi VARCHAR(500), -- DOI 标识符 【PubMed: Article/ELocationID[@EIdType="doi"] 或 ArticleId[@IdType="doi"]】
|
||
journal VARCHAR(500), -- 期刊全名 【PubMed: Article/Journal/Title】
|
||
journal_issn VARCHAR(20), -- 期刊 ISSN 【PubMed: Article/Journal/ISSN】
|
||
journal_iso VARCHAR(100), -- NLM ISO 缩写(如 "N Engl J Med")【PubMed: Article/Journal/ISOAbbreviation】
|
||
volume VARCHAR(50), -- 卷号 【PubMed: Article/Journal/JournalIssue/Volume】
|
||
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】
|
||
language VARCHAR(10) DEFAULT 'en', -- 语言 【PubMed: Article/Language】
|
||
raw_xml_hash VARCHAR(64), -- 原始 XML SHA-256 哈希(去重与增量更新用,非 PubMed 字段)
|
||
pmc_id VARCHAR(20), -- PMC ID(PMC1234567)【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 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】
|
||
full_text_path VARCHAR(255), -- COS 对象存储键(内部)
|
||
trial_reg JSONB, -- 试验注册号 {nct, eudract, chictr} 【PubMed: Article/DataBankList/DataBank,注册号在 ArticleId[@IdType="ClinicalTrials.gov"] 中也有】
|
||
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 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 原始字段)
|
||
pre_extracted_data JSONB, -- Table 1 预提取数据(AI 抽取,非 PubMed 原始字段)
|
||
is_negative_result BOOLEAN NOT NULL DEFAULT FALSE, -- 是否为阴性结果(AI 判定,非 PubMed 原始字段)
|
||
rct_detection JSONB, -- RCT 自动检测结果 {is_rct, confidence, source}(AI,非 PubMed 原始字段)
|
||
search_tsv TSVECTOR, -- 全文检索向量(内部 PG tsvector,非 PubMed 字段)
|
||
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】
|
||
tag_ids UUID[], -- 反范式标签ID数组 + GIN,免 JOIN global_literature_tags【由 tag_service.tag_article() 维护,使用 PostgreSQL unnest() + ARRAY() 构造,非 PubMed 字段】
|
||
source VARCHAR(30) NOT NULL DEFAULT 'pubmed_ftp', -- 数据来源(内部追踪用:pubmed_api/pubmed_ftp,非 PubMed 字段)
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
-- ═══ B-tree 索引(既有筛选) ═══
|
||
CREATE INDEX idx_gl_pub_date ON global_literature(pub_date);
|
||
CREATE INDEX idx_gl_pub_year ON global_literature(pub_year);
|
||
CREATE INDEX idx_gl_journal_issn ON global_literature(journal_issn);
|
||
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 @> 查询
|
||
|
||
-- ═══ BRIN 索引(时序数据,体积小两个数量级) ═══
|
||
CREATE INDEX ix_gl_pub_date_brin ON global_literature USING brin(pub_date);
|
||
CREATE INDEX ix_gl_pub_year_brin ON global_literature USING brin(pub_year);
|
||
|
||
-- ═══ Covering Index(date 排序 + 高频引用列,index-only scan) ═══
|
||
CREATE INDEX ix_gl_pub_date_covering ON global_literature(pub_date, id) INCLUDE (journal_issn, cited_by_count, is_oa, retracted, is_negative_result, is_preprint, journal, pub_year, article_date, doi, pmc_id, language, citation_status);
|
||
|
||
-- ═══ Partial Index(稀疏布尔,体积缩小 99.9%) ═══
|
||
CREATE INDEX ix_gl_retracted_true ON global_literature(retracted) WHERE retracted = TRUE;
|
||
CREATE INDEX ix_gl_is_oa_true ON global_literature(is_oa) WHERE is_oa = TRUE;
|
||
CREATE INDEX ix_gl_is_negative_true ON global_literature(is_negative_result) WHERE is_negative_result = TRUE;
|
||
CREATE INDEX ix_gl_is_preprint_true ON global_literature(is_preprint) WHERE is_preprint = TRUE;
|
||
|
||
-- ═══ GIN 反范式标签数组索引(标签筛选免 JOIN global_literature_tags) ═══
|
||
CREATE INDEX ix_gl_tag_ids_gin ON global_literature USING gin(tag_ids);
|
||
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 触发器 `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`([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 结构速查:**
|
||
|
||
| 表字段 | PubMed XML Path | 说明 |
|
||
|---|---|---|
|
||
| `pmid` | `MedlineCitation/PMID` | Pubmed ID |
|
||
| `title` | `Article/ArticleTitle` | 文章标题 |
|
||
| `abstract` | `Article/Abstract/AbstractText` | 多个 AbstractText 拼接,按 Label 区分 |
|
||
| `authors` | `Article/AuthorList/Author` | LastName + ForeName + AffiliationInfo |
|
||
| `doi` | `ArticleId[@IdType="doi"]` | 优先取 PubmedData 下的 ArticleIdList |
|
||
| `journal` | `Article/Journal/Title` | 期刊全名 |
|
||
| `journal_issn` | `Article/Journal/ISSN` | 注意区分 Print / Electronic |
|
||
| `journal_iso` | `Article/Journal/ISOAbbreviation` | NLM 标准缩写 |
|
||
| `volume` | `Article/Journal/JournalIssue/Volume` | 卷 |
|
||
| `issue` | `Article/Journal/JournalIssue/Issue` | 期 |
|
||
| `pages` | `Article/Pagination/MedlinePgn` | 页码范围 |
|
||
| `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` | 不可靠,不应视为修订依据。仅 Year/Month/Day,DATE 类型 |
|
||
| `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` | NLM 整条编目记录完成日(含 MeSH 标引在内的全部处理完成)。实践中作为 [MHDA] 近似值使用。仅 Year/Month/Day,DATE 类型 |
|
||
| `meshed_date` | 系统内部字段 | MeSH 数据版本日期。通常=date_completed(MeSH 是编目最后一步,两者同天)。年更后=新的 date_completed。NULL=无MeSH。DATE 类型 |
|
||
| `publication_status` | `PubmedData/PublicationStatus` | "epublish" / "ppublish" / "aheadofprint" |
|
||
| `article_date` | `Article/ArticleDate` | [EPDAT] 电子出版日期(印刷版之前的日期)|
|
||
| `create_date` | `PubmedData/History/PubMedPubDate[@PubStatus="pubmed"]` 或 `MedlineCitation/DateCreated` | [CRDT] PubMed 记录创建日期。仅 Year/Month/Day,DATE 类型 |
|
||
| `entrez_date` | `PubmedData/History/PubMedPubDate[@PubStatus="entrez"]` | [EDAT] PubMed 收录日期。仅 Year/Month/Day,DATE 类型 |
|
||
| `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 — 肿瘤科标签树
|
||
|
||
```sql
|
||
CREATE TABLE global_tags (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 标签ID
|
||
mesh_ui VARCHAR(20), -- MeSH 唯一标识符(UI)
|
||
name_zh VARCHAR(200) NOT NULL, -- 中文名称
|
||
name_en VARCHAR(200), -- 英文名称
|
||
path VARCHAR(500) NOT NULL, -- 标签路径(如 /癌种/肺癌/非小细胞肺癌)
|
||
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 入口词列表(~200k,NLM 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), -- 图标名
|
||
is_selectable BOOLEAN NOT NULL DEFAULT TRUE, -- 是否可选(叶子节点)
|
||
article_count INTEGER DEFAULT 0, -- 关联文献数
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX idx_tags_path ON global_tags(path);
|
||
CREATE INDEX idx_tags_parent ON global_tags(parent_id);
|
||
CREATE INDEX idx_tags_category ON global_tags(tag_category);
|
||
CREATE INDEX idx_tags_mesh_ui ON global_tags(mesh_ui) WHERE mesh_ui IS NOT NULL;
|
||
```
|
||
|
||
**tag_category 枚举**:`cancer`(癌种) | `histology`(组织分型) | `gene`(驱动基因) | `treatment`(治疗方式) | `study_type`(研究类型) | `endpoint`(临床终点) | `scenario`(临床场景) | `journal_tier`(期刊等级)
|
||
|
||
### 2.3 global_literature_tags — 文献标签关联
|
||
|
||
```sql
|
||
CREATE TABLE global_literature_tags (
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
tag_id UUID NOT NULL REFERENCES global_tags(id) ON DELETE CASCADE, -- 标签ID
|
||
is_major BOOLEAN NOT NULL DEFAULT FALSE, -- 是否为主要主题(MeSH Major Topic)
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
PRIMARY KEY (literature_id, tag_id)
|
||
);
|
||
CREATE INDEX idx_glt_tag ON global_literature_tags(tag_id);
|
||
```
|
||
|
||
> 预估值:约 6000 万行(600万×10标签),B-tree 索引足够,不需要分区。两种查询都命中索引。
|
||
|
||
### 2.4 global_journals — 期刊库
|
||
|
||
```sql
|
||
CREATE TABLE global_journals (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 期刊ID
|
||
name VARCHAR(500) NOT NULL, -- 期刊名称
|
||
issn VARCHAR(20), -- 印刷版 ISSN
|
||
eissn VARCHAR(20), -- 电子版 ISSN
|
||
tier VARCHAR(5) NOT NULL DEFAULT '4', -- 期刊等级:1/2/3/4
|
||
impact_factor DOUBLE PRECISION, -- 影响因子
|
||
publisher VARCHAR(300), -- 出版商
|
||
article_count INTEGER DEFAULT 0, -- 收录文献数
|
||
is_active BOOLEAN DEFAULT TRUE, -- 是否活跃
|
||
priority_score REAL, -- 综合优先级评分 0-100
|
||
priority_source VARCHAR(20) DEFAULT 'auto', -- 优先级来源:auto/manual
|
||
specialty VARCHAR(50), -- 专科领域:oncology
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 更新时间
|
||
UNIQUE (issn),
|
||
UNIQUE (name)
|
||
);
|
||
CREATE INDEX idx_gj_tier ON global_journals(tier);
|
||
CREATE INDEX idx_gj_priority ON global_journals(priority_score DESC);
|
||
```
|
||
|
||
|
||
**权重评分规则(priority_score):**
|
||
|
||
| 维度 | 规则 | 最高加分 |
|
||
|---|---|---|
|
||
| Tier 基础分 | 1→80, 2→65, 3→50, 4→20 | 80 |
|
||
| IF 加成分 | ≥20→+15, ≥10→+10, ≥5→+5, ≥2→+2 | 15 |
|
||
| 收录量加成分 | ≥5000→+10, ≥1000→+5, ≥100→+2 | 10 |
|
||
| 专科加成分 | specialty='oncology'→+10 | 10 |
|
||
|
||
封顶 100 分。`priority_source='manual'` 时自动任务跳过该行。用户关注的期刊查询时 +20。
|
||
|
||
**specialty 自动判定:** Tier 1-3 → `oncology`;name 包含 oncol/cancer/tumor/neoplas → `oncology`;其余 NULL。
|
||
|
||
---
|
||
|
||
## 三、用户交互表
|
||
|
||
### 3.1 user_subscriptions — 关注领域
|
||
|
||
```sql
|
||
CREATE TABLE user_subscriptions (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 订阅ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
tag_id UUID NOT NULL REFERENCES global_tags(id) ON DELETE CASCADE, -- 标签ID
|
||
match_mode VARCHAR(10) NOT NULL DEFAULT 'standard', -- 匹配模式:loose/standard/strict
|
||
is_active BOOLEAN NOT NULL DEFAULT TRUE, -- 是否激活
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 更新时间
|
||
UNIQUE (user_id, tag_id)
|
||
);
|
||
CREATE INDEX idx_us_user_active ON user_subscriptions(user_id, is_active);
|
||
CREATE INDEX idx_us_tag_active ON user_subscriptions(tag_id, is_active);
|
||
```
|
||
|
||
**match_mode**:`loose`(任一命中) | `standard`(主标签命中,默认) | `strict`(全部命中)
|
||
|
||
### 3.2 user_feed — 每日推送 ⚠️ 需要分区
|
||
|
||
```sql
|
||
CREATE TABLE user_feed (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- FeedID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
matched_tags JSONB NOT NULL DEFAULT '[]', -- 命中的标签列表
|
||
priority VARCHAR(20) NOT NULL DEFAULT 'related', -- 优先级:must_read/recommended/related
|
||
is_read BOOLEAN NOT NULL DEFAULT FALSE, -- 是否已读
|
||
read_at TIMESTAMPTZ, -- 阅读时间
|
||
is_dismissed BOOLEAN NOT NULL DEFAULT FALSE, -- 是否已忽略
|
||
pushed_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 推送时间
|
||
);
|
||
CREATE INDEX idx_uf_user_push ON user_feed(user_id, pushed_at);
|
||
CREATE INDEX idx_uf_user_priority ON user_feed(user_id, is_dismissed, priority);
|
||
CREATE INDEX idx_uf_user_lit ON user_feed(user_id, literature_id);
|
||
```
|
||
|
||
> 预估值:年增数千万行。当前模型未分区,**生产需改为按月分区 + 自动删除旧分区**(保留最近3个月)。查询永远只走最新分区。
|
||
|
||
### 3.3 user_literature — 个人收藏
|
||
|
||
```sql
|
||
CREATE TABLE user_literature (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 收藏ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
folder_id UUID REFERENCES user_folders(id) ON DELETE SET NULL, -- 所属收藏夹
|
||
reading_status VARCHAR(20) NOT NULL DEFAULT 'unread', -- 阅读状态:unread/reading/read
|
||
rating INTEGER CHECK (rating >= 1 AND rating <= 5), -- 评分 1-5
|
||
personal_tags JSONB DEFAULT '[]', -- 个人标签
|
||
saved_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 收藏时间
|
||
read_started_at TIMESTAMPTZ, -- 开始阅读时间
|
||
read_finished_at TIMESTAMPTZ, -- 读完时间
|
||
UNIQUE (user_id, literature_id)
|
||
);
|
||
CREATE INDEX idx_ul_user_saved ON user_literature(user_id, saved_at);
|
||
CREATE INDEX idx_ul_literature ON user_literature(literature_id);
|
||
```
|
||
|
||
### 3.4 user_folders — 收藏夹
|
||
|
||
```sql
|
||
CREATE TABLE user_folders (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 收藏夹ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
name VARCHAR(200) NOT NULL, -- 收藏夹名称
|
||
description TEXT, -- 描述
|
||
color VARCHAR(7) DEFAULT '#808080', -- 颜色(十六进制)
|
||
is_shared BOOLEAN NOT NULL DEFAULT FALSE, -- 是否共享
|
||
sort_order INTEGER DEFAULT 0, -- 排序序号
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX idx_folders_user ON user_folders(user_id);
|
||
CREATE INDEX idx_folders_shared ON user_folders(tenant_id, is_shared) WHERE is_shared = TRUE;
|
||
```
|
||
|
||
### 3.5 user_notes — 阅读笔记
|
||
|
||
```sql
|
||
CREATE TABLE user_notes (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 笔记ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
content TEXT NOT NULL, -- 笔记内容
|
||
quote_text TEXT, -- 引用的原文
|
||
is_private BOOLEAN NOT NULL DEFAULT TRUE, -- 是否私密
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX idx_un_user ON user_notes(user_id, created_at);
|
||
CREATE INDEX idx_un_literature ON user_notes(literature_id);
|
||
```
|
||
|
||
### 3.6 user_pdf_highlights — PDF批注
|
||
|
||
```sql
|
||
CREATE TABLE user_pdf_highlights (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 批注ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
highlight_type VARCHAR(20) NOT NULL, -- 批注类型:highlight/underline/strikeout
|
||
page_number INTEGER NOT NULL, -- 页码
|
||
position JSONB NOT NULL, -- 位置坐标
|
||
text TEXT, -- 批注文本
|
||
note TEXT, -- 批注笔记
|
||
color VARCHAR(7) DEFAULT '#ffff00', -- 高亮颜色
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX idx_hl_user_lit ON user_pdf_highlights(user_id, literature_id);
|
||
```
|
||
|
||
### 3.7 user_feedback — 文献反馈
|
||
|
||
```sql
|
||
CREATE TABLE user_feedback (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 反馈ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
literature_id UUID REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID(可为NULL)
|
||
feedback_type VARCHAR(30) NOT NULL, -- 反馈类型
|
||
detail TEXT, -- 反馈详情
|
||
is_resolved BOOLEAN DEFAULT FALSE, -- 是否已处理
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
UNIQUE (user_id, feedback_type)
|
||
);
|
||
```
|
||
|
||
> `literature_id` 可为 NULL(反馈不一定针对具体文献,也可以是针对搜索结果或系统功能)。`UNIQUE (user_id, feedback_type)` 确保同一用户不会重复提交同类型反馈。
|
||
|
||
### 3.8 user_journal_subscriptions — 期刊关注
|
||
|
||
```sql
|
||
CREATE TABLE user_journal_subscriptions (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 订阅ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
journal_id UUID NOT NULL REFERENCES global_journals(id) ON DELETE CASCADE, -- 期刊ID
|
||
is_active BOOLEAN DEFAULT TRUE, -- 是否激活
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX ix_ujs_user_active ON user_journal_subscriptions(user_id, is_active);
|
||
CREATE INDEX ix_ujs_journal ON user_journal_subscriptions(journal_id);
|
||
```
|
||
|
||
> 关注/取消关注采用软删除(`is_active`),与 `user_subscriptions` 模式一致。关注后,该期刊的新文献会自动推送到用户 Feed。
|
||
|
||
### 3.9 user_activity_logs — 用户活动日志
|
||
|
||
```sql
|
||
CREATE TABLE user_activity_logs (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 日志ID
|
||
user_id UUID REFERENCES users(id) ON DELETE SET NULL, -- 用户ID
|
||
tenant_id UUID REFERENCES tenants(id) ON DELETE SET NULL, -- 租户ID
|
||
activity_type VARCHAR(50) NOT NULL, -- 活动类型
|
||
target_type VARCHAR(30), -- 目标类型(literature/search等)
|
||
target_id VARCHAR(200), -- 目标ID
|
||
detail JSONB, -- 详情 JSON
|
||
ip_address VARCHAR(45), -- 客户端IP
|
||
user_agent VARCHAR(500), -- 用户代理
|
||
session_id VARCHAR(100), -- 会话ID
|
||
duration_seconds INTEGER, -- 停留时长(秒)
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
CREATE INDEX ix_ual_user_created ON user_activity_logs(user_id, created_at);
|
||
CREATE INDEX ix_ual_type_created ON user_activity_logs(activity_type, created_at);
|
||
CREATE INDEX ix_ual_target ON user_activity_logs(target_type, target_id);
|
||
```
|
||
|
||
**activity_type 枚举:** `page_view`(页面访问) | `search`(搜索) | `view_literature`(查看文献) | `save_literature`(收藏) | `export`(导出) | `browse_journals`(浏览期刊) | `click_tag`(点击标签)
|
||
|
||
> 前端通过 `router.afterEach` 自动追踪 `page_view`;关键交互(搜索、收藏、导出等)通过 `trackAction()` 手动埋点。未登录用户不记录。配合 login_logs(登录安全)和 api_usage_logs(API 调用)构成完整的四维分析体系。
|
||
|
||
### 3.10 user_dismissed_tags — 用户负偏好标签(不感兴趣学习)
|
||
|
||
```sql
|
||
CREATE TABLE user_dismissed_tags (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 记录ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
tag_id UUID NOT NULL REFERENCES global_tags(id) ON DELETE CASCADE, -- 标签ID
|
||
dismiss_count INTEGER NOT NULL DEFAULT 1, -- 不感兴趣次数
|
||
last_dismissed_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 最后不感兴趣时间
|
||
UNIQUE(user_id, tag_id)
|
||
);
|
||
CREATE INDEX ix_udt_user_count ON user_dismissed_tags(user_id, dismiss_count);
|
||
```
|
||
|
||
**说明:** 用户点"不感兴趣"后,系统提取该文章的标签累计到 `dismiss_count`。当某标签的 `dismiss_count >= 3` 时,Feed 引擎不再为用户推送该标签下的文章。可在管理后台或用户设置中重置。`user_id` + `tag_id` 唯一约束确保每个用户对每个标签只有一条累计记录。
|
||
|
||
---
|
||
|
||
## 四、团队协作表
|
||
|
||
### 4.1 teams + team_members — 团队与成员
|
||
|
||
```sql
|
||
CREATE TABLE teams (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 团队ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
name VARCHAR(200) NOT NULL, -- 团队名称
|
||
description TEXT, -- 描述
|
||
created_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 创建者
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX idx_teams_tenant ON teams(tenant_id);
|
||
|
||
CREATE TABLE team_members (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 成员ID
|
||
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, -- 团队ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
role VARCHAR(20) NOT NULL DEFAULT 'member', -- 角色:member/lead
|
||
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 加入时间
|
||
UNIQUE (user_id, team_id)
|
||
);
|
||
CREATE INDEX idx_tm_user_team ON team_members(user_id, team_id);
|
||
CREATE INDEX idx_tm_team ON team_members(team_id);
|
||
```
|
||
|
||
### 4.2 invitations + shared_folders — 邀请与共享
|
||
|
||
```sql
|
||
CREATE TABLE invitations (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 邀请ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
inviter_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 邀请人
|
||
email VARCHAR(320) NOT NULL, -- 被邀请人邮箱
|
||
role VARCHAR(20) NOT NULL DEFAULT 'viewer', -- 被邀请角色
|
||
token VARCHAR(200) UNIQUE NOT NULL, -- 邀请令牌
|
||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/accepted/expired
|
||
expires_at TIMESTAMPTZ NOT NULL, -- 过期时间
|
||
accepted_at TIMESTAMPTZ, -- 接受时间
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
|
||
CREATE TABLE shared_folders (
|
||
folder_id UUID NOT NULL REFERENCES user_folders(id) ON DELETE CASCADE, -- 收藏夹ID
|
||
target_type VARCHAR(20) NOT NULL, -- 共享目标类型:team/user
|
||
target_id UUID NOT NULL, -- 共享目标ID
|
||
permission VARCHAR(20) NOT NULL DEFAULT 'view', -- 权限:view/edit
|
||
created_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 共享发起人
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
PRIMARY KEY (folder_id, target_type, target_id)
|
||
);
|
||
```
|
||
|
||
### 4.3 journal_club_queue — Journal Club 文献排队
|
||
|
||
```sql
|
||
CREATE TABLE journal_club_queue (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 排队ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
suggested_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 建议人
|
||
title VARCHAR(500), -- 讨论标题
|
||
scheduled_date DATE, -- 排期日期
|
||
presenter_id UUID REFERENCES users(id) ON DELETE SET NULL, -- 主讲人
|
||
status VARCHAR(20) NOT NULL DEFAULT 'queued', -- 状态:queued/completed/cancelled
|
||
discussion_notes TEXT, -- 讨论纪要
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 更新时间
|
||
UNIQUE (tenant_id, literature_id, scheduled_date)
|
||
);
|
||
CREATE INDEX idx_jc_tenant ON journal_club_queue(tenant_id, status);
|
||
```
|
||
|
||
---
|
||
|
||
## 五、业务扩展表
|
||
|
||
### 5.1 drug_approvals — FDA/NMPA/EMA 药品审批
|
||
|
||
```sql
|
||
CREATE TABLE drug_approvals (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 审批ID
|
||
drug_name VARCHAR(500) NOT NULL, -- 药品商品名
|
||
generic_name VARCHAR(500) NOT NULL, -- 通用名/活性成分
|
||
target VARCHAR(200), -- 靶点/作用机制
|
||
indication TEXT NOT NULL, -- 获批适应症
|
||
cancer_type_tag_id UUID REFERENCES global_tags(id) ON DELETE SET NULL, -- 关联癌种标签
|
||
approval_agency VARCHAR(10) NOT NULL, -- 审批机构:FDA/NMPA/EMA
|
||
approval_type VARCHAR(30), -- 审批类型:regular/accelerated/breakthrough
|
||
approval_date DATE NOT NULL, -- 批准日期
|
||
pivotal_trial_nct VARCHAR(20), -- 关键试验 NCT 号
|
||
pivotal_trial_pmid INTEGER, -- 关键试验 PMID(非外键)
|
||
source_url VARCHAR(1000), -- 数据来源 URL
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
CREATE INDEX idx_da_agency_date ON drug_approvals(approval_agency, approval_date);
|
||
```
|
||
|
||
> `pivotal_trial_pmid` 是普通 INTEGER 而非外键:因为试验可能在文献入库前就已记录,删除文献也不应级联删除审批记录。
|
||
|
||
### 5.2 guideline_versions + guideline_evidence — 指南版本与证据
|
||
|
||
```sql
|
||
CREATE TABLE guideline_versions (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 指南版本ID
|
||
guideline_source VARCHAR(10) NOT NULL, -- 指南来源:NCCN/ASCO/CSCO
|
||
cancer_type_tag_id UUID NOT NULL REFERENCES global_tags(id) ON DELETE RESTRICT, -- 关联癌种标签
|
||
version VARCHAR(30) NOT NULL, -- 版本号
|
||
publish_date DATE NOT NULL, -- 发布日期
|
||
change_summary_zh TEXT, -- 中文变更摘要
|
||
change_summary_en TEXT, -- 英文变更摘要
|
||
pdf_url VARCHAR(1000), -- PDF 下载 URL
|
||
source_url VARCHAR(1000), -- 来源 URL
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
UNIQUE (guideline_source, cancer_type_tag_id, version)
|
||
);
|
||
|
||
CREATE TABLE guideline_evidence (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 证据ID
|
||
guideline_version_id UUID NOT NULL REFERENCES guideline_versions(id) ON DELETE CASCADE, -- 指南版本ID
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
recommendation TEXT, -- 推荐意见
|
||
recommendation_level VARCHAR(10), -- 推荐级别:1A/1B/2A/2B/3
|
||
treatment_line VARCHAR(20), -- 治疗线数:first/second/third
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
UNIQUE (guideline_version_id, literature_id)
|
||
);
|
||
```
|
||
|
||
> `guideline_versions.cancer_type_tag_id` 使用 `ON DELETE RESTRICT`:指南指向的癌种标签禁止被删除。
|
||
|
||
---
|
||
|
||
## 六、审批表(企业版)
|
||
|
||
### 6.1 approval_workflows — 审批工作流
|
||
|
||
```sql
|
||
CREATE TABLE approval_workflows (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 工作流ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
submitted_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 提交人
|
||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/approved/rejected
|
||
current_step INTEGER NOT NULL DEFAULT 1, -- 当前步骤
|
||
total_steps INTEGER NOT NULL DEFAULT 1, -- 总步骤数
|
||
comment TEXT, -- 备注
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
|
||
CREATE TABLE approval_steps (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 步骤ID
|
||
workflow_id UUID NOT NULL REFERENCES approval_workflows(id) ON DELETE CASCADE, -- 工作流ID
|
||
step_order INTEGER NOT NULL, -- 步骤序号
|
||
approver_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 审批人
|
||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/approved/rejected
|
||
comment TEXT, -- 审批意见
|
||
decided_at TIMESTAMPTZ, -- 审批时间
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
```
|
||
|
||
### 6.2 ai_provider_config — AI 供应商配置
|
||
|
||
```sql
|
||
CREATE TABLE ai_provider_config (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 配置ID
|
||
provider VARCHAR(50) NOT NULL, -- AI 供应商:deepseek/openai/claude
|
||
api_key VARCHAR(500) NOT NULL, -- API Key(加密存储)
|
||
base_url VARCHAR(500) NOT NULL, -- API 基础 URL
|
||
model_name VARCHAR(100) NOT NULL, -- 模型名称
|
||
temperature INTEGER NOT NULL DEFAULT 3, -- 温度参数(0-10,展示为0.0-1.0)
|
||
is_active BOOLEAN NOT NULL DEFAULT TRUE, -- 是否激活
|
||
updated_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 最后修改人
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## 七、运营与基础设施表
|
||
|
||
### 7.1 pipeline_runs — 数据管道日志
|
||
|
||
```sql
|
||
CREATE TABLE pipeline_runs (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 运行ID
|
||
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, -- 总文献数
|
||
articles_new INTEGER DEFAULT 0, -- 新增文献数
|
||
articles_updated INTEGER DEFAULT 0, -- 更新文献数
|
||
articles_deleted INTEGER DEFAULT 0, -- 删除文献数
|
||
articles_filtered INTEGER DEFAULT 0, -- 过滤文献数
|
||
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() -- 创建时间
|
||
);
|
||
CREATE INDEX idx_pr_type ON pipeline_runs(run_type, created_at);
|
||
```
|
||
|
||
### 7.2 tenant_subscriptions — Stripe 订阅
|
||
|
||
```sql
|
||
CREATE TABLE tenant_subscriptions (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 订阅ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE UNIQUE, -- 租户ID
|
||
stripe_customer_id VARCHAR(100), -- Stripe 客户ID
|
||
stripe_subscription_id VARCHAR(100), -- Stripe 订阅ID
|
||
plan_id VARCHAR(50), -- 套餐ID
|
||
status VARCHAR(30) NOT NULL DEFAULT 'active', -- 状态:active/past_due/canceled
|
||
current_period_start TIMESTAMPTZ NOT NULL, -- 当前计费周期开始
|
||
current_period_end TIMESTAMPTZ NOT NULL, -- 当前计费周期结束
|
||
cancel_at_period_end BOOLEAN DEFAULT FALSE, -- 是否到期取消
|
||
trial_end TIMESTAMPTZ, -- 试用结束时间
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX idx_ts_stripe_sub ON tenant_subscriptions(stripe_subscription_id);
|
||
|
||
CREATE TABLE subscription_events ( -- 订阅事件日志
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 事件ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
event_type VARCHAR(30) NOT NULL, -- 事件类型
|
||
from_plan VARCHAR(30), -- 原套餐
|
||
to_plan VARCHAR(30), -- 目标套餐
|
||
stripe_event_id VARCHAR(100), -- Stripe 事件ID
|
||
metadata JSONB, -- 附加元数据
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
CREATE INDEX idx_se_tenant ON subscription_events(tenant_id);
|
||
```
|
||
|
||
### 7.3 payment_orders — 支付订单
|
||
|
||
```sql
|
||
CREATE TABLE payment_orders (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 订单ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
plan VARCHAR(30) NOT NULL, -- 套餐:pro/team/enterprise
|
||
amount INTEGER NOT NULL, -- 金额(分)
|
||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/paid/expired/cancelled
|
||
out_trade_no VARCHAR(64) UNIQUE NOT NULL, -- 商户订单号
|
||
prepay_id VARCHAR(128), -- 预支付ID(微信)
|
||
code_url VARCHAR(512), -- 支付二维码 URL
|
||
pay_time TIMESTAMPTZ, -- 支付时间
|
||
transaction_id VARCHAR(64), -- 支付平台交易ID
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
CREATE INDEX idx_po_tenant ON payment_orders(tenant_id);
|
||
```
|
||
|
||
### 7.4 api_keys + api_usage_logs — API密钥与使用日志
|
||
|
||
```sql
|
||
CREATE TABLE api_keys (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 密钥ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 创建者
|
||
name VARCHAR(200) NOT NULL, -- 密钥名称
|
||
key_prefix VARCHAR(20) NOT NULL, -- 密钥前缀(便于识别)
|
||
key_hash VARCHAR(128) NOT NULL, -- 密钥哈希
|
||
scopes JSONB DEFAULT '[]', -- 权限范围
|
||
last_used_at TIMESTAMPTZ, -- 最后使用时间
|
||
expires_at TIMESTAMPTZ, -- 过期时间
|
||
is_active BOOLEAN DEFAULT TRUE, -- 是否激活
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
CREATE INDEX idx_ak_tenant_active ON api_keys(tenant_id, is_active);
|
||
|
||
CREATE TABLE api_usage_logs (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 日志ID
|
||
api_key_id UUID REFERENCES api_keys(id) ON DELETE SET NULL, -- API密钥ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
endpoint VARCHAR(300) NOT NULL, -- 请求端点
|
||
method VARCHAR(10) NOT NULL, -- HTTP 方法
|
||
status_code INTEGER, -- HTTP 状态码
|
||
response_ms INTEGER, -- 响应时间(毫秒)
|
||
ip_address VARCHAR(45), -- 请求IP
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
CREATE INDEX idx_aul_tenant ON api_usage_logs(tenant_id);
|
||
CREATE INDEX idx_aul_tenant_created ON api_usage_logs(tenant_id, created_at);
|
||
```
|
||
|
||
### 7.5 export_jobs — 导出任务
|
||
|
||
```sql
|
||
CREATE TABLE export_jobs (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 任务ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
export_format VARCHAR(10) NOT NULL, -- 导出格式:ris/bibtex/csv/pdf
|
||
citation_style VARCHAR(50), -- 引文格式
|
||
filter_params JSONB, -- 筛选参数
|
||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/processing/done/failed
|
||
file_url VARCHAR(1000), -- 文件下载 URL
|
||
file_size_bytes INTEGER, -- 文件大小(字节)
|
||
error_message TEXT, -- 错误信息
|
||
total_items INTEGER, -- 导出条目数
|
||
completed_at TIMESTAMPTZ, -- 完成时间
|
||
expires_at TIMESTAMPTZ, -- 文件过期时间
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
CREATE INDEX idx_ej_user_status ON export_jobs(user_id, status);
|
||
```
|
||
|
||
### 7.6 system_notifications — 系统通知
|
||
|
||
```sql
|
||
CREATE TABLE system_notifications (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 通知ID
|
||
title VARCHAR(300) NOT NULL, -- 通知标题
|
||
content TEXT, -- 通知内容
|
||
target_type VARCHAR(20) NOT NULL DEFAULT 'all', -- 目标类型:all/tenant/user
|
||
target_id UUID, -- 目标ID(对应租户或用户)
|
||
is_read BOOLEAN DEFAULT FALSE, -- 是否已读
|
||
read_at TIMESTAMPTZ, -- 阅读时间
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## 八、系统评价 / Meta 分析
|
||
|
||
### 8.1 systematic_reviews — 系统评价项目
|
||
|
||
```sql
|
||
CREATE TABLE systematic_reviews (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 评价项目ID
|
||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 创建者
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
title VARCHAR(500) NOT NULL, -- 评价标题
|
||
objective TEXT, -- 研究目的
|
||
protocol TEXT, -- 研究方案
|
||
inclusion_criteria JSONB DEFAULT '[]', -- 纳入标准 [str]
|
||
exclusion_criteria JSONB DEFAULT '[]', -- 排除标准 [str]
|
||
databases_searched JSONB DEFAULT '[]', -- 检索数据库 [str]
|
||
search_strategy JSONB DEFAULT '{}', -- 检索策略 {pubmed: "..."}
|
||
prisma_stage VARCHAR(20) DEFAULT 'identification', -- PRISMA 阶段
|
||
status VARCHAR(20) DEFAULT 'draft', -- 状态:draft/active/completed/archived
|
||
notes TEXT, -- 备注
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
```
|
||
|
||
**prisma_stage 流转**:`identification → screening → eligibility → included`
|
||
|
||
**status**:`draft / active / completed / archived`
|
||
|
||
### 8.2 review_literatures — 系统评价文献
|
||
|
||
```sql
|
||
CREATE TABLE review_literatures (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 记录ID
|
||
review_id UUID NOT NULL REFERENCES systematic_reviews(id) ON DELETE CASCADE, -- 系统评价ID
|
||
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
|
||
stage VARCHAR(20) NOT NULL DEFAULT 'screening', -- 当前阶段
|
||
exclusion_reason TEXT, -- 排除原因
|
||
notes TEXT, -- 备注
|
||
quality_score VARCHAR(10), -- 质量评分:high/moderate/low/unclear
|
||
quality_detail JSONB DEFAULT '{}', -- 质量评估细节 {domain: "low", ...}
|
||
pico JSONB, -- 评价级 PICO(与文献库隔离)
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
```
|
||
|
||
> `pico` 字段与 `global_literature.pico` 隔离:同一篇文献被多个系统评价引用时,各评价可独立编辑 PICO。
|
||
|
||
---
|
||
|
||
## 九、MDT 多学科协作
|
||
|
||
### 9.1 mdt_cases — MDT 病例
|
||
|
||
```sql
|
||
CREATE TABLE mdt_cases (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 病例ID
|
||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
|
||
title VARCHAR(300) NOT NULL, -- 病例标题
|
||
patient_summary TEXT NOT NULL, -- 患者摘要
|
||
cancer_type VARCHAR(100), -- 癌种(冗余字段,用于快速筛选)
|
||
status VARCHAR(20) DEFAULT 'open', -- 状态:open/closed
|
||
created_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 创建者
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
|
||
);
|
||
```
|
||
|
||
### 9.2 mdt_decisions — MDT 决策
|
||
|
||
```sql
|
||
CREATE TABLE mdt_decisions (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 决策ID
|
||
case_id UUID NOT NULL REFERENCES mdt_cases(id) ON DELETE CASCADE, -- 关联病例
|
||
conclusion TEXT NOT NULL, -- 决策结论
|
||
meeting_date TIMESTAMPTZ, -- 会议时间
|
||
participants JSONB DEFAULT '[]', -- 参会人员 [{user_id, display_name, role}]
|
||
decided_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 决策者
|
||
decided_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 决策时间
|
||
);
|
||
```
|
||
|
||
### 9.3 mdt_evidence — MDT 引用证据
|
||
|
||
```sql
|
||
CREATE TABLE mdt_evidence (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 证据ID
|
||
case_id UUID NOT NULL REFERENCES mdt_cases(id) ON DELETE CASCADE, -- 关联病例
|
||
decision_id UUID REFERENCES mdt_decisions(id) ON DELETE SET NULL, -- 关联决策
|
||
literature_id UUID REFERENCES global_literature(id) ON DELETE SET NULL, -- 关联文献(可为NULL)
|
||
pmid INTEGER, -- PubMed ID(备用)
|
||
citation TEXT, -- 引文文本
|
||
relevance_note TEXT, -- 相关性说明
|
||
added_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 添加人
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
|
||
);
|
||
```
|
||
|
||
> `literature_id` 和 `pmid` 双字段:文献可能尚未入库(只有 PMID),此时先存 PMID,待 pipeline 灌入后关联。
|
||
|
||
---
|
||
|
||
## 十、全表统计数据
|
||
|
||
| # | 表名 | 预估数据量 | 说明 |
|
||
|---|------|:---:|---|
|
||
| 1 | tenants | 百~千 | |
|
||
| 2 | users | 千~万 | |
|
||
| 3 | user_tenants | 千~万 | |
|
||
| 4 | login_logs | 万~百万 | |
|
||
| 5 | global_literature | **~600万** | B-tree 索引足够 |
|
||
| 6 | global_tags | ~800 | |
|
||
| 7 | global_literature_tags | **~6000万** | B-tree 索引足够 |
|
||
| 8 | global_journals | ~9000 | priority_score DESC 索引 |
|
||
| 9 | user_subscriptions | 千~万 | |
|
||
| 10 | user_journal_subscriptions | 千~万 | |
|
||
| 11 | user_feed | **年增数千万** | ⚠️ 生产需按月分区,保留3个月 |
|
||
| 12 | user_literature | 万~百万 | |
|
||
| 13 | user_folders | 千~万 | |
|
||
| 14 | user_notes | 万~十万 | |
|
||
| 15 | user_pdf_highlights | 万~十万 | |
|
||
| 16 | user_feedback | 千 | |
|
||
| 17 | teams | 千 | |
|
||
| 18 | team_members | 千~万 | |
|
||
| 19 | invitations | 千 | |
|
||
| 20 | shared_folders | 千 | |
|
||
| 21 | journal_club_queue | 千 | |
|
||
| 22 | drug_approvals | ~500/年 | |
|
||
| 23 | guideline_versions | ~200/年 | |
|
||
| 24 | guideline_evidence | 万 | |
|
||
| 25 | approval_workflows | 千 | |
|
||
| 26 | approval_steps | 千 | |
|
||
| 27 | ai_provider_config | ~5 | |
|
||
| 28 | pipeline_runs | ~365/年 | |
|
||
| 29 | tenant_subscriptions | 百~千 | |
|
||
| 29 | subscription_events | 千 | |
|
||
| 30 | payment_orders | 千 | |
|
||
| 31 | api_keys | 百 | |
|
||
| 32 | api_usage_logs | 百万 | 按月分区(可选) |
|
||
| 33 | system_notifications | 千 | |
|
||
| 34 | systematic_reviews | 百~千 | |
|
||
| 35 | review_literatures | 千~万 | |
|
||
| 36 | mdt_cases | 百~千 | |
|
||
| 37 | mdt_decisions | 百~千 | |
|
||
| 38 | mdt_evidence | 千 | |
|
||
| 39 | export_jobs | 千 | |
|